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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: af5ed88db24d18583875f6882b7b9d4ef794a944
4
- data.tar.gz: 0384195c283d6d18d4a62534b3ea9909134351f5
3
+ metadata.gz: eb8767007a2ccb458b008a2cb617c77cd2bdfdf4
4
+ data.tar.gz: d83c2a017287da67e5fe134a8f5dd2c9e7ba6db7
5
5
  SHA512:
6
- metadata.gz: ec009deb6f1f6b60c253a200803442a109d0f59f8b415528471e984676681250c4d529b70ff91f868211347f24181e5de7c1a463a46331eec8c454160919fac6
7
- data.tar.gz: 4150d2d06239c7aa689c438d78b89f3e0f0430d3e77f8e25d9b9a50c43dba0649386adb1c038094493bd079e3ab4871cc0f26a48de783e48a981d12bfb54f1a1
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. `priv_msg` or `notice`|priv_msg|
48
- |channel_keys|keys used to format channel. %s will be replaced with value specified by channel_keys if this option is used|nil|
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
 
@@ -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"
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 => 'priv_msg'
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, :default => 0.5
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
- COMMAND_LIST = %w[priv_msg notice]
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
- unless COMMAND_LIST.include?(@command)
63
- raise Fluent::ConfigError, "command must be one of #{COMMAND_LIST.join(', ')}"
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
- @conn.send_message(build_message(record), build_channel(record))
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.command = @command
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.map do |key|
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.map do |key|
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
- $log.warn "out_irc: the specified key '#{key}' not found in record. [#{record}]"
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
- @channel % values
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
- attr_accessor :command, :channel, :nick, :user, :real, :password
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
- @joined[channel] = true
277
+ log.debug { "out_irc: join to #{channel}" }
208
278
  end
209
279
 
210
- def send_message(msg, channel)
280
+ def send_message(command, channel, message)
211
281
  join(channel) unless joined?(channel)
212
- IRCParser.message(@command) do |m|
282
+ IRCParser.message(command) do |m|
213
283
  m.target = channel
214
- m.body = msg
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
@@ -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 #{COMMAND.to_s}
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.to_s, d.instance.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, :nick
79
- assert_equal m.nick, 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, :user
83
- assert_equal m.user, USER
84
- assert_equal m.postfix, REAL
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 m.channels, ["##{CHANNEL}"]
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, COMMAND
92
- assert_equal m.target, "##{CHANNEL}"
93
- assert_equal m.body, body
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, :join
117
- assert_equal m.channels, ["#chan1"]
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, COMMAND
121
- assert_equal m.target, "#chan1"
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, :join
125
- assert_equal m.channels, ["#chan2"]
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, COMMAND
129
- assert_equal m.target, "#chan2"
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, COMMAND
133
- assert_equal m.target, "#chan1"
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.6
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-18 00:00:00.000000000 Z
11
+ date: 2015-03-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: fluentd