coffeemaker 0.0.1.beta.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.
- data/.gitignore +4 -0
- data/Gemfile +4 -0
- data/Rakefile +1 -0
- data/bin/coffeemaker +6 -0
- data/coffeemaker.gemspec +23 -0
- data/goliath.rb +18 -0
- data/http.rb +57 -0
- data/lib/coffeemaker/bot/irc/commands.rb +21 -0
- data/lib/coffeemaker/bot/irc/connection.rb +61 -0
- data/lib/coffeemaker/bot/irc/message.rb +96 -0
- data/lib/coffeemaker/bot/irc.rb +35 -0
- data/lib/coffeemaker/bot.rb +19 -0
- data/lib/coffeemaker/runner.rb +62 -0
- data/lib/coffeemaker/version.rb +3 -0
- data/lib/coffeemaker.rb +4 -0
- metadata +85 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
data/bin/coffeemaker
ADDED
data/coffeemaker.gemspec
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "coffeemaker/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "coffeemaker"
|
7
|
+
s.version = Coffeemaker::VERSION
|
8
|
+
s.authors = ["Paweł Pacana"]
|
9
|
+
s.email = ["pawel.pacana@gmail.com"]
|
10
|
+
s.homepage = "http://drug.org.pl/projects/coffeemaker"
|
11
|
+
s.summary = %q{IRC bot, how unexpected!}
|
12
|
+
s.description = %q{IRC bot that serves as foundation for greater ideas. "Not Invented Here" applies greatly.}
|
13
|
+
|
14
|
+
s.rubyforge_project = "coffeemaker"
|
15
|
+
|
16
|
+
s.files = `git ls-files`.split("\n")
|
17
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
18
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
19
|
+
s.require_paths = ["lib"]
|
20
|
+
|
21
|
+
s.add_runtime_dependency "eventmachine", "~> 1.0.0.beta.3"
|
22
|
+
s.add_runtime_dependency "activesupport"
|
23
|
+
end
|
data/goliath.rb
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'goliath/connection'
|
2
|
+
require 'goliath/api'
|
3
|
+
|
4
|
+
module Goliath
|
5
|
+
module Constants
|
6
|
+
IRC_CONNECTION = 'IRC_CONNECTION'
|
7
|
+
end
|
8
|
+
|
9
|
+
class Request
|
10
|
+
alias_method :initialize_without_irc, :initialize
|
11
|
+
|
12
|
+
def initialize(app, conn, env)
|
13
|
+
initialize_without_irc(app, conn, env)
|
14
|
+
env[IRC_CONNECTION] = conn.irc
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
data/http.rb
ADDED
@@ -0,0 +1,57 @@
|
|
1
|
+
require 'coffeemaker'
|
2
|
+
require 'coffeemaker/goliath'
|
3
|
+
require 'json'
|
4
|
+
|
5
|
+
module Coffeemaker
|
6
|
+
class HTTP
|
7
|
+
class Connection < Goliath::Connection
|
8
|
+
attr_accessor :irc
|
9
|
+
end
|
10
|
+
|
11
|
+
class Router < Goliath::API
|
12
|
+
get '/:channel' do
|
13
|
+
run Stream.new
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
class Stream < Goliath::API
|
18
|
+
def on_close(env)
|
19
|
+
env[IRC_CONNECTION].part(env[:channel])
|
20
|
+
end
|
21
|
+
|
22
|
+
def response(env)
|
23
|
+
env[:channel] = params[:channel]
|
24
|
+
env[IRC_CONNECTION].join(env[:channel])
|
25
|
+
|
26
|
+
env[IRC_CONNECTION].on_message = Proc.new do |msg|
|
27
|
+
env.stream_send("#{msg.to_json}\n")
|
28
|
+
end
|
29
|
+
streaming_response(202, {'X-Stream' => 'Goliath'})
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def initialize(options)
|
34
|
+
@host = options.delete(:http_host)
|
35
|
+
@port = options.delete(:http_port)
|
36
|
+
@options = options
|
37
|
+
end
|
38
|
+
|
39
|
+
def start(irc_connection)
|
40
|
+
Goliath.env = :production
|
41
|
+
@http = EM.start_server(@host, @port, Coffeemaker::HTTP::Connection) do |c|
|
42
|
+
c.port = 8080
|
43
|
+
c.irc = irc_connection
|
44
|
+
c.status = {}
|
45
|
+
c.config = {}
|
46
|
+
c.options = {}
|
47
|
+
c.logger = Logger.new(STDOUT)
|
48
|
+
c.app = Goliath::Rack::Builder.build(Coffeemaker::HTTP::Router, Coffeemaker::HTTP::Router.new)
|
49
|
+
c.api = Coffeemaker::HTTP::Stream.new
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def stop
|
54
|
+
EM.stop_server(@http)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'coffeemaker/bot/irc'
|
2
|
+
|
3
|
+
module Coffeemaker
|
4
|
+
class Bot
|
5
|
+
class Irc
|
6
|
+
module Commands
|
7
|
+
def join(channel)
|
8
|
+
send_command :join, channel
|
9
|
+
end
|
10
|
+
|
11
|
+
def part(channel)
|
12
|
+
send_command :part, channel
|
13
|
+
end
|
14
|
+
|
15
|
+
def message(recipient, text)
|
16
|
+
send_command :privmsg, recipient, ":#{text}"
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
require 'coffeemaker/bot/irc'
|
2
|
+
require 'coffeemaker/bot/irc/message'
|
3
|
+
require 'coffeemaker/bot/irc/commands'
|
4
|
+
require 'eventmachine'
|
5
|
+
|
6
|
+
module Coffeemaker
|
7
|
+
class Bot
|
8
|
+
class Irc
|
9
|
+
module Connection
|
10
|
+
class Error < StandardError; end
|
11
|
+
|
12
|
+
include ::EM::Deferrable
|
13
|
+
include ::EM::Protocols::LineText2
|
14
|
+
include ::Coffeemaker::Bot::Irc::Commands
|
15
|
+
|
16
|
+
attr_accessor :port, :host, :options, :on_message
|
17
|
+
|
18
|
+
def connection_completed
|
19
|
+
@reconnecting = false
|
20
|
+
@connected = true
|
21
|
+
_send_command :user, [options[:nick]] * 4
|
22
|
+
_send_command :nick, options[:nick]
|
23
|
+
succeed
|
24
|
+
end
|
25
|
+
|
26
|
+
def receive_line(data)
|
27
|
+
msg = ::Coffeemaker::Bot::Irc::Message.new(data)
|
28
|
+
case msg.command
|
29
|
+
when :ping
|
30
|
+
send_command :pong
|
31
|
+
when :privmsg, :topic, :part, :join
|
32
|
+
on_message.call(msg) if on_message
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def unbind
|
37
|
+
@deferred_status = nil
|
38
|
+
if @connected or @reconnecting
|
39
|
+
EM.add_timer(1) do
|
40
|
+
reconnect(@host, @port)
|
41
|
+
end
|
42
|
+
@connected = false
|
43
|
+
@reconnecting = true
|
44
|
+
else
|
45
|
+
raise Error, "unable to connect to server #{@host}:#{@port}"
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
private
|
50
|
+
def _send_command(name, *args)
|
51
|
+
cmd = [name.to_s.upcase] + args
|
52
|
+
send_data("#{cmd.flatten.join(' ')}\r\n")
|
53
|
+
end
|
54
|
+
|
55
|
+
def send_command(name, *args)
|
56
|
+
callback { _send_command(name, *args) }
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
require 'coffeemaker'
|
2
|
+
|
3
|
+
module Coffeemaker
|
4
|
+
class Bot
|
5
|
+
class Irc
|
6
|
+
class Message
|
7
|
+
attr_accessor :raw, :prefix, :params
|
8
|
+
|
9
|
+
def initialize(msg = nil)
|
10
|
+
@raw = msg
|
11
|
+
parse if msg
|
12
|
+
end
|
13
|
+
|
14
|
+
def numeric_reply?
|
15
|
+
!!numeric_reply
|
16
|
+
end
|
17
|
+
|
18
|
+
def numeric_reply
|
19
|
+
@numeric_reply ||= @command.match(/^\d\d\d$/)
|
20
|
+
end
|
21
|
+
|
22
|
+
def nick
|
23
|
+
return unless @prefix
|
24
|
+
@nick ||= @prefix[/^(\S+)!/, 1]
|
25
|
+
end
|
26
|
+
|
27
|
+
def user
|
28
|
+
return unless @prefix
|
29
|
+
@user ||= @prefix[/^\S+!(\S+)@/, 1]
|
30
|
+
end
|
31
|
+
|
32
|
+
def host
|
33
|
+
return unless @prefix
|
34
|
+
@host ||= @prefix[/@(\S+)$/, 1]
|
35
|
+
end
|
36
|
+
|
37
|
+
def server
|
38
|
+
return unless @prefix
|
39
|
+
return if @prefix.match(/[@!]/)
|
40
|
+
@server ||= @prefix[/^(\S+)/, 1]
|
41
|
+
end
|
42
|
+
|
43
|
+
def error?
|
44
|
+
!!error
|
45
|
+
end
|
46
|
+
|
47
|
+
def error
|
48
|
+
return @error if @error
|
49
|
+
@error = @command.to_i if numeric_reply? && @command[/[45]\d\d/]
|
50
|
+
end
|
51
|
+
|
52
|
+
def channel?
|
53
|
+
!!channel
|
54
|
+
end
|
55
|
+
|
56
|
+
def channel
|
57
|
+
return @channel if @channel
|
58
|
+
if regular_command? and params.first.start_with?("#")
|
59
|
+
@channel = params.first
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def message
|
64
|
+
return @message if @message
|
65
|
+
if error?
|
66
|
+
@message = error.to_s
|
67
|
+
elsif regular_command?
|
68
|
+
@message = params.last
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def command
|
73
|
+
@command.downcase.to_sym
|
74
|
+
end
|
75
|
+
|
76
|
+
private
|
77
|
+
def parse
|
78
|
+
match = @raw.match(/(^:(\S+) )?(\S+)(.*)/)
|
79
|
+
_, @prefix, @command, raw_params = match.captures
|
80
|
+
|
81
|
+
raw_params.strip!
|
82
|
+
if match = raw_params.match(/:(.*)/)
|
83
|
+
@params = match.pre_match.split(" ")
|
84
|
+
@params << match[1]
|
85
|
+
else
|
86
|
+
@params = raw_params.split(" ")
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def regular_command?
|
91
|
+
%w(privmsg join part quit).include? @command.downcase
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'coffeemaker/bot'
|
2
|
+
require 'coffeemaker/bot/irc/connection'
|
3
|
+
require 'active_support/core_ext/module/delegation'
|
4
|
+
|
5
|
+
module Coffeemaker
|
6
|
+
class Bot
|
7
|
+
class Irc
|
8
|
+
include EM::Deferrable
|
9
|
+
|
10
|
+
attr_reader :connection
|
11
|
+
delegate :join, :part, :privmsg, to: :connection
|
12
|
+
|
13
|
+
def initialize(options)
|
14
|
+
@host = options.delete(:irc_host)
|
15
|
+
@port = options.delete(:irc_port)
|
16
|
+
@callback = options.delete(:on_message)
|
17
|
+
@options = options
|
18
|
+
end
|
19
|
+
|
20
|
+
def start
|
21
|
+
@connection = EM.connect(@host, @port, Connection) do |c|
|
22
|
+
c.host = @host
|
23
|
+
c.port = @port
|
24
|
+
c.on_message = @callback
|
25
|
+
c.options = @options
|
26
|
+
c.callback { succeed }
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def stop
|
31
|
+
@connection.close_connection
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'coffeemaker/bot/irc'
|
2
|
+
|
3
|
+
module Coffeemaker
|
4
|
+
class Bot
|
5
|
+
def initialize(options)
|
6
|
+
@irc = Irc.new(options)
|
7
|
+
end
|
8
|
+
|
9
|
+
def start(&block)
|
10
|
+
@irc.start.callback do
|
11
|
+
block.call(@irc) if block_given?
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def stop
|
16
|
+
@irc.stop
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
require 'coffeemaker/bot'
|
2
|
+
require 'eventmachine'
|
3
|
+
require 'optparse'
|
4
|
+
require 'logger'
|
5
|
+
|
6
|
+
module Coffeemaker
|
7
|
+
class Runner
|
8
|
+
def initialize(argv)
|
9
|
+
@options = default_options
|
10
|
+
parse_options!(argv)
|
11
|
+
|
12
|
+
if logfile = @options.delete(:logfile)
|
13
|
+
@logger = Logger.new(logfile =~ /stdout/i ? STDOUT : logfile)
|
14
|
+
end
|
15
|
+
@channels = @options.delete(:channels)
|
16
|
+
end
|
17
|
+
|
18
|
+
def start
|
19
|
+
EM.run do
|
20
|
+
bot = Coffeemaker::Bot.new(@options)
|
21
|
+
bot.start do |irc|
|
22
|
+
@channels.each { |channel| irc.join(channel) }
|
23
|
+
end
|
24
|
+
|
25
|
+
trap ("INT") do
|
26
|
+
bot.stop
|
27
|
+
EM.stop
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
def default_options
|
34
|
+
{
|
35
|
+
irc_host: 'localhost',
|
36
|
+
irc_port: 6667,
|
37
|
+
nick: 'coffeemaker',
|
38
|
+
channels: [],
|
39
|
+
on_message: Proc.new { |msg| @logger.info(msg) if @logger }
|
40
|
+
}
|
41
|
+
end
|
42
|
+
|
43
|
+
def parse_options!(argv)
|
44
|
+
OptionParser.new do |option|
|
45
|
+
option.default_argv = argv
|
46
|
+
option.banner = "Usage: coffeemaker [options]"
|
47
|
+
option.on('-n NICK') { |nick| @options[:nick] = nick }
|
48
|
+
option.on('-p PORT', Numeric) { |port| @options[:irc_port] = port }
|
49
|
+
option.on('-s HOST') { |host| @options[:irc_host] = host }
|
50
|
+
option.on('-c CHANNELS', 'comma-separated list of channels') { |channels| @options[:channels] = channels.split }
|
51
|
+
option.on('-l LOG_FILE', 'path to log file or STDOUT') { |logfile| @options[:logfile] = logfile }
|
52
|
+
option.on('--help') { puts option; exit }
|
53
|
+
begin
|
54
|
+
option.parse!
|
55
|
+
rescue OptionParser::RequiredArgument, OptionParser::InvalidOption
|
56
|
+
puts option
|
57
|
+
exit
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
data/lib/coffeemaker.rb
ADDED
metadata
ADDED
@@ -0,0 +1,85 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: coffeemaker
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1.beta.1
|
5
|
+
prerelease: 6
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Paweł Pacana
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2011-10-28 00:00:00.000000000Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: eventmachine
|
16
|
+
requirement: &23452580 !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ~>
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: 1.0.0.beta.3
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: *23452580
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: activesupport
|
27
|
+
requirement: &23450520 !ruby/object:Gem::Requirement
|
28
|
+
none: false
|
29
|
+
requirements:
|
30
|
+
- - ! '>='
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '0'
|
33
|
+
type: :runtime
|
34
|
+
prerelease: false
|
35
|
+
version_requirements: *23450520
|
36
|
+
description: IRC bot that serves as foundation for greater ideas. "Not Invented Here"
|
37
|
+
applies greatly.
|
38
|
+
email:
|
39
|
+
- pawel.pacana@gmail.com
|
40
|
+
executables:
|
41
|
+
- coffeemaker
|
42
|
+
extensions: []
|
43
|
+
extra_rdoc_files: []
|
44
|
+
files:
|
45
|
+
- .gitignore
|
46
|
+
- Gemfile
|
47
|
+
- Rakefile
|
48
|
+
- bin/coffeemaker
|
49
|
+
- coffeemaker.gemspec
|
50
|
+
- goliath.rb
|
51
|
+
- http.rb
|
52
|
+
- lib/coffeemaker.rb
|
53
|
+
- lib/coffeemaker/bot.rb
|
54
|
+
- lib/coffeemaker/bot/irc.rb
|
55
|
+
- lib/coffeemaker/bot/irc/commands.rb
|
56
|
+
- lib/coffeemaker/bot/irc/connection.rb
|
57
|
+
- lib/coffeemaker/bot/irc/message.rb
|
58
|
+
- lib/coffeemaker/runner.rb
|
59
|
+
- lib/coffeemaker/version.rb
|
60
|
+
homepage: http://drug.org.pl/projects/coffeemaker
|
61
|
+
licenses: []
|
62
|
+
post_install_message:
|
63
|
+
rdoc_options: []
|
64
|
+
require_paths:
|
65
|
+
- lib
|
66
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
67
|
+
none: false
|
68
|
+
requirements:
|
69
|
+
- - ! '>='
|
70
|
+
- !ruby/object:Gem::Version
|
71
|
+
version: '0'
|
72
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ! '>'
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: 1.3.1
|
78
|
+
requirements: []
|
79
|
+
rubyforge_project: coffeemaker
|
80
|
+
rubygems_version: 1.8.10
|
81
|
+
signing_key:
|
82
|
+
specification_version: 3
|
83
|
+
summary: IRC bot, how unexpected!
|
84
|
+
test_files: []
|
85
|
+
has_rdoc:
|