IRC Backends

sopel.irc is the core IRC module for Sopel.

This sub-package contains everything that is related to the IRC protocol (connection, commands, abstract client, etc.) and that can be used to implement the Sopel bot.

In particular, it defines the interface for the IRC backend (AbstractIRCBackend), and the interface for the bot itself (AbstractBot). This is all internal code that isn’t supposed to be used directly by a plugin developer, who should worry about sopel.bot.Sopel only.

Important

When working on core IRC protocol related features, consult protocol documentation at https://www.irchelp.org/protocol/rfc/

Bot Interface

class sopel.irc.AbstractBot(settings)

Bases: object

Abstract definition of Sopel’s interface.

action(text, dest)

Send a CTCP ACTION PRIVMSG to a user or channel.

Parameters
  • text (str) – the text to send in the CTCP ACTION

  • dest (str) – the destination of the CTCP ACTION

The same loop detection and length restrictions apply as with say(), though automatic message splitting is not available.

backend

IRC connection backend.

cap_req(plugin_name, capability, arg=None, failure_callback=None, success_callback=None)

Tell Sopel to request a capability when it starts.

Parameters
  • plugin_name (str) – the plugin requesting the capability

  • capability (str) – the capability requested, optionally prefixed with - or =

  • arg (str) – arguments for the capability request

  • failure_callback (function) – a function that will be called if the capability request fails

  • success_callback (function) – a function that will be called if the capability is successfully requested

By prefixing the capability with -, it will be ensured that the capability is not enabled. Similarly, by prefixing the capability with =, it will be ensured that the capability is enabled. Requiring and disabling is “first come, first served”; if one plugin requires a capability, and another prohibits it, this function will raise an exception in whichever plugin loads second. An exception will also be raised if the plugin is being loaded after the bot has already started, and the request would change the set of enabled capabilities.

If the capability is not prefixed, and no other plugin prohibits it, it will be requested. Otherwise, it will not be requested. Since capability requests that are not mandatory may be rejected by the server, as well as by other plugins, a plugin which makes such a request should account for that possibility.

The actual capability request to the server is handled after the completion of this function. In the event that the server denies a request, the failure_callback function will be called, if provided. The arguments will be a Sopel object, and the capability which was rejected. This can be used to disable callables which rely on the capability. It will be be called either if the server NAKs the request, or if the server enabled it and later DELs it.

The success_callback function will be called upon acknowledgment of the capability from the server, whether during the initial capability negotiation, or later.

If arg is given, and does not exactly match what the server provides or what other plugins have requested for that capability, it is considered a conflict.

change_current_nick(new_nick)

Change the current nick without configuration modification.

Parameters

new_nick (str) – new nick to be used by the bot

property config

The sopel.config.Config for the current Sopel instance.

connection_registered

Flag stating whether the IRC Connection is registered yet.

dispatch(pretrigger)

Handle running the appropriate callables for an incoming message.

Parameters

pretrigger (sopel.trigger.PreTrigger) – Sopel PreTrigger object

Raises

NotImplementedError – if the subclass does not implement this required method

Important

This method MUST be implemented by concrete subclasses.

enabled_capabilities

A set containing the IRCv3 capabilities that the bot has enabled.

get_irc_backend()

Set up the IRC backend based on the bot’s settings.

Returns

the initialized IRC backend object

Return type

an object implementing the interface of AbstractIRCBackend

property isupport

Features advertised by the server.

Type

ISupport instance

join(channel, password=None)

Join a channel.

Parameters
  • channel (str) – the channel to join

  • password (str) – an optional channel password

If channel contains a space, and no password is given, the space is assumed to split the argument into the channel to join and its password. channel should not contain a space if password is given.

kick(nick, channel, text=None)

Kick a nick from a channel.

Parameters
  • nick (str) – nick to kick out of the channel

  • channel (str) – channel to kick nick from

  • text (str) – optional text for the kick

The bot must be an operator in the specified channel for this to work.

New in version 7.0.

log_raw(line, prefix)

Log a raw line to the raw log.

Parameters
  • line (str) – the raw line

  • prefix (str) – additional information to prepend to the log line

The prefix is usually either >> for an outgoing line or << for a received one.

