diff --git a/cogs/CONSTANTS.py b/cogs/CONSTANTS.py index bc85f9a..d4a1a04 100644 --- a/cogs/CONSTANTS.py +++ b/cogs/CONSTANTS.py @@ -18,3 +18,6 @@ REACTION_ERROR = '\u26a0' # REACTION_NOT_FOUND = '\u2139' # Same as REACTION_INFO REACTION_INFO = '\u2139' # Same as REACTION_NOT_FOUND + +# Roles +OFFICER_ROLE = 436913087245975553 diff --git a/cogs/__init__.py b/cogs/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/cogs/admin.py b/cogs/admin.py index b32dd17..a08ba31 100644 --- a/cogs/admin.py +++ b/cogs/admin.py @@ -5,7 +5,7 @@ from contextlib import redirect_stdout import io -import cogs.util +from cogs.CONSTANTS import OFFICER_ROLE class Admin(commands.Cog, name='Admin'): @@ -13,7 +13,7 @@ def __init__(self, bot): self.bot = bot @commands.command(hidden=True) - @commands.check(cogs.util.is_officer_check) + @commands.has_role(OFFICER_ROLE) async def load(self, ctx, extension_name : str): """Loads an extension.""" try: @@ -27,7 +27,7 @@ async def load(self, ctx, extension_name : str): await ctx.send("{} loaded.".format(extension_name)) @commands.command(hidden=True) - @commands.check(cogs.util.is_officer_check) + @commands.has_role(OFFICER_ROLE) async def unload(self, ctx, extension_name : str): """Unloads an extension.""" if extension_name.startswith("cogs."): @@ -37,7 +37,7 @@ async def unload(self, ctx, extension_name : str): await ctx.send("{} unloaded.".format(extension_name)) @commands.command(hidden=True) - @commands.check(cogs.util.is_officer_check) + @commands.has_role(OFFICER_ROLE) async def reload(self, ctx, extension_name : str): """Unloads and then loads an extension""" try: @@ -51,12 +51,12 @@ async def reload(self, ctx, extension_name : str): await ctx.send("{} reloaded.".format(extension_name)) @commands.command(hidden=True) - @commands.check(cogs.util.is_officer_check) + @commands.has_role(OFFICER_ROLE) async def whereami(self, ctx): await ctx.send("You are in {} with id {}".format(ctx.channel.name, ctx.channel.id)) @commands.command(hidden=True, name="eval") - @commands.check(cogs.util.is_owner) + @commands.is_owner() async def admin_eval(self, ctx, *, cmd : str): """Evaluates Python code only if the executor is hjarrell""" env = { diff --git a/cogs/compile.py b/cogs/compile.py index f005eff..1775bc8 100644 --- a/cogs/compile.py +++ b/cogs/compile.py @@ -3,7 +3,7 @@ import requests import json -import cogs.util +from CONSTANTS import OFFICER_ROLE class Compile(commands.Cog, name='Compile'): @@ -42,7 +42,7 @@ async def _compile(self, ctx, lang_id : int, *, program : str): await ctx.send("Program failed with output:\n\n```json\n{}\n```".format(json.dumps(response, sort_keys=True, indent=4))) @commands.command(name="compdebug") - @commands.check(cogs.util.is_officer_check) + @commands.has_role(OFFICER_ROLE) async def debug_compile(self, ctx): """Toggles whether to print the full compile output""" self.is_debug = not self.is_debug diff --git a/cogs/memes.py b/cogs/memes.py index 30257d2..aeabf21 100644 --- a/cogs/memes.py +++ b/cogs/memes.py @@ -4,7 +4,7 @@ import praw from discord.ext import commands -import cogs.util +from cogs.CONSTANTS import OFFICER_ROLE class Memes(commands.Cog, name='Memes'): """All meme related commands""" @@ -32,7 +32,7 @@ async def about(self, ctx): await ctx.send(file=discord.File('alligator.jpg')) @commands.command() - @commands.check(cogs.util.is_officer_check) + @commands.has_role(OFFICER_ROLE) async def randplaying(self, ctx): """Randomly changes the playing text""" new_playing = random.choice(self.playing_strings) @@ -40,7 +40,7 @@ async def randplaying(self, ctx): await ctx.send('I am now {}'.format(new_playing)) @commands.command() - @commands.check(cogs.util.is_officer_check) + @commands.has_role(OFFICER_ROLE) async def setplaying(self, ctx, *, playing: str): """Lets an officer set the playing text""" await self.bot.change_presence(activity=discord.Game(name=playing)) diff --git a/cogs/messages.py b/cogs/messages.py index a2e211b..949a8a3 100644 --- a/cogs/messages.py +++ b/cogs/messages.py @@ -1,3 +1,4 @@ +import asyncio import discord from discord.ext import commands @@ -72,6 +73,72 @@ async def on_message(self, msg): except discord.HTTPException as e: await msg.channel.send('Cannot post answer due to excessive character count! Maximum factorial allowed is `801!`.') + +class ALBotMessageClear(commands.Cog, name='Message Clear'): + """Functions for handling message deletion in channels""" + def __init__(self, bot): + self.bot = bot + + @commands.has_role(CONSTANTS.OFFICER_ROLE) + @commands.command() + async def clear(self, ctx, a_number): + # Checks if number is positive int + if not a_number.isdigit() or not int(a_number) > 0: + await ctx.channel.send(content="Please input a number larger than zero") + return + + # checks the message reaction to see if the user confirms or cancels the command and returns True or False respectively + async def confirms(self, ctx, user, bot_msg): + while True: + + def check(reaction: discord.Reaction, adder: discord.User) -> bool: + return adder == user and reaction.message.id == bot_msg.id + + reaction, adder = await self.bot.wait_for('reaction_add', timeout=30.0, check=check) + + if reaction.emoji == "✅": + return True + elif reaction.emoji == "❌": + return False + + # checks user permissions to see if they can manage messages in the channel + if self.bot.get_channel(ctx.channel.id).permissions_for(ctx.author).manage_messages: + user = ctx.channel.last_message.author + user_msg = ctx.channel.last_message + + # warns the user and confirms the clear command + await ctx.channel.send("WARNING: You are about to delete {} messages, are you sure you want to do this?".format(a_number)) + bot_msg = ctx.channel.last_message + + #adds reactions to the bot message + reactions = ["✅","❌"] + for emoji in reactions: + await bot_msg.add_reaction(emoji) + + # Waits 30s for a user reaction and continues only if they respond with ❌ or ✅ + try: + cont = await confirms(self, ctx, user, bot_msg) + except asyncio.TimeoutError: + await bot_msg.delete() + await ctx.channel.send('Clear command Timeout') + return + + # Cancels the command and deletes the bot message + if not cont: + await bot_msg.delete() + await ctx.channel.send(content='Clear command cancelled') + return + + # deletes bot message, user msg, then loops through channel deleting messages + await bot_msg.delete() + await user_msg.delete() + async for message in ctx.channel.history(limit=int(a_number)): + if not message.pinned: + await message.delete() + await asyncio.sleep(0.4) + await ctx.channel.send(content='@{} Successfully deleted {} messages'.format(ctx.author, int(a_number))) + def setup(bot): bot.add_cog(ALBotMessageDeletionHandlers(bot, SQLConnection())) bot.add_cog(ALBotFactorialHandler(bot)) + bot.add_cog(ALBotMessageClear(bot)) diff --git a/cogs/util.py b/cogs/util.py deleted file mode 100644 index d30e79a..0000000 --- a/cogs/util.py +++ /dev/null @@ -1,9 +0,0 @@ -import discord - -def is_officer_check(ctx): - """Check to see if the author is an officer""" - return discord.utils.get(ctx.message.author.roles, name='officer') is not None - -def is_owner(ctx): - """Check to see if the author is an hjarrell""" - return ctx.message.author.id == 402565484072927235 diff --git a/database/__init__.py b/database/__init__.py new file mode 100644 index 0000000..e69de29