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 ADDED
@@ -0,0 +1,4 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in coffeemaker.gemspec
4
+ gemspec
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
data/bin/coffeemaker ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'coffeemaker/runner'
4
+
5
+ cm = Coffeemaker::Runner.new(ARGV)
6
+ cm.start
@@ -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
@@ -0,0 +1,3 @@
1
+ module Coffeemaker
2
+ VERSION = "0.0.1.beta.1"
3
+ end
@@ -0,0 +1,4 @@
1
+ require 'coffeemaker/version'
2
+
3
+ module Coffeemaker
4
+ end
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: