rastman 0.1.4
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/MIT-LICENSE +20 -0
- data/README +31 -0
- data/TODO +4 -0
- data/bin/rastman_mkcalls.rb +181 -0
- data/lib/rastman.rb +352 -0
- metadata +58 -0
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2007 Mathieu Lajugie
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
# = Rastman is a Ruby interface for the Asterisk Manager API
|
2
|
+
#
|
3
|
+
# When creating a new Rastman::Manager instance, it connects to an Asterisk server
|
4
|
+
# on the manager port (<tt>5038</tt> by default), sends a login request
|
5
|
+
# and listens to events published by the server. The instance can also
|
6
|
+
# be used to send Manager API requests to the server.
|
7
|
+
#
|
8
|
+
# == Example
|
9
|
+
#
|
10
|
+
# require "rubygems"
|
11
|
+
# require "rastman"
|
12
|
+
#
|
13
|
+
# rastman = Rastman::Manager.new("myuser", "secret")
|
14
|
+
#
|
15
|
+
# rastman.add_event_hook(:event) { |evt| puts "Event received: [#{evt.inspect}]"
|
16
|
+
#
|
17
|
+
# rastman.connect
|
18
|
+
#
|
19
|
+
# rastman.events(:eventmask => "user,call")
|
20
|
+
#
|
21
|
+
# rastman.ping
|
22
|
+
#
|
23
|
+
# rastman.originate :channel => "SIP/myphone@mydomain.com",
|
24
|
+
# :context => "default", :exten => "voicemail",
|
25
|
+
# :priority => 1
|
26
|
+
#
|
27
|
+
# rastman.logoff
|
28
|
+
#
|
29
|
+
#
|
30
|
+
# Created by Mathieul on 2007-02-08.
|
31
|
+
# Copyright (c) 2007. All rights reserved.
|
data/TODO
ADDED
@@ -0,0 +1,181 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# $Id$
|
3
|
+
|
4
|
+
require "optparse"
|
5
|
+
require "ostruct"
|
6
|
+
require "yaml"
|
7
|
+
require "rubygems"
|
8
|
+
require "rastman"
|
9
|
+
|
10
|
+
CMD = File.basename(__FILE__)
|
11
|
+
|
12
|
+
class CmdLine
|
13
|
+
|
14
|
+
def self.parse(args, opts)
|
15
|
+
# set default options
|
16
|
+
opts[:login] ||= "username"
|
17
|
+
opts[:pwd] ||= "secret"
|
18
|
+
opts[:host] ||= "localhost"
|
19
|
+
opts[:recipient] ||= "1234567890@example.com"
|
20
|
+
opts[:context] ||= "default"
|
21
|
+
opts[:exten] ||= "987"
|
22
|
+
opts[:rounds] ||= 1
|
23
|
+
opts[:rounds_delay] ||= 20
|
24
|
+
opts[:calls] ||= 1
|
25
|
+
opts[:calls_delay] ||= 1
|
26
|
+
opts[:view_events] = false if opts[:view_events].nil?
|
27
|
+
opts[:rc] = false
|
28
|
+
|
29
|
+
o = OptionParser.new do |o|
|
30
|
+
o.banner = "Usage: #{CMD} [options]"
|
31
|
+
o.separator ""
|
32
|
+
o.separator "Options:"
|
33
|
+
|
34
|
+
o.on("-l", "--login <login>",
|
35
|
+
String,
|
36
|
+
"Login to use from .../etc/manager.conf (default: #{opts[:login]})") do |login|
|
37
|
+
opts[:login] = login
|
38
|
+
end
|
39
|
+
|
40
|
+
o.on("-p", "--pwd <password>",
|
41
|
+
String,
|
42
|
+
"Password as declared in .../etc/manager.conf (default: #{opts[:pwd]})") do |pwd|
|
43
|
+
opts[:pwd] = pwd
|
44
|
+
end
|
45
|
+
|
46
|
+
o.on("-h", "--host <asterisk host name>",
|
47
|
+
String,
|
48
|
+
"Host name running the Asterisk server (default: #{opts[:host]})") do |host|
|
49
|
+
opts[:host] = host
|
50
|
+
end
|
51
|
+
|
52
|
+
o.on("-r", "--recipient <URI>",
|
53
|
+
String,
|
54
|
+
"Recipient URI (default: #{opts[:recipient]})") do |recipient|
|
55
|
+
opts[:recipient] = recipient
|
56
|
+
end
|
57
|
+
|
58
|
+
o.on("-c", "--context <context>",
|
59
|
+
String,
|
60
|
+
"Dialplan Context to connect the call to (default: #{opts[:context]})") do |context|
|
61
|
+
opts[:context] = context
|
62
|
+
end
|
63
|
+
|
64
|
+
o.on("-e", "--exten <extension>",
|
65
|
+
String,
|
66
|
+
"Dialplan Extension to connect the call to (default: #{opts[:exten]})") do |exten|
|
67
|
+
opts[:exten] = exten
|
68
|
+
end
|
69
|
+
|
70
|
+
o.on("-n", "--num-rounds <number>",
|
71
|
+
Integer,
|
72
|
+
"Number of rounds of call generation to make (default: #{opts[:rounds]})") do |rounds|
|
73
|
+
opts[:rounds] = rounds
|
74
|
+
end
|
75
|
+
|
76
|
+
o.on("-d", "--rounds-delay <delay>",
|
77
|
+
Float,
|
78
|
+
"Number of seconds between two rounds (default: #{opts[:rounds_delay]})") do |delay|
|
79
|
+
opts[:rounds_delay] = delay
|
80
|
+
end
|
81
|
+
|
82
|
+
o.on("-a", "--num-calls <number>",
|
83
|
+
Integer,
|
84
|
+
"Number of rounds of call generation to make (default: #{opts[:calls]})") do |calls|
|
85
|
+
opts[:calls] = calls
|
86
|
+
end
|
87
|
+
|
88
|
+
o.on("-t", "--calls-delay <delay>",
|
89
|
+
Float,
|
90
|
+
"Number of seconds between two calls (default: #{opts[:calls_delay]})") do |delay|
|
91
|
+
opts[:calls_delay] = delay
|
92
|
+
end
|
93
|
+
|
94
|
+
o.on("-v", "--view-events",
|
95
|
+
"View events generated by Asterisk (default: #{opts[:view_events]})") do |view_events|
|
96
|
+
opts[:view_events] = view_events
|
97
|
+
end
|
98
|
+
|
99
|
+
o.on("-m", "--[no-]make-rc",
|
100
|
+
"Save options in $HOME/.rastman_mkcallsrc instead of generating calls") do |rc|
|
101
|
+
opts[:rc] = rc
|
102
|
+
end
|
103
|
+
|
104
|
+
o.on_tail("-?", "--help", "Show this message") do
|
105
|
+
puts o
|
106
|
+
exit
|
107
|
+
end
|
108
|
+
|
109
|
+
o.parse!(args)
|
110
|
+
end
|
111
|
+
opts
|
112
|
+
end
|
113
|
+
|
114
|
+
end
|
115
|
+
|
116
|
+
# retrieve default parameters if resource file exists
|
117
|
+
rc_options, rc_file = {}, File.join(ENV["HOME"], ".rastman_mkcallsrc")
|
118
|
+
rc_options = YAML::load(File.read(rc_file)) if File.exists?(rc_file)
|
119
|
+
|
120
|
+
# parse command line
|
121
|
+
opts = CmdLine.parse(ARGV, rc_options)
|
122
|
+
|
123
|
+
# save resource file if asked for
|
124
|
+
if opts[:rc] == true
|
125
|
+
opts.delete(:rc)
|
126
|
+
File.open(rc_file, "w") { |f| f.write(YAML::dump(opts)) }
|
127
|
+
puts "#{CMD}: options saved in #{rc_file}."
|
128
|
+
exit
|
129
|
+
end
|
130
|
+
|
131
|
+
cmdr = Rastman::Manager.new(opts[:login], opts[:pwd], :host => opts[:host],
|
132
|
+
:connect => true, :eventmask => "off")
|
133
|
+
|
134
|
+
lsnr = nil
|
135
|
+
if opts[:view_events] == true
|
136
|
+
require 'pp'
|
137
|
+
lsnr = Rastman::Manager.new(opts[:login], opts[:pwd], :host => opts[:host])
|
138
|
+
lsnr.add_event_hook(:event) { |evt| pp evt; puts }
|
139
|
+
lsnr.connect
|
140
|
+
end
|
141
|
+
|
142
|
+
Signal.trap("INT") do
|
143
|
+
puts "#{CMD}: Logging off. Please wait for Asterisk to complete the logoff request."
|
144
|
+
cmdr.logoff
|
145
|
+
lsnr.logoff unless lsnr.nil?
|
146
|
+
Signal.trap("INT", "DEFAULT")
|
147
|
+
end
|
148
|
+
|
149
|
+
to = (opts[:recipient].include?("/") ? "": "SIP/") + opts[:recipient]
|
150
|
+
|
151
|
+
puts "Starting #{opts[:rounds]} rounds of #{opts[:calls]} calls."
|
152
|
+
puts "Call delay is #{opts[:calls_delay]} secs."
|
153
|
+
puts "Round delay is #{opts[:rounds_delay]} secs."
|
154
|
+
puts
|
155
|
+
|
156
|
+
# round loop
|
157
|
+
(1..opts[:rounds]).each do |round_number|
|
158
|
+
puts "Round ##{round_number}: #{opts[:calls]} calls"
|
159
|
+
caller_id_prefix = "#{Time.now.strftime "1%M%S"}"
|
160
|
+
# calls loop
|
161
|
+
(1..opts[:calls]).each do |call_number|
|
162
|
+
puts " Call ##{call_number}"
|
163
|
+
cmdr.originate(:channel => to,
|
164
|
+
:context => opts[:context],
|
165
|
+
:exten => opts[:exten],
|
166
|
+
:priority => 1,
|
167
|
+
:callerid => %|#{caller_id_prefix}#{"%05d" % call_number}|)
|
168
|
+
sleep(opts[:calls_delay])
|
169
|
+
end
|
170
|
+
sleep(opts[:rounds_delay]) unless round_number == opts[:rounds]
|
171
|
+
end
|
172
|
+
|
173
|
+
if lsnr.nil?
|
174
|
+
cmdr.logoff
|
175
|
+
else
|
176
|
+
sleep 0.1 while cmdr.connected?
|
177
|
+
end
|
178
|
+
|
179
|
+
#
|
180
|
+
# Created by Mathieul on 2007-02-08.
|
181
|
+
# Copyright (c) 2007. All rights reserved.
|
data/lib/rastman.rb
ADDED
@@ -0,0 +1,352 @@
|
|
1
|
+
# $Id$
|
2
|
+
# RASTMAN - Ruby Asterisk Manager api
|
3
|
+
#
|
4
|
+
|
5
|
+
require 'monitor'
|
6
|
+
require 'socket'
|
7
|
+
require 'timeout'
|
8
|
+
require 'logger'
|
9
|
+
|
10
|
+
$rnlog = Logger.new(STDOUT)
|
11
|
+
$rnlog.level = Logger::ERROR
|
12
|
+
|
13
|
+
# The Rastman module contains the Rastman::Manager class that is used to
|
14
|
+
# connect to an Asterisk server to send requests and listen to events.
|
15
|
+
module Rastman
|
16
|
+
|
17
|
+
# Generic Rastman error
|
18
|
+
class RastmanError < StandardError; end
|
19
|
+
# Rastman Login error
|
20
|
+
class LoginError < RastmanError; end
|
21
|
+
# Rastman Connection error
|
22
|
+
class NotConnectedError < RastmanError; end
|
23
|
+
# Rastman Deconnection error
|
24
|
+
class DisconnectedError < RastmanError; end
|
25
|
+
|
26
|
+
LINE_SEPARATOR = "\r\n"
|
27
|
+
|
28
|
+
class << self
|
29
|
+
|
30
|
+
# Change the log level (default: Logger::ERROR).
|
31
|
+
# Can be set to any of the following:
|
32
|
+
# * Logger::DEBUG
|
33
|
+
# * Logger::INFO
|
34
|
+
# * Logger::WARN
|
35
|
+
# * Logger::ERROR
|
36
|
+
# * Logger::FATAL
|
37
|
+
# * Logger::ANY
|
38
|
+
def log_level=(level)
|
39
|
+
$rnlog.level = level
|
40
|
+
end
|
41
|
+
|
42
|
+
# Return the current log level.
|
43
|
+
def log_level
|
44
|
+
$rnlog.level
|
45
|
+
end
|
46
|
+
|
47
|
+
# Change it for a new one
|
48
|
+
def set_logger(new_logger)
|
49
|
+
$rnlog = new_logger
|
50
|
+
$rnlog.info('Rastman#set_logger: done')
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
54
|
+
|
55
|
+
module Parser
|
56
|
+
|
57
|
+
def parse_line(line) # :nodoc:
|
58
|
+
raise LocalJumpError, "no block given" unless block_given?
|
59
|
+
@buffer ||= ""
|
60
|
+
@buffer += line
|
61
|
+
# if the line doesn't end with CRLF, we store it for the next call
|
62
|
+
if /(^[\w\s\/-]*):[\s]*(.*)\r\n$/m =~ @buffer
|
63
|
+
yield($1.downcase.to_sym, $2)
|
64
|
+
@buffer = ""
|
65
|
+
else
|
66
|
+
if @buffer[-2..-1] == "\r\n"
|
67
|
+
yield(:unknown, "UNKNOWN")
|
68
|
+
@buffer = ""
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
end
|
74
|
+
|
75
|
+
# A connection with an Asterisk server is established through a
|
76
|
+
# Rastman::Manager object. This object can then be used to
|
77
|
+
# send Asterisk Manager requests and listens to Asterisk events.
|
78
|
+
#
|
79
|
+
# Note that events generated from the result of a request won't be sent
|
80
|
+
# immediately back by Asterisk on the same connection.
|
81
|
+
# So you should probably use one object to send the requests,
|
82
|
+
# and one object to listen to the events.
|
83
|
+
class Manager
|
84
|
+
include Parser
|
85
|
+
|
86
|
+
attr_reader :host, :login
|
87
|
+
|
88
|
+
COMMANDS = [:login, :logoff, :events, :originate, :originate!, :redirect,
|
89
|
+
:redirect!, :hangup, :hangup!, :ping, :ping!, :setvar, :setvar!,
|
90
|
+
:command, :command!]
|
91
|
+
WAIT_FOR_ANSWER = 0.1
|
92
|
+
|
93
|
+
# Returns a Rastman::Manager object. The login and pwd parameters
|
94
|
+
# are the username and secret attributes of the user configured
|
95
|
+
# in /path/to/asterisk/configuration/manager.conf.
|
96
|
+
#
|
97
|
+
# The following options are also accepted:
|
98
|
+
# * <tt>:port</tt>: Manager port to connect to (<i>default: 5038</i>),
|
99
|
+
# * <tt>:host</tt>: Host name where Asterisk is running (<i>default: localhost</i>),
|
100
|
+
# * <tt>:reconnect_timeout</tt>: Timeout between two reconnect attempts (<i>default: 1</i>),
|
101
|
+
# * <tt>:connect</tt>: Flag to request a connection right away (<i>default: false</i>).
|
102
|
+
# * <tt>:eventmask</tt>: Manager event mask (i.e.: "user,call") (<i>default: on</i>).
|
103
|
+
def initialize(login, pwd, opts = {})
|
104
|
+
@login, @pwd = login, pwd
|
105
|
+
@port = opts[:port] || 5038
|
106
|
+
@host = opts[:host] || 'localhost'
|
107
|
+
@version = opts[:version]
|
108
|
+
@eventmask = opts[:eventmask] || 'on'
|
109
|
+
@reconnect_timeout = opts[:reconnect_timeout] || 1
|
110
|
+
@connected, @hooks = false, {}
|
111
|
+
@conn_lock = nil
|
112
|
+
@conn_lock.extend(MonitorMixin)
|
113
|
+
add_event_hook(:reconnect) { event_reconnect }
|
114
|
+
connect if opts[:connect]
|
115
|
+
end
|
116
|
+
|
117
|
+
# Connects to the server, attempt to login, and start listening to
|
118
|
+
# events if successfull.
|
119
|
+
#
|
120
|
+
# If +should_reconnect+ is true, then the object will attempt to reconnect
|
121
|
+
# until successful (the reconnection timeout is configured in new).
|
122
|
+
def connect(should_reconnect = true)
|
123
|
+
@conn = TCPSocket.new(@host, @port)
|
124
|
+
login
|
125
|
+
@should_reconnect = should_reconnect
|
126
|
+
@listener = Listener.new(@conn, @hooks, :version => @version)
|
127
|
+
@listener.start
|
128
|
+
end
|
129
|
+
|
130
|
+
# Returns true if the object is currently connected to the server.
|
131
|
+
def connected?
|
132
|
+
$rnlog.debug("Rastman::Manager#connected?: @connected = #{@connected}")
|
133
|
+
@connected == true
|
134
|
+
end
|
135
|
+
|
136
|
+
# Disconnects from the server (without sending the _LogOff_ request).
|
137
|
+
def disconnect
|
138
|
+
raise NotConnectedError, "No active connection" if @conn.closed?
|
139
|
+
@should_reconnect = false
|
140
|
+
@conn.close
|
141
|
+
$rnlog.debug("Rastman::Manager#disconnect: closed connection")
|
142
|
+
end
|
143
|
+
|
144
|
+
# Adds (or replaces) a block to be executed when the specified event occurs:
|
145
|
+
# * <tt>:event</tt>: an event was received
|
146
|
+
# * <tt>:action</tt>: a response to an action was received
|
147
|
+
# * <tt>:reconnect</tt>: the object got reconnected to the server
|
148
|
+
# * <tt>:disconnect</tt>: the object got disconnected from the server
|
149
|
+
def add_event_hook(event, &block)
|
150
|
+
check_supported_event(event)
|
151
|
+
@hooks[event] = block
|
152
|
+
end
|
153
|
+
|
154
|
+
# Delete the block that was to be executed for the specified event.
|
155
|
+
def del_event_hook(event)
|
156
|
+
check_supported_event(event)
|
157
|
+
@hooks.delete(event)
|
158
|
+
end
|
159
|
+
|
160
|
+
def join
|
161
|
+
@listener.join
|
162
|
+
end
|
163
|
+
|
164
|
+
private
|
165
|
+
def check_supported_event(event)
|
166
|
+
unless [:event, :action, :disconnect, :reconnect].include?(event)
|
167
|
+
raise ArgumentError, "Unsupported event #{event}"
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
def event_reconnect #:nodoc:
|
172
|
+
@connected = false
|
173
|
+
$rnlog.debug("Rastman::Manager#event_reconnect: should_reconnect(#{@should_reconnect})")
|
174
|
+
@hooks[:disconnect].call({:event => :disconnect}) unless @hooks[:disconnect].nil?
|
175
|
+
if @should_reconnect == true
|
176
|
+
while @connected == false
|
177
|
+
sleep @reconnect_timeout
|
178
|
+
connect rescue nil
|
179
|
+
end
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
def login # :nodoc:
|
184
|
+
send_action({ :action => 'login', :username => @login, :secret => @pwd, :events => @eventmask })
|
185
|
+
wait_for_login_acknowledgement
|
186
|
+
end
|
187
|
+
|
188
|
+
def wait_for_login_acknowledgement # :nodoc:
|
189
|
+
Timeout.timeout(10) do
|
190
|
+
event, done = {}, false
|
191
|
+
until done == true do
|
192
|
+
@conn.each(LINE_SEPARATOR) do |line|
|
193
|
+
if line == LINE_SEPARATOR
|
194
|
+
done = true
|
195
|
+
break
|
196
|
+
else
|
197
|
+
parse_line(line) { |key, value| event[key] = value }
|
198
|
+
end
|
199
|
+
done = true if @conn.closed?
|
200
|
+
end
|
201
|
+
if event[:response] == "Success"
|
202
|
+
@connected = true
|
203
|
+
else
|
204
|
+
raise LoginError, event[:message]
|
205
|
+
end
|
206
|
+
$rnlog.debug("Rastman::Manager#wait_for_login_acknowledgement: @connected(#{@connected}) event(#{event.inspect})")
|
207
|
+
end
|
208
|
+
end
|
209
|
+
rescue Timeout::Error
|
210
|
+
raise NotConnectedError, "No answer received for login request"
|
211
|
+
end
|
212
|
+
|
213
|
+
# send_action actually send the action requested to Asterisk.
|
214
|
+
# If expects_answer_before is NOT set (default), it sends the
|
215
|
+
# request and returns nil right away.
|
216
|
+
# If expects_answer_before is set, it sends the request, waits
|
217
|
+
# for expects_answer_before seconds or until the response is received
|
218
|
+
# (whichever comes first) and returns true if the response if received,
|
219
|
+
# false if not.
|
220
|
+
def send_action(action, expects_answer_before = nil) # :nodoc:
|
221
|
+
$rnlog.debug("Rastman::Manager#send_action: SEND '#{action[:action]}' (#{action.inspect})")
|
222
|
+
unless expects_answer_before.nil?
|
223
|
+
action_id = action[:actionid] ||= "#{action[:action]}-#{action.hash}"
|
224
|
+
answer = []
|
225
|
+
@listener.request_answer_for(action_id, answer)
|
226
|
+
end
|
227
|
+
@conn_lock.synchronize do
|
228
|
+
action.each do |key, value|
|
229
|
+
$rnlog.debug("Rastman::Manager#send_action: write (#{key.to_s}: #{value}\\r\\n)")
|
230
|
+
@conn.write("#{key.to_s}: #{value}\r\n")
|
231
|
+
end
|
232
|
+
$rnlog.debug("Rastman::Manager#send_action: write (\\r\\n)")
|
233
|
+
@conn.write("\r\n")
|
234
|
+
end
|
235
|
+
result = nil
|
236
|
+
unless expects_answer_before.nil?
|
237
|
+
waited = 0
|
238
|
+
until answer.length > 0
|
239
|
+
sleep WAIT_FOR_ANSWER
|
240
|
+
waited += WAIT_FOR_ANSWER
|
241
|
+
if waited >= expects_answer_before
|
242
|
+
answer << {}
|
243
|
+
end
|
244
|
+
end
|
245
|
+
result = answer.first[:response] == "Success" ? true : false rescue false
|
246
|
+
end
|
247
|
+
return result
|
248
|
+
rescue Exception => ex
|
249
|
+
$rnlog.info("Rastman::Manager#send_action: exception caught (connection probably closed already): #{ex}")
|
250
|
+
end
|
251
|
+
|
252
|
+
def method_missing(meth, *args, &block) # :nodoc:
|
253
|
+
unless COMMANDS.include?(meth)
|
254
|
+
raise NoMethodError.new(
|
255
|
+
"undefined method `#{meth}' for " +
|
256
|
+
"#{self.inspect}:#{self.class.name}"
|
257
|
+
)
|
258
|
+
end
|
259
|
+
command = meth.to_s.downcase
|
260
|
+
if command[-1] == ?!
|
261
|
+
case args.size
|
262
|
+
when 1
|
263
|
+
expects_answer_before = 10
|
264
|
+
action = args.first
|
265
|
+
when 2
|
266
|
+
expects_answer_before, action = args
|
267
|
+
else
|
268
|
+
raise ArgumentError, "#{meth} wrong number of arguments (#{args.size} for 1 or 2)"
|
269
|
+
end
|
270
|
+
command.chop!
|
271
|
+
else
|
272
|
+
action = args.size == 0 ? {} : args[0]
|
273
|
+
expects_answer_before = nil
|
274
|
+
end
|
275
|
+
action.merge!({ :action => command })
|
276
|
+
@should_reconnect = false if command == "logoff"
|
277
|
+
send_action(action, expects_answer_before)
|
278
|
+
end
|
279
|
+
|
280
|
+
end
|
281
|
+
|
282
|
+
class Listener # :nodoc: all
|
283
|
+
include Parser
|
284
|
+
|
285
|
+
def initialize(conn, hooks, opts = {})
|
286
|
+
@conn, @hooks = conn, hooks
|
287
|
+
@action_ids = {}
|
288
|
+
@mode_1_0 = opts[:version] == "1.0"
|
289
|
+
end
|
290
|
+
|
291
|
+
def start
|
292
|
+
@th = Thread.new do
|
293
|
+
Thread.current.abort_on_exception = true
|
294
|
+
begin
|
295
|
+
event = {}
|
296
|
+
@conn.each(LINE_SEPARATOR) do |line|
|
297
|
+
$rnlog.debug("Rastman::Listener#start: read (#{line.strip})")
|
298
|
+
if line == LINE_SEPARATOR
|
299
|
+
if event[:event].nil?
|
300
|
+
@hooks[:action].call(event.clone) unless @hooks[:action].nil?
|
301
|
+
unless event[:actionid].nil?
|
302
|
+
container = @action_ids.delete(event[:actionid])
|
303
|
+
container << event.clone unless container.nil?
|
304
|
+
end
|
305
|
+
else
|
306
|
+
reformat_userevent(event) if @mode_1_0
|
307
|
+
@hooks[:event].call(event.clone) unless @hooks[:event].nil?
|
308
|
+
end
|
309
|
+
event.clear
|
310
|
+
else
|
311
|
+
parse_line(line) { |key, value| event[key] = value }
|
312
|
+
end
|
313
|
+
end
|
314
|
+
rescue IOError => e
|
315
|
+
$rnlog.warn("Rastman::Listener#start: IOError (#{e}) occured => we close the connection")
|
316
|
+
@conn.close rescue nil
|
317
|
+
end
|
318
|
+
$rnlog.info("Rastman::Listener#start: calling reconnect hook before stopping")
|
319
|
+
@hooks[:reconnect].call({:event => :reconnect}) unless @hooks[:reconnect].nil?
|
320
|
+
end
|
321
|
+
end
|
322
|
+
|
323
|
+
def join
|
324
|
+
@th.join
|
325
|
+
end
|
326
|
+
|
327
|
+
def request_answer_for(id, container)
|
328
|
+
@action_ids[id] = container
|
329
|
+
end
|
330
|
+
|
331
|
+
private
|
332
|
+
def reformat_userevent(evt)
|
333
|
+
name = evt[:event].split("UserEvent")[1]
|
334
|
+
unless name.nil?
|
335
|
+
evt[:userevent] = name
|
336
|
+
evt[:event] = "UserEvent"
|
337
|
+
vars = evt.delete(:variables)
|
338
|
+
vars.split("|").each do |pair|
|
339
|
+
k, v = pair.split(/: */)
|
340
|
+
evt[k.downcase.to_sym] = v || ""
|
341
|
+
end unless vars.nil?
|
342
|
+
evt.delete(:channel)
|
343
|
+
end
|
344
|
+
end
|
345
|
+
|
346
|
+
end
|
347
|
+
|
348
|
+
end
|
349
|
+
|
350
|
+
#
|
351
|
+
# Created by Mathieul on 2007-02-08.
|
352
|
+
# Copyright (c) 2007. All rights reserved.
|
metadata
ADDED
@@ -0,0 +1,58 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
rubygems_version: 0.9.2
|
3
|
+
specification_version: 1
|
4
|
+
name: rastman
|
5
|
+
version: !ruby/object:Gem::Version
|
6
|
+
version: 0.1.4
|
7
|
+
date: 2007-06-28 00:00:00 -07:00
|
8
|
+
summary: Asterisk Manager API interface for Ruby
|
9
|
+
require_paths:
|
10
|
+
- lib
|
11
|
+
email: mathieul@zlaj.org
|
12
|
+
homepage: http://rastman.rubyforge.org/
|
13
|
+
rubyforge_project:
|
14
|
+
description: Asterisk Manager API interface for Ruby
|
15
|
+
autorequire:
|
16
|
+
default_executable:
|
17
|
+
bindir: bin
|
18
|
+
has_rdoc: true
|
19
|
+
required_ruby_version: !ruby/object:Gem::Version::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">="
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: 1.8.4
|
24
|
+
version:
|
25
|
+
platform: ruby
|
26
|
+
signing_key:
|
27
|
+
cert_chain:
|
28
|
+
post_install_message:
|
29
|
+
authors:
|
30
|
+
- Mathieu Lajugie
|
31
|
+
files:
|
32
|
+
- MIT-LICENSE
|
33
|
+
- README
|
34
|
+
- TODO
|
35
|
+
- bin/rastman_mkcalls.rb
|
36
|
+
- lib/rastman.rb
|
37
|
+
test_files: []
|
38
|
+
|
39
|
+
rdoc_options:
|
40
|
+
- --quiet
|
41
|
+
- --title
|
42
|
+
- Rastman Documentation
|
43
|
+
- --opname
|
44
|
+
- index.html
|
45
|
+
- --line-numbers
|
46
|
+
- --main
|
47
|
+
- README
|
48
|
+
- --inline-source
|
49
|
+
extra_rdoc_files:
|
50
|
+
- README
|
51
|
+
executables:
|
52
|
+
- rastman_mkcalls.rb
|
53
|
+
extensions: []
|
54
|
+
|
55
|
+
requirements: []
|
56
|
+
|
57
|
+
dependencies: []
|
58
|
+
|