daioikachan 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
[![Build Status](https://travis-ci.org/choplin/fluent-plugin-irc.svg)](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
|