Files
hmgrisen/bot/src/cogs/dev_tools.py
T
2026-11-03 01:42:21 +01:00

274 lines
9.3 KiB
Python

import asyncio
from os import listdir
import discord
from discord.ext import commands
from cogs.utils import embed_templates
class DevTools(commands.Cog):
"""Commands for developers to mangage cogs and other functionality of the bot"""
def __init__(self, bot: commands.Bot):
"""
Parameters
----------
bot (commands.Bot): The bot instance
"""
self.bot = bot
@commands.is_owner()
@commands.bot_has_permissions(embed_links=True)
@commands.cooldown(1, 10, commands.BucketType.guild)
@commands.command(name="changepresence", description="Endre botten sin nåværende status")
async def changepresence(self, ctx: commands.Context, activity_type: str, message: str, status_type: str):
"""
Change the bot's presence status
Parameters
----------
ctx (commands.Context): Context object
activity_type (str): The type of activity to set the bot's status to
message (str): The message to set the bot's status to
status_type (str): The type of status to set the bot's status to
"""
activities = {"playing": 0, "listening": 2, "watching": 3}
activity_type = activities.get(activity_type, 0)
status_types = {
"online": discord.Status.online,
"dnd": discord.Status.dnd,
"idle": discord.Status.idle,
"offline": discord.Status.offline,
}
status_type = status_types.get(status_type, discord.Status.online)
await self.bot.change_presence(status=status_type, activity=discord.Activity(type=activity_type, name=message))
embed = discord.Embed(color=ctx.me.color, description="Endret Presence!")
await ctx.reply(embed=embed)
@commands.is_owner()
@commands.bot_has_permissions(embed_links=True, add_reactions=True)
@commands.command(name="leave", description="Forlater en server. Om ingen er gitt, forlat der det ble sendt fra")
async def leave(self, ctx: commands.Context, guild_id: int | None = None):
"""
Make bot leave a specified guild. If no guild is specified, leave the guild the command was sent from
Parameters
----------
ctx (commands.Context): Context object
guild_id (int): The ID of the guild to leave
"""
# If no guild id is specified, leave the current guild
guild_id = guild_id if guild_id else ctx.guild.id
# Get guild
try:
guild = await self.bot.fetch_guild(guild_id)
except (discord.errors.Forbidden, discord.errors.NotFound):
embed = embed_templates.error_warning("Bot is not a member of this guild")
return await ctx.reply(embed=embed)
# Send confirmation message for leaving
confirmation_msg = await ctx.reply(f"Do you want to leave {guild.name} (`{guild.id}`)?")
await confirmation_msg.add_reaction("✅")
# Check confirmation
def comfirm(reaction: discord.Reaction, user: discord.Member):
return reaction.message.id == confirmation_msg.id and user == ctx.author and str(reaction.emoji) == "✅"
try:
await self.bot.wait_for("reaction_add", timeout=15.0, check=comfirm)
except asyncio.TimeoutError:
await ctx.message.delete()
await confirmation_msg.delete()
else:
await guild.leave()
try:
embed = discord.Embed(color=ctx.me.color, description="Guild left!")
await ctx.reply(embed=embed)
except discord.errors.Forbidden:
pass
@commands.is_owner()
@commands.bot_has_permissions(embed_links=True)
@commands.group(name="cogs", description="Administrer cogs")
async def cogs(self, ctx: commands.Context):
"""
Cog management commands
Parameters
----------
ctx (commands.Context): Context object
"""
if not ctx.invoked_subcommand:
await ctx.send_help(ctx.command)
@cogs.command(name="unload", description="Avslutt en cog")
async def cogs_unload(self, ctx: commands.Context, cog: str):
"""
Disables a specified cog
Parameters
----------
ctx (commands.Context): Context object
cog (str): The name of the cog to disable
"""
for file in listdir("./src/cogs"):
if not file.endswith(".py"):
continue
name = file[:-3]
if name == cog:
await self.bot.unload_extension(f"cogs.{name}")
embed = discord.Embed(color=ctx.me.color, description=f"{cog} has been disabled")
return await ctx.reply(embed=embed)
embed = embed_templates.error_warning(f"{cog} does not exist")
await ctx.reply(embed=embed)
@cogs.command(name="load", description="Aktiver en cog")
async def cogs_load(self, ctx: commands.Context, cog: str):
"""
Enables a speicifed cog
Parameters
----------
ctx (commands.Context): Context object
cog (str): The name of the cog to enable
"""
for file in listdir("./src/cogs"):
if not file.endswith(".py"):
continue
name = file[:-3]
if name == cog:
await self.bot.load_extension(f"cogs.{name}")
embed = discord.Embed(color=ctx.me.color, description=f"{cog} loaded")
return await ctx.reply(embed=embed)
embed = embed_templates.error_warning(f"{cog} does not exist")
await ctx.reply(embed=embed)
@cogs.command(name="reload", description="Last inn en cog på nytt")
async def cogs_reload(self, ctx: commands.Context, cog: str):
"""
Reloads a specified cog
Parameters
----------
ctx (commands.Context): Context object
cog (str): The name of the cog to reload
"""
for file in listdir("./src/cogs"):
if not file.endswith(".py"):
continue
name = file[:-3]
if name == cog:
await self.bot.reload_extension(f"cogs.{name}")
embed = discord.Embed(color=ctx.me.color, description=f"{cog} has been reloaded")
return await ctx.reply(embed=embed)
embed = embed_templates.error_warning(f"{cog} does not exist")
await ctx.reply(embed=embed)
@cogs.command(name="reloadunloaded", description="Last inn alle cogs på nytt, selv de som ikke er lastet inn")
async def cogs_reloadunloaded(self, ctx: commands.Context):
"""
Reloads all cogs, including previously disabled ones
Parameters
----------
ctx (commands.Context): Context object
"""
extensions = [f"cogs.{file[:-3]}" for file in listdir("./src/cogs") if file.endswith(".py")]
failed = await self.reload_extensions(extensions)
embed = discord.Embed(color=ctx.me.color, description=self.reload_summary("Reloaded all cogs", failed))
await ctx.reply(embed=embed)
@cogs.command(name="reloadall", description="Last inn alle cogs på nytt")
async def cogs_reloadall(self, ctx: commands.Context):
"""
Reloads all previously enabled cogs
Parameters
----------
ctx (commands.Context): Context object
"""
# Only loaded cogs can be reloaded. Copied to a list since reloading modifies bot.extensions
failed = await self.reload_extensions(list(self.bot.extensions))
embed = discord.Embed(
color=ctx.me.color, description=self.reload_summary("Reloaded all previously enabled cogs", failed)
)
await ctx.reply(embed=embed)
async def reload_extensions(self, extensions: list[str]) -> dict[str, Exception]:
"""
Reloads the given extensions, loading the ones that aren't loaded yet
Parameters
----------
extensions (list[str]): Names of the extensions to reload
Returns
----------
(dict[str, Exception]): The extensions that failed and why
"""
failed = {}
for extension in extensions:
try:
if extension in self.bot.extensions:
await self.bot.reload_extension(extension)
else:
await self.bot.load_extension(extension)
except commands.ExtensionError as e:
self.bot.logger.error(f"Failed to reload {extension}: {e}")
failed[extension] = e
return failed
def reload_summary(self, message: str, failed: dict[str, Exception]) -> str:
"""
Builds the reply for the reload commands
Parameters
----------
message (str): The message to show when everything was reloaded
failed (dict[str, Exception]): The extensions that failed and why
Returns
----------
(str): The reply text
"""
if not failed:
return message
failures = "\n".join(f"* `{extension}`: {type(e.__cause__ or e).__name__}" for extension, e in failed.items())
return f"{message}, except:\n{failures}"
async def setup(bot: commands.Bot):
"""
Add the cog to the bot on extension load
Parameters
----------
bot (commands.Bot): Bot instance
"""
await bot.add_cog(DevTools(bot))