csbot.irc module

exception csbot.irc.IRCParseError[source]

Bases: Exception

Raised by IRCMessage.parse() when a message can’t be parsed.

class csbot.irc.IRCMessage(raw: str, prefix: Optional[str], command: str, params: List[str], command_name: str)[source]

Bases: object

Represents an IRC message.

The IRC message format, paraphrased and simplified from RFC2812, is:

message = [":" prefix " "] command {" " parameter} [" :" trailing]

Has the following attributes:

Parameters:
  • raw (str) – The raw IRC message
  • prefix (str or None) – Prefix part of the message, usually the origin
  • command (str) – IRC command
  • params (list of str) – List of command parameters (including trailing)
  • command_name (str) – Name of IRC command (see below)

The command_name attribute is intended to be the “readable” form of the command. Usually it will be the same as command, but numeric replies recognised in RFC2812 will have their corresponding name instead.

raw
prefix
command
params
command_name
REGEX = re.compile('(:(?P<prefix>\\S+) )?(?P<command>\\S+)(?P<params>( (?!:)\\S+)*)( :(?P<trailing>.*))?')

Regular expression to extract message components from a message.

FORCE_TRAILING = {'PRIVMSG', 'QUIT', 'USER'}

Commands to force trailing parameter (:blah) for

classmethod parse(line)[source]

Create an IRCMessage object by parsing a raw message.

classmethod create(command, params=None, prefix=None)[source]

Create an IRCMessage from its core components.

The raw and command_name attributes will be generated based on the message details.

pretty

Get a more readable version of the raw IRC message.

Pretty much identical to the raw IRC message, but numeric commands that have names end up being NUMERIC/NAME.

pad_params(length, default=None)[source]

Pad parameters to length with default.

Useful when a command has optional parameters:

>>> msg = IRCMessage.parse(':nick!user@host KICK #channel other')
>>> channel, nick, reason = msg.params
Traceback (most recent call last):
  ...
ValueError: need more than 2 values to unpack
>>> channel, nick, reason = msg.pad_params(3)
class csbot.irc.IRCUser(raw: str, nick: str, user: Optional[str], host: Optional[str])[source]

Bases: object

Provide access to the parts of an IRC user string.

The following parts of the user string are available, set to None if that part of the string is absent:

Parameters:
  • raw – Raw user string
  • nick – Nick of the user
  • user – Username of the user (excluding leading ~)
  • host – Hostname of the user
>>> IRCUser.parse('my_nick!some_user@host.name')
IRCUser(raw='my_nick!some_user@host.name', nick='my_nick', user='some_user', host='host.name')
raw
nick
user
host
REGEX = re.compile('(?P<raw>(?P<nick>[^!]+)(!~*(?P<user>[^@]+))?(@(?P<host>.+))?)')

Username parsing regex. Stripping out the “~” might be a Freenode peculiarity…

classmethod parse(raw)[source]

Create an IRCUser from a raw user string.

class csbot.irc.IRCCodec[source]

Bases: codecs.Codec

The encoding scheme to use for IRC messages.

IRC messages are “just bytes” with no encoding made explicit in the protocol definition or the messages. Ideally we’d like to handle IRC messages as proper strings.

encode(input, errors='strict')[source]

Encode a message as UTF-8.

decode(input, errors='strict')[source]

Decode a message.

IRC messages could pretty much be in any encoding. Here we just try the two most likely candidates: UTF-8, falling back to CP1252. Unfortunately, any encoding where every byte is valid (e.g. CP1252) makes it impossible to detect encoding errors - if input isn’t UTF-8 or CP1252-compatible, the result might be a bit odd.

exception csbot.irc.IRCClientError[source]

Bases: Exception

class csbot.irc.IRCClient(*, loop=None, **kwargs)[source]

Bases: object

Internet Relay Chat client protocol.

A line-oriented protocol for communicating with IRC servers. It handles receiving data at several layers of abstraction:

It also handles sending data at several layers of abstraction:

  • send_line(): raw IRC command, e.g. self.send_line('JOIN #cs-york-dev')
  • send(): IRCMessage, e.g. self.send(IRCMessage.create('JOIN', params=['#cs-york-dev']))
  • <action>(...): e.g. self.join('#cs-york-dev').

The API and implementation is inspired by irc3 and Twisted.

  • TODO: NAMES
  • TODO: MODE
  • TODO: More sophisticated CTCP? (see Twisted)
  • TODO: MOTD?
  • TODO: SSL
codec = <csbot.irc.IRCCodec object>

Codec for encoding/decoding IRC messages.

static DEFAULTS()

Generate a default configuration. Easier to call this and update the result than relying on dict.copy().

available_capabilities = None

Available client capabilities

enabled_capabilities = None

Enabled client capabilities

disconnect()[source]

Disconnect from the IRC server.

Use quit() for a more graceful disconnect.

line_received(line: str)[source]

Callback for received raw IRC message.

line_sent(line: str)[source]

Callback for sent raw IRC message.

Subclasses can implement this to get access to the actual message that was sent (which may have been truncated from what was passed to send_line()).

message_received(msg)[source]

Callback for received parsed IRC message.

send_line(data: str)[source]

Send a raw IRC message to the server.

Encodes, terminates and sends data to the server. If the line would be longer than the maximum allowed by the IRC specification, it is trimmed to fit (without breaking UTF-8 sequences).

