konn-rupircd 0.6.0 → 0.6.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/Manifest +21 -0
- data/Rakefile +16 -0
- data/VERSION +1 -0
- data/changes.html +41 -0
- data/changes.ja.html +41 -0
- data/index.html +75 -0
- data/index.ja.html +72 -0
- data/info.txt +54 -0
- data/ircd.rb +26 -0
- data/lib/rupircd/channel.rb +418 -0
- data/lib/rupircd/conf.rb +26 -0
- data/lib/rupircd/message.rb +254 -0
- data/lib/rupircd/server.rb +677 -0
- data/lib/rupircd/user.rb +103 -0
- data/lib/rupircd/utils.rb +32 -0
- data/lib/rupircd.rb +12 -0
- data/mkpassword.rb +3 -0
- data/motd.txt +16 -0
- data/rupircd.gemspec +72 -0
- data/sample.conf +10 -0
- data/usage.html +112 -0
- data/usage.ja.html +112 -0
- metadata +25 -1
@@ -0,0 +1,418 @@
|
|
1
|
+
=begin
|
2
|
+
= rupircd -- Ruby Pseudo IRC Deamon
|
3
|
+
|
4
|
+
ver 0.6 2009-08-11T23:45:52+09:00
|
5
|
+
|
6
|
+
Copyright (c) 2009 Hiromi Ishii
|
7
|
+
|
8
|
+
You can redistribute it and/or modify it under the same term as Ruby.
|
9
|
+
=end
|
10
|
+
|
11
|
+
require 'rupircd/utils'
|
12
|
+
require 'rupircd/message'
|
13
|
+
|
14
|
+
module IRCd
|
15
|
+
|
16
|
+
class ChannelMode
|
17
|
+
include Utils
|
18
|
+
attr_accessor :bans, :topic_op_only, :ops, :invite, :voices, :anony, :moderate, :n,
|
19
|
+
:quiet, :private, :secret, :reops, :key, :user_max, :excepts,
|
20
|
+
:dont_need_invite, :creators
|
21
|
+
|
22
|
+
def initialize(server, chname)
|
23
|
+
@chname = chname
|
24
|
+
@server = server
|
25
|
+
@bans = []
|
26
|
+
@topic_op_only = false
|
27
|
+
@creators = []
|
28
|
+
@ops = []
|
29
|
+
@invite = false
|
30
|
+
@voices = []
|
31
|
+
@anony = false
|
32
|
+
@moderate = false
|
33
|
+
@n = false
|
34
|
+
@quiet = false
|
35
|
+
@private = false
|
36
|
+
@secret = false
|
37
|
+
@reops = []
|
38
|
+
@key = ""
|
39
|
+
@user_max = -1
|
40
|
+
@excepts = []
|
41
|
+
@invite_onlys = []
|
42
|
+
end
|
43
|
+
|
44
|
+
def set_flags(who, flags, param)
|
45
|
+
args = param.dup
|
46
|
+
toggle = flags[0] == ?+
|
47
|
+
rpl = [flags[0,1]]
|
48
|
+
flags = flags[1..3]
|
49
|
+
flags.each_byte{|b|
|
50
|
+
rpl.push b.chr
|
51
|
+
case b
|
52
|
+
when ?n
|
53
|
+
@n = toggle
|
54
|
+
when ?i
|
55
|
+
@invite = toggle
|
56
|
+
when ?a
|
57
|
+
@anony = toggle
|
58
|
+
when ?m
|
59
|
+
@moderate = toggle
|
60
|
+
when ?p
|
61
|
+
@secret = false if toggle
|
62
|
+
@private = toggle
|
63
|
+
when ?s
|
64
|
+
@private = false if toggle
|
65
|
+
@secret = toggle
|
66
|
+
when ?k
|
67
|
+
key = param.shift
|
68
|
+
if toggle
|
69
|
+
@key = key
|
70
|
+
elsif key == @key
|
71
|
+
@key = ""
|
72
|
+
end
|
73
|
+
when ?l
|
74
|
+
@user_max = param.shift.to_i
|
75
|
+
when ?r
|
76
|
+
if toggle
|
77
|
+
@reops << mask_to_regex(param.shift)
|
78
|
+
else
|
79
|
+
@reops.delete(mask_to_regex(param.shift))
|
80
|
+
end
|
81
|
+
when ?t
|
82
|
+
@topic_op_only = toggle
|
83
|
+
when ?o
|
84
|
+
user = @server.user_from_nick(param.shift)
|
85
|
+
if toggle
|
86
|
+
@ops << user
|
87
|
+
@ops.compact!
|
88
|
+
@ops.uniq!
|
89
|
+
else
|
90
|
+
@ops.delete(user)
|
91
|
+
end
|
92
|
+
when ?v
|
93
|
+
user = @server.user_from_nick(param.shift)
|
94
|
+
if toggle
|
95
|
+
@voices << user
|
96
|
+
@voices.uniq!
|
97
|
+
@voices.compact!
|
98
|
+
else
|
99
|
+
@voices.delete(user)
|
100
|
+
end
|
101
|
+
when ?b, ?e, ?I
|
102
|
+
masks = param.shift
|
103
|
+
mask_list = []
|
104
|
+
list_msg = ""
|
105
|
+
endof_msg = ""
|
106
|
+
what = ""
|
107
|
+
case b
|
108
|
+
when ?b
|
109
|
+
mask_list = @bans
|
110
|
+
list_msg = "367"
|
111
|
+
endof_msg = "368"
|
112
|
+
what = "ban"
|
113
|
+
when ?e
|
114
|
+
mask_list = @excepts
|
115
|
+
list_msg = "348"
|
116
|
+
endof_msg = "349"
|
117
|
+
what = "exception"
|
118
|
+
when ?I
|
119
|
+
mask_list = @invite_onlys
|
120
|
+
list_msg = "346"
|
121
|
+
endof_msg = "347"
|
122
|
+
what = "invite"
|
123
|
+
end
|
124
|
+
if masks
|
125
|
+
if toggle
|
126
|
+
mask_list << mask_to_regex(masks)
|
127
|
+
else
|
128
|
+
mask_list.delete(mask_to_regex(masks))
|
129
|
+
end
|
130
|
+
else
|
131
|
+
rpl.pop
|
132
|
+
mask_list.each{|msk|
|
133
|
+
@server.send_server_message(who, list_msg, @chname, msk)
|
134
|
+
}
|
135
|
+
@server.send_server_message(who, endof_msg, @chname, "End of channel #{what} list")
|
136
|
+
end
|
137
|
+
else
|
138
|
+
@server.send_server_message(who, "472", rpl.pop, "is unknown mode char to me for #@chname")
|
139
|
+
end
|
140
|
+
}
|
141
|
+
[rpl.join(""), args].flatten if rpl.size > 1
|
142
|
+
end
|
143
|
+
|
144
|
+
def proc_mask(toggle, rpl, param, mask_list, reply_msg, endof_list, name)
|
145
|
+
masks = param.shift
|
146
|
+
if masks
|
147
|
+
if toggle
|
148
|
+
mask_list << mask_to_regex(masks)
|
149
|
+
else
|
150
|
+
mask_list.delete(mask_to_regex(masks))
|
151
|
+
end
|
152
|
+
else
|
153
|
+
rpl.pop
|
154
|
+
mask_list.each{|msk|
|
155
|
+
@server.send_server_message(who, reply_msg, @chname, msk)
|
156
|
+
}
|
157
|
+
@server.send_server_message(who, endof_list, @chname, "End of channel #{name} list")
|
158
|
+
end
|
159
|
+
end
|
160
|
+
private :proc_mask
|
161
|
+
|
162
|
+
def get_flags
|
163
|
+
params = []
|
164
|
+
flg = "+"
|
165
|
+
flg << "n" if @n
|
166
|
+
flg << "i" if @invite
|
167
|
+
flg << "a" if @anony
|
168
|
+
flg << "m" if @moderate
|
169
|
+
flg << "q" if @quiet
|
170
|
+
flg << "p" if @private
|
171
|
+
flg << "s" if @secret
|
172
|
+
flg << "k" unless @key.empty?
|
173
|
+
flg << "t" if @topic_op_only
|
174
|
+
if @user_max > -1
|
175
|
+
flg << "l"
|
176
|
+
params << @user_max
|
177
|
+
end
|
178
|
+
params.unshift flg
|
179
|
+
end
|
180
|
+
|
181
|
+
def add_op(hoge)
|
182
|
+
@ops.push hoge
|
183
|
+
end
|
184
|
+
|
185
|
+
def unregister(who)
|
186
|
+
@ops.delete(who)
|
187
|
+
@voices.delete(who)
|
188
|
+
end
|
189
|
+
|
190
|
+
def op?(who)
|
191
|
+
@ops.include?(who)
|
192
|
+
end
|
193
|
+
|
194
|
+
def voiced?(who)
|
195
|
+
@voices.include?(who)
|
196
|
+
end
|
197
|
+
|
198
|
+
def can_change_topic?(who)
|
199
|
+
!@topic_op_only || op?(who)
|
200
|
+
end
|
201
|
+
|
202
|
+
def can_talk?(who)
|
203
|
+
!@moderate || op?(who) || @voices.include?(who)
|
204
|
+
end
|
205
|
+
|
206
|
+
def can_invite?(who)
|
207
|
+
!@invite || op?(who)
|
208
|
+
end
|
209
|
+
|
210
|
+
def banned?(who)
|
211
|
+
@bans.any?{|mask|
|
212
|
+
who.to_s =~ mask
|
213
|
+
}
|
214
|
+
end
|
215
|
+
|
216
|
+
end
|
217
|
+
|
218
|
+
class Channel
|
219
|
+
attr_reader :name, :members, :mode
|
220
|
+
|
221
|
+
def initialize(server, who, chname)
|
222
|
+
@server = server
|
223
|
+
@mode = ChannelMode.new(server, chname)
|
224
|
+
@name = chname
|
225
|
+
@topic = ""
|
226
|
+
@members = []
|
227
|
+
@invited = []
|
228
|
+
end
|
229
|
+
|
230
|
+
def join(who, key="")
|
231
|
+
if @mode.banned?(who)
|
232
|
+
["474", @name, "Cannot join channel (+b)"]
|
233
|
+
elsif @mode.invite && !@invited.include?(who)
|
234
|
+
["473", @name, "Cannot join channel (+i)"]
|
235
|
+
elsif !@mode.key.empty? && @mode.key != key
|
236
|
+
["475", @name, "Cannot join channel (+k)"]
|
237
|
+
elsif @mode.user_max >= @members.size
|
238
|
+
["471", @name, "Cannot join channel (+l)"]
|
239
|
+
elsif !joined?(who)
|
240
|
+
who.joined_channels.push self
|
241
|
+
@members.unshift who
|
242
|
+
|
243
|
+
@mode.add_op who if @members.size == 1
|
244
|
+
sends = []
|
245
|
+
sends << get_topic(who)
|
246
|
+
sends += names(who)
|
247
|
+
@server.send_client_message(who, who, Command::JOIN, @name)
|
248
|
+
@server.handle_reply(who, sends)
|
249
|
+
send_to_other_members(who, Command::JOIN, @name)
|
250
|
+
return nil
|
251
|
+
end
|
252
|
+
end
|
253
|
+
|
254
|
+
def kick(who, by, msg="no reason")
|
255
|
+
if !@members.include?(by)
|
256
|
+
["442", @name, "You're not on that channel"]
|
257
|
+
elsif !@members.include?(who)
|
258
|
+
["441", who.nick, @name, "They aren't on that channel"]
|
259
|
+
elsif @mode.op?(by)
|
260
|
+
send_to_members(by, Command::KICK, @name, who.nick, msg)
|
261
|
+
unregister(who)
|
262
|
+
nil
|
263
|
+
else
|
264
|
+
["482", @name, "You're not channel operator"]
|
265
|
+
end
|
266
|
+
end
|
267
|
+
|
268
|
+
def send_to_members(user, command, *args)
|
269
|
+
@members.each{|mem|
|
270
|
+
@server.send_client_message(mem, user, command, *args)
|
271
|
+
}
|
272
|
+
end
|
273
|
+
|
274
|
+
def send_to_other_members(user, command, *args)
|
275
|
+
(@members-[user]).each{|mem|
|
276
|
+
@server.send_client_message(mem, user, command, *args)
|
277
|
+
}
|
278
|
+
end
|
279
|
+
|
280
|
+
def joined?(who)
|
281
|
+
@members.include?(who)
|
282
|
+
end
|
283
|
+
|
284
|
+
def invite(from, who)
|
285
|
+
if !joined?(from)
|
286
|
+
return ["442", @name, "You're not on that channel"]
|
287
|
+
elsif joined?(who)
|
288
|
+
return ["443", who.nick, "is already on channel"]
|
289
|
+
elsif !@mode.can_invite?(from)
|
290
|
+
return ["482", @name, "You're not channel operator"]
|
291
|
+
else
|
292
|
+
@invited.push who
|
293
|
+
@server.send_client_message(who, from, Command::INVITE, who.nick, @name)
|
294
|
+
return ["341", who.nick, @name]
|
295
|
+
end
|
296
|
+
end
|
297
|
+
|
298
|
+
def unregister(usr)
|
299
|
+
@members.delete(usr)
|
300
|
+
@mode.unregister(usr)
|
301
|
+
usr.joined_channels.delete(self)
|
302
|
+
end
|
303
|
+
|
304
|
+
def handle_mode(who, params)
|
305
|
+
if params.empty?
|
306
|
+
["324", @name, *@mode.get_flags]
|
307
|
+
else
|
308
|
+
flags, *targets = params
|
309
|
+
flags = "+" + flags if flags[0,1] != "+" && flags[0,1] != "-"
|
310
|
+
if @mode.op?(who)
|
311
|
+
rpl = @mode.set_flags(who, flags, targets)
|
312
|
+
send_to_members(who, Command::MODE, @name, *rpl) if rpl
|
313
|
+
nil
|
314
|
+
elsif !joined?(who)
|
315
|
+
return ["482", @name, "You're not channel operator"]
|
316
|
+
else
|
317
|
+
return ["442", @name, "You're not on that channel"]
|
318
|
+
end
|
319
|
+
end
|
320
|
+
end
|
321
|
+
|
322
|
+
def part(who, message="")
|
323
|
+
if joined?(who)
|
324
|
+
send_to_members(who, Command::PART, @name, message)
|
325
|
+
unregister(who)
|
326
|
+
return nil
|
327
|
+
else
|
328
|
+
return ["442", @name, "You're not on that channel"]
|
329
|
+
end
|
330
|
+
end
|
331
|
+
|
332
|
+
def set_topic(who, str)
|
333
|
+
if @mode.can_change_topic?(who)
|
334
|
+
@topic = str
|
335
|
+
send_to_members(who, Command::TOPIC, @name, @topic)
|
336
|
+
return nil
|
337
|
+
elsif !@members.include?(who)
|
338
|
+
return ["442", @name, "You're not on that channel"]
|
339
|
+
else
|
340
|
+
return ["482", @name, "You're not channel operator"]
|
341
|
+
end
|
342
|
+
end
|
343
|
+
|
344
|
+
def get_topic(user, text=false)
|
345
|
+
return nil if @mode.secret && !joined?(user)
|
346
|
+
return @topic if text
|
347
|
+
if @topic.empty?
|
348
|
+
["331", @name, "No topic is set"]
|
349
|
+
else
|
350
|
+
["332", @name, @topic]
|
351
|
+
end
|
352
|
+
end
|
353
|
+
|
354
|
+
def privmsg(who, msg)
|
355
|
+
if @mode.n && !@members.include?(who)
|
356
|
+
return ["404", @name, "Cannot send to channel"]
|
357
|
+
elsif @mode.can_talk?(who)
|
358
|
+
send_to_other_members(who, Command::PRIVMSG, @name, msg)
|
359
|
+
nil
|
360
|
+
else
|
361
|
+
return ["404", @name, "Cannot send to channel"]
|
362
|
+
end
|
363
|
+
end
|
364
|
+
|
365
|
+
def notice(who, msg)
|
366
|
+
if @mode.can_talk?(who)
|
367
|
+
send_to_other_members(who, Command::NOTICE, @name, msg)
|
368
|
+
end
|
369
|
+
return nil
|
370
|
+
end
|
371
|
+
|
372
|
+
def visible?(user)
|
373
|
+
!@mode.secret || joined?(user)
|
374
|
+
end
|
375
|
+
|
376
|
+
def names(user)
|
377
|
+
return nil if @mode.secret && !joined?(user)
|
378
|
+
mems = @members.dup
|
379
|
+
unless @members.include?(user)
|
380
|
+
mems = mems.find_all{|usr|
|
381
|
+
!usr.invisible
|
382
|
+
}
|
383
|
+
end
|
384
|
+
memlist = mems.map{|us|
|
385
|
+
if @mode.op?(us)
|
386
|
+
pr = "@"
|
387
|
+
else
|
388
|
+
pr = ""
|
389
|
+
end
|
390
|
+
pr+us.nick
|
391
|
+
}.join(" ")
|
392
|
+
sends = []
|
393
|
+
unless memlist.empty?
|
394
|
+
prefix = if @mode.secret
|
395
|
+
"@"
|
396
|
+
elsif @mode.private
|
397
|
+
"*"
|
398
|
+
else
|
399
|
+
"="
|
400
|
+
end
|
401
|
+
sends << ["353", prefix, @name, memlist]
|
402
|
+
end
|
403
|
+
sends << ["366", @name, "End of NAMES list"]
|
404
|
+
sends
|
405
|
+
end
|
406
|
+
|
407
|
+
end
|
408
|
+
|
409
|
+
class NoModeChannel < Channel
|
410
|
+
def handle_mode(user, *ar)
|
411
|
+
["477", @name, "Channel doesn't support modes"]
|
412
|
+
end
|
413
|
+
end
|
414
|
+
|
415
|
+
class SafeChannel < Channel
|
416
|
+
end # 仕様 がよく分から ない ので実装出来なくても しよう がない
|
417
|
+
|
418
|
+
end
|
data/lib/rupircd/conf.rb
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
=begin
|
2
|
+
= rupircd -- Ruby Pseudo IRC Deamon
|
3
|
+
|
4
|
+
ver 0.6 2009-08-11T23:45:52+09:00
|
5
|
+
|
6
|
+
Copyright (c) 2009 Hiromi Ishii
|
7
|
+
|
8
|
+
You can redistribute it and/or modify it under the same term as Ruby.
|
9
|
+
=end
|
10
|
+
|
11
|
+
module IRCd
|
12
|
+
class FileConf
|
13
|
+
attr_reader :conf
|
14
|
+
attr_accessor :path
|
15
|
+
|
16
|
+
def initialize(path)
|
17
|
+
@path = path
|
18
|
+
@conf = {}
|
19
|
+
end
|
20
|
+
|
21
|
+
def load
|
22
|
+
@conf = eval(open(@path){|f| f.read })
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,254 @@
|
|
1
|
+
=begin
|
2
|
+
|
3
|
+
= rice - Ruby Irc interfaCE
|
4
|
+
|
5
|
+
$Id: irc.rb,v 1.9 2001/06/13 10:22:24 akira Exp $
|
6
|
+
|
7
|
+
Copyright (c) 2001 akira yamada <akira@ruby-lang.org>
|
8
|
+
You can redistribute it and/or modify it under the same term as Ruby.
|
9
|
+
|
10
|
+
=end
|
11
|
+
|
12
|
+
module IRCd
|
13
|
+
class Error < StandardError; end
|
14
|
+
class UnknownCommand < Error
|
15
|
+
attr_reader :cmd
|
16
|
+
def initialize(cmd)
|
17
|
+
@cmd = cmd
|
18
|
+
super("Unknown command: #@cmd")
|
19
|
+
end
|
20
|
+
end
|
21
|
+
class NotEnoughParameter < Error; end
|
22
|
+
|
23
|
+
class Message
|
24
|
+
|
25
|
+
def self.parse(msg)
|
26
|
+
prefix = nil
|
27
|
+
command = ""
|
28
|
+
tmp = []
|
29
|
+
if msg[0] == ?:
|
30
|
+
prefix, command, *tmp = msg[1..-1].split(" ")
|
31
|
+
else
|
32
|
+
command, *tmp = msg.split(" ")
|
33
|
+
end
|
34
|
+
command
|
35
|
+
params = []
|
36
|
+
eoc = false
|
37
|
+
tmp.each_with_index{|ar,ind|
|
38
|
+
if eoc
|
39
|
+
params[-1] += ' ' + ar
|
40
|
+
elsif ar[0] == ?:
|
41
|
+
eoc = true
|
42
|
+
params << ar[1..-1]
|
43
|
+
else
|
44
|
+
params << ar
|
45
|
+
end
|
46
|
+
}
|
47
|
+
self.build(prefix, command, params)
|
48
|
+
end
|
49
|
+
|
50
|
+
def self.build(prefix, command, params)
|
51
|
+
cmd = command
|
52
|
+
cmd.upcase! if String === cmd
|
53
|
+
if Command::Commands.include?(cmd)
|
54
|
+
Command::Commands[cmd.upcase].new(prefix, cmd, params)
|
55
|
+
elsif Reply::Replies.include?(cmd)
|
56
|
+
Reply::Replies[cmd].new(prefix, cmd, params)
|
57
|
+
else
|
58
|
+
raise UnknownCommand, command
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def initialize(prefix, command, params)
|
63
|
+
@prefix = prefix
|
64
|
+
@command = command
|
65
|
+
@params = params
|
66
|
+
end
|
67
|
+
attr_reader :prefix, :command, :params
|
68
|
+
|
69
|
+
def to_s
|
70
|
+
str = ''
|
71
|
+
if @prefix && !@prefix.to_s.empty?
|
72
|
+
str << ':'
|
73
|
+
str << @prefix
|
74
|
+
str << ' '
|
75
|
+
end
|
76
|
+
|
77
|
+
str << @command
|
78
|
+
|
79
|
+
if @params
|
80
|
+
f = false
|
81
|
+
@params.each_with_index do |param, ind|
|
82
|
+
param = param.to_s
|
83
|
+
str << ' '
|
84
|
+
if !f && (param.empty? || /[: ]/ =~ param)
|
85
|
+
str << ':'
|
86
|
+
str << param
|
87
|
+
f = true
|
88
|
+
elsif !f && ind == params.size - 1
|
89
|
+
str << ':'
|
90
|
+
str << param
|
91
|
+
else
|
92
|
+
str << param
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
str << "\x0D\x0A"
|
98
|
+
|
99
|
+
str
|
100
|
+
end
|
101
|
+
|
102
|
+
|
103
|
+
def to_a
|
104
|
+
[@prefix, @command, @params]
|
105
|
+
end
|
106
|
+
|
107
|
+
def inspect
|
108
|
+
sprintf('#<%s:0x%x prefix:%s command:%s params:%s>',
|
109
|
+
self.class, self.object_id, @prefix, @command, @params.inspect)
|
110
|
+
end
|
111
|
+
|
112
|
+
end # Message
|
113
|
+
|
114
|
+
|
115
|
+
module Command
|
116
|
+
class Command < Message
|
117
|
+
end # Command
|
118
|
+
|
119
|
+
Commands = {}
|
120
|
+
%w(PASS NICK USER OPER MODE SERVICE QUIT SQUIT CLOSE
|
121
|
+
JOIN PART TOPIC NAMES LIST INVITE KICK
|
122
|
+
PRIVMSG NOTICE MOTD LUSERS VERSION STATS LINKS
|
123
|
+
TIME CONNECT TRACE ADMIN INFO SERVLIST SQUERY
|
124
|
+
WHO WHOIS WHOWAS KILL PING PONG ERROR
|
125
|
+
AWAY REHASH DIE RESTART SUMMON USERS WALLOPS USERHOST ISON
|
126
|
+
).each do |cmd|
|
127
|
+
eval <<E
|
128
|
+
class #{cmd} < Command
|
129
|
+
end
|
130
|
+
Commands['#{cmd}'] = #{cmd}
|
131
|
+
|
132
|
+
def #{cmd.downcase}(*params)
|
133
|
+
#{cmd}.new(nil, '#{cmd}', params)
|
134
|
+
end
|
135
|
+
module_function :#{cmd.downcase}
|
136
|
+
E
|
137
|
+
end
|
138
|
+
|
139
|
+
# XXX:
|
140
|
+
class PRIVMSG
|
141
|
+
def to_s
|
142
|
+
str = ''
|
143
|
+
if @prefix
|
144
|
+
str << ':'
|
145
|
+
str << @prefix
|
146
|
+
str << ' '
|
147
|
+
end
|
148
|
+
|
149
|
+
str << @command
|
150
|
+
|
151
|
+
str << ' '
|
152
|
+
str << @params[0]
|
153
|
+
|
154
|
+
str << ' :'
|
155
|
+
str << @params[1..-1].join(' ')
|
156
|
+
|
157
|
+
str << "\x0D\x0A"
|
158
|
+
str
|
159
|
+
end
|
160
|
+
end
|
161
|
+
end # Command
|
162
|
+
|
163
|
+
module Reply
|
164
|
+
class Reply < Message
|
165
|
+
end
|
166
|
+
|
167
|
+
class CommandResponse < Reply
|
168
|
+
end
|
169
|
+
|
170
|
+
class ErrorReply < Reply
|
171
|
+
end
|
172
|
+
|
173
|
+
Replies = {}
|
174
|
+
%w(001,RPL_WELCOME 002,RPL_YOURHOST 003,RPL_CREATED
|
175
|
+
004,RPL_MYINFO 005,RPL_BOUNCE
|
176
|
+
302,RPL_USERHOST 303,RPL_ISON 301,RPL_AWAY
|
177
|
+
305,RPL_UNAWAY 306,RPL_NOWAWAY 311,RPL_WHOISUSER
|
178
|
+
312,RPL_WHOISSERVER 313,RPL_WHOISOPERATOR
|
179
|
+
317,RPL_WHOISIDLE 318,RPL_ENDOFWHOIS
|
180
|
+
319,RPL_WHOISCHANNELS 314,RPL_WHOWASUSER
|
181
|
+
369,RPL_ENDOFWHOWAS 321,RPL_LISTSTART
|
182
|
+
322,RPL_LIST 323,RPL_LISTEND 325,RPL_UNIQOPIS
|
183
|
+
324,RPL_CHANNELMODEIS 331,RPL_NOTOPIC
|
184
|
+
332,RPL_TOPIC 341,RPL_INVITING 342,RPL_SUMMONING
|
185
|
+
346,RPL_INVITELIST 347,RPL_ENDOFINVITELIST
|
186
|
+
348,RPL_EXCEPTLIST 349,RPL_ENDOFEXCEPTLIST
|
187
|
+
351,RPL_VERSION 352,RPL_WHOREPLY 315,RPL_ENDOFWHO
|
188
|
+
353,RPL_NAMREPLY 366,RPL_ENDOFNAMES 364,RPL_LINKS
|
189
|
+
365,RPL_ENDOFLINKS 367,RPL_BANLIST 368,RPL_ENDOFBANLIST
|
190
|
+
371,RPL_INFO 374,RPL_ENDOFINFO 375,RPL_MOTDSTART
|
191
|
+
372,RPL_MOTD 376,RPL_ENDOFMOTD 381,RPL_YOUREOPER
|
192
|
+
382,RPL_REHASHING 383,RPL_YOURESERVICE 391,RPL_TIM
|
193
|
+
392,RPL_ 393,RPL_USERS 394,RPL_ENDOFUSERS 395,RPL_NOUSERS
|
194
|
+
200,RPL_TRACELINK 201,RPL_TRACECONNECTING
|
195
|
+
202,RPL_TRACEHANDSHAKE 203,RPL_TRACEUNKNOWN
|
196
|
+
204,RPL_TRACEOPERATOR 205,RPL_TRACEUSER 206,RPL_TRACESERVER
|
197
|
+
207,RPL_TRACESERVICE 208,RPL_TRACENEWTYPE 209,RPL_TRACECLASS
|
198
|
+
210,RPL_TRACERECONNECT 261,RPL_TRACELOG 262,RPL_TRACEEND
|
199
|
+
211,RPL_STATSLINKINFO 212,RPL_STATSCOMMANDS 219,RPL_ENDOFSTATS
|
200
|
+
242,RPL_STATSUPTIME 243,RPL_STATSOLINE 221,RPL_UMODEIS
|
201
|
+
234,RPL_SERVLIST 235,RPL_SERVLISTEND 251,RPL_LUSERCLIENT
|
202
|
+
252,RPL_LUSEROP 253,RPL_LUSERUNKNOWN 254,RPL_LUSERCHANNELS
|
203
|
+
255,RPL_LUSERME 256,RPL_ADMINME 257,RPL_ADMINLOC1
|
204
|
+
258,RPL_ADMINLOC2 259,RPL_ADMINEMAIL 263,RPL_TRYAGAIN
|
205
|
+
401,ERR_NOSUCHNICK 402,ERR_NOSUCHSERVER 403,ERR_NOSUCHCHANNEL
|
206
|
+
404,ERR_CANNOTSENDTOCHAN 405,ERR_TOOMANYCHANNELS
|
207
|
+
406,ERR_WASNOSUCHNICK 407,ERR_TOOMANYTARGETS
|
208
|
+
408,ERR_NOSUCHSERVICE 409,ERR_NOORIGIN 411,ERR_NORECIPIENT
|
209
|
+
412,ERR_NOTEXTTOSEND 413,ERR_NOTOPLEVEL 414,ERR_WILDTOPLEVEL
|
210
|
+
415,ERR_BADMASK 421,ERR_UNKNOWNCOMMAND 422,ERR_NOMOTD
|
211
|
+
423,ERR_NOADMININFO 424,ERR_FILEERROR 431,ERR_NONICKNAMEGIVEN
|
212
|
+
432,ERR_ERRONEUSNICKNAME 433,ERR_NICKNAMEINUSE
|
213
|
+
436,ERR_NICKCOLLISION 437,ERR_UNAVAILRESOURCE
|
214
|
+
441,ERR_USERNOTINCHANNEL 442,ERR_NOTONCHANNEL
|
215
|
+
443,ERR_USERONCHANNEL 444,ERR_NOLOGIN 445,ERR_SUMMONDISABLED
|
216
|
+
446,ERR_USERSDISABLED 451,ERR_NOTREGISTERED
|
217
|
+
461,ERR_NEEDMOREPARAMS 462,ERR_ALREADYREGISTRED
|
218
|
+
463,ERR_NOPERMFORHOST 464,ERR_PASSWDMISMATCH
|
219
|
+
465,ERR_YOUREBANNEDCREEP 466,ERR_YOUWILLBEBANNED
|
220
|
+
467,ERR_KEYSE 471,ERR_CHANNELISFULL 472,ERR_UNKNOWNMODE
|
221
|
+
473,ERR_INVITEONLYCHAN 474,ERR_BANNEDFROMCHAN
|
222
|
+
475,ERR_BADCHANNELKEY 476,ERR_BADCHANMASK 477,ERR_NOCHANMODES
|
223
|
+
478,ERR_BANLISTFULL 481,ERR_NOPRIVILEGES 482,ERR_CHANOPRIVSNEEDED
|
224
|
+
483,ERR_CANTKILLSERVER 484,ERR_RESTRICTED
|
225
|
+
485,ERR_UNIQOPPRIVSNEEDED 491,ERR_NOOPERHOST
|
226
|
+
501,ERR_UMODEUNKNOWNFLAG 502,ERR_USERSDONTMATCH
|
227
|
+
231,RPL_SERVICEINFO 232,RPL_ENDOFSERVICES
|
228
|
+
233,RPL_SERVICE 300,RPL_NONE 316,RPL_WHOISCHANOP
|
229
|
+
361,RPL_KILLDONE 362,RPL_CLOSING 363,RPL_CLOSEEND
|
230
|
+
373,RPL_INFOSTART 384,RPL_MYPORTIS 213,RPL_STATSCLINE
|
231
|
+
214,RPL_STATSNLINE 215,RPL_STATSILINE 216,RPL_STATSKLINE
|
232
|
+
217,RPL_STATSQLINE 218,RPL_STATSYLINE 240,RPL_STATSVLINE
|
233
|
+
241,RPL_STATSLLINE 244,RPL_STATSHLINE 244,RPL_STATSSLINE
|
234
|
+
246,RPL_STATSPING 247,RPL_STATSBLINE 250,RPL_STATSDLINE
|
235
|
+
492,ERR_NOSERVICEHOST
|
236
|
+
).each do |num_cmd|
|
237
|
+
num, cmd = num_cmd.split(',', 2)
|
238
|
+
eval <<E
|
239
|
+
class #{cmd} < #{if num[0] == ?0 || num[0] == ?2 || num[0] == ?3
|
240
|
+
'CommandResponse'
|
241
|
+
elsif num[0] == ?4 || num[0] == ?5
|
242
|
+
'ErrorReply'
|
243
|
+
end}
|
244
|
+
end
|
245
|
+
Replies['#{num}'] = #{cmd}
|
246
|
+
|
247
|
+
def #{cmd.downcase}(*params)
|
248
|
+
#{cmd}.new(nil, '#{cmd}', params)
|
249
|
+
end
|
250
|
+
module_function :#{cmd.downcase}
|
251
|
+
E
|
252
|
+
end
|
253
|
+
end # Reply
|
254
|
+
end # IRCd
|