From d349a398c5af4aa48ea41e0f44f3070cc78a6dd3 Mon Sep 17 00:00:00 2001 From: flifloo Date: Wed, 6 Jan 2021 17:02:15 +0100 Subject: [PATCH 01/16] Switch utils to slash commands --- administrator/__init__.py | 2 ++ extensions/help.py | 22 +++++++++---- extensions/utils.py | 67 +++++++++++++++++---------------------- 3 files changed, 46 insertions(+), 45 deletions(-) diff --git a/administrator/__init__.py b/administrator/__init__.py index b948753..00a4ae1 100644 --- a/administrator/__init__.py +++ b/administrator/__init__.py @@ -1,10 +1,12 @@ from discord import Intents +from discord_slash import SlashCommand from administrator.config import config import db from discord.ext import commands bot = commands.Bot(command_prefix=config.get("prefix"), intents=Intents.all()) +slash = SlashCommand(bot, auto_register=True) import extensions diff --git a/extensions/help.py b/extensions/help.py index 3d958f2..5ffbf43 100644 --- a/extensions/help.py +++ b/extensions/help.py @@ -2,6 +2,7 @@ from discord import Embed from discord.ext import commands from discord.ext.commands import CommandNotFound, MissingRequiredArgument, BadArgument, MissingPermissions, \ NoPrivateMessage, CommandError, NotOwner +from discord_slash import SlashContext from administrator import config from administrator.check import ExtensionDisabled @@ -38,19 +39,26 @@ class Help(commands.Cog): @commands.Cog.listener() async def on_command_error(self, ctx: commands.Context, error): + await self.error_handler(ctx, error) + + @commands.Cog.listener() + async def on_slash_command_error(self, ctx: SlashContext, error: Exception): + await self.error_handler(ctx, error) + + @staticmethod + async def error_handler(ctx, error: Exception): if isinstance(error, CommandNotFound): - await ctx.message.add_reaction("\u2753") + await ctx.send(content="\u2753") elif isinstance(error, MissingRequiredArgument) or isinstance(error, BadArgument): - await ctx.message.add_reaction("\u274C") - elif isinstance(error, NotOwner) or isinstance(error, MissingPermissions)\ + await ctx.send(content="\u274C") + elif isinstance(error, NotOwner) or isinstance(error, MissingPermissions) \ or isinstance(error, NoPrivateMessage): - await ctx.message.add_reaction("\U000026D4") + await ctx.send(content="\U000026D4") elif isinstance(error, ExtensionDisabled): - await ctx.message.add_reaction("\U0001F6AB") + await ctx.send(content="\U0001F6AB") else: - await ctx.send("An error occurred !") + await ctx.send(content="An error occurred !") raise error - await ctx.message.delete(delay=30) def setup(bot): diff --git a/extensions/utils.py b/extensions/utils.py index 7b6982f..e197bfc 100644 --- a/extensions/utils.py +++ b/extensions/utils.py @@ -1,9 +1,11 @@ from datetime import datetime -from discord import Embed, Member, Guild +from discord import Embed, Guild from discord.ext import commands -from discord.ext.commands import BadArgument, CommandError +from discord_slash import cog_ext, SlashContext, SlashCommandOptionType +from discord_slash.utils import manage_commands +from administrator import slash from administrator.check import is_enabled from administrator.logger import logger @@ -15,34 +17,17 @@ logger = logger.getChild(extension_name) class Utils(commands.Cog): def __init__(self, bot: commands.Bot): self.bot = bot + slash.get_cog_commands(self) def description(self): return "Some tools" - @commands.group("utils", pass_context=True) - @is_enabled() - async def utils(self, ctx: commands.Context): - if ctx.invoked_subcommand is None: - await ctx.invoke(self.utils_help) - - @utils.group("help", pass_context=True) - async def utils_help(self, ctx: commands.Context): - embed = Embed(title="Utils help") - try: - if await self.eval.can_run(ctx): - embed.add_field(name="eval \`\`\`code\`\`\`", value="Execute some code", inline=False) - except CommandError: - pass - embed.add_field(name="ping", value="Return the ping with the discord API", inline=False) - embed.add_field(name="info [@user]", value="Show information on guild or user specified", inline=False) - await ctx.send(embed=embed) - @commands.group("eval", pass_context=True) @commands.is_owner() async def eval(self, ctx: commands.Context): - start = ctx.message.content.find("```") - end = ctx.message.content.find("```", start+3) - command = ctx.message.content[start+3:end] + start = ctx.message.content.find("```python") + end = ctx.message.content.find("```", start+9) + command = ctx.message.content[start+9:end] try: exec("async def __ex(self, ctx):\n" + command.replace("\n", "\n ")) out = str(await locals()["__ex"](self, ctx)) @@ -55,20 +40,20 @@ class Utils(commands.Cog): except Exception as e: await ctx.send(f"```{e.__class__.__name__}: {e}```") - @commands.group("ping", pass_context=True) + @cog_ext.cog_slash(name="ping", description="Return the ping with the discord API") @is_enabled() - async def ping(self, ctx: commands.Context): + async def ping(self, ctx: SlashContext): start = datetime.now() - msg = await ctx.send(f"Discord WebSocket latency: `{round(self.bot.latency*1000)}ms`") - await msg.edit(content=msg.content+"\n"+f"Bot latency: `{round((msg.created_at - start).microseconds/1000)}ms`") + content = f"Discord WebSocket latency: `{round(self.bot.latency*1000)}ms`" + await ctx.send(content=content) + await ctx.edit(content=content+"\n"+f"Bot latency: `{round((datetime.now() - start).microseconds/1000)}ms`") - @commands.group("info", pass_context=True) + @cog_ext.cog_slash(name="info", + description="Show information on guild or user specified", + options=[manage_commands.create_option("user", "A user", SlashCommandOptionType.USER, False)]) @is_enabled() - async def info(self, ctx: commands.Context): - if len(ctx.message.mentions) > 1: - raise BadArgument() - elif ctx.message.mentions: - user: Member = ctx.message.mentions[0] + async def info(self, ctx: SlashContext, user: SlashCommandOptionType.USER = None): + if user: embed = Embed(title=str(user)) embed.set_author(name="User infos", icon_url=user.avatar_url) embed.add_field(name="Display name", value=user.display_name) @@ -129,11 +114,11 @@ class Utils(commands.Cog): embed.add_field(name="Shard ID", value=guild.shard_id) embed.add_field(name="Created at", value=guild.created_at) - await ctx.send(embed=embed) + await ctx.send(embeds=[embed]) - @commands.group("about", pass_context=True) + @cog_ext.cog_slash(name="about", description="Show information about the bot") @is_enabled() - async def about(self, ctx: commands.Context): + async def about(self, ctx: SlashContext): embed = Embed(title=self.bot.user.display_name, description=self.bot.description) embed.set_author(name="Administrator", icon_url=self.bot.user.avatar_url, url="https://github.com/flifloo") flifloo = self.bot.get_user(177393521051959306) @@ -142,9 +127,12 @@ class Utils(commands.Cog): value=(await self.bot.application_info()).owner.display_name) embed.add_field(name="Guilds", value=str(len(self.bot.guilds))) embed.add_field(name="Extensions", value=str(len(self.bot.extensions))) - embed.add_field(name="Commands", value=str(len(self.bot.all_commands))) + embed.add_field(name="Commands", value=str(len(self.bot.all_commands)+len(slash.commands))) embed.add_field(name="Latency", value=f"{round(self.bot.latency*1000)} ms") - await ctx.send(embed=embed) + await ctx.send(embeds=[embed]) + + def cog_unload(self): + slash.remove_cog_commands(self) def setup(bot): @@ -165,3 +153,6 @@ def teardown(bot): logger.error(f"Error unloading: {e}") else: logger.info(f"Unload successful") + + + From eb34ebd9476f7a6be00f4b314d722cfbb4768720 Mon Sep 17 00:00:00 2001 From: flifloo Date: Wed, 6 Jan 2021 17:53:30 +0100 Subject: [PATCH 02/16] Switch extension to slash commands --- administrator/__init__.py | 2 +- extensions/extension.py | 93 ++++++++++++++++++++------------------- 2 files changed, 49 insertions(+), 46 deletions(-) diff --git a/administrator/__init__.py b/administrator/__init__.py index 00a4ae1..5ef856f 100644 --- a/administrator/__init__.py +++ b/administrator/__init__.py @@ -6,7 +6,7 @@ import db from discord.ext import commands bot = commands.Bot(command_prefix=config.get("prefix"), intents=Intents.all()) -slash = SlashCommand(bot, auto_register=True) +slash = SlashCommand(bot, auto_register=True, auto_delete=True) import extensions diff --git a/extensions/extension.py b/extensions/extension.py index 653cf90..93a5cad 100644 --- a/extensions/extension.py +++ b/extensions/extension.py @@ -2,9 +2,12 @@ from traceback import format_exc from discord.ext import commands from discord import Embed, Guild -from discord.ext.commands import MissingPermissions, BadArgument, CommandError +from discord.ext.commands import BadArgument +from discord_slash import cog_ext, SlashContext, SlashCommandOptionType +from discord_slash.utils import manage_commands import db +from administrator import slash from administrator.logger import logger @@ -15,69 +18,66 @@ logger = logger.getChild(extension_name) class Extension(commands.Cog): def __init__(self, bot: commands.Bot): self.bot = bot + slash.get_cog_commands(self) def description(self): return "Manage bot's extensions" - @commands.group("extension", pass_context=True) - async def extension(self, ctx: commands.Context): - if ctx.invoked_subcommand is None: - await ctx.invoke(self.extension_help) - - @extension.group("help", pass_context=True) - async def extension_help(self, ctx: commands.Context): - embed = Embed(title="Extension help") - for c, n, v in [[self.extension_list, "extension list", "List all enabled extensions"], - [self.extension_enable, "extension enable", "Enable an extensions"], - [self.extension_disable, "extension disable", "Disable an extensions"], - [self.extension_loaded, "extension loaded", "List all loaded extensions"], - [self.extension_load, "extension load ", "Load an extension"], - [self.extension_unload, "extension unload ", "Unload an extension"], - [self.extension_reload, "extension reload ", "Reload an extension"]]: - try: - if await c.can_run(ctx): - embed.add_field(name=n, value=v, inline=False) - except CommandError: - pass - - if not embed.fields: - raise MissingPermissions("") - await ctx.send(embed=embed) - - @extension.group("list", pass_context=True) + @cog_ext.cog_subcommand(base="extension", name="list", description="List all enabled extensions") @commands.has_guild_permissions(administrator=True) - async def extension_list(self, ctx: commands.Context): + async def extension_list(self, ctx: SlashContext): s = db.Session() embed = Embed(title="Extensions list") for es in s.query(db.ExtensionState).filter(db.ExtensionState.guild_id == ctx.guild.id): embed.add_field(name=es.extension_name, value="Enable" if es.state else "Disable") - await ctx.send(embed=embed) + s.close() + await ctx.send(embeds=[embed]) - @extension.group("enable", pass_context=True) + @cog_ext.cog_subcommand(base="extension", + name="enable", + description="Enable an extensions", + options=[manage_commands.create_option("extension", "The extension to enable", + SlashCommandOptionType.STRING, True)]) @commands.has_guild_permissions(administrator=True) - async def extension_enable(self, ctx: commands.Context, name: str): + async def extension_enable(self, ctx: SlashContext, name: str): s = db.Session() es = s.query(db.ExtensionState).get((name, ctx.guild.id)) - if not es or es.state: + if not es: raise BadArgument() - es.state = True - s.add(es) - s.commit() - s.close() - await ctx.message.add_reaction("\U0001f44d") + elif es.state: + message = "Extension already enabled" + else: + es.state = True + s.add(es) + s.commit() + s.close() + message = "\U0001f44d" + await ctx.send(content=message) - @extension.group("disable", pass_context=True) + @cog_ext.cog_subcommand(base="extension", + name="disable", + description="Disable an extensions", + options=[manage_commands.create_option("extension", "The extension to disable", + SlashCommandOptionType.STRING, True)]) @commands.has_guild_permissions(administrator=True) - async def extension_disable(self, ctx: commands.Context, name: str): + async def extension_disable(self, ctx: SlashContext, name: str): s = db.Session() es = s.query(db.ExtensionState).get((name, ctx.guild.id)) - if not es or not es.state: + if not es: raise BadArgument() - es.state = False - s.add(es) - s.commit() - s.close() - await ctx.message.add_reaction("\U0001f44d") + elif not es.state: + message = "Extension already disabled" + else: + es.state = False + s.add(es) + s.commit() + s.close() + message = "\U0001f44d" + await ctx.send(content=message) + + @commands.group("extension", pass_context=True) + async def extension(self, ctx: commands.Context): + pass @extension.group("loaded", pass_context=True) @commands.is_owner() @@ -152,6 +152,9 @@ class Extension(commands.Cog): s.commit() s.close() + def cog_unload(self): + slash.remove_cog_commands(self) + def setup(bot): logger.info(f"Loading...") From 8c1b3af1eb04c5a31e9b3b74ea97c46b251fddbc Mon Sep 17 00:00:00 2001 From: flifloo Date: Sun, 10 Jan 2021 00:13:37 +0100 Subject: [PATCH 03/16] Switch greetings to slash commands --- extensions/greetings.py | 81 ++++++++++++++++++++++------------------- 1 file changed, 44 insertions(+), 37 deletions(-) diff --git a/extensions/greetings.py b/extensions/greetings.py index bd169a1..20c554e 100644 --- a/extensions/greetings.py +++ b/extensions/greetings.py @@ -1,18 +1,13 @@ from discord.ext import commands from discord import Member, Embed, Forbidden -from discord.ext.commands import BadArgument +from discord_slash import cog_ext, SlashContext, SlashCommandOptionType +from discord_slash.utils import manage_commands -from administrator.check import is_enabled from administrator.logger import logger -from administrator import db, config +from administrator import db, slash from administrator.utils import event_is_enabled -def check_greetings_message_type(message_type): - if message_type not in ["join", "leave"]: - raise BadArgument() - - extension_name = "greetings" logger = logger.getChild(extension_name) @@ -20,68 +15,77 @@ logger = logger.getChild(extension_name) class Greetings(commands.Cog): def __init__(self, bot: commands.Bot): self.bot = bot + slash.get_cog_commands(self) def description(self): return "Setup join and leave message" - @commands.group("greetings", pass_context=True) - @is_enabled() - @commands.guild_only() - @commands.has_permissions(manage_guild=True) - async def greetings(self, ctx: commands.Context): - if ctx.invoked_subcommand is None: - await ctx.invoke(self.greetings_help) - - @greetings.group("help", pass_context=True) - async def greetings_help(self, ctx: commands.Context): + @cog_ext.cog_subcommand(base="greetings", guild_ids=[693108780434587708], name="help", + description="Help about greetings") + async def greetings_help(self, ctx: SlashContext): embed = Embed(title="Greetings help") embed.add_field(name="set ", value="Set the greetings message\n" "`{}` will be replace by the username", inline=False) embed.add_field(name="show ", value="Show the greetings message", inline=False) embed.add_field(name="toggle ", value="Enable or disable the greetings message", inline=False) - await ctx.send(embed=embed) + await ctx.send(embeds=[embed]) - @greetings.group("set", pass_context=True) - async def greetings_set(self, ctx: commands.Context, message_type: str): - check_greetings_message_type(message_type) - message = ctx.message.content.replace(config.get("prefix")+"greetings set " + message_type, "").strip() + @cog_ext.cog_subcommand(base="greetings", guild_ids=[693108780434587708], name="set", + description="Set the greetings message\n`{}` will be replace by the username", + options=[ + manage_commands.create_option("type", "The join or leave message", + SlashCommandOptionType.STRING, True, + [manage_commands.create_choice("join", "join"), + manage_commands.create_choice("leave", "leave")]), + manage_commands.create_option("message", "The message", SlashCommandOptionType.STRING, + True) + ]) + async def greetings_set(self, ctx: SlashContext, message_type: str, message: str): s = db.Session() m = s.query(db.Greetings).filter(db.Greetings.guild == ctx.guild.id).first() if not m: m = db.Greetings(ctx.guild.id) s.add(m) setattr(m, message_type+"_enable", True) - setattr(m, message_type+"_message", message) + setattr(m, message_type+"_message", message.replace("\\n", '\n')) s.commit() - await ctx.message.add_reaction("\U0001f44d") + await ctx.send(content="\U0001f44d") - @greetings.group("show", pass_context=True) - async def greetings_show(self, ctx: commands.Context, message_type: str): - check_greetings_message_type(message_type) + @cog_ext.cog_subcommand(base="greetings", guild_ids=[693108780434587708], name="show", + description="Show the greetings message", + options=[manage_commands.create_option("type", "The join or leave message", + SlashCommandOptionType.STRING, True, + [manage_commands.create_choice("join", "join"), + manage_commands.create_choice("leave", "leave")])]) + async def greetings_show(self, ctx: SlashContext, message_type: str): s = db.Session() m = s.query(db.Greetings).filter(db.Greetings.guild == ctx.guild.id).first() s.close() if not m: - await ctx.send(f"No {message_type} message set !") + await ctx.send(content=f"No {message_type} message set !") else: if message_type == "join": - await ctx.send(embed=m.join_embed(ctx.guild.name, str(ctx.message.author))) + await ctx.send(embeds=[m.join_embed(ctx.guild.name, str(ctx.author))]) else: - await ctx.send(m.leave_msg(str(ctx.message.author))) + await ctx.send(content=m.leave_msg(str(ctx.author))) - @greetings.group("toggle", pass_context=True) - async def greetings_toggle(self, ctx: commands.Context, message_type: str): - check_greetings_message_type(message_type) + @cog_ext.cog_subcommand(base="greetings", guild_ids=[693108780434587708], name="toggle", + description="Enable or disable the greetings message", + options=[manage_commands.create_option("type", "The join or leave message", + SlashCommandOptionType.STRING, True, + [manage_commands.create_choice("join", "join"), + manage_commands.create_choice("leave", "leave")])]) + async def greetings_toggle(self, ctx: SlashContext, message_type: str): s = db.Session() m = s.query(db.Greetings).filter(db.Greetings.guild == ctx.guild.id).first() if not m: - await ctx.send(f"No {message_type} message set !") + await ctx.send(content=f"No {message_type} message set !") else: setattr(m, message_type+"_enable", not getattr(m, message_type+"_enable")) s.commit() - await ctx.send(f"{message_type.title()} message is " + - ("enable" if getattr(m, message_type+"_enable") else "disable")) + await ctx.send(content=f"{message_type.title()} message is " + + ("enable" if getattr(m, message_type+"_enable") else "disable")) s.close() @commands.Cog.listener() @@ -108,6 +112,9 @@ class Greetings(commands.Cog): if m and m.leave_enable: await member.guild.system_channel.send(m.leave_msg(str(member))) + def cog_unload(self): + slash.remove_cog_commands(self) + def setup(bot): logger.info(f"Loading...") From 1b5d8410aeb28edf9b778a815b7c8a51eb032b09 Mon Sep 17 00:00:00 2001 From: flifloo Date: Wed, 3 Feb 2021 14:46:22 +0100 Subject: [PATCH 04/16] Updating check for slash commands --- administrator/check.py | 71 +++++++++++++++++++++++++++++++++++------ extensions/extension.py | 7 ++-- extensions/greetings.py | 21 +++++++++--- requirements.txt | 1 + 4 files changed, 84 insertions(+), 16 deletions(-) diff --git a/administrator/check.py b/administrator/check.py index 3d1cb6a..0e8d902 100644 --- a/administrator/check.py +++ b/administrator/check.py @@ -1,4 +1,8 @@ +import functools + +from discord import Permissions from discord.ext import commands +from discord.ext.commands import NoPrivateMessage, NotOwner, MissingPermissions import db @@ -8,12 +12,61 @@ class ExtensionDisabled(commands.CheckFailure): def is_enabled(): - async def check(ctx: commands.Context): - if ctx.command.cog and ctx.guild: - s = db.Session() - es = s.query(db.ExtensionState).get((ctx.command.cog.qualified_name, ctx.guild.id)) - s.close() - if es and not es.state: - raise ExtensionDisabled() - return True - return commands.check(check) + def check(func): + @functools.wraps(func) + async def wrapped(*args): + ctx = args[1] + if ctx.guild: + s = db.Session() + es = s.query(db.ExtensionState).get((args[0].qualified_name, ctx.guild.id)) + s.close() + if es and not es.state: + raise ExtensionDisabled() + return await func(*args) + return wrapped + return check + + +def is_owner(): + def check(func): + @functools.wraps(func) + async def wrapped(*args): + ctx = args[1] + if not await ctx._discord.is_owner(ctx.author): + raise NotOwner('You do not own this bot.') + return await func(*args) + return wrapped + return check + + +def guild_only(): + def check(func): + @functools.wraps(func) + async def wrapped(*args): + if args[1].guild is None: + raise NoPrivateMessage() + return await func(*args) + return wrapped + return check + + +def has_permissions(**perms): + invalid = set(perms) - set(Permissions.VALID_FLAGS) + if invalid: + raise TypeError('Invalid permission(s): %s' % (', '.join(invalid))) + + def check(func): + @functools.wraps(func) + async def wrapped(*args): + ctx = args[1] + ch = ctx.channel + permissions = ch.permissions_for(ctx.author) + + missing = [perm for perm, value in perms.items() if getattr(permissions, perm) != value] + + if not missing: + return await func(*args) + + raise MissingPermissions(missing) + return wrapped + return check diff --git a/extensions/extension.py b/extensions/extension.py index 93a5cad..f6635cb 100644 --- a/extensions/extension.py +++ b/extensions/extension.py @@ -8,6 +8,7 @@ from discord_slash.utils import manage_commands import db from administrator import slash +from administrator.check import has_permissions from administrator.logger import logger @@ -24,7 +25,7 @@ class Extension(commands.Cog): return "Manage bot's extensions" @cog_ext.cog_subcommand(base="extension", name="list", description="List all enabled extensions") - @commands.has_guild_permissions(administrator=True) + @has_permissions(administrator=True) async def extension_list(self, ctx: SlashContext): s = db.Session() embed = Embed(title="Extensions list") @@ -38,7 +39,7 @@ class Extension(commands.Cog): description="Enable an extensions", options=[manage_commands.create_option("extension", "The extension to enable", SlashCommandOptionType.STRING, True)]) - @commands.has_guild_permissions(administrator=True) + @has_permissions(administrator=True) async def extension_enable(self, ctx: SlashContext, name: str): s = db.Session() es = s.query(db.ExtensionState).get((name, ctx.guild.id)) @@ -59,7 +60,7 @@ class Extension(commands.Cog): description="Disable an extensions", options=[manage_commands.create_option("extension", "The extension to disable", SlashCommandOptionType.STRING, True)]) - @commands.has_guild_permissions(administrator=True) + @has_permissions(administrator=True) async def extension_disable(self, ctx: SlashContext, name: str): s = db.Session() es = s.query(db.ExtensionState).get((name, ctx.guild.id)) diff --git a/extensions/greetings.py b/extensions/greetings.py index 20c554e..ad59907 100644 --- a/extensions/greetings.py +++ b/extensions/greetings.py @@ -3,6 +3,7 @@ from discord import Member, Embed, Forbidden from discord_slash import cog_ext, SlashContext, SlashCommandOptionType from discord_slash.utils import manage_commands +from administrator.check import is_enabled, guild_only, has_permissions from administrator.logger import logger from administrator import db, slash from administrator.utils import event_is_enabled @@ -20,8 +21,11 @@ class Greetings(commands.Cog): def description(self): return "Setup join and leave message" - @cog_ext.cog_subcommand(base="greetings", guild_ids=[693108780434587708], name="help", + @cog_ext.cog_subcommand(base="greetings", name="help", description="Help about greetings") + @is_enabled() + @guild_only() + @has_permissions(manage_guild=True) async def greetings_help(self, ctx: SlashContext): embed = Embed(title="Greetings help") embed.add_field(name="set ", value="Set the greetings message\n" @@ -31,7 +35,7 @@ class Greetings(commands.Cog): embed.add_field(name="toggle ", value="Enable or disable the greetings message", inline=False) await ctx.send(embeds=[embed]) - @cog_ext.cog_subcommand(base="greetings", guild_ids=[693108780434587708], name="set", + @cog_ext.cog_subcommand(base="greetings", name="set", description="Set the greetings message\n`{}` will be replace by the username", options=[ manage_commands.create_option("type", "The join or leave message", @@ -41,6 +45,9 @@ class Greetings(commands.Cog): manage_commands.create_option("message", "The message", SlashCommandOptionType.STRING, True) ]) + @is_enabled() + @guild_only() + @has_permissions(manage_guild=True) async def greetings_set(self, ctx: SlashContext, message_type: str, message: str): s = db.Session() m = s.query(db.Greetings).filter(db.Greetings.guild == ctx.guild.id).first() @@ -52,12 +59,15 @@ class Greetings(commands.Cog): s.commit() await ctx.send(content="\U0001f44d") - @cog_ext.cog_subcommand(base="greetings", guild_ids=[693108780434587708], name="show", + @cog_ext.cog_subcommand(base="greetings", name="show", description="Show the greetings message", options=[manage_commands.create_option("type", "The join or leave message", SlashCommandOptionType.STRING, True, [manage_commands.create_choice("join", "join"), manage_commands.create_choice("leave", "leave")])]) + @is_enabled() + @guild_only() + @has_permissions(manage_guild=True) async def greetings_show(self, ctx: SlashContext, message_type: str): s = db.Session() m = s.query(db.Greetings).filter(db.Greetings.guild == ctx.guild.id).first() @@ -70,12 +80,15 @@ class Greetings(commands.Cog): else: await ctx.send(content=m.leave_msg(str(ctx.author))) - @cog_ext.cog_subcommand(base="greetings", guild_ids=[693108780434587708], name="toggle", + @cog_ext.cog_subcommand(base="greetings", name="toggle", description="Enable or disable the greetings message", options=[manage_commands.create_option("type", "The join or leave message", SlashCommandOptionType.STRING, True, [manage_commands.create_choice("join", "join"), manage_commands.create_choice("leave", "leave")])]) + @is_enabled() + @guild_only() + @has_permissions(manage_guild=True) async def greetings_toggle(self, ctx: SlashContext, message_type: str): s = db.Session() m = s.query(db.Greetings).filter(db.Greetings.guild == ctx.guild.id).first() diff --git a/requirements.txt b/requirements.txt index 56d1dbc..8f95633 100644 --- a/requirements.txt +++ b/requirements.txt @@ -3,6 +3,7 @@ async-timeout==3.0.1 attrs==20.2.0 chardet==3.0.4 discord==1.0.1 +discord-py-slash-command==1.0.8.5 discord.py==1.5.1 feedparser==6.0.2 idna==2.10 From ba55cb7c45608a52945c4e81f89255a5e6024d3c Mon Sep 17 00:00:00 2001 From: flifloo Date: Wed, 3 Feb 2021 15:14:58 +0100 Subject: [PATCH 05/16] Switch invite to slash commands --- extensions/invite.py | 57 ++++++++++++++++++++++++++------------------ 1 file changed, 34 insertions(+), 23 deletions(-) diff --git a/extensions/invite.py b/extensions/invite.py index e569c16..77dacf3 100644 --- a/extensions/invite.py +++ b/extensions/invite.py @@ -1,12 +1,16 @@ import re -from discord import Embed, Member, Guild +from discord import Embed, Member, Guild, Role, CategoryChannel +from discord.abc import GuildChannel from discord.errors import Forbidden from discord.ext import commands from discord.ext.commands import BadArgument +from discord_slash import cog_ext, SlashContext, SlashCommandOptionType +from discord_slash.utils import manage_commands import db -from administrator.check import is_enabled +from administrator import slash +from administrator.check import is_enabled, guild_only, has_permissions from administrator.logger import logger from administrator.utils import event_is_enabled @@ -20,41 +24,48 @@ class Invite(commands.Cog): def __init__(self, bot: commands.Bot): self.bot = bot self.invites = {} + slash.get_cog_commands(self) self.bot.loop.create_task(self.update_invites()) def description(self): return "Get role from a special invite link" - @commands.group("invite", pass_context=True) + @cog_ext.cog_subcommand(base="invite", name="help", description="Help about invite") @is_enabled() - @commands.guild_only() - @commands.has_guild_permissions(administrator=True) - async def invite(self, ctx: commands.Context): - if ctx.invoked_subcommand is None: - await ctx.invoke(self.invite_help) - - @invite.group("help", pass_context=True) - async def invite_help(self, ctx: commands.Context): + @guild_only() + @has_permissions(administrator=True) + async def invite_help(self, ctx: SlashContext): embed = Embed(title="Invite help") embed.add_field(name="invite create <#channel> <@role>", value="Create a invite link to a role", inline=False) embed.add_field(name="invite delete ", value="Remove a invite", inline=False) - await ctx.send(embed=embed) + await ctx.send(embeds=[embed]) - @invite.group("create", pass_context=True) - async def invite_add(self, ctx: commands.Context, channel: str, role: str): - if not channel_mention_re.fullmatch(channel) or len(ctx.message.channel_mentions) != 1 or\ - not role_mention_re.fullmatch(role) or len(ctx.message.role_mentions) != 1: + @cog_ext.cog_subcommand(base="invite", name="create", description="Create a invite link to a role", + options=[ + manage_commands.create_option("channel", "The channel to join", + SlashCommandOptionType.CHANNEL, True), + manage_commands.create_option("role", "The role to give", + SlashCommandOptionType.ROLE, True) + ]) + @is_enabled() + @guild_only() + @has_permissions(administrator=True) + async def invite_add(self, ctx: SlashContext, channel: GuildChannel, role: Role): + if isinstance(channel, CategoryChannel): raise BadArgument() - - inv = await ctx.message.channel_mentions[0].create_invite() + inv = await channel.create_invite() s = db.Session() - s.add(db.InviteRole(ctx.guild.id, inv.code, ctx.message.role_mentions[0].id)) + s.add(db.InviteRole(ctx.guild.id, inv.code, role.id)) s.commit() s.close() - await ctx.send(f"Invite created: `{inv.url}`") + await ctx.send(content=f"Invite created: `{inv.url}`") - @invite.group("delete", pass_context=True) - async def invite_delete(self, ctx: commands.Context, code: str): + @cog_ext.cog_subcommand(base="invite", name="delete", description="Remove a invite", options=[ + manage_commands.create_option("code", "The invitation code", SlashCommandOptionType.STRING, True)]) + @is_enabled() + @guild_only() + @has_permissions(administrator=True) + async def invite_delete(self, ctx: SlashContext, code: str): inv = next(filter(lambda i: i.code == code, await ctx.guild.invites()), None) if not inv: raise BadArgument() @@ -68,7 +79,7 @@ class Invite(commands.Cog): s.commit() s.close() await inv.delete() - await ctx.message.add_reaction("\U0001f44d") + await ctx.send(content="\U0001f44d") async def update_invites(self): for g in self.bot.guilds: From a2afff2334da015e284de7055fd99d2304de9a14 Mon Sep 17 00:00:00 2001 From: flifloo Date: Wed, 3 Feb 2021 22:05:19 +0100 Subject: [PATCH 06/16] Switch pcp to slash commands --- administrator/check.py | 3 +- extensions/pcp.py | 235 +++++++++++++++++++---------------------- 2 files changed, 108 insertions(+), 130 deletions(-) diff --git a/administrator/check.py b/administrator/check.py index 0e8d902..ea1c63d 100644 --- a/administrator/check.py +++ b/administrator/check.py @@ -21,7 +21,8 @@ def is_enabled(): es = s.query(db.ExtensionState).get((args[0].qualified_name, ctx.guild.id)) s.close() if es and not es.state: - raise ExtensionDisabled() + return + # raise ExtensionDisabled() return await func(*args) return wrapped return check diff --git a/extensions/pcp.py b/extensions/pcp.py index 47dad44..1b79576 100644 --- a/extensions/pcp.py +++ b/extensions/pcp.py @@ -1,10 +1,14 @@ import re -from discord import Embed, Member +from discord import Member, Role from discord.ext import commands -from discord.ext.commands import BadArgument, MissingPermissions, CommandError +from discord.ext.commands import BadArgument +from discord_slash import cog_ext, SlashCommandOptionType, SlashContext +from discord_slash.utils import manage_commands import db +from administrator import slash +from administrator.check import guild_only, has_permissions from administrator.logger import logger @@ -18,75 +22,55 @@ user_mention_re = re.compile(r"^<@![0-9]+>$") class PCP(commands.Cog): def __init__(self, bot: commands.Bot): self.bot = bot + slash.get_cog_commands(self) def description(self): return "PCP Univ Lyon 1" - @commands.group("pcp", pass_context=True) - @commands.guild_only() - async def pcp(self, ctx: commands.Context): - group = ctx.message.content.replace(f"{ctx.prefix}{ctx.command} ", "").upper() - if group: - s = db.Session() - p = s.query(db.PCP).get(ctx.guild.id) - s.close() - if p and re.fullmatch(p.roles_re, group): - await ctx.message.add_reaction("\U000023f3") - role = next(filter(lambda r: r.name.upper() == group, ctx.guild.roles), None) - - def roles() -> list: - return list(filter( - lambda r: re.fullmatch(p.roles_re, r.name.upper()) or - (p.start_role_re and re.fullmatch(p.start_role_re, r.name.upper())), - ctx.author.roles - )) - - if not role or role.name in map(lambda r: r.name, roles()): - await ctx.message.remove_reaction("\U000023f3", self.bot.user) - raise BadArgument() - - while roles(): - await ctx.author.remove_roles(*roles()) - - while role not in ctx.author.roles: - await ctx.author.add_roles(role) - await ctx.message.remove_reaction("\U000023f3", self.bot.user) - await ctx.message.add_reaction("\U0001f44d") - return - - if ctx.invoked_subcommand is None: - await ctx.invoke(self.pcp_help) - - @pcp.group("help", pass_context=True) - async def pcp_help(self, ctx: commands.Context): - embed = Embed(title="PCP help") + @cog_ext.cog_subcommand(base="pcp", name="join", description="Join your group", options=[ + manage_commands.create_option("group", "The target group to join", SlashCommandOptionType.ROLE, True)]) + @guild_only() + async def pcp(self, ctx: SlashContext, role: Role): s = db.Session() p = s.query(db.PCP).get(ctx.guild.id) s.close() - if p: - embed.add_field(name="pcp ", value="Join your group", inline=False) - for c, n, v in [[self.pcp_group, "pcp group", "Manage PCP group"], - [self.pcp_pin, "pcp pin ", "Pin a message with the url"], - [self.pcp_unpin, "pcp unpin ", "Unpin a message with the url"]]: - try: - if await c.can_run(ctx): - embed.add_field(name=n, value=v, inline=False) - except CommandError: - pass - if not embed.fields: - raise MissingPermissions("") - await ctx.send(embed=embed) + if p and re.fullmatch(p.roles_re, role.name.upper()): + await ctx.send(content="\U000023f3") - @pcp.group("pin", pass_context=True) - async def pcp_pin(self, ctx: commands.Context, url: str): + async def roles() -> list: + return list(filter( + lambda r: re.fullmatch(p.roles_re, r.name.upper()) or + (p.start_role_re and re.fullmatch(p.start_role_re, r.name.upper())), + (await ctx.guild.fetch_member(ctx.author.id)).roles + )) + + if not role or role.name in map(lambda r: r.name, await roles()): + await ctx.delete() + raise BadArgument() + + while await roles(): + await ctx.author.remove_roles(*(await roles())) + + while role not in (await ctx.guild.fetch_member(ctx.author.id)).roles: + await ctx.author.add_roles(role) + await ctx.edit(content="\U0001f44d") + + @cog_ext.cog_subcommand(base="pcp", name="pin", description="Pin a message with the url", options=[ + manage_commands.create_option("url", "message URL", SlashCommandOptionType.STRING, True) + ]) + @guild_only() + async def pcp_pin(self, ctx: SlashContext, url: str): await self.pin(ctx, url, True) - @pcp.group("unpin", pass_context=True) - async def pcp_unpin(self, ctx: commands.Context, url: str): + @cog_ext.cog_subcommand(base="pcp", name="unpin", description="Unpin a message with the url", options=[ + manage_commands.create_option("url", "message URL", SlashCommandOptionType.STRING, True) + ]) + @guild_only() + async def pcp_unpin(self, ctx: SlashContext, url: str): await self.pin(ctx, url, False) @staticmethod - async def pin(ctx: commands.Context, url: str, action: bool): + async def pin(ctx: SlashContext, url: str, action: bool): r = msg_url_re.fullmatch(url) if not r: raise BadArgument() @@ -97,7 +81,7 @@ class PCP(commands.Cog): raise BadArgument() m = await c.fetch_message(int(r[1])) - if not m: + if not m or m.is_system(): raise BadArgument() if action: @@ -107,35 +91,23 @@ class PCP(commands.Cog): await m.unpin() msg = "unpinned a message" - await ctx.send(f"{ctx.author.mention} {msg}") + await ctx.send(content=f"{ctx.author.mention} {msg}") - @pcp.group("group", pass_context=True) - @commands.has_permissions(administrator=True) - async def pcp_group(self, ctx: commands.Context): - if ctx.invoked_subcommand is None: - await ctx.invoke(self.pcp_group_help) - - @pcp_group.group("help", pass_context=True) - async def pcp_group_help(self, ctx: commands.Context): - embed = Embed(title="PCP group help") - embed.add_field(name="pcp group set [Welcome role Regex]", - value="Set regex for group role", inline=False) - embed.add_field(name="pcp group unset", value="Unset regex for group role", inline=False) - embed.add_field(name="pcp group subject", value="Manage subjects for group", inline=False) - embed.add_field(name="pcp group fix_vocal", - value="Check all text channel permissions to reapply vocal permissions", inline=False) - await ctx.send(embed=embed) - - @pcp_group.group("fix_vocal", pass_context=True) - async def pcp_group_fix_vocal(self, ctx: commands.Context): + @cog_ext.cog_subcommand(base="pcp", subcommand_group="group", name="fix_vocal", + description="Check all text channel permissions to reapply vocal permissions") + @has_permissions(administrator=True) + async def pcp_group_fix_vocal(self, ctx: SlashContext): s = db.Session() p = s.query(db.PCP).get(ctx.guild.id) s.close() if not p: raise BadArgument() + message = "\U000023f3" + await ctx.send(content=message) for cat in filter(lambda c: re.fullmatch(p.roles_re, c.name.upper()), ctx.guild.categories): - await ctx.send(f"{cat.name}...") + message += f"\n{cat.name}..." + await ctx.edit(content=message) teachers = [] for t in cat.text_channels: for p in t.overwrites: @@ -144,11 +116,20 @@ class PCP(commands.Cog): voc = next(filter(lambda c: c.name == "vocal-1", cat.voice_channels), None) for t in teachers: await voc.set_permissions(t, view_channel=True) - await ctx.send(f"{cat.name} done") - await ctx.message.add_reaction("\U0001f44d") + message += f"\n{cat.name} done" + await ctx.edit(content=message) + message += "\n\U0001f44d" + await ctx.edit(content=message) - @pcp_group.group("set", pass_context=True) - async def pcp_group_set(self, ctx: commands.Context, roles_re: str, start_role_re: str = None): + @cog_ext.cog_subcommand(base="pcp", subcommand_group="group", name="set", description="Set regex for group role", + options=[ + manage_commands.create_option("role", "Roles regex", + SlashCommandOptionType.STRING, True), + manage_commands.create_option("role2", "Start roles regex", + SlashCommandOptionType.STRING, False) + ]) + @has_permissions(administrator=True) + async def pcp_group_set(self, ctx: SlashContext, roles_re: str, start_role_re: str = None): s = db.Session() p = s.query(db.PCP).get(ctx.guild.id) if p: @@ -159,9 +140,11 @@ class PCP(commands.Cog): s.add(p) s.commit() s.close() - await ctx.message.add_reaction("\U0001f44d") + await ctx.send(content="\U0001f44d") - @pcp_group.group("unset", pass_context=True) + @cog_ext.cog_subcommand(base="pcp", subcommand_group="group", name="unset", + description="Unset regex for group role") + @has_permissions(administrator=True) async def pcp_group_unset(self, ctx: commands.Context): s = db.Session() p = s.query(db.PCP).get(ctx.guild.id) @@ -173,33 +156,21 @@ class PCP(commands.Cog): s.close() await ctx.message.add_reaction("\U0001f44d") - @pcp_group.group("subject", pass_context=True) - async def pcp_group_subject(self, ctx: commands.Context): - if ctx.invoked_subcommand is None: - await ctx.invoke(self.pcp_group_subject_help) - - @pcp_group_subject.group("help", pass_context=True) - async def pcp_group_subject_help(self, ctx: commands.Context): - embed = Embed(title="PCP group subject help") - embed.add_field(name="pcp group subject add <@group> [@teacher]", value="Add a subject to a group", - inline=False) - embed.add_field(name="pcp group subject bulk <@group> [subject1] [subject2] ...", value="Bulk subject add", - inline=False) - embed.add_field(name="pcp group subject remove <@group>", value="Remove a subject to a group", - inline=False) - await ctx.send(embed=embed) - - @pcp_group_subject.group("add", pass_context=True) - async def pcp_group_subject_add(self, ctx: commands.Context, name: str, group: str, teacher: str = None): - if not role_mention_re.fullmatch(group): - raise BadArgument() - if teacher and not user_mention_re.fullmatch(teacher): - raise BadArgument() - elif teacher and\ - not next(filter(lambda r: r.name == "professeurs", ctx.message.mentions[0].roles), None): + @cog_ext.cog_subcommand(base="pcp", subcommand_group="subject", name="add", description="Add a subject to a group", + options=[ + manage_commands.create_option("name", "The subject name", + SlashCommandOptionType.STRING, True), + manage_commands.create_option("group", "The group", + SlashCommandOptionType.ROLE, True), + manage_commands.create_option("teacher", "The teacher", + SlashCommandOptionType.USER, False) + ]) + @has_permissions(administrator=True) + async def pcp_group_subject_add(self, ctx: SlashContext, name: str, group: Role, teacher: Member = None): + if teacher and not next(filter(lambda r: r.name == "professeurs", teacher.roles), None): raise BadArgument() - cat = next(filter(lambda c: c.name.upper() == ctx.message.role_mentions[0].name.upper(), + cat = next(filter(lambda c: c.name.upper() == group.name.upper(), ctx.guild.categories), None) if not cat: raise BadArgument() @@ -210,26 +181,32 @@ class PCP(commands.Cog): voc = next(filter(lambda c: c.name == "vocal-1", cat.voice_channels), None) if not voc: voc = await cat.create_voice_channel("vocal-1") - if ctx.message.mentions: - await chan.set_permissions(ctx.message.mentions[0], read_messages=True) - await voc.set_permissions(ctx.message.mentions[0], view_channel=True) + if teacher: + await chan.set_permissions(teacher, read_messages=True) + await voc.set_permissions(teacher, view_channel=True) - await ctx.message.add_reaction("\U0001f44d") + await ctx.send(content="\U0001f44d") - @pcp_group_subject.group("bulk", pass_context=True) - async def pcp_group_subject_bulk(self, ctx: commands.Context, mention, *names): - if not role_mention_re.fullmatch(mention): - raise BadArgument() - for n in names: - await ctx.invoke(self.pcp_group_subject_add, n, mention) + @cog_ext.cog_subcommand(base="pcp", subcommand_group="subject", name="bulk", + description="Remove a subject to a group", options=[ + manage_commands.create_option("group", "The group", SlashCommandOptionType.ROLE, True), + manage_commands.create_option("names", "Subjects names", SlashCommandOptionType.STRING, True) + ]) + @has_permissions(administrator=True) + async def pcp_group_subject_bulk(self, ctx: SlashContext, group: Role, names: str): + for n in names.split(" "): + await self.pcp_group_subject_add.invoke(ctx, n, group) - @pcp_group_subject.group("remove", pass_context=True) - async def pcp_group_subject_remove(self, ctx: commands.Context, name: str, group: str): - if not role_mention_re.fullmatch(group): - raise BadArgument() - - cat = next(filter(lambda c: c.name.upper() == ctx.message.role_mentions[0].name.upper(), - ctx.guild.categories), None) + @cog_ext.cog_subcommand(base="pcp", subcommand_group="subject", name="remove",description="Bulk subject add", + options=[ + manage_commands.create_option("name", "The subject name", + SlashCommandOptionType.STRING, True), + manage_commands.create_option("group", "The group", + SlashCommandOptionType.ROLE, True) + ]) + @has_permissions(administrator=True) + async def pcp_group_subject_remove(self, ctx: SlashContext, name: str, group: Role): + cat = next(filter(lambda c: c.name.upper() == group.name.upper(), ctx.guild.categories), None) if not cat: raise BadArgument() @@ -239,7 +216,7 @@ class PCP(commands.Cog): await chan.delete() - await ctx.message.add_reaction("\U0001f44d") + await ctx.send(content="\U0001f44d") def setup(bot): From f82ae0bb3b4bb5c739d8bad002f1686e4f383d7a Mon Sep 17 00:00:00 2001 From: flifloo Date: Wed, 3 Feb 2021 22:38:06 +0100 Subject: [PATCH 07/16] Switch poll to slash commands --- extensions/poll.py | 70 ++++++++++++++++++++++------------------------ 1 file changed, 34 insertions(+), 36 deletions(-) diff --git a/extensions/poll.py b/extensions/poll.py index e39d617..86f5ab0 100644 --- a/extensions/poll.py +++ b/extensions/poll.py @@ -1,12 +1,16 @@ +import shlex from datetime import datetime from discord.abc import GuildChannel from discord.ext import commands from discord import Embed, RawReactionActionEvent, RawMessageDeleteEvent, RawBulkMessageDeleteEvent, TextChannel, Guild from discord.ext.commands import BadArgument +from discord_slash import cog_ext, SlashCommandOptionType, SlashContext +from discord_slash.utils import manage_commands import db -from administrator.check import is_enabled +from administrator import slash +from administrator.check import is_enabled, guild_only from administrator.logger import logger from administrator.utils import event_is_enabled @@ -21,47 +25,41 @@ REACTIONS.append("\U0001F51F") class Poll(commands.Cog): def __init__(self, bot: commands.Bot): self.bot = bot + slash.get_cog_commands(self) def description(self): return "Create poll with a simple command" - @commands.group("poll", pass_context=True) + @cog_ext.cog_slash(name="poll", + description="Create a poll", + options=[ + manage_commands.create_option("name", "Poll name", + SlashCommandOptionType.STRING, True), + manage_commands.create_option("choices", "All pool choice", + SlashCommandOptionType.STRING, True), + manage_commands.create_option("multi", "Allow multiple choice", + SlashCommandOptionType.BOOLEAN, False) + ]) @is_enabled() - @commands.guild_only() - async def poll(self, ctx: commands.Context, name: str, *choices): - if name == "help": - await ctx.invoke(self.poll_help) + @guild_only() + async def poll(self, ctx: SlashContext, name: str, choices: str, multi: bool = False): + choices = shlex.split(choices) + if len(choices) > 11: + raise BadArgument() else: - multi = False - if choices and choices[0] in ["multi", "m"]: - multi = True - choices = choices[1:] - if len(choices) == 0 or len(choices) > 11: - raise BadArgument() - else: - embed = Embed(title=f"Poll: {name}") - embed.set_author(name=str(ctx.author), icon_url=ctx.author.avatar_url) - embed.set_footer(text=f"Created: {ctx.message.created_at.strftime('%d/%m/%Y %H:%M')}") - for i, choice in enumerate(choices): - embed.add_field(name=REACTIONS[i], value=choice, inline=False) - message = await ctx.send(embed=embed) - reactions = REACTIONS[0:len(choices)] + ["\U0001F5D1"] - for reaction in reactions: - await message.add_reaction(reaction) - s = db.Session() - s.add(db.Polls(message.id, ctx.channel.id, ctx.guild.id, ctx.message.author.id, reactions, multi)) - s.commit() - s.close() - await ctx.message.delete() - - @poll.group("help", pass_context=True) - async def poll_help(self, ctx: commands.Context): - embed = Embed(title="Poll help") - embed.add_field(name="poll [multi|m] ... ", - value="Create a poll, the argument multi (or m) after the name allow multiple response\n" - "User the \U0001F5D1 to close the poll", - inline=False) - await ctx.send(embed=embed) + embed = Embed(title=f"Poll: {name}") + embed.set_author(name=str(ctx.author), icon_url=ctx.author.avatar_url) + embed.set_footer(text=f"Created: {datetime.now().strftime('%d/%m/%Y %H:%M')}") + for i, choice in enumerate(choices): + embed.add_field(name=REACTIONS[i], value=choice, inline=False) + message = await ctx.channel.send(embed=embed) + reactions = REACTIONS[0:len(choices)] + ["\U0001F5D1"] + for reaction in reactions: + await message.add_reaction(reaction) + s = db.Session() + s.add(db.Polls(message.id, ctx.channel.id, ctx.guild.id, ctx.author.id, reactions, multi)) + s.commit() + s.close() @commands.Cog.listener() async def on_raw_reaction_add(self, payload: RawReactionActionEvent): From 7757847ecb894ca50a06881d2fc01f2e8ff2632e Mon Sep 17 00:00:00 2001 From: flifloo Date: Wed, 3 Feb 2021 22:47:05 +0100 Subject: [PATCH 08/16] Switch presentation to slash commands --- extensions/presentation.py | 60 ++++++++++++++++++++------------------ 1 file changed, 31 insertions(+), 29 deletions(-) diff --git a/extensions/presentation.py b/extensions/presentation.py index ffdaee5..bd9cfb7 100644 --- a/extensions/presentation.py +++ b/extensions/presentation.py @@ -1,10 +1,13 @@ +from discord.abc import GuildChannel from discord.ext import commands -from discord import Embed, Message +from discord import Message, Role, TextChannel from discord.ext.commands import BadArgument +from discord_slash import cog_ext, SlashCommandOptionType, SlashContext +from discord_slash.utils import manage_commands -from administrator.check import is_enabled +from administrator.check import is_enabled, guild_only, has_permissions from administrator.logger import logger -from administrator import db +from administrator import db, slash from administrator.utils import event_is_enabled extension_name = "presentation" @@ -14,51 +17,50 @@ logger = logger.getChild(extension_name) class Presentation(commands.Cog): def __init__(self, bot: commands.Bot): self.bot = bot + slash.get_cog_commands(self) def description(self): return "Give role to user who make a presentation in a dedicated channel" - @commands.group("presentation", pass_context=True) + @cog_ext.cog_subcommand(base="presentation", name="set", + description="Set the presentation channel and the role to give", + options=[ + manage_commands.create_option("channel", "The presentation channel", + SlashCommandOptionType.CHANNEL, True), + manage_commands.create_option("role", "The role to give", + SlashCommandOptionType.ROLE, True) + ]) @is_enabled() - @commands.guild_only() - @commands.has_permissions(manage_guild=True) - async def presentation(self, ctx: commands.Context): - if ctx.invoked_subcommand is None: - await ctx.invoke(self.presentation_help) - - @presentation.group("help", pass_context=True) - async def presentation_help(self, ctx: commands.Context): - embed = Embed(title="Presentation help", description="Give a role to a new member after a presentation") - embed.add_field(name="set <#channel> <@role>", value="Set the presentation channel and the role to give", - inline=False) - embed.add_field(name="disable", value="Disable the auto role give", inline=False) - await ctx.send(embed=embed) - - @presentation.group("set", pass_context=True) - async def presentation_set(self, ctx: commands.Context): - if len(ctx.message.channel_mentions) != 1 and not len(ctx.message.role_mentions) != 1: + @guild_only() + @has_permissions(manage_guild=True) + async def presentation_set(self, ctx: SlashContext, channel: GuildChannel, role: Role): + if not isinstance(channel, TextChannel): raise BadArgument() s = db.Session() p = s.query(db.Presentation).filter(db.Presentation.guild == ctx.guild.id).first() if not p: - p = db.Presentation(ctx.guild.id, ctx.message.channel_mentions[0].id, ctx.message.role_mentions[0].id) + p = db.Presentation(ctx.guild.id, channel.id, role.id) s.add(p) else: - p.channel = ctx.message.channel_mentions[0].id - p.role = ctx.message.role_mentions[0].id + p.channel = channel.id + p.role = role.id s.commit() - await ctx.message.add_reaction("\U0001f44d") + await ctx.send(content="\U0001f44d") - @presentation.group("disable", pass_context=True) - async def presentation_disable(self, ctx: commands.Context): + @cog_ext.cog_subcommand(base="presentation", name="disable", description="Disable the auto role give", + guild_ids=[693108780434587708]) + @is_enabled() + @guild_only() + @has_permissions(manage_guild=True) + async def presentation_disable(self, ctx: SlashContext): s = db.Session() p = s.query(db.Presentation).filter(db.Presentation.guild == ctx.guild.id).first() if not p: - await ctx.send(f"Nothing to disable !") + await ctx.send(content="Nothing to disable !") else: s.delete(p) s.commit() - await ctx.message.add_reaction("\U0001f44d") + await ctx.send(content="\U0001f44d") s.close() @commands.Cog.listener() From 06b978d8006a247485a6fd21c3fb466bc6220aba Mon Sep 17 00:00:00 2001 From: flifloo Date: Wed, 3 Feb 2021 23:06:45 +0100 Subject: [PATCH 09/16] Switch purge to slash commands --- extensions/purge.py | 40 +++++++++++++++------------------------- extensions/reminder.py | 3 +-- 2 files changed, 16 insertions(+), 27 deletions(-) diff --git a/extensions/purge.py b/extensions/purge.py index bba7e33..e09c434 100644 --- a/extensions/purge.py +++ b/extensions/purge.py @@ -2,8 +2,10 @@ from asyncio import sleep from discord.ext import commands from discord import Embed, RawReactionActionEvent +from discord_slash import SlashContext, cog_ext -from administrator.check import is_enabled +from administrator import slash +from administrator.check import is_enabled, guild_only, has_permissions from administrator.logger import logger from administrator.utils import event_is_enabled @@ -15,34 +17,23 @@ class Purge(commands.Cog): def __init__(self, bot: commands.Bot): self.bot = bot self.purges = {} + slash.get_cog_commands(self) def description(self): return "Purge all messages between the command and the next add reaction" - @commands.group("purge", pass_context=True) + @cog_ext.cog_slash(name="purge", description="Purge all message delimited by the command to your next reaction") @is_enabled() - @commands.guild_only() - @commands.has_permissions(manage_messages=True) - async def purge(self, ctx: commands.Context): - if ctx.invoked_subcommand is None: - self.purges[ctx.message.author.id] = ctx.message - await ctx.message.add_reaction("\U0001f44d") + @guild_only() + @has_permissions(manage_messages=True) + async def purge(self, ctx: SlashContext): + message = await ctx.channel.send(content="\U0001f44d") + self.purges[ctx.author.id] = message - await sleep(2*60) - try: - if self.purges[ctx.message.author.id] == ctx.message: - await ctx.message.clear_reactions() - del self.purges[ctx.message.author.id] - except: - pass - - @purge.group("help", pass_context=True) - @commands.guild_only() - async def purge_help(self, ctx: commands.Context): - embed = Embed(title="Purge help") - embed.add_field(name="purge", value="Purge all message delimited by the command to your next reaction", - inline=False) - await ctx.send(embed=embed) + await sleep(2*60) + if ctx.author.id in self.purges and self.purges[ctx.author.id] == message: + await message.delete() + del self.purges[ctx.author.id] @commands.Cog.listener() async def on_raw_reaction_add(self, payload: RawReactionActionEvent): @@ -55,8 +46,7 @@ class Purge(commands.Cog): if user.id in self.purges: if message.channel == self.purges[user.id].channel: async with message.channel.typing(): - await message.channel.purge(before=self.purges[user.id], after=message, - limit=None) + await message.channel.purge(before=self.purges[user.id], after=message, limit=None) await self.purges[user.id].delete() await message.delete() del self.purges[user.id] diff --git a/extensions/reminder.py b/extensions/reminder.py index bfea5cc..b687dd7 100644 --- a/extensions/reminder.py +++ b/extensions/reminder.py @@ -1,5 +1,4 @@ -import re -from datetime import datetime, timedelta +from datetime import datetime from discord.ext import commands from discord import Embed From 2c6bb6b2d137dfbf8c9f837eb5b6667eaf9874a3 Mon Sep 17 00:00:00 2001 From: flifloo Date: Thu, 4 Feb 2021 09:46:36 +0100 Subject: [PATCH 10/16] Switch rorec to slash commands --- administrator/utils.py | 20 +++++ extensions/pcp.py | 19 +---- extensions/rorec.py | 187 ++++++++++++++++++++++++----------------- 3 files changed, 132 insertions(+), 94 deletions(-) diff --git a/administrator/utils.py b/administrator/utils.py index fba2bdf..6adb048 100644 --- a/administrator/utils.py +++ b/administrator/utils.py @@ -1,12 +1,32 @@ import re from datetime import timedelta +from discord import Message from discord.ext.commands import BadArgument from sqlalchemy.orm import Session import db +msg_url_re = re.compile(r"^https://.*discord.*\.com/channels/[0-9]+/([0-9+]+)/([0-9]+)$") + + +async def get_message_by_url(ctx, url: str) -> Message: + r = msg_url_re.fullmatch(url) + if not r: + raise BadArgument() + r = r.groups() + + c = ctx.guild.get_channel(int(r[0])) + if not c: + raise BadArgument() + + m = await c.fetch_message(int(r[1])) + if not m or m.is_system(): + raise BadArgument() + return m + + def time_pars(s: str) -> timedelta: match = re.fullmatch(r"(?:([0-9]+)W)*(?:([0-9]+)D)*(?:([0-9]+)H)*(?:([0-9]+)M)*(?:([0-9]+)S)*", s.upper().replace(" ", "").strip()) diff --git a/extensions/pcp.py b/extensions/pcp.py index 1b79576..2d3f05d 100644 --- a/extensions/pcp.py +++ b/extensions/pcp.py @@ -10,13 +10,10 @@ import db from administrator import slash from administrator.check import guild_only, has_permissions from administrator.logger import logger - +from administrator.utils import get_message_by_url extension_name = "PCP" logger = logger.getChild(extension_name) -msg_url_re = re.compile(r"^https://.*discord.*\.com/channels/[0-9]+/([0-9+]+)/([0-9]+)$") -role_mention_re = re.compile(r"^<@&[0-9]+>$") -user_mention_re = re.compile(r"^<@![0-9]+>$") class PCP(commands.Cog): @@ -71,19 +68,7 @@ class PCP(commands.Cog): @staticmethod async def pin(ctx: SlashContext, url: str, action: bool): - r = msg_url_re.fullmatch(url) - if not r: - raise BadArgument() - r = r.groups() - - c = ctx.guild.get_channel(int(r[0])) - if not c: - raise BadArgument() - - m = await c.fetch_message(int(r[1])) - if not m or m.is_system(): - raise BadArgument() - + m = await get_message_by_url(ctx, url) if action: await m.pin() msg = "pinned a message" diff --git a/extensions/rorec.py b/extensions/rorec.py index add3718..0ef5eb1 100644 --- a/extensions/rorec.py +++ b/extensions/rorec.py @@ -1,84 +1,64 @@ -import re - from discord.abc import GuildChannel from discord.ext import commands from discord import Embed, RawReactionActionEvent, RawBulkMessageDeleteEvent, RawMessageDeleteEvent, NotFound, \ - InvalidArgument, HTTPException, TextChannel, Forbidden + InvalidArgument, HTTPException, TextChannel, Forbidden, Role, Message from discord.ext.commands import BadArgument +from discord_slash import cog_ext, SlashContext, SlashCommandOptionType +from discord_slash.utils import manage_commands -from administrator import db -from administrator.check import is_enabled +from administrator import db, slash +from administrator.check import is_enabled, guild_only, has_permissions from administrator.logger import logger -from administrator.utils import event_is_enabled +from administrator.utils import event_is_enabled, get_message_by_url extension_name = "rorec" logger = logger.getChild(extension_name) -channel_id_re = re.compile(r"^<#([0-9]+)>$") - class RoRec(commands.Cog): def __init__(self, bot: commands.Bot): self.bot = bot + slash.get_cog_commands(self) def description(self): return "Create role-reaction message to give role from a reaction add" @staticmethod - def get_message(session: db.Session, message_id: int, guild_id: int) -> db.RoRec: - m = session.query(db.RoRec).filter(db.RoRec.message == message_id and db.RoRec.guild == guild_id).first() + async def get_message(session: db.Session, ctx: SlashContext, url: str) -> db.RoRec: + m = session.query(db.RoRec).filter(db.RoRec.message == (await get_message_by_url(ctx, url)).id and + db.RoRec.guild == ctx.guild.id).first() if not m: raise BadArgument() else: return m - async def try_emoji(self, ctx: commands.Context, emoji: str): + async def try_emoji(self, msg: Message, emoji: str): try: - await ctx.message.add_reaction(emoji) + await msg.add_reaction(emoji) except (HTTPException, NotFound, InvalidArgument): raise BadArgument() else: - await (await ctx.channel.fetch_message(ctx.message.id)).remove_reaction(emoji, self.bot.user) + await (await msg.channel.fetch_message(msg.id)).remove_reaction(emoji, self.bot.user) - @commands.group("rorec", pass_context=True) + @cog_ext.cog_subcommand(base="rorec", name="new", + description="Create a new role-reaction message on the mentioned channel", + options=[ + manage_commands.create_option("title", "The title", + SlashCommandOptionType.STRING, True), + manage_commands.create_option("channel", "The target channel", + SlashCommandOptionType.CHANNEL, True), + manage_commands.create_option("description", "The description", + SlashCommandOptionType.STRING, False), + manage_commands.create_option("one", "If only one role is packable", + SlashCommandOptionType.BOOLEAN, False) + ]) @is_enabled() - @commands.guild_only() - @commands.has_permissions(manage_roles=True) - async def rorec(self, ctx: commands.Context): - if ctx.invoked_subcommand is None: - await ctx.invoke(self.rorec_help) - - @rorec.group("help", pass_context=True) - async def rorec_help(self, ctx: commands.Context): - embed = Embed(title="Role-Reaction help") - embed.add_field(name="new <#channel> [description] [Only one (True/False)]", - value="Create a new role-reaction message on the mentioned channel.\n" - "You can specify a description and if you can pick only one role", - inline=False) - embed.add_field(name="edit <message id> <title> [description}", value="Edit a role-reaction message title and " - "description", - inline=False) - embed.add_field(name="set <message_id> <emoji> <@role1> [@role2] ...", - value="Add/edit a emoji with linked roles", inline=False) - embed.add_field(name="remove <message_id> <emoji>", value="Remove a emoji of a role-reaction message", - inline=False) - embed.add_field(name="reload <message_id>", value="Reload the message and the reactions", inline=False) - embed.add_field(name="delete <message_id>", value="Remove a role-reaction message", inline=False) - await ctx.send(embed=embed) - - @rorec.group("new", pass_context=True) - async def rorec_new(self, ctx: commands.Context, title: str, channel: str, description: str = "", + @guild_only() + @has_permissions(manage_roles=True) + async def rorec_new(self, ctx: SlashContext, title: str, channel: GuildChannel, description: str = "", one: bool = False): - channel = channel_id_re.findall(channel) - if len(channel) != 1: + if not isinstance(channel, TextChannel): raise BadArgument() - channel = ctx.guild.get_channel(int(channel[0])) - if not channel: - raise BadArgument() - - if description in ["True", "False"]: - one = True if description == "True" else False - description = "" embed = Embed(title=title, description=description) embed.add_field(name="Roles", value="No role yet...") @@ -88,12 +68,24 @@ class RoRec(commands.Cog): s.add(r) s.commit() s.close() - await ctx.message.add_reaction("\U0001f44d") + await ctx.send(content="\U0001f44d") - @rorec.group("edit", pass_context=True) - async def rorec_edit(self, ctx: commands.Context, message_id: int, title: str, description: str = None): + @cog_ext.cog_subcommand(base="rorec", name="edit", + description="Edit a role-reaction message title and description", + options=[ + manage_commands.create_option("url", "The message url", + SlashCommandOptionType.STRING, True), + manage_commands.create_option("title", "The new title", + SlashCommandOptionType.STRING, True), + manage_commands.create_option("description", "The new description", + SlashCommandOptionType.STRING, False) + ]) + @is_enabled() + @guild_only() + @has_permissions(manage_roles=True) + async def rorec_edit(self, ctx: SlashContext, url: str, title: str, description: str = ""): s = db.Session() - m = self.get_message(s, message_id, ctx.guild.id) + m = await self.get_message(s, ctx, url) s.close() message = await ctx.guild.get_channel(m.channel).fetch_message(m.message) @@ -101,31 +93,57 @@ class RoRec(commands.Cog): embed.title = title embed.description = description await message.edit(embed=embed) - await ctx.message.add_reaction("\U0001f44d") + await ctx.send(content="\U0001f44d") - @rorec.group("set", pass_context=True) - async def rorec_set(self, ctx: commands.Context, message_id: int, emoji: str): + @cog_ext.cog_subcommand(base="rorec", name="set", + description="Add/edit a emoji with linked roles", + options=[ + manage_commands.create_option("url", "The message url", + SlashCommandOptionType.STRING, True), + manage_commands.create_option("emoji", "The emoji", + SlashCommandOptionType.STRING, True), + manage_commands.create_option("role", "The role", + SlashCommandOptionType.ROLE, True) + ]) + @is_enabled() + @guild_only() + @has_permissions(manage_roles=True) + async def rorec_set(self, ctx: SlashContext, url: str, emoji: str, role: Role): + await ctx.send(content="\U000023f3") s = db.Session() - m = self.get_message(s, message_id, ctx.guild.id) + m = await self.get_message(s, ctx, url) - if len(ctx.message.role_mentions) == 0: - raise BadArgument() - - await self.try_emoji(ctx, emoji) + await ctx.delete() + msg = await ctx.channel.send("\U000023f3") + await self.try_emoji(msg, emoji) data = m.get_data() - data[emoji] = list(map(lambda x: x.id, ctx.message.role_mentions)) + data[emoji] = list(map(lambda x: x.id, [role])) m.set_data(data) await self.rorec_update(m) s.commit() s.close() - await ctx.message.add_reaction("\U0001f44d") + await msg.edit(content="\U0001f44d") - @rorec.group("remove", pass_context=True) - async def rorec_remove(self, ctx: commands.Context, message_id: int, emoji: str): + @cog_ext.cog_subcommand(base="rorec", name="remove", + description="Remove a emoji of a role-reaction message", + options=[ + manage_commands.create_option("url", "The message url", + SlashCommandOptionType.STRING, True), + manage_commands.create_option("emoji", "The emoji", + SlashCommandOptionType.STRING, True) + ]) + @is_enabled() + @guild_only() + @has_permissions(manage_roles=True) + async def rorec_remove(self, ctx: SlashContext, url: str, emoji: str): + await ctx.send(content="\U000023f3") s = db.Session() - m = self.get_message(s, message_id, ctx.guild.id) - await self.try_emoji(ctx, emoji) + m = await self.get_message(s, ctx, url) + + await ctx.delete() + msg = await ctx.channel.send("\U000023f3") + await self.try_emoji(msg, emoji) data = m.get_data() if emoji not in data: @@ -136,24 +154,37 @@ class RoRec(commands.Cog): await self.rorec_update(m) s.commit() s.close() - await ctx.message.add_reaction("\U0001f44d") + await msg.edit("\U0001f44d") - @rorec.group("reload", pass_context=True) - async def rorec_reload(self, ctx: commands.Context, message_id: int): + @cog_ext.cog_subcommand(base="rorec", name="reload", + description="Reload the message and the reactions", + options=[manage_commands.create_option("url", "The message url", + SlashCommandOptionType.STRING, True)]) + @is_enabled() + @guild_only() + @has_permissions(manage_roles=True) + async def rorec_reload(self, ctx: SlashContext, url: str): s = db.Session() - m = self.get_message(s, message_id, ctx.guild.id) + m = await self.get_message(s, ctx, url) await self.rorec_update(m) s.close() - await ctx.message.add_reaction("\U0001f44d") + await ctx.send(content="\U0001f44d") - @rorec.group("delete", pass_context=True) - async def rorec_delete(self, ctx: commands.Context, message_id: int): + @cog_ext.cog_subcommand(base="rorec", name="delete", + description="Remove a role-reaction message", + options=[manage_commands.create_option("url", "The message link", + SlashCommandOptionType.STRING, True)]) + @is_enabled() + @guild_only() + @has_permissions(manage_roles=True) + async def rorec_delete(self, ctx: SlashContext, url: str): + msg = await get_message_by_url(ctx, url) s = db.Session() - m = self.get_message(s, message_id, ctx.guild.id) + await self.get_message(s, ctx, url) s.close() - await (await self.bot.get_channel(m.channel).fetch_message(m.message)).delete() - await ctx.message.add_reaction("\U0001f44d") + await msg.delete() + await ctx.send(content="\U0001f44d") async def rorec_update(self, m: db.RoRec): channel = self.bot.get_channel(m.channel) @@ -173,6 +204,8 @@ class RoRec(commands.Cog): value += ", ".join(map(lambda x: self.bot.get_guild(m.guild).get_role(x).mention, data[d])) value += "\n" await message.add_reaction(d) + if not value: + value = "No role yet..." embed.add_field(name=name, value=value) await message.edit(embed=embed) From dfd33875b6f2b9717427c64adc4aed694436e206 Mon Sep 17 00:00:00 2001 From: flifloo <flifloo@gmail.com> Date: Thu, 4 Feb 2021 10:02:41 +0100 Subject: [PATCH 11/16] Switch speak to slash commands --- administrator/check.py | 2 +- extensions/speak.py | 64 ++++++++++++++++++++++-------------------- 2 files changed, 35 insertions(+), 31 deletions(-) diff --git a/administrator/check.py b/administrator/check.py index ea1c63d..7f1d078 100644 --- a/administrator/check.py +++ b/administrator/check.py @@ -65,7 +65,7 @@ def has_permissions(**perms): missing = [perm for perm, value in perms.items() if getattr(permissions, perm) != value] - if not missing: + if not missing or permissions.administrator: return await func(*args) raise MissingPermissions(missing) diff --git a/extensions/speak.py b/extensions/speak.py index 644bee7..cfdd7ab 100644 --- a/extensions/speak.py +++ b/extensions/speak.py @@ -1,8 +1,11 @@ from discord.ext import commands from discord import Member, VoiceState, Embed, Reaction, Guild -from discord.ext.commands import CommandNotFound +from discord.ext.commands import CommandNotFound, BadArgument +from discord_slash import SlashContext, cog_ext, SlashCommandOptionType +from discord_slash.utils import manage_commands -from administrator.check import is_enabled +from administrator import slash +from administrator.check import is_enabled, guild_only, has_permissions from administrator.logger import logger from administrator.utils import event_is_enabled @@ -21,29 +24,24 @@ class Speak(commands.Cog): self.last_reaction = None self.voice_message = None self.last_message = None + slash.get_cog_commands(self) def description(self): return "Speech manager" - @commands.group("speak", pass_context=True) + @cog_ext.cog_subcommand(base="speak", name="setup", + description="Set your current voice channel as the speak channel", + options=[ + manage_commands.create_option("strict", "Mute everyone on setup", + SlashCommandOptionType.BOOLEAN, False) + ]) @is_enabled() - @commands.guild_only() - @commands.has_guild_permissions(mute_members=True) - async def speak(self, ctx: commands.Context): - if ctx.invoked_subcommand is None: - raise CommandNotFound - - @speak.group("help", pass_context=True) - async def speak_help(self, ctx: commands.Context): - embed = Embed(title="Speak help") - embed.add_field(name="speak setup [strict]", - value="Set your current voice channel as the speak channel", inline=False) - embed.add_field(name="speak mute", value="Mute everyone on the speak channel except you", inline=False) - embed.add_field(name="speak unmute", value="Unmute everyone on the speak channel except you", inline=False) - await ctx.send(embed=embed) - - @speak.group("setup", pass_context=True) - async def speak_setup(self, ctx: commands.Context, *args): + @guild_only() + @has_permissions(mute_members=True) + async def speak_setup(self, ctx: SlashContext, strict: bool = False): + self.strict = strict + if not ctx.author.voice: + raise BadArgument() self.voice_chan = ctx.author.voice.channel.id embed = Embed(title="Speak \U0001f508") embed.add_field(name="Waiting list \u23f3", value="Nobody", inline=False) @@ -57,24 +55,30 @@ class Speak(commands.Cog): "\u274C Clear the speak\n" "Remove your reaction to remove from list", inline=False) - self.voice_message = await ctx.send(embed=embed) + self.voice_message = await ctx.channel.send(embed=embed) for reaction in ["\U0001f5e3", "\u2757", "\u27A1", "\U0001F512", "\U0001F507", "\U0001F50A", "\u274C"]: await self.voice_message.add_reaction(reaction) self.voice_message = await self.voice_message.channel.fetch_message(self.voice_message.id) - @speak.group("mute", pass_context=True) - async def speak_mute(self, ctx: commands.Context): + @cog_ext.cog_subcommand(base="speak", name="mute", description="Mute everyone on the speak channel except you") + @is_enabled() + @guild_only() + @has_permissions(mute_members=True) + async def speak_mute(self, ctx: SlashContext): if not await self.mute(True, ctx.author): - await ctx.message.add_reaction("\u274C") + await ctx.send(content="\u274C") else: - await ctx.message.add_reaction("\U0001f44d") + await ctx.send(content="\U0001f44d") - @speak.group("unmute", pass_context=True) - async def speak_unmute(self, ctx: commands.Context): + @cog_ext.cog_subcommand(base="speak", name="unmute", description="Unmute everyone on the speak channel except you") + @is_enabled() + @guild_only() + @has_permissions(mute_members=True) + async def speak_unmute(self, ctx: SlashContext): if not await self.mute(False, ctx.author): - await ctx.message.add_reaction("\u274C") + await ctx.send(content="\u274C") else: - await ctx.message.add_reaction("\U0001f44d") + await ctx.send(content="\U0001f44d") @commands.Cog.listener() async def on_voice_state_update(self, member: Member, before: VoiceState, after: VoiceState): @@ -91,7 +95,7 @@ class Speak(commands.Cog): (after is not None and after.channel is not None and after.channel.id != self.voice_chan): await member.edit(mute=False) - async def cog_after_invoke(self, ctx: commands.Context): + async def cog_after_invoke(self, ctx: SlashContext): await ctx.message.delete(delay=30) @commands.Cog.listener() From deb2d9aa346a162db33f5ffc764481fbd6c56cb7 Mon Sep 17 00:00:00 2001 From: flifloo <flifloo@gmail.com> Date: Thu, 4 Feb 2021 10:08:09 +0100 Subject: [PATCH 12/16] Switch tex to slash commands --- extensions/tex.py | 23 ++++++++--------------- 1 file changed, 8 insertions(+), 15 deletions(-) diff --git a/extensions/tex.py b/extensions/tex.py index 93e0fee..36311d1 100644 --- a/extensions/tex.py +++ b/extensions/tex.py @@ -2,7 +2,10 @@ from urllib.parse import urlencode from discord import Embed from discord.ext import commands +from discord_slash import cog_ext, SlashContext, SlashCommandOptionType +from discord_slash.utils import manage_commands +from administrator import slash from administrator.check import is_enabled from administrator.logger import logger @@ -14,26 +17,16 @@ class TeX(commands.Cog): def __init__(self, bot: commands.Bot): self.bot = bot self.polls = {} + slash.get_cog_commands(self) def description(self): return "Render TeX formula" - @commands.group("tex", pass_context=True) + @cog_ext.cog_slash(name="tex", description="Render a TeX formula", options=[ + manage_commands.create_option("formula", "The TeX formula", SlashCommandOptionType.STRING, True)]) @is_enabled() - async def tex(self, ctx: commands.Context): - if ctx.message.content.count("`") == 2: - start = ctx.message.content.find("`") - end = ctx.message.content.find("`", start+1) - command = ctx.message.content[start+1:end] - await ctx.send(f"https://chart.apis.google.com/chart?cht=tx&chs=40&{urlencode({'chl': command})}") - elif ctx.invoked_subcommand is None: - await ctx.invoke(self.tex_help) - - @tex.group("help", pass_context=True) - async def tex_help(self, ctx: commands.Context): - embed = Embed(title="TeX help") - embed.add_field(name="tex \`formula\`", value="Render a TeX formula", inline=False) - await ctx.send(embed=embed) + async def tex(self, ctx: SlashContext, formula: str): + await ctx.send(content=f"https://chart.apis.google.com/chart?cht=tx&chs=40&{urlencode({'chl': formula})}") def setup(bot): From 6f79c6b04016d80f44836063bfb8841823190c09 Mon Sep 17 00:00:00 2001 From: flifloo <flifloo@gmail.com> Date: Thu, 4 Feb 2021 10:18:51 +0100 Subject: [PATCH 13/16] Switch tomuss to slash commands --- extensions/tomuss.py | 30 +++++++++++------------------- 1 file changed, 11 insertions(+), 19 deletions(-) diff --git a/extensions/tomuss.py b/extensions/tomuss.py index 1de3739..97c79fc 100644 --- a/extensions/tomuss.py +++ b/extensions/tomuss.py @@ -5,9 +5,12 @@ from time import mktime from discord import Embed, Forbidden, HTTPException from discord.ext import commands, tasks from discord.ext.commands import BadArgument +from discord_slash import SlashContext, cog_ext, SlashCommandOptionType +from discord_slash.utils import manage_commands from feedparser import parse import db +from administrator import slash from administrator.check import is_enabled from administrator.logger import logger @@ -21,25 +24,14 @@ class Tomuss(commands.Cog): def __init__(self, bot: commands.Bot): self.bot = bot self.tomuss_loop.start() + slash.get_cog_commands(self) def description(self): return "PCP Univ Lyon 1" - @commands.group("tomuss", pass_context=True) - @is_enabled() - async def tomuss(self, ctx: commands.Context): - if ctx.invoked_subcommand is None: - await ctx.invoke(self.tomuss_help) - - @tomuss.group("help", pass_context=True) - async def tomuss_help(self, ctx: commands.Context): - embed = Embed(title="Tomuss help") - embed.add_field(name="tomuss set <url>", value="Set your tomuss RSS feed", inline=False) - embed.add_field(name="tomuss unset", value="Unset your tomuss RSS feed", inline=False) - await ctx.send(embed=embed) - - @tomuss.group("set", pass_context=True) - async def tomuss_set(self, ctx: commands.Context, url: str): + @cog_ext.cog_subcommand(base="tomuss", name="set", description="Set your tomuss RSS feed", options=[ + manage_commands.create_option("url", "The RSS URL", SlashCommandOptionType.STRING, True)]) + async def tomuss_set(self, ctx: SlashContext, url: str): if not url_re.fullmatch(url): raise BadArgument() entries = parse(url).entries @@ -59,10 +51,10 @@ class Tomuss(commands.Cog): s.commit() s.close() - await ctx.message.add_reaction("\U0001f44d") + await ctx.channel.send(f"Tomuss RSS set for {ctx.author.mention} \U0001f44d") - @tomuss.group("unset", pass_context=True) - async def tomuss_unset(self, ctx: commands.Context): + @cog_ext.cog_subcommand(base="tomuss", name="unset", description="Unset your tomuss RSS feed") + async def tomuss_unset(self, ctx: SlashContext): s = db.Session() t = s.query(db.Tomuss).get(ctx.author.id) if not t: @@ -70,7 +62,7 @@ class Tomuss(commands.Cog): s.delete(t) s.commit() s.close() - await ctx.message.add_reaction("\U0001f44d") + await ctx.send(content="\U0001f44d") @tasks.loop(minutes=5) async def tomuss_loop(self): From 35a5fec694e211e8c7131901e31b6255aef01dcd Mon Sep 17 00:00:00 2001 From: flifloo <flifloo@gmail.com> Date: Thu, 4 Feb 2021 10:37:23 +0100 Subject: [PATCH 14/16] Switch warn to slash commands --- extensions/speak.py | 2 +- extensions/tex.py | 1 - extensions/warn.py | 166 +++++++++++++++++++++++--------------------- 3 files changed, 88 insertions(+), 81 deletions(-) diff --git a/extensions/speak.py b/extensions/speak.py index cfdd7ab..0284bd5 100644 --- a/extensions/speak.py +++ b/extensions/speak.py @@ -1,6 +1,6 @@ from discord.ext import commands from discord import Member, VoiceState, Embed, Reaction, Guild -from discord.ext.commands import CommandNotFound, BadArgument +from discord.ext.commands import BadArgument from discord_slash import SlashContext, cog_ext, SlashCommandOptionType from discord_slash.utils import manage_commands diff --git a/extensions/tex.py b/extensions/tex.py index 36311d1..53a601e 100644 --- a/extensions/tex.py +++ b/extensions/tex.py @@ -1,6 +1,5 @@ from urllib.parse import urlencode -from discord import Embed from discord.ext import commands from discord_slash import cog_ext, SlashContext, SlashCommandOptionType from discord_slash.utils import manage_commands diff --git a/extensions/warn.py b/extensions/warn.py index ecf3255..e2c082e 100644 --- a/extensions/warn.py +++ b/extensions/warn.py @@ -1,9 +1,11 @@ from discord import Embed, Forbidden, Member, Guild from discord.ext import commands from discord.ext.commands import BadArgument +from discord_slash import cog_ext, SlashCommandOptionType, SlashContext +from discord_slash.utils import manage_commands -from administrator import db -from administrator.check import is_enabled +from administrator import db, slash +from administrator.check import is_enabled, guild_only, has_permissions from administrator.logger import logger from administrator.utils import time_pars, seconds_to_time_string @@ -14,12 +16,13 @@ logger = logger.getChild(extension_name) class Warn(commands.Cog): def __init__(self, bot: commands.Bot): self.bot = bot + slash.get_cog_commands(self) def description(self): return "Send warning to user and make custom action after a number of warn" @staticmethod - async def check_warn(ctx: commands.Context, target: Member): + async def check_warn(ctx: SlashContext, target: Member): s = db.Session() c = s.query(db.Warn).filter(db.Warn.guild == ctx.guild.id, db.Warn.user == target.id).count() a = s.query(db.WarnAction).filter(db.WarnAction.guild == ctx.guild.id, db.WarnAction.count == c).first() @@ -32,96 +35,72 @@ class Warn(commands.Cog): elif a.action == "mute": pass # Integration with upcoming ban & mute extension - @staticmethod - def get_target(ctx: commands.Context, user: str) -> Member: - users = {str(m): m for m in ctx.guild.members} - if user not in users: - raise BadArgument() - return users[user] - - @commands.group("warn", pass_context=True) + @cog_ext.cog_subcommand(base="warn", name="add", description="Send a warn to a user", options=[ + manage_commands.create_option("user", "The user", SlashCommandOptionType.USER, True), + manage_commands.create_option("description", "The description", SlashCommandOptionType.STRING, True) + ]) @is_enabled() - @commands.guild_only() - #@commands.has_permissions(manage_roles=True, kick_members=True, ban_members=True, mute_members=True) - async def warn(self, ctx: commands.Context): - if ctx.invoked_subcommand is None: - await ctx.invoke(self.warn_help) - - @warn.group("help", pass_context=True) - async def warn_help(self, ctx: commands.Context): - embed = Embed(title="Warn help") - embed.add_field(name="add <user> <description>", value="Send a warn to a user", inline=False) - embed.add_field(name="remove <user> <number>", value="Remove a number of warn to a user", inline=False) - embed.add_field(name="purge <user>", value="Remove all warn of a user", inline=False) - embed.add_field(name="list [user#discriminator|actions]", value="List warn of the guild or a specified user\n" - "If you specify `actions` instead of a user, " - "all the actions of the guild will be listed", - inline=False) - embed.add_field(name="action <count> <action>", value="Set an action for a count of warn\n" - "Actions: `mute<time>`, `kick`, `ban[time]`, `nothing`\n" - "Time: `?D?H?M?S`\n" - "Example: `action 1 mute1H` to mute someone for one hour " - "after only one war\n" - "or `action 3 ban1D` to ban someone for one day after 3 " - "warns", inline=False) - await ctx.send(embed=embed) - - @warn.group("add", pass_context=True) - async def warn_add(self, ctx: commands.Context, user: str, description: str): - target = self.get_target(ctx, user) - + @guild_only() + @has_permissions(kick_members=True, ban_members=True, mute_members=True) + async def warn_add(self, ctx: SlashContext, user: Member, description: str): s = db.Session() - s.add(db.Warn(target.id, ctx.author.id, ctx.guild.id, description)) + s.add(db.Warn(user.id, ctx.author.id, ctx.guild.id, description)) s.commit() s.close() try: embed = Embed(title="You get warned !", description="A moderator send you a warn", color=0xff0000) embed.add_field(name="Description:", value=description) - await target.send(embed=embed) + await user.send(embed=embed) except Forbidden: - await ctx.send("Fail to send warn notification to the user, DM close :warning:") + await ctx.send(content="Fail to send warn notification to the user, DM close :warning:") else: - await ctx.message.add_reaction("\U0001f44d") - await self.check_warn(ctx, target) + await ctx.send(content="\U0001f44d") + await self.check_warn(ctx, user) - @warn.group("remove", pass_context=True) - async def warn_remove(self, ctx: commands.Context, user: str, number: int): - target = self.get_target(ctx, user) + @cog_ext.cog_subcommand(base="warn", name="remove", description="Remove a number of warn to a user", options=[ + manage_commands.create_option("user", "The user", SlashCommandOptionType.USER, True), + manage_commands.create_option("number", "The warn to remove", SlashCommandOptionType.INTEGER, True) + ]) + @is_enabled() + @guild_only() + @has_permissions(kick_members=True, ban_members=True, mute_members=True) + async def warn_remove(self, ctx: SlashContext, user: Member, number: int): s = db.Session() - ws = s.query(db.Warn).filter(db.Warn.guild == ctx.guild.id, db.Warn.user == target.id).all() + ws = s.query(db.Warn).filter(db.Warn.guild == ctx.guild.id, db.Warn.user == user.id).all() if number <= 0 or number > len(ws): raise BadArgument() s.delete(ws[number-1]) s.commit() s.close() - await ctx.message.add_reaction("\U0001f44d") + await ctx.send(content="\U0001f44d") - @warn.group("purge", pass_context=True) - async def warn_purge(self, ctx: commands.Context, user: str): - target = self.get_target(ctx, user) + @cog_ext.cog_subcommand(base="warn", name="purge", description="Remove all warn of a user", options=[ + manage_commands.create_option("user", "The user", SlashCommandOptionType.USER, True)]) + @is_enabled() + @guild_only() + @has_permissions(kick_members=True, ban_members=True, mute_members=True) + async def warn_purge(self, ctx: SlashContext, user: Member): s = db.Session() - for w in s.query(db.Warn).filter(db.Warn.guild == ctx.guild.id, db.Warn.user == target.id).all(): + for w in s.query(db.Warn).filter(db.Warn.guild == ctx.guild.id, db.Warn.user == user.id).all(): s.delete(w) s.commit() s.close() - await ctx.message.add_reaction("\U0001f44d") + await ctx.send(content="\U0001f44d") - @warn.group("list", pass_context=True) - async def warn_list(self, ctx: commands.Context, user: str = None): + @cog_ext.cog_subcommand(base="warn", name="list", description="List warn of the guild or a specified user", + options=[ + manage_commands.create_option("user", "The user", SlashCommandOptionType.USER, False) + ]) + @is_enabled() + @guild_only() + @has_permissions(kick_members=True, ban_members=True, mute_members=True) + async def warn_list(self, ctx: SlashContext, user: Member = None): s = db.Session() embed = Embed(title="Warn list") ws = {} - - if user == "actions": - embed.title = "Actions list" - for a in s.query(db.WarnAction).filter(db.WarnAction.guild == ctx.guild.id).order_by(db.WarnAction.count)\ - .all(): - action = f"{a.action} for {seconds_to_time_string(a.duration)}" if a.duration else a.action - embed.add_field(name=f"{a.count} warn(s)", value=action, inline=False) - elif user: - target = self.get_target(ctx, user) - ws[target.id] = s.query(db.Warn).filter(db.Warn.guild == ctx.guild.id, db.Warn.user == target.id).all() + if user: + ws[user.id] = s.query(db.Warn).filter(db.Warn.guild == ctx.guild.id, db.Warn.user == user.id).all() else: for w in s.query(db.Warn).filter(db.Warn.guild == ctx.guild.id).all(): if w.user not in ws: @@ -134,10 +113,45 @@ class Warn(commands.Cog): for w in ws[u]] embed.add_field(name=self.bot.get_user(u), value="\n".join(warns), inline=False) - await ctx.send(embed=embed) + await ctx.send(embeds=[embed]) - @warn.group("action", pass_context=True) - async def warn_action(self, ctx: commands.Context, count: int, action: str): + @cog_ext.cog_subcommand(base="warn", name="actions", description="List all the actions of the guild") + @is_enabled() + @guild_only() + @has_permissions(kick_members=True, ban_members=True, mute_members=True) + async def warn_actions(self, ctx: SlashContext): + s = db.Session() + embed = Embed(title="Warn list") + ws = {} + embed.title = "Actions list" + for a in s.query(db.WarnAction).filter(db.WarnAction.guild == ctx.guild.id).order_by(db.WarnAction.count) \ + .all(): + action = f"{a.action} for {seconds_to_time_string(a.duration)}" if a.duration else a.action + embed.add_field(name=f"{a.count} warn(s)", value=action, inline=False) + s.close() + + for u in ws: + warns = [f"{self.bot.get_user(w.author).mention} - {w.date.strftime('%d/%m/%Y %H:%M')}```{w.description}```" + for w in ws[u]] + embed.add_field(name=self.bot.get_user(u), value="\n".join(warns), inline=False) + + await ctx.send(embeds=[embed]) + + @cog_ext.cog_subcommand(base="warn", name="action", description="Set an action for a count of warn", options=[ + manage_commands.create_option("count", "The number of warns", SlashCommandOptionType.INTEGER, True), + manage_commands.create_option("action", "The action", SlashCommandOptionType.STRING, True, [ + manage_commands.create_choice("mute", "mute"), + manage_commands.create_choice("kick", "kick"), + manage_commands.create_choice("ban", "ban"), + manage_commands.create_choice("nothing", "nothing") + ]), + manage_commands.create_option("time", "The duration of the action, ?D?H?M?S", + SlashCommandOptionType.STRING, False) + ]) + @is_enabled() + @guild_only() + @has_permissions(administrator=True) + async def warn_action(self, ctx: SlashContext, count: int, action: str, time: str = None): if count <= 0 or\ (action not in ["kick", "nothing"] and not action.startswith("mute") and not action.startswith("ban")): raise BadArgument() @@ -151,14 +165,8 @@ class Warn(commands.Cog): else: raise BadArgument() else: - time = None - if action.startswith("mute"): - time = time_pars(action.replace("mute", "")).total_seconds() - action = "mute" - elif action.startswith("ban"): - if action[3:]: - time = time_pars(action.replace("ban", "")).total_seconds() - action = "ban" + if time: + time = time_pars(time).total_seconds() if a: a.action = action a.duration = time @@ -167,7 +175,7 @@ class Warn(commands.Cog): s.commit() s.close() - await ctx.message.add_reaction("\U0001f44d") + await ctx.send(content="\U0001f44d") @commands.Cog.listener() async def on_guild_remove(self, guild: Guild): From ebaf45e84aeea3a009a3091f0373082924337491 Mon Sep 17 00:00:00 2001 From: flifloo <flifloo@gmail.com> Date: Thu, 4 Feb 2021 10:52:13 +0100 Subject: [PATCH 15/16] Switch reminder to slash commands --- extensions/reminder.py | 67 +++++++++++++++++++----------------------- 1 file changed, 30 insertions(+), 37 deletions(-) diff --git a/extensions/reminder.py b/extensions/reminder.py index b687dd7..528f579 100644 --- a/extensions/reminder.py +++ b/extensions/reminder.py @@ -4,10 +4,12 @@ from discord.ext import commands from discord import Embed from discord.ext.commands import BadArgument from discord.ext import tasks +from discord_slash import SlashContext, cog_ext, SlashCommandOptionType +from discord_slash.utils import manage_commands from administrator.check import is_enabled from administrator.logger import logger -from administrator import db +from administrator import db, slash from administrator.utils import time_pars, seconds_to_time_string extension_name = "reminders" @@ -17,61 +19,52 @@ logger = logger.getChild(extension_name) class Reminders(commands.Cog, name="Reminder"): def __init__(self, bot: commands.Bot): self.bot = bot + slash.get_cog_commands(self) def description(self): return "Create and manage reminders" - @commands.group("reminder", pass_context=True) + @cog_ext.cog_subcommand(base="reminder", name="add", description="Add a reminder to your reminders list", options=[ + manage_commands.create_option("message", "The message", SlashCommandOptionType.STRING, True), + manage_commands.create_option("time", "When, ?D?H?M?S", SlashCommandOptionType.STRING, True) + ]) @is_enabled() - async def reminder(self, ctx: commands.Context): - if ctx.invoked_subcommand is None: - await ctx.invoke(self.reminder_help) - - @reminder.group("help", pass_context=True) - async def reminder_help(self, ctx: commands.Context): - embed = Embed(title="Reminder help") - embed.add_field(name="reminder add <message> <time>", value="Add a reminder to your reminders list\n" - "Time: ?D?H?M?S", inline=False) - embed.add_field(name="reminder list", value="Show your tasks list", inline=False) - embed.add_field(name="reminder remove [N°]", value="Show your tasks list with if no id given\n" - "Remove the task withe the matching id", inline=False) - await ctx.send(embed=embed) - - @reminder.group("add", pass_context=True) - async def reminder_add(self, ctx: commands.Context, message: str, time: str): + async def reminder_add(self, ctx: SlashContext, message: str, time: str): time = time_pars(time) now = datetime.now() s = db.Session() - s.add(db.Task(message, ctx.author.id, ctx.channel.id, now + time, ctx.message.created_at)) + s.add(db.Task(message, ctx.author.id, ctx.channel.id, now + time, datetime.now())) s.commit() s.close() - await ctx.send(f"""Remind you in {seconds_to_time_string(time.total_seconds())} !""") + await ctx.send(content=f"""Remind you in {seconds_to_time_string(time.total_seconds())} !""") - @reminder.group("list", pass_context=True) - async def reminder_list(self, ctx: commands.Context): + @cog_ext.cog_subcommand(base="reminder", name="list", description="Show your tasks list") + @is_enabled() + async def reminder_list(self, ctx: SlashContext): embed = Embed(title="Tasks list") s = db.Session() for t in s.query(db.Task).filter(db.Task.user == ctx.author.id).all(): embed.add_field(name=f"N°{t.id} | {t.date.strftime('%d/%m/%Y %H:%M')}", value=f"{t.message}", inline=False) s.close() - await ctx.send(embed=embed) + await ctx.send(embeds=[embed]) - @reminder.group("remove", pass_context=True) - async def reminder_remove(self, ctx: commands.Context, n: int = None): - if n is None: - await ctx.invoke(self.reminder_list) + @cog_ext.cog_subcommand(base="reminder", name="remove", description="Remove the task withe the matching id", + options=[ + manage_commands.create_option("id", "The reminder id", + SlashCommandOptionType.INTEGER, True)]) + @is_enabled() + async def reminder_remove(self, ctx: SlashContext, n: int): + s = db.Session() + t = s.query(db.Task).filter(db.Task.id == n).first() + if t and t.user == ctx.author.id: + s.delete(t) + s.commit() + s.close() + await ctx.send(content="\U0001f44d") else: - s = db.Session() - t = s.query(db.Task).filter(db.Task.id == n).first() - if t and t.user == ctx.author.id: - s.delete(t) - s.commit() - s.close() - await ctx.message.add_reaction("\U0001f44d") - else: - s.close() - raise BadArgument() + s.close() + raise BadArgument() @tasks.loop(minutes=1) async def reminders_loop(self): From a036397b80093ed46be4674e4f4066e97ba69224 Mon Sep 17 00:00:00 2001 From: flifloo <flifloo@gmail.com> Date: Thu, 4 Feb 2021 10:53:26 +0100 Subject: [PATCH 16/16] Switch help to slash commands --- extensions/greetings.py | 14 -------------- extensions/help.py | 40 ++++++++++++++-------------------------- extensions/invite.py | 17 ----------------- 3 files changed, 14 insertions(+), 57 deletions(-) diff --git a/extensions/greetings.py b/extensions/greetings.py index ad59907..08469c4 100644 --- a/extensions/greetings.py +++ b/extensions/greetings.py @@ -21,20 +21,6 @@ class Greetings(commands.Cog): def description(self): return "Setup join and leave message" - @cog_ext.cog_subcommand(base="greetings", name="help", - description="Help about greetings") - @is_enabled() - @guild_only() - @has_permissions(manage_guild=True) - async def greetings_help(self, ctx: SlashContext): - embed = Embed(title="Greetings help") - embed.add_field(name="set <join/leave> <message>", value="Set the greetings message\n" - "`{}` will be replace by the username", - inline=False) - embed.add_field(name="show <join/leave>", value="Show the greetings message", inline=False) - embed.add_field(name="toggle <join/leave>", value="Enable or disable the greetings message", inline=False) - await ctx.send(embeds=[embed]) - @cog_ext.cog_subcommand(base="greetings", name="set", description="Set the greetings message\n`{}` will be replace by the username", options=[ diff --git a/extensions/help.py b/extensions/help.py index 5ffbf43..591e1de 100644 --- a/extensions/help.py +++ b/extensions/help.py @@ -1,10 +1,8 @@ -from discord import Embed from discord.ext import commands from discord.ext.commands import CommandNotFound, MissingRequiredArgument, BadArgument, MissingPermissions, \ - NoPrivateMessage, CommandError, NotOwner + NoPrivateMessage, NotOwner from discord_slash import SlashContext -from administrator import config from administrator.check import ExtensionDisabled from administrator.logger import logger @@ -20,23 +18,6 @@ class Help(commands.Cog): def description(self): return "Give help and command list" - @commands.command("help", pass_context=True) - async def help(self, ctx: commands.Context): - embed = Embed(title="Help") - - for c in filter(lambda x: x != "Help", self.bot.cogs): - cog = self.bot.cogs[c] - try: - if await getattr(cog, c.lower()).can_run(ctx): - embed.add_field(name=c, - value=cog.description() + "\n" + - f"`{config.get('prefix')}{c.lower()} help` for more information", - inline=False) - except CommandError: - pass - - await ctx.send(embed=embed) - @commands.Cog.listener() async def on_command_error(self, ctx: commands.Context, error): await self.error_handler(ctx, error) @@ -45,21 +26,28 @@ class Help(commands.Cog): async def on_slash_command_error(self, ctx: SlashContext, error: Exception): await self.error_handler(ctx, error) - @staticmethod - async def error_handler(ctx, error: Exception): + async def error_handler(self, ctx, error: Exception): if isinstance(error, CommandNotFound): - await ctx.send(content="\u2753") + await self.reaction(ctx, "\u2753") elif isinstance(error, MissingRequiredArgument) or isinstance(error, BadArgument): - await ctx.send(content="\u274C") + await self.reaction(ctx, "\u274C") elif isinstance(error, NotOwner) or isinstance(error, MissingPermissions) \ or isinstance(error, NoPrivateMessage): - await ctx.send(content="\U000026D4") + await self.reaction(ctx, "\U000026D4") elif isinstance(error, ExtensionDisabled): - await ctx.send(content="\U0001F6AB") + await self.reaction(ctx, "\U0001F6AB") else: await ctx.send(content="An error occurred !") raise error + @staticmethod + async def reaction(ctx, react: str): + m = getattr(ctx, "message", None) + if m: + await m.add_reaction(react) + else: + await ctx.send(content=react) + def setup(bot): logger.info(f"Loading...") diff --git a/extensions/invite.py b/extensions/invite.py index 77dacf3..c64cfe3 100644 --- a/extensions/invite.py +++ b/extensions/invite.py @@ -30,23 +30,6 @@ class Invite(commands.Cog): def description(self): return "Get role from a special invite link" - @cog_ext.cog_subcommand(base="invite", name="help", description="Help about invite") - @is_enabled() - @guild_only() - @has_permissions(administrator=True) - async def invite_help(self, ctx: SlashContext): - embed = Embed(title="Invite help") - embed.add_field(name="invite create <#channel> <@role>", value="Create a invite link to a role", inline=False) - embed.add_field(name="invite delete <code>", value="Remove a invite", inline=False) - await ctx.send(embeds=[embed]) - - @cog_ext.cog_subcommand(base="invite", name="create", description="Create a invite link to a role", - options=[ - manage_commands.create_option("channel", "The channel to join", - SlashCommandOptionType.CHANNEL, True), - manage_commands.create_option("role", "The role to give", - SlashCommandOptionType.ROLE, True) - ]) @is_enabled() @guild_only() @has_permissions(administrator=True)