If rate limiting is enabled, the message may not be sent immediately.

send(msg)[source]

Send an IRCMessage.

class Waiter(predicate: Callable[[csbot.irc.IRCMessage], Tuple[bool, Any]], future: _asyncio.Future)[source]

Bases: object

PredicateType = typing.Callable[[csbot.irc.IRCMessage], typing.Tuple[bool, typing.Any]]
wait_for_message(predicate: Callable[[csbot.irc.IRCMessage], Tuple[bool, Any]]) → _asyncio.Future[source]

Wait for a message that matches predicate.

predicate should return a (did_match, result) tuple, where did_match is a boolean indicating if the message is a match, and result is the value to return.

Returns a future that is resolved with result on the first matching message.

process_wait_for_message(msg)[source]
request_capabilities(*, enable: Iterable[str] = None, disable: Iterable[str] = None) → Awaitable[bool][source]

Request a change to the enabled IRCv3 capabilities.

enable and disable are sets of capability names, with disable taking precedence.

Returns a future which resolves with True if the request is successful, or False otherwise.

set_nick(nick)[source]

Ask the server to set our nick.

join(channel)[source]

Join a channel.

leave(channel, message=None)[source]

Leave a channel, with an optional message.

quit(message=None, reconnect=False)[source]

Leave the server.

If reconnect is False, then the client will not attempt to reconnect after the server closes the connection.

msg(to, message)[source]

Send message to a channel/nick.

act(to, action)[source]

Send action as a CTCP ACTION to a channel/nick.

notice(to, message)[source]

Send message as a NOTICE to a channel/nick.

set_topic(channel, topic)[source]

Try and set a channel’s topic.

get_topic(channel)[source]

Ask server to send the topic for channel.

Will cause on_topic_changed() at some point in the future.

ctcp_query(to, command, data=None)[source]

Send CTCP query.

ctcp_reply(to, command, data=None)[source]

Send CTCP reply.

irc_RPL_WELCOME(msg)[source]

Received welcome from server, now we can start communicating.

Welcome should include the accepted nick as the first parameter. This may be different to the nick we requested (e.g. truncated to a maximum length); if this is the case we store the new nick and fire the on_nick_changed() event.

irc_ERR_NICKNAMEINUSE(msg)[source]

Attempted nick is in use, try another.

Adds an underscore to the end of the current nick. If the server truncated the nick, replaces the last non-underscore with an underscore.

irc_PING(msg)[source]

IRC PING/PONG keepalive.

irc_CAP(msg)[source]

Dispatch CAP subcommands to their own methods.

irc_CAP_LS(msg)[source]

Response to CAP LS, giving list of available capabilities.

irc_CAP_ACK(msg)[source]

Response to CAP REQ, acknowledging capability changes.

irc_CAP_NAK(msg)[source]

Response to CAP REQ, rejecting capability changes.

irc_NICK(msg)[source]

Somebody’s nick changed.

irc_JOIN(msg)[source]

Somebody joined a channel.

irc_PART(msg)[source]

Somebody left a channel.

irc_KICK(msg)[source]

Somebody was kicked from a channel.

irc_QUIT(msg)[source]

Somebody quit the server.

irc_TOPIC(msg)[source]

A channel’s topic changed.

irc_RPL_TOPIC(msg)[source]

Topic notification, usually after joining a channel.

irc_PRIVMSG(msg)[source]

Received a PRIVMSG.

TODO: Implement CTCP queries.

irc_NOTICE(msg)[source]

Received a NOTICE.

TODO: Implement CTCP replies.

on_capabilities_available(capabilities)[source]

Client capabilities are available.

Called with a set of client capability names when we get a response to CAP LS.

on_capability_enabled(name)[source]

Client capability enabled.

Called when enabling client capability name has been acknowledged.

on_capability_disabled(name)[source]

Client capability disabled.

Called when disabling client capability name has been acknowledged.

on_welcome()[source]

Successfully signed on to the server.

on_nick_changed(nick)[source]

Changed nick.

on_joined(channel)[source]

Joined a channel.

on_left(channel)[source]

Left a channel.

on_kicked(channel, by, reason)[source]

Kicked from a channel.

on_privmsg(user, to, message)[source]

Received a message, either directly or in a channel.

on_notice(user, to, message)[source]

Received a notice, either directly or in a channel.

on_action(user, to, action)[source]

Received CTCP ACTION. Common enough to deserve its own event.

on_ctcp_query_ACTION(user, to, data)[source]

Turn CTCP ACTION into on_action() event.

on_user_renamed(oldnick, newnick)[source]

User changed nick.

connect()[source]

Connect to the IRC server.

connection_lost(exc)[source]

Handle a broken connection by attempting to reconnect.

Won’t reconnect if the broken connection was deliberate (i.e. close() was called).

connection_made()[source]

Callback for successful connection.

Register with the IRC server.

on_user_joined(user, channel)[source]

User joined a channel.

read_loop()[source]

Read and dispatch lines until the connection closes.

run(run_once=False)[source]

Run the bot, reconnecting when the connection is lost.

on_user_left(user, channel, message)[source]

User left a channel.

on_user_kicked(user, channel, by, reason)[source]

User kicked from a channel.

on_user_quit(user, message)[source]

User disconnected.

on_topic_changed(user, channel, topic)[source]

user changed the topic of channel to topic.

csbot.irc.main()[source]