rumpy 0.9.15 → 0.9.17
Sign up to get free protection for your applications and to get access to all the features.
- data/README.rdoc +20 -16
- data/lib/rumpy.rb +96 -58
- metadata +4 -4
data/README.rdoc
CHANGED
@@ -14,29 +14,33 @@ Our goal is 'DO NOT REINVENT THE WHEEL'.
|
|
14
14
|
== Getting started
|
15
15
|
|
16
16
|
* Rumpy uses 3 configs:
|
17
|
-
database.yml :: Your bot's database preferences.
|
18
|
-
lang.yml :: Your bot's responces. Append to existing keys more answers and use them like @lang['someanswer]. **There must be at least 3 keys : hello /used when somebody add bot/, stranger /when somebody trying talk to bot without authorization/, authorized /when bot get authorized/.
|
19
|
-
xmpp.yml :: Your bot's account settings.
|
17
|
+
+database.yml+ :: Your bot's database preferences.
|
18
|
+
+lang.yml+ :: Your bot's responces. Append to existing keys more answers and use them like @lang['someanswer]. **There must be at least 3 keys : hello /used when somebody add bot/, stranger /when somebody trying talk to bot without authorization/, authorized /when bot get authorized/.
|
19
|
+
+xmpp.yml+ :: Your bot's account settings.
|
20
20
|
Look at Examples section to see this configs.
|
21
21
|
* Implement your ActiveRecord models in one directory. You have to implement at least one model, for users.
|
22
22
|
* Prepare your database.
|
23
23
|
* Start writing your class:
|
24
|
-
* Mix in Rumpy::Bot module
|
24
|
+
* Mix in Rumpy::Bot module
|
25
|
+
|
25
26
|
include Rumpy::Bot
|
26
27
|
* Define some instance variables:
|
27
|
-
@models_path
|
28
|
-
@config_path
|
29
|
-
@main_model
|
30
|
-
@pid_file
|
31
|
-
@log_file
|
32
|
-
@log_level
|
33
|
-
@
|
34
|
-
@
|
35
|
-
@
|
28
|
+
@models_path :: Path to directory, containing all ruby files with models.
|
29
|
+
@config_path :: Path to directory, containing all ruby config files
|
30
|
+
@main_model :: Symbol, that stands for main model. If your main model is, e.g. +User+, set @main_model to +:user+
|
31
|
+
@pid_file :: Optional variable, sets location of the file to which pid of detached process will be saved. If not set, it equals +NameOfYourBotClass.downcase+ + '.pid'.
|
32
|
+
@log_file :: Optional variable, sets location of the file for errors. Default is +STDERR+.
|
33
|
+
@log_level :: Optional variable, sets the logging severity threshold. Possible values are Logger::DEBUG < Logger::INFO < Logger::WARN < Logger::ERROR < Logger::FATAL < Logger::UNKNOWN. Default is Logger::INFO.
|
34
|
+
@log_shift_age :: Optional variable, sets number of old log files to keep, or frequency of rotation (daily, weekly or monthly).
|
35
|
+
@log_shift_size :: Optional variable, sets maximum logfile size.
|
36
|
+
@log_progname :: Optional variable, sets logging program name.
|
37
|
+
@log_formatter :: Optional variable, sets logging formatter. @log_formatter#call is invoked with 4 arguments; +severity+, +time+, +progname+ and +msg+ for each log.
|
38
|
+
@bot_name :: Optional name of the bot. Default is name of bot's class.
|
39
|
+
@bot_version :: Optional version of the bot. Default is 1.0.0.
|
36
40
|
* Write 3 methods:
|
37
|
-
backend_func() -> [[receiver, message]*]
|
38
|
-
parser_func(msg) -> pars_result
|
39
|
-
do_func(usermodel, pars_results) -> msg
|
41
|
+
backend_func() -> [[receiver, message]*] :: This optional method is running all the time in the loop. Returns array of pairs [receiver, message].
|
42
|
+
parser_func(msg) -> +pars_result+ :: This method parses any incoming message and returs results.
|
43
|
+
do_func(usermodel, pars_results) -> +msg+ :: This method uses results from +parser_func+, doing some stuff with model of user, from whom message was received. Returns message to be send to this user
|
40
44
|
* Run bot:
|
41
45
|
You can run your bot without detaching:
|
42
46
|
Rumpy.run YourBotClassName
|
data/lib/rumpy.rb
CHANGED
@@ -10,9 +10,12 @@ module Rumpy
|
|
10
10
|
# Create new instance of `botclass`, start it in new process,
|
11
11
|
# detach this process and save the pid of process in pid_file
|
12
12
|
def self.start(botclass)
|
13
|
-
bot
|
14
|
-
pf
|
13
|
+
bot = botclass.new
|
14
|
+
pf = pid_file bot
|
15
15
|
return false if File.exist? pf
|
16
|
+
|
17
|
+
bot.log_file = "#{botclass.to_s.downcase}.log"
|
18
|
+
|
16
19
|
pid = fork do
|
17
20
|
bot.start
|
18
21
|
end
|
@@ -54,24 +57,17 @@ module Rumpy
|
|
54
57
|
module Bot
|
55
58
|
attr_reader :pid_file
|
56
59
|
|
60
|
+
# if @log_file isn't set, initialize it
|
61
|
+
def log_file=(logfile)
|
62
|
+
@log_file ||= logfile
|
63
|
+
end
|
64
|
+
|
57
65
|
# one and only public function, defined in this module
|
58
66
|
# simply initializes bot's variables, connection, etc.
|
59
67
|
# and starts bot
|
60
68
|
def start
|
61
|
-
|
62
|
-
@log_file ||= STDERR
|
63
|
-
@log_level ||= Logger::INFO
|
64
|
-
@logger = Logger.new @log_file
|
65
|
-
@logger.level = @log_level
|
66
|
-
@logger.datetime_format = "%Y-%m-%d %H:%M:%S"
|
67
|
-
end
|
68
|
-
Signal.trap :TERM do |signo|
|
69
|
-
@logger.info 'terminating'
|
70
|
-
@logger.close
|
71
|
-
exit
|
72
|
-
end
|
69
|
+
logger_init
|
73
70
|
|
74
|
-
@logger.info 'starting bot'
|
75
71
|
init
|
76
72
|
connect
|
77
73
|
prepare_users
|
@@ -83,13 +79,30 @@ module Rumpy
|
|
83
79
|
start_backend_thread
|
84
80
|
|
85
81
|
@logger.info 'Bot is going ONLINE'
|
86
|
-
|
82
|
+
send_msg Jabber::Presence.new(nil, @status, @priority)
|
83
|
+
|
84
|
+
add_signal_trap
|
87
85
|
|
88
86
|
Thread.stop
|
87
|
+
rescue => e
|
88
|
+
general_error e
|
89
|
+
exit
|
89
90
|
end # def start
|
90
91
|
|
91
92
|
private
|
92
93
|
|
94
|
+
def logger_init
|
95
|
+
@log_file ||= STDERR
|
96
|
+
@log_level ||= Logger::INFO
|
97
|
+
@logger = Logger.new @log_file, @log_shift_age, @log_shift_size
|
98
|
+
@logger.level = @log_level
|
99
|
+
@logger.progname = @log_progname
|
100
|
+
@logger.formatter = @log_formatter
|
101
|
+
@logger.datetime_format = "%Y-%m-%d %H:%M:%S"
|
102
|
+
|
103
|
+
@logger.info 'starting bot'
|
104
|
+
end
|
105
|
+
|
93
106
|
def init
|
94
107
|
@logger.debug 'initializing some variables'
|
95
108
|
|
@@ -156,7 +169,7 @@ module Rumpy
|
|
156
169
|
@logger.info "deleting from database user with jid #{user.jid}"
|
157
170
|
user.destroy
|
158
171
|
else
|
159
|
-
start_user_thread user
|
172
|
+
start_user_thread user
|
160
173
|
end
|
161
174
|
end
|
162
175
|
|
@@ -180,14 +193,22 @@ module Rumpy
|
|
180
193
|
@queues[item.jid.strip.to_s].enq :unsubscribe
|
181
194
|
item.remove
|
182
195
|
when :subscribed
|
183
|
-
|
196
|
+
user = @main_model.new
|
197
|
+
user.jid = item.jid.strip.to_s
|
198
|
+
user.save
|
199
|
+
start_user_thread user
|
200
|
+
|
201
|
+
@logger.info "added new user: #{user.jid}"
|
184
202
|
send_msg Jabber::Message.new(item.jid, @lang['authorized']).set_type :chat
|
185
203
|
end
|
186
204
|
rescue ActiveRecord::StatementInvalid
|
187
|
-
|
188
|
-
|
189
|
-
|
205
|
+
statement_invalid
|
206
|
+
retry
|
207
|
+
rescue ActiveRecord::ConnectionTimeoutError
|
208
|
+
connection_timeout_error
|
190
209
|
retry
|
210
|
+
rescue => e
|
211
|
+
general_error e
|
191
212
|
end
|
192
213
|
end
|
193
214
|
end # def set_subscription_callback
|
@@ -196,7 +217,7 @@ module Rumpy
|
|
196
217
|
@client.add_message_callback do |msg|
|
197
218
|
if msg.type != :error and msg.body and msg.from then
|
198
219
|
if @roster[msg.from] and @roster[msg.from].subscription == :both then
|
199
|
-
@logger.debug "got normal message
|
220
|
+
@logger.debug "got normal message #{msg}"
|
200
221
|
|
201
222
|
@queues[msg.from.strip.to_s].enq msg
|
202
223
|
else # if @roster[msg.from] and @roster[msg.from].subscription == :both
|
@@ -234,50 +255,69 @@ module Rumpy
|
|
234
255
|
begin
|
235
256
|
loop do
|
236
257
|
backend_func().each do |result|
|
237
|
-
|
258
|
+
message = Jabber::Message.new(*result).set_type :chat
|
259
|
+
send_msg message if message.body and message.to
|
238
260
|
end
|
239
261
|
end
|
240
262
|
rescue ActiveRecord::StatementInvalid
|
241
|
-
|
242
|
-
|
243
|
-
|
263
|
+
statement_invalid
|
264
|
+
retry
|
265
|
+
rescue ActiveRecord::ConnectionTimeoutError
|
266
|
+
connection_timeout_error
|
244
267
|
retry
|
245
268
|
rescue => e
|
246
|
-
|
247
|
-
$logger.error e.backtrace
|
269
|
+
general_error e
|
248
270
|
end # begin
|
249
271
|
end if self.respond_to? :backend_func
|
250
272
|
end # def start_backend_thread
|
251
273
|
|
252
|
-
def
|
253
|
-
|
274
|
+
def add_signal_trap
|
275
|
+
Signal.trap :TERM do |signo| # soft stop
|
276
|
+
@logger.info 'Bot is unavailable'
|
277
|
+
send_msg Jabber::Presence.new.set_type :unavailable
|
278
|
+
|
279
|
+
@queues.each do |user, queue|
|
280
|
+
queue.enq :halt
|
281
|
+
end
|
282
|
+
until @queues.empty?
|
283
|
+
sleep 1
|
284
|
+
end
|
285
|
+
@client.close
|
286
|
+
|
287
|
+
@logger.info 'terminating'
|
288
|
+
@logger.close
|
289
|
+
exit
|
290
|
+
end
|
291
|
+
end
|
292
|
+
|
293
|
+
def start_user_thread(user)
|
294
|
+
Thread.new(user) do |user|
|
295
|
+
|
254
296
|
loop do
|
255
|
-
msg = @queues[
|
297
|
+
msg = @queues[user.jid].deq
|
256
298
|
|
257
299
|
begin
|
258
|
-
if msg
|
259
|
-
|
300
|
+
if msg.kind_of? Symbol then # :unsubscribe or :halt
|
301
|
+
if msg == :unsubscribe
|
302
|
+
@logger.info "removing user #{user.jid}"
|
303
|
+
user.destroy
|
304
|
+
end
|
305
|
+
@queues.delete user.jid
|
260
306
|
break
|
261
307
|
end
|
262
308
|
|
263
|
-
user = @main_model.find_by_jid msg.from
|
264
|
-
|
265
309
|
pars_results = parser_func msg.body
|
266
310
|
@logger.debug "parsed message: #{pars_results.inspect}"
|
267
|
-
|
311
|
+
message = do_func user, pars_results
|
312
|
+
send_msg msg.answer.set_body message unless message.empty?
|
268
313
|
rescue ActiveRecord::StatementInvalid
|
269
|
-
|
270
|
-
@logger.info 'Reconnecting to database'
|
271
|
-
@main_model.connection.reconnect!
|
314
|
+
statement_invalid
|
272
315
|
retry
|
273
316
|
rescue ActiveRecord::ConnectionTimeoutError
|
274
|
-
|
275
|
-
@logger.info 'sleep and retry again'
|
276
|
-
sleep 3
|
317
|
+
connection_timeout_error
|
277
318
|
retry
|
278
319
|
rescue => e
|
279
|
-
|
280
|
-
@logger.error e.backtrace
|
320
|
+
general_error e
|
281
321
|
end # begin
|
282
322
|
|
283
323
|
@main_model.connection_pool.release_connection
|
@@ -292,23 +332,21 @@ module Rumpy
|
|
292
332
|
@client.send msg
|
293
333
|
end
|
294
334
|
|
295
|
-
def
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
start_user_thread user.jid
|
300
|
-
@logger.info "added new user: #{jid}"
|
335
|
+
def statement_invalid
|
336
|
+
@logger.warn 'Statement Invalid catched'
|
337
|
+
@logger.info 'Reconnecting to database'
|
338
|
+
@main_model.connection.reconnect!
|
301
339
|
end
|
302
340
|
|
303
|
-
def
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
@queues.delete user.jid
|
341
|
+
def connection_timeout_error
|
342
|
+
@logger.warn 'ActiveRecord::ConnectionTimeoutError'
|
343
|
+
@logger.info 'sleep and retry again'
|
344
|
+
sleep 3
|
345
|
+
end
|
309
346
|
|
310
|
-
|
311
|
-
|
347
|
+
def general_error(exception)
|
348
|
+
@logger.error exception.inspect
|
349
|
+
@logger.error exception.backtrace
|
312
350
|
end
|
313
351
|
end # module Rumpy::Bot
|
314
352
|
end # module Rumpy
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rumpy
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 25
|
5
5
|
prerelease: false
|
6
6
|
segments:
|
7
7
|
- 0
|
8
8
|
- 9
|
9
|
-
-
|
10
|
-
version: 0.9.
|
9
|
+
- 17
|
10
|
+
version: 0.9.17
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Tsokurov A.G.
|
@@ -16,7 +16,7 @@ autorequire:
|
|
16
16
|
bindir: bin
|
17
17
|
cert_chain: []
|
18
18
|
|
19
|
-
date: 2011-08-
|
19
|
+
date: 2011-08-18 00:00:00 +03:00
|
20
20
|
default_executable:
|
21
21
|
dependencies:
|
22
22
|
- !ruby/object:Gem::Dependency
|