fluent-plugin-irc 0.0.6 → 0.0.7
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 +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
|