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,144 @@
|
|
1
|
+
|
2
|
+
module Net::IRC::Client::ChannelManager
|
3
|
+
# For managing channel
|
4
|
+
def on_rpl_namreply(m)
|
5
|
+
type = m[1]
|
6
|
+
channel = m[2]
|
7
|
+
init_channel(channel)
|
8
|
+
|
9
|
+
@channels.synchronize do
|
10
|
+
m[3].split(" ").each do |u|
|
11
|
+
_, mode, nick = *u.match(/\A([@+]?)(.+)/)
|
12
|
+
|
13
|
+
@channels[channel][:users] << nick
|
14
|
+
@channels[channel][:users].uniq!
|
15
|
+
|
16
|
+
op = @server_config.mode_parser.mark_to_op(mode)
|
17
|
+
if op
|
18
|
+
@channels[channel][:modes] << [op, nick]
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
case type
|
23
|
+
when "@" # secret
|
24
|
+
@channels[channel][:modes] << [:s, nil]
|
25
|
+
when "*" # private
|
26
|
+
@channels[channel][:modes] << [:p, nil]
|
27
|
+
when "=" # public
|
28
|
+
end
|
29
|
+
|
30
|
+
@channels[channel][:modes].uniq!
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
# For managing channel
|
35
|
+
def on_part(m)
|
36
|
+
nick = m.prefix.nick
|
37
|
+
channel = m[0]
|
38
|
+
init_channel(channel)
|
39
|
+
|
40
|
+
@channels.synchronize do
|
41
|
+
info = @channels[channel]
|
42
|
+
if info
|
43
|
+
info[:users].delete(nick)
|
44
|
+
info[:modes].delete_if {|u|
|
45
|
+
u[1] == nick
|
46
|
+
}
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
# For managing channel
|
52
|
+
def on_quit(m)
|
53
|
+
nick = m.prefix.nick
|
54
|
+
|
55
|
+
@channels.synchronize do
|
56
|
+
@channels.each do |channel, info|
|
57
|
+
info[:users].delete(nick)
|
58
|
+
info[:modes].delete_if {|u|
|
59
|
+
u[1] == nick
|
60
|
+
}
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
# For managing channel
|
66
|
+
def on_kick(m)
|
67
|
+
users = m[1].split(",")
|
68
|
+
|
69
|
+
@channels.synchronize do
|
70
|
+
m[0].split(",").each do |chan|
|
71
|
+
init_channel(chan)
|
72
|
+
info = @channels[chan]
|
73
|
+
if info
|
74
|
+
users.each do |nick|
|
75
|
+
info[:users].delete(nick)
|
76
|
+
info[:modes].delete_if {|u|
|
77
|
+
u[1] == nick
|
78
|
+
}
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
# For managing channel
|
86
|
+
def on_join(m)
|
87
|
+
nick = m.prefix.nick
|
88
|
+
channel = m[0]
|
89
|
+
|
90
|
+
@channels.synchronize do
|
91
|
+
init_channel(channel)
|
92
|
+
|
93
|
+
@channels[channel][:users] << nick
|
94
|
+
@channels[channel][:users].uniq!
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
# For managing channel
|
99
|
+
def on_nick(m)
|
100
|
+
oldnick = m.prefix.nick
|
101
|
+
newnick = m[0]
|
102
|
+
|
103
|
+
@channels.synchronize do
|
104
|
+
@channels.each do |channel, info|
|
105
|
+
info[:users].map! {|i|
|
106
|
+
(i == oldnick) ? newnick : i
|
107
|
+
}
|
108
|
+
info[:modes].map! {|mode, target|
|
109
|
+
(target == oldnick) ? [mode, newnick] : [mode, target]
|
110
|
+
}
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
# For managing channel
|
116
|
+
def on_mode(m)
|
117
|
+
channel = m[0]
|
118
|
+
@channels.synchronize do
|
119
|
+
init_channel(channel)
|
120
|
+
|
121
|
+
modes = @server_config.mode_parser.parse(m)
|
122
|
+
modes[:negative].each do |mode|
|
123
|
+
@channels[channel][:modes].delete(mode)
|
124
|
+
end
|
125
|
+
|
126
|
+
modes[:positive].each do |mode|
|
127
|
+
@channels[channel][:modes] << mode
|
128
|
+
end
|
129
|
+
|
130
|
+
@channels[channel][:modes].uniq!
|
131
|
+
[modes[:negative], modes[:positive]]
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
# For managing channel
|
136
|
+
def init_channel(channel)
|
137
|
+
@channels[channel] ||= {
|
138
|
+
:modes => [],
|
139
|
+
:users => [],
|
140
|
+
}
|
141
|
+
end
|
142
|
+
|
143
|
+
end
|
144
|
+
|
@@ -0,0 +1,117 @@
|
|
1
|
+
class Net::IRC::Client
|
2
|
+
include Net::IRC
|
3
|
+
include Constants
|
4
|
+
|
5
|
+
attr_reader :host, :port, :opts
|
6
|
+
attr_reader :prefix, :channels
|
7
|
+
|
8
|
+
def initialize(host, port, opts={})
|
9
|
+
@host = host
|
10
|
+
@port = port
|
11
|
+
@opts = OpenStruct.new(opts)
|
12
|
+
@log = @opts.logger || ::Logger.new($stdout)
|
13
|
+
@server_config = Message::ServerConfig.new
|
14
|
+
@channels = {
|
15
|
+
# "#channel" => {
|
16
|
+
# :modes => [],
|
17
|
+
# :users => [],
|
18
|
+
# }
|
19
|
+
}
|
20
|
+
@channels.extend(MonitorMixin)
|
21
|
+
end
|
22
|
+
|
23
|
+
# Connect to server and start loop.
|
24
|
+
def start
|
25
|
+
# reset config
|
26
|
+
@server_config = Message::ServerConfig.new
|
27
|
+
@socket = TCPSocket.new(@host, @port)
|
28
|
+
on_connected
|
29
|
+
post PASS, @opts.pass if @opts.pass
|
30
|
+
post NICK, @opts.nick
|
31
|
+
post USER, @opts.user, "0", "*", @opts.real
|
32
|
+
|
33
|
+
buffer = BufferedTokenizer.new("\r\n")
|
34
|
+
|
35
|
+
while data = @socket.readpartial(4096)
|
36
|
+
buffer.extract(data).each do |line|
|
37
|
+
l = "#{line}\r\n"
|
38
|
+
begin
|
39
|
+
@log.debug "RECEIVE: #{l.chomp}"
|
40
|
+
m = Message.parse(l)
|
41
|
+
next if on_message(m) === true
|
42
|
+
name = "on_#{(COMMANDS[m.command.upcase] || m.command).downcase}"
|
43
|
+
send(name, m) if respond_to?(name)
|
44
|
+
rescue Exception => e
|
45
|
+
warn e
|
46
|
+
warn e.backtrace.join("\r\t")
|
47
|
+
raise
|
48
|
+
rescue Message::InvalidMessage
|
49
|
+
@log.error "MessageParse: " + l.inspect
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
rescue IOError
|
54
|
+
ensure
|
55
|
+
finish
|
56
|
+
end
|
57
|
+
|
58
|
+
# Close connection to server.
|
59
|
+
def finish
|
60
|
+
begin
|
61
|
+
@socket.close
|
62
|
+
rescue
|
63
|
+
end
|
64
|
+
on_disconnected
|
65
|
+
end
|
66
|
+
|
67
|
+
# Catch all messages.
|
68
|
+
# If this method return true, aother callback will not be called.
|
69
|
+
def on_message(m)
|
70
|
+
end
|
71
|
+
|
72
|
+
# Default RPL_WELCOME callback.
|
73
|
+
# This sets @prefix from the message.
|
74
|
+
def on_rpl_welcome(m)
|
75
|
+
@prefix = Prefix.new(m[1][/\S+\z/])
|
76
|
+
end
|
77
|
+
|
78
|
+
# Default RPL_ISUPPORT callback.
|
79
|
+
# This detects server's configurations.
|
80
|
+
def on_rpl_isupport(m)
|
81
|
+
@server_config.set(m)
|
82
|
+
end
|
83
|
+
|
84
|
+
# Default PING callback. Response PONG.
|
85
|
+
def on_ping(m)
|
86
|
+
post PONG, @prefix ? @prefix.nick : ""
|
87
|
+
end
|
88
|
+
|
89
|
+
# Call when socket connected.
|
90
|
+
def on_connected
|
91
|
+
end
|
92
|
+
|
93
|
+
# Call when socket closed.
|
94
|
+
def on_disconnected
|
95
|
+
end
|
96
|
+
|
97
|
+
private
|
98
|
+
|
99
|
+
# Post message to server.
|
100
|
+
#
|
101
|
+
# include Net::IRC::Constants
|
102
|
+
# post PRIVMSG, "#channel", "foobar"
|
103
|
+
def post(command, *params)
|
104
|
+
m = Message.new(nil, command, params.map {|s|
|
105
|
+
if s
|
106
|
+
s.force_encoding("ASCII-8BIT") if s.respond_to? :force_encoding
|
107
|
+
#s.gsub(/\r\n|[\r\n]/, " ")
|
108
|
+
s.tr("\r\n", " ")
|
109
|
+
else
|
110
|
+
""
|
111
|
+
end
|
112
|
+
})
|
113
|
+
|
114
|
+
@log.debug "SEND: #{m.to_s.chomp}"
|
115
|
+
@socket << m.to_s
|
116
|
+
end
|
117
|
+
end # Client
|
@@ -0,0 +1,214 @@
|
|
1
|
+
module Net::IRC::Constants # :nodoc:
|
2
|
+
RPL_WELCOME = '001'
|
3
|
+
RPL_YOURHOST = '002'
|
4
|
+
RPL_CREATED = '003'
|
5
|
+
RPL_MYINFO = '004'
|
6
|
+
RPL_ISUPPORT = '005'
|
7
|
+
RPL_USERHOST = '302'
|
8
|
+
RPL_ISON = '303'
|
9
|
+
RPL_AWAY = '301'
|
10
|
+
RPL_UNAWAY = '305'
|
11
|
+
RPL_NOWAWAY = '306'
|
12
|
+
RPL_WHOISUSER = '311'
|
13
|
+
RPL_WHOISSERVER = '312'
|
14
|
+
RPL_WHOISOPERATOR = '313'
|
15
|
+
RPL_WHOISIDLE = '317'
|
16
|
+
RPL_ENDOFWHOIS = '318'
|
17
|
+
RPL_WHOISCHANNELS = '319'
|
18
|
+
RPL_WHOWASUSER = '314'
|
19
|
+
RPL_ENDOFWHOWAS = '369'
|
20
|
+
RPL_LISTSTART = '321'
|
21
|
+
RPL_LIST = '322'
|
22
|
+
RPL_LISTEND = '323'
|
23
|
+
RPL_UNIQOPIS = '325'
|
24
|
+
RPL_CHANNELMODEIS = '324'
|
25
|
+
RPL_NOTOPIC = '331'
|
26
|
+
RPL_TOPIC = '332'
|
27
|
+
RPL_INVITING = '341'
|
28
|
+
RPL_SUMMONING = '342'
|
29
|
+
RPL_INVITELIST = '346'
|
30
|
+
RPL_ENDOFINVITELIST = '347'
|
31
|
+
RPL_EXCEPTLIST = '348'
|
32
|
+
RPL_ENDOFEXCEPTLIST = '349'
|
33
|
+
RPL_VERSION = '351'
|
34
|
+
RPL_WHOREPLY = '352'
|
35
|
+
RPL_ENDOFWHO = '315'
|
36
|
+
RPL_NAMREPLY = '353'
|
37
|
+
RPL_ENDOFNAMES = '366'
|
38
|
+
RPL_LINKS = '364'
|
39
|
+
RPL_ENDOFLINKS = '365'
|
40
|
+
RPL_BANLIST = '367'
|
41
|
+
RPL_ENDOFBANLIST = '368'
|
42
|
+
RPL_INFO = '371'
|
43
|
+
RPL_ENDOFINFO = '374'
|
44
|
+
RPL_MOTDSTART = '375'
|
45
|
+
RPL_MOTD = '372'
|
46
|
+
RPL_ENDOFMOTD = '376'
|
47
|
+
RPL_YOUREOPER = '381'
|
48
|
+
RPL_REHASHING = '382'
|
49
|
+
RPL_YOURESERVICE = '383'
|
50
|
+
RPL_TIME = '391'
|
51
|
+
RPL_USERSSTART = '392'
|
52
|
+
RPL_USERS = '393'
|
53
|
+
RPL_ENDOFUSERS = '394'
|
54
|
+
RPL_NOUSERS = '395'
|
55
|
+
RPL_TRACELINK = '200'
|
56
|
+
RPL_TRACECONNECTING = '201'
|
57
|
+
RPL_TRACEHANDSHAKE = '202'
|
58
|
+
RPL_TRACEUNKNOWN = '203'
|
59
|
+
RPL_TRACEOPERATOR = '204'
|
60
|
+
RPL_TRACEUSER = '205'
|
61
|
+
RPL_TRACESERVER = '206'
|
62
|
+
RPL_TRACESERVICE = '207'
|
63
|
+
RPL_TRACENEWTYPE = '208'
|
64
|
+
RPL_TRACECLASS = '209'
|
65
|
+
RPL_TRACERECONNECT = '210'
|
66
|
+
RPL_TRACELOG = '261'
|
67
|
+
RPL_TRACEEND = '262'
|
68
|
+
RPL_STATSLINKINFO = '211'
|
69
|
+
RPL_STATSCOMMANDS = '212'
|
70
|
+
RPL_ENDOFSTATS = '219'
|
71
|
+
RPL_STATSUPTIME = '242'
|
72
|
+
RPL_STATSOLINE = '243'
|
73
|
+
RPL_UMODEIS = '221'
|
74
|
+
RPL_SERVLIST = '234'
|
75
|
+
RPL_SERVLISTEND = '235'
|
76
|
+
RPL_LUSERCLIENT = '251'
|
77
|
+
RPL_LUSEROP = '252'
|
78
|
+
RPL_LUSERUNKNOWN = '253'
|
79
|
+
RPL_LUSERCHANNELS = '254'
|
80
|
+
RPL_LUSERME = '255'
|
81
|
+
RPL_ADMINME = '256'
|
82
|
+
RPL_ADMINLOC1 = '257'
|
83
|
+
RPL_ADMINLOC2 = '258'
|
84
|
+
RPL_ADMINEMAIL = '259'
|
85
|
+
RPL_TRYAGAIN = '263'
|
86
|
+
ERR_NOSUCHNICK = '401'
|
87
|
+
ERR_NOSUCHSERVER = '402'
|
88
|
+
ERR_NOSUCHCHANNEL = '403'
|
89
|
+
ERR_CANNOTSENDTOCHAN = '404'
|
90
|
+
ERR_TOOMANYCHANNELS = '405'
|
91
|
+
ERR_WASNOSUCHNICK = '406'
|
92
|
+
ERR_TOOMANYTARGETS = '407'
|
93
|
+
ERR_NOSUCHSERVICE = '408'
|
94
|
+
ERR_NOORIGIN = '409'
|
95
|
+
ERR_NORECIPIENT = '411'
|
96
|
+
ERR_NOTEXTTOSEND = '412'
|
97
|
+
ERR_NOTOPLEVEL = '413'
|
98
|
+
ERR_WILDTOPLEVEL = '414'
|
99
|
+
ERR_BADMASK = '415'
|
100
|
+
ERR_UNKNOWNCOMMAND = '421'
|
101
|
+
ERR_NOMOTD = '422'
|
102
|
+
ERR_NOADMININFO = '423'
|
103
|
+
ERR_FILEERROR = '424'
|
104
|
+
ERR_NONICKNAMEGIVEN = '431'
|
105
|
+
ERR_ERRONEUSNICKNAME = '432'
|
106
|
+
ERR_NICKNAMEINUSE = '433'
|
107
|
+
ERR_NICKCOLLISION = '436'
|
108
|
+
ERR_UNAVAILRESOURCE = '437'
|
109
|
+
ERR_USERNOTINCHANNEL = '441'
|
110
|
+
ERR_NOTONCHANNEL = '442'
|
111
|
+
ERR_USERONCHANNEL = '443'
|
112
|
+
ERR_NOLOGIN = '444'
|
113
|
+
ERR_SUMMONDISABLED = '445'
|
114
|
+
ERR_USERSDISABLED = '446'
|
115
|
+
ERR_NOTREGISTERED = '451'
|
116
|
+
ERR_NEEDMOREPARAMS = '461'
|
117
|
+
ERR_ALREADYREGISTRED = '462'
|
118
|
+
ERR_NOPERMFORHOST = '463'
|
119
|
+
ERR_PASSWDMISMATCH = '464'
|
120
|
+
ERR_YOUREBANNEDCREEP = '465'
|
121
|
+
ERR_YOUWILLBEBANNED = '466'
|
122
|
+
ERR_KEYSET = '467'
|
123
|
+
ERR_CHANNELISFULL = '471'
|
124
|
+
ERR_UNKNOWNMODE = '472'
|
125
|
+
ERR_INVITEONLYCHAN = '473'
|
126
|
+
ERR_BANNEDFROMCHAN = '474'
|
127
|
+
ERR_BADCHANNELKEY = '475'
|
128
|
+
ERR_BADCHANMASK = '476'
|
129
|
+
ERR_NOCHANMODES = '477'
|
130
|
+
ERR_BANLISTFULL = '478'
|
131
|
+
ERR_NOPRIVILEGES = '481'
|
132
|
+
ERR_CHANOPRIVSNEEDED = '482'
|
133
|
+
ERR_CANTKILLSERVER = '483'
|
134
|
+
ERR_RESTRICTED = '484'
|
135
|
+
ERR_UNIQOPPRIVSNEEDED = '485'
|
136
|
+
ERR_NOOPERHOST = '491'
|
137
|
+
ERR_UMODEUNKNOWNFLAG = '501'
|
138
|
+
ERR_USERSDONTMATCH = '502'
|
139
|
+
RPL_SERVICEINFO = '231'
|
140
|
+
RPL_ENDOFSERVICES = '232'
|
141
|
+
RPL_SERVICE = '233'
|
142
|
+
RPL_NONE = '300'
|
143
|
+
RPL_WHOISCHANOP = '316'
|
144
|
+
RPL_KILLDONE = '361'
|
145
|
+
RPL_CLOSING = '362'
|
146
|
+
RPL_CLOSEEND = '363'
|
147
|
+
RPL_INFOSTART = '373'
|
148
|
+
RPL_MYPORTIS = '384'
|
149
|
+
RPL_STATSCLINE = '213'
|
150
|
+
RPL_STATSNLINE = '214'
|
151
|
+
RPL_STATSILINE = '215'
|
152
|
+
RPL_STATSKLINE = '216'
|
153
|
+
RPL_STATSQLINE = '217'
|
154
|
+
RPL_STATSYLINE = '218'
|
155
|
+
RPL_STATSVLINE = '240'
|
156
|
+
RPL_STATSLLINE = '241'
|
157
|
+
RPL_STATSHLINE = '244'
|
158
|
+
RPL_STATSSLINE = '244'
|
159
|
+
RPL_STATSPING = '246'
|
160
|
+
RPL_STATSBLINE = '247'
|
161
|
+
RPL_STATSDLINE = '250'
|
162
|
+
ERR_NOSERVICEHOST = '492'
|
163
|
+
|
164
|
+
PASS = 'PASS'
|
165
|
+
NICK = 'NICK'
|
166
|
+
USER = 'USER'
|
167
|
+
OPER = 'OPER'
|
168
|
+
MODE = 'MODE'
|
169
|
+
SERVICE = 'SERVICE'
|
170
|
+
QUIT = 'QUIT'
|
171
|
+
SQUIT = 'SQUIT'
|
172
|
+
JOIN = 'JOIN'
|
173
|
+
PART = 'PART'
|
174
|
+
TOPIC = 'TOPIC'
|
175
|
+
NAMES = 'NAMES'
|
176
|
+
LIST = 'LIST'
|
177
|
+
INVITE = 'INVITE'
|
178
|
+
KICK = 'KICK'
|
179
|
+
PRIVMSG = 'PRIVMSG'
|
180
|
+
NOTICE = 'NOTICE'
|
181
|
+
MOTD = 'MOTD'
|
182
|
+
LUSERS = 'LUSERS'
|
183
|
+
VERSION = 'VERSION'
|
184
|
+
STATS = 'STATS'
|
185
|
+
LINKS = 'LINKS'
|
186
|
+
TIME = 'TIME'
|
187
|
+
CONNECT = 'CONNECT'
|
188
|
+
TRACE = 'TRACE'
|
189
|
+
ADMIN = 'ADMIN'
|
190
|
+
INFO = 'INFO'
|
191
|
+
SERVLIST = 'SERVLIST'
|
192
|
+
SQUERY = 'SQUERY'
|
193
|
+
WHO = 'WHO'
|
194
|
+
WHOIS = 'WHOIS'
|
195
|
+
WHOWAS = 'WHOWAS'
|
196
|
+
KILL = 'KILL'
|
197
|
+
PING = 'PING'
|
198
|
+
PONG = 'PONG'
|
199
|
+
ERROR = 'ERROR'
|
200
|
+
AWAY = 'AWAY'
|
201
|
+
REHASH = 'REHASH'
|
202
|
+
DIE = 'DIE'
|
203
|
+
RESTART = 'RESTART'
|
204
|
+
SUMMON = 'SUMMON'
|
205
|
+
USERS = 'USERS'
|
206
|
+
WALLOPS = 'WALLOPS'
|
207
|
+
USERHOST = 'USERHOST'
|
208
|
+
ISON = 'ISON'
|
209
|
+
end
|
210
|
+
|
211
|
+
Net::IRC::COMMANDS = Net::IRC::Constants.constants.inject({}) {|r, i| # :nodoc:
|
212
|
+
r.update(Net::IRC::Constants.const_get(i).to_s => i.to_s.freeze)
|
213
|
+
}
|
214
|
+
|
@@ -0,0 +1,85 @@
|
|
1
|
+
class Net::IRC::Message::ModeParser
|
2
|
+
|
3
|
+
ONE_PARAM_MASK = 0
|
4
|
+
ONE_PARAM = 1
|
5
|
+
ONE_PARAM_FOR_POSITIVE = 2
|
6
|
+
NO_PARAM = 3
|
7
|
+
|
8
|
+
def initialize
|
9
|
+
@modes = {}
|
10
|
+
@op_to_mark_map = {}
|
11
|
+
@mark_to_op_map = {}
|
12
|
+
|
13
|
+
# Initialize for ircd 2.11 (RFC2812+)
|
14
|
+
set(:CHANMODES, 'beIR,k,l,imnpstaqr')
|
15
|
+
set(:PREFIX, '(ov)@+')
|
16
|
+
end
|
17
|
+
|
18
|
+
def mark_to_op(mark)
|
19
|
+
mark.empty? ? nil : @mark_to_op_map[mark.to_sym]
|
20
|
+
end
|
21
|
+
|
22
|
+
def set(key, value)
|
23
|
+
case key
|
24
|
+
when :PREFIX
|
25
|
+
if value =~ /\A\(([a-zA-Z]+)\)(.+)\z/
|
26
|
+
@op_to_mark_map = {}
|
27
|
+
key, value = Regexp.last_match[1], Regexp.last_match[2]
|
28
|
+
key.scan(/./).zip(value.scan(/./)) {|pair|
|
29
|
+
@op_to_mark_map[pair[0].to_sym] = pair[1].to_sym
|
30
|
+
}
|
31
|
+
@mark_to_op_map = @op_to_mark_map.invert
|
32
|
+
end
|
33
|
+
when :CHANMODES
|
34
|
+
@modes = {}
|
35
|
+
value.split(",").each_with_index do |s, kind|
|
36
|
+
s.scan(/./).each {|c|
|
37
|
+
@modes[c.to_sym] = kind
|
38
|
+
}
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def parse(arg)
|
44
|
+
params = arg.kind_of?(Net::IRC::Message) ? arg.to_a : arg.split(" ")
|
45
|
+
params.shift
|
46
|
+
|
47
|
+
ret = {
|
48
|
+
:positive => [],
|
49
|
+
:negative => [],
|
50
|
+
}
|
51
|
+
|
52
|
+
current = ret[:positive]
|
53
|
+
until params.empty?
|
54
|
+
s = params.shift
|
55
|
+
s.scan(/./).each do |c|
|
56
|
+
c = c.to_sym
|
57
|
+
case c
|
58
|
+
when :+
|
59
|
+
current = ret[:positive]
|
60
|
+
when :-
|
61
|
+
current = ret[:negative]
|
62
|
+
else
|
63
|
+
case @modes[c]
|
64
|
+
when ONE_PARAM_MASK,ONE_PARAM
|
65
|
+
current << [c, params.shift]
|
66
|
+
when ONE_PARAM_FOR_POSITIVE
|
67
|
+
if current.equal?(ret[:positive])
|
68
|
+
current << [c, params.shift]
|
69
|
+
else
|
70
|
+
current << [c, nil]
|
71
|
+
end
|
72
|
+
when NO_PARAM
|
73
|
+
current << [c, nil]
|
74
|
+
else
|
75
|
+
if @op_to_mark_map[c]
|
76
|
+
current << [c, params.shift]
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
ret
|
84
|
+
end
|
85
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
class Net::IRC::Message::ServerConfig
|
2
|
+
attr_reader :mode_parser
|
3
|
+
|
4
|
+
def initialize
|
5
|
+
@config = {}
|
6
|
+
@mode_parser = Net::IRC::Message::ModeParser.new
|
7
|
+
end
|
8
|
+
|
9
|
+
def set(arg)
|
10
|
+
params = arg.kind_of?(Net::IRC::Message) ? arg.to_a : arg.split(" ")
|
11
|
+
|
12
|
+
params[1..-1].each do |s|
|
13
|
+
case s
|
14
|
+
when /\A:?are supported by this server\z/
|
15
|
+
# Ignore
|
16
|
+
when /\A([^=]+)=(.*)\z/
|
17
|
+
key = Regexp.last_match[1].to_sym
|
18
|
+
value = Regexp.last_match[2]
|
19
|
+
@config[key] = value
|
20
|
+
@mode_parser.set(key, value) if key == :CHANMODES || key == :PREFIX
|
21
|
+
else
|
22
|
+
@config[s] = true
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def [](key)
|
28
|
+
@config[key]
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,109 @@
|
|
1
|
+
class Net::IRC::Message
|
2
|
+
include Net::IRC
|
3
|
+
|
4
|
+
class InvalidMessage < Net::IRC::IRCException; end
|
5
|
+
|
6
|
+
attr_reader :prefix, :command, :params
|
7
|
+
|
8
|
+
# Parse string and return new Message.
|
9
|
+
# If the string is invalid message, this method raises Net::IRC::Message::InvalidMessage.
|
10
|
+
def self.parse(str)
|
11
|
+
_, prefix, command, *rest = *PATTERN::MESSAGE_PATTERN.match(str)
|
12
|
+
raise InvalidMessage, "Invalid message: #{str.dump}" unless _
|
13
|
+
|
14
|
+
case
|
15
|
+
when rest[0] && !rest[0].empty?
|
16
|
+
middle, trailer, = *rest
|
17
|
+
when rest[2] && !rest[2].empty?
|
18
|
+
middle, trailer, = *rest[2, 2]
|
19
|
+
when rest[1]
|
20
|
+
params = []
|
21
|
+
trailer = rest[1]
|
22
|
+
when rest[3]
|
23
|
+
params = []
|
24
|
+
trailer = rest[3]
|
25
|
+
else
|
26
|
+
params = []
|
27
|
+
end
|
28
|
+
|
29
|
+
params ||= middle.split(/ /)[1..-1]
|
30
|
+
params << trailer if trailer
|
31
|
+
|
32
|
+
new(prefix, command, params)
|
33
|
+
end
|
34
|
+
|
35
|
+
def initialize(prefix, command, params)
|
36
|
+
@prefix = Prefix.new(prefix.to_s)
|
37
|
+
@command = command
|
38
|
+
@params = params
|
39
|
+
end
|
40
|
+
|
41
|
+
# Same as @params[n].
|
42
|
+
def [](n)
|
43
|
+
@params[n]
|
44
|
+
end
|
45
|
+
|
46
|
+
# Iterate params.
|
47
|
+
def each(&block)
|
48
|
+
@params.each(&block)
|
49
|
+
end
|
50
|
+
|
51
|
+
# Stringfy message to raw IRC message.
|
52
|
+
def to_s
|
53
|
+
str = ""
|
54
|
+
|
55
|
+
str << ":#{@prefix} " unless @prefix.empty?
|
56
|
+
str << @command
|
57
|
+
|
58
|
+
if @params
|
59
|
+
f = false
|
60
|
+
@params.each do |param|
|
61
|
+
f = !f && (param.empty? || param[0] == ?: || param.include?(" "))
|
62
|
+
str << " "
|
63
|
+
str << ":" if f
|
64
|
+
str << param
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
str << "\x0D\x0A"
|
69
|
+
|
70
|
+
str
|
71
|
+
end
|
72
|
+
alias to_str to_s
|
73
|
+
|
74
|
+
# Same as params.
|
75
|
+
def to_a
|
76
|
+
@params.dup
|
77
|
+
end
|
78
|
+
|
79
|
+
# If the message is CTCP, return true.
|
80
|
+
def ctcp?
|
81
|
+
#message = @params[1]
|
82
|
+
#message[0] == ?\01 && message[-1] == ?\01
|
83
|
+
/\x01(?>[^\x00\x01\r\n]*)\x01/ === @params[1]
|
84
|
+
end
|
85
|
+
|
86
|
+
def ctcps
|
87
|
+
messages = []
|
88
|
+
@params[1].gsub!(/\x01(?>[^\x00\x01\r\n]*)\x01/) do
|
89
|
+
messages << ctcp_decode($&)
|
90
|
+
""
|
91
|
+
end
|
92
|
+
messages
|
93
|
+
end
|
94
|
+
|
95
|
+
def inspect
|
96
|
+
"#<%s:0x%x prefix:%s command:%s params:%s>" % [
|
97
|
+
self.class,
|
98
|
+
self.object_id,
|
99
|
+
@prefix,
|
100
|
+
@command,
|
101
|
+
@params.inspect
|
102
|
+
]
|
103
|
+
end
|
104
|
+
|
105
|
+
autoload :ModeParser, "net/irc/message/modeparser"
|
106
|
+
autoload :ServerConfig, "net/irc/message/serverconfig"
|
107
|
+
#autoload :ISupportModeParser, "net/irc/message/isupportmodeparser"
|
108
|
+
end # Message
|
109
|
+
|