adhearsion 0.7.6 → 0.7.7

Sign up to get free protection for your applications and to get access to all the features.
Files changed (61) hide show
  1. data/.version +1 -1
  2. data/CHANGELOG +43 -25
  3. data/Rakefile +0 -5
  4. data/TODO +51 -2
  5. data/ahn +2 -1
  6. data/apps/default/Rakefile +16 -7
  7. data/apps/default/config/adhearsion.yml +22 -1
  8. data/apps/default/config/helpers/manager_proxy.yml +1 -0
  9. data/apps/default/config/helpers/micromenus/images/arrow-off.gif +0 -0
  10. data/apps/default/config/helpers/micromenus/images/arrow-on.gif +0 -0
  11. data/apps/default/config/helpers/micromenus/images/error.gif +0 -0
  12. data/apps/default/config/helpers/micromenus/images/folder-off.gif +0 -0
  13. data/apps/default/config/helpers/micromenus/images/folder-on.gif +0 -0
  14. data/apps/default/config/helpers/micromenus/images/folder.png +0 -0
  15. data/apps/default/config/helpers/micromenus/images/ggbridge.jpg +0 -0
  16. data/apps/default/config/helpers/micromenus/images/green.png +0 -0
  17. data/apps/default/config/helpers/micromenus/images/microbrowser.bg.gif +0 -0
  18. data/apps/default/config/helpers/micromenus/images/red.png +0 -0
  19. data/apps/default/config/helpers/micromenus/images/url-off.gif +0 -0
  20. data/apps/default/config/helpers/micromenus/images/url-on.gif +0 -0
  21. data/apps/default/config/helpers/micromenus/images/yellow.png +0 -0
  22. data/apps/default/config/helpers/micromenus/javascripts/animation.js +1341 -0
  23. data/apps/default/config/helpers/micromenus/javascripts/carousel.js +1238 -0
  24. data/apps/default/config/helpers/micromenus/javascripts/columnav.js +306 -0
  25. data/apps/default/config/helpers/micromenus/javascripts/connection.js +965 -0
  26. data/apps/default/config/helpers/micromenus/javascripts/container.js +4727 -0
  27. data/apps/default/config/helpers/micromenus/javascripts/container_core.js +2915 -0
  28. data/apps/default/config/helpers/micromenus/javascripts/dom.js +892 -0
  29. data/apps/default/config/helpers/micromenus/javascripts/dragdrop.js +2921 -907
  30. data/apps/default/config/helpers/micromenus/javascripts/event.js +1771 -0
  31. data/apps/default/config/helpers/micromenus/javascripts/yahoo.js +433 -0
  32. data/apps/default/config/helpers/micromenus/stylesheets/carousel.css +78 -0
  33. data/apps/default/config/helpers/micromenus/stylesheets/columnav.css +135 -0
  34. data/apps/default/config/helpers/micromenus/stylesheets/microbrowsers.css +42 -0
  35. data/apps/default/config/helpers/multi_messenger.yml +5 -1
  36. data/apps/default/config/migration.rb +10 -0
  37. data/apps/default/extensions.rb +1 -1
  38. data/apps/default/helpers/factorial.alien.c +3 -3
  39. data/apps/default/helpers/lookup.rb +2 -1
  40. data/apps/default/helpers/manager_proxy.rb +67 -15
  41. data/apps/default/helpers/micromenus.rb +173 -31
  42. data/apps/default/helpers/multi_messenger.rb +20 -3
  43. data/lib/adhearsion.rb +218 -88
  44. data/lib/constants.rb +1 -0
  45. data/lib/core_extensions.rb +15 -9
  46. data/lib/phone_number.rb +85 -0
  47. data/lib/rami.rb +3 -2
  48. data/lib/servlet_container.rb +47 -24
  49. data/lib/sexy_migrations.rb +70 -0
  50. data/test/asterisk_module_test.rb +9 -9
  51. data/test/specs/numerical_string_spec.rb +53 -0
  52. metadata +31 -11
  53. data/apps/default/config/helpers/micromenus/javascripts/builder.js +0 -131
  54. data/apps/default/config/helpers/micromenus/javascripts/controls.js +0 -834
  55. data/apps/default/config/helpers/micromenus/javascripts/effects.js +0 -956
  56. data/apps/default/config/helpers/micromenus/javascripts/prototype.js +0 -2319
  57. data/apps/default/config/helpers/micromenus/javascripts/scriptaculous.js +0 -51
  58. data/apps/default/config/helpers/micromenus/javascripts/slider.js +0 -278
  59. data/apps/default/config/helpers/micromenus/javascripts/unittest.js +0 -557
  60. data/apps/default/config/helpers/micromenus/stylesheets/firefox.css +0 -10
  61. data/apps/default/config/helpers/micromenus/stylesheets/firefox.xul.css +0 -44
