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 }