adhearsion 0.7.3 → 0.7.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. data/.version +1 -1
  2. data/Rakefile +13 -11
  3. data/ahn +47 -39
  4. data/{new_projects → apps/default}/Rakefile +5 -4
  5. data/{new_projects → apps/default}/config/adhearsion.sqlite3 +0 -0
  6. data/{new_projects → apps/default}/config/adhearsion.yml +1 -0
  7. data/{new_projects → apps/default}/config/database.rb +0 -0
  8. data/{new_projects → apps/default}/config/database.yml +0 -0
  9. data/{new_projects → apps/default}/config/helpers/drb_server.yml +0 -0
  10. data/{new_projects → apps/default}/config/helpers/factorial.alien.c.yml +0 -0
  11. data/{new_projects → apps/default}/config/helpers/manager_proxy.yml +0 -0
  12. data/{new_projects → apps/default}/config/helpers/micromenus.yml +0 -0
  13. data/{new_projects → apps/default}/config/helpers/micromenus/collab.rb +8 -3
  14. data/{new_projects → apps/default}/config/helpers/micromenus/images/tux.bmp +0 -0
  15. data/{new_projects → apps/default}/config/helpers/micromenus/javascripts/builder.js +0 -0
  16. data/{new_projects → apps/default}/config/helpers/micromenus/javascripts/controls.js +0 -0
  17. data/{new_projects → apps/default}/config/helpers/micromenus/javascripts/dragdrop.js +0 -0
  18. data/{new_projects → apps/default}/config/helpers/micromenus/javascripts/effects.js +0 -0
  19. data/{new_projects → apps/default}/config/helpers/micromenus/javascripts/prototype.js +0 -0
  20. data/{new_projects → apps/default}/config/helpers/micromenus/javascripts/scriptaculous.js +0 -0
  21. data/{new_projects → apps/default}/config/helpers/micromenus/javascripts/slider.js +0 -0
  22. data/{new_projects → apps/default}/config/helpers/micromenus/javascripts/unittest.js +0 -0
  23. data/{new_projects → apps/default}/config/helpers/micromenus/stylesheets/firefox.css +0 -0
  24. data/{new_projects → apps/default}/config/helpers/micromenus/stylesheets/firefox.xul.css +0 -0
  25. data/apps/default/config/helpers/multi_messenger.yml +5 -0
  26. data/{new_projects → apps/default}/config/helpers/weather.yml +0 -0
  27. data/apps/default/config/helpers/xbmc.yml +2 -0
  28. data/{new_projects → apps/default}/config/migration.rb +0 -0
  29. data/{new_projects → apps/default}/extensions.rb +0 -0
  30. data/{new_projects → apps/default}/helpers/drb_server.rb +3 -3
  31. data/{new_projects → apps/default}/helpers/factorial.alien.c +0 -0
  32. data/{new_projects → apps/default}/helpers/manager_proxy.rb +0 -0
  33. data/{new_projects → apps/default}/helpers/micromenus.rb +3 -5
  34. data/apps/default/helpers/multi_messenger.rb +36 -0
  35. data/{new_projects → apps/default}/helpers/oscar_wilde_quotes.rb +0 -0
  36. data/{new_projects → apps/default}/helpers/weather.rb +3 -3
  37. data/{new_projects → apps/default}/helpers/xbmc.rb +0 -0
  38. data/{new_projects/logs/database.log → apps/default/logs/adhearsion.log} +0 -0
  39. data/apps/default/logs/database.log +0 -0
  40. data/lib/adhearsion.rb +172 -58
  41. data/lib/logging.rb +68 -0
  42. data/lib/rami.rb +9 -9
  43. data/lib/servlet_container.rb +1 -1
  44. metadata +52 -55
  45. data/new_projects/config/helpers/xbmc.yml +0 -1
data/.version CHANGED
@@ -1 +1 @@
1
- 0.7.3
1
+ 0.7.4
data/Rakefile CHANGED
@@ -14,7 +14,15 @@ integrating anything and everything.}
14
14
  # Dir['test/*.rb'].each do |f| require f end
15
15
  #end
16
16
 
17
- gem_spec = Gem::Specification.new do |s|
17
+ desc "Generate RDoc documentation for Adhearsion"
18
+ Rake::RDocTask.new do |rdoc|
19
+ rdoc.title = "Generated Adhearsion API Documentation"
20
+ rdoc.rdoc_dir = File.join('docs', 'rdoc')
21
+ rdoc.main = "lib/adhearsion.rb"
22
+ rdoc.options << '-N' # For line numbers
23
+ end
24
+
25
+ GEM_SPEC = Gem::Specification.new do |s|
18
26
 
19
27
  s.name = 'adhearsion'
20
28
  s.rubyforge_project = 'adhearsion'
@@ -25,14 +33,13 @@ gem_spec = Gem::Specification.new do |s|
25
33
  s.homepage = 'http://adhearsion.com'
26
34
 
27
35
  s.add_dependency 'activerecord', '>= 1.14.4'
28
- s.add_dependency 'activesupport', '>= 1.3.1'
29
36
  s.add_dependency 'rake', '>= 0.7.1'
30
37
 
31
38
  s.platform = Gem::Platform::RUBY
32
39
  s.require_path = 'lib'
33
40
  s.executables = 'ahn'
34
41
  s.bindir = '.'
35
- #s.extra_rdoc_files = ['LICENSE']
42
+ s.extra_rdoc_files = ['LICENSE']
36
43
 
37
44
  inc = Dir['**/*'] << '.version'
38
45
  exc = Dir['{log,pkg}/**/*']
@@ -42,16 +49,11 @@ gem_spec = Gem::Specification.new do |s|
42
49
  # test_files = Dir['tests/*.rb'] # Will be added when not so buggy
43
50
  end
44
51
 
45
- Rake::GemPackageTask.new gem_spec do |pkg|
52
+ Rake::GemPackageTask.new GEM_SPEC do |pkg|
46
53
  pkg.need_zip = false
47
54
  pkg.need_tar = false
48
55
  end
49
56
 
50
- #desc "Generate documentation for Adhearsion"
51
- #Rake::RDocTask.new do |rdoc|
52
- # rdoc.rdoc_dir 'doc'
53
- #end
54
-
55
57
  desc "Pulls down the entire wiki in HTML format"
