isaac 0.2.5 → 0.2.6
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/CHANGES +43 -0
- data/README.rdoc +41 -1
- data/isaac.gemspec +9 -6
- data/lib/isaac.rb +4 -210
- data/lib/isaac/bot.rb +338 -0
- metadata +5 -4
- data/LICENSE +0 -6
data/CHANGES
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
= master / unreleasedd
|
2
|
+
|
3
|
+
* Require 'isaac/bot' to avoid top level bot/methods [Julian Langschaedel]
|
4
|
+
|
5
|
+
* Add action(), kick() and mode() [Danny Tatom]
|
6
|
+
|
7
|
+
* SSL support - configure {|c| c.ssl = true} [postmodern]
|
8
|
+
|
9
|
+
* on :join/:part-events [postmodern]
|
10
|
+
|
11
|
+
= 0.2.5 / 2009-04-25
|
12
|
+
|
13
|
+
* Comply to the RFC - lines end with \r\n, not just \n.
|
14
|
+
|
15
|
+
= 0.2.4 / 2009-04-01
|
16
|
+
|
17
|
+
* Bug fixes.
|
18
|
+
|
19
|
+
= 0.2.3 / 2009-04-01
|
20
|
+
|
21
|
+
* Pass regular expression groups to block parameters.
|
22
|
+
|
23
|
+
* Internal refactoring of queue and tests.
|
24
|
+
|
25
|
+
= 0.2.2 / 2009-02-23
|
26
|
+
|
27
|
+
* Irrelevant.
|
28
|
+
|
29
|
+
= 0.2.1 / 2009-02-23
|
30
|
+
|
31
|
+
* config() has been renamed to configure().
|
32
|
+
|
33
|
+
* part() and topic() commands added.
|
34
|
+
|
35
|
+
* Comply to the RFC and wait for 001-004 messages until sending commands.
|
36
|
+
|
37
|
+
* Respond to CTCP action with the content of configure {|c| c.version = "" }.
|
38
|
+
|
39
|
+
* Improved documentation.
|
40
|
+
|
41
|
+
= 0.2.0 / 2009-02-23
|
42
|
+
|
43
|
+
* This is a complete rewrite of Isaac.
|
data/README.rdoc
CHANGED
@@ -74,8 +74,48 @@ Errors, as specified by RFC 1459, can be reacted upon as well. If you e.g. try t
|
|
74
74
|
|
75
75
|
Available variables: +nick+ and +channel+.
|
76
76
|
|
77
|
+
=== Non-top level bots
|
78
|
+
You might not want to pollute the top-level namespace with Isaac
|
79
|
+
helpers, or you want to define multiple bots. This can be done
|
80
|
+
easily, by requiring `isaac/bot` instead of `isaac`:
|
81
|
+
|
82
|
+
require 'isaac/bot'
|
83
|
+
|
84
|
+
bot = Isaac::Bot.new do
|
85
|
+
configure do
|
86
|
+
…
|
87
|
+
end
|
88
|
+
|
89
|
+
on :channel do
|
90
|
+
…
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
bot.start
|
95
|
+
|
77
96
|
== Contribute
|
78
97
|
The source is hosted at GitHub: http://github.com/ichverstehe/isaac
|
79
98
|
|
80
99
|
== License
|
81
|
-
|
100
|
+
Copyright (c) 2009 Harry Vangberg <harry@vangberg.name>
|
101
|
+
|
102
|
+
Permission is hereby granted, free of charge, to any person
|
103
|
+
obtaining a copy of this software and associated documentation
|
104
|
+
files (the "Software"), to deal in the Software without
|
105
|
+
restriction, including without limitation the rights to use,
|
106
|
+
copy, modify, merge, publish, distribute, sublicense, and/or sell
|
107
|
+
copies of the Software, and to permit persons to whom the
|
108
|
+
Software is furnished to do so, subject to the following
|
109
|
+
conditions:
|
110
|
+
|
111
|
+
The above copyright notice and this permission notice shall be
|
112
|
+
included in all copies or substantial portions of the Software.
|
113
|
+
|
114
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
115
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
116
|
+
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
117
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
118
|
+
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
119
|
+
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
120
|
+
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
121
|
+
OTHER DEALINGS IN THE SOFTWARE.
|
data/isaac.gemspec
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
Gem::Specification.new do |s|
|
2
2
|
s.name = "isaac"
|
3
|
-
s.version = "0.2.
|
4
|
-
s.date = "2009-
|
3
|
+
s.version = "0.2.6"
|
4
|
+
s.date = "2009-12-21"
|
5
5
|
s.summary = "The smallish DSL for writing IRC bots"
|
6
6
|
s.email = "harry@vangberg.name"
|
7
7
|
s.homepage = "http://github.com/ichverstehe/isaac"
|
@@ -9,11 +9,14 @@ Gem::Specification.new do |s|
|
|
9
9
|
s.rubyforge_project = "isaac"
|
10
10
|
s.has_rdoc = true
|
11
11
|
s.authors = ["Harry Vangberg"]
|
12
|
-
s.files = [
|
13
|
-
"
|
12
|
+
s.files = [
|
13
|
+
"README.rdoc",
|
14
|
+
"CHANGES",
|
14
15
|
"isaac.gemspec",
|
15
|
-
"lib/isaac.rb"
|
16
|
+
"lib/isaac.rb",
|
17
|
+
"lib/isaac/bot.rb"
|
18
|
+
]
|
16
19
|
s.rdoc_options = ["--main", "README.rdoc"]
|
17
|
-
s.extra_rdoc_files = ["
|
20
|
+
s.extra_rdoc_files = ["CHANGES", "README.rdoc"]
|
18
21
|
end
|
19
22
|
|
data/lib/isaac.rb
CHANGED
@@ -1,217 +1,11 @@
|
|
1
|
-
require '
|
1
|
+
require 'isaac/bot'
|
2
2
|
|
3
|
-
|
4
|
-
VERSION = '0.2.1'
|
5
|
-
|
6
|
-
Config = Struct.new(:server, :port, :password, :nick, :realname, :version, :environment, :verbose)
|
7
|
-
|
8
|
-
def self.bot
|
9
|
-
@bot ||= Bot.new
|
10
|
-
end
|
11
|
-
|
12
|
-
class Bot
|
13
|
-
attr_accessor :config, :irc, :nick, :channel, :message, :userhost, :match,
|
14
|
-
:error
|
15
|
-
|
16
|
-
def initialize(&b)
|
17
|
-
@events = {}
|
18
|
-
@config = Config.new("localhost", 6667, nil, "isaac", "Isaac", 'isaac', :production, false)
|
19
|
-
|
20
|
-
instance_eval(&b) if block_given?
|
21
|
-
end
|
22
|
-
|
23
|
-
def configure(&b)
|
24
|
-
b.call(@config)
|
25
|
-
end
|
26
|
-
|
27
|
-
def on(event, match=//, &block)
|
28
|
-
match = match.to_s if match.is_a? Integer
|
29
|
-
(@events[event] ||= []) << [Regexp.new(match), block]
|
30
|
-
end
|
31
|
-
|
32
|
-
def helpers(&b)
|
33
|
-
instance_eval(&b)
|
34
|
-
end
|
35
|
-
|
36
|
-
def halt
|
37
|
-
throw :halt
|
38
|
-
end
|
39
|
-
|
40
|
-
def raw(command)
|
41
|
-
@irc.message(command)
|
42
|
-
end
|
43
|
-
|
44
|
-
def msg(recipient, text)
|
45
|
-
raw("PRIVMSG #{recipient} :#{text}")
|
46
|
-
end
|
47
|
-
|
48
|
-
def join(*channels)
|
49
|
-
channels.each {|channel| raw("JOIN #{channel}")}
|
50
|
-
end
|
51
|
-
|
52
|
-
def part(*channels)
|
53
|
-
channels.each {|channel| raw("PART #{channel}")}
|
54
|
-
end
|
55
|
-
|
56
|
-
def topic(channel, text)
|
57
|
-
raw("TOPIC #{channel} :#{text}")
|
58
|
-
end
|
59
|
-
|
60
|
-
def start
|
61
|
-
puts "Connecting to #{@config.server}:#{@config.port}" unless @config.environment == :test
|
62
|
-
@irc = IRC.new(self, @config)
|
63
|
-
@irc.connect
|
64
|
-
end
|
65
|
-
|
66
|
-
def dispatch(event, env={})
|
67
|
-
self.nick, self.userhost, self.channel, self.error =
|
68
|
-
env[:nick], env[:userhost], env[:channel], env[:error]
|
69
|
-
self.message = env[:message] || ""
|
70
|
-
|
71
|
-
if handler = find(event, message)
|
72
|
-
regexp, block = *handler
|
73
|
-
self.match = message.match(regexp).captures
|
74
|
-
invoke block
|
75
|
-
end
|
76
|
-
end
|
77
|
-
|
78
|
-
private
|
79
|
-
def find(type, message)
|
80
|
-
if events = @events[type]
|
81
|
-
events.detect {|regexp,_| message.match(regexp)}
|
82
|
-
end
|
83
|
-
end
|
84
|
-
|
85
|
-
def invoke(block)
|
86
|
-
mc = class << self; self; end
|
87
|
-
mc.send :define_method, :__isaac_event_handler, &block
|
88
|
-
|
89
|
-
bargs = case block.arity <=> 0
|
90
|
-
when -1; match
|
91
|
-
when 0; []
|
92
|
-
when 1; match[0..block.arity-1]
|
93
|
-
end
|
94
|
-
|
95
|
-
catch(:halt) { __isaac_event_handler(*bargs) }
|
96
|
-
end
|
97
|
-
end
|
98
|
-
|
99
|
-
class IRC
|
100
|
-
def initialize(bot, config)
|
101
|
-
@bot, @config = bot, config
|
102
|
-
@transfered = 0
|
103
|
-
@registration = []
|
104
|
-
end
|
105
|
-
|
106
|
-
def connect
|
107
|
-
@socket = TCPSocket.open(@config.server, @config.port)
|
108
|
-
@queue = Queue.new(@socket, @bot.config.server)
|
109
|
-
message "PASS #{@config.password}" if @config.password
|
110
|
-
message "NICK #{@config.nick}"
|
111
|
-
message "USER #{@config.nick} 0 * :#{@config.realname}"
|
112
|
-
@queue.lock
|
113
|
-
|
114
|
-
while line = @socket.gets
|
115
|
-
parse line
|
116
|
-
end
|
117
|
-
end
|
118
|
-
|
119
|
-
def parse(input)
|
120
|
-
puts "<< #{input}" if @bot.config.verbose
|
121
|
-
case input.chomp
|
122
|
-
when /(^:\S+ )?00([1-4])/
|
123
|
-
@registration << $2.to_i
|
124
|
-
if registered?
|
125
|
-
@queue.unlock
|
126
|
-
@bot.dispatch(:connect)
|
127
|
-
end
|
128
|
-
when /(^:(\S+)!\S+ )?PRIVMSG \S+ :?\001VERSION\001/
|
129
|
-
message "NOTICE #{$2} :\001VERSION #{@bot.config.version}\001"
|
130
|
-
when /^PING (\S+)/
|
131
|
-
@queue.unlock
|
132
|
-
message "PONG #{$1}"
|
133
|
-
when /(^:(\S+)!(\S+) )?PRIVMSG (\S+) :?(.*)/
|
134
|
-
env = { :nick => $2, :userhost => $3, :channel => $4, :message => $5 }
|
135
|
-
type = env[:channel].match(/^#/) ? :channel : :private
|
136
|
-
@bot.dispatch(type, env)
|
137
|
-
when /(^:\S+ )?([4-5]\d\d) \S+ (\S+)/
|
138
|
-
env = {:error => $2.to_i, :message => $2, :nick => $3, :channel => $3}
|
139
|
-
@bot.dispatch(:error, env)
|
140
|
-
when /(^:\S+ )?PONG/
|
141
|
-
@queue.unlock
|
142
|
-
end
|
143
|
-
end
|
144
|
-
|
145
|
-
def registered?
|
146
|
-
([1,2,3,4] - @registration).empty?
|
147
|
-
end
|
148
|
-
|
149
|
-
def message(msg)
|
150
|
-
@queue << msg
|
151
|
-
end
|
152
|
-
end
|
153
|
-
|
154
|
-
class Queue
|
155
|
-
def initialize(socket, server)
|
156
|
-
# We need server for pinging us out of an excess flood
|
157
|
-
@socket, @server = socket, server
|
158
|
-
@queue, @lock, @transfered = [], false, 0
|
159
|
-
end
|
160
|
-
|
161
|
-
def lock
|
162
|
-
@lock = true
|
163
|
-
end
|
164
|
-
|
165
|
-
def unlock
|
166
|
-
@lock, @transfered = false, 0
|
167
|
-
invoke
|
168
|
-
end
|
169
|
-
|
170
|
-
def <<(message)
|
171
|
-
@queue << message
|
172
|
-
invoke
|
173
|
-
end
|
174
|
-
|
175
|
-
private
|
176
|
-
def message_to_send?
|
177
|
-
!@lock && !@queue.empty?
|
178
|
-
end
|
179
|
-
|
180
|
-
def transfered_after_next_send
|
181
|
-
@transfered + @queue.first.size + 2 # the 2 is for \r\n
|
182
|
-
end
|
183
|
-
|
184
|
-
def exceed_limit?
|
185
|
-
transfered_after_next_send > 1472
|
186
|
-
end
|
187
|
-
|
188
|
-
def lock_and_ping
|
189
|
-
lock
|
190
|
-
@socket.print "PING :#{@server}\r\n"
|
191
|
-
end
|
192
|
-
|
193
|
-
def next_message
|
194
|
-
@queue.shift.to_s.chomp + "\r\n"
|
195
|
-
end
|
196
|
-
|
197
|
-
def invoke
|
198
|
-
while message_to_send?
|
199
|
-
if exceed_limit?
|
200
|
-
lock_and_ping; break
|
201
|
-
else
|
202
|
-
@transfered = transfered_after_next_send
|
203
|
-
@socket.print next_message
|
204
|
-
# puts ">> #{msg}" if @bot.config.verbose
|
205
|
-
end
|
206
|
-
end
|
207
|
-
end
|
208
|
-
end
|
209
|
-
end
|
3
|
+
$bot = Isaac::Bot.new
|
210
4
|
|
211
5
|
%w(configure helpers on).each do |method|
|
212
6
|
eval(<<-EOF)
|
213
7
|
def #{method}(*args, &block)
|
214
|
-
|
8
|
+
$bot.#{method}(*args, &block)
|
215
9
|
end
|
216
10
|
EOF
|
217
11
|
end
|
@@ -219,6 +13,6 @@ end
|
|
219
13
|
at_exit do
|
220
14
|
unless defined?(Test::Unit)
|
221
15
|
raise $! if $!
|
222
|
-
|
16
|
+
$bot.start
|
223
17
|
end
|
224
18
|
end
|
data/lib/isaac/bot.rb
ADDED
@@ -0,0 +1,338 @@
|
|
1
|
+
require 'socket'
|
2
|
+
|
3
|
+
module Isaac
|
4
|
+
VERSION = '0.2.1'
|
5
|
+
|
6
|
+
Config = Struct.new(:server, :port, :ssl, :password, :nick, :realname, :version, :environment, :verbose)
|
7
|
+
|
8
|
+
class Bot
|
9
|
+
attr_accessor :config, :irc, :nick, :channel, :message, :user, :host, :match,
|
10
|
+
:error
|
11
|
+
|
12
|
+
def initialize(&b)
|
13
|
+
@events = {}
|
14
|
+
@config = Config.new("localhost", 6667, false, nil, "isaac", "Isaac", 'isaac', :production, false)
|
15
|
+
|
16
|
+
instance_eval(&b) if block_given?
|
17
|
+
end
|
18
|
+
|
19
|
+
def configure(&b)
|
20
|
+
b.call(@config)
|
21
|
+
end
|
22
|
+
|
23
|
+
def on(event, match=//, &block)
|
24
|
+
match = match.to_s if match.is_a? Integer
|
25
|
+
(@events[event] ||= []) << [Regexp.new(match), block]
|
26
|
+
end
|
27
|
+
|
28
|
+
def helpers(&b)
|
29
|
+
instance_eval(&b)
|
30
|
+
end
|
31
|
+
|
32
|
+
def halt
|
33
|
+
throw :halt
|
34
|
+
end
|
35
|
+
|
36
|
+
def raw(command)
|
37
|
+
@irc.message(command)
|
38
|
+
end
|
39
|
+
|
40
|
+
def msg(recipient, text)
|
41
|
+
raw("PRIVMSG #{recipient} :#{text}")
|
42
|
+
end
|
43
|
+
|
44
|
+
def action(recipient, text)
|
45
|
+
raw("PRIVMSG #{recipient} :\001ACTION #{text}\001")
|
46
|
+
end
|
47
|
+
|
48
|
+
def join(*channels)
|
49
|
+
channels.each {|channel| raw("JOIN #{channel}")}
|
50
|
+
end
|
51
|
+
|
52
|
+
def part(*channels)
|
53
|
+
channels.each {|channel| raw("PART #{channel}")}
|
54
|
+
end
|
55
|
+
|
56
|
+
def topic(channel, text)
|
57
|
+
raw("TOPIC #{channel} :#{text}")
|
58
|
+
end
|
59
|
+
|
60
|
+
def mode(channel, option)
|
61
|
+
raw("MODE #{channel} #{option}")
|
62
|
+
end
|
63
|
+
|
64
|
+
def kick(channel, user, reason=nil)
|
65
|
+
raw("KICK #{channel} #{user} :#{reason}")
|
66
|
+
end
|
67
|
+
|
68
|
+
def quit(message=nil)
|
69
|
+
command = message ? "QUIT :#{message}" : "QUIT"
|
70
|
+
raw command
|
71
|
+
end
|
72
|
+
|
73
|
+
def start
|
74
|
+
puts "Connecting to #{@config.server}:#{@config.port}" unless @config.environment == :test
|
75
|
+
@irc = IRC.new(self, @config)
|
76
|
+
@irc.connect
|
77
|
+
end
|
78
|
+
|
79
|
+
def message
|
80
|
+
@message ||= ""
|
81
|
+
end
|
82
|
+
|
83
|
+
def dispatch(event, msg=nil)
|
84
|
+
if msg
|
85
|
+
@nick, @user, @host, @channel, @error, @message =
|
86
|
+
msg.nick, msg.user, msg.host, msg.channel, msg.error, msg.message
|
87
|
+
end
|
88
|
+
|
89
|
+
if handler = find(event, message)
|
90
|
+
regexp, block = *handler
|
91
|
+
self.match = message.match(regexp).captures
|
92
|
+
invoke block
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
private
|
97
|
+
def find(type, message)
|
98
|
+
if events = @events[type]
|
99
|
+
events.detect {|regexp,_| message.match(regexp)}
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
def invoke(block)
|
104
|
+
mc = class << self; self; end
|
105
|
+
mc.send :define_method, :__isaac_event_handler, &block
|
106
|
+
|
107
|
+
# -1 splat arg, send everything
|
108
|
+
# 0 no args, send nothing
|
109
|
+
# 1 defined number of args, send only those
|
110
|
+
bargs = case block.arity <=> 0
|
111
|
+
when -1; match
|
112
|
+
when 0; []
|
113
|
+
when 1; match[0..block.arity-1]
|
114
|
+
end
|
115
|
+
|
116
|
+
catch(:halt) { __isaac_event_handler(*bargs) }
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
class IRC
|
121
|
+
def initialize(bot, config)
|
122
|
+
@bot, @config = bot, config
|
123
|
+
@transfered = 0
|
124
|
+
@registration = []
|
125
|
+
end
|
126
|
+
|
127
|
+
def connect
|
128
|
+
tcp_socket = TCPSocket.open(@config.server, @config.port)
|
129
|
+
|
130
|
+
if @config.ssl
|
131
|
+
begin
|
132
|
+
require 'openssl'
|
133
|
+
rescue ::LoadError
|
134
|
+
raise(RuntimeError,"unable to require 'openssl'",caller)
|
135
|
+
end
|
136
|
+
|
137
|
+
ssl_context = OpenSSL::SSL::SSLContext.new
|
138
|
+
ssl_context.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
139
|
+
|
140
|
+
unless @config.environment == :test
|
141
|
+
puts "Using SSL with #{@config.server}:#{@config.port}"
|
142
|
+
end
|
143
|
+
|
144
|
+
@socket = OpenSSL::SSL::SSLSocket.new(tcp_socket, ssl_context)
|
145
|
+
@socket.sync = true
|
146
|
+
@socket.connect
|
147
|
+
else
|
148
|
+
@socket = tcp_socket
|
149
|
+
end
|
150
|
+
|
151
|
+
@queue = Queue.new(@socket, @bot.config.server)
|
152
|
+
message "PASS #{@config.password}" if @config.password
|
153
|
+
message "NICK #{@config.nick}"
|
154
|
+
message "USER #{@config.nick} 0 * :#{@config.realname}"
|
155
|
+
@queue.lock
|
156
|
+
|
157
|
+
while line = @socket.gets
|
158
|
+
parse line
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
def parse(input)
|
163
|
+
puts "<< #{input}" if @bot.config.verbose
|
164
|
+
msg = Message.new(input)
|
165
|
+
|
166
|
+
if ("001".."004").include? msg.command
|
167
|
+
@registration << msg.command
|
168
|
+
if registered?
|
169
|
+
@queue.unlock
|
170
|
+
@bot.dispatch(:connect)
|
171
|
+
end
|
172
|
+
elsif msg.command == "PRIVMSG"
|
173
|
+
if msg.params.last == "\001VERSION\001"
|
174
|
+
message "NOTICE #{msg.nick} :\001VERSION #{@bot.config.version}\001"
|
175
|
+
end
|
176
|
+
|
177
|
+
type = msg.channel? ? :channel : :private
|
178
|
+
@bot.dispatch(type, msg)
|
179
|
+
elsif msg.error?
|
180
|
+
@bot.dispatch(:error, msg)
|
181
|
+
elsif msg.command == "PING"
|
182
|
+
@queue.unlock
|
183
|
+
message "PONG :#{msg.params.first}"
|
184
|
+
elsif msg.command == "PONG"
|
185
|
+
@queue.unlock
|
186
|
+
else
|
187
|
+
event = msg.command.downcase.to_sym
|
188
|
+
@bot.dispatch(event, msg)
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
def registered?
|
193
|
+
(("001".."004").to_a - @registration).empty?
|
194
|
+
end
|
195
|
+
|
196
|
+
def message(msg)
|
197
|
+
@queue << msg
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
201
|
+
class Message
|
202
|
+
attr_accessor :raw, :prefix, :command, :params
|
203
|
+
|
204
|
+
def initialize(msg=nil)
|
205
|
+
@raw = msg
|
206
|
+
parse if msg
|
207
|
+
end
|
208
|
+
|
209
|
+
def numeric_reply?
|
210
|
+
@numeric_reply ||= !!@command.match(/^\d\d\d$/)
|
211
|
+
end
|
212
|
+
|
213
|
+
def parse
|
214
|
+
match = @raw.match(/(^:(\S+) )?(\S+)(.*)/)
|
215
|
+
_, @prefix, @command, raw_params = match.captures
|
216
|
+
|
217
|
+
raw_params.strip!
|
218
|
+
if match = raw_params.match(/:(.*)/)
|
219
|
+
@params = match.pre_match.split(" ")
|
220
|
+
@params << match[1]
|
221
|
+
else
|
222
|
+
@params = raw_params.split(" ")
|
223
|
+
end
|
224
|
+
end
|
225
|
+
|
226
|
+
def nick
|
227
|
+
return unless @prefix
|
228
|
+
@nick ||= @prefix[/^(\S+)!/, 1]
|
229
|
+
end
|
230
|
+
|
231
|
+
def user
|
232
|
+
return unless @prefix
|
233
|
+
@user ||= @prefix[/^\S+!(\S+)@/, 1]
|
234
|
+
end
|
235
|
+
|
236
|
+
def host
|
237
|
+
return unless @prefix
|
238
|
+
@host ||= @prefix[/@(\S+)$/, 1]
|
239
|
+
end
|
240
|
+
|
241
|
+
def server
|
242
|
+
return unless @prefix
|
243
|
+
return if @prefix.match(/[@!]/)
|
244
|
+
@server ||= @prefix[/^(\S+)/, 1]
|
245
|
+
end
|
246
|
+
|
247
|
+
def error?
|
248
|
+
!!error
|
249
|
+
end
|
250
|
+
|
251
|
+
def error
|
252
|
+
return @error if @error
|
253
|
+
@error = command.to_i if numeric_reply? && command[/[45]\d\d/]
|
254
|
+
end
|
255
|
+
|
256
|
+
def channel?
|
257
|
+
!!channel
|
258
|
+
end
|
259
|
+
|
260
|
+
def channel
|
261
|
+
return @channel if @channel
|
262
|
+
if regular_command? and params.first.start_with?("#")
|
263
|
+
@channel = params.first
|
264
|
+
end
|
265
|
+
end
|
266
|
+
|
267
|
+
def message
|
268
|
+
return @message if @message
|
269
|
+
if error?
|
270
|
+
@message = error.to_s
|
271
|
+
elsif regular_command?
|
272
|
+
@message = params.last
|
273
|
+
end
|
274
|
+
end
|
275
|
+
|
276
|
+
private
|
277
|
+
# This is a late night hack. Fix.
|
278
|
+
def regular_command?
|
279
|
+
%w(PRIVMSG JOIN PART QUIT).include? command
|
280
|
+
end
|
281
|
+
end
|
282
|
+
|
283
|
+
class Queue
|
284
|
+
def initialize(socket, server)
|
285
|
+
# We need server for pinging us out of an excess flood
|
286
|
+
@socket, @server = socket, server
|
287
|
+
@queue, @lock, @transfered = [], false, 0
|
288
|
+
end
|
289
|
+
|
290
|
+
def lock
|
291
|
+
@lock = true
|
292
|
+
end
|
293
|
+
|
294
|
+
def unlock
|
295
|
+
@lock, @transfered = false, 0
|
296
|
+
invoke
|
297
|
+
end
|
298
|
+
|
299
|
+
def <<(message)
|
300
|
+
@queue << message
|
301
|
+
invoke
|
302
|
+
end
|
303
|
+
|
304
|
+
private
|
305
|
+
def message_to_send?
|
306
|
+
!@lock && !@queue.empty?
|
307
|
+
end
|
308
|
+
|
309
|
+
def transfered_after_next_send
|
310
|
+
@transfered + @queue.first.size + 2 # the 2 is for \r\n
|
311
|
+
end
|
312
|
+
|
313
|
+
def exceed_limit?
|
314
|
+
transfered_after_next_send > 1472
|
315
|
+
end
|
316
|
+
|
317
|
+
def lock_and_ping
|
318
|
+
lock
|
319
|
+
@socket.print "PING :#{@server}\r\n"
|
320
|
+
end
|
321
|
+
|
322
|
+
def next_message
|
323
|
+
@queue.shift.to_s.chomp + "\r\n"
|
324
|
+
end
|
325
|
+
|
326
|
+
def invoke
|
327
|
+
while message_to_send?
|
328
|
+
if exceed_limit?
|
329
|
+
lock_and_ping; break
|
330
|
+
else
|
331
|
+
@transfered = transfered_after_next_send
|
332
|
+
@socket.print next_message
|
333
|
+
# puts ">> #{msg}" if @bot.config.verbose
|
334
|
+
end
|
335
|
+
end
|
336
|
+
end
|
337
|
+
end
|
338
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: isaac
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.6
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Harry Vangberg
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2009-
|
12
|
+
date: 2009-12-21 00:00:00 +01:00
|
13
13
|
default_executable:
|
14
14
|
dependencies: []
|
15
15
|
|
@@ -20,13 +20,14 @@ executables: []
|
|
20
20
|
extensions: []
|
21
21
|
|
22
22
|
extra_rdoc_files:
|
23
|
-
-
|
23
|
+
- CHANGES
|
24
24
|
- README.rdoc
|
25
25
|
files:
|
26
26
|
- README.rdoc
|
27
|
-
-
|
27
|
+
- CHANGES
|
28
28
|
- isaac.gemspec
|
29
29
|
- lib/isaac.rb
|
30
|
+
- lib/isaac/bot.rb
|
30
31
|
has_rdoc: true
|
31
32
|
homepage: http://github.com/ichverstehe/isaac
|
32
33
|
licenses: []
|
data/LICENSE
DELETED
@@ -1,6 +0,0 @@
|
|
1
|
-
------------------------------------------------------------------------------
|
2
|
-
"THE BEER-WARE LICENSE" (Revision 42):
|
3
|
-
<ichverstehe@gmail.com> wrote this file. As long as you retain this notice you
|
4
|
-
can do whatever you want with this stuff. If we meet some day, and you think
|
5
|
-
this stuff is worth it, you can buy me a beer in return.
|
6
|
-
------------------------------------------------------------------------------
|