property myinfo

Server/network information.

Type

MyInfo instance

New in version 7.0.

property name

Sopel’s “real name”, as used for WHOIS responses.

property nick

Sopel’s current nick.

Changing this while Sopel is running is unsupported and can result in undefined behavior.

notice(text, dest)

Send an IRC NOTICE to a user or channel (dest).

Parameters
  • text (str) – the text to send in the NOTICE

  • dest (str) – the destination of the NOTICE

on_close()

Call shutdown methods.

on_connect()

Handle successful establishment of IRC connection.

on_error()

Handle any uncaptured error in the bot itself.

on_message(message)

Handle an incoming IRC message.

Parameters

message (str) – the received raw IRC message

on_message_sent(raw)

Handle any message sent through the connection.

Parameters

raw (str) – raw text message sent through the connection

When a message is sent through the IRC connection, the bot will log the raw message. If necessary, it will also simulate the echo-message feature of IRCv3.

part(channel, msg=None)

Leave a channel.

Parameters
  • channel (str) – the channel to leave

  • msg (str) – the message to display when leaving a channel

quit(message)

Disconnect from IRC and close the bot.

reply(text, dest, reply_to, notice=False)

Send a PRIVMSG to a user or channel, prepended with a nickname.

Parameters
  • text (str) – the text of the reply

  • dest (str) – the destination of the reply

  • reply_to (str) – the nickname that will be prepended to text

  • notice (bool) – whether to send the reply as a NOTICE or not, defaults to False

If notice is True, send a NOTICE rather than a PRIVMSG.

The same loop detection and length restrictions apply as with say(), though automatic message splitting is not available.

run(host, port=6667)

Connect to IRC server and run the bot forever.

Parameters
  • host (str) – the IRC server hostname

  • port (int) – the IRC server port

say(text, recipient, max_messages=1, truncation='', trailing='')

Send a PRIVMSG to a user or channel.

Parameters
  • text (str) – the text to send

  • recipient (str) – the message recipient

  • max_messages (int) – split text into at most this many messages if it is too long to fit in one (optional)

  • truncation (str) – string to append if text is too long to fit in a single message, or into the last message if max_messages is greater than 1 (optional)

  • trailing (str) – string to append after text and (if used) truncation (optional)

By default, this will attempt to send the entire text in one message. If the text is too long for the server, it may be truncated.

If max_messages is given, the text will be split into at most that many messages. The split is made at the last space character before the “safe length” (which is calculated based on the bot’s nickname and hostmask), or exactly at the “safe length” if no such space character exists.

If the text is too long to fit into the specified number of messages using the above splitting, the final message will contain the entire remainder, which may be truncated by the server. You can specify truncation to tell Sopel how it should indicate that the remaining text was cut off. Note that the truncation parameter must include leading whitespace if you desire any between it and the truncated text.

The trailing parameter is always appended to text, after the point where truncation would be inserted if necessary. It’s useful for making sure e.g. a link is always included, even if the summary your plugin fetches is too long to fit.

Here are some examples of how the truncation and trailing parameters work, using an artificially low maximum line length:

# bot.say() outputs <text> + <truncation?> + <trailing>
#                   always     if needed       always

bot.say(
    '"This is a short quote.',
    truncation=' […]',
    trailing='"')
# Sopel says: "This is a short quote."

bot.say(
    '"This quote is very long and will not fit on a line.',
    truncation=' […]',
    trailing='"')
# Sopel says: "This quote is very long […]"

bot.say(
    # note the " included at the end this time
    '"This quote is very long and will not fit on a line."',
    truncation=' […]')
# Sopel says: "This quote is very long […]
# The ending " goes missing

New in version 7.1: The truncation and trailing parameters.

settings

Bot settings.

property user

Sopel’s user/ident.

write(args, text=None)

Send a command to the server.

Parameters
  • args (iterable) – an iterable of strings, which will be joined by spaces

  • text (str) – a string that will be prepended with a : and added to the end of the command

args is an iterable of strings, which are joined by spaces. text is treated as though it were the final item in args, but is preceded by a :. This is a special case which means that text, unlike the items in args, may contain spaces (though this constraint is not checked by write).

