""" Contains the Gwendolyn class, a subclass of the discord command bot. *Classes* --------- Gwendolyn(discord.ext.commands.Bot) """ import os # Used for loading cogs in Gwendolyn.addCogs import finnhub # Used to add a finhub client to the bot import discord # Used for discord.Intents and discord.Status import discord_slash # Used to initialized SlashCommands object import git # Used to pull when stopping from discord.ext import commands # Used to inherit from commands.bot from pymongo import MongoClient # Used for database management from gwendolyn.funcs import Money, StarWars, Games, Other, LookupFuncs from gwendolyn.utils import (get_options, get_credentials, log_this, DatabaseFuncs, EventHandler, ErrorHandler, long_strings) class Gwendolyn(commands.Bot): """ A multifunctional Discord bot. *Methods* --------- log(messages: Union[str, list], channel: str = "", level: int = 20) stop(ctx: discord_slash.context.SlashContext) defer(ctx: discord_slash.context.SlashContext) """ # pylint: disable=too-many-instance-attributes def __init__(self): """Initialize the bot.""" intents = discord.Intents.default() intents.members = True initiation_parameters = { "command_prefix": " ", "case_insensitive": True, "intents": intents, "status": discord.Status.dnd } super().__init__(**initiation_parameters) self._add_clients_and_options() self._add_util_classes() self._add_function_containers() self._add_cogs() def _add_clients_and_options(self): """Add all the client, option and credentials objects.""" self.long_strings = long_strings() self.options = get_options() self.credentials = get_credentials() finnhub_key = self.credentials["finnhub_key"] self.finnhub_client = finnhub.Client(api_key=finnhub_key) mongo_user = self.credentials["mongo_db_user"] mongo_password = self.credentials["mongo_db_password"] mongo_url = f"mongodb+srv://{mongo_user}:{mongo_password}@gwendolyn" mongo_url += ".qkwfy.mongodb.net/Gwendolyn?retryWrites=true&w=majority" database_clint = MongoClient(mongo_url) if self.options["testing"]: self.log("Testing mode") self.database = database_clint["Gwendolyn-Test"] else: self.database = database_clint["Gwendolyn"] def _add_util_classes(self): """Add all the classes used as utility.""" self.database_funcs = DatabaseFuncs(self) self.event_handler = EventHandler(self) self.error_handler = ErrorHandler(self) slash_parameters = { "sync_commands": True, "sync_on_cog_reload": True, "override_type": True } self.slash = discord_slash.SlashCommand(self, **slash_parameters) def _add_function_containers(self): """Add all the function containers used for commands.""" self.star_wars = StarWars(self) self.other = Other(self) self.lookup_funcs = LookupFuncs(self) self.games = Games(self) self.money = Money(self) def _add_cogs(self): """Load cogs.""" for filename in os.listdir("./gwendolyn/cogs"): if filename.endswith(".py"): self.load_extension(f"gwendolyn.cogs.{filename[:-3]}") def log(self, messages, channel: str = "", level: int = 20): # pylint:disable=no-self-use """Log a message. Described in utils/util_functions.py.""" log_this(messages, channel, level) async def stop(self, ctx: discord_slash.context.SlashContext): """ Stop the bot, and stop running games. Only stops the bot if the user in ctx.author is one of the admins given in options.txt. *parameters* ------------ ctx: discord_slash.context.SlashContext The context of the "/stop" slash command. """ if f"#{ctx.author.id}" in self.options["admins"]: await ctx.send("Pulling git repo and restarting...") await self.change_presence(status=discord.Status.offline) self.database_funcs.wipe_games() if not self.options["testing"]: git_client = git.cmd.Git("") git_client.pull() self.log("Logging out", level=25) await self.close() else: log_message = f"{ctx.author.display_name} tried to stop me!" self.log(log_message, str(ctx.channel_id)) await ctx.send(f"I don't think I will, {ctx.author.display_name}") async def defer(self, ctx: discord_slash.context.SlashContext): """Send a "Gwendolyn is thinking" message to the user.""" try: await ctx.defer() except discord_slash.error.AlreadyResponded: self.log("defer failed")