omnibot 0.0.21 → 0.0.22

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 465f87c9979e343517e0f221dabedcbcac84d15f
4
- data.tar.gz: 5c1a5bd12cb86e83df8d2f8c683fcc221831d8e4
3
+ metadata.gz: 420769502e3d02ff10e46d8f35ebe5d9e58e204e
4
+ data.tar.gz: c3f3c964d5aeceaa6db8fc4bcd57003d3886c6be
5
5
  SHA512:
6
- metadata.gz: 75137c71b758d1d5290c72643b2ff32c477300f0cfd995357c7ae0ce0405ce8204a72844b889fbdf1e9f621e40222d737b99b74667745de58d6ab4e477471837
7
- data.tar.gz: 12bedd9e3751ba3214b69973520e41df70555290e6d66cdd0fa94f9716846db0f897bedd1f93493fe88fcce18f77f9f9ca5d565e3e119a49f72ced6fa025a95f
6
+ metadata.gz: b9adb175015c16571bd145838e1a1c743196453f49346e1f00c67ede9d48d99a97f03d0409191d4b517c7e9fa06099ca7d2111139cac562e816e193569c252dd
7
+ data.tar.gz: bf4820829a12830951f963eb8c9a617a831f711328ad419c7db7a69a1854267da647ede463d5f7d4a00b9be157aefed9ac3a1acdcd5828cb4fac4f5b976e2d67
data/Rakefile CHANGED
@@ -1 +1 @@
1
- require "bundler/gem_tasks"
1
+ require 'bundler/gem_tasks'
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env ruby
2
2
  # encoding: utf-8
3
3
  # You can specify this script in ~/.forward for mail forwarding
4
- IO.popen('omnisend','w') do |io|
5
- IO.new(STDIN.fileno, 'r:UTF-8').each { |s| io.write s }
4
+ IO.popen('omnisend', 'w') do |io|
5
+ IO.new(STDIN.fileno, 'r:UTF-8').each { |s| io.write s }
6
6
  end
@@ -14,48 +14,47 @@ require 'sqlite3'
14
14
  require 'socket'
15
15
  require 'date'
16
16
  require 'tmpdir'
17
+ require 'retryable'
17
18
 
18
- require "xray/thread_dump_signal_handler"
19
+ require 'xray/thread_dump_signal_handler'
19
20
 
20
21
  # patch from https://github.com/ln/xmpp4r/issues/3
21
22
 
22
- if RUBY_VERSION < "1.9"
23
+ if RUBY_VERSION < '1.9'
23
24
  # ...
24
25
  else
25
- # Encoding patch
26
- require 'socket'
27
- class TCPSocket
28
- def external_encoding
29
- Encoding::BINARY
30
- end
31
- end
32
-
33
- require 'rexml/source'
34
- class REXML::IOSource
35
- alias_method :encoding_assign, :encoding=
36
- def encoding=(value)
37
- encoding_assign(value) if value
38
- end
39
- end
40
-
41
- begin
42
- # OpenSSL is optional and can be missing
43
- require 'openssl'
44
- class OpenSSL::SSL::SSLSocket
45
- def external_encoding
46
- Encoding::BINARY
47
- end
48
- end
49
- rescue
50
- end
26
+ # Encoding patch
27
+ require 'socket'
28
+ class TCPSocket
29
+ def external_encoding
30
+ Encoding::BINARY
31
+ end
32
+ end
33
+
34
+ require 'rexml/source'
35
+ class REXML::IOSource
36
+ alias_method :encoding_assign, :encoding=
37
+ def encoding=(value)
38
+ encoding_assign(value) if value
39
+ end
40
+ end
41
+
42
+ begin
43
+ # OpenSSL is optional and can be missing
44
+ require 'openssl'
45
+ class OpenSSL::SSL::SSLSocket
46
+ def external_encoding
47
+ Encoding::BINARY
48
+ end
49
+ end
50
+ rescue # rubocop:disable Lint/HandleExceptions
51
+ end
51
52
  end
52
53
 
53
54
  # -----------------
54
55
 
55
56
  module OmniBot
56
-
57
- %w[ helpers jabberbot amqpconsumer omnisend launcher loggedcommand periodiccommand mailchecker ].each do |file|
58
- require "omnibot/#{file}.rb"
59
- end
60
-
57
+ %w( helpers jabberbot amqpconsumer omnisend launcher loggedcommand periodiccommand mailchecker ).each do |file|
58
+ require "omnibot/#{file}.rb"
59
+ end
61
60
  end
