asterisk-manager 0.1.0
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/.document +5 -0
- data/.rspec +1 -0
- data/Gemfile +8 -0
- data/Gemfile.lock +31 -0
- data/LICENSE.txt +20 -0
- data/README.md +81 -0
- data/Rakefile +49 -0
- data/VERSION +1 -0
- data/lib/asterisk-manager.rb +7 -0
- data/lib/asterisk-manager/call.rb +33 -0
- data/lib/asterisk-manager/call_event_observer.rb +28 -0
- data/lib/asterisk-manager/channel.rb +55 -0
- data/lib/asterisk-manager/channel_event_observer.rb +37 -0
- data/lib/asterisk-manager/channel_event_poller.rb +49 -0
- data/lib/asterisk-manager/conference.rb +30 -0
- data/lib/asterisk-manager/connection.rb +43 -0
- data/lib/asterisk-manager/event.rb +17 -0
- data/lib/asterisk-manager/event_listener.rb +51 -0
- data/script/curses_display +83 -0
- data/script/event_stream +31 -0
- data/spec/asterisk-manager_spec.rb +7 -0
- data/spec/spec_helper.rb +12 -0
- metadata +138 -0
data/.document
ADDED
data/.rspec
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--color
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
GEM
|
2
|
+
remote: http://rubygems.org/
|
3
|
+
specs:
|
4
|
+
diff-lcs (1.2.4)
|
5
|
+
git (1.2.5)
|
6
|
+
jeweler (1.8.4)
|
7
|
+
bundler (~> 1.0)
|
8
|
+
git (>= 1.2.5)
|
9
|
+
rake
|
10
|
+
rdoc
|
11
|
+
json (1.7.7)
|
12
|
+
rake (10.0.4)
|
13
|
+
rdoc (4.0.1)
|
14
|
+
json (~> 1.4)
|
15
|
+
rspec (2.13.0)
|
16
|
+
rspec-core (~> 2.13.0)
|
17
|
+
rspec-expectations (~> 2.13.0)
|
18
|
+
rspec-mocks (~> 2.13.0)
|
19
|
+
rspec-core (2.13.1)
|
20
|
+
rspec-expectations (2.13.0)
|
21
|
+
diff-lcs (>= 1.1.3, < 2.0)
|
22
|
+
rspec-mocks (2.13.1)
|
23
|
+
|
24
|
+
PLATFORMS
|
25
|
+
ruby
|
26
|
+
|
27
|
+
DEPENDENCIES
|
28
|
+
bundler
|
29
|
+
jeweler
|
30
|
+
rdoc
|
31
|
+
rspec
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2013 John Wulff
|
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.md
ADDED
@@ -0,0 +1,81 @@
|
|
1
|
+
asterisk-manager
|
2
|
+
================
|
3
|
+
Ruby Gem for connecting to an Asterisk server via the AMI (Asterisk Manager
|
4
|
+
Interface) protocol.
|
5
|
+
|
6
|
+
|
7
|
+
Events
|
8
|
+
------
|
9
|
+
Create a connection and login
|
10
|
+
|
11
|
+
connection = AsteriskManager::Connection.new host: 'asterisk.example.com',
|
12
|
+
port: 5038,
|
13
|
+
username: 'admin',
|
14
|
+
password: 'secret'
|
15
|
+
connection.login
|
16
|
+
|
17
|
+
Setup an EventListener
|
18
|
+
|
19
|
+
event_listener = EventListener.new connection: connection
|
20
|
+
event_listener.listen
|
21
|
+
|
22
|
+
Setup a ChannelObserver and have it subscribe to events via the EventListener
|
23
|
+
|
24
|
+
channel_observer = ChannelObserver.new
|
25
|
+
channel_observer.subscribe event_listener
|
26
|
+
|
27
|
+
Setup a CallObserver and have it subscribe to events via the EventListener
|
28
|
+
|
29
|
+
call_observer = CallObserver.new
|
30
|
+
call_observer.subscribe event_listener
|
31
|
+
|
32
|
+
Now make a call and take a look
|
33
|
+
|
34
|
+
channel_observer.channels.inspect
|
35
|
+
{
|
36
|
+
"1367101866.1995" => #<AsteriskManager::Channel @unique_id="1367101866.1995",
|
37
|
+
@sip_id="SIP/501-0000078b",
|
38
|
+
@state="Up",
|
39
|
+
@created_at=2013-04-27 15:31:07 -0700>,
|
40
|
+
"1367101866.1996" => #<AsteriskManager::Channel @unique_id="1367101866.1996",
|
41
|
+
@sip_id="SIP/203-0000078c",
|
42
|
+
@state="Up",
|
43
|
+
@created_at=2013-04-27 15:31:07 -0700>
|
44
|
+
}
|
45
|
+
|
46
|
+
call_observer.calls.inspect
|
47
|
+
{
|
48
|
+
[ "1367101866.1995", "1367101866.1996" ] => #<AsteriskManager::Call
|
49
|
+
@channel_1=#<AsteriskManager::Channel @unique_id="1367101866.1995",
|
50
|
+
@sip_id="SIP/501-0000078b",
|
51
|
+
@state=nil,
|
52
|
+
@created_at=2013-04-27 15:31:07 -0700>,
|
53
|
+
@channel_2=#<AsteriskManager::Channel @unique_id="1367101866.1996",
|
54
|
+
@sip_id="SIP/203-0000078c",
|
55
|
+
@state=nil,
|
56
|
+
@created_at=2013-04-27 15:31:07 -0700>,
|
57
|
+
@created_at=2013-04-27 15:31:07 -0700>
|
58
|
+
}
|
59
|
+
|
60
|
+
Scripts
|
61
|
+
-------
|
62
|
+
The `curses_display` script opens a connection to an Asterisk server, sets up a
|
63
|
+
ChannelObserver and a CallObserver that subscribe to events via an
|
64
|
+
EventListener. The observers maintain lists of current Channels and Calls. A
|
65
|
+
curses loop renders a pretty list of these Channels and Calls.
|
66
|
+
|
67
|
+
`script/curses_display asterisk.example.com 5038 admin secret`
|
68
|
+
|
69
|
+
----------------------------------------------Channels----------------------------------------------
|
70
|
+
Unique ID SIP ID State Age
|
71
|
+
----------------------------------------------------------------------------------------------------
|
72
|
+
1367100883.1993 SIP/501-00000789 Up 6.372375
|
73
|
+
1367100883.1994 SIP/203-0000078a Up 6.356668
|
74
|
+
-----------------------------------------------Calls------------------------------------------------
|
75
|
+
Channel 1 Unique ID Channel 1 SIP ID Channel 2 Unique ID Channel 2 SIP ID Age
|
76
|
+
----------------------------------------------------------------------------------------------------
|
77
|
+
1367100883.1993 SIP/501-00000789 1367100883.1994 SIP/203-0000078a 6.140765
|
78
|
+
|
79
|
+
Copyright
|
80
|
+
---------
|
81
|
+
Copyright (c) 2013 John Wulff. See LICENSE.txt for further details.
|
data/Rakefile
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'bundler'
|
5
|
+
begin
|
6
|
+
Bundler.setup(:default, :development)
|
7
|
+
rescue Bundler::BundlerError => e
|
8
|
+
$stderr.puts e.message
|
9
|
+
$stderr.puts "Run `bundle install` to install missing gems"
|
10
|
+
exit e.status_code
|
11
|
+
end
|
12
|
+
require 'rake'
|
13
|
+
|
14
|
+
require 'jeweler'
|
15
|
+
Jeweler::Tasks.new do |gem|
|
16
|
+
# gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
|
17
|
+
gem.name = "asterisk-manager"
|
18
|
+
gem.homepage = "http://github.com/jwulff/asterisk-manager"
|
19
|
+
gem.license = "MIT"
|
20
|
+
gem.summary = "Ruby Gem for connecting to an Asterisk server via the AMI (Asterisk Manager Interface) protocol."
|
21
|
+
gem.description = "Ruby Gem for connecting to an Asterisk server via the AMI (Asterisk Manager Interface) protocol."
|
22
|
+
gem.email = "johnw@orcasnet.com"
|
23
|
+
gem.authors = ["John Wulff"]
|
24
|
+
# dependencies defined in Gemfile
|
25
|
+
end
|
26
|
+
Jeweler::RubygemsDotOrgTasks.new
|
27
|
+
|
28
|
+
require 'rspec/core'
|
29
|
+
require 'rspec/core/rake_task'
|
30
|
+
RSpec::Core::RakeTask.new(:spec) do |spec|
|
31
|
+
spec.pattern = FileList['spec/**/*_spec.rb']
|
32
|
+
end
|
33
|
+
|
34
|
+
RSpec::Core::RakeTask.new(:rcov) do |spec|
|
35
|
+
spec.pattern = 'spec/**/*_spec.rb'
|
36
|
+
spec.rcov = true
|
37
|
+
end
|
38
|
+
|
39
|
+
task :default => :spec
|
40
|
+
|
41
|
+
require 'rdoc/task'
|
42
|
+
Rake::RDocTask.new do |rdoc|
|
43
|
+
version = File.exist?('VERSION') ? File.read('VERSION') : ""
|
44
|
+
|
45
|
+
rdoc.rdoc_dir = 'rdoc'
|
46
|
+
rdoc.title = "asterisk-manager #{version}"
|
47
|
+
rdoc.rdoc_files.include('README*')
|
48
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
49
|
+
end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.1.0
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module AsteriskManager
|
2
|
+
class Call
|
3
|
+
attr_accessor :channel_1,
|
4
|
+
:channel_2,
|
5
|
+
:created_at
|
6
|
+
|
7
|
+
def initialize(arguments = {})
|
8
|
+
self.channel_1 = arguments[:channel_1]
|
9
|
+
self.channel_2 = arguments[:channel_2]
|
10
|
+
self.created_at = Time.now
|
11
|
+
end
|
12
|
+
|
13
|
+
def seconds
|
14
|
+
Time.now - created_at
|
15
|
+
end
|
16
|
+
|
17
|
+
def duration
|
18
|
+
x = seconds.round
|
19
|
+
hours = x / 3600
|
20
|
+
minutes = (x - hours * 3600) / 60
|
21
|
+
seconds = (x - hours * 3600 - minutes * 60)
|
22
|
+
"#{hours}:#{'%02d' % minutes}:#{'%02d' % seconds}"
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.calls
|
26
|
+
@calls ||= {}
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.for_channel_1_and_channel_2(channel_1, channel_2)
|
30
|
+
calls[ [ channel_1, channel_2 ] ] ||= new(channel_1: channel_1, channel_2: channel_2)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module AsteriskManager
|
2
|
+
class CallEventObserver
|
3
|
+
def subscribe(event_listener)
|
4
|
+
event_listener.subscribe self, 'Bridge', 'Unlink'
|
5
|
+
end
|
6
|
+
|
7
|
+
def receive_event(event)
|
8
|
+
case event.type
|
9
|
+
when 'Bridge'
|
10
|
+
bridge(event)
|
11
|
+
when 'Unlink'
|
12
|
+
unlink(event)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def bridge(event)
|
17
|
+
channel_1 = Channel.for_unique_id(event['Uniqueid1'])
|
18
|
+
channel_2 = Channel.for_unique_id(event['Uniqueid2'])
|
19
|
+
channel_1.sip_id = event['Channel1']
|
20
|
+
channel_2.sip_id = event['Channel2']
|
21
|
+
Call.for_channel_1_and_channel_2 channel_1, channel_2
|
22
|
+
end
|
23
|
+
|
24
|
+
def unlink(event)
|
25
|
+
Call.calls.delete([ Channel.for_unique_id(event['Uniqueid1']), Channel.for_unique_id(event['Uniqueid2']) ])
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
module AsteriskManager
|
2
|
+
class Channel
|
3
|
+
attr_accessor :unique_id,
|
4
|
+
:sip_id,
|
5
|
+
:state,
|
6
|
+
:caller_id_number,
|
7
|
+
:caller_id_name,
|
8
|
+
:application_name,
|
9
|
+
:application_data,
|
10
|
+
:created_at
|
11
|
+
|
12
|
+
def initialize(arguments = {})
|
13
|
+
self.unique_id = arguments[:unique_id]
|
14
|
+
self.sip_id = arguments[:sip_id]
|
15
|
+
self.state = arguments[:state]
|
16
|
+
self.caller_id_number = arguments[:caller_id_number]
|
17
|
+
self.caller_id_name = arguments[:caller_id_name]
|
18
|
+
self.application_name = arguments[:application_name]
|
19
|
+
self.application_data = arguments[:application_data]
|
20
|
+
self.created_at = Time.now
|
21
|
+
end
|
22
|
+
|
23
|
+
def application
|
24
|
+
"#{application_name}(#{application_data})" if application_name.to_s.size > 0
|
25
|
+
end
|
26
|
+
|
27
|
+
def caller_id
|
28
|
+
"#{caller_id_number} #{caller_id_name}".strip
|
29
|
+
end
|
30
|
+
|
31
|
+
def seconds
|
32
|
+
Time.now - created_at
|
33
|
+
end
|
34
|
+
|
35
|
+
def duration
|
36
|
+
x = seconds.round
|
37
|
+
hours = x / 3600
|
38
|
+
minutes = (x - hours * 3600) / 60
|
39
|
+
seconds = (x - hours * 3600 - minutes * 60)
|
40
|
+
"#{hours}:#{'%02d' % minutes}:#{'%02d' % seconds}"
|
41
|
+
end
|
42
|
+
|
43
|
+
def <=>(other_channel)
|
44
|
+
unique_id.to_f <=> other_channel.unique_id.to_f
|
45
|
+
end
|
46
|
+
|
47
|
+
def self.channels
|
48
|
+
@channels ||= {}
|
49
|
+
end
|
50
|
+
|
51
|
+
def self.for_unique_id(unique_id)
|
52
|
+
channels[unique_id] ||= new(unique_id: unique_id)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module AsteriskManager
|
2
|
+
class ChannelEventObserver
|
3
|
+
def subscribe(event_listener)
|
4
|
+
event_listener.subscribe self, 'Newchannel', 'Newstate', 'Hangup'
|
5
|
+
end
|
6
|
+
|
7
|
+
def receive_event(event)
|
8
|
+
case event.type
|
9
|
+
when 'Newchannel'
|
10
|
+
new_channel(event)
|
11
|
+
when 'Newstate'
|
12
|
+
new_state(event)
|
13
|
+
when 'Hangup'
|
14
|
+
hangup(event)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def new_channel(event)
|
19
|
+
channel = Channel.for_unique_id(event['Uniqueid'])
|
20
|
+
channel.sip_id = event['Channel']
|
21
|
+
channel.state = event['ChannelStateDesc']
|
22
|
+
channel.caller_id_number = event['CallerIDNum']
|
23
|
+
channel.caller_id_name = event['CallerIDName']
|
24
|
+
end
|
25
|
+
|
26
|
+
def new_state(event)
|
27
|
+
channel = Channel.for_unique_id(event['Uniqueid'])
|
28
|
+
channel.state = event['ChannelStateDesc']
|
29
|
+
channel.caller_id_number = event['CallerIDNum']
|
30
|
+
channel.caller_id_name = event['CallerIDName']
|
31
|
+
end
|
32
|
+
|
33
|
+
def hangup(event)
|
34
|
+
Channel.channels.delete event['Uniqueid']
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
module AsteriskManager
|
2
|
+
class ChannelEventPoller
|
3
|
+
attr_accessor :connection,
|
4
|
+
:poll_thread
|
5
|
+
|
6
|
+
def initialize(arguments = {})
|
7
|
+
self.connection = arguments[:connection]
|
8
|
+
end
|
9
|
+
|
10
|
+
def subscribe(event_listener)
|
11
|
+
event_listener.subscribe self, 'CoreShowChannel'
|
12
|
+
end
|
13
|
+
|
14
|
+
def receive_event(event)
|
15
|
+
case event.type
|
16
|
+
when 'CoreShowChannel'
|
17
|
+
core_show_channel(event)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def core_show_channel(event)
|
22
|
+
channel = Channel.for_unique_id(event['UniqueID'])
|
23
|
+
channel.sip_id = event['Channel']
|
24
|
+
channel.state = event['ChannelStateDesc']
|
25
|
+
channel.caller_id_number = event['CallerIDnum']
|
26
|
+
channel.application_name = event['Application']
|
27
|
+
channel.application_data = event['ApplicationData']
|
28
|
+
duration_pieces = event['Duration'].split(':')
|
29
|
+
duration = duration_pieces[0].to_i * 3600 + duration_pieces[1].to_i * 60 + duration_pieces[2].to_i
|
30
|
+
channel.created_at = Time.now - duration
|
31
|
+
if event['BridgedUniqueID'] != ''
|
32
|
+
bridged_channel = Channel.for_unique_id(event['BridgedUniqueID'])
|
33
|
+
bridged_channel.sip_id = event['BridgedChannel']
|
34
|
+
channel_1, channel_2 = [ channel, bridged_channel ].sort
|
35
|
+
Call.for_channel_1_and_channel_2 channel_1, channel_2
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def poll
|
40
|
+
self.poll_thread = Thread.new do
|
41
|
+
loop do
|
42
|
+
connection.send "Action: CoreShowChannels\r\n"
|
43
|
+
connection.send "\r\n"
|
44
|
+
sleep 5
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module AsteriskManager
|
2
|
+
class Conference
|
3
|
+
attr_accessor :name
|
4
|
+
|
5
|
+
def initialize(arguments = {})
|
6
|
+
self.name = arguments[:name]
|
7
|
+
end
|
8
|
+
|
9
|
+
def channels
|
10
|
+
Channel.channels.values.select do |channel|
|
11
|
+
channel.application_name == 'ConfBridge' && channel.application_data.split(',').first == name
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.conferences
|
16
|
+
conferences = {}
|
17
|
+
Channel.channels.values.each do |channel|
|
18
|
+
if channel.application_name == 'ConfBridge'
|
19
|
+
name = channel.application_data.split(',').first
|
20
|
+
conferences[name] = new(name: name)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
conferences
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.for_name(name)
|
27
|
+
conferences[name] ||= new(name: name)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'socket'
|
2
|
+
|
3
|
+
module AsteriskManager
|
4
|
+
class Connection
|
5
|
+
attr_accessor :host,
|
6
|
+
:port,
|
7
|
+
:username,
|
8
|
+
:password
|
9
|
+
|
10
|
+
def initialize(arguments = {})
|
11
|
+
self.host = arguments[:host]
|
12
|
+
self.port = arguments[:port]
|
13
|
+
self.username = arguments[:username]
|
14
|
+
self.password = arguments[:password]
|
15
|
+
login
|
16
|
+
end
|
17
|
+
|
18
|
+
def socket
|
19
|
+
@socket ||= TCPSocket.new host, port
|
20
|
+
end
|
21
|
+
|
22
|
+
def send(value)
|
23
|
+
socket.write value
|
24
|
+
end
|
25
|
+
|
26
|
+
def read_line
|
27
|
+
if !@socket || (response = socket.gets).nil?
|
28
|
+
@socket = nil
|
29
|
+
login
|
30
|
+
read_line
|
31
|
+
else
|
32
|
+
response
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def login
|
37
|
+
send "Action: Login\r\n"
|
38
|
+
send "Username: #{username}\r\n"
|
39
|
+
send "Secret: #{password}\r\n"
|
40
|
+
send "\r\n"
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
require 'thread'
|
2
|
+
|
3
|
+
module AsteriskManager
|
4
|
+
class EventListener
|
5
|
+
attr_accessor :connection,
|
6
|
+
:subscribers,
|
7
|
+
:listen_thread
|
8
|
+
|
9
|
+
def initialize(arguments = {})
|
10
|
+
self.connection = arguments[:connection]
|
11
|
+
self.subscribers = {}
|
12
|
+
end
|
13
|
+
|
14
|
+
def subscribe(subscriber, *event_types)
|
15
|
+
subscribers[subscriber] ||= []
|
16
|
+
subscribers[subscriber] |= event_types
|
17
|
+
end
|
18
|
+
|
19
|
+
def unsubscribe(subscriber, *event_types)
|
20
|
+
if event_types
|
21
|
+
subscribers[subscriber] -= event_types
|
22
|
+
else
|
23
|
+
subscribers.delete subscriber
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def notify_subscribers(event)
|
28
|
+
subscribers.each_pair do |subscriber, event_types|
|
29
|
+
if event_types.include?(event.type)
|
30
|
+
subscriber.receive_event(event)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def listen
|
36
|
+
self.listen_thread = Thread.new do
|
37
|
+
event_attributes = {}
|
38
|
+
loop do
|
39
|
+
line = connection.read_line
|
40
|
+
if line == "\r\n"
|
41
|
+
notify_subscribers Event.new(event_attributes)
|
42
|
+
event_attributes = {}
|
43
|
+
else
|
44
|
+
key, value = line.chomp.split(': ', 2)
|
45
|
+
event_attributes[key] = value
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,83 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
require 'curses'
|
3
|
+
require File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib', 'asterisk-manager'))
|
4
|
+
include AsteriskManager
|
5
|
+
|
6
|
+
connection = Connection.new host: ARGV[0],
|
7
|
+
port: ARGV[1],
|
8
|
+
username: ARGV[2],
|
9
|
+
password: ARGV[3]
|
10
|
+
|
11
|
+
event_listener = EventListener.new connection: connection
|
12
|
+
event_listener.listen
|
13
|
+
|
14
|
+
channel_event_poller = ChannelEventPoller.new connection: connection
|
15
|
+
channel_event_poller.subscribe event_listener
|
16
|
+
channel_event_poller.poll
|
17
|
+
|
18
|
+
channel_event_observer = ChannelEventObserver.new
|
19
|
+
channel_event_observer.subscribe event_listener
|
20
|
+
|
21
|
+
call_event_observer = CallEventObserver.new
|
22
|
+
call_event_observer.subscribe event_listener
|
23
|
+
|
24
|
+
Curses.init_screen
|
25
|
+
loop do
|
26
|
+
lines = []
|
27
|
+
# Channels
|
28
|
+
width = 180
|
29
|
+
columns = 6
|
30
|
+
channels = []
|
31
|
+
channels << [ 'Unique ID', 'SIP ID', 'State', 'Caller ID', 'Application', 'Duration' ]
|
32
|
+
channels << [ '-' * width ]
|
33
|
+
Channel.channels.values.each do |channel|
|
34
|
+
channels << [ channel.unique_id, channel.sip_id, channel.state, channel.caller_id, channel.application, channel.duration ]
|
35
|
+
end
|
36
|
+
channels.collect! do |line|
|
37
|
+
line.collect! do |column|
|
38
|
+
column.to_s.rjust(width / columns)
|
39
|
+
end
|
40
|
+
line.join
|
41
|
+
end
|
42
|
+
lines += [ 'Channels'.center(width, '-') ] + channels
|
43
|
+
|
44
|
+
# Calls
|
45
|
+
width = 180
|
46
|
+
columns = 5
|
47
|
+
calls = []
|
48
|
+
calls << [ 'Channel 1 Unique ID', 'Channel 1 SIP ID', 'Channel 2 Unique ID', 'Channel 2 SIP ID', 'Duration' ]
|
49
|
+
calls << [ '-' * width ]
|
50
|
+
Call.calls.values.each do |call|
|
51
|
+
calls << [ call.channel_1.unique_id, call.channel_1.sip_id, call.channel_2.unique_id, call.channel_2.sip_id, call.duration ]
|
52
|
+
end
|
53
|
+
calls.collect! do |line|
|
54
|
+
line.collect! do |column|
|
55
|
+
column.to_s.rjust(width / columns)
|
56
|
+
end
|
57
|
+
line.join
|
58
|
+
end
|
59
|
+
lines += [ 'Calls'.center(width, '-') ] + calls
|
60
|
+
|
61
|
+
# Conferences
|
62
|
+
width = 180
|
63
|
+
columns = 2
|
64
|
+
conferences = []
|
65
|
+
conferences << [ 'Name', 'Members' ]
|
66
|
+
conferences << [ '-' * width ]
|
67
|
+
Conference.conferences.values.each do |conference|
|
68
|
+
conferences << [ conference.name, conference.channels.collect(&:caller_id_number) ]
|
69
|
+
end
|
70
|
+
conferences.collect! do |line|
|
71
|
+
line.collect! do |column|
|
72
|
+
column.to_s.rjust(width / columns)
|
73
|
+
end
|
74
|
+
line.join
|
75
|
+
end
|
76
|
+
lines += [ 'Conferences'.center(width, '-') ] + conferences
|
77
|
+
|
78
|
+
Curses.clear
|
79
|
+
Curses.noecho
|
80
|
+
Curses.addstr(lines.join("\n"))
|
81
|
+
Curses.refresh
|
82
|
+
sleep 0.1
|
83
|
+
end
|
data/script/event_stream
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
require File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib', 'asterisk-manager'))
|
3
|
+
include AsteriskManager
|
4
|
+
|
5
|
+
connection = Connection.new host: ARGV[0],
|
6
|
+
port: ARGV[1],
|
7
|
+
username: ARGV[2],
|
8
|
+
password: ARGV[3]
|
9
|
+
|
10
|
+
event_listener = EventListener.new connection: connection
|
11
|
+
event_listener.listen
|
12
|
+
|
13
|
+
class SimpleObserver
|
14
|
+
def receive_event(event)
|
15
|
+
puts event.attributes.inspect
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
event_listener.subscribe SimpleObserver.new, 'Newchannel', 'Newstate', 'Hangup', 'Bridge', 'Unlink'
|
20
|
+
|
21
|
+
channel_observer = ChannelObserver.new
|
22
|
+
channel_observer.subscribe event_listener
|
23
|
+
|
24
|
+
call_observer = CallObserver.new
|
25
|
+
call_observer.subscribe event_listener
|
26
|
+
|
27
|
+
loop do
|
28
|
+
puts channel_observer.channels.inspect
|
29
|
+
puts call_observer.calls.inspect
|
30
|
+
sleep 1
|
31
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
2
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
3
|
+
require 'rspec'
|
4
|
+
require 'asterisk-manager'
|
5
|
+
|
6
|
+
# Requires supporting files with custom matchers and macros, etc,
|
7
|
+
# in ./support/ and its subdirectories.
|
8
|
+
Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}
|
9
|
+
|
10
|
+
RSpec.configure do |config|
|
11
|
+
|
12
|
+
end
|
metadata
ADDED
@@ -0,0 +1,138 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: asterisk-manager
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- John Wulff
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2013-04-28 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: rspec
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :development
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ! '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '0'
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: rdoc
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ! '>='
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '0'
|
38
|
+
type: :development
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ! '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '0'
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: bundler
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ! '>='
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0'
|
54
|
+
type: :development
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ! '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
- !ruby/object:Gem::Dependency
|
63
|
+
name: jeweler
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
65
|
+
none: false
|
66
|
+
requirements:
|
67
|
+
- - ! '>='
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '0'
|
70
|
+
type: :development
|
71
|
+
prerelease: false
|
72
|
+
version_requirements: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ! '>='
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: '0'
|
78
|
+
description: Ruby Gem for connecting to an Asterisk server via the AMI (Asterisk Manager
|
79
|
+
Interface) protocol.
|
80
|
+
email: johnw@orcasnet.com
|
81
|
+
executables: []
|
82
|
+
extensions: []
|
83
|
+
extra_rdoc_files:
|
84
|
+
- LICENSE.txt
|
85
|
+
- README.md
|
86
|
+
files:
|
87
|
+
- .document
|
88
|
+
- .rspec
|
89
|
+
- Gemfile
|
90
|
+
- Gemfile.lock
|
91
|
+
- LICENSE.txt
|
92
|
+
- README.md
|
93
|
+
- Rakefile
|
94
|
+
- VERSION
|
95
|
+
- lib/asterisk-manager.rb
|
96
|
+
- lib/asterisk-manager/call.rb
|
97
|
+
- lib/asterisk-manager/call_event_observer.rb
|
98
|
+
- lib/asterisk-manager/channel.rb
|
99
|
+
- lib/asterisk-manager/channel_event_observer.rb
|
100
|
+
- lib/asterisk-manager/channel_event_poller.rb
|
101
|
+
- lib/asterisk-manager/conference.rb
|
102
|
+
- lib/asterisk-manager/connection.rb
|
103
|
+
- lib/asterisk-manager/event.rb
|
104
|
+
- lib/asterisk-manager/event_listener.rb
|
105
|
+
- script/curses_display
|
106
|
+
- script/event_stream
|
107
|
+
- spec/asterisk-manager_spec.rb
|
108
|
+
- spec/spec_helper.rb
|
109
|
+
homepage: http://github.com/jwulff/asterisk-manager
|
110
|
+
licenses:
|
111
|
+
- MIT
|
112
|
+
post_install_message:
|
113
|
+
rdoc_options: []
|
114
|
+
require_paths:
|
115
|
+
- lib
|
116
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
117
|
+
none: false
|
118
|
+
requirements:
|
119
|
+
- - ! '>='
|
120
|
+
- !ruby/object:Gem::Version
|
121
|
+
version: '0'
|
122
|
+
segments:
|
123
|
+
- 0
|
124
|
+
hash: -2590160780499817378
|
125
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
126
|
+
none: false
|
127
|
+
requirements:
|
128
|
+
- - ! '>='
|
129
|
+
- !ruby/object:Gem::Version
|
130
|
+
version: '0'
|
131
|
+
requirements: []
|
132
|
+
rubyforge_project:
|
133
|
+
rubygems_version: 1.8.25
|
134
|
+
signing_key:
|
135
|
+
specification_version: 3
|
136
|
+
summary: Ruby Gem for connecting to an Asterisk server via the AMI (Asterisk Manager
|
137
|
+
Interface) protocol.
|
138
|
+
test_files: []
|