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 +3 -1
- data/lib/jabbot.rb +7 -8
- data/lib/jabbot/bot.rb +85 -49
- data/lib/jabbot/config.rb +16 -14
- data/lib/jabbot/handlers.rb +134 -33
- data/lib/jabbot/macros.rb +9 -0
- data/test/test_handler.rb +17 -17
- metadata +17 -6
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
|
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
|
|
data/lib/jabbot.rb
CHANGED
@@ -12,7 +12,7 @@ require 'jabbot/handlers.rb'
|
|
12
12
|
require 'jabbot/macros.rb'
|
13
13
|
|
14
14
|
module Jabbot
|
15
|
-
VERSION = '0.3.
|
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
|
-
#
|
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
|
-
#
|
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
|
46
|
+
end
|
47
47
|
|
48
|
+
# xmpp4r runs in another thread.
|
48
49
|
Thread.abort_on_exception = true
|
49
|
-
|
50
|
-
# EOF
|
data/lib/jabbot/bot.rb
CHANGED
@@ -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
|
-
#
|
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
|
-
|
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
|
-
#
|
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
|
-
|
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
|
-
#
|
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
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
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
|
-
#
|
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
|
-
#
|
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
|
-
#
|
133
|
-
#
|
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
|
-
#
|
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
|
-
#
|
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
|
-
#
|
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
|
-
#
|
231
|
-
#
|
266
|
+
# Returns the configure Hash.
|
232
267
|
def configure
|
233
268
|
yield @config
|
234
|
-
@conf =
|
269
|
+
@conf = @config.to_hash
|
235
270
|
end
|
236
271
|
|
272
|
+
# Internal: Maps configuration settings to real methods.
|
237
273
|
#
|
238
|
-
#
|
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
|
-
#
|
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?
|
data/lib/jabbot/config.rb
CHANGED
@@ -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
|
-
#
|
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
|
-
#
|
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
|
-
#
|
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
|
data/lib/jabbot/handlers.rb
CHANGED
@@ -1,17 +1,30 @@
|
|
1
1
|
module Jabbot
|
2
2
|
module Handlers
|
3
|
+
# Public: Add a handler for a given type.
|
3
4
|
#
|
4
|
-
#
|
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
|
-
#
|
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
|
-
|
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
|
-
|
47
|
-
|
48
|
-
|
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
|
-
|
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
|
-
#
|
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 }
|
65
|
-
@
|
66
|
-
next sum unless token =~
|
67
|
-
sym = token.sub(':', '').to_sym
|
68
|
-
regex = @options[sym] || '[^\s]+'
|
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
|
-
@
|
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
|
-
#
|
78
|
-
#
|
176
|
+
# Returns a Boolean if it recognized the given message.
|
79
177
|
def recognize?(message)
|
80
|
-
return false if @
|
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
|
-
#
|
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
|
-
|
94
|
-
|
95
|
-
if @
|
96
|
-
matches = message.text.match(@
|
97
|
-
@
|
98
|
-
|
99
|
-
elsif @
|
100
|
-
|
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
|
-
|
201
|
+
params[:text] = message.text
|
103
202
|
end
|
104
203
|
|
105
|
-
|
204
|
+
handle(message, params)
|
106
205
|
end
|
107
206
|
|
207
|
+
# Internal: Call the assigned message handler if any.
|
108
208
|
#
|
109
|
-
#
|
110
|
-
# hash
|
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
|
data/lib/jabbot/macros.rb
CHANGED
@@ -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
|
data/test/test_handler.rb
CHANGED
@@ -5,48 +5,48 @@ context "Handler" do
|
|
5
5
|
handler = Jabbot::Handler.new
|
6
6
|
|
7
7
|
handler.pattern = nil
|
8
|
-
assert_nil handler.
|
9
|
-
assert_nil handler.instance_eval { @
|
8
|
+
assert_nil handler.pattern
|
9
|
+
assert_nil handler.instance_eval { @tokens }
|
10
10
|
|
11
11
|
handler.pattern = ""
|
12
|
-
assert_nil handler.
|
13
|
-
assert_nil handler.instance_eval { @
|
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
|
21
|
-
assert_equal 0, handler.instance_eval
|
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]+)
|
29
|
-
assert_equal 1, handler.instance_eval { @
|
30
|
-
assert_equal :command, handler.instance_eval { @
|
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]+)
|
38
|
-
assert_equal 2, handler.instance_eval { @
|
39
|
-
assert_equal :command, handler.instance_eval { @
|
40
|
-
assert_equal :subcommand, handler.instance_eval { @
|
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))
|
47
|
-
assert_equal 2, handler.instance_eval { @
|
48
|
-
assert_equal :time, handler.instance_eval { @
|
49
|
-
assert_equal :hour, handler.instance_eval { @
|
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.
|
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-
|
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:
|
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: *
|
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
|
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
|
|