This repository has been archived on 2021-03-28. You can view files and clone it, but cannot push or open issues/pull-requests.
frank/frank/module/module.py

126 lines
3.3 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)
if func:
# 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)
else:
raise InvalidCommand(f'Unknown command: {cmd}')
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
"""
if cls.PREFIX:
if isinstance(cls.PREFIX, list):
return prefix in cls.PREFIX
else:
return prefix == cls.PREFIX
else:
return False