rumpy 0.9.15 → 0.9.17

Sign up to get free protection for your applications and to get access to all the features.
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