PerfectlyNormal-Flexo 0.3.9

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.
@@ -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