fluent-plugin-irc 0.0.6 → 0.0.7
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +5 -2
- data/fluent-plugin-irc.gemspec +1 -1
- data/lib/fluent/plugin/out_irc.rb +96 -25
- data/test/plugin/test_out_irc.rb +113 -25
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: eb8767007a2ccb458b008a2cb617c77cd2bdfdf4
|
4
|
+
data.tar.gz: d83c2a017287da67e5fe134a8f5dd2c9e7ba6db7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8fe24e8d588c9d7a2eb13fc4c17beafd1c1b9159dd96121aad1d7bf295d3429509299f340711e327ca298db975c77351a3441e0ddd6ec9f38a9c3151f35d9449
|
7
|
+
data.tar.gz: 7cf9bf5284d5398e92ea9b2a30a881e4ac523732bdcd0568fa153dbe38bb38c685ee9cb14c332ef2b7a6a4205045719e7c8c076eef6ef2e9ddabb75e78b83b7e
|
data/README.md
CHANGED
@@ -36,6 +36,7 @@ Fluent plugin to send messages to IRC server
|
|
36
36
|
|host|IRC server host|localhost|
|
37
37
|
|port|IRC server port number|6667|
|
38
38
|
|channel|channel to send messages (without first '#')||
|
39
|
+
|channel_keys|keys used to format channel. %s will be replaced with value specified by channel_keys if this option is used|nil|
|
39
40
|
|nick|nickname registerd of IRC|fluentd|
|
40
41
|
|user|user name registerd of IRC|fluentd|
|
41
42
|
|real|real name registerd of IRC|fluentd|
|
@@ -44,8 +45,10 @@ Fluent plugin to send messages to IRC server
|
|
44
45
|
|time_key|key name for time|time|
|
45
46
|
|time_format|time format. This will be formatted with Time#strftime.|%Y/%m/%d %H:%M:%S|
|
46
47
|
|tag_key|key name for tag|tag|
|
47
|
-
|command|irc command. `
|
48
|
-
|
|
48
|
+
|command|irc command. `privmsg` or `notice`|privmsg|
|
49
|
+
|command_keys|keys used to format command. %s will be replaced with value specified by command_keys if this option is used|nil|
|
50
|
+
|send_interval|interval (sec) to send message. defence Excess Flood|2|
|
51
|
+
|send_queue_limit|maximum size of send message queue|100|
|
49
52
|
|
50
53
|
## Copyright
|
51
54
|
|
data/fluent-plugin-irc.gemspec
CHANGED
@@ -3,7 +3,7 @@ $:.push File.expand_path("../lib", __FILE__)
|
|
3
3
|
|
4
4
|
Gem::Specification.new do |s|
|
5
5
|
s.name = "fluent-plugin-irc"
|
6
|
-
s.version = "0.0.
|
6
|
+
s.version = "0.0.7"
|
7
7
|
s.authors = ["OKUNO Akihiro"]
|
8
8
|
s.email = ["choplin.choplin@gmail.com"]
|
9
9
|
s.homepage = "https://github.com/choplin/fluent-plugin-irc"
|
@@ -25,17 +25,28 @@ module Fluent
|
|
25
25
|
config_param :time_key , :string , :default => 'time'
|
26
26
|
config_param :time_format , :string , :default => '%Y/%m/%d %H:%M:%S'
|
27
27
|
config_param :tag_key , :string , :default => 'tag'
|
28
|
-
config_param :command , :string , :default => '
|
28
|
+
config_param :command , :string , :default => 'privmsg'
|
29
|
+
config_param :command_keys, :default => nil do |val|
|
30
|
+
val.split(',')
|
31
|
+
end
|
29
32
|
|
30
|
-
config_param :blocking_timeout, :time,
|
33
|
+
config_param :blocking_timeout, :time, :default => 0.5
|
34
|
+
config_param :send_queue_limit, :integer, :default => 100
|
35
|
+
config_param :send_interval, :time, :default => 2
|
31
36
|
|
32
|
-
|
37
|
+
COMMAND_MAP = {
|
38
|
+
'priv_msg' => :priv_msg,
|
39
|
+
'privmsg' => :priv_msg,
|
40
|
+
'notice' => :notice,
|
41
|
+
}
|
33
42
|
|
34
43
|
# To support log_level option implemented by Fluentd v0.10.43
|
35
44
|
unless method_defined?(:log)
|
36
45
|
define_method("log") { $log }
|
37
46
|
end
|
38
47
|
|
48
|
+
attr_reader :conn # for test
|
49
|
+
|
39
50
|
def initialize
|
40
51
|
super
|
41
52
|
require 'irc_parser'
|
@@ -59,10 +70,19 @@ module Fluent
|
|
59
70
|
end
|
60
71
|
@channel = '#'+@channel
|
61
72
|
|
62
|
-
|
63
|
-
|
73
|
+
if @command_keys
|
74
|
+
begin
|
75
|
+
@command % (['1'] * @command_keys.length)
|
76
|
+
rescue ArgumentError
|
77
|
+
raise Fluent::ConfigError, "string specifier '%s' and command_keys specification mismatch"
|
78
|
+
end
|
79
|
+
else
|
80
|
+
unless @command = COMMAND_MAP[@command]
|
81
|
+
raise Fluent::ConfigError, "command must be one of #{COMMAND_MAP.keys.join(', ')}"
|
82
|
+
end
|
64
83
|
end
|
65
84
|
|
85
|
+
@send_queue = []
|
66
86
|
end
|
67
87
|
|
68
88
|
def start
|
@@ -71,6 +91,8 @@ module Fluent
|
|
71
91
|
begin
|
72
92
|
@loop = Coolio::Loop.new
|
73
93
|
@conn = create_connection
|
94
|
+
@timer = TimerWatcher.new(@send_interval, true, log, &method(:on_timer))
|
95
|
+
@loop.attach(@timer)
|
74
96
|
@thread = Thread.new(&method(:run))
|
75
97
|
rescue => e
|
76
98
|
puts e
|
@@ -102,17 +124,34 @@ module Fluent
|
|
102
124
|
end
|
103
125
|
|
104
126
|
es.each do |time,record|
|
127
|
+
if @send_queue.size >= @send_queue_limit
|
128
|
+
log.warn "out_irc: send queue size exceeded send_queue_limit(#{@send_queue_limit}), discards"
|
129
|
+
break
|
130
|
+
end
|
131
|
+
|
105
132
|
filter_record(tag, time, record)
|
106
|
-
|
133
|
+
|
134
|
+
command = build_command(record)
|
135
|
+
channel = build_channel(record)
|
136
|
+
message = build_message(record)
|
137
|
+
|
138
|
+
log.debug { "out_irc: push {command:\"#{command}\", channel:\"#{channel}\", message:\"#{message}\"}" }
|
139
|
+
@send_queue.push([command, channel, message])
|
107
140
|
end
|
108
141
|
end
|
109
142
|
|
143
|
+
def on_timer
|
144
|
+
return if @send_queue.empty?
|
145
|
+
command, channel, message = @send_queue.shift
|
146
|
+
log.info { "out_irc: send {command:\"#{command}\", channel:\"#{channel}\", message:\"#{message}\"}" }
|
147
|
+
@conn.send_message(command, channel, message)
|
148
|
+
end
|
149
|
+
|
110
150
|
private
|
111
151
|
|
112
152
|
def create_connection
|
113
153
|
conn = IRCConnection.connect(@host, @port)
|
114
|
-
conn.
|
115
|
-
conn.channel = '#'+@channel
|
154
|
+
conn.log = log
|
116
155
|
conn.nick = @nick
|
117
156
|
conn.user = @user
|
118
157
|
conn.real = @real
|
@@ -122,35 +161,56 @@ module Fluent
|
|
122
161
|
end
|
123
162
|
|
124
163
|
def build_message(record)
|
125
|
-
values = @out_keys
|
126
|
-
begin
|
127
|
-
record.fetch(key).to_s
|
128
|
-
rescue KeyError
|
129
|
-
log.warn "out_irc: the specified key '#{key}' not found in record. [#{record}]"
|
130
|
-
''
|
131
|
-
end
|
132
|
-
end
|
133
|
-
|
164
|
+
values = fetch_keys(record, @out_keys)
|
134
165
|
@message % values
|
135
166
|
end
|
136
167
|
|
137
168
|
def build_channel(record)
|
138
169
|
return @channel unless @channel_keys
|
139
170
|
|
140
|
-
values = @channel_keys
|
171
|
+
values = fetch_keys(record, @channel_keys)
|
172
|
+
@channel % values
|
173
|
+
end
|
174
|
+
|
175
|
+
def build_command(record)
|
176
|
+
return @command unless @command_keys
|
177
|
+
|
178
|
+
values = fetch_keys(record, @command_keys)
|
179
|
+
unless command = COMMAND_MAP[@command % values]
|
180
|
+
log.warn "out_irc: command is not one of #{COMMAND_MAP.keys.join(', ')}, use privmsg"
|
181
|
+
end
|
182
|
+
command || :priv_msg
|
183
|
+
end
|
184
|
+
|
185
|
+
def fetch_keys(record, keys)
|
186
|
+
Array(keys).map do |key|
|
141
187
|
begin
|
142
188
|
record.fetch(key).to_s
|
143
189
|
rescue KeyError
|
144
|
-
|
190
|
+
log.warn "out_irc: the specified key '#{key}' not found in record. [#{record}]"
|
145
191
|
''
|
146
192
|
end
|
147
193
|
end
|
194
|
+
end
|
195
|
+
|
196
|
+
class TimerWatcher < Coolio::TimerWatcher
|
197
|
+
def initialize(interval, repeat, log, &callback)
|
198
|
+
@callback = callback
|
199
|
+
@log = log
|
200
|
+
super(interval, repeat)
|
201
|
+
end
|
148
202
|
|
149
|
-
|
203
|
+
def on_timer
|
204
|
+
@callback.call
|
205
|
+
rescue
|
206
|
+
@log.error $!.to_s
|
207
|
+
@log.error_backtrace
|
208
|
+
end
|
150
209
|
end
|
151
210
|
|
152
211
|
class IRCConnection < Cool.io::TCPSocket
|
153
|
-
|
212
|
+
attr_reader :joined # for test
|
213
|
+
attr_accessor :log, :nick, :user, :real, :password
|
154
214
|
|
155
215
|
def initialize(*args)
|
156
216
|
super
|
@@ -179,13 +239,23 @@ module Fluent
|
|
179
239
|
data.each_line do |line|
|
180
240
|
begin
|
181
241
|
msg = IRCParser.parse(line)
|
242
|
+
log.debug { "out_irc: on_read :#{msg.class.to_sym}" }
|
182
243
|
case msg.class.to_sym
|
244
|
+
when :rpl_welcome
|
245
|
+
log.info { "out_irc: welcome \"#{msg.nick}\" to \"#{msg.prefix}\"" }
|
183
246
|
when :ping
|
184
247
|
IRCParser.message(:pong) do |m|
|
185
248
|
m.target = msg.target
|
186
249
|
m.body = msg.body
|
187
250
|
write m
|
188
251
|
end
|
252
|
+
when :join
|
253
|
+
log.info { "out_irc: joined to #{msg.channels.join(', ')}" }
|
254
|
+
msg.channels.each {|channel| @joined[channel] = true }
|
255
|
+
when :err_nick_name_in_use
|
256
|
+
log.warn "out_irc: nickname \"#{msg.error_nick}\" is already in use. use \"#{@nick}_\" instead."
|
257
|
+
@nick = "#{@nick}_"
|
258
|
+
on_connect
|
189
259
|
when :error
|
190
260
|
log.warn "out_irc: an error occured. \"#{msg.error_message}\""
|
191
261
|
end
|
@@ -204,16 +274,17 @@ module Fluent
|
|
204
274
|
m.channels = channel
|
205
275
|
write m
|
206
276
|
end
|
207
|
-
|
277
|
+
log.debug { "out_irc: join to #{channel}" }
|
208
278
|
end
|
209
279
|
|
210
|
-
def send_message(
|
280
|
+
def send_message(command, channel, message)
|
211
281
|
join(channel) unless joined?(channel)
|
212
|
-
IRCParser.message(
|
282
|
+
IRCParser.message(command) do |m|
|
213
283
|
m.target = channel
|
214
|
-
m.body =
|
284
|
+
m.body = message
|
215
285
|
write m
|
216
286
|
end
|
287
|
+
channel # return channel for test
|
217
288
|
end
|
218
289
|
end
|
219
290
|
end
|
data/test/plugin/test_out_irc.rb
CHANGED
@@ -20,9 +20,11 @@ class IRCOutputTest < Test::Unit::TestCase
|
|
20
20
|
def config(
|
21
21
|
port: PORT,
|
22
22
|
channel: CHANNEL,
|
23
|
-
channel_keys: ""
|
23
|
+
channel_keys: "",
|
24
|
+
command: COMMAND.to_s,
|
25
|
+
command_keys: nil
|
24
26
|
)
|
25
|
-
%[
|
27
|
+
config = %[
|
26
28
|
type irc
|
27
29
|
host localhost
|
28
30
|
port #{port}
|
@@ -31,13 +33,17 @@ class IRCOutputTest < Test::Unit::TestCase
|
|
31
33
|
nick #{NICK}
|
32
34
|
user #{USER}
|
33
35
|
real #{REAL}
|
34
|
-
command #{
|
36
|
+
command #{command}
|
35
37
|
message #{MESSAGE}
|
36
38
|
out_keys tag,time,msg
|
37
39
|
time_key time
|
38
40
|
time_format #{TIME_FORMAT}
|
39
41
|
tag_key tag
|
42
|
+
send_queue_limit 10
|
43
|
+
send_interval 0.5
|
40
44
|
]
|
45
|
+
config += %[command_keys #{command_keys}] if command_keys
|
46
|
+
config
|
41
47
|
end
|
42
48
|
|
43
49
|
|
@@ -54,12 +60,14 @@ class IRCOutputTest < Test::Unit::TestCase
|
|
54
60
|
assert_equal NICK, d.instance.nick
|
55
61
|
assert_equal USER, d.instance.user
|
56
62
|
assert_equal REAL, d.instance.real
|
57
|
-
assert_equal COMMAND
|
63
|
+
assert_equal COMMAND, d.instance.command
|
58
64
|
assert_equal MESSAGE, d.instance.message
|
59
65
|
assert_equal ["tag","time","msg"], d.instance.out_keys
|
60
66
|
assert_equal "time", d.instance.time_key
|
61
67
|
assert_equal TIME_FORMAT, d.instance.time_format
|
62
68
|
assert_equal "tag", d.instance.tag_key
|
69
|
+
assert_equal 10, d.instance.send_queue_limit
|
70
|
+
assert_equal 0.5, d.instance.send_interval
|
63
71
|
end
|
64
72
|
|
65
73
|
def test_configure_channel_keys
|
@@ -68,6 +76,22 @@ class IRCOutputTest < Test::Unit::TestCase
|
|
68
76
|
assert_equal ["channel"], d.instance.channel_keys
|
69
77
|
end
|
70
78
|
|
79
|
+
def test_configure_command_keys
|
80
|
+
d = create_driver(config(command:"%s", command_keys:"command"))
|
81
|
+
assert_equal "%s", d.instance.command
|
82
|
+
assert_equal ["command"], d.instance.command_keys
|
83
|
+
end
|
84
|
+
|
85
|
+
def test_configure_command
|
86
|
+
assert_raise Fluent::ConfigError do
|
87
|
+
create_driver(config(command: 'foo'))
|
88
|
+
end
|
89
|
+
|
90
|
+
assert_nothing_raised { create_driver(config(command: 'priv_msg')) }
|
91
|
+
assert_nothing_raised { create_driver(config(command: 'privmsg')) }
|
92
|
+
assert_nothing_raised { create_driver(config(command: 'notice')) }
|
93
|
+
end
|
94
|
+
|
71
95
|
def test_emit
|
72
96
|
msg = "test"
|
73
97
|
msgs = [{"msg" => msg}]
|
@@ -75,22 +99,22 @@ class IRCOutputTest < Test::Unit::TestCase
|
|
75
99
|
|
76
100
|
emit_test(msgs) do |socket|
|
77
101
|
m = IRCParser.parse(socket.gets)
|
78
|
-
assert_equal m.class.to_sym
|
79
|
-
assert_equal m.nick
|
102
|
+
assert_equal :nick, m.class.to_sym
|
103
|
+
assert_equal NICK, m.nick
|
80
104
|
|
81
105
|
m = IRCParser.parse(socket.gets)
|
82
|
-
assert_equal m.class.to_sym
|
83
|
-
assert_equal m.user
|
84
|
-
assert_equal m.postfix
|
106
|
+
assert_equal :user, m.class.to_sym
|
107
|
+
assert_equal USER, m.user
|
108
|
+
assert_equal REAL, m.postfix
|
85
109
|
|
86
110
|
m = IRCParser.parse(socket.gets)
|
87
|
-
assert_equal m.class.to_sym, :join
|
88
|
-
assert_equal
|
111
|
+
assert_equal :join, m.class.to_sym, :join
|
112
|
+
assert_equal ["##{CHANNEL}"], m.channels
|
89
113
|
|
90
114
|
m = IRCParser.parse(socket.gets)
|
91
|
-
assert_equal m.class.to_sym
|
92
|
-
assert_equal
|
93
|
-
assert_equal
|
115
|
+
assert_equal COMMAND, m.class.to_sym
|
116
|
+
assert_equal "##{CHANNEL}", m.target
|
117
|
+
assert_equal body, m.body
|
94
118
|
|
95
119
|
assert_nil socket.gets # expects EOF
|
96
120
|
end
|
@@ -113,29 +137,91 @@ class IRCOutputTest < Test::Unit::TestCase
|
|
113
137
|
socket.gets # ignore USER
|
114
138
|
|
115
139
|
m = IRCParser.parse(socket.gets)
|
116
|
-
assert_equal m.class.to_sym
|
117
|
-
assert_equal
|
140
|
+
assert_equal :join, m.class.to_sym
|
141
|
+
assert_equal ["#chan1"], m.channels
|
118
142
|
|
119
143
|
m = IRCParser.parse(socket.gets)
|
120
|
-
assert_equal m.class.to_sym
|
121
|
-
assert_equal
|
144
|
+
assert_equal COMMAND, m.class.to_sym
|
145
|
+
assert_equal "#chan1", m.target
|
122
146
|
|
123
147
|
m = IRCParser.parse(socket.gets)
|
124
|
-
assert_equal m.class.to_sym
|
125
|
-
assert_equal
|
148
|
+
assert_equal :join, m.class.to_sym
|
149
|
+
assert_equal ["#chan2"], m.channels
|
126
150
|
|
127
151
|
m = IRCParser.parse(socket.gets)
|
128
|
-
assert_equal m.class.to_sym
|
129
|
-
assert_equal
|
152
|
+
assert_equal COMMAND, m.class.to_sym
|
153
|
+
assert_equal "#chan2", m.target
|
130
154
|
|
131
155
|
m = IRCParser.parse(socket.gets)
|
132
|
-
assert_equal m.class.to_sym
|
133
|
-
assert_equal
|
156
|
+
assert_equal COMMAND, m.class.to_sym
|
157
|
+
assert_equal "#chan1", m.target
|
134
158
|
|
135
159
|
assert_nil socket.gets # expects EOF
|
136
160
|
end
|
137
161
|
end
|
138
162
|
|
163
|
+
def test_dynamic_command
|
164
|
+
msgs = [
|
165
|
+
{"msg" => "test", "command" => "privmsg"},
|
166
|
+
{"msg" => "test", "command" => "priv_msg"},
|
167
|
+
{"msg" => "test", "command" => "notice"},
|
168
|
+
{"msg" => "test", "command" => "something_wrong"},
|
169
|
+
]
|
170
|
+
|
171
|
+
extra_config = {
|
172
|
+
command: "%s",
|
173
|
+
command_keys: "command",
|
174
|
+
}
|
175
|
+
|
176
|
+
emit_test(msgs, extra_config: extra_config) do |socket|
|
177
|
+
socket.gets # ignore NICK
|
178
|
+
socket.gets # ignore USER
|
179
|
+
|
180
|
+
m = IRCParser.parse(socket.gets)
|
181
|
+
assert_equal :join, m.class.to_sym
|
182
|
+
|
183
|
+
m = IRCParser.parse(socket.gets)
|
184
|
+
assert_equal :priv_msg, m.class.to_sym
|
185
|
+
|
186
|
+
m = IRCParser.parse(socket.gets)
|
187
|
+
assert_equal :priv_msg, m.class.to_sym
|
188
|
+
|
189
|
+
m = IRCParser.parse(socket.gets)
|
190
|
+
assert_equal :notice, m.class.to_sym
|
191
|
+
|
192
|
+
m = IRCParser.parse(socket.gets)
|
193
|
+
assert_equal :priv_msg, m.class.to_sym # replaced by default priv_msg
|
194
|
+
|
195
|
+
assert_nil socket.gets # expects EOF
|
196
|
+
end
|
197
|
+
end
|
198
|
+
|
199
|
+
def test_fallback_err_nick_name_in_use
|
200
|
+
msgs = [
|
201
|
+
{"msg" => "test", "command" => "privmsg"},
|
202
|
+
]
|
203
|
+
|
204
|
+
emit_test(msgs) do |socket, d|
|
205
|
+
socket.gets # ignore NICK
|
206
|
+
socket.gets # ignore USER
|
207
|
+
socket.gets # ignore join
|
208
|
+
socket.gets # ignore priv_msg
|
209
|
+
|
210
|
+
# imitate to receive :err_nick_name_in_use
|
211
|
+
conn = d.instance.instance_variable_get(:@conn)
|
212
|
+
IRCParser.message(:err_nick_name_in_use) do |m|
|
213
|
+
conn.on_read(m.to_s)
|
214
|
+
end
|
215
|
+
|
216
|
+
sleep 1
|
217
|
+
|
218
|
+
# test to use `#{NICK}_` instead
|
219
|
+
m = IRCParser.parse(socket.gets)
|
220
|
+
assert_equal :nick, m.class.to_sym
|
221
|
+
assert_equal "#{NICK}_", m.nick
|
222
|
+
end
|
223
|
+
end
|
224
|
+
|
139
225
|
private
|
140
226
|
|
141
227
|
def emit_test(msgs, extra_config: {}, &block)
|
@@ -145,13 +231,15 @@ class IRCOutputTest < Test::Unit::TestCase
|
|
145
231
|
|
146
232
|
thread = Thread.new do
|
147
233
|
s = serv.accept
|
148
|
-
block.call(s)
|
234
|
+
block.call(s, d)
|
149
235
|
s.close
|
150
236
|
end
|
151
237
|
|
152
238
|
d.run do
|
153
239
|
msgs.each do |m|
|
154
240
|
d.emit(m, Fluent::Engine.now)
|
241
|
+
channel = d.instance.on_timer
|
242
|
+
d.instance.conn.joined[channel] = true # pseudo join
|
155
243
|
end
|
156
244
|
# How to remove sleep?
|
157
245
|
# It is necessary to ensure that no data remains in Cool.io write buffer before detach.
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: fluent-plugin-irc
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.7
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- OKUNO Akihiro
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-03-
|
11
|
+
date: 2015-03-25 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: fluentd
|