net-irc2 0.0.10
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.
- checksums.yaml +7 -0
- data/AUTHORS.txt +33 -0
- data/ChangeLog +95 -0
- data/README +91 -0
- data/Rakefile +69 -0
- data/examples/2ch.rb +225 -0
- data/examples/2ig.rb +267 -0
- data/examples/client.rb +23 -0
- data/examples/echo_bot.rb +31 -0
- data/examples/echo_bot_celluloid.rb +33 -0
- data/examples/gig.rb +192 -0
- data/examples/gmail.rb +202 -0
- data/examples/gtig.rb +420 -0
- data/examples/hatena-star-stream.rb +270 -0
- data/examples/hcig.rb +285 -0
- data/examples/hig.rb +771 -0
- data/examples/iig.rb +819 -0
- data/examples/ircd.rb +358 -0
- data/examples/lig.rb +551 -0
- data/examples/lingr.rb +327 -0
- data/examples/mixi.rb +252 -0
- data/examples/sig.rb +188 -0
- data/examples/tig.rb +2712 -0
- data/lib/net/irc/client/channel_manager.rb +144 -0
- data/lib/net/irc/client.rb +117 -0
- data/lib/net/irc/constants.rb +214 -0
- data/lib/net/irc/message/modeparser.rb +85 -0
- data/lib/net/irc/message/serverconfig.rb +30 -0
- data/lib/net/irc/message.rb +109 -0
- data/lib/net/irc/pattern.rb +68 -0
- data/lib/net/irc/server.rb +186 -0
- data/lib/net/irc.rb +77 -0
- data/spec/channel_manager_spec.rb +184 -0
- data/spec/modeparser_spec.rb +165 -0
- data/spec/net-irc_spec.rb +337 -0
- data/spec/spec.opts +1 -0
- metadata +91 -0
@@ -0,0 +1,68 @@
|
|
1
|
+
# coding: ASCII-8BIT
|
2
|
+
module Net::IRC::PATTERN # :nodoc:
|
3
|
+
# letter = %x41-5A / %x61-7A ; A-Z / a-z
|
4
|
+
# digit = %x30-39 ; 0-9
|
5
|
+
# hexdigit = digit / "A" / "B" / "C" / "D" / "E" / "F"
|
6
|
+
# special = %x5B-60 / %x7B-7D
|
7
|
+
# ; "[", "]", "\", "`", "_", "^", "{", "|", "}"
|
8
|
+
LETTER = 'A-Za-z'
|
9
|
+
DIGIT = '0-9'
|
10
|
+
HEXDIGIT = "#{DIGIT}A-Fa-f"
|
11
|
+
SPECIAL = '\x5B-\x60\x7B-\x7D'
|
12
|
+
|
13
|
+
# shortname = ( letter / digit ) *( letter / digit / "-" )
|
14
|
+
# *( letter / digit )
|
15
|
+
# ; as specified in RFC 1123 [HNAME]
|
16
|
+
# hostname = shortname *( "." shortname )
|
17
|
+
SHORTNAME = "[#{LETTER}#{DIGIT}](?:[-#{LETTER}#{DIGIT}]*[#{LETTER}#{DIGIT}])?"
|
18
|
+
HOSTNAME = "#{SHORTNAME}(?:\\.#{SHORTNAME})*"
|
19
|
+
|
20
|
+
# servername = hostname
|
21
|
+
SERVERNAME = HOSTNAME
|
22
|
+
|
23
|
+
# nickname = ( letter / special ) *8( letter / digit / special / "-" )
|
24
|
+
#NICKNAME = "[#{LETTER}#{SPECIAL}#{DIGIT}_][-#{LETTER}#{DIGIT}#{SPECIAL}]*"
|
25
|
+
NICKNAME = "\\S+" # for multibytes
|
26
|
+
|
27
|
+
# user = 1*( %x01-09 / %x0B-0C / %x0E-1F / %x21-3F / %x41-FF )
|
28
|
+
# ; any octet except NUL, CR, LF, " " and "@"
|
29
|
+
USER = '[\x01-\x09\x0B-\x0C\x0E-\x1F\x21-\x3F\x41-\xFF]+'
|
30
|
+
|
31
|
+
# ip4addr = 1*3digit "." 1*3digit "." 1*3digit "." 1*3digit
|
32
|
+
IP4ADDR = "[#{DIGIT}]{1,3}(?:\\.[#{DIGIT}]{1,3}){3}"
|
33
|
+
# ip6addr = 1*hexdigit 7( ":" 1*hexdigit )
|
34
|
+
# ip6addr =/ "0:0:0:0:0:" ( "0" / "FFFF" ) ":" ip4addr
|
35
|
+
IP6ADDR = "(?:[#{HEXDIGIT}]+(?::[#{HEXDIGIT}]+){7}|0:0:0:0:0:(?:0|FFFF):#{IP4ADDR})"
|
36
|
+
# hostaddr = ip4addr / ip6addr
|
37
|
+
HOSTADDR = "(?:#{IP4ADDR}|#{IP6ADDR})"
|
38
|
+
|
39
|
+
# host = hostname / hostaddr
|
40
|
+
HOST = "(?:#{HOSTNAME}|#{HOSTADDR})"
|
41
|
+
|
42
|
+
# prefix = servername / ( nickname [ [ "!" user ] "@" host ] )
|
43
|
+
PREFIX = "(?:#{NICKNAME}(?:(?:!#{USER})?@#{HOST})?|#{SERVERNAME})"
|
44
|
+
|
45
|
+
# nospcrlfcl = %x01-09 / %x0B-0C / %x0E-1F / %x21-39 / %x3B-FF
|
46
|
+
# ; any octet except NUL, CR, LF, " " and ":"
|
47
|
+
NOSPCRLFCL = '\x01-\x09\x0B-\x0C\x0E-\x1F\x21-\x39\x3B-\xFF'
|
48
|
+
|
49
|
+
# command = 1*letter / 3digit
|
50
|
+
COMMAND = "(?:[#{LETTER}]+|[#{DIGIT}]{3})"
|
51
|
+
|
52
|
+
# SPACE = %x20 ; space character
|
53
|
+
# middle = nospcrlfcl *( ":" / nospcrlfcl )
|
54
|
+
# trailing = *( ":" / " " / nospcrlfcl )
|
55
|
+
# params = *14( SPACE middle ) [ SPACE ":" trailing ]
|
56
|
+
# =/ 14( SPACE middle ) [ SPACE [ ":" ] trailing ]
|
57
|
+
MIDDLE = "[#{NOSPCRLFCL}][:#{NOSPCRLFCL}]*"
|
58
|
+
TRAILING = "[: #{NOSPCRLFCL}]*"
|
59
|
+
PARAMS = "(?:((?: #{MIDDLE}){0,14})(?: :(#{TRAILING}))?|((?: #{MIDDLE}){14}):?(#{TRAILING}))"
|
60
|
+
|
61
|
+
# crlf = %x0D %x0A ; "carriage return" "linefeed"
|
62
|
+
# message = [ ":" prefix SPACE ] command [ params ] crlf
|
63
|
+
CRLF = '\x0D\x0A'
|
64
|
+
MESSAGE = "(?::(#{PREFIX}) )?(#{COMMAND})#{PARAMS}\s*#{CRLF}"
|
65
|
+
|
66
|
+
CLIENT_PATTERN = /\A#{NICKNAME}(?:(?:!#{USER})?@#{HOST})\z/on
|
67
|
+
MESSAGE_PATTERN = /\A#{MESSAGE}\z/on
|
68
|
+
end # PATTERN
|
@@ -0,0 +1,186 @@
|
|
1
|
+
class Net::IRC::Server
|
2
|
+
# Server global state for accessing Server::Session
|
3
|
+
attr_accessor :state
|
4
|
+
attr_accessor :sessions
|
5
|
+
|
6
|
+
def initialize(host, port, session_class, opts={})
|
7
|
+
@host = host
|
8
|
+
@port = port
|
9
|
+
@session_class = session_class
|
10
|
+
@opts = OpenStruct.new(opts)
|
11
|
+
@sessions = []
|
12
|
+
@state = {}
|
13
|
+
end
|
14
|
+
|
15
|
+
# Start server loop.
|
16
|
+
def start
|
17
|
+
@serv = TCPServer.new(@host, @port)
|
18
|
+
@log = @opts.logger || Logger.new($stdout)
|
19
|
+
@log.info "Host: #{@host} Port:#{@port}"
|
20
|
+
@accept = Thread.start do
|
21
|
+
loop do
|
22
|
+
Thread.start(@serv.accept) do |s|
|
23
|
+
begin
|
24
|
+
@log.info "Client connected, new session starting..."
|
25
|
+
s = @session_class.new(self, s, @log, @opts)
|
26
|
+
@sessions << s
|
27
|
+
s.start
|
28
|
+
rescue Exception => e
|
29
|
+
puts e
|
30
|
+
puts e.backtrace
|
31
|
+
ensure
|
32
|
+
@sessions.delete(s)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
@accept.join
|
38
|
+
end
|
39
|
+
|
40
|
+
# Close all sessions.
|
41
|
+
def finish
|
42
|
+
Thread.exclusive do
|
43
|
+
@accept.kill
|
44
|
+
begin
|
45
|
+
@serv.close
|
46
|
+
rescue
|
47
|
+
end
|
48
|
+
@sessions.each do |s|
|
49
|
+
s.finish
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
|
55
|
+
class Session
|
56
|
+
include Net::IRC
|
57
|
+
include Constants
|
58
|
+
|
59
|
+
attr_reader :prefix, :nick, :real, :host
|
60
|
+
|
61
|
+
# Override subclass.
|
62
|
+
def server_name
|
63
|
+
"net-irc"
|
64
|
+
end
|
65
|
+
|
66
|
+
# Override subclass.
|
67
|
+
def server_version
|
68
|
+
"0.0.0"
|
69
|
+
end
|
70
|
+
|
71
|
+
# Override subclass.
|
72
|
+
def available_user_modes
|
73
|
+
"eixwy"
|
74
|
+
end
|
75
|
+
|
76
|
+
# Override subclass.
|
77
|
+
def available_channel_modes
|
78
|
+
"spknm"
|
79
|
+
end
|
80
|
+
|
81
|
+
def initialize(server, socket, logger, opts={})
|
82
|
+
@server, @socket, @log, @opts = server, socket, logger, opts
|
83
|
+
end
|
84
|
+
|
85
|
+
def self.start(*args)
|
86
|
+
new(*args).start
|
87
|
+
end
|
88
|
+
|
89
|
+
# Start session loop.
|
90
|
+
def start
|
91
|
+
on_connected
|
92
|
+
while l = @socket.gets
|
93
|
+
begin
|
94
|
+
@log.debug "RECEIVE: #{l.chomp}"
|
95
|
+
m = Message.parse(l)
|
96
|
+
next if on_message(m) === true
|
97
|
+
|
98
|
+
name = "on_#{(COMMANDS[m.command.upcase] || m.command).downcase}"
|
99
|
+
send(name, m) if respond_to?(name)
|
100
|
+
|
101
|
+
break if m.command == QUIT
|
102
|
+
rescue Message::InvalidMessage
|
103
|
+
@log.error "MessageParse: " + l.inspect
|
104
|
+
end
|
105
|
+
end
|
106
|
+
rescue IOError
|
107
|
+
ensure
|
108
|
+
finish
|
109
|
+
end
|
110
|
+
|
111
|
+
# Close this session.
|
112
|
+
def finish
|
113
|
+
begin
|
114
|
+
@socket.close
|
115
|
+
rescue
|
116
|
+
end
|
117
|
+
on_disconnected
|
118
|
+
end
|
119
|
+
|
120
|
+
# Default PASS callback.
|
121
|
+
# Set @pass.
|
122
|
+
def on_pass(m)
|
123
|
+
@pass = m.params[0]
|
124
|
+
end
|
125
|
+
|
126
|
+
# Default NICK callback.
|
127
|
+
# Set @nick.
|
128
|
+
def on_nick(m)
|
129
|
+
@nick = m.params[0]
|
130
|
+
@prefix = Prefix.new("#{@nick}!#{@user}@#{@host}") if defined? @prefix
|
131
|
+
end
|
132
|
+
|
133
|
+
# Default USER callback.
|
134
|
+
# Set @user, @real, @host and call initial_message.
|
135
|
+
def on_user(m)
|
136
|
+
@user, @real = m.params[0], m.params[3]
|
137
|
+
@nick ||= @user
|
138
|
+
@host = @socket.peeraddr[2]
|
139
|
+
@prefix = Prefix.new("#{@nick}!#{@user}@#{@host}")
|
140
|
+
initial_message
|
141
|
+
end
|
142
|
+
|
143
|
+
# Call when socket connected.
|
144
|
+
def on_connected
|
145
|
+
end
|
146
|
+
|
147
|
+
# Call when socket closed.
|
148
|
+
def on_disconnected
|
149
|
+
end
|
150
|
+
|
151
|
+
# Catch all messages.
|
152
|
+
# If this method return true, aother callback will not be called.
|
153
|
+
def on_message(m)
|
154
|
+
end
|
155
|
+
|
156
|
+
# Default PING callback. Response PONG.
|
157
|
+
def on_ping(m)
|
158
|
+
post server_name, PONG, m.params[0]
|
159
|
+
end
|
160
|
+
|
161
|
+
private
|
162
|
+
# Post message to server.
|
163
|
+
#
|
164
|
+
# include Net::IRC::Constants
|
165
|
+
# post prefix, PRIVMSG, "#channel", "foobar"
|
166
|
+
def post(prefix, command, *params)
|
167
|
+
m = Message.new(prefix, command, params.map {|s|
|
168
|
+
#s.gsub(/\r\n|[\r\n]/, " ")
|
169
|
+
s.tr("\r\n", " ")
|
170
|
+
})
|
171
|
+
@log.debug "SEND: #{m.to_s.chomp}"
|
172
|
+
@socket << m
|
173
|
+
rescue IOError
|
174
|
+
finish
|
175
|
+
end
|
176
|
+
|
177
|
+
# Call when client connected.
|
178
|
+
# Send RPL_WELCOME sequence. If you want to customize, override this method at subclass.
|
179
|
+
def initial_message
|
180
|
+
post server_name, RPL_WELCOME, @nick, "Welcome to the Internet Relay Network #{@prefix}"
|
181
|
+
post server_name, RPL_YOURHOST, @nick, "Your host is #{server_name}, running version #{server_version}"
|
182
|
+
post server_name, RPL_CREATED, @nick, "This server was created #{Time.now}"
|
183
|
+
post server_name, RPL_MYINFO, @nick, "#{server_name} #{server_version} #{available_user_modes} #{available_channel_modes}"
|
184
|
+
end
|
185
|
+
end
|
186
|
+
end # Server
|
data/lib/net/irc.rb
ADDED
@@ -0,0 +1,77 @@
|
|
1
|
+
#!ruby
|
2
|
+
|
3
|
+
require "bundler"
|
4
|
+
Bundler.setup
|
5
|
+
|
6
|
+
require "ostruct"
|
7
|
+
require "socket"
|
8
|
+
require "logger"
|
9
|
+
require 'buftok'
|
10
|
+
|
11
|
+
module Net; end
|
12
|
+
|
13
|
+
module Net::IRC
|
14
|
+
VERSION = "0.0.10".freeze
|
15
|
+
class IRCException < StandardError; end
|
16
|
+
|
17
|
+
require "net/irc/constants"
|
18
|
+
require "net/irc/pattern"
|
19
|
+
|
20
|
+
autoload :Message, "net/irc/message"
|
21
|
+
autoload :Client, "net/irc/client"
|
22
|
+
autoload :Server, "net/irc/server"
|
23
|
+
|
24
|
+
class Prefix < String
|
25
|
+
def nick
|
26
|
+
extract[0]
|
27
|
+
end
|
28
|
+
|
29
|
+
def user
|
30
|
+
extract[1]
|
31
|
+
end
|
32
|
+
|
33
|
+
def host
|
34
|
+
extract[2]
|
35
|
+
end
|
36
|
+
|
37
|
+
# Extract Prefix String to [nick, user, host] Array.
|
38
|
+
def extract
|
39
|
+
_, *ret = *self.match(/\A([^\s!]+)(?:!([^\s@]+)@(\S+))?\z/)
|
40
|
+
ret
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
# Encode to CTCP message. Prefix and postfix \x01.
|
45
|
+
def ctcp_encode(str)
|
46
|
+
"\x01#{ctcp_quote(str)}\x01"
|
47
|
+
end
|
48
|
+
#alias :ctcp_encoding :ctcp_encode
|
49
|
+
module_function :ctcp_encode #, :ctcp_encoding
|
50
|
+
|
51
|
+
# Decode from CTCP message delimited with \x01.
|
52
|
+
def ctcp_decode(str)
|
53
|
+
ctcp_dequote(str.delete("\x01"))
|
54
|
+
end
|
55
|
+
#alias :ctcp_decoding :ctcp_decode
|
56
|
+
module_function :ctcp_decode #, :ctcp_decoding
|
57
|
+
|
58
|
+
def ctcp_quote(str)
|
59
|
+
low_quote(str.gsub("\\", "\\\\\\\\").gsub("\x01", "\\a"))
|
60
|
+
end
|
61
|
+
module_function :ctcp_quote
|
62
|
+
|
63
|
+
def ctcp_dequote(str)
|
64
|
+
low_dequote(str).gsub("\\a", "\x01").gsub(/\\(.|\z)/m, "\\1")
|
65
|
+
end
|
66
|
+
module_function :ctcp_dequote
|
67
|
+
|
68
|
+
private
|
69
|
+
def low_quote(str)
|
70
|
+
str.gsub("\x10", "\x10\x10").gsub("\x00", "\x10\x30").gsub("\r", "\x10r").gsub("\n", "\x10n")
|
71
|
+
end
|
72
|
+
|
73
|
+
def low_dequote(str)
|
74
|
+
str.gsub("\x10n", "\n").gsub("\x10r", "\r").gsub("\x10\x30", "\x00").gsub("\x10\x10", "\x10")
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
@@ -0,0 +1,184 @@
|
|
1
|
+
#!spec
|
2
|
+
|
3
|
+
$LOAD_PATH << "lib"
|
4
|
+
$LOAD_PATH << "../lib"
|
5
|
+
|
6
|
+
require "rubygems"
|
7
|
+
require "rspec"
|
8
|
+
require "thread"
|
9
|
+
require "net/irc"
|
10
|
+
require "net/irc/client/channel_manager"
|
11
|
+
include Net::IRC
|
12
|
+
include Constants
|
13
|
+
|
14
|
+
class ChannelManagerTestServerSession < Net::IRC::Server::Session
|
15
|
+
@@testq = SizedQueue.new(1)
|
16
|
+
@@instance = nil
|
17
|
+
|
18
|
+
def self.testq
|
19
|
+
@@testq
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.instance
|
23
|
+
@@instance
|
24
|
+
end
|
25
|
+
|
26
|
+
def initialize(*args)
|
27
|
+
super
|
28
|
+
@@instance = self
|
29
|
+
end
|
30
|
+
|
31
|
+
def on_message(m)
|
32
|
+
@@testq << m
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
class ChannelManagerTestClient < Net::IRC::Client
|
37
|
+
include Net::IRC::Client::ChannelManager
|
38
|
+
@@testq = SizedQueue.new(1)
|
39
|
+
|
40
|
+
def self.testq
|
41
|
+
@@testq
|
42
|
+
end
|
43
|
+
|
44
|
+
def on_message(m)
|
45
|
+
@@testq << m
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
describe Net::IRC, "server and client" do
|
50
|
+
before :all do
|
51
|
+
@port = nil
|
52
|
+
@server, @client = nil, nil
|
53
|
+
|
54
|
+
Thread.abort_on_exception = true
|
55
|
+
Thread.start do
|
56
|
+
@server = Net::IRC::Server.new("localhost", @port, ChannelManagerTestServerSession, {
|
57
|
+
:logger => Logger.new(nil),
|
58
|
+
})
|
59
|
+
@server.start
|
60
|
+
end
|
61
|
+
|
62
|
+
Thread.pass
|
63
|
+
true until @server.instance_variable_get(:@serv)
|
64
|
+
|
65
|
+
@port = @server.instance_variable_get(:@serv).addr[1]
|
66
|
+
|
67
|
+
Thread.start do
|
68
|
+
@client = ChannelManagerTestClient.new("localhost", @port, {
|
69
|
+
:nick => "foonick",
|
70
|
+
:user => "foouser",
|
71
|
+
:real => "foo real name",
|
72
|
+
:pass => "foopass",
|
73
|
+
:logger => Logger.new(nil),
|
74
|
+
})
|
75
|
+
@client.start
|
76
|
+
end
|
77
|
+
|
78
|
+
Thread.pass
|
79
|
+
true until @client
|
80
|
+
end
|
81
|
+
|
82
|
+
server_q = ChannelManagerTestServerSession.testq
|
83
|
+
client_q = ChannelManagerTestClient.testq
|
84
|
+
|
85
|
+
it "client should manage channel mode/users correctly" do
|
86
|
+
client = @client
|
87
|
+
client.instance_variable_set(:@prefix, Prefix.new("foonick!foouser@localhost"))
|
88
|
+
|
89
|
+
true until ChannelManagerTestServerSession.instance
|
90
|
+
ChannelManagerTestServerSession.instance.instance_eval do
|
91
|
+
Thread.exclusive do
|
92
|
+
post client.prefix, JOIN, "#test"
|
93
|
+
post nil, NOTICE, "#test", "sep1"
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
true until client_q.pop.to_s == "NOTICE #test sep1\r\n"
|
98
|
+
|
99
|
+
c = @client.instance_variable_get(:@channels)
|
100
|
+
c.synchronize do
|
101
|
+
c.should be_a_kind_of(Hash)
|
102
|
+
c["#test"].should be_a_kind_of(Hash)
|
103
|
+
c["#test"][:modes].should be_a_kind_of(Array)
|
104
|
+
c["#test"][:users].should be_a_kind_of(Array)
|
105
|
+
c["#test"][:users].should == ["foonick"]
|
106
|
+
end
|
107
|
+
|
108
|
+
ChannelManagerTestServerSession.instance.instance_eval do
|
109
|
+
Thread.exclusive do
|
110
|
+
post "test1!test@localhost", JOIN, "#test"
|
111
|
+
post "test2!test@localhost", JOIN, "#test"
|
112
|
+
post nil, NOTICE, "#test", "sep2"
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
true until client_q.pop.to_s == "NOTICE #test sep2\r\n"
|
117
|
+
c.synchronize do
|
118
|
+
c["#test"][:users].should == ["foonick", "test1", "test2"]
|
119
|
+
end
|
120
|
+
|
121
|
+
ChannelManagerTestServerSession.instance.instance_eval do
|
122
|
+
Thread.exclusive do
|
123
|
+
post nil, RPL_NAMREPLY, client.prefix.nick, "@", "#test", "foo1 foo2 foo3 @foo4 +foo5"
|
124
|
+
post nil, NOTICE, "#test", "sep3"
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
true until client_q.pop.to_s == "NOTICE #test sep3\r\n"
|
129
|
+
c.synchronize do
|
130
|
+
c["#test"][:users].should == ["foonick", "test1", "test2", "foo1", "foo2", "foo3", "foo4", "foo5"]
|
131
|
+
c["#test"][:modes].should include([:s, nil])
|
132
|
+
c["#test"][:modes].should include([:o, "foo4"])
|
133
|
+
c["#test"][:modes].should include([:v, "foo5"])
|
134
|
+
end
|
135
|
+
|
136
|
+
ChannelManagerTestServerSession.instance.instance_eval do
|
137
|
+
Thread.exclusive do
|
138
|
+
post nil, RPL_NAMREPLY, client.prefix.nick, "@", "#test1", "foo1 foo2 foo3 @foo4 +foo5"
|
139
|
+
post "foo4!foo@localhost", QUIT, "message"
|
140
|
+
post "foo5!foo@localhost", PART, "#test1", "message"
|
141
|
+
post client.prefix, KICK, "#test", "foo1", "message"
|
142
|
+
post client.prefix, MODE, "#test", "+o", "foo2"
|
143
|
+
post nil, NOTICE, "#test", "sep4"
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
true until client_q.pop.to_s == "NOTICE #test sep4\r\n"
|
148
|
+
c.synchronize do
|
149
|
+
c["#test"][:users].should == ["foonick", "test1", "test2", "foo2", "foo3", "foo5"]
|
150
|
+
c["#test1"][:users].should == ["foo1", "foo2", "foo3"]
|
151
|
+
c["#test"][:modes].should_not include([:o, "foo4"])
|
152
|
+
c["#test"][:modes].should include([:v, "foo5"])
|
153
|
+
c["#test1"][:modes].should_not include([:v, "foo5"])
|
154
|
+
c["#test"][:modes].should include([:o, "foo2"])
|
155
|
+
end
|
156
|
+
|
157
|
+
ChannelManagerTestServerSession.instance.instance_eval do
|
158
|
+
Thread.exclusive do
|
159
|
+
post "foonick!test@localhost", NICK, "foonick2"
|
160
|
+
post "foonick2!test@localhost", NICK, "foonick"
|
161
|
+
post "foo2!test@localhost", NICK, "bar2"
|
162
|
+
post "foo3!test@localhost", NICK, "bar3"
|
163
|
+
post nil, NOTICE, "#test", "sep5"
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
true until client_q.pop.to_s == "NOTICE #test sep5\r\n"
|
168
|
+
c.synchronize do
|
169
|
+
c["#test"][:users].should == ["foonick", "test1", "test2", "bar2", "bar3", "foo5"]
|
170
|
+
c["#test1"][:users].should == ["foo1", "bar2", "bar3"]
|
171
|
+
c["#test"][:modes].should_not include([:o, "foo4"])
|
172
|
+
c["#test"][:modes].should include([:v, "foo5"])
|
173
|
+
c["#test1"][:modes].should_not include([:v, "foo5"])
|
174
|
+
c["#test"][:modes].should_not include([:o, "foo2"])
|
175
|
+
c["#test"][:modes].should include([:o, "bar2"])
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
after :all do
|
180
|
+
@server.finish
|
181
|
+
@client.finish
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
@@ -0,0 +1,165 @@
|
|
1
|
+
|
2
|
+
$LOAD_PATH << "lib"
|
3
|
+
$LOAD_PATH << "../lib"
|
4
|
+
|
5
|
+
require "net/irc"
|
6
|
+
include Net::IRC
|
7
|
+
include Constants
|
8
|
+
|
9
|
+
describe Message::ModeParser do
|
10
|
+
it "should parse RFC2812+ correctly" do
|
11
|
+
parser = Message::ModeParser.new
|
12
|
+
|
13
|
+
parser.parse("#Finish +im")[:positive].should == [[:i, nil], [:m, nil]]
|
14
|
+
parser.parse("#Finish +o Kilroy")[:positive].should == [[:o, "Kilroy"]]
|
15
|
+
parser.parse("#Finish +v Kilroy")[:positive].should == [[:v, "Kilroy"]]
|
16
|
+
parser.parse("#Fins -s")[:negative].should == [[:s, nil]]
|
17
|
+
parser.parse("#42 +k oulu")[:positive].should == [[:k, "oulu"]]
|
18
|
+
parser.parse("#eu-opers +l 10")[:positive].should == [[:l, "10"]]
|
19
|
+
parser.parse("&oulu +b")[:positive].should == [[:b, nil]]
|
20
|
+
parser.parse("&oulu +b *!*@*")[:positive].should == [[:b, "*!*@*"]]
|
21
|
+
|
22
|
+
parser.parse("&oulu +b *!*@*.edu")[:positive].should == [[:b, "*!*@*.edu"]]
|
23
|
+
parser.parse("#oulu +e")[:positive].should == [[:e, nil]]
|
24
|
+
parser.parse("#oulu +e *!*@*.edu")[:positive].should == [[:e, "*!*@*.edu"]]
|
25
|
+
parser.parse("#oulu +I")[:positive].should == [[:I, nil]]
|
26
|
+
parser.parse("#oulu +I *!*@*.edu")[:positive].should == [[:I, "*!*@*.edu"]]
|
27
|
+
parser.parse("#oulu +R")[:positive].should == [[:R, nil]]
|
28
|
+
parser.parse("#oulu +R *!*@*.edu")[:positive].should == [[:R, "*!*@*.edu"]]
|
29
|
+
|
30
|
+
parser.parse("#foo +ooo foo bar baz").should == {
|
31
|
+
:positive => [[:o, "foo"], [:o, "bar"], [:o, "baz"]],
|
32
|
+
:negative => [],
|
33
|
+
}
|
34
|
+
parser.parse("#foo +oo-o foo bar baz").should == {
|
35
|
+
:positive => [[:o, "foo"], [:o, "bar"]],
|
36
|
+
:negative => [[:o, "baz"]],
|
37
|
+
}
|
38
|
+
parser.parse("#foo -oo+o foo bar baz").should == {
|
39
|
+
:positive => [[:o, "baz"]],
|
40
|
+
:negative => [[:o, "foo"], [:o, "bar"]],
|
41
|
+
}
|
42
|
+
parser.parse("#foo +imv foo").should == {
|
43
|
+
:positive => [[:i, nil], [:m, nil], [:v, "foo"]],
|
44
|
+
:negative => [],
|
45
|
+
}
|
46
|
+
|
47
|
+
parser.parse("#foo +lk 10 foo").should == {
|
48
|
+
:positive => [[:l, "10"], [:k, "foo"]],
|
49
|
+
:negative => [],
|
50
|
+
}
|
51
|
+
parser.parse("#foo -l+k foo").should == {
|
52
|
+
:positive => [[:k, "foo"]],
|
53
|
+
:negative => [[:l, nil]],
|
54
|
+
}
|
55
|
+
parser.parse("#foo +ao foo").should == {
|
56
|
+
:positive => [[:a, nil], [:o, "foo"]],
|
57
|
+
:negative => [],
|
58
|
+
}
|
59
|
+
end
|
60
|
+
|
61
|
+
it "should parse modes of Hyperion ircd correctly" do
|
62
|
+
parser = Message::ModeParser.new
|
63
|
+
parser.set(:CHANMODES, 'bdeIq,k,lfJD,cgijLmnPQrRstz')
|
64
|
+
|
65
|
+
parser.parse("#Finish +im")[:positive].should == [[:i, nil], [:m, nil]]
|
66
|
+
parser.parse("#Finish +o Kilroy")[:positive].should == [[:o, "Kilroy"]]
|
67
|
+
parser.parse("#Finish +v Kilroy")[:positive].should == [[:v, "Kilroy"]]
|
68
|
+
parser.parse("#Fins -s")[:negative].should == [[:s, nil]]
|
69
|
+
parser.parse("#42 +k oulu")[:positive].should == [[:k, "oulu"]]
|
70
|
+
parser.parse("#eu-opers +l 10")[:positive].should == [[:l, "10"]]
|
71
|
+
parser.parse("&oulu +b")[:positive].should == [[:b, nil]]
|
72
|
+
parser.parse("&oulu +b *!*@*")[:positive].should == [[:b, "*!*@*"]]
|
73
|
+
parser.parse("&oulu +b *!*@*.edu")[:positive].should == [[:b, "*!*@*.edu"]]
|
74
|
+
|
75
|
+
parser.parse("#oulu +e")[:positive].should == [[:e, nil]]
|
76
|
+
parser.parse("#oulu +e *!*@*.edu")[:positive].should == [[:e, "*!*@*.edu"]]
|
77
|
+
parser.parse("#oulu +I")[:positive].should == [[:I, nil]]
|
78
|
+
parser.parse("#oulu +I *!*@*.edu")[:positive].should == [[:I, "*!*@*.edu"]]
|
79
|
+
|
80
|
+
parser.parse("#foo +ooo foo bar baz").should == {
|
81
|
+
:positive => [[:o, "foo"], [:o, "bar"], [:o, "baz"]],
|
82
|
+
:negative => [],
|
83
|
+
}
|
84
|
+
parser.parse("#foo +oo-o foo bar baz").should == {
|
85
|
+
:positive => [[:o, "foo"], [:o, "bar"]],
|
86
|
+
:negative => [[:o, "baz"]],
|
87
|
+
}
|
88
|
+
parser.parse("#foo -oo+o foo bar baz").should == {
|
89
|
+
:positive => [[:o, "baz"]],
|
90
|
+
:negative => [[:o, "foo"], [:o, "bar"]],
|
91
|
+
}
|
92
|
+
parser.parse("#foo +imv foo").should == {
|
93
|
+
:positive => [[:i, nil], [:m, nil], [:v, "foo"]],
|
94
|
+
:negative => [],
|
95
|
+
}
|
96
|
+
|
97
|
+
parser.parse("#foo +lk 10 foo").should == {
|
98
|
+
:positive => [[:l, "10"], [:k, "foo"]],
|
99
|
+
:negative => [],
|
100
|
+
}
|
101
|
+
parser.parse("#foo -l+k foo").should == {
|
102
|
+
:positive => [[:k, "foo"]],
|
103
|
+
:negative => [[:l, nil]],
|
104
|
+
}
|
105
|
+
parser.parse("#foo +cv foo").should == {
|
106
|
+
:positive => [[:c, nil], [:v, "foo"]],
|
107
|
+
:negative => [],
|
108
|
+
}
|
109
|
+
end
|
110
|
+
|
111
|
+
it "should parse modes of Unreal ircd correctly" do
|
112
|
+
parser = Message::ModeParser.new
|
113
|
+
parser.set(:PREFIX, '(qaohv)~&@%+')
|
114
|
+
parser.set(:CHANMODES, 'beI,kfL,lj,psmntirRcOAQKVCuzNSMTG')
|
115
|
+
|
116
|
+
parser.parse("#Finish +im")[:positive].should == [[:i, nil], [:m, nil]]
|
117
|
+
parser.parse("#Finish +o Kilroy")[:positive].should == [[:o, "Kilroy"]]
|
118
|
+
parser.parse("#Finish +v Kilroy")[:positive].should == [[:v, "Kilroy"]]
|
119
|
+
parser.parse("#Fins -s")[:negative].should == [[:s, nil]]
|
120
|
+
parser.parse("#42 +k oulu")[:positive].should == [[:k, "oulu"]]
|
121
|
+
parser.parse("#eu-opers +l 10")[:positive].should == [[:l, "10"]]
|
122
|
+
parser.parse("&oulu +b")[:positive].should == [[:b, nil]]
|
123
|
+
parser.parse("&oulu +b *!*@*")[:positive].should == [[:b, "*!*@*"]]
|
124
|
+
parser.parse("&oulu +b *!*@*.edu")[:positive].should == [[:b, "*!*@*.edu"]]
|
125
|
+
|
126
|
+
parser.parse("#oulu +e")[:positive].should == [[:e, nil]]
|
127
|
+
parser.parse("#oulu +e *!*@*.edu")[:positive].should == [[:e, "*!*@*.edu"]]
|
128
|
+
parser.parse("#oulu +I")[:positive].should == [[:I, nil]]
|
129
|
+
parser.parse("#oulu +I *!*@*.edu")[:positive].should == [[:I, "*!*@*.edu"]]
|
130
|
+
|
131
|
+
parser.parse("#foo +ooo foo bar baz").should == {
|
132
|
+
:positive => [[:o, "foo"], [:o, "bar"], [:o, "baz"]],
|
133
|
+
:negative => [],
|
134
|
+
}
|
135
|
+
parser.parse("#foo +oo-o foo bar baz").should == {
|
136
|
+
:positive => [[:o, "foo"], [:o, "bar"]],
|
137
|
+
:negative => [[:o, "baz"]],
|
138
|
+
}
|
139
|
+
parser.parse("#foo -oo+o foo bar baz").should == {
|
140
|
+
:positive => [[:o, "baz"]],
|
141
|
+
:negative => [[:o, "foo"], [:o, "bar"]],
|
142
|
+
}
|
143
|
+
parser.parse("#foo +imv foo").should == {
|
144
|
+
:positive => [[:i, nil], [:m, nil], [:v, "foo"]],
|
145
|
+
:negative => [],
|
146
|
+
}
|
147
|
+
|
148
|
+
parser.parse("#foo +lk 10 foo").should == {
|
149
|
+
:positive => [[:l, "10"], [:k, "foo"]],
|
150
|
+
:negative => [],
|
151
|
+
}
|
152
|
+
parser.parse("#foo -l+k foo").should == {
|
153
|
+
:positive => [[:k, "foo"]],
|
154
|
+
:negative => [[:l, nil]],
|
155
|
+
}
|
156
|
+
parser.parse("#foo -q+ah foo bar baz").should == {
|
157
|
+
:positive => [[:a, "bar"], [:h, "baz"]],
|
158
|
+
:negative => [[:q, "foo"]],
|
159
|
+
}
|
160
|
+
parser.parse("#foo +Av foo").should == {
|
161
|
+
:positive => [[:A, nil], [:v, "foo"]],
|
162
|
+
:negative => [],
|
163
|
+
}
|
164
|
+
end
|
165
|
+
end
|