@@ -7,10 +7,10 @@ require 'xmpp4r-simple'
7
7
  class MultiMessenger
8
8
 
9
9
  Format = /\A[\w\._%-]+@[\w\.-]+\.[a-zA-Z]{2,4}\z/
10
-
11
- def initialize username, password
10
+ def initialize username, password, accept_subs=false
12
11
  @username, @password = username, password
13
12
  @connection = Jabber::Simple.new username, password
13
+ @connection.accept_subscriptions = accept_subs
14
14
  end
15
15
 
16
16
  def connected?() @connection.connected? end
@@ -28,9 +28,26 @@ config = $HELPERS['multi_messenger']
28
28
 
29
29
  username = config['username']||''
30
30
  password = config['password']||''
31
+ accept_subs = config['accept_subscriptions']
31
32
 
32
33
  log "MultiMessenger: Connecting to #{username}"
33
- jabber = MultiMessenger.new username, password
34
+ jabber = MultiMessenger.new username, password, accept_subs
35
+
36
+ jabber.connection.received_messages do |msg|
37
+ # Need to do something with each message? Do it here.
38
+ end
39
+
40
+ jabber.connection.presence_updates do |friend, old_presence, new_presence|
41
+ # Handle presence updates here, optionally
42
+ end
43
+
44
+ jabber.connection.subscription_requests do |friend, presence|
45
+ # When a subscription request comes in, handle it here.
46
+ end
47
+
48
+ jabber.connection.new_subscriptions do |friend, presence|
49
+ # Handle each subscription notification
50
+ end
34
51
 
35
52
  InstantMessenger.use_service jabber
36
53
  $HUTDOWN.hook { jabber.connection.disconnect }
@@ -16,6 +16,7 @@
16
16
  # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17
17
 
18
18
  require 'core_extensions'
19
+ require 'phone_number'
19
20
 
20
21
  module Asterisk
21
22
 
@@ -41,18 +42,18 @@ module Asterisk
41
42
  # AGI. This code has been kept here in case these bugs are fixed.
42
43
  def old_input digits=nil, user_options=Hash.new('')
43
44
  used_options = {:variable => String.random, :digits => digits}
44
- used_options.default = ''
45
- used_options.merge! user_options
46
- args = []
47
- %w(variable soundfile digits option attempts timeout).each do |x|
48
- args << used_options[x.to_sym]
49
- end
50
- log "THESE ARE THE INPUT ARGS: " + args.inspect
51
- exec :read, args
52
- $OSCAR[:VARS] << args.first
53
- x = get_variable(args.first).gsub(/[\(\)]/, "")
54
- debug "RETURN: #{x.inspect}"
55
- x.simplify
45
+ used_options.default = ''
46
+ used_options.merge! user_options
47
+ args = []
48
+ %w(variable soundfile digits option attempts timeout).each do |x|
49
+ args << used_options[x.to_sym]
50
+ end
51
+ log "THESE ARE THE INPUT ARGS: " + args.inspect
52
+ exec :read, args
53
+ $OSCAR[:VARS] << args.first
54
+ x = get_variable(args.first).gsub(/[\(\)]/, "")
55
+ debug "RETURN: #{x.inspect}"
56
+ x.simplify
56
57
  end
57
58
 
58
59
  # Input is used to receive keypad input from the user, pausing until they
@@ -66,6 +67,8 @@ module Asterisk
66
67
  #
67
68
  # When called without any arguments (or a first argument of -1), the user is
68
69
  # able to enter digits ad infinitum until they press the pound (#) key.
70
+ #
71
+ # Note: input() does NOT catch "#" character! Use wait_for_digit instead.
69
72
  def input digits=nil, hash={}
