feat: add multiple discord bot cogs

Adds new cogs including DataManager, Hiring, KofiShop, Logging, ModMail, MORS, ServiceReview, StaffMsg, and Translator to enhance bot functionality for data management, hiring processes, logging events, and more.
This commit is contained in:
2025-09-23 00:28:29 -04:00
parent c2367369f1
commit 81f2eee409
63 changed files with 2824 additions and 6 deletions

286
translator/commands.py Normal file
View File

@@ -0,0 +1,286 @@
# -*- coding: utf-8 -*-
import discord
import json
from typing import Optional
from redbot.core import commands, app_commands
from redbot.core.utils.chat_formatting import box, pagify
class CommandsMixin:
"""This class holds all the commands for the cog."""
@commands.hybrid_command(aliases=["ts"])
@app_commands.describe(
to_language="The language to translate to.",
text="The text to translate. For prefix commands, wrap multi-word text in quotes.",
from_language="[Optional] The language to translate from. Defaults to Common."
)
async def translate(self, ctx: commands.Context, to_language: str, text: str, from_language: Optional[str] = None):
"""Translates text from one language to another."""
from_lang_name = from_language if from_language else "common"
from_matches = await self._find_language(from_lang_name)
if not from_matches:
return await ctx.send(f"Could not find the 'from' language: `{from_lang_name}`")
if len(from_matches) > 1:
possible = [f"`{self.all_languages[k]['name']}` (`{k}`)" for k in from_matches]
return await ctx.send(f"Multiple 'from' languages found for `{from_lang_name}`. Please be more specific:\n" + ", ".join(possible))
from_lang_key = from_matches[0]
to_matches = await self._find_language(to_language)
if not to_matches:
return await ctx.send(f"Could not find the 'to' language: `{to_language}`")
if len(to_matches) > 1:
possible = [f"`{self.all_languages[k]['name']}` (`{k}`)" for k in to_matches]
return await ctx.send(f"Multiple 'to' languages found for `{to_language}`. Please be more specific:\n" + ", ".join(possible))
to_lang_key = to_matches[0]
from_lang_obj = self.all_languages[from_lang_key]
to_lang_obj = self.all_languages[to_lang_key]
try:
common_text = from_lang_obj['from_func'](text)
translated_text = to_lang_obj['to_func'](common_text)
except Exception as e:
await ctx.send(f"An error occurred during translation: `{e}`")
return
webhook = None
if ctx.guild and ctx.guild.me.guild_permissions.manage_webhooks:
for wh in await ctx.channel.webhooks():
if wh.name == "Translator Cog Webhook":
webhook = wh
break
if webhook is None:
webhook = await ctx.channel.create_webhook(name="Translator Cog Webhook")
if webhook:
if ctx.interaction:
await ctx.interaction.response.defer(ephemeral=True)
await webhook.send(content=translated_text, username=ctx.author.display_name, avatar_url=ctx.author.display_avatar.url)
await ctx.interaction.followup.send("Translation sent.", ephemeral=True)
else:
if ctx.channel.permissions_for(ctx.guild.me).manage_messages:
try:
await ctx.message.delete()
except discord.Forbidden:
pass
await webhook.send(content=translated_text, username=ctx.author.display_name, avatar_url=ctx.author.display_avatar.url)
else:
embed = discord.Embed(title=f"Translation to {to_lang_obj['name']}", color=await ctx.embed_color())
embed.add_field(name="Original Text", value=box(text), inline=False)
embed.add_field(name="Translated Text", value=box(translated_text), inline=False)
await ctx.send(embed=embed)
@commands.hybrid_command()
async def languages(self, ctx: commands.Context):
"""Lists all available languages."""
sorted_langs = sorted(self.all_languages.values(), key=lambda x: x['name'])
lang_list = [f"* `{lang['name']}`" for lang in sorted_langs]
output = "Available Languages:\n" + "\n".join(lang_list)
pages = [box(page) for page in pagify(output, page_length=1000)]
await ctx.send_interactive(pages, box_lang="md")
@commands.group(aliases=["px"], invoke_without_command=True)
async def proxy(self, ctx: commands.Context):
"""Toggles your translation proxy on or off."""
current_setting = await self.config.user(ctx.author).proxy_enabled()
new_setting = not current_setting
await self.config.user(ctx.author).proxy_enabled.set(new_setting)
status = "enabled" if new_setting else "disabled"
await ctx.send(f"Proxying is now `{status}`.")
@proxy.command(name="list")
async def proxy_list(self, ctx: commands.Context):
"""Shows your currently registered sonas."""
sonas = await self.config.user(ctx.author).sonas()
if not sonas:
return await ctx.send("You have no sonas registered.")
msg = "Your registered sonas:\n"
for name, data in sonas.items():
display_name = data.get("display_name", name)
lang_name = self.all_languages.get(data['language'], {}).get('name', 'Unknown Language')
if 'claws' in data:
start_claw, end_claw = data['claws']
elif 'brackets' in data: # Fallback for old data
start_claw, end_claw = data['brackets']
else:
continue
claw_info = f"Starts with `{start_claw}`" if not end_claw else f"`{start_claw}` and `{end_claw}`"
msg += f" - **{display_name}** (Internal Name: `{name}`): Translates to `{lang_name}`. Claws: {claw_info}\n"
for page in pagify(msg):
await ctx.send(page)
@proxy.command(name="add", aliases=["+"])
async def proxy_add(self, ctx: commands.Context, name: str, language: str, display_name: str, start_claw: str, end_claw: str = "", avatar: Optional[str] = None):
"""Registers a new sona with a name and avatar."""
matches = await self._find_language(language)
if not matches:
return await ctx.send(f"Language `{language}` not found.")
if len(matches) > 1:
possible = [f"`{self.all_languages[k]['name']}` (`{k}`)" for k in matches]
return await ctx.send(f"Multiple languages found for `{language}`. Please be more specific:\n" + ", ".join(possible))
lang_key = matches[0]
sona_key = name.lower()
avatar_url = avatar
if not avatar_url and ctx.message.attachments:
avatar_url = ctx.message.attachments[0].url
async with self.config.user(ctx.author).sonas() as sonas:
if sona_key in sonas:
return await ctx.send(f"A sona named `{name}` already exists.")
sonas[sona_key] = {
"display_name": display_name,
"avatar_url": avatar_url,
"language": lang_key,
"claws": [start_claw, end_claw]
}
await ctx.send(f"Sona `{name}` registered as `{display_name}` to translate to `{self.all_languages[lang_key]['name']}`.")
@proxy.command(name="remove", aliases=["-"])
async def proxy_remove(self, ctx: commands.Context, *, name: str):
"""Removes a sona."""
sona_key = name.lower()
async with self.config.user(ctx.author).sonas() as sonas:
if sona_key not in sonas:
return await ctx.send(f"No sona named `{name}` found.")
del sonas[sona_key]
await ctx.send(f"Sona `{name}` has been removed.")
@proxy.group()
async def sona(self, ctx: commands.Context):
"""Commands for managing a sona's appearance."""
pass
@proxy.group(name="auto")
async def proxy_auto(self, ctx: commands.Context):
"""Manage automatic translation in a channel."""
pass
@proxy_auto.command(name="set")
async def proxy_auto_set(self, ctx: commands.Context, sona_name: str):
"""Sets a sona to automatically translate your messages in this channel."""
sona_key = sona_name.lower()
sonas = await self.config.user(ctx.author).sonas()
if sona_key not in sonas:
return await ctx.send(f"No sona named `{sona_name}` found. Please register it first.")
async with self.config.channel(ctx.channel).autotranslate_users() as autotranslate_users:
autotranslate_users[str(ctx.author.id)] = sona_key
await ctx.send(f"Autotranslation enabled for you in this channel as **{sonas[sona_key]['display_name']}**.")
@proxy_auto.command(name="off")
async def proxy_auto_off(self, ctx: commands.Context):
"""Disables autotranslation for you in this channel."""
async with self.config.channel(ctx.channel).autotranslate_users() as autotranslate_users:
if str(ctx.author.id) in autotranslate_users:
del autotranslate_users[str(ctx.author.id)]
await ctx.send("Autotranslation has been disabled for you in this channel.")
else:
await ctx.send("You do not have autotranslation enabled in this channel.")
@sona.command(name="name")
async def sona_name(self, ctx: commands.Context, name: str, *, display_name: str):
"""Changes the display name of a sona."""
sona_key = name.lower()
async with self.config.user(ctx.author).sonas() as sonas:
if sona_key not in sonas:
return await ctx.send(f"No sona named `{name}` found.")
sonas[sona_key]["display_name"] = display_name
await ctx.send(f"Sona `{name}`'s display name has been changed to `{display_name}`.")
@sona.command(name="avatar")
async def sona_avatar(self, ctx: commands.Context, name: str, url: Optional[str] = None):
"""Changes the avatar of a sona.
You can either provide a direct image URL or upload an image with the command.
"""
sona_key = name.lower()
if not url and not ctx.message.attachments:
return await ctx.send("You must provide an image URL or upload an image.")
avatar_url = url
if ctx.message.attachments:
avatar_url = ctx.message.attachments[0].url
async with self.config.user(ctx.author).sonas() as sonas:
if sona_key not in sonas:
return await ctx.send(f"No sona named `{name}` found.")
sonas[sona_key]["avatar_url"] = avatar_url
await ctx.send(f"Sona `{name}`'s avatar has been updated.")
@commands.group(aliases=["tset"])
@commands.has_permissions(manage_guild=True)
async def translatorset(self, ctx: commands.Context):
"""Admin commands for the Translator cog."""
pass
@translatorset.group(name="language", aliases=["lang"])
async def translatorset_language(self, ctx: commands.Context):
"""Manage custom languages."""
pass
@translatorset_language.command(name="add", aliases=["+"])
async def translatorset_language_add(self, ctx: commands.Context, name: str, *, json_map: str):
"""Adds a new custom language."""
lang_key = name.lower()
if lang_key in self.all_languages:
return await ctx.send(f"A language with the key `{lang_key}` already exists.")
try:
lang_map = json.loads(json_map)
if not isinstance(lang_map, dict):
raise ValueError("JSON map must be an object/dictionary.")
except (json.JSONDecodeError, ValueError) as e:
return await ctx.send(f"Invalid JSON map provided: `{e}`")
new_lang_data = {'name': name.capitalize(), 'type': 'greedy', 'map': lang_map, 'is_custom': True}
async with self.config.languages() as languages:
languages[lang_key] = new_lang_data
await self._initialize_languages()
await ctx.send(f"Custom language `{name}` added successfully.")
@translatorset_language.command(name="remove", aliases=["-"])
async def translatorset_language_remove(self, ctx: commands.Context, name: str):
"""Removes a custom language."""
lang_key = name.lower()
current_data = await self.config.languages()
lang_obj = current_data.get(lang_key)
if not lang_obj or not lang_obj.get('is_custom', False):
return await ctx.send(f"No custom language named `{name}` found.")
async with self.config.languages() as languages:
if lang_key in languages:
del languages[lang_key]
await self._initialize_languages()
await ctx.send(f"Custom language `{name}` removed.")
@translatorset_language.command(name="listcustom")
async def translatorset_language_listcustom(self, ctx: commands.Context):
"""Lists all custom-added languages."""
custom_langs = [f"`{lang['name']}`" for lang in self.all_languages.values() if lang.get('is_custom')]
if not custom_langs:
return await ctx.send("There are no custom languages.")
await ctx.send("Custom Languages:\n" + ", ".join(custom_langs))
@translatorset_language.command(name="listbase")
async def translatorset_language_listbase(self, ctx: commands.Context):
"""Lists all base (built-in) languages."""
base_langs = [f"`{lang['name']}`" for lang in self.all_languages.values() if not lang.get('is_custom')]
await ctx.send("Base Languages:\n" + ", ".join(base_langs))