# =====IMPORTS===== # Future imports from __future__ import annotations # Built-in imports import shlex # Third-party imports import yaml import discord # Typing imports from typing import TYPE_CHECKING, List if TYPE_CHECKING: # Own imports from .module import Module from discord import Message class Frank(discord.Client): """ Main class of the bot; works by adding modules, which all define their own behavior. """ PREFIX = 'fr' """Prefix to use Frank inside Discord.""" def __init__(self, modules: List[Module], config_file: str = 'frank.yaml'): """ Args: modules: modules to load config_file: path to yaml config file; ignored if non-existent """ super().__init__() self._modules = modules self._loaded_modules = [] try: with open(config_file, 'r') as f: self._config = yaml.load(f, Loader=yaml.FullLoader) except FileNotFoundError: self._config = None async def on_ready(self): """Runs when the bot has succesfully connected to Discord""" print('Connected') # Startup all modules for module in self._modules: if self._config and module.NAME in self._config: loaded = module(self, config=self._config[module.NAME]) else: loaded = module(self) await loaded._start() self._loaded_modules.append(loaded) print('All modules loaded') async def stop(self): """Stop all module daemons and exit.""" for module in self._loaded_modules: await module.stop() async def on_message(self, message: Message): """ Runs when a new message is sent in the Discord channel. Args: message: object representing the received message; see https://discordpy.readthedocs.io/en/latest/api.html#message """ try: cmd = shlex.split(message.content.strip()) except ValueError: return if cmd and cmd[0] == self.PREFIX: module = next((mod for mod in self._loaded_modules if mod.match(cmd[1])), None) if module: await module(cmd=cmd[2:], author=message.author, channel=message.channel, mid=message.id)