net-irc 0.0.3 → 0.0.4
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/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
|
+
|