refactor(everything): combined all commands into command groups

This commit is contained in:
neo
2025-09-29 10:22:46 -04:00
committed by GitHub
47 changed files with 1092 additions and 517 deletions

44
bot.py
View File

@@ -86,21 +86,33 @@ class DiscordBot(commands.Bot):
for folder in os.listdir(cogs_path):
folder_path = os.path.join(cogs_path, folder)
if os.path.isdir(folder_path) and not folder.startswith('__'):
for file in os.listdir(folder_path):
if file.endswith(".py") and not file.startswith('__'):
extension = file[:-3]
full_name = f"{folder}.{extension}".lower()
if extension.lower() in disabled_cogs or full_name in disabled_cogs:
self.logger.info(f"Skipped disabled extension '{full_name}'")
continue
try:
await self.load_extension(f"cogs.{folder}.{extension}")
self.logger.info(f"Loaded extension '{folder}.{extension}'")
except Exception as e:
exception = f"{type(e).__name__}: {e}"
self.logger.error(
f"Failed to load extension {folder}.{extension}\n{exception}"
)
init_file = os.path.join(folder_path, "__init__.py")
if os.path.exists(init_file):
try:
await self.load_extension(f"cogs.{folder}")
if folder in ["owner", "help"]:
self.logger.info(f"Loaded extension '{folder}'")
except Exception as e:
exception = f"{type(e).__name__}: {e}"
self.logger.error(
f"Failed to load extension {folder}\n{exception}"
)
else:
for file in os.listdir(folder_path):
if file.endswith(".py") and not file.startswith('__'):
extension = file[:-3]
full_name = f"{folder}.{extension}".lower()
if extension.lower() in disabled_cogs or full_name in disabled_cogs:
self.logger.info(f"Skipped disabled extension '{full_name}'")
continue
try:
await self.load_extension(f"cogs.{folder}.{extension}")
self.logger.info(f"Loaded extension '{folder}.{extension}'")
except Exception as e:
exception = f"{type(e).__name__}: {e}"
self.logger.error(
f"Failed to load extension {folder}.{extension}\n{exception}"
)
for file in os.listdir(cogs_path):
if file.endswith(".py") and not file.startswith('__'):
@@ -271,6 +283,8 @@ class DiscordBot(commands.Bot):
color=0xE02B2B,
)
await context.send(embed=embed)
elif isinstance(error, commands.CheckFailure):
return
else:
raise error

108
cogs/fun/__init__.py Normal file
View File

@@ -0,0 +1,108 @@
import discord
from discord.ext import commands
from discord.ext.commands import Context
from .coinflip import coinflip_command
from .eightball import eightball_command
from .minesweeper import minesweeper_command
from .randomfact import randomfact_command
from .rockpaperscissors import rps_command
class Fun(commands.GroupCog, name="fun"):
def __init__(self, bot) -> None:
self.bot = bot
super().__init__()
@commands.group(name="fun", invoke_without_command=True)
async def fun_group(self, context: Context):
embed = discord.Embed(
title="Fun Commands",
description="Use `.fun <subcommand>` or slash `/fun <subcommand>`.",
color=0x7289DA
)
embed.set_author(name="Fun", icon_url="https://yes.nighty.works/raw/eW5lLm.webp")
embed.add_field(name="Available", value="coinflip, 8ball, minesweeper, randomfact, rps", inline=False)
await context.send(embed=embed)
async def _invoke_hybrid(self, context: Context, name: str, **kwargs):
command = self.bot.get_command(name)
if command is not None:
await context.invoke(command, **kwargs)
else:
await context.send(f"Unknown fun command: {name}")
def _require_group_prefix(context: Context) -> bool:
if getattr(context, "interaction", None):
return True
group = getattr(getattr(context, "cog", None), "qualified_name", "").lower()
if not group:
return True
prefix = context.prefix or ""
content = context.message.content.strip().lower()
return content.startswith(f"{prefix}{group} ")
@fun_group.command(name="coinflip")
async def fun_group_coinflip(self, context: Context):
await self._invoke_hybrid(context, "coinflip")
@fun_group.command(name="8ball")
async def fun_group_8ball(self, context: Context, *, question: str):
await self._invoke_hybrid(context, "8ball", question=question)
@fun_group.command(name="minesweeper")
async def fun_group_minesweeper(self, context: Context):
await self._invoke_hybrid(context, "minesweeper")
@fun_group.command(name="randomfact")
async def fun_group_randomfact(self, context: Context):
await self._invoke_hybrid(context, "randomfact")
@fun_group.command(name="rps")
async def fun_group_rps(self, context: Context):
await self._invoke_hybrid(context, "rps")
@commands.check(_require_group_prefix)
@commands.hybrid_command(
name="coinflip",
description="Make a coin flip, but give your bet before."
)
async def coinflip(self, context):
return await coinflip_command()(self, context)
@commands.check(_require_group_prefix)
@commands.hybrid_command(
name="8ball",
description="Ask any question to the bot.",
)
async def eight_ball(self, context, *, question: str):
return await eightball_command()(self, context, question=question)
@commands.check(_require_group_prefix)
@commands.hybrid_command(
name="minesweeper",
description="Play a buttoned minesweeper mini-game."
)
async def minesweeper(self, context):
return await minesweeper_command()(self, context)
@commands.check(_require_group_prefix)
@commands.hybrid_command(name="randomfact", description="Get a random fact.")
async def randomfact(self, context):
return await randomfact_command()(self, context)
@commands.check(_require_group_prefix)
@commands.hybrid_command(
name="rps", description="Play the rock paper scissors game against the bot."
)
async def rock_paper_scissors(self, context):
return await rps_command()(self, context)
async def setup(bot) -> None:
cog = Fun(bot)
await bot.add_cog(cog)
bot.logger.info("Loaded extension 'fun.coinflip'")
bot.logger.info("Loaded extension 'fun.8ball'")
bot.logger.info("Loaded extension 'fun.minesweeper'")
bot.logger.info("Loaded extension 'fun.randomfact'")
bot.logger.info("Loaded extension 'fun.rps'")

View File

@@ -1,7 +1,6 @@
import random
import discord
from discord.ext import commands
from discord.ext.commands import Context
class Choice(discord.ui.View):
def __init__(self) -> None:
@@ -22,18 +21,12 @@ class Choice(discord.ui.View):
self.value = "tails"
self.stop()
class CoinFlip(commands.Cog, name="coinflip"):
def __init__(self, bot) -> None:
self.bot = bot
def coinflip_command():
@commands.hybrid_command(
name="coinflip", description="Make a coin flip, but give your bet before."
name="coinflip",
description="Make a coin flip, but give your bet before."
)
async def coinflip(self, context: Context) -> None:
"""
Make a coin flip, but give your bet before.
:param context: The hybrid command context.
"""
async def coinflip(self, context):
buttons = Choice()
embed = discord.Embed(
title="Coinflip",
@@ -59,6 +52,5 @@ class CoinFlip(commands.Cog, name="coinflip"):
)
embed.set_author(name="Fun", icon_url="https://yes.nighty.works/raw/eW5lLm.webp")
await message.edit(embed=embed, view=None, content=None)
async def setup(bot) -> None:
await bot.add_cog(CoinFlip(bot))
return coinflip

View File

@@ -1,26 +1,12 @@
import random
import discord
from discord import app_commands
from discord.ext import commands
from discord.ext.commands import Context
class EightBall(commands.Cog, name="8ball"):
def __init__(self, bot) -> None:
self.bot = bot
def eightball_command():
@commands.hybrid_command(
name="8ball",
description="Ask any question to the bot.",
)
@app_commands.describe(question="The question you want to ask.")
async def eight_ball(self, context: Context, *, question: str) -> None:
"""
Ask any question to the bot.
:param context: The hybrid command context.
:param question: The question that should be asked by the user.
"""
async def eight_ball(self, context, *, question: str):
answers = [
"It is certain.",
"It is decidedly so.",
@@ -51,7 +37,5 @@ class EightBall(commands.Cog, name="8ball"):
embed.set_author(name="Fun", icon_url="https://yes.nighty.works/raw/eW5lLm.webp")
embed.set_footer(text=f"The question was: {question}")
await context.send(embed=embed)
async def setup(bot) -> None:
await bot.add_cog(EightBall(bot))
return eight_ball

View File

@@ -2,8 +2,6 @@ import random
from itertools import repeat
import discord
from discord.ext import commands
from discord.ext.commands import Context
class RowButton(discord.ui.Button):
def __init__(self, ctx, label, custom_id, bombs, board):
@@ -66,7 +64,6 @@ class RowButton(discord.ui.Button):
await interaction.edit_original_response(view=view)
class MsView(discord.ui.View):
def __init__(self, ctx, options, bombs, board):
super().__init__()
@@ -155,21 +152,13 @@ class MsView(discord.ui.View):
self.GetBoardPos(pos)
] = bombemo
class Minesweeper(commands.Cog, name="minesweeper"):
def __init__(self, bot) -> None:
self.bot = bot
def minesweeper_command():
@commands.hybrid_command(
name="minesweeper",
description="Play a buttoned minesweeper mini-game."
)
async def minesweeper(self, context: Context) -> None:
"""
Play a buttoned minesweeper mini-game.
:param context: The hybrid command context.
"""
board = [[" "] * 5 for _ in range(5)] # Unicode block character, usually doesnt show up in Discord or github, search up invisible character on google
async def minesweeper(self, context):
board = [[" "] * 5 for _ in range(5)]
bombs = 0
bombpositions = []
for x in repeat(None, random.randint(4, 11)):
@@ -197,7 +186,5 @@ class Minesweeper(commands.Cog, name="minesweeper"):
view = MsView(context, ExtractBlocks(), bombpositions, board)
message = await context.send(embed=embed, view=view)
view.message = message
async def setup(bot) -> None:
await bot.add_cog(Minesweeper(bot))
return minesweeper

