redial-ruby-agi 2.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. data/ChangeLog +44 -0
  2. data/LICENSE +222 -0
  3. data/README.md +25 -0
  4. data/Rakefile +20 -0
  5. data/VERSION +1 -0
  6. data/examples/call_log.rb +18 -0
  7. data/lib/ruby-agi.rb +5 -0
  8. data/lib/ruby-agi/agi.rb +891 -0
  9. data/lib/ruby-agi/asterisk_variable.rb +242 -0
  10. data/lib/ruby-agi/command.rb +911 -0
  11. data/lib/ruby-agi/error.rb +48 -0
  12. data/lib/ruby-agi/return_status.rb +31 -0
  13. data/lib/ruby-agi/rs/answer.rb +68 -0
  14. data/lib/ruby-agi/rs/channel_status.rb +127 -0
  15. data/lib/ruby-agi/rs/exec.rb +69 -0
  16. data/lib/ruby-agi/rs/get_variable.rb +76 -0
  17. data/lib/ruby-agi/rs/hangup.rb +76 -0
  18. data/lib/ruby-agi/rs/noop.rb +68 -0
  19. data/lib/ruby-agi/rs/receive_char.rb +112 -0
  20. data/lib/ruby-agi/rs/receive_text.rb +73 -0
  21. data/lib/ruby-agi/rs/record_file.rb +184 -0
  22. data/lib/ruby-agi/rs/result.rb +47 -0
  23. data/lib/ruby-agi/rs/return_status.rb +98 -0
  24. data/lib/ruby-agi/rs/say_digits.rb +85 -0
  25. data/lib/ruby-agi/rs/say_number.rb +88 -0
  26. data/lib/ruby-agi/rs/say_phonetic.rb +88 -0
  27. data/lib/ruby-agi/rs/say_time.rb +90 -0
  28. data/lib/ruby-agi/rs/send_image.rb +83 -0
  29. data/lib/ruby-agi/rs/send_text.rb +83 -0
  30. data/lib/ruby-agi/rs/set_auto_hangup.rb +69 -0
  31. data/lib/ruby-agi/rs/set_caller_id.rb +72 -0
  32. data/lib/ruby-agi/rs/set_context.rb +71 -0
  33. data/lib/ruby-agi/rs/set_extension.rb +72 -0
  34. data/lib/ruby-agi/rs/set_music.rb +71 -0
  35. data/lib/ruby-agi/rs/set_priority.rb +69 -0
  36. data/lib/ruby-agi/rs/set_variable.rb +76 -0
  37. data/lib/ruby-agi/rs/stream_file.rb +138 -0
  38. data/lib/ruby-agi/rs/tdd_mode.rb +95 -0
  39. data/lib/ruby-agi/rs/verbose.rb +73 -0
  40. data/lib/ruby-agi/rs/wait_for_digit.rb +90 -0
  41. data/lib/ruby-agi/rs/wait_for_digits.rb +88 -0
  42. data/ruby-agi.gemspec +51 -0
  43. data/test/unit.rb +37 -0
  44. metadata +94 -0
@@ -0,0 +1,242 @@
1
+ #
2
+ # File: asterisk_variables.rb
3
+ #
4
+ # ruby-agi: Ruby Language API for Asterisk
5
+ #
6
+ # Copyright (C) <2005> Mohammad Khan <info@beeplove.com>
7
+ #
8
+ # This program is free software; you can redistribute it and/or modify
9
+ # it under the terms of the GNU General Public License as published by
10
+ # the Free Software Foundation; either version 2 of the License, or
11
+ # (at your option) any later version.
12
+ #
13
+ # This program is distributed in the hope that it will be useful,
14
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
15
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16
+ # GNU General Public License for more details.
17
+ #
18
+ # You should have received a copy of the GNU General Public License
19
+ # along with this program; if not, write to the Free Software
20
+ # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
21
+ #
22
+
23
+ # == Overview
24
+ #
25
+
26
+ # Followings are the list of AGI variables
27
+ # -- ruby-agi >> agi_request
28
+ # -- ruby-agi >> agi_channel
29
+ # -- ruby-agi >> agi_language
30
+ # -- ruby-agi >> agi_type
31
+ # -- ruby-agi >> agi_uniqueid
32
+ # -- ruby-agi >> agi_callerid
33
+ # -- ruby-agi >> agi_dnid
34
+ # -- ruby-agi >> agi_rdnis
35
+ # -- ruby-agi >> agi_context
36
+ # -- ruby-agi >> agi_extension
37
+ # -- ruby-agi >> agi_priority
38
+ # -- ruby-agi >> agi_enhanced
39
+ # -- ruby-agi >> agi_accountcode
40
+
41
+
42
+ require 'ruby-agi/agi.rb'
43
+ require 'ruby-agi/error.rb'
44
+
45
+ class AGI
46
+ end
47
+
48
+ class AsteriskVariable < AGI
49
+
50
+ def initialize
51
+ env
52
+ end
53
+
54
+ protected
55
+ def env
56
+ if @asterisk_variable.nil?
57
+ @asterisk_variable = Hash.new
58
+ semaphore do
59
+ $stdin.each_line do | line |
60
+ line.strip!
61
+ break if line.nil? or line.size.zero? or line == "\n"
62
+ key, value = line.split(':')
63
+ value.strip! if not value.nil?
64
+ if ((not key.nil?) and (not key.to_s.size.zero?))
65
+ @asterisk_variable[key.to_s] = value
66
+ end
67
+ end
68
+ end
69
+ end
70
+
71
+ return @asterisk_variable
72
+ end
73
+
74
+ protected
75
+ def read_env(name)
76
+ if debug?
77
+ semaphore do
78
+ $stderr.puts " -- ruby-agi-trunk << Read asterisk variable '#{name}'"
79
+ $stderr.puts " -- ruby-agi-trunk >> Asterisk variable #{name} = #{env[name].to_s}"
80
+ end
81
+ end
82
+
83
+ return env[name]
84
+ end
85
+
86
+ public
87
+ def request
88
+ return read_env('agi_request')
89
+ end
90
+
91
+ public
92
+ def channel
93
+ return read_env('agi_channel')
94
+ end
95
+
96
+ public
97
+ def language
98
+ return read_env('agi_language')
99
+ end
100
+
101
+ public
102
+ def type
103
+ return read_env('agi_type')
104
+ end
105
+
106
+ public
107
+ def uniqueid
108
+ return read_env('agi_uniqueid')
109
+ end
110
+
111
+ public
112
+ def callerid
113
+ if @callerid.nil?
114
+ init_caller_variable
115
+ end
116
+
117
+ return @callerid
118
+ end
119
+
120
+ public
121
+ def dnid
122
+ return read_env('agi_dnid')
123
+ end
124
+
125
+ public
126
+ def rdnid
127
+ return read_env('agi_rdnid')
128
+ end
129
+
130
+ public
131
+ def context
132
+ return read_env('agi_context')
133
+ end
134
+
135
+ public
136
+ def extension
137
+ return read_env('agi_extension')
138
+ end
139
+
140
+ public
141
+ def priority
142
+ return read_env('agi_priority')
143
+ end
144
+
145
+ public
146
+ def enhanced
147
+ return read_env('agi_enhanced')
148
+ end
149
+
150
+ public
151
+ def accountcode
152
+ return read_env('agi_accountcode')
153
+ end
154
+
155
+ ####################################################################
156
+ #### additional methods generated from existing basic methods ####
157
+ ####################################################################
158
+
159
+ public
160
+ def calleridname
161
+ if @calleridname.nil?
162
+ init_caller_variable
163
+ end
164
+
165
+ return @calleridname
166
+ end
167
+
168
+ public
169
+ def calleridnumber
170
+ if @calleridnumber.nil?
171
+ init_caller_variable
172
+ end
173
+
174
+ return @calleridnumber
175
+ end
176
+
177
+ # following methods are not confirm that they are AGI variables
178
+ public
179
+ def callingpres
180
+ return read_env('agi_callingpres')
181
+ end
182
+
183
+ public
184
+ def callingani2
185
+ return read_env('agi_callingani2')
186
+ end
187
+
188
+ public
189
+ def callington
190
+ return read_env('agi_callington')
191
+ end
192
+
193
+ public
194
+ def callingtns
195
+ return read_env('agi_callingtns')
196
+ end
197
+
198
+ # asterisk-1.0.9 : agi_callerid (default value is 'unknown')
199
+ # asterisk-1.0.10 : agi_callerid (default value is 'unknown')
200
+ # asterisk-1.0.10 : agi_callerid, agi_calleridname (default value for both is 'unknown')
201
+ protected
202
+ def init_caller_variable
203
+ @callerid = read_env('agi_callerid').to_s.strip
204
+ if @callerid == "unknown"
205
+ @callerid = ""
206
+ @calleridname = ""
207
+ @calleridnumber = ""
208
+ elsif Regexp.new(/^\d+\d$/).match(@callerid)
209
+ @calleridname = read_env('agi_calleridname')
210
+ if @calleridname.nil?
211
+ @calleridname = "UNKNOWN"
212
+ end
213
+ @calleridnumber = @callerid
214
+ else
215
+ @calleridname = read_env('agi_calleridname')
216
+ if @calleridname.nil? # for asterisk that don't have agi variable 'agi_calleridname'
217
+ name = Regexp.new(/\".+\"/).match(@callerid)
218
+ number = Regexp.new(/\<\d+\>/).match(@callerid)
219
+
220
+ if name.nil?
221
+ @calleridname = ""
222
+ else
223
+ name = name.to_s
224
+ name.gsub!(/\"/, '')
225
+ @calleridname = name.to_s.strip
226
+ end
227
+
228
+ if number.nil?
229
+ @calleridnumber = ""
230
+ else
231
+ number = number.to_s
232
+ number.sub!(/\</, '')
233
+ number.sub!(/\>/, '')
234
+ @calleridnumber = number.to_s.strip
235
+ end
236
+ else # for asterisk that have agi variable 'agi_calleridname'
237
+ @calleridnumber = @callerid
238
+ @callerid = "\"#{@calleridname}\" <#{@calleridnumber}>"
239
+ end
240
+ end
241
+ end
242
+ end
@@ -0,0 +1,911 @@
1
+ #
2
+ # File: command.rb
3
+ #
4
+ # ruby-agi: Ruby Language API for Asterisk
5
+ #
6
+ # Copyright (C) <2005> Mohammad Khan <info@beeplove.com>
7
+ #
8
+ # This program is free software; you can redistribute it and/or modify
9
+ # it under the terms of the GNU General Public License as published by
10
+ # the Free Software Foundation; either version 2 of the License, or
11
+ # (at your option) any later version.
12
+ #
13
+ # This program is distributed in the hope that it will be useful,
14
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
15
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16
+ # GNU General Public License for more details.
17
+ #
18
+ # You should have received a copy of the GNU General Public License
19
+ # along with this program; if not, write to the Free Software
20
+ # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
21
+ #
22
+
23
+ # this would be the most used class
24
+
25
+ # most common arguements
26
+ # timeout: nil, zero, neative =forever,
27
+ # escape_digit, by default #, if nil passed as argument there will not have any escape
28
+ # escape_digits, by default #, if nil passed as argument there will not have any escape
29
+ # channel, current channel otherwise mentioned
30
+ # max_digit, maximum number of digits would be accepted from user, always 255 otherwise mentioned
31
+ # filename, name of file to be streamed, recoreds ..
32
+ # beep, always yes, otherwise passed false or nil as argument
33
+ # format, always gsm otherwise mentioned
34
+ # secs, all methods need time as an argument are passed as seconds
35
+
36
+ require 'sync'
37
+
38
+ require 'ruby-agi/agi.rb'
39
+ require 'ruby-agi/error.rb'
40
+ require 'ruby-agi/return_status.rb'
41
+
42
+ class AGI
43
+ end
44
+
45
+ class ReturnStatus
46
+ end
47
+
48
+ class Answer < ReturnStatus
49
+ end
50
+
51
+
52
+ class Command < AGI
53
+ def initialize
54
+ end
55
+
56
+ #
57
+ # Answers channel if not already in answer state.
58
+ #
59
+ # <b>Parameters</b>
60
+ # - none
61
+ #
62
+ # <b>Returns</b>
63
+ # - ReturnStatus object
64
+ #
65
+ # failure: 200 result=-1
66
+ # success: 200 result=0
67
+ # Command Reference: ANSWER
68
+ #
69
+ public
70
+ def answer
71
+ cmd = "ANSWER"
72
+ rs = exec_command(cmd, Answer)
73
+ raise CommandError, rs.to_s if rs.command_error?
74
+ return rs
75
+ end
76
+
77
+ #
78
+ # Cause the channel to automatically hangup at <time> seconds in the future.
79
+ # Of course it can be hungup before then as well.
80
+ # Setting to 0 will cause the autohangup feature to be disabled on this channel.
81
+ #
82
+ # <b>Parameters</b>
83
+ # - time : number of seconds, after this time period channel will be automatically hung up
84
+ #
85
+ # <b>Returns</b>
86
+ # - ReturnStatus object
87
+ #
88
+ # 200 result=0
89
+ # Command Reference: SET AUTOHANGUP <time>
90
+ #
91
+ public
92
+ def set_auto_hangup(secs=0)
93
+ cmd = "SET AUTOHANGUP #{secs.to_s}"
94
+ rs = exec_command(cmd, SetAutoHangup)
95
+ raise CommandError, rs.to_s if rs.command_error?
96
+ return rs
97
+ end
98
+
99
+ #
100
+ # Returns the status of the specified channel.
101
+ # If no channel name is given the returns the status of the current channel.
102
+ #
103
+ # <b>Parameters</b>
104
+ # - channel : name of the channel.
105
+ #
106
+ # <b>Returns</b>
107
+ # - ReturnStatus object
108
+ #
109
+ # failure: 200 result=-1
110
+ # success: 200 result=<status>
111
+ # <status> values:
112
+ # 0 Channel is down and available
113
+ # 1 Channel is down, but reserved
114
+ # 2 Channel is off hook
115
+ # 3 Digits (or equivalent) have been dialed
116
+ # 4 Line is ringing
117
+ # 5 Remote end is ringing
118
+ # 6 Line is up
119
+ # 7 Line is busy
120
+ # Command Reference: CHANNEL STATUS [<channelname>]
121
+ #
122
+ public
123
+ def channel_status(channel=nil)
124
+ if channel.nil?
125
+ channel = ""
126
+ end
127
+
128
+ cmd = "CHANNEL STATUS #{channel.to_s}"
129
+ rs = exec_command(cmd, ChannelStatus)
130
+ raise CommandError, rs.to_s if rs.command_error?
131
+ return rs
132
+ end
133
+
134
+ #
135
+ # Executes <application> with given <options>.
136
+ # Applications are the functions you use to create a dial plan in extensions.conf.
137
+ #
138
+ # <b>Parameters</b>
139
+ # - application : Asterisk command of the application
140
+ # - options : options to be passed with the asterisk application
141
+ #
142
+ # <b>Returns</b>
143
+ # - ReturnStatus object
144
+ #
145
+ # failure: 200 result=-2
146
+ # success: 200 result=<ret>
147
+ # Command Reference: EXEC <application> <options>
148
+ #
149
+ public
150
+ def exec(application, options=nil)
151
+ if options.nil?
152
+ options = ""
153
+ end
154
+ cmd = "EXEC #{application.to_s} #{options.to_s}"
155
+ rs = exec_command(cmd, Exec)
156
+ raise CommandError, rs.to_s if rs.command_error?
157
+ return rs
158
+ end
159
+
160
+
161
+ #
162
+ # method to get digit(s)
163
+ # pressing '#' will always terminate the input process
164
+ #
165
+ # <b>Parameters</b>
166
+ # - filename : audio to be played before get as input
167
+ # - timeout : maximum allowed time in second(s) to receive each digit
168
+ # wait for ever if timeout is nil or negative or zero
169
+ # - max_digit: maximum number of digits to get as input
170
+ # wait for unlimited number of digits if max_digit is nil or negative or zero
171
+ # <b>Returns</b>
172
+ # - ReturnStatus object
173
+ #
174
+ # failure: 200 result=-1
175
+ # timeout: 200 result=<digits> (timeout)
176
+ # success: 200 result=<digits>
177
+ # <digits> is the digits pressed.
178
+ # Command Reference: GET DATA <file to be streamed> [timeout] [max digits]
179
+ #
180
+ public
181
+ def wait_for_digits(filename, timeout=nil, max_digit=nil)
182
+
183
+ timeout = sanitize_timeout(timeout)
184
+ max_digit = sanitize_max_digit(max_digit)
185
+
186
+ cmd = "GET DATA #{filename.to_s} #{timeout.to_s} #{max_digit.to_s}"
187
+
188
+ rs = exec_command(cmd, WaitForDigits)
189
+ raise CommandError, rs.to_s if rs.command_error?
190
+ return rs
191
+ end
192
+
193
+ #
194
+ # method to read a variable
195
+ #
196
+ # <b>Parameters</b>
197
+ # - name : name of the variable to read
198
+ #
199
+ # <b>Returns</b>
200
+ # - ReturnStatus object
201
+ #
202
+ # Does not work with global variables. Does not work with some variables that are generated by modules.
203
+ # failure or not set: 200 result=0
204
+ # success: 200 result=1 <value>
205
+ # Command Reference: GET VARIABLE <variablename>
206
+ #
207
+ public
208
+ def get_variable(name)
209
+ cmd = "GET VARIABLE #{name.to_s}"
210
+ rs = exec_command(cmd, GetVariable)
211
+ raise CommandError, rs.to_s if rs.command_error?
212
+ return rs
213
+ end
214
+
215
+ #
216
+ # method to hang up the specified channel.
217
+ # If no channel name is given, hangs up the current channel.
218
+ #
219
+ # <b>Parameters</b>
220
+ # - channel : name of the channel to hang up
221
+ # <b>Returns</b>
222
+ # - ReturnStatus object
223
+ #
224
+ # failure: 200 result=-1
225
+ # success: 200 result=1
226
+ # Command Reference: HANGUP [<channelname>]
227
+ #
228
+ public
229
+ def hangup(channel=nil)
230
+ if channel.nil?
231
+ channel = ""
232
+ end
233
+
234
+ cmd = "HANGUP #{channel.to_s}"
235
+ rs = exec_command(cmd, Hangup)
236
+ raise CommandError, rs.to_s if rs.command_error?
237
+ return rs
238
+ end
239
+
240
+ #
241
+ # method that Does nothing !!
242
+ #
243
+ # <b>Parameters</b>
244
+ # - msg : message to pass this method
245
+ #
246
+ # <b>Returns</b>
247
+ # - ReturnStatus object
248
+ # success: 200 result=0
249
+ # Command Reference: Usage: NOOP
250
+ #
251
+ public
252
+ def noop(msg)
253
+ cmd = "NOOP #{msg.to_s}"
254
+ rs = exec_command(cmd, Noop)
255
+ raise CommandError, rs.to_s if rs.command_error?
256
+ return rs
257
+ end
258
+
259
+ #
260
+ # Receives a character of text on a channel, and discards any further characters after the first one waiting.
261
+ # Most channels do not support the reception of text. See Asterisk Text for details.
262
+ #
263
+ # <b>Parameters</b>
264
+ # - timeout : maximum time to wait for input in seconds
265
+ # negative or zero is not acceptable
266
+ #
267
+ # <b>Returns</b>
268
+ # - ReturnStatus object
269
+ #
270
+ # failure or hangup: 200 result=-1 (hangup)
271
+ # timeout: 200 result=<char> (timeout)
272
+ # success: 200 result=<char>
273
+ # <char> is the character received, or 0 if the channel does not support text reception.
274
+ # Command Reference: RECEIVE CHAR <timeout>
275
+ #
276
+ public
277
+ def receive_char(timeout)
278
+ timeout = sanitize_timeout(timeout)
279
+ raise(ArgumentError, "timeout need to be positive") if (timeout < 1)
280
+
281
+ cmd = "RECEIVE CHAR #{timeout.to_s}"
282
+ rs = exec_command(cmd, ReceiveChar)
283
+ raise CommandError, rs.to_s if rs.command_error?
284
+ return rs
285
+ end
286
+
287
+ #
288
+ # Receives a string text on a channel.
289
+ # Most channels do not support the reception of text.
290
+ #
291
+ # <b>Parameters</b>
292
+ # - timeout : time to wait for input in seconds
293
+ # negative or zero is not acceptable
294
+ #
295
+ # <b>Returns</b>
296
+ # - ReturnStatus object
297
+ #
298
+ # failure, hangup, or timeout: 200 result=-1
299
+ # success: 200 result=<text>
300
+ # <text> is the text received on the channel.
301
+ # Command Reference: RECEIVE TEXT <timeout>
302
+ #
303
+ public
304
+ def receive_text(timeout)
305
+ timeout = sanitize_timeout(timeout)
306
+ raise(ArgumentError, "timeout need to be positive") if (timeout < 1)
307
+
308
+ cmd = "RECEIVE TEXT #{timeout.to_s}"
309
+ rs = exec_command(cmd, ReceiveText)
310
+ raise CommandError, rs.to_s if rs.command_error?
311
+ return rs
312
+ end
313
+
314
+ #
315
+ # Record to a file until <escape digits> are received as dtmf.
316
+ #
317
+ # <b>Parameters</b>
318
+ # - filename : location of the file where the audio file will be saved
319
+ # - format : specify what kind of file will be recorded.
320
+ # - escape_digits : digit(s) to be pressed to complete recording
321
+ # - timeout : maximum record time in seconds
322
+ # nil, negative or 0 for no timeout.
323
+ # - offset : [offset samples] is optional,
324
+ # and if provided will seek to the offset without exceeding the end of the file.
325
+ # - silence : number of seconds of silence allowed before the function returns
326
+ # despite the lack of dtmf digits or reaching timeout.
327
+ # Silence value must be preceeded by "s=" and is optional.
328
+ #
329
+ # <b>Returns</b>
330
+ # - ReturnStatus object
331
+ #
332
+ # failure to write: 200 result=-1 (writefile)
333
+ # failure on waitfor: 200 result=-1 (waitfor) endpos=<offset>
334
+ # hangup: 200 result=0 (hangup) endpos=<offset>
335
+ # interrrupted: 200 result=<digit> (dtmf) endpos=<offset>
336
+ # timeout: 200 result=0 (timeout) endpos=<offset>
337
+ # random error: 200 result=<error> (randomerror) endpos=<offset>
338
+ # <offset> is the end offset in the file being recorded.
339
+ # <digit> is the ascii code for the digit pressed.
340
+ # <error> ?????
341
+ #
342
+ # Command Reference: RECORD FILE <filename> <format> <escape digits> <timeout> [offset samples] [BEEP] [s=<silence>]
343
+ #
344
+ public
345
+ def record_file(filename, format='gsm', escape_digits='#', timeout=nil, beep=true)
346
+
347
+ format = sanitize_file_format(format)
348
+ escape_digits = sanitize_escape_digits(escape_digits)
349
+ timeout = sanitize_timeout(timeout)
350
+
351
+
352
+ if ((escape_digits == "X") and (timeout == -1))
353
+ raise(ArgumentError, "need at least one valid escape digit or timeout need te positive")
354
+ end
355
+
356
+ cmd = "RECORD FILE #{filename} #{format} #{escape_digits} #{timeout}"
357
+ cmd = "#{cmd} beep" if beep == true
358
+ rs = exec_command(cmd, RecordFile)
359
+ raise CommandError, rs.to_s if rs.command_error?
360
+ return rs
361
+ end
362
+
363
+
364
+ #
365
+ # Say a given digit string, returning early if any of the given DTMF digits are received on the channel.
366
+ #
367
+ # <b>Parameters</b>
368
+ # - number : number to announce
369
+ # - escape_digit : if digit pressed during playback, will return from announce
370
+ #
371
+ # <b>Returns</b>
372
+ # - ReturnStatus (object)
373
+ #
374
+ # failure: 200 result=-1
375
+ # success: 200 result=0
376
+ # digit pressed: 200 result=<digit>
377
+ # <digit> is the ascii code for the digit pressed.
378
+ #
379
+ # Command Reference: SAY DIGITS <number> <escape digits>
380
+ #
381
+ public
382
+ def say_digits(digit_string, escape_digits=nil)
383
+ escape_digits = sanitize_escape_digits(escape_digits)
384
+
385
+ cmd = "SAY DIGITS #{digit_string.to_s} #{escape_digits}"
386
+ rs = exec_command(cmd, SayDigits)
387
+ raise CommandError, rs.to_s if rs.command_error?
388
+ return rs
389
+ end
390
+
391
+ #
392
+ # Say a given number, returning early if any of the given DTMF digits are received on the channel.
393
+ #
394
+ # <b>Parameters</b>
395
+ # - number : number to announce
396
+ # - escape_digit : if pressed, return from program
397
+ #
398
+ # <b>Returns</b>
399
+ # - ReturnStatus object
400
+ #
401
+ # failure: 200 result=-1
402
+ # success: 200 result=0
403
+ # digit pressed: 200 result=<digit>
404
+ #<digit> is the ascii code for the digit pressed.
405
+ #
406
+ # Command Reference: SAY NUMBER <number> <escape digits>
407
+ #
408
+ public
409
+ def say_number(number, escape_digits=nil)
410
+ escape_digits = sanitize_escape_digits(escape_digits)
411
+ cmd = "SAY NUMBER #{number.to_s} #{escape_digits.to_s}"
412
+ rs = exec_command(cmd, SayNumber)
413
+ raise CommandError, rs.to_s if rs.command_error?
414
+ return rs
415
+ end
416
+
417
+ #
418
+ # Say a given character string with phonetics, returning early if any of the given DTMF digits are received on the channel.
419
+ #
420
+ # <b>Parameters</b>
421
+ # - string : character string to announce
422
+ # - escape_digit : digit to be pressed to escape from program
423
+ #
424
+ # <b>Returns</b>
425
+ # - ReturnStatus (object)
426
+ #
427
+ # failure: 200 result=-1
428
+ # success: 200 result=0
429
+ # digit pressed: 200 result=<digit>
430
+ # <digit> is the ascii code for the digit pressed.
431
+ #
432
+ # Command Reference: SAY PHONETIC <string> <escape digits>
433
+ #
434
+ public
435
+ def say_phonetic(string, escape_digits=nil)
436
+ escape_digits = sanitize_escape_digits(escape_digits)
437
+ cmd = "SAY PHONETIC #{string.to_s} #{escape_digits}"
438
+ rs = exec_command(cmd, SayPhonetic)
439
+ raise CommandError, rs.to_s if rs.command_error?
440
+ return rs
441
+ end
442
+
443
+ #
444
+ # Say a given time, returning early if any of the given DTMF digits are received on the channel.
445
+ #
446
+ # <b>Parameters</b>
447
+ # - time : number of seconds elapsed since 00:00:00 on January 1, 1970, Coordinated Universal Time (UTC).
448
+ # - escape_digits : digit to be pressed to escape from the program
449
+ #
450
+ # <b>Returns</b>
451
+ # - ReturnStatus (object)
452
+ #
453
+ # failure: 200 result=-1
454
+ # success: 200 result=0
455
+ # digit pressed: 200 result=<digit>
456
+ # <digit> is the ascii code for the digit pressed.
457
+ #
458
+ # Command Reference: SAY TIME <time> <escape digits>
459
+ #
460
+ public
461
+ def say_time(time=Time.now.to_i, escape_digits='#')
462
+ escape_digits = sanitize_escape_digits(escape_digits)
463
+ cmd = "SAY TIME #{time.to_s} #{escape_digits.to_s}"
464
+ rs = exec_command(cmd, SayTime)
465
+ raise CommandError, rs.to_s if rs.command_error?
466
+ return rs
467
+ end
468
+
469
+ #
470
+ # Sends the given image on a channel.
471
+ # Most channels do not support the transmission of images.
472
+ # Image names should not include extensions.
473
+ #
474
+ # <b>Parameters</b>
475
+ # - image : location of image
476
+ #
477
+ # <b>Returns</b>
478
+ # - ReturnStatus (object)
479
+ #
480
+ # failure: 200 result=-1
481
+ # success: 200 result=0
482
+ #
483
+ # Command Reference: SEND IMAGE <image>
484
+ #
485
+ public
486
+ def send_image(image)
487
+ cmd = "SEND IMAGE #{image.to_s}"
488
+ rs = exec_command(cmd, SendImage)
489
+ raise CommandError, rs.to_s if rs.command_error?
490
+ return rs
491
+ end
492
+
493
+ #
494
+ # Sends the given text on a channel.
495
+ # Most channels do not support the transmission of text.
496
+ # Text consisting of greater than one word should be placed in quotes since the command only accepts a single argument.
497
+ #
498
+ # <b>Parameters</b>
499
+ # - text : text to be send
500
+ #
501
+ # <b>Returns</b>
502
+ # - ReturnStatus (object)
503
+ #
504
+ # failure: 200 result=-1
505
+ # success: 200 result=0
506
+ #
507
+ # Command Reference: SEND TEXT "<text to send>"
508
+ #
509
+ public
510
+ def send_text(text)
511
+ cmd = "SEND TEXT #{text.to_s}"
512
+ rs = exec_command(cmd, SendText)
513
+ raise CommandError, rs.to_s if rs.command_error?
514
+ return rs
515
+ end
516
+
517
+ #
518
+ # Changes the callerid of the current channel.
519
+ #
520
+ # <b>Parameters</b>
521
+ # - number : number to be set a callerid
522
+ #
523
+ # <b>Returns</b>
524
+ # - ReturnStatus (object)
525
+ #
526
+ # 200 result=1
527
+ #
528
+ # Command Reference: SET CALLERID <number>
529
+ #
530
+ public
531
+ def set_caller_id(number)
532
+ cmd = "SET CALLERID #{number.to_s}"
533
+ rs = exec_command(cmd, SetCallerID)
534
+ raise CommandError, rs.to_s if rs.command_error?
535
+ return rs
536
+ end
537
+
538
+ #
539
+ # Sets the context for continuation upon exiting the application.
540
+ #
541
+ # <b>Parameters</b>
542
+ # - context : name of the context
543
+ #
544
+ # <b>Returns</b>
545
+ # - ReturnStatus object
546
+ #
547
+ # 200 result=0
548
+ # Note: no checking is done to verify that the context is valid.
549
+ # Specifying an invalid context will cause the call to drop
550
+ #
551
+ # Command Reference: SET CONTEXT <desired context>
552
+ #
553
+ public
554
+ def set_context(context)
555
+ cmd = "SET CONTEXT #{context.to_s}"
556
+ rs = exec_command(cmd, SetContext)
557
+ raise CommandError, rs.to_s if rs.command_error?
558
+ return rs
559
+ end
560
+
561
+ #
562
+ # Changes the extension for continuation upon exiting the application.
563
+ #
564
+ # <b>Parameters</b>
565
+ # - extension: name or number of extension to be set
566
+ #
567
+ # <b>Returns</b>
568
+ # - ReturnStatus object
569
+ #
570
+ # 200 result=0
571
+ # Note: no checking is done to verify that the extension extists.
572
+ # If the extension does not exist, the PBX will attempt to move to the "i" (invalid) extension.
573
+ # If the invalid "i" extension does not exist, the call will drop
574
+ #
575
+ # Command Reference: SET EXTENSION <new extension>
576
+ #
577
+ public
578
+ def set_extension(extension)
579
+ cmd = "SET EXTENSION #{extension.to_s}"
580
+ rs = exec_command(cmd, SetExtension)
581
+ raise CommandError, rs.to_s if rs.command_error?
582
+ return rs
583
+ end
584
+
585
+ #
586
+ # Enables/Disables the music on hold generator.
587
+ #
588
+ # <b>Parameters</b>
589
+ # - mode : on or off
590
+ # - moh_class : name of the music on hold class
591
+ # 'default' for not provided or nil
592
+ #
593
+ # <b>Returns</b>
594
+ # - ReturnStatus object
595
+ #
596
+ # 200 result=0
597
+ # Command Reference: SET MUSIC <on|off> <class>
598
+ #
599
+ public
600
+ def set_music(mode=true, moh_class='default')
601
+ if ((mode == true) or (not (mode == 0)))
602
+ mode = 'ON'
603
+ else
604
+ mode = 'OFF'
605
+ end
606
+ cmd = "SET MUSIC #{mode.to_s} #{moh_class.to_s}"
607
+ rs = exec_command(cmd, SetMusic)
608
+ raise CommandError, rs.to_s if rs.command_error?
609
+ return rs
610
+ end
611
+
612
+ #
613
+ # Changes the priority for continuation upon exiting the application.
614
+ #
615
+ # <b>Parameters</b>
616
+ # - priority : number of priority
617
+ #
618
+ # <b>Returns</b>
619
+ # - ReturnStatus object
620
+ #
621
+ # 200 result=0
622
+ # Command Reference: SET PRIORITY <num>
623
+ #
624
+ public
625
+ def set_priority(priority)
626
+ cmd = "SET PRIORITY #{priority.to_s}"
627
+ rs = exec_command(cmd, SetPriority)
628
+ raise CommandError, rs.to_s if rs.command_error?
629
+ return rs
630
+ end
631
+
632
+ #
633
+ # set variable: Sets a channel variable
634
+ # These variables live in the channel Asterisk creates
635
+ # when you pickup a phone and as such they are both local
636
+ # and temporary. Variables created in one channel can not
637
+ # be accessed by another channel. When you hang up the phone,
638
+ # the channel is deleted and any variables in that channel are deleted as well.
639
+ #
640
+ # <b>Parameters</b>
641
+ # - variablename : name of the variable
642
+ # - value : value to be set for the variable
643
+ #
644
+ # <b>Returns</b>
645
+ # - ReturnStatus object
646
+ #
647
+ # 200 result=1
648
+ # Command Reference: SET VARIABLE <variablename> <value>
649
+ #
650
+ public
651
+ def set_variable(name, value)
652
+ cmd = "SET VARIABLE #{name.to_s} \"#{value.to_s}\""
653
+ rs = exec_command(cmd, SetVariable)
654
+ raise CommandError, rs.to_s if rs.command_error?
655
+ return rs
656
+ end
657
+
658
+
659
+ #
660
+ # stream file: Sends audio file on channel
661
+ # Send the given file, allowing playback to be interrupted by the given digits, if any.
662
+ # Use double quotes for the digits if you wish none to be permitted.
663
+ # If sample offset is provided then the audio will seek to sample offset before play starts.
664
+ # Remember, the file extension must not be included in the filename.
665
+ #
666
+ # <b>Parameters</b>
667
+ # - filename : location of the file to be played
668
+ # - escape_digit: digit to be pressed to escape from playback
669
+ #
670
+ # <b>Returns</b>
671
+ # - ReturnStatus object
672
+ #
673
+ # failure: 200 result=-1 endpos=<sample offset>
674
+ # failure on open: 200 result=0 endpos=0
675
+ # success: 200 result=0 endpos=<offset>
676
+ # digit pressed: 200 result=<digit> endpos=<offset>
677
+ # <offset> is the stream position streaming stopped. If it equals <sample offset> there was probably an error.
678
+ # <digit> is the ascii code for the digit pressed.
679
+ # Command Reference: STREAM FILE <filename> <escape digits> [sample offset]
680
+ #
681
+ public
682
+ def stream_file(filename, escape_digits='#')
683
+
684
+ escape_digits = sanitize_escape_digits(escape_digits)
685
+
686
+ cmd = "STREAM FILE #{filename.to_s} #{escape_digits}"
687
+ rs = exec_command(cmd, StreamFile)
688
+ raise CommandError, rs.to_s if rs.command_error?
689
+
690
+ return rs
691
+ end
692
+
693
+ #
694
+ # tdd mode: Activates TDD mode on channels supporting it, to enable communication with TDDs.
695
+ # Enable/Disable TDD transmission/reception on a channel.
696
+ # This function is currently (01July2005) only supported on Zap channels.
697
+ # As of 02July2005, this function never returns 0 (Not Capable).
698
+ # If it fails for any reason, -1 (Failure) will be returned, otherwise 1 (Success) will be returned.
699
+ # The capability for returning 0 if the channel is not capable of TDD MODE is a future plan.
700
+ #
701
+ # <b>Parameters</b>
702
+ # - mode : mode of the tdd to be set
703
+ # set tdd on if non-zero or 'true' specified
704
+ #
705
+ # <b>Returns</b>
706
+ # - ReturnStatus object
707
+ #
708
+ # failure: 200 result=-1
709
+ # not capable: 200 result=0
710
+ # success: 200 result=1
711
+ # Command Reference: TDD MODE <on|off|mate>
712
+ #
713
+ public
714
+ def tdd_mode(mode=true)
715
+ if ((mode == true) or ( not (mode == 1)))
716
+ mode = 'ON'
717
+ else
718
+ mode = 'OFF'
719
+ end
720
+ cmd = "TDD MODE #{mode.to_s}"
721
+ rs = exec_command(cmd, TDDMode)
722
+ raise CommandError, rs.to_s if rs.command_error?
723
+ return rs
724
+ end
725
+
726
+ #
727
+ # Sends <message> to the console via verbose message system.
728
+ # The Asterisk verbosity system works as follows.
729
+ # The Asterisk user gets to set the desired verbosity at startup time
730
+ # or later using the console 'set verbose' command.
731
+ # Messages are displayed on the console if their verbose level
732
+ # is less than or equal to desired verbosity set by the user.
733
+ # More important messages should have a low verbose level;
734
+ # less important messages should have a high verbose level.
735
+ #
736
+ # <b>Parameters</b>
737
+ # - message : message to be send as log
738
+ # - level : verbose level to be set
739
+ # [level] is the the verbose level (1-4)
740
+ # If you specify a verbose level less than 1 or greater than 4, the verbosity is 1.
741
+ # The default verbosity seems to be 0 (in 1.0.8),
742
+ # and supplying a 0 (zero) verbosity does work:
743
+ # the message will be displayed regardless of the console verbosity setting.
744
+ #
745
+ # <b>Returns</b>
746
+ # - ReturnStatus object
747
+ #
748
+ # 200 result=1
749
+ # Command Reference: VERBOSE <message> [level]
750
+ #
751
+ public
752
+ def verbose(message, level=3)
753
+ cmd = "VERBOSE \"#{message.to_s}\" #{level.to_s}"
754
+ rs = exec_command(cmd, Verbose)
755
+ raise CommandError, rs.to_s if rs.command_error?
756
+ return rs
757
+ end
758
+
759
+ #
760
+ # wait for digit: Waits for a digit to be pressed
761
+ # Waits up to <timeout> milliseconds for channel to receive a DTMF digit.
762
+ #
763
+ # <b>Parameters</b>
764
+ # - timeout : maximum allow waiting time in seconds to get input
765
+ # nil, zero or negative would be considered as infinite wait time
766
+ #
767
+ # <b>Returns</b>
768
+ # - ReturnStatus object
769
+ #
770
+ # failure: 200 result=-1
771
+ # timeout: 200 result=0
772
+ # success: 200 result=<digit>
773
+ # <digit> is the ascii code for the digit received.
774
+ # Command Reference: WAIT FOR DIGIT <timeout>
775
+ #
776
+ public
777
+ def wait_for_digit(timeout=nil)
778
+ timeout = sanitize_timeout(timeout)
779
+
780
+ cmd = "WAIT FOR DIGIT #{timeout.to_s}"
781
+ rs = exec_command(cmd, WaitForDigit)
782
+ raise CommandError, rs.to_s if rs.command_error?
783
+ return rs
784
+ end
785
+
786
+ #
787
+ # method to dial out
788
+ #
789
+ # <b>Parameters</b>
790
+ # - telephone_number : telephone_number or extension to dial
791
+ # - protocol : protocol to be used to make this call
792
+ # - username : username to be used to make this call using the specified protocol
793
+ # - context : name of the context to be used for authentication
794
+ # - timeout : maximum allowed time in seconds to make this call
795
+ # - options : options to be passed in 'Dial' command
796
+ #
797
+ # <b>Returns</b>
798
+ # - ReturnStatus object
799
+ #
800
+ # Command Reference: Dial(type/identifier,timeout,options,URL)
801
+ #
802
+ def dial(telephone_number=nil, protocol=nil, username=nil, context='default', timeout=nil, options=nil)
803
+ dial_string = nil
804
+
805
+ if protocol == 'LOCAL'
806
+ return nil if (telephone_number.nil? or context.nil?)
807
+ dial_string = "LOCAL/#{telephone_number}@#{context}"
808
+ elsif protocol == 'IAX2'
809
+ return nil if (telephone_number.nil? or username.nil? or context.nil?)
810
+ telephone_number.strip!
811
+ dial_string = "IAX2/#{username}@#{context}/#{telephone_number}"
812
+ elsif protocol == 'SIP'
813
+ else
814
+ return nil
815
+ end
816
+
817
+ timeout.nil? ? dial_string += "|" : dial_string += "|#{timeout}"
818
+ options.nil? ? dial_string += "|" : dial_string += "|#{options}"
819
+ rs = exec('DIAL', dial_string)
820
+ return rs
821
+ end
822
+
823
+ ###### Process methods #######
824
+
825
+ public
826
+ def raw_command(cmd)
827
+ rs = exec_command(cmd)
828
+ raise CommandError, rs.to_s if rs.command_error?
829
+ return rs
830
+ end
831
+
832
+ protected
833
+ def exec_command(cmd, return_status=nil)
834
+ rs = nil
835
+ begin
836
+ semaphore do
837
+ $stderr.puts " -- ruby-agi << #{cmd}" if debug?
838
+ $stdout.puts cmd
839
+ responses = $stdin.gets
840
+ $stderr.puts " -- ruby-agi >> #{responses}" if debug?
841
+ if return_status.nil?
842
+ rs = ReturnStatus.new(cmd, responses)
843
+ else
844
+ rs = return_status.new(cmd, responses)
845
+ end
846
+ end
847
+
848
+ rescue Errno::EPIPE
849
+ raise HangupError, "Line hung up during command execution!!"
850
+ end
851
+
852
+ return rs
853
+ end
854
+
855
+ protected
856
+ def sanitize_escape_digits(digits)
857
+ if digits.nil?
858
+ digits = "#"
859
+ elsif digits.size == 0
860
+ digits = "X"
861
+ else
862
+ digits = digits.to_s
863
+ end
864
+
865
+ return digits
866
+ end
867
+
868
+ protected
869
+ def sanitize_timeout(timeout)
870
+ if timeout.nil? or timeout <= 0
871
+ timeout = -1
872
+ else
873
+ timeout = timeout * 1000
874
+ end
875
+
876
+ return timeout
877
+ end
878
+
879
+ protected
880
+ def sanitize_file_format(format)
881
+ if format.nil? or format.size == 0
882
+ format = "gsm"
883
+ else
884
+ format = format.to_s
885
+ end
886
+
887
+ return format
888
+ end
889
+
890
+ protected
891
+ def sanitize_max_digit(max_digit)
892
+ if ((max_digit.nil?) or (max_digit <= 0))
893
+ max_digit = ""
894
+ else
895
+ max_digit = max_digit.to_i
896
+ end
897
+
898
+ return max_digit
899
+ end
900
+
901
+ ############################################
902
+ ### More synthetic methods ###
903
+ ############################################
904
+
905
+ public
906
+ def jump_to(context, extension, priority)
907
+ set_context(context) if not context.nil?
908
+ set_extension(extension) if not extension.nil?
909
+ set_priority(priority) if not priority.nil?
910
+ end
911
+ end