247 lines
7.3 KiB
Python
Executable File
247 lines
7.3 KiB
Python
Executable File
#!/usr/bin/env python3
|
||
"""
|
||
This contains decorators and tools for creating callable plugin functions.
|
||
"""
|
||
import functools
|
||
|
||
|
||
def hook(value=False):
|
||
"""
|
||
Decorate a function to be called every time a PRIVMSG is received.
|
||
|
||
Args:
|
||
value: Either True or False. If True the function is called every
|
||
time a PRIVMSG is received. If False it is not. Default is False.
|
||
|
||
PRIVMSGs are ordinary messages sent from either a channel or a user (a
|
||
private message). In a busy channel, this function will be called quite
|
||
a lot. Please consider this carefully before applying as it can have
|
||
significant performance implications.
|
||
|
||
Note: Do not use this with the commands decorator.
|
||
"""
|
||
def add_attribute(function):
|
||
function.hook = value
|
||
return function
|
||
return add_attribute
|
||
|
||
|
||
def thread(value=True):
|
||
"""
|
||
Decorate a function to specify if it should be run in a separate 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.
|
||
|
||
Args:
|
||
value: Either True or False. If True the function is called in
|
||
a separate thread. If False from the main thread.
|
||
|
||
"""
|
||
def add_attribute(function):
|
||
function.thread = value
|
||
return function
|
||
return add_attribute
|
||
|
||
|
||
def commands(*command_list):
|
||
"""
|
||
Decorate a function to set one or more commands to trigger it.
|
||
|
||
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, rest of the line, excluding leading whitespace, as the second group.
|
||
Parameters 1 through 4, seperated by whitespace, will be groups 3-6.
|
||
|
||
Args:
|
||
command: A string, which can be a regular expression.
|
||
|
||
Returns:
|
||
A function with a new command appended to the commands
|
||
attribute. If there is no commands attribute, it is added.
|
||
|
||
Example:
|
||
@commands("hello"):
|
||
If the command prefix is "\.", this would trigger on lines starting
|
||
with ".hello".
|
||
|
||
@commands('j', 'join')
|
||
If the command prefix is "\.", this would trigger on lines starting
|
||
with either ".j" or ".join".
|
||
|
||
"""
|
||
def add_attribute(function):
|
||
if not hasattr(function, "commands"):
|
||
function.commands = []
|
||
function.commands.extend(command_list)
|
||
return function
|
||
return add_attribute
|
||
|
||
|
||
def rate(user=0, channel=0, server=0):
|
||
"""
|
||
Decorate a function to limit how often it can be triggered on a per-user
|
||
basis, in a channel, or across the server (bot). A value of zero 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 of sched, or rate limiting will not work properly.
|
||
"""
|
||
def add_attribute(function):
|
||
function.rate = user
|
||
function.channel_rate = channel
|
||
function.global_rate = server
|
||
return function
|
||
return add_attribute
|
||
|
||
|
||
def require_privmsg(message=None):
|
||
"""
|
||
Decorate a function to only be triggerable from a private message.
|
||
|
||
If it is triggered in a channel message, `message` will be said if given.
|
||
"""
|
||
def actual_decorator(function):
|
||
@functools.wraps(function)
|
||
def _nop(*args, **kwargs):
|
||
# Assign trigger and bot for easy access later
|
||
bot, trigger = args[0:2]
|
||
if trigger.is_privmsg:
|
||
return function(*args, **kwargs)
|
||
else:
|
||
if message and not callable(message):
|
||
bot.say(message)
|
||
return _nop
|
||
# Hack to allow decorator without parens
|
||
if callable(message):
|
||
return actual_decorator(message)
|
||
return actual_decorator
|
||
|
||
|
||
def require_chanmsg(message=None):
|
||
"""
|
||
Decorate a function to only be triggerable from a channel message.
|
||
|
||
If it is triggered in a private message, `message` will be said if given.
|
||
"""
|
||
def actual_decorator(function):
|
||
@functools.wraps(function)
|
||
def _nop(*args, **kwargs):
|
||
# Assign trigger and bot for easy access later
|
||
bot, trigger = args[0:2]
|
||
if not trigger.is_privmsg:
|
||
return function(*args, **kwargs)
|
||
else:
|
||
if message and not callable(message):
|
||
bot.say(message)
|
||
return _nop
|
||
# Hack to allow decorator without parens
|
||
if callable(message):
|
||
return actual_decorator(message)
|
||
return actual_decorator
|
||
|
||
|
||
def require_privilege(level, message=None):
|
||
"""
|
||
Decorate a function to require at least the given channel permission.
|
||
|
||
`level` can be one of the privilege levels defined in this module. If the
|
||
user does not have the privilege, `message` will be said if given. If it is
|
||
a private message, no checking will be done.
|
||
"""
|
||
def actual_decorator(function):
|
||
@functools.wraps(function)
|
||
def guarded(bot, trigger, *args, **kwargs):
|
||
# If this is a privmsg, ignore privilege requirements
|
||
if trigger.is_privmsg or trigger.admin:
|
||
return function(bot, trigger, *args, **kwargs)
|
||
channel_privs = bot.privileges[trigger.channel]
|
||
allowed = channel_privs.get(trigger.nick, 0) >= level
|
||
if not allowed:
|
||
if message and not callable(message):
|
||
bot.say(message)
|
||
else:
|
||
return function(bot, trigger, *args, **kwargs)
|
||
return guarded
|
||
return actual_decorator
|
||
|
||
|
||
def require_admin(message=None):
|
||
"""
|
||
Decorate a function to require the triggering user to be a bot admin.
|
||
|
||
If they are not, `message` will be said if given.
|
||
"""
|
||
def actual_decorator(function):
|
||
@functools.wraps(function)
|
||
def guarded(bot, trigger, *args, **kwargs):
|
||
if not trigger.admin:
|
||
if message and not callable(message):
|
||
bot.say(message)
|
||
else:
|
||
return function(bot, trigger, *args, **kwargs)
|
||
return guarded
|
||
# Hack to allow decorator without parens
|
||
if callable(message):
|
||
return actual_decorator(message)
|
||
return actual_decorator
|
||
|
||
|
||
def require_owner(message=None):
|
||
"""
|
||
Decorate a function to require the triggering user to be the bot owner.
|
||
|
||
If they are not, `message` will be said if given.
|
||
"""
|
||
def actual_decorator(function):
|
||
@functools.wraps(function)
|
||
def guarded(bot, trigger, *args, **kwargs):
|
||
if not trigger.owner:
|
||
if message and not callable(message):
|
||
bot.say(message)
|
||
else:
|
||
return function(bot, trigger, *args, **kwargs)
|
||
return guarded
|
||
# Hack to allow decorator without parens
|
||
if callable(message):
|
||
return actual_decorator(message)
|
||
return actual_decorator
|
||
|
||
|
||
def example(ex_input, ex_output=None):
|
||
"""
|
||
Decorate a function with an example input, and optionally a sample output.
|
||
|
||
Examples are added to the bot.doc dictionary with the function name as
|
||
the key alongside it's calling command. The 'commands' decorator should
|
||
be used with it.
|
||
"""
|
||
def add_attribute(function):
|
||
if not hasattr(function, "example"):
|
||
function.example = []
|
||
function.example.append((ex_input, ex_output))
|
||
return function
|
||
return add_attribute
|
||
|
||
|
||
def url_callback(url):
|
||
"""
|
||
Decore a function with a callback to the URL module.
|
||
|
||
This URL will be added to the bot.url_callbacks dictionary in the bot's
|
||
memory which the URL module will compare it's URL's against. If a key in
|
||
the bot.url_callbacks dict is found inside the gathered URL, this
|
||
function will be called instead.
|
||
"""
|
||
def add_attribute(function):
|
||
if not hasattr(function, "url_callback"):
|
||
function.url_callback = []
|
||
function.url_callback.append(url)
|
||
return function
|
||
return add_attribute
|