newton 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 +22 -0
- data/README.md +189 -0
- data/Rakefile +6 -0
- data/examples/autovoice.rb +45 -0
- data/examples/echo_bot.rb +22 -0
- data/examples/excess_flood.rb +23 -0
- data/examples/memo.rb +50 -0
- data/examples/schema.rb +41 -0
- data/examples/secure_eval.rb +46 -0
- data/lib/newton/ban.rb +40 -0
- data/lib/newton/bot.rb +361 -0
- data/lib/newton/callback.rb +24 -0
- data/lib/newton/channel.rb +362 -0
- data/lib/newton/constants.rb +123 -0
- data/lib/newton/exceptions.rb +25 -0
- data/lib/newton/formatted_logger.rb +64 -0
- data/lib/newton/irc.rb +261 -0
- data/lib/newton/isupport.rb +96 -0
- data/lib/newton/mask.rb +46 -0
- data/lib/newton/message.rb +162 -0
- data/lib/newton/message_queue.rb +62 -0
- data/lib/newton/rubyext/infinity.rb +1 -0
- data/lib/newton/rubyext/module.rb +18 -0
- data/lib/newton/rubyext/queue.rb +19 -0
- data/lib/newton/rubyext/string.rb +24 -0
- data/lib/newton/syncable.rb +55 -0
- data/lib/newton/user.rb +226 -0
- data/lib/newton.rb +1 -0
- data/test/helper.rb +60 -0
- data/test/test_commands.rb +85 -0
- data/test/test_events.rb +89 -0
- data/test/test_helpers.rb +14 -0
- data/test/test_irc.rb +38 -0
- data/test/test_message.rb +117 -0
- data/test/test_parse.rb +153 -0
- data/test/test_queue.rb +49 -0
- data/test/tests.rb +9 -0
- metadata +100 -0
@@ -0,0 +1,55 @@
|
|
1
|
+
module Newton
|
2
|
+
module Syncable
|
3
|
+
# Blocks until the object is synced.
|
4
|
+
#
|
5
|
+
# @return [void]
|
6
|
+
def wait_until_synced(attr)
|
7
|
+
attr = attr.to_sym
|
8
|
+
while true
|
9
|
+
return if @synced_attributes.include?(attr)
|
10
|
+
sleep 0.1
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
# @api private
|
15
|
+
# @return [void]
|
16
|
+
def sync(attribute, value, data = false)
|
17
|
+
if data
|
18
|
+
@data[attribute] = value
|
19
|
+
else
|
20
|
+
instance_variable_set("@#{attribute}", value)
|
21
|
+
end
|
22
|
+
@synced_attributes << attribute
|
23
|
+
end
|
24
|
+
|
25
|
+
def synced?(attribute)
|
26
|
+
@synced_attributes.include?(attribute)
|
27
|
+
end
|
28
|
+
|
29
|
+
def unsync(attribute)
|
30
|
+
@synced_attributes.delete(attribute)
|
31
|
+
end
|
32
|
+
|
33
|
+
# @api private
|
34
|
+
def attr(attribute, data = false, unsync = false)
|
35
|
+
unless unsync
|
36
|
+
if @when_requesting_synced_attribute
|
37
|
+
@when_requesting_synced_attribute.call(attribute)
|
38
|
+
end
|
39
|
+
wait_until_synced(attribute)
|
40
|
+
end
|
41
|
+
|
42
|
+
if data
|
43
|
+
return @data[attribute]
|
44
|
+
else
|
45
|
+
return instance_variable_get("@#{attribute}")
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
# @api private
|
50
|
+
# @return [void]
|
51
|
+
def mark_as_synced(attribute)
|
52
|
+
@synced_attributes << attribute
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
data/lib/newton/user.rb
ADDED
@@ -0,0 +1,226 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
module Newton
|
3
|
+
class User
|
4
|
+
include Syncable
|
5
|
+
|
6
|
+
@users = {}
|
7
|
+
class << self
|
8
|
+
|
9
|
+
# @overload find_ensured(nick, bot)
|
10
|
+
# Finds or creates a user based on his nick.
|
11
|
+
#
|
12
|
+
# @param [String] nick The user's nickname
|
13
|
+
# @param [Bot] bot An instance of Bot
|
14
|
+
# @overload find_ensured(user, nick, host, bot)
|
15
|
+
# Finds or creates a user based on his nick but already
|
16
|
+
# setting user and host.
|
17
|
+
#
|
18
|
+
# @param [String] user The username
|
19
|
+
# @param [String] nick The nickname
|
20
|
+
# @param [String] host The user's hostname
|
21
|
+
# @param [Bot] bot An instance of bot
|
22
|
+
#
|
23
|
+
# @return [User]
|
24
|
+
def find_ensured(*args)
|
25
|
+
# FIXME CASEMAPPING
|
26
|
+
case args.size
|
27
|
+
when 2
|
28
|
+
nick = args.first
|
29
|
+
bargs = [args.first]
|
30
|
+
bot = args.last
|
31
|
+
when 4
|
32
|
+
nick = args[1]
|
33
|
+
bot = args.pop
|
34
|
+
bargs = args
|
35
|
+
else
|
36
|
+
raise ArgumentError
|
37
|
+
end
|
38
|
+
downcased_nick = nick.irc_downcase(bot.irc.isupport["CASEMAPPING"])
|
39
|
+
@users[downcased_nick] ||= new(*bargs, bot)
|
40
|
+
@users[downcased_nick]
|
41
|
+
end
|
42
|
+
|
43
|
+
# Finds a user.
|
44
|
+
#
|
45
|
+
# @param [String] nick nick of a user
|
46
|
+
# @return [User, nil]
|
47
|
+
def find(nick)
|
48
|
+
@users[nick]
|
49
|
+
end
|
50
|
+
|
51
|
+
# @return [Array<User>] Returns all users
|
52
|
+
def all
|
53
|
+
@users.values
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
|
58
|
+
# @return [String]
|
59
|
+
attr_accessor :nick
|
60
|
+
# @return [Bot]
|
61
|
+
attr_accessor :bot
|
62
|
+
# @return [Boolean]
|
63
|
+
attr_accessor :synced
|
64
|
+
|
65
|
+
# By default, you can use methods like User#user, User#host and
|
66
|
+
# alike – If you however fear that another thread might change
|
67
|
+
# data while you're using it and if this means a critical issue to
|
68
|
+
# your code, you can store the result of this method and work with
|
69
|
+
# that instead.
|
70
|
+
#
|
71
|
+
# @example
|
72
|
+
# on :channel do
|
73
|
+
# data = user.data
|
74
|
+
# do_something_with(data.user)
|
75
|
+
# do_something_with(data.host)
|
76
|
+
# end
|
77
|
+
# @return [Hash]
|
78
|
+
attr_accessor :data
|
79
|
+
def initialize(*args)
|
80
|
+
@data = {
|
81
|
+
:user => nil,
|
82
|
+
:host => nil,
|
83
|
+
:realname => nil,
|
84
|
+
:authname => nil,
|
85
|
+
:idle => 0,
|
86
|
+
:signed_on_at => nil,
|
87
|
+
:unknown? => false,
|
88
|
+
:channels => [],
|
89
|
+
:secure? => false,
|
90
|
+
}
|
91
|
+
case args.size
|
92
|
+
when 2
|
93
|
+
@nick, @bot = args
|
94
|
+
when 4
|
95
|
+
@data[:user], @nick, @data[:host], @bot = args
|
96
|
+
else
|
97
|
+
raise ArgumentError
|
98
|
+
end
|
99
|
+
|
100
|
+
@synced_attributes = Set.new
|
101
|
+
|
102
|
+
@when_requesting_synced_attribute = lambda {|attr|
|
103
|
+
unless @synced
|
104
|
+
unsync attr
|
105
|
+
whois
|
106
|
+
end
|
107
|
+
}
|
108
|
+
end
|
109
|
+
|
110
|
+
# Checks if the user is identified. Currently officially supports
|
111
|
+
# Quakenet and Freenode.
|
112
|
+
#
|
113
|
+
# @return [Boolean] true if the user is identified
|
114
|
+
def authed?
|
115
|
+
@data[:authname]
|
116
|
+
end
|
117
|
+
|
118
|
+
# Queries the IRC server for information on the user. This will
|
119
|
+
# set the User's state to not synced. After all information are
|
120
|
+
# received, the object will be set back to synced.
|
121
|
+
#
|
122
|
+
# @return [void]
|
123
|
+
def whois
|
124
|
+
@synced = false
|
125
|
+
@data.keys.each do |attr|
|
126
|
+
unsync attr
|
127
|
+
end
|
128
|
+
@bot.raw "WHOIS #@nick #@nick"
|
129
|
+
end
|
130
|
+
|
131
|
+
# Send a message to the user.
|
132
|
+
#
|
133
|
+
# @param [String] message the message
|
134
|
+
# @return [void]
|
135
|
+
def send(message)
|
136
|
+
@bot.msg(@nick, message)
|
137
|
+
end
|
138
|
+
alias_method :privmsg, :send
|
139
|
+
|
140
|
+
# Send a CTCP to the user.
|
141
|
+
#
|
142
|
+
# @param [String] message the ctcp message
|
143
|
+
# @return [void]
|
144
|
+
def ctcp(message)
|
145
|
+
send "\001#{message}\001"
|
146
|
+
end
|
147
|
+
|
148
|
+
# Send an action (/me) to the user.
|
149
|
+
#
|
150
|
+
# @param [String] message the message
|
151
|
+
# @return [void]
|
152
|
+
def action(message)
|
153
|
+
@bot.action(@name, message)
|
154
|
+
end
|
155
|
+
|
156
|
+
# @return [String]
|
157
|
+
def to_s
|
158
|
+
@nick
|
159
|
+
end
|
160
|
+
|
161
|
+
# @return [String]
|
162
|
+
def inspect
|
163
|
+
"#<User nick=#{@nick.inspect}>"
|
164
|
+
end
|
165
|
+
|
166
|
+
# Generates a mask for the user.
|
167
|
+
#
|
168
|
+
# @param [String] s a pattern for generating the mask. %n =
|
169
|
+
# nickname – %u = username – %h = host – %r = realname – %a =
|
170
|
+
# authname
|
171
|
+
#
|
172
|
+
# @return [Mask]
|
173
|
+
def mask(s = "%n!%u@%h")
|
174
|
+
s = s.gsub(/%(.)/) {
|
175
|
+
case $1
|
176
|
+
when "n"
|
177
|
+
@nick
|
178
|
+
when "u"
|
179
|
+
self.user
|
180
|
+
when "h"
|
181
|
+
self.host
|
182
|
+
when "r"
|
183
|
+
self.realname
|
184
|
+
when "a"
|
185
|
+
self.authname
|
186
|
+
end
|
187
|
+
}
|
188
|
+
|
189
|
+
Mask.new(s)
|
190
|
+
end
|
191
|
+
|
192
|
+
# Provides synced access to user attributes.
|
193
|
+
def method_missing(m, *args)
|
194
|
+
if m.to_s =~ /^(.+)_unsynced$/
|
195
|
+
m = $1.to_sym
|
196
|
+
unsync = true
|
197
|
+
end
|
198
|
+
|
199
|
+
if @data.has_key?(m)
|
200
|
+
attr(m, true, unsync = false)
|
201
|
+
else
|
202
|
+
super
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
# @return [Boolean]
|
207
|
+
def ==(other)
|
208
|
+
return case other
|
209
|
+
when self.class
|
210
|
+
@nick == other.nick
|
211
|
+
when String
|
212
|
+
self.to_s == other
|
213
|
+
when Bot
|
214
|
+
self.nick == other.config.nick
|
215
|
+
else
|
216
|
+
false
|
217
|
+
end
|
218
|
+
end
|
219
|
+
alias_method :eql?, "=="
|
220
|
+
|
221
|
+
# @return [Fixnum]
|
222
|
+
def hash
|
223
|
+
@nick.hash
|
224
|
+
end
|
225
|
+
end
|
226
|
+
end
|
data/lib/newton.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'newton/bot'
|
data/test/helper.rb
ADDED
@@ -0,0 +1,60 @@
|
|
1
|
+
$LOAD_PATH.unshift 'lib'
|
2
|
+
require 'newton'
|
3
|
+
require 'rubygems'
|
4
|
+
require 'test/unit'
|
5
|
+
require 'contest'
|
6
|
+
require 'rr'
|
7
|
+
require 'timeout'
|
8
|
+
begin
|
9
|
+
require 'ruby-debug'
|
10
|
+
rescue LoadError; end
|
11
|
+
|
12
|
+
module Test::Unit::Assertions
|
13
|
+
def assert_empty_buffer(io)
|
14
|
+
assert_raise(Errno::EAGAIN) { io.read_nonblock 1 }
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
class MockSocket
|
19
|
+
def self.pipe
|
20
|
+
socket1, socket2 = new, new
|
21
|
+
socket1.in, socket2.out = IO.pipe
|
22
|
+
socket2.in, socket1.out = IO.pipe
|
23
|
+
[socket1, socket2]
|
24
|
+
end
|
25
|
+
|
26
|
+
attr_accessor :in, :out
|
27
|
+
def gets()
|
28
|
+
Timeout.timeout(1) {@in.gets}
|
29
|
+
end
|
30
|
+
def puts(m) @out.puts(m) end
|
31
|
+
def print(m) @out.print(m) end
|
32
|
+
def eof?() @in.eof? end
|
33
|
+
def empty?
|
34
|
+
begin
|
35
|
+
@in.read_nonblock(1)
|
36
|
+
false
|
37
|
+
rescue Errno::EAGAIN
|
38
|
+
true
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
class Test::Unit::TestCase
|
44
|
+
include RR::Adapters::TestUnit
|
45
|
+
|
46
|
+
def mock_bot(&b)
|
47
|
+
@socket, @server = MockSocket.pipe
|
48
|
+
stub(TCPSocket).open(anything, anything) {@socket}
|
49
|
+
bot = Newton::Bot.new(&b)
|
50
|
+
bot.config.environment = :test
|
51
|
+
Thread.start { bot.start }
|
52
|
+
bot
|
53
|
+
end
|
54
|
+
|
55
|
+
def bot_is_connected
|
56
|
+
assert_equal "NICK isaac\r\n", @server.gets
|
57
|
+
assert_equal "USER isaac 0 * :Isaac\r\n", @server.gets
|
58
|
+
1.upto(4) {|i| @server.print ":localhost 00#{i}\r\n"}
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,85 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'helper')
|
2
|
+
|
3
|
+
class TestCommands < Test::Unit::TestCase
|
4
|
+
test "raw messages can be send" do
|
5
|
+
bot = mock_bot {}
|
6
|
+
bot_is_connected
|
7
|
+
|
8
|
+
bot.raw "PRIVMSG foo :bar baz"
|
9
|
+
assert_equal "PRIVMSG foo :bar baz\r\n", @server.gets
|
10
|
+
end
|
11
|
+
|
12
|
+
test "messages are sent to recipient" do
|
13
|
+
bot = mock_bot {}
|
14
|
+
bot_is_connected
|
15
|
+
|
16
|
+
bot.msg "foo", "bar baz"
|
17
|
+
assert_equal "PRIVMSG foo :bar baz\r\n", @server.gets
|
18
|
+
end
|
19
|
+
|
20
|
+
test "actions are sent to recipient" do
|
21
|
+
bot = mock_bot {}
|
22
|
+
bot_is_connected
|
23
|
+
|
24
|
+
bot.action "foo", "bar"
|
25
|
+
assert_equal "PRIVMSG foo :\001ACTION bar\001\r\n", @server.gets
|
26
|
+
end
|
27
|
+
|
28
|
+
test "channels are joined" do
|
29
|
+
bot = mock_bot {}
|
30
|
+
bot_is_connected
|
31
|
+
|
32
|
+
bot.join "#foo", "#bar"
|
33
|
+
assert_equal "JOIN #foo\r\n", @server.gets
|
34
|
+
assert_equal "JOIN #bar\r\n", @server.gets
|
35
|
+
end
|
36
|
+
|
37
|
+
test "channels are parted" do
|
38
|
+
bot = mock_bot {}
|
39
|
+
bot_is_connected
|
40
|
+
|
41
|
+
bot.part "#foo", "#bar"
|
42
|
+
assert_equal "PART #foo\r\n", @server.gets
|
43
|
+
assert_equal "PART #bar\r\n", @server.gets
|
44
|
+
end
|
45
|
+
|
46
|
+
test "topic is set" do
|
47
|
+
bot = mock_bot {}
|
48
|
+
bot_is_connected
|
49
|
+
|
50
|
+
bot.topic "#foo", "bar baz"
|
51
|
+
assert_equal "TOPIC #foo :bar baz\r\n", @server.gets
|
52
|
+
end
|
53
|
+
|
54
|
+
test "modes can be set" do
|
55
|
+
bot = mock_bot {}
|
56
|
+
bot_is_connected
|
57
|
+
|
58
|
+
bot.mode "#foo", "+o"
|
59
|
+
assert_equal "MODE #foo +o\r\n", @server.gets
|
60
|
+
end
|
61
|
+
|
62
|
+
test "can kick users" do
|
63
|
+
bot = mock_bot {}
|
64
|
+
bot_is_connected
|
65
|
+
|
66
|
+
bot.kick "foo", "bar", "bein' a baz"
|
67
|
+
assert_equal "KICK foo bar :bein' a baz\r\n", @server.gets
|
68
|
+
end
|
69
|
+
|
70
|
+
test "quits" do
|
71
|
+
bot = mock_bot {}
|
72
|
+
bot_is_connected
|
73
|
+
|
74
|
+
bot.quit
|
75
|
+
assert_equal "QUIT\r\n", @server.gets
|
76
|
+
end
|
77
|
+
|
78
|
+
test "quits with message" do
|
79
|
+
bot = mock_bot {}
|
80
|
+
bot_is_connected
|
81
|
+
|
82
|
+
bot.quit "I'm outta here!"
|
83
|
+
assert_equal "QUIT :I'm outta here!\r\n", @server.gets
|
84
|
+
end
|
85
|
+
end
|
data/test/test_events.rb
ADDED
@@ -0,0 +1,89 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'helper')
|
2
|
+
|
3
|
+
class TestEvents < Test::Unit::TestCase
|
4
|
+
# This is stupid, but it's just there to make it easier to transform to the new
|
5
|
+
# Message class. Should be fixed.
|
6
|
+
def dispatch(type, env)
|
7
|
+
msg = Newton::Message.new(":john!doe@example.com PRIVMSG #foo :#{env[:message]}")
|
8
|
+
@bot.dispatch(type, msg)
|
9
|
+
end
|
10
|
+
|
11
|
+
test "events are registered" do
|
12
|
+
@bot = mock_bot {
|
13
|
+
on(:channel, /Hello/) {msg "foo", "yr formal!"}
|
14
|
+
on(:channel, /Hey/) {msg "foo", "bar baz"}
|
15
|
+
}
|
16
|
+
bot_is_connected
|
17
|
+
|
18
|
+
dispatch(:channel, :message => "Hey")
|
19
|
+
assert_equal "PRIVMSG foo :bar baz\r\n", @server.gets
|
20
|
+
end
|
21
|
+
|
22
|
+
test "catch-all events" do
|
23
|
+
@bot = mock_bot {
|
24
|
+
on(:channel) {msg "foo", "bar baz"}
|
25
|
+
}
|
26
|
+
bot_is_connected
|
27
|
+
|
28
|
+
dispatch(:channel, :message => "lolcat")
|
29
|
+
assert_equal "PRIVMSG foo :bar baz\r\n", @server.gets
|
30
|
+
end
|
31
|
+
|
32
|
+
test "event can be halted" do
|
33
|
+
@bot = mock_bot {
|
34
|
+
on(:channel, /Hey/) { halt; msg "foo", "bar baz" }
|
35
|
+
}
|
36
|
+
bot_is_connected
|
37
|
+
|
38
|
+
dispatch(:channel, :message => "Hey")
|
39
|
+
assert @server.empty?
|
40
|
+
end
|
41
|
+
|
42
|
+
test "connect-event is dispatched at connection" do
|
43
|
+
@bot = mock_bot {
|
44
|
+
on(:connect) {msg "foo", "bar baz"}
|
45
|
+
}
|
46
|
+
bot_is_connected
|
47
|
+
|
48
|
+
assert_equal "PRIVMSG foo :bar baz\r\n", @server.gets
|
49
|
+
end
|
50
|
+
|
51
|
+
test "regular expression match is accessible" do
|
52
|
+
@bot = mock_bot {
|
53
|
+
on(:channel, /foo (bar)/) {msg "foo", match[0]}
|
54
|
+
}
|
55
|
+
bot_is_connected
|
56
|
+
|
57
|
+
dispatch(:channel, :message => "foo bar")
|
58
|
+
|
59
|
+
assert_equal "PRIVMSG foo :bar\r\n", @server.gets
|
60
|
+
end
|
61
|
+
|
62
|
+
test "regular expression matches are handed to block arguments" do
|
63
|
+
@bot = mock_bot {
|
64
|
+
on :channel, /(foo) (bar)/ do |a,b|
|
65
|
+
raw "#{a}"
|
66
|
+
raw "#{b}"
|
67
|
+
end
|
68
|
+
}
|
69
|
+
bot_is_connected
|
70
|
+
|
71
|
+
dispatch(:channel, :message => "foo bar")
|
72
|
+
|
73
|
+
assert_equal "foo\r\n", @server.gets
|
74
|
+
assert_equal "bar\r\n", @server.gets
|
75
|
+
end
|
76
|
+
|
77
|
+
test "only specified number of captures are handed to block args" do
|
78
|
+
@bot = mock_bot {
|
79
|
+
on :channel, /(foo) (bar)/ do |a|
|
80
|
+
raw "#{a}"
|
81
|
+
end
|
82
|
+
}
|
83
|
+
bot_is_connected
|
84
|
+
|
85
|
+
dispatch(:channel, :message => "foo bar")
|
86
|
+
|
87
|
+
assert_equal "foo\r\n", @server.gets
|
88
|
+
end
|
89
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'helper')
|
2
|
+
|
3
|
+
class TestHelpers < Test::Unit::TestCase
|
4
|
+
test "helpers are registered" do
|
5
|
+
bot = mock_bot {
|
6
|
+
helpers { def foo; msg "foo", "bar baz"; end }
|
7
|
+
on(:private, //) {foo}
|
8
|
+
}
|
9
|
+
bot_is_connected
|
10
|
+
|
11
|
+
bot.irc.parse ":johnny!john@doe.com PRIVMSG isaac :hello, you!"
|
12
|
+
assert_equal "PRIVMSG foo :bar baz\r\n", @server.gets
|
13
|
+
end
|
14
|
+
end
|
data/test/test_irc.rb
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'helper')
|
2
|
+
|
3
|
+
class TestIrc < Test::Unit::TestCase
|
4
|
+
test "a new bot connects to IRC" do
|
5
|
+
bot = mock_bot {}
|
6
|
+
|
7
|
+
assert_equal "NICK isaac\r\n", @server.gets
|
8
|
+
assert_equal "USER isaac 0 * :#{bot.config.realname}\r\n", @server.gets
|
9
|
+
end
|
10
|
+
|
11
|
+
test "password is sent if specified" do
|
12
|
+
bot = mock_bot {
|
13
|
+
configure {|c| c.password = "foo"}
|
14
|
+
}
|
15
|
+
assert_equal "PASS foo\r\n", @server.gets
|
16
|
+
end
|
17
|
+
|
18
|
+
test "no messages are sent when registration isn't complete" do
|
19
|
+
bot = mock_bot {
|
20
|
+
on(:connect) {raw "Connected!"}
|
21
|
+
}
|
22
|
+
2.times { @server.gets } # NICK / USER
|
23
|
+
bot.dispatch :connect
|
24
|
+
|
25
|
+
assert @server.empty?
|
26
|
+
end
|
27
|
+
|
28
|
+
test "no messages are sent until registration is complete" do
|
29
|
+
bot = mock_bot {
|
30
|
+
on(:connect) {raw "Connected!"}
|
31
|
+
}
|
32
|
+
2.times { @server.gets } # NICK / USER
|
33
|
+
bot.dispatch :connect
|
34
|
+
|
35
|
+
1.upto(4) {|i| @server.puts ":localhost 00#{i}"}
|
36
|
+
assert_equal "Connected!\r\n", @server.gets
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,117 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'helper')
|
2
|
+
|
3
|
+
class TestMessage < Test::Unit::TestCase
|
4
|
+
include Newton
|
5
|
+
|
6
|
+
test "host prefix" do
|
7
|
+
msg = Message.new(":jeff!spicoli@beach.com QUIT")
|
8
|
+
assert_equal "jeff!spicoli@beach.com", msg.prefix
|
9
|
+
assert_equal "jeff", msg.nick
|
10
|
+
assert_equal "spicoli", msg.user
|
11
|
+
assert_equal "beach.com", msg.host
|
12
|
+
assert_nil msg.server
|
13
|
+
end
|
14
|
+
|
15
|
+
test "server prefix" do
|
16
|
+
msg = Message.new(":some.server.com PING")
|
17
|
+
assert_equal "some.server.com", msg.prefix
|
18
|
+
assert_equal "some.server.com", msg.server
|
19
|
+
assert_nil msg.nick
|
20
|
+
assert_nil msg.user
|
21
|
+
assert_nil msg.host
|
22
|
+
end
|
23
|
+
|
24
|
+
test "without prefix" do
|
25
|
+
msg = Message.new("PING foo.bar")
|
26
|
+
assert_nil msg.prefix
|
27
|
+
assert_nil msg.nick
|
28
|
+
assert_nil msg.host
|
29
|
+
end
|
30
|
+
|
31
|
+
test "command" do
|
32
|
+
msg = Message.new("PING foo.bar")
|
33
|
+
assert_equal "PING", msg.command
|
34
|
+
end
|
35
|
+
|
36
|
+
test "numeric reply" do
|
37
|
+
msg = Message.new("409")
|
38
|
+
assert msg.numeric_reply?
|
39
|
+
assert_equal "409", msg.command
|
40
|
+
end
|
41
|
+
|
42
|
+
test "single param" do
|
43
|
+
msg = Message.new("PING foo.bar")
|
44
|
+
assert_equal 1, msg.params.size
|
45
|
+
assert_equal "foo.bar", msg.params[0]
|
46
|
+
end
|
47
|
+
|
48
|
+
test "multiple params" do
|
49
|
+
msg = Message.new("FOO bar baz")
|
50
|
+
assert_equal 2, msg.params.size
|
51
|
+
assert_equal ["bar", "baz"], msg.params
|
52
|
+
end
|
53
|
+
|
54
|
+
test "single param with whitespace" do
|
55
|
+
msg = Message.new("FOO :bar baz")
|
56
|
+
assert_equal 1, msg.params.size
|
57
|
+
assert_equal "bar baz", msg.params[0]
|
58
|
+
end
|
59
|
+
|
60
|
+
test "single param with whitespace and colon" do
|
61
|
+
msg = Message.new("FOO :bar :baz")
|
62
|
+
assert_equal 1, msg.params.size
|
63
|
+
assert_equal "bar :baz", msg.params[0]
|
64
|
+
end
|
65
|
+
|
66
|
+
test "multiple params with whitespace" do
|
67
|
+
msg = Message.new("FOO bar :lol cat")
|
68
|
+
assert_equal 2, msg.params.size
|
69
|
+
assert_equal "bar", msg.params[0]
|
70
|
+
assert_equal "lol cat", msg.params[1]
|
71
|
+
end
|
72
|
+
|
73
|
+
test "multiple params with whitespace and colon" do
|
74
|
+
msg = Message.new("FOO bar :lol :cat")
|
75
|
+
assert_equal 2, msg.params.size
|
76
|
+
assert_equal "bar", msg.params[0]
|
77
|
+
assert_equal "lol :cat", msg.params[1]
|
78
|
+
end
|
79
|
+
|
80
|
+
test "error" do
|
81
|
+
msg = Message.new("200")
|
82
|
+
assert_equal false, msg.error?
|
83
|
+
assert_nil msg.error
|
84
|
+
|
85
|
+
msg = Message.new("400")
|
86
|
+
assert_equal true, msg.error?
|
87
|
+
assert_equal 400, msg.error
|
88
|
+
end
|
89
|
+
|
90
|
+
test "if error, #message has the error code string" do
|
91
|
+
msg = Message.new("400")
|
92
|
+
assert_equal "400", msg.message
|
93
|
+
end
|
94
|
+
|
95
|
+
test "channel has channel name" do
|
96
|
+
msg = Message.new(":foo!bar@baz.com PRIVMSG #awesome :lol cat")
|
97
|
+
assert_equal true, msg.channel?
|
98
|
+
assert_equal "#awesome", msg.channel
|
99
|
+
end
|
100
|
+
|
101
|
+
test "channel has nothing when receiver is a nick" do
|
102
|
+
msg = Message.new(":foo!bar@baz.com PRIVMSG john :wazzup boy?")
|
103
|
+
assert_equal false, msg.channel?
|
104
|
+
assert_equal nil, msg.channel
|
105
|
+
end
|
106
|
+
|
107
|
+
test "privmsg has #message" do
|
108
|
+
msg = Message.new(":foo!bar@baz.com PRIVMSG #awesome :lol cat")
|
109
|
+
assert_equal "lol cat", msg.message
|
110
|
+
end
|
111
|
+
|
112
|
+
# test "non-privmsg doesn't have #message" do
|
113
|
+
# msg = Message.new("PING :foo bar")
|
114
|
+
# p msg.message
|
115
|
+
# assert_nil msg.message
|
116
|
+
# end
|
117
|
+
end
|