@@ -1,76 +1,76 @@
1
1
  # encoding: utf-8
2
2
 
3
3
  module OmniBot
4
-
5
- # AMQP consumer class
6
- class AMQPConsumer
7
-
8
- attr_accessor :handlers
9
- attr_accessor :db
10
-
11
- def send_message message
12
- begin
13
- @omnibot.add_message [Time.now, message]
14
- rescue Object => e
15
- OmniLog::error "Sending message error: #{e.message}\ntrace:\n#{Helpers::backtrace e}\nIgnoring..."
16
- end
17
- end
18
-
19
- def initialize config
20
- @config = config
21
- end
22
-
23
- def amqp_loop
24
- AMQP.start do |connection|
25
- OmniLog::info "Setup amqp gem #{AMQP::VERSION}, AMQP protocol #{AMQ::Protocol::VERSION}..."
26
- mq = AMQP::Channel.new(connection)
27
- exchange = mq.direct(Helpers::amqp_exchange_name)
28
- queue = mq.queue('', :exclusive => true).bind(exchange, :routing_key => Helpers::amqp_routing_key)
29
-
30
- OmniLog::info "Setup omnibot..."
31
- @omnibot = JabberBot.new(Jabber::JID::new(@config['omnibotuser']), @config['omnibotpass'])
32
- @omnibot.timer_provider = EM
33
- @omnibot.set_subscriber Jabber::JID::new(@config['notifyjid']), @config['notifyresource']
34
- @omnibot.connect
35
-
36
- @handlers.each_with_index do |handler, index|
37
- OmniLog::info "Setup handler #{handler.to_s}..."
38
- handler.timer_provider = EM
39
- handler.set_jabber_messenger { |message| send_message message }
40
- handler.startup_pause = index*10
41
- handler.start
42
- end
43
-
44
- OmniLog::info "==== AMQP is ready ===="
45
-
46
- queue.subscribe do |msg|
47
- message = Marshal.load(Base64.decode64(msg)).force_encoding('UTF-8')
48
- send_message message
49
- end
50
-
51
- end
52
-
53
- # it is a function rescue block, don't be afraid
54
- rescue => e
55
- OmniLog::error "AMQP/Jabber setup error: #{e.message}\ntrace:\n#{Helpers::backtrace e}\nExiting..."
56
- AMQP.stop{ EM.stop }
57
- end
58
-
59
- # Main AMQP loop
60
- def start
61
-
62
- # exit hook
63
- Signal.trap('INT') do
64
- OmniLog::info "It's a trap, should exit..."
65
- AMQP.stop{ EM.stop }
66
- end
67
-
68
- amqp_loop
69
-
70
- OmniLog::info "Exited"
71
- end
72
-
73
- end
74
-
4
+ # AMQP consumer class
5
+ class AMQPConsumer
6
+ attr_accessor :handlers
7
+ attr_accessor :db
8
+
9
+ def send_message(message)
10
+ @omnibot.add_message [Time.now, message]
11
+ rescue Object => e
12
+ OmniLog::error "Sending message error: #{e.message}\ntrace:\n#{Helpers::backtrace e}\nIgnoring..."
13
+ end
14
+
15
+ def initialize(config)
16
+ @config = config
17
+ end
18
+
19
+ def amqp_loop
20
+ AMQP.start do |connection|
21
+ OmniLog::info "Setup amqp gem #{AMQP::VERSION}, AMQP protocol #{AMQ::Protocol::VERSION}..."
22
+
23
+ connection.on_tcp_connection_loss do |conn, _settings|
24
+ OmniLog::info '[network failure] Trying to reconnect...'
25
+ conn.reconnect(false, 30)
26
+ end
27
+
28
+ mq = AMQP::Channel.new(connection)
29
+ exchange = mq.direct(Helpers::amqp_exchange_name)
30
+ queue = mq.queue('', exclusive: true).bind(exchange, routing_key: Helpers::amqp_routing_key)
31
+
32
+ OmniLog::info 'Setup omnibot...'
33
+ @omnibot = JabberBot.new(Jabber::JID::new(@config['omnibotuser']), @config['omnibotpass'])
34
+ @omnibot.timer_provider = EM
35
+ @omnibot.set_subscriber Jabber::JID::new(@config['notifyjid']), @config['notifyresource']
36
+ @omnibot.connect
37
+
38
+ @handlers.each_with_index do |handler, index|
39
+ OmniLog::info "Setup handler #{handler}..."
40
+ handler.timer_provider = EM
41
+ handler.jabber_messenger { |message| send_message message }
42
+ handler.startup_pause = index * 10
43
+ handler.start
44
+ end
45
+
46
+ OmniLog::info '==== AMQP is ready ===='
47
+
48
+ queue.subscribe do |msg|
49
+ message = Marshal.load(Base64.decode64(msg)).force_encoding('UTF-8')
50
+ send_message message
51
+ end
52
+ end
53
+ end
54
+
55
+ # Main AMQP loop
56
+ def start
57
+ # exit hook
58
+ Signal.trap('INT') do
59
+ OmniLog::info "It's a trap, should exit..."
60
+ AMQP.stop { EM.stop }
61
+ end
62
+
63
+ begin
64
+ exception_cb = proc { |e| OmniLog::error "Cannot connect to AMQP: #{e.message}" }
65
+ Retryable.retryable(tries: 5, sleep: ->(n) { 3**n }, exception_cb: exception_cb, on: AMQP::TCPConnectionFailed) do
66
+ amqp_loop
67
+ end
68
+ rescue => e
69
+ OmniLog::error "AMQP/Jabber setup error: #{e.message}\ntrace:\n#{Helpers::backtrace e}\nExiting..."
70
+ AMQP.stop { EM.stop }
71
+ end
72
+
73
+ OmniLog::info 'Exited'
74
+ end
75
+ end
75
76
  end