56
58
  task :wiki => [:rm_wiki] do
57
59
  require 'open-uri'
@@ -107,7 +109,7 @@ task :update => [:repackage] do
107
109
  puts "Done."
108
110
  end
109
111
 
110
- desc 'Create both the gem and the Trixbox RPM. Called "d" for "distribute"'
111
- task :d => [:repackage] do
112
+ desc 'Creates the Trixbox RPM.'
113
+ task :trixbox => [:repackage] do
112
114
  # Coming soon
113
115
  end
data/ahn CHANGED
@@ -21,43 +21,66 @@
21
21
  # WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
22
22
  # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23
23
 
24
- usage = %"Usage:
24
+ usage = "Usage:
25
25
  ahn create /path/to/directory
26
26
  ahn start [daemon] [directory]
27
27
  ahn version
28
28
  ahn help|-h|--h|--help|-help
29
- ahn install helpername
30
- ahn system install systemname
31
- ahn search keyword
32
- ahn uninstall/remove helpername
33
29
 
34
- The helper management system is still under development. Not all of these
35
- commands will work. It's coming soon, though! :)"
30
+ Under development:
31
+ ahn install helpername
32
+ ahn system install systemname
33
+ ahn search keyword
34
+ ahn uninstall/remove helpername"
36
35
 
36
+ $: << File.join(File.dirname(__FILE__), 'lib')
37
37
  ADHEARSION_VERSION = File.read(File.join(File.dirname(__FILE__), '.version')).strip
38
- ARGV.unshift 'start' if ARGV.empty?
38
+ ARGV.unshift 'start' if ARGV.empty? # Set the default operation
39
+
40
+ require 'yaml'
41
+ adhearsion_config = File.join('config', 'adhearsion.yml')
42
+ CONFIG = File.readable?(adhearsion_config) ? YAML.load_file(adhearsion_config) : {}
43
+
44
+ $HUTDOWN = []
45
+ class << $HUTDOWN
46
+ def hook(&block) self << block end
47
+ def now!
48
+ log "Shutting down gracefully."
49
+ $HUTDOWN.each { |hook| hook.call }
50
+ exit
51
+ end
52
+ end
53
+ ['INT','TERM'].each do |sig|
54
+ trap sig do $HUTDOWN.now! end
55
+ end
56
+
57
+ require 'logging'
58
+ register_logger StandardLogger.new(STDOUT)
39
59
 
40
60
  case ARGV.shift
41
- when /help|-h|--h|-help|--help/ then puts usage
61
+ when 'help', '-h', '--h', '-help', '--help' then puts usage
42
62
  when 'version' then puts "Adhearsion v#{ADHEARSION_VERSION}"
43
- when 'create'
63
+ when /^create(:[\w_.]+)?$/
44
64
  require 'fileutils'
45
65
  include FileUtils
46
66
 
47
67
  dest_dir_relative = ARGV.shift || Dir.pwd
48
68
  dest_dir = File.expand_path dest_dir_relative
49
69
  base_dir = File.expand_path File.dirname(__FILE__)
70
+ App = $&.index(':') ? $&.split(':')[1..-1] * ':' : "default"
71
+
72
+ abort %'No app "#{App}" found!' unless File.directory? "#{base_dir}/apps/#{App}"
50
73
 
51
74
  from, to = [], []
52
- (Dir["#{base_dir}/{new_projects,docs}/**/*"] + ["#{base_dir}/LICENSE"]).each do |key|
75
+ (Dir["{#{base_dir}/apps/#{App},docs}/**/*"] + ["#{base_dir}/LICENSE"]).each do |key|
53
76
  value = key[base_dir.length..-1]
54
- value.sub! 'new_projects/', '' if value.index 'new_projects/'
77
+ value.sub! "apps/#{App}/", '' if value.index "apps/#{App}/"
55
78
 
56
79
  to << dest_dir + value
57
80
  from << key
58
81
  end
59
82
 
60
- #from.each_index { |i| puts "#{from[i]} => #{to[i]}" }
83
+ #from.each_index { |i| debug "#{from[i]} => #{to[i]}" }
61
84
 
62
85
  # Check overwriting. Abort, Overwrite, Skip
63
86
 
@@ -105,46 +128,31 @@ If you would like a local copy of the Adhearsion wiki, run
105
128
  by contributing documentation improvements by visiting the
106
129
  online, editable version at http://docs.adhearsion.com!
107
130
  MESSAGE
108
-
131
+
109
132
  when 'start'
110
133
 
111
134
  $DAEMON = false
112
- app = Dir.pwd
135
+ target = Dir.pwd
113
136
 
114
137
  arg = ARGV.shift
115
138
  if arg =~ /daemon(ize)?/
116
139
  $DAEMON = true
117
140
  arg = ARGV.shift
118
141
  end
119
- app = File.expand_path(arg || app)
142
+ target = File.expand_path(arg || target)
120
143
 
121
- Dir.chdir app
144
+ Dir.chdir target
145
+
146
+ register_logger StandardLogger.new('logs/adhearsion.log')
122
147
 
