ponder 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.
- data/LICENSE +7 -0
- data/README.md +338 -0
- data/examples/echo.rb +25 -0
- data/examples/github_blog.rb +31 -0
- data/lib/ponder.rb +22 -0
- data/lib/ponder/async_irc.rb +117 -0
- data/lib/ponder/callback.rb +42 -0
- data/lib/ponder/connection.rb +37 -0
- data/lib/ponder/delegate.rb +11 -0
- data/lib/ponder/filter.rb +7 -0
- data/lib/ponder/formatting.rb +30 -0
- data/lib/ponder/irc.rb +110 -0
- data/lib/ponder/logger/blind_io.rb +11 -0
- data/lib/ponder/logger/twoflogger.rb +93 -0
- data/lib/ponder/logger/twoflogger18.rb +93 -0
- data/lib/ponder/thaum.rb +242 -0
- data/lib/ponder/version.rb +3 -0
- data/lib/ruby/1.8/string.rb +5 -0
- data/ponder.gemspec +39 -0
- data/test/all_tests.rb +7 -0
- data/test/test_async_irc.rb +108 -0
- data/test/test_callback.rb +55 -0
- data/test/test_helper.rb +3 -0
- data/test/test_irc.rb +130 -0
- metadata +107 -0
@@ -0,0 +1,42 @@
|
|
1
|
+
module Ponder
|
2
|
+
class Callback
|
3
|
+
LISTENED_TYPES = [:connect, :channel, :query, :join, :part, :quit, :nickchange, :kick, :topic, :disconnect] # + 3-digit numbers
|
4
|
+
|
5
|
+
def initialize(event_type = :channel, match = //, proc = Proc.new {})
|
6
|
+
unless self.class::LISTENED_TYPES.include?(event_type) || event_type.is_a?(Integer)
|
7
|
+
raise TypeError, "#{event_type} is an unsupported event-type"
|
8
|
+
end
|
9
|
+
|
10
|
+
self.match = match
|
11
|
+
self.proc = proc
|
12
|
+
end
|
13
|
+
|
14
|
+
def call(event_type, event_data = {})
|
15
|
+
if (event_type == :channel) || (event_type == :query)
|
16
|
+
@proc.call(event_data) if event_data[:message] =~ @match
|
17
|
+
elsif event_type == :topic
|
18
|
+
@proc.call(event_data) if event_data[:topic] =~ @match
|
19
|
+
else
|
20
|
+
@proc.call(event_data)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def match=(match)
|
27
|
+
if match.is_a?(Regexp)
|
28
|
+
@match = match
|
29
|
+
else
|
30
|
+
raise TypeError, "#{match} must be a Regexp"
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def proc=(proc)
|
35
|
+
if proc.is_a?(Proc)
|
36
|
+
@proc = proc
|
37
|
+
else
|
38
|
+
raise TypeError, "#{proc} must be a Proc"
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'eventmachine'
|
2
|
+
|
3
|
+
module Ponder
|
4
|
+
class Connection < EventMachine::Connection
|
5
|
+
include EventMachine::Protocols::LineText2
|
6
|
+
|
7
|
+
def initialize(thaum)
|
8
|
+
@thaum = thaum
|
9
|
+
end
|
10
|
+
|
11
|
+
def connection_completed
|
12
|
+
@thaum.register
|
13
|
+
end
|
14
|
+
|
15
|
+
def unbind
|
16
|
+
@thaum.connected = false
|
17
|
+
@thaum.process_callbacks :disconnect
|
18
|
+
@thaum.traffic_logger.info '-- Ponder disconnected'
|
19
|
+
@thaum.console_logger.info '-- Ponder disconnected'
|
20
|
+
|
21
|
+
if @thaum.config.reconnect
|
22
|
+
@thaum.traffic_logger.info "-- Reconnecting in #{@thaum.config.reconnect_interval} seconds"
|
23
|
+
@thaum.console_logger.info "-- Reconnecting in #{@thaum.config.reconnect_interval} seconds"
|
24
|
+
|
25
|
+
EventMachine::add_timer(@thaum.config.reconnect_interval) do
|
26
|
+
reconnect @thaum.config.server, @thaum.config.port
|
27
|
+
end
|
28
|
+
else
|
29
|
+
EventMachine::stop_event_loop
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def receive_line(line)
|
34
|
+
@thaum.parse line.force_encoding('utf-8')
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
module Ponder
|
2
|
+
module Delegate
|
3
|
+
def delegate
|
4
|
+
thaum = self
|
5
|
+
|
6
|
+
(IRC.instance_methods + [:configure, :on, :connect, :reload!, :reloading?]).each do |method|
|
7
|
+
Object.send(:define_method, method) { |*args, &block| thaum.send(method, *args, &block) }
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module Ponder
|
2
|
+
module Formatting
|
3
|
+
PLAIN = 15.chr
|
4
|
+
BOLD = 2.chr
|
5
|
+
ITALIC = 22.chr
|
6
|
+
UNDERLINE = 31.chr
|
7
|
+
COLOR_CODE = 3.chr
|
8
|
+
UNCOLOR_CODE = COLOR_CODE
|
9
|
+
|
10
|
+
#mIRC color codes from http://www.mirc.com/help/colors.html
|
11
|
+
COLORS = {:white => '00',
|
12
|
+
:black => '01',
|
13
|
+
:blue => '02',
|
14
|
+
:green => '03',
|
15
|
+
:red => '04',
|
16
|
+
:brown => '05',
|
17
|
+
:purple => '06',
|
18
|
+
:orange => '07',
|
19
|
+
:yellow => '08',
|
20
|
+
:lime => '09',
|
21
|
+
:teal => '10',
|
22
|
+
:cyan => '11',
|
23
|
+
:royal => '12',
|
24
|
+
:pink => '13',
|
25
|
+
:gray => '14',
|
26
|
+
:silver => '15'
|
27
|
+
}
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
data/lib/ponder/irc.rb
ADDED
@@ -0,0 +1,110 @@
|
|
1
|
+
module Ponder
|
2
|
+
module IRC
|
3
|
+
# raw IRC messages
|
4
|
+
def raw(message)
|
5
|
+
@connection.send_data "#{message}\r\n"
|
6
|
+
@traffic_logger.info ">> #{message}"
|
7
|
+
@console_logger.info ">> #{message}"
|
8
|
+
end
|
9
|
+
|
10
|
+
# send a message
|
11
|
+
def message(recipient, message)
|
12
|
+
raw "PRIVMSG #{recipient} :#{message}"
|
13
|
+
end
|
14
|
+
|
15
|
+
# register when connected
|
16
|
+
def register
|
17
|
+
raw "NICK #{@config.nick}"
|
18
|
+
raw "USER #{@config.username} * * :#{@config.real_name}"
|
19
|
+
raw "PASS #{@config.password}" if @config.password
|
20
|
+
end
|
21
|
+
|
22
|
+
# send a notice
|
23
|
+
def notice(recipient, message)
|
24
|
+
raw "NOTICE #{recipient} :#{message}"
|
25
|
+
end
|
26
|
+
|
27
|
+
# set a mode
|
28
|
+
def mode(recipient, option)
|
29
|
+
raw "MODE #{recipient} #{option}"
|
30
|
+
end
|
31
|
+
|
32
|
+
# kick a user
|
33
|
+
def kick(channel, user, reason = nil)
|
34
|
+
if reason
|
35
|
+
raw "KICK #{channel} #{user} :#{reason}"
|
36
|
+
else
|
37
|
+
raw "KICK #{channel} #{user}"
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
# perform an action
|
42
|
+
def action(recipient, message)
|
43
|
+
raw "PRIVMSG #{recipient} :\001ACTION #{message}\001"
|
44
|
+
end
|
45
|
+
|
46
|
+
# set a topic
|
47
|
+
def topic(channel, topic)
|
48
|
+
raw "TOPIC #{channel} :#{topic}"
|
49
|
+
end
|
50
|
+
|
51
|
+
# joining a channel
|
52
|
+
def join(channel, password = nil)
|
53
|
+
if password
|
54
|
+
raw "JOIN #{channel} #{password}"
|
55
|
+
else
|
56
|
+
raw "JOIN #{channel}"
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
# parting a channel
|
61
|
+
def part(channel, message = nil)
|
62
|
+
if message
|
63
|
+
raw "PART #{channel} :#{message}"
|
64
|
+
else
|
65
|
+
raw "PART #{channel}"
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
# quitting
|
70
|
+
def quit(message = nil)
|
71
|
+
if message
|
72
|
+
raw "QUIT :#{message}"
|
73
|
+
else
|
74
|
+
raw 'QUIT'
|
75
|
+
end
|
76
|
+
|
77
|
+
@config.reconnect = false # so Ponder does not reconnect after the socket has been closed
|
78
|
+
end
|
79
|
+
|
80
|
+
# rename
|
81
|
+
def rename(nick)
|
82
|
+
raw "NICK :#{nick}"
|
83
|
+
end
|
84
|
+
|
85
|
+
# set an away status
|
86
|
+
def away(message = nil)
|
87
|
+
if message
|
88
|
+
raw "AWAY :#{message}"
|
89
|
+
else
|
90
|
+
raw "AWAY"
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
# cancel an away status
|
95
|
+
def back
|
96
|
+
away
|
97
|
+
end
|
98
|
+
|
99
|
+
# invite an user to a channel
|
100
|
+
def invite(nick, channel)
|
101
|
+
raw "INVITE #{nick} #{channel}"
|
102
|
+
end
|
103
|
+
|
104
|
+
# ban an user
|
105
|
+
def ban(channel, address)
|
106
|
+
mode channel, "+b #{address}"
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
@@ -0,0 +1,11 @@
|
|
1
|
+
module Ponder
|
2
|
+
module Logger
|
3
|
+
class BlindIo
|
4
|
+
def initialize
|
5
|
+
[:debug, :info, :warn, :error, :fatal, :unknown, :start_logging, :stop_logging].each do |method_name|
|
6
|
+
self.class.send(:define_method, method_name, Proc.new { |*args| nil })
|
7
|
+
end
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,93 @@
|
|
1
|
+
require 'pathname'
|
2
|
+
require 'thread'
|
3
|
+
autoload :FileUtils, 'fileutils'
|
4
|
+
|
5
|
+
module Ponder
|
6
|
+
module Logger
|
7
|
+
class Twoflogger
|
8
|
+
attr_accessor :level, :levels, :time_format
|
9
|
+
|
10
|
+
def initialize(destination = Ponder.root.join('logs', 'log.log'), level = :debug, time_format = '%Y-%m-%d %H:%M:%S', levels = {:debug => 0, :info => 1, :warn => 2, :error => 3, :fatal => 4, :unknown => 5})
|
11
|
+
@level = level
|
12
|
+
@time_format = time_format
|
13
|
+
@levels = levels
|
14
|
+
@queue = Queue.new
|
15
|
+
@mutex = Mutex.new
|
16
|
+
@running = false
|
17
|
+
|
18
|
+
define_level_shorthand_methods
|
19
|
+
self.log_dev = destination
|
20
|
+
end
|
21
|
+
|
22
|
+
def start_logging
|
23
|
+
@running = true
|
24
|
+
@thread = Thread.new do
|
25
|
+
begin
|
26
|
+
while @running do
|
27
|
+
write(@queue.pop)
|
28
|
+
end
|
29
|
+
ensure
|
30
|
+
@log_dev.close if @log_dev.is_a?(File)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def stop_logging
|
36
|
+
@running = false
|
37
|
+
end
|
38
|
+
|
39
|
+
def log_dev=(destination)
|
40
|
+
stop_logging
|
41
|
+
|
42
|
+
if destination.is_a?(Pathname)
|
43
|
+
unless destination.exist?
|
44
|
+
unless destination.dirname.directory?
|
45
|
+
FileUtils.mkdir_p destination.dirname
|
46
|
+
end
|
47
|
+
|
48
|
+
File.new(destination, 'w+')
|
49
|
+
end
|
50
|
+
@log_dev = File.open(destination, 'a+')
|
51
|
+
@log_dev.sync = true
|
52
|
+
elsif destination.is_a?(IO)
|
53
|
+
@log_dev = destination
|
54
|
+
else
|
55
|
+
raise TypeError, 'need a Pathname or IO'
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
private
|
60
|
+
|
61
|
+
def define_level_shorthand_methods
|
62
|
+
@levels.each_pair do |level_name, severity|
|
63
|
+
self.class.send(:define_method, level_name, Proc.new { |*messages| queue(severity, *messages) })
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def queue(severity, *messages)
|
68
|
+
raise(ArgumentError, 'Need a message') if messages.empty?
|
69
|
+
raise(ArgumentError, 'Need messages that respond to #to_s') if messages.any? { |message| !message.respond_to?(:to_s) }
|
70
|
+
|
71
|
+
if severity >= @levels[@level]
|
72
|
+
message_hashes = messages.map { |message| {:severity => severity, :message => message} }
|
73
|
+
|
74
|
+
@mutex.synchronize do
|
75
|
+
message_hashes.each do |hash|
|
76
|
+
@queue << hash
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def write(*message_hashes)
|
83
|
+
begin
|
84
|
+
message_hashes.each do |hash|
|
85
|
+
@log_dev.puts "#{@levels.key(hash[:severity])} #{Time.now.strftime(@time_format)} #{hash[:message]}"
|
86
|
+
end
|
87
|
+
rescue => e
|
88
|
+
puts e.message, *e.backtrace
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
@@ -0,0 +1,93 @@
|
|
1
|
+
require 'pathname'
|
2
|
+
require 'thread'
|
3
|
+
autoload :FileUtils, 'fileutils'
|
4
|
+
|
5
|
+
module Ponder
|
6
|
+
module Logger
|
7
|
+
class Twoflogger
|
8
|
+
attr_accessor :level, :levels, :time_format
|
9
|
+
|
10
|
+
def initialize(destination = Ponder.root.join('logs', 'log.log'), level = :debug, time_format = '%Y-%m-%d %H:%M:%S', levels = {:debug => 0, :info => 1, :warn => 2, :error => 3, :fatal => 4, :unknown => 5})
|
11
|
+
@level = level
|
12
|
+
@time_format = time_format
|
13
|
+
@levels = levels
|
14
|
+
@queue = Queue.new
|
15
|
+
@mutex = Mutex.new
|
16
|
+
@running = false
|
17
|
+
|
18
|
+
define_level_shorthand_methods
|
19
|
+
self.log_dev = destination
|
20
|
+
end
|
21
|
+
|
22
|
+
def start_logging
|
23
|
+
@running = true
|
24
|
+
@thread = Thread.new do
|
25
|
+
begin
|
26
|
+
while @running do
|
27
|
+
write(@queue.pop)
|
28
|
+
end
|
29
|
+
ensure
|
30
|
+
@log_dev.close if @log_dev.is_a?(File)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def stop_logging
|
36
|
+
@running = false
|
37
|
+
end
|
38
|
+
|
39
|
+
def log_dev=(destination)
|
40
|
+
stop_logging
|
41
|
+
|
42
|
+
if destination.is_a?(Pathname)
|
43
|
+
unless destination.exist?
|
44
|
+
unless destination.dirname.directory?
|
45
|
+
FileUtils.mkdir_p destination.dirname
|
46
|
+
end
|
47
|
+
|
48
|
+
File.new(destination, 'w+')
|
49
|
+
end
|
50
|
+
@log_dev = File.open(destination, 'a+')
|
51
|
+
@log_dev.sync = true
|
52
|
+
elsif destination.is_a?(IO)
|
53
|
+
@log_dev = destination
|
54
|
+
else
|
55
|
+
raise TypeError, 'need a Pathname or IO'
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
private
|
60
|
+
|
61
|
+
def define_level_shorthand_methods
|
62
|
+
@levels.each_pair do |level_name, severity|
|
63
|
+
self.class.send(:define_method, level_name, Proc.new { |*messages| queue(severity, *messages) })
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def queue(severity, *messages)
|
68
|
+
raise(ArgumentError, 'Need a message') if messages.empty?
|
69
|
+
raise(ArgumentError, 'Need messages that respond to #to_s') if messages.any? { |message| !message.respond_to?(:to_s) }
|
70
|
+
|
71
|
+
if severity >= @levels[@level]
|
72
|
+
message_hashes = messages.map { |message| {:severity => severity, :message => message} }
|
73
|
+
|
74
|
+
@mutex.synchronize do
|
75
|
+
message_hashes.each do |hash|
|
76
|
+
@queue << hash
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def write(*message_hashes)
|
83
|
+
begin
|
84
|
+
message_hashes.each do |hash|
|
85
|
+
@log_dev.puts "#{@levels.index(hash[:severity])} #{Time.now.strftime(@time_format)} #{hash[:message]}"
|
86
|
+
end
|
87
|
+
rescue => e
|
88
|
+
puts e.message, *e.backtrace
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
data/lib/ponder/thaum.rb
ADDED
@@ -0,0 +1,242 @@
|
|
1
|
+
require 'ponder/callback'
|
2
|
+
require 'ponder/connection'
|
3
|
+
require 'ponder/irc'
|
4
|
+
require 'ponder/async_irc'
|
5
|
+
require 'ponder/filter'
|
6
|
+
require 'ostruct'
|
7
|
+
|
8
|
+
module Ponder
|
9
|
+
class Thaum
|
10
|
+
include IRC
|
11
|
+
include AsyncIRC
|
12
|
+
|
13
|
+
if RUBY_VERSION >= '1.9'
|
14
|
+
require 'ponder/delegate'
|
15
|
+
include Delegate
|
16
|
+
end
|
17
|
+
|
18
|
+
attr_reader :config
|
19
|
+
attr_accessor :connected, :traffic_logger, :error_logger, :console_logger, :empty_logger
|
20
|
+
|
21
|
+
def initialize
|
22
|
+
@config = OpenStruct.new(:server => 'localhost',
|
23
|
+
:port => 6667,
|
24
|
+
:nick => 'Ponder',
|
25
|
+
:username => 'Ponder',
|
26
|
+
:real_name => 'Ponder',
|
27
|
+
:verbose => true,
|
28
|
+
:logging => false,
|
29
|
+
:reconnect => true,
|
30
|
+
:reconnect_interval => 30,
|
31
|
+
:auto_rename => true
|
32
|
+
)
|
33
|
+
|
34
|
+
@empty_logger = Logger::BlindIo.new
|
35
|
+
@traffic_logger = @empty_logger
|
36
|
+
@error_logger = @empty_logger
|
37
|
+
@console_logger = Logger::Twoflogger.new($stdout)
|
38
|
+
|
39
|
+
@observer_queues = {}
|
40
|
+
|
41
|
+
@connected = false
|
42
|
+
@reloading = false
|
43
|
+
|
44
|
+
# user callbacks
|
45
|
+
@callbacks = Hash.new { |hash, key| hash[key] = [] }
|
46
|
+
|
47
|
+
# standard callbacks for PING, VERSION, TIME and Nickname is already in use
|
48
|
+
on :query, /^\001PING \d+\001$/ do |env|
|
49
|
+
time = env[:message].scan(/\d+/)[0]
|
50
|
+
notice env[:nick], "\001PING #{time}\001"
|
51
|
+
end
|
52
|
+
|
53
|
+
on :query, /^\001VERSION\001$/ do |env|
|
54
|
+
notice env[:nick], "\001VERSION Ponder #{Ponder::VERSION} (http://github.com/tbuehlmann/ponder)\001"
|
55
|
+
end
|
56
|
+
|
57
|
+
on :query, /^\001TIME\001$/ do |env|
|
58
|
+
notice env[:nick], "\001TIME #{Time.now.strftime('%a %b %d %H:%M:%S %Y')}\001"
|
59
|
+
end
|
60
|
+
|
61
|
+
on 433 do
|
62
|
+
rename "#{@config.nick}#{rand(10)}#{rand(10)}#{rand(10)}"
|
63
|
+
end
|
64
|
+
|
65
|
+
# before and after filter
|
66
|
+
@before_filters = Hash.new { |hash, key| hash[key] = [] }
|
67
|
+
@after_filters = Hash.new { |hash, key| hash[key] = [] }
|
68
|
+
end
|
69
|
+
|
70
|
+
def configure(&block)
|
71
|
+
unless @reloading
|
72
|
+
block.call(@config)
|
73
|
+
|
74
|
+
# logger changes (if differing from initialize)
|
75
|
+
if @config.logging
|
76
|
+
@traffic_logger = @config.traffic_logger ? @config.traffic_logger : Logger::Twoflogger.new(Ponder.root.join('logs', 'traffic.log'))
|
77
|
+
@error_logger = @config.error_logger ? @config.error_logger : Logger::Twoflogger.new(Ponder.root.join('logs', 'error.log'))
|
78
|
+
end
|
79
|
+
|
80
|
+
unless @config.verbose
|
81
|
+
@console_logger = @empty_logger
|
82
|
+
end
|
83
|
+
|
84
|
+
# remove auto_rename callback (if differing from initialize)
|
85
|
+
unless @config.auto_rename
|
86
|
+
@callbacks.delete(433)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
def on(event_types = [:channel], match = //, &block)
|
92
|
+
if event_types.is_a?(Array)
|
93
|
+
callbacks = event_types.map { |event_type| Callback.new(event_type, match, block) }
|
94
|
+
else
|
95
|
+
callbacks = [Callback.new(event_types, match, block)]
|
96
|
+
event_types = [event_types]
|
97
|
+
end
|
98
|
+
|
99
|
+
callbacks.each_with_index do |callback, index|
|
100
|
+
@callbacks[event_types[index]] << callback
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
def connect
|
105
|
+
unless @reloading
|
106
|
+
@traffic_logger.start_logging
|
107
|
+
@error_logger.start_logging
|
108
|
+
@console_logger.start_logging
|
109
|
+
|
110
|
+
@traffic_logger.info '-- Starting Ponder'
|
111
|
+
@console_logger.info '-- Starting Ponder'
|
112
|
+
|
113
|
+
EventMachine::run do
|
114
|
+
@connection = EventMachine::connect(@config.server, @config.port, Connection, self)
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
def reload!
|
120
|
+
@reloading = true
|
121
|
+
@callbacks.clear
|
122
|
+
load $0
|
123
|
+
@reloading = false
|
124
|
+
end
|
125
|
+
|
126
|
+
def reloading?
|
127
|
+
@reloading
|
128
|
+
end
|
129
|
+
|
130
|
+
# parsing incoming traffic
|
131
|
+
def parse(message)
|
132
|
+
message.chomp!
|
133
|
+
@traffic_logger.info "<< #{message}"
|
134
|
+
@console_logger.info "<< #{message}"
|
135
|
+
|
136
|
+
case message
|
137
|
+
when /^PING \S+$/
|
138
|
+
raw message.sub(/PING/, 'PONG')
|
139
|
+
|
140
|
+
when /^:\S+ (\d\d\d) /
|
141
|
+
number = $1.to_i
|
142
|
+
parse_event(number, :type => number, :params => $')
|
143
|
+
|
144
|
+
when /^:(\S+)!(\S+)@(\S+) PRIVMSG #(\S+) :/
|
145
|
+
parse_event(:channel, :type => :channel, :nick => $1, :user => $2, :host => $3, :channel => "##{$4}", :message => $')
|
146
|
+
|
147
|
+
when /^:(\S+)!(\S+)@(\S+) PRIVMSG \S+ :/
|
148
|
+
parse_event(:query, :type => :query, :nick => $1, :user => $2, :host => $3, :message => $')
|
149
|
+
|
150
|
+
when /^:(\S+)!(\S+)@(\S+) JOIN :*(\S+)$/
|
151
|
+
parse_event(:join, :type => :join, :nick => $1, :user => $2, :host => $3, :channel => $4)
|
152
|
+
|
153
|
+
when /^:(\S+)!(\S+)@(\S+) PART (\S+)/
|
154
|
+
parse_event(:part, :type => :part, :nick => $1, :user => $2, :host => $3, :channel => $4, :message => $'.sub(/ :/, ''))
|
155
|
+
|
156
|
+
when /^:(\S+)!(\S+)@(\S+) QUIT/
|
157
|
+
parse_event(:quit, :type => :quit, :nick => $1, :user => $2, :host => $3, :message => $'.sub(/ :/, ''))
|
158
|
+
|
159
|
+
when /^:(\S+)!(\S+)@(\S+) NICK :/
|
160
|
+
parse_event(:nickchange, :type => :nickchange, :nick => $1, :user => $2, :host => $3, :new_nick => $')
|
161
|
+
|
162
|
+
when /^:(\S+)!(\S+)@(\S+) KICK (\S+) (\S+) :/
|
163
|
+
parse_event(:kick, :type => :kick, :nick => $1, :user => $2, :host => $3, :channel => $4, :victim => $5, :reason => $')
|
164
|
+
|
165
|
+
when /^:(\S+)!(\S+)@(\S+) TOPIC (\S+) :/
|
166
|
+
parse_event(:topic, :type => :topic, :nick => $1, :user => $2, :host => $3, :channel => $4, :topic => $')
|
167
|
+
end
|
168
|
+
|
169
|
+
@observer_queues.each do |queue, regexps|
|
170
|
+
regexps.each do |regexp|
|
171
|
+
if message =~ regexp
|
172
|
+
queue << message
|
173
|
+
end
|
174
|
+
end
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
# process callbacks with its begin; rescue; end
|
179
|
+
def process_callbacks(event_type, event_data)
|
180
|
+
@callbacks[event_type].each do |callback|
|
181
|
+
EM.defer(
|
182
|
+
Proc.new do
|
183
|
+
begin
|
184
|
+
stop_running = false
|
185
|
+
|
186
|
+
# before filters (specific filters first, then :all)
|
187
|
+
(@before_filters[event_type] + @before_filters[:all]).each do |filter|
|
188
|
+
if filter.call(event_type, event_data) == false
|
189
|
+
stop_running = true
|
190
|
+
break
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
unless stop_running
|
195
|
+
# handling
|
196
|
+
callback.call(event_type, event_data)
|
197
|
+
|
198
|
+
# after filters (specific filters first, then :all)
|
199
|
+
(@after_filters[event_type] + @after_filters[:all]).each do |filter|
|
200
|
+
filter.call(event_type, event_data)
|
201
|
+
end
|
202
|
+
end
|
203
|
+
rescue => e
|
204
|
+
@error_logger.error(e.message, *e.backtrace)
|
205
|
+
@console_logger.error(e.message, *e.backtrace)
|
206
|
+
end
|
207
|
+
end
|
208
|
+
)
|
209
|
+
end
|
210
|
+
end
|
211
|
+
|
212
|
+
def before_filter(event_types = :all, match = //, &block)
|
213
|
+
filter(@before_filters, event_types, match, block)
|
214
|
+
end
|
215
|
+
|
216
|
+
def after_filter(event_types = :all, match = //, &block)
|
217
|
+
filter(@after_filters, event_types, match, block)
|
218
|
+
end
|
219
|
+
|
220
|
+
private
|
221
|
+
|
222
|
+
# parses incoming traffic (types)
|
223
|
+
def parse_event(event_type, event_data = {})
|
224
|
+
if ((event_type == 376) || (event_type == 422)) && !@connected
|
225
|
+
@connected = true
|
226
|
+
process_callbacks(:connect, event_data)
|
227
|
+
end
|
228
|
+
|
229
|
+
process_callbacks(event_type, event_data)
|
230
|
+
end
|
231
|
+
|
232
|
+
def filter(filter_type, event_types = :all, match = //, block)
|
233
|
+
if event_types.is_a?(Array)
|
234
|
+
event_types.each do |event_type|
|
235
|
+
filter_type[event_type] << Filter.new(event_type, match, block)
|
236
|
+
end
|
237
|
+
elsif
|
238
|
+
filter_type[event_types] << Filter.new(event_types, match, block)
|
239
|
+
end
|
240
|
+
end
|
241
|
+
end
|
242
|
+
end
|