Additional API features

Sopel includes a number of additional functions that are useful for various common IRC tasks.

Note that sopel.web was deprecated in 6.2.0, and is not included in this documentation; it will be removed completely in Sopel 8. Plugins should use requests directly.

sopel.tools

Useful miscellaneous tools and shortcuts for Sopel plugins

Availability: 3+

class sopel.tools.Ddict(default=None)

A default dict implementation available for Python 2.x support.

It was used to make multi-dimensional dicts easy to use when the bot worked with Python version < 2.5.

Deprecated since version 7.0: Use collections.defaultdict instead.

class sopel.tools.Identifier(identifier)

A unicode subclass which acts appropriately for IRC identifiers.

When used as normal unicode objects, case will be preserved. However, when comparing two Identifier objects, or comparing a Identifier object with a unicode object, the comparison will be case insensitive. This case insensitivity includes the case convention conventions regarding [], {}, |, \, ^ and ~ described in RFC 2812.

is_nick()

Check if the Identifier is a nickname (i.e. not a channel)

Returns

True if this Identifier is a nickname; False if it appears to be a channel

>>> from sopel import tools
>>> ident = tools.Identifier('Sopel')
>>> ident.is_nick()
True
>>> ident = tools.Identifier('#sopel')
>>> ident.is_nick()
False
lower()

Get the RFC 2812-compliant lowercase version of this identifier.

Returns

RFC 2812-compliant lowercase version of the Identifier instance

Return type

str

class sopel.tools.OutputRedirect(logpath, stderr=False, quiet=False)

Redirect the output to the terminal and a log file.

A simplified object used to write to both the terminal and a log file.

flush()

Flush the file writing buffer.

write(string)

Write the given string to the logfile and terminal.

Parameters

string (str) – the string to write

class sopel.tools.SopelIdentifierMemory(*args)

Special Sopel memory that stores Identifier as key.

This is a convenient subclass of SopelMemory that always casts its keys as instances of Identifier:

>>> from sopel import tools
>>> memory = tools.SopelIdentifierMemory()
>>> memory['Exirel'] = 'king'
>>> list(memory.items())
[(Identifier('Exirel'), 'king')]
>>> tools.Identifier('exirel') in memory
True
>>> 'exirel' in memory
True

As seen in the example above, it is possible to perform various operations with both Identifier and str objects, taking advantage of the case-insensitive behavior of Identifier.

Note

Internally, it will try to do key = tools.Identifier(key), which will raise an exception if it cannot instantiate the key properly:

>>> memory[1] = 'error'
AttributeError: 'int' object has no attribute 'lower'

New in version 7.1.

class sopel.tools.SopelMemory(*args)

A simple thread-safe dict implementation.

In order to prevent exceptions when iterating over the values and changing them at the same time from different threads, we use a blocking lock in __setitem__ and contains.

New in version 3.1: As Willie.WillieMemory

Changed in version 4.0: Moved to tools.WillieMemory

Changed in version 6.0: Renamed from WillieMemory to SopelMemory

contains(key)

Check if key is in the memory

Parameters

key (str) – key to check for

Deprecated since version 7.0: Will be removed in Sopel 8. If you aren’t already using the in operator, you should be.

class sopel.tools.SopelMemoryWithDefault(*args)

Same as SopelMemory, but subclasses from collections.defaultdict.

New in version 4.3: As WillieMemoryWithDefault

Changed in version 6.0: Renamed to SopelMemoryWithDefault

contains(key)

Check if key is in the memory

Parameters

key (str) – key to check for

Deprecated since version 7.0: Will be removed in Sopel 8. If you aren’t already using the in operator, you should be.

sopel.tools.chain_loaders(*lazy_loaders)

Chain lazy loaders into one.

Parameters

lazy_loaders (function) – one or more lazy loader functions

Returns

a lazy loader that combines all of the given ones

Return type

function

This function takes any number of lazy loaders as arguments and merges them together into one. It’s primarily a helper for lazy rule decorators such as sopel.plugin.url_lazy().

Important

This function doesn’t check the uniqueness of regexes generated by all the loaders.

sopel.tools.check_pid(pid)

Check if a process is running with the given PID.

Parameters

pid (int) – PID to check

Return bool

True if the given PID is running, False otherwise

Availability: POSIX systems only.

Note

Matching the os.kill() behavior this function needs on Windows was rejected in Python issue #14480, so check_pid() cannot be used on Windows systems.

sopel.tools.compile_rule(nick, pattern, alias_nicks)

Compile a rule regex and fill in nickname placeholders.

Parameters
  • nick (str) – the nickname to use when replacing $nick and $nickname placeholders in the pattern

  • pattern (str) – the rule regex pattern

  • alias_nicks (list) – a list of alternatives that should also be accepted instead of nick

Returns

the compiled regex pattern, with placeholders for $nick and $nickname filled in

Return type

re.Pattern

Will not recompile an already compiled pattern.

Deprecated since version 7.1: Rule regexp tools are now part of the internal machinery. This function is deprecated and will be removed in Sopel 8.

sopel.tools.deprecated(reason=None, version=None, removed_in=None, warning_in=None, stack_frame=- 1, func=None)

Decorator to mark deprecated functions in Sopel’s API

Parameters
  • reason (str) – optional text added to the deprecation warning

  • version (str) – optional version number when the decorated function is deprecated

  • removed_in (str) – optional version number when the deprecated function will be removed

  • warning_in (str) – optional version number when the decorated function should start emitting a warning when called

  • stack_frame (int) – optional stack frame to output; defaults to -1; should almost always be negative

  • func (callable) – deprecated function

Returns

a callable that depends on how the decorator is called; either the decorated function, or a decorator with the appropriate parameters

Any time the decorated func is called, a deprecation warning will be printed to sys.stderr, with the last frame of the traceback. The optional warning_in argument suppresses the warning on Sopel versions older than that, allowing for multi-stage deprecation timelines.

The decorator can be used with or without arguments:

from sopel import tools

@tools.deprecated
def func1():
    print('func 1')

@tools.deprecated()
def func2():
    print('func 2')