In other words, both sopel.write(('PRIVMSG',), 'Hello, world!') and sopel.write(('PRIVMSG', ':Hello, world!')) will send PRIVMSG :Hello, world! to the server.

Newlines and carriage returns ('\n' and '\r') are removed before sending. Additionally, if the message (after joining) is longer than than 510 characters, any remaining characters will not be sent.

See also

The connection backend is responsible for formatting and sending the message through the IRC connection. See the sopel.irc.abstract_backends.AbstractIRCBackend.send_command() method for more information.

Backend Interface

class sopel.irc.abstract_backends.AbstractIRCBackend(bot)

Abstract class defining the interface and basic logic of an IRC backend.

Parameters

bot (sopel.bot.Sopel) – a Sopel instance

Some methods of this class MUST be overridden by a subclass, or the backend implementation will not function correctly.

irc_send(data)

Send an IRC line as raw data.

Parameters

data (bytes) – raw line to send

is_connected()

Tell if the backend is connected or not.

Return type

bool

on_irc_error(pretrigger)

Action to perform when the server sends an error event.

Parameters

pretrigger (sopel.trigger.PreTrigger) – PreTrigger object with the error event

On IRC error, if bot.hasquit is set, the backend should close the connection so the bot can quit or reconnect as required.

prepare_command(*args, **kwargs)

Prepare an IRC command from args and optional text.

Parameters
  • args (list) – list of text, arguments of the IRC command to send

  • text (str) – optional text to send with the IRC command

Returns

the raw message to send through the connection

Return type

str

From RFC 2812 Internet Relay Chat: Client Protocol, Section 2.3:

IRC messages are always lines of characters terminated with a CR-LF (Carriage Return - Line Feed) pair, and these messages SHALL NOT exceed 512 characters in length, counting all characters including the trailing CR-LF. Thus, there are 510 characters maximum allowed for the command and its parameters. There is no provision for continuation of message lines.

The length in the RFC refers to the length in bytes, which can be bigger than the length of the Unicode string. This method cuts the message until its length fits within this limit of 510 bytes.

The returned message contains the CR-LF pair required at the end, and can be sent as-is.

send_command(*args, **kwargs)

Send a command through the IRC connection.

Parameters
  • args – IRC command to send with its argument(s)

  • text (str) – the text to send (optional keyword argument)

Example:

# send the INFO command
backend.send_command('INFO')
# send the NICK command with the argument 'Sopel'
backend.send_command('NICK', 'Sopel')
# send the PRIVMSG command to channel #sopel with some text
backend.send_command('PRIVMSG', '#sopel', text='Hello world!')

Note

This will call the sopel.bot.Sopel.on_message_sent() callback on the bot instance with the raw message sent.

send_join(channel, password=None)

Send a JOIN command to channel with optional password.

Parameters
  • channel (str) – channel to join

  • password (str) – optional password for protected channels

send_kick(channel, nick, reason=None)

Send a KICK command for nick in channel .

Parameters
  • channel (str) – the channel from which to kick nick

  • nick (str) – nickname to kick from the channel

  • reason (str) – optional reason for the kick

send_nick(nick)

Send a NICK command with a nick.

Parameters

nick (str) – nickname to take

send_notice(dest, text)

Send a NOTICE command to dest with text.

Parameters
  • dest (str) – nickname or channel name

  • text (str) – the text to send

send_part(channel, reason=None)

Send a PART command to channel.

Parameters
  • channel (str) – the channel to part

  • text (str) – optional text for leaving the channel

send_pass(password)

Send a PASS command with a password.

Parameters

password (str) – password for authentication

send_ping(host)

Send a PING command to the server.

Parameters

host (str) – IRC server host

A PING command should be sent at a regular interval to make sure the server knows the IRC connection is still active.

send_pong(host)

Send a PONG command to the server.

Parameters

host (str) – IRC server host

A PONG command must be sent each time the server sends a PING command to the client.

send_privmsg(dest, text)

Send a PRIVMSG command to dest with text.

Parameters
  • dest (str) – nickname or channel name

  • text (str) – the text to send

send_quit(reason=None)

Send a QUIT command.

Parameters

reason (str) – optional text for leaving the server

This won’t send anything if the backend isn’t connected.