123
148
  puts %{\nStarting Adhearsion v#{ADHEARSION_VERSION}
124
149
  Written by Jay Phillips of Codemecca LLC, et al.
125
150
  http://adhearsion.com\n\n}
126
151
 
127
- %w(rubygems yaml uri open-uri abbrev thread).each do |lib| require lib end
152
+ %w(rubygems uri open-uri abbrev thread).each { |lib| require lib }
128
153
 
129
- $: << File.join(File.dirname(__FILE__), 'lib')
130
154
  $: << 'config'
131
155
 
132
- adhearsion_config = File.join('config', 'adhearsion.yml')
133
- CONFIG = File.readable?(adhearsion_config) ? YAML.load_file(adhearsion_config) : {}
134
-
135
- $HUTDOWN = []
136
- class << $HUTDOWN
137
- def hook(&block) self << block end
138
- def now!
139
- puts "Shutting down gracefully."
140
- $HUTDOWN.each { |hook| hook.call }
141
- exit
142
- end
143
- end
144
- ['INT','TERM'].each do |sig|
145
- trap sig do $HUTDOWN.now! end
146
- end
147
-
148
156
  require 'adhearsion'
149
157
  require 'database' if CONFIG['enable_database']
150
158
  require 'servlet_container'
@@ -162,7 +170,7 @@ when 'start'
162
170
  if config.delete('enabled') != false
163
171
  require 'inline'
164
172
  lang = f[f.rindex('.')+1..-1]
165
- puts "Loading helper #{f} as #{lang.upcase}"
173
+ log "Loading helper #{f} as #{lang.upcase}"
166
174
 
167
175
  $HELPERS[f] = config
168
176
  inline do |builder|
@@ -178,17 +186,17 @@ when 'start'
178
186
  config_file = File.join %W(config helpers #{name}.yml)
179
187
  config = File.readable?(config_file) ? YAML.load_file(config_file) : {}
180
188
  if config.delete('enabled') != false
181
- puts "Parsing helper #{name}"
189
+ log "Parsing helper #{name}"
182
190
  $HELPERS[name] = config
183
191
  eval File.read(f)
184
192
  end
185
193
  end
186
194
  end
187
195
 
188
- sc = ServletContainer.new
196
+ sc = ServletContainer.new(CONFIG['port'] || 4573)
189
197
  $HUTDOWN.hook { sc.shutdown }
190
198
 
191
- puts "Dallas, we have liftoff!"
199
+ log "Dallas, we have liftoff!"
192
200
  sc.server.join
193
201
 
194
202
  else puts "Unrecognized command!", usage
@@ -5,10 +5,11 @@ require 'rake/rdoctask'
5
5
  Summary = 'Adhearsion is a professional integration system for
6
6
  integrating anything and everything.'
7
7
 
8
- #desc "Generate documentation for Adhearsion"
9
- #Rake::RDocTask.new do |rdoc|
10
- # rdoc.rdoc_dir 'doc'
11
- #end
8
+ desc "Generate RDoc documentation for Adhearsion"
9
+ Rake::RDocTask.new do |rdoc|
10
+ rdoc.title = "Generated Adhearsion Application-specific Documentation"
11
+ rdoc.rdoc_dir = File.join('doc', 'rdoc')
12
+ end
12
13
 
13
14
  desc "Pulls down the entire wiki in HTML format"
14
15
  task :wiki => [:rm_wiki] do
@@ -1,6 +1,7 @@
1
1
  answer_before_call: true
2
2
  hangup_after_call: true
3
3
  enable_database: false
4
+ port: 4573
4
5
 
5
6
  # This information is completely optional. It may be the
6
7
  # case that a particular helper you're using will need
@@ -1,7 +1,7 @@
1
1
  heading "Adhearsion Micromenus Home"
2
2
 
3
- # A simple example of a helper used in a Micromenu.
4
- item oscar_wilde_quote + ' - Oscar Wilde'
3
+ # Below is a simple example of a helper used in a Micromenu.
4
+ # item oscar_wilde_quote + ' - Oscar Wilde'
5
5
 
6
6
  # Use dial plan logic here in the Micromenu!
7
7
  call "Check your voicemail!" do
@@ -49,7 +49,12 @@ end
49
49
 
50
50
  item 'View Users' do
51
51
  item 'Select a user to call below.'
52
- User.find(:all).each { |user| call user.ivr_extension, user.name }
52
+ begin
53
+ User.find(:all).each { |user| call user.extension, user.name }
54
+ rescue
55
+ item "Tried to use an ActiveRecord User class, but one didn't exist!"
56
+ item "Have you configured your database?"
57
+ end
53
58
  end
54
59
 
55
60
  image 'tux'
@@ -0,0 +1,5 @@
1
+ # Feel free to use your Gmail account here! Can also
2
+ # be any Jabber/XMPP account.
3
+ username: test@adhearsion.com
4
+ password: password
5
+ enabled: false
@@ -0,0 +1,2 @@
1
+ enabled: false
2
+ ip: 192.168.1.136:80
@@ -21,10 +21,10 @@ host = config['host'] || 'localhost'
21
21
  port = config['port'] || 9050
22
22
  DRb.start_service "druby://#{host}:#{port}", PBX
23
23
 
24
- puts "Started DRb server on #{DRb.uri}."
25
- puts "DRb Server Access Control List:"
24
+ log "Started DRb server on #{DRb.uri}."
25
+ log "DRb Server Access Control List:"
26
26
  0.step permissions.length-1, 2 do |i|
27
- puts " #{permissions[i].upcase} #{permissions[i+1]}"
27
+ log " #{permissions[i].upcase} #{permissions[i+1]}"
28
28
  end
29
29
 
30
30
  $HUTDOWN.hook do
@@ -307,9 +307,8 @@ class MicromenusServlet < WEBrick::HTTPServlet::AbstractServlet
307
307
  }
308
308
 
309
309
  def do_GET(request, response)
310
- puts 'trying do'
311
310
  response.status = 200
312
- puts "Request from: " + request['User-Agent']
311
+ log "Request from: " + request['User-Agent']
313
312
 
314
313
  route = request.path[1..-1].split '/'
315
314
  if route.first == 'images'
@@ -344,9 +343,9 @@ class MicromenusServlet < WEBrick::HTTPServlet::AbstractServlet
344
343
  end
345
344
  end
346
345
 
347
- $MICROMENU_THREAD = Thread.new do
346
+ $MICROMENUS_SERVER = Thread.new do
348
347
  micromenu_server = WEBrick::HTTPServer.new :Port => ($HELPERS.micromenus.port || 1337)
349
- micromenu_server.logger = Logger.new 'logs/database.log', 10, 1.megabyte
348
+
350
349
  micromenu_server.mount '/', MicromenusServlet
351
350
  $HUTDOWN.hook {
352
351
  micromenu_server.stop
@@ -354,7 +353,6 @@ $MICROMENU_THREAD = Thread.new do
354
353
  micromenu_server.start
355
354
  end
356
355
 
357
-
358
356
  # This before_call hook is the magic behind the call() method in the micromenus.
359
357
  before_call :low do
360
358
  # PSEUDOCODE
@@ -0,0 +1,36 @@
1
+ =begin Adhearsion metadata
2
+
3
+ =end
4
+
5
+ require 'xmpp4r-simple'
6
+
7
+ class MultiMessenger
8
+
9
+ Format = /\A[\w\._%-]+@[\w\.-]+\.[a-zA-Z]{2,4}\z/
10
+
11
+ def initialize username, password
12
+ @username, @password = username, password
13
+ @connection = Jabber::Simple.new username, password
14
+ end
15
+
16
+ def connected?() @connection.connected? end
17
+ def your_username?(un) @username.downcase == un.downcase end
18
+ def your_service?(s) %w'xmpp jabber gtalk'.include? s.to_s.downcase end
19
+ def may_be_yours?(sn) true end
20
+ def im username, message
21
+ #debug "Connection #{@connection.inspect}, UN: #{username}, MSG: #{message}"
22
+ @connection.deliver(username, message)
23
+ end
24
+ attr_reader :connection
25
+ end
26
+
27
+ config = $HELPERS['multi_messenger']
28
+
29
+ username = config['username']||''
30
+ password = config['password']||''
31
+
32
+ log "MultiMessenger: Connecting to #{username}"
33
+ jabber = MultiMessenger.new username, password
34
+
35
+ InstantMessenger.use_service jabber
36
+ $HUTDOWN.hook { jabber.connection.disconnect }
@@ -42,9 +42,9 @@ class Weather
42
42
  end
43
43
 
44
44
  def self.weather where
45
- puts "Resolving ID"
45
+ debug "Resolving ID"
46
46
  id = where.simplify.is_a?(Fixnum) ? where : get_id(where)
47
- puts "Getting Yahoo Data"
47
+ debug "Getting Yahoo Data"
48
48
  url = "http://xml.weather.yahoo.com/forecastrss/#{id}_#{($HELPERS['weather'].units || 'fahrenheit')[0].chr}.xml"
49
49
  begin xml = REXML::Document.new open(url).read rescue return nil end
50
50
  xml2hash xml.elements
@@ -74,7 +74,7 @@ class Weather
74
74
  doc = REXML::Document.new response
75
75
  results = doc.elements.to_a('/search/loc')
76
76
  if results.size.zero?
77
- puts "Couldn't resolve weather information for #{where}"
77
+ warn "Couldn't resolve weather information for #{where}"
78
78
  nil
79
79
  else results[0].attributes['id']
80
80
  end
File without changes
@@ -23,13 +23,19 @@ module Asterisk
23
23
  # within Adhearsion. The first argument should be the case-insensitive name
24
24
  # of the application and any arguments needed by the application can simply
25
25
  # by trailed onto the end of the method. For a full list of Asterisk's
26
- # applications, see "this":http://www.voip-info.org/wiki/index.php?page=Asterisk+-+documentation+of+application+commands
26
+ # applications, see
27
+ # "this":http://www.voip-info.org/wiki/index.php?page=Asterisk+-+documentation+of+application+commands
27
28
  def exec(app, *options)
28
29
  result = rawr "EXEC #{app} " + (options * '|')
29
30
  result = result[/-?\d+$/]
30
31
  result == "-2" ? false : result
31
32
  end
32
33
 
34
+ # If this method is invoked during a call, the Call Detail Records (the logs
35
+ # Asterisk keeps on its calls) will not be written when the call completes.
36
+ def no_cdr() rawr :NoCDR end
37
+ alias do_not_bill no_cdr
38
+
33
39
  # This method of receiving user input uses the fantastic Asterisk
34
40
  # Read() application, but its results are pretty inconsitent over
35
41
  # AGI. This code has been kept here in case these bugs are fixed.
@@ -45,7 +51,7 @@ module Asterisk
45
51
  exec :read, args
46
52
  $OSCAR[:VARS] << args.first
47
53
  x = get_variable(args.first).gsub(/[\(\)]/, "")
48
- puts "RETURN: #{x.inspect}"
54
+ debug "RETURN: #{x.inspect}"
49
55
  x.simplify
50
56
  end
51
57
 
@@ -105,8 +111,16 @@ module Asterisk
105
111
  end
106
112
 
107
113
  # An abstracted remote mutator for setting Asterisk variables.
108
- def set_variable(key,value) rawr "SET VARIABLE #{key} #{value}" end
109
-
114
+ # You may optionally pass as a third argument ":normally" to
115
+ # have the variable set with Set() instead of AGI.
116
+ def set_variable(key,value,mode=:abnormally)
117
+ case mode
118
+ when :abnormally then rawr "SET VARIABLE #{key} #{value}"
119
+ when :normally then exec :Set, "#{key}=#{value}"
120
+ else raise "Invalid mode when setting variable!"
121
+ end
122
+ end
123
+
110
124
  # An abstracted remote accessor for retrieving Asterisk variables.
111
125
  def get_variable(key, default=nil)
112
126
  result = rawr "GET VARIABLE #{key}"
@@ -225,20 +239,38 @@ module Asterisk
225
239
  end
226
240
  end
227
241
 
228
- # Simply print()s the command over the AGI IO socket.
229
- def putc(what) PBX.io.print what end
242
+ # Very simply sends the command over the AGI IO socket and forgets about it.
243
+ def putc(what) PBX.io.print "#{what}" end
230
244
 
231
245
  # The magical method that handles how objects passed to dial() are converted to their
232
- # corresponding Asterisk-recognizable technology/extension identifier. Likely wouldn't
233
- # be used much outside of dial().
246
+ # corresponding Asterisk-recognizable technology/extension identifier. This likely
247
+ # wouldn't be used much outside of dial(), but you may find it useful.
234
248
  def properize who
235
- possible_methods = [:users, :members, :user, :member].select { |pm| who.respond_to? pm } # These are the convention methods for Group-like objects
236
- who = who.send possible_methods.first if possible_methods.any? # If 'who' has any of the possible_methods, replace 'who' with the return value of that method
237
- return unless who
238
- who = [who] unless who.kind_of?(Enumerable) && !who.kind_of?(String) # In case we can't perform collection algorithms on 'who', let's encapsulate it in an Array
239
- # If the first thing in the Enumerable responds to a User-like convention, then set 'who' equal to the extension accessor of those objects
240
- who.map! { |p| p.send possible_methods.first }.compact! if (possible_methods = [:extension, :extensions].select { |pm| who.first.respond_to? pm }).any?
241
- # Now replace each item in the Enumerable with its form converted to extension (assuming it's not already in that format)
249
+ # These are the convention methods for Group-like objects
250
+ possible_methods = [:users, :members, :user, :member].select do |pm|
251
+ who.respond_to? pm
252
+ end
253
+
254
+ # If 'who' has any of the possible_methods, replace 'who' with the return value
255
+ # of that method
256
+ who = who.send possible_methods.first if possible_methods.any?
257
+ return nil unless who
258
+
259
+ # In case we can't perform collection algorithms on 'who', let's encapsulate it in
260
+ # an Array
261
+ who = [who] unless who.kind_of?(Enumerable) && !who.kind_of?(String)
262
+
263
+ # If the first thing in the Enumerable responds to a User-like convention, then
264
+ # set 'who' equal to the extension accessor of those objects. We refine the "who"
265
+ # argument more and more in this way until it's finally in a way Asterisk can read.
266
+ who.map! do |p|
267
+ p.send possible_methods.first
268
+ end.compact! if (possible_methods = [:extension, :extensions].select do |pm|
269
+ who.first.respond_to? pm
270
+ end).any?
271
+
272
+ # Now replace each item in the Enumerable with its form converted to
273
+ # extension (assuming it's not already in that format)
242
274
  who.map! do |ext|
243
275
  if ext.kind_of?(String) && ext.index(?/) then ext
244
276
  else
@@ -246,7 +278,7 @@ module Asterisk
246
278
  "SIP/#{ext}"
247
279
  end
248
280
  end
249
- who *= '&' # Finally, join() anything left in the Array with an '&'
281
+ who * '&' # Finally, join() anything left in the Array with an '&'
250
282
  end
251
283
 
252
284
  # Returns the status of the last dial(). Possible dial
@@ -268,39 +300,30 @@ module Asterisk
268
300
  def hangup() rawr 'HANGUP' end
269
301
  # Direct translation of the Asterisk NoOp() application. Used primarily for
270
302
  # viewing debug information in the Asterisk CLI.
271
- def noop(*options) rawr "NOOP #{options}" end
303
+ def noop(*options) rawr "NOOP #{options}" end
272
304
 
273
- # The SIP/ZAP/IAX/IAX2/Zap methods allow for cleaner addressing of particular
274
- # extensions, abstracting Asterisk's representation. If not given any
275
- # argument, this methods simply return a symbol identifying their
276
- # appropriate protocol.
277
-
278
- # See the SIP class
279
- def SIP(ext=nil) ext ? "SIP/#{ext}" : :SIP end
280
- # See the ZAP class
281
- def ZAP(ext=nil) ext ? "Zap/#{ext}" : :ZAP end
282
- # See the IAX class
283
- def IAX(ext=nil) ext ? "IAX2/#{ext}" : :IAX end
284
- alias IAX2 IAX
285
- alias Zap ZAP
286
-
287
- # For the completely selfish purpose of sugaring the syntax, two tiny holder classes
288
- # are created to allow the synonym of SIP(1404) to be SIP[1404]. Both functions,
289
- # when given an argument, return a String that Asterisk understands representing that
290
- # particular channel.
291
-
292
- # Syntax sugar for declaring SIP devices. Use: SIP/123 or SIP[123]
305
+ # This SIP class abstracts several SIP-related functions. In your dialplan,
306
+ # if you should want to set a SIP header specifically, simply use the class
307
+ # []= operator. For example, you may want to do SIP['Alert-Info'] = 'Ring-Answer'
308
+ # which will affect the next SIP call placed. Additionally, you can use this
309
+ # class's division operator to naturally represent "technology/channel"
310
+ # endpoints (e.g. SIP/1550 or SIP/:tweedledum)
293
311
  class SIP
294
- def self.[](arg=nil) SIP(arg) end
295
- def self./(arg=nil) SIP(arg) end
312
+ def self.[](header)
313
+ exec
314
+ end
315
+ def self.[]=(header, value)
316
+ exec :SipAddHeader, "#{header}: #{value}"
317
+ end
318
+ def self./(ext) "SIP/#{ext}" end
296
319
  end
297
- # Syntax sugar for declaring IAX devices. Use: IAX/123 or IAX[123]
320
+
298
321
  class IAX
299
322
  def self.[](arg=nil) IAX(arg) end
300
323
  def self./(arg=nil) IAX(arg) end
301
324
  end
302
325
 
303
- # Syntax sugar for declaring ZAP devices. Use: ZAP/123 or ZAP[123]
326
+
304
327
  class ZAP
305
328
  def self.[](arg=nil) ZAP(arg) end
306
329
  def self./(arg=nil) ZAP(arg) end
@@ -379,24 +402,115 @@ class InvalidRailsDirectory < Exception;end
379
402
  # When instantiated with an absolute location to a Rails app, the new RailsApp will perform
380
403
  # a number of useful observations about the files and directories available, such as loading
381
404
  # the database configuration and listing all of the models. All observations are made accessible
382
- # through attribute accessors.
383
- class RailsApp
384
- def initialize path=Dir.pwd
385
- update! path
386
- end
405
+ # through attribute accessors. Note: This implementation is experimental.
406
+ class RailsApp
407
+ def initialize path=Dir.pwd
408
+ update! path
409
+ end
410
+
411
+ # Performs the observations on the Rails app once more.
412
+ def update! path
413
+ @path = path
414
+ @database_config_file = File.join @path, 'config', 'database.yml'
415
+ if File.readable? @database_config_file
416
+ @database_config = YAML.load_file @database_config_file
417
+ else raise InvalidRailsDirectory.new("Database config file #{@database_config_file} not found!")
418
+ end
419
+ @models_folder = File.join path, 'app', 'models'
420
+ @models_files = Dir[ File.join(@models_folder, '*.rb') ]
421
+ @time_updated = Time.now
422
+ end
423
+ attr_reader :database_config_file, :database_config, :path, :models_folder, :models_files,
424
+ :models_names, :time_updated
425
+ end
387
426
 
388
- # Performs the observations on the Rails app once more.
389
- def update! path
390
- @path = path
391
- @database_config_file = File.join @path, 'config', 'database.yml'
392
- if File.readable? @database_config_file
393
- @database_config = YAML.load_file @database_config_file
394
- else raise InvalidRailsDirectory.new("Database config file #{@database_config_file} not found!")
427
+ # This class is the subsystem for anything that may facilitate instant messaging with Adhearsion.
428
+ # If you've seen the im() method used in Adhearsion before, its features can be attributed to
429
+ # the way the InstantMessenger class handles any number of helpers wanting to provide IM
430
+ # functionality.
431
+ #
432
+ # If you're wanting to write your own instant messaging helper, you simply need to create some
433
+ # kind of object that has all of the InstantMessenger::MANDATORY_METHODS. Calling
434
+ # InstantMessenger.use_service(your_instance) and passing this conforming object will keep it
435
+ # referenced internally and potentially used when im() is called.
436
+ class InstantMessenger
437
+
438
+ # Here is a synopsis of what the MANDATORY_METHODS do:
439
+ # * your_username?(screenname) takes a screenname and checks whether its own instance
440
+ # uses it.
441
+ # * may_be_yours?(screename) takes a screenname account and returns a boolean
442
+ # for whether the screen name *could* belong to the instance. For example, in the
443
+ # MultiMessenger helper, this argument is checked against a regular expression for
444
+ # email addresses because all Jabber accounts use this format.
445
+ # * im(sn,msg) is a method all instances ultimately receive to send a message.
446
+ # The Hash argument can be :through (for service) and :with (for username)
447
+ MANDATORY_METHODS = [:your_username?, :may_be_yours?, :im, :your_service?, :connected?]
448
+ @@messengers ||= []
449
+
450
+ # If you're writing your own instant messaging system, you would call this method
451
+ # method on the object that conforms to InstantMessenger::MANDATORY_METHODS. It's
452
+ # placed in a class Array variable and available to the im() method throughout Adhearsion.
453
+ def InstantMessenger.use_service instance
454
+ MANDATORY_METHODS.each do |m|
455
+ raise ArgumentError, "#{instance} must implement '#{m}'!" unless instance.respond_to? m
456
+ end
457
+ @@messengers << instance
458
+ end
459
+
460
+ cattr_reader :messengers
461
+
462
+ def InstantMessenger.im sn, msg, hash={}
463
+ if hash[:through] && hash[:with]
464
+ candidates = @@messengers.select do |c|
465
+ # Find the service that both matches the service name and the username.
466
+ c.your_service?(hash[:through]) && c.your_username?(hash[:with]) && c.connected?
467
+ end
468
+ elsif hash[:through] || hash[:with]
469
+ candidates = @@messengers.select(&:connected?)
470
+ if hash[:through]
471
+ candidates += @@messengers.select do |c|
472
+ c.your_service?(hash[:through]) && c.connected?
473
+ end
474
+ end
475
+ if hash[:with]
476
+ candidates += @@messengers.select do |c|
477
+ c.your_username?(hash[:with]) && c.connected?
478
+ end
479
+ end
480
+ else
481
+ candidates = @@messengers.select { |x| x.may_be_yours? sn }
482
+ end
483
+
484
+ if candidates.size == 1
485
+ # We've found our match!
486
+ candidates.first.im sn, msg
487
+ elsif candidates.empty?
488
+ # The app creator probably didn't enable their IMing helper
489
+ raise ArgumentError, "No instant messaging services found! Did you enable your helper?"
490
+ else
491
+ raise ArgumentError, "Too ambiguous! " +
492
+ "Please pass a :with => 'screenname' or :through => :your_service argument to clarify!"
395
493
  end
396
- @models_folder = File.join path, 'app', 'models'
397
- @models_files = Dir[ File.join(@models_folder, '*.rb') ]
398
- @time_updated = Time.now
399
494
  end
400
- attr_reader :database_config_file, :database_config, :path, :models_folder, :models_files,
401
- :models_names, :time_updated
402
495
  end
496
+
497
+ # Send a message to anyone, provided a proper instant messaging helper has been installed
498
+ # and configured. Simply pass the screen name and message of the user. If you're using
499
+ # multiple helpers for different services, you may need to pass in Hash-key arguments to
500
+ # clarify.
501
+ #
502
+ # Understood Hash-key arguments include:
503
+ # * :through => The service name (as a symbol or string) for the instant message.
504
+ # * :with => The username with which the account is signed on
505
+ #
506
+ # It is sometimes unnecessary to provide Hash-key arguments when your helper can intelligently
507
+ # distinguish its format from others'. For example, if you have both an AIM IM helper and a
508
+ # GTalk/Jabber/XMPP helper registered, then the latter knows its screen names take the format
509
+ # of email addresses, thus im("Jicksta", "AIM!!!") and im("x@Gmail.com", "GTalk!!!") both
510
+ # work without having to do im("Jicksta", "AIM!!!", :through => :AIM)
511
+ # or im(x@Gmail.com,"GTalk!!!", :through => :GTalk). If multiple AIM users were logged on, you
512
+ # would *need* to do im("Jicksta","Was gibt's?", :with => "RegisteredUser") where "RegisteredUser"
513
+ # is one of the names you're signed into AIM with. Note: this example doesn't use the :through
514
+ # Hash-key argument because it assumes no other helpers are installed with this same format.
515
+ # If you had a MSN or Yahoo helper installed too, you'd need to do specify :through because
516
+ def im(sn, msg, hash={}) InstantMessenger.im(sn,msg,hash) end
@@ -0,0 +1,68 @@
1
+ require 'logger'
2
+
3
+ LOGGERS = []
4
+ *LOGGING_METHODS = :fatal, :error, :warn, :info, :debug
5
+ #*LOGGING_METHODS = :debug, :log, :warn, :error, :fatal
6
+ $LOGGING_SEVERITY ||= CONFIG['logging_severity'] || LOGGING_METHODS.size-1
7
+
8
+ $HUTDOWN.hook do
9
+ LOGGERS.each { |l| l.close if l.respond_to?(:close) }
10
+ end
11
+
12
+ # Lets helper authors and Adhearsion developers specify a logging endpoint.
13
+ # The only requirement of a logger is that it implements each of the
14
+ # methods defined in LOGGING_METHODS. A logging endpoint could be STDOUT,
15
+ # a log file, Jabber screen name, IRC channel, or a dispatched email.
16
+ def register_logger logger
17
+ deviance = LOGGING_METHODS.reject {|x| logger.respond_to? x }
18
+ raise "Logger must implement the #{deviance.to_sentence} method(s)" if deviance.any?
19
+ LOGGERS << logger
20
+ end
21
+
22
+ # Provides a safe setter for the logging severity. Its one argument should
23
+ # be a symbol matching something in the LOGGING_METHODS array. The symbol
24
+ # provided corresponds to the *minimum* logging level. For example, if
25
+ # this method were invoked as such
26
+ #
27
+ # logging_severity = :warn
28
+ #
29
+ # Then only calls to fatal(), error(), and warn() would be processed.
30
+ # Calls to log() or debug() would both be ignored.
31
+ def set_logging_severity sym
32
+ i = LOGGING_METHODS.index sym
33
+ raise "Specified severity #{sym} not valid!" unless i
34
+ $LOGGING_SEVERITY = i
35
+ end
36
+
37
+ # Simply returns the current logging severity
38
+ def logging_severity() LOGGING_METHODS[$LOGGING_SEVERITY] end
39
+
40
+ # Used in development phases to log developer-related information.
41
+ def debug(msg) __log__ msg, 4, :debug end
42
+
43
+ # The standard logging method. Give it a message to log.
44
+ def info(msg) __log__ msg, 3, :info end
45
+ alias log info
46
+
47
+ # For reporting messages about potentially hazardous issues.
48
+ def warn(msg) __log__ msg, 2, :warn end
49
+
50
+ # Will Robinson, we've had an error! Let's report it! You may
51
+ # wish to have these errors emailed to the administrator when
52
+ # they occur.
53
+ def error(msg) __log__ msg, 1, :error end
54
+
55
+ # This is for seriously bad errors. You may wish to have
56
+ # these errors emailed to the administrator when they occur.
57
+ def fatal(msg) __log__ msg, 0, :fatal end
58
+
59
+ def __log__ msg, severity, method
60
+ LOGGERS.each { |x| x.__send__ method, msg if $LOGGING_SEVERITY >= severity }
61
+ end
62
+
63
+ class StandardLogger < Logger
64
+ def initialize *args
65
+ super *args
66
+ formatter = lambda {}
67
+ end
68
+ end
@@ -366,12 +366,12 @@ end
366
366
 
367
367
 
368
368
  def connect
369
- puts "Connecting to Asterisk Manager Interface at #{@host}:#{@port}"
369
+ log "Connecting to Asterisk Manager Interface at #{@host}:#{@port}"
370
370
  @sock = nil
371
371
  begin
372
372
  @sock = TCPSocket.new(@host,@port)
373
373
  rescue => e
374
- $stderr.puts <<-MSG
374
+ warn <<-MSG
375
375
 
376
376
  * Adhearsion First Time Setup
377
377
  *
@@ -409,7 +409,7 @@ def connect
409
409
  return true
410
410
  end
411
411
  rescue Timeout::Error => e
412
- puts "LOGIN TIMEOUT"
412
+ error "LOGIN TIMEOUT"
413
413
  return false
414
414
  end
415
415
 
@@ -467,7 +467,7 @@ def mainloop
467
467
  end
468
468
  end
469
469
  rescue IOError => e
470
- puts "Socket disconnected #{e}"
470
+ error "Socket disconnected #{e}"
471
471
  end
472
472
  end
473
473
  end
@@ -504,9 +504,9 @@ public
504
504
  # Starts the server and connects to asterisk.
505
505
  def run
506
506
  if connect
507
- puts "#{Time.now} MSG: LOGGED IN"
507
+ log "#{Time.now} MSG: LOGGED IN"
508
508
  else
509
- puts "#{Time.now} MSG: LOGIN FAILED"
509
+ error "#{Time.now} MSG: LOGIN FAILED"
510
510
  exit
511
511
  end
512
512
  mainloop
@@ -811,12 +811,12 @@ def send_action(action=nil,t=10)
811
811
  end
812
812
  return result
813
813
  rescue Exception => e
814
- puts "#{e}: TIMEOUT #{t} #{sent_id}"
815
- puts e.backtrace
814
+ warn "#{e}: TIMEOUT #{t} #{sent_id}"
815
+ debug e.backtrace
816
816
  return result
817
817
  end
818
818
 
819
819
  end
820
820
  rescue Exception => e
821
- puts e
821
+ error e
822
822
  end
@@ -104,7 +104,7 @@ class ServletContainer
104
104
  begin
105
105
  +send(target_context.to_s.to_sym)
106
106
  rescue => e
107
- STDERR.puts e.inspect
107
+ error e.inspect
108
108
  +lambda {
109
109
  play 'were-sorry'
110
110
  hangup
metadata CHANGED
@@ -3,8 +3,8 @@ rubygems_version: 0.9.0
3
3
  specification_version: 1
4
4
  name: adhearsion
5
5
  version: !ruby/object:Gem::Version
6
- version: 0.7.3
7
- date: 2006-12-29 00:00:00 -06:00
6
+ version: 0.7.4
7
+ date: 2007-01-20 00:00:00 -06:00
8
8
  summary: Adhearsion is a professional integration system for integrating anything and everything.
9
9
  require_paths:
10
10
  - lib
@@ -30,60 +30,66 @@ authors:
30
30
  - Jay Phillips
31
31
  files:
32
32
  - ahn
33
+ - apps
33
34
  - docs
34
35
  - lib
35
36
  - LICENSE
36
- - new_projects
37
37
  - pkg
38
38
  - Rakefile
39
39
  - test
40
+ - apps/codemeccollab
41
+ - apps/default
42
+ - apps/default/config
43
+ - apps/default/extensions.rb
44
+ - apps/default/helpers
45
+ - apps/default/logs
46
+ - apps/default/Rakefile
47
+ - apps/default/config/adhearsion.sqlite3
48
+ - apps/default/config/adhearsion.yml
49
+ - apps/default/config/database.rb
50
+ - apps/default/config/database.yml
51
+ - apps/default/config/helpers
52
+ - apps/default/config/migration.rb
53
+ - apps/default/config/helpers/drb_server.yml
54
+ - apps/default/config/helpers/factorial.alien.c.yml
55
+ - apps/default/config/helpers/manager_proxy.yml
56
+ - apps/default/config/helpers/micromenus
57
+ - apps/default/config/helpers/micromenus.yml
58
+ - apps/default/config/helpers/multi_messenger.yml
59
+ - apps/default/config/helpers/weather.yml
60
+ - apps/default/config/helpers/xbmc.yml
61
+ - apps/default/config/helpers/micromenus/collab.rb
62
+ - apps/default/config/helpers/micromenus/images
63
+ - apps/default/config/helpers/micromenus/javascripts
64
+ - apps/default/config/helpers/micromenus/stylesheets
65
+ - apps/default/config/helpers/micromenus/images/tux.bmp
66
+ - apps/default/config/helpers/micromenus/javascripts/builder.js
67
+ - apps/default/config/helpers/micromenus/javascripts/controls.js
68
+ - apps/default/config/helpers/micromenus/javascripts/dragdrop.js
69
+ - apps/default/config/helpers/micromenus/javascripts/effects.js
70
+ - apps/default/config/helpers/micromenus/javascripts/prototype.js
71
+ - apps/default/config/helpers/micromenus/javascripts/scriptaculous.js
72
+ - apps/default/config/helpers/micromenus/javascripts/slider.js
73
+ - apps/default/config/helpers/micromenus/javascripts/unittest.js
74
+ - apps/default/config/helpers/micromenus/stylesheets/firefox.css
75
+ - apps/default/config/helpers/micromenus/stylesheets/firefox.xul.css
76
+ - apps/default/helpers/drb_server.rb
77
+ - apps/default/helpers/factorial.alien.c
78
+ - apps/default/helpers/manager_proxy.rb
79
+ - apps/default/helpers/micromenus.rb
80
+ - apps/default/helpers/multi_messenger.rb
81
+ - apps/default/helpers/oscar_wilde_quotes.rb
82
+ - apps/default/helpers/weather.rb
83
+ - apps/default/helpers/xbmc.rb
84
+ - apps/default/logs/adhearsion.log
85
+ - apps/default/logs/database.log
40
86
  - lib/adhearsion.rb
41
87
  - lib/constants.rb
42
88
  - lib/core_extensions.rb
43
89
  - lib/database_functions.rb
90
+ - lib/logging.rb
44
91
  - lib/rami.rb
45
92
  - lib/servlet_container.rb
46
- - new_projects/config
47
- - new_projects/extensions.rb
48
- - new_projects/helpers
49
- - new_projects/logs
50
- - new_projects/Rakefile
51
- - new_projects/config/adhearsion.sqlite3
52
- - new_projects/config/adhearsion.yml
53
- - new_projects/config/database.rb
54
- - new_projects/config/database.yml
55
- - new_projects/config/helpers
56
- - new_projects/config/migration.rb
57
- - new_projects/config/helpers/drb_server.yml
58
- - new_projects/config/helpers/factorial.alien.c.yml
59
- - new_projects/config/helpers/manager_proxy.yml
60
- - new_projects/config/helpers/micromenus
61
- - new_projects/config/helpers/micromenus.yml
62
- - new_projects/config/helpers/weather.yml
63
- - new_projects/config/helpers/xbmc.yml
64
- - new_projects/config/helpers/micromenus/collab.rb
65
- - new_projects/config/helpers/micromenus/images
66
- - new_projects/config/helpers/micromenus/javascripts
67
- - new_projects/config/helpers/micromenus/stylesheets
68
- - new_projects/config/helpers/micromenus/images/tux.bmp
69
- - new_projects/config/helpers/micromenus/javascripts/builder.js
70
- - new_projects/config/helpers/micromenus/javascripts/controls.js
71
- - new_projects/config/helpers/micromenus/javascripts/dragdrop.js
72
- - new_projects/config/helpers/micromenus/javascripts/effects.js
73
- - new_projects/config/helpers/micromenus/javascripts/prototype.js
74
- - new_projects/config/helpers/micromenus/javascripts/scriptaculous.js
75
- - new_projects/config/helpers/micromenus/javascripts/slider.js
76
- - new_projects/config/helpers/micromenus/javascripts/unittest.js
77
- - new_projects/config/helpers/micromenus/stylesheets/firefox.css
78
- - new_projects/config/helpers/micromenus/stylesheets/firefox.xul.css
79
- - new_projects/helpers/drb_server.rb
80
- - new_projects/helpers/factorial.alien.c
81
- - new_projects/helpers/manager_proxy.rb
82
- - new_projects/helpers/micromenus.rb
83
- - new_projects/helpers/oscar_wilde_quotes.rb
84
- - new_projects/helpers/weather.rb
85
- - new_projects/helpers/xbmc.rb
86
- - new_projects/logs/database.log
87
93
  - test/core_extensions_test.rb
88
94
  - test/dial_test.rb
89
95
  - test/test_micromenus.rb
@@ -92,8 +98,8 @@ test_files: []
92
98
 
93
99
  rdoc_options: []
94
100
 
95
- extra_rdoc_files: []
96
-
101
+ extra_rdoc_files:
102
+ - LICENSE
97
103
  executables:
98
104
  - ahn
99
105
  extensions: []
@@ -110,15 +116,6 @@ dependencies:
110
116
  - !ruby/object:Gem::Version
111
117
  version: 1.14.4
112
118
  version:
113
- - !ruby/object:Gem::Dependency
114
- name: activesupport
115
- version_requirement:
116
- version_requirements: !ruby/object:Gem::Version::Requirement
117
- requirements:
118
- - - ">="
119
- - !ruby/object:Gem::Version
120
- version: 1.3.1
121
- version:
122
119
  - !ruby/object:Gem::Dependency
123
120
  name: rake
124
121
  version_requirement:
@@ -1 +0,0 @@
1
- ip: 192.168.1.136:80