70
73
  timeout, file = (hash[:timeout] || -1), (hash[:play] || hash[:file] || 'beep')
71
74
  result = rawr "GET DATA #{file} #{timeout} #{digits}"
@@ -150,7 +153,7 @@ module Asterisk
150
153
  # returns nil. If the asterisk or pound key was pressed, a String is returned of that
151
154
  # response. In all other cases, the Fixnum of the number pressed is returned.
152
155
  def wait_for_digit timeout=-1
153
- digit = rawr("WAIT FOR DIGIT #{timeout < 0 ? -1 : timeout / 1000.0}").match(/=(.*)$/)[1].to_i
156
+ digit = rawr("WAIT FOR DIGIT #{timeout < 0 ? -1 : timeout * 1000.0}").match(/=(.*)$/)[1].to_i
154
157
  return nil if digit <= 0 # If there was an error or timeout
155
158
  return digit.chr if (32..45).include? digit
156
159
  digit - ?0
@@ -167,12 +170,34 @@ module Asterisk
167
170
  end
168
171
  end
169
172
 
173
+ def detach!
174
+ PBX.io.close
175
+ end
176
+
170
177
  # An abstracted remote accessor for retrieving Asterisk variables.
171
178
  def get_variable(key, default=nil)
172
179
  result = rawr "GET VARIABLE #{key}"
173
- result[0..12] == "200 result=0" ? default : result[13..-1]
180
+ result[0..12] == "200 result=0" ? default : result[14..-2].strip
174
181
  end
175
182
 
183
+ # Plays a tone over the call.
184
+ #
185
+ # Usage:
186
+ # - tone:busy
187
+ # - tone:dial
188
+ # - tone:info
189
+ # - tone:record
190
+ # - tone "3333/33,0/15000" # Random custom tone
191
+ def tone type
192
+ exec 'PlayTones', (case type
193
+ when :busy then "480+620/500,0/500"
194
+ when :dial then "440+480/2000,0/4000"
195
+ when :info then "!950/330,!1400/330,!1800/330,0"
196
+ when :record then "1400/500,0/15000"
197
+ else type
198
+ end)
199
+ end
200
+
176
201
  # == Dialing Users
177
202
  #
178
203
  # This method will be used in most dial plans written with Adhearsion.
@@ -230,7 +255,18 @@ module Asterisk
230
255
  # last_dial_status. Note: last_dial_status returns a Symbol representing
231
256
  # the result of the last call. For more information, see its appropriate
232
257
  # documentation.
233
- #
258
+ #
259
+ # === Setting CallerID
260
+ #
261
+ # To set the callerid number, simply pass either a number or a String of the
262
+ # digits to dial() as a Symbol key argument.
263
+ #
264
+ # dial :crusher, :callerid => 1_555_123_4567
265
+ # dial :picard, :callerid => 12224561701
266
+ # dial :laforge, :callerid => '14442223333'
267
+ #
268
+ # === Dial options
269
+ #
234
270
  # Additionally, you can use the :options hash key argument to specify options
235
271
  # which Asterisk's Dial() application normally takes. For example:
236
272
  #
@@ -243,7 +279,17 @@ module Asterisk
243
279
  # http://www.voip-info.org/wiki-Asterisk+cmd+Dial
244
280
  def dial who, hash={}
245
281
  # if who.is_a_group? TODO: Set the CDR GROUP() value if a Group is dialed.
246
- exec :dial, properize(who), *[hash[:for], hash[:options]]
282
+
283
+ # TODO Must be provided as a number until the Set(CALLERID(name)=something)
284
+ # stuff is figured out.
285
+ cid = hash.delete :callerid
286
+ if cid
287
+ cid = cid.to_s
288
+ cid = "1" << cid if cid.size == 10
289
+ rawr %(SET CALLERID "#{cid}") if cid
290
+ end
291
+
292
+ exec :dial, PBX.properize(who), *[hash[:for], hash[:options]]
247
293
  end
248
294
 
249
295
  # As you might expect, last_call_successful? returns a boolean depending on whether
@@ -258,6 +304,11 @@ module Asterisk
258
304
  !last_call_successful?
259
305
  end
260
306
 
307
+ # Send a DTMF tone over the line
308
+ def dtmf digits
309
+ exec "SendDTMF", digits.to_s
310
+ end
311
+
261
312
  # The stream_file() method comes right over from the AGI protocol.