send_user(user, mode, nick, name)

Send a USER command with a user.

Parameters
  • user (str) – IRC username

  • mode (str) – mode(s) to send for the user

  • nick (str) – nickname associated with this user

  • name (str) – “real name” for the user

Backends

class sopel.irc.backends.AsynchatBackend(bot, server_timeout=None, ping_interval=None, **kwargs)

Bases: sopel.irc.abstract_backends.AbstractIRCBackend, asynchat.async_chat

IRC backend implementation using asynchat (asyncore).

Parameters
  • bot (sopel.bot.Sopel) – a Sopel instance

  • server_timeout (int) – connection timeout in seconds

  • ping_interval (int) – ping interval in seconds

The server_timeout option defaults to 120 seconds if not provided.

The ping_interval defaults to server_timeout * 0.45 if not specified.

collect_incoming_data(data)

Try to make sense of incoming data as Unicode.

Parameters

data (bytes) – the incoming raw bytes

The incoming line is discarded (and thus ignored) if guessing the text encoding and decoding it fails.

found_terminator()

Handle the end of an incoming message.

handle_close()

Called when the connection must be closed.

handle_connect()

Called when the active opener’s socket actually makes a connection.

handle_error()

Called when an exception is raised and not otherwise handled.

This method is an override of asyncore.dispatcher.handle_error(), the asynchat.async_chat being a subclass of asyncore.dispatcher.

initiate_connect(host, port, source_address)

Initiate IRC connection.

Parameters
  • host (str) – IRC server hostname

  • port (int) – IRC server port

  • source_address (str) – the source address from which to initiate the connection attempt

irc_send(data)

Send an IRC line as raw data to the socket connection.

Parameters

data (bytes) – raw line to send

This uses asyncore.dispatcher.send() method to send data directly. This method is thread-safe.

is_connected()

Tell if the backend is connected or not.

Return type

bool

on_irc_error(pretrigger)

Action to perform when the server sends an error event.

Parameters

pretrigger (sopel.trigger.PreTrigger) – PreTrigger object with the error event

On IRC error, if bot.hasquit is set, the backend should close the connection so the bot can quit or reconnect as required.

on_job_error(scheduler, job, exc)

Called when a job from the Job Scheduler fails.

on_scheduler_error(scheduler, exc)

Called when the Job Scheduler fails.

register_timeout_jobs(handlers)

Register the timeout handlers for the timeout scheduler.

run_forever()

Run forever.

class sopel.irc.backends.SSLAsynchatBackend(bot, verify_ssl=True, ca_certs=None, **kwargs)

Bases: sopel.irc.backends.AsynchatBackend

SSL-aware extension of AsynchatBackend.

Parameters
  • bot (sopel.bot.Sopel) – a Sopel instance

  • verify_ssl (bool) – whether to validate the IRC server’s certificate (default True, for good reason)

  • ca_certs (str) – filesystem path to a CA Certs file containing trusted root certificates

handle_connect()

Handle potential TLS connection.

recv(buffer_size)

SSL-aware override for recv().

From a (now deleted) blog post by Evan “K7FOS” Fosmark: https://k7fos.com/2010/09/ssl-support-in-asynchatasync_chat

send(data)

SSL-aware override for send().

ISUPPORT

IRC Tools for ISUPPORT management.

When a server wants to advertise its features and settings, it can use the RPL_ISUPPORT command (005 numeric) with a list of arguments.

class sopel.irc.isupport.ISupport(**kwargs)

Storage class for IRC’s ISUPPORT feature.

An instance of ISupport can be used as a read-only dict, to store features advertised by the IRC server:

>>> isupport = ISupport(chanlimit=(('&', None), ('#', 70)))
>>> isupport['CHANLIMIT']
(('&', None) ('#', 70))
>>> isupport.CHANLIMIT  # some parameters are also properties
{
    '&': None,
    '#': 70,
}
>>> 'chanlimit' in isupport  # case-insensitive
True
>>> 'chanmode' in isupport
False
>>> isupport.CHANMODE  # not advertised by the server!
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'ISupport' object has no attribute 'CHANMODE'

The list of possible parameters can be found at modern.ircdocs.horse’s RPL_ISUPPORT Parameters.

