diff --git a/minearchy_bot/__init__.py b/minearchy_bot/__init__.py index bb67a43..b004a30 100644 --- a/minearchy_bot/__init__.py +++ b/minearchy_bot/__init__.py @@ -1 +1,101 @@ -from .core import * +from __future__ import annotations + +__all__ = ("MinearchyBot",) + +from asyncio import run as run_coro +from inspect import cleandoc as strip_doc +from itertools import chain as chain_iter +from pathlib import Path +from time import time as current_time +from traceback import format_exc as format_exit + +from aiohttp import ClientSession as AIOHTTPSession +from discord import AllowedMentions, Game, Intents, Webhook +from discord.ext.commands import ( + Bot as CommandsBot, + ExtensionFailed, + NoEntryPointError, + when_mentioned_or, +) + +from .minecraft_server import GeyserServer +from .util import override + + +class MinearchyBot(CommandsBot): + ready_timestamp: float + log_webhook: Webhook + + def __init__(self, *, token: str, webhook_url: str) -> None: + self.token = token + self.webhook_url = webhook_url + + self.server = GeyserServer( + java_ip="play.landsofminearchy.com", + bedrock_ip="bedrock.landsofminearchy.com", + ) + + super().__init__( + command_prefix=when_mentioned_or("="), + strip_after_prefix=True, + case_insensitive=True, + status=Game("on play.landsofminearchy.com"), + owner_ids={512640455834337290, 160087716757897216}, + allowed_mentions=AllowedMentions.none(), + max_messages=100, + intents=Intents( + guilds=True, + members=True, + messages=True, + message_content=True, + ), + help_attrs=dict( + brief="Sends help.", + help="Sends all the commands of the bot, or help of a specific command or module.", + ), + ) + + @override + async def on_ready(self) -> None: + print( + strip_doc( + f""" + Connected to Discord! + User: {self.user} + ID: {self.user.id} + """ + ) + ) + + self.ready_timestamp = current_time() + await self.log_webhook.send("Bot is now online!") + + async def load_extensions(self) -> None: + cogs = Path(__file__).parent / "cogs" + for file_name in chain_iter( + map( + lambda file_path: ".".join(file_path.relative_to(cogs.parent.parent).parts)[:-3], + cogs.rglob("*.py"), + ), + ("jishaku",), + ): + try: + await self.load_extension(file_name) + print(f"Loaded {file_name}") + except (ExtensionFailed, NoEntryPointError): + print(f"Couldn't load {file_name}:\n{format_exit()}") + + @override + def run(self) -> None: + async def runner() -> None: + async with self, AIOHTTPSession() as self.session: + self.log_webhook = Webhook.from_url( + self.webhook_url, session=self.session, bot_token=self.token + ) + await self.load_extensions() + await self.start(self.token, reconnect=True) + + try: + run_coro(runner()) + except KeyboardInterrupt: + pass diff --git a/minearchy_bot/cogs/error_handler.py b/minearchy_bot/cogs/error_handler.py index 39b5911..652e3db 100644 --- a/minearchy_bot/cogs/error_handler.py +++ b/minearchy_bot/cogs/error_handler.py @@ -3,6 +3,7 @@ from __future__ import annotations from contextlib import suppress as suppress_error from traceback import format_exception as format_exit from typing import TYPE_CHECKING +from asyncio import gather as await_parallel from discord import HTTPException from discord.ext.commands import ( @@ -19,7 +20,7 @@ from discord.ext.commands import ( if TYPE_CHECKING: from discord.ext.commands import CommandError, Context - from ..core import MinearchyBot + from .. import MinearchyBot class ErrorHandler(Cog): @@ -62,7 +63,12 @@ class ErrorHandler(Cog): else: trace = "".join(format_exit(type(error), error, error.__traceback__)) print(f"Ignoring exception in command {ctx.command}:\n{trace}") - await self.bot.log_webhook.send(f"<@512640455834337290>```{trace}```") + await await_parallel( + self.bot.log_webhook.send(f"<@512640455834337290>```{trace}```"), + ctx.reply( + "An error occurred while executing the command. The error has been reported." + ), + ) async def setup(bot: MinearchyBot) -> None: diff --git a/minearchy_bot/cogs/minecraft_server.py b/minearchy_bot/cogs/minecraft_server.py index 24b03c4..e69a5f4 100644 --- a/minearchy_bot/cogs/minecraft_server.py +++ b/minearchy_bot/cogs/minecraft_server.py @@ -8,7 +8,7 @@ from discord.ui import Button, View if TYPE_CHECKING: from discord.ext.commands import Context - from ..core import MinearchyBot + from .. import MinearchyBot class MinecraftServer( diff --git a/minearchy_bot/cogs/misc.py b/minearchy_bot/cogs/misc.py index ea4f9c4..015e035 100644 --- a/minearchy_bot/cogs/misc.py +++ b/minearchy_bot/cogs/misc.py @@ -17,7 +17,7 @@ if TYPE_CHECKING: from discord import Message from discord.ext.commands import Context - from ..core import MinearchyBot + from .. import MinearchyBot class Miscellaneous( @@ -72,7 +72,8 @@ class Miscellaneous( self.sniped[message.channel.id].appendleft((message, int(current_time()))) - self.sniped[message.channel.id] = self.sniped[message.channel.id][:5] # type: ignore + while len(self.sniped[message.channel.id]) > 5: + self.sniped[message.channel.id].pop() @command( brief="Sends the latest deleted messages.", @@ -82,7 +83,7 @@ class Miscellaneous( ), ) @has_permissions(manage_messages=True) # needs to be able to delete messages to run the command - async def snipe(self, ctx: Context, channel: TextChannel = None) -> None: # type: ignore + async def snipe(self, ctx: Context, channel: TextChannel = None) -> None: if channel is None: channel = ctx.channel @@ -113,13 +114,13 @@ class Miscellaneous( name=str(i) + ("" if i else " (latest)"), value=strip_doc( f""" - Author: {message.author.mention} (ID: {message.author.id}, Plain: {escape_markdown(str(message.author))}) - Deleted at: (Relative: ) - Content: - ``` - {message.content.replace('`', f'{zwsp}`{zwsp}')} - ``` - """ + Author: {message.author.mention} (ID: {message.author.id}, Plain: {escape_markdown(str(message.author))}) + Deleted at: (Relative: ) + Content: + ``` + {message.content.replace('`', f'{zwsp}`{zwsp}')} + ``` + """ ), inline=False, ) diff --git a/minearchy_bot/cogs/moderation.py b/minearchy_bot/cogs/moderation.py index a75ea23..7b790b4 100644 --- a/minearchy_bot/cogs/moderation.py +++ b/minearchy_bot/cogs/moderation.py @@ -7,7 +7,7 @@ from discord import Member from discord.ext.commands import Cog, Context, command, has_permissions if TYPE_CHECKING: - from ..core import MinearchyBot + from .. import MinearchyBot class Moderation(Cog): diff --git a/minearchy_bot/core/__init__.py b/minearchy_bot/core/__init__.py deleted file mode 100644 index fcb7aaf..0000000 --- a/minearchy_bot/core/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from .minearchy_bot import * diff --git a/minearchy_bot/core/minearchy_bot.py b/minearchy_bot/core/minearchy_bot.py deleted file mode 100644 index 06f2096..0000000 --- a/minearchy_bot/core/minearchy_bot.py +++ /dev/null @@ -1,101 +0,0 @@ -from __future__ import annotations - -__all__ = ("MinearchyBot",) - -from asyncio import run as run_coro -from inspect import cleandoc as strip_doc -from itertools import chain as chain_iter -from pathlib import Path -from time import time as current_time -from traceback import format_exc as format_exit - -from aiohttp import ClientSession as AIOHTTPSession -from discord import AllowedMentions, Game, Intents, Webhook -from discord.ext.commands import ( - Bot as CommandsBot, - ExtensionFailed, - NoEntryPointError, - when_mentioned_or, -) - -from ..minecraft_server import GeyserServer -from ..util import override - - -class MinearchyBot(CommandsBot): - ready_timestamp: float - log_webhook: Webhook - - def __init__(self, *, token: str, webhook_url: str) -> None: - self.token = token - self.webhook_url = webhook_url - - self.server = GeyserServer( - java_ip="play.landsofminearchy.com", - bedrock_ip="bedrock.landsofminearchy.com", - ) - - super().__init__( - command_prefix=when_mentioned_or("="), - strip_after_prefix=True, - case_insensitive=True, - status=Game("on play.landsofminearchy.com"), - owner_ids={512640455834337290, 160087716757897216}, - allowed_mentions=AllowedMentions.none(), - max_messages=100, - intents=Intents( - guilds=True, - members=True, - messages=True, - message_content=True, - ), - help_attrs=dict( - brief="Sends help.", - help="Sends all the commands of the bot, or help of a specific command or module.", - ), - ) - - @override - async def on_ready(self) -> None: - print( - strip_doc( - f""" - Connected to Discord! - User: {self.user} - ID: {self.user.id} - """ - ) - ) - - self.ready_timestamp = current_time() - await self.log_webhook.send("Bot is now online!") - - async def load_extensions(self) -> None: - cogs = Path(__file__).parent.parent / "cogs" - for file_name in chain_iter( - map( - lambda file_path: ".".join(file_path.relative_to(cogs.parent.parent).parts)[:-3], - cogs.rglob("*.py"), - ), - ("jishaku",), - ): - try: - await self.load_extension(file_name) - print(f"Loaded {file_name}") - except (ExtensionFailed, NoEntryPointError): - print(f"Couldn't load {file_name}:\n{format_exit()}") - - @override - def run(self) -> None: - async def runner() -> None: - async with self, AIOHTTPSession() as self.session: - self.log_webhook = Webhook.from_url( - self.webhook_url, session=self.session, bot_token=self.token - ) - await self.load_extensions() - await self.start(self.token, reconnect=True) - - try: - run_coro(runner()) - except KeyboardInterrupt: - pass