zabbirc 0.0.1
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 +7 -0
- data/bin/zabbirc +32 -0
- data/bin/zabbirc-install +16 -0
- data/config/config.rb +5 -0
- data/lib/zabbirc/configuration.rb +43 -0
- data/lib/zabbirc/irc/plugin.rb +20 -0
- data/lib/zabbirc/irc/plugin_methods.rb +208 -0
- data/lib/zabbirc/logger.rb +45 -0
- data/lib/zabbirc/op.rb +67 -0
- data/lib/zabbirc/op_list.rb +47 -0
- data/lib/zabbirc/priority.rb +38 -0
- data/lib/zabbirc/service.rb +86 -0
- data/lib/zabbirc/services/base.rb +56 -0
- data/lib/zabbirc/services/events.rb +31 -0
- data/lib/zabbirc/services/ops.rb +38 -0
- data/lib/zabbirc/setting.rb +31 -0
- data/lib/zabbirc/stop_error.rb +3 -0
- data/lib/zabbirc/zabbix/connection.rb +26 -0
- data/lib/zabbirc/zabbix/event.rb +94 -0
- data/lib/zabbirc/zabbix/host.rb +6 -0
- data/lib/zabbirc/zabbix/resource/associations.rb +30 -0
- data/lib/zabbirc/zabbix/resource/base.rb +52 -0
- data/lib/zabbirc/zabbix/resource/finders.rb +37 -0
- data/lib/zabbirc/zabbix/trigger.rb +47 -0
- data/lib/zabbirc/zabbix/user.rb +10 -0
- data/lib/zabbirc.rb +24 -0
- data/spec/bot_spec.rb +186 -0
- data/spec/spec_helper.rb +96 -0
- data/spec/support/mock_bot.rb +29 -0
- data/templates/zabbirc_config.rb +14 -0
- data/thread_test.rb +46 -0
- data/tmp/playground.rb +37 -0
- metadata +171 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: fa6b2a62b76aecb1541a8608444b54aa0435258a
|
4
|
+
data.tar.gz: cd3e76f12c22caed15cf276cf4518dac82160946
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: ec786a1141cff5e769b91cefe1ca45b9e2fa97b7f4a15ad70f5fa13c614acfb63c4674f606f365d473e2f92938589fc154703ea2cad767b661f8869196141be9
|
7
|
+
data.tar.gz: 3a0b23a5017ceb2f8aabdcc73af0a300bb2795595a2f813a77d1ff054a35d0ed469b7b44869c9a2dfd5071777eca7269b20af4d07651cb9bc907098fb5748a4a
|
data/bin/zabbirc
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
require 'zabbirc'
|
3
|
+
require 'pry'
|
4
|
+
this_file = __FILE__
|
5
|
+
config_path = File.expand_path(ARGV[0])
|
6
|
+
|
7
|
+
unless config_path
|
8
|
+
$stderr.puts "No config path specified"
|
9
|
+
exit false
|
10
|
+
end
|
11
|
+
unless File.exists?(config_path)
|
12
|
+
$stderr.puts "Could not find config file: #{config_path}"
|
13
|
+
exit false
|
14
|
+
end
|
15
|
+
|
16
|
+
require_relative config_path
|
17
|
+
|
18
|
+
Zabbirc.logger # initializes logger
|
19
|
+
exit false unless Zabbirc::Zabbix::Connection.test_connection
|
20
|
+
|
21
|
+
s = Zabbirc::Service.new
|
22
|
+
|
23
|
+
trap "SIGINT" do
|
24
|
+
s.stop
|
25
|
+
end
|
26
|
+
|
27
|
+
trap "SIGTERM" do
|
28
|
+
s.stop
|
29
|
+
end
|
30
|
+
|
31
|
+
s.start
|
32
|
+
s.join
|
data/bin/zabbirc-install
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
case ARGV.size
|
3
|
+
when 0
|
4
|
+
install_path = Dir.pwd
|
5
|
+
when 1
|
6
|
+
install_path = ARGV[0]
|
7
|
+
when 2
|
8
|
+
puts "Too many arguments"
|
9
|
+
exit 1
|
10
|
+
end
|
11
|
+
|
12
|
+
puts "Installing config file into: #{install_path}"
|
13
|
+
templates_path = Pathname.new(File.expand_path(Pathname.new(File.dirname(__FILE__)).join("../templates")))
|
14
|
+
|
15
|
+
FileUtils.cp(templates_path.join("zabbirc_config.rb"), install_path)
|
16
|
+
puts "Installed"
|
data/config/config.rb
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'active_support/configurable'
|
2
|
+
|
3
|
+
module Zabbirc
|
4
|
+
def self.configure(&block)
|
5
|
+
block.call(@config ||= Zabbirc::Configuration.new)
|
6
|
+
end
|
7
|
+
|
8
|
+
def self.config
|
9
|
+
@config
|
10
|
+
end
|
11
|
+
|
12
|
+
class Configuration #:nodoc:
|
13
|
+
include ActiveSupport::Configurable
|
14
|
+
|
15
|
+
config_accessor :zabbix_api_url
|
16
|
+
config_accessor :zabbix_login
|
17
|
+
config_accessor :zabbix_password
|
18
|
+
|
19
|
+
config_accessor :irc_server
|
20
|
+
config_accessor :irc_channels
|
21
|
+
|
22
|
+
config_accessor :events_check_interval
|
23
|
+
config_accessor :notify_about_events_from_last
|
24
|
+
|
25
|
+
def param_name
|
26
|
+
config.param_name.respond_to?(:call) ? config.param_name.call : config.param_name
|
27
|
+
end
|
28
|
+
|
29
|
+
# define param_name writer (copied from AS::Configurable)
|
30
|
+
writer, line = 'def param_name=(value); config.param_name = value; end', __LINE__
|
31
|
+
singleton_class.class_eval writer, __FILE__, line
|
32
|
+
class_eval writer, __FILE__, line
|
33
|
+
end
|
34
|
+
|
35
|
+
# this is ugly. why can't we pass the default value to config_accessor...?
|
36
|
+
configure do |config|
|
37
|
+
config.events_check_interval = 10.seconds
|
38
|
+
config.notify_about_events_from_last = 5.minutes
|
39
|
+
|
40
|
+
config.irc_server = "irc.freenode.org"
|
41
|
+
config.irc_channels = ["#zabbirc-test", "#zabbirc-test-2"]
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require_relative "plugin_methods"
|
2
|
+
|
3
|
+
module Zabbirc
|
4
|
+
module Irc
|
5
|
+
class Plugin
|
6
|
+
include Cinch::Plugin
|
7
|
+
include PluginMethods
|
8
|
+
|
9
|
+
listen_to :join, method: :sync_ops
|
10
|
+
listen_to :leaving, method: :sync_ops
|
11
|
+
|
12
|
+
match "settings", method: :show_settings
|
13
|
+
match /settings set ([#_a-zA-Z0-9]+)( ([#\-_a-zA-Z0-9]+))?/, method: :set_setting
|
14
|
+
match "events", method: :list_events
|
15
|
+
match /status ([a-zA-Z0-9\-.]+)/, method: :host_status
|
16
|
+
match /latest ([a-zA-Z0-9\-.]+)( (\d+))?/, method: :host_latest
|
17
|
+
match /ack (\d+) (.*)/, method: :acknowledge_event
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,208 @@
|
|
1
|
+
module Zabbirc
|
2
|
+
module Irc
|
3
|
+
module PluginMethods
|
4
|
+
|
5
|
+
def acknowledge_event m, eventid, message
|
6
|
+
return unless authenticate m.user.nick
|
7
|
+
op = get_op m
|
8
|
+
event = find_event m, eventid
|
9
|
+
return unless event
|
10
|
+
|
11
|
+
if event.acknowledge "#{op.nick}: #{message}"
|
12
|
+
m.reply "#{op.nick}: Event `#{event.label}` acknowledged with message: #{message}"
|
13
|
+
else
|
14
|
+
m.reply "#{op.nick}: Could not acknowledge event `#{event.label}`"
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def host_status m, host
|
19
|
+
return unless authenticate m.user.nick
|
20
|
+
op = get_op m
|
21
|
+
host = find_host m, host
|
22
|
+
return unless host
|
23
|
+
|
24
|
+
triggers = Zabbix::Trigger.get(hostids: host.id, filter: {value: 1}, selectHosts: :extend)
|
25
|
+
triggers = triggers.sort{|x,y| x.priority <=> y.priority }
|
26
|
+
msg = ["#{op.nick}: Host: #{host.name}"]
|
27
|
+
if triggers.empty?
|
28
|
+
msg[0] << " - status: OK"
|
29
|
+
else
|
30
|
+
msg[0] << " - status: #{triggers.size} problems"
|
31
|
+
triggers.each do |trigger|
|
32
|
+
msg << "#{op.nick}: status: #{trigger.label}"
|
33
|
+
end
|
34
|
+
end
|
35
|
+
m.reply msg.join("\n")
|
36
|
+
end
|
37
|
+
|
38
|
+
def host_latest m, host, _rest, limit
|
39
|
+
limit ||= 8
|
40
|
+
return unless authenticate m.user.nick
|
41
|
+
op = get_op m
|
42
|
+
host = find_host m, host
|
43
|
+
return unless host
|
44
|
+
|
45
|
+
msg = ["#{op.nick}: Host: #{host.name}"]
|
46
|
+
events = Zabbix::Event.get(hostids: host.id, limit: limit, selectHosts: :extend, selectRelatedObject: :extend, sortfield: :clock, sortorder: "DESC")
|
47
|
+
if events.empty?
|
48
|
+
msg[0] << " - no events found"
|
49
|
+
else
|
50
|
+
msg[0] << " - showing last #{events.size} events"
|
51
|
+
events.each do |event|
|
52
|
+
msg << "#{op.nick}: !latest: #{event.label}"
|
53
|
+
end
|
54
|
+
end
|
55
|
+
m.reply msg.join("\n")
|
56
|
+
end
|
57
|
+
|
58
|
+
def sync_ops m, u=nil
|
59
|
+
return if u and u.nick == bot.nick
|
60
|
+
bot.zabbirc_service.ops_service.iterate
|
61
|
+
end
|
62
|
+
|
63
|
+
### Settings
|
64
|
+
def show_settings m
|
65
|
+
return unless authenticate m.user.nick
|
66
|
+
op = get_op m
|
67
|
+
m.reply "#{op.nick}: #{op.setting}"
|
68
|
+
end
|
69
|
+
|
70
|
+
def set_setting m, key, _rest, value
|
71
|
+
return unless authenticate m.user.nick
|
72
|
+
op = get_op m
|
73
|
+
case key
|
74
|
+
when "notify"
|
75
|
+
set_notify m, op, value
|
76
|
+
when "events_priority"
|
77
|
+
set_events_priority m, op, value
|
78
|
+
when "primary_channel"
|
79
|
+
set_primary_channel m, op, value
|
80
|
+
else
|
81
|
+
m.reply "#{op.nick}: unknown setting `#{key}`"
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
def set_notify m, op, value
|
86
|
+
if value.nil?
|
87
|
+
m.reply "#{op.nick}: notify allowed values: true, on, 1, false, off, 0"
|
88
|
+
return
|
89
|
+
end
|
90
|
+
case value
|
91
|
+
when "true", "on", "1"
|
92
|
+
op.setting.set :notify, true
|
93
|
+
when "false", "off", "0"
|
94
|
+
op.setting.set :notify, false
|
95
|
+
else
|
96
|
+
m.reply "#{op.nick}: uknown value `#{value}`. Allowed values: true, on, 1, false, off, 0"
|
97
|
+
return
|
98
|
+
end
|
99
|
+
m.reply "#{op.nick}: setting `notify` was set to `#{op.setting.get :notify}`"
|
100
|
+
end
|
101
|
+
|
102
|
+
def set_events_priority m, op, value
|
103
|
+
if value.nil?
|
104
|
+
m.reply "#{op.nick}: events_priority allowed values: #{Priority::PRIORITIES.values.collect{|v| "`#{v}`"}.join(', ')} or numeric #{Priority::PRIORITIES.keys.join(", ")} "
|
105
|
+
return
|
106
|
+
end
|
107
|
+
begin
|
108
|
+
value = value.to_i if value =~ /^\d+$/
|
109
|
+
priority = Priority.new value
|
110
|
+
rescue ArgumentError
|
111
|
+
m.reply "#{op.nick}: uknown value `#{value}`. Allowed values: #{Priority::PRIORITIES.values.collect{|v| "`#{v}`"}.join(', ')} or numeric #{Priority::PRIORITIES.keys.join(", ")} "
|
112
|
+
return
|
113
|
+
end
|
114
|
+
op.setting.set :events_priority, priority.code
|
115
|
+
m.reply "#{op.nick}: setting `events_priority` was set to `#{op.setting.get :events_priority}`"
|
116
|
+
end
|
117
|
+
|
118
|
+
def set_primary_channel m, op, value
|
119
|
+
channel_names = op.channels.collect(&:name)
|
120
|
+
if value.nil?
|
121
|
+
m.reply "#{op.nick}: notify allowed values: #{channel_names.join(", ")}"
|
122
|
+
return
|
123
|
+
end
|
124
|
+
case value
|
125
|
+
when *channel_names
|
126
|
+
op.setting.set :primary_channel, value
|
127
|
+
else
|
128
|
+
m.reply "#{op.nick}: uknown value `#{value}`. Allowed values: #{channel_names.join(", ")}"
|
129
|
+
return
|
130
|
+
end
|
131
|
+
m.reply "#{op.nick}: setting `primary_channel` was set to `#{op.setting.get :primary_channel}`"
|
132
|
+
end
|
133
|
+
|
134
|
+
### Events
|
135
|
+
def list_events m
|
136
|
+
return unless authenticate m.user.nick
|
137
|
+
events = Zabbix::Event.recent
|
138
|
+
msg = if events.any?
|
139
|
+
events.collect do |e|
|
140
|
+
"#{m.user.nick}: #{e.label}"
|
141
|
+
end.join("\n")
|
142
|
+
else
|
143
|
+
"#{m.user.nick}: No last events"
|
144
|
+
end
|
145
|
+
m.reply msg
|
146
|
+
end
|
147
|
+
|
148
|
+
def ops
|
149
|
+
@ops ||= bot.zabbirc_service.ops
|
150
|
+
end
|
151
|
+
|
152
|
+
### Authentication and helpers
|
153
|
+
def authenticate obj
|
154
|
+
nick = get_nick obj
|
155
|
+
ops.authenticate nick
|
156
|
+
end
|
157
|
+
|
158
|
+
def get_op obj
|
159
|
+
nick = get_nick obj
|
160
|
+
ops.get nick
|
161
|
+
end
|
162
|
+
|
163
|
+
def get_nick obj
|
164
|
+
case obj
|
165
|
+
when Cinch::Message
|
166
|
+
obj.user.nick
|
167
|
+
when Cinch::User
|
168
|
+
obj.nick
|
169
|
+
when String
|
170
|
+
obj
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
private
|
175
|
+
|
176
|
+
def find_host m, host
|
177
|
+
op = get_op m
|
178
|
+
hosts = Zabbix::Host.get(search: {host: host})
|
179
|
+
case hosts.size
|
180
|
+
when 0
|
181
|
+
m.reply "#{op.nick}: Host not found `#{host}`"
|
182
|
+
when 1
|
183
|
+
return hosts.first
|
184
|
+
when 2..10
|
185
|
+
m.reply "#{op.nick}: Found #{hosts.size} hosts: #{hosts.collect(&:name).join(', ')}. Be more specific"
|
186
|
+
else
|
187
|
+
m.reply "#{op.nick}: Found #{hosts.size} Be more specific"
|
188
|
+
end
|
189
|
+
false
|
190
|
+
end
|
191
|
+
|
192
|
+
def find_event m, eventid
|
193
|
+
op = get_op m
|
194
|
+
begin
|
195
|
+
event = Zabbix::Event.find(eventid, {selectHosts: :extend, selectRelatedObject: :extend})
|
196
|
+
if event.nil?
|
197
|
+
m.reply "#{op.nick} Could not find event with id `#{eventid}`"
|
198
|
+
return false
|
199
|
+
end
|
200
|
+
event
|
201
|
+
rescue Zabbix::IDNotUniqueError => e
|
202
|
+
m.reply "#{op.nick} Could not find event: #{e}"
|
203
|
+
false
|
204
|
+
end
|
205
|
+
end
|
206
|
+
end
|
207
|
+
end
|
208
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module Zabbirc
|
2
|
+
def self.logger
|
3
|
+
@logger ||= ::Logger.new(STDERR, 10, 1.megabyte).tap do |logger|
|
4
|
+
logger.formatter = Zabbirc::Logger::Formatter.new
|
5
|
+
end
|
6
|
+
end
|
7
|
+
|
8
|
+
module Logger
|
9
|
+
class Formatter
|
10
|
+
Format = "%s, [%s#%d T%d] %5s -- %s: %s\n"
|
11
|
+
|
12
|
+
attr_accessor :datetime_format
|
13
|
+
|
14
|
+
def initialize
|
15
|
+
@datetime_format = nil
|
16
|
+
end
|
17
|
+
|
18
|
+
def call(severity, time, progname, msg)
|
19
|
+
Format % [severity[0..0], format_datetime(time), $$, Thread.current.object_id, severity, progname, msg2str(msg)]
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def format_datetime(time)
|
25
|
+
if @datetime_format.nil?
|
26
|
+
time.strftime("%Y-%m-%dT%H:%M:%S.") << "%06d " % time.usec
|
27
|
+
else
|
28
|
+
time.strftime(@datetime_format)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def msg2str(msg)
|
33
|
+
case msg
|
34
|
+
when ::String
|
35
|
+
msg
|
36
|
+
when ::Exception
|
37
|
+
"#{ msg.message } (#{ msg.class })\n" <<
|
38
|
+
(msg.backtrace || []).join("\n")
|
39
|
+
else
|
40
|
+
msg.inspect
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
data/lib/zabbirc/op.rb
ADDED
@@ -0,0 +1,67 @@
|
|
1
|
+
module Zabbirc
|
2
|
+
class Op
|
3
|
+
|
4
|
+
attr_reader :channels, :setting, :nick
|
5
|
+
def initialize zabbix_user: nil, irc_user: nil
|
6
|
+
raise ArgumentError, 'zabbix_user' if zabbix_user.nil?
|
7
|
+
raise ArgumentError, 'irc_user' if irc_user.nil?
|
8
|
+
@nick = zabbix_user.alias
|
9
|
+
@zabbix_user = zabbix_user
|
10
|
+
@irc_user = irc_user
|
11
|
+
|
12
|
+
@notified_events = {}
|
13
|
+
@channels = Set.new
|
14
|
+
@setting = Setting.new
|
15
|
+
end
|
16
|
+
|
17
|
+
def primary_channel
|
18
|
+
@channels.find{|c| c.name == @setting.get(:primary_channel) }
|
19
|
+
end
|
20
|
+
|
21
|
+
def interesting_priority
|
22
|
+
Priority.new @setting.get(:events_priority)
|
23
|
+
end
|
24
|
+
|
25
|
+
def add_channel channel
|
26
|
+
@setting.fetch :primary_channel, channel
|
27
|
+
@channels << channel
|
28
|
+
end
|
29
|
+
|
30
|
+
def remove_channel channel
|
31
|
+
@channels.delete channel
|
32
|
+
|
33
|
+
if channel == @setting.get(:primary_channel)
|
34
|
+
@setting.set :primary_channel, @channels.first
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def notify event
|
39
|
+
return if event.priority < interesting_priority
|
40
|
+
@notified_events ||= {}
|
41
|
+
return if @notified_events.key? event.id
|
42
|
+
channel = primary_channel
|
43
|
+
return unless channel
|
44
|
+
channel.send "#{@nick}: #{event.label}"
|
45
|
+
@notified_events[event.id] = Time.now
|
46
|
+
clear_notified_events
|
47
|
+
end
|
48
|
+
|
49
|
+
def interested_in? event
|
50
|
+
return false unless setting.get :notify
|
51
|
+
return false if @notified_events.key? event.id
|
52
|
+
event.priority >= interesting_priority
|
53
|
+
end
|
54
|
+
|
55
|
+
def event_notified event
|
56
|
+
@notified_events[event.id] = Time.now
|
57
|
+
end
|
58
|
+
|
59
|
+
private
|
60
|
+
|
61
|
+
def clear_notified_events
|
62
|
+
@notified_events.delete_if do |event_id, timestamp|
|
63
|
+
timestamp < (Zabbirc.config.notify_about_events_from_last * 2).seconds.ago
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
module Zabbirc
|
2
|
+
class OpList
|
3
|
+
include Enumerable
|
4
|
+
|
5
|
+
def initialize ops=nil
|
6
|
+
@ops = {}
|
7
|
+
if ops
|
8
|
+
ops.each do |op|
|
9
|
+
add op
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def authenticate name
|
15
|
+
@ops.key? name
|
16
|
+
end
|
17
|
+
|
18
|
+
alias_method :exists?, :authenticate
|
19
|
+
|
20
|
+
def get name
|
21
|
+
@ops[name]
|
22
|
+
end
|
23
|
+
|
24
|
+
def add op
|
25
|
+
if exists? op.nick
|
26
|
+
return get(op.nick)
|
27
|
+
end
|
28
|
+
@ops[op.nick] = op
|
29
|
+
end
|
30
|
+
|
31
|
+
def each &block
|
32
|
+
@ops.values.each &block
|
33
|
+
end
|
34
|
+
|
35
|
+
def interested_in event
|
36
|
+
self.class.new(find_all{ |op| op.interested_in? event })
|
37
|
+
end
|
38
|
+
|
39
|
+
def notify event
|
40
|
+
group_by(&:primary_channel).each do |channel, ops|
|
41
|
+
op_targets = ops.collect{|op| "#{op.nick}:" }.join(" ")
|
42
|
+
channel.send "#{op_targets} #{event.label}"
|
43
|
+
ops.each{ |op| op.event_notified event }
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module Zabbirc
|
2
|
+
class Priority
|
3
|
+
include Comparable
|
4
|
+
|
5
|
+
PRIORITIES = {
|
6
|
+
0 => :not_classified,
|
7
|
+
1 => :information,
|
8
|
+
2 => :warning,
|
9
|
+
3 => :average,
|
10
|
+
4 => :high,
|
11
|
+
5 => :disaster
|
12
|
+
}
|
13
|
+
|
14
|
+
attr_reader :number, :code
|
15
|
+
def initialize priority
|
16
|
+
case priority
|
17
|
+
when String, Symbol
|
18
|
+
raise ArgumentError, "unknown priority `#{priority}`" unless PRIORITIES.key(priority.to_sym)
|
19
|
+
@number = PRIORITIES.key(priority.to_sym)
|
20
|
+
@code = priority.to_sym
|
21
|
+
when Integer
|
22
|
+
raise ArgumentError, "unknown priority `#{priority}`" unless PRIORITIES[priority]
|
23
|
+
@number = priority
|
24
|
+
@code = PRIORITIES[@number]
|
25
|
+
else
|
26
|
+
raise ArgumentError, "cannot create priority from `#{priority}` of class `#{priority.class}`"
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def <=> other
|
31
|
+
number <=> other.number
|
32
|
+
end
|
33
|
+
|
34
|
+
def to_s
|
35
|
+
code.to_s
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,86 @@
|
|
1
|
+
require 'cinch'
|
2
|
+
|
3
|
+
module Zabbirc
|
4
|
+
class Service
|
5
|
+
attr_reader :cinch_bot
|
6
|
+
attr_reader :ops, :ops_service
|
7
|
+
attr_reader :running
|
8
|
+
|
9
|
+
def initialize
|
10
|
+
@semaphores_mutex = Mutex.new
|
11
|
+
@semaphores = Hash.new { |h, k| h[k] = Mutex.new }
|
12
|
+
@ops = OpList.new
|
13
|
+
@running = false
|
14
|
+
|
15
|
+
initialize_bot
|
16
|
+
|
17
|
+
@ops_service = Services::Ops.new self, @cinch_bot
|
18
|
+
@events_service = Services::Events.new self, @cinch_bot
|
19
|
+
end
|
20
|
+
|
21
|
+
def initialize_bot
|
22
|
+
@cinch_bot = Cinch::Bot.new do
|
23
|
+
configure do |c|
|
24
|
+
c.server = Zabbirc.config.irc_server
|
25
|
+
c.channels = Zabbirc.config.irc_channels
|
26
|
+
c.nick = "zabbirc"
|
27
|
+
c.plugins.plugins = [Irc::Plugin]
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
# Stores reference to this Zabbirc::Service to be available in plugins
|
32
|
+
@cinch_bot.instance_variable_set :@zabbirc_service, self
|
33
|
+
@cinch_bot.class_eval do
|
34
|
+
attr_reader :zabbirc_service
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def synchronize(name, &block)
|
39
|
+
# Must run the default block +/ fetch in a thread safe way in order to
|
40
|
+
# ensure we always get the same mutex for a given name.
|
41
|
+
semaphore = @semaphores_mutex.synchronize { @semaphores[name] }
|
42
|
+
semaphore.synchronize(&block)
|
43
|
+
end
|
44
|
+
|
45
|
+
def start join=true
|
46
|
+
return if @running
|
47
|
+
@running = true
|
48
|
+
@cinch_bot_thread = Thread.new do
|
49
|
+
begin
|
50
|
+
@cinch_bot.start
|
51
|
+
rescue => e
|
52
|
+
Zabbirc.logger.fatal "Cinch bot start error: #{e}"
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
@controll_thread = Thread.new do
|
57
|
+
begin
|
58
|
+
sleep
|
59
|
+
rescue StopError
|
60
|
+
@ops_service.stop
|
61
|
+
@events_service.stop
|
62
|
+
|
63
|
+
Zabbirc.logger.info "Stopping ops service: #{@ops_service.join}"
|
64
|
+
Zabbirc.logger.info "Stopping events service: #{@events_service.join}"
|
65
|
+
|
66
|
+
@cinch_bot.quit
|
67
|
+
ensure
|
68
|
+
@running = false
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
@ops_service.start
|
73
|
+
@events_service.start
|
74
|
+
|
75
|
+
@cinch_bot_thread.join if join
|
76
|
+
end
|
77
|
+
|
78
|
+
def stop
|
79
|
+
@controll_thread.raise StopError
|
80
|
+
end
|
81
|
+
|
82
|
+
def join
|
83
|
+
@cinch_bot_thread.join
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
module Zabbirc
|
2
|
+
module Services
|
3
|
+
class Base
|
4
|
+
LOOP_SLEEP = 10
|
5
|
+
attr_reader :running
|
6
|
+
|
7
|
+
def initialize service, cinch_bot
|
8
|
+
@service = service
|
9
|
+
@cinch_bot = cinch_bot
|
10
|
+
@running = false
|
11
|
+
@mutex = Mutex.new
|
12
|
+
end
|
13
|
+
|
14
|
+
def synchronize &block
|
15
|
+
@mutex.synchronize &block
|
16
|
+
end
|
17
|
+
|
18
|
+
def join
|
19
|
+
slept = 0
|
20
|
+
while slept < (LOOP_SLEEP * 2)
|
21
|
+
return true if @running == false
|
22
|
+
sleep 1
|
23
|
+
slept += 1
|
24
|
+
end
|
25
|
+
false
|
26
|
+
end
|
27
|
+
|
28
|
+
def start
|
29
|
+
main_thread = Thread.current
|
30
|
+
@thread = Thread.new do
|
31
|
+
begin
|
32
|
+
loop do
|
33
|
+
Thread.handle_interrupt(StopError => :never) do
|
34
|
+
@running = true
|
35
|
+
iterate
|
36
|
+
end
|
37
|
+
Thread.handle_interrupt(StopError => :immediate) do
|
38
|
+
sleep LOOP_SLEEP
|
39
|
+
end
|
40
|
+
end
|
41
|
+
rescue StopError => e
|
42
|
+
# nothing, ensure block sets the variables
|
43
|
+
rescue => e
|
44
|
+
main_thread.raise e
|
45
|
+
ensure
|
46
|
+
@running = false
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def stop
|
52
|
+
@thread.raise StopError
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|