Callable decorators¶
sopel.plugin¶
This contains decorators and other tools for creating Sopel plugins.
-
sopel.plugin.
ADMIN
= 8¶ Privilege level for the +a channel permission
New in version 4.1.
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.
-
sopel.plugin.
HALFOP
= 2¶ Privilege level for the +h channel permission
New in version 4.1.
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.
-
sopel.plugin.
NOLIMIT
= 1¶ Return value for
callable
s, which suppresses rate limiting.Returning this value means the triggering user will not be prevented from triggering the same callable again within the rate limit. This can be used, for example, to allow a user to retry a failed command immediately.
New in version 4.0.
-
sopel.plugin.
OP
= 4¶ Privilege level for the +o channel permission
New in version 4.1.
-
sopel.plugin.
OPER
= 32¶ Privilege level for the +y/+Y channel permissions
Note: Except for these (non-standard) channel modes, Sopel does not monitor or store any user’s OPER status.
New in version 7.0.0.
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.
-
sopel.plugin.
OWNER
= 16¶ Privilege level for the +q channel permission
New in version 4.1.
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.
-
sopel.plugin.
VOICE
= 1¶ Privilege level for the +v channel permission
New in version 4.1.
-
sopel.plugin.
action_command
(*command_list)¶ Decorate a function to trigger on CTCP ACTION lines.
- Parameters
command_list (str) – one or more command name(s) to match
This decorator can be used to add multiple commands to one callable in a single line. The resulting match object will have the command as the first group; the rest of the line, excluding leading whitespace, as the second group; and parameters 1 through 4, separated by whitespace, as groups 3-6.
Example:
@action_command("hello!") # Would trigger on "/me hello!"
New in version 7.0.
Note
You can use a regular expression for the command name(s), but this is not recommended since version 7.1. For backward compatibility, this behavior will be kept until version 8.0.
Regex patterns are confusing for your users; please don’t use them in command names!
If you need to use a regex pattern, please use the
rule()
decorator instead, with thectcp()
decorator:@rule(r'hello!?') @ctcp('ACTION') # Would trigger on "/me hello!" and "/me hello"
-
sopel.plugin.
action_commands
(*command_list)¶ Alias to
action_command()
.
-
sopel.plugin.
command
(*command_list)¶ Decorate a function to set one or more commands that should trigger it.
- Parameters
command_list (str) – one or more command name(s) to match
This decorator can be used to add multiple commands to one callable in a single line. The resulting match object will have the command as the first group; the rest of the line, excluding leading whitespace, as the second group; and parameters 1 through 4, separated by whitespace, as groups 3-6.
Example:
@command("hello") # If the command prefix is "\.", this would trigger on lines # starting with ".hello". @command('j', 'join') # If the command prefix is "\.", this would trigger on lines # starting with either ".j" or ".join".
You can use a space in the command name to implement subcommands:
@command('main sub1', 'main sub2') # For ".main sub1", trigger.group(1) will return "main sub1" # For ".main sub2", trigger.group(1) will return "main sub2"
But in that case, be careful with the order of the names: if a more generic pattern is defined first, it will have priority over less generic patterns. So for instance, to have
.main
and.main sub
working properly, you need to declare them like this:@command('main sub', 'main') # This command will react properly to ".main sub" and ".main"
Then, you can check
trigger.group(1)
to know if it was used asmain sub
or justmain
in your callable. If you declare them in the wrong order,.main
will have priority and you won’t be able to take advantage of that.Another option is to declare command with subcommands only, like this:
@command('main sub1') # this command will be triggered on .main sub1 @command('main sub2') # this other command will be triggered on .main sub2
In that case,
.main
won’t trigger anything, and you won’t have to inspect the trigger’s groups to know which subcommand is triggered.Note
If you use this decorator multiple times, remember that the decorators are invoked in the reverse order of appearance:
# These two decorators... @command('hi') @command('hello') # ...are equivalent to this single decorator @command('hello', 'hi')
See also the Function Definitions chapter from the Python documentation for more information about functions and decorators.
Note
You can use a regular expression for the command name(s), but this is not recommended since version 7.1. For backward compatibility, this behavior will be kept until version 8.0.
Regex patterns are confusing for your users; please don’t use them in command names!
If you still want to use a regex pattern, please use the
rule()
decorator instead. For extra arguments and subcommands based on a regex pattern, you should handle these inside your decorated function, by using thetrigger
object.
-
sopel.plugin.
ctcp
(function=None, *command_list)¶ Decorate a callable to trigger on CTCP commands (mostly,
ACTION
).- Parameters
command_list (str) – one or more CTCP command(s) on which to trigger
New in version 7.1: This is now
ctcp
instead ofintent
, and it can be called without argument, in which case it will assumeACTION
.Note
This used to be
@intent
, for a long dead feature in the IRCv3 spec. It is now replaced by@ctcp
, which can be used without arguments. In that case, Sopel will assume it should trigger onACTION
.As
sopel.module
will be removed in Sopel 9, so will@intent
.
-
sopel.plugin.
echo
(function=None)¶ Decorate a function to specify that it should receive echo messages.
This decorator can be used to listen in on the messages that Sopel is sending and react accordingly.
-
sopel.plugin.
event
(*event_list)¶ Decorate a function to be triggered on specific IRC events.
- Parameters
event_list (str) – one or more event name(s) on which to trigger
This is one of a number of events, such as ‘JOIN’, ‘PART’, ‘QUIT’, etc. (More details can be found in RFC 1459.) When the Sopel bot is sent one of these events, the function will execute. Note that the default
rule()
(.*
) will match any line of the correct event type(s). If any rule is explicitly specified, it overrides the default.See also
sopel.tools.events
provides human-readable names for many of the numeric events, which may help your code be clearer.
-
class
sopel.plugin.
example
(msg, result=None, privmsg=False, admin=False, owner=False, repeat=1, re=False, ignore=None, user_help=False, online=False, vcr=False)¶ Decorate a function with an example, and optionally test output.
- Parameters
msg (str) – the example command (required; see below)
result (str) – the command’s expected output (optional; see below)
privmsg (bool) – if
True
, the example will be tested as if it was received in a private message to the bot; otherwise, in a channel (optional; defaultFalse
)admin (bool) – whether to treat the test message as having come from a bot admin (optional; default
False
)owner (bool) – whether to treat the test message as having come from the bot’s owner (optional; default
False
)repeat (int) – how many times to repeat the test; useful for commands that return random results (optional; default
1
)re (bool) – if
True
, theresult
is interpreted as a regular expression and used to match the command’s output (optional; see below)ignore (list) –
list
of regular expression patterns to match ignored output (optional; see below)user_help (bool) – whether this example should be included in user-facing help output such as .help command (optional; default
False
; see below)online (bool) – if
True
,pytest
will mark this example as “online” (optional; defaultFalse
; see below)vcr (bool) – if
True
, this example’s HTTP requests & responses will be recorded for later reuse (optional; defaultFalse
; see below)
For compatibility with the built-in help plugin,
msg
must use the defaulthelp_prefix
if it is a prefixed command. Other command types should give example invocations that work with Sopel’s default settings, especially if using the “example test” functionality to automatically generate a test(s) for the function.The presence of a
result
will generate tests automatically when Sopel’s test suite is run, usingmsg
as input. The exact behavior of the tests depends on the remaining optionalexample
arguments.Passing
re=True
, in particular, is useful for matchingresult
s that are random and/or dependent on an external API. This way, an example test can check the format of the result without caring about the exact data.Giving a list of
ignore``d patterns is helpful for commands that may return intermittent errors (mostly calls to an external API that isn't necessarily stable), especially when coupled with the ``repeat
parameter.By default, Sopel’s help plugin will display only one example (the one closest to the function’s def statement, due to how decorators work). You can override this choice or include multiple examples by passing
user_help=True
to one or moreexample
decorator(s).Passing
online=True
makes that particular example skippable if Sopel’s test suite is run in offline mode, which is mostly useful to make life easier for other developers working on Sopel without Internet access.Finally,
vcr=True
records the example’s HTTP requests and responses for replaying during later test runs. It can be an alternative (or complement) toonline
, and is especially useful for testing plugin code that calls on inconsistent or flaky remote APIs. The recorded “cassettes” of responses can be committed alongside the code for use by CI services, etc. (See VCR.py & pytest-vcr)
-
sopel.plugin.
find
(*patterns)¶ Decorate a function to be called for each time a pattern is found in a line.
- Parameters
patterns (str) – one or more regular expression(s)
Each argument is a regular expression which will trigger the function:
@find('hello', 'here') # will trigger once on "hello you" # will trigger twice on "hello here" # will trigger once on "I'm right here!"
This decorator can be used multiple times to add more rules:
@find('here') @find('hello') # will trigger once on "hello you" # will trigger twice on "hello here" # will trigger once on "I'm right here!"
If the Sopel instance is in a channel, or sent a
PRIVMSG
, the function will execute for each time a received message matches an expression. Each match will also contain the position of the instance it found.Inside the regular expression, some special directives can be used.
$nick
will be replaced with the nick of the bot and,
or:
, and$nickname
will be replaced with the nick of the bot:@find('$nickname') # will trigger for each time the bot's nick is in a trigger
New in version 7.1.
Note
The regex rule will match once for each non-overlapping match, from left to right, and the function will execute for each of these matches.
To match only once from anywhere in the line, use the
search()
decorator instead. To match only once from the start of the line, use therule()
decorator instead.
-
sopel.plugin.
find_lazy
(*loaders)¶ Decorate a callable as a find rule with lazy loading.
- Parameters
loaders (function) – one or more functions to generate a list of compiled regexes to match patterns in a line
Each
loader
function must accept asettings
parameter and return a list (or tuple) of compiled regular expressions:import re def loader(settings): return [re.compile(r'<your_rule_pattern>')]
It will be called by Sopel when the bot parses the plugin to register the find rules to get its regexes. The
settings
argument will be the bot’ssopel.config.Config
object.If any of the
loader
functions raises aPluginError
exception, the find rule will be ignored; it will not fail the plugin’s loading.The decorated function will behave like any other
callable()
:from sopel import plugin @plugin.find_lazy(loader) def my_find_rule_handler(bot, trigger): bot.say('Rule triggered by: %s' % trigger.group(0))
New in version 7.1.
See also
When more than one loader is provided, they will be chained together with the
sopel.tools.chain_loaders()
function.
-
sopel.plugin.
interval
(*intervals)¶ Decorate a function to be called by the bot every n seconds.
- Parameters
intervals (int) – one or more duration(s), in seconds
This decorator can be used multiple times for multiple intervals, or multiple intervals can be given in multiple arguments. The first time the function will be called is n seconds after the bot was started.
Plugin functions decorated by
interval
must only takebot
as their argument; they do not get atrigger
. Thebot
argument will not have a context, so functions likebot.say()
will not have a default destination.There is no guarantee that the bot is connected to a server or in any channels when the function is called, so care must be taken.
Example:
from sopel import plugin @plugin.interval(5) def spam_every_5s(bot): if "#here" in bot.channels: bot.say("It has been five seconds!", "#here")
-
sopel.plugin.
label
(value)¶ Decorate a function to add a rule label.
- Parameters
value (str) – a label for the rule
The rule label allows the documentation and the logging system to refer to this function by its label. A function can have one and only one label:
@label('on_hello') @rule('hello') # will trigger on hello, and be labelled as "on_hello"
Note
By default, the “label” of a callable will be its function name, which can be confusing for end-users: the goal of the
label
decorator is to make generic rules as user-friendly as commands are, by giving them some name that isn’t tied to an identifier in the source code.
-
sopel.plugin.
nickname_command
(*command_list)¶ Decorate a function to trigger on lines starting with “$nickname: command”.
- Parameters
command_list (str) – one or more command name(s) to match
This decorator can be used to add multiple commands to one callable in a single line. The resulting match object will have the command as the first group; the rest of the line, excluding leading whitespace, as the second group; and parameters 1 through 4, separated by whitespace, as groups 3-6.
Example:
@nickname_command("hello!") # Would trigger on "$nickname: hello!", "$nickname, hello!", # "$nickname hello!", "$nickname hello! parameter1" and # "$nickname hello! p1 p2 p3 p4 p5 p6 p7 p8 p9".
Note
You can use a regular expression for the command name(s), but this is not recommended since version 7.1. For backward compatibility, this behavior will be kept until version 8.0.
Regex patterns are confusing for your users; please don’t use them in command names!
If you need to use a regex pattern, please use the
rule()
decorator instead, with the$nick
variable:@rule(r'$nick .*') # Would trigger on anything starting with "$nickname[:,]? ", # and would never have any additional parameters, as the # command would match the rest of the line.
-
sopel.plugin.
nickname_commands
(*command_list)¶ Alias to
nickname_command()
.
-
sopel.plugin.
output_prefix
(prefix)¶ Decorate a function to add a prefix on its output.
- Parameters
prefix (str) – the prefix to add (must include trailing whitespace if desired; Sopel does not assume it should add anything)
Prefix will be added to text sent through:
-
sopel.plugin.
priority
(value)¶ Decorate a function to be executed with higher or lower priority.
- Parameters
value (str) – one of
high
,medium
, orlow
; defaults tomedium
The priority allows you to control the order of callable execution, if your plugin needs it.
-
sopel.plugin.
rate
(user=0, channel=0, server=0)¶ Decorate a function to be rate-limited.
- Parameters
How often a function can be triggered on a per-user basis, in a channel, or across the server (bot) can be controlled with this decorator. A value of
0
means no limit. If a function is given a rate of 20, that function may only be used once every 20 seconds in the scope corresponding to the parameter. Users on the admin list in Sopel’s configuration are exempted from rate limits.Rate-limited functions that use scheduled future commands should import
threading.Timer
instead ofsched
, or rate limiting will not work properly.
-
sopel.plugin.
require_account
(message=None, reply=False)¶ Decorate a function to require services/NickServ authentication.
- Parameters
New in version 7.0.
Note
Only some networks support services authentication, and not all of those implement the standards required for clients like Sopel to determine authentication status. This decorator will block all use of functions it decorates on networks that lack the relevant features.
-
sopel.plugin.
require_admin
(message=None, reply=False)¶ Decorate a function to require the triggering user to be a bot admin.
- Parameters
When the triggering user is not an admin, the command is not run, and the bot will say the
message
if given. By default, it usesbot.say()
, but whenreply
is true, then it usesbot.reply()
instead.Changed in version 7.0: Added the
reply
parameter.
-
sopel.plugin.
require_bot_privilege
(level, message=None, reply=False)¶ Decorate a function to require a minimum channel privilege for the bot.
- Parameters
level
can be one of the privilege level constants defined in this module. If the bot does not have the privilege, the bot will saymessage
if given. By default, it usesbot.say()
, but whenreply
is true, then it usesbot.reply()
instead.Privilege requirements are ignored in private messages.
New in version 7.1.
-
sopel.plugin.
require_chanmsg
(message=None, reply=False)¶ Decorate a function to only be triggerable from a channel message.
- Parameters
A decorated plugin callable will be triggered only by messages from a channel:
from sopel import plugin @plugin.command('.mycommand') @plugin.require_chanmsg('Channel only command.') def manage_topic(bot, trigger): # trigger on channel messages only
If the decorated function is triggered by a private message,
message
will be said if given. By default, it usesbot.say()
, but whenreply
is true, then it usesbot.reply()
instead.Changed in version 7.0: Added the
reply
parameter.
-
sopel.plugin.
require_owner
(message=None, reply=False)¶ Decorate a function to require the triggering user to be the bot owner.
- Parameters
When the triggering user is not the bot’s owner, the command is not run, and the bot will say
message
if given. By default, it usesbot.say()
, but whenreply
is true, then it usesbot.reply()
instead.Changed in version 7.0: Added the
reply
parameter.
-
sopel.plugin.
require_privilege
(level, message=None, reply=False)¶ Decorate a function to require at least the given channel permission.
- Parameters
level
can be one of the privilege level constants defined in this module. If the user does not have the privilege, the bot will saymessage
if given. By default, it usesbot.say()
, but whenreply
is true, then it usesbot.reply()
instead.Privilege requirements are ignored in private messages.
Changed in version 7.0: Added the
reply
parameter.
-
sopel.plugin.
require_privmsg
(message=None, reply=False)¶ Decorate a function to only be triggerable from a private message.
- Parameters
If the decorated function is triggered by a channel message,
message
will be said if given. By default, it usesbot.say()
, but whenreply
is true, then it usesbot.reply()
instead.Changed in version 7.0: Added the
reply
parameter.
-
sopel.plugin.
rule
(*patterns)¶ Decorate a function to be called when a line matches the given pattern.
- Parameters
patterns (str) – one or more regular expression(s)
Each argument is a regular expression which will trigger the function:
@rule('hello', 'how') # will trigger once on "how are you?" # will trigger once on "hello, what's up?"
This decorator can be used multiple times to add more rules:
@rule('how') @rule('hello') # will trigger once on "how are you?" # will trigger once on "hello, what's up?"
If the Sopel instance is in a channel, or sent a
PRIVMSG
, where a string matching this expression is said, the function will execute. Note that captured groups here will be retrievable through theTrigger
object later.Inside the regular expression, some special directives can be used.
$nick
will be replaced with the nick of the bot and,
or:
, and$nickname
will be replaced with the nick of the bot.Changed in version 7.0: The
rule()
decorator can be called with multiple positional arguments, each used to add a rule. This is equivalent to decorating the same function multiple times with this decorator.
-
sopel.plugin.
rule_lazy
(*loaders)¶ Decorate a callable as a rule with lazy loading.
- Parameters
loaders (function) – one or more functions to generate a list of compiled regexes to match URLs
Each
loader
function must accept asettings
parameter and return a list (or tuple) of compiled regular expressions:import re def loader(settings): return [re.compile(r'<your_rule_pattern>')]
It will be called by Sopel when the bot parses the plugin to register rules to get its regexes. The
settings
argument will be the bot’ssopel.config.Config
object.If any of the
loader
functions raises aPluginError
exception, the rule will be ignored; it will not fail the plugin’s loading.The decorated function will behave like any other
callable()
:from sopel import plugin @plugin.rule_lazy(loader) def my_rule_handler(bot, trigger): bot.say('Rule triggered by: %s' % trigger.group(0))
New in version 7.1.
See also
When more than one loader is provided, they will be chained together with the
sopel.tools.chain_loaders()
function.
-
sopel.plugin.
search
(*patterns)¶ Decorate a function to be called when a pattern matches anywhere in a line.
- Parameters
patterns (str) – one or more regular expression(s)
Each argument is a regular expression which will trigger the function:
@search('hello', 'here') # will trigger once on "hello you" # will trigger twice on "hello here" # will trigger once on "I'm right here!"
This decorator can be used multiple times to add more search rules:
@search('here') @search('hello') # will trigger once on "hello you" # will trigger twice on "hello here" (once per expression) # will trigger once on "I'm right here!"
If the Sopel instance is in a channel, or sent a PRIVMSG, where a part of a string matching this expression is said, the function will execute. Note that captured groups here will be retrievable through the
Trigger
object later. The match will also contain the position of the first instance found.Inside the regular expression, some special directives can be used.
$nick
will be replaced with the nick of the bot and,
or:
, and$nickname
will be replaced with the nick of the bot:@search('$nickname') # will trigger once when the bot's nick is in a trigger
New in version 7.1.
Note
The regex rule will match for the first instance only, starting from the left of the line, and the function will execute only once per regular expression.
To match for each time an expression is found, use the
find()
decorator instead. To match only once from the start of the line, use therule()
decorator instead.
-
sopel.plugin.
search_lazy
(*loaders)¶ Decorate a callable as a search rule with lazy loading.
- Parameters
loaders (function) – one or more functions to generate a list of compiled regexes to match patterns in a line
Each
loader
function must accept asettings
parameter and return a list (or tuple) of compiled regular expressions:import re def loader(settings): return [re.compile(r'<your_rule_pattern>')]
It will be called by Sopel when the bot parses the plugin to register the search rules to get its regexes. The
settings
argument will be the bot’ssopel.config.Config
object.If any of the
loader
functions raises aPluginError
exception, the find rule will be ignored; it will not fail the plugin’s loading.The decorated function will behave like any other
callable()
:from sopel import plugin @plugin.search_lazy(loader) def my_search_rule_handler(bot, trigger): bot.say('Rule triggered by: %s' % trigger.group(0))
New in version 7.1.
See also
When more than one loader is provided, they will be chained together with the
sopel.tools.chain_loaders()
function.
-
sopel.plugin.
thread
(value)¶ Decorate a function to specify if it should be run in a separate thread.
- Parameters
value (bool) – if
True
, the function is called in a separate thread; otherwise, from the bot’s main thread
Functions run in a separate thread (as is the default) will not prevent the bot from executing other functions at the same time. Functions not run in a separate thread may be started while other functions are still running, but additional functions will not start until it is completed.
-
sopel.plugin.
unblockable
(function)¶ Decorate a function to exempt it from the ignore/blocks system.
For example, this can be used to ensure that important events such as
JOIN
are always recorded:from sopel import plugin @plugin.event('JOIN') @plugin.unblockable def on_join_callable(bot, trigger): # do something when a user JOIN a channel # a blocked nickname or hostname *will* trigger this pass
See also
Sopel’s
dispatch()
method.
-
sopel.plugin.
url
(*url_rules)¶ Decorate a function to handle URLs.
- Parameters
url_rules (str) – one or more regex pattern(s) to match URLs
This decorator takes a regex string that will be matched against URLs in a message. The function it decorates is like any other callable:
from sopel import plugin @plugin.url(r'https://example.com/bugs/([a-z0-9]+)') @plugin.url(r'https://short.com/([a-z0-9]+)') def handle_example_bugs(bot, trigger): bot.reply('Found bug ID #%s' % trigger.group(1))
The
bot
is an instance ofSopelWrapper
, andtrigger
is the usualTrigger
object.Under the hood, when Sopel collects the decorated handler it uses an instance of
sopel.plugins.rules.URLCallback
to register it to itsrules manager
and itsregister_url_callback()
method.Changed in version 7.0: The same function can be decorated multiple times with
url()
to register different URL patterns.Changed in version 7.0: More than one pattern can be provided as positional argument at once.
Changed in version 7.1: The
match
parameter is obsolete and can be omitted. When present however, it represents the same match as thetrigger
argument.This behavior will be kept for backward compatibility and will be removed in Sopel 9.
See also
To detect URLs, Sopel uses a matching pattern built from a list of URL schemes, configured by
auto_url_schemes
.
-
sopel.plugin.
url_lazy
(*loaders)¶ Decorate a function to handle URL, using lazy-loading for its regex.
- Parameters
loaders (function) – one or more functions to generate a list of compiled regexes to match URLs.
Each
loader
function must accept asettings
parameter and return a list (or tuple) of compiled regular expressions:import re def loader(settings): return [re.compile(r'<your_url_pattern>')]
It will be called by Sopel when the bot parses the plugin to register URL callbacks to get its regexes. The
settings
argument will be the bot’ssopel.config.Config
object.If any of the
loader
functions raises aPluginError
exception, the URL callback will be ignored; it will not fail the plugin’s loading.The decorated function will behave like any other
callable()
:from sopel import plugin @plugin.url_lazy(loader) def my_url_handler(bot, trigger): bot.say('URL found: %s' % trigger.group(0))
New in version 7.1.
See also
When more than one loader is provided, they will be chained together with the
sopel.tools.chain_loaders()
function.
About sopel.module
¶
Before Sopel 7.1, sopel.module
was the preferred and only way to decorate
callables for plugins. However, since the term module
can be confusing
(mostly because it already has a special meaning in Python), it has been
replaced by plugin
in most cases related to add-ons for Sopel.
The sopel.module
sub-module is replaced by sopel.plugin
.
Deprecated since version 7.1: Use sopel.plugin
instead. This will be removed in Sopel 9.
-
sopel.module.
intent
(*intent_list)¶ Decorate a callable to trigger on intent messages.
- Parameters
intent_list (str) – one or more intent(s) on which to trigger (really, the only useful value is
ACTION
)
New in version 5.2.0.
Deprecated since version 7.1.
Important
This will be removed in Sopel 9, as the IRCv3 intent specification is long dead. You can use
@ctcp
instead.