diff --git a/.docs/aurora/index.md b/.docs/aurora/index.md index 48905f1..f6e5527 100644 --- a/.docs/aurora/index.md +++ b/.docs/aurora/index.md @@ -10,7 +10,7 @@ Aurora is a fully-featured moderation system. It is heavily inspired by Galactic ## Installation ```bash -[p]repo add seacogs https://coastalcommits.com/SeaswimmerTheFsh/SeaCogs +[p]repo add seacogs https://www.coastalcommits.com/SeaswimmerTheFsh/SeaCogs [p]cog install seacogs aurora [p]cog load aurora ``` diff --git a/.docs/backup.md b/.docs/backup.md index 550f7da..238ba8e 100644 --- a/.docs/backup.md +++ b/.docs/backup.md @@ -5,7 +5,7 @@ Backup allows you to export a JSON list of all of your installed repositories an ## Installation ```bash -[p]repo add seacogs https://coastalcommits.com/SeaswimmerTheFsh/SeaCogs +[p]repo add seacogs https://www.coastalcommits.com/SeaswimmerTheFsh/SeaCogs [p]cog install seacogs backup [p]cog load backup ``` diff --git a/.docs/bible.md b/.docs/bible.md index 2590187..b748de2 100644 --- a/.docs/bible.md +++ b/.docs/bible.md @@ -6,7 +6,7 @@ This cog does require an api key to work. ## Installation ```bash -[p]repo add seacogs https://coastalcommits.com/SeaswimmerTheFsh/SeaCogs +[p]repo add seacogs https://www.coastalcommits.com/SeaswimmerTheFsh/SeaCogs [p]cog install seacogs bible [p]cog load bible ``` diff --git a/.docs/nerdify.md b/.docs/nerdify.md index b7fe478..c1c50a9 100644 --- a/.docs/nerdify.md +++ b/.docs/nerdify.md @@ -5,7 +5,7 @@ Nerdify allows you to nerdify other people's text. ## Installation ```bash -[p]repo add seacogs https://coastalcommits.com/SeaswimmerTheFsh/SeaCogs +[p]repo add seacogs https://www.coastalcommits.com/SeaswimmerTheFsh/SeaCogs [p]cog install seacogs nerdify [p]cog load nerdify ``` diff --git a/.docs/pterodactyl/configuration.md b/.docs/pterodactyl/configuration.md index e37698f..87349bd 100644 --- a/.docs/pterodactyl/configuration.md +++ b/.docs/pterodactyl/configuration.md @@ -31,11 +31,12 @@ Available placeholders: - `.$M` - replaced with message content - `.$N` - replaced with author's display name (or guild nickname, if set) - `.$U` - replaced with the author's username (NOT display name, you should usually use `.$N`) +- `.$V` - replaced with the configured invite link Default value: ```json -tellraw @a ["",{"text":".$N ","color":".$C"},{"text":" (DISCORD): ","color":"blue"},{"text":".$M","color":"white"}] +tellraw @a ["",{"text":".$N ","color":".$C","insertion":"<@.$I>","hoverEvent":{"action":"show_text","contents":"Shift click to mention this user inside Discord"}},{"text":"(DISCORD):","color":"blue","clickEvent":{"action":"open_url","value":".$V"},"hoverEvent":{"action":"show_text","contents":"Click to join the Discord Server"}},{"text":" .$M","color":"white"}] ``` ## `consolechannel` @@ -62,6 +63,12 @@ This is to prevent the console channel from flooding and getting backed up by Di Default value: `None` +## `invite` + +This option determines what url the chat command will substitute in for the Discord invite placeholder. + +Default value: `None` + ## `ip` This option determines whether or not IP's will be redacted when posted in chat or to the console channel. diff --git a/.docs/pterodactyl/index.md b/.docs/pterodactyl/index.md index 748a465..bd3455e 100644 --- a/.docs/pterodactyl/index.md +++ b/.docs/pterodactyl/index.md @@ -10,7 +10,7 @@ Pterodactyl allows for connecting to a Pterodactyl server through websockets. It ## Installation ```bash -[p]repo add seacogs https://coastalcommits.com/SeaswimmerTheFsh/SeaCogs +[p]repo add seacogs https://www.coastalcommits.com/SeaswimmerTheFsh/SeaCogs [p]cog install seacogs pterodactyl [p]cog load aurora ``` diff --git a/aurora/aurora.py b/aurora/aurora.py index 3053484..1f946f4 100644 --- a/aurora/aurora.py +++ b/aurora/aurora.py @@ -19,7 +19,8 @@ from pytimeparse2 import disable_dateutil, parse from redbot.core import app_commands, commands, data_manager from redbot.core.app_commands import Choice from redbot.core.bot import Red -from redbot.core.utils.chat_formatting import box, error, warning +from redbot.core.utils.chat_formatting import (box, error, humanize_list, + warning) from aurora.importers.aurora import ImportAuroraView from aurora.importers.galacticbot import ImportGalacticBotView @@ -46,8 +47,8 @@ class Aurora(commands.Cog): It is heavily inspired by GalacticBot, and is designed to be a more user-friendly alternative to Red's core Mod cogs. This cog stores all of its data in an SQLite database.""" - __author__ = "SeaswimmerTheFsh" - __version__ = "2.0.5" + __author__ = ["SeaswimmerTheFsh"] + __version__ = "2.0.6" async def red_delete_data_for_user(self, *, requester, user_id: int): if requester == "discord_deleted_user": @@ -86,6 +87,16 @@ class Aurora(commands.Cog): disable_dateutil() self.handle_expiry.start() + def format_help_for_context(self, ctx: commands.Context) -> str: + pre_processed = super().format_help_for_context(ctx) or "" + n = "\n" if "\n\n" not in pre_processed else "" + text = [ + f"{pre_processed}{n}", + f"Cog Version: **{self.__version__}**", + f"Author: {humanize_list(self.__author__)}", + ] + return "\n".join(text) + async def cog_load(self): """This method prepares the database schema for all of the guilds the bot is currently in.""" guilds: list[discord.Guild] = self.bot.guilds diff --git a/aurora/utilities/logger.py b/aurora/utilities/logger.py index 84a7836..9ea3c2f 100644 --- a/aurora/utilities/logger.py +++ b/aurora/utilities/logger.py @@ -1,3 +1,3 @@ -import logging +from red_commons.logging import getLogger -logger = logging.getLogger("red.sea.aurora") +logger = getLogger("red.seacogs.aurora") diff --git a/backup/backup.py b/backup/backup.py index b973776..cde40fa 100644 --- a/backup/backup.py +++ b/backup/backup.py @@ -7,28 +7,38 @@ import contextlib import json -import logging import re +from red_commons.logging import getLogger from redbot.cogs.downloader import errors from redbot.cogs.downloader.converters import InstalledCog from redbot.core import commands from redbot.core.bot import Red -from redbot.core.utils.chat_formatting import error, text_to_file - +from redbot.core.utils.chat_formatting import (error, humanize_list, + text_to_file) # pylint: disable=protected-access class Backup(commands.Cog): """A utility to make reinstalling repositories and cogs after migrating the bot far easier.""" - __author__ = "SeaswimmerTheFsh" - __version__ = "1.0.0" + __author__ = ["SeaswimmerTheFsh"] + __version__ = "1.0.1" def __init__(self, bot: Red): super().__init__() self.bot = bot - self.logger = logging.getLogger("red.sea.backup") + self.logger = getLogger("red.seacogs.backup") + + def format_help_for_context(self, ctx: commands.Context) -> str: + pre_processed = super().format_help_for_context(ctx) or "" + n = "\n" if "\n\n" not in pre_processed else "" + text = [ + f"{pre_processed}{n}", + f"Cog Version: **{self.__version__}**", + f"Author: {humanize_list(self.__author__)}", + ] + return "\n".join(text) @commands.group(autohelp=True) @commands.is_owner() @@ -87,8 +97,11 @@ class Backup(commands.Cog): try: export = json.loads(await ctx.message.attachments[0].read()) except (json.JSONDecodeError, IndexError): - await ctx.send(error("Please provide a valid JSON export file.")) - return + try: + export = json.loads(await ctx.message.reference.resolved.attachments[0].read()) + except (json.JSONDecodeError, IndexError): + await ctx.send(error("Please provide a valid JSON export file.")) + return downloader = ctx.bot.get_cog("Downloader") if downloader is None: diff --git a/backup/info.json b/backup/info.json index fb779da..fccea3e 100644 --- a/backup/info.json +++ b/backup/info.json @@ -7,9 +7,9 @@ "end_user_data_statement" : "This cog does not store end user data.", "hidden": false, "disabled": false, - "min_bot_version": "3.5.5", + "min_bot_version": "3.5.0", "max_bot_version": "3.5.5", - "min_python_version": [3, 10, 0], + "min_python_version": [3, 9, 0], "tags": [ "utility", "backup", diff --git a/bible/bible.py b/bible/bible.py index dc809ca..5b7305f 100644 --- a/bible/bible.py +++ b/bible/bible.py @@ -5,14 +5,14 @@ # ____) | __/ (_| \__ \\ V V /| | | | | | | | | | | | __/ | # |_____/ \___|\__,_|___/ \_/\_/ |_|_| |_| |_|_| |_| |_|\___|_| -import logging import random import aiohttp from discord import Embed +from red_commons.logging import getLogger from redbot.core import Config, commands from redbot.core.bot import Red -from redbot.core.utils.chat_formatting import error +from redbot.core.utils.chat_formatting import error, humanize_list import bible.errors from bible.models import Version @@ -21,8 +21,8 @@ from bible.models import Version class Bible(commands.Cog): """Retrieve Bible verses from the API.bible API.""" - __author__ = "SeaswimmerTheFsh" - __version__ = "1.0.0" + __author__ = ["SeaswimmerTheFsh"] + __version__ = "1.0.1" def __init__(self, bot: Red): super().__init__() @@ -31,10 +31,20 @@ class Bible(commands.Cog): self.config = Config.get_conf( self, identifier=481923957134912, force_registration=True ) - self.logger = logging.getLogger("red.sea.bible") + self.logger = getLogger("red.seacogs.bible") self.config.register_global(bible="de4e12af7f28f599-02") self.config.register_user(bible=None) + def format_help_for_context(self, ctx: commands.Context) -> str: + pre_processed = super().format_help_for_context(ctx) or "" + n = "\n" if "\n\n" not in pre_processed else "" + text = [ + f"{pre_processed}{n}", + f"Cog Version: **{self.__version__}**", + f"Author: {humanize_list(self.__author__)}", + ] + return "\n".join(text) + async def translate_book_name(self, bible_id: str, book_name: str) -> str: """Translate a book name to a book ID.""" book_name_list = [ diff --git a/nerdify/nerdify.py b/nerdify/nerdify.py index 333be8e..dc97886 100644 --- a/nerdify/nerdify.py +++ b/nerdify/nerdify.py @@ -17,12 +17,22 @@ from redbot.core.utils import chat_formatting, common_filters class Nerdify(commands.Cog): """Nerdify your text.""" - __author__ = "SeaswimmerTheFsh" - __version__ = "1.3.2" + __author__ = ["SeaswimmerTheFsh"] + __version__ = "1.3.3" def __init__(self, bot): self.bot = bot + def format_help_for_context(self, ctx: commands.Context) -> str: + pre_processed = super().format_help_for_context(ctx) or "" + n = "\n" if "\n\n" not in pre_processed else "" + text = [ + f"{pre_processed}{n}", + f"Cog Version: **{self.__version__}**", + f"Author: {chat_formatting.humanize_list(self.__author__)}", + ] + return "\n".join(text) + @commands.command(aliases=["nerd"]) async def nerdify( self, ctx: commands.Context, *, text: Optional[str] = None diff --git a/pterodactyl/config.py b/pterodactyl/config.py index a8ad138..770c631 100644 --- a/pterodactyl/config.py +++ b/pterodactyl/config.py @@ -13,7 +13,7 @@ def register_config(config_obj: Config) -> None: join_regex=r"^\[\d{2}:\d{2}:\d{2} INFO\]: ([^<\n]+) joined the game$", leave_regex=r"^\[\d{2}:\d{2}:\d{2} INFO\]: ([^<\n]+) left the game$", achievement_regex=r"^\[\d{2}:\d{2}:\d{2} INFO\]: (.*) has (made the advancement|completed the challenge) \[(.*)\]$", - chat_command='tellraw @a ["",{"text":".$N ","color":".$C"},{"text":" (DISCORD): ","color":"blue"},{"text":".$M","color":"white"}]', + chat_command='tellraw @a ["",{"text":".$N ","color":".$C","insertion":"<@.$I>","hoverEvent":{"action":"show_text","contents":"Shift click to mention this user inside Discord"}},{"text":"(DISCORD):","color":"blue","clickEvent":{"action":"open_url","value":".$V"},"hoverEvent":{"action":"show_text","contents":"Click to join the Discord Server"}},{"text":" .$M","color":"white"}]', # noqa: E501 api_endpoint="minecraft", chat_channel=None, startup_msg='Server started!', @@ -21,5 +21,6 @@ def register_config(config_obj: Config) -> None: join_msg='Welcome to the server! 👋', leave_msg='Goodbye! 👋', mask_ip=True, + invite=None, regex_blacklist={}, ) diff --git a/pterodactyl/info.json b/pterodactyl/info.json index 8d8d875..0a10241 100644 --- a/pterodactyl/info.json +++ b/pterodactyl/info.json @@ -9,7 +9,7 @@ "disabled": false, "min_bot_version": "3.5.0", "min_python_version": [3, 8, 0], - "requirements": ["py-dactyl", "websockets"], + "requirements": ["git+https://github.com/SeaswimmerTheFsh/pydactyl", "websockets"], "tags": [ "pterodactyl", "minecraft", diff --git a/pterodactyl/logger.py b/pterodactyl/logger.py index a1781ab..482fefc 100644 --- a/pterodactyl/logger.py +++ b/pterodactyl/logger.py @@ -1,3 +1,4 @@ -import logging +from red_commons.logging import getLogger -logger = logging.getLogger('red.sea.pterodactyl') +logger = getLogger('red.seacogs.pterodactyl') +websocket_logger = getLogger('red.seacogs.pterodactyl.websocket') diff --git a/pterodactyl/pterodactyl.py b/pterodactyl/pterodactyl.py index de017f4..ba8c80a 100644 --- a/pterodactyl/pterodactyl.py +++ b/pterodactyl/pterodactyl.py @@ -1,13 +1,14 @@ import asyncio import json -from typing import Mapping, Optional +from typing import Mapping, Optional, Union import discord import websockets from pydactyl import PterodactylClient -from redbot.core import commands +from redbot.core import app_commands, commands +from redbot.core.app_commands import Choice from redbot.core.bot import Red -from redbot.core.utils.chat_formatting import box +from redbot.core.utils.chat_formatting import box, error from redbot.core.utils.views import ConfirmView from pterodactyl.config import config, register_config @@ -91,11 +92,85 @@ class Pterodactyl(commands.Cog): "M": message.content.replace('"',''), "N": message.author.display_name, "U": message.author.name, + "V": await config.invite() or "use [p]pterodactyl config invite to change me", } for key, value in placeholders.items(): command = command.replace('.$' + key, value) return command + async def power(self, ctx: Union[discord.Interaction, commands.Context], action: str, action_ing: str, warning: str = '') -> None: + if isinstance(ctx, discord.Interaction): + author = ctx.user + else: + author = ctx.author + + current_status = await config.current_status() + + if current_status == action_ing: + if isinstance(ctx, discord.Interaction): + return await ctx.response.send_message(f"Server is already {action_ing}.", ephemeral=True) + return await ctx.send(f"Server is already {action_ing}.") + + if current_status in ["starting", "stopping"]: + if isinstance(ctx, discord.Interaction): + return await ctx.response.send_message("Another power action is already in progress.", ephemeral=True) + return await ctx.send("Another power action is already in progress.") + + view = ConfirmView(author, disable_buttons=True) + + if isinstance(ctx, discord.Interaction): + await ctx.response.send_message(f"{warning}Are you sure you want to {action} the server?", view=view) + else: + message = await ctx.send(f"{warning}Are you sure you want to {action} the server?", view=view) + + await view.wait() + + if view.result is True: + if isinstance(ctx, discord.Interaction): + await ctx.edit_original_response(content=f"Sending websocket command to {action} server...", view=None) + else: + await message.edit(content=f"Sending websocket command to {action} server...", view=None) + + await self.websocket.send(json.dumps({"event": "set state", "args": [action]})) + + if isinstance(ctx, discord.Interaction): + await ctx.edit_original_response(content=f"Server {action_ing}", view=None) + else: + await message.edit(content=f"Server {action_ing}", view=None) + + else: + if isinstance(ctx, discord.Interaction): + await ctx.edit_original_response(content="Cancelled.", view=None) + else: + await message.edit(content="Cancelled.", view=None) + + async def send_command(self, ctx: Union[discord.Interaction, commands.Context], command: str): + channel = self.bot.get_channel(await config.console_channel()) + if isinstance(ctx, discord.Interaction): + if channel: + await channel.send(f"Received console command from {ctx.user.id}: {command[:1900]}") + try: + await self.websocket.send(json.dumps({"event": "send command", "args": [command]})) + await ctx.response.send_message(f"Command sent to server. {box(command, 'json')}", ephemeral=True) + except websockets.exceptions.ConnectionClosed as e: + logger.error("WebSocket connection closed: %s", e) + await ctx.response.send_message(error("WebSocket connection closed.")) + self.task.cancel() + self.retry_counter = 0 + self.task = self.get_task() + else: + if channel: + await channel.send(f"Received console command from {ctx.author.id}: {command[:1900]}") + try: + await self.websocket.send(json.dumps({"event": "send command", "args": [command]})) + await ctx.send(f"Command sent to server. {box(command, 'json')}") + except websockets.exceptions.ConnectionClosed as e: + logger.error("WebSocket connection closed: %s", e) + await ctx.send(error("WebSocket connection closed.")) + self.task.cancel() + self.retry_counter = 0 + self.task = self.get_task() + @commands.Cog.listener() async def on_red_api_tokens_update(self, service_name: str, api_tokens: Mapping[str,str]): # pylint: disable=unused-argument if service_name == "pterodactyl": @@ -104,48 +179,70 @@ class Pterodactyl(commands.Cog): self.retry_counter = 0 self.task = self.get_task() + slash_pterodactyl = app_commands.Group(name="pterodactyl", description="Pterodactyl allows you to manage your Pterodactyl Panel from Discord.") + + @slash_pterodactyl.command(name = "command", description = "Send a command to the server console.") + async def slash_pterodactyl_command(self, interaction: discord.Interaction, command: str) -> None: + """Send a command to the server console. + + Parameters: + ----------- + command: str + The command to send to the server.""" + return await self.send_command(interaction, command) + + @slash_pterodactyl.command(name = "power", description = "Send power actions to the server.") + @app_commands.choices(action=[ + Choice(name="Start", value="start"), + Choice(name="Stop", value="stop"), + Choice(name="Restart", value="restart"), + Choice(name="⚠️ Kill ⚠️", value="kill") + ]) + async def slash_pterodactyl_power(self, interaction: discord.Interaction, action: app_commands.Choice[str]) -> None: + """Send power actions to the server. + + Parameters: + ----------- + action: app_commands.Choice[str] + The action to perform on the server.""" + if action.value == "kill": + return await self.power(interaction, action.value, "stopping... (forcefully killed)", warning="**⚠️ Forcefully killing the server process can corrupt data in some cases. ⚠️**\n") + return await self.power(interaction, action.value, f"{action.value}ing...") + @commands.group(autohelp = True, name = "pterodactyl", aliases = ["ptero"]) async def pterodactyl(self, ctx: commands.Context) -> None: """Pterodactyl allows you to manage your Pterodactyl Panel from Discord.""" + @pterodactyl.command(name = "command", aliases = ["cmd", "execute", "exec"]) + @commands.admin() + async def pterodactyl_command(self, ctx: commands.Context, *, command: str) -> None: + """Send a command to the server console.""" + return await self.send_command(ctx, command) + @pterodactyl.group(autohelp = True, name = "power") @commands.admin() async def pterodactyl_power(self, ctx: commands.Context) -> None: """Send power actions to the server.""" @pterodactyl_power.command(name = "start") - async def pterodactyl_power_start(self, ctx: commands.Context) -> None: + async def pterodactyl_power_start(self, ctx: commands.Context) -> Optional[discord.Message]: """Start the server.""" - current_status = await config.current_status() - if current_status == "running": - return await ctx.send("Server is already running.") - if current_status in ["starting", "stopping"]: - return await ctx.send("Another power action is already in progress.") - message = await ctx.send("Sending websocket command to start server...") - await self.websocket.send(json.dumps({"event": "set state", "args": ["start"]})) - await message.edit(content="Server starting...") + return await self.power(ctx, "start", "starting...") @pterodactyl_power.command(name = "stop") - async def pterodactyl_power_stop(self, ctx: commands.Context) -> None: + async def pterodactyl_power_stop(self, ctx: commands.Context) -> Optional[discord.Message]: """Stop the server.""" - current_status = await config.current_status() - if current_status == "stopped": - return await ctx.send("Server is already stopped.") - if current_status in ["starting", "stopping"]: - return await ctx.send("Another power action is already in progress.") - message = await ctx.send("Sending websocket command to stop server...") - await self.websocket.send(json.dumps({"event": "set state", "args": ["stop"]})) - await message.edit(content="Server stopping...") + return await self.power(ctx, "stop", "stopping...") @pterodactyl_power.command(name = "restart") - async def pterodactyl_power_restart(self, ctx: commands.Context) -> None: + async def pterodactyl_power_restart(self, ctx: commands.Context) -> Optional[discord.Message]: """Restart the server.""" - current_status = await config.current_status() - if current_status in ["starting", "stopping"]: - return await ctx.send("Another power action is already in progress.") - message = await ctx.send("Sending websocket command to restart server...") - await self.websocket.send(json.dumps({"event": "set state", "args": ["restart"]})) - await message.edit(content="Server restarting...") + return await self.power(ctx, "restart", "restarting...") + + @pterodactyl_power.command(name = "kill") + async def pterodactyl_power_kill(self, ctx: commands.Context) -> Optional[discord.Message]: + """Kill the server.""" + return await self.power(ctx, "kill", "stopping... (forcefully killed)", warning="**⚠️ Forcefully killing the server process can corrupt data in some cases. ⚠️**\n") @pterodactyl.group(autohelp = True, name = "config", aliases = ["settings", "set"]) @commands.is_owner() @@ -181,6 +278,12 @@ class Pterodactyl(commands.Cog): await config.console_channel.set(channel.id) await ctx.send(f"Console channel set to {channel.mention}") + @pterodactyl_config.command(name = "invite") + async def pterodactyl_config_invite(self, ctx: commands.Context, invite: str) -> None: + """Set the invite link for your server.""" + await config.invite.set(invite) + await ctx.send(f"Invite link set to {invite}") + @pterodactyl_config.group(name = "chat") async def pterodactyl_config_chat(self, ctx: commands.Context): """Configure chat settings.""" @@ -287,7 +390,7 @@ class Pterodactyl(commands.Cog): await config.api_endpoint.set(endpoint) await ctx.send(f"API endpoint set to {endpoint}") - @pterodactyl_config_regex.group(name = "blacklist", aliases = ['block', 'blocklist']) + @pterodactyl_config_regex.group(name = "blacklist", aliases = ['block', 'blocklist'],) async def pterodactyl_config_regex_blacklist(self, ctx: commands.Context): """Blacklist regex patterns.""" @@ -345,6 +448,7 @@ class Pterodactyl(commands.Cog): leave_msg = await config.leave_msg() mask_ip = await config.mask_ip() api_endpoint = await config.api_endpoint() + invite = await config.invite() regex_blacklist: dict = await config.regex_blacklist() embed = discord.Embed(color = await ctx.embed_color(), title="Pterodactyl Configuration") embed.description = f"""**Base URL:** {base_url} @@ -357,6 +461,7 @@ class Pterodactyl(commands.Cog): **Leave Message:** {leave_msg} **Mask IP:** {self.get_bool_str(mask_ip)} **API Endpoint:** `{api_endpoint}` + **Invite:** {invite} **Chat Command:** {box(chat_command, 'json')} **Chat Regex:** {box(chat_regex, 're')} diff --git a/pterodactyl/websocket.py b/pterodactyl/websocket.py index 5403725..716e3d8 100644 --- a/pterodactyl/websocket.py +++ b/pterodactyl/websocket.py @@ -1,7 +1,6 @@ # pylint: disable=cyclic-import import json import re -from logging import getLogger from typing import Optional, Union import aiohttp @@ -11,7 +10,7 @@ from pydactyl import PterodactylClient from redbot.core.utils.chat_formatting import bold, pagify from pterodactyl.config import config -from pterodactyl.logger import logger +from pterodactyl.logger import logger, websocket_logger from pterodactyl.pterodactyl import Pterodactyl @@ -23,7 +22,7 @@ async def establish_websocket_connection(coginstance: Pterodactyl) -> None: websocket_credentials = await retrieve_websocket_credentials(coginstance) - async with websockets.connect(websocket_credentials['data']['socket'], origin=base_url, ping_timeout=60, logger=getLogger("red.sea.pterodactyl.websocket")) as websocket: + async with websockets.connect(websocket_credentials['data']['socket'], origin=base_url, ping_timeout=60, logger=websocket_logger) as websocket: logger.info("WebSocket connection established") auth_message = json.dumps({"event": "auth", "args": [websocket_credentials['data']['token']]}) @@ -58,7 +57,7 @@ async def establish_websocket_connection(coginstance: Pterodactyl) -> None: if content.startswith('['): pagified_content = pagify(content, delims=[" ", "\n"]) for page in pagified_content: - await channel.send(content=page) + await channel.send(content=page, allowed_mentions=discord.AllowedMentions.none()) server_message = await check_if_server_message(content) if server_message: @@ -135,10 +134,7 @@ async def retrieve_websocket_credentials(coginstance: Pterodactyl) -> Optional[d coginstance.task.cancel() raise ValueError("Pterodactyl server ID not set. Please set it using `[p]pterodactyl config serverid`.") - #FIXME - pydactyl should not be overriding the global python logger, but until that issue is fixed, - # we need to set the pydactyl logger to debug so it doesn't ignore any non-error log - # relevant issue - https://github.com/iamkubi/pydactyl/issues/82 - client = PterodactylClient(base_url, api_key, debug=True).client + client = PterodactylClient(base_url, api_key).client coginstance.client = client websocket_credentials = client.servers.get_websocket(server_id) logger.debug("""Websocket connection details retrieved: @@ -156,48 +152,39 @@ def remove_ansi_escape_codes(text: str) -> str: return ansi_escape.sub('', text) async def check_if_server_message(text: str) -> Union[bool, str]: - logger.debug("Checking if message is a server message") regex = await config.server_regex() match: Optional[re.Match[str]] = re.match(regex, text) if match: logger.debug("Message is a server message") return match.group(1) - logger.debug("Message is not a server message") return False async def check_if_chat_message(text: str) -> Union[bool, dict]: - logger.debug("Checking if message is a chat message") regex = await config.chat_regex() match: Optional[re.Match[str]] = re.match(regex, text) if match: groups = {"username": match.group(1), "message": match.group(2)} logger.debug("Message is a chat message\n%s", json.dumps(groups)) return groups - logger.debug("Message is not a chat message") return False async def check_if_join_message(text: str) -> Union[bool, str]: - logger.debug("Checking if message is a join message") regex = await config.join_regex() match: Optional[re.Match[str]] = re.match(regex, text) if match: logger.debug("Message is a join message") return match.group(1) - logger.debug("Message is not a join message") return False async def check_if_leave_message(text: str) -> Union[bool, str]: - logger.debug("Checking if message is a leave message") regex = await config.leave_regex() match: Optional[re.Match[str]] = re.match(regex, text) if match: logger.debug("Message is a leave message") return match.group(1) - logger.debug("Message is not a leave message") return False async def check_if_achievement_message(text: str) -> Union[bool, dict]: - logger.debug("Checking if message is an achievement message") regex = await config.achievement_regex() match: Optional[re.Match[str]] = re.match(regex, text) if match: @@ -206,9 +193,8 @@ async def check_if_achievement_message(text: str) -> Union[bool, dict]: groups["challenge"] = True else: groups["challenge"] = False - logger.debug("Message is an achievement message\n%s", json.dumps(groups)) + logger.debug("Message is an achievement message") return groups - logger.debug("Message is not an achievement message") return False async def get_info(username: str) -> Optional[dict]: @@ -217,7 +203,7 @@ async def get_info(username: str) -> Optional[dict]: async with aiohttp.ClientSession() as session: async with session.get(f"https://playerdb.co/api/player/{endpoint}/{username}") as response: if response.status == 200: - logger.debug("Player info retrieved for %s\n%s", username, json.dumps(await response.json())) + logger.debug("Player info retrieved for %s", username) return await response.json() logger.error("Failed to retrieve player info for %s: %s", username, response.status) return None @@ -230,10 +216,10 @@ async def send_chat_discord(coginstance: Pterodactyl, username: str, message: st webhook = discord.utils.get(webhooks, name="Pterodactyl Chat") if webhook is None: webhook = await channel.create_webhook(name="Pterodactyl Chat") - await webhook.send(content=message, username=username, avatar_url=avatar_url, allowed_mentions=discord.AllowedMentions.none()) + await webhook.send(content=message, username=username, avatar_url=avatar_url, allowed_mentions=discord.AllowedMentions(everyone=False, roles=False, users=True)) logger.debug("Chat message sent to Discord") else: - logger.debug("Chat channel not set. Skipping sending chat message to Discord") + logger.warning("Chat channel not set. Skipping sending chat message to Discord") async def generate_join_leave_embed(username: str, join: bool) -> discord.Embed: embed = discord.Embed() @@ -260,7 +246,7 @@ async def generate_achievement_embed(username: str, achievement: str, challenge: return embed def mask_ip(string: str) -> str: - def check(match): + def check(match: re.Match[str]): ip = match.group(0) masked_ip = '.'.join(r'\*' * len(octet) for octet in ip.split('.')) return masked_ip