jabbot 0.3.1 → 0.3.2

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.
data/README.md CHANGED
@@ -27,7 +27,7 @@ and it works great :)
27
27
 
28
28
  # Respond to query if they come from the right crowd
29
29
  # query "message" => "user" is just some syntax sugar
30
- # query "message", "user" will work to
30
+ # query "message", "user" will work, too
31
31
  query :from => [:cjno, :irbno] do |message, params|
32
32
  post "#{message.user} I agree" => message.user
33
33
  end
@@ -48,6 +48,8 @@ Jabbot uses the [at\_exit hook](http://ruby-doc.org/core/classes/Kernel.html#M00
48
48
 
49
49
  ### Configuration
50
50
 
51
+ _Deprecated: The option to configure by YAML files will be removed in the next stable release._
52
+
51
53
  Jabbot looks for a configuration file in ./config/bot.yml. It should contain
52
54
  atleast:
53
55
 
@@ -12,7 +12,7 @@ require 'jabbot/handlers.rb'
12
12
  require 'jabbot/macros.rb'
13
13
 
14
14
  module Jabbot
15
- VERSION = '0.3.1'
15
+ VERSION = '0.3.2'
16
16
 
17
17
  @@app_file = lambda do
18
18
  ignore = [
@@ -29,22 +29,21 @@ module Jabbot
29
29
  path || $0
30
30
  end.call
31
31
 
32
+ # Public: File name of the application file (inspired by Sinatra).
32
33
  #
33
- # File name of the application file. Inspired by Sinatra
34
- #
34
+ # Returns the String application filename.
35
35
  def self.app_file
36
36
  @@app_file
37
37
  end
38
38
 
39
+ # Public: Determines if the application should be auto-run.
39
40
  #
40
- # Runs application if application file is the script being executed
41
- #
41
+ # Returns a Boolean indicatin wether to auto-run the application or not.
42
42
  def self.run?
43
43
  self.app_file == $0
44
44
  end
45
45
 
46
- end # module Jabbot
46
+ end
47
47
 
48
+ # xmpp4r runs in another thread.
48
49
  Thread.abort_on_exception = true
49
-
50
- # EOF
@@ -2,22 +2,38 @@ require 'logger'
2
2
  require File.join(File.expand_path(File.dirname(__FILE__)), 'macros')
3
3
  require File.join(File.expand_path(File.dirname(__FILE__)), 'handlers')
4
4
 
5
+ require 'eventmachine'
6
+
5
7
  module Jabbot
8
+ # The main Bot class.
6
9
  #
7
- # Main bot "controller" class
8
- #
10
+ # It handles the connection as well as the method dispatching.
9
11
  class Bot
10
12
  include Jabbot::Handlers
11
13
  attr_reader :client
12
14
  attr_reader :users
13
15
 
14
- Message = Struct.new(:user, :text, :time) do
16
+ Message = Struct.new(:user, :text, :time, :type) do
15
17
  def to_s
16
18
  "#{user}: #{text}"
17
19
  end
20
+
21
+ # Encode a message in JSON
22
+ # A message is just a hash of its values
23
+ def to_json(*a)
24
+ {
25
+ :user => user,
26
+ :text => text,
27
+ :time => time,
28
+ :type => type
29
+ }.to_json(*a)
30
+ end
18
31
  end
19
32
 
20
- def initialize(options = nil)
33
+ # Public: Initialize a Bot instance.
34
+ #
35
+ # options - A Jabbot::Config options instance (default: nil).
36
+ def initialize(options=nil)
21
37
  @conf = nil
22
38
  @config = options || Jabbot::Config.default << Jabbot::FileConfig.new
23
39
  @log = nil
@@ -28,18 +44,25 @@ module Jabbot
28
44
  raise SystemExit.new(krash.message)
29
45
  end
30
46
 
31
- # Enable debugging mode.
47
+ # Internal: Enable debugging mode.
48
+ #
32
49
  # All xmpp4r-internal calls to Jabber::Debuglog are
33
50
  # printed to $stderr by default.
34
51
  # You may change the logger by using
52
+ #
35
53
  # Jabber::Logger = Logger.new(…)
54
+ #
55
+ # Returns nothing.
36
56
  def debug!
37
57
  Jabber::debug = true
38
58
  end
39
59
 
60
+ # Internal: Connect to Jabber and join channel.
40
61
  #
41
- # connect to Jabber and join channel
62
+ # It will exit the process and log any exception
63
+ # on `$stderr` on failure.
42
64
  #
65
+ # Returns nothing.
43
66
  def connect
44
67
  @jid = Jabber::JID.new(login)
45
68
  @mucjid = Jabber::JID.new("#{channel}@#{server}")
@@ -64,24 +87,30 @@ module Jabbot
64
87
  $stderr.puts args.inspect
65
88
  $stderr.puts "exiting..."
66
89
 
90
+ EventMachine.stop_event_loop
67
91
  exit
68
92
  end
69
- @connected = true
70
93
  begin
71
94
  @client.connect
72
95
  @client.auth(password)
73
96
  @muc = Jabber::MUC::SimpleMUCClient.new(@client)
74
97
  muc_handlers.call(@muc)
75
98
  @muc.join(@mucjid)
76
- rescue => errmsg
99
+ @connected = true
100
+ rescue => errmsg
101
+ @connected = false
77
102
  $stderr.write "#{errmsg.class}\n#{errmsg}, #{errmsg.backtrace.join("\n")}"
78
103
  exit 1
79
104
  end
80
105
  end
81
106
 
107
+ # Public: Starts the jabber bot.
82
108
  #
83
- # Run application
109
+ # Internally it starts the jabber connection inside of `EventMachine.run`,
110
+ # so you are free to use all EventMachine tasks out there for asynchronously
111
+ # working on input data.
84
112
  #
113
+ # Returns nothing.
85
114
  def run!
86
115
  puts "Jabbot #{Jabbot::VERSION} imposing as #{login} on #{channel}@#{server}"
87
116
 
@@ -95,31 +124,29 @@ module Jabbot
95
124
  Kernel.trap(:QUIT, onclose_block)
96
125
 
97
126
  debug! if config[:debug]
98
- connect
99
- poll
100
- end
101
127
 
102
- #
103
- # just a lame infinite loop to keep the bot alive while he is connected
104
- # :)
105
- #
106
- def poll
107
- while connected?
108
- break unless connected?
109
- sleep 1
128
+ # connect the bot and keep it running
129
+ EventMachine.run do
130
+ connect
131
+
132
+ stop_timer = EventMachine.add_periodic_timer(1) do
133
+ if !connected?
134
+ EventMachine.stop_event_loop
135
+ end
136
+ end
110
137
  end
111
138
  end
112
139
 
140
+ # Internal: Get information if the bot is still connected.
113
141
  #
114
- # still connected?
115
- #
142
+ # Returns the connection state as a Boolean.
116
143
  def connected?
117
144
  @connected
118
145
  end
119
146
 
147
+ # Public: Close the server connection.
120
148
  #
121
- # close connection
122
- #
149
+ # Returns nothing.
123
150
  def close
124
151
  if connected?
125
152
  @connected = false
@@ -128,23 +155,33 @@ module Jabbot
128
155
  end
129
156
  alias_method :quit, :close
130
157
 
158
+ # Public: Send a message to a given user or publicly.
131
159
  #
132
- # send message
133
- # alternative: send query to user
160
+ # msg - A String message.
161
+ # to - A String username to send to (default: nil).
134
162
  #
163
+ # Returns nothing.
135
164
  def send_message(msg, to=nil)
136
- @muc.say(msg.to_s, to)
165
+ @muc.say(msg.to_s, to) if connected?
137
166
  end
138
167
 
168
+ # Internal: Assigns handlers for different xmpp messages.
169
+ #
170
+ # The handled messages are:
139
171
  #
140
- # defines what to do on different actions
172
+ # * public messages
173
+ # * private messages
174
+ # * joins
175
+ # * leaves
176
+ # * subject changes
141
177
  #
178
+ # Returs nothing.
142
179
  def muc_handlers
143
180
  Proc.new do |muc|
144
181
  muc.on_message do |time, nick, text|
145
182
  if time.nil?
146
183
  begin
147
- dispatch_messages(:message, [Message.new(nick, text, Time.now)]) unless nick == config[:nick]
184
+ dispatch_messages(:message, [Message.new(nick, text, Time.now, :public)]) unless nick == config[:nick]
148
185
  rescue Exception => boom
149
186
  log.fatal boom.inspect
150
187
  log.fatal boom.backtrace[0..5].join("\n")
@@ -155,7 +192,7 @@ module Jabbot
155
192
  muc.on_private_message do |time, nick, text|
156
193
  if time.nil?
157
194
  begin
158
- dispatch_messages(:private, [Message.new(nick, text, Time.now)]) unless nick == config[:nick]
195
+ dispatch_messages(:private, [Message.new(nick, text, Time.now, :query)]) unless nick == config[:nick]
159
196
  rescue Exception => boom
160
197
  log.fatal boom.inspect
161
198
  log.fatal boom.backtrace[0..5].join("\n")
@@ -169,7 +206,7 @@ module Jabbot
169
206
  end
170
207
  if time.nil?
171
208
  begin
172
- dispatch_messages(:join, [Message.new(nick, "join", Time.now)]) unless nick == config[:nick]
209
+ dispatch_messages(:join, [Message.new(nick, "join", Time.now, :join)]) unless nick == config[:nick]
173
210
  rescue Exception => boom
174
211
  log.fatal boom.inspect
175
212
  log.fatal boom.backtrace[0..5].join("\n")
@@ -181,7 +218,7 @@ module Jabbot
181
218
  @users.delete(nick)
182
219
  if time.nil?
183
220
  begin
184
- dispatch_messages(:leave, [Message.new(nick, "leave", Time.now)])
221
+ dispatch_messages(:leave, [Message.new(nick, "leave", Time.now, :leave)])
185
222
  rescue Exception => boom
186
223
  log.fatal boom.inspect
187
224
  log.fatal boom.backtrace[0..5].join("\n")
@@ -192,32 +229,30 @@ module Jabbot
192
229
  muc.on_subject do |time, nick, subject|
193
230
  if time.nil?
194
231
  begin
195
- dispatch_messages(:subject, [Message.new(nick, subject, Time.now)])
232
+ dispatch_messages(:subject, [Message.new(nick, subject, Time.now, :subject)])
196
233
  rescue Exception => boom
197
234
  log.fatal boom.inspect
198
235
  log.fatal boom.backtrace[0..5].join("\n")
199
236
  end
200
237
  end
201
238
  end
202
-
203
- # not working
204
- #muc.on_self_leave do |*args|
205
- # p args
206
- #end
207
239
  end
208
240
  end
209
241
 
242
+ # Dispatch a collection of messages.
210
243
  #
211
- # Dispatch a collection of messages
244
+ # type - The Symbol type to be processed.
245
+ # messages - An Array of String messages to be dispatched.
212
246
  #
247
+ # Returns the Integer count of messages dispatched.
213
248
  def dispatch_messages(type, messages)
214
249
  messages.each { |message| dispatch(type, message) }
215
250
  messages.length
216
251
  end
217
252
 
253
+ # Internal: Instanciates a logger.
218
254
  #
219
- # Return logger instance
220
- #
255
+ # Returns logger instance.
221
256
  def log
222
257
  return @log if @log
223
258
  os = config[:log_file] ? File.open(config[:log_file], "a") : $stdout
@@ -226,17 +261,18 @@ module Jabbot
226
261
  @log
227
262
  end
228
263
 
264
+ # Public: Set configure options for the bot.
229
265
  #
230
- # Configure bot
231
- #
266
+ # Returns the configure Hash.
232
267
  def configure
233
268
  yield @config
234
- @conf = nil
269
+ @conf = @config.to_hash
235
270
  end
236
271
 
272
+ # Internal: Maps configuration settings to real methods.
237
273
  #
238
- # Map configuration settings
239
- #
274
+ # Returns the value of the configuration setting
275
+ # or nil if none is found.
240
276
  def method_missing(name, *args, &block)
241
277
  return super unless config.key?(name)
242
278
 
@@ -244,9 +280,9 @@ module Jabbot
244
280
  config[name]
245
281
  end
246
282
 
283
+ # Public: Get the current configuration settings.
247
284
  #
248
- # Return configuration
249
- #
285
+ # Returns the configuration Hash.
250
286
  def config
251
287
  return @conf if @conf
252
288
  @conf = @config.to_hash
@@ -257,7 +293,7 @@ end
257
293
  # Expose DSL
258
294
  include Jabbot::Macros
259
295
 
260
- # Run bot if macros has been used
296
+ # Run bot if macros has been used.
261
297
  at_exit do
262
298
  raise $! if $!
263
299
  @@bot.run! if run?
@@ -1,7 +1,6 @@
1
1
  require 'optparse'
2
2
 
3
3
  module Jabbot
4
- #
5
4
  # Jabbot configuration. Use either Jabbot::CliConfig.new or
6
5
  # JabbotFileConfig.new setup a new bot from either command line or file
7
6
  # (respectively). Configurations can be chained so they override each other:
@@ -36,19 +35,23 @@ module Jabbot
36
35
  @settings = settings
37
36
  end
38
37
 
38
+ # Public: Add a configuration object to override given settings
39
39
  #
40
- # Add a configuration object to override given settings
40
+ # config -
41
41
  #
42
+ # Returns the class object.
42
43
  def add(config)
43
44
  @configs << config
44
45
  self
45
46
  end
46
-
47
47
  alias_method :<<, :add
48
48
 
49
+ # Internal: Maps calls to non existant functions to
50
+ # configuration values, if they exist.
49
51
  #
50
- # Makes it possible to access configuration settings as attributes
52
+ # name, *args and &block as described in the core classes.
51
53
  #
54
+ # Returns the configuration value if any.
52
55
  def method_missing(name, *args, &block)
53
56
  regex = /=$/
54
57
  attr_name = name.to_s.sub(regex, '').to_sym
@@ -61,9 +64,9 @@ module Jabbot
61
64
  @settings[attr_name]
62
65
  end
63
66
 
67
+ # Public: Merges configurations and returns a hash with all options
64
68
  #
65
- # Merges configurations and returns a hash with all options
66
- #
69
+ # Returns a Hash of the configuration.
67
70
  def to_hash
68
71
  hash = {}.merge(@settings)
69
72
  @configs.each { |conf| hash.merge!(conf.to_hash) }
@@ -75,18 +78,17 @@ module Jabbot
75
78
  end
76
79
  end
77
80
 
78
- #
79
- # Configuration from files
80
- #
81
+ # Deprecated: Configuration from files
81
82
  class FileConfig < Config
82
-
83
+ # Public: Initializes a new FileConfig object.
83
84
  #
84
- # Accepts a stream or a file to read configuration from
85
- # Default is to read configuration from ./config/bot.yml
86
- #
87
- # If a stream is passed it is not closed from within the method
88
85
  #
86
+ # fos - Accepts a Stream or a String filename to read configuration from
87
+ # (default: "./config/bot.yml")
88
+ # If a stream is passed it is not closed from within the method.
89
89
  def initialize(fos = File.expand_path("config/bot.yml"))
90
+ warn "Jabbot::FileConfig is deprecated and will be removed in the next version."
91
+
90
92
  stream = fos.is_a?(String) ? File.open(fos, "r") : fos
91
93
 
92
94
  begin
@@ -1,17 +1,30 @@
1
1
  module Jabbot
2
2
  module Handlers
3
+ # Public: Add a handler for a given type.
3
4
  #
4
- # Add a handler for this bot
5
+ # type - The Symbol representation of the type to be handled.
6
+ # handler - The Jabbot::Handler instance to handle a message.
5
7
  #
8
+ # Returns the handler.
6
9
  def add_handler(type, handler)
7
10
  handlers[type] << handler
8
11
  handler
9
12
  end
10
13
 
14
+ # Public: Dispatch a message based on is type.
15
+ #
16
+ # type - The Symbol representation of the type to be dispatched.
17
+ # message - The String message to be handled.
18
+ #
19
+ # Returns nothing.
11
20
  def dispatch(type, message)
12
21
  handlers[type].each {|handler| handler.dispatch(message) }
13
22
  end
14
23
 
24
+ # Internal: Setup Arrays of all handler types.
25
+ #
26
+ # Returns a Hash containing the possible handler types and
27
+ # its associated Arrays of handlers.
15
28
  def handlers
16
29
  @handlers ||= {
17
30
  :message => [],
@@ -22,20 +35,51 @@ module Jabbot
22
35
  }
23
36
  end
24
37
 
38
+ # Deprecated: Set the handler types and Arrays
39
+ #
40
+ # hash - A hash containing the handler types and associated Arrays
41
+ # (see `handlers`).
42
+ #
43
+ # Returns nothing.
25
44
  def handlers=(hash)
26
45
  @handlers = hash
27
46
  end
28
47
  end
29
48
 
30
- #
31
- # A Handler object is an object which can handle a direct message, tweet or
32
- # at reply.
49
+ # A Handler consists of a pattern to match a given message,
50
+ # some options and a handler block to be called on dispatch.
33
51
  #
34
52
  class Handler
53
+ # Public: Initialize a new handler instance.
54
+ #
55
+ # pattern - The String, Symbol or Regexp pattern to match the messages
56
+ # against or a Hash (default: nil).
57
+ # If pattern is a Hash containing just one key :exact,
58
+ # its value is used as the pattern and should therefore be
59
+ # a String or Regexp.
60
+ # If the pattern is a Hash, but not containing
61
+ # the key :exact, the pattern is set to nil
62
+ # and the passed value is re-used as `options`.
63
+ # A pattern of nil will match every message.
64
+ # options - The Hash options to refine the handler (default: {})
65
+ # :from - A String, Symbol or Array of usernames to
66
+ # accept messages from
67
+ # * - Any String here is later used in the pattern
68
+ # parsing and its value is used as a replacement
69
+ # of the pattern parameter and should be a
70
+ # valid String to be used in a Regexp,
71
+ # containing just one match group.
72
+ # blk - The block to handle a pattern-matched message
73
+ # and respond to it.
74
+ # It will be passed to arguments:
75
+ # message - The actual Message struct.
76
+ # params - An Array of matched params if any.
35
77
  def initialize(pattern = nil, options = {}, &blk)
78
+ @exact_match = false
36
79
  if pattern.is_a?(Hash)
37
80
  if pattern.keys.first == :exact
38
- pattern = /\A#{pattern[:exact]}\Z/
81
+ @exact_match = true
82
+ pattern = pattern[:exact]
39
83
  else
40
84
  options = pattern
41
85
  pattern = nil
@@ -43,72 +87,129 @@ module Jabbot
43
87
  end
44
88
 
45
89
  @options = options
46
- @options[:from].collect! {|s| s.to_s } if @options[:from] && @options[:from].is_a?(Array)
47
- @options[:from] = [@options[:from].to_s] if @options[:from] && [String, Symbol].include?(@options[:from].class)
48
- @handler = nil
90
+ if from = @options[:from]
91
+ if from.respond_to?(:collect)
92
+ @options[:from] = from.collect {|s| s.to_s }
93
+ elsif from.respond_to?(:to_s)
94
+ @options[:from] = [@options[:from].to_s]
95
+ else
96
+ @options[:from] = nil
97
+ end
98
+ end
99
+
49
100
  @handler = block_given? ? blk : nil
50
- self.pattern = pattern
101
+
102
+ # Set pattern (parse it if needed)
103
+ self.pattern = pattern ? pattern.dup : pattern
51
104
  end
52
105
 
106
+ # Internal: Parse pattern string and set parameter options.
107
+ #
108
+ # There are a few special cases:
109
+ #
110
+ # If the pattern is nil, empty or the Symbol :all, the handler is
111
+ # dispatched on all incoming messages for the given type.
53
112
  #
54
- # Parse pattern string and set options
113
+ # If the pattern is a Regexp it is used as-is.
55
114
  #
115
+ # If the pattern is a String or any other Symbol (coerced to a String)
116
+ # it is parsed.
117
+ #
118
+ # Parsing:
119
+ #
120
+ # Every word in the pattern starting with a colon (:) and followed by
121
+ # any non-whitespace characters is used as a parameter match name.
122
+ #
123
+ # Matched pattern names are then replaced to match any
124
+ # non-whitespace character by default.
125
+ # Otherwise defined patterns may be used instead.
126
+ #
127
+ # If @exact_match is set, the resulting pattern is nested
128
+ # between \A and \Z to match a whole string without
129
+ # leading or trailing characters.
130
+ #
131
+ # Example:
132
+ #
133
+ # handler.pattern = "Welcome :me"
134
+ # # => /Welcome ([^\s]+)/
135
+ #
136
+ # handler.pattern = "Welcome :me" # with @exact_match = true
137
+ # # => /\AWelcome ([^\s]+)\Z/
138
+ #
139
+ # options = { "me" => "([aeiou]+)" }
140
+ # handler.pattern = "Welcome :me"
141
+ # # => /Welcome ([aeiou]+)/
142
+ #
143
+ # Returns nothing.
56
144
  def pattern=(pattern)
145
+ @pattern = nil
57
146
  return if pattern.nil? || pattern == '' || pattern == :all
58
147
 
59
148
  if pattern.is_a?(Regexp)
60
149
  @options[:pattern] = pattern
150
+ @pattern = pattern
61
151
  return
62
152
  end
63
153
 
64
- words = pattern.split.collect {|s| s.strip } # Get all words in pattern
65
- @options[:tokens] = words.inject([]) do |sum, token| # Find all tokens, ie :symbol :like :names
66
- next sum unless token =~ /^:.*/ # Don't process regular words
67
- sym = token.sub(':', '').to_sym # Turn token string into symbol, ie ":token" => :token
68
- regex = @options[sym] || '[^\s]+' # Fetch regex if configured, else use any character but space matching
154
+ words = pattern.split.collect {|s| s.strip } # Get all words in pattern
155
+ @tokens = words.inject([]) do |sum, token| # Find all tokens, ie :symbol :like :names
156
+ next sum unless token =~ /^:.+/ # Don't process regular words
157
+ sym = token.sub(':', '').to_sym # Turn token string into symbol, ie ":token" => :token
158
+ regex = @options[sym] || '[^\s]+' # Fetch regex if configured, else use any character but space matching
69
159
  pattern.sub!(/(^|\s)#{token}(\s|$)/, '\1(' + regex.to_s + ')\2') # Make sure regex captures named switch
70
160
  sum << sym
71
161
  end
72
162
 
73
- @options[:pattern] = /#{pattern}(\s.+)?/
163
+ if @exact_match
164
+ @pattern = /\A#{pattern}\Z/
165
+ else
166
+ @pattern = /#{pattern}/
167
+ end
74
168
  end
75
169
 
170
+ # Public: Get the pattern RegExp.
171
+ attr_reader :pattern
172
+
173
+ # Internal: Determines if this handler is suited to handle
174
+ # an incoming message.
76
175
  #
77
- # Determines if this handler is suited to handle an incoming message
78
- #
176
+ # Returns a Boolean if it recognized the given message.
79
177
  def recognize?(message)
80
- return false if @options[:pattern] && message.text !~ @options[:pattern] # Pattern check
178
+ return false if @pattern && message.text !~ @pattern
81
179
 
82
180
  users = @options[:from] ? @options[:from] : nil
83
181
  return false if users && !users.include?(message.user) # Check allowed senders
84
182
  true
85
183
  end
86
184
 
185
+ # Public: Process a message to build params hash and handle it.
87
186
  #
88
- # Process message to build params hash and pass message along with params of
89
- # to +handle+
187
+ # message - The incoming String message.
90
188
  #
189
+ # Returns the response from `handle`.
91
190
  def dispatch(message)
92
191
  return unless recognize?(message)
93
- @params = {}
94
-
95
- if @options[:pattern] && @options[:tokens]
96
- matches = message.text.match(@options[:pattern])
97
- @options[:tokens].each_with_index { |token, i| @params[token] = matches[i+1] }
98
- @params[:text] = (matches[@options[:tokens].length+1] || "").strip
99
- elsif @options[:pattern] && !@options[:tokens]
100
- @params = message.text.match(@options[:pattern]).to_a[1..-1] || []
192
+ params = {}
193
+
194
+ if @pattern && @tokens
195
+ matches = message.text.match(@pattern)
196
+ @tokens.each_with_index {|token, i| params[token] = matches[i+1] }
197
+ params[:text] = (matches[@tokens.length+1] || '').strip
198
+ elsif @pattern && !@tokens
199
+ params = message.text.match(@pattern).to_a[1..-1] || []
101
200
  else
102
- @params[:text] = message.text
201
+ params[:text] = message.text
103
202
  end
104
203
 
105
- return handle(message, @params)
204
+ handle(message, params)
106
205
  end
107
206
 
207
+ # Internal: Call the assigned message handler if any.
108
208
  #
109
- # Handle a message. Calls the internal Proc with the message and the params
110
- # hash as parameters.
209
+ # message - The incoming String message.
210
+ # params - The hash containing matched tokens.
111
211
  #
212
+ # Returns the return from the handler block.
112
213
  def handle(message, params)
113
214
  @handler.call(message, params) if @handler
114
215
  end
@@ -25,6 +25,9 @@ module Jabbot
25
25
  # blk - The block to execute on successfull match
26
26
  def message(pattern = nil, options = {}, &blk)
27
27
  add_handler(:message, pattern, options, &blk)
28
+
29
+ # if :query => true, add this block for queries, too
30
+ add_handler(:private, pattern, options, &blk) if options && options[:query]
28
31
  end
29
32
 
30
33
  # Add query handler
@@ -104,6 +107,12 @@ module Jabbot
104
107
  if msg.is_a?(Hash) && msg.keys.size == 1
105
108
  to = msg.values.first
106
109
  msg = msg.keys.first
110
+ elsif to.kind_of?(Struct)
111
+ if to.type == :query
112
+ to = to.user
113
+ else
114
+ to = nil
115
+ end
107
116
  end
108
117
  bot.send_message(msg, to)
109
118
  end
@@ -5,48 +5,48 @@ context "Handler" do
5
5
  handler = Jabbot::Handler.new
6
6
 
7
7
  handler.pattern = nil
8
- assert_nil handler.instance_eval { @options[:pattern] }
9
- assert_nil handler.instance_eval { @options[:tokens] }
8
+ assert_nil handler.pattern
9
+ assert_nil handler.instance_eval { @tokens }
10
10
 
11
11
  handler.pattern = ""
12
- assert_nil handler.instance_eval { @options[:pattern] }
13
- assert_nil handler.instance_eval { @options[:tokens] }
12
+ assert_nil handler.pattern
13
+ assert_nil handler.instance_eval { @tokens }
14
14
  end
15
15
 
16
16
  test "turn regular pattern into regex" do
17
17
  handler = Jabbot::Handler.new
18
18
  handler.pattern = "command"
19
19
 
20
- assert_equal(/command(\s.+)?/, handler.instance_eval { @options[:pattern] })
21
- assert_equal 0, handler.instance_eval { @options[:tokens] }.length
20
+ assert_equal(/command/, handler.pattern)
21
+ assert_equal 0, handler.instance_eval{ @tokens }.length
22
22
  end
23
23
 
24
24
  test "convert single named switch to regex" do
25
25
  handler = Jabbot::Handler.new
26
26
  handler.pattern = ":command"
27
27
 
28
- assert_equal(/([^\s]+)(\s.+)?/, handler.instance_eval { @options[:pattern] })
29
- assert_equal 1, handler.instance_eval { @options[:tokens] }.length
30
- assert_equal :command, handler.instance_eval { @options[:tokens].first }
28
+ assert_equal(/([^\s]+)/, handler.pattern)
29
+ assert_equal 1, handler.instance_eval { @tokens }.length
30
+ assert_equal :command, handler.instance_eval { @tokens.first }
31
31
  end
32
32
 
33
33
  test "convert several named switches to regexen" do
34
34
  handler = Jabbot::Handler.new
35
35
  handler.pattern = ":command fixed_word :subcommand"
36
36
 
37
- assert_equal(/([^\s]+) fixed_word ([^\s]+)(\s.+)?/, handler.instance_eval { @options[:pattern] })
38
- assert_equal 2, handler.instance_eval { @options[:tokens] }.length
39
- assert_equal :command, handler.instance_eval { @options[:tokens].first }
40
- assert_equal :subcommand, handler.instance_eval { @options[:tokens][1] }
37
+ assert_equal(/([^\s]+) fixed_word ([^\s]+)/, handler.pattern)
38
+ assert_equal 2, handler.instance_eval { @tokens }.length
39
+ assert_equal :command, handler.instance_eval { @tokens.first }
40
+ assert_equal :subcommand, handler.instance_eval { @tokens[1] }
41
41
  end
42
42
 
43
43
  test "convert several named switches to regexen specified by options" do
44
44
  handler = Jabbot::Handler.new(":time :hour", :hour => /\d\d/)
45
45
 
46
- assert_equal(/([^\s]+) ((?-mix:\d\d))(\s.+)?/, handler.instance_eval { @options[:pattern] })
47
- assert_equal 2, handler.instance_eval { @options[:tokens] }.length
48
- assert_equal :time, handler.instance_eval { @options[:tokens].first }
49
- assert_equal :hour, handler.instance_eval { @options[:tokens][1] }
46
+ assert_equal(/([^\s]+) ((?-mix:\d\d))/, handler.pattern)
47
+ assert_equal 2, handler.instance_eval { @tokens }.length
48
+ assert_equal :time, handler.instance_eval { @tokens.first }
49
+ assert_equal :hour, handler.instance_eval { @tokens[1] }
50
50
  end
51
51
 
52
52
  test "recognize empty pattern" do
metadata CHANGED
@@ -2,7 +2,7 @@
2
2
  name: jabbot
3
3
  version: !ruby/object:Gem::Version
4
4
  prerelease:
5
- version: 0.3.1
5
+ version: 0.3.2
6
6
  platform: ruby
7
7
  authors:
8
8
  - badboy
@@ -10,7 +10,7 @@ autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
12
 
13
- date: 2011-04-12 00:00:00 +02:00
13
+ date: 2011-06-25 00:00:00 +02:00
14
14
  default_executable:
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency
@@ -19,23 +19,34 @@ dependencies:
19
19
  requirement: &id001 !ruby/object:Gem::Requirement
20
20
  none: false
21
21
  requirements:
22
- - - ">="
22
+ - - ~>
23
23
  - !ruby/object:Gem::Version
24
24
  version: "0.4"
25
25
  type: :runtime
26
26
  version_requirements: *id001
27
27
  - !ruby/object:Gem::Dependency
28
- name: shoulda
28
+ name: eventmachine
29
29
  prerelease: false
30
30
  requirement: &id002 !ruby/object:Gem::Requirement
31
+ none: false
32
+ requirements:
33
+ - - ~>
34
+ - !ruby/object:Gem::Version
35
+ version: "0.12"
36
+ type: :runtime
37
+ version_requirements: *id002
38
+ - !ruby/object:Gem::Dependency
39
+ name: shoulda
40
+ prerelease: false
41
+ requirement: &id003 !ruby/object:Gem::Requirement
31
42
  none: false
32
43
  requirements:
33
44
  - - ">="
34
45
  - !ruby/object:Gem::Version
35
46
  version: 2.10.1
36
47
  type: :development
37
- version_requirements: *id002
38
- description: " Jabbot is a Ruby micro-framework for creating Jabber/MUC bots,\n heavily inspired by Sinatra and Twibot.\n\n I modified the code of Twibot to fit my needs.\n The original Twibot code is located at:\n http://github.com/cjohansen/twibot/tree/master\n\n A big thank you to Christian Johansen, who wrote the code for Twibot.\n Jabbot is heavily based on his code.\n\n It's as easy as definig a small message handler:\n message do |message, params|\n post message.text\n end\n"
48
+ version_requirements: *id003
49
+ description: " Jabbot is a Ruby micro-framework for creating Jabber/MUC bots,\n heavily inspired by Sinatra and Twibot.\n\n I modified the code of Twibot to fit my needs.\n The original Twibot code by Christian Johansen is located at:\n http://github.com/cjohansen/twibot\n\n It's as easy as definig a small message handler:\n message {|message, params|\n post message.text\n }\n"
39
50
  email: badboy@archlinux.us
40
51
  executables: []
41
52