rumpy 0.9.22 → 0.9.23

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/lib/rumpy/bot.rb +317 -0
  2. data/lib/rumpy/version.rb +3 -0
  3. metadata +41 -63
@@ -0,0 +1,317 @@
1
+ require 'rubygems'
2
+ require 'xmpp4r/client'
3
+ require 'xmpp4r/roster'
4
+ require 'xmpp4r/version'
5
+ require 'active_record'
6
+ require 'logger'
7
+
8
+ # include this module into your bot's class
9
+ module Bot
10
+ attr_reader :pid_file
11
+
12
+ # if @log_file isn't set, initialize it
13
+ def log_file=(logfile)
14
+ @log_file ||= logfile
15
+ end
16
+
17
+ # one and only public function, defined in this module
18
+ # simply initializes bot's variables, connection, etc.
19
+ # and starts bot
20
+ def start
21
+ logger_init
22
+
23
+ init
24
+
25
+ connect
26
+
27
+ set_iq_callback
28
+ set_subscription_callback
29
+ set_message_callback
30
+
31
+ start_backend_thread
32
+ start_output_queue_thread
33
+
34
+ prepare_users
35
+
36
+ @logger.info 'Bot is going ONLINE'
37
+ @output_queue.enq Jabber::Presence.new(nil, @status, @priority)
38
+
39
+ add_signal_trap
40
+
41
+ Thread.stop
42
+ rescue => ex
43
+ general_error ex
44
+ exit
45
+ end # def start
46
+
47
+ private
48
+
49
+ def logger_init
50
+ unless @logger
51
+ @log_file ||= STDERR
52
+ @log_level ||= Logger::INFO
53
+ @logger = Logger.new @log_file, @log_shift_age, @log_shift_size
54
+ @logger.level = @log_level
55
+ @logger.datetime_format = "%Y-%m-%d %H:%M:%S"
56
+ end
57
+
58
+ @logger.info 'starting bot'
59
+ end
60
+
61
+ def init
62
+ @config_path ||= 'config'
63
+ @main_model ||= :user
64
+
65
+ @logger.debug 'initializing some variables'
66
+
67
+ xmppconfig = YAML::load_file @config_path + '/xmpp.yml'
68
+ @logger.info 'loaded xmpp.yml'
69
+ @logger.debug "xmpp.yml: #{xmppconfig.inspect}"
70
+ @lang = YAML::load_file @config_path + '/lang.yml'
71
+ @logger.info 'loaded lang.yml'
72
+ @logger.debug "lang.yml: #{@lang.inspect}"
73
+ @jid = Jabber::JID.new xmppconfig['jid']
74
+ @priority = xmppconfig['priority']
75
+ @status = xmppconfig['status']
76
+ @password = xmppconfig['password']
77
+ @client = Jabber::Client.new @jid
78
+ Jabber::Version::SimpleResponder.new(@client, @bot_name || self.class.to_s, @bot_version || '1.0.0', RUBY_PLATFORM)
79
+
80
+ if @models_files
81
+ dbconfig = YAML::load_file @config_path + '/database.yml'
82
+ @logger.info 'loaded database.yml'
83
+ @logger.debug "database.yml: #{dbconfig.inspect}"
84
+ ActiveRecord::Base.establish_connection dbconfig
85
+ @logger.info 'database connection established'
86
+ @models_files.each do |file|
87
+ self.class.require file
88
+ @logger.info "added models file '#{file}'"
89
+ end
90
+ end
91
+
92
+ @main_model = Object.const_get @main_model.to_s.capitalize
93
+ @logger.info "main model set to #{@main_model}"
94
+
95
+ @queues = Hash.new do |h, k|
96
+ h[k] = Queue.new
97
+ end
98
+
99
+ @output_queue = Queue.new
100
+ end # def init
101
+
102
+ def connect
103
+ @logger.debug 'establishing xmpp connection'
104
+
105
+ @client.connect
106
+ @client.auth @password
107
+ @roster = Jabber::Roster::Helper.new @client
108
+ @roster.wait_for_roster
109
+
110
+ @logger.info 'xmpp connection established'
111
+ end
112
+
113
+ def set_iq_callback
114
+ @client.add_iq_callback do |iq|
115
+ @logger.debug "got iq #{iq}"
116
+ if iq.type == :get # hack for pidgin (STOP USING IT)
117
+ response = iq.answer true
118
+ if iq.elements['time'] == "<time xmlns='urn:xmpp:time'/>"
119
+ @logger.debug 'this is time request, okay'
120
+ response.set_type :result
121
+ tm = Time.now
122
+ response.elements['time'].add REXML::Element.new('tzo')
123
+ response.elements['time/tzo'].text = tm.xmlschema[-6..-1]
124
+ response.elements['time'].add REXML::Element.new('utc')
125
+ response.elements['time/utc'].text = tm.utc.xmlschema
126
+ else
127
+ response.set_type :error
128
+ end # if iq.elements['time']
129
+ @output_queue.enq response
130
+ end
131
+ end
132
+ end # def set_iq_callback
133
+
134
+ def set_subscription_callback
135
+ @roster.add_subscription_request_callback do |item, presence|
136
+ jid = presence.from
137
+ @roster.accept_subscription jid
138
+ @output_queue.enq presence.answer.set_type :subscribe
139
+ @output_queue.enq Jabber::Message.new(jid, @lang['hello']).set_type :chat
140
+
141
+ @logger.info "#{jid} just subscribed"
142
+ end
143
+ @roster.add_subscription_callback do |item, presence|
144
+ begin
145
+ case presence.type
146
+ when :unsubscribed, :unsubscribe
147
+ @logger.info "#{item.jid} wanna unsubscribe"
148
+ @queues[item.jid.strip.to_s].enq :unsubscribe
149
+ item.remove
150
+ when :subscribed
151
+ user = @main_model.new
152
+ user.jid = item.jid.strip.to_s
153
+ user.save
154
+ start_user_thread user
155
+
156
+ @logger.info "added new user: #{user.jid}"
157
+ @output_queue.enq Jabber::Message.new(item.jid, @lang['authorized']).set_type :chat
158
+ end
159
+ rescue ActiveRecord::StatementInvalid
160
+ statement_invalid_error
161
+ retry
162
+ rescue ActiveRecord::ConnectionTimeoutError
163
+ connection_timeout_error
164
+ retry
165
+ rescue => ex
166
+ general_error ex
167
+ end
168
+ end
169
+ end # def set_subscription_callback
170
+
171
+ def set_message_callback
172
+ @client.add_message_callback do |msg|
173
+ if msg.type != :error && msg.body && msg.from
174
+ if @roster[msg.from] && @roster[msg.from].subscription == :both
175
+ @logger.debug "got normal message #{msg}"
176
+
177
+ @queues[msg.from.strip.to_s].enq msg
178
+ else
179
+ @logger.debug "user not in roster: #{msg.from}"
180
+
181
+ @output_queue.enq msg.answer.set_body @lang['stranger']
182
+ end
183
+ end
184
+ end
185
+ end # def set_message_callback
186
+
187
+ def start_backend_thread
188
+ Thread.new do
189
+ begin
190
+ loop do
191
+ backend_func().each do |result|
192
+ message = Jabber::Message.new(*result).set_type :chat
193
+ @output_queue.enq message if message.body && message.to
194
+ end
195
+ end
196
+ rescue ActiveRecord::StatementInvalid
197
+ statement_invalid_error
198
+ retry
199
+ rescue ActiveRecord::ConnectionTimeoutError
200
+ connection_timeout_error
201
+ retry
202
+ rescue => ex
203
+ general_error ex
204
+ end # begin
205
+ end if self.respond_to? :backend_func
206
+ end # def start_backend_thread
207
+
208
+ def start_output_queue_thread
209
+ Thread.new do
210
+ @logger.info "Output queue initialized"
211
+ until (msg = @output_queue.deq) == :halt do
212
+ if msg.nil?
213
+ @logger.debug "got nil message. wtf?"
214
+ else
215
+ @logger.debug "sending message #{msg}"
216
+ @client.send msg
217
+ end
218
+ end
219
+ @logger.info "Output queue destroyed"
220
+ end
221
+ end # def start_output_queue_thread
222
+
223
+ def add_signal_trap
224
+ Signal.trap :TERM do |signo| # soft stop
225
+ @logger.info 'Bot is unavailable'
226
+ @output_queue.enq Jabber::Presence.new.set_type :unavailable
227
+
228
+ @queues.each do |user, queue|
229
+ queue.enq :halt
230
+ end
231
+ sleep 1 until @queues.empty?
232
+
233
+ @output_queue.enq :halt
234
+ sleep 1 until @output_queue.empty?
235
+
236
+ @client.close
237
+
238
+ @logger.info 'terminating'
239
+ @logger.close
240
+ exit
241
+ end
242
+ end
243
+
244
+ def prepare_users
245
+ @logger.debug 'clear wrong users'
246
+
247
+ @roster.items.each do |jid, item|
248
+ user = @main_model.find_by_jid jid.strip.to_s
249
+ if user.nil? || item.subscription != :both
250
+ @logger.info "deleting from roster user with jid #{jid}"
251
+ item.remove
252
+ end
253
+ end
254
+ @main_model.find_each do |user|
255
+ items = @roster.find user.jid
256
+ if items.empty?
257
+ @logger.info "deleting from database user with jid #{user.jid}"
258
+ user.destroy
259
+ else
260
+ start_user_thread user
261
+ end
262
+ end
263
+
264
+ @main_model.connection_pool.release_connection
265
+ end # def prepare_users
266
+
267
+ def start_user_thread(user)
268
+ Thread.new(user) do |user|
269
+ @logger.debug "thread for user #{user.jid} started"
270
+
271
+ until (msg = @queues[user.jid].deq).kind_of? Symbol do
272
+ begin
273
+ pars_results = parser_func msg.body
274
+ @logger.debug "parsed message: #{pars_results.inspect}"
275
+ answer = do_func user, pars_results
276
+ @output_queue.enq msg.answer.set_body answer unless answer.nil? or answer.empty?
277
+ rescue ActiveRecord::StatementInvalid
278
+ statement_invalid_error
279
+ retry
280
+ rescue ActiveRecord::ConnectionTimeoutError
281
+ connection_timeout_error
282
+ retry
283
+ rescue => ex
284
+ general_error ex
285
+ end # begin
286
+
287
+ @main_model.connection_pool.release_connection
288
+ end # until (msg = @queues[user.jid].deq).kind_of? Symbol do
289
+
290
+ if msg == :unsubscribe
291
+ @logger.info "removing user #{user.jid}"
292
+ user.destroy
293
+ end
294
+
295
+ @queues.delete user.jid
296
+
297
+ end # Thread.new do
298
+ end # def start_user_thread(user)
299
+
300
+ def statement_invalid_error
301
+ @logger.warn 'Statement Invalid catched'
302
+ @logger.info 'Reconnecting to database'
303
+ @main_model.connection.reconnect!
304
+ end
305
+
306
+ def connection_timeout_error
307
+ @logger.warn 'ActiveRecord::ConnectionTimeoutError'
308
+ @logger.info 'sleep and retry again'
309
+ sleep 3
310
+ end
311
+
312
+ def general_error(exception)
313
+ @logger.error exception.inspect
314
+ @logger.error exception.backtrace
315
+ end
316
+ end # module Rumpy::Bot
317
+
@@ -0,0 +1,3 @@
1
+ module Rumpy
2
+ VERSION = '0.9.23'
3
+ end
metadata CHANGED
@@ -1,99 +1,77 @@
1
- --- !ruby/object:Gem::Specification
1
+ --- !ruby/object:Gem::Specification
2
2
  name: rumpy
