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 +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
|
|