@tools.deprecated(reason='obsolete', version='7.0', removed_in='8.0')
def func3():
    print('func 3')

which will output the following in a console:

>>> func1()
Deprecated: func1
File "<stdin>", line 1, in <module>
func 1
>>> func2()
Deprecated: func2
File "<stdin>", line 1, in <module>
func 2
>>> func3()
Deprecated since 7.0, will be removed in 8.0: obsolete
File "<stdin>", line 1, in <module>
func 3

The stack_frame argument can be used to choose which stack frame is printed along with the message text. By default, this decorator prints the most recent stack frame (the last entry in the list, -1), corresponding to where the decorated function itself was called. However, in certain cases such as deprecating conditional behavior within an object constructor, it can be useful to show a less recent stack frame instead.

Note

There is nothing that prevents this decorator to be used on a class’s method, or on any existing callable.

New in version 7.0: Parameters reason, version, and removed_in.

New in version 7.1: The warning_in and stack_frame parameters.

sopel.tools.get_action_command_pattern(command)

Get the uncompiled regex pattern for action commands.

Parameters

command (str) – the command name

Returns

a regex pattern that will match the given command

Return type

str

Deprecated since version 7.1: Command regexp tools are now part of the internal machinery. This function is deprecated and will be removed in Sopel 8.

sopel.tools.get_action_command_regexp(command)

Get a compiled regexp object that implements the command.

Parameters

command (str) – the name of the command

Returns

a compiled regexp object that implements the command

Return type

re.Pattern

Deprecated since version 7.1: Command regexp tools are now part of the internal machinery. This function is deprecated and will be removed in Sopel 8.

sopel.tools.get_command_pattern(prefix, command)

Get the uncompiled regex pattern for standard commands.

Parameters
  • prefix (str) – the command prefix (interpreted as regex)

  • command (str) – the command name

Returns

a regex pattern that will match the given command

Return type

str

Deprecated since version 7.1: Command regexp tools are now part of the internal machinery. This function is deprecated and will be removed in Sopel 8.

sopel.tools.get_command_regexp(prefix, command)

Get a compiled regexp object that implements the command.

Parameters
  • prefix (str) – the command prefix (interpreted as regex)

  • command (str) – the name of the command

Returns

a compiled regexp object that implements the command

Return type

re.Pattern

Deprecated since version 7.1: Command regexp tools are now part of the internal machinery. This function is deprecated and will be removed in Sopel 8.

sopel.tools.get_hostmask_regex(mask)

Get a compiled regex pattern for an IRC hostmask

Parameters

mask (str) – the hostmask that the pattern should match

Returns

a compiled regex pattern matching the given mask

Return type

re.Pattern

sopel.tools.get_input(prompt)

Get decoded input from the terminal (equivalent to Python 3’s input).

Parameters

prompt (str) – what to display as a prompt on the terminal

Returns

the user’s input

Return type

str

Deprecated since version 7.1: Use of this function will become a warning when Python 2 support is dropped in Sopel 8.0. The function will be removed in Sopel 8.1.

sopel.tools.get_logger(plugin_name)

Return a logger for a plugin.

Parameters

plugin_name (str) – name of the plugin

Returns

the logger for the given plugin

This:

from sopel import tools
LOGGER = tools.get_logger('my_custom_plugin')

is equivalent to this:

import logging
LOGGER = logging.getLogger('sopel.externals.my_custom_plugin')

Internally, Sopel configures logging for the sopel namespace, so external plugins can’t benefit from it with logging.getLogger(__name__) as they won’t be in the same namespace. This function uses the plugin_name with a prefix inside this namespace.

New in version 7.0.

sopel.tools.get_nickname_command_pattern(command)

Get the uncompiled regex pattern for a nickname command.

Parameters

command (str) – the command name

Returns

a regex pattern that will match the given nickname command

Return type

str

Deprecated since version 7.1: Command regexp tools are now part of the internal machinery. This function is deprecated and will be removed in Sopel 8.

sopel.tools.get_nickname_command_regexp(nick, command, alias_nicks)

Get a compiled regexp object that implements the nickname command.

Parameters
  • nick (str) – the bot’s nickname

  • command (str) – the command name

  • alias_nicks (list) – a list of alternatives that should also be accepted instead of nick

Returns

a compiled regex pattern that implements the given nickname command

Return type

re.Pattern

Deprecated since version 7.1: Command regexp tools are now part of the internal machinery. This function is deprecated and will be removed in Sopel 8.

sopel.tools.get_raising_file_and_line(tb=None)

Get the file and line number where an exception happened.

Parameters

tb – the traceback (uses the most recent exception if not given)

Returns

a tuple of the filename and line number

Return type

(str, int)

Deprecated since version 7.0: Use Python’s built-in logging system, with the logger.exception method. This method makes sure to log the exception with the traceback and the relevant information (filename, line number, etc.).

sopel.tools.get_sendable_message(text, max_length=400)

Get a sendable text message, with its excess when needed.

Parameters
  • txt (str) – text to send (expects Unicode-encoded string)

  • max_length (int) – maximum length of the message to be sendable

Returns

a tuple of two values, the sendable text and its excess text

Return type

(str, str)

We’re arbitrarily saying that the max is 400 bytes of text when messages will be split. Otherwise, we’d have to account for the bot’s hostmask, which is hard.

The max_length is the max length of text in bytes, but we take care of Unicode 2-byte characters by working on the Unicode string, then making sure the bytes version is smaller than the max length.

New in version 6.6.2.

sopel.tools.iteritems()

D.items() -> a set-like object providing a view on D’s items

sopel.tools.iterkeys()

D.keys() -> a set-like object providing a view on D’s keys

sopel.tools.itervalues()

D.values() -> an object providing a view on D’s values

sopel.tools.stderr(string)

Print the given string to stderr.

Parameters

string (str) – the string to output

This is equivalent to print >> sys.stderr, string

sopel.tools.web

The tools.web package contains utility functions for interaction with web applications, APIs, or websites in your plugins.

New in version 7.0.

Note

Some parts of this module will remain accessible through sopel.web as well until its final removal in Sopel 8. This is for backward compatibility only; please update old code as soon as possible.