property CHANLIMIT

Expose CHANLIMIT as a dict, if advertised by the server.

This exposes information about the maximum number of channels that the bot can join for each prefix:

>>> isupport.CHANLIMIT
{
    '#': 70,
    '&': None,
}

In that example, the bot may join 70 # channels and any number of & channels.

This attribute is not available if the server does not provide the right information, and accessing it will raise an AttributeError.

property CHANMODES

Expose CHANMODES as a dict.

This exposes information about 4 types of channel modes:

>>> isupport.CHANMODES
{
    'A': 'b',
    'B': 'k',
    'C': 'l',
    'D': 'imnpst',
}

The values are empty if the server does not provide this information.

property MAXLIST

Expose MAXLIST as a dict, if advertised by the server.

This exposes information about maximums for combinations of modes:

>>> isupport.MAXLIST
{
    'beI': 100,
    'q': 50,
    'b': 50,
}

This attribute is not available if the server does not provide the right information, and accessing it will raise an AttributeError.

property PREFIX

Expose PREFIX as a dict, if advertised by the server.

This exposes information about the modes and nick prefixes used for user privileges in channels:

>>> isupport.PREFIX
{
    'q': '~',
    'a': '&',
    'o': '@',
    'h': '%',
    'v': '+',
}

This attribute is not available if the server does not provide the right information, and accessing it will raise an AttributeError.

property TARGMAX

Expose TARGMAX as a dict, if advertised by the server.

This exposes information about the maximum number of arguments for each command:

>>> isupport.TARGMAX
{
    'JOIN': None,
    'PRIVMSG': 3,
    'WHOIS': 1,
}
>>> isupport['TARGMAX']  # internal representation
(('JOIN', None), ('PRIVMSG', 3), ('WHOIS', 1))

This attribute is not available if the server does not provide the right information, and accessing it will raise an AttributeError.

The internal representation of TARGMAX is a tuple of 2-value tuples as seen above.

apply(**kwargs)

Build a new instance of ISupport.

Returns

a new instance, updated with the latest advertised features

Return type

ISupport

This method applies the latest advertised features from the server: the result contains the new and updated parameters, and doesn’t contain the removed parameters (marked by -{PARAMNAME}):

>>> updated = {'-AWAYLEN': None, 'NICKLEN': 25, 'CHANNELLEN': 10}
>>> new = isupport.apply(**updated)
>>> 'CHANNELLEN' in new
True
>>> 'AWAYLEN' in new
False
get(name, default=None)

Retrieve value for the feature name.

Parameters
  • name (str) – feature to retrieve

  • default – default value if the feature is not advertised (defaults to None)

Returns

the value for that feature, if advertised, or default

Utility

class sopel.irc.utils.CapReq(prefix, plugin, failure=None, arg=None, success=None)

Represents a pending CAP REQ request.

Parameters
  • prefix (str) – either = (must be enabled), - (must not be enabled), or empty string (desired but optional)

  • plugin (str) – the requesting plugin’s name

  • failure (function) – function to call if this capability request fails

  • arg (str) – optional capability value; the request will fail if the server’s value is different

  • success (function) – function to call if this capability request succeeds

The success and failure callbacks must accept two arguments: bot (a Sopel instance) and cap (the name of the requested capability, as a string).

See also

For more information on how capability requests work, see the documentation for sopel.irc.AbstractBot.cap_req().

class sopel.irc.utils.MyInfo(client, servername, version)

Store client, servername, and version from RPL_MYINFO events.

sopel.irc.utils.get_cnames(domain)

Determine the CNAMEs for a given domain.

Parameters

domain (str) – domain to check

Returns

list (of str)

sopel.irc.utils.safe(string)

Remove newlines from a string.

Parameters

string (str) – input text to process

Returns

the string without newlines

Return type

str

Raises

TypeError – when string is None

This function removes newlines from a string and always returns a unicode string (as in str on Python 3 and unicode on Python 2), but doesn’t strip or alter it in any other way:

>>> safe('some text\r\n')
'some text'

This is useful to ensure a string can be used in a IRC message.

Changed in version 7.1: This function now raises a TypeError instead of an unpredictable behaviour when given None.