76
-
@@ -1,71 +1,71 @@
1
1
  # encoding: utf-8
2
2
 
3
3
  module OmniBot
4
+ class OmniLog
5
+ def self.init_log
6
+ logger = Logger.new('omnibot.log')
7
+ logger.level = Logger::DEBUG
8
+ logger
9
+ end
4
10
 
5
- class OmniLog
6
-
7
- def self.init_log
8
- logger = Logger.new('omnibot.log')
9
- logger.level = Logger::DEBUG
10
- logger
11
- end
12
-
13
- def self.log=(value)
14
- @logger = value
15
- end
16
-
17
- def self.log
18
- @logger
19
- end
20
-
21
- def self.debug(progname = nil, &block); @logger.debug(progname, &block); end
22
- def self.info(progname = nil, &block); @logger.info(progname, &block); end
23
- def self.warn(progname = nil, &block); @logger.warn(progname, &block); end
24
- def self.error(progname = nil, &block); @logger.error(progname, &block); end
25
- def self.fatal(progname = nil, &block); @logger.fatal(progname, &block); end
26
- end
27
-
28
- # Helper class for counting reconnect attempts
29
- class AttemptCounter
30
- def report
31
- OmniLog::debug "AttemptCounter: try #{@counter} of #{@max_attempts}"
32
- end
33
- public
34
- def initialize max_attempts
35
- @counter = 0
36
- @max_attempts = max_attempts
37
- OmniLog::debug "AttemptCounter inited"
38
- end
39
-
40
- def out_of_attempts?
41
- @counter >= @max_attempts
42
- end
43
-
44
- def increase
45
- @counter += 1
46
- report
47
- end
48
- end
49
-
50
- class Helpers
51
- def self.backtrace e
52
- e.respond_to?(:backtrace) && e.backtrace ? e.backtrace.join("\n\t") : ""
53
- end
54
-
55
- def self.same_day? t1, t2
56
- t1.year == t2.year && t1.month == t2.month && t1.day == t2.day
57
- end
58
-
59
- def self.amqp_exchange_name
60
- 'omnibot-exchange'
61
- end
62
-
63
- def self.amqp_routing_key
64
- 'omnibot-routing'
65
- end
66
-
67
- end
11
+ def self.log=(value)
12
+ @logger = value
13
+ end
68
14
 