3
- version: !ruby/object:Gem::Version
4
- hash: 23
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.9.23
5
5
  prerelease:
6
- segments:
7
- - 0
8
- - 9
9
- - 22
10
- version: 0.9.22
11
6
  platform: ruby
12
- authors:
7
+ authors:
13
8
  - Tsokurov A.G.
14
9
  - Pogoda M.V.
15
10
  autorequire:
16
11
  bindir: bin
17
12
  cert_chain: []
18
-
19
- date: 2011-11-20 00:00:00 Z
20
- dependencies:
21
- - !ruby/object:Gem::Dependency
13
+ date: 2012-01-10 00:00:00.000000000 Z
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
22
16
  name: activerecord
23
- prerelease: false
24
- requirement: &id001 !ruby/object:Gem::Requirement
17
+ requirement: &71062650 !ruby/object:Gem::Requirement
25
18
  none: false
26
- requirements:
27
- - - ">"
28
- - !ruby/object:Gem::Version
29
- hash: 7
30
- segments:
31
- - 3
32
- - 0
33
- version: "3.0"
19
+ requirements:
20
+ - - ! '>'
21
+ - !ruby/object:Gem::Version
22
+ version: '3.0'
34
23
  type: :runtime
35
- version_requirements: *id001
36
- - !ruby/object:Gem::Dependency
37
- name: xmpp4r
38
24
  prerelease: false