View File

@@ -1,15 +1,10 @@
import aiohttp
import discord
from discord.ext import commands
from discord.ext.commands import Context
class RandomFact(commands.Cog, name="randomfact"):
def __init__(self, bot) -> None:
self.bot = bot
def randomfact_command():
@commands.hybrid_command(name="randomfact", description="Get a random fact.")
async def randomfact(self, context: Context) -> None:
async def randomfact(self, context):
async with aiohttp.ClientSession() as session:
async with session.get(
"https://uselessfacts.jsph.pl/random.json?language=en"
@@ -30,7 +25,5 @@ class RandomFact(commands.Cog, name="randomfact"):
)
embed.set_author(name="Fun", icon_url="https://yes.nighty.works/raw/eW5lLm.webp")
await context.send(embed=embed)
async def setup(bot) -> None:
await bot.add_cog(RandomFact(bot))
return randomfact

View File

@@ -1,7 +1,6 @@
import random
import discord
from discord.ext import commands
from discord.ext.commands import Context
class RockPaperScissors(discord.ui.Select):
def __init__(self) -> None:
@@ -39,7 +38,6 @@ class RockPaperScissors(discord.ui.Select):
winner = (3 + user_choice_index - bot_choice_index) % 3
# Get the user mention
user_mention = interaction.user.mention
if winner == 0:
@@ -61,14 +59,11 @@ class RockPaperScissorsView(discord.ui.View):
super().__init__()
self.add_item(RockPaperScissors())
class RPS(commands.Cog, name="rps"):
def __init__(self, bot) -> None:
self.bot = bot
def rps_command():
@commands.hybrid_command(
name="rps", description="Play the rock paper scissors game against the bot."
)
async def rock_paper_scissors(self, context: Context) -> None:
async def rock_paper_scissors(self, context):
view = RockPaperScissorsView()
embed = discord.Embed(
title="Rock Paper Scissors",
@@ -77,6 +72,5 @@ class RPS(commands.Cog, name="rps"):
)
embed.set_author(name="Fun", icon_url="https://yes.nighty.works/raw/eW5lLm.webp")
await context.send(embed=embed, view=view)
async def setup(bot) -> None:
await bot.add_cog(RPS(bot))
return rock_paper_scissors

113
cogs/general/__init__.py Normal file
View File

@@ -0,0 +1,113 @@
import discord
from discord.ext import commands
from discord.ext.commands import Context
from .ping import ping_command
from .uptime import uptime_command
from .botinfo import botinfo_command
from .serverinfo import serverinfo_command
from .feedback import feedback_command
def _require_group_prefix(context: Context) -> bool:
if getattr(context, "interaction", None):
return True
group = getattr(getattr(context, "cog", None), "qualified_name", "").lower()
if not group:
return True
prefix = context.prefix or ""
content = context.message.content.strip().lower()
return content.startswith(f"{prefix}{group} ")
class General(commands.GroupCog, name="general"):
def __init__(self, bot) -> None:
self.bot = bot
super().__init__()
@commands.group(name="general", invoke_without_command=True)
async def general_group(self, context: Context):
embed = discord.Embed(
title="General Commands",
description="Use `.general <subcommand>` or `/general <subcommand>`.",
color=0x7289DA
)
embed.set_author(name="General", icon_url="https://yes.nighty.works/raw/y5SEZ9.webp")
embed.add_field(name="Available", value="ping, uptime, botinfo, serverinfo, feedback", inline=False)
await context.send(embed=embed)
async def _invoke_hybrid(self, context: Context, name: str):
command = self.bot.get_command(name)
if command is not None:
await context.invoke(command)
else:
await context.send(f"Unknown general command: {name}")
@general_group.command(name="ping")
async def general_group_ping(self, context: Context):
await self._invoke_hybrid(context, "ping")
@general_group.command(name="uptime")
async def general_group_uptime(self, context: Context):
await self._invoke_hybrid(context, "uptime")
@general_group.command(name="botinfo")
async def general_group_botinfo(self, context: Context):
await self._invoke_hybrid(context, "botinfo")
@general_group.command(name="serverinfo")
async def general_group_serverinfo(self, context: Context):
await self._invoke_hybrid(context, "serverinfo")
@general_group.command(name="feedback")
async def general_group_feedback(self, context: Context):
await self._invoke_hybrid(context, "feedback")
@commands.check(_require_group_prefix)
@commands.hybrid_command(
name="ping",
description="Check if the bot is alive.",
)
async def ping(self, context):
return await ping_command()(self, context)
@commands.check(_require_group_prefix)
@commands.hybrid_command(
name="uptime",
description="Check how long the bot has been running.",
)
async def uptime(self, context):
return await uptime_command()(self, context)
@commands.check(_require_group_prefix)
@commands.hybrid_command(
name="botinfo",
description="Get some useful (or not) information about the bot.",
)
async def botinfo(self, context):
return await botinfo_command()(self, context)
@commands.check(_require_group_prefix)
@commands.hybrid_command(
name="serverinfo",
description="Get some useful (or not) information about the server.",
)
async def serverinfo(self, context):
return await serverinfo_command()(self, context)
@commands.check(_require_group_prefix)
@commands.hybrid_command(
name="feedback",
description="Submit a feedback for the owners of the bot"
)
async def feedback(self, context):
return await feedback_command()(self, context)
async def setup(bot) -> None:
cog = General(bot)
await bot.add_cog(cog)
bot.logger.info("Loaded extension 'general.ping'")
bot.logger.info("Loaded extension 'general.uptime'")
bot.logger.info("Loaded extension 'general.botinfo'")
bot.logger.info("Loaded extension 'general.serverinfo'")
bot.logger.info("Loaded extension 'general.feedback'")

View File

@@ -1,23 +1,13 @@
import platform
import discord
from discord.ext import commands
from discord.ext.commands import Context
class BotInfo(commands.Cog, name="botinfo"):
def __init__(self, bot) -> None:
self.bot = bot
def botinfo_command():
@commands.hybrid_command(
name="botinfo",
description="Get some useful (or not) information about the bot.",
)
async def botinfo(self, context: Context) -> None:
"""
Get some useful (or not) information about the bot.
:param context: The hybrid command context.
"""
async def botinfo(self, context):
embed = discord.Embed(
title="Syntrel Discord Bot",
color=0x7289DA,
@@ -36,6 +26,5 @@ class BotInfo(commands.Cog, name="botinfo"):
await context.interaction.response.send_message(embed=embed, ephemeral=True)
else:
await context.send(embed=embed)
async def setup(bot) -> None:
await bot.add_cog(BotInfo(bot))
return botinfo

View File

@@ -2,7 +2,6 @@ import discord
from discord import app_commands
from discord.ext import commands
class FeedbackForm(discord.ui.Modal, title="Feeedback"):
feedback = discord.ui.TextInput(
label="What do you think about this bot?",
@@ -17,20 +16,11 @@ class FeedbackForm(discord.ui.Modal, title="Feeedback"):
self.answer = str(self.feedback)
self.stop()
class Feedback(commands.Cog, name="feedback"):
def __init__(self, bot) -> None:
self.bot = bot
def feedback_command():
@app_commands.command(
name="feedback", description="Submit a feedback for the owners of the bot"
)
async def feedback(self, interaction: discord.Interaction) -> None:
"""
Submit a feedback for the owners of the bot.
:param interaction: The application command interaction.
"""
async def feedback(self, interaction: discord.Interaction):
feedback_form = FeedbackForm()
await interaction.response.send_modal(feedback_form)
@@ -53,7 +43,5 @@ class Feedback(commands.Cog, name="feedback"):
color=0x7289DA,
).set_author(name="Feedback System", icon_url="https://yes.nighty.works/raw/y5SEZ9.webp")
)
async def setup(bot) -> None:
await bot.add_cog(Feedback(bot))
return feedback

View File

