omnibot 0.0.21 → 0.0.22

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.
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
-