39
- requirement: &id002 !ruby/object:Gem::Requirement
25
+ version_requirements: *71062650
26
+ - !ruby/object:Gem::Dependency
27
+ name: xmpp4r
28
+ requirement: &71062130 !ruby/object:Gem::Requirement
40
29
  none: false
41
- requirements:
42
- - - ">="
43
- - !ruby/object:Gem::Version
44
- hash: 1
45
- segments:
46
- - 0
47
- - 5
48
- version: "0.5"
30
+ requirements:
31
+ - - ! '>='
32
+ - !ruby/object:Gem::Version
33
+ version: '0.5'
49
34
  type: :runtime
50
- version_requirements: *id002
35
+ prerelease: false
36
+ version_requirements: *71062130
51
37
  description: Rumpy is some kind of framework to make up your own jabber bot quickly.
52
- email:
38
+ email:
53
39
  - mpogoda@lavabit.com
54
40
  - me@ximik.net
55
41
  executables: []
56
-
57
42
  extensions: []
58
-
59
- extra_rdoc_files:
43
+ extra_rdoc_files:
60
44
  - README.rdoc
61
- files:
45
+ files:
62
46
  - lib/rumpy.rb
63
47
  - README.rdoc
48
+ - lib/rumpy/bot.rb
49
+ - lib/rumpy/version.rb
64
50
  homepage: https://github.com/Ximik/Rumpy