69
- end
15
+ def self.log
16
+ @logger
17
+ end
18
+
19
+ def self.debug(progname = nil, &block); @logger.debug(progname, &block); end
20
+
21
+ def self.info(progname = nil, &block); @logger.info(progname, &block); end
22
+
23
+ def self.warn(progname = nil, &block); @logger.warn(progname, &block); end
24
+
25
+ def self.error(progname = nil, &block); @logger.error(progname, &block); end
26
+
27
+ def self.fatal(progname = nil, &block); @logger.fatal(progname, &block); end
28
+ end
29
+
30
+ # Helper class for counting reconnect attempts
31
+ class AttemptCounter
32
+ def report
33
+ OmniLog::debug "AttemptCounter: try #{@counter} of #{@max_attempts}"
34
+ end
70
35
 
36
+ public
71
37
 
38
+ def initialize(max_attempts)
39
+ @counter = 0
40
+ @max_attempts = max_attempts
41
+ OmniLog::debug 'AttemptCounter inited'
42
+ end
43
+
44
+ def out_of_attempts?
45
+ @counter >= @max_attempts
46
+ end
47
+
48
+ def increase
49
+ @counter += 1
50
+ report
51
+ end
52
+ end
53
+
54
+ class Helpers
55
+ def self.backtrace(e)
56
+ e.respond_to?(:backtrace) && e.backtrace ? e.backtrace.join("\n\t") : ''
57
+ end
58
+
59
+ def self.same_day?(t1, t2)
60
+ t1.year == t2.year && t1.month == t2.month && t1.day == t2.day
61
+ end
62
+
63
+ def self.amqp_exchange_name
64
+ 'omnibot-exchange'
65
+ end
66
+
67
+ def self.amqp_routing_key
68
+ 'omnibot-routing'
69
+ end
70
+ end
71
+ end
@@ -1,209 +1,202 @@
1
1
  # encoding: utf-8
2
2
 
3
3
  module OmniBot