sopel.tools.web.r_entity = re.compile('&([^;\\s]+);')

Regular expression to match HTML entities.

sopel.tools.web.DEFAULT_HEADERS = {'User-Agent': 'Sopel/7.1.2 (https://sopel.chat)'}

Default header dict for use with requests methods.

Use it like this:

import requests

from sopel.tools import web

result = requests.get(
    'https://some.site/api/endpoint',
    headers=web.DEFAULT_HEADERS
)

Important

You should never modify this directly in your plugin code. Make a copy and use update() if you need to add or change headers:

from sopel.tools import web

default_headers = web.DEFAULT_HEADERS.copy()
custom_headers = {'Accept': 'text/*'}

default_headers.update(custom_headers)
sopel.tools.web.USER_AGENT = 'Sopel/7.1.2 (https://sopel.chat)'

User agent string to be sent with HTTP requests.

Meant to be passed like so:

import requests

from sopel.tools import web

result = requests.get(
    'https://some.site/api/endpoint',
    user_agent=web.USER_AGENT
)
sopel.tools.web.decode(html)

Decode HTML entities into Unicode text.

Parameters

html (str) – the HTML page or snippet to process

Return str

html with all entity references replaced

sopel.tools.web.entity(match)

Convert an entity reference to the appropriate character.

Parameters

match (str) – the entity name or code, as matched by r_entity

Return str

the Unicode character corresponding to the given match string, or a fallback representation if the reference cannot be resolved to a character

sopel.tools.web.iri_to_uri(iri)

Decodes an internationalized domain name (IDN).

sopel.tools.web.quote(string, safe='/')

Safely encodes a string for use in a URL.

Parameters
  • string (str) – the string to encode

  • safe (str) – a list of characters that should not be quoted; defaults to '/'

Return str

the string with special characters URL-encoded

Note

This is a shim to make writing cross-compatible plugins for both Python 2 and Python 3 easier.

sopel.tools.web.quote_query(string)

Safely encodes a URL’s query parameters.

Parameters

string (str) – a URL containing query parameters

Return str

the input URL with query parameter values URL-encoded

sopel.tools.web.search_urls(text, exclusion_char=None, clean=False, schemes=None)

Extracts all URLs in text.

Parameters
  • text (str) – the text to search for URLs

  • exclusion_char (str) – optional character that, if placed before a URL in the text, will exclude it from being extracted

  • clean (bool) – if True, all found URLs are passed through trim_url() before being returned; default False

  • schemes (list) – optional list of URL schemes to look for; defaults to ['http', 'https', 'ftp']

Returns

generator iterator of all URLs found in text

To get the URLs as a plain list, use e.g.:

list(search_urls(text))
sopel.tools.web.trim_url(url)

Removes extra punctuation from URLs found in text.

Parameters

url (str) – the raw URL match

Return str

the cleaned URL

This function removes trailing punctuation that looks like it was not intended to be part of the URL:

  • trailing sentence- or clause-ending marks like ., ;, etc.

  • unmatched trailing brackets/braces like }, ), etc.

It is intended for use with the output of search_urls(), which may include trailing punctuation when used on input from chat.

sopel.tools.web.unquote(string)

Decodes a URL-encoded string.

Parameters

string (str) – the string to decode

Return str

the decoded string

Note

This is a shim to make writing cross-compatible plugins for both Python 2 and Python 3 easier.

sopel.tools.web.urlencode(query, doseq=False, safe='', encoding=None, errors=None, quote_via=<function quote_plus>)

Encode a dict or sequence of two-element tuples into a URL query string.

If any values in the query arg are sequences and doseq is true, each sequence element is converted to a separate parameter.

If the query arg is a sequence of two-element tuples, the order of the parameters in the output will match the order of parameters in the input.

The components of a query arg may each be either a string or a bytes type.

The safe, encoding, and errors parameters are passed down to the function specified by quote_via (encoding and errors only if a component is a str).

sopel.tools.web.urlencode_non_ascii(b)

Safely encodes non-ASCII characters in a URL.

sopel.tools.time

Tools for getting and displaying the time.

sopel.tools.time.format_time(db=None, config=None, zone=None, nick=None, channel=None, time=None)

Return a formatted string of the given time in the given zone.

Parameters
  • db (SopelDB) – bot database object (optional)

  • config (Config) – bot config object (optional)

  • zone (str) – name of timezone to use (optional)

  • nick (str) – nick whose time format to use, if set (optional)

  • channel (str) – channel whose time format to use, if set (optional)

  • time (datetime) – the time value to format (optional)

time, if given, should be a naive datetime.datetime object and will be treated as being in the UTC timezone. If it is not given, the current time will be used. If zone is given it must be present in the IANA Time Zone Database; get_timezone can be helpful for this. If zone is not given, UTC will be assumed.

The format for the string is chosen in the following order:

  1. The format for the nick nick in db, if one is set and valid.

  2. The format for the channel channel in db, if one is set and valid.

  3. The default format in config, if one is set and valid.

  4. ISO-8601

If db is not given or is not set up, steps 1 and 2 are skipped. If config is not given, step 3 will be skipped.

sopel.tools.time.get_channel_timezone(db, channel)

Get a channel’s timezone from database.

Parameters
  • db (SopelDB) – Bot’s database handler (usually bot.db)

  • channel (Identifier) – IRC channel name

Returns

the timezone associated with the channel

If a timezone cannot be found for channel, or if it is invalid, None will be returned.

sopel.tools.time.get_nick_timezone(db, nick)

Get a nick’s timezone from database.

Parameters
  • db (SopelDB) – Bot’s database handler (usually bot.db)

  • nick (Identifier) – IRC nickname

Returns

the timezone associated with the nick

If a timezone cannot be found for nick, or if it is invalid, None will be returned.

sopel.tools.time.get_time_unit(years=0, months=0, days=0, hours=0, minutes=0, seconds=0)

Map a time in (y, m, d, h, min, s) to its labels.

Parameters
  • years (int) – number of years

  • months (int) – number of months

  • days (int) – number of days

  • hours (int) – number of hours

  • minutes (int) – number of minutes

  • seconds (int) – number of seconds

