adhearsion 0.7.0
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE +339 -0
- data/Rakefile +108 -0
- data/ahn +195 -0
- data/lib/adhearsion.rb +402 -0
- data/lib/constants.rb +20 -0
- data/lib/core_extensions.rb +157 -0
- data/lib/database_functions.rb +76 -0
- data/lib/rami.rb +822 -0
- data/lib/servlet_container.rb +146 -0
- data/new_projects/Rakefile +100 -0
- data/new_projects/config/adhearsion.sqlite3 +0 -0
- data/new_projects/config/adhearsion.yml +11 -0
- data/new_projects/config/database.rb +50 -0
- data/new_projects/config/database.yml +10 -0
- data/new_projects/config/helpers/drb_server.yml +43 -0
- data/new_projects/config/helpers/factorial.alien.c.yml +1 -0
- data/new_projects/config/helpers/manager_proxy.yml +7 -0
- data/new_projects/config/helpers/micromenus.yml +1 -0
- data/new_projects/config/helpers/micromenus/collab.rb +55 -0
- data/new_projects/config/helpers/micromenus/images/tux.bmp +0 -0
- data/new_projects/config/helpers/micromenus/javascripts/builder.js +131 -0
- data/new_projects/config/helpers/micromenus/javascripts/controls.js +834 -0
- data/new_projects/config/helpers/micromenus/javascripts/dragdrop.js +944 -0
- data/new_projects/config/helpers/micromenus/javascripts/effects.js +956 -0
- data/new_projects/config/helpers/micromenus/javascripts/prototype.js +2319 -0
- data/new_projects/config/helpers/micromenus/javascripts/scriptaculous.js +51 -0
- data/new_projects/config/helpers/micromenus/javascripts/slider.js +278 -0
- data/new_projects/config/helpers/micromenus/javascripts/unittest.js +557 -0
- data/new_projects/config/helpers/micromenus/stylesheets/firefox.css +10 -0
- data/new_projects/config/helpers/micromenus/stylesheets/firefox.xul.css +44 -0
- data/new_projects/config/helpers/weather.yml +1 -0
- data/new_projects/config/helpers/xbmc.yml +1 -0
- data/new_projects/config/migration.rb +53 -0
- data/new_projects/extensions.rb +56 -0
- data/new_projects/helpers/drb_server.rb +32 -0
- data/new_projects/helpers/factorial.alien.c +32 -0
- data/new_projects/helpers/manager_proxy.rb +43 -0
- data/new_projects/helpers/micromenus.rb +374 -0
- data/new_projects/helpers/oscar_wilde_quotes.rb +197 -0
- data/new_projects/helpers/weather.rb +85 -0
- data/new_projects/helpers/xbmc.rb +12 -0
- data/new_projects/logs/database.log +0 -0
- data/test/core_extensions_test.rb +26 -0
- data/test/dial_test.rb +43 -0
- data/test/stress_tests/test.rb +13 -0
- data/test/stress_tests/test.yml +13 -0
- data/test/test_micromenus.rb +0 -0
- metadata +131 -0
@@ -0,0 +1,44 @@
|
|
1
|
+
window {
|
2
|
+
background-color: grey;
|
3
|
+
padding: 20px 10px;
|
4
|
+
}
|
5
|
+
#haupt {
|
6
|
+
text-align: center;
|
7
|
+
display: block;
|
8
|
+
-moz-border-radius: 20px;
|
9
|
+
border: 10px solid black;
|
10
|
+
background-color: white;
|
11
|
+
padding: 10px;
|
12
|
+
padding-bottom: 20px;
|
13
|
+
}
|
14
|
+
|
15
|
+
label.header {
|
16
|
+
font-size: 30px;
|
17
|
+
text-align: center;
|
18
|
+
}
|
19
|
+
|
20
|
+
a {
|
21
|
+
background-color: #EEE;
|
22
|
+
padding: 2px;
|
23
|
+
display: block;
|
24
|
+
margin: 4px 0;
|
25
|
+
border: 5px #EEE solid;
|
26
|
+
-moz-border-radius: 5px;
|
27
|
+
color: black;
|
28
|
+
text-align: left;
|
29
|
+
text-decoration: none;
|
30
|
+
}
|
31
|
+
|
32
|
+
a:hover {
|
33
|
+
color: white;
|
34
|
+
background-color: black;
|
35
|
+
padding: 2px;
|
36
|
+
border: 5px black solid;
|
37
|
+
-moz-border-radius: 5px;
|
38
|
+
}
|
39
|
+
|
40
|
+
description {
|
41
|
+
text-align: center;
|
42
|
+
padding: 15px;
|
43
|
+
width: 100%;
|
44
|
+
}
|
@@ -0,0 +1 @@
|
|
1
|
+
units: fahrenheit # can be 'fahrenheit' or 'celsuis' (case sensitive)
|
@@ -0,0 +1 @@
|
|
1
|
+
ip: 192.168.1.136:80
|
@@ -0,0 +1,53 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
require 'yaml'
|
3
|
+
require 'rubygems'
|
4
|
+
require 'active_record'
|
5
|
+
|
6
|
+
# A migration script uses a database configuration and creates tables
|
7
|
+
# very conveniently in a database-agnostic way. Below, add any customizations
|
8
|
+
# to the sample schema or leave it as-is. When done, execute this script.
|
9
|
+
|
10
|
+
ActiveRecord::Base.establish_connection YAML.load_file('config/database.yml')
|
11
|
+
|
12
|
+
class CreateUsers < ActiveRecord::Migration
|
13
|
+
# Available column types are :primary_key, :string, :text, :integer,
|
14
|
+
# :float, :datetime, :timestamp, :time, :date, :binary, and :boolean
|
15
|
+
def self.up
|
16
|
+
create_table :users do |t|
|
17
|
+
t.column :name, :string
|
18
|
+
t.column :callerid_name, :string
|
19
|
+
t.column :callerid_num, :string
|
20
|
+
t.column :group_id, :integer # Foreign key
|
21
|
+
t.column :ivr_extension, :string
|
22
|
+
t.column :extension, :string
|
23
|
+
t.column :email, :string
|
24
|
+
t.column :im_username, :string
|
25
|
+
t.column :im_provider, :string
|
26
|
+
# t.column :billed_time, :integer, :null => false
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.down
|
31
|
+
drop_table :users
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
class CreateGroups < ActiveRecord::Migration
|
36
|
+
def self.up
|
37
|
+
create_table :groups do |t|
|
38
|
+
t.column :name, :string
|
39
|
+
t.column :administrator_email, :string
|
40
|
+
t.column :callerid_name, :string
|
41
|
+
t.column :callerid_num, :string
|
42
|
+
#t.column :usage_limit, :integer
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def self.down
|
47
|
+
drop_table :groups
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
# Run "rake migrate" to run this script properly.
|
52
|
+
# CreateUsers.up
|
53
|
+
# CreateGroups.up
|
@@ -0,0 +1,56 @@
|
|
1
|
+
|
2
|
+
from_pwnyourphone {
|
3
|
+
# You rock! Be awesome and record us a message! We'll put supportive messages on the podcast!"
|
4
|
+
# Get ready to record. 5, 4, 3, 1 *BEEP*
|
5
|
+
play %(you-rock record-us-a-message we-put-messages-on-podcast get-ready five-countdown beep)
|
6
|
+
record :for => 3.minutes, :to => "/recordings/pwner.#{Time.now.to_i}.#{calleridnumber}.gsm"
|
7
|
+
}
|
8
|
+
|
9
|
+
internal {
|
10
|
+
callee = User.find_by_ivr_extension extension
|
11
|
+
if callee
|
12
|
+
|
13
|
+
voicemail extension if last_dial_status != :answer
|
14
|
+
|
15
|
+
#elsif extension.to_s =~ //#/^(((\d{1,3})?\d{1{3})?[1-9]\d{2})?[1-9]\d{6}$/
|
16
|
+
# puts "got here"
|
17
|
+
# dial "SIP/#{extension}@sipphone"
|
18
|
+
else
|
19
|
+
case extension
|
20
|
+
when 21 then play weather_report
|
21
|
+
when 32 then dial "SIP/zoip@demo.zoip.org"
|
22
|
+
when 888 then loop { print '.'; XBMC.sendkey XBMC.translate(wait_for_digit) }
|
23
|
+
when 9999
|
24
|
+
if rand(20) == 0
|
25
|
+
play %W(a-connect-charge-of #{rand(50) + 10} cents-per-minute will-apply)
|
26
|
+
sleep 1.second
|
27
|
+
play %w(just-kidding-not-upset)
|
28
|
+
end
|
29
|
+
check_voicemail caller.voicemailbox
|
30
|
+
else
|
31
|
+
play 'all-your-base'
|
32
|
+
end
|
33
|
+
end
|
34
|
+
}
|
35
|
+
|
36
|
+
old {
|
37
|
+
case extension
|
38
|
+
when 1 then play weather_report("Richardson, Texas")
|
39
|
+
when 2 then play weather_report(input(5, :play => 'zip-code'))
|
40
|
+
when 3 then +xbmc
|
41
|
+
when 4
|
42
|
+
dial :tweedledee
|
43
|
+
end
|
44
|
+
}
|
45
|
+
|
46
|
+
xbmc {
|
47
|
+
loop {
|
48
|
+
print '.'
|
49
|
+
XBMC.sendkey XBMC.translate(wait_for_digit)
|
50
|
+
}
|
51
|
+
}
|
52
|
+
|
53
|
+
from_gizmo {
|
54
|
+
#dial :tweedledum
|
55
|
+
+xbmc
|
56
|
+
}
|
@@ -0,0 +1,32 @@
|
|
1
|
+
|
2
|
+
require 'drb'
|
3
|
+
require 'drb/acl'
|
4
|
+
require 'thread'
|
5
|
+
|
6
|
+
# Load the access control list
|
7
|
+
config = $HELPERS['drb_server']
|
8
|
+
|
9
|
+
permissions = []
|
10
|
+
# For greater control over the ACL
|
11
|
+
if config['raw_acl']
|
12
|
+
permissions = config['raw_acl'].flatten
|
13
|
+
else
|
14
|
+
[config['deny']].flatten.each { |ip| permissions << "deny" << ip }
|
15
|
+
[config['allow']].flatten.each { |ip| permissions << "allow" << ip }
|
16
|
+
end
|
17
|
+
|
18
|
+
DRb.install_acl ACL.new(permissions)
|
19
|
+
|
20
|
+
host = config['host'] || 'localhost'
|
21
|
+
port = config['port'] || 9050
|
22
|
+
DRb.start_service "druby://#{host}:#{port}", PBX
|
23
|
+
|
24
|
+
puts "Started DRb server on #{DRb.uri}."
|
25
|
+
puts "DRb Server Access Control List:"
|
26
|
+
0.step permissions.length-1, 2 do |i|
|
27
|
+
puts " #{permissions[i].upcase} #{permissions[i+1]}"
|
28
|
+
end
|
29
|
+
|
30
|
+
$HUTDOWN.hook do
|
31
|
+
DRb.stop_service
|
32
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
|
2
|
+
/*
|
3
|
+
=begin Adhearsion metadata
|
4
|
+
|
5
|
+
name: Native Factorial
|
6
|
+
author:
|
7
|
+
name: Jay Phillips
|
8
|
+
blog: http://jicksta.com
|
9
|
+
email: Jicksta -at- Gmail.com
|
10
|
+
gems:
|
11
|
+
- soap4r
|
12
|
+
- rubyinline: >= 0.8.2
|
13
|
+
instructions: >
|
14
|
+
Yes, this is a pure C file!!!
|
15
|
+
This is an example of writing Adhearsion extensions in
|
16
|
+
other languages. The first time this file is executed
|
17
|
+
it will be compiled and the binary form will be cached.
|
18
|
+
|
19
|
+
If your Adhearsion system is heavily dependent on
|
20
|
+
an intensive helper, it may be advantageous to rewrite
|
21
|
+
it in a language such as C or C++ and use it like this.
|
22
|
+
|
23
|
+
=end
|
24
|
+
*/
|
25
|
+
|
26
|
+
int fast_factorial(int input) {
|
27
|
+
int sum = 0, count = 1;
|
28
|
+
while(count <= input) {
|
29
|
+
sum += count++;
|
30
|
+
}
|
31
|
+
return sum;
|
32
|
+
}
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'rami'
|
2
|
+
class PBX
|
3
|
+
include Rami
|
4
|
+
|
5
|
+
@@sip_users = {}
|
6
|
+
|
7
|
+
@@rami_server_thread = Thread.current
|
8
|
+
|
9
|
+
@@rami_server = Rami::Server.new $HELPERS.manager_proxy
|
10
|
+
@@rami_server.console = 1
|
11
|
+
@@rami_server.run
|
12
|
+
|
13
|
+
@@rami_client = Client.new @@rami_server
|
14
|
+
@@rami_client.timeout = 10
|
15
|
+
|
16
|
+
def self.rami_client() @@rami_client end
|
17
|
+
|
18
|
+
$HUTDOWN.hook do
|
19
|
+
@@rami_client.stop
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.sip_users
|
23
|
+
if !@@sip_users[:expiration] || @@sip_users[:expiration] <= Time.now
|
24
|
+
sip_db = PBX.rami_client.command("database show SIP/Registry").first
|
25
|
+
sip_db = sip_db[ sip_db.keys.select { |x| x.is_a? Fixnum }.first ]
|
26
|
+
sip_db = sip_db.gsub( /--[A-Z ]+?--/ , '').strip
|
27
|
+
users = sip_db.split "\n"
|
28
|
+
users.collect! do |user|
|
29
|
+
fields = user.split ':'
|
30
|
+
{ :username => fields[4],
|
31
|
+
:ip => fields[1].strip,
|
32
|
+
:port => fields[2],
|
33
|
+
:address => fields[6] }
|
34
|
+
end
|
35
|
+
@@sip_users[:users] = users
|
36
|
+
@@sip_users[:expiration] = 90.seconds.from_now
|
37
|
+
end
|
38
|
+
@@sip_users[:users]
|
39
|
+
end
|
40
|
+
def self.record channel, file, format, mix
|
41
|
+
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,374 @@
|
|
1
|
+
# Micromenus Adhearsion helper
|
2
|
+
# Copyright 2006 Jay Phillips
|
3
|
+
#
|
4
|
+
# This program is free software; you can redistribute it and/or
|
5
|
+
# modify it under the terms of the GNU General Public License
|
6
|
+
# as published by the Free Software Foundation; either version 2
|
7
|
+
# of the License, or (at your option) any later version.
|
8
|
+
#
|
9
|
+
# This program is distributed in the hope that it will be useful,
|
10
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
11
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
12
|
+
# GNU General Public License for more details.
|
13
|
+
#
|
14
|
+
# You should have received a copy of the GNU General Public License
|
15
|
+
# along with this program; if not, write to the Free Software
|
16
|
+
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
17
|
+
|
18
|
+
require 'rubygems'
|
19
|
+
require 'builder'
|
20
|
+
require 'webrick'
|
21
|
+
require 'stringio'
|
22
|
+
|
23
|
+
class WEBrick::HTTPRequest
|
24
|
+
def ip() @peeraddr[3] end
|
25
|
+
end
|
26
|
+
|
27
|
+
# Micromenu catchers are special hooks that allow integration
|
28
|
+
# between micromenus and incoming calls (specifically, incoming
|
29
|
+
# calls generated by the micromenus)
|
30
|
+
$MICROMENU_CALL_HOOKS = []
|
31
|
+
class << $MICROMENU_CALL_HOOKS
|
32
|
+
def purge_expired!
|
33
|
+
self.synchronize { |hooks| hooks.delete_if { |h| h.expiration < Time.now } }
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
class MicromenusServlet < WEBrick::HTTPServlet::AbstractServlet
|
38
|
+
|
39
|
+
class MicromenuGenerator
|
40
|
+
|
41
|
+
def initialize request, model, io=$stdout
|
42
|
+
@request, @io, @config = request, io, []
|
43
|
+
@xml = Builder::XmlMarkup.new(:target => @io, :indent => 3)
|
44
|
+
self.extend model
|
45
|
+
end
|
46
|
+
|
47
|
+
attr_accessor :name, :io, :config, :request
|
48
|
+
|
49
|
+
def process request
|
50
|
+
route = request.dup
|
51
|
+
|
52
|
+
if route.empty?
|
53
|
+
start "Error" do
|
54
|
+
build_text "Bad URL!"
|
55
|
+
build_text "Must point to a micromenu!"
|
56
|
+
end
|
57
|
+
return
|
58
|
+
end
|
59
|
+
|
60
|
+
title = " Adhearsion Micromenus"
|
61
|
+
|
62
|
+
filename = route.shift
|
63
|
+
load_menu filename
|
64
|
+
until route.empty?
|
65
|
+
segment = route.shift
|
66
|
+
broken = segment.match(/^([\w_.]+);?(\d*)$/)
|
67
|
+
segment, id = broken[1], broken[2]
|
68
|
+
id = nil if id.empty?
|
69
|
+
|
70
|
+
matches = @config.select do |x|
|
71
|
+
x[:type] == :menu && x[:text].nameify == segment
|
72
|
+
end
|
73
|
+
dest = matches[(id.simplify || 1) - 1]
|
74
|
+
|
75
|
+
|
76
|
+
if dest then
|
77
|
+
@config.clear
|
78
|
+
title = dest[:text]
|
79
|
+
dest[:block].call
|
80
|
+
else
|
81
|
+
start "Error" do
|
82
|
+
item '404 Not found!'
|
83
|
+
item "Req: #{request.inspect}"
|
84
|
+
return
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
# Let any headers override the default title
|
90
|
+
@config.each do |item|
|
91
|
+
if item[:type] == :heading
|
92
|
+
title = @config.delete(item)[:text]
|
93
|
+
break
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
start title do
|
98
|
+
@config.each do |item|
|
99
|
+
case item[:type]
|
100
|
+
when :menu
|
101
|
+
build_menu item[:text], item[:uri], request
|
102
|
+
when :item
|
103
|
+
build_text item[:text]
|
104
|
+
when :heading
|
105
|
+
build_header item[:text]
|
106
|
+
when :image
|
107
|
+
build_image item[:text]
|
108
|
+
when :call
|
109
|
+
build_call item[:number], item[:text]
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
def load_menu filename
|
116
|
+
@config.clear
|
117
|
+
file = File.join('config','helpers', 'micromenus', filename + '.rb')
|
118
|
+
unless File.readable? file
|
119
|
+
item "Bad URL!"
|
120
|
+
item "File inexistent!"
|
121
|
+
else
|
122
|
+
eval File.read(File.join('config','helpers', 'micromenus', filename + '.rb'))
|
123
|
+
end
|
124
|
+
@config
|
125
|
+
end
|
126
|
+
def join_url url, *pages
|
127
|
+
url *= '/' if url.is_a? Array
|
128
|
+
'/' + if url.empty?
|
129
|
+
pages * '/'
|
130
|
+
else
|
131
|
+
((url[-1] == ?/) ? url : url + "/") + pages * '/'
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
def get_refresh() @refresh end
|
136
|
+
|
137
|
+
module PolycomPhone
|
138
|
+
|
139
|
+
def content_type() "text/html" end
|
140
|
+
|
141
|
+
def start name='', &block
|
142
|
+
@xml.html do
|
143
|
+
@xml.head do
|
144
|
+
@xml.title name
|
145
|
+
end
|
146
|
+
@xml.body do
|
147
|
+
yield
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
def build_menu str, uri, request
|
153
|
+
#build_menu item[:text], item[:uri], request
|
154
|
+
#request.flatten! if request.is_a? Array
|
155
|
+
@xml.p { @xml.a str, :href => join_url(request, uri) }
|
156
|
+
end
|
157
|
+
|
158
|
+
def build_text str
|
159
|
+
@xml.p str
|
160
|
+
end
|
161
|
+
|
162
|
+
def build_prompt
|
163
|
+
|
164
|
+
end
|
165
|
+
|
166
|
+
def build_image filename, hash=nil
|
167
|
+
@xml.img :src => "/images/#{filename}"
|
168
|
+
end
|
169
|
+
|
170
|
+
def build_header str
|
171
|
+
@xml.h1 str
|
172
|
+
end
|
173
|
+
|
174
|
+
def build_call number, name=number
|
175
|
+
@xml.a name, :href => "tel://#{number}"
|
176
|
+
@xml.br
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
module XulUi
|
181
|
+
|
182
|
+
include PolycomPhone
|
183
|
+
def content_type() "application/vnd.mozilla.xul+xml" end
|
184
|
+
|
185
|
+
def start name='', &block
|
186
|
+
@xml.instruct!
|
187
|
+
@xml.instruct! 'xml-stylesheet', :href => "chrome://global/skin/", :type => "text/css"
|
188
|
+
@xml.instruct! 'xml-stylesheet', :href => "/stylesheets/firefox.xul.css", :type => "text/css"
|
189
|
+
@xml.window :title => name,
|
190
|
+
'xmlns:html' => "http://www.w3.org/1999/xhtml",
|
191
|
+
:xmlns => "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" do
|
192
|
+
@xml.vbox :id => 'haupt' do
|
193
|
+
#@xml.label name, :class => 'header'
|
194
|
+
yield
|
195
|
+
end
|
196
|
+
end
|
197
|
+
end
|
198
|
+
|
199
|
+
def build_call number, name=number
|
200
|
+
# The tel:// UI doesn't do Firefox much good. Mexuar, maybe?
|
201
|
+
@xml.html:a, name, :href => "tel://#{number}"
|
202
|
+
@xml.html:br
|
203
|
+
end
|
204
|
+
|
205
|
+
def build_menu str, uri, request
|
206
|
+
@xml.html:a, str, :href => join_url(request, uri)
|
207
|
+
end
|
208
|
+
|
209
|
+
def build_text str
|
210
|
+
@xml.description str
|
211
|
+
@xml.html:br
|
212
|
+
end
|
213
|
+
|
214
|
+
def build_image filename, hash=nil
|
215
|
+
@xml.html:img, :src => "/images/#{filename}"
|
216
|
+
end
|
217
|
+
|
218
|
+
def build_header str
|
219
|
+
@xml.label str, :class => 'header'
|
220
|
+
end
|
221
|
+
|
222
|
+
end
|
223
|
+
|
224
|
+
|
225
|
+
module FirefoxUi
|
226
|
+
|
227
|
+
include PolycomPhone
|
228
|
+
|
229
|
+
def start name='', &block
|
230
|
+
@xml.html do
|
231
|
+
@xml.head do
|
232
|
+
@xml.title name
|
233
|
+
@xml.link :rel => 'stylesheet', :media => 'all', :href => '/stylesheets/firefox.css'
|
234
|
+
end
|
235
|
+
@xml.body do
|
236
|
+
yield
|
237
|
+
end
|
238
|
+
end
|
239
|
+
end
|
240
|
+
def build_call number, name=number
|
241
|
+
# The tel:// UI doesn't do Firefox much good. Mexuar, maybe?
|
242
|
+
@xml.a name, :href => "tel://#{number}"
|
243
|
+
@xml.br
|
244
|
+
end
|
245
|
+
|
246
|
+
end
|
247
|
+
|
248
|
+
|
249
|
+
private
|
250
|
+
|
251
|
+
|
252
|
+
def image name
|
253
|
+
name += '.bmp' unless name.index ?.
|
254
|
+
@config << {:type => :image, :text => name}
|
255
|
+
end
|
256
|
+
|
257
|
+
def refresh_every time
|
258
|
+
@refresh = time
|
259
|
+
end
|
260
|
+
|
261
|
+
def heading str
|
262
|
+
@config << {:type => :heading, :text => str}
|
263
|
+
end
|
264
|
+
alias header heading
|
265
|
+
|
266
|
+
def item title, &block
|
267
|
+
hash = {:text => title, :type => :item }
|
268
|
+
if block_given?
|
269
|
+
hash[:block], hash[:type], hash[:uri] = block, :menu, title.nameify
|
270
|
+
collisions = @config.select { |c| c[:type] == :menu && c[:text].nameify == hash[:uri]}.length
|
271
|
+
hash[:uri] += ";#{collisions + 1}" if collisions.nonzero?
|
272
|
+
end
|
273
|
+
@config << hash
|
274
|
+
end
|
275
|
+
def items array
|
276
|
+
array.each { |x| item x }
|
277
|
+
end
|
278
|
+
|
279
|
+
def guess_sip_user
|
280
|
+
return @guessed_user if @guessed_user
|
281
|
+
selection = PBX.sip_users.select { |x| x[:ip] == request.ip }.first
|
282
|
+
@guessed_user = selection ? selection.username : nil
|
283
|
+
end
|
284
|
+
|
285
|
+
def call number, name=number, &block
|
286
|
+
instance = {:type => :call, :text => name, :number => number}
|
287
|
+
if block_given?
|
288
|
+
$MICROMENU_CALL_HOOKS.purge_expired!
|
289
|
+
num = "555551337#{rand(8_999_999_999) + 1_000_000_000}"
|
290
|
+
instance[:number] = num
|
291
|
+
$MICROMENU_CALL_HOOKS.synchronize do |hooks|
|
292
|
+
hooks << { :expiration => 90.seconds.from_now, :extension => num, :hook => block }
|
293
|
+
end
|
294
|
+
end
|
295
|
+
@config << instance
|
296
|
+
end
|
297
|
+
|
298
|
+
def action title, &block
|
299
|
+
# Just like menu() but without a submenu.
|
300
|
+
# Useful for performing an action and refreshing.
|
301
|
+
end
|
302
|
+
end
|
303
|
+
|
304
|
+
USER_AGENT_MAP = {
|
305
|
+
"Polycom" => MicromenuGenerator::PolycomPhone,
|
306
|
+
"Firefox" => MicromenuGenerator::XulUi#FirefoxUi
|
307
|
+
}
|
308
|
+
|
309
|
+
def do_GET(request, response)
|
310
|
+
puts 'trying do'
|
311
|
+
response.status = 200
|
312
|
+
puts "Request from: " + request['User-Agent']
|
313
|
+
|
314
|
+
route = request.path[1..-1].split '/'
|
315
|
+
if route.first == 'images'
|
316
|
+
file = File.join(%w(config helpers micromenus images), route[1..-1])
|
317
|
+
# TODO: Handle missing files
|
318
|
+
response.content_type = WEBrick::HTTPUtils::mime_type file, WEBrick::HTTPUtils::DefaultMimeTypes
|
319
|
+
response.body = File.read file
|
320
|
+
|
321
|
+
elsif route.first == 'stylesheets'
|
322
|
+
file = File.join %w(config helpers micromenus stylesheets), route[1..-1]
|
323
|
+
response.content_type = 'text/css'
|
324
|
+
response.body = File.read file
|
325
|
+
else
|
326
|
+
mg = MicromenuGenerator.new request, resolve_brand(request['User-Agent']), StringIO.new
|
327
|
+
response.content_type = mg.content_type
|
328
|
+
response['Expires'] = 2
|
329
|
+
|
330
|
+
mg.process route
|
331
|
+
|
332
|
+
refresh = mg.get_refresh
|
333
|
+
response['Refresh'] = refresh if refresh
|
334
|
+
|
335
|
+
response.body = mg.io.string
|
336
|
+
end
|
337
|
+
end
|
338
|
+
|
339
|
+
def resolve_brand useragent
|
340
|
+
USER_AGENT_MAP.each do |k,v|
|
341
|
+
return v if useragent.index k
|
342
|
+
end
|
343
|
+
MicromenuGenerator::PolycomPhone # Polycom's the default since it's XHTML
|
344
|
+
end
|
345
|
+
end
|
346
|
+
|
347
|
+
$MICROMENU_THREAD = Thread.new do
|
348
|
+
micromenu_server = WEBrick::HTTPServer.new :Port => ($HELPERS.micromenus.port || 1337)
|
349
|
+
micromenu_server.logger = Logger.new 'logs/database.log', 10, 1.megabyte
|
350
|
+
micromenu_server.mount '/', MicromenusServlet
|
351
|
+
$HUTDOWN.hook {
|
352
|
+
micromenu_server.stop
|
353
|
+
}
|
354
|
+
micromenu_server.start
|
355
|
+
end
|
356
|
+
|
357
|
+
|
358
|
+
# This before_call hook is the magic behind the call() method in the micromenus.
|
359
|
+
before_call :low do
|
360
|
+
# PSEUDOCODE
|
361
|
+
# Check extension for format. next unless it matches
|
362
|
+
# Delete all expired hooks
|
363
|
+
# Find first match in the collection of hooks
|
364
|
+
# Pull the first match out of the collection
|
365
|
+
# Execute that match's block finish
|
366
|
+
extension = Thread.current[:VARS]['extension'].to_s
|
367
|
+
|
368
|
+
next unless extension.length == 19 && extension.starts_with?("555551337")
|
369
|
+
$MICROMENU_CALL_HOOKS.purge_expired!
|
370
|
+
match = $MICROMENU_CALL_HOOKS.detect { |x| x.extension == extension }
|
371
|
+
next unless match
|
372
|
+
Thread.current[:VARS]['context'] = :interrupted
|
373
|
+
+match.hook
|
374
|
+
end
|