daioikachan 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/.gitignore +17 -0
- data/.travis.yml +12 -0
- data/CHANGELOG.md +3 -0
- data/Gemfile +3 -0
- data/LICENSE +22 -0
- data/README.md +141 -0
- data/README/in_daioikachan.md +63 -0
- data/README/out_irc.md +58 -0
- data/Rakefile +14 -0
- data/VERSION +1 -0
- data/bin/daioikachan +53 -0
- data/daioikachan.gemspec +26 -0
- data/examples/example.conf +57 -0
- data/examples/multi_slack.conf +46 -0
- data/lib/fluent/plugin/in_daioikachan.rb +160 -0
- data/lib/fluent/plugin/out_irc.rb +283 -0
- data/test/helper.rb +29 -0
- data/test/plugin/test_in_daioikachan.rb +105 -0
- metadata +148 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 8c10cbfb0cfbfbc731033be5a02a9f8ea843f4f5
|
4
|
+
data.tar.gz: fe2a499b9da5d66a57458234d5de4f475034b734
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 585730388e9aff3c91ef30e36060957d3363be9cc6b376d9f23c843d1b617112383466a5166d212d4319be2083529989c9bff9d706a29ee4fd72d85f1d3d7f89
|
7
|
+
data.tar.gz: 4910660d2dbd34642730b438362b7b9b799efb8946f3cd5abb7bbbd3a6beebce1589e83a61d2029fb50bd21b5763ff5ca69ac3da70f2c3e13fbdebdca2d33f7b
|
data/.gitignore
ADDED
data/.travis.yml
ADDED
data/CHANGELOG.md
ADDED
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2015 Naotoshi Seo
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,141 @@
|
|
1
|
+
# Daioikachan
|
2
|
+
|
3
|
+
[Ikachan](https://github.com/yappo/p5-App-Ikachan) compatible interface with multiple backends (IRC, Slack, etc).
|
4
|
+
|
5
|
+
## Requirements
|
6
|
+
|
7
|
+
* Ruby
|
8
|
+
|
9
|
+
## Installation
|
10
|
+
|
11
|
+
Write Gemfile:
|
12
|
+
|
13
|
+
```
|
14
|
+
source "https://rubygems.org"
|
15
|
+
|
16
|
+
gem 'daioikachan'
|
17
|
+
```
|
18
|
+
|
19
|
+
Run:
|
20
|
+
|
21
|
+
```
|
22
|
+
$ bundle
|
23
|
+
```
|
24
|
+
|
25
|
+
IRC, Slack backends are bundled as default.
|
26
|
+
|
27
|
+
Daioikachan supports plugin architecture. You may add your favirite backends as gems. See Plugin section for details.
|
28
|
+
|
29
|
+
## How to Run
|
30
|
+
|
31
|
+
This is an example to post messages for both IRC and Slack.
|
32
|
+
|
33
|
+
### Start
|
34
|
+
|
35
|
+
Generate a sample [daioikachan.conf](./examples/example.conf):
|
36
|
+
|
37
|
+
```
|
38
|
+
$ bundle exec daioikachan -g daioikachan.conf
|
39
|
+
```
|
40
|
+
|
41
|
+
Create `.env` and configure your IRC server and Slack token:
|
42
|
+
|
43
|
+
```
|
44
|
+
IRC_SERVER=XX.XX.XX.XX
|
45
|
+
SLACK_API_TOKEN=XXX-XXXXX-XXXXXX-XXXXX
|
46
|
+
````
|
47
|
+
|
48
|
+
Start `daioikachan` as
|
49
|
+
|
50
|
+
```
|
51
|
+
$ bundle exec daioikachan
|
52
|
+
```
|
53
|
+
|
54
|
+
### Test
|
55
|
+
|
56
|
+
Test to post a message to `#daioikachan` channel of both IRC and Slack via `daioikachan` like:
|
57
|
+
|
58
|
+
```
|
59
|
+
$ curl -d "channel=#daioikachan&message=test daioikachan" http://localhost:4979/notice
|
60
|
+
```
|
61
|
+
|
62
|
+
Look whether posting to IRC, and Slack succeeds.
|
63
|
+
|
64
|
+
## Configuration
|
65
|
+
|
66
|
+
See [example.conf](./example.conf) or [multi_slack.conf](./examples/multi_slack.conf) as examples.
|
67
|
+
|
68
|
+
You might've noticed that the config file is similar with Fluentd.
|
69
|
+
Yes, `daioikachan` is created based on Fluentd.
|
70
|
+
So, you can use `routing` functions which Fluentd has (`tag` and `label`).
|
71
|
+
|
72
|
+
See [config-file](http://docs.fluentd.org/articles/config-file) documentation of Fluentd for details.
|
73
|
+
|
74
|
+
See following pages for built-in plugins.
|
75
|
+
|
76
|
+
* [in_daioikachan](./README/in_daioikachan.md)
|
77
|
+
* [fluent-plugin-irc](./README/out_irc.md)
|
78
|
+
* [fluent-plugin-slack](https://github.com/sowawa/fluent-plugin-slack)
|
79
|
+
|
80
|
+
If you need other backends, search other fluentd output plugins and add them. Enjoy!
|
81
|
+
|
82
|
+
## Plugin
|
83
|
+
|
84
|
+
You can create and add your own backends for daioikachan as a Fluentd Plugin.
|
85
|
+
|
86
|
+
See http://www.fluentd.org/plugins#notifications for available plugins.
|
87
|
+
|
88
|
+
## API
|
89
|
+
|
90
|
+
### /notice
|
91
|
+
|
92
|
+
Send `notice` message.
|
93
|
+
|
94
|
+
```
|
95
|
+
$ curl -d "channel=#channel&message=test message" http://localhost:4979/notice
|
96
|
+
```
|
97
|
+
|
98
|
+
### /privmsg
|
99
|
+
|
100
|
+
Send `privmsg` message.
|
101
|
+
|
102
|
+
```
|
103
|
+
$ curl -d "channel=#channel&message=test message" http://localhost:4979/privmsg
|
104
|
+
```
|
105
|
+
|
106
|
+
### /join
|
107
|
+
|
108
|
+
The server always returns 200, and ignore.
|
109
|
+
|
110
|
+
IRC client (`fluent-plugin-irc`) automatically joins to a channel on sending message.
|
111
|
+
Slack client does not require to join to a channel.
|
112
|
+
|
113
|
+
### /leave
|
114
|
+
|
115
|
+
The server always returns 200, and ignores.
|
116
|
+
|
117
|
+
## ChangeLog
|
118
|
+
|
119
|
+
See [CHANGELOG.md](CHANGELOG.md) for details.
|
120
|
+
|
121
|
+
## Contributing
|
122
|
+
|
123
|
+
1. Fork it
|
124
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
125
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
126
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
127
|
+
5. Create new [Pull Request](../../pull/new/master)
|
128
|
+
|
129
|
+
## Copyright
|
130
|
+
|
131
|
+
### daioikachan, and fluent-plugin-daioikachan
|
132
|
+
|
133
|
+
Copyright (c) 2015 Naotoshi Seo. See [LICENSE](LICENSE) for details.
|
134
|
+
|
135
|
+
### fluent-plugin-irc
|
136
|
+
|
137
|
+
See https://github.com/choplin/fluent-plugin-irc
|
138
|
+
|
139
|
+
### fluent-plugin-slack
|
140
|
+
|
141
|
+
See https://github.com/sowawa/fluent-plugin-slack
|
@@ -0,0 +1,63 @@
|
|
1
|
+
# Daioikachan plugin for Fluentd
|
2
|
+
|
3
|
+
This plugin provides [ikachan](https://github.com/yappo/p5-App-Ikachan) compatible interface by Fluentd.
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Bundled with `daioikachan` gem.
|
8
|
+
|
9
|
+
## Configuration
|
10
|
+
|
11
|
+
```apache
|
12
|
+
<source>
|
13
|
+
type daioikachan
|
14
|
+
bind 127.0.0.1
|
15
|
+
port 4979
|
16
|
+
backlog 2048
|
17
|
+
|
18
|
+
# optional Puma parameters
|
19
|
+
min_threads 0
|
20
|
+
max_threads 4
|
21
|
+
</source>
|
22
|
+
```
|
23
|
+
|
24
|
+
Receiving API post like below,
|
25
|
+
|
26
|
+
```
|
27
|
+
$ curl -d "channel=#channel&message=test message" http://localhost:4979/notice
|
28
|
+
```
|
29
|
+
|
30
|
+
emits an event as
|
31
|
+
|
32
|
+
```
|
33
|
+
notice.channel {"command":"notice","channel":"channel","message":"test message"}
|
34
|
+
```
|
35
|
+
|
36
|
+
## API
|
37
|
+
|
38
|
+
### /notice
|
39
|
+
|
40
|
+
Send `notice` message.
|
41
|
+
|
42
|
+
```
|
43
|
+
$ curl -d "channel=#channel&message=test message" http://localhost:4979/notice
|
44
|
+
```
|
45
|
+
|
46
|
+
### /privmsg
|
47
|
+
|
48
|
+
Send `privmsg` message.
|
49
|
+
|
50
|
+
```
|
51
|
+
$ curl -d "channel=#channel&message=test message" http://localhost:4979/privmsg
|
52
|
+
```
|
53
|
+
|
54
|
+
### /join
|
55
|
+
|
56
|
+
The server always returns 200, and ignored.
|
57
|
+
|
58
|
+
IRC client should automatically join to a channel on sending message.
|
59
|
+
Slack client does not require to join to a channel.
|
60
|
+
|
61
|
+
### /leave
|
62
|
+
|
63
|
+
The server always returns 200, and ignored.
|
data/README/out_irc.md
ADDED
@@ -0,0 +1,58 @@
|
|
1
|
+
# Fluent::Plugin::Irc, a plugin for [Fluentd](http://fluentd.org)
|
2
|
+
|
3
|
+
[](https://travis-ci.org/choplin/fluent-plugin-irc)
|
4
|
+
|
5
|
+
Fluent plugin to send messages to IRC server
|
6
|
+
|
7
|
+
## Installation
|
8
|
+
|
9
|
+
`$ fluent-gem install fluent-plugin-irc`
|
10
|
+
|
11
|
+
## Configuration
|
12
|
+
|
13
|
+
### Example
|
14
|
+
|
15
|
+
```
|
16
|
+
<match **>
|
17
|
+
type irc
|
18
|
+
host localhost
|
19
|
+
port 6667
|
20
|
+
channel fluentd
|
21
|
+
nick fluentd
|
22
|
+
user fluentd
|
23
|
+
real fluentd
|
24
|
+
message notice: %s [%s] %s
|
25
|
+
out_keys tag,time,message
|
26
|
+
time_key time
|
27
|
+
time_format %Y/%m/%d %H:%M:%S
|
28
|
+
tag_key tag
|
29
|
+
</match>
|
30
|
+
```
|
31
|
+
|
32
|
+
### Parameter
|
33
|
+
|
34
|
+
|parameter|description|default|
|
35
|
+
|---|---|---|
|
36
|
+
|host|IRC server host|localhost|
|
37
|
+
|port|IRC server port number|6667|
|
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|
|
40
|
+
|nick|nickname registerd of IRC|fluentd|
|
41
|
+
|user|user name registerd of IRC|fluentd|
|
42
|
+
|real|real name registerd of IRC|fluentd|
|
43
|
+
|message|message format. %s will be replaced with value specified by out_keys||
|
44
|
+
|out_keys|keys used to format messages||
|
45
|
+
|time_key|key name for time|time|
|
46
|
+
|time_format|time format. This will be formatted with Time#strftime.|%Y/%m/%d %H:%M:%S|
|
47
|
+
|tag_key|key name for tag|tag|
|
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
|
+
|max_send_queue|maximum size of send message queue|100|
|
52
|
+
|
53
|
+
## Copyright
|
54
|
+
|
55
|
+
<table>
|
56
|
+
<tr><td>Copyright</td><td>Copyright (c) 2015 OKUNO Akihiro</td></tr>
|
57
|
+
<tr><td>License</td><td>Apache License, Version 2.0</td></tr>
|
58
|
+
</table>
|
data/Rakefile
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env rake
|
2
|
+
|
3
|
+
require 'bundler'
|
4
|
+
Bundler::GemHelper.install_tasks
|
5
|
+
|
6
|
+
require 'rake/testtask'
|
7
|
+
|
8
|
+
Rake::TestTask.new(:test) do |test|
|
9
|
+
test.libs << 'lib' << 'test'
|
10
|
+
test.test_files = (Dir["test/plugin/test_*.rb"] - ["helper.rb"]).sort
|
11
|
+
test.verbose = true
|
12
|
+
end
|
13
|
+
|
14
|
+
task :default => [:test]
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.0.1
|
data/bin/daioikachan
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# -*- coding: utf-8 -*-
|
3
|
+
require 'rubygems' unless defined?(gem)
|
4
|
+
ROOT = File.expand_path('..', File.dirname(__FILE__))
|
5
|
+
$LOAD_PATH << File.expand_path(File.join(ROOT, 'lib'))
|
6
|
+
|
7
|
+
require 'dotenv'
|
8
|
+
Dotenv.load
|
9
|
+
|
10
|
+
# tweak default_options
|
11
|
+
require 'fluent/supervisor'
|
12
|
+
class Fluent::Supervisor
|
13
|
+
class << self
|
14
|
+
alias :fluentd_default_options :default_options
|
15
|
+
end
|
16
|
+
def self.default_options
|
17
|
+
fluentd_default_options.merge({
|
18
|
+
:config_path => 'daioikachan.conf',
|
19
|
+
:plugin_dirs => [File.join(ROOT, 'lib/fluent/plugin')],
|
20
|
+
})
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
# handle argv to generate conf
|
25
|
+
require 'optparse'
|
26
|
+
opts = {}
|
27
|
+
opt = OptionParser.new
|
28
|
+
opt.on('-g', "--generate PATH", "generate a sample configuration file") {|s|
|
29
|
+
opts[:generate_path] = s
|
30
|
+
}
|
31
|
+
begin
|
32
|
+
opt.parse!(ARGV)
|
33
|
+
rescue OptionParser::InvalidOption => e
|
34
|
+
argv = ARGV # suppress warning to constant
|
35
|
+
argv.unshift(*(e.args)) # restore and ignore unknown options to pass to Fluentd
|
36
|
+
end
|
37
|
+
|
38
|
+
if generate_path = opts[:generate_path]
|
39
|
+
require 'fileutils'
|
40
|
+
FileUtils.mkdir_p File.dirname(generate_path)
|
41
|
+
if File.exist?(generate_path)
|
42
|
+
puts "#{generate_path} already exists."
|
43
|
+
else
|
44
|
+
File.open(generate_path, "w") {|f|
|
45
|
+
conf = File.read File.join(ROOT, 'examples/example.conf')
|
46
|
+
f.write conf
|
47
|
+
}
|
48
|
+
puts "Installed #{generate_path}."
|
49
|
+
end
|
50
|
+
exit 0
|
51
|
+
end
|
52
|
+
|
53
|
+
require 'fluent/command/fluentd'
|
data/daioikachan.gemspec
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
$:.push File.expand_path('../lib', __FILE__)
|
3
|
+
|
4
|
+
Gem::Specification.new do |gem|
|
5
|
+
gem.name = "daioikachan"
|
6
|
+
gem.description = "Ikachan compatible interface with multiple backends (IRC, Slack, etc)"
|
7
|
+
gem.homepage = "https://github.com/sonots/daioikachan"
|
8
|
+
gem.summary = gem.description
|
9
|
+
gem.version = File.read("VERSION").strip
|
10
|
+
gem.authors = ["Naotoshi Seo"]
|
11
|
+
gem.email = "sonots@gmail.com"
|
12
|
+
gem.has_rdoc = false
|
13
|
+
gem.license = 'MIT License'
|
14
|
+
gem.files = `git ls-files`.split("\n")
|
15
|
+
gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
16
|
+
gem.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
17
|
+
gem.require_paths = ['lib']
|
18
|
+
|
19
|
+
gem.add_dependency "fluentd", "~> 0.12.0"
|
20
|
+
gem.add_dependency "dotenv"
|
21
|
+
gem.add_dependency "puma"
|
22
|
+
# gem.add_dependency "fluent-plugin-irc"
|
23
|
+
gem.add_dependency "irc_parser"
|
24
|
+
gem.add_dependency "fluent-plugin-slack", ">= 0.5.0"
|
25
|
+
gem.add_development_dependency "rake"
|
26
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
<source>
|
2
|
+
type daioikachan
|
3
|
+
bind 0.0.0.0
|
4
|
+
port 4979
|
5
|
+
min_threads 0
|
6
|
+
max_threads 4
|
7
|
+
backlog 1024
|
8
|
+
@label @raw
|
9
|
+
</source>
|
10
|
+
|
11
|
+
<label @raw>
|
12
|
+
<match **>
|
13
|
+
type copy
|
14
|
+
<store>
|
15
|
+
type stdout
|
16
|
+
</store>
|
17
|
+
<store>
|
18
|
+
type relabel
|
19
|
+
@label @slack
|
20
|
+
</store>
|
21
|
+
<store>
|
22
|
+
type relabel
|
23
|
+
@label @irc
|
24
|
+
</store>
|
25
|
+
</match>
|
26
|
+
</label>
|
27
|
+
|
28
|
+
<label @irc>
|
29
|
+
<match **>
|
30
|
+
type irc
|
31
|
+
host "#{ENV['IRC_SERVER']}"
|
32
|
+
port 6667
|
33
|
+
nick daioikachan
|
34
|
+
user daioikachan
|
35
|
+
real daioikachan
|
36
|
+
command %s
|
37
|
+
command_keys command
|
38
|
+
channel %s
|
39
|
+
channel_keys channel
|
40
|
+
message %s
|
41
|
+
out_keys message
|
42
|
+
send_interval 2s # IRC has Excess Flood limit, this is the default value taken from ikachan
|
43
|
+
</match>
|
44
|
+
</label>
|
45
|
+
|
46
|
+
<label @slack>
|
47
|
+
<match **>
|
48
|
+
type slack
|
49
|
+
token "#{ENV['SLACK_API_TOKEN']}"
|
50
|
+
username daioikachan
|
51
|
+
channel %s
|
52
|
+
channel_keys channel
|
53
|
+
color good
|
54
|
+
icon_emoji :ghost:
|
55
|
+
flush_interval 1s # slack API has limit as a post / sec
|
56
|
+
</match>
|
57
|
+
</label>
|
@@ -0,0 +1,46 @@
|
|
1
|
+
<source>
|
2
|
+
type daioikachan
|
3
|
+
bind 0.0.0.0
|
4
|
+
port 4979
|
5
|
+
min_threads 0
|
6
|
+
max_threads 4
|
7
|
+
backlog 1024
|
8
|
+
@label @raw
|
9
|
+
</source>
|
10
|
+
|
11
|
+
<label @raw>
|
12
|
+
<match **>
|
13
|
+
type copy
|
14
|
+
<store>
|
15
|
+
type stdout
|
16
|
+
</store>
|
17
|
+
<store>
|
18
|
+
type relabel
|
19
|
+
@label @slack
|
20
|
+
</store>
|
21
|
+
</match>
|
22
|
+
</label>
|
23
|
+
|
24
|
+
<label @slack>
|
25
|
+
# #fluentd_warn => team1.slack.com#general
|
26
|
+
<match *.fluentd_warn>
|
27
|
+
type slack
|
28
|
+
token "#{ENV['TEAM1_TOKEN']}"
|
29
|
+
username daioikachan
|
30
|
+
channel general
|
31
|
+
color bad
|
32
|
+
icon_emoji :ghost:
|
33
|
+
flush_interval 1s # slack API has limit as a post / sec
|
34
|
+
</match>
|
35
|
+
# other channels => default_team.slack.com#${channel}
|
36
|
+
<match **>
|
37
|
+
type slack
|
38
|
+
token "#{ENV['DEFAULT_TEAM_TOKEN']}"
|
39
|
+
username daioikachan
|
40
|
+
channel %s
|
41
|
+
channel_keys channel
|
42
|
+
color bad
|
43
|
+
icon_emoji :ghost:
|
44
|
+
flush_interval 1s # slack API has limit as a post / sec
|
45
|
+
</match>
|
46
|
+
</label>
|
@@ -0,0 +1,160 @@
|
|
1
|
+
module Fluent
|
2
|
+
class DaioikachanInput < Input
|
3
|
+
Plugin.register_input('daioikachan', self)
|
4
|
+
|
5
|
+
def initialize
|
6
|
+
require 'puma'
|
7
|
+
require 'uri'
|
8
|
+
super
|
9
|
+
end
|
10
|
+
|
11
|
+
config_param :port, :integer, :default => 4979
|
12
|
+
config_param :bind, :string, :default => '0.0.0.0'
|
13
|
+
config_param :min_threads, :integer, :default => 0
|
14
|
+
config_param :max_threads, :integer, :default => 4
|
15
|
+
config_param :backlog, :integer, :default => nil
|
16
|
+
|
17
|
+
def configure(conf)
|
18
|
+
super
|
19
|
+
end
|
20
|
+
|
21
|
+
def start
|
22
|
+
super
|
23
|
+
|
24
|
+
# Refer puma's Runner and Rack handler for puma server setup
|
25
|
+
@server = ::Puma::Server.new(method(:on_request))
|
26
|
+
@server.min_threads = @min_threads
|
27
|
+
@server.max_threads = @max_threads
|
28
|
+
@server.leak_stack_on_error = false
|
29
|
+
setup_http
|
30
|
+
|
31
|
+
@app = App.new(self)
|
32
|
+
|
33
|
+
@thread = Thread.new(&method(:run))
|
34
|
+
end
|
35
|
+
|
36
|
+
def shutdown
|
37
|
+
@server.stop(true)
|
38
|
+
@thread.join
|
39
|
+
end
|
40
|
+
|
41
|
+
def run
|
42
|
+
@server.run(false)
|
43
|
+
rescue => e
|
44
|
+
log.error "unexpected error", :error => e.to_s
|
45
|
+
log.error_backtrace e.backtrace
|
46
|
+
end
|
47
|
+
|
48
|
+
def on_request(env)
|
49
|
+
@app.run(env)
|
50
|
+
end
|
51
|
+
|
52
|
+
def setup_http
|
53
|
+
log.info "listening http on #{@bind}:#{@port}"
|
54
|
+
|
55
|
+
opts = [@bind, @port, true]
|
56
|
+
opts << @backlog if @backlog
|
57
|
+
@server.add_tcp_listener(*opts)
|
58
|
+
end
|
59
|
+
|
60
|
+
class App
|
61
|
+
class BadRequest < StandardError; end
|
62
|
+
class InternalServerError < StandardError; end
|
63
|
+
class NotFound < StandardError; end
|
64
|
+
|
65
|
+
attr_reader :router, :log
|
66
|
+
|
67
|
+
def initialize(plugin)
|
68
|
+
@router = plugin.router
|
69
|
+
@log = plugin.log
|
70
|
+
end
|
71
|
+
|
72
|
+
def run(env)
|
73
|
+
# req = Rack::Request.new(env)
|
74
|
+
method = env['REQUEST_METHOD'.freeze] # req.method
|
75
|
+
path = URI.parse(env['REQUEST_URI'.freeze]).path # req.path
|
76
|
+
body = env['rack.input'].read # req.body.read
|
77
|
+
params = Rack::Utils.parse_query(body)
|
78
|
+
|
79
|
+
begin
|
80
|
+
if method == 'POST'
|
81
|
+
case path
|
82
|
+
when '/notice'
|
83
|
+
notice(params)
|
84
|
+
when '/privmsg'
|
85
|
+
privmsg(params)
|
86
|
+
when '/join'
|
87
|
+
return ok
|
88
|
+
when '/leave'
|
89
|
+
return ok
|
90
|
+
else
|
91
|
+
return not_found
|
92
|
+
end
|
93
|
+
else
|
94
|
+
return not_found
|
95
|
+
end
|
96
|
+
rescue BadRequest => e
|
97
|
+
bad_request(e.message)
|
98
|
+
rescue NotFound => e
|
99
|
+
not_found(e.message)
|
100
|
+
rescue InternalServerError => e
|
101
|
+
internal_server_error(e.message)
|
102
|
+
rescue => e
|
103
|
+
internal_server_error("#{e.class} #{e.message} #{e.backtrace.first}")
|
104
|
+
else
|
105
|
+
ok
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
def notice(params)
|
110
|
+
channel, message = build_channel(params), build_message(params)
|
111
|
+
tag = "notice.#{channel}"
|
112
|
+
record = params.merge('command' => 'notice', 'channel' => channel, 'message' => message)
|
113
|
+
router.emit(tag, Fluent::Engine.now, record)
|
114
|
+
end
|
115
|
+
|
116
|
+
def privmsg(params)
|
117
|
+
channel, message = build_channel(params), build_message(params)
|
118
|
+
tag = "privmsg.#{channel}"
|
119
|
+
record = params.merge('command' => 'privmsg', 'channel' => channel, 'message' => message)
|
120
|
+
router.emit(tag, Fluent::Engine.now, record)
|
121
|
+
end
|
122
|
+
|
123
|
+
private
|
124
|
+
|
125
|
+
def build_channel(params)
|
126
|
+
unless channel = params.delete('channel')
|
127
|
+
raise BadRequest.new('`channel` parameter is mandatory')
|
128
|
+
end
|
129
|
+
if channel.start_with?('#')
|
130
|
+
channel[1..-1] # remove starting #
|
131
|
+
else
|
132
|
+
channel
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
def build_message(params)
|
137
|
+
unless message = params.delete('message')
|
138
|
+
raise BadRequest.new('`message` parameter is mandatory')
|
139
|
+
end
|
140
|
+
message # should I truncate message to max_length?
|
141
|
+
end
|
142
|
+
|
143
|
+
def ok(msg = nil)
|
144
|
+
[200, {'Content-type'=>'text/plain'}, ["OK\n#{msg}"]]
|
145
|
+
end
|
146
|
+
|
147
|
+
def bad_request(msg = nil)
|
148
|
+
[400, {'Content-type'=>'text/plain'}, ["Bad Request\n#{msg}"]]
|
149
|
+
end
|
150
|
+
|
151
|
+
def not_found(msg = nil)
|
152
|
+
[404, {'Content-type'=>'text/plain'}, ["Not Found\n#{msg}"]]
|
153
|
+
end
|
154
|
+
|
155
|
+
def internal_server_error(msg = nil)
|
156
|
+
[500, {'Content-type'=>'text/plain'}, ["Internal Server Error\n#{msg}"]]
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|
@@ -0,0 +1,283 @@
|
|
1
|
+
module Fluent
|
2
|
+
class IRCOutput < Fluent::Output
|
3
|
+
Fluent::Plugin.register_output('irc', self)
|
4
|
+
|
5
|
+
include SetTimeKeyMixin
|
6
|
+
include SetTagKeyMixin
|
7
|
+
|
8
|
+
config_set_default :include_time_key, true
|
9
|
+
config_set_default :include_tag_key, true
|
10
|
+
|
11
|
+
config_param :host , :string , :default => 'localhost'
|
12
|
+
config_param :port , :integer , :default => 6667
|
13
|
+
config_param :channel , :string
|
14
|
+
config_param :channel_keys, :default => nil do |val|
|
15
|
+
val.split(',')
|
16
|
+
end
|
17
|
+
config_param :nick , :string , :default => 'fluentd'
|
18
|
+
config_param :user , :string , :default => 'fluentd'
|
19
|
+
config_param :real , :string , :default => 'fluentd'
|
20
|
+
config_param :password , :string , :default => nil
|
21
|
+
config_param :message , :string
|
22
|
+
config_param :out_keys do |val|
|
23
|
+
val.split(',')
|
24
|
+
end
|
25
|
+
config_param :time_key , :string , :default => 'time'
|
26
|
+
config_param :time_format , :string , :default => '%Y/%m/%d %H:%M:%S'
|
27
|
+
config_param :tag_key , :string , :default => 'tag'
|
28
|
+
config_param :command , :string , :default => 'privmsg'
|
29
|
+
config_param :command_keys, :default => nil do |val|
|
30
|
+
val.split(',')
|
31
|
+
end
|
32
|
+
|
33
|
+
config_param :blocking_timeout, :time, :default => 0.5
|
34
|
+
config_param :max_send_queue, :integer, :default => 100
|
35
|
+
config_param :send_interval, :time, :default => 2
|
36
|
+
|
37
|
+
COMMAND_MAP = {
|
38
|
+
'priv_msg' => :priv_msg,
|
39
|
+
'privmsg' => :priv_msg,
|
40
|
+
'notice' => :notice,
|
41
|
+
}
|
42
|
+
|
43
|
+
# To support log_level option implemented by Fluentd v0.10.43
|
44
|
+
unless method_defined?(:log)
|
45
|
+
define_method("log") { $log }
|
46
|
+
end
|
47
|
+
|
48
|
+
attr_reader :conn # for test
|
49
|
+
|
50
|
+
def initialize
|
51
|
+
super
|
52
|
+
require 'irc_parser'
|
53
|
+
end
|
54
|
+
|
55
|
+
def configure(conf)
|
56
|
+
super
|
57
|
+
|
58
|
+
begin
|
59
|
+
@message % (['1'] * @out_keys.length)
|
60
|
+
rescue ArgumentError
|
61
|
+
raise Fluent::ConfigError, "string specifier '%s' and out_keys specification mismatch"
|
62
|
+
end
|
63
|
+
|
64
|
+
if @channel_keys
|
65
|
+
begin
|
66
|
+
@channel % (['1'] * @channel_keys.length)
|
67
|
+
rescue ArgumentError
|
68
|
+
raise Fluent::ConfigError, "string specifier '%s' and channel_keys specification mismatch"
|
69
|
+
end
|
70
|
+
end
|
71
|
+
@channel = '#'+@channel
|
72
|
+
|
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
|
83
|
+
end
|
84
|
+
|
85
|
+
@send_queue = []
|
86
|
+
end
|
87
|
+
|
88
|
+
def start
|
89
|
+
super
|
90
|
+
|
91
|
+
begin
|
92
|
+
@loop = Coolio::Loop.new
|
93
|
+
@conn = create_connection
|
94
|
+
@timer = TimerWatcher.new(@send_interval, true, log, &method(:on_timer))
|
95
|
+
@loop.attach(@timer)
|
96
|
+
@thread = Thread.new(&method(:run))
|
97
|
+
rescue => e
|
98
|
+
puts e
|
99
|
+
raise Fluent::ConfigError, "failed to connect IRC server #{@host}:#{@port}"
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
def shutdown
|
104
|
+
super
|
105
|
+
@loop.watchers.each { |w| w.detach }
|
106
|
+
@loop.stop
|
107
|
+
@conn.close
|
108
|
+
@thread.join
|
109
|
+
end
|
110
|
+
|
111
|
+
def run
|
112
|
+
@loop.run(@blocking_timeout)
|
113
|
+
rescue => e
|
114
|
+
log.error "unexpected error", :error => e, :error_class => e.class
|
115
|
+
log.error_backtrace
|
116
|
+
end
|
117
|
+
|
118
|
+
def emit(tag, es, chain)
|
119
|
+
chain.next
|
120
|
+
|
121
|
+
if @conn.closed?
|
122
|
+
log.warn "out_irc: connection is closed. try to reconnect"
|
123
|
+
@conn = create_connection
|
124
|
+
end
|
125
|
+
|
126
|
+
es.each do |time,record|
|
127
|
+
if @send_queue.size >= @max_send_queue
|
128
|
+
log.warn "out_irc: send queue size exceeded max_send_queue(#{@max_send_queue}), discards"
|
129
|
+
break
|
130
|
+
end
|
131
|
+
|
132
|
+
filter_record(tag, time, record)
|
133
|
+
command, channel, message = build_command(record), build_channel(record), build_message(record)
|
134
|
+
log.debug { "out_irc: push {command:\"#{command}\", channel:\"#{channel}\", message:\"#{message}\"}" }
|
135
|
+
@send_queue.push([command, channel, message])
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
def on_timer
|
140
|
+
return if @send_queue.empty?
|
141
|
+
command, channel, message = @send_queue.shift
|
142
|
+
log.info { "out_irc: send {command:\"#{command}\", channel:\"#{channel}\", message:\"#{message}\"}" }
|
143
|
+
@conn.send_message(command, channel, message)
|
144
|
+
end
|
145
|
+
|
146
|
+
private
|
147
|
+
|
148
|
+
def create_connection
|
149
|
+
conn = IRCConnection.connect(@host, @port)
|
150
|
+
conn.log = log
|
151
|
+
conn.nick = @nick
|
152
|
+
conn.user = @user
|
153
|
+
conn.real = @real
|
154
|
+
conn.password = @password
|
155
|
+
conn.attach(@loop)
|
156
|
+
conn
|
157
|
+
end
|
158
|
+
|
159
|
+
def build_message(record)
|
160
|
+
values = fetch_keys(record, @out_keys)
|
161
|
+
@message % values
|
162
|
+
end
|
163
|
+
|
164
|
+
def build_channel(record)
|
165
|
+
return @channel unless @channel_keys
|
166
|
+
|
167
|
+
values = fetch_keys(record, @channel_keys)
|
168
|
+
@channel % values
|
169
|
+
end
|
170
|
+
|
171
|
+
def build_command(record)
|
172
|
+
return @command unless @command_keys
|
173
|
+
|
174
|
+
values = fetch_keys(record, @command_keys)
|
175
|
+
unless command = COMMAND_MAP[@command % values]
|
176
|
+
log.warn "out_irc: command is not one of #{COMMAND_MAP.keys.join(', ')}, use privmsg"
|
177
|
+
end
|
178
|
+
command || :priv_msg
|
179
|
+
end
|
180
|
+
|
181
|
+
def fetch_keys(record, keys)
|
182
|
+
Array(keys).map do |key|
|
183
|
+
begin
|
184
|
+
record.fetch(key).to_s
|
185
|
+
rescue KeyError
|
186
|
+
log.warn "out_irc: the specified key '#{key}' not found in record. [#{record}]"
|
187
|
+
''
|
188
|
+
end
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
class TimerWatcher < Coolio::TimerWatcher
|
193
|
+
def initialize(interval, repeat, log, &callback)
|
194
|
+
@callback = callback
|
195
|
+
@log = log
|
196
|
+
super(interval, repeat)
|
197
|
+
end
|
198
|
+
|
199
|
+
def on_timer
|
200
|
+
@callback.call
|
201
|
+
rescue
|
202
|
+
@log.error $!.to_s
|
203
|
+
@log.error_backtrace
|
204
|
+
end
|
205
|
+
end
|
206
|
+
|
207
|
+
class IRCConnection < Cool.io::TCPSocket
|
208
|
+
attr_reader :joined # for test
|
209
|
+
attr_accessor :log, :nick, :user, :real, :password
|
210
|
+
|
211
|
+
def initialize(*args)
|
212
|
+
super
|
213
|
+
@joined = {}
|
214
|
+
end
|
215
|
+
|
216
|
+
def on_connect
|
217
|
+
if @password
|
218
|
+
IRCParser.message(:pass) do |m|
|
219
|
+
m.password = @password
|
220
|
+
write m
|
221
|
+
end
|
222
|
+
end
|
223
|
+
IRCParser.message(:nick) do |m|
|
224
|
+
m.nick = @nick
|
225
|
+
write m
|
226
|
+
end
|
227
|
+
IRCParser.message(:user) do |m|
|
228
|
+
m.user = @user
|
229
|
+
m.postfix = @real
|
230
|
+
write m
|
231
|
+
end
|
232
|
+
end
|
233
|
+
|
234
|
+
def on_read(data)
|
235
|
+
data.each_line do |line|
|
236
|
+
begin
|
237
|
+
msg = IRCParser.parse(line)
|
238
|
+
log.debug { "out_irc: on_read :#{msg.class.to_sym}" }
|
239
|
+
case msg.class.to_sym
|
240
|
+
when :ping
|
241
|
+
IRCParser.message(:pong) do |m|
|
242
|
+
m.target = msg.target
|
243
|
+
m.body = msg.body
|
244
|
+
write m
|
245
|
+
end
|
246
|
+
when :join
|
247
|
+
log.info { "out_irc: joined to #{msg.channels.join(', ')}" }
|
248
|
+
msg.channels.each {|channel| @joined[channel] = true }
|
249
|
+
when :err_nick_name_in_use
|
250
|
+
log.warn "out_irc: nickname \"#{msg.error_nick}\" is already in use."
|
251
|
+
when :error
|
252
|
+
log.warn "out_irc: an error occured. \"#{msg.error_message}\""
|
253
|
+
end
|
254
|
+
rescue
|
255
|
+
#TODO
|
256
|
+
end
|
257
|
+
end
|
258
|
+
end
|
259
|
+
|
260
|
+
def joined?(channel)
|
261
|
+
@joined[channel]
|
262
|
+
end
|
263
|
+
|
264
|
+
def join(channel)
|
265
|
+
IRCParser.message(:join) do |m|
|
266
|
+
m.channels = channel
|
267
|
+
write m
|
268
|
+
end
|
269
|
+
log.debug { "out_irc: join to #{channel}" }
|
270
|
+
end
|
271
|
+
|
272
|
+
def send_message(command, channel, message)
|
273
|
+
join(channel) unless joined?(channel)
|
274
|
+
IRCParser.message(command) do |m|
|
275
|
+
m.target = channel
|
276
|
+
m.body = message
|
277
|
+
write m
|
278
|
+
end
|
279
|
+
channel # return channel for test
|
280
|
+
end
|
281
|
+
end
|
282
|
+
end
|
283
|
+
end
|
data/test/helper.rb
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
require 'fileutils'
|
3
|
+
require 'fluent/log'
|
4
|
+
require 'fluent/test'
|
5
|
+
|
6
|
+
unless defined?(Test::Unit::AssertionFailedError)
|
7
|
+
class Test::Unit::AssertionFailedError < StandardError
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
def unused_port
|
12
|
+
s = TCPServer.open(0)
|
13
|
+
port = s.addr[1]
|
14
|
+
s.close
|
15
|
+
port
|
16
|
+
end
|
17
|
+
|
18
|
+
def ipv6_enabled?
|
19
|
+
require 'socket'
|
20
|
+
|
21
|
+
begin
|
22
|
+
TCPServer.open("::1", 0)
|
23
|
+
true
|
24
|
+
rescue
|
25
|
+
false
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
$log = Fluent::Log.new(Fluent::Test::DummyLogDevice.new, Fluent::Log::LEVEL_WARN)
|
@@ -0,0 +1,105 @@
|
|
1
|
+
require_relative '../helper'
|
2
|
+
require 'fluent/plugin/in_daioikachan'
|
3
|
+
require 'net/https'
|
4
|
+
|
5
|
+
class DaioikachanInputTest < Test::Unit::TestCase
|
6
|
+
def post(path, params, header = {}, ssl = false)
|
7
|
+
http = Net::HTTP.new("127.0.0.1", PORT)
|
8
|
+
if ssl
|
9
|
+
http.use_ssl = true
|
10
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
11
|
+
end
|
12
|
+
req = Net::HTTP::Post.new(path, header)
|
13
|
+
if params.is_a?(String)
|
14
|
+
req.body = params
|
15
|
+
else
|
16
|
+
req.set_form_data(params)
|
17
|
+
end
|
18
|
+
http.request(req)
|
19
|
+
end
|
20
|
+
|
21
|
+
def setup
|
22
|
+
Fluent::Test.setup
|
23
|
+
end
|
24
|
+
|
25
|
+
PORT = unused_port
|
26
|
+
CONFIG = %[
|
27
|
+
port #{PORT}
|
28
|
+
]
|
29
|
+
|
30
|
+
def create_driver(conf = CONFIG)
|
31
|
+
Fluent::Test::InputTestDriver.new(Fluent::DaioikachanInput).configure(conf, true)
|
32
|
+
end
|
33
|
+
|
34
|
+
def test_default_configure
|
35
|
+
d = create_driver(%[])
|
36
|
+
assert_equal 4979, d.instance.port
|
37
|
+
assert_equal '0.0.0.0', d.instance.bind
|
38
|
+
assert_equal 0, d.instance.min_threads
|
39
|
+
assert_equal 4, d.instance.max_threads
|
40
|
+
assert_equal nil, d.instance.backlog
|
41
|
+
end
|
42
|
+
|
43
|
+
def test_configure
|
44
|
+
d = create_driver(%[
|
45
|
+
port #{PORT}
|
46
|
+
bind 127.0.0.1
|
47
|
+
min_threads 0
|
48
|
+
max_threads 4
|
49
|
+
backlog 1024
|
50
|
+
])
|
51
|
+
assert_equal PORT, d.instance.port
|
52
|
+
assert_equal '127.0.0.1', d.instance.bind
|
53
|
+
assert_equal 0, d.instance.min_threads
|
54
|
+
assert_equal 4, d.instance.max_threads
|
55
|
+
assert_equal 1024, d.instance.backlog
|
56
|
+
end
|
57
|
+
|
58
|
+
def test_notice
|
59
|
+
d = create_driver
|
60
|
+
|
61
|
+
time = Time.parse("2011-01-02 13:14:15 UTC").to_i
|
62
|
+
Fluent::Engine.now = time
|
63
|
+
|
64
|
+
d.expect_emit "notice.channel", time, {"command" => "notice", "channel" => "channel", "message" => "message"}
|
65
|
+
|
66
|
+
d.run do
|
67
|
+
d.expected_emits.each {|tag, time, record|
|
68
|
+
res = post("/notice", {command: "notice", channel: "channel", message: "message"})
|
69
|
+
assert_equal "200", res.code
|
70
|
+
}
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def test_privmsg
|
75
|
+
d = create_driver
|
76
|
+
|
77
|
+
time = Time.parse("2011-01-02 13:14:15 UTC").to_i
|
78
|
+
Fluent::Engine.now = time
|
79
|
+
|
80
|
+
d.expect_emit "privmsg.channel", time, {"command" => "privmsg", "channel" => "channel", "message" => "message"}
|
81
|
+
|
82
|
+
d.run do
|
83
|
+
d.expected_emits.each {|tag, time, record|
|
84
|
+
res = post("/privmsg", {command: "privmsg", channel: "channel", message: "message"})
|
85
|
+
assert_equal "200", res.code
|
86
|
+
}
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def test_join
|
91
|
+
d = create_driver
|
92
|
+
d.run do
|
93
|
+
res = post("/join", {})
|
94
|
+
assert_equal "200", res.code
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
def test_leave
|
99
|
+
d = create_driver
|
100
|
+
d.run do
|
101
|
+
res = post("/leave", {})
|
102
|
+
assert_equal "200", res.code
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
metadata
ADDED
@@ -0,0 +1,148 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: daioikachan
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Naotoshi Seo
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-03-22 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: fluentd
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 0.12.0
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 0.12.0
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: dotenv
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: puma
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: irc_parser
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: fluent-plugin-slack
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: 0.5.0
|
76
|
+
type: :runtime
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: 0.5.0
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: rake
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
description: Ikachan compatible interface with multiple backends (IRC, Slack, etc)
|
98
|
+
email: sonots@gmail.com
|
99
|
+
executables:
|
100
|
+
- daioikachan
|
101
|
+
extensions: []
|
102
|
+
extra_rdoc_files: []
|
103
|
+
files:
|
104
|
+
- ".gitignore"
|
105
|
+
- ".travis.yml"
|
106
|
+
- CHANGELOG.md
|
107
|
+
- Gemfile
|
108
|
+
- LICENSE
|
109
|
+
- README.md
|
110
|
+
- README/in_daioikachan.md
|
111
|
+
- README/out_irc.md
|
112
|
+
- Rakefile
|
113
|
+
- VERSION
|
114
|
+
- bin/daioikachan
|
115
|
+
- daioikachan.gemspec
|
116
|
+
- examples/example.conf
|
117
|
+
- examples/multi_slack.conf
|
118
|
+
- lib/fluent/plugin/in_daioikachan.rb
|
119
|
+
- lib/fluent/plugin/out_irc.rb
|
120
|
+
- test/helper.rb
|
121
|
+
- test/plugin/test_in_daioikachan.rb
|
122
|
+
homepage: https://github.com/sonots/daioikachan
|
123
|
+
licenses:
|
124
|
+
- MIT License
|
125
|
+
metadata: {}
|
126
|
+
post_install_message:
|
127
|
+
rdoc_options: []
|
128
|
+
require_paths:
|
129
|
+
- lib
|
130
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
131
|
+
requirements:
|
132
|
+
- - ">="
|
133
|
+
- !ruby/object:Gem::Version
|
134
|
+
version: '0'
|
135
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
136
|
+
requirements:
|
137
|
+
- - ">="
|
138
|
+
- !ruby/object:Gem::Version
|
139
|
+
version: '0'
|
140
|
+
requirements: []
|
141
|
+
rubyforge_project:
|
142
|
+
rubygems_version: 2.2.2
|
143
|
+
signing_key:
|
144
|
+
specification_version: 4
|
145
|
+
summary: Ikachan compatible interface with multiple backends (IRC, Slack, etc)
|
146
|
+
test_files:
|
147
|
+
- test/helper.rb
|
148
|
+
- test/plugin/test_in_daioikachan.rb
|