65
- licenses:
51
+ licenses:
66
52
  - MIT
67
53
  post_install_message:
68
- rdoc_options:
54
+ rdoc_options:
69
55
  - --main
70
56
  - README.rdoc
71
- require_paths:
57
+ require_paths:
72
58
  - lib
73
- required_ruby_version: !ruby/object:Gem::Requirement
59
+ required_ruby_version: !ruby/object:Gem::Requirement
74
60
  none: false
75
- requirements:
76
- - - ">="
77
- - !ruby/object:Gem::Version
78
- hash: 3
79
- segments:
80
- - 0
81
- version: "0"
82
- required_rubygems_version: !ruby/object:Gem::Requirement
61
+ requirements:
62
+ - - ! '>='
63
+ - !ruby/object:Gem::Version
64
+ version: '0'
65
+ required_rubygems_version: !ruby/object:Gem::Requirement
83
66
  none: false
84
- requirements:
85
- - - ">="
86
- - !ruby/object:Gem::Version
87
- hash: 3
88
- segments:
89
- - 0
90
- version: "0"
67
+ requirements:
68
+ - - ! '>='
69
+ - !ruby/object:Gem::Version
70
+ version: '0'
91
71
  requirements: []
92
-
93
72
  rubyforge_project:
94
- rubygems_version: 1.8.10
73
+ rubygems_version: 1.8.15
95
74
  signing_key:
96
75
  specification_version: 3
97
76
  summary: Rumpy == jabber bot framework
98
77
  test_files: []
99
-