Returns

a tuple of 2-value tuples, each for a time amount and its label

Return type

tuple

This helper function get a time split in years, months, days, hours, minutes, and seconds to return a tuple with the correct label for each unit. The label is pluralized and account for zéro, one, and more than one value per unit:

>>> get_time_unit(days=1, hours=15, minutes=54, seconds=19)
(
    (0, 'years'),
    (0, 'months'),
    (1, 'day'),
    (15, 'hours'),
    (54, 'minutes'),
    (19, 'seconds'),
)

This function can be used with seconds_to_split():

>>> get_time_unit(*seconds_to_split(143659))
# ... same result as the example above

Note

This function always returns a tuple with all time units, even when their amount is 0 (which is their default value).

sopel.tools.time.get_timezone(db=None, config=None, zone=None, nick=None, channel=None)

Find, and return, the appropriate timezone.

Parameters
  • db (SopelDB) – bot database object (optional)

  • config (Config) – bot config object (optional)

  • zone (str) – preferred timezone name (optional)

  • nick (str) – nick whose timezone to use, if set (optional)

  • channel (str) – channel whose timezone to use, if set (optional)

Timezone is pulled in the following priority:

  1. zone, if it is valid

  2. The timezone for the channel or nick zone in db if one is set and valid.

  3. The timezone for the nick nick in db, if one is set and valid.

  4. The timezone for the channel channel in db, if one is set and valid.

  5. The default timezone in config, if one is set and valid.

If db is not given, or given but not set up, steps 2 and 3 will be skipped. If config is not given, step 4 will be skipped. If no step yields a valid timezone, None is returned.

Valid timezones are those present in the IANA Time Zone Database.

See also

The validate_timezone() function handles the validation and formatting of the timezone.

sopel.tools.time.seconds_to_human(secs, granularity=2)

Format timedelta as a human-readable relative time.

Parameters
  • secs (timedelta or integer) – time difference to format

  • granularity (int) – number of time units to return (default to 2)

Inspiration for function structure from: https://gist.github.com/Highstaker/280a09591df4a5fb1363b0bbaf858f0d

Examples:

>>> seconds_to_human(65707200)
'2 years, 1 month ago'
>>> seconds_to_human(-17100)  # negative amount
'in 4 hours, 45 minutes'
>>> seconds_to_human(-709200)
'in 8 days, 5 hours'
>>> seconds_to_human(39441600, 1)  # 1 year + 3 months
'1 year ago'

This function can be used with a timedelta:

>>> from datetime import timedelta
>>> seconds_to_human(timedelta(days=42, seconds=278))
'1 month, 11 days ago'

The granularity argument controls how detailed the result is:

>>> seconds_to_human(3672)  # 2 by default
'1 hour, 1 minute ago'
>>> seconds_to_human(3672, granularity=3)
'1 hour, 1 minute, 12 seconds ago'
>>> seconds_to_human(3672, granularity=1)
'1 hour ago'
sopel.tools.time.seconds_to_split(seconds)

Split an amount of seconds into years, months, days, etc.

Parameters

seconds (int) – amount of time in seconds

Returns

the time split into a tuple of years, months, days, hours, minutes, and seconds

Return type

tuple

Examples:

>>> seconds_to_split(7800)
(0, 0, 0, 2, 10, 0)
>>> seconds_to_split(143659)
(0, 0, 1, 15, 54, 19)
sopel.tools.time.validate_format(tformat)

Validate a time format string.

Parameters

tformat (str) – the format string to validate

Returns

the format string, if valid

Raises

ValueError – when tformat is not a valid time format string

sopel.tools.time.validate_timezone(zone)

Return an IETF timezone from the given IETF zone or common abbreviation.

Parameters

zone (str) – in a strict or a human-friendly format

Returns

the valid IETF timezone properly formatted

Raises

ValueError – when zone is not a valid timezone

Prior to checking timezones, two transformations are made to make the zone names more human-friendly:

  1. the string is split on ', ', the pieces reversed, and then joined with / (“New York, America” becomes “America/New York”)

  2. Remaining spaces are replaced with _

This means new york, america becomes America/New_York, and utc becomes UTC. In the majority of user-facing interactions, such case-insensitivity will be expected.

If the zone is not valid, ValueError will be raised.

sopel.tools.calculation

Tools to help safely do calculations from user input

sopel.tools.calculation.eval_equation = <sopel.tools.calculation.EquationEvaluator object>

Evaluates a Python equation expression and returns the result.

Parameters
  • equation (str) – the equation to evaluate

  • timeout (int or float) – optional timeout value

Supports addition (+), subtraction (-), multiplication (*), division (/), power (**) and modulo (%).

sopel.tools.target

class sopel.tools.target.Channel(name)

A representation of a channel Sopel is in.

Parameters

name (Identifier) – the channel name

add_user(user, privs=0)

Add user to this channel.

Parameters
  • user (User) – the new user to add

  • privs (int) – privilege bitmask (see constants in sopel.plugin)

Called when a new user JOINs the channel.

clear_user(nick)

Remove nick from this channel.

Parameters

nick (Identifier) – the nickname of the user to remove

Called after a user leaves the channel via PART, KICK, QUIT, etc.

has_privilege(nick, privilege)

Tell if a user has a privilege level or above in this channel.

Parameters
  • nick (str) – a user’s nick in this channel

  • privilege (int) – privilege level to check

Return type

bool

This method checks the user’s privilege level in this channel, i.e. if it has this level or higher privileges:

>>> channel.add_user(some_user, plugin.OP)
>>> channel.has_privilege(some_user.nick, plugin.VOICE)
True

The nick argument can be either a str or a sopel.tools.Identifier. If the user is not in this channel, it will be considered as not having any privilege.

See also

There are other methods to check the exact privilege level of a user, such as is_oper(), is_owner(), is_admin(), is_op(), is_halfop(), and is_voiced().

Important

Not all IRC networks support all privilege levels. If you intend for your plugin to run on any network, it is safest to rely only on the presence of standard modes: +v (voice) and +o (op).

is_admin(nick)

Tell if a user has the ADMIN privilege level.

Parameters

