rastman 0.1.4
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
|