4
-
5
- # Jabber bot with reconnection and dnd-care logic
6
- class JabberBot
7
-
8
- def dump_presence p
9
- p ? "Presence status=#{p.status} type=#{p.type} show=#{p.show} from=#{p.from} to=#{p.to} xml=(#{p.to_s})" : "nil"
10
- end
11
-
12
- def on_message_handler m
13
- OmniLog::debug "Got jabber message from #{m.from}:\n#{m.body}\n."
14
- end
15
-
16
- def is_needed_user? jid
17
- jid.strip == @subscriber && @subscriber_resource.match((jid.resource or ''))
18
- end
19
-
20
- def on_presence_callback old_presence, new_presence
21
- OmniLog::debug "Presence changed:\n...old #{dump_presence old_presence}\n...new #{dump_presence new_presence}"
22
- if is_needed_user? old_presence.from
23
- @subscriber_online = check_presence? old_presence
24
- @subscriber_concrete_jid = old_presence.from
25
- OmniLog::debug "Subscriber #{@subscriber} is #{@subscriber_online ? "ready" : "not ready"}"
26
- pump_messages if @subscriber_online
27
-
28
- unless @greeting_done
29
- @greeting_done = true
30
- add_message [Time.now, 'Hello, I am online']
31
- end
32
- end
33
- end
34
-
35
- def on_subscripton_request_callback item, pres
36
- OmniLog::debug "Subscription request item=#{item} pres=#{dump_presence pres}"
37
- end
38
-
39
- def on_exception_handler e, stream, sym_where
40
- OmniLog::error "Jabber exception of #{e ? e.class : nil} happens at symbol \"#{sym_where}\": #{e}\nbacktrace\n#{Helpers::backtrace e}"
41
- OmniLog::debug "stream is #{stream} vs client #{@client}"
42
- on_generic_exception_handler e
43
- end
44
-
45
- def safe_reconnect
46
- begin
47
- reconnect
48
- rescue Jabber::ClientAuthenticationFailure => e
49
- OmniLog::error "Authentification error: #{e.class}: #{e}"
50
- raise
51
- rescue Exception => e
52
- OmniLog::error "Reconnect hard error: #{e.class}: #{e}"
53
- on_generic_exception_handler e
54
- end
55
- end
56
-
57
- def on_generic_exception_handler e
58
- if e && (e.kind_of?(Jabber::ServerDisconnected) || e.class.to_s =~ /^Errno::.+/ || e.kind_of?(SocketError))
59
- OmniLog::warn "Looking to error, ign=#{@ignore_reconnect}, tp=#{@timer_provider}"
60
- OmniLog::error "No timer provider assigned" unless @timer_provider
61
- # attempt counter is set when it's needed to connect
62
- unless @ignore_reconnect
63
- @timer_provider.add_timer(@reconnect_pause) { try_reconnect }
64
- end
65
- else
66
- OmniLog::warn "Ignoring error #{e}"
67
- end
68
- end
69
-
70
- def reconnect
71
- OmniLog::debug 'Going to reconnect'
72
- @client.connect
73
- @client.auth(@password)
74
- @client.send(Jabber::Presence.new.set_type(:available))
75
- end
76
-
77
- def try_reconnect
78
- OmniLog::debug "Called try_reconnect, #{@client.inspect}, #{@client.is_connected?}"
79
- return if @client.is_connected?
80
-
81
- OmniLog::debug 'Called try_reconnect'
82
-
83
- @attempt_counter = AttemptCounter.new(5) unless @attempt_counter
84
- @attempt_counter.increase
85
-
86
- if @attempt_counter.out_of_attempts?
87
- OmniLog::warn "Can't reconect too often, sleep for #{@reconnect_long_pause/60} minutes..."
88
- @attempt_counter = nil
89
- @ignore_reconnect = true
90
- @timer_provider.add_timer(@reconnect_long_pause) {
91
- @ignore_reconnect = false
92
- try_reconnect
93
- }
94
- return
95
- end
96
-
97
- safe_reconnect
98
-
99
- if @client.is_connected?
100
- @attempt_counter = nil
101
- @roster = Jabber::Roster::Helper.new(@client)
102
- @roster.add_subscription_request_callback { |item, pres| on_subscripton_request_callback item, pres }
103
- end
104
-
105
- OmniLog::debug "Client #{@client.is_connected? ? 'is' : 'isn\'t'} connected"
106
- end
107
-
108
- def check_presence? presence
109
- raise 'No subscriber' unless @subscriber
110
-
111
- if presence.type == nil
112
- OmniLog::debug "Subscriber status #{presence.show ? presence.show : 'online'}"
113
- return presence.show == nil || presence.show == :chat
114
- elsif presence.type == :unavailable
115
- OmniLog::debug "Subscriber goes offline"
116
- return false
117
- else
118
- return false
119
- end
120
- end
121
-
122
- def say_when_human orig, now
123
- if Helpers::same_day? now, orig
124
- amount = now - orig
125
- if amount < 60
126
- return "just now"
127
- elsif amount < 60*60
128
- return "less than a hour ago"
129
- elsif amount < 60*60*2
130
- return " two hours ago"
131
- elsif amount < 60*60*6
132
- return amount.div(3600).to_s + " hours ago"
133
- end
134
- end
135
- return orig.to_s
136
- end
137
-
138
-
139
- def pump_messages
140
- while msg = @messages.shift
141
- send msg
142
- end
143
- end
144
-
145
- public
146
-
147
- attr_writer :timer_provider
148
-
149
- def initialize jid, password
150
- @client = Jabber::Client::new(jid)
151
- @password = password
152
- raise 'No jid set' if jid.empty?
153
- raise 'No password set' unless password
154
-
155
- @ignore_reconnect = false
156
- @reconnect_pause = 10
157
- @reconnect_long_pause = 60*15
158
-
159
- @messages = []
160
- @subscriber_online = false
161
- @subscriber_concrete_jid = nil
162
-
163
- @client.on_exception { |e, stream, sym_where| on_exception_handler(e, stream, sym_where) }
164
- @client.add_message_callback { |m| on_message_handler m }
165
- @client.add_presence_callback { |from, to| on_presence_callback from, to }
166
- end
167
-
168
- def connect
169
- try_reconnect
170
- end
171
-
172
- def disconnect
173
- @client.close
174
- end
175
-
176
- def set_subscriber jid, resource=nil
177
- @subscriber = jid
178
- if resource == nil || resource == ''
179
- @subscriber_resource = /.*/
180
- else
181
- @subscriber_resource = Regexp.new(resource)
182
- end
183
- end
184
-
185
- def add_message message
186
- OmniLog::debug "Register a message, " + (@subscriber_online ? "should send immediately" : "will send later")
187
- @messages << message
188
- pump_messages if @subscriber_online
189
- end
190
-
191
- def send message
192
- raise 'Not connected' unless @client.is_connected?
193
- raise 'No concrete jid' unless @subscriber_concrete_jid
194
-
195
- OmniLog::info "Sending a message..."
196
- orig = message[0]
197
- content = message[1]
198
-
199
- body = "Omnibot reported " + say_when_human(orig, Time.now) + ":\n" + content.to_s
200
- OmniLog::debug body
201
-
202
- msg = Jabber::Message::new(@subscriber_concrete_jid, body)
203
- msg.type = :chat
204
- @client.send(msg)
205
- end
206
- end
207
-
4
+ # Jabber bot with reconnection and dnd-care logic
5
+ class JabberBot
6
+ def dump_presence(p)
7
+ p ? "Presence status=#{p.status} type=#{p.type} show=#{p.show} from=#{p.from} to=#{p.to} xml=(#{p})" : 'nil'
8
+ end
9
+
10
+ def on_message_handler(m)
11
+ OmniLog::debug "Got jabber message from #{m.from}:\n#{m.body}\n."
12
+ end
13
+
14
+ def needed_user?(jid)
15
+ jid.strip == @subscriber && @subscriber_resource.match((jid.resource || ''))
16
+ end
17
+
18
+ def on_presence_callback(old_presence, _new_presence)
19
+ # OmniLog::debug "Presence changed:\n...old #{dump_presence old_presence}\n...new #{dump_presence new_presence}"
20
+ return unless needed_user? old_presence.from
21
+ @subscriber_online = check_presence? old_presence
22
+ @subscriber_concrete_jid = old_presence.from
23
+ OmniLog::debug "Subscriber #{@subscriber} is #{@subscriber_online ? 'ready' : 'not ready'}"
24
+ pump_messages if @subscriber_online
25
+
26
+ unless @greeting_done
27
+ @greeting_done = true
28
+ add_message [Time.now, 'Hello, I am online']
29
+ end
30
+ end
31
+
32
+ def on_subscripton_request_callback(item, pres)
33
+ OmniLog::debug "Subscription request item=#{item} pres=#{dump_presence pres}"
34
+ end
35
+
36
+ def on_exception_handler(e, stream, sym_where)
37
+ OmniLog::error "Jabber exception of #{e ? e.class : nil} happens at symbol \"#{sym_where}\": #{e}\nbacktrace\n#{Helpers::backtrace e}"
38
+ OmniLog::debug "stream is #{stream} vs client #{@client}"
39
+ on_generic_exception_handler e
40
+ end
41
+
42
+ def safe_reconnect
43
+ reconnect
44
+ rescue Jabber::ClientAuthenticationFailure => e
45
+ OmniLog::error "Authentification error: #{e.class}: #{e}"
46
+ raise
47
+ rescue Exception => e # rubocop:disable Lint/RescueException
48
+ # needed to handle all errors from xmpp
49
+ OmniLog::error "Reconnect hard error: #{e.class}: #{e}"
50
+ on_generic_exception_handler e
51
+ end
52
+
53
+ def on_generic_exception_handler(e)
54
+ if e && (e.is_a?(Jabber::ServerDisconnected) || e.class.to_s =~ /^Errno::.+/ || e.is_a?(SocketError))
55
+ OmniLog::warn "Looking to error, ign=#{@ignore_reconnect}, tp=#{@timer_provider}"
56
+ OmniLog::error 'No timer provider assigned' unless @timer_provider
57
+ # attempt counter is set when it's needed to connect
58
+ unless @ignore_reconnect
59
+ @timer_provider.add_timer(@reconnect_pause) { try_reconnect }
60
+ end
61
+ else
62
+ OmniLog::warn "Ignoring error #{e}"
63
+ end
64
+ end
65
+
66
+ def reconnect
67
+ OmniLog::debug 'Going to reconnect'
68
+ @client.connect
69
+ @client.auth(@password)
70
+ @client.send(Jabber::Presence.new.set_type(:available))
71
+ end
72
+
73
+ def try_reconnect
74
+ OmniLog::debug "Called try_reconnect, #{@client.inspect}, #{@client.is_connected?}"
75
+ return if @client.is_connected?
76
+
77
+ OmniLog::debug 'Called try_reconnect'
78
+
79
+ @attempt_counter = AttemptCounter.new(5) unless @attempt_counter
80
+ @attempt_counter.increase
81
+
82
+ if @attempt_counter.out_of_attempts?
83
+ OmniLog::warn "Can't reconect too often, sleep for #{@reconnect_long_pause / 60} minutes..."
84
+ @attempt_counter = nil
85
+ @ignore_reconnect = true
86
+ @timer_provider.add_timer(@reconnect_long_pause) do
87
+ @ignore_reconnect = false
88
+ try_reconnect
89
+ end
90
+ return
91
+ end
92
+
93
+ safe_reconnect
94
+
95
+ if @client.is_connected?
96
+ @attempt_counter = nil
97
+ @roster = Jabber::Roster::Helper.new(@client)
98
+ @roster.add_subscription_request_callback { |item, pres| on_subscripton_request_callback item, pres }
99
+ end
100
+
101
+ OmniLog::debug "Client #{@client.is_connected? ? 'is' : 'isn\'t'} connected"
102
+ end
103
+
104
+ def check_presence?(presence)
105
+ raise 'No subscriber' unless @subscriber
106
+
107
+ if presence.type.nil?
108
+ OmniLog::debug "Subscriber status #{presence.show ? presence.show : 'online'}"
109
+ return presence.show.nil? || presence.show == :chat
110
+ elsif presence.type == :unavailable
111
+ OmniLog::debug 'Subscriber goes offline'
112
+ return false
113
+ else
114
+ return false
115
+ end
116
+ end
117
+
118
+ def say_when_human(orig, now)
119
+ if Helpers::same_day? now, orig
120
+ amount = now - orig
121
+ if amount < 60
122
+ return 'just now'
123
+ elsif amount < 60 * 60
124
+ return 'less than a hour ago'
125
+ elsif amount < 60 * 60 * 2
126
+ return ' two hours ago'
127
+ elsif amount < 60 * 60 * 6
128
+ return amount.div(3600).to_s + ' hours ago'
129
+ end
130
+ end
131
+ orig.to_s
132
+ end
133
+
134
+ def pump_messages
135
+ while (msg = @messages.shift)
136
+ send msg
137
+ end
138
+ end
139
+
140
+ public
141
+
142
+ attr_writer :timer_provider
143
+
144
+ def initialize(jid, password)
145
+ @client = Jabber::Client::new(jid)
146
+ @password = password
147
+ raise 'No jid set' if jid.empty?
148
+ raise 'No password set' unless password
149
+
150
+ @ignore_reconnect = false
151
+ @reconnect_pause = 10
152
+ @reconnect_long_pause = 60 * 15
153
+
154
+ @messages = []
155
+ @subscriber_online = false
156
+ @subscriber_concrete_jid = nil
157
+
158
+ @client.on_exception { |e, stream, sym_where| on_exception_handler(e, stream, sym_where) }
159
+ @client.add_message_callback { |m| on_message_handler m }
160
+ @client.add_presence_callback { |from, to| on_presence_callback from, to }
161
+ end
162
+
163
+ def connect
164
+ try_reconnect
165
+ end
166
+
167
+ def disconnect
168
+ @client.close
169
+ end
170
+
171
+ def set_subscriber(jid, resource = nil)
172
+ @subscriber = jid
173
+ if resource.nil? || resource == ''
174
+ @subscriber_resource = /.*/
175
+ else
176
+ @subscriber_resource = Regexp.new(resource)
177
+ end
178
+ end
179
+
180
+ def add_message(message)
181
+ OmniLog::debug 'Register a message, ' + (@subscriber_online ? 'should send immediately' : 'will send later')
182
+ @messages << message
183
+ pump_messages if @subscriber_online
184
+ end
185
+
186
+ def send(message)
187
+ raise 'Not connected' unless @client.is_connected?
188
+ raise 'No concrete jid' unless @subscriber_concrete_jid
189
+
190
+ OmniLog::info 'Sending a message...'
191
+ orig = message[0]
192
+ content = message[1]
193
+
194
+ body = 'Omnibot reported ' + say_when_human(orig, Time.now) + ":\n" + content.to_s
195
+ OmniLog::debug body
196
+
197
+ msg = Jabber::Message::new(@subscriber_concrete_jid, body)
198
+ msg.type = :chat
199
+ @client.send(msg)
200
+ end
201
+ end
208
202
  end
209
-