WIP: Make Aurora commands hybrid commands #17
17 changed files with 228 additions and 84 deletions
|
@ -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
|
||||
```
|
||||
|
|
|
@ -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
|
||||
```
|
||||
|
|
|
@ -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
|
||||
```
|
||||
|
|
|
@ -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
|
||||
```
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
```
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
import logging
|
||||
from red_commons.logging import getLogger
|
||||
|
||||
logger = logging.getLogger("red.sea.aurora")
|
||||
logger = getLogger("red.seacogs.aurora")
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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 = [
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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={},
|
||||
)
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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')
|
||||
|
|
|
@ -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')}
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue