rami 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.
- data/LICENSE +26 -0
- data/README +3 -0
- data/bin/server.rb +27 -0
- data/bin/test.rb +144 -0
- data/lib/rami.rb +748 -0
- data/manager.txt +43 -0
- metadata +44 -0
data/LICENSE
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# Copyright (c) 2005, Chris Ochs
|
|
2
|
+
# All rights reserved.
|
|
3
|
+
|
|
4
|
+
# Redistribution and use in source and binary forms, with or without
|
|
5
|
+
# modification, are permitted provided that the following conditions are met:
|
|
6
|
+
#
|
|
7
|
+
# * Redistributions of source code must retain the above copyright notice,
|
|
8
|
+
# this list of conditions and the following disclaimer.
|
|
9
|
+
# * Redistributions in binary form must reproduce the above copyright notice,
|
|
10
|
+
# this list of conditions and the following disclaimer in the
|
|
11
|
+
# documentation and/or other materials provided with the distribution.
|
|
12
|
+
# * Neither the name of Chris Ochs nor the names of its contributors
|
|
13
|
+
# may be used to endorse or promote products derived from this software
|
|
14
|
+
# without specific prior written permission.
|
|
15
|
+
#
|
|
16
|
+
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
17
|
+
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
18
|
+
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
19
|
+
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
|
|
20
|
+
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
21
|
+
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
22
|
+
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
23
|
+
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
24
|
+
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
25
|
+
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
26
|
+
|
data/README
ADDED
data/bin/server.rb
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
#!/usr/bin/env ruby -w
|
|
2
|
+
|
|
3
|
+
require 'rubygems'
|
|
4
|
+
require 'rami'
|
|
5
|
+
include Rami
|
|
6
|
+
|
|
7
|
+
server = RamiServer.new
|
|
8
|
+
|
|
9
|
+
## if you want all events printed to the console set this to 1
|
|
10
|
+
server.console =1
|
|
11
|
+
|
|
12
|
+
## The username and secret used to login to the Asterisk manager interface
|
|
13
|
+
server.username = 'asterisk'
|
|
14
|
+
server.secret = 'secret'
|
|
15
|
+
|
|
16
|
+
## The host and port that the manager interface is running on.
|
|
17
|
+
server.host = 'localhost'
|
|
18
|
+
server.port = 5038
|
|
19
|
+
|
|
20
|
+
## DRb URI
|
|
21
|
+
drburi = "druby://localhost:9000"
|
|
22
|
+
|
|
23
|
+
server.run
|
|
24
|
+
|
|
25
|
+
DRb.start_service(drburi, server)
|
|
26
|
+
DRb.thread.join
|
|
27
|
+
|
data/bin/test.rb
ADDED
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
#!/usr/bin/env ruby -w
|
|
2
|
+
|
|
3
|
+
require 'rubygems'
|
|
4
|
+
require 'rami'
|
|
5
|
+
include Rami
|
|
6
|
+
|
|
7
|
+
def print_results(t)
|
|
8
|
+
if t.size > 0
|
|
9
|
+
t.each do |array|
|
|
10
|
+
array.each {|key,value| puts "#{key}: #{value}"}
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
puts "\n\n"
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
client = RamiClient.new
|
|
17
|
+
|
|
18
|
+
## Redirect
|
|
19
|
+
client.timeout = 10
|
|
20
|
+
t = client.redirect({'Action' => 'Redirect', 'Channel' => 'test','ExtraChannel' => 'test', 'Context' => 'mainmenu', 'Exten' => '201', 'Priority' => 1})
|
|
21
|
+
print_results(t)
|
|
22
|
+
|
|
23
|
+
## Queues
|
|
24
|
+
client.timeout = 10
|
|
25
|
+
t = client.queues
|
|
26
|
+
print_results(t)
|
|
27
|
+
|
|
28
|
+
## IAXpeers
|
|
29
|
+
client.timeout = 10
|
|
30
|
+
t = client.iax_peers
|
|
31
|
+
print_results(t)
|
|
32
|
+
|
|
33
|
+
## SIPpeers
|
|
34
|
+
client.timeout = 10
|
|
35
|
+
t = client.sip_peers
|
|
36
|
+
print_results(t)
|
|
37
|
+
|
|
38
|
+
## SIPshowpeer
|
|
39
|
+
client.timeout = 10
|
|
40
|
+
t = client.sip_show_peer('test_peer')
|
|
41
|
+
print_results(t)
|
|
42
|
+
|
|
43
|
+
## ParkedCalls
|
|
44
|
+
client.timeout = 10
|
|
45
|
+
t = client.parked_calls
|
|
46
|
+
print_results(t)
|
|
47
|
+
|
|
48
|
+
## Hangup
|
|
49
|
+
client.timeout = 10
|
|
50
|
+
t = client.hangup('SIP/test')
|
|
51
|
+
print_results(t)
|
|
52
|
+
|
|
53
|
+
## ExtensionState
|
|
54
|
+
client.timeout = 10
|
|
55
|
+
t = client.extension_state('menu','201')
|
|
56
|
+
print_results(t)
|
|
57
|
+
|
|
58
|
+
## SetVar
|
|
59
|
+
client.timeout = 10
|
|
60
|
+
t = client.setvar('IAX2/teliax-6','testmon',1)
|
|
61
|
+
print_results(t)
|
|
62
|
+
|
|
63
|
+
## GetVar
|
|
64
|
+
client.timeout = 10
|
|
65
|
+
t = client.getvar('IAX2/teliax-6','testmon')
|
|
66
|
+
print_results(t)
|
|
67
|
+
|
|
68
|
+
## Monitor
|
|
69
|
+
client.timeout = 10
|
|
70
|
+
t = client.monitor('SIP/test','testmon',1)
|
|
71
|
+
print_results(t)
|
|
72
|
+
|
|
73
|
+
## StopMonitor
|
|
74
|
+
t = client.stop_monitor('SIP/test')
|
|
75
|
+
print_results(t)
|
|
76
|
+
|
|
77
|
+
## ChangeMonitor
|
|
78
|
+
t = client.change_monitor('SIP/test','testmon')
|
|
79
|
+
print_results(t)
|
|
80
|
+
|
|
81
|
+
## Agents
|
|
82
|
+
client.timeout = 10
|
|
83
|
+
t = client.agents
|
|
84
|
+
print_results(t)
|
|
85
|
+
|
|
86
|
+
## MailboxCount
|
|
87
|
+
client.timeout = 10
|
|
88
|
+
t = client.mailbox_count(1002)
|
|
89
|
+
print_results(t)
|
|
90
|
+
|
|
91
|
+
## MailboxStatus
|
|
92
|
+
t = client.mailbox_status(1001)
|
|
93
|
+
print_results(t)
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
## Ping
|
|
97
|
+
client.timeout = 10
|
|
98
|
+
t = client.ping
|
|
99
|
+
print_results(t)
|
|
100
|
+
|
|
101
|
+
## DBPut
|
|
102
|
+
client.timeout = 10
|
|
103
|
+
t = client.dbput('testfamily','testkey','testval')
|
|
104
|
+
print_results(t)
|
|
105
|
+
|
|
106
|
+
## DBGet
|
|
107
|
+
client.timeout = 10
|
|
108
|
+
t = client.dbget('testfamily','testkey')
|
|
109
|
+
print_results(t)
|
|
110
|
+
|
|
111
|
+
## Originate
|
|
112
|
+
# Waits for Hangup or OriginateFailed. Returns all associated events
|
|
113
|
+
client.timeout = 90
|
|
114
|
+
t = client.originate({'Channel' => 'IAX2/nbts', 'Context' =>'default','Exten' =>'1000','Priority' =>1})
|
|
115
|
+
print_results(t)
|
|
116
|
+
|
|
117
|
+
# Does not wait, returns immediately. Associated events available via get_events or find_events.
|
|
118
|
+
client.timeout = 10
|
|
119
|
+
t = client.originate({'Async' => 1,'Channel' => 'IAX2/nbts', 'Context' =>'default','Exten' =>'1000','Priority' =>1})
|
|
120
|
+
print_results(t)
|
|
121
|
+
|
|
122
|
+
## Status
|
|
123
|
+
t = client.status()
|
|
124
|
+
print_results(t)
|
|
125
|
+
|
|
126
|
+
## AbsoluteTimeout
|
|
127
|
+
t = client.absolute_timeout('IAX2/nbts',20)
|
|
128
|
+
print_results(t)
|
|
129
|
+
|
|
130
|
+
## Command
|
|
131
|
+
t = client.command('show manager commands')
|
|
132
|
+
print_results(t)
|
|
133
|
+
|
|
134
|
+
## Find events in the state queue
|
|
135
|
+
t = client.find_events('Event','NewCallerID')
|
|
136
|
+
print_results(t)
|
|
137
|
+
# OR
|
|
138
|
+
t = client.find_events('any','SIP/test_extension')
|
|
139
|
+
print_results(t)
|
|
140
|
+
|
|
141
|
+
## Get all events in the state queue
|
|
142
|
+
t = client.get_events
|
|
143
|
+
print_results(t)
|
|
144
|
+
|
data/lib/rami.rb
ADDED
|
@@ -0,0 +1,748 @@
|
|
|
1
|
+
#
|
|
2
|
+
# RAMI - Ruby classes for implementing a proxy server/client api for the Asterisk Manager Interface
|
|
3
|
+
#
|
|
4
|
+
#
|
|
5
|
+
# Copyright (c) 2005, Chris Ochs
|
|
6
|
+
# All rights reserved.
|
|
7
|
+
|
|
8
|
+
# Redistribution and use in source and binary forms, with or without
|
|
9
|
+
# modification, are permitted provided that the following conditions are met:
|
|
10
|
+
#
|
|
11
|
+
# * Redistributions of source code must retain the above copyright notice,
|
|
12
|
+
# this list of conditions and the following disclaimer.
|
|
13
|
+
# * Redistributions in binary form must reproduce the above copyright notice,
|
|
14
|
+
# this list of conditions and the following disclaimer in the
|
|
15
|
+
# documentation and/or other materials provided with the distribution.
|
|
16
|
+
# * Neither the name of Chris Ochs nor the names of its contributors
|
|
17
|
+
# may be used to endorse or promote products derived from this software
|
|
18
|
+
# without specific prior written permission.
|
|
19
|
+
#
|
|
20
|
+
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
21
|
+
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
22
|
+
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
23
|
+
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
|
|
24
|
+
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
25
|
+
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
26
|
+
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
27
|
+
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
28
|
+
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
29
|
+
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
30
|
+
|
|
31
|
+
require 'drb'
|
|
32
|
+
require 'monitor'
|
|
33
|
+
require 'socket'
|
|
34
|
+
require 'timeout'
|
|
35
|
+
|
|
36
|
+
# A proxy server/client api for the Asterisk Manager Interface
|
|
37
|
+
module Rami
|
|
38
|
+
|
|
39
|
+
# The Rami client library. To use the library make sure the server has been started and then call RamiClient.new and set the
|
|
40
|
+
# host, port, and timeout attributes. All methods return an array of hashes, or an empty array if no data is available.
|
|
41
|
+
#
|
|
42
|
+
# Each hash will contain an asterisk response packet. Note that not all response packets are always returned. If a response
|
|
43
|
+
# packet is necessary for the actual communication with asterisk, but does not in itself have any meaningful content, then
|
|
44
|
+
# the packet is droppped. For example some actions might generate an initial response packet of something like "Response Follows",
|
|
45
|
+
# followed by one or more response packets with the actual data, followed by a final response packet which contains "Response Complete".
|
|
46
|
+
# In this case the first and last response will not be included in the array of hashes passed to the caller.
|
|
47
|
+
#
|
|
48
|
+
# I tried to document the things that need it the most. Some things should be fairly evident, such as methods for simple
|
|
49
|
+
# commands like Monitor or Ping.
|
|
50
|
+
#
|
|
51
|
+
# For examples, see test.rb in the bin directory. It contains sample code for calling every available method.
|
|
52
|
+
#
|
|
53
|
+
# Not all manager commands are currently supported. This is only because I have not yet had the time to add them.
|
|
54
|
+
# I tried to add the most complicated commands first, so adding the remaining commands is fairly simple.
|
|
55
|
+
# If there is a command you need let me know and I will make sure it gets included.
|
|
56
|
+
# If you want to add your own action command it's fairly simple. Add a method in RamiClient for
|
|
57
|
+
# the new command, and then add a section in the RamiClient::send_action loop to harvest the response.
|
|
58
|
+
class RamiClient
|
|
59
|
+
|
|
60
|
+
# Server hostname. Default localhost
|
|
61
|
+
attr_writer :host
|
|
62
|
+
# Server port. Default 9000
|
|
63
|
+
attr_writer :port
|
|
64
|
+
# Number of seconds before a method call will timeout. Default 10.
|
|
65
|
+
attr_writer :timeout
|
|
66
|
+
|
|
67
|
+
def initialize
|
|
68
|
+
@host = 'localhost'
|
|
69
|
+
@port = 9000
|
|
70
|
+
@timeout = 10
|
|
71
|
+
@action_id = Time.now().to_f
|
|
72
|
+
DRb.start_service()
|
|
73
|
+
@client = DRbObject.new(nil,"druby://#{@host}:#{@port}")
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def absolute_timeout(channel=nil,tout=nil)
|
|
77
|
+
increment_action_id
|
|
78
|
+
return @client.send_action({'ActionID' => @action_id, 'Action' => 'AbsoluteTimeout', 'Channel' => channel, 'Timeout' => tout},@timeout)
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def agents
|
|
82
|
+
increment_action_id
|
|
83
|
+
return @client.send_action({'ActionID' => @action_id, 'Action' => 'Agents'},@timeout)
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def change_monitor(channel=nil,file=nil)
|
|
87
|
+
increment_action_id
|
|
88
|
+
return @client.send_action({'ActionID' => @action_id, 'Action' => 'ChangeMonitor', 'Channel' =>channel, 'File' => file},@timeout)
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def command(command)
|
|
92
|
+
increment_action_id
|
|
93
|
+
return @client.send_action({'ActionID' => @action_id, 'Action' => 'Command', 'Command' => command},@timeout)
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def dbput(family,key,val)
|
|
97
|
+
increment_action_id
|
|
98
|
+
return @client.send_action({'ActionID' => @action_id, 'Action' => 'DBPut', 'Family' => family, 'Key' => key, 'Val' => val},@timeout)
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def dbget(family,key)
|
|
102
|
+
increment_action_id
|
|
103
|
+
return @client.send_action({'ActionID' => @action_id, 'Action' => 'DBGet', 'Family' => family, 'Key' => key},@timeout)
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def extension_state(context,exten)
|
|
107
|
+
increment_action_id
|
|
108
|
+
return @client.send_action({'ActionID' => @action_id, 'Action' => 'ExtensionState', 'Context' => context, 'Exten' => exten},@timeout)
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
# If called with key and value, searches the state queue for events matching the key and value given.
|
|
112
|
+
# The key is an exact match, the value is a regex. You can also call find_events with key=any, which will match
|
|
113
|
+
# any entry with the given value
|
|
114
|
+
#
|
|
115
|
+
# The returned results are deleted from the queue. See RamiServer for more information on the queue structure.
|
|
116
|
+
def find_events(key=nil,value=nil)
|
|
117
|
+
return @client.find_events(key,value)
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
# Get all events from the state queue. The returned results are deleted from the queue.
|
|
121
|
+
# See RamiServer for more information on the queue structure.
|
|
122
|
+
def get_events
|
|
123
|
+
return @client.get_events
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
def getvar(channel,variable)
|
|
127
|
+
increment_action_id
|
|
128
|
+
return @client.send_action({'ActionID' => @action_id, 'Action' => 'GetVar', 'Channel' => channel, 'Variable' => variable},@timeout)
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
def hangup(channel=nil)
|
|
132
|
+
increment_action_id
|
|
133
|
+
return @client.send_action({'ActionID' => @action_id, 'Action' => 'Hangup', 'Channel' => channel},@timeout)
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
# IAXpeers is bugged. The response does not contain an action id, nor does it contain any key/value pairs in the response.
|
|
137
|
+
# For this reason it gets put into the state queue where it can be retrieved using find_events('any','iax2 peers'). iax_peers
|
|
138
|
+
# will always return {'Response' => 'Success'}
|
|
139
|
+
def iax_peers
|
|
140
|
+
increment_action_id
|
|
141
|
+
@client.send_action({'ActionID' => @action_id, 'Action' => 'IAXpeers'},1)
|
|
142
|
+
return [{'Response' => 'Success'}]
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
def monitor(channel=nil,file=nil,mix=nil)
|
|
146
|
+
increment_action_id
|
|
147
|
+
return @client.send_action({'ActionID' => @action_id, 'Action' => 'Monitor', 'Channel' =>channel, 'File' => file, 'Mix' => mix},@timeout)
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
def mailbox_status(mailbox=nil)
|
|
151
|
+
increment_action_id
|
|
152
|
+
return @client.send_action({'ActionID' => @action_id, 'Action' => 'MailboxStatus', 'Mailbox' => mailbox},@timeout)
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
def mailbox_count(mailbox=nil)
|
|
156
|
+
increment_action_id
|
|
157
|
+
return @client.send_action({'ActionID' => @action_id, 'Action' => 'MailboxCount', 'Mailbox' => mailbox},@timeout)
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
# h is a hash with the following keys. keys that are nil will not be passed to asterisk.
|
|
161
|
+
# * Channel
|
|
162
|
+
# * Context
|
|
163
|
+
# * Exten
|
|
164
|
+
# * Priority
|
|
165
|
+
# * Timeout
|
|
166
|
+
# * CallerID
|
|
167
|
+
# * Variable
|
|
168
|
+
# * Account
|
|
169
|
+
# * Application
|
|
170
|
+
# * Data
|
|
171
|
+
# * Async
|
|
172
|
+
# If Async has a value, the method will wait until the call is hungup or fails. On hangup,
|
|
173
|
+
# Asterisk will response with Hangup event, and on failure it will respond with an OriginateFailed event.
|
|
174
|
+
# If Async is nil, the method will return immediately and the associated events can be obtained by calling
|
|
175
|
+
# find_events() or get_events().
|
|
176
|
+
def originate(h={})
|
|
177
|
+
increment_action_id
|
|
178
|
+
return @client.send_action({'ActionID' => @action_id,
|
|
179
|
+
'Action' => 'Originate',
|
|
180
|
+
'Channel' => h['Channel'],
|
|
181
|
+
'Context' => h['Context'],
|
|
182
|
+
'Exten' =>h['Exten'],
|
|
183
|
+
'Priority' =>h['Priority'],
|
|
184
|
+
'Timeout' =>h['Timeout'],
|
|
185
|
+
'CallerID' =>h['CallerID'],
|
|
186
|
+
'Variable' =>h['Variable'],
|
|
187
|
+
'Account' =>h['Account'],
|
|
188
|
+
'Application' =>h['Application'],
|
|
189
|
+
'Data' =>h['Data'],
|
|
190
|
+
'Async' => h['Async']}.delete_if {|key, value| value.nil? },@timeout)
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
def parked_calls
|
|
194
|
+
increment_action_id
|
|
195
|
+
return @client.send_action({'ActionID' => @action_id, 'Action' => 'ParkedCalls'},@timeout)
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
def ping
|
|
199
|
+
increment_action_id
|
|
200
|
+
return @client.send_action({'ActionID' => @action_id, 'Action' => 'Ping'},@timeout)
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
# Queues is just like IAXpeers. You can use find_events('any','default') to get the response from the state queue.
|
|
204
|
+
def queues
|
|
205
|
+
increment_action_id
|
|
206
|
+
@client.send_action({'ActionID' => @action_id, 'Action' => 'Queues'},@timeout)
|
|
207
|
+
return [{'Response' => 'Success'}]
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
# h is a hash with the following keys. keys that are nil will not be passed to asterisk.
|
|
211
|
+
# * Channel
|
|
212
|
+
# * ExtraChannel
|
|
213
|
+
# * Context
|
|
214
|
+
# * Exten
|
|
215
|
+
# * Priority
|
|
216
|
+
def redirect(h={})
|
|
217
|
+
increment_action_id
|
|
218
|
+
return @client.send_action({'ActionID' => @action_id,
|
|
219
|
+
'Action' => 'Redirect',
|
|
220
|
+
'Channel' => h['Channel'],
|
|
221
|
+
'ExtraChannel' => h['ExtraChannel'],
|
|
222
|
+
'Context' => h['Context'],
|
|
223
|
+
'Exten' => h['Exten'],
|
|
224
|
+
'Priority' => h['Priority']}.delete_if {|key,value| value.nil?},@timeout)
|
|
225
|
+
end
|
|
226
|
+
|
|
227
|
+
def setvar(channel,variable,value)
|
|
228
|
+
increment_action_id
|
|
229
|
+
return @client.send_action({'ActionID' => @action_id, 'Action' => 'SetVar', 'Channel' => channel, 'Variable' => variable, 'Value' => value},@timeout)
|
|
230
|
+
end
|
|
231
|
+
|
|
232
|
+
# Unlike IAXpeers, SIPpeers returns an event for each peer that is easily parsed and usable.
|
|
233
|
+
def sip_peers
|
|
234
|
+
increment_action_id
|
|
235
|
+
return @client.send_action({'ActionID' => @action_id, 'Action' => 'SIPpeers'},@timeout)
|
|
236
|
+
end
|
|
237
|
+
|
|
238
|
+
# Detailed information about a particular peer.
|
|
239
|
+
def sip_show_peer(peer)
|
|
240
|
+
increment_action_id
|
|
241
|
+
return @client.send_action({'ActionID' => @action_id, 'Action' => 'SIPshowpeer', 'Peer' => peer},@timeout)
|
|
242
|
+
end
|
|
243
|
+
|
|
244
|
+
def status(channel=nil)
|
|
245
|
+
increment_action_id
|
|
246
|
+
if channel.nil?
|
|
247
|
+
return @client.send_action({'ActionID' => @action_id, 'Action' => 'Status'},@timeout)
|
|
248
|
+
else
|
|
249
|
+
return @client.send_action({'ActionID' => @action_id, 'Action' => 'Status', 'Channel' => channel},@timeout)
|
|
250
|
+
end
|
|
251
|
+
end
|
|
252
|
+
|
|
253
|
+
def stop_monitor(channel=nil)
|
|
254
|
+
increment_action_id
|
|
255
|
+
return @client.send_action({'ActionID' => @action_id, 'Action' => 'StopMonitor', 'Channel' =>channel},@timeout)
|
|
256
|
+
end
|
|
257
|
+
|
|
258
|
+
private
|
|
259
|
+
def increment_action_id
|
|
260
|
+
@action_id = Time.now().to_f
|
|
261
|
+
end
|
|
262
|
+
|
|
263
|
+
def send_action(action=nil,tout=nil)
|
|
264
|
+
increment_action_id
|
|
265
|
+
action['ActionID'] = @action_id
|
|
266
|
+
return @client.send_action(action,tout)
|
|
267
|
+
end
|
|
268
|
+
|
|
269
|
+
end
|
|
270
|
+
|
|
271
|
+
|
|
272
|
+
# RamiServer maintains one open connection to asterisk. It uses one thread to constantly read responses and stick them into
|
|
273
|
+
# the appropriate queue. Clients connect via DRb and are each serviced in their own thread.
|
|
274
|
+
#
|
|
275
|
+
# The server uses two queues to hold responses from asterisk. The action queue holds all responses that contain an ActionID.
|
|
276
|
+
# The state queue holds all responses that do not have an ActionID. The action queue is only used internally, while the state
|
|
277
|
+
# queue can be queried via RamiClient.get_events and RamiClient.find_events.
|
|
278
|
+
#
|
|
279
|
+
# To run the server, call RamiServer.new, set the attributes, then call the run method.
|
|
280
|
+
# For an example of how to run the server, see bin/server.rb.
|
|
281
|
+
class RamiServer
|
|
282
|
+
# If set to 1, console logging is turned on. Default 0
|
|
283
|
+
attr_writer :console
|
|
284
|
+
# Asterisk manager username. Default asterisk.
|
|
285
|
+
attr_writer :username
|
|
286
|
+
# Asterisk manager secret. Default secret.
|
|
287
|
+
attr_writer :secret
|
|
288
|
+
# Asterisk manager hostname. Default localhost
|
|
289
|
+
attr_writer :host
|
|
290
|
+
# Asterisk manager port. Default 5038
|
|
291
|
+
attr_writer :port
|
|
292
|
+
# The number of responses to hold in the state queue. The state queue is a FIFO list. Default is 100.
|
|
293
|
+
attr_writer :event_cache
|
|
294
|
+
|
|
295
|
+
Thread.current.abort_on_exception=true
|
|
296
|
+
|
|
297
|
+
include DRbUndumped
|
|
298
|
+
|
|
299
|
+
def initialize
|
|
300
|
+
@console = 0
|
|
301
|
+
@username = 'asterisk'
|
|
302
|
+
@secret = 'secret'
|
|
303
|
+
@host = 'localhost'
|
|
304
|
+
@port = 5038
|
|
305
|
+
@eventcount = 1
|
|
306
|
+
@event_cache = 100
|
|
307
|
+
|
|
308
|
+
@sock = nil
|
|
309
|
+
@socklock = nil
|
|
310
|
+
@socklock.extend(MonitorMixin)
|
|
311
|
+
|
|
312
|
+
@action_events = []
|
|
313
|
+
@action_events.extend(MonitorMixin)
|
|
314
|
+
@action_events_pending = @action_events.new_cond
|
|
315
|
+
|
|
316
|
+
@state_events = []
|
|
317
|
+
@state_events.extend(MonitorMixin)
|
|
318
|
+
@state_events_pending = @state_events.new_cond
|
|
319
|
+
|
|
320
|
+
end
|
|
321
|
+
|
|
322
|
+
|
|
323
|
+
|
|
324
|
+
|
|
325
|
+
private
|
|
326
|
+
|
|
327
|
+
def connect
|
|
328
|
+
@sock = TCPSocket.new(@host,@port)
|
|
329
|
+
login = {'Action' => 'login', 'Username' => @username, 'Secret' => @secret, 'Events' => 'On'}
|
|
330
|
+
writesock(login)
|
|
331
|
+
accum = {}
|
|
332
|
+
login = 0
|
|
333
|
+
status = Timeout.timeout(10) do
|
|
334
|
+
while login == 0
|
|
335
|
+
@sock.each("\r\n") do |line|
|
|
336
|
+
if line.include?(':')
|
|
337
|
+
key,value = parseline(line) if line.include?(':')
|
|
338
|
+
accum[key] = value
|
|
339
|
+
end
|
|
340
|
+
if @console == 1
|
|
341
|
+
print "#{Time.now} RECV #{@eventcount}: #{line}"
|
|
342
|
+
end
|
|
343
|
+
if line == "\r\n" and accum['Message'] == 'Authentication accepted' and accum['Response'] == 'Success'
|
|
344
|
+
login =1
|
|
345
|
+
@eventcount += 1
|
|
346
|
+
break
|
|
347
|
+
end
|
|
348
|
+
end
|
|
349
|
+
end
|
|
350
|
+
return true
|
|
351
|
+
end
|
|
352
|
+
rescue Exception => e
|
|
353
|
+
puts "LOGIN TIMEOUT"
|
|
354
|
+
return false
|
|
355
|
+
end
|
|
356
|
+
|
|
357
|
+
|
|
358
|
+
def mainloop
|
|
359
|
+
|
|
360
|
+
ast_reader = Thread.new do
|
|
361
|
+
Thread.current.abort_on_exception=true
|
|
362
|
+
linecount = 0
|
|
363
|
+
loop do
|
|
364
|
+
event = {}
|
|
365
|
+
@sock.each("\r\n") do |line|
|
|
366
|
+
linecount += 1
|
|
367
|
+
type = 'state'
|
|
368
|
+
if @console == 1
|
|
369
|
+
print "#{Time.now} RECV #{@eventcount}: #{line}"
|
|
370
|
+
end
|
|
371
|
+
if line == "\r\n"
|
|
372
|
+
if event.size == 0
|
|
373
|
+
if @console == 1
|
|
374
|
+
print "#{Time.now} MSG: #{@eventcount} RECEIVED EXTRA CR/LF #{line}"
|
|
375
|
+
end
|
|
376
|
+
next
|
|
377
|
+
end
|
|
378
|
+
if event['ActionID']
|
|
379
|
+
type = 'action'
|
|
380
|
+
end
|
|
381
|
+
|
|
382
|
+
if @console == 1
|
|
383
|
+
print "#{Time.now} MSG: #{@eventcount} finished (type=#{type}) #{line}"
|
|
384
|
+
end
|
|
385
|
+
|
|
386
|
+
if type == 'action'
|
|
387
|
+
@action_events.synchronize do
|
|
388
|
+
@action_events << event.clone
|
|
389
|
+
event.clear
|
|
390
|
+
@action_events_pending.signal
|
|
391
|
+
end
|
|
392
|
+
elsif type == 'state'
|
|
393
|
+
@state_events.synchronize do
|
|
394
|
+
@state_events << event.clone
|
|
395
|
+
if @state_events.size >= @event_cache
|
|
396
|
+
@state_events.shift
|
|
397
|
+
end
|
|
398
|
+
event.clear
|
|
399
|
+
@state_events_pending.signal
|
|
400
|
+
end
|
|
401
|
+
end
|
|
402
|
+
@eventcount += 1
|
|
403
|
+
elsif line =~/^[\w\s\/-]*:[\s]*.*\r\n$/
|
|
404
|
+
key,value = parseline(line)
|
|
405
|
+
if key == 'ActionID'
|
|
406
|
+
value = value.gsub(' ','')
|
|
407
|
+
end
|
|
408
|
+
event[key] = value
|
|
409
|
+
else
|
|
410
|
+
event[linecount] = line
|
|
411
|
+
end
|
|
412
|
+
end
|
|
413
|
+
end
|
|
414
|
+
end
|
|
415
|
+
end
|
|
416
|
+
|
|
417
|
+
def parseline(line)
|
|
418
|
+
if line =~/(^[\w\s\/-]*:[\s]*)(.*\r\n$)/
|
|
419
|
+
key = $1
|
|
420
|
+
value = $2
|
|
421
|
+
key = key.gsub(/[\s:]*/,'')
|
|
422
|
+
value = value.gsub(/\r\n/,'')
|
|
423
|
+
return [key,value]
|
|
424
|
+
else
|
|
425
|
+
return ["UNKNOWN","UNKNOWN"]
|
|
426
|
+
end
|
|
427
|
+
|
|
428
|
+
end
|
|
429
|
+
|
|
430
|
+
def writelog(msg)
|
|
431
|
+
print "#{Time.now} #{msg}\r\n"
|
|
432
|
+
end
|
|
433
|
+
|
|
434
|
+
def writesock(action)
|
|
435
|
+
@socklock.synchronize do
|
|
436
|
+
action.each do |key,value|
|
|
437
|
+
@sock.write("#{key}: #{value}\r\n")
|
|
438
|
+
print "#{Time.now} SEND #{@eventcount}: #{key}: #{value}\r\n"
|
|
439
|
+
end
|
|
440
|
+
@sock.write("\r\n")
|
|
441
|
+
print "#{Time.now} SEND #{@eventcount}: \r\n"
|
|
442
|
+
end
|
|
443
|
+
end
|
|
444
|
+
|
|
445
|
+
|
|
446
|
+
public
|
|
447
|
+
|
|
448
|
+
# Starts the server and connects to asterisk.
|
|
449
|
+
def run
|
|
450
|
+
if connect
|
|
451
|
+
puts "#{Time.now} MSG: LOGGED IN"
|
|
452
|
+
else
|
|
453
|
+
puts "#{Time.now} MSG: LOGIN FAILED"
|
|
454
|
+
exit
|
|
455
|
+
end
|
|
456
|
+
mainloop
|
|
457
|
+
end
|
|
458
|
+
|
|
459
|
+
# Should only be called via Drb by RamiClient
|
|
460
|
+
def find_events(key=nil,value=nil)
|
|
461
|
+
print "#{Time.now} find_events: #{key}: #{value}\r\n"
|
|
462
|
+
found = []
|
|
463
|
+
@state_events.synchronize do
|
|
464
|
+
if @state_events.empty?
|
|
465
|
+
return found
|
|
466
|
+
else
|
|
467
|
+
@state_events_pending.wait_while {@state_events.empty?}
|
|
468
|
+
@state_events.clone.each do |e|
|
|
469
|
+
if key == 'any' and e.to_s =~/#{value}/
|
|
470
|
+
found.push(e)
|
|
471
|
+
@state_events.delete(e)
|
|
472
|
+
elsif key != 'any' and e[key] =~/#{value}/
|
|
473
|
+
found.push(e)
|
|
474
|
+
@state_events.delete(e)
|
|
475
|
+
end
|
|
476
|
+
end
|
|
477
|
+
return found
|
|
478
|
+
end
|
|
479
|
+
end
|
|
480
|
+
end
|
|
481
|
+
|
|
482
|
+
# Should only be called via Drb by RamiClient
|
|
483
|
+
def get_events
|
|
484
|
+
found = []
|
|
485
|
+
@state_events.synchronize do
|
|
486
|
+
if @state_events.empty?
|
|
487
|
+
return found
|
|
488
|
+
else
|
|
489
|
+
@state_events_pending.wait_while {@state_events.empty?}
|
|
490
|
+
@state_events.clone.each do |e|
|
|
491
|
+
found.push(e)
|
|
492
|
+
end
|
|
493
|
+
@state_events.clear
|
|
494
|
+
return found
|
|
495
|
+
end
|
|
496
|
+
end
|
|
497
|
+
end
|
|
498
|
+
|
|
499
|
+
# Should only be called via Drb by RamiClient
|
|
500
|
+
def send_action(action=nil,t=10)
|
|
501
|
+
sent_id = action['ActionID'].to_s
|
|
502
|
+
result = []
|
|
503
|
+
finished = 0
|
|
504
|
+
status = Timeout.timeout(t) do
|
|
505
|
+
writesock(action)
|
|
506
|
+
|
|
507
|
+
## Some action responses have no action id or specific formatting, so we just return immediately and the caller can call
|
|
508
|
+
## get_events or find_events to get the response.
|
|
509
|
+
|
|
510
|
+
## IAXpeer - Just return immediately
|
|
511
|
+
if action['Action'] == 'IAXpeers'
|
|
512
|
+
finished =1
|
|
513
|
+
return
|
|
514
|
+
end
|
|
515
|
+
|
|
516
|
+
## Queues - Just return immediately
|
|
517
|
+
if action['Action'] == 'Queues'
|
|
518
|
+
finished =1
|
|
519
|
+
return
|
|
520
|
+
end
|
|
521
|
+
|
|
522
|
+
|
|
523
|
+
while finished == 0
|
|
524
|
+
@action_events.synchronize do
|
|
525
|
+
@action_events_pending.wait_while {@action_events.empty?}
|
|
526
|
+
@action_events.clone.each do |e|
|
|
527
|
+
|
|
528
|
+
## Action responses that contain an ActionID
|
|
529
|
+
if e['ActionID'].to_s == sent_id
|
|
530
|
+
|
|
531
|
+
## Ping - Single response has ActionID
|
|
532
|
+
if action['Action'] == 'Ping' and e['Response'].gsub(/\s/,'') == 'Pong'
|
|
533
|
+
@action_events.delete(e)
|
|
534
|
+
result << e
|
|
535
|
+
finished = 1
|
|
536
|
+
end
|
|
537
|
+
|
|
538
|
+
## Command - Single response has ActionID
|
|
539
|
+
if action['Action'] == 'Command' and e['Response'].gsub(/\s/,'') == 'Follows'
|
|
540
|
+
@action_events.delete(e)
|
|
541
|
+
result << e
|
|
542
|
+
finished = 1
|
|
543
|
+
end
|
|
544
|
+
|
|
545
|
+
## Hangup - Single response has ActionID
|
|
546
|
+
if action['Action'] == 'Hangup'
|
|
547
|
+
@action_events.delete(e)
|
|
548
|
+
result << e
|
|
549
|
+
finished = 1
|
|
550
|
+
end
|
|
551
|
+
|
|
552
|
+
## ExtensionState - Single response has ActionID
|
|
553
|
+
if action['Action'] == 'ExtensionState'
|
|
554
|
+
@action_events.delete(e)
|
|
555
|
+
result << e
|
|
556
|
+
finished = 1
|
|
557
|
+
end
|
|
558
|
+
|
|
559
|
+
## SetVar - Single response has ActionID
|
|
560
|
+
if action['Action'] == 'SetVar'
|
|
561
|
+
@action_events.delete(e)
|
|
562
|
+
result << e
|
|
563
|
+
finished = 1
|
|
564
|
+
end
|
|
565
|
+
|
|
566
|
+
## GetVar - Single response has ActionID
|
|
567
|
+
if action['Action'] == 'GetVar'
|
|
568
|
+
@action_events.delete(e)
|
|
569
|
+
result << e
|
|
570
|
+
finished = 1
|
|
571
|
+
end
|
|
572
|
+
|
|
573
|
+
## Redirect - Single response has ActionID
|
|
574
|
+
if action['Action'] == 'Redirect'
|
|
575
|
+
@action_events.delete(e)
|
|
576
|
+
result << e
|
|
577
|
+
finished = 1
|
|
578
|
+
end
|
|
579
|
+
|
|
580
|
+
## DBPut - Single response has ActionID
|
|
581
|
+
if action['Action'] == 'DBPut'
|
|
582
|
+
@action_events.delete(e)
|
|
583
|
+
result << e
|
|
584
|
+
finished = 1
|
|
585
|
+
end
|
|
586
|
+
|
|
587
|
+
## DBGet - Single response has ActionID
|
|
588
|
+
if action['Action'] == 'DBGet' and (e['Response'] == 'Error' or e['Event'] == 'DBGetResponse')
|
|
589
|
+
@action_events.delete(e)
|
|
590
|
+
result << e
|
|
591
|
+
finished = 1
|
|
592
|
+
end
|
|
593
|
+
|
|
594
|
+
## Monitor - Single response has ActionID
|
|
595
|
+
if action['Action'] == 'Monitor'
|
|
596
|
+
@action_events.delete(e)
|
|
597
|
+
result << e
|
|
598
|
+
finished = 1
|
|
599
|
+
end
|
|
600
|
+
|
|
601
|
+
## Stop Monitor - Single response has ActionID
|
|
602
|
+
if action['Action'] == 'StopMonitor'
|
|
603
|
+
@action_events.delete(e)
|
|
604
|
+
result << e
|
|
605
|
+
finished = 1
|
|
606
|
+
end
|
|
607
|
+
|
|
608
|
+
## ChangeMonitor - Single response has ActionID
|
|
609
|
+
if action['Action'] == 'ChangeMonitor'
|
|
610
|
+
@action_events.delete(e)
|
|
611
|
+
result << e
|
|
612
|
+
finished = 1
|
|
613
|
+
end
|
|
614
|
+
|
|
615
|
+
## MailboxStatus - Single response has ActionID
|
|
616
|
+
if action['Action'] == 'MailboxStatus'
|
|
617
|
+
@action_events.delete(e)
|
|
618
|
+
result << e
|
|
619
|
+
finished = 1
|
|
620
|
+
end
|
|
621
|
+
|
|
622
|
+
## MailboxCount - Single response has ActionID
|
|
623
|
+
if action['Action'] == 'MailboxCount'
|
|
624
|
+
@action_events.delete(e)
|
|
625
|
+
result << e
|
|
626
|
+
finished = 1
|
|
627
|
+
end
|
|
628
|
+
|
|
629
|
+
## AbsoluteTimeout - Single response has ActionID
|
|
630
|
+
if action['Action'] == 'AbsoluteTimeout'
|
|
631
|
+
@action_events.delete(e)
|
|
632
|
+
result << e
|
|
633
|
+
finished = 1
|
|
634
|
+
end
|
|
635
|
+
|
|
636
|
+
## SIPshowpeer - Single response has ActionID
|
|
637
|
+
if action['Action'] == 'SIPshowpeer'
|
|
638
|
+
@action_events.delete(e)
|
|
639
|
+
result << e
|
|
640
|
+
finished = 1
|
|
641
|
+
end
|
|
642
|
+
|
|
643
|
+
## Logoff - Single response has ActionID
|
|
644
|
+
if action['Action'] == 'Logoff'
|
|
645
|
+
@action_events.delete(e)
|
|
646
|
+
result << e
|
|
647
|
+
finished = 1
|
|
648
|
+
end
|
|
649
|
+
|
|
650
|
+
|
|
651
|
+
## Originate - Single response has ActionID, multiple events generated
|
|
652
|
+
## end event is Hangup or OriginateFailed.
|
|
653
|
+
if action['Action'] == 'Originate'
|
|
654
|
+
if action['Async']
|
|
655
|
+
if action['Action'] == 'Originate' and e['Message'] == 'Originate successfully queued'
|
|
656
|
+
@action_events.delete(e)
|
|
657
|
+
result << e
|
|
658
|
+
finished = 1
|
|
659
|
+
end
|
|
660
|
+
else
|
|
661
|
+
eventfinished =0
|
|
662
|
+
while eventfinished == 0
|
|
663
|
+
@state_events.synchronize do
|
|
664
|
+
@state_events_pending.wait_while {@state_events.empty?}
|
|
665
|
+
@state_events.clone.each do |s|
|
|
666
|
+
if s['Channel'] =~/#{action['Channel']}/ and (s['Event'] == 'Hangup' or s['Event'] == 'OriginateFailed')
|
|
667
|
+
@state_events.delete(s)
|
|
668
|
+
result << s
|
|
669
|
+
eventfinished =1
|
|
670
|
+
finished =1
|
|
671
|
+
elsif s['Channel'] =~/#{action['Channel']}/
|
|
672
|
+
@state_events.delete(s)
|
|
673
|
+
result << s
|
|
674
|
+
end
|
|
675
|
+
end
|
|
676
|
+
end
|
|
677
|
+
end
|
|
678
|
+
end
|
|
679
|
+
end
|
|
680
|
+
|
|
681
|
+
## ParkedCalls - multiple responses has ActionID
|
|
682
|
+
if action['Action'] == 'ParkedCalls'
|
|
683
|
+
if e['Message'] == 'Parked calls will follow'
|
|
684
|
+
@action_events.delete(e)
|
|
685
|
+
elsif e['Event'] == 'ParkedCallsComplete'
|
|
686
|
+
@action_events.delete(e)
|
|
687
|
+
finished =1
|
|
688
|
+
else
|
|
689
|
+
@action_events.delete(e)
|
|
690
|
+
result << e
|
|
691
|
+
end
|
|
692
|
+
end
|
|
693
|
+
|
|
694
|
+
## SIPpeers - multiple responses has ActionID
|
|
695
|
+
if action['Action'] == 'SIPpeers'
|
|
696
|
+
if e['Message'] == 'Peer status list will follow'
|
|
697
|
+
@action_events.delete(e)
|
|
698
|
+
elsif e['Event'] == 'PeerlistComplete'
|
|
699
|
+
@action_events.delete(e)
|
|
700
|
+
finished =1
|
|
701
|
+
elsif e['Event'] == 'PeerEntry'
|
|
702
|
+
@action_events.delete(e)
|
|
703
|
+
result << e
|
|
704
|
+
end
|
|
705
|
+
end
|
|
706
|
+
|
|
707
|
+
## Agents - multiple responses has ActionID
|
|
708
|
+
if action['Action'] == 'Agents'
|
|
709
|
+
if e['Message'] == 'Agents will follow'
|
|
710
|
+
@action_events.delete(e)
|
|
711
|
+
elsif e['Event'] == 'AgentsComplete'
|
|
712
|
+
@action_events.delete(e)
|
|
713
|
+
finished =1
|
|
714
|
+
elsif e['Event'] == 'Agents'
|
|
715
|
+
@action_events.delete(e)
|
|
716
|
+
result << e
|
|
717
|
+
end
|
|
718
|
+
end
|
|
719
|
+
|
|
720
|
+
## Status - multiple responses has ActionID
|
|
721
|
+
if action['Action'] == 'Status'
|
|
722
|
+
if e['Message'] == 'Channel status will follow'
|
|
723
|
+
@action_events.delete(e)
|
|
724
|
+
elsif e['Event'] == 'StatusComplete'
|
|
725
|
+
@action_events.delete(e)
|
|
726
|
+
finished =1
|
|
727
|
+
elsif e['Event'] == 'Status'
|
|
728
|
+
@action_events.delete(e)
|
|
729
|
+
result << e
|
|
730
|
+
end
|
|
731
|
+
end
|
|
732
|
+
|
|
733
|
+
end
|
|
734
|
+
end
|
|
735
|
+
end
|
|
736
|
+
sleep 0.10
|
|
737
|
+
end
|
|
738
|
+
end
|
|
739
|
+
return result
|
|
740
|
+
rescue Exception => e
|
|
741
|
+
puts "#{e}: TIMEOUT #{t} #{sent_id}"
|
|
742
|
+
return result
|
|
743
|
+
end
|
|
744
|
+
|
|
745
|
+
end
|
|
746
|
+
rescue Exception => e
|
|
747
|
+
puts e
|
|
748
|
+
end
|
data/manager.txt
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# Action Privilege Synopsis
|
|
2
|
+
# ------ --------- --------
|
|
3
|
+
# AbsoluteTimeout call,all Set Absolute Timeout
|
|
4
|
+
# AgentCallbackLo agent,all Sets an agent as logged in by callback
|
|
5
|
+
# AgentLogoff agent,all Sets an agent as no longer logged in
|
|
6
|
+
# Agents agent,all Lists agents and their status
|
|
7
|
+
# ChangeMonitor call,all Change monitoring filename of a channel
|
|
8
|
+
# Command command,all Execute Asterisk CLI Command
|
|
9
|
+
# DBGet system,all Get DB Entry
|
|
10
|
+
# DBPut system,all Put DB Entry
|
|
11
|
+
# Events <none> Control Event Flow
|
|
12
|
+
# ExtensionState call,all Check Extension Status
|
|
13
|
+
# Getvar call,all Gets a Channel Variable
|
|
14
|
+
# Hangup call,all Hangup Channel
|
|
15
|
+
# IAXnetstats <none> Show IAX Netstats
|
|
16
|
+
# IAXpeers <none> List IAX Peers
|
|
17
|
+
# ListCommands <none> List available manager commands
|
|
18
|
+
# Logoff <none> Logoff Manager
|
|
19
|
+
# MailboxCount call,all Check Mailbox Message Count
|
|
20
|
+
# MailboxStatus call,all Check Mailbox
|
|
21
|
+
# Monitor call,all Monitor a channel
|
|
22
|
+
# Originate call,all Originate Call
|
|
23
|
+
# ParkedCalls <none> List parked calls
|
|
24
|
+
# Ping <none> Keepalive command
|
|
25
|
+
# QueueAdd agent,all Add interface to queue.
|
|
26
|
+
# QueuePause agent,all Makes a queue member temporarily unavailable
|
|
27
|
+
# QueueRemove agent,all Remove interface from queue.
|
|
28
|
+
# Queues <none> Queues
|
|
29
|
+
# QueueStatus <none> Queue Status
|
|
30
|
+
# Redirect call,all Redirect (transfer) a call
|
|
31
|
+
# SetCDRUserField call,all Set the CDR UserField
|
|
32
|
+
# Setvar call,all Set Channel Variable
|
|
33
|
+
# SIPpeers system,all List SIP peers (text format)
|
|
34
|
+
# SIPshowpeer system,all Show SIP peer (text format)
|
|
35
|
+
# Status call,all Lists channel status
|
|
36
|
+
# StopMonitor call,all Stop monitoring a channel
|
|
37
|
+
# ZapDialOffhook <none> Dial over Zap channel while offhook
|
|
38
|
+
# ZapDNDoff <none> Toggle Zap channel Do Not Disturb status OFF
|
|
39
|
+
# ZapDNDon <none> Toggle Zap channel Do Not Disturb status ON
|
|
40
|
+
# ZapHangup <none> Hangup Zap Channel
|
|
41
|
+
# ZapShowChannels <none> Show status zapata channels
|
|
42
|
+
# ZapTransfer <none> Transfer Zap Channel
|
|
43
|
+
#
|
metadata
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
rubygems_version: 0.8.11
|
|
3
|
+
specification_version: 1
|
|
4
|
+
name: rami
|
|
5
|
+
version: !ruby/object:Gem::Version
|
|
6
|
+
version: "0.1"
|
|
7
|
+
date: 2005-10-17 00:00:00 -07:00
|
|
8
|
+
summary: A proxy server/client api for the Asterisk Manager Interface
|
|
9
|
+
require_paths:
|
|
10
|
+
- lib
|
|
11
|
+
email: chris@paymentonline.com
|
|
12
|
+
homepage:
|
|
13
|
+
rubyforge_project:
|
|
14
|
+
description:
|
|
15
|
+
autorequire: rami
|
|
16
|
+
default_executable:
|
|
17
|
+
bindir: bin
|
|
18
|
+
has_rdoc: true
|
|
19
|
+
required_ruby_version: !ruby/object:Gem::Version::Requirement
|
|
20
|
+
requirements:
|
|
21
|
+
-
|
|
22
|
+
- ">"
|
|
23
|
+
- !ruby/object:Gem::Version
|
|
24
|
+
version: 0.0.0
|
|
25
|
+
version:
|
|
26
|
+
platform: ruby
|
|
27
|
+
signing_key:
|
|
28
|
+
cert_chain:
|
|
29
|
+
authors:
|
|
30
|
+
- Chris Ochs
|
|
31
|
+
files:
|
|
32
|
+
- README
|
|
33
|
+
- LICENSE
|
|
34
|
+
- manager.txt
|
|
35
|
+
- lib/rami.rb
|
|
36
|
+
- bin/server.rb
|
|
37
|
+
- bin/test.rb
|
|
38
|
+
test_files: []
|
|
39
|
+
rdoc_options: []
|
|
40
|
+
extra_rdoc_files: []
|
|
41
|
+
executables: []
|
|
42
|
+
extensions: []
|
|
43
|
+
requirements: []
|
|
44
|
+
dependencies: []
|