262
313
  # Its use is very similar to the Background() application from
263
314
  # Asterisk's extensions.conf language. A file is played in the
@@ -293,8 +344,29 @@ module Asterisk
293
344
  # Festival is pretty buggy (and pretty inefficient). In theory,
294
345
  # this should speak out any text supplied to it.
295
346
  def speak text
296
- text.gsub!('\n').gsub!('\r').strip!
297
- exec :festival, "'#{text}'"
347
+ text.gsub! "\n",''
348
+ text.gsub! "\r",''
349
+ text.strip!
350
+ exec :Festival, "#{text.inspect}"
351
+ end
352
+
353
+ def build_local first, second=nil
354
+ num = 1
355
+ context = nil
356
+ if first.kind_of? Fixnum
357
+ num = first
358
+ context = second if second
359
+ else
360
+ context = first
361
+ num = second if second
362
+ end
363
+ returning("Local/#{num}") do |s|
364
+ s << "@#{context}" if context
365
+ end
366
+ end
367
+
368
+ def external_invoke *args
369
+ dial build_local(*args)
298
370
  end
299
371
 
300
372
  # Since voicemail is used so frequently, extra effort has been made to
@@ -362,12 +434,40 @@ module Asterisk
362
434
  def play *files
363
435
  files.flatten!
364
436
  files.each do |f|
365
- if f.simplify.is_a? Fixnum then exec :saynumber, f
437
+ if f.kind_of? Time
438
+ exec :SayUnixTime, f.to_i
439
+ elsif f.kind_of? Symbol then exec :playback, f
440
+ elsif f[0] == ?$
441
+ # Speak a currency. TODO: support currencies other than dollars.
442
+ full,dollars,decimal,cents = *f.match(/^\$(\d+)(\.(\d{1,2}).*)?$/)
443
+ if full
444
+ exec :saynumber, dollars
445
+ exec :playback, (dollars == '1') ? 'dollar' : 'dollars'
446
+ if decimal
447
+ exec :playback, 'and'
448
+ exec :saynumber, cents
449
+ exec :playback, (cents == '1') ? 'cent' : 'cents'
450
+ end
451
+ else exec :playback, f
452
+ end
453
+ elsif f.simplify.kind_of? Numeric then exec :saynumber, f
366
454
  else exec :playback, f
367
455
  end
368
456
  end
369
457
  end
370
458
 
459
+ # Compiles the provided Asterisk dialplan pattern into a Ruby regular
460
+ # expression. For more usage of Asterisk's pattern syntax, see
461
+ # http://www.voip-info.org/wiki/view/Asterisk+Dialplan+Patterns
462
+ def _ pattern
463
+ # Uncomment the following code fragment for complete compatability.
464
+ # The fragment handles the seldom-used hyphen number spacer with no
465
+ # meaning.
466
+ Regexp.new '^' << pattern.#gsub(/(?!\[[\w+-]+)-(?![\w-]+\])/,'').
467
+ gsub('X', '[0-9]').gsub('Z', '[1-9]').gsub('N','[2-9]').
468
+ gsub('.','.+').gsub('!','.*') << '$'
469
+ end
470
+
371
471
  # == Dialplan Instruction Caching
372
472
  #
373
473
  # If a cynic ever says Ruby doesn't make an efficient dial plan manager,
@@ -548,37 +648,7 @@ module Asterisk
548
648
  end
549
649
 
550
650
  # Very simply sends the command over the AGI IO socket and forgets about it.
