Test Failed
Push — master ( f016cd...f8dfd1 )
by Vyacheslav
01:47
created

goagi.*AGI.GoSub   B

Complexity

Conditions 8

Size

Total Lines 22
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 8
eloc 17
dl 0
loc 22
rs 7.3333
c 0
b 0
f 0
nop 4
1
package goagi
2
3
import (
4
	"fmt"
5
)
6
7
// Command sends command as string to the AGI and returns response valus with
8
// text response
9
func (agi *AGI) Command(cmd string) (code int, result int, respStr string, err error) {
10
	resp, err := agi.execute(cmd)
11
	if err != nil {
12
		return -1, -1, "", err
13
	}
14
	return resp.code, int(resp.result), resp.raw, nil
15
}
16
17
// Answer executes AGI command "ANSWER"
18
// Answers channel if not already in answer state.
19
func (agi *AGI) Answer() (bool, error) {
20
	resp, err := agi.execute("ANSWER")
21
	if err != nil {
22
		return false, err
23
	}
24
	return resp.isOk(), nil
25
}
26
27
// AsyncAGIBreak Interrupts Async AGI
28
//	Interrupts expected flow of Async AGI commands and returns control
29
// to previous source (typically, the PBX dialplan).
30
func (agi *AGI) AsyncAGIBreak() (bool, error) {
31
	resp, err := agi.execute("ASYNCAGI BREAK")
32
	if err != nil {
33
		return false, err
34
	}
35
	// Asterisk res_agi always returns 200 result=0
36
	// but for the future try to check response.
37
	return resp.isOk(), nil
38
}
39
40
// ChannelStatus returns status of the connected channel.
41
//
42
// If no channel name is given (empty line) then returns the status of the current channel.
43
//
44
//Return values:
45
//	0 - Channel is down and available.
46
//	1 - Channel is down, but reserved.
47
//	2 - Channel is off hook.
48
//	3 - Digits (or equivalent) have been dialed.
49
//	4 - Line is ringing.
50
//	5 - Remote end is ringing.
51
//	6 - Line is up.
52
//	7 - Line is busy.
53
func (agi *AGI) ChannelStatus(channel string) (int, error) {
54
	resp, err := agi.execute("CHANNEL STATUS " + channel)
55
	if err != nil {
56
		return -1, err
57
	}
58
	if resp.result == -1 {
59
		return -1, errorNew("No channel name matched the argument given.")
60
	}
61
	return int(resp.result), nil
62
}
63
64
// ControlStreamFile sends audio file on channel and allows the listener
65
// to control the stream.
66
//	Send the given file, allowing playback to be controlled by the given digits, if any.
67
// Use double quotes for the digits if you wish none to be permitted. If offsetms
68
// is provided then the audio will seek to offsetms before play starts.
69
//	Returns 0 if playback completes without a digit being pressed, or the ASCII numerical
70
// value of the digit if one was pressed, or -1 on error or if the channel was
71
// disconnected.
72
//	Returns the position where playback was terminated as endpos.
73
//	Example:
74
//	agi.ControlStreamFile("prompt_en", "19", "3000", "#", "0", "#", "1600")
75
//	agi.ControlStreamFile("prompt_en", "")
76
//	agi.ControlStreamFile("prompt_en", "19", "", "", "", "#", "1600")
77
func (agi *AGI) ControlStreamFile(filename, digits string, args ...interface{}) (int32, error) {
78
	cmd := "CONTROL STREAM FILE " + filename
79
	if len(digits) > 0 {
80
		cmd += " " + digits
81
	} else {
82
		cmd += " \"\""
83
	}
84
	resp, err := agi.execute(cmd, args...)
85
	if err != nil {
86
		return 0, err
87
	}
88
	if resp.result == -1 {
89
		return resp.result, errorNew("Error or channel disconnected.")
90
	}
91
92
	return resp.endpos, nil
93
}
94
95
// DatabaseDel deletes an entry in the Asterisk database for a given family and key.
96
//	Returns status and error if fails.
97
func (agi *AGI) DatabaseDel(family, key string) (bool, error) {
98
	resp, err := agi.execute("DATABASE DELETE", family, key)
99
	if err != nil {
100
		return false, err
101
	}
102
	ok := resp.code == 200 && resp.result == 1
103
	return ok, nil
104
}
105
106
// DatabaseDelTree deletes a family or specific keytree within a family in the Asterisk database.
107
func (agi *AGI) DatabaseDelTree(family, keytree string) (bool, error) {
108
	resp, err := agi.execute("DATABASE DELTREE", family, keytree)
109
	if err != nil {
110
		return false, err
111
	}
112
	ok := resp.code == 200 && resp.result == 1
113
	return ok, nil
114
}
115
116
// DatabaseGet Retrieves an entry in the Asterisk database for a given family and key.
117
//	Returns value as string or error if failed or value not set
118
func (agi *AGI) DatabaseGet(family, key string) (string, error) {
119
	resp, err := agi.execute("DATABASE GET", family, key)
120
	if err != nil {
121
		return "", err
122
	}
123
	if resp.result == 0 {
124
		return "", errorNew("Value not set.")
125
	}
126
	return resp.value, nil
127
}
128
129
// DatabasePut adds or updates an entry in the Asterisk database for
130
// a given family, key, and value.
131
func (agi *AGI) DatabasePut(family, key, val string) (bool, error) {
132
	resp, err := agi.execute("DATABASE PUT", family, key, val)
133
	if err != nil {
134
		return false, err
135
	}
136
	ok := resp.code == 200 && resp.result == 1
137
	return ok, nil
138
}
139
140
// Exec executes application with given options.
141
func (agi *AGI) Exec(app, opts string) (int, error) {
142
	opts = `"` + opts + `"`
143
	resp, err := agi.execute("EXEC", app, opts)
144
	if err != nil {
145
		return -1, err
146
	}
147
148
	if resp.result == -2 {
149
		return -2, errorNew("Could not find application " + app)
150
	}
151
	return int(resp.result), nil
152
}
153
154
// GetData Stream the given file, and receive DTMF data.
155
func (agi *AGI) GetData(file string, args ...interface{}) (digit string, timeout bool, err error) {
156
	cmd := "GET DATA " + file
157
	resp, err := agi.execute(cmd, args...)
158
	if err != nil {
159
		return "", false, err
160
	}
161
	if resp.result < 0 {
162
		return "", false, errorNew("Failed get data.")
163
	}
164
	timeout = resp.value == "timeout"
165
	digit = string(resp.result)
166
	return
167
}
168
169
// GetFullVariable evaluates a channel expression
170
func (agi *AGI) GetFullVariable(name string, channel ...string) (string, error) {
171
	cmd := "GET FULL VARIABLE " + name
172
	var resp *agiResp
173
	var err error
174
	if len(channel) > 0 {
175
		resp, err = agi.execute(cmd, channel[0])
176
	} else {
177
		resp, err = agi.execute(cmd)
178
	}
179
	if err != nil {
180
		return "", err
181
	}
182
	if resp.result == 0 {
183
		return "", errorNew("Variable is not set.")
184
	}
185
186
	return resp.value, nil
187
}
188
189
// GetOption Stream file, prompt for DTMF, with timeout.
190
//	Behaves similar to STREAM FILE but used with a timeout option.
191
//	Returns digit pressed, offset and error
192
func (agi *AGI) GetOption(filename, digits string, timeout int32) (int, int32, error) {
193
	cmd := "GET OPTION " + filename
194
	resp, err := agi.execute(cmd, digits, timeout)
195
	if err != nil {
196
		return -1, 0, err
197
	}
198
199
	if resp.result == -1 {
200
		return -1, 0, errorNew("Command failure")
201
	}
202
203
	if resp.result == 0 && resp.endpos == 0 {
204
		return -1, 0, errorNew("Failure on open")
205
	}
206
207
	return int(resp.result), resp.endpos, nil
208
}
209
210
// GetVariable Gets a channel variable.
211
func (agi *AGI) GetVariable(name string) (string, error) {
212
	resp, err := agi.execute("GET VARIABLE", name)
213
	if err != nil {
214
		return "", err
215
	}
216
	if resp.result == 0 {
217
		return "", errorNew("Variable is not set.")
218
	}
219
220
	return resp.value, nil
221
}
222
223
// GetVariable Gets a channel variable.
0 ignored issues
show
introduced by
comment on exported method AGI.GoSub should be of the form "GoSub ..."
Loading history...
224
func (agi *AGI) GoSub(ctx, ext, prio, args string) (bool, error) {
225
	resp, err := agi.execute("GOSUB", ctx, ext, prio, args)
226
	if err != nil {
227
		return false, err
228
	}
229
	if resp.result == -1 {
230
		return false, errorNew("Failed Gosub")
231
	}
232
	if resp.result == 0 && resp.code == 100 {
233
		chStr, chErr := agi.read()
234
		select {
235
		case str := <-chStr:
236
			resp, err = parseResponse(str)
237
			if err != nil {
238
				return false, err
239
			}
240
			return resp.code == 200, nil
241
		case err := <-chErr:
242
			return false, err
243
		}
244
	}
245
	return true, nil
246
}
247
248
// Hangup a channel.
249
//	Hangs up the specified channel. If no channel name is given, hangs up the current channel
250
func (agi *AGI) Hangup(channel ...string) (bool, error) {
251
	cmd := "HANGUP"
252
	if len(channel) > 0 {
253
		cmd += " " + channel[0]
254
	}
255
	resp, err := agi.execute(cmd)
256
	if err != nil {
257
		return false, err
258
	}
259
	if resp.result == -1 {
260
		return false, errorNew("Failed hangup")
261
	}
262
	return true, nil
263
}
264
265
// Noop Does nothing.
266
func (agi *AGI) Noop() error {
267
	_, err := agi.execute("NOOP")
268
	return err
269
}
270
271
// ReceiveChar Receives one character from channels supporting it.
272
//	Most channels do not support the reception of text. Returns the decimal value of
273
// the character if one is received, or 0 if the channel does not support text reception.
274
//	timeout - The maximum time to wait for input in milliseconds, or 0 for infinite. Most channels
275
//	Returns -1 only on error/hangup.
276
func (agi *AGI) ReceiveChar(timeout int32) (int, error) {
277
	resp, err := agi.execute("RECEIVE CHAR", timeout)
278
	if err != nil {
279
		return -1, err
280
	}
281
	if resp.result == -1 {
282
		return -1, errorNew("Channel error or hangup.")
283
	}
284
	if resp.result == 0 {
285
		return -1, errorNew("Channel does not support text reception.")
286
	}
287
	return int(resp.result), nil
288
}
289
290
// ReceiveText Receives text from channels supporting it.
291
//	timeout - The timeout to be the maximum time to wait for input in milliseconds, or 0 for infinite.
292
func (agi *AGI) ReceiveText(timeout int32) (string, error) {
293
	resp, err := agi.execute("RECEIVE TEXT", timeout)
294
	if err != nil {
295
		return "", err
296
	}
297
	if resp.result == -1 {
298
		return "", errorNew("Failure, hangup or timeout.")
299
	}
300
	return resp.value, nil
301
}
302
303
// RecordFile Record to a file until a given dtmf digit in the sequence is received.
304
// The format will specify what kind of file will be recorded. The timeout is the
305
// maximum record time in milliseconds, or -1 for no timeout.
306
//	offset samples is optional, and, if provided, will seek to the offset without
307
// exceeding the end of the file.
308
//	beep causes Asterisk to play a beep to the channel that is about to be recorded.
309
//	silence is the number of seconds of silence allowed before the function returns
310
// despite the lack of dtmf digits or reaching timeout.
311
//	silence is the number of seconds of silence that are permitted before the
312
// recording is terminated, regardless of the escape_digits or timeout arguments
313
func (agi *AGI) RecordFile(file, format, escDigits string,
314
	timeout, offset int, beep bool, silence int) error {
315
316
	cmd := "RECORD FILE"
317
	cmd = fmt.Sprintf("%s %s %s %s", cmd, file, format, escDigits)
318
	if beep {
319
		cmd = fmt.Sprintf("%s BEEP", cmd)
320
	}
321
	if silence > 0 {
322
		cmd = fmt.Sprintf("%s s=%d", cmd, silence)
323
	}
324
	resp, err := agi.execute(cmd)
325
	if err != nil {
326
		return err
327
	}
328
	if resp.result <= 0 {
329
		return errorNew("Failed record file")
330
	}
331
	return nil
332
}
333
334
func (agi *AGI) say(cmd string, args ...interface{}) error {
335
	resp, err := agi.execute("SAY "+cmd, args...)
336
	if err != nil {
337
		return err
338
	}
339
	if resp.result < 0 {
340
		return errorNew("Failure")
341
	}
342
	return nil
343
}
344
345
// SayAlpha says a given character string, returning early if any of the given
346
// DTMF digits are received on the channel.
347
func (agi *AGI) SayAlpha(number, escDigits string) error {
348
	return agi.say("ALPHA", number, escDigits)
349
}
350
351
// SayDate say a given date, returning early if any of the given DTMF digits
352
// are received on the channel
353
func (agi *AGI) SayDate(date, escDigits string) error {
354
	return agi.say("DATE", date, escDigits)
355
}
356
357
// SayDatetime say a given time, returning early if any of the given DTMF
358
// digits are received on the channel
359
func (agi *AGI) SayDatetime(time, escDigits, format, timezone string) error {
360
	return agi.say("DATETIME", time, escDigits, format, timezone)
361
}
362
363
// SayDigits say a given digit string, returning early if any of the given
364
// DTMF digits are received on the channel
365
func (agi *AGI) SayDigits(number, escDigits string) error {
366
	return agi.say("DIGITS", number, escDigits)
367
}
368
369
// SayNumber say a given digit string, returning early if any of the given
370
// DTMF digits are received on the channel
371
func (agi *AGI) SayNumber(number, escDigits string) error {
372
	return agi.say("NUMBER", number, escDigits)
373
}
374
375
// SayPhonetic say a given character string with phonetics, returning early
376
// if any of the given DTMF digits are received on the channel
377
func (agi *AGI) SayPhonetic(str, escDigits string) error {
378
	return agi.say("PHONETIC", str, escDigits)
379
}
380
381
// SayTime say a given time, returning early if any of the given DTMF digits
382
// are received on the channel
383
func (agi *AGI) SayTime(time, escDigits string) error {
384
	return agi.say("TIME", time, escDigits)
385
}
386
387
// SendImage Sends the given image on a channel. Most channels do not support
388
// the transmission of images.
389
func (agi *AGI) SendImage(image string) error {
390
	resp, err := agi.execute("SEND IMAGE", image)
391
	if err != nil {
392
		return err
393
	}
394
	if resp.result < 0 {
395
		return errorNew("Failure or hangup.")
396
	}
397
	return nil
398
}
399
400
// SendText Sends the given text on a channel. Most channels do not support
401
// the transmission of text.
402
func (agi *AGI) SendText(text string) error {
403
	text = fmt.Sprintf("\"%s\"", text)
404
	resp, err := agi.execute("SEND TEXT", text)
405
	if err != nil {
406
		return err
407
	}
408
	if resp.result < 0 {
409
		return errorNew("Failure or hangup.")
410
	}
411
	return nil
412
}
413
414
// SetAutoHangup Cause the channel to automatically hangup at time seconds in the future.
415
// Setting to 0 will cause the autohangup feature to be disabled on this channel.
416
func (agi *AGI) SetAutoHangup(seconds int) error {
417
	resp, err := agi.execute("SET AUTOHANGUP", seconds)
418
	if err != nil {
419
		return err
420
	}
421
	if resp.result != 0 {
422
		return errorNew("Failure or hangup.")
423
	}
424
	return nil
425
}
426
427
// SetCallerid Changes the callerid of the current channel.
428
func (agi *AGI) SetCallerid(clid string) error {
429
	resp, err := agi.execute("SET CALLERID", clid)
430
	if err != nil {
431
		return err
432
	}
433
	if resp.result != 1 {
434
		return errorNew("Failure or hangup.")
435
	}
436
	return nil
437
}
438
439
// SetContext Sets the context for continuation upon exiting the application.
440
func (agi *AGI) SetContext(ctx string) error {
441
	resp, err := agi.execute("SET CONTEXT", ctx)
442
	if err != nil {
443
		return err
444
	}
445
	if resp.result != 0 {
446
		return errorNew("Failure or hangup.")
447
	}
448
	return nil
449
}
450
451
// SetExtension Changes the extension for continuation upon exiting the application.
452
func (agi *AGI) SetExtension(ext string) error {
453
	resp, err := agi.execute("SET EXTENSION", ext)
454
	if err != nil {
455
		return err
456
	}
457
	if resp.result != 0 {
458
		return errorNew("Failure or hangup.")
459
	}
460
	return nil
461
}
462
463
// SetMusic Enables/Disables the music on hold generator. If class is not specified,
464
// then the default music on hold class will be used.
465
//	Parameters: opt is "on" or "off", and music class as string
466
func (agi *AGI) SetMusic(opt string, class ...string) error {
467
	if opt != "on" && opt != "off" {
468
		return errorNew("Invalid opt: '" + opt + "'. Must be 'on' or 'off'.")
469
	}
470
471
	if class != nil {
472
		opt = fmt.Sprintf("%s %s", opt, class[0])
473
	}
474
475
	resp, err := agi.execute("SET MUSIC", opt)
476
	if err != nil {
477
		return err
478
	}
479
	if resp.result != 0 {
480
		return errorNew("Failure or hangup.")
481
	}
482
	return nil
483
}
484
485
// SetPriority Changes the priority for continuation upon exiting the application.
486
// The priority must be a valid priority or label.
487
func (agi *AGI) SetPriority(priority string) error {
488
	resp, err := agi.execute("SET PRIORITY", priority)
489
	if err != nil {
490
		return err
491
	}
492
	if resp.result != 0 {
493
		return errorNew("Failure or hangup.")
494
	}
495
	return nil
496
}
497
498
// SetVariable Sets a variable to the current channel.
499
func (agi *AGI) SetVariable(name, value string) error {
500
	value = fmt.Sprintf("\"%s\"", value)
501
	resp, err := agi.execute("SET VARIABLE", name, value)
502
	if err != nil {
503
		return err
504
	}
505
	if resp.result != 1 {
506
		return errorNew("Failure or hangup.")
507
	}
508
	return nil
509
}
510
511
// StreamFile Send the given file, allowing playback to be interrupted by the given
512
// digits, if any.
513
func (agi *AGI) StreamFile(file, escDigits string, offset int) (int, error) {
514
	resp, err := agi.execute("STREAM FILE", file, escDigits, offset)
515
	if err != nil {
516
		return -1, err
517
	}
518
	if resp.result == -1 {
519
		return -1, errorNew("Failure or hangup.")
520
	}
521
	return int(resp.result), nil
522
}
523
524
// TDDMode Enable/Disable TDD transmission/reception on a channel.
525
//	Modes: on, off, mate, tdd
526
func (agi *AGI) TDDMode(mode string) error {
527
	resp, err := agi.execute("TDD MODE", mode)
528
	if err != nil {
529
		return err
530
	}
531
	if resp.result != 1 {
532
		return errorNew("Failure or hangup.")
533
	}
534
	return nil
535
}
536
537
// Verbose Sends message to the console via verbose message system.
538
// level is the verbose level (1-4)
539
func (agi *AGI) Verbose(msg string, level ...int) error {
540
	var err error
541
	msg = fmt.Sprintf("\"%s\"", msg)
542
	if level == nil {
543
		_, err = agi.execute("VERBOSE", msg)
544
		return err
545
	}
546
547
	lvl := level[0]
548
	if lvl < 1 && lvl > 4 {
549
		lvl = 1
550
	}
551
	_, err = agi.execute("VERBOSE", msg, lvl)
552
	return err
553
}
554
555
// WaitForDigit Waits up to timeout *milliseconds* for channel to receive a DTMF digit.
556
// Use -1 for the timeout value if you desire the call to block indefinitely.
557
//	Return digit pressed as string or error
558
func (agi *AGI) WaitForDigit(timeout int) (string, error) {
559
	resp, err := agi.execute("WAIT FOR DIGIT", timeout)
560
	if err != nil {
561
		return "", err
562
	}
563
	if resp.result == -1 {
564
		return "", errorNew("Failed run command")
565
	}
566
	if resp.result == 0 {
567
		return "", nil
568
	}
569
	return string(resp.result), nil
570
}
571