nick (str) – a user’s nick in this channel

Return type

bool

Unlike has_privilege(), this method checks if the user has been explicitly granted the ADMIN privilege level:

>>> channel.add_user(some_user, plugin.ADMIN)
>>> channel.is_admin(some_user.nick)
True
>>> channel.is_voiced(some_user.nick)
False

Note that you can always have more than one privilege level:

>>> channel.add_user(some_user, plugin.ADMIN | plugin.VOICE)
>>> channel.is_admin(some_user.nick)
True
>>> channel.is_voiced(some_user.nick)
True

Important

Not all IRC networks support this privilege mode. If you are writing a plugin for public distribution, ensure your code behaves sensibly if only +v (voice) and +o (op) modes exist.

is_halfop(nick)

Tell if a user has the HALFOP privilege level.

Parameters

nick (str) – a user’s nick in this channel

Return type

bool

Unlike has_privilege(), this method checks if the user has been explicitly granted the HALFOP privilege level:

>>> channel.add_user(some_user, plugin.HALFOP)
>>> channel.is_halfop(some_user.nick)
True
>>> channel.is_voiced(some_user.nick)
False

Note that you can always have more than one privilege level:

>>> channel.add_user(some_user, plugin.HALFOP | plugin.VOICE)
>>> channel.is_halfop(some_user.nick)
True
>>> channel.is_voiced(some_user.nick)
True

Important

Not all IRC networks support this privilege mode. If you are writing a plugin for public distribution, ensure your code behaves sensibly if only +v (voice) and +o (op) modes exist.

is_op(nick)

Tell if a user has the OP privilege level.

Parameters

nick (str) – a user’s nick in this channel

Return type

bool

Unlike has_privilege(), this method checks if the user has been explicitly granted the OP privilege level:

>>> channel.add_user(some_user, plugin.OP)
>>> channel.is_op(some_user.nick)
True
>>> channel.is_voiced(some_user.nick)
False

Note that you can always have more than one privilege level:

>>> channel.add_user(some_user, plugin.OP | plugin.VOICE)
>>> channel.is_op(some_user.nick)
True
>>> channel.is_voiced(some_user.nick)
True
is_oper(nick)

Tell if a user has the OPER (operator) privilege level.

Parameters

nick (str) – a user’s nick in this channel

Return type

bool

Unlike has_privilege(), this method checks if the user has been explicitly granted the OPER privilege level:

>>> channel.add_user(some_user, plugin.OPER)
>>> channel.is_oper(some_user.nick)
True
>>> channel.is_voiced(some_user.nick)
False

Note that you can always have more than one privilege level:

>>> channel.add_user(some_user, plugin.OPER | plugin.VOICE)
>>> channel.is_oper(some_user.nick)
True
>>> channel.is_voiced(some_user.nick)
True

Important

Not all IRC networks support this privilege mode. If you are writing a plugin for public distribution, ensure your code behaves sensibly if only +v (voice) and +o (op) modes exist.

is_owner(nick)

Tell if a user has the OWNER privilege level.

Parameters

nick (str) – a user’s nick in this channel

Return type

bool

Unlike has_privilege(), this method checks if the user has been explicitly granted the OWNER privilege level:

>>> channel.add_user(some_user, plugin.OWNER)
>>> channel.is_owner(some_user.nick)
True
>>> channel.is_voiced(some_user.nick)
False

Note that you can always have more than one privilege level:

>>> channel.add_user(some_user, plugin.OWNER | plugin.VOICE)
>>> channel.is_owner(some_user.nick)
True
>>> channel.is_voiced(some_user.nick)
True

Important

Not all IRC networks support this privilege mode. If you are writing a plugin for public distribution, ensure your code behaves sensibly if only +v (voice) and +o (op) modes exist.

is_voiced(nick)

Tell if a user has the VOICE privilege level.

Parameters

nick (str) – a user’s nick in this channel

Return type

bool

Unlike has_privilege(), this method checks if the user has been explicitly granted the VOICE privilege level:

>>> channel.add_user(some_user, plugin.VOICE)
>>> channel.is_voiced(some_user.nick)
True
>>> channel.add_user(some_user, plugin.OP)
>>> channel.is_voiced(some_user.nick)
False

Note that you can always have more than one privilege level:

>>> channel.add_user(some_user, plugin.VOICE | plugin.OP)
>>> channel.is_voiced(some_user.nick)
True
>>> channel.is_op(some_user.nick)
True
last_who

The last time a WHO was requested for the channel.

modes

The channel’s modes.

For type A modes (nick/address list), the value is a set. For type B (parameter) or C (parameter when setting), the value is a string. For type D, the value is True.

Note

Type A modes may only contain changes the bot has observed. Sopel does not automatically populate all modes and lists.

name

The name of the channel.

privileges

The permissions of the users in the channel.

This maps nickname Identifiers to bitwise integer values. This can be compared to appropriate constants from sopel.plugin.

rename_user(old, new)

Rename a user.

Parameters

Called on NICK events.

topic

The topic of the channel.

users

The users in the channel.

This maps nickname Identifiers to User objects.

class sopel.tools.target.User(nick, user, host)

A representation of a user Sopel is aware of.

Parameters
  • nick (Identifier) – the user’s nickname

  • user (str) – the user’s local username (“user” in user@host.name)

  • host (str) – the user’s hostname (“host.name” in user@host.name)

account

The IRC services account of the user.

This relies on IRCv3 account tracking being enabled.

away

Whether the user is marked as away.

channels

The channels the user is in.

This maps channel name Identifiers to Channel objects.

host

The user’s hostname.

property hostmask

The user’s full hostmask.

nick

The user’s nickname.

user

The user’s local username.

sopel.tools.events

class sopel.tools.events

An enumeration of all the standardized and notable IRC numeric events

This allows you to do, for example, @plugin.event(events.RPL_WELCOME) rather than @plugin.event('001')