551
- def putc(what) PBX.io.print "#{what}" end
552
-
553
- # The magical method that handles how objects passed to dial() are converted to their
554
- # corresponding Asterisk-recognizable technology/extension identifier. This likely
555
- # wouldn't be used much outside of dial(), but you may find it useful.
556
- def properize who
557
- # These are the convention methods for Group-like objects
558
- group_method = who.is_a_group?
559
-
560
- # If 'who' has any of the possible_methods, replace 'who' with the return value
561
- # of that method
562
- who = who.send group_method if group_method
563
- return nil unless who
564
-
565
- # In case we can't perform collection algorithms on 'who', let's encapsulate it in
566
- # an Array
567
- who = [who] unless who.kind_of?(Enumerable) && !who.kind_of?(String)
568
-
569
- user_method = who.first.is_a_user?
570
- # If the first thing in the Enumerable responds to a User-like convention, then
571
- # set 'who' equal to the extension(s) accessor of those objects. We refine the "who"
572
- # argument more and more in this way until it's finally in a way Asterisk can read.
573
- who.map! { |p| p.send user_method }.compact! if user_method
574
-
575
- # Now replace each item in the Enumerable with its form converted to
576
- # extension (assuming it's not already in that format)
577
- who.map! do |ext|
578
- (ext.kind_of?(String) && ext.index(?/)) ? ext : "SIP/#{ext}"
579
- end
580
- who * '&' # Finally, join() anything left in the Array with an '&'
581
- end
651
+ def putc(what) PBX.io.print "#{what}" end
582
652
 
583
653
  # Returns the status of the last dial(). Possible dial
584
654
  # statuses include :answer, :busy, :noanswer, :cancel,
@@ -600,59 +670,119 @@ module Asterisk
600
670
  # Direct translation of the Asterisk NoOp() application. Used primarily for
601
671
  # viewing debug information in the Asterisk CLI.
602
672
  def noop(*options) rawr "NOOP #{options}" end
603
- end
604
-
605
- # This SIP class abstracts several SIP-related functions. In your dialplan,
606
- # if you should want to set a SIP header specifically, simply use the class
607
- # []= operator. For example, you may want to do SIP['Alert-Info'] = 'Ring-Answer'
608
- # which will affect the next SIP call placed. Additionally, you can use this
609
- # class's division operator to naturally represent "technology/channel"
610
- # endpoints (e.g. SIP/1550 or SIP/:tweedledum)
611
- class SIP
612
-
613
- # This will set the header value specified within the square bracked to the
614
- # value given after the equals sign. Note: this can only be called if the
615
- # current call's thread is handling an Asterisk call over AGI (i.e. dialplan).
616
- def self.[]=(header, value)
617
- exec :SipAddHeader, "#{header}: #{value}"
618
- end
619
673
 
620
- # Simple convenience method to make Adhearsion's DSL more Asterisk-like.
621
- # Virtually anything can be provided after the "/", as long as its to_s()
622
- # method is what you intended to represent.
623
- def self./(ext) "SIP/#{ext}" end
624
674
  end
625
675
 
