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.
Files changed (3) hide show
  1. data/README.rdoc +20 -16
  2. data/lib/rumpy.rb +96 -58
  3. 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 :: Path to directory, containing all ruby files with models.
28
- @config_path :: Path to directory, containing all ruby config files
29
- @main_model :: Symbol, that stands for main model. If your main model is, e.g. User, set @main_model to :user
30
- @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'.
31
- @log_file :: Optional variable, sets location of the file for errors. Default is STDERR.
32
- @log_level :: Optional variable, sets the verbosity of log. Possible values are Logger::DEBUG < Logger::INFO < Logger::WARN < Logger::ERROR < Logger::FATAL < Logger::UNKNOWN. Default is Logger::INFO
33
- @logger :: If you wanna specify more parameters to logger, you always can create logger yourself. If this variable set, @log_file & @log_level are ignored.
34
- @bot_name :: Optional name of the bot. Default is name of bot's class.
35
- @bot_version :: Optional version of the bot. Default is 1.0.0.
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]*] :: This optional method is running all the time in the loop. Returns array of pairs [receiver, message].
38
- parser_func(msg) -> pars_result :: This method parses any incoming message and returs results.
39
- 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
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 = botclass.new
14
- pf = pid_file bot
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
- if @logger.nil? then # if user haven't created his own logger
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
- @client.send Jabber::Presence.new.set_priority(@priority).set_status(@status)
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.jid
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
- add_jid item.jid
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
- @logger.warn 'Statement Invalid catched'
188
- @logger.info 'Reconnecting to database'
189
- @main_model.connection.reconnect!
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 from #{msg.from}"
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
- send_msg Jabber::Message.new(*result).set_type :chat
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
- @logger.warn 'Statement Invalid catched'
242
- @logger.info 'Reconnecting to database'
243
- @main_model.connection.reconnect!
263
+ statement_invalid
264
+ retry
265
+ rescue ActiveRecord::ConnectionTimeoutError
266
+ connection_timeout_error
244
267
  retry
245
268
  rescue => e
246
- $logger.error e.inspect
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 start_user_thread(userjid)
253
- Thread.new(userjid) do |userjid|
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[userjid].deq
297
+ msg = @queues[user.jid].deq
256
298
 
257
299
  begin
258
- if msg == :unsubscribe then
259
- remove_jid userjid
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
- send_msg msg.answer.set_body do_func(user, pars_results)
311
+ message = do_func user, pars_results
312
+ send_msg msg.answer.set_body message unless message.empty?
268
313
  rescue ActiveRecord::StatementInvalid
269
- @logger.warn 'Statement Invalid catched!'
270
- @logger.info 'Reconnecting to database'
271
- @main_model.connection.reconnect!
314
+ statement_invalid
272
315
  retry
273
316
  rescue ActiveRecord::ConnectionTimeoutError
274
- @logger.warn 'ActiveRecord::ConnectionTimeoutError'
275
- @logger.info 'sleep and retry again'
276
- sleep 3
317
+ connection_timeout_error
277
318
  retry
278
319
  rescue => e
279
- @logger.error e.inspect
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 add_jid(jid)
296
- user = @main_model.new
297
- user.jid = jid.strip.to_s
298
- user.save
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 remove_jid(jid)
304
- user = @main_model.find_by_jid jid
305
- unless user.nil?
306
- @logger.info "removing user #{jid}"
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
- user.destroy
311
- end
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: 37
4
+ hash: 25
5
5
  prerelease: false
6
6
  segments:
7
7
  - 0
8
8
  - 9
9
- - 15
10
- version: 0.9.15
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-15 00:00:00 +03:00
19
+ date: 2011-08-18 00:00:00 +03:00
20
20
  default_executable:
21
21
  dependencies:
22
22
  - !ruby/object:Gem::Dependency