jabbot 0.3.1 → 0.3.2

Sign up to get free protection for your applications and to get access to all the features.
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