rumpy 0.9.15 → 0.9.17
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.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
|