2020-08-25 17:07:27 +02:00
|
|
|
# =====IMPORTS=====
|
|
|
|
# Future imports
|
|
|
|
from __future__ import annotations
|
|
|
|
|
|
|
|
# Built-in imports
|
2020-08-08 09:19:09 +02:00
|
|
|
import shlex
|
2020-08-25 17:07:27 +02:00
|
|
|
|
|
|
|
# Third-party imports
|
2020-08-08 09:29:32 +02:00
|
|
|
import yaml
|
2020-08-25 17:07:27 +02:00
|
|
|
import discord
|
|
|
|
|
|
|
|
# Typing imports
|
|
|
|
from typing import TYPE_CHECKING, List
|
|
|
|
if TYPE_CHECKING:
|
|
|
|
# Own imports
|
|
|
|
from .module import Module
|
|
|
|
from discord import Message
|
2020-08-08 09:19:09 +02:00
|
|
|
|
|
|
|
|
|
|
|
class Frank(discord.Client):
|
2020-08-25 17:07:27 +02:00
|
|
|
"""
|
|
|
|
Main class of the bot; works by adding modules, which all define
|
|
|
|
their own behavior.
|
|
|
|
"""
|
|
|
|
|
2020-08-25 19:11:45 +02:00
|
|
|
def __init__(self, modules: List[Module], config_file: str = 'frank.yaml',
|
|
|
|
prefix: str = 'fr'):
|
2020-08-25 17:07:27 +02:00
|
|
|
"""
|
|
|
|
Args:
|
|
|
|
modules: modules to load
|
|
|
|
config_file: path to yaml config file; ignored if non-existent
|
2020-08-25 19:11:45 +02:00
|
|
|
prefix: prefix to activate Frank in the Discord server
|
2020-08-25 17:07:27 +02:00
|
|
|
"""
|
2020-08-08 09:19:09 +02:00
|
|
|
|
|
|
|
super().__init__()
|
|
|
|
self._modules = modules
|
|
|
|
self._loaded_modules = []
|
|
|
|
|
2020-08-25 19:11:45 +02:00
|
|
|
self.PREFIX = prefix
|
|
|
|
|
2020-08-08 09:29:32 +02:00
|
|
|
try:
|
2020-08-25 17:07:27 +02:00
|
|
|
with open(config_file, 'r') as f:
|
2020-08-08 13:12:09 +02:00
|
|
|
self._config = yaml.load(f, Loader=yaml.FullLoader)
|
2020-08-08 09:29:32 +02:00
|
|
|
|
|
|
|
except FileNotFoundError:
|
2020-08-08 13:12:09 +02:00
|
|
|
self._config = None
|
2020-08-08 09:29:32 +02:00
|
|
|
|
2020-08-08 09:19:09 +02:00
|
|
|
async def on_ready(self):
|
2020-08-26 14:54:55 +02:00
|
|
|
"""
|
|
|
|
Runs when the bot has succesfully connected to Discord
|
|
|
|
"""
|
2020-08-25 17:07:27 +02:00
|
|
|
|
|
|
|
print('Connected')
|
2020-08-08 09:19:09 +02:00
|
|
|
|
|
|
|
# Startup all modules
|
|
|
|
for module in self._modules:
|
2020-08-08 13:12:09 +02:00
|
|
|
if self._config and module.NAME in self._config:
|
|
|
|
loaded = module(self, config=self._config[module.NAME])
|
2020-08-08 09:29:32 +02:00
|
|
|
|
|
|
|
else:
|
|
|
|
loaded = module(self)
|
|
|
|
|
2020-08-25 17:07:27 +02:00
|
|
|
await loaded._start()
|
2020-08-08 09:19:09 +02:00
|
|
|
self._loaded_modules.append(loaded)
|
|
|
|
|
2020-08-25 17:07:27 +02:00
|
|
|
print('All modules loaded')
|
|
|
|
|
|
|
|
async def stop(self):
|
2020-08-26 14:54:55 +02:00
|
|
|
"""
|
|
|
|
Stop all module daemons and exit.
|
|
|
|
"""
|
2020-08-25 17:07:27 +02:00
|
|
|
|
|
|
|
for module in self._loaded_modules:
|
|
|
|
await module.stop()
|
2020-08-08 09:19:09 +02:00
|
|
|
|
2020-08-25 17:07:27 +02:00
|
|
|
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())
|
2020-08-08 09:19:09 +02:00
|
|
|
|
2020-08-25 17:07:27 +02:00
|
|
|
except ValueError:
|
|
|
|
return
|
2020-08-12 18:42:43 +02:00
|
|
|
|
2020-08-25 17:07:27 +02:00
|
|
|
if cmd and cmd[0] == self.PREFIX:
|
|
|
|
module = next((mod for mod in self._loaded_modules
|
|
|
|
if mod.match(cmd[1])), None)
|
2020-08-08 09:19:09 +02:00
|
|
|
|
|
|
|
if module:
|
2020-08-25 17:07:27 +02:00
|
|
|
await module(cmd=cmd[2:], author=message.author,
|
|
|
|
channel=message.channel, mid=message.id)
|