ERR_ALREADYREGISTERED = '462'
ERR_ALREADYREGISTRED = '462'
ERR_BADCHANMASK = '476'
ERR_BADCHANNELKEY = '475'
ERR_BADMASK = '415'
ERR_BANLISTFULL = '478'
ERR_BANNEDFROMCHAN = '474'
ERR_CANNOTSENDTOCHAN = '404'
ERR_CANTKILLSERVER = '483'
ERR_CHANNELISFULL = '471'
ERR_CHANOPRIVSNEEDED = '482'
ERR_ERRONEUSNICKNAME = '432'
ERR_FILEERROR = '424'
ERR_INVALIDCAPCMD = '410'
ERR_INVITEONLYCHAN = '473'
ERR_KEYINVALID = '767'
ERR_KEYNOPERMISSION = '769'
ERR_KEYNOTSET = '768'
ERR_KEYSET = '467'
ERR_METADATALIMIT = '764'
ERR_MONLISTFULL = '734'
ERR_NEEDMOREPARAMS = '461'
ERR_NICKCOLLISION = '436'
ERR_NICKLOCKED = '902'
ERR_NICKNAMEINUSE = '433'
ERR_NOADMININFO = '423'
ERR_NOCHANMODES = '477'
ERR_NOLOGIN = '444'
ERR_NOMATCHINGKEY = '766'
ERR_NOMOTD = '422'
ERR_NONICKNAMEGIVEN = '431'
ERR_NOOPERHOST = '491'
ERR_NOORIGIN = '409'
ERR_NOPERMFORHOST = '463'
ERR_NOPRIVILEGES = '481'
ERR_NORECIPIENT = '411'
ERR_NOSUCHCHANNEL = '403'
ERR_NOSUCHNICK = '401'
ERR_NOSUCHSERVER = '402'
ERR_NOSUCHSERVICE = '408'
ERR_NOTEXTTOSEND = '412'
ERR_NOTONCHANNEL = '442'
ERR_NOTOPLEVEL = '413'
ERR_NOTREGISTERED = '451'
ERR_PASSWDMISMATCH = '464'
ERR_RESTRICTED = '484'
ERR_SASLABORTED = '906'
ERR_SASLALREADY = '907'
ERR_SASLFAIL = '904'
ERR_SASLTOOLONG = '905'
ERR_STARTTLS = '691'
ERR_SUMMONDISABLED = '445'
ERR_TARGETINVALID = '765'
ERR_TOOMANYCHANNELS = '405'
ERR_TOOMANYTARGETS = '407'
ERR_UMODEUNKNOWNFLAG = '501'
ERR_UNAVAILRESOURCE = '437'
ERR_UNIQOPPRIVSNEEDED = '485'
ERR_UNKNOWNCOMMAND = '421'
ERR_UNKNOWNMODE = '472'
ERR_USERNOTINCHANNEL = '441'
ERR_USERONCHANNEL = '443'
ERR_USERSDISABLED = '446'
ERR_USERSDONTMATCH = '502'
ERR_WASNOSUCHNICK = '406'
ERR_WILDTOPLEVEL = '414'
ERR_YOUREBANNEDCREEP = '465'
ERR_YOUWILLBEBANNED = '466'
RPL_ADMINEMAIL = '259'
RPL_ADMINLOC1 = '257'
RPL_ADMINLOC2 = '258'
RPL_ADMINME = '256'
RPL_AWAY = '301'
RPL_BANLIST = '367'
RPL_BOUNCE = '005'
RPL_CHANNELMODEIS = '324'
RPL_CREATED = '003'
RPL_ENDOFBANLIST = '368'
RPL_ENDOFEXCEPTLIST = '349'
RPL_ENDOFINFO = '374'
RPL_ENDOFINVITELIST = '347'
RPL_ENDOFMONLIST = '733'
RPL_ENDOFMOTD = '376'
RPL_ENDOFNAMES = '366'
RPL_ENDOFSTATS = '219'
RPL_ENDOFUSERS = '394'
RPL_ENDOFWHO = '315'
RPL_ENDOFWHOIS = '318'
RPL_ENDOFWHOWAS = '369'
RPL_EXCEPTLIST = '348'
RPL_INFO = '371'
RPL_INVITELIST = '346'
RPL_INVITING = '341'
RPL_ISON = '303'
RPL_ISUPPORT = '005'
RPL_KEYVALUE = '761'
RPL_LIST = '322'
RPL_LISTEND = '323'
RPL_LISTSTART = '321'
RPL_LOGGEDIN = '900'
RPL_LOGGEDOUT = '901'
RPL_LUSERCHANNELS = '254'
RPL_LUSERCLIENT = '251'
RPL_LUSERME = '255'
RPL_LUSEROP = '252'
RPL_LUSERUNKNOWN = '253'
RPL_METADATAEND = '762'
RPL_MONLIST = '732'
RPL_MONOFFLINE = '731'
RPL_MONONLINE = '730'
RPL_MOTD = '372'
RPL_MOTDSTART = '375'
RPL_MYINFO = '004'
RPL_NAMREPLY = '353'
RPL_NONE = '300'
RPL_NOTOPIC = '331'
RPL_NOUSERS = '395'
RPL_NOWAWAY = '306'
RPL_REHASHING = '382'
RPL_SASLMECHS = '908'
RPL_SASLSUCCESS = '903'
RPL_SERVLIST = '234'
RPL_SERVLISTEND = '235'
RPL_STARTTLS = '670'
RPL_STATSCLINE = '213'
RPL_STATSCOMMANDS = '212'
RPL_STATSHLINE = '244'
RPL_STATSILINE = '215'
RPL_STATSKLINE = '216'
RPL_STATSLINKINFO = '211'
RPL_STATSLLINE = '241'
RPL_STATSNLINE = '214'
RPL_STATSOLINE = '243'
RPL_STATSUPTIME = '242'
RPL_STATSYLINE = '218'
RPL_SUMMONING = '342'
RPL_TIME = '391'
RPL_TOPIC = '332'
RPL_TRACECLASS = '209'
RPL_TRACECONNECTING = '201'
RPL_TRACEEND = '262'
RPL_TRACEHANDSHAKE = '202'
RPL_TRACELOG = '261'
RPL_TRACENEWTYPE = '208'
RPL_TRACEOPERATOR = '204'
RPL_TRACERECONNECT = '210'
RPL_TRACESERVER = '206'
RPL_TRACESERVICE = '207'
RPL_TRACEUNKNOWN = '203'
RPL_TRACEUSER = '205'
RPL_TRYAGAIN = '263'
RPL_UMODEIS = '221'
RPL_UNAWAY = '305'
RPL_UNIQOPIS = '325'
RPL_USERHOST = '302'
RPL_USERS = '393'
RPL_USERSSTART = '392'
RPL_VERSION = '351'
RPL_WELCOME = '001'
RPL_WHOISCHANNELS = '319'
RPL_WHOISIDLE = '317'
RPL_WHOISKEYVALUE = '760'
RPL_WHOISOPERATOR = '313'
RPL_WHOISSERVER = '312'
RPL_WHOISUSER = '311'
RPL_WHOREPLY = '352'
RPL_WHOSPCRPL = '354'
RPL_WHOWASUSER = '314'
RPL_YOUREOPER = '381'
RPL_YOURESERVICE = '383'
RPL_YOURHOST = '002'

