2 # RFC 2812 Internet Relay Chat: Client Protocol
5 # "Welcome to the Internet Relay Network
6 # <nick>!<user>@<host>"
8 # "Your host is <servername>, running version <ver>"
10 # "This server was created <date>"
12 # "<servername> <version> <available user modes>
13 # <available channel modes>"
15 # - The server sends Replies 001 to 004 to a user upon
16 # successful registration.
19 # # "Try server <server name>, port <port number>"
21 # "005 nick PREFIX=(ov)@+ CHANTYPES=#& :are supported by this server"
23 # - Sent by the server to a user to suggest an alternative
24 # server. This is often used when the connection is
25 # refused because the server is already full.
28 # ":*1<reply> *( " " <reply> )"
30 # - Reply format used by USERHOST to list replies to
31 # the query list. The reply string is composed as
34 # reply = nickname [ "*" ] "=" ( "+" / "-" ) hostname
36 # The '*' indicates whether the client has registered
37 # as an Operator. The '-' or '+' characters represent
38 # whether the client has set an AWAY message or not
42 # ":*1<nick> *( " " <nick> )"
44 # - Reply format used by ISON to list replies to the
48 # "<nick> :<away message>"
50 # ":You are no longer marked as being away"
52 # ":You have been marked as being away"
54 # - These replies are used with the AWAY command (if
55 # allowed). RPL_AWAY is sent to any client sending a
56 # PRIVMSG to a client which is away. RPL_AWAY is only
57 # sent by the server to which the client is connected.
58 # Replies RPL_UNAWAY and RPL_NOWAWAY are sent when the
59 # client removes and sets an AWAY message.
62 # "<nick> <user> <host> * :<real name>"
64 # "<nick> <server> :<server info>"
66 # "<nick> :is an IRC operator"
68 # "<nick> <integer> :seconds idle"
70 # "<nick> :End of WHOIS list"
72 # "<nick> :*( ( "@" / "+" ) <channel> " " )"
74 # - Replies 311 - 313, 317 - 319 are all replies
75 # generated in response to a WHOIS message. Given that
76 # there are enough parameters present, the answering
77 # server MUST either formulate a reply out of the above
78 # numerics (if the query nick is found) or return an
79 # error reply. The '*' in RPL_WHOISUSER is there as
80 # the literal character and not as a wild card. For
81 # each reply set, only RPL_WHOISCHANNELS may appear
82 # more than once (for long lists of channel names).
83 # The '@' and '+' characters next to the channel name
84 # indicate whether a client is a channel operator or
85 # has been granted permission to speak on a moderated
86 # channel. The RPL_ENDOFWHOIS reply is used to mark
87 # the end of processing a WHOIS message.
90 # "<nick> <user> <host> * :<real name>"
92 # "<nick> :End of WHOWAS"
94 # - When replying to a WHOWAS message, a server MUST use
95 # the replies RPL_WHOWASUSER, RPL_WHOISSERVER or
96 # ERR_WASNOSUCHNICK for each nickname in the presented
97 # list. At the end of all reply batches, there MUST
98 # be RPL_ENDOFWHOWAS (even if there was only one reply
99 # and it was an error).
102 # Obsolete. Not used.
105 # "<channel> <# visible> :<topic>"
109 # - Replies RPL_LIST, RPL_LISTEND mark the actual replies
110 # with data and end of the server's response to a LIST
111 # command. If there are no channels available to return,
112 # only the end reply MUST be sent.
115 # "<channel> <nickname>"
117 RPL_CHANNELMODEIS=324
118 # "<channel> <mode> <mode params>"
121 # "<channel> :No topic is set"
123 # "<channel> :<topic>"
125 # - When sending a TOPIC message to determine the
126 # channel topic, one of two replies is sent. If
127 # the topic is set, RPL_TOPIC is sent back else
131 # <channel> <set by> <unixtime>
135 # - Returned by the server to indicate that the
136 # attempted INVITE message was successful and is
137 # being passed onto the end client.
140 # "<user> :Summoning user to IRC"
142 # - Returned by a server answering a SUMMON message to
143 # indicate that it is summoning that user.
146 # "<channel> <invitemask>"
147 RPL_ENDOFINVITELIST=347
148 # "<channel> :End of channel invite list"
150 # - When listing the 'invitations masks' for a given channel,
151 # a server is required to send the list back using the
152 # RPL_INVITELIST and RPL_ENDOFINVITELIST messages. A
153 # separate RPL_INVITELIST is sent for each active mask.
154 # After the masks have been listed (or if none present) a
155 # RPL_ENDOFINVITELIST MUST be sent.
158 # "<channel> <exceptionmask>"
159 RPL_ENDOFEXCEPTLIST=349
160 # "<channel> :End of channel exception list"
162 # - When listing the 'exception masks' for a given channel,
163 # a server is required to send the list back using the
164 # RPL_EXCEPTLIST and RPL_ENDOFEXCEPTLIST messages. A
165 # separate RPL_EXCEPTLIST is sent for each active mask.
166 # After the masks have been listed (or if none present)
167 # a RPL_ENDOFEXCEPTLIST MUST be sent.
170 # "<version>.<debuglevel> <server> :<comments>"
172 # - Reply by the server showing its version details.
173 # The <version> is the version of the software being
174 # used (including any patchlevel revisions) and the
175 # <debuglevel> is used to indicate if the server is
176 # running in "debug mode".
178 # The "comments" field may contain any comments about
179 # the version or further version details.
182 # "<channel> <user> <host> <server> <nick>
183 # ( "H" / "G" > ["*"] [ ( "@" / "+" ) ]
184 # :<hopcount> <real name>"
187 # "<name> :End of WHO list"
189 # - The RPL_WHOREPLY and RPL_ENDOFWHO pair are used
190 # to answer a WHO message. The RPL_WHOREPLY is only
191 # sent if there is an appropriate match to the WHO
192 # query. If there is a list of parameters supplied
193 # with a WHO message, a RPL_ENDOFWHO MUST be sent
194 # after processing each list item with <name> being
198 # "( "=" / "*" / "@" ) <channel>
199 # :[ "@" / "+" ] <nick> *( " " [ "@" / "+" ] <nick> )
200 # - "@" is used for secret channels, "*" for private
201 # channels, and "=" for others (public channels).
204 # "<channel> :End of NAMES list"
206 # - To reply to a NAMES message, a reply pair consisting
207 # of RPL_NAMREPLY and RPL_ENDOFNAMES is sent by the
208 # server back to the client. If there is no channel
209 # found as in the query, then only RPL_ENDOFNAMES is
210 # returned. The exception to this is when a NAMES
211 # message is sent with no parameters and all visible
212 # channels and contents are sent back in a series of
213 # RPL_NAMEREPLY messages with a RPL_ENDOFNAMES to mark
217 # "<mask> <server> :<hopcount> <server info>"
219 # "<mask> :End of LINKS list"
221 # - In replying to the LINKS message, a server MUST send
222 # replies back using the RPL_LINKS numeric and mark the
223 # end of the list using an RPL_ENDOFLINKS reply.
226 # "<channel> <banmask>"
228 # "<channel> :End of channel ban list"
230 # - When listing the active 'bans' for a given channel,
231 # a server is required to send the list back using the
232 # RPL_BANLIST and RPL_ENDOFBANLIST messages. A separate
233 # RPL_BANLIST is sent for each active banmask. After the
234 # banmasks have been listed (or if none present) a
235 # RPL_ENDOFBANLIST MUST be sent.
240 # ":End of INFO list"
242 # - A server responding to an INFO message is required to
243 # send all its 'info' in a series of RPL_INFO messages
244 # with a RPL_ENDOFINFO reply to indicate the end of the
248 # ":- <server> Message of the day - "
252 # ":End of MOTD command"
254 # - When responding to the MOTD message and the MOTD file
255 # is found, the file is displayed line by line, with
256 # each line no longer than 80 characters, using
257 # RPL_MOTD format replies. These MUST be surrounded
258 # by a RPL_MOTDSTART (before the RPL_MOTDs) and an
259 # RPL_ENDOFMOTD (after).
262 # ":You are now an IRC operator"
264 # - RPL_YOUREOPER is sent back to a client which has
265 # just successfully issued an OPER message and gained
269 # "<config file> :Rehashing"
271 # - If the REHASH option is used and an operator sends
272 # a REHASH message, an RPL_REHASHING is sent back to
276 # "You are service <servicename>"
278 # - Sent by the server to a service upon successful
282 # "<server> :<string showing server's local time>"
284 # - When replying to the TIME message, a server MUST send
285 # the reply using the RPL_TIME format above. The string
286 # showing the time need only contain the correct day and
287 # time there. There is no further requirement for the
291 # ":UserID Terminal Host"
293 # ":<username> <ttyline> <hostname>"
297 # ":Nobody logged in"
299 # - If the USERS message is handled by a server, the
300 # replies RPL_USERSTART, RPL_USERS, RPL_ENDOFUSERS and
301 # RPL_NOUSERS are used. RPL_USERSSTART MUST be sent
302 # first, following by either a sequence of RPL_USERS
303 # or a single RPL_NOUSER. Following this is
307 # "Link <version & debug level> <destination>
308 # <next server> V<protocol version>
309 # <link uptime in seconds> <backstream sendq>
311 RPL_TRACECONNECTING=201
312 # "Try. <class> <server>"
313 RPL_TRACEHANDSHAKE=202
314 # "H.S. <class> <server>"
316 # "???? <class> [<client IP address in dot form>]"
317 RPL_TRACEOPERATOR=204
318 # "Oper <class> <nick>"
320 # "User <class> <nick>"
322 # "Serv <class> <int>S <int>C <server>
323 # <nick!user|*!*>@<host|server> V<protocol version>"
325 # "Service <class> <name> <type> <active type>"
327 # "<newtype> 0 <client name>"
329 # "Class <class> <count>"
330 RPL_TRACERECONNECT=210
333 # "File <logfile> <debug level>"
335 # "<server name> <version & debug level> :End of TRACE"
337 # - The RPL_TRACE* are all returned by the server in
338 # response to the TRACE message. How many are
339 # returned is dependent on the TRACE message and
340 # whether it was sent by an operator or not. There
341 # is no predefined order for which occurs first.
342 # Replies RPL_TRACEUNKNOWN, RPL_TRACECONNECTING and
343 # RPL_TRACEHANDSHAKE are all used for connections
344 # which have not been fully established and are either
345 # unknown, still attempting to connect or in the
346 # process of completing the 'server handshake'.
347 # RPL_TRACELINK is sent by any server which handles
348 # a TRACE message and has to pass it on to another
349 # server. The list of RPL_TRACELINKs sent in
350 # response to a TRACE command traversing the IRC
351 # network should reflect the actual connectivity of
352 # the servers themselves along that path.
354 # RPL_TRACENEWTYPE is to be used for any connection
355 # which does not fit in the other categories but is
356 # being displayed anyway.
357 # RPL_TRACEEND is sent to indicate the end of the list.
360 # ":Current local users: 3 Max: 4"
362 # ":Current global users: 3 Max: 4"
364 # "::Highest connection count: 4 (4 clients) (251 since server was
366 RPL_STATSLINKINFO=211
367 # "<linkname> <sendq> <sent messages>
368 # <sent Kbytes> <received messages>
369 # <received Kbytes> <time open>"
371 # - reports statistics on a connection. <linkname>
372 # identifies the particular connection, <sendq> is
373 # the amount of data that is queued and waiting to be
374 # sent <sent messages> the number of messages sent,
375 # and <sent Kbytes> the amount of data sent, in
376 # Kbytes. <received messages> and <received Kbytes>
377 # are the equivalent of <sent messages> and <sent
378 # Kbytes> for received data, respectively. <time
379 # open> indicates how long ago the connection was
380 # opened, in seconds.
382 RPL_STATSCOMMANDS=212
383 # "<command> <count> <byte count> <remote count>"
385 # - reports statistics on commands usage.
388 # "<stats letter> :End of STATS report"
391 # ":Server Up %d days %d:%02d:%02d"
393 # - reports the server uptime.
396 # "O <hostmask> * <name>"
398 # - reports the allowed hosts from where user may become IRC
402 # "<user mode string>"
404 # - To answer a query about a client's own mode,
405 # RPL_UMODEIS is sent back.
408 # "<name> <server> <mask> <type> <hopcount> <info>"
411 # "<mask> <type> :End of service listing"
413 # - When listing services in reply to a SERVLIST message,
414 # a server is required to send the list back using the
415 # RPL_SERVLIST and RPL_SERVLISTEND messages. A separate
416 # RPL_SERVLIST is sent for each service. After the
417 # services have been listed (or if none present) a
418 # RPL_SERVLISTEND MUST be sent.
421 # ":There are <integer> users and <integer>
422 # services on <integer> servers"
424 # "<integer> :operator(s) online"
426 # "<integer> :unknown connection(s)"
427 RPL_LUSERCHANNELS=254
428 # "<integer> :channels formed"
430 # ":I have <integer> clients and <integer>
433 # - In processing an LUSERS message, the server
434 # sends a set of replies from RPL_LUSERCLIENT,
435 # RPL_LUSEROP, RPL_USERUNKNOWN,
436 # RPL_LUSERCHANNELS and RPL_LUSERME. When
437 # replying, a server MUST send back
438 # RPL_LUSERCLIENT and RPL_LUSERME. The other
439 # replies are only sent back if a non-zero count
443 # "<server> :Administrative info"
451 # - When replying to an ADMIN message, a server
452 # is expected to use replies RPL_ADMINME
453 # through to RPL_ADMINEMAIL and provide a text
454 # message with each. For RPL_ADMINLOC1 a
455 # description of what city, state and country
456 # the server is in is expected, followed by
457 # details of the institution (RPL_ADMINLOC2)
458 # and finally the administrative contact for the
459 # server (an email address here is REQUIRED)
463 # "<command> :Please wait a while and try again."
465 # - When a server drops a command without processing it,
466 # it MUST use the reply RPL_TRYAGAIN to inform the
467 # originating client.
471 # Error replies are found in the range from 400 to 599.
474 # "<nickname> :No such nick/channel"
476 # - Used to indicate the nickname parameter supplied to a
477 # command is currently unused.
480 # "<server name> :No such server"
482 # - Used to indicate the server name given currently
485 ERR_NOSUCHCHANNEL=403
486 # "<channel name> :No such channel"
488 # - Used to indicate the given channel name is invalid.
490 ERR_CANNOTSENDTOCHAN=404
491 # "<channel name> :Cannot send to channel"
493 # - Sent to a user who is either (a) not on a channel
494 # which is mode +n or (b) not a chanop (or mode +v) on
495 # a channel which has mode +m set or where the user is
496 # banned and is trying to send a PRIVMSG message to
499 ERR_TOOMANYCHANNELS=405
500 # "<channel name> :You have joined too many channels"
502 # - Sent to a user when they have joined the maximum
503 # number of allowed channels and they try to join
506 ERR_WASNOSUCHNICK=406
507 # "<nickname> :There was no such nickname"
509 # - Returned by WHOWAS to indicate there is no history
510 # information for that nickname.
512 ERR_TOOMANYTARGETS=407
513 # "<target> :<error code> recipients. <abort message>"
515 # - Returned to a client which is attempting to send a
516 # PRIVMSG/NOTICE using the user@host destination format
517 # and for a user@host which has several occurrences.
519 # - Returned to a client which trying to send a
520 # PRIVMSG/NOTICE to too many recipients.
522 # - Returned to a client which is attempting to JOIN a safe
523 # channel using the shortname when there are more than one
526 ERR_NOSUCHSERVICE=408
527 # "<service name> :No such service"
529 # - Returned to a client which is attempting to send a SQUERY
530 # to a service which does not exist.
533 # ":No origin specified"
535 # - PING or PONG message missing the originator parameter.
538 # ":No recipient given (<command>)"
542 # "<mask> :No toplevel domain specified"
544 # "<mask> :Wildcard in toplevel domain"
546 # "<mask> :Bad Server/host mask"
548 # - 412 - 415 are returned by PRIVMSG to indicate that
549 # the message wasn't delivered for some reason.
550 # ERR_NOTOPLEVEL and ERR_WILDTOPLEVEL are errors that
551 # are returned when an invalid use of
552 # "PRIVMSG $<server>" or "PRIVMSG #<host>" is attempted.
554 ERR_UNKNOWNCOMMAND=421
555 # "<command> :Unknown command"
557 # - Returned to a registered client to indicate that the
558 # command sent is unknown by the server.
561 # ":MOTD File is missing"
563 # - Server's MOTD file could not be opened by the server.
566 # "<server> :No administrative info available"
568 # - Returned by a server in response to an ADMIN message
569 # when there is an error in finding the appropriate
573 # ":File error doing <file op> on <file>"
575 # - Generic error message used to report a failed file
576 # operation during the processing of a message.
578 ERR_NONICKNAMEGIVEN=431
579 # ":No nickname given"
581 # - Returned when a nickname parameter expected for a
582 # command and isn't found.
584 ERR_ERRONEUSNICKNAME=432
585 # "<nick> :Erroneous nickname"
587 # - Returned after receiving a NICK message which contains
588 # characters which do not fall in the defined set. See
589 # section 2.3.1 for details on valid nicknames.
591 ERR_NICKNAMEINUSE=433
592 # "<nick> :Nickname is already in use"
594 # - Returned when a NICK message is processed that results
595 # in an attempt to change to a currently existing
598 ERR_NICKCOLLISION=436
599 # "<nick> :Nickname collision KILL from <user>@<host>"
601 # - Returned by a server to a client when it detects a
602 # nickname collision (registered of a NICK that
603 # already exists by another server).
605 ERR_UNAVAILRESOURCE=437
606 # "<nick/channel> :Nick/channel is temporarily unavailable"
608 # - Returned by a server to a user trying to join a channel
609 # currently blocked by the channel delay mechanism.
611 # - Returned by a server to a user trying to change nickname
612 # when the desired nickname is blocked by the nick delay
615 ERR_USERNOTINCHANNEL=441
616 # "<nick> <channel> :They aren't on that channel"
618 # - Returned by the server to indicate that the target
619 # user of the command is not on the given channel.
622 # "<channel> :You're not on that channel"
624 # - Returned by the server whenever a client tries to
625 # perform a channel affecting command for which the
626 # client isn't a member.
628 ERR_USERONCHANNEL=443
629 # "<user> <channel> :is already on channel"
631 # - Returned when a client tries to invite a user to a
632 # channel they are already on.
635 # "<user> :User not logged in"
637 # - Returned by the summon after a SUMMON command for a
638 # user was unable to be performed since they were not
642 ERR_SUMMONDISABLED=445
643 # ":SUMMON has been disabled"
645 # - Returned as a response to the SUMMON command. MUST be
646 # returned by any server which doesn't implement it.
648 ERR_USERSDISABLED=446
649 # ":USERS has been disabled"
651 # - Returned as a response to the USERS command. MUST be
652 # returned by any server which does not implement it.
654 ERR_NOTREGISTERED=451
655 # ":You have not registered"
657 # - Returned by the server to indicate that the client
658 # MUST be registered before the server will allow it
659 # to be parsed in detail.
661 ERR_NEEDMOREPARAMS=461
662 # "<command> :Not enough parameters"
664 # - Returned by the server by numerous commands to
665 # indicate to the client that it didn't supply enough
668 ERR_ALREADYREGISTRED=462
669 # ":Unauthorized command (already registered)"
671 # - Returned by the server to any link which tries to
672 # change part of the registered details (such as
673 # password or user details from second USER message).
675 ERR_NOPERMFORHOST=463
676 # ":Your host isn't among the privileged"
678 # - Returned to a client which attempts to register with
679 # a server which does not been setup to allow
680 # connections from the host the attempted connection
683 ERR_PASSWDMISMATCH=464
684 # ":Password incorrect"
686 # - Returned to indicate a failed attempt at registering
687 # a connection for which a password was required and
688 # was either not given or incorrect.
690 ERR_YOUREBANNEDCREEP=465
691 # ":You are banned from this server"
693 # - Returned after an attempt to connect and register
694 # yourself with a server which has been setup to
695 # explicitly deny connections to you.
697 ERR_YOUWILLBEBANNED=466
699 # - Sent by a server to a user to inform that access to the
700 # server will soon be denied.
703 # "<channel> :Channel key already set"
704 ERR_CHANNELISFULL=471
705 # "<channel> :Cannot join channel (+l)"
707 # "<char> :is unknown mode char to me for <channel>"
708 ERR_INVITEONLYCHAN=473
709 # "<channel> :Cannot join channel (+i)"
710 ERR_BANNEDFROMCHAN=474
711 # "<channel> :Cannot join channel (+b)"
712 ERR_BADCHANNELKEY=475
713 # "<channel> :Cannot join channel (+k)"
715 # "<channel> :Bad Channel Mask"
717 # "<channel> :Channel doesn't support modes"
719 # "<channel> <char> :Channel list is full"
722 # ":Permission Denied- You're not an IRC operator"
724 # - Any command requiring operator privileges to operate
725 # MUST return this error to indicate the attempt was
728 ERR_CHANOPRIVSNEEDED=482
729 # "<channel> :You're not channel operator"
731 # - Any command requiring 'chanop' privileges (such as
732 # MODE messages) MUST return this error if the client
733 # making the attempt is not a chanop on the specified
737 ERR_CANTKILLSERVER=483
738 # ":You can't kill a server!"
740 # - Any attempts to use the KILL command on a server
741 # are to be refused and this error returned directly
745 # ":Your connection is restricted!"
747 # - Sent by the server to a user upon connection to indicate
748 # the restricted nature of the connection (user mode "+r").
750 ERR_UNIQOPPRIVSNEEDED=485
751 # ":You're not the original channel operator"
753 # - Any MODE requiring "channel creator" privileges MUST
754 # return this error if the client making the attempt is not
755 # a chanop on the specified channel.
758 # ":No O-lines for your host"
760 # - If a client sends an OPER message and the server has
761 # not been configured to allow connections from the
762 # client's host as an operator, this error MUST be
765 ERR_UMODEUNKNOWNFLAG=501
766 # ":Unknown MODE flag"
768 # - Returned by the server to indicate that a MODE
769 # message was sent with a nickname parameter and that
770 # the a mode flag sent was not recognized.
772 ERR_USERSDONTMATCH=502
773 # ":Cannot change mode for other users"
775 # - Error sent to any user trying to view or change the
776 # user mode for a user other than themselves.
778 # 5.3 Reserved numerics
780 # These numerics are not described above since they fall into one of
781 # the following categories:
783 # 1. no longer in use;
785 # 2. reserved for future planned use;
787 # 3. in current use but are part of a non-generic 'feature' of
788 # the current IRC server.
790 RPL_ENDOFSERVICES=232
811 ERR_NOSERVICEHOST=492
814 # implements RFC 2812 and prior IRC RFCs.
815 # clients register handler proc{}s for different server events and IrcClient
819 attr_reader :server, :client
821 # create a new IrcClient instance
823 @server = Server.new # The Server
824 @client = @server.user("") # The User representing the client on this Server
828 # This is used by some messages to build lists of users that
829 # will be delegated when the ENDOF... message is received
833 # key:: server event to handle
834 # value:: proc object called when event occurs
835 # set a handler for a server event
837 # ==server events currently supported:
839 # welcome:: server welcome message on connect
840 # yourhost:: your host details (on connection)
841 # created:: when the server was started
842 # isupport:: information about what this server supports
843 # ping:: server pings you (default handler returns a pong)
844 # nicktaken:: you tried to change nick to one that's in use
845 # badnick:: you tried to change nick to one that's invalid
846 # topic:: someone changed the topic of a channel
847 # topicinfo:: on joining a channel or asking for the topic, tells you
848 # who set it and when
849 # names:: server sends list of channel members when you join
850 # motd:: server message of the day
851 # privmsg:: privmsg, the core of IRC, a message to you from someone
852 # public:: optionally instead of getting privmsg you can hook to only
854 # msg:: or only the private ones, or both
855 # kick:: someone got kicked from a channel
856 # part:: someone left a channel
857 # quit:: someone quit IRC
858 # join:: someone joined a channel
859 # changetopic:: the topic of a channel changed
860 # invite:: you are invited to a channel
861 # nick:: someone changed their nick
862 # mode:: a mode change
863 # notice:: someone sends you a notice
864 # unknown:: any other message not handled by the above
866 @handlers[key] = value
870 # remove a handler for a server event
871 def deletehandler(key)
872 @handlers.delete(key)
875 # takes a server string, checks for PING, PRIVMSG, NOTIFY, etc, and parses
876 # numeric server replies, calling the appropriate handler for each, and
877 # sending it a hash containing the data from the server
878 def process(serverstring)
880 data[:serverstring] = serverstring
882 unless serverstring =~ /^(:(\S+)\s)?(\S+)(\s(.*))?/
883 raise "Unparseable Server Message!!!: #{serverstring}"
886 prefix, command, params = $2, $3, $5
889 data[:source] = prefix
890 if prefix =~ /^(\S+)!(\S+)$/
891 data[:source] = @server.user(prefix)
893 if @server.hostname && @server.hostname != data[:source]
894 warning "Unknown origin #{data[:source]} for message\n#{serverstring.inspect}"
896 @server.instance_variable_set(:@hostname, data[:source])
898 data[:source] = @server
902 # split parameters in an array
904 params.scan(/(?!:)(\S+)|:(.*)/) { argv << ($1 || $2) } if params
908 data[:pingid] = argv[0]
911 data[:pingid] = argv[0]
913 when /^(\d+)$/ # numerical server message
917 # "Welcome to the Internet Relay Network
918 # <nick>!<user>@<host>"
924 @client = @server.user(data[:netmask])
926 when /Welcome to the Internet Relay Network\s(\S+)/
928 when /Welcome.*\s+(\S+)$/
933 @client = @server.user(data[:nick]) unless set
934 handle(:welcome, data)
936 # "Your host is <servername>, running version <ver>"
937 handle(:yourhost, data)
939 # "This server was created <date>"
940 data[:message] = argv[1]
941 handle(:created, data)
943 # "<servername> <version> <available user modes>
944 # <available channel modes>"
945 @server.parse_my_info(params.split(' ', 2).last)
946 data[:servername] = @server.hostname
947 data[:version] = @server.version
948 data[:usermodes] = @server.usermodes
949 data[:chanmodes] = @server.chanmodes
950 handle(:myinfo, data)
952 # "PREFIX=(ov)@+ CHANTYPES=#& :are supported by this server"
953 # "MODES=4 CHANLIMIT=#:20 NICKLEN=16 USERLEN=10 HOSTLEN=63
954 # TOPICLEN=450 KICKLEN=450 CHANNELLEN=30 KEYLEN=23 CHANTYPES=#
955 # PREFIX=(ov)@+ CASEMAPPING=ascii CAPAB IRCD=dancer :are available
958 @server.parse_isupport(argv[1..-2].join(' '))
959 handle(:isupport, data)
960 when ERR_NICKNAMEINUSE
961 # "* <nick> :Nickname is already in use"
962 data[:nick] = argv[1]
963 data[:message] = argv[2]
964 handle(:nicktaken, data)
965 when ERR_ERRONEUSNICKNAME
966 # "* <nick> :Erroneous nickname"
967 data[:nick] = argv[1]
968 data[:message] = argv[2]
969 handle(:badnick, data)
971 data[:channel] = @server.get_channel(argv[1])
972 data[:topic] = argv[2]
975 data[:channel].topic.text = data[:topic]
977 warning "Received topic #{data[:topic].inspect} for channel #{data[:channel].inspect} I was not on"
982 data[:nick] = @server.user(argv[0])
983 data[:channel] = @server.get_channel(argv[1])
984 data[:source] = @server.user(argv[2])
985 data[:time] = Time.at(argv[3].to_i)
988 data[:channel].topic.set_by = data[:nick]
989 data[:channel].topic.set_on = data[:time]
991 warning "Received topic #{data[:topic].inspect} for channel #{data[:channel].inspect} I was not on"
994 handle(:topicinfo, data)
996 # "( "=" / "*" / "@" ) <channel>
997 # :[ "@" / "+" ] <nick> *( " " [ "@" / "+" ] <nick> )
998 # - "@" is used for secret channels, "*" for private
999 # channels, and "=" for others (public channels).
1000 data[:channeltype] = argv[1]
1001 data[:channel] = argv[2]
1003 chan = @server.get_channel(data[:channel])
1005 warning "Received names #{data[:topic].inspect} for channel #{data[:channel].inspect} I was not on"
1010 argv[3].scan(/\S+/).each { |u|
1011 # FIXME beware of servers that allow multiple prefixes
1012 if(u =~ /^(#{@server.supports[:prefix][:prefixes].join})?(.*)$/)
1015 users << [user, umode]
1020 u = @server.user(ar[0])
1021 chan.users << u unless chan.users.include?(u)
1023 m = @server.supports[:prefix][:prefixes].index(ar[1].to_sym)
1024 m = @server.supports[:prefix][:modes][m]
1025 chan.mode[m.to_sym].set(u)
1030 data[:channel] = argv[1]
1031 data[:users] = @tmpusers
1032 handle(:names, data)
1033 @tmpusers = Array.new
1034 when RPL_LUSERCLIENT
1035 # ":There are <integer> users and <integer>
1036 # services on <integer> servers"
1037 data[:message] = argv[1]
1038 handle(:luserclient, data)
1040 # "<integer> :operator(s) online"
1041 data[:ops] = argv[1].to_i
1042 handle(:luserop, data)
1043 when RPL_LUSERUNKNOWN
1044 # "<integer> :unknown connection(s)"
1045 data[:unknown] = argv[1].to_i
1046 handle(:luserunknown, data)
1047 when RPL_LUSERCHANNELS
1048 # "<integer> :channels formed"
1049 data[:channels] = argv[1].to_i
1050 handle(:luserchannels, data)
1052 # ":I have <integer> clients and <integer> servers"
1053 data[:message] = argv[1]
1054 handle(:luserme, data)
1056 # ":MOTD File is missing"
1057 data[:message] = argv[1]
1058 handle(:motd_missing, data)
1060 # ":Current local users: 3 Max: 4"
1061 data[:message] = argv[1]
1062 handle(:localusers, data)
1063 when RPL_GLOBALUSERS
1064 # ":Current global users: 3 Max: 4"
1065 data[:message] = argv[1]
1066 handle(:globalusers, data)
1068 # ":Highest connection count: 4 (4 clients) (251 since server was
1070 data[:message] = argv[1]
1071 handle(:statsconn, data)
1073 # "<nick> :- <server> Message of the Day -"
1074 if argv[1] =~ /^-\s+(\S+)\s/
1079 if(argv[1] =~ /^-\s+(.*)$/)
1087 data[:text] = argv[1]
1088 handle(:datastr, data)
1090 handle(:unknown, data)
1092 # end of numeric replies
1094 # you can either bind to 'PRIVMSG', to get every one and
1095 # parse it yourself, or you can bind to 'MSG', 'PUBLIC',
1096 # etc and get it all nicely split up for you.
1098 data[:target] = @server.user_or_channel(argv[0])
1099 data[:message] = argv[1]
1100 handle(:privmsg, data)
1103 if data[:target].kind_of?(Channel)
1104 handle(:public, data)
1109 data[:target] = @server.user_or_channel(argv[0])
1110 data[:message] = argv[1]
1113 handle(:notice, data)
1115 # "server notice" (not from user, noone to reply to)
1116 handle(:snotice, data)
1119 data[:channel] = @server.channel(argv[0])
1120 data[:target] = @server.user(argv[1])
1121 data[:message] = argv[2]
1123 @server.delete_user_from_channel(data[:target], data[:channel])
1124 if data[:target] == @client
1125 @server.delete_channel(data[:channel])
1130 data[:channel] = @server.channel(argv[0])
1131 data[:message] = argv[1]
1133 @server.delete_user_from_channel(data[:source], data[:channel])
1134 if data[:source] == @client
1135 @server.delete_channel(data[:channel])
1140 data[:message] = argv[0]
1141 data[:was_on] = @server.channels.inject(ChannelList.new) { |list, ch|
1142 list << ch if ch.users.include?(data[:source])
1145 @server.delete_user(data[:source])
1149 data[:channel] = @server.channel(argv[0])
1150 data[:channel].users << data[:source]
1154 data[:channel] = @server.channel(argv[0])
1155 data[:topic] = Channel::Topic.new(argv[1], data[:source], Time.new)
1156 data[:channel].topic.replace(data[:topic])
1158 handle(:changetopic, data)
1160 data[:target] = @server.user(argv[0])
1161 data[:channel] = @server.channel(argv[1])
1163 handle(:invite, data)
1165 data[:is_on] = @server.channels.inject(ChannelList.new) { |list, ch|
1166 list << ch if ch.users.include?(data[:source])
1170 data[:newnick] = argv[0]
1171 data[:oldnick] = data[:source].nick.dup
1172 data[:source].nick = data[:newnick]
1174 debug "#{data[:oldnick]} (now #{data[:newnick]}) was on #{data[:is_on].join(', ')}"
1178 # MODE ([+-]<modes> (<params>)*)*
1179 # When a MODE message is received by a server,
1180 # Type C will have parameters too, so we must
1181 # be able to consume parameters for all
1184 data[:channel] = @server.user_or_channel(argv[0])
1185 data[:modestring] = argv[1..-1].join(" ")
1190 # data[:modes] is an array where each element
1191 # is either a flag which doesn't need parameters
1192 # or an array with a flag which needs parameters
1193 # and the corresponding parameter
1195 # array of indices in data[:modes] where parameters
1197 who_wants_params = []
1199 argv[1..-1].each { |arg|
1200 setting = arg[0].chr
1201 if "+-".include?(setting)
1202 arg[1..-1].each_byte { |b|
1205 when *@server.supports[:chanmodes][:typea]
1206 data[:modes] << [setting + m]
1207 who_wants_params << data[:modes].length - 1
1208 when *@server.supports[:chanmodes][:typeb]
1209 data[:modes] << [setting + m]
1210 who_wants_params << data[:modes].length - 1
1211 when *@server.supports[:chanmodes][:typec]
1213 data[:modes] << [setting + m]
1214 who_wants_params << data[:modes].length - 1
1216 data[:modes] << setting + m
1218 when *@server.supports[:chanmodes][:typed]
1219 data[:modes] << setting + m
1220 when *@server.supports[:prefix][:modes]
1221 data[:modes] << [setting + m]
1222 who_wants_params << data[:modes].length - 1
1224 warn "Unknown mode #{m} in #{serverstring}"
1228 idx = who_wants_params.shift
1230 warn "Oops, problems parsing #{serverstring}"
1233 data[:modes][idx] << arg
1238 data[:modes].each { |mode|
1241 set = mode[0][0].chr == "+" ? :set : :reset
1242 key = mode[0][1].chr.to_sym
1244 data[:channel].mode[key].send(set, val)
1246 set = mode[0].chr == "+" ? :set : :reset
1247 key = mode[1].chr.to_sym
1248 data[:channel].mode[key].send(set)
1254 handle(:unknown, data)
1260 # key:: server event name
1261 # data:: hash containing data about the event, passed to the proc
1262 # call client's proc for an event, if they set one as a handler
1263 def handle(key, data)
1264 if(@handlers.has_key?(key))
1265 @handlers[key].call(data)