PerfectlyNormal-Flexo 0.3.9

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,106 @@
1
+ require 'thread'
2
+ require 'caselesshash'
3
+
4
+ module Flexo
5
+ # Here's the interesting parts. This class is responsible for...
6
+ # Managing plugins! Who would've thought that...
7
+ class PluginManager
8
+ # As usual. Set up some things. Create some arrays, some hashes,
9
+ # get a mutex. All that
10
+ def initialize(mutex)
11
+ @manager = Flexo::Manager.instance
12
+ @plugins_available = []
13
+ @plugins_loaded = CaselessHash.new
14
+ @plugins_path = @manager.config['plugin.path'].to_a
15
+
16
+ find_plugins
17
+ end
18
+
19
+ # Goes through the folders sent to initialize and looks for
20
+ # anything resembling a plugin. Then shove them into a hash
21
+ # so that we can find them later
22
+ def find_plugins
23
+ @manager.logger.debug " Going to search for plugins (in #{@plugins_path.join(',')}"
24
+ @plugins_path.each do |dir|
25
+ Dir["#{dir}/*.rb"].each do |file|
26
+ begin
27
+ require file
28
+ rescue => e
29
+ @manager.logger.warn(e.message)
30
+ next
31
+ end
32
+ end
33
+
34
+ ObjectSpace.each_object(Class) do |klass|
35
+ if klass < Flexo::Plugin
36
+ @manager.logger.debug("#{klass} looks like a valid Flexo-plugin")
37
+ unless klass::NAME
38
+ raise PluginNameError, "The plugin hasn't specified a name!"
39
+ end
40
+
41
+ if @plugins_available.any? { |plugin|
42
+ plugin::NAME.downcase == klass::NAME.downcase }
43
+ raise PluginConflictError,
44
+ "The plugin name #{klass::NAME} is already being used by another plugin."
45
+ end
46
+ @plugins_available << klass
47
+ end
48
+ end
49
+ end
50
+ end
51
+
52
+ # Find a plugin and its class by its name
53
+ def get_plugin_class(name)
54
+ @plugins_available.find { |plugin|
55
+ plugin::NAME.downcase == name.downcase
56
+ }
57
+ end
58
+
59
+ # Load a plugin
60
+ def load_plugin(name)
61
+ @manager.logger.debug("Going to load plugin #{name}")
62
+ klass = get_plugin_class(name)
63
+ raise Flexo::PluginNotFoundError, "The #{name} plugin could not be found." unless klass
64
+
65
+ if !@plugins_loaded.key?(klass::NAME)
66
+ begin
67
+ klass::DEPENDS.each do |dependency|
68
+ if(dependency[0..4] == "ruby-")
69
+ begin
70
+ require dependency[5..-1]
71
+ rescue LoadError
72
+ raise PluginDependencyError, "Plugin requires #{dependency[5..-1]}, which can't be found"
73
+ end
74
+
75
+ next
76
+ end
77
+
78
+ dep = get_plugin_class(dependency)
79
+ if !@plugins_loaded.key?(dep::NAME)
80
+ load_plugin(dep::NAME)
81
+ end
82
+ end
83
+
84
+ plugin = klass.new(@plugins_loaded)
85
+ rescue Flexo::PluginError => e
86
+ puts "Plugin encountered an error (#{e.class}): #{e.message}"
87
+ puts e.backtrace
88
+ rescue Flexo::PluginDependencyError => e
89
+ @manager.logger.warn e.message
90
+ return nil
91
+ rescue Flexo::PluginNotFoundError => e
92
+ @manager.logger.warn e.message
93
+ return nil
94
+ end
95
+
96
+ @manager.mutex { @plugins_loaded[klass::NAME] = plugin }
97
+ @manager.logger.debug " Loaded #{plugin.class}"
98
+ else
99
+ @manager.logger.debug " Already loaded #{klass.class}"
100
+ plugin = @plugins_loaded[klass::NAME]
101
+ end
102
+
103
+ plugin
104
+ end
105
+ end
106
+ end
@@ -0,0 +1,601 @@
1
+ module Flexo
2
+ # One of the important classes. This contains convenience-functions for
3
+ # sending almost every IRC command imaginable. And if that's not enough,
4
+ # it'll also have a raw()-function. This is the only class that's supposed
5
+ # to interact with Server, to make sure that only sanitized content is sent
6
+ # out from Flexo.
7
+ #
8
+ # Most documentation for the functions are grabbed from RFC2812
9
+ # available at http://tools.ietf.org/html/rfc2812.
10
+ class Sender
11
+ # Sets up the link to the manager, and creates a send queue.
12
+ def initialize
13
+ @manager = Flexo::Manager.instance
14
+ @queue = Queue.new
15
+
16
+ @manager.thread do
17
+ while cmd = @queue.pop
18
+ @manager.server.quote cmd
19
+ sleep 1
20
+ end
21
+ end
22
+ end
23
+
24
+ # Shoves the command into the queue
25
+ def quote(line)
26
+ @manager.logger.debug("Adding \"#{line.rstrip}\" to the outgoing queue")
27
+ @queue << line.rstrip
28
+ end
29
+
30
+ # 3.1.1 Password message
31
+ # Command: PASS
32
+ # Paramteres: <password>
33
+ #
34
+ # The PASS command is used to set a 'connection password'.
35
+ # The optional password can and MUST be set before any
36
+ # attempt to register when the connection is made.
37
+ # Currently this requires that user send a PASS command
38
+ # before sending the NICK/USER combination.
39
+ #
40
+ # Numeric Replies:
41
+ # ERR_NEEDMOREPARAMS
42
+ # ERR_ALREADYREGISTRED
43
+ #
44
+ # Example:
45
+ # PASS secretpasswordhere
46
+ def pass(password)
47
+ quote "PASS #{password.to_s}"
48
+ end
49
+
50
+ # 3.1.2 Nick message
51
+ # Command: NICK
52
+ # Parameters: <nickname>
53
+ #
54
+ # NICK message is used to give the user a nickname if connecting,
55
+ # or change the previous one if already connected
56
+ #
57
+ # Numeric Replies:
58
+ # ERR_NONICKNAMEGIVEN
59
+ # ERR_ERRONEUSNICKNAME
60
+ # ERR_NICKNAMEINUSE
61
+ # ERR_NICKCOLLISION
62
+ #
63
+ # Example:
64
+ # NICK Wiz # Introducing new nick "Wiz".
65
+ # :WiZ NICK Kilroy # WiZ changed his nickname to Kilroy.
66
+ def nick(nickname)
67
+ quote "NICK #{nickname.to_s}"
68
+ end
69
+
70
+ # 3.1.3 User message
71
+ # Command: USER
72
+ # Parameters: <user> <mode> <unused> <realname>
73
+ #
74
+ # The USER command is used at the beginning of connection to specify
75
+ # the username, hostname and realname of a new user.
76
+ #
77
+ # The <mode> parameter should be a numeric, and can be used to
78
+ # automatically set user modes when registering with the server.
79
+ # This parameter is a bitmask, with only 2 bits having any
80
+ # signification: if the bit 2 is set, the user mode 'w' will
81
+ # be set and if the bit 3 is set, the user mode 'i' will be set.
82
+ # (See Section 3.1.5 "User Modes").
83
+ # The <realname> may contain space characters.
84
+ #
85
+ # Numeric Replies:
86
+ # ERR_NEEDMOREPARAMS
87
+ # ERR_ALREADYREGISTRED
88
+ #
89
+ # Example:
90
+ # USER guest 0 * :Ronnie Reagan # User registering themselves with a
91
+ # # username of "guest" and real name
92
+ # # "Ronnie Reagan".
93
+ # USER guest 8 * :Ronnie Reagan # User registering themselves with a
94
+ # # username of "guest" and real name
95
+ # # "Ronnie Reagan", and asking to be set
96
+ # # invisible.
97
+ def user(user, mode, realname)
98
+ quote "USER #{user.to_s} #{mode.to_i} * :#{realname.to_s}"
99
+ end
100
+
101
+ # 3.1.4 Oper message
102
+ # Command: OPER
103
+ # Parameters: <name> <password>
104
+ #
105
+ # A normal user uses the OPER command to obtain operator privileges.
106
+ # The combination of <name> and <password> are REQUIRED to gain
107
+ # Operator privileges. Upon success, the user will receive a MODE
108
+ # message (see section 3.1.5) indicating the new user modes.
109
+ #
110
+ # Numeric Replies:
111
+ # ERR_NEEDMOREPARAMS
112
+ # RPL_YOUREOPER
113
+ # ERR_NOOPERHOST
114
+ # ERR_PASSWDMISMATCH
115
+ # Example:
116
+ # OPER foo bar # Attempt to register as an operator
117
+ # using a username of "foo" and "bar" as the password.
118
+ def oper(name, password)
119
+ quote "OPER #{name.to_s} #{password.to_s}"
120
+ end
121
+
122
+ # 3.1.5 User mode message
123
+ # Command: MODE
124
+ # Parameters: <nickname> ±<modes>
125
+ #
126
+ # The user MODE's are typically changes which affect either how the
127
+ # client is seen by others or what 'extra' messages the client is sent.
128
+ # A user MODE command MUST only be accepted if both the sender of the
129
+ # message and the nickname given as a parameter are both the same. If
130
+ # no other parameter is given, then the server will return the current
131
+ # settings for the nick.
132
+ #
133
+ # The available modes are as follows:
134
+ # a - user is flagged as away;
135
+ # i - marks a users as invisible;
136
+ # w - user receives wallops;
137
+ # r - restricted user connection;
138
+ # o - operator flag;
139
+ # O - local operator flag;
140
+ # s - marks a user for receipt of server notices.
141
+ #
142
+ # Additional modes may be available later on.
143
+ #
144
+ # The flag 'a' SHALL NOT be toggled by the user using the MODE command,
145
+ # instead use of the AWAY command is REQUIRED.
146
+ #
147
+ # If a user attempts to make themselves an operator using the "+o" or
148
+ # "+O" flag, the attempt SHOULD be ignored as users could bypass the
149
+ # authentication mechanisms of the OPER command. There is no
150
+ # restriction, however, on anyone `deopping' themselves (using "-o" or
151
+ # "-O").
152
+ #
153
+ # On the other hand, if a user attempts to make themselves unrestricted
154
+ # using the "-r" flag, the attempt SHOULD be ignored. There is no
155
+ # restriction, however, on anyone `deopping' themselves (using "+r").
156
+ # This flag is typically set by the server upon connection for
157
+ # administrative reasons. While the restrictions imposed are left up
158
+ # to the implementation, it is typical that a restricted user not be
159
+ # allowed to change nicknames, nor make use of the channel operator
160
+ # status on channels.
161
+ #
162
+ # The flag 's' is obsolete but MAY still be used.
163
+ #
164
+ # Numeric Replies:
165
+ # ERR_NEEDMOREPARAMS
166
+ # ERR_USERSDONTMATCH
167
+ # ERR_UMODEUNKNOWNFLAG
168
+ # RPL_UMODEIS
169
+ # Examples:
170
+ # MODE WiZ -w # Command by WiZ to turn off
171
+ # # reception of WALLOPS messages.
172
+ # MODE Angel +i # Command from Angel to make herself invisible.
173
+ # MODE WiZ -o # WiZ 'deopping' (removing operator status).
174
+ def usermode(nickname, mode)
175
+ quote "MODE #{nickname.to_s} #{mode.to_s}"
176
+ end
177
+
178
+ # 3.1.6 Service message
179
+ # Command: SERVICE
180
+ # Parameters: <nickname> <reserved> <distribution> <type>
181
+ # <reserved> <info>
182
+ #
183
+ # The SERVICE command to register a new service. Command parameters
184
+ # specify the service nickname, distribution, type and info of a new
185
+ # service.
186
+ #
187
+ # The <distribution> parameter is used to specify the visibility of a
188
+ # service. The service may only be known to servers which have a name
189
+ # matching the distribution. For a matching server to have knowledge
190
+ # of the service, the network path between that server and the server
191
+ # on which the service is connected MUST be composed of servers which
192
+ # names all match the mask.
193
+ #
194
+ # The <type> parameter is currently reserved for future usage.
195
+ #
196
+ # Numeric Replies:
197
+ # ERR_ALREADYREGISTRED
198
+ # ERR_NEEDMOREPARAMS
199
+ # ERR_ERRONEUSNICKNAME
200
+ # RPL_YOURESERVICE
201
+ # RPL_YOURHOST
202
+ # RPL_MYINFO
203
+ # Example:
204
+ # SERVICE dict * *.fr 0 0 :French Dictionary
205
+ # # Service registering itself with a name of "dict".
206
+ # # This service will only be available on servers which
207
+ # # name matches "*.fr".
208
+ def service(nickname, distribution, type, info)
209
+ # Won't be implemented by me
210
+ end
211
+
212
+ # 3.1.7 Quit
213
+ # Command: QUIT
214
+ # Parameters: <Quit Message>
215
+ #
216
+ # A client session is terminated with a quit message. The server
217
+ # acknowledges this by sending an ERROR message to the client.
218
+ #
219
+ # Numeric Replies:
220
+ # None.
221
+ # Example:
222
+ # QUIT :Gone to have lunch
223
+ def quit(message="")
224
+ quote "QUIT :#{message.to_s}"
225
+ end
226
+
227
+ # 3.1.8 Squit
228
+ # Command: SQUIT
229
+ # Parameters: <server> <comment>
230
+ #
231
+ # The SQUIT command is available only to operators. It is used to
232
+ # disconnect server links. Also servers can generate SQUIT messages on
233
+ # error conditions. A SQUIT message may also target a remote server
234
+ # connection. In this case, the SQUIT message will simply be sent to
235
+ # the remote server without affecting the servers in between the
236
+ # operator and the remote server.
237
+ #
238
+ # The <comment> SHOULD be supplied by all operators who execute a SQUIT
239
+ # for a remote server. The server ordered to disconnect its peer
240
+ # generates a WALLOPS message with <comment> included, so that other
241
+ # users may be aware of the reason of this action.
242
+ #
243
+ # Numeric replies:
244
+ # ERR_NOPRIVILEGES
245
+ # ERR_NOSUCHSERVER
246
+ # ERR_NEEDMOREPARAMS
247
+ # Examples:
248
+ # SQUIT tolsun.oulu.fi :Bad Link ? # Command to uplink of the server
249
+ # # tolson.oulu.fi to terminate its
250
+ # # connection with comment "Bad Link".
251
+ # :Trillian SQUIT cm22.eng.umd.edu :Server out of control
252
+ # # Command from Trillian from to
253
+ # # disconnect "cm22.eng.umd.edu"
254
+ # # from the net with comment
255
+ # # "Server out of control".
256
+ def squit(server, message="")
257
+ end
258
+
259
+ # 3.2.1 Join message
260
+ # Command: JOIN
261
+ # Parameters: <channel>, <key>
262
+ #
263
+ # The JOIN command is used by a user to request to start listening to
264
+ # the specific channel. Servers MUST be able to parse arguments in the
265
+ # form of a list of target, but SHOULD NOT use lists when sending JOIN
266
+ # messages to clients.
267
+ #
268
+ # Once a user has joined a channel, he receives information about
269
+ # all commands his server receives affecting the channel. This
270
+ # includes JOIN, MODE, KICK, PART, QUIT and of course PRIVMSG/NOTICE.
271
+ # This allows channel members to keep track of the other channel
272
+ # members, as well as channel modes.
273
+ #
274
+ # If a JOIN is successful, the user receives a JOIN message as
275
+ # confirmation and is then sent the channel's topic (using RPL_TOPIC) and
276
+ # the list of users who are on the channel (using RPL_NAMREPLY), which
277
+ # MUST include the user joining.
278
+ #
279
+ # Note that this message accepts a special argument ("0"), which is
280
+ # a special request to leave all channels the user is currently a member
281
+ # of. The server will process this message as if the user had sent
282
+ # a PART command (See Section 3.2.2) for each channel he is a member
283
+ # of.
284
+ #
285
+ # Numeric Replies:
286
+ # ERR_NEEDMOREPARAMS
287
+ # ERR_BANNEDFROMCHAN
288
+ # ERR_INVITEONLYCHAN
289
+ # ERR_BADCHANNELKEY
290
+ # ERR_CHANNELISFULL
291
+ # ERR_BADCHANMASK
292
+ # ERR_NOSUCHCHANNEL
293
+ # ERR_TOOMANYCHANNELS
294
+ # ERR_TOOMANYTARGETS
295
+ # ERR_UNAVAILRESOURCE
296
+ # RPL_TOPIC
297
+ # Examples:
298
+ # JOIN #foobar # Command to join channel #foobar.
299
+ # JOIN &foo fubar # Command to join channel &foo using
300
+ # # key "fubar".
301
+ # JOIN #foo,&bar fubar # Command to join channel #foo using
302
+ # # key "fubar" and &bar using no key.
303
+ # JOIN #foo,#bar fubar,foobar # Command to join channel #foo using
304
+ # # key "fubar", and channel #bar using
305
+ # # key "foobar".
306
+ # JOIN #foo,#bar # Command to join channels #foo and
307
+ # # #bar.
308
+ # JOIN 0 # Leave all currently joined
309
+ # # channels.
310
+ def join(channels, key="")
311
+ channels.each do |channel|
312
+ quote "JOIN #{channel.to_s} #{key.to_s}"
313
+ end
314
+ end
315
+
316
+ # 3.2.2 Part message
317
+ # Command: PART
318
+ # Parameters: <channel> <part message>
319
+ #
320
+ # The PART command causes the user sending the message to be removed
321
+ # from the list of active members for all given channels listed in the
322
+ # parameter string. If a "Part Message" is given, this will be sent
323
+ # instead of the default message, the nickname. This request is always
324
+ # granted by the server.
325
+ #
326
+ # Servers MUST be able to parse arguments in the form of a list of
327
+ # target, but SHOULD NOT use lists when sending PART messages to
328
+ # clients.
329
+ #
330
+ # Numeric Replies:
331
+ # ERR_NEEDMOREPARAMS
332
+ # ERR_NOSUCHCHANNEL
333
+ # ERR_NOTONCHANNEL
334
+ # Examples:
335
+ # PART #twilight_zone # Command to leave channel "#twilight_zone"
336
+ # PART #oz-ops,&group5 # Command to leave both channels
337
+ # # "&group5" and "#oz-ops".
338
+ def part(channels, message="")
339
+ channels.each do |channel|
340
+ cmd = "PART #{channel.to_s}"
341
+ cmd += " :#{message}" if message != ""
342
+ quote cmd
343
+ end
344
+ end
345
+
346
+ # 3.2.3 Channel mode message
347
+ # Command: MODE
348
+ # Parameters: <channel> *( ( "-" / "+" ) *<modes> *<modeparams> )
349
+ #
350
+ # The MODE command is provided so that users may query and change the
351
+ # characteristics of a channel. Note that there is a maximum limit of three (3) changes per
352
+ # command for modes that take a parameter.
353
+ #
354
+ # Numeric Replies:
355
+ # ERR_NEEDMOREPARAMS
356
+ # ERR_KEYSET
357
+ # ERR_NOCHANMODES
358
+ # ERR_CHANOPRIVSNEEDED
359
+ # ERR_USERNOTINCHANNEL
360
+ # ERR_UNKNOWNMODE
361
+ # RPL_CHANNELMODEIS
362
+ # RPL_BANLIST
363
+ # RPL_ENDOFBANLIST
364
+ # RPL_EXCEPTLIST
365
+ # RPL_ENDOFEXCEPTLIST
366
+ # RPL_INVITELIST
367
+ # RPL_ENDOFINVITELIST
368
+ # RPL_UNIQOPIS
369
+ #
370
+ # Information about channel modes and how they work can be found
371
+ # in RFC2811 (http://tools.ietf.org/html/rfc2811)
372
+ def chanmode(channel, *modes)
373
+ quote "MODE #{channel.to_s} #{modes.join(' ')}"
374
+ end
375
+
376
+ # 3.2.4 Topic message
377
+ # Command: TOPIC
378
+ # Parameters: <channel> [ <topic> ]
379
+ #
380
+ # The TOPIC command is used to change or view the topic of a channel.
381
+ # The topic for channel <channel> is returned if there is no <topic>
382
+ # given. If the <topic> parameter is present, the topic for that
383
+ # channel will be changed, if this action is allowed for the user
384
+ # requesting it. If the <topic> parameter is an empty string, the
385
+ # topic for that channel will be removed.
386
+ #
387
+ # Numeric Replies:
388
+ # ERR_NEEDMOREPARAMS
389
+ # ERR_NOTONCHANNEL
390
+ # RPL_NOTOPIC
391
+ # RPL_TOPIC
392
+ # ERR_CHANOPRIVSNEEDED
393
+ # ERR_NOCHANMODES
394
+ # Examples:
395
+ # :WiZ!jto@tolsun.oulu.fi TOPIC #test :New # User Wiz setting the topic.
396
+ # TOPIC #test :another topic # Command to set the topic on #test
397
+ # # to "another topic".
398
+ # TOPIC #test : # Command to clear the topic on #test.
399
+ # TOPIC #test # Command to check the topic for #test.
400
+ def topic(channel, topic="")
401
+ cmd = "TOPIC #{channel.to_s} "
402
+ cmd += ":#{topic.to_s}" if topic != ""
403
+ quote cmd
404
+ end
405
+
406
+ # FIXME: 3.2.5 Names message
407
+ # FIXME: 3.2.6 List message
408
+
409
+ # 3.2.7 Invite message
410
+ # Command: INVITE
411
+ # Parameters: <nickname> <channel>
412
+ #
413
+ # The INVITE command is used to invite a user to a channel. The
414
+ # parameter <nickname> is the nickname of the person to be invited to
415
+ # the target channel <channel>. There is no requirement that the
416
+ # channel the target user is being invited to must exist or be a valid
417
+ # channel. However, if the channel exists, only members of the channel
418
+ # are allowed to invite other users. When the channel has invite-only
419
+ # flag set, only channel operators may issue INVITE command.
420
+ #
421
+ # Only the user inviting and the user being invited will receive
422
+ # notification of the invitation. Other channel members are not
423
+ # notified. (This is unlike the MODE changes, and is occasionally the
424
+ # source of trouble for users.)
425
+ #
426
+ # Numeric Replies:
427
+ # ERR_NEEDMOREPARAMS
428
+ # ERR_NOSUCHNICK
429
+ # ERR_NOTONCHANNEL
430
+ # ERR_USERONCHANNEL
431
+ # ERR_CHANOPRIVSNEEDED
432
+ # RPL_INVITING
433
+ # RPL_AWAY
434
+ # Examples:
435
+ # :Angel!wings@irc.org INVITE Wiz #Dust # Message to WiZ when he has
436
+ # # been invited by user Angel
437
+ # # to channel #Dust
438
+ # INVITE Wiz #Twilight_Zone # Command to invite WiZ to
439
+ # # #Twilight_zone
440
+ def invite(nick, channel)
441
+ quote "INVITE #{nick.to_s} #{channel.to_s}"
442
+ end
443
+
444
+ # 3.2.8 Kick command
445
+ # Command: KICK
446
+ # Parameters: <channel> <user> [<comment>]
447
+ #
448
+ # The KICK command can be used to request the forced removal of a user
449
+ # from a channel. It causes the <user> to PART from the <channel> by
450
+ # force. For the message to be syntactically correct, there MUST be
451
+ # either one channel parameter and multiple user parameter, or as many
452
+ # channel parameters as there are user parameters. If a "comment" is
453
+ # given, this will be sent instead of the default message, the nickname
454
+ # of the user issuing the KICK.
455
+ #
456
+ # Numeric Replies:
457
+ # ERR_NEEDMOREPARAMS
458
+ # ERR_NOSUCHCHANNEL
459
+ # ERR_BADCHANMASK
460
+ # ERR_CHANOPRIVSNEEDED
461
+ # ERR_USERNOTINCHANNEL
462
+ # ERR_NOTONCHANNEL
463
+ def kick(channel, user, message="")
464
+ quote "KICK #{channel} #{user} :#{message}"
465
+ end
466
+
467
+ # 3.3.1 Private messages
468
+ # Command: PRIVMSG
469
+ # Parameters: <target> <text to be sent>
470
+ #
471
+ # PRIVMSG is used to send private messages between users, as well as to
472
+ # send messages to channels. <msgtarget> is usually the nickname of
473
+ # the recipient of the message, or a channel name.
474
+ #
475
+ # The <msgtarget> parameter may also be a host mask (#<mask>) or server
476
+ # mask ($<mask>). In both cases the server will only send the PRIVMSG
477
+ # to those who have a server or host matching the mask. The mask MUST
478
+ # have at least 1 (one) "." in it and no wildcards following the last
479
+ # ".". This requirement exists to prevent people sending messages to
480
+ # "#*" or "$*", which would broadcast to all users. Wildcards are the
481
+ # '*' and '?' characters. This extension to the PRIVMSG command is
482
+ # only available to operators.
483
+ #
484
+ # Numeric Replies:
485
+ # ERR_NORECIPIENT
486
+ # ERR_NOTEXTTOSEND
487
+ # ERR_CANNOTSENDTOCHAN
488
+ # ERR_NOTOPLEVEL
489
+ # ERR_WILDTOPLEVEL
490
+ # ERR_TOOMANYTARGETS
491
+ # ERR_NOSUCHNICK
492
+ # RPL_AWAY
493
+ def privmsg(target, text)
494
+ quote "PRIVMSG #{target.to_s} :#{text.to_s}"
495
+ end
496
+
497
+ # 3.3.2 Notice
498
+ # Command: NOTICE
499
+ # Parameters: <target> <text>
500
+ #
501
+ # The NOTICE command is used similarly to PRIVMSG. The difference
502
+ # between NOTICE and PRIVMSG is that automatic replies MUST NEVER be
503
+ # sent in response to a NOTICE message. This rule applies to servers
504
+ # too - they MUST NOT send any error reply back to the client on
505
+ # receipt of a notice. The object of this rule is to avoid loops
506
+ # between clients automatically sending something in response to
507
+ # something it received.
508
+ #
509
+ # This command is available to services as well as users.
510
+ # This is typically used by services, and automatons (clients with
511
+ # either an AI or other interactive program controlling their actions).
512
+ #
513
+ # See PRIVMSG for more details on replies.
514
+ def notice(target, text)
515
+ quote "NOTICE #{target.to_s} :#{text.to_s}"
516
+ end
517
+
518
+ # 3.4.1 Motd message
519
+ # Command: MOTD
520
+ # Parameters: [ <target> ]
521
+ #
522
+ # The MOTD command is used to get the "Message Of The Day" of the given
523
+ # server, or current server if <target> is omitted.
524
+ #
525
+ # Wildcards are allowed in the <target> parameter.
526
+ #
527
+ # Numeric Replies:
528
+ # RPL_MOTDSTART
529
+ # RPL_MOTD
530
+ # RPL_ENDOFMOTD
531
+ # ERR_NOMOTD
532
+ def motd(target="")
533
+ quote "MOTD #{target}"
534
+ end
535
+
536
+ # FIXME: 3.4.2 Lusers message
537
+ # FIXME: 3.4.3 Version message
538
+ # FIXME: 3.4.4 Stats message
539
+ # FIXME: 3.4.5 Links message
540
+ # FIXME: 3.4.6 Time message
541
+ # FIXME: 3.4.7 Connect message
542
+ # FIXME: 3.4.8 Trace message
543
+ # FIXME: 3.4.9 Admin command
544
+ # FIXME: 3.4.10 Info command
545
+ # FIXME: 3.5 Service Query and Commands
546
+ # FIXME: 3.6 User based queries
547
+ # FIXME: 3.7.1 KILL
548
+
549
+ # 3.7.2 Ping message
550
+ #
551
+ # Command: PING
552
+ # Parameters: <server1> [ <server2> ]
553
+ #
554
+ # The PING command is used to test the presence of an active client or
555
+ # server at the other end of the connection. Servers send a PING
556
+ # message at regular intervals if no other activity detected coming
557
+ # from a connection. If a connection fails to respond to a PING
558
+ # message within a set amount of time, that connection is closed. A
559
+ # PING message MAY be sent even if the connection is active.
560
+ #
561
+ # When a PING message is received, the appropriate PONG message MUST be
562
+ # sent as reply to <server1> (server which sent the PING message out)
563
+ # as soon as possible. If the <server2> parameter is specified, it
564
+ # represents the target of the ping, and the message gets forwarded
565
+ # there.
566
+ #
567
+ # Numeric Replies:
568
+ # ERR_NOORIGIN
569
+ # ERR_NOSUCHSERVER
570
+ # Examples:
571
+ # PING tolsun.oulu.fi # Command to send a PING message to server
572
+ # PING :irc.funet.fi # Ping message sent by server "irc.funet.fi"
573
+ def ping(server, server2="")
574
+ quote "PING #{server.to_s} #{server2.to_s}"
575
+ end
576
+
577
+ # 3.7.3 Pong message
578
+ #
579
+ # Command: PONG
580
+ # Parameters: <server> [ <server2> ]
581
+ #
582
+ # PONG message is a reply to ping message. If parameter <server2> is
583
+ # given, this message MUST be forwarded to given target. The <server>
584
+ # parameter is the name of the entity who has responded to PING message
585
+ # and generated this message.
586
+ #
587
+ # Numeric Replies:
588
+ # ERR_NOORIGIN
589
+ # ERR_NOSUCHSERVER
590
+ # Example:
591
+ # PONG csd.bu.edu tolsun.oulu.fi # PONG message from csd.bu.edu to
592
+ # # tolsun.oulu.fi
593
+ def pong(server, server2="")
594
+ quote "PONG #{server.to_s} #{server2.to_s}"
595
+ end
596
+
597
+ # FIXME: 3.7.4 Error
598
+
599
+ # FIXME: 4. Optional features
600
+ end
601
+ end