sopel.tools.jobs

Sopel’s Job Scheduler: internal tool for job management.

Important

As of Sopel 5.3, this is an internal tool used by Sopel to manage internal jobs and should not be used by plugin authors. Its usage and documentation is for Sopel core development and advanced developers. It is subject to rapid changes between versions without much (or any) warning.

class sopel.tools.jobs.Job(intervals, plugin=None, label=None, handler=None, threaded=True, doc=None)

Holds information about when a function should be called next.

Parameters
  • intervals (iterable) – set of intervals; each is a number of seconds between calls to handler

  • plugin (str) – optional plugin name to which the job belongs

  • label (str) – optional label (name) for the job

  • handler (function) – function to be called when the job is ready to execute

  • doc (str) – optional documentation for the job

Job is a simple structure that holds information about when a function should be called next. They are best used with a Scheduler that will manage job execution when they are ready.

The function to execute is the handler, which must be a callable with this signature:

def handler(manager):
    # perform action periodically
    # return is optional

The manager parameter can be any kind of object; usually it’s an instance of sopel.bot.Sopel.

When a job is ready, you can execute it by calling its execute() method (providing the appropriate manager argument):

if job.is_ready_to_run(time.time()):
    job.execute(manager)  # marked as running
    # "next times" have been updated; the job is not running

In that case, execute takes care of the running state of the job.

Alternatively, you can use a with statement to perform action before and/or after executing the job; in that case, the with statement takes precedence, and the execute() method won’t interfere:

with job:
    # the job is now running, you can perform pre-execute action
    job.execute()  # execute the job's action, no state modification
    # the job is still marked as "running"
    # you can perform post-execute action

# outside of the with statement, the job is not running anymore

See also

The sopel.plugins.jobs.Scheduler class is specifically designed for plugins’ jobs, expecting an instance of sopel.bot.Sopel as a manager, and should be used to manipulate plugin jobs.

In all other case, the sopel.tools.jobs.Scheduler class is a generic job scheduler.

execute(manager)

Execute the job’s handler and return its result.

Parameters

manager (object) – used as argument to the job’s handler

Returns

the return value from the handler’s execution

This method executes the job’s handler. It doesn’t change its running state, as this must be done by the caller:

with job:  # mark as running
    # before execution
    job.execute(manager)
    # after execution
classmethod from_callable(settings, handler)

Instantiate a Job from the bot’s settings and a handler.

Parameters
get_doc()

Get the job’s documentation.

Return type

str

A job’s documentation is a short text that can be displayed to a user.

get_job_label()

Get the job’s label.

Return type

str

A job can have a label, which can identify the job by string, the same way rules can be. This label can be used to manipulate or display the job’s information in a more human-readable way. Note that the label has no effect on the job’s execution.

get_plugin_name()

Get the job’s plugin name.

Return type

str

The job’s plugin name will be used in various places to select, register, unregister, and manipulate the job based on its plugin, which is referenced by its name.

intervals

Set of intervals at which to execute the job.

is_ready_to_run(at_time)

Check if this job is (or will be) ready to run at the given time.

Parameters

at_time (int) – Timestamp to check, in seconds

Returns

True if the job is (or will be) ready to run, False otherwise

Return type

bool

is_running

Running flag: it tells if the job is running or not.

This flag is set and cleared automatically by the execute() method. It is also set and cleared when the job is used with the with statement:

with job:
    # you do something before executing the job
    # this ensures that the job is marked as "running"

Note

When set manually or with the with statement, the execute() method won’t clear this attribute itself.

is_threaded()

Tell if the job’s execution should be in a thread.

Returns

True if the execution should be in a thread, False otherwise

Return type

bool

classmethod kwargs_from_callable(handler)

Generate the keyword arguments to create a new instance.

Parameters

handler (function) – callable used to generate keyword arguments

Returns

a map of keyword arguments

Return type

dict

This classmethod takes the handler’s attributes to generate a map of keyword arguments for the class. This can be used by the from_callable() classmethod to instantiate a new rule object.

The expected attributes are the ones set by decorators from the sopel.plugin module.

next(current_time)

Update next_times, assuming it executed at current_time.

Parameters

current_time (int) – timestamp of the current time

Returns

a modified job object

next_times

Tracking of when to execute the job next time.

class sopel.tools.jobs.Scheduler(manager)

Generic Job Scheduler.

Parameters

manager (object) – manager passed to jobs as argument

Scheduler is a thread that keeps track of Jobs and periodically checks which ones are ready to execute. When ready, their execute() method is called, either in a separate thread or in the scheduler’s thread (it depends on the job’s is_threaded() method).

It can be started as any other thread:

# on bot's startup
scheduler = jobs.Scheduler(bot)
scheduler.start()  # run the thread forever

Then it runs forever until the stop() method is called, usually when the bot shuts down.

Note

Thread safety is ensured with threading’s Lock and Event when:

These actions can be performed while the scheduler is running.

Important

This is an internal tool used by Sopel to manage internal jobs and should not be used by plugin authors. Its usage and documentation is for Sopel core development and advanced developers. It is subject to rapid changes between versions without much (or any) warning.

clear_jobs()

