146 lines
3.5 KiB
Python
146 lines
3.5 KiB
Python
# =====IMPORTS=====
|
|
# Future imports
|
|
from __future__ import annotations
|
|
|
|
# Built-in imports
|
|
import asyncio
|
|
|
|
# Own imports
|
|
from .exceptions import InvalidCommand
|
|
from .meta import ModuleMeta
|
|
from .decorators import RegexCommand
|
|
|
|
# Typing imports
|
|
from typing import TYPE_CHECKING
|
|
|
|
if TYPE_CHECKING:
|
|
# Built-in imports
|
|
from typing import List, Dict
|
|
|
|
# Third-party imports
|
|
from discord.abc import User, Messageable
|
|
|
|
# Own imports
|
|
from suzybot.frank import Frank
|
|
|
|
|
|
class Module(ModuleMeta):
|
|
"""
|
|
Base class for modules; all custom modules should inherit from this.
|
|
"""
|
|
|
|
PREFIX = []
|
|
"""
|
|
Prefix to activate this module.
|
|
"""
|
|
|
|
NAME = ""
|
|
"""
|
|
The name is used in various places, such as the config file and the
|
|
help function.
|
|
"""
|
|
|
|
HELP = ""
|
|
"""
|
|
Short description of the module to use in the help function.
|
|
"""
|
|
|
|
def __init__(self, client: Frank, config: Dict = None):
|
|
"""
|
|
Args:
|
|
client: client using this module; used to communicate.
|
|
config: dict containing the config for this module (Frank client
|
|
reads this from the config file).
|
|
"""
|
|
|
|
super().__init__()
|
|
|
|
self._client = client
|
|
self._config = config
|
|
|
|
self._tasks = []
|
|
|
|
def pre_start(self):
|
|
"""
|
|
Overwrite this function to run code (e.g. add variables...) before
|
|
starting the daemons.
|
|
"""
|
|
|
|
pass
|
|
|
|
async def _start(self):
|
|
"""Start up defined daemons for this module."""
|
|
|
|
self.pre_start()
|
|
|
|
for daemon in self.daemons: # pylint: disable=no-member
|
|
task = asyncio.create_task(daemon())
|
|
self._tasks.append(task)
|
|
|
|
async def stop(self):
|
|
"""
|
|
Stop all tasks for this module.
|
|
"""
|
|
|
|
for task in self._tasks:
|
|
task.cancel()
|
|
|
|
async def __call__(
|
|
self, cmd: List[str], author: User, channel: Messageable, mid: int
|
|
):
|
|
"""
|
|
Execute the command, if found.
|
|
|
|
Args:
|
|
cmd: list of command arguments; if empty, default command is used
|
|
author: author of message
|
|
channel: channel the message was sent in
|
|
mid: message id
|
|
"""
|
|
|
|
if cmd:
|
|
func = next(
|
|
(func for func in self.commands if func.match(cmd[0])), None
|
|
)
|
|
|
|
# Throw error if no function is found
|
|
if not func:
|
|
raise InvalidCommand(f"Unknown command: {cmd}")
|
|
|
|
# A RegexCommand can use the prefix, as it's not a fixed string
|
|
if isinstance(func, RegexCommand):
|
|
await func(
|
|
prefix=cmd[0],
|
|
cmd=cmd[1:],
|
|
author=author,
|
|
channel=channel,
|
|
mid=mid,
|
|
)
|
|
|
|
else:
|
|
await func(
|
|
cmd=cmd[1:], author=author, channel=channel, mid=mid
|
|
)
|
|
|
|
elif self.default:
|
|
await self.default(author=author, channel=channel, mid=mid)
|
|
|
|
@classmethod
|
|
def match(cls, prefix: str) -> bool:
|
|
"""
|
|
Checks wether the given prefix matches the module.
|
|
|
|
Args:
|
|
prefix: prefix to check
|
|
"""
|
|
|
|
# Always return False if there's no PREFIX defined
|
|
if not cls.PREFIX:
|
|
return False
|
|
|
|
if isinstance(cls.PREFIX, list):
|
|
return prefix in cls.PREFIX
|
|
|
|
else:
|
|
return prefix == cls.PREFIX
|