github.com/CyCoreSystems/ari@v4.8.4+incompatible/client/native/channel.go (about)

     1  package native
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"time"
     7  
     8  	"github.com/CyCoreSystems/ari"
     9  	"github.com/CyCoreSystems/ari/rid"
    10  )
    11  
    12  // Channel provides the ARI Channel accessors for the native client
    13  type Channel struct {
    14  	client *Client
    15  }
    16  
    17  // List lists the current channels and returns the list of channel handles
    18  func (c *Channel) List(filter *ari.Key) (cx []*ari.Key, err error) {
    19  	var channels = []struct {
    20  		ID string `json:"id"`
    21  	}{}
    22  
    23  	if filter == nil {
    24  		filter = ari.NewKey(ari.ChannelKey, "")
    25  	}
    26  
    27  	err = c.client.get("/channels", &channels)
    28  	for _, i := range channels {
    29  		k := c.client.stamp(ari.NewKey(ari.ChannelKey, i.ID))
    30  		if filter.Match(k) {
    31  			cx = append(cx, k)
    32  		}
    33  	}
    34  
    35  	return
    36  }
    37  
    38  // Hangup hangs up the given channel using the (optional) reason.
    39  func (c *Channel) Hangup(key *ari.Key, reason string) error {
    40  	if key == nil || key.ID == "" {
    41  		return errors.New("channel key not supplied")
    42  	}
    43  	if reason == "" {
    44  		reason = "normal"
    45  	}
    46  
    47  	var req string
    48  	if reason != "" {
    49  		req = fmt.Sprintf("reason=%s", reason)
    50  	}
    51  	return c.client.del("/channels/"+key.ID, nil, req)
    52  }
    53  
    54  // Data retrieves the current state of the channel
    55  func (c *Channel) Data(key *ari.Key) (*ari.ChannelData, error) {
    56  	if key == nil || key.ID == "" {
    57  		return nil, errors.New("channel key not supplied")
    58  	}
    59  
    60  	var data = new(ari.ChannelData)
    61  	if err := c.client.get("/channels/"+key.ID, data); err != nil {
    62  		return nil, dataGetError(err, "channel", "%v", key.ID)
    63  	}
    64  	data.Key = c.client.stamp(key)
    65  
    66  	return data, nil
    67  }
    68  
    69  // Get gets the lazy handle for the given channel
    70  func (c *Channel) Get(key *ari.Key) *ari.ChannelHandle {
    71  	return ari.NewChannelHandle(c.client.stamp(key), c, nil)
    72  }
    73  
    74  // Originate originates a channel and returns the handle.
    75  //
    76  // **Note** that referenceKey is completely optional.  It is used for placing
    77  // the new channel onto the correct Asterisk node and for assigning default
    78  // values for communications parameters such as codecs.
    79  func (c *Channel) Originate(referenceKey *ari.Key, req ari.OriginateRequest) (*ari.ChannelHandle, error) {
    80  	h, err := c.StageOriginate(referenceKey, req)
    81  	if err != nil {
    82  		return nil, err
    83  	}
    84  	return h, h.Exec()
    85  }
    86  
    87  // StageOriginate creates a new channel handle with a channel originate request
    88  // staged.
    89  //
    90  // **Note** that referenceKey is completely optional.  It is used for placing
    91  // the new channel onto the correct Asterisk node and for assigning default
    92  // values for communications parameters such as codecs.
    93  func (c *Channel) StageOriginate(referenceKey *ari.Key, req ari.OriginateRequest) (*ari.ChannelHandle, error) {
    94  	if referenceKey != nil && req.Originator == "" && referenceKey.Kind == ari.ChannelKey {
    95  		req.Originator = referenceKey.ID
    96  	}
    97  
    98  	if req.ChannelID == "" {
    99  		req.ChannelID = rid.New(rid.Channel)
   100  	}
   101  
   102  	return ari.NewChannelHandle(c.client.stamp(ari.NewKey(ari.ChannelKey, req.ChannelID)), c,
   103  		func(ch *ari.ChannelHandle) error {
   104  			type response struct {
   105  				ID string `json:"id"`
   106  			}
   107  
   108  			var resp response
   109  
   110  			return c.client.post("/channels", &resp, &req)
   111  		},
   112  	), nil
   113  }
   114  
   115  // Create creates a channel and returns the handle. TODO: expand
   116  // differences between originate and create.
   117  func (c *Channel) Create(key *ari.Key, req ari.ChannelCreateRequest) (*ari.ChannelHandle, error) {
   118  	if key != nil && req.Originator == "" && key.Kind == ari.ChannelKey {
   119  		req.Originator = key.ID
   120  	}
   121  
   122  	if req.ChannelID == "" {
   123  		req.ChannelID = rid.New(rid.Channel)
   124  	}
   125  
   126  	err := c.client.post("/channels/create", nil, &req)
   127  	if err != nil {
   128  		return nil, err
   129  	}
   130  
   131  	return ari.NewChannelHandle(c.client.stamp(ari.NewKey(ari.ChannelKey, req.ChannelID)), c, nil), nil
   132  }
   133  
   134  // Continue tells a channel to process to the given ARI context and extension
   135  func (c *Channel) Continue(key *ari.Key, context, extension string, priority int) (err error) {
   136  	req := struct {
   137  		Context   string `json:"context"`
   138  		Extension string `json:"extension"`
   139  		Priority  int    `json:"priority"`
   140  	}{
   141  		Context:   context,
   142  		Extension: extension,
   143  		Priority:  priority,
   144  	}
   145  	return c.client.post("/channels/"+key.ID+"/continue", nil, &req)
   146  }
   147  
   148  // Busy sends the busy status code to the channel (TODO: does this play a busy signal too)
   149  func (c *Channel) Busy(key *ari.Key) error {
   150  	return c.Hangup(key, "busy")
   151  }
   152  
   153  // Congestion sends the congestion status code to the channel (TODO: does this play a tone?)
   154  func (c *Channel) Congestion(key *ari.Key) error {
   155  	return c.Hangup(key, "congestion")
   156  }
   157  
   158  // Answer answers a channel, if ringing (TODO: does this return an error if already answered?)
   159  func (c *Channel) Answer(key *ari.Key) error {
   160  	return c.client.post("/channels/"+key.ID+"/answer", nil, nil)
   161  }
   162  
   163  // Ring causes a channel to start ringing (TODO: does this return an error if already ringing?)
   164  func (c *Channel) Ring(key *ari.Key) error {
   165  	return c.client.post("/channels/"+key.ID+"/ring", nil, nil)
   166  }
   167  
   168  // StopRing causes a channel to stop ringing (TODO: does this return an error if not ringing?)
   169  func (c *Channel) StopRing(key *ari.Key) error {
   170  	return c.client.del("/channels/"+key.ID+"/ring", nil, "")
   171  }
   172  
   173  // Hold puts a channel on hold (TODO: does this return an error if already on hold?)
   174  func (c *Channel) Hold(key *ari.Key) error {
   175  	return c.client.post("/channels/"+key.ID+"/hold", nil, nil)
   176  }
   177  
   178  // StopHold removes a channel from hold (TODO: does this return an error if not on hold)
   179  func (c *Channel) StopHold(key *ari.Key) (err error) {
   180  	return c.client.del("/channels/"+key.ID+"/hold", nil, "")
   181  }
   182  
   183  // Mute mutes a channel in the given direction (TODO: does this return an error if already muted)
   184  func (c *Channel) Mute(key *ari.Key, dir ari.Direction) error {
   185  	if dir == "" {
   186  		dir = ari.DirectionBoth
   187  	}
   188  
   189  	req := struct {
   190  		Direction ari.Direction `json:"direction,omitempty"`
   191  	}{
   192  		Direction: dir,
   193  	}
   194  	return c.client.post("/channels/"+key.ID+"/mute", nil, &req)
   195  }
   196  
   197  // Unmute unmutes a channel in the given direction (TODO: does this return an error if unmuted)
   198  func (c *Channel) Unmute(key *ari.Key, dir ari.Direction) (err error) {
   199  	if dir == "" {
   200  		dir = ari.DirectionBoth
   201  	}
   202  	req := fmt.Sprintf("direction=%s", dir)
   203  	return c.client.del("/channels/"+key.ID+"/mute", nil, req)
   204  }
   205  
   206  // SendDTMF sends a string of digits and symbols to the channel
   207  func (c *Channel) SendDTMF(key *ari.Key, dtmf string, opts *ari.DTMFOptions) error {
   208  
   209  	if opts == nil {
   210  		opts = &ari.DTMFOptions{}
   211  	}
   212  
   213  	if opts.Duration < 1 {
   214  		opts.Duration = 100 // ARI default, for documenation
   215  	}
   216  	if opts.Between < 1 {
   217  		opts.Between = 100 // ARI default, for documentation
   218  	}
   219  
   220  	req := struct {
   221  		Dtmf     string `json:"dtmf,omitempty"`
   222  		Before   int    `json:"before,omitempty"`
   223  		Between  int    `json:"between,omitempty"`
   224  		Duration int    `json:"duration,omitempty"`
   225  		After    int    `json:"after,omitempty"`
   226  	}{
   227  		Dtmf:     dtmf,
   228  		Before:   int(opts.Before / time.Millisecond),
   229  		After:    int(opts.After / time.Millisecond),
   230  		Duration: int(opts.Duration / time.Millisecond),
   231  		Between:  int(opts.Between / time.Millisecond),
   232  	}
   233  
   234  	return c.client.post("/channels/"+key.ID+"/dtmf", nil, &req)
   235  }
   236  
   237  // MOH plays the given music on hold class to the channel TODO: does this error when already playing MOH?
   238  func (c *Channel) MOH(key *ari.Key, class string) error {
   239  	req := struct {
   240  		Class string `json:"mohClass,omitempty"`
   241  	}{
   242  		Class: class,
   243  	}
   244  	return c.client.post("/channels/"+key.ID+"/moh", nil, &req)
   245  }
   246  
   247  // StopMOH stops any music on hold playing on the channel (TODO: does this error when no MOH is playing?)
   248  func (c *Channel) StopMOH(key *ari.Key) error {
   249  	return c.client.del("/channels/"+key.ID+"/moh", nil, "")
   250  }
   251  
   252  // Silence silences a channel (TODO: does this error when already silences)
   253  func (c *Channel) Silence(key *ari.Key) error {
   254  	return c.client.post("/channels/"+key.ID+"/silence", nil, nil)
   255  }
   256  
   257  // StopSilence stops the silence on a channel (TODO: does this error when not silenced)
   258  func (c *Channel) StopSilence(key *ari.Key) error {
   259  	return c.client.del("/channels/"+key.ID+"/silence", nil, "")
   260  }
   261  
   262  // Play plays the given media URI on the channel, using the playbackID as
   263  // the identifier of the created ARI Playback entity
   264  func (c *Channel) Play(key *ari.Key, playbackID string, mediaURI string) (*ari.PlaybackHandle, error) {
   265  	h, err := c.StagePlay(key, playbackID, mediaURI)
   266  	if err != nil {
   267  		return nil, err
   268  	}
   269  	return h, h.Exec()
   270  }
   271  
   272  // StagePlay stages a `Play` operation on the bridge
   273  func (c *Channel) StagePlay(key *ari.Key, playbackID string, mediaURI string) (*ari.PlaybackHandle, error) {
   274  	resp := make(map[string]interface{})
   275  	req := struct {
   276  		Media string `json:"media"`
   277  	}{
   278  		Media: mediaURI,
   279  	}
   280  
   281  	playbackKey := c.client.stamp(ari.NewKey(ari.PlaybackKey, playbackID))
   282  	return ari.NewPlaybackHandle(playbackKey, c.client.Playback(), func(pb *ari.PlaybackHandle) error {
   283  		return c.client.post("/channels/"+key.ID+"/play/"+playbackID, &resp, &req)
   284  	}), nil
   285  }
   286  
   287  // Record records audio on the channel, using the name parameter as the name of the
   288  // created LiveRecording entity.
   289  func (c *Channel) Record(key *ari.Key, name string, opts *ari.RecordingOptions) (*ari.LiveRecordingHandle, error) {
   290  	h, err := c.StageRecord(key, name, opts)
   291  	if err != nil {
   292  		return nil, err
   293  	}
   294  	return h, h.Exec()
   295  }
   296  
   297  // StageRecord stages a `Record` opreation
   298  func (c *Channel) StageRecord(key *ari.Key, name string, opts *ari.RecordingOptions) (*ari.LiveRecordingHandle, error) {
   299  
   300  	if opts == nil {
   301  		opts = &ari.RecordingOptions{}
   302  	}
   303  
   304  	resp := make(map[string]interface{})
   305  	req := struct {
   306  		Name        string `json:"name"`
   307  		Format      string `json:"format"`
   308  		MaxDuration int    `json:"maxDurationSeconds"`
   309  		MaxSilence  int    `json:"maxSilenceSeconds"`
   310  		IfExists    string `json:"ifExists,omitempty"`
   311  		Beep        bool   `json:"beep"`
   312  		TerminateOn string `json:"terminateOn,omitempty"`
   313  	}{
   314  		Name:        name,
   315  		Format:      opts.Format,
   316  		MaxDuration: int(opts.MaxDuration / time.Second),
   317  		MaxSilence:  int(opts.MaxSilence / time.Second),
   318  		IfExists:    opts.Exists,
   319  		Beep:        opts.Beep,
   320  		TerminateOn: opts.Terminate,
   321  	}
   322  
   323  	recordingKey := c.client.stamp(ari.NewKey(ari.LiveRecordingKey, name))
   324  
   325  	return ari.NewLiveRecordingHandle(recordingKey, c.client.LiveRecording(), func(h *ari.LiveRecordingHandle) error {
   326  		return c.client.post("/channels/"+key.ID+"/record", &resp, &req)
   327  	}), nil
   328  }
   329  
   330  // Snoop snoops on a channel, using the the given snoopID as the new channel handle ID (TODO: confirm and expand description)
   331  func (c *Channel) Snoop(key *ari.Key, snoopID string, opts *ari.SnoopOptions) (*ari.ChannelHandle, error) {
   332  	h, err := c.StageSnoop(key, snoopID, opts)
   333  	if err != nil {
   334  		return nil, err
   335  	}
   336  	return h, h.Exec()
   337  }
   338  
   339  // StageSnoop creates a new `ChannelHandle` with a `Snoop` operation staged.
   340  func (c *Channel) StageSnoop(key *ari.Key, snoopID string, opts *ari.SnoopOptions) (*ari.ChannelHandle, error) {
   341  	if opts == nil {
   342  		opts = &ari.SnoopOptions{App: c.client.ApplicationName()}
   343  	}
   344  	if snoopID == "" {
   345  		snoopID = rid.New(rid.Snoop)
   346  	}
   347  
   348  	// Create the snooping channel's key
   349  	k := c.client.stamp(ari.NewKey(ari.ChannelKey, snoopID))
   350  
   351  	return ari.NewChannelHandle(k, c, func(ch *ari.ChannelHandle) error {
   352  		return c.client.post("/channels/"+key.ID+"/snoop/"+snoopID, nil, &opts)
   353  	}), nil
   354  }
   355  
   356  // Dial dials the given calling channel identifier
   357  func (c *Channel) Dial(key *ari.Key, callingChannelID string, timeout time.Duration) error {
   358  	req := struct {
   359  		// Caller is the (optional) channel ID of another channel to which media negotiations for the newly-dialed channel will be associated.
   360  		Caller string `json:"caller,omitempty"`
   361  
   362  		// Timeout is the maximum amount of time to allow for the dial to complete.
   363  		Timeout int `json:"timeout"`
   364  	}{
   365  		Caller:  callingChannelID,
   366  		Timeout: int(timeout.Seconds()),
   367  	}
   368  
   369  	return c.client.post("/channels/"+key.ID+"/dial", nil, &req)
   370  }
   371  
   372  // Subscribe creates a new subscription for ARI events related to this channel
   373  func (c *Channel) Subscribe(key *ari.Key, n ...string) ari.Subscription {
   374  	return c.client.Bus().Subscribe(key, n...)
   375  }
   376  
   377  // GetVariable gets the value of the given variable
   378  func (c *Channel) GetVariable(key *ari.Key, name string) (string, error) {
   379  	var m struct {
   380  		Value string `json:"value"`
   381  	}
   382  
   383  	err := c.client.get(fmt.Sprintf("/channels/%s/variable?variable=%s", key.ID, name), &m)
   384  	return m.Value, err
   385  }
   386  
   387  // SetVariable sets the value of the given channel variable
   388  func (c *Channel) SetVariable(key *ari.Key, name, value string) error {
   389  	req := struct {
   390  		Name  string `json:"variable"`
   391  		Value string `json:"value,omitempty"`
   392  	}{
   393  		Name:  name,
   394  		Value: value,
   395  	}
   396  
   397  	return c.client.post(fmt.Sprintf("/channels/%s/variable", key.ID), nil, &req)
   398  }