adhearsion 0.7.6 → 0.7.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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