refactor(idevice): commands into a single GroupCog

Merged idevice-related commands into a unified GroupCog in cogs/idevice/__init__.py, replacing individual Cog classes with command factory functions. Updated bot.py and help.py to support the new structure and improved command categorization. This refactor simplifies extension loading and command management for idevice troubleshooting features.
This commit is contained in:
neoarz
2025-09-28 22:57:26 -04:00
parent 72cdd9b403
commit 51393ece85
8 changed files with 110 additions and 98 deletions

2
bot.py
View File

@@ -90,7 +90,7 @@ class DiscordBot(commands.Bot):
if os.path.exists(init_file): if os.path.exists(init_file):
try: try:
await self.load_extension(f"cogs.{folder}") await self.load_extension(f"cogs.{folder}")
if folder not in ["fun", "general"]: if folder not in ["fun", "general", "idevice"]:
self.logger.info(f"Loaded extension '{folder}'") self.logger.info(f"Loaded extension '{folder}'")
except Exception as e: except Exception as e:
exception = f"{type(e).__name__}: {e}" exception = f"{type(e).__name__}: {e}"

View File

@@ -38,17 +38,14 @@ class Help(commands.Cog, name="help"):
category_mapping = { category_mapping = {
# General Commands # General Commands
"help": "general", "general": "general",
"botinfo": "general",
"serverinfo": "general",
"ping": "general",
"feedback": "general",
"uptime": "general",
# "context_menus": "general",
# Fun Commands # Fun Commands
"fun": "fun", "fun": "fun",
# idevice Commands
"idevice": "idevice",
# Moderation Commands # Moderation Commands
"kick": "moderation", "kick": "moderation",
"ban": "moderation", "ban": "moderation",
@@ -70,13 +67,6 @@ class Help(commands.Cog, name="help"):
"afc": "sidestore", "afc": "sidestore",
"udid": "sidestore", "udid": "sidestore",
# idevice Commands
"idevice": "idevice",
"noapps": "idevice",
"errorcodes": "idevice",
"developermode": "idevice",
"mountddi": "idevice",
# Owner Commands # Owner Commands
"sync": "owner", "sync": "owner",
"cog_management": "owner", "cog_management": "owner",
@@ -180,7 +170,7 @@ class Help(commands.Cog, name="help"):
commands_in_category.append((app_command.name, description)) commands_in_category.append((app_command.name, description))
seen_names.add(app_command.name) seen_names.add(app_command.name)
if hasattr(app_command, 'commands') and category == "fun": if hasattr(app_command, 'commands') and category in ["fun", "general", "idevice"]:
for subcommand in app_command.commands: for subcommand in app_command.commands:
if subcommand.name in seen_names: if subcommand.name in seen_names:
continue continue

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

@@ -0,0 +1,59 @@
import discord
from discord.ext import commands
from discord.ext.commands import Context
from .idevice import idevice_command
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.hybrid_command(
name="idevice",
description="Get help with idevice commands and troubleshooting."
)
async def idevice(self, context):
return await idevice_command()(self, context)
@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.hybrid_command(
name="developermode",
description="How to turn on developer mode"
)
async def developermode(self, context):
return await developermode_command()(self, context)
@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.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.idevice'")
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 import time
class Developermode(commands.Cog, name="developermode"): def developermode_command():
def __init__(self, bot) -> None:
self.bot = bot
@commands.hybrid_command( @commands.hybrid_command(
name="developermode", description="How to turn on developer mode" name="developermode", description="How to turn on developer mode"
) )
async def developermode(self, context: Context) -> None: async def developermode(self, context):
embed = discord.Embed( embed = discord.Embed(
color=0xfa8c4a, color=0xfa8c4a,
description=( description=(
@@ -41,6 +38,4 @@ class Developermode(commands.Cog, name="developermode"):
else: else:
await context.send(embed=embed, view=view) await context.send(embed=embed, view=view)
return developermode
async def setup(bot) -> None:
await bot.add_cog(Developermode(bot))

View File

@@ -6,54 +6,41 @@ from discord.ext import commands
from discord.ext.commands import Context from discord.ext.commands import Context
class ErrorCodes(commands.Cog, name="errorcodes"): def errorcodes_command():
def __init__(self, bot) -> None: @commands.hybrid_command(name="errorcodes", description="Look up an idevice error code by name or number")
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")
@app_commands.describe(name="Start typing to search all error names and codes") @app_commands.describe(name="Start typing to search all error names and codes")
@app_commands.autocomplete(name=errorcode_autocomplete) async def errorcodes(self, context, name: str):
async def errorcode(self, context: 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 key = name
if key not in self.key_to_data: if key not in key_to_data:
try: try:
num = int(name) num = int(name)
key = self.code_to_key.get(num) key = code_to_key.get(num)
except ValueError: except ValueError:
key = None 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: if context.interaction:
await context.interaction.response.send_message("Error not found.", ephemeral=True) await context.interaction.response.send_message("Error not found.", ephemeral=True)
else: else:
await context.send("Error not found.") await context.send("Error not found.")
return return
title, code = self.key_to_data[key] title, code = key_to_data[key]
embed = discord.Embed( embed = discord.Embed(
description=f"## Error Code: {code}\n\n**Name**: `{key}`\n**Description**: {title}", description=f"## Error Code: {code}\n\n**Name**: `{key}`\n**Description**: {title}",
@@ -74,8 +61,6 @@ class ErrorCodes(commands.Cog, name="errorcodes"):
else: else:
await context.send(embed=embed, view=view) await context.send(embed=embed, view=view)
async def setup(bot) -> None: return errorcodes
cog = ErrorCodes(bot)
await bot.add_cog(cog)

View File

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

View File

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

View File

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