@@ -1,22 +1,12 @@
import discord
from discord.ext import commands
from discord.ext.commands import Context
class Ping(commands.Cog, name="ping"):
def __init__(self, bot) -> None:
self.bot = bot
def ping_command():
@commands.hybrid_command(
name="ping",
description="Check if the bot is alive.",
)
async def ping(self, context: Context) -> None:
"""
Check if the bot is alive.
:param context: The hybrid command context.
"""
async def ping(self, context):
embed = discord.Embed(
title="🏓 Pong!",
description=f"The bot latency is {round(self.bot.latency * 1000)}ms.",
@@ -31,7 +21,5 @@ class Ping(commands.Cog, name="ping"):
await inter.followup.send(embed=embed, ephemeral=True)
else:
await context.send(embed=embed)
async def setup(bot) -> None:
await bot.add_cog(Ping(bot))
return ping

View File

@@ -1,22 +1,13 @@
import discord
from discord.ext import commands
from discord.ext.commands import Context
class ServerInfo(commands.Cog, name="serverinfo"):
def __init__(self, bot) -> None:
self.bot = bot
def serverinfo_command():
@commands.hybrid_command(
name="serverinfo",
description="Get some useful (or not) information about the server.",
)
@commands.guild_only() # This decorator ensures the command only works in servers
async def serverinfo(self, context: Context) -> None:
"""
Get some useful (or not) information about the server.
:param context: The hybrid command context.
"""
# Additional check (though @commands.guild_only() should handle this)
@commands.guild_only()
async def serverinfo(self, context):
if context.guild is None:
await context.send("This command can only be used in a server, not in DMs!")
return
@@ -53,6 +44,5 @@ class ServerInfo(commands.Cog, name="serverinfo"):
await context.interaction.response.send_message(embed=embed, ephemeral=True)
else:
await context.send(embed=embed)
async def setup(bot) -> None:
await bot.add_cog(ServerInfo(bot))
return serverinfo

View File

@@ -1,7 +1,5 @@
import discord
from discord.ext import commands
from discord.ext.commands import Context
class UptimeView(discord.ui.View):
def __init__(self, bot):
@@ -18,21 +16,12 @@ class UptimeView(discord.ui.View):
embed.set_author(name="Uptime", icon_url="https://yes.nighty.works/raw/y5SEZ9.webp")
await interaction.response.edit_message(embed=embed, view=self)
class Uptime(commands.Cog, name="uptime"):
def __init__(self, bot) -> None:
self.bot = bot
def uptime_command():
@commands.hybrid_command(
name="uptime",
description="Check how long the bot has been running.",
)
async def uptime(self, context: Context) -> None:
"""
Check how long the bot has been running.
:param context: The hybrid command context.
"""
async def uptime(self, context):
embed = discord.Embed(
title="Bot Uptime",
description=f"The bot has been running for **{self.bot.get_uptime()}**",
@@ -48,7 +37,5 @@ class Uptime(commands.Cog, name="uptime"):
await inter.followup.send(embed=embed, view=view, ephemeral=True)
else:
await context.send(embed=embed, view=view)
async def setup(bot) -> None:
await bot.add_cog(Uptime(bot))
return uptime

View File

@@ -37,67 +37,22 @@ class Help(commands.Cog, name="help"):
async def help(self, context: Context, category: str = None) -> None:
category_mapping = {
# General Commands
"help": "general",
"botinfo": "general",
"serverinfo": "general",
"ping": "general",
"feedback": "general",
"uptime": "general",
# "context_menus": "general",
# Fun Commands
"randomfact": "fun",
"coinflip": "fun",
"rps": "fun",
"8ball": "fun",
"minesweeper": "fun",
# Moderation Commands
"kick": "moderation",
"ban": "moderation",
"nick": "moderation",
"purge": "moderation",
"hackban": "moderation",
"warnings": "moderation",
"archive": "moderation",
# SideStore Commands
"sidestore": "sidestore",
"refresh": "sidestore",
"code": "sidestore",
"crash": "sidestore",
"pairing": "sidestore",
"server": "sidestore",
"half": "sidestore",
"sparse": "sidestore",
"afc": "sidestore",
"udid": "sidestore",
# idevice Commands
"general": "general",
"fun": "fun",
"idevice": "idevice",
"noapps": "idevice",
"errorcodes": "idevice",
"developermode": "idevice",
"mountddi": "idevice",
"miscellaneous": "miscellaneous",
"moderation": "moderation",
"sidestore": "sidestore",
"utilities": "utilities",
# Owner Commands
"sync": "owner",
"cog_management": "owner",
"logs": "owner",
"invite": "owner",
"load": "owner",
"unload": "owner",
"reload": "owner",
"shutdown": "owner",
"say": "owner",
"invite": "owner",
"logs": "owner",
# Utilities Commands
"translate": "utilities",
# Miscellaneous Commands
"keanu": "miscellaneous",
"labubu": "miscellaneous",
"piracy": "miscellaneous",
"tryitandsee": "miscellaneous",
"rr": "miscellaneous",
}
category_descriptions = {
@@ -183,6 +138,14 @@ class Help(commands.Cog, name="help"):
description = app_command.description.partition("\n")[0] if getattr(app_command, "description", None) else "No description available"
commands_in_category.append((app_command.name, description))
seen_names.add(app_command.name)
if hasattr(app_command, 'commands') and category not in ["owner"]:
for subcommand in app_command.commands:
if subcommand.name in seen_names:
continue
sub_desc = subcommand.description.partition("\n")[0] if getattr(subcommand, "description", None) else "No description available"
commands_in_category.append((f"{app_command.name} {subcommand.name}", sub_desc))
seen_names.add(f"{app_command.name} {subcommand.name}")
if not commands_in_category:
embed = discord.Embed(

126
cogs/idevice/__init__.py Normal file
View File

@@ -0,0 +1,126 @@
import discord
from discord import app_commands
from discord.ext import commands
from discord.ext.commands import Context
from .idevice import ideviceView
from .error_codes import errorcodes_command
from .developermode import developermode_command
from .noapps import noapps_command
from .mountddi import mountddi_command
class Idevice(commands.GroupCog, name="idevice"):
def __init__(self, bot) -> None:
self.bot = bot
super().__init__()
@commands.group(name="idevice", invoke_without_command=True)
async def idevice_group(self, context: Context):
embed = discord.Embed(
title="idevice Commands",
description="Choose a command from the dropdown below to get help with specific issues:",
color=0xfa8c4a
)
embed.set_author(name="idevice", icon_url="https://yes.nighty.works/raw/snLMuO.png")
view = ideviceView(self.bot)
await context.send(embed=embed, view=view)
@idevice_group.command(name="help")
async def idevice_group_help(self, context: Context):
embed = discord.Embed(
title="idevice Commands",
description="Choose a command from the dropdown below to get help with specific issues:",
color=0xfa8c4a
)
embed.set_author(name="idevice", icon_url="https://yes.nighty.works/raw/snLMuO.png")
view = ideviceView(self.bot)
await context.send(embed=embed, view=view)
async def _invoke_hybrid(self, context: Context, name: str):
command = self.bot.get_command(name)
if command is not None:
await context.invoke(command)
else:
await context.send(f"Unknown idevice command: {name}")
def _require_group_prefix(context: Context) -> bool:
if getattr(context, "interaction", None):
return True
group = getattr(getattr(context, "cog", None), "qualified_name", "").lower()
if not group:
return True
prefix = context.prefix or ""
content = context.message.content.strip().lower()
return content.startswith(f"{prefix}{group} ")
@idevice_group.command(name="errorcodes")
async def idevice_group_errorcodes(self, context: Context):
await self._invoke_hybrid(context, "errorcodes")
@idevice_group.command(name="developermode")
async def idevice_group_developermode(self, context: Context):
await self._invoke_hybrid(context, "developermode")
@idevice_group.command(name="noapps")
async def idevice_group_noapps(self, context: Context):
await self._invoke_hybrid(context, "noapps")
@idevice_group.command(name="mountddi")
async def idevice_group_mountddi(self, context: Context):
await self._invoke_hybrid(context, "mountddi")
@app_commands.command(
name="help",
description="idevice troubleshooting help"
)
async def help(self, interaction: discord.Interaction):
embed = discord.Embed(
title="idevice Commands",
description="Choose a command from the dropdown below to get help with specific issues:",
color=0xfa8c4a
)
embed.set_author(name="idevice", icon_url="https://yes.nighty.works/raw/snLMuO.png")
view = ideviceView(self.bot)
await interaction.response.send_message(embed=embed, view=view, ephemeral=True)
@commands.check(_require_group_prefix)
@commands.hybrid_command(
name="errorcodes",
description="Look up error codes and their meanings."
)
async def errorcodes(self, context, *, error_code: str = None):
return await errorcodes_command()(self, context, error_code=error_code)
@commands.check(_require_group_prefix)
@commands.hybrid_command(
name="developermode",
description="How to turn on developer mode"
)
async def developermode(self, context):
return await developermode_command()(self, context)
@commands.check(_require_group_prefix)
@commands.hybrid_command(
name="noapps",
description="Help when apps aren't showing in installed apps view"
)
async def noapps(self, context):
return await noapps_command()(self, context)
@commands.check(_require_group_prefix)
@commands.hybrid_command(
name="mountddi",
description="How to manually mount DDI"
)
async def mountddi(self, context):
return await mountddi_command()(self, context)
async def setup(bot) -> None:
cog = Idevice(bot)
await bot.add_cog(cog)
bot.logger.info("Loaded extension 'idevice.help'")
bot.logger.info("Loaded extension 'idevice.errorcodes'")
bot.logger.info("Loaded extension 'idevice.developermode'")
bot.logger.info("Loaded extension 'idevice.noapps'")
bot.logger.info("Loaded extension 'idevice.mountddi'")

View File

@@ -5,14 +5,11 @@ from discord.ext.commands import Context
import time
class Developermode(commands.Cog, name="developermode"):
def __init__(self, bot) -> None:
self.bot = bot
def developermode_command():
@commands.hybrid_command(
name="developermode", description="How to turn on developer mode"
)
async def developermode(self, context: Context) -> None:
async def developermode(self, context):
embed = discord.Embed(
color=0xfa8c4a,
description=(
@@ -40,7 +37,5 @@ class Developermode(commands.Cog, name="developermode"):
await context.interaction.response.send_message(embed=embed, view=view)
else:
await context.send(embed=embed, view=view)
async def setup(bot) -> None:
await bot.add_cog(Developermode(bot))
return developermode

View File

@@ -6,54 +6,41 @@ from discord.ext import commands
from discord.ext.commands import Context
class ErrorCodes(commands.Cog, name="errorcodes"):
def __init__(self, bot) -> None:
self.bot = bot
self.errors = self.load_errors()
self.key_to_data = {error['name']: (error['description'], error['code']) for error in self.errors}
self.code_to_key = {error['code']: error['name'] for error in self.errors}
def load_errors(self):
json_path = os.path.join(os.path.dirname(__file__), 'files/errorcodes.json')
try:
with open(json_path, 'r', encoding='utf-8') as f:
return json.load(f)
except FileNotFoundError:
self.bot.logger.error(f"Error codes JSON file not found: {json_path}")
return []
except json.JSONDecodeError as e:
self.bot.logger.error(f"Error parsing error codes JSON: {e}")
return []
async def errorcode_autocomplete(self, interaction: discord.Interaction, current: str):
current_lower = current.lower()
items = []
for key, (title, code) in self.key_to_data.items():
if not current or current_lower in key.lower() or current_lower in title.lower() or current_lower in str(code):
items.append(app_commands.Choice(name=f"{key} » {title} ({code})", value=key))
if len(items) >= 25:
break
return items
@commands.hybrid_command(name="errorcode", description="Look up an idevice error code by name or number")
def errorcodes_command():
@commands.hybrid_command(name="errorcodes", description="Look up an idevice error code by name or number")
@app_commands.describe(name="Start typing to search all error names and codes")
@app_commands.autocomplete(name=errorcode_autocomplete)
async def errorcode(self, context: Context, name: str):
async def errorcodes(self, context, name: str):
def load_errors():
json_path = os.path.join(os.path.dirname(__file__), 'files/errorcodes.json')
try:
with open(json_path, 'r', encoding='utf-8') as f:
return json.load(f)
except FileNotFoundError:
self.bot.logger.error(f"Error codes JSON file not found: {json_path}")
return []
except json.JSONDecodeError as e:
self.bot.logger.error(f"Error parsing error codes JSON: {e}")
return []
errors = load_errors()
key_to_data = {error['name']: (error['description'], error['code']) for error in errors}
code_to_key = {error['code']: error['name'] for error in errors}
key = name
if key not in self.key_to_data:
if key not in key_to_data:
try:
num = int(name)
key = self.code_to_key.get(num)
key = code_to_key.get(num)
except ValueError:
key = None
if key is None or key not in self.key_to_data:
if key is None or key not in key_to_data:
if context.interaction:
await context.interaction.response.send_message("Error not found.", ephemeral=True)
else:
await context.send("Error not found.")
return
title, code = self.key_to_data[key]
title, code = key_to_data[key]
embed = discord.Embed(
description=f"## Error Code: {code}\n\n**Name**: `{key}`\n**Description**: {title}",
@@ -73,9 +60,7 @@ class ErrorCodes(commands.Cog, name="errorcodes"):
await context.interaction.response.send_message(embed=embed, view=view)
else:
await context.send(embed=embed, view=view)
async def setup(bot) -> None:
cog = ErrorCodes(bot)
await bot.add_cog(cog)
return errorcodes

View File

@@ -253,14 +253,11 @@ class ideviceView(discord.ui.View):
self.add_item(ideviceSelect(bot))
class idevice(commands.Cog, name="idevice"):
def __init__(self, bot) -> None:
self.bot = bot
def idevice_command():
@commands.hybrid_command(
name="idevice", description="idevice troubleshooting and help"
)
async def idevice(self, context: Context) -> None:
async def idevice(self, context):
embed = discord.Embed(
title="idevice Commands",
description="Choose a command from the dropdown below to get help with specific issues:",
@@ -274,8 +271,5 @@ class idevice(commands.Cog, name="idevice"):
await context.interaction.response.send_message(embed=embed, view=view, ephemeral=True)
else:
await context.send(embed=embed, view=view)
async def setup(bot) -> None:
await bot.add_cog(idevice(bot))
return idevice

View File

@@ -4,16 +4,13 @@ from discord.ext.commands import Context
import os
class Mountddi(commands.Cog, name="mountddi"):
def __init__(self, bot) -> None:
self.bot = bot
def mountddi_command():
@commands.hybrid_command(
name="mountddi",
description="How to manually mount DDI"
)
async def mountddi(self, ctx: Context) -> None:
await ctx.defer()
async def mountddi(self, context):
await context.defer()
embed = discord.Embed(
color=0xfa8c4a,
@@ -46,15 +43,12 @@ class Mountddi(commands.Cog, name="mountddi"):
emoji="<:githubicon:1417717356846776340>"
))
ddi_file_path = os.path.join(os.path.dirname(__file__), 'files/DDI.zip')
file = discord.File(ddi_file_path, filename='DDI.zip') if os.path.exists(ddi_file_path) else None
if file:
await ctx.send(embed=embed, view=view, file=file)
await context.send(embed=embed, view=view, file=file)
else:
await ctx.send(embed=embed, view=view)
async def setup(bot) -> None:
await bot.add_cog(Mountddi(bot))
await context.send(embed=embed, view=view)
return mountddi

View File

@@ -5,14 +5,11 @@ from discord.ext.commands import Context
import time
class Noapps(commands.Cog, name="noapps"):
def __init__(self, bot) -> None:
self.bot = bot
def noapps_command():
@commands.hybrid_command(
name="noapps", description="Help when apps aren't showing in installed apps view"
)
async def noapps(self, context: Context) -> None:
async def noapps(self, context):
embed = discord.Embed(
color=0xfa8c4a,
description=(
@@ -41,7 +38,5 @@ class Noapps(commands.Cog, name="noapps"):
await context.interaction.response.send_message(embed=embed, view=view)
else:
await context.send(embed=embed, view=view)
async def setup(bot) -> None:
await bot.add_cog(Noapps(bot))
return noapps

View File

@@ -0,0 +1,112 @@
import discord
from discord.ext import commands
from discord.ext.commands import Context
from .rickroll import rr_command
from .labubu import labubu_command
from .tryitandsee import tryitandsee_command
from .piracy import piracy_command
from .keanu import keanu_command
class Miscellaneous(commands.GroupCog, name="misc"):
def __init__(self, bot) -> None:
self.bot = bot
super().__init__()
@commands.group(name="miscellaneous", aliases=["misc"], invoke_without_command=True)
async def miscellaneous_group(self, context: Context):
embed = discord.Embed(
title="Miscellaneous Commands",
description="Use `.misc <subcommand>` or `/misc <subcommand>`.",
color=0x7289DA
)
embed.set_author(name="Miscellaneous", icon_url="https://yes.nighty.works/raw/YxMC0r.png")
embed.add_field(name="Available", value="rr, labubu, tryitandsee, piracy, keanu", inline=False)
await context.send(embed=embed)
async def _invoke_hybrid(self, context: Context, name: str):
command = self.bot.get_command(name)
if command is not None:
await context.invoke(command)
else:
await context.send(f"Unknown miscellaneous command: {name}")
def _require_group_prefix(context: Context) -> bool:
if getattr(context, "interaction", None):
return True
group = getattr(getattr(context, "cog", None), "qualified_name", "").lower()
if not group:
return True
prefix = context.prefix or ""
content = context.message.content.strip().lower()
return content.startswith(f"{prefix}{group} ")
@miscellaneous_group.command(name="rr")
async def miscellaneous_group_rr(self, context: Context):
await self._invoke_hybrid(context, "rr")
@miscellaneous_group.command(name="labubu")
async def miscellaneous_group_labubu(self, context: Context):
await self._invoke_hybrid(context, "labubu")
@miscellaneous_group.command(name="tryitandsee")
async def miscellaneous_group_tryitandsee(self, context: Context):
await self._invoke_hybrid(context, "tryitandsee")
@miscellaneous_group.command(name="piracy")
async def miscellaneous_group_piracy(self, context: Context):
await self._invoke_hybrid(context, "piracy")
@miscellaneous_group.command(name="keanu")
async def miscellaneous_group_keanu(self, context: Context):
await self._invoke_hybrid(context, "keanu")
@commands.check(_require_group_prefix)
@commands.hybrid_command(
name="rr",
description="Rickroll"
)
async def rr(self, context):
return await rr_command()(self, context)
@commands.check(_require_group_prefix)
@commands.hybrid_command(
name="labubu",
description="Labubu ASCII art"
)
async def labubu(self, context):
return await labubu_command()(self, context)
@commands.check(_require_group_prefix)
@commands.hybrid_command(
name="tryitandsee",
description="Try it and see"
)
async def tryitandsee(self, context):
return await tryitandsee_command()(self, context)
@commands.check(_require_group_prefix)
@commands.hybrid_command(
name="piracy",
description="FBI Anti Piracy Warning"
)
async def piracy(self, context):
return await piracy_command()(self, context)
@commands.check(_require_group_prefix)
@commands.hybrid_command(
name="keanu",
description="Reeves"
)
async def keanu(self, context):
return await keanu_command()(self, context)
async def setup(bot) -> None:
cog = Miscellaneous(bot)
await bot.add_cog(cog)
bot.logger.info("Loaded extension 'miscellaneous.rr'")
bot.logger.info("Loaded extension 'miscellaneous.labubu'")
bot.logger.info("Loaded extension 'miscellaneous.tryitandsee'")
bot.logger.info("Loaded extension 'miscellaneous.piracy'")
bot.logger.info("Loaded extension 'miscellaneous.keanu'")

View File

@@ -4,15 +4,12 @@ from discord.ext.commands import Context
import random
class Keanu(commands.Cog, name="keanu"):
def __init__(self, bot) -> None:
self.bot = bot
def keanu_command():
@commands.hybrid_command(
name="keanu",
description="Reeves",
)
async def keanu(self, context: Context) -> None:
async def keanu(self, context):
images = [
"https://yes.nighty.works/raw/z0HqUM.png",
"https://yes.nighty.works/raw/1Jc0j6.avif",
@@ -48,7 +45,5 @@ class Keanu(commands.Cog, name="keanu"):
await inter.followup.send(embed=embed, ephemeral=True)
else:
await context.send(embed=embed)
async def setup(bot) -> None:
await bot.add_cog(Keanu(bot))
return keanu

View File

@@ -3,15 +3,12 @@ from discord.ext import commands
from discord.ext.commands import Context
class Labubu(commands.Cog, name="labubu"):
def __init__(self, bot) -> None:
self.bot = bot
def labubu_command():
@commands.hybrid_command(
name="labubu",
description="Labubu ASCII art",
)
async def labubu(self, context: Context) -> None:
async def labubu(self, context):
labubu_art = """⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⠿⠛⠀⠙⠿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿
⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⠗⠀⠀⣀⣄⠀⢿⣿⣿⣿⠟⠁⢠⡆⠉⠙⢻⣿⣿⣿⣿⣿⣿⣿⣿⣿
⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⠟⠀⠀⣴⣿⡟⠀⠘⣿⣿⠋⠀⠀⠀⢠⣶⡀⠈⢻⣿⣿⣿⣿⣿⣿⣿⣿
@@ -63,7 +60,5 @@ class Labubu(commands.Cog, name="labubu"):
await inter.followup.send(embed=embed, ephemeral=True)
else:
await context.send(embed=embed)
async def setup(bot) -> None:
await bot.add_cog(Labubu(bot))
return labubu

View File

@@ -3,15 +3,12 @@ from discord.ext import commands
from discord.ext.commands import Context
class Piracy(commands.Cog, name="piracy"):
def __init__(self, bot) -> None:
self.bot = bot
def piracy_command():
@commands.hybrid_command(
name="piracy",
description="FBI Anti Piracy Warning",
)
async def piracy(self, context: Context) -> None:
async def piracy(self, context):
embed = discord.Embed(
color=0xE02B2B,
)
@@ -27,7 +24,5 @@ class Piracy(commands.Cog, name="piracy"):
await inter.followup.send(embed=embed, ephemeral=True)
else:
await context.send(embed=embed)
async def setup(bot) -> None:
await bot.add_cog(Piracy(bot))
return piracy

View File

@@ -3,15 +3,12 @@ from discord.ext import commands
from discord.ext.commands import Context
class Rr(commands.Cog, name="rr"):
def __init__(self, bot) -> None:
self.bot = bot
def rr_command():
@commands.hybrid_command(
name="rr",
description="Rickroll",
)
async def rr(self, context: Context) -> None:
async def rr(self, context):
gif_url = "https://yes.nighty.works/raw/JzjMcs.gif"
embed = discord.Embed(
@@ -28,7 +25,5 @@ class Rr(commands.Cog, name="rr"):
await inter.followup.send(embed=embed, ephemeral=True)
else:
await context.send(embed=embed)
async def setup(bot) -> None:
await bot.add_cog(Rr(bot))
return rr

View File

@@ -3,15 +3,12 @@ from discord.ext import commands
from discord.ext.commands import Context
class TryItAndSee(commands.Cog, name="tryitandsee"):
def __init__(self, bot) -> None:
self.bot = bot
def tryitandsee_command():
@commands.hybrid_command(
name="tryitandsee",
description="Try it and see",
)
async def tryitandsee(self, context: Context) -> None:
async def tryitandsee(self, context):
gif_url = "https://yes.nighty.works/raw/1BQP8c.gif"
embed = discord.Embed(
@@ -28,7 +25,5 @@ class TryItAndSee(commands.Cog, name="tryitandsee"):
await inter.followup.send(embed=embed, ephemeral=True)
else:
await context.send(embed=embed)
async def setup(bot) -> None:
await bot.add_cog(TryItAndSee(bot))
return tryitandsee

139
cogs/moderation/__init__.py Normal file
View File

@@ -0,0 +1,139 @@
import discord
from discord.ext import commands
from discord.ext.commands import Context
from .ban import ban_command
from .kick import kick_command
from .purge import purge_command
from .warnings import warnings_command
from .archive import archive_command
from .hackban import hackban_command
from .nick import nick_command
class Moderation(commands.GroupCog, name="moderation"):
def __init__(self, bot) -> None:
self.bot = bot
super().__init__()
@commands.group(name="moderation", invoke_without_command=True)
async def moderation_group(self, context: Context):
embed = discord.Embed(
title="Moderation Commands",
description="Use `.moderation <subcommand>` or `/moderation <subcommand>`.",
color=0x7289DA
)
embed.add_field(name="Available", value="ban, kick, purge, warnings, archive, hackban, nick", inline=False)
await context.send(embed=embed)
async def _invoke_hybrid(self, context: Context, name: str, **kwargs):
command = self.bot.get_command(name)
if command is not None:
await context.invoke(command, **kwargs)
else:
await context.send(f"Unknown moderation command: {name}")
def _require_group_prefix(context: Context) -> bool:
if getattr(context, "interaction", None):
return True
group = getattr(getattr(context, "cog", None), "qualified_name", "").lower()
if not group:
return True
prefix = context.prefix or ""
content = context.message.content.strip().lower()
return content.startswith(f"{prefix}{group} ")
@moderation_group.command(name="ban")
async def moderation_group_ban(self, context: Context, user: discord.User, *, reason: str = "Not specified", delete_messages: str = "none"):
await self._invoke_hybrid(context, "ban", user=user, reason=reason, delete_messages=delete_messages)
@moderation_group.command(name="kick")
async def moderation_group_kick(self, context: Context, user: discord.User, *, reason: str = "Not specified"):
await self._invoke_hybrid(context, "kick", user=user, reason=reason)
@moderation_group.command(name="purge")
async def moderation_group_purge(self, context: Context, amount: int):
await self._invoke_hybrid(context, "purge", amount=amount)
@moderation_group.command(name="warnings")
async def moderation_group_warnings(self, context: Context):
await self._invoke_hybrid(context, "warnings")
@moderation_group.command(name="archive")
async def moderation_group_archive(self, context: Context, limit: int = 10):
await self._invoke_hybrid(context, "archive", limit=limit)
@moderation_group.command(name="hackban")
async def moderation_group_hackban(self, context: Context, user_id: int, *, reason: str = "Not specified"):
await self._invoke_hybrid(context, "hackban", user_id=user_id, reason=reason)
@moderation_group.command(name="nick")
async def moderation_group_nick(self, context: Context, user: discord.User, *, nickname: str = None):
await self._invoke_hybrid(context, "nick", user=user, nickname=nickname)
@commands.check(_require_group_prefix)
@commands.hybrid_command(
name="ban",
description="Bans a user from the server."
)
async def ban(self, context, user: discord.User, *, reason: str = "Not specified", delete_messages: str = "none"):
return await ban_command()(self, context, user=user, reason=reason, delete_messages=delete_messages)
@commands.check(_require_group_prefix)
@commands.hybrid_command(
name="kick",
description="Kicks a user from the server."
)
async def kick(self, context, user: discord.User, *, reason: str = "Not specified"):
return await kick_command()(self, context, user=user, reason=reason)
@commands.check(_require_group_prefix)
@commands.hybrid_command(
name="purge",
description="Delete a number of messages."
)
async def purge(self, context, amount: int):
return await purge_command()(self, context, amount=amount)
@commands.check(_require_group_prefix)
@commands.hybrid_command(
name="warnings",
description="Manage warnings of a user on a server."
)
async def warnings(self, context):
return await warnings_command()(self, context)
@commands.check(_require_group_prefix)
@commands.hybrid_command(
name="archive",
description="Archives in a text file the last messages with a chosen limit of messages."
)
async def archive(self, context, limit: int = 10):
return await archive_command()(self, context, limit=limit)
@commands.check(_require_group_prefix)
@commands.hybrid_command(
name="hackban",
description="Bans a user without the user having to be in the server."
)
async def hackban(self, context, user_id: int, *, reason: str = "Not specified"):
return await hackban_command()(self, context, user_id=user_id, reason=reason)
@commands.check(_require_group_prefix)
@commands.hybrid_command(
name="nick",
description="Change the nickname of a user on a server."
)
async def nick(self, context, user: discord.User, *, nickname: str = None):
return await nick_command()(self, context, user=user, nickname=nickname)
async def setup(bot) -> None:
cog = Moderation(bot)
await bot.add_cog(cog)
bot.logger.info("Loaded extension 'moderation.ban'")
bot.logger.info("Loaded extension 'moderation.kick'")
bot.logger.info("Loaded extension 'moderation.purge'")
bot.logger.info("Loaded extension 'moderation.warnings'")
bot.logger.info("Loaded extension 'moderation.archive'")
bot.logger.info("Loaded extension 'moderation.hackban'")
bot.logger.info("Loaded extension 'moderation.nick'")

View File

@@ -6,10 +6,7 @@ from discord.ext import commands
from discord.ext.commands import Context
class Archive(commands.Cog, name="archive"):
def __init__(self, bot) -> None:
self.bot = bot
def archive_command():
@commands.hybrid_command(
name="archive",
description="Archives in a text file the last messages with a chosen limit of messages.",
@@ -18,7 +15,7 @@ class Archive(commands.Cog, name="archive"):
@app_commands.describe(
limit="The limit of messages that should be archived.",
)
async def archive(self, context: Context, limit: int = 10) -> None:
async def archive(self, context, limit: int = 10):
"""
Archives in a text file the last messages with a chosen limit of messages. This command requires the MESSAGE_CONTENT intent to work properly.
@@ -54,7 +51,5 @@ class Archive(commands.Cog, name="archive"):
f = discord.File(log_file)
await context.send(file=f)
os.remove(log_file)
async def setup(bot) -> None:
await bot.add_cog(Archive(bot))
return archive

View File

@@ -4,10 +4,7 @@ from discord.ext import commands
from discord.ext.commands import Context
class Ban(commands.Cog, name="ban"):
def __init__(self, bot) -> None:
self.bot = bot
def ban_command():
@commands.hybrid_command(
name="ban",
description="Bans a user from the server.",
@@ -27,8 +24,8 @@ class Ban(commands.Cog, name="ban"):
app_commands.Choice(name="Last 7 days", value="7d"),
])
async def ban(
self, context: Context, user: discord.User, *, reason: str = "Not specified", delete_messages: str = "none"
) -> None:
self, context, user: discord.User, *, reason: str = "Not specified", delete_messages: str = "none"
):
try:
member = context.guild.get_member(user.id)
if not member:
@@ -211,7 +208,5 @@ class Ban(commands.Cog, name="ban"):
"7d": "Last 7 days"
}
return time_formats.get(delete_option, "Unknown time period")
async def setup(bot) -> None:
await bot.add_cog(Ban(bot))
return ban

View File

@@ -4,10 +4,7 @@ from discord.ext import commands
from discord.ext.commands import Context
class HackBan(commands.Cog, name="hackban"):
def __init__(self, bot) -> None:
self.bot = bot
def hackban_command():
@commands.hybrid_command(
name="hackban",
description="Bans a user without the user having to be in the server.",
@@ -19,8 +16,8 @@ class HackBan(commands.Cog, name="hackban"):
reason="The reason why the user should be banned.",
)
async def hackban(
self, context: Context, user_id: str, *, reason: str = "Not specified"
) -> None:
self, context, user_id: str, *, reason: str = "Not specified"
):
"""
Bans a user without the user having to be in the server.
@@ -47,7 +44,5 @@ class HackBan(commands.Cog, name="hackban"):
color=0xE02B2B,
).set_author(name="Moderation", icon_url="https://yes.nighty.works/raw/CPKHQd.png")
await context.send(embed=embed)
async def setup(bot) -> None:
await bot.add_cog(HackBan(bot))
return hackban

View File

@@ -4,10 +4,7 @@ from discord.ext import commands
from discord.ext.commands import Context
class Kick(commands.Cog, name="kick"):
def __init__(self, bot) -> None:
self.bot = bot
def kick_command():
@commands.hybrid_command(
name="kick",
description="Kicks a user from the server.",
@@ -17,8 +14,8 @@ class Kick(commands.Cog, name="kick"):
reason="The reason why the user should be kicked.",
)
async def kick(
self, context: Context, user: discord.User, *, reason: str = "Not specified"
) -> None:
self, context, user: discord.User, *, reason: str = "Not specified"
):
try:
member = context.guild.get_member(user.id)
if not member:
@@ -122,7 +119,5 @@ class Kick(commands.Cog, name="kick"):
color=0xE02B2B,
).set_author(name="Moderation", icon_url="https://yes.nighty.works/raw/CPKHQd.png")
await context.send(embed=embed, ephemeral=True)
async def setup(bot) -> None:
await bot.add_cog(Kick(bot))
return kick

View File

@@ -4,10 +4,7 @@ from discord.ext import commands
from discord.ext.commands import Context
class Nick(commands.Cog, name="nick"):
def __init__(self, bot) -> None:
self.bot = bot
def nick_command():
@commands.hybrid_command(
name="nick",
description="Change the nickname of a user on a server.",
@@ -17,8 +14,8 @@ class Nick(commands.Cog, name="nick"):
nickname="The new nickname that should be set.",
)
async def nick(
self, context: Context, user: discord.User, *, nickname: str = None
) -> None:
self, context, user: discord.User, *, nickname: str = None
):
"""
Change the nickname of a user on a server.
@@ -60,7 +57,5 @@ class Nick(commands.Cog, name="nick"):
color=0xE02B2B,
).set_author(name="Moderation", icon_url="https://yes.nighty.works/raw/CPKHQd.png")
await context.send(embed=embed, ephemeral=True)
async def setup(bot) -> None:
await bot.add_cog(Nick(bot))
return nick

View File

@@ -4,10 +4,7 @@ from discord.ext import commands
from discord.ext.commands import Context
class Purge(commands.Cog, name="purge"):
def __init__(self, bot) -> None:
self.bot = bot
def purge_command():
@commands.hybrid_command(
name="purge",
description="Delete a number of messages.",
@@ -15,13 +12,7 @@ class Purge(commands.Cog, name="purge"):
@commands.has_guild_permissions(manage_messages=True)
@commands.bot_has_permissions(manage_messages=True)
@app_commands.describe(amount="The amount of messages that should be deleted.")
async def purge(self, context: Context, amount: int) -> None:
"""
Delete a number of messages.
:param context: The hybrid command context.
:param amount: The number of messages that should be deleted.
"""
async def purge(self, context, amount: int):
await context.send("Deleting messages...")
purged_messages = await context.channel.purge(limit=amount + 1)
embed = discord.Embed(
@@ -31,7 +22,5 @@ class Purge(commands.Cog, name="purge"):
)
embed.set_author(name="Moderation", icon_url="https://yes.nighty.works/raw/CPKHQd.png")
await context.channel.send(embed=embed)
async def setup(bot) -> None:
await bot.add_cog(Purge(bot))
return purge

View File

@@ -4,11 +4,8 @@ from discord.ext import commands
from discord.ext.commands import Context
class Warnings(commands.Cog, name="warnings"):
def __init__(self, bot) -> None:
self.bot = bot
async def send_embed(self, context: Context, embed: discord.Embed, *, ephemeral: bool = False) -> None:
def warnings_command():
async def send_embed(context, embed: discord.Embed, *, ephemeral: bool = False) -> None:
interaction = getattr(context, "interaction", None)
if interaction is not None:
if interaction.response.is_done():
@@ -22,7 +19,7 @@ class Warnings(commands.Cog, name="warnings"):
name="warning",
description="Manage warnings of a user on a server.",
)
async def warning(self, context: Context) -> None:
async def warning(self, context) -> None:
"""
Manage warnings of a user on a server.
@@ -170,5 +167,4 @@ class Warnings(commands.Cog, name="warnings"):
async def setup(bot) -> None:
await bot.add_cog(Warnings(bot))
return warning

198
cogs/sidestore/__init__.py Normal file
View File

@@ -0,0 +1,198 @@
import discord
from discord import app_commands
from discord.ext import commands
from discord.ext.commands import Context
from .sidestore import SidestoreView
from .refresh import refresh_command
from .code import code_command
from .crash import crash_command
from .pairing import pairing_command
from .server import server_command
from .afc import afc_command
from .udid import udid_command
from .half import half_command
from .sparse import sparse_command
class Sidestore(commands.GroupCog, name="sidestore"):
def __init__(self, bot) -> None:
self.bot = bot
super().__init__()
@commands.group(name="sidestore", invoke_without_command=True)
async def sidestore_group(self, context: Context):
embed = discord.Embed(
title="SideStore Commands",
description="Choose a command from the dropdown below to get help with specific issues:",
color=0x8e82f9
)
embed.set_author(name="SideStore", icon_url="https://github.com/SideStore/assets/blob/main/icons/classic/Default.png?raw=true")
view = SidestoreView(self.bot)
await context.send(embed=embed, view=view)
@sidestore_group.command(name="help")
async def sidestore_group_help(self, context: Context):
embed = discord.Embed(
title="SideStore Commands",
description="Choose a command from the dropdown below to get help with specific issues:",
color=0x8e82f9
)
embed.set_author(name="SideStore", icon_url="https://github.com/SideStore/assets/blob/main/icons/classic/Default.png?raw=true")
view = SidestoreView(self.bot)
await context.send(embed=embed, view=view)
async def _invoke_hybrid(self, context: Context, name: str):
command = self.bot.get_command(name)
if command is not None:
await context.invoke(command)
else:
await context.send(f"Unknown SideStore command: {name}")
def _require_group_prefix(context: Context) -> bool:
if getattr(context, "interaction", None):
return True
group = getattr(getattr(context, "cog", None), "qualified_name", "").lower()
if not group:
return True
prefix = context.prefix or ""
content = context.message.content.strip().lower()
return content.startswith(f"{prefix}{group} ")
@sidestore_group.command(name="refresh")
async def sidestore_group_refresh(self, context: Context):
await self._invoke_hybrid(context, "refresh")
@sidestore_group.command(name="code")
async def sidestore_group_code(self, context: Context):
await self._invoke_hybrid(context, "code")
@sidestore_group.command(name="crash")
async def sidestore_group_crash(self, context: Context):
await self._invoke_hybrid(context, "crash")
@sidestore_group.command(name="pairing")
async def sidestore_group_pairing(self, context: Context):
await self._invoke_hybrid(context, "pairing")
@sidestore_group.command(name="server")
async def sidestore_group_server(self, context: Context):
await self._invoke_hybrid(context, "server")
@sidestore_group.command(name="afc")
async def sidestore_group_afc(self, context: Context):
await self._invoke_hybrid(context, "afc")
@sidestore_group.command(name="udid")
async def sidestore_group_udid(self, context: Context):
await self._invoke_hybrid(context, "udid")
@sidestore_group.command(name="half")
async def sidestore_group_half(self, context: Context):
await self._invoke_hybrid(context, "half")
@sidestore_group.command(name="sparse")
async def sidestore_group_sparse(self, context: Context):
await self._invoke_hybrid(context, "sparse")
@app_commands.command(
name="help",
description="SideStore troubleshooting help"
)
async def help(self, interaction: discord.Interaction):
embed = discord.Embed(
title="SideStore Commands",
description="Choose a command from the dropdown below to get help with specific issues:",
color=0x8e82f9
)
embed.set_author(name="SideStore", icon_url="https://github.com/SideStore/assets/blob/main/icons/classic/Default.png?raw=true")
view = SidestoreView(self.bot)
await interaction.response.send_message(embed=embed, view=view, ephemeral=True)
@commands.check(_require_group_prefix)
@commands.hybrid_command(
name="refresh",
description="Help with refreshing or installing apps"
)
async def refresh(self, context):
return await refresh_command()(self, context)
@commands.check(_require_group_prefix)
@commands.hybrid_command(
name="code",
description="No code received when signing in with Apple ID"
)
async def code(self, context):
return await code_command()(self, context)
@commands.check(_require_group_prefix)
@commands.hybrid_command(
name="crash",
description="Help with SideStore crashing issues"
)
async def crash(self, context):
return await crash_command()(self, context)
@commands.check(_require_group_prefix)
@commands.hybrid_command(
name="pairing",
description="Help with pairing file issues"
)
async def pairing(self, context):
return await pairing_command()(self, context)
@commands.check(_require_group_prefix)
@commands.hybrid_command(
name="server",
description="Help with anisette server issues"
)
async def server(self, context):
return await server_command()(self, context)
@commands.check(_require_group_prefix)
@commands.hybrid_command(
name="afc",
description="Help with AFC Connection Failure issues"
)
async def afc(self, context):
return await afc_command()(self, context)
@commands.check(_require_group_prefix)
@commands.hybrid_command(
name="udid",
description="SideStore could not determine device UDID"
)
async def udid(self, context):
return await udid_command()(self, context)
@commands.check(_require_group_prefix)
@commands.hybrid_command(
name="half",
description="Help with half-installed apps"
)
async def half(self, context):
return await half_command()(self, context)
@commands.check(_require_group_prefix)
@commands.hybrid_command(
name="sparse",
description="Help with sparse bundle issues"
)
async def sparse(self, context):
return await sparse_command()(self, context)
async def setup(bot) -> None:
cog = Sidestore(bot)
await bot.add_cog(cog)
bot.logger.info("Loaded extension 'sidestore.help'")
bot.logger.info("Loaded extension 'sidestore.refresh'")
bot.logger.info("Loaded extension 'sidestore.code'")
bot.logger.info("Loaded extension 'sidestore.crash'")
bot.logger.info("Loaded extension 'sidestore.pairing'")
bot.logger.info("Loaded extension 'sidestore.server'")
bot.logger.info("Loaded extension 'sidestore.afc'")
bot.logger.info("Loaded extension 'sidestore.udid'")
bot.logger.info("Loaded extension 'sidestore.half'")
bot.logger.info("Loaded extension 'sidestore.sparse'")

View File

@@ -5,14 +5,11 @@ from discord.ext.commands import Context
import time
class Afc(commands.Cog, name="afc"):
def __init__(self, bot) -> None:
self.bot = bot
def afc_command():
@commands.hybrid_command(
name="afc", description="Help with AFC Connection Failure issues"
)
async def afc(self, context: Context) -> None:
async def afc(self, context):
embed = discord.Embed(
color=0x8e82f9,
description=(
@@ -45,5 +42,4 @@ class Afc(commands.Cog, name="afc"):
await context.send(embed=embed, view=view)
async def setup(bot) -> None:
await bot.add_cog(Afc(bot))
return afc

View File

@@ -5,14 +5,11 @@ from discord.ext.commands import Context
import time
class Code(commands.Cog, name="code"):
def __init__(self, bot) -> None:
self.bot = bot
def code_command():
@commands.hybrid_command(
name="code", description="No code received when signing in with Apple ID"
)
async def code(self, context: Context) -> None:
async def code(self, context):
embed = discord.Embed(
color=0x8e82f9,
description=(
@@ -59,5 +56,4 @@ class Code(commands.Cog, name="code"):
await context.send(embed=embed, view=view)
async def setup(bot) -> None:
await bot.add_cog(Code(bot))
return code

View File

@@ -5,14 +5,11 @@ from discord.ext.commands import Context
import time
class Crash(commands.Cog, name="crash"):
def __init__(self, bot) -> None:
self.bot = bot
def crash_command():
@commands.hybrid_command(
name="crash", description="Help with SideStore crashing issues"
)
async def crash(self, context: Context) -> None:
async def crash(self, context):
embed = discord.Embed(
color=0x8e82f9,
description=(
@@ -43,5 +40,4 @@ class Crash(commands.Cog, name="crash"):
await context.send(embed=embed, view=view)
async def setup(bot) -> None:
await bot.add_cog(Crash(bot))
return crash

View File

@@ -5,14 +5,11 @@ from discord.ext.commands import Context
import time
class Half(commands.Cog, name="half"):
def __init__(self, bot) -> None:
self.bot = bot
def half_command():
@commands.hybrid_command(
name="half", description="Help when apps get stuck installing"
)
async def half(self, context: Context) -> None:
async def half(self, context):
embed = discord.Embed(
color=0x8e82f9,
description=(
@@ -52,5 +49,4 @@ class Half(commands.Cog, name="half"):
await context.send(embed=embed, view=view)
async def setup(bot) -> None:
await bot.add_cog(Half(bot))
return half

View File

@@ -5,14 +5,11 @@ from discord.ext.commands import Context
import time
class Pairing(commands.Cog, name="pairing"):
def __init__(self, bot) -> None:
self.bot = bot
def pairing_command():
@commands.hybrid_command(
name="pairing", description="Help with pairing file issues"
)
async def pairing(self, context: Context) -> None:
async def pairing(self, context):
embed = discord.Embed(
color=0x8e82f9,
description=(
@@ -57,5 +54,4 @@ class Pairing(commands.Cog, name="pairing"):
await context.send(embed=embed, view=view)
async def setup(bot) -> None:
await bot.add_cog(Pairing(bot))
return pairing

View File

@@ -5,14 +5,11 @@ from discord.ext.commands import Context
import time
class Refresh(commands.Cog, name="refresh"):
def __init__(self, bot) -> None:
self.bot = bot
def refresh_command():
@commands.hybrid_command(
name="refresh", description="Help with refreshing or installing apps"
)
async def refresh(self, context: Context) -> None:
async def refresh(self, context):
embed = discord.Embed(
color=0x8e82f9,
description=(
@@ -47,5 +44,4 @@ class Refresh(commands.Cog, name="refresh"):
await context.send(embed=embed, view=view)
async def setup(bot) -> None:
await bot.add_cog(Refresh(bot))
return refresh

View File

@@ -5,14 +5,11 @@ from discord.ext.commands import Context
import time
class Server(commands.Cog, name="server"):
def __init__(self, bot) -> None:
self.bot = bot
def server_command():
@commands.hybrid_command(
name="server", description="Help with anisette server issues"
)
async def server(self, context: Context) -> None:
async def server(self, context):
embed = discord.Embed(
color=0x8e82f9,
description=(
@@ -51,5 +48,4 @@ class Server(commands.Cog, name="server"):
await context.send(embed=embed, view=view)
async def setup(bot) -> None:
await bot.add_cog(Server(bot))
return server

View File

@@ -107,14 +107,11 @@ class SidestoreView(discord.ui.View):
self.add_item(SidestoreSelect(bot))
class Sidestore(commands.Cog, name="sidestore"):
def __init__(self, bot) -> None:
self.bot = bot
def sidestore_command():
@commands.hybrid_command(
name="sidestore", description="SideStore troubleshooting and help"
name="help", description="SideStore troubleshooting and help"
)
async def sidestore(self, context: Context) -> None:
async def sidestore(self, context):
embed = discord.Embed(
title="SideStore Commands",
description="Choose a command from the dropdown below to get help with specific issues:",
@@ -128,7 +125,5 @@ class Sidestore(commands.Cog, name="sidestore"):
await context.interaction.response.send_message(embed=embed, view=view, ephemeral=True)
else:
await context.send(embed=embed, view=view)
async def setup(bot) -> None:
await bot.add_cog(Sidestore(bot))
return sidestore

View File

@@ -5,14 +5,11 @@ from discord.ext.commands import Context
import time
class Sparse(commands.Cog, name="sparse"):
def __init__(self, bot) -> None:
self.bot = bot
def sparse_command():
@commands.hybrid_command(
name="sparse", description="Information about SparseRestore exploit"
)
async def sparse(self, context: Context) -> None:
async def sparse(self, context):
embed = discord.Embed(
color=0x8e82f9,
description=(
@@ -46,5 +43,4 @@ class Sparse(commands.Cog, name="sparse"):
await context.send(embed=embed, view=view)
async def setup(bot) -> None:
await bot.add_cog(Sparse(bot))
return sparse

View File

@@ -5,14 +5,11 @@ from discord.ext.commands import Context
import time
class Udid(commands.Cog, name="udid"):
def __init__(self, bot) -> None:
self.bot = bot
def udid_command():
@commands.hybrid_command(
name="udid", description="SideStore could not determine device UDID"
)
async def udid(self, context: Context) -> None:
async def udid(self, context):
embed = discord.Embed(
color=0x8e82f9,
description=(
@@ -45,5 +42,4 @@ class Udid(commands.Cog, name="udid"):
await context.send(embed=embed, view=view)
async def setup(bot) -> None:
await bot.add_cog(Udid(bot))
return udid

View File

@@ -0,0 +1,56 @@
import discord
from discord.ext import commands
from discord.ext.commands import Context
from .translate import translate_command
class Utilities(commands.GroupCog, name="utils"):
def __init__(self, bot) -> None:
self.bot = bot
super().__init__()
@commands.group(name="utilities", aliases=["utils"], invoke_without_command=True)
async def utilities_group(self, context: Context):
embed = discord.Embed(
title="Utilities Commands",
description="Use `.utils <subcommand>` or `/utils <subcommand>`.",
color=0x7289DA
)
embed.set_author(name="Utilities", icon_url="https://yes.nighty.works/raw/8VLDcg.webp")
embed.add_field(name="Available", value="translate", inline=False)
await context.send(embed=embed)
async def _invoke_hybrid(self, context: Context, name: str, **kwargs):
command = self.bot.get_command(name)
if command is not None:
await context.invoke(command, **kwargs)
else:
await context.send(f"Unknown utilities command: {name}")
def _require_group_prefix(context: Context) -> bool:
if getattr(context, "interaction", None):
return True
group = getattr(getattr(context, "cog", None), "qualified_name", "").lower()
if not group:
return True
prefix = context.prefix or ""
content = context.message.content.strip().lower()
return content.startswith(f"{prefix}{group} ")
@utilities_group.command(name="translate")
async def utilities_group_translate(self, context: Context, text: str = None, to_lang: str = "en", from_lang: str = None):
await self._invoke_hybrid(context, "translate", text=text, to_lang=to_lang, from_lang=from_lang)
@commands.check(_require_group_prefix)
@commands.hybrid_command(
name="translate",
description="Translate text to another language"
)
async def translate(self, context, text: str = None, to_lang: str = "en", from_lang: str = None):
return await translate_command()(self, context, text=text, to_lang=to_lang, from_lang=from_lang)
async def setup(bot) -> None:
cog = Utilities(bot)
await bot.add_cog(cog)
bot.logger.info("Loaded extension 'utilities.translate'")

View File

@@ -9,10 +9,8 @@ import json
import urllib.parse
class Translate(commands.Cog, name="translate"):
def __init__(self, bot) -> None:
self.bot = bot
self.languages = {
def translate_command():
languages = {
"auto": "Auto-detect",
"en": "English",
"es": "Spanish",
@@ -136,7 +134,7 @@ class Translate(commands.Cog, name="translate"):
"lv": "Latvian",
}
async def send_embed(self, context: Context, embed: discord.Embed, *, ephemeral: bool = False) -> None:
async def send_embed(context, embed: discord.Embed, *, ephemeral: bool = False) -> None:
interaction = getattr(context, "interaction", None)
if interaction is not None:
if interaction.response.is_done():
@@ -146,7 +144,7 @@ class Translate(commands.Cog, name="translate"):
else:
await context.send(embed=embed)
async def _translate_with_google_web(self, text: str, from_lang: str = "auto", to_lang: str = "en") -> dict:
async def _translate_with_google_web(text: str, from_lang: str = "auto", to_lang: str = "en") -> dict:
try:
base_url = "https://translate.googleapis.com/translate_a/single"
@@ -196,11 +194,11 @@ class Translate(commands.Cog, name="translate"):
except Exception:
return None
async def language_autocomplete(self, interaction: discord.Interaction, current: str) -> list[app_commands.Choice[str]]:
async def language_autocomplete(interaction: discord.Interaction, current: str) -> list[app_commands.Choice[str]]:
current = current.lower()
choices = []
for code, name in self.languages.items():
for code, name in languages.items():
if current in code.lower() or current in name.lower():
display_name = f"{code} - {name}"
if len(display_name) > 100:
@@ -213,7 +211,7 @@ class Translate(commands.Cog, name="translate"):
if not choices:
popular = ["en", "es", "fr", "de", "it", "pt", "ru", "ja", "ko", "zh-CN"]
for code in popular:
name = self.languages.get(code, code)
name = languages.get(code, code)
choices.append(app_commands.Choice(name=f"{code} - {name}", value=code))
return choices
@@ -229,7 +227,7 @@ class Translate(commands.Cog, name="translate"):
)
@app_commands.autocomplete(to_lang=language_autocomplete)
@app_commands.autocomplete(from_lang=language_autocomplete)
async def translate(self, context: Context, text: str = None, to_lang: str = "en", from_lang: str = None):
async def translate(self, context, text: str = None, to_lang: str = "en", from_lang: str = None):
if not text or not text.strip():
if context.message and context.message.reference and context.message.reference.resolved:
replied_message = context.message.reference.resolved
@@ -241,7 +239,7 @@ class Translate(commands.Cog, name="translate"):
description="The replied message has no text content to translate.",
color=0xE02B2B,
).set_author(name="Utility", icon_url="https://yes.nighty.works/raw/8VLDcg.webp")
await self.send_embed(context, embed, ephemeral=True)
await send_embed(context, embed, ephemeral=True)
return
else:
embed = discord.Embed(
@@ -249,34 +247,34 @@ class Translate(commands.Cog, name="translate"):
description="Please provide text to translate or reply to a message with text.",
color=0xE02B2B,
).set_author(name="Utility", icon_url="https://yes.nighty.works/raw/8VLDcg.webp")
await self.send_embed(context, embed, ephemeral=True)
await send_embed(context, embed, ephemeral=True)
return
if to_lang not in self.languages:
if to_lang not in languages:
embed = discord.Embed(
title="Error",
description=f"Invalid target language code: `{to_lang}`. Use the autocomplete feature to see available languages.",
color=0xE02B2B,
).set_author(name="Utility", icon_url="https://yes.nighty.works/raw/8VLDcg.webp")
await self.send_embed(context, embed, ephemeral=True)
await send_embed(context, embed, ephemeral=True)
return
if from_lang and from_lang not in self.languages:
if from_lang and from_lang not in languages:
embed = discord.Embed(
title="Error",
description=f"Invalid source language code: `{from_lang}`. Use the autocomplete feature to see available languages.",
color=0xE02B2B,
).set_author(name="Utility", icon_url="https://yes.nighty.works/raw/8VLDcg.webp")
await self.send_embed(context, embed, ephemeral=True)
await send_embed(context, embed, ephemeral=True)
return
result = await self._translate_with_google_web(text, from_lang or "auto", to_lang)
result = await _translate_with_google_web(text, from_lang or "auto", to_lang)
if result and result.get("translatedText"):
detected_lang = result.get("detectedSourceLanguage", from_lang or "auto")
from_lang_name = self.languages.get(detected_lang, detected_lang)
to_lang_name = self.languages.get(to_lang, to_lang)
from_lang_name = languages.get(detected_lang, detected_lang)
to_lang_name = languages.get(to_lang, to_lang)
embed = discord.Embed(
title="Translation",
@@ -286,18 +284,17 @@ class Translate(commands.Cog, name="translate"):
embed.set_author(name="Utility", icon_url="https://yes.nighty.works/raw/8VLDcg.webp")
embed.set_footer(text=f"{from_lang_name} » {to_lang_name}")
await self.send_embed(context, embed)
await send_embed(context, embed)
else:
embed = discord.Embed(
title="Error",
description="Translation failed. Please try again later.",
color=0xE02B2B,
).set_author(name="Utility", icon_url="https://yes.nighty.works/raw/8VLDcg.webp")
await self.send_embed(context, embed, ephemeral=True)
await send_embed(context, embed, ephemeral=True)
async def setup(bot):
await bot.add_cog(Translate(bot))
return translate