adhearsion 0.7.5 → 0.7.6
Sign up to get free protection for your applications and to get access to all the features.
- data/.version +1 -1
- data/CHANGELOG +25 -18
- data/LICENSE +386 -269
- data/Rakefile +4 -4
- data/TODO +22 -0
- data/ahn +72 -56
- data/apps/default/config/adhearsion.sqlite3 +0 -0
- data/apps/default/config/adhearsion.yml +52 -1
- data/apps/default/config/helpers/growler.yml +21 -0
- data/apps/default/config/migration.rb +7 -11
- data/apps/default/helpers/growler.rb +53 -0
- data/apps/default/helpers/lookup.rb +27 -18
- data/apps/default/helpers/manager_proxy.rb +19 -2
- data/apps/default/helpers/micromenus.rb +1 -1
- data/lib/adhearsion.rb +257 -44
- data/lib/constants.rb +15 -12
- data/lib/core_extensions.rb +11 -11
- data/lib/drb_server.rb +101 -0
- data/lib/logging.rb +18 -1
- data/lib/servlet_container.rb +23 -23
- data/test/asterisk_module_test.rb +1 -1
- metadata +35 -23
- data/apps/default/helpers/drb_server.rb +0 -32
data/Rakefile
CHANGED
@@ -4,8 +4,7 @@ require 'rake/rdoctask'
|
|
4
4
|
require 'rake/gempackagetask'
|
5
5
|
|
6
6
|
ADHEARSION_VERSION = File.read('.version').strip
|
7
|
-
Summary = %{Adhearsion is
|
8
|
-
integrating anything and everything.}
|
7
|
+
Summary = %{Adhearsion is metaprogramming framework for developing collaboration software.}
|
9
8
|
|
10
9
|
#task :default => [:test]
|
11
10
|
|
@@ -27,14 +26,15 @@ GEM_SPEC = Gem::Specification.new do |s|
|
|
27
26
|
s.name = 'adhearsion'
|
28
27
|
s.rubyforge_project = 'adhearsion'
|
29
28
|
s.author = 'Jay Phillips'
|
30
|
-
s.email = '
|
29
|
+
s.email = 'jay -at- codemecca dot com'
|
31
30
|
s.version = ADHEARSION_VERSION
|
32
31
|
s.summary = Summary
|
33
32
|
s.homepage = 'http://adhearsion.com'
|
34
33
|
|
35
34
|
s.add_dependency 'activerecord', '>= 1.14.4'
|
36
35
|
s.add_dependency 'rake', '>= 0.7.1'
|
37
|
-
|
36
|
+
s.add_dependency 'daemons', '>= 1.0.5'
|
37
|
+
|
38
38
|
s.platform = Gem::Platform::RUBY
|
39
39
|
s.require_path = 'lib'
|
40
40
|
s.executables = 'ahn'
|
data/TODO
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Port Jason's T2S work into trunk when stable. Jason?
|
2
|
+
|
3
|
+
Port in new Micromenus browser GUI
|
4
|
+
|
5
|
+
Support ITAD routing using ENUM lookups.
|
6
|
+
Major file structure refactoring!
|
7
|
+
|
8
|
+
Port in RAI's AMI work.
|
9
|
+
|
10
|
+
Port in RAI's Rails app initialization and routing integration.
|
11
|
+
|
12
|
+
Develop a reasonable RSpec testing framework for Adhearsion.
|
13
|
+
|
14
|
+
Have AGI server optionally powered by mongrel
|
15
|
+
|
16
|
+
Port Micromenus server over to Camping
|
17
|
+
|
18
|
+
Adhearsion CLI. Should use the 'breakpoint' library's DRb extension to run an irb session in an external Ruby interpreter (Adhearsion's)
|
19
|
+
|
20
|
+
Let record() take a block of functionality to record. This feature will probably only be available when an AMI connection is present.
|
21
|
+
|
22
|
+
Build in hunt-group support into dial()
|
data/ahn
CHANGED
@@ -1,25 +1,24 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
|
3
|
-
# This is the main executable file
|
4
|
-
|
5
|
-
#
|
6
|
-
|
7
|
-
#
|
8
|
-
#
|
9
|
-
#
|
10
|
-
#
|
11
|
-
# of the
|
12
|
-
#
|
13
|
-
|
14
|
-
#
|
15
|
-
#
|
16
|
-
|
17
|
-
#
|
18
|
-
#
|
19
|
-
#
|
20
|
-
#
|
21
|
-
|
22
|
-
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
3
|
+
# This is the main executable file.
|
4
|
+
|
5
|
+
# Adhearsion, open source technology integrator
|
6
|
+
# Copyright (C) 2006,2007 Jay Phillips
|
7
|
+
#
|
8
|
+
# This library is free software; you can redistribute it and/or
|
9
|
+
# modify it under the terms of the GNU Lesser General Public
|
10
|
+
# License as published by the Free Software Foundation; either
|
11
|
+
# version 2.1 of the License, or (at your option) any later version.
|
12
|
+
#
|
13
|
+
# This library is distributed in the hope that it will be useful,
|
14
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
15
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
16
|
+
# Lesser General Public License for more details.
|
17
|
+
#
|
18
|
+
# You should have received a copy of the GNU Lesser General Public
|
19
|
+
# License along with this library; if not, write to the Free Software
|
20
|
+
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
21
|
+
require 'rubygems'
|
23
22
|
|
24
23
|
usage = "Usage:
|
25
24
|
ahn create /path/to/directory
|
@@ -33,9 +32,9 @@ Under development:
|
|
33
32
|
ahn search keyword
|
34
33
|
ahn uninstall/remove helpername"
|
35
34
|
|
36
|
-
$: << File.join(File.dirname(__FILE__), 'lib')
|
35
|
+
$: << File.join(File.expand_path(File.dirname(__FILE__)), 'lib')
|
37
36
|
ADHEARSION_VERSION = File.read(File.join(File.dirname(__FILE__), '.version')).strip
|
38
|
-
ARGV.unshift 'start'
|
37
|
+
ARGV.unshift 'start' if ARGV.empty? # Set the default operation
|
39
38
|
|
40
39
|
require 'yaml'
|
41
40
|
|
@@ -103,15 +102,17 @@ Adhearsion project generated!
|
|
103
102
|
|
104
103
|
Start your new app with "ahn start #{dest_dir_relative}"
|
105
104
|
|
106
|
-
|
107
|
-
|
105
|
+
If you wish to use Adhearsion to control Asterisk's dialplan,
|
106
|
+
change the contexts you wish to be affected in your
|
107
|
+
/etc/asterisk/extensions.conf file to the following:
|
108
108
|
|
109
109
|
[your_context_name]
|
110
110
|
exten => _X.,1,AGI(agi://1.2.3.4) ; This IP here
|
111
111
|
|
112
|
-
To use databases edit config/database.yml for the
|
112
|
+
To use databases, edit config/database.yml for the
|
113
113
|
connection information and, optionally, config/database.rb
|
114
|
-
to change the default database object models.
|
114
|
+
to change the default database object models. To create your
|
115
|
+
tables, you may wish to use config/migration.rb.
|
115
116
|
|
116
117
|
Asterisk Manager interface integration is highly recommended.
|
117
118
|
Edit your /etc/asterisk/manager.conf file and enable the
|
@@ -131,12 +132,23 @@ when 'start'
|
|
131
132
|
target = Dir.pwd
|
132
133
|
|
133
134
|
arg = ARGV.shift
|
134
|
-
if arg =~ /daemon(
|
135
|
+
if arg =~ /daemon(ized)?/
|
135
136
|
$DAEMON = true
|
136
137
|
arg = ARGV.shift
|
137
138
|
end
|
138
139
|
target = File.expand_path(arg || target)
|
139
140
|
|
141
|
+
puts %{\nStarting Adhearsion v#{ADHEARSION_VERSION}
|
142
|
+
Written by Jay Phillips of Codemecca LLC, et al.
|
143
|
+
http://adhearsion.com\n\n}
|
144
|
+
|
145
|
+
if $DAEMON
|
146
|
+
require 'rubygems'
|
147
|
+
require 'daemons'
|
148
|
+
puts "Daemonizing now!"
|
149
|
+
Daemons.daemonize
|
150
|
+
end
|
151
|
+
|
140
152
|
Dir.chdir target
|
141
153
|
|
142
154
|
adhearsion_config = File.join('config', 'adhearsion.yml')
|
@@ -146,10 +158,7 @@ when 'start'
|
|
146
158
|
register_logger StandardLogger.new(STDOUT)
|
147
159
|
|
148
160
|
register_logger StandardLogger.new('logs/adhearsion.log')
|
149
|
-
|
150
|
-
puts %{\nStarting Adhearsion v#{ADHEARSION_VERSION}
|
151
|
-
Written by Jay Phillips of Codemecca LLC, et al.
|
152
|
-
http://adhearsion.com\n\n}
|
161
|
+
|
153
162
|
|
154
163
|
%w(rubygems uri open-uri abbrev thread).each { |lib| require lib }
|
155
164
|
|
@@ -158,45 +167,52 @@ when 'start'
|
|
158
167
|
require 'adhearsion'
|
159
168
|
require 'database' if CONFIG['enable_database']
|
160
169
|
require 'servlet_container'
|
161
|
-
require
|
170
|
+
require 'constants'
|
171
|
+
|
172
|
+
if CONFIG['drb'] && CONFIG['drb']['enabled']
|
173
|
+
require 'drb_server'
|
174
|
+
DRbServerManager.start
|
175
|
+
end
|
162
176
|
|
163
177
|
# Load appropriate helpers
|
164
178
|
$HELPERS = {}
|
165
179
|
Contexts::Container.new.run_inside do
|
166
180
|
# Start with compiled helpers
|
167
|
-
class
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
181
|
+
class Object
|
182
|
+
class << self
|
183
|
+
aliens = Dir[File.join('helpers', '*.alien.*')]
|
184
|
+
aliens.each do |f|
|
185
|
+
f = File.basename f
|
186
|
+
config_file = File.join %W(config helpers #{f}.yml)
|
187
|
+
config = File.readable?(config_file) ? YAML.load_file(config_file) : {}
|
188
|
+
if config.delete('enabled') != false
|
189
|
+
require 'inline'
|
190
|
+
lang = f[f.rindex('.') + 1..-1]
|
191
|
+
log "Loading helper #{f} as #{lang.upcase}"
|
177
192
|
|
178
|
-
|
179
|
-
|
180
|
-
|
193
|
+
$HELPERS[f] = config
|
194
|
+
inline do |builder|
|
195
|
+
builder.send lang, File.read(File.join('helpers', f))
|
196
|
+
end
|
181
197
|
end
|
182
198
|
end
|
183
199
|
end
|
184
|
-
end
|
185
200
|
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
201
|
+
# Load Ruby helpers
|
202
|
+
Dir[File.join('helpers', '*.rb')].each do |f|
|
203
|
+
name = File.basename(f)[/^[^.]+/]
|
204
|
+
config_file = File.join %W(config helpers #{name}.yml)
|
205
|
+
config = File.readable?(config_file) ? YAML.load_file(config_file) : {}
|
206
|
+
if config.delete('enabled') != false
|
207
|
+
log "Parsing helper #{name}"
|
208
|
+
$HELPERS[name] = config
|
209
|
+
eval File.read(f)
|
210
|
+
end
|
195
211
|
end
|
196
212
|
end
|
197
213
|
end
|
198
214
|
|
199
|
-
sc = ServletContainer.new(CONFIG['port'] || 4573)
|
215
|
+
sc = ServletContainer.new((CONFIG['port'] || 4573), (CONFIG['host'] || '0.0.0.0'))
|
200
216
|
$HUTDOWN.hook { sc.shutdown }
|
201
217
|
|
202
218
|
log "Dallas, we have liftoff!"
|
Binary file
|
@@ -1,6 +1,7 @@
|
|
1
1
|
answer_before_call: true
|
2
2
|
hangup_after_call: true
|
3
3
|
enable_database: false
|
4
|
+
host: 0.0.0.0
|
4
5
|
port: 4573
|
5
6
|
|
6
7
|
# This information is completely optional. It may be the
|
@@ -17,7 +18,57 @@ port: 4573
|
|
17
18
|
# zip: 77777
|
18
19
|
# country: United States
|
19
20
|
|
21
|
+
|
22
|
+
# If you wish to use named conferences in your dial plan,
|
23
|
+
# uncomment the lines below. To use, pass the name to
|
24
|
+
# the join() method as a Symbol. e.g. join :management
|
25
|
+
#
|
20
26
|
# conferences:
|
21
27
|
# management: 1000
|
22
28
|
# marketing: 1001
|
23
|
-
# development: 1002
|
29
|
+
# development: 1002
|
30
|
+
|
31
|
+
drb:
|
32
|
+
enabled: false
|
33
|
+
host: 127.0.0.1
|
34
|
+
port: 9050
|
35
|
+
|
36
|
+
deny: all
|
37
|
+
allow:
|
38
|
+
- 127.0.0.1
|
39
|
+
- 192.168.1.*
|
40
|
+
|
41
|
+
# Set your access control permissions above.
|
42
|
+
# Values for deny and allow can be several
|
43
|
+
# things:
|
44
|
+
#
|
45
|
+
# * The 'all' keyword can be used to match
|
46
|
+
# everything.
|
47
|
+
# * A single IP can be given right after
|
48
|
+
# the colon.
|
49
|
+
# * A single IP with wildcards can be given
|
50
|
+
# * Or, combining all of these, a YAML list
|
51
|
+
# can be used to specify many policies.
|
52
|
+
# See the comments below for more examples.
|
53
|
+
#
|
54
|
+
# Also note, the "host" field above may also
|
55
|
+
# affect the ability for DRb clients to connect
|
56
|
+
# to the server. If you wish you receive
|
57
|
+
# connections from the 192.168.1.*, listen on
|
58
|
+
# the IP associated with this machine on that
|
59
|
+
# subnet.
|
60
|
+
#
|
61
|
+
#
|
62
|
+
# USING YAML LISTS
|
63
|
+
#
|
64
|
+
# YAML allows lists of items to be created by
|
65
|
+
# prepending a hyphen to each list element.
|
66
|
+
# These lists can be used to refine your
|
67
|
+
# access control list better. Example:
|
68
|
+
#
|
69
|
+
# deny: all
|
70
|
+
# allow:
|
71
|
+
# - 192.168.1.123
|
72
|
+
# - 192.168.1.99
|
73
|
+
# - 66.199.34.44
|
74
|
+
|
@@ -0,0 +1,21 @@
|
|
1
|
+
enabled: false
|
2
|
+
|
3
|
+
# This is how growl identifies this client.
|
4
|
+
app_name: Adhearsion
|
5
|
+
|
6
|
+
# When dealing with only a single machine, set this
|
7
|
+
# to your desktop's IP. If you want multiple servers,
|
8
|
+
# you can manually pass the IP address as the second
|
9
|
+
# argument to growl() and password as the third.
|
10
|
+
|
11
|
+
# Leave ip empty if you intend to specify it manually
|
12
|
+
ip: localhost
|
13
|
+
password: # Leave as-is if none
|
14
|
+
|
15
|
+
# This is a list of the different types of notifications
|
16
|
+
# Growl should expect from this client. These can be
|
17
|
+
# anything you want. Names are case sensitive. Note: At
|
18
|
+
# least one must exist!
|
19
|
+
notifications:
|
20
|
+
- Standard Notification
|
21
|
+
- Incoming Call
|
@@ -5,7 +5,8 @@ require 'active_record'
|
|
5
5
|
|
6
6
|
# A migration script uses a database configuration and creates tables
|
7
7
|
# very conveniently in a database-agnostic way. Below, add any customizations
|
8
|
-
# to the sample schema or leave it as-is. When done,
|
8
|
+
# to the sample schema or leave it as-is. When done, type "rake migrate" to
|
9
|
+
# have this schema generated.
|
9
10
|
|
10
11
|
ActiveRecord::Base.establish_connection YAML.load_file('config/database.yml')
|
11
12
|
|
@@ -15,15 +16,14 @@ class CreateUsers < ActiveRecord::Migration
|
|
15
16
|
def self.up
|
16
17
|
create_table :users do |t|
|
17
18
|
t.column :name, :string
|
18
|
-
t.column :callerid_name, :string
|
19
|
-
t.column :callerid_num, :string
|
20
19
|
t.column :group_id, :integer # Foreign key
|
21
|
-
t.column :ivr_extension, :string
|
22
20
|
t.column :extension, :string
|
23
|
-
t.column :email, :string
|
24
|
-
t.column :im_username, :string
|
25
|
-
t.column :im_provider, :string
|
26
21
|
# t.column :billed_time, :integer, :null => false
|
22
|
+
|
23
|
+
# Feel free to remove or change this to "email". Gmail offers email,
|
24
|
+
# instant messaging, calendars, and so forth -- all of which you
|
25
|
+
# can integrate with using one simple username.
|
26
|
+
t.column :gmail, :string
|
27
27
|
end
|
28
28
|
end
|
29
29
|
|
@@ -47,7 +47,3 @@ class CreateGroups < ActiveRecord::Migration
|
|
47
47
|
drop_table :groups
|
48
48
|
end
|
49
49
|
end
|
50
|
-
|
51
|
-
# Run "rake migrate" to run this script properly.
|
52
|
-
# CreateUsers.up
|
53
|
-
# CreateGroups.up
|
@@ -0,0 +1,53 @@
|
|
1
|
+
=begin Adhearsion metadata
|
2
|
+
name: Adhearsion Growler
|
3
|
+
author:
|
4
|
+
name: Phil Kates
|
5
|
+
email: hawk684 -at- gmail.com
|
6
|
+
modified-by: Jay Phillips
|
7
|
+
gems:
|
8
|
+
- ruby-growl
|
9
|
+
=end
|
10
|
+
|
11
|
+
require 'ruby-growl'
|
12
|
+
|
13
|
+
GROWL_SERVER = unless $HELPERS['growler']['ip'] then nil else
|
14
|
+
Growl.new $HELPERS['growler']['ip'] || 'localhost',
|
15
|
+
$HELPERS['growler']['app_name'] || "Adhearsion",
|
16
|
+
$HELPERS['growler']['notifications'],
|
17
|
+
nil, $HELPERS['growler']['password']
|
18
|
+
end
|
19
|
+
|
20
|
+
# Sends a message to an OSX desktop's Growl notification server. If you
|
21
|
+
# intend to only notify a single machine, you can specify the parameters
|
22
|
+
# in growler.yml.
|
23
|
+
#
|
24
|
+
# == Usage:
|
25
|
+
#
|
26
|
+
# - message: The notification or message you wish to send!
|
27
|
+
# - type (optional): The type of notification as specified in the growler.yml
|
28
|
+
# config file. This defaults to the first "notifications" entry. If you want
|
29
|
+
# growl() to automatically set this for you, simply send it nil.
|
30
|
+
# - ip (optional): The desktop's IP address you wish to notify. This
|
31
|
+
# by default uses what's in growler.yml or, if unavailable, "localhost".
|
32
|
+
# - password (optional): a password if one is needed. Defaults to nothing.
|
33
|
+
#
|
34
|
+
# == Examples:
|
35
|
+
# - growl "Isn't it about time you debugged me?"
|
36
|
+
# - growl "Call from #{callerid}!", "Incoming Call"
|
37
|
+
# - growl "The caller queue size is #{queue.size}", nil, "192.168.1.133"
|
38
|
+
# - growl "Join conference 1234!", nil, "192.168.50.151", "Secretz!"
|
39
|
+
def growl message, type=nil, ip=nil, password=nil
|
40
|
+
type = $HELPERS['growler']['notifications'].first unless type
|
41
|
+
|
42
|
+
# Create a new Growl client if an IP was specified, otherwise use
|
43
|
+
# our server created when Adhearsion booted.
|
44
|
+
svr = unless ip then GROWL_SERVER else
|
45
|
+
Growl.new ip, $HELPERS['growler']['app_name'] || "Adhearsion",
|
46
|
+
$HELPERS['growler']['notifications'], nil,
|
47
|
+
$HELPERS['growler']['password']
|
48
|
+
end
|
49
|
+
|
50
|
+
# TODO support priorities and stickies. May need to use hash-key argments?
|
51
|
+
# TODO handle unreachable desktops
|
52
|
+
svr.notify type, type, message
|
53
|
+
end
|
@@ -10,25 +10,34 @@ def lookup number
|
|
10
10
|
hash = {}
|
11
11
|
url = "http://www.whitepages.com/9901/search/ReversePhone?phone=#{number}"
|
12
12
|
doc = Hpricot open(url)
|
13
|
+
|
14
|
+
# This div contains all the information we need, unless it's an unlisted number
|
15
|
+
if (results = doc.at "#results_single_listing") then
|
16
|
+
# This div's h3 contains the name of the caller
|
17
|
+
hash[:first_name], hash[:last_name] = results.at('h3').inner_html.split(/,\s*/).reverse
|
18
|
+
|
19
|
+
# Now we just need the rest of the information contained in p's.
|
20
|
+
meta = results/'p'
|
21
|
+
meta.pop # Discard the useless p element
|
22
|
+
|
23
|
+
hash[:number] = meta.pop.inner_html
|
24
|
+
city_info = meta.pop.inner_html
|
25
|
+
city_info = city_info.match /(.+), ([A-Za-z]{2}) (\d{5})/
|
26
|
+
hash[:city] = city_info[1]
|
27
|
+
hash[:state] = city_info[2]
|
28
|
+
hash[:zip] = city_info[3]
|
29
|
+
|
30
|
+
hash[:address] = meta.map(&:inner_html) * " "
|
31
|
+
elsif (results = doc.at "#results_single_phone_info") then
|
32
|
+
meta = results/'span'
|
33
|
+
hash[:location] = (meta.pop.inner_html.match /Location: (.*)/)[1]
|
34
|
+
end
|
13
35
|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
# Now we just need the rest of the information contained in p's.
|
21
|
-
meta = results/'p'
|
22
|
-
meta.pop # Discard the useless p element
|
23
|
-
|
24
|
-
hash[:number] = meta.pop.inner_html
|
25
|
-
city_info = meta.pop.inner_html
|
26
|
-
city_info = city_info.match /(.+), ([A-Za-z]{2}) (\d{5})/
|
27
|
-
hash[:city] = city_info[1]
|
28
|
-
hash[:state] = city_info[2]
|
29
|
-
hash[:zip] = city_info[3]
|
30
|
-
|
31
|
-
hash[:address] = meta.map(&:inner_html) * " "
|
36
|
+
if hash[:first_name] or hash[:last_name] then
|
37
|
+
hash[:composite] = "#{hash[:first_name]} #{hash[:last_name]}"
|
38
|
+
else
|
39
|
+
hash[:composite] = hash[:location]
|
40
|
+
end
|
32
41
|
|
33
42
|
hash
|
34
43
|
end
|