Clear current Job queue and start fresh.

This method is thread safe. However, it won’t cancel or stop any currently running jobs.

manager

Job manager, used as argument for jobs.

register(job)

Register a Job to the current job queue.

Parameters

job (sopel.tools.jobs.Job) – job to register

This method is thread safe.

remove_callable_job(callable)

Remove callable from the job queue.

Parameters

callable (function) – the callable to remove

This method is thread safe. However, it won’t cancel or stop any currently running jobs.

run()

Run forever until stop() is called.

This method waits at most a second between each iteration. At each step it retrieves the jobs that are ready for execution, and executes them. See the Job.execute() method for more information.

Internally, it loops forever until its stopping event is set.

Note

This should not be called directly, as it will be done by the threading.Thread.start() method.

stop()

Ask the job scheduler to stop.

The scheduler thread will stop its loop over jobs to process, but it won’t join the thread, or clear its queue—this has to be done separately by the calling thread:

scheduler.stop()  # ask the scheduler to stop
scheduler.join()  # wait for the scheduler to actually stop

Note that this won’t cancel or stop any currently running jobs.

stopping

Stopping flag. See stop().

sopel.formatting

The formatting module includes functions to apply IRC formatting to text.

Availability: 4.5+

sopel.formatting.CONTROL_BOLD = '\x02'

The control code to start or end bold formatting.

sopel.formatting.CONTROL_COLOR = '\x03'

The control code to start or end color formatting.

sopel.formatting.CONTROL_HEX_COLOR = '\x04'

The control code to start or end hexadecimal color formatting.

sopel.formatting.CONTROL_ITALIC = '\x1d'

The control code to start or end italic formatting.

sopel.formatting.CONTROL_MONOSPACE = '\x11'

The control code to start or end monospace formatting.

sopel.formatting.CONTROL_NORMAL = '\x0f'

The control code to reset formatting.

sopel.formatting.CONTROL_REVERSE = '\x16'

The control code to start or end reverse-color formatting.

sopel.formatting.CONTROL_STRIKETHROUGH = '\x1e'

The control code to start or end strikethrough formatting.

sopel.formatting.CONTROL_UNDERLINE = '\x1f'

The control code to start or end underlining.

sopel.formatting.bold(text)

Return the text, with bold IRC formatting.

Parameters

text (str) – the text to format

sopel.formatting.color(text, fg=None, bg=None)

Return the text, with the given colors applied in IRC formatting.

Parameters
  • text (str) – the text to format

  • fg (mixed) – the foreground color

  • bg (mixed) – the background color

The color can be a string of the color name, or an integer in the range 0-99. The known color names can be found in the colors class of this module.

class sopel.formatting.colors
BLACK = '01'
BLUE = '02'
BROWN = '05'
CYAN = '11'
FUCHSIA = '13'
GRAY = '14'
GREEN = '03'
GREY = '14'
LIGHT_BLUE = '12'
LIGHT_CYAN = '11'
LIGHT_GRAY = '15'
LIGHT_GREEN = '09'
LIGHT_GREY = '15'
LIGHT_PURPLE = '13'
LIME = '09'
MAROON = '05'
NAVY = '02'
OLIVE = '07'
ORANGE = '07'
PINK = '13'
PURPLE = '06'
RED = '04'
ROYAL = '12'
SILVER = '15'
TEAL = '10'
WHITE = '00'
YELLOW = '08'
sopel.formatting.hex_color(text, fg=None, bg=None)

Return the text, with the given colors applied in IRC formatting.

Parameters
  • text (str) – the text to format

  • fg (str) – the foreground color

  • bg (str) – the background color

The color can be provided with a string of either 3 or 6 hexadecimal digits. As in CSS, 3-digit colors will be interpreted as if they were 6-digit colors with each digit repeated (e.g. color c90 is identical to cc9900). Do not include the leading # symbol.

Note

This is a relatively new IRC formatting convention. Use only when you can afford to have its meaning lost, as not many clients support it yet.

sopel.formatting.italic(text)

Return the text, with italic IRC formatting.

Parameters

text (str) – the text to format

sopel.formatting.monospace(text)

Return the text, with monospace IRC formatting.

Parameters

text (str) – the text to format

Note

This is a relatively new IRC formatting convention. Use only when you can afford to have its meaning lost, as not many clients support it yet.

sopel.formatting.plain(text)

Return the text without any IRC formatting.

Parameters

text (str) – text with potential IRC formatting control code(s)

Return type

str

sopel.formatting.reverse(text)

Return the text, with reverse-color IRC formatting.

Parameters

text (str) – the text to format

Note

This code isn’t super well supported, and its behavior even in clients that understand it (e.g. mIRC) can be unpredictable. Use it carefully.

sopel.formatting.strikethrough(text)

Return the text, with strikethrough IRC formatting.

Parameters

text (str) – the text to format

Note

This is a relatively new IRC formatting convention. Use only when you can afford to have its meaning lost, as not many clients support it yet.

sopel.formatting.underline(text)

Return the text, with underline IRC formatting.

Parameters

text (str) – the text to format

sopel.logger

class sopel.logger.ChannelOutputFormatter(fmt='[%(filename)s] %(message)s', datefmt=None)

Output formatter for log messages destined for an IRC channel.

Parameters

Implementation of a logging.Formatter.

formatException(exc_info)

Format the exception info as a string for output.

Parameters

exc_info (tuple) – standard exception information returned by exc_info()

class sopel.logger.IrcLoggingHandler(bot, level)

Logging handler for output to an IRC channel.

Parameters

Implementation of a logging.Handler.

emit(record)

Emit a log record to the IRC channel.

Parameters

record (logging.LogRecord) – the log record to output

sopel.logger.get_logger(name=None)

Return a logger for a module, if the name is given.

Deprecated since version 7.0: Sopel’s own code should use logging.getLogger(__name__) instead, and external plugins should use sopel.tools.get_logger().

This will emit a deprecation warning in Sopel 8.0, and it will be removed in Sopel 9.0.

sopel.logger.setup_logging(settings)

Set up logging based on the bot’s configuration settings.

Parameters

settings (sopel.config.Config) – configuration settings object