module Irc
# RFC 2812 Internet Relay Chat: Client Protocol
- #
+ #
RPL_WELCOME=001
# "Welcome to the Internet Relay Network
# <nick>!<user>@<host>"
RPL_MYINFO=004
# "<servername> <version> <available user modes>
# <available channel modes>"
- #
+ #
# - The server sends Replies 001 to 004 to a user upon
# successful registration.
- #
+ #
# RPL_BOUNCE=005
# # "Try server <server name>, port <port number>"
RPL_ISUPPORT=005
# "005 nick PREFIX=(ov)@+ CHANTYPES=#& :are supported by this server"
- #
+ #
# - Sent by the server to a user to suggest an alternative
# server. This is often used when the connection is
# refused because the server is already full.
- #
+ #
RPL_USERHOST=302
# ":*1<reply> *( " " <reply> )"
- #
+ #
# - Reply format used by USERHOST to list replies to
# the query list. The reply string is composed as
# follows:
- #
+ #
# reply = nickname [ "*" ] "=" ( "+" / "-" ) hostname
- #
+ #
# The '*' indicates whether the client has registered
# as an Operator. The '-' or '+' characters represent
# whether the client has set an AWAY message or not
# respectively.
- #
+ #
RPL_ISON=303
# ":*1<nick> *( " " <nick> )"
- #
+ #
# - Reply format used by ISON to list replies to the
# query list.
- #
+ #
RPL_AWAY=301
# "<nick> :<away message>"
RPL_UNAWAY=305
# ":You are no longer marked as being away"
RPL_NOWAWAY=306
# ":You have been marked as being away"
- #
+ #
# - These replies are used with the AWAY command (if
# allowed). RPL_AWAY is sent to any client sending a
# PRIVMSG to a client which is away. RPL_AWAY is only
# sent by the server to which the client is connected.
# Replies RPL_UNAWAY and RPL_NOWAWAY are sent when the
# client removes and sets an AWAY message.
- #
+ #
RPL_WHOISUSER=311
# "<nick> <user> <host> * :<real name>"
RPL_WHOISSERVER=312
# "<nick> :End of WHOIS list"
RPL_WHOISCHANNELS=319
# "<nick> :*( ( "@" / "+" ) <channel> " " )"
- #
+ #
# - Replies 311 - 313, 317 - 319 are all replies
# generated in response to a WHOIS message. Given that
# there are enough parameters present, the answering
# has been granted permission to speak on a moderated
# channel. The RPL_ENDOFWHOIS reply is used to mark
# the end of processing a WHOIS message.
- #
+ #
RPL_WHOWASUSER=314
# "<nick> <user> <host> * :<real name>"
RPL_ENDOFWHOWAS=369
# "<nick> :End of WHOWAS"
- #
+ #
# - When replying to a WHOWAS message, a server MUST use
# the replies RPL_WHOWASUSER, RPL_WHOISSERVER or
# ERR_WASNOSUCHNICK for each nickname in the presented
# list. At the end of all reply batches, there MUST
# be RPL_ENDOFWHOWAS (even if there was only one reply
# and it was an error).
- #
+ #
RPL_LISTSTART=321
# Obsolete. Not used.
- #
+ #
RPL_LIST=322
# "<channel> <# visible> :<topic>"
RPL_LISTEND=323
# ":End of LIST"
- #
+ #
# - Replies RPL_LIST, RPL_LISTEND mark the actual replies
# with data and end of the server's response to a LIST
# command. If there are no channels available to return,
# only the end reply MUST be sent.
- #
+ #
RPL_UNIQOPIS=325
# "<channel> <nickname>"
- #
+ #
RPL_CHANNELMODEIS=324
# "<channel> <mode> <mode params>"
- #
+ #
RPL_NOTOPIC=331
# "<channel> :No topic is set"
RPL_TOPIC=332
# "<channel> :<topic>"
- #
+ #
# - When sending a TOPIC message to determine the
# channel topic, one of two replies is sent. If
# the topic is set, RPL_TOPIC is sent back else
# RPL_NOTOPIC.
- #
+ #
RPL_TOPIC_INFO=333
# <channel> <set by> <unixtime>
RPL_INVITING=341
# "<channel> <nick>"
- #
+ #
# - Returned by the server to indicate that the
# attempted INVITE message was successful and is
# being passed onto the end client.
- #
+ #
RPL_SUMMONING=342
# "<user> :Summoning user to IRC"
- #
+ #
# - Returned by a server answering a SUMMON message to
# indicate that it is summoning that user.
- #
+ #
RPL_INVITELIST=346
# "<channel> <invitemask>"
RPL_ENDOFINVITELIST=347
# "<channel> :End of channel invite list"
- #
+ #
# - When listing the 'invitations masks' for a given channel,
# a server is required to send the list back using the
# RPL_INVITELIST and RPL_ENDOFINVITELIST messages. A
# separate RPL_INVITELIST is sent for each active mask.
# After the masks have been listed (or if none present) a
# RPL_ENDOFINVITELIST MUST be sent.
- #
+ #
RPL_EXCEPTLIST=348
# "<channel> <exceptionmask>"
RPL_ENDOFEXCEPTLIST=349
# "<channel> :End of channel exception list"
- #
+ #
# - When listing the 'exception masks' for a given channel,
# a server is required to send the list back using the
# RPL_EXCEPTLIST and RPL_ENDOFEXCEPTLIST messages. A
# separate RPL_EXCEPTLIST is sent for each active mask.
# After the masks have been listed (or if none present)
# a RPL_ENDOFEXCEPTLIST MUST be sent.
- #
+ #
RPL_VERSION=351
# "<version>.<debuglevel> <server> :<comments>"
- #
+ #
# - Reply by the server showing its version details.
# The <version> is the version of the software being
# used (including any patchlevel revisions) and the
# <debuglevel> is used to indicate if the server is
# running in "debug mode".
- #
+ #
# The "comments" field may contain any comments about
# the version or further version details.
- #
+ #
RPL_WHOREPLY=352
# "<channel> <user> <host> <server> <nick>
# ( "H" / "G" > ["*"] [ ( "@" / "+" ) ]
# :<hopcount> <real name>"
- #
+ #
RPL_ENDOFWHO=315
# "<name> :End of WHO list"
- #
+ #
# - The RPL_WHOREPLY and RPL_ENDOFWHO pair are used
# to answer a WHO message. The RPL_WHOREPLY is only
# sent if there is an appropriate match to the WHO
# with a WHO message, a RPL_ENDOFWHO MUST be sent
# after processing each list item with <name> being
# the item.
- #
+ #
RPL_NAMREPLY=353
# "( "=" / "*" / "@" ) <channel>
# :[ "@" / "+" ] <nick> *( " " [ "@" / "+" ] <nick> )
# - "@" is used for secret channels, "*" for private
# channels, and "=" for others (public channels).
- #
+ #
RPL_ENDOFNAMES=366
# "<channel> :End of NAMES list"
- #
+ #
# - To reply to a NAMES message, a reply pair consisting
# of RPL_NAMREPLY and RPL_ENDOFNAMES is sent by the
# server back to the client. If there is no channel
# channels and contents are sent back in a series of
# RPL_NAMEREPLY messages with a RPL_ENDOFNAMES to mark
# the end.
- #
+ #
RPL_LINKS=364
# "<mask> <server> :<hopcount> <server info>"
RPL_ENDOFLINKS=365
# "<mask> :End of LINKS list"
- #
+ #
# - In replying to the LINKS message, a server MUST send
# replies back using the RPL_LINKS numeric and mark the
# end of the list using an RPL_ENDOFLINKS reply.
- #
+ #
RPL_BANLIST=367
# "<channel> <banmask>"
RPL_ENDOFBANLIST=368
# "<channel> :End of channel ban list"
- #
+ #
# - When listing the active 'bans' for a given channel,
# a server is required to send the list back using the
# RPL_BANLIST and RPL_ENDOFBANLIST messages. A separate
# RPL_BANLIST is sent for each active banmask. After the
# banmasks have been listed (or if none present) a
# RPL_ENDOFBANLIST MUST be sent.
- #
+ #
RPL_INFO=371
# ":<string>"
RPL_ENDOFINFO=374
# ":End of INFO list"
- #
+ #
# - A server responding to an INFO message is required to
# send all its 'info' in a series of RPL_INFO messages
# with a RPL_ENDOFINFO reply to indicate the end of the
# replies.
- #
+ #
RPL_MOTDSTART=375
# ":- <server> Message of the day - "
RPL_MOTD=372
# ":- <text>"
RPL_ENDOFMOTD=376
# ":End of MOTD command"
- #
+ #
# - When responding to the MOTD message and the MOTD file
# is found, the file is displayed line by line, with
# each line no longer than 80 characters, using
# RPL_MOTD format replies. These MUST be surrounded
# by a RPL_MOTDSTART (before the RPL_MOTDs) and an
# RPL_ENDOFMOTD (after).
- #
+ #
RPL_YOUREOPER=381
# ":You are now an IRC operator"
- #
+ #
# - RPL_YOUREOPER is sent back to a client which has
# just successfully issued an OPER message and gained
# operator status.
- #
+ #
RPL_REHASHING=382
# "<config file> :Rehashing"
- #
+ #
# - If the REHASH option is used and an operator sends
# a REHASH message, an RPL_REHASHING is sent back to
# the operator.
- #
+ #
RPL_YOURESERVICE=383
# "You are service <servicename>"
- #
+ #
# - Sent by the server to a service upon successful
# registration.
- #
+ #
RPL_TIME=391
# "<server> :<string showing server's local time>"
- #
+ #
# - When replying to the TIME message, a server MUST send
# the reply using the RPL_TIME format above. The string
# showing the time need only contain the correct day and
# time there. There is no further requirement for the
# time string.
- #
+ #
RPL_USERSSTART=392
# ":UserID Terminal Host"
RPL_USERS=393
# ":End of users"
RPL_NOUSERS=395
# ":Nobody logged in"
- #
+ #
# - If the USERS message is handled by a server, the
# replies RPL_USERSTART, RPL_USERS, RPL_ENDOFUSERS and
# RPL_NOUSERS are used. RPL_USERSSTART MUST be sent
# first, following by either a sequence of RPL_USERS
# or a single RPL_NOUSER. Following this is
# RPL_ENDOFUSERS.
- #
+ #
RPL_TRACELINK=200
# "Link <version & debug level> <destination>
# <next server> V<protocol version>
# "File <logfile> <debug level>"
RPL_TRACEEND=262
# "<server name> <version & debug level> :End of TRACE"
- #
+ #
# - The RPL_TRACE* are all returned by the server in
# response to the TRACE message. How many are
# returned is dependent on the TRACE message and
# response to a TRACE command traversing the IRC
# network should reflect the actual connectivity of
# the servers themselves along that path.
- #
+ #
# RPL_TRACENEWTYPE is to be used for any connection
# which does not fit in the other categories but is
# being displayed anyway.
# RPL_TRACEEND is sent to indicate the end of the list.
- #
+ #
RPL_LOCALUSERS=265
# ":Current local users: 3 Max: 4"
RPL_GLOBALUSERS=266
# "<linkname> <sendq> <sent messages>
# <sent Kbytes> <received messages>
# <received Kbytes> <time open>"
- #
+ #
# - reports statistics on a connection. <linkname>
# identifies the particular connection, <sendq> is
# the amount of data that is queued and waiting to be
# Kbytes> for received data, respectively. <time
# open> indicates how long ago the connection was
# opened, in seconds.
- #
+ #
RPL_STATSCOMMANDS=212
# "<command> <count> <byte count> <remote count>"
- #
+ #
# - reports statistics on commands usage.
- #
+ #
RPL_ENDOFSTATS=219
# "<stats letter> :End of STATS report"
- #
+ #
RPL_STATSUPTIME=242
# ":Server Up %d days %d:%02d:%02d"
- #
+ #
# - reports the server uptime.
- #
+ #
RPL_STATSOLINE=243
# "O <hostmask> * <name>"
- #
+ #
# - reports the allowed hosts from where user may become IRC
# operators.
- #
+ #
RPL_UMODEIS=221
# "<user mode string>"
- #
+ #
# - To answer a query about a client's own mode,
# RPL_UMODEIS is sent back.
- #
+ #
RPL_SERVLIST=234
# "<name> <server> <mask> <type> <hopcount> <info>"
- #
+ #
RPL_SERVLISTEND=235
# "<mask> <type> :End of service listing"
- #
+ #
# - When listing services in reply to a SERVLIST message,
# a server is required to send the list back using the
# RPL_SERVLIST and RPL_SERVLISTEND messages. A separate
# RPL_SERVLIST is sent for each service. After the
# services have been listed (or if none present) a
# RPL_SERVLISTEND MUST be sent.
- #
+ #
RPL_LUSERCLIENT=251
# ":There are <integer> users and <integer>
# services on <integer> servers"
RPL_LUSERME=255
# ":I have <integer> clients and <integer>
# servers"
- #
+ #
# - In processing an LUSERS message, the server
# sends a set of replies from RPL_LUSERCLIENT,
# RPL_LUSEROP, RPL_USERUNKNOWN,
# RPL_LUSERCLIENT and RPL_LUSERME. The other
# replies are only sent back if a non-zero count
# is found for them.
- #
+ #
RPL_ADMINME=256
# "<server> :Administrative info"
RPL_ADMINLOC1=257
# ":<admin info>"
RPL_ADMINEMAIL=259
# ":<admin info>"
- #
+ #
# - When replying to an ADMIN message, a server
# is expected to use replies RPL_ADMINME
# through to RPL_ADMINEMAIL and provide a text
# and finally the administrative contact for the
# server (an email address here is REQUIRED)
# in RPL_ADMINEMAIL.
- #
+ #
RPL_TRYAGAIN=263
# "<command> :Please wait a while and try again."
- #
+ #
# - When a server drops a command without processing it,
# it MUST use the reply RPL_TRYAGAIN to inform the
# originating client.
- #
+ #
# 5.2 Error Replies
- #
+ #
# Error replies are found in the range from 400 to 599.
- #
+ #
ERR_NOSUCHNICK=401
# "<nickname> :No such nick/channel"
- #
+ #
# - Used to indicate the nickname parameter supplied to a
# command is currently unused.
- #
+ #
ERR_NOSUCHSERVER=402
# "<server name> :No such server"
- #
+ #
# - Used to indicate the server name given currently
# does not exist.
- #
+ #
ERR_NOSUCHCHANNEL=403
# "<channel name> :No such channel"
- #
+ #
# - Used to indicate the given channel name is invalid.
- #
+ #
ERR_CANNOTSENDTOCHAN=404
# "<channel name> :Cannot send to channel"
- #
+ #
# - Sent to a user who is either (a) not on a channel
# which is mode +n or (b) not a chanop (or mode +v) on
# a channel which has mode +m set or where the user is
# banned and is trying to send a PRIVMSG message to
# that channel.
- #
+ #
ERR_TOOMANYCHANNELS=405
# "<channel name> :You have joined too many channels"
- #
+ #
# - Sent to a user when they have joined the maximum
# number of allowed channels and they try to join
# another channel.
- #
+ #
ERR_WASNOSUCHNICK=406
# "<nickname> :There was no such nickname"
- #
+ #
# - Returned by WHOWAS to indicate there is no history
# information for that nickname.
- #
+ #
ERR_TOOMANYTARGETS=407
# "<target> :<error code> recipients. <abort message>"
- #
+ #
# - Returned to a client which is attempting to send a
# PRIVMSG/NOTICE using the user@host destination format
# and for a user@host which has several occurrences.
- #
+ #
# - Returned to a client which trying to send a
# PRIVMSG/NOTICE to too many recipients.
- #
+ #
# - Returned to a client which is attempting to JOIN a safe
# channel using the shortname when there are more than one
# such channel.
- #
+ #
ERR_NOSUCHSERVICE=408
# "<service name> :No such service"
- #
+ #
# - Returned to a client which is attempting to send a SQUERY
# to a service which does not exist.
- #
+ #
ERR_NOORIGIN=409
# ":No origin specified"
- #
+ #
# - PING or PONG message missing the originator parameter.
- #
+ #
ERR_NORECIPIENT=411
# ":No recipient given (<command>)"
ERR_NOTEXTTOSEND=412
# "<mask> :Wildcard in toplevel domain"
ERR_BADMASK=415
# "<mask> :Bad Server/host mask"
- #
+ #
# - 412 - 415 are returned by PRIVMSG to indicate that
# the message wasn't delivered for some reason.
# ERR_NOTOPLEVEL and ERR_WILDTOPLEVEL are errors that
# are returned when an invalid use of
# "PRIVMSG $<server>" or "PRIVMSG #<host>" is attempted.
- #
+ #
ERR_UNKNOWNCOMMAND=421
# "<command> :Unknown command"
- #
+ #
# - Returned to a registered client to indicate that the
# command sent is unknown by the server.
- #
+ #
ERR_NOMOTD=422
# ":MOTD File is missing"
- #
+ #
# - Server's MOTD file could not be opened by the server.
- #
+ #
ERR_NOADMININFO=423
# "<server> :No administrative info available"
- #
+ #
# - Returned by a server in response to an ADMIN message
# when there is an error in finding the appropriate
# information.
- #
+ #
ERR_FILEERROR=424
# ":File error doing <file op> on <file>"
- #
+ #
# - Generic error message used to report a failed file
# operation during the processing of a message.
- #
+ #
ERR_NONICKNAMEGIVEN=431
# ":No nickname given"
- #
+ #
# - Returned when a nickname parameter expected for a
# command and isn't found.
- #
+ #
ERR_ERRONEUSNICKNAME=432
# "<nick> :Erroneous nickname"
- #
+ #
# - Returned after receiving a NICK message which contains
# characters which do not fall in the defined set. See
# section 2.3.1 for details on valid nicknames.
- #
+ #
ERR_NICKNAMEINUSE=433
# "<nick> :Nickname is already in use"
- #
+ #
# - Returned when a NICK message is processed that results
# in an attempt to change to a currently existing
# nickname.
- #
+ #
ERR_NICKCOLLISION=436
# "<nick> :Nickname collision KILL from <user>@<host>"
- #
+ #
# - Returned by a server to a client when it detects a
# nickname collision (registered of a NICK that
# already exists by another server).
- #
+ #
ERR_UNAVAILRESOURCE=437
# "<nick/channel> :Nick/channel is temporarily unavailable"
- #
+ #
# - Returned by a server to a user trying to join a channel
# currently blocked by the channel delay mechanism.
- #
+ #
# - Returned by a server to a user trying to change nickname
# when the desired nickname is blocked by the nick delay
# mechanism.
- #
+ #
ERR_USERNOTINCHANNEL=441
# "<nick> <channel> :They aren't on that channel"
- #
+ #
# - Returned by the server to indicate that the target
# user of the command is not on the given channel.
- #
+ #
ERR_NOTONCHANNEL=442
# "<channel> :You're not on that channel"
- #
+ #
# - Returned by the server whenever a client tries to
# perform a channel affecting command for which the
# client isn't a member.
- #
+ #
ERR_USERONCHANNEL=443
# "<user> <channel> :is already on channel"
- #
+ #
# - Returned when a client tries to invite a user to a
# channel they are already on.
- #
+ #
ERR_NOLOGIN=444
# "<user> :User not logged in"
- #
+ #
# - Returned by the summon after a SUMMON command for a
# user was unable to be performed since they were not
# logged in.
- #
- #
+ #
+ #
ERR_SUMMONDISABLED=445
# ":SUMMON has been disabled"
- #
+ #
# - Returned as a response to the SUMMON command. MUST be
# returned by any server which doesn't implement it.
- #
+ #
ERR_USERSDISABLED=446
# ":USERS has been disabled"
- #
+ #
# - Returned as a response to the USERS command. MUST be
# returned by any server which does not implement it.
- #
+ #
ERR_NOTREGISTERED=451
# ":You have not registered"
- #
+ #
# - Returned by the server to indicate that the client
# MUST be registered before the server will allow it
# to be parsed in detail.
- #
+ #
ERR_NEEDMOREPARAMS=461
# "<command> :Not enough parameters"
- #
+ #
# - Returned by the server by numerous commands to
# indicate to the client that it didn't supply enough
# parameters.
- #
+ #
ERR_ALREADYREGISTRED=462
# ":Unauthorized command (already registered)"
- #
+ #
# - Returned by the server to any link which tries to
# change part of the registered details (such as
# password or user details from second USER message).
- #
+ #
ERR_NOPERMFORHOST=463
# ":Your host isn't among the privileged"
- #
+ #
# - Returned to a client which attempts to register with
# a server which does not been setup to allow
# connections from the host the attempted connection
# is tried.
- #
+ #
ERR_PASSWDMISMATCH=464
# ":Password incorrect"
- #
+ #
# - Returned to indicate a failed attempt at registering
# a connection for which a password was required and
# was either not given or incorrect.
- #
+ #
ERR_YOUREBANNEDCREEP=465
# ":You are banned from this server"
- #
+ #
# - Returned after an attempt to connect and register
# yourself with a server which has been setup to
# explicitly deny connections to you.
- #
+ #
ERR_YOUWILLBEBANNED=466
- #
+ #
# - Sent by a server to a user to inform that access to the
# server will soon be denied.
- #
+ #
ERR_KEYSET=467
# "<channel> :Channel key already set"
ERR_CHANNELISFULL=471
# "<channel> :Channel doesn't support modes"
ERR_BANLISTFULL=478
# "<channel> <char> :Channel list is full"
- #
+ #
ERR_NOPRIVILEGES=481
# ":Permission Denied- You're not an IRC operator"
- #
+ #
# - Any command requiring operator privileges to operate
# MUST return this error to indicate the attempt was
# unsuccessful.
- #
+ #
ERR_CHANOPRIVSNEEDED=482
# "<channel> :You're not channel operator"
- #
+ #
# - Any command requiring 'chanop' privileges (such as
# MODE messages) MUST return this error if the client
# making the attempt is not a chanop on the specified
# channel.
- #
- #
+ #
+ #
ERR_CANTKILLSERVER=483
# ":You can't kill a server!"
- #
+ #
# - Any attempts to use the KILL command on a server
# are to be refused and this error returned directly
# to the client.
- #
+ #
ERR_RESTRICTED=484
# ":Your connection is restricted!"
- #
+ #
# - Sent by the server to a user upon connection to indicate
# the restricted nature of the connection (user mode "+r").
- #
+ #
ERR_UNIQOPPRIVSNEEDED=485
# ":You're not the original channel operator"
- #
+ #
# - Any MODE requiring "channel creator" privileges MUST
# return this error if the client making the attempt is not
# a chanop on the specified channel.
- #
+ #
ERR_NOOPERHOST=491
# ":No O-lines for your host"
- #
+ #
# - If a client sends an OPER message and the server has
# not been configured to allow connections from the
# client's host as an operator, this error MUST be
# returned.
- #
+ #
ERR_UMODEUNKNOWNFLAG=501
# ":Unknown MODE flag"
- #
+ #
# - Returned by the server to indicate that a MODE
# message was sent with a nickname parameter and that
# the a mode flag sent was not recognized.
- #
+ #
ERR_USERSDONTMATCH=502
# ":Cannot change mode for other users"
- #
+ #
# - Error sent to any user trying to view or change the
# user mode for a user other than themselves.
- #
+ #
# 5.3 Reserved numerics
- #
+ #
# These numerics are not described above since they fall into one of
# the following categories:
- #
+ #
# 1. no longer in use;
- #
+ #
# 2. reserved for future planned use;
- #
+ #
# 3. in current use but are part of a non-generic 'feature' of
# the current IRC server.
- RPL_SERVICEINFO=231
+ RPL_SERVICEINFO=231
RPL_ENDOFSERVICES=232
RPL_SERVICE=233
- RPL_NONE=300
+ RPL_NONE=300
RPL_WHOISCHANOP=316
- RPL_KILLDONE=361
+ RPL_KILLDONE=361
RPL_CLOSING=362
- RPL_CLOSEEND=363
+ RPL_CLOSEEND=363
RPL_INFOSTART=373
RPL_MYPORTIS=384
- RPL_STATSCLINE=213
+ RPL_STATSCLINE=213
RPL_STATSNLINE=214
- RPL_STATSILINE=215
+ RPL_STATSILINE=215
RPL_STATSKLINE=216
RPL_STATSQLINE=217
RPL_STATSYLINE=218
- RPL_STATSVLINE=240
+ RPL_STATSVLINE=240
RPL_STATSLLINE=241
- RPL_STATSHLINE=244
+ RPL_STATSHLINE=244
RPL_STATSSLINE=244
- RPL_STATSPING=246
+ RPL_STATSPING=246
RPL_STATSBLINE=247
ERR_NOSERVICEHOST=492
-
+ RPL_DATASTR=290
+
# implements RFC 2812 and prior IRC RFCs.
- # clients register handler proc{}s for different server events and IrcClient
+ # clients register handler proc{}s for different server events and Client
# handles dispatch
- class IrcClient
- # create a new IrcClient instance
+ class Client
+
+ attr_reader :server, :user
+
+ # create a new Client instance
def initialize
+ @server = Server.new # The Server
+ @user = @server.user("") # The User representing the client on this Server
+
@handlers = Hash.new
- @users = Array.new
+
+ # This is used by some messages to build lists of users that
+ # will be delegated when the ENDOF... message is received
+ @tmpusers = []
+ end
+
+ # clear the server and reset the User
+ def reset
+ @server.clear
+ @user = @server.user("")
end
-
+
# key:: server event to handle
# value:: proc object called when event occurs
# set a handler for a server event
#
# ==server events currently supported:
#
- # created:: when the server was started
+ # TODO handle errors ERR_NOSUCHNICK, ERR_NOSUCHCHANNEL
+ # TODO handle errors ERR_CHANOPRIVSNEEDED, ERR_CANNOTSENDTOCHAN
+ #
+ # welcome:: server welcome message on connect
# yourhost:: your host details (on connection)
+ # created:: when the server was started
+ # isupport:: information about what this server supports
# ping:: server pings you (default handler returns a pong)
# nicktaken:: you tried to change nick to one that's in use
# badnick:: you tried to change nick to one that's invalid
# topicinfo:: on joining a channel or asking for the topic, tells you
# who set it and when
# names:: server sends list of channel members when you join
- # welcome:: server welcome message on connect
# motd:: server message of the day
# privmsg:: privmsg, the core of IRC, a message to you from someone
# public:: optionally instead of getting privmsg you can hook to only
def []=(key, value)
@handlers[key] = value
end
-
+
# key:: event name
# remove a handler for a server event
def deletehandler(key)
@handlers.delete(key)
end
-
+
# takes a server string, checks for PING, PRIVMSG, NOTIFY, etc, and parses
# numeric server replies, calling the appropriate handler for each, and
# sending it a hash containing the data from the server
def process(serverstring)
data = Hash.new
data[:serverstring] = serverstring
-
- unless serverstring =~ /^(:(\S+)\s)?(\S+)(\s(.*))?/
- raise "Unparseable Server Message!!!: #{serverstring}"
+
+ unless serverstring.chomp =~ /^(:(\S+)\s)?(\S+)(\s(.*))?$/
+ raise "Unparseable Server Message!!!: #{serverstring.inspect}"
end
-
+
prefix, command, params = $2, $3, $5
-
+
if prefix != nil
- data[:source] = prefix
- if prefix =~ /^(\S+)!(\S+)$/
- data[:sourcenick] = $1
- data[:sourceaddress] = $2
+ # Most servers will send a full nick!user@host prefix for
+ # messages from users. Therefore, when the prefix doesn't match this
+ # syntax it's usually the server hostname.
+ #
+ # This is not always true, though, since some servers do not send a
+ # full hostmask for user messages.
+ #
+ if prefix =~ /^#{Regexp::Irc::BANG_AT}$/
+ data[:source] = @server.user(prefix)
+ else
+ if @server.hostname
+ if @server.hostname != prefix
+ # TODO do we want to be able to differentiate messages that are passed on to us from /other/ servers?
+ debug "Origin #{prefix} for message\n\t#{serverstring.inspect}\nis neither a user hostmask nor the server hostname\nI'll pretend that it's from the server anyway"
+ data[:source] = @server
+ else
+ data[:source] = @server
+ end
+ else
+ @server.instance_variable_set(:@hostname, prefix)
+ data[:source] = @server
+ end
end
end
# split parameters in an array
argv = []
params.scan(/(?!:)(\S+)|:(.*)/) { argv << ($1 || $2) } if params
-
- case command
- when 'PING'
- data[:pingid] = argv[0]
- handle(:ping, data)
- when /^(\d+)$/ # numeric server message
+
+ if command =~ /^(\d+)$/ # Numeric replies
+ data[:target] = argv[0]
+ # A numeric reply /should/ be directed at the client, except when we're connecting with a used nick, in which case
+ # it's directed at '*'
+ not_us = !([@user.nick, '*'].include?(data[:target]))
+ if not_us
+ warning "Server reply #{serverstring.inspect} directed at #{data[:target]} instead of client (#{@user.nick})"
+ end
+
num=command.to_i
case num
+ when RPL_WELCOME
+ # "Welcome to the Internet Relay Network
+ # <nick>!<user>@<host>"
+ if not_us
+ warning "Server thinks client (#{@user.inspect}) has a different nick"
+ @user.nick = data[:target]
+ end
+ if argv[1] =~ /([^@!\s]+)(?:!([^@!\s]+?))?@(\S+)/
+ nick = $1
+ user = $2
+ host = $3
+ warning "Welcome message nick mismatch (#{nick} vs #{data[:target]})" if nick != data[:target]
+ @user.user = user if user
+ @user.host = host if host
+ end
+ handle(:welcome, data)
when RPL_YOURHOST
# "Your host is <servername>, running version <ver>"
- # TODO how standard is this "version <ver>? should i parse it?
data[:message] = argv[1]
handle(:yourhost, data)
when RPL_CREATED
when RPL_MYINFO
# "<servername> <version> <available user modes>
# <available channel modes>"
- data[:servername] = argv[1]
- data[:version] = argv[2]
- data[:usermodes] = argv[3]
- data[:chanmodes] = argv[4]
+ @server.parse_my_info(params.split(' ', 2).last)
+ data[:servername] = @server.hostname
+ data[:version] = @server.version
+ data[:usermodes] = @server.usermodes
+ data[:chanmodes] = @server.chanmodes
+ handle(:myinfo, data)
+ when RPL_ISUPPORT
+ # "PREFIX=(ov)@+ CHANTYPES=#& :are supported by this server"
+ # "MODES=4 CHANLIMIT=#:20 NICKLEN=16 USERLEN=10 HOSTLEN=63
+ # TOPICLEN=450 KICKLEN=450 CHANNELLEN=30 KEYLEN=23 CHANTYPES=#
+ # PREFIX=(ov)@+ CASEMAPPING=ascii CAPAB IRCD=dancer :are available
+ # on this server"
+ #
+ @server.parse_isupport(argv[1..-2].join(' '))
+ handle(:isupport, data)
when ERR_NICKNAMEINUSE
# "* <nick> :Nickname is already in use"
data[:nick] = argv[1]
data[:message] = argv[2]
handle(:badnick, data)
when RPL_TOPIC
- data[:channel] = argv[1]
+ data[:channel] = @server.get_channel(argv[1])
data[:topic] = argv[2]
+
+ if data[:channel]
+ data[:channel].topic.text = data[:topic]
+ else
+ warning "Received topic #{data[:topic].inspect} for channel #{data[:channel].inspect} I was not on"
+ end
+
handle(:topic, data)
when RPL_TOPIC_INFO
- data[:nick] = argv[0]
- data[:channel] = argv[1]
- data[:source] = argv[2]
- data[:unixtime] = argv[3]
+ data[:nick] = @server.user(argv[0])
+ data[:channel] = @server.get_channel(argv[1])
+
+ # This must not be an IRC::User because it might not be an actual User,
+ # and we risk overwriting valid User data
+ data[:source] = argv[2].to_irc_netmask(:server => @server)
+
+ data[:time] = Time.at(argv[3].to_i)
+
+ if data[:channel]
+ data[:channel].topic.set_by = data[:source]
+ data[:channel].topic.set_on = data[:time]
+ else
+ warning "Received topic #{data[:topic].inspect} for channel #{data[:channel].inspect} I was not on"
+ end
+
handle(:topicinfo, data)
when RPL_NAMREPLY
# "( "=" / "*" / "@" ) <channel>
# :[ "@" / "+" ] <nick> *( " " [ "@" / "+" ] <nick> )
# - "@" is used for secret channels, "*" for private
# channels, and "=" for others (public channels).
+ data[:channeltype] = argv[1]
+ data[:channel] = argv[2]
+
+ chan = @server.get_channel(data[:channel])
+ unless chan
+ warning "Received names #{data[:topic].inspect} for channel #{data[:channel].inspect} I was not on"
+ return
+ end
+
+ users = []
argv[3].scan(/\S+/).each { |u|
- if(u =~ /^([@+])?(.*)$/)
- umode = $1 || ""
+ # FIXME beware of servers that allow multiple prefixes
+ if(u =~ /^([#{@server.supports[:prefix][:prefixes].join}])?(.*)$/)
+ umode = $1
user = $2
- @users << [user, umode]
+ users << [user, umode]
+ end
+ }
+
+ users.each { |ar|
+ u = @server.user(ar[0])
+ chan.add_user(u, :silent => true)
+ debug "Adding user #{u}"
+ if ar[1]
+ m = @server.supports[:prefix][:prefixes].index(ar[1].to_sym)
+ ms = @server.supports[:prefix][:modes][m]
+ debug "\twith mode #{ar[1]} (#{ms})"
+ chan.mode[ms].set(u)
end
}
+ @tmpusers += users
when RPL_ENDOFNAMES
data[:channel] = argv[1]
- data[:users] = @users
+ data[:users] = @tmpusers
handle(:names, data)
- @users = Array.new
- when RPL_ISUPPORT
- # "PREFIX=(ov)@+ CHANTYPES=#& :are supported by this server"
- # "MODES=4 CHANLIMIT=#:20 NICKLEN=16 USERLEN=10 HOSTLEN=63
- # TOPICLEN=450 KICKLEN=450 CHANNELLEN=30 KEYLEN=23 CHANTYPES=#
- # PREFIX=(ov)@+ CASEMAPPING=ascii CAPAB IRCD=dancer :are available
- # on this server"
- #
- argv.each {|a|
- if a =~ /^(.*)=(.*)$/
- data[$1.downcase.to_sym] = $2
- end
- }
- handle(:isupport, data)
+ @tmpusers = Array.new
when RPL_LUSERCLIENT
# ":There are <integer> users and <integer>
# services on <integer> servers"
# (re)started)"
data[:message] = argv[1]
handle(:statsconn, data)
- when RPL_WELCOME
- # "Welcome to the Internet Relay Network
- # <nick>!<user>@<host>"
- case argv[1]
- when /((\S+)!(\S+))/
- data[:netmask] = $1
- data[:nick] = $2
- data[:address] = $3
- when /Welcome to the Internet Relay Network\s(\S+)/
- data[:nick] = $1
- when /Welcome.*\s+(\S+)$/
- data[:nick] = $1
- when /^(\S+)$/
- data[:nick] = $1
- end
- handle(:welcome, data)
when RPL_MOTDSTART
# "<nick> :- <server> Message of the Day -"
if argv[1] =~ /^-\s+(\S+)\s/
when RPL_ENDOFMOTD
data[:motd] = @motd
handle(:motd, data)
+ when RPL_DATASTR
+ data[:text] = argv[1]
+ handle(:datastr, data)
else
handle(:unknown, data)
end
- # end of numeric replies
- when 'PRIVMSG'
+ return # We've processed the numeric reply
+ end
+
+ # Otherwise, the command should be a single word
+ case command.to_sym
+ when :PING
+ data[:pingid] = argv[0]
+ handle(:ping, data)
+ when :PONG
+ data[:pingid] = argv[0]
+ handle(:pong, data)
+ when :PRIVMSG
# you can either bind to 'PRIVMSG', to get every one and
# parse it yourself, or you can bind to 'MSG', 'PUBLIC',
# etc and get it all nicely split up for you.
- data[:target] = argv[0]
+
+ begin
+ data[:target] = @server.user_or_channel(argv[0])
+ rescue
+ # The previous may fail e.g. when the target is a server or something
+ # like that (e.g. $<mask>). In any of these cases, we just use the
+ # String as a target
+ # FIXME we probably want to explicitly check for the #<mask> $<mask>
+ data[:target] = argv[0]
+ end
data[:message] = argv[1]
handle(:privmsg, data)
-
+
# Now we split it
- if(data[:target] =~ /^(#|&).*/)
+ if data[:target].kind_of?(Channel)
handle(:public, data)
else
handle(:msg, data)
end
- when 'KICK'
- data[:channel] = argv[0]
- data[:target] = argv[1]
+ when :NOTICE
+ begin
+ data[:target] = @server.user_or_channel(argv[0])
+ rescue
+ # The previous may fail e.g. when the target is a server or something
+ # like that (e.g. $<mask>). In any of these cases, we just use the
+ # String as a target
+ # FIXME we probably want to explicitly check for the #<mask> $<mask>
+ data[:target] = argv[0]
+ end
+ data[:message] = argv[1]
+ case data[:source]
+ when User
+ handle(:notice, data)
+ else
+ # "server notice" (not from user, noone to reply to)
+ handle(:snotice, data)
+ end
+ when :KICK
+ data[:channel] = @server.channel(argv[0])
+ data[:target] = @server.user(argv[1])
data[:message] = argv[2]
+
+ @server.delete_user_from_channel(data[:target], data[:channel])
+ if data[:target] == @user
+ @server.delete_channel(data[:channel])
+ end
+
handle(:kick, data)
- when 'PART'
- data[:channel] = argv[0]
+ when :PART
+ data[:channel] = @server.channel(argv[0])
data[:message] = argv[1]
+
+ @server.delete_user_from_channel(data[:source], data[:channel])
+ if data[:source] == @user
+ @server.delete_channel(data[:channel])
+ end
+
handle(:part, data)
- when 'QUIT'
+ when :QUIT
data[:message] = argv[0]
+ data[:was_on] = @server.channels.inject(ChannelList.new) { |list, ch|
+ list << ch if ch.has_user?(data[:source])
+ list
+ }
+
+ @server.delete_user(data[:source])
+
handle(:quit, data)
- when 'JOIN'
- data[:channel] = argv[0]
+ when :JOIN
+ data[:channel] = @server.channel(argv[0])
+ data[:channel].add_user(data[:source])
+
handle(:join, data)
- when 'TOPIC'
- data[:channel] = argv[0]
- data[:topic] = argv[1]
+ when :TOPIC
+ data[:channel] = @server.channel(argv[0])
+ data[:topic] = Channel::Topic.new(argv[1], data[:source], Time.new)
+ data[:channel].topic.replace(data[:topic])
+
handle(:changetopic, data)
- when 'INVITE'
- data[:target] = argv[0]
- data[:channel] = argv[1]
+ when :INVITE
+ data[:target] = @server.user(argv[0])
+ data[:channel] = @server.channel(argv[1])
+
handle(:invite, data)
- when 'NICK'
- data[:nick] = argv[0]
+ when :NICK
+ data[:is_on] = @server.channels.inject(ChannelList.new) { |list, ch|
+ list << ch if ch.has_user?(data[:source])
+ list
+ }
+
+ data[:newnick] = argv[0]
+ data[:oldnick] = data[:source].nick.dup
+ data[:source].nick = data[:newnick]
+
+ debug "#{data[:oldnick]} (now #{data[:newnick]}) was on #{data[:is_on].join(', ')}"
+
handle(:nick, data)
- when 'MODE'
- data[:channel] = argv[0]
- data[:modestring] = argv[1]
- data[:targets] = argv[2]
- handle(:mode, data)
- when 'NOTICE'
- data[:target] = argv[0]
- data[:message] = argv[1]
- if data[:sourcenick]
- handle(:notice, data)
+ when :MODE
+ # MODE ([+-]<modes> (<params>)*)*
+ # When a MODE message is received by a server,
+ # Type C will have parameters too, so we must
+ # be able to consume parameters for all
+ # but Type D modes
+
+ data[:channel] = @server.user_or_channel(argv[0])
+ data[:modestring] = argv[1..-1].join(" ")
+ case data[:channel]
+ when User
+ # TODO
+ warning "Unhandled user mode message '#{serverstring}'"
else
- # "server notice" (not from user, noone to reply to
- handle(:snotice, data)
+ # data[:modes] is an array where each element
+ # is either a flag which doesn't need parameters
+ # or an array with a flag which needs parameters
+ # and the corresponding parameter
+ data[:modes] = []
+ # array of indices in data[:modes] where parameters
+ # are needed
+ who_wants_params = []
+
+ argv[1..-1].each { |arg|
+ setting = arg[0].chr
+ if "+-".include?(setting)
+ arg[1..-1].each_byte { |b|
+ m = b.chr
+ case m.to_sym
+ when *@server.supports[:chanmodes][:typea]
+ data[:modes] << [setting + m]
+ who_wants_params << data[:modes].length - 1
+ when *@server.supports[:chanmodes][:typeb]
+ data[:modes] << [setting + m]
+ who_wants_params << data[:modes].length - 1
+ when *@server.supports[:chanmodes][:typec]
+ if setting == "+"
+ data[:modes] << [setting + m]
+ who_wants_params << data[:modes].length - 1
+ else
+ data[:modes] << setting + m
+ end
+ when *@server.supports[:chanmodes][:typed]
+ data[:modes] << setting + m
+ when *@server.supports[:prefix][:modes]
+ data[:modes] << [setting + m]
+ who_wants_params << data[:modes].length - 1
+ else
+ warning "Unknown mode #{m} in #{serverstring.inspect}"
+ end
+ }
+ else
+ idx = who_wants_params.shift
+ if idx.nil?
+ warning "Oops, problems parsing #{serverstring.inspect}"
+ break
+ end
+ data[:modes][idx] << arg
+ end
+ }
end
+
+ data[:modes].each { |mode|
+ case mode
+ when Array
+ set = mode[0][0].chr == "+" ? :set : :reset
+ key = mode[0][1].chr.to_sym
+ val = mode[1]
+ data[:channel].mode[key].send(set, val)
+ else
+ set = mode[0].chr == "+" ? :set : :reset
+ key = mode[1].chr.to_sym
+ data[:channel].mode[key].send(set)
+ end
+ } if data[:modes]
+
+ handle(:mode, data)
else
+ warning "Unknown message #{serverstring.inspect}"
handle(:unknown, data)
end
end
private
-
+
# key:: server event name
# data:: hash containing data about the event, passed to the proc
# call client's proc for an event, if they set one as a handler