274 lines
9.3 KiB
Python
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))
|