net-irc 0.0.3 → 0.0.4
Sign up to get free protection for your applications and to get access to all the features.
- data/ChangeLog +7 -0
- data/examples/hatena-star-stream.rb +265 -0
- data/examples/lig.rb +3 -0
- data/examples/mixi.rb +236 -0
- data/examples/tig.rb +68 -21
- data/lib/net/irc.rb +6 -811
- data/lib/net/irc/client.rb +227 -0
- data/lib/net/irc/constants.rb +214 -0
- data/lib/net/irc/message.rb +100 -0
- data/lib/net/irc/message/modeparser.rb +44 -0
- data/lib/net/irc/message/modeparser/hyperion.rb +88 -0
- data/lib/net/irc/message/modeparser/rfc1459.rb +21 -0
- data/lib/net/irc/pattern.rb +68 -0
- data/lib/net/irc/server.rb +188 -0
- data/spec/modeparser_spec.rb +43 -0
- data/spec/net-irc_spec.rb +7 -7
- metadata +17 -3
@@ -0,0 +1,227 @@
|
|
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
|
+
@channels = {
|
14
|
+
# "#channel" => {
|
15
|
+
# :modes => [],
|
16
|
+
# :users => [],
|
17
|
+
# }
|
18
|
+
}
|
19
|
+
@channels.extend(MonitorMixin)
|
20
|
+
end
|
21
|
+
|
22
|
+
# Connect to server and start loop.
|
23
|
+
def start
|
24
|
+
@socket = TCPSocket.open(@host, @port)
|
25
|
+
on_connected
|
26
|
+
post PASS, @opts.pass if @opts.pass
|
27
|
+
post NICK, @opts.nick
|
28
|
+
post USER, @opts.user, "0", "*", @opts.real
|
29
|
+
while l = @socket.gets
|
30
|
+
begin
|
31
|
+
@log.debug "RECEIVE: #{l.chomp}"
|
32
|
+
m = Message.parse(l)
|
33
|
+
next if on_message(m) === true
|
34
|
+
name = "on_#{(COMMANDS[m.command.upcase] || m.command).downcase}"
|
35
|
+
send(name, m) if respond_to?(name)
|
36
|
+
rescue Exception => e
|
37
|
+
warn e
|
38
|
+
warn e.backtrace.join("\r\t")
|
39
|
+
raise
|
40
|
+
rescue Message::InvalidMessage
|
41
|
+
@log.error "MessageParse: " + l.inspect
|
42
|
+
end
|
43
|
+
end
|
44
|
+
rescue IOError
|
45
|
+
ensure
|
46
|
+
finish
|
47
|
+
end
|
48
|
+
|
49
|
+
# Close connection to server.
|
50
|
+
def finish
|
51
|
+
begin
|
52
|
+
@socket.close
|
53
|
+
rescue
|
54
|
+
end
|
55
|
+
on_disconnected
|
56
|
+
end
|
57
|
+
|
58
|
+
# Catch all messages.
|
59
|
+
# If this method return true, aother callback will not be called.
|
60
|
+
def on_message(m)
|
61
|
+
end
|
62
|
+
|
63
|
+
# Default RPL_WELCOME callback.
|
64
|
+
# This sets @prefix from the message.
|
65
|
+
def on_rpl_welcome(m)
|
66
|
+
@prefix = Prefix.new(m[1][/\S+$/])
|
67
|
+
end
|
68
|
+
|
69
|
+
# Default PING callback. Response PONG.
|
70
|
+
def on_ping(m)
|
71
|
+
post PONG, @prefix ? @prefix.nick : ""
|
72
|
+
end
|
73
|
+
|
74
|
+
# For managing channel
|
75
|
+
def on_rpl_namreply(m)
|
76
|
+
type = m[1]
|
77
|
+
channel = m[2]
|
78
|
+
init_channel(channel)
|
79
|
+
|
80
|
+
@channels.synchronize do
|
81
|
+
m[3].split(/\s+/).each do |u|
|
82
|
+
_, mode, nick = *u.match(/^([@+]?)(.+)/)
|
83
|
+
|
84
|
+
@channels[channel][:users] << nick
|
85
|
+
@channels[channel][:users].uniq!
|
86
|
+
|
87
|
+
case mode
|
88
|
+
when "@" # channel operator
|
89
|
+
@channels[channel][:modes] << [:o, nick]
|
90
|
+
when "+" # voiced (under moderating mode)
|
91
|
+
@channels[channel][:modes] << [:v, nick]
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
case type
|
96
|
+
when "@" # secret
|
97
|
+
@channels[channel][:modes] << [:s, nil]
|
98
|
+
when "*" # private
|
99
|
+
@channels[channel][:modes] << [:p, nil]
|
100
|
+
when "=" # public
|
101
|
+
end
|
102
|
+
|
103
|
+
@channels[channel][:modes].uniq!
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
# For managing channel
|
108
|
+
def on_part(m)
|
109
|
+
nick = m.prefix.nick
|
110
|
+
channel = m[0]
|
111
|
+
init_channel(channel)
|
112
|
+
|
113
|
+
@channels.synchronize do
|
114
|
+
info = @channels[channel]
|
115
|
+
if info
|
116
|
+
info[:users].delete(nick)
|
117
|
+
info[:modes].delete_if {|u|
|
118
|
+
u[1] == nick
|
119
|
+
}
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
# For managing channel
|
125
|
+
def on_quit(m)
|
126
|
+
nick = m.prefix.nick
|
127
|
+
|
128
|
+
@channels.synchronize do
|
129
|
+
@channels.each do |channel, info|
|
130
|
+
info[:users].delete(nick)
|
131
|
+
info[:modes].delete_if {|u|
|
132
|
+
u[1] == nick
|
133
|
+
}
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
# For managing channel
|
139
|
+
def on_kick(m)
|
140
|
+
users = m[1].split(/,/)
|
141
|
+
|
142
|
+
@channels.synchronize do
|
143
|
+
m[0].split(/,/).each do |chan|
|
144
|
+
init_channel(chan)
|
145
|
+
info = @channels[chan]
|
146
|
+
if info
|
147
|
+
users.each do |nick|
|
148
|
+
info[:users].delete(nick)
|
149
|
+
info[:modes].delete_if {|u|
|
150
|
+
u[1] == nick
|
151
|
+
}
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
# For managing channel
|
159
|
+
def on_join(m)
|
160
|
+
nick = m.prefix.nick
|
161
|
+
channel = m[0]
|
162
|
+
|
163
|
+
@channels.synchronize do
|
164
|
+
init_channel(channel)
|
165
|
+
|
166
|
+
@channels[channel][:users] << nick
|
167
|
+
@channels[channel][:users].uniq!
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
# For managing channel
|
172
|
+
def on_mode(m)
|
173
|
+
channel = m[0]
|
174
|
+
@channels.synchronize do
|
175
|
+
init_channel(channel)
|
176
|
+
|
177
|
+
mode = Message::ModeParser::RFC1459::Channel.parse(m)
|
178
|
+
mode[:negative].each do |m|
|
179
|
+
@channels[channel][:modes].delete(m)
|
180
|
+
end
|
181
|
+
|
182
|
+
mode[:positive].each do |m|
|
183
|
+
@channels[channel][:modes] << m
|
184
|
+
end
|
185
|
+
|
186
|
+
@channels[channel][:modes].uniq!
|
187
|
+
[mode[:negative], mode[:positive]]
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
# For managing channel
|
192
|
+
def init_channel(channel)
|
193
|
+
@channels[channel] ||= {
|
194
|
+
:modes => [],
|
195
|
+
:users => [],
|
196
|
+
}
|
197
|
+
end
|
198
|
+
|
199
|
+
# Do nothing.
|
200
|
+
# This is for avoiding error on calling super.
|
201
|
+
# So you can always call super at subclass.
|
202
|
+
def method_missing(name, *args)
|
203
|
+
end
|
204
|
+
|
205
|
+
# Call when socket connected.
|
206
|
+
def on_connected
|
207
|
+
end
|
208
|
+
|
209
|
+
# Call when socket closed.
|
210
|
+
def on_disconnected
|
211
|
+
end
|
212
|
+
|
213
|
+
private
|
214
|
+
|
215
|
+
# Post message to server.
|
216
|
+
#
|
217
|
+
# include Net::IRC::Constants
|
218
|
+
# post PRIVMSG, "#channel", "foobar"
|
219
|
+
def post(command, *params)
|
220
|
+
m = Message.new(nil, command, params.map {|s|
|
221
|
+
s ? s.gsub(/[\r\n]/, " ") : ""
|
222
|
+
})
|
223
|
+
|
224
|
+
@log.debug "SEND: #{m.to_s.chomp}"
|
225
|
+
@socket << m
|
226
|
+
end
|
227
|
+
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_BOUNCE = '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) => i)
|
213
|
+
}
|
214
|
+
|
@@ -0,0 +1,100 @@
|
|
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
|
+
str << " "
|
62
|
+
if !f && (param.size == 0 || / / =~ param || /^:/ =~ param)
|
63
|
+
str << ":#{param}"
|
64
|
+
f = true
|
65
|
+
else
|
66
|
+
str << param
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
str << "\x0D\x0A"
|
72
|
+
|
73
|
+
str
|
74
|
+
end
|
75
|
+
alias to_str to_s
|
76
|
+
|
77
|
+
# Same as params.
|
78
|
+
def to_a
|
79
|
+
@params
|
80
|
+
end
|
81
|
+
|
82
|
+
# If the message is CTCP, return true.
|
83
|
+
def ctcp?
|
84
|
+
message = @params[1]
|
85
|
+
message[0] == 1 && message[message.length-1] == 1
|
86
|
+
end
|
87
|
+
|
88
|
+
def inspect
|
89
|
+
'#<%s:0x%x prefix:%s command:%s params:%s>' % [
|
90
|
+
self.class,
|
91
|
+
self.object_id,
|
92
|
+
@prefix,
|
93
|
+
@command,
|
94
|
+
@params.inspect
|
95
|
+
]
|
96
|
+
end
|
97
|
+
|
98
|
+
autoload :ModeParser, "net/irc/message/modeparser"
|
99
|
+
end # Message
|
100
|
+
|