rumpy 0.9.22 → 0.9.23

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