Compare commits

..

35 commits

Author SHA1 Message Date
354b505e6e
Merge branch 'main' into aurora-hybrid
Some checks failed
Actions / Lint Code (Ruff & Pylint) (pull_request) Failing after 22s
Actions / Build Documentation (MkDocs) (pull_request) Successful in 27s
2024-03-07 20:00:09 +00:00
fdb785ffd7
feat(backup): allow for retrieving backup exports from bot messages if you reply to them
Some checks failed
Actions / Lint Code (Ruff & Pylint) (push) Failing after 23s
Actions / Build Documentation (MkDocs) (push) Successful in 28s
2024-03-07 13:47:40 -05:00
178a92559c
feat(repo): added help formatters for version numbers to cogs that have them and migrated to using red's inbuilt loggers instead of logging.getLogger
Some checks failed
Actions / Lint Code (Ruff & Pylint) (push) Failing after 31s
Actions / Build Documentation (MkDocs) (push) Successful in 26s
2024-03-07 03:38:34 -05:00
ae31a61436
fix(pterodactyl): log websocket messages to VERBOSE (log level 5) and not DEBUG
All checks were successful
Actions / Lint Code (Ruff & Pylint) (push) Successful in 18s
Actions / Build Documentation (MkDocs) (push) Successful in 23s
2024-03-07 02:52:44 -05:00
9e826d6ba5
misc(pterodactyl): testing something
All checks were successful
Actions / Lint Code (Ruff & Pylint) (push) Successful in 26s
Actions / Build Documentation (MkDocs) (push) Successful in 25s
2024-03-07 02:39:49 -05:00
b5362ff153
fix(pterodactyl): update PterodactylClient initialization to use my own logger instead of setting debug to true
Some checks failed
Actions / Lint Code (Ruff & Pylint) (push) Failing after 20s
Actions / Build Documentation (MkDocs) (push) Successful in 25s
2024-03-07 02:33:14 -05:00
2e5fa81eac
fix(pterodactyl): use custom fork of pydactyl library to fix https://github.com/iamkubi/pydactyl/issues/82
Some checks failed
Actions / Lint Code (Ruff & Pylint) (push) Failing after 30s
Actions / Build Documentation (MkDocs) (push) Successful in 30s
2024-03-07 02:19:22 -05:00
c65fdd698c
fix(pterodactyl): pylint fixes 2024-03-07 02:19:00 -05:00
497f6a0a1a
fix(repo): changed all loggers to red.seacogs.<cog_name> instead of red.sea.<cog_name>
Some checks failed
Actions / Lint Code (Ruff & Pylint) (push) Failing after 49s
Actions / Build Documentation (MkDocs) (push) Successful in 43s
2024-03-07 01:32:27 -05:00
f059145681
fix(pterodactyl): missed one!
Some checks failed
Actions / Lint Code (Ruff & Pylint) (push) Failing after 23s
Actions / Build Documentation (MkDocs) (push) Successful in 27s
2024-03-07 01:04:27 -05:00
41a8d575d4
fix(pterodactyl): fixed InteractionResponded error when using a power slash command
Some checks failed
Actions / Lint Code (Ruff & Pylint) (push) Failing after 22s
Actions / Build Documentation (MkDocs) (push) Successful in 24s
2024-03-07 01:03:36 -05:00
dd62b7d7ce
fix(pterodactyl): whoops!
Some checks failed
Actions / Lint Code (Ruff & Pylint) (push) Failing after 22s
Actions / Build Documentation (MkDocs) (push) Successful in 26s
2024-03-07 00:59:14 -05:00
bbb54f0f55
feat(pterodactyl): fixed slash commands
Some checks failed
Actions / Lint Code (Ruff & Pylint) (push) Failing after 21s
Actions / Build Documentation (MkDocs) (push) Successful in 25s
2024-03-07 00:56:50 -05:00
f033f6a483
fix(pterodactyl): maybe fixed hybrid config commands, if this doesn't work, I'll move configuration commands to a separate command
Some checks failed
Actions / Lint Code (Ruff & Pylint) (push) Failing after 23s
Actions / Build Documentation (MkDocs) (push) Successful in 26s
2024-03-07 00:21:51 -05:00
1bb7e22b95
fix(pterodactyl): fixed another issue with hybrid commands
Some checks failed
Actions / Lint Code (Ruff & Pylint) (push) Failing after 21s
Actions / Build Documentation (MkDocs) (push) Successful in 23s
2024-03-07 00:14:52 -05:00
f707b70097
fix(pterodactyl): fixed broken slash commands
Some checks failed
Actions / Lint Code (Ruff & Pylint) (push) Failing after 21s
Actions / Build Documentation (MkDocs) (push) Successful in 24s
2024-03-07 00:13:43 -05:00
64ab2fbf82
feat(pterodactyl): added a pterodactyl command command to execute commands on the server and made pterodactyl command and pterodactyl power into hybrid commands
Some checks failed
Actions / Lint Code (Ruff & Pylint) (push) Failing after 24s
Actions / Build Documentation (MkDocs) (push) Successful in 30s
2024-03-07 00:00:15 -05:00
9d64c15a87
misc(pterodactyl): added another ⚠️
Some checks failed
Actions / Lint Code (Ruff & Pylint) (push) Failing after 20s
Actions / Build Documentation (MkDocs) (push) Successful in 22s
2024-03-05 02:28:10 -05:00
8c58e1746e
feat(pterodactyl): added ptero power kill command
Some checks failed
Actions / Lint Code (Ruff & Pylint) (push) Failing after 18s
Actions / Build Documentation (MkDocs) (push) Successful in 22s
2024-03-05 02:25:58 -05:00
52be531807
fix(pterodactyl): type hints 2024-03-05 02:24:20 -05:00
80cb729e72
fix(pterodactyl): make sure views get removed properly
Some checks failed
Actions / Lint Code (Ruff & Pylint) (push) Failing after 21s
Actions / Build Documentation (MkDocs) (push) Successful in 22s
2024-03-05 02:19:59 -05:00
df92bc34cc
fix(pterodactyl): fixed some bugs
Some checks failed
Actions / Lint Code (Ruff & Pylint) (push) Failing after 19s
Actions / Build Documentation (MkDocs) (push) Successful in 22s
2024-03-05 02:18:11 -05:00
b2f27f9490
feat(pterodactyl): added confirmation prompts to the power commands
Some checks failed
Actions / Lint Code (Ruff & Pylint) (push) Failing after 19s
Actions / Build Documentation (MkDocs) (push) Successful in 24s
2024-03-05 02:16:32 -05:00
093a6b9077
fix(backup): reduced python version constraint
Some checks failed
Actions / Lint Code (Ruff & Pylint) (push) Failing after 19s
Actions / Build Documentation (MkDocs) (push) Successful in 23s
2024-03-05 02:13:56 -05:00
8639615c49
docs(): updated urls to www.coastalcommits.com from coastalcommits.com
Some checks failed
Actions / Lint Code (Ruff & Pylint) (push) Failing after 24s
Actions / Build Documentation (MkDocs) (push) Successful in 26s
2024-03-04 23:54:56 -05:00
dec154fb4c
fix(backup): changed version constraints again
Some checks failed
Actions / Lint Code (Ruff & Pylint) (push) Failing after 18s
Actions / Build Documentation (MkDocs) (push) Successful in 23s
there have been no changes to downloader that break this since 3.5.0's release. however, 3.5.6 will be breaking most likely
2024-03-04 23:43:14 -05:00
aaba9ebd59
fix(backup): reduced minimum bot version to 3.5.2
Some checks failed
Actions / Lint Code (Ruff & Pylint) (push) Failing after 20s
Actions / Build Documentation (MkDocs) (push) Successful in 22s
2024-03-04 23:37:22 -05:00
75adf692c1
docs(pterodactyl): updated an outdated default value in the docs
Some checks failed
Actions / Lint Code (Ruff & Pylint) (push) Failing after 19s
Actions / Build Documentation (MkDocs) (push) Successful in 23s
2024-03-04 23:16:09 -05:00
85d5316f43
fix(pterodactyl): forgot two!
Some checks failed
Actions / Lint Code (Ruff & Pylint) (push) Failing after 19s
Actions / Build Documentation (MkDocs) (push) Successful in 23s
2024-03-04 23:13:37 -05:00
13dba790b7
fix(pterodactyl): removed leftover debug statement 2024-03-04 23:13:25 -05:00
d01985eea6
fix(pterodactyl): removed a bunch of useless/redundant logging statements
Some checks failed
Actions / Lint Code (Ruff & Pylint) (push) Failing after 19s
Actions / Build Documentation (MkDocs) (push) Successful in 23s
2024-03-04 23:12:32 -05:00
0baad46298
misc(pterodactyl): temporarily adding a debug statement to test something
Some checks failed
Actions / Lint Code (Ruff & Pylint) (push) Failing after 20s
Actions / Build Documentation (MkDocs) (push) Successful in 24s
2024-03-04 23:06:51 -05:00
3d3c5f708e
docs(pterodactyl): added new placeholder to chat command docs
Some checks failed
Actions / Lint Code (Ruff & Pylint) (push) Failing after 19s
Actions / Build Documentation (MkDocs) (push) Successful in 26s
2024-03-04 23:04:36 -05:00
34c34e745a
feat(pterodactyl): added a discord invite placeholder
Some checks failed
Actions / Lint Code (Ruff & Pylint) (push) Failing after 22s
Actions / Build Documentation (MkDocs) (push) Successful in 25s
updated default chat command as well, and also a configuration value and related command
2024-03-04 22:59:43 -05:00
87dfc03812
fix(pterodactyl): don't ping users, roles, or @everyone/@here in console messages + ping only users in chat messages
Some checks failed
Actions / Lint Code (Ruff & Pylint) (push) Failing after 20s
Actions / Build Documentation (MkDocs) (push) Successful in 25s
2024-03-04 19:53:12 -05:00
17 changed files with 228 additions and 84 deletions

View file

@ -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
```

View file

@ -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
```

View file

@ -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
```

View file

@ -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
```

View file

@ -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.

View file

@ -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
```

View file

@ -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

View file

@ -1,3 +1,3 @@
import logging
from red_commons.logging import getLogger
logger = logging.getLogger("red.sea.aurora")
logger = getLogger("red.seacogs.aurora")

View file

@ -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()
@ -86,6 +96,9 @@ class Backup(commands.Cog):
"""Import your installed repositories and cogs from an export file."""
try:
export = json.loads(await ctx.message.attachments[0].read())
except (json.JSONDecodeError, IndexError):
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

View file

@ -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",

View file

@ -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 = [

View file

@ -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

View file

@ -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={},
)

View file

@ -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",

View file

@ -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')

View file

@ -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')}

View file

@ -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