626
- class IAX
627
- # Simple convenience method to make Adhearsion's DSL more Asterisk-like.
628
- # Virtually anything can be provided after the "/", as long as its to_s()
629
- # method is what you intended to represent.
630
- def self./(arg=nil) "IAX2/#{arg}" end
676
+ # # This Endpoint module is only used when using the
677
+ # # forward-slash operator on the dialplan technology
678
+ # # classes such as IAX, SIP, Zap, Local, SCCP, etc.
679
+ # # The String's eigencalss returned from IAX/:foobar
680
+ # # extends this module.
681
+ # module Endpoint
682
+ # def /(x) to_s << "/" << x.to_s end
683
+ # end
684
+ #
685
+ # # Use like IAX/:trunk/12345
686
+ # class IAX
687
+ # def self./(x) "IAX2/#{x}".extend Endpoint end
688
+ # end
689
+ # # Use like SIP/:trunk/12345
690
+ # class SIP
691
+ # def self./(x) "SIP/#{x}".extend Endpoint end
692
+ # end
693
+ # # Use like SCCP/:trunk/12345
694
+ # class SCCP
695
+ # def self./(x) "SCCP/#{x}".extend Endpoint end
696
+ # end
697
+ # # Use like Zap/:channel/12345
698
+ # class Zap
699
+ # def self./(x) "ZAP/#{x}".extend Endpoint end
700
+ # end
701
+ # # Use like Local/'extension@context'
702
+ # class Local
703
+ # def self./(x) "Local/#{x}".extend Endpoint end
704
+ # end
705
+ # class GTalk
706
+ # def self./(x) "gtalk/#{x}".extend Endpoint end
707
+ # end
708
+ #
709
+ # # Use like IAX2/:trunk/12345
710
+ # class IAX2 < IAX;end
711
+ # # Use like ZAP/:channel/12345
712
+ # class ZAP < Zap;end
713
+
714
+ # Contributed by Bruce Williams of Five Runs.
715
+ module DialplanTechnologies
716
+ module Endpoint
717
+ def /(x) to_s << "/" << x.to_s end
718
+ end
719
+
720
+ class Base
721
+ def self./(x) "#{self}/#{x}".extend(Endpoint) end
722
+ end
723
+
724
+ class << self
725
+ def define(&block)
726
+ instance_eval(&block)
727
+ end
728
+ def add(*technologies)
729
+ options = technologies.last.is_a?(Hash) ? technologies.pop : {}
730
+ technologies.each do |name|
731
+ eval %{class ::#{name} < Base; def self.to_s; "#{options[:as] || name}"; end; end}
732
+ end
733
+ end
734
+ end
735
+
631
736
  end
632
737
 
633
- class ZAP
634
- # Simple convenience method to make Adhearsion's DSL more Asterisk-like.
635
- # Virtually anything can be provided after the "/", as long as its to_s()
636
- # method is what you intended to represent.
637
- def self./(arg=nil) "Zap/#{arg}" end
738
+ DialplanTechnologies.define do
739
+ add :IAX2, :SIP, :SCCP, :ZAP, :Local
740
+ add :GTalk, :as => :gtalk
741
+ add :Zap, :as => :ZAP
742
+ add :IAX, :as => :IAX2
638
743
  end
639
744
 
640
- # For users who may try to use the (proper) IAX2 form. See IAX for more info.
641
- class IAX2 < IAX; end
642
-
643
- # For users who may try to use the (common) "Zap" form. See ZAP for more info.
644
- class Zap < ZAP; end
645
745
 
646
746
  # The PBX object is an object manifestation of the PBX with which Adhearsion will be
647
747
  # associated. Helpers often open up this class and add methods to it. Note: when doing
648
748
  # this in your own helpers, all methods will need to be class (a.k.a. static) methods
649
749
  # since no actual instance of this object is passed around.
650
750
  class PBX
651
- def method_missing name
751
+ def self.method_missing name
652
752
  raise NameError, "Method #{name} not found! Are you sure manager_proxy is configured properly?"
653
753
  end
754
+
755
+ # The magical method that handles how objects passed to dial() are converted to their
756
+ # corresponding Asterisk-recognizable technology/extension identifier. This likely
757
+ # wouldn't be used much outside of dial(), but you may find it useful.
758
+ def self.properize who
759
+
760
+ group_method = who.is_a_group?
761
+
762
+ # If 'who' has any of the possible_methods, replace 'who' with the return value
763
+ # of that method
764
+ who = who.send group_method if group_method
765
+ return nil unless who
766
+
767
+ # In case we can't perform collection algorithms on 'who', let's encapsulate it in
768
+ # an Array
769
+ who = [who] unless who.kind_of?(Enumerable) && !who.kind_of?(String)
770
+
771
+ user_method = who.first.is_a_user?
772
+ # If the first thing in the Enumerable responds to a User-like convention, then
773
+ # set 'who' equal to the extension(s) accessor of those objects. We refine the "who"
774
+ # argument more and more in this way until it's finally in a way Asterisk can read.
775
+ who.map! { |p| p.send user_method }.compact! if user_method
776
+
777
+ # Now replace each item in the Enumerable with its form converted to
778
+ # extension (assuming it's not already in that format)
779
+ who.map! do |ext|
780
+ (ext.kind_of?(String) && ext.index(?/)) ? ext : "SIP/#{ext}"
781
+ end
782
+ who * '&' # Finally, join() anything left in the Array with an '&'
783
+ end
654
784
 
655
- def PBX.io() Thread.current[:io] end
785
+ def self.io() Thread.current[:io] end
656
786
  end
657
787
 
658
788
  # The Contexts class is a blank slate in which the extensions.rb file is evaluated.
@@ -822,4 +952,4 @@ end
822
952
  # is one of the names you're signed into AIM with. Note: this example doesn't use the :through
823
953
  # Hash-key argument because it assumes no other helpers are installed with this same format.
824
954
  # If you had a MSN or Yahoo helper installed too, you'd need to do specify :through because
825
- def im(sn, msg, hash={}) InstantMessenger.im(sn,msg,hash) end
955
+ def im(sn, msg, hash={}) InstantMessenger.im(sn,msg,hash) end