From d80729c94e85a2259e12befea264ff47d7dc4f61 Mon Sep 17 00:00:00 2001 From: Myned Date: Sun, 14 Jan 2018 01:23:43 -0500 Subject: [PATCH 01/13] Add batch files to gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 442a16f..bfc80a1 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,7 @@ *.DS_Store *.pkl *.png +*.bat # Byte-compiled / optimized / DLL files __pycache__/ From 27c134644874193e1fa97ae6c510aa1e68e1c4ec Mon Sep 17 00:00:00 2001 From: Myned Date: Sun, 14 Jan 2018 01:24:17 -0500 Subject: [PATCH 02/13] Shorten FileNotFound warning output --- src/utils/utils.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/utils/utils.py b/src/utils/utils.py index 5d3e33d..5c83718 100644 --- a/src/utils/utils.py +++ b/src/utils/utils.py @@ -33,8 +33,7 @@ except FileNotFoundError: with open('config.json', 'w') as outfile: jsn.dump({'client_id': 0, 'info_channel': 0, 'owner_id': 0, 'permissions': 126016, 'playing': 'a game', 'prefix': [',', 'm,'], 'selfbot': False, 'token': 'str'}, outfile, indent=4, sort_keys=True) - raise FileNotFoundError( - 'FILE NOT FOUND : config.json created with abstract values. Restart run.py with correct values') + print('FILE NOT FOUND : config.json created with abstract values. Restart run.py with correct values') def setdefault(filename, default=None, json=False): From 76d0d348485291672cfdcb0255a3c96dfb6bbf6d Mon Sep 17 00:00:00 2001 From: Myned Date: Sun, 14 Jan 2018 01:24:42 -0500 Subject: [PATCH 03/13] Change Modumind user agent to Modufur --- src/utils/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/utils.py b/src/utils/utils.py index 5c83718..a2d8f92 100644 --- a/src/utils/utils.py +++ b/src/utils/utils.py @@ -93,7 +93,7 @@ last_commands = {} async def fetch(url, *, params={}, json=False, response=False): - async with session.get(url, params=params, headers={'User-Agent': 'Myned/Modumind'}) as r: + async with session.get(url, params=params, headers={'User-Agent': 'Myned/Modufur'}) as r: if response: return r elif json: From ba9f5f3ac27e5381c93c34a4ab78dc804b7c07ac Mon Sep 17 00:00:00 2001 From: Myned Date: Sun, 14 Jan 2018 01:25:18 -0500 Subject: [PATCH 04/13] Add ping-like test command --- src/run.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/run.py b/src/run.py index 2f10f4f..6c8f9ea 100644 --- a/src/run.py +++ b/src/run.py @@ -62,7 +62,7 @@ bot = cmds.Bot(command_prefix=get_prefix, self_bot=u.config['selfbot'], formatte @bot.command(help='help', brief='brief', description='description', usage='usage', hidden=True) async def test(ctx): - pass + await ctx.send('test') # Send and print ready message to #testing and console after logon From b433ea6273a24c5ea937ceba230aeedb0eaaa6cc Mon Sep 17 00:00:00 2001 From: Myned Date: Sun, 14 Jan 2018 01:25:49 -0500 Subject: [PATCH 05/13] Change delete_after to 7 secs in on_command_error output --- src/run.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/run.py b/src/run.py index 6c8f9ea..45de512 100644 --- a/src/run.py +++ b/src/run.py @@ -144,7 +144,7 @@ async def on_command_error(ctx, error): if isinstance(error, err.NotFound): print('NOT FOUND') elif isinstance(error, errext.CheckFailure): - await ctx.send('**Insufficient permissions**', delete_after=10) + await ctx.send('**Insufficient permissions**', delete_after=7) await ctx.message.add_reaction('\N{NO ENTRY}') elif isinstance(error, errext.CommandNotFound): print('INVALID COMMAND : {}'.format(error), file=sys.stderr) From 1c1ecf9d6b0516f66dd5715c95f9a84acea51b7b Mon Sep 17 00:00:00 2001 From: Myned Date: Sun, 14 Jan 2018 01:26:13 -0500 Subject: [PATCH 06/13] Add MissingRequiredArgument and BadArgument to on_command_error --- src/run.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/run.py b/src/run.py index 45de512..1d72368 100644 --- a/src/run.py +++ b/src/run.py @@ -143,6 +143,12 @@ async def on_error(error, *args, **kwargs): async def on_command_error(ctx, error): if isinstance(error, err.NotFound): print('NOT FOUND') + elif isinstance(error, errext.MissingRequiredArgument): + await ctx.send('**Missing required argument**', delete_after=7) + await ctx.message.add_reaction('\N{CROSS MARK}') + elif isinstance(error, errext.BadArgument): + await ctx.send(f'**Invalid argument.** {error}', delete_after=7) + await ctx.message.add_reaction('\N{CROSS MARK}') elif isinstance(error, errext.CheckFailure): await ctx.send('**Insufficient permissions**', delete_after=7) await ctx.message.add_reaction('\N{NO ENTRY}') From e069023dc6f69fc8363bfea2af76c35006e6c7c5 Mon Sep 17 00:00:00 2001 From: Myned Date: Sun, 14 Jan 2018 01:26:46 -0500 Subject: [PATCH 07/13] Import ext.commands errors --- src/cogs/management.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/cogs/management.py b/src/cogs/management.py index 3bd4130..55cb058 100644 --- a/src/cogs/management.py +++ b/src/cogs/management.py @@ -6,6 +6,7 @@ from datetime import datetime as dt import discord as d from discord import errors as err from discord.ext import commands as cmds +from discord.ext.commands import errors as errext from misc import exceptions as exc from misc import checks From 09c09199bdddbffa67786addaaf25b2134e3579e Mon Sep 17 00:00:00 2001 From: Myned Date: Sun, 14 Jan 2018 01:27:09 -0500 Subject: [PATCH 08/13] Add "purge|pur" aliases to prune group command --- src/cogs/management.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cogs/management.py b/src/cogs/management.py index 55cb058..97aa933 100644 --- a/src/cogs/management.py +++ b/src/cogs/management.py @@ -29,7 +29,7 @@ class Administration: self.deleting = True self.bot.loop.create_task(self.delete()) - @cmds.group(aliases=['pru', 'clear', 'cl'], hidden=True) + @cmds.group(aliases=['pru', 'purge', 'pur', 'clear', 'cl'], hidden=True) @cmds.is_owner() async def prune(self, ctx): pass From e2bc1e650b6374e2e869aee2ff438121ad438a4b Mon Sep 17 00:00:00 2001 From: Myned Date: Sun, 14 Jan 2018 01:28:27 -0500 Subject: [PATCH 09/13] Add purge user from channel command --- src/cogs/management.py | 51 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/src/cogs/management.py b/src/cogs/management.py index 97aa933..bbcdd55 100644 --- a/src/cogs/management.py +++ b/src/cogs/management.py @@ -38,6 +38,57 @@ class Administration: async def _prune_user(self, ctx): pass + @_prune_user.command(name='channel', aliases=['channels', 'chans', 'chan', 'ch', 'c']) + async def _prune_user_channel(self, ctx, user: d.User, *channels: d.TextChannel): + def confirm(r, u): + if u is ctx.author: + if r.emoji == '\N{OCTAGONAL SIGN}': + raise exc.Abort + if r.emoji == '\N{THUMBS UP SIGN}': + return True + return False + + if not channels: + channels = [ctx.channel] + + try: + pruning = await ctx.send(f'\N{HOURGLASS} **Pruning** {user.mention}**\'s messages from** {"**,** ".join([channel.mention for channel in channels])} **might take some time.** Proceed, {ctx.author.mention}?') + await pruning.add_reaction('\N{THUMBS UP SIGN}') + await pruning.add_reaction('\N{OCTAGONAL SIGN}') + await asyncio.sleep(1) + + await self.bot.wait_for('reaction_add', check=confirm, timeout=10 * 60) + + deleting = await ctx.send(f'\N{WASTEBASKET} **Deleting** {user.mention}**\'s messages...**') + await asyncio.sleep(1) + + c = 0 + for channel in channels: + await deleting.edit(content=f'\N{WASTEBASKET} **Deleting** {user.mention}**\'s messages from** {channel.mention}') + + deleted = await channel.purge(check=lambda m: m.author.id == user.id, before=pruning, limit=None) + c += len(deleted) + + await asyncio.sleep(1) + + for channel in channels: + missed = 0 + async for message in channel.history(before=pruning, limit=None): + if message.author.id == user.id: + missed += 1 + + if missed > 0: + await ctx.send(f'\N{DOUBLE EXCLAMATION MARK} `{missed}` **messages were not deleted in** {channel.mention}') + + await ctx.send(f'\N{WHITE HEAVY CHECK MARK} **Finished deleting** `{c}` **of** {user.mention}**\'s messages**') + + except exc.Abort: + await ctx.send('**Deletion aborted**', delete_after=7) + await ctx.message.add_reaction('\N{CROSS MARK}') + except TimeoutError: + await ctx.send('**Deletion timed out**', delete_after=7) + await ctx.message.add_reaction('\N{CROSS MARK}') + @_prune_user.command(name='all', aliases=['a'], brief='Prune a user\'s messages from the guild', description='about flag centers on message 50 of 101 messages\n\npfg \{user id\} [before|after|about] [\{message id\}]\n\nExample:\npfg \{user id\} before \{message id\}', hidden=True) @cmds.is_owner() async def _prune_user_all(self, ctx, user, when=None, reference=None): From 3bd77dea32a777c8add3adcbb5630823567d1e92 Mon Sep 17 00:00:00 2001 From: Myned Date: Sun, 14 Jan 2018 01:29:06 -0500 Subject: [PATCH 10/13] Fix mistaken group command decorator --- src/cogs/booru.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cogs/booru.py b/src/cogs/booru.py index 2be7247..42009b9 100644 --- a/src/cogs/booru.py +++ b/src/cogs/booru.py @@ -1195,7 +1195,7 @@ class MsG: n += 1 # Searches for and returns images from e621.net given tags when not blacklisted - @cmds.group(aliases=['e6', '6'], brief='e621 | NSFW', description='e621 | NSFW\nTag-based search for e621.net\n\nYou can only search 5 tags and 6 images at once for now.\ne6 [tags...] ([# of images])') + @cmds.command(aliases=['e6', '6'], brief='e621 | NSFW', description='e621 | NSFW\nTag-based search for e621.net\n\nYou can only search 5 tags and 6 images at once for now.\ne6 [tags...] ([# of images])') @checks.is_nsfw() async def e621(self, ctx, *args): try: From 1be05458157cc246fd7ffbceb7fc49ba67c2c8bb Mon Sep 17 00:00:00 2001 From: Myned Date: Sun, 14 Jan 2018 02:16:27 -0500 Subject: [PATCH 11/13] Change output formatting to align with program-wide rules --- src/cogs/owner.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cogs/owner.py b/src/cogs/owner.py index fd0b560..0d7542e 100644 --- a/src/cogs/owner.py +++ b/src/cogs/owner.py @@ -192,7 +192,7 @@ class Tools: finally: sys.stdout = sys.__stdout__ sys.stderr = sys.__stderr__ - print('Reset sys output.') + print('RESET : sys.std output/error') @cmds.command(name=',execute', aliases=[',exec'], hidden=True) @cmds.is_owner() From 0581ab8f5987a695d46b60de732a1940a933e09e Mon Sep 17 00:00:00 2001 From: Myned Date: Sun, 14 Jan 2018 02:16:46 -0500 Subject: [PATCH 12/13] Add command to list all guilds bot is in --- src/cogs/owner.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/cogs/owner.py b/src/cogs/owner.py index 0d7542e..03915fd 100644 --- a/src/cogs/owner.py +++ b/src/cogs/owner.py @@ -69,6 +69,17 @@ class Bot: await ctx.send('https://discordapp.com/oauth2/authorize?&client_id={}&scope=bot&permissions={}'.format(u.config['client_id'], u.config['permissions']), delete_after=5) + @cmds.command(name=',guilds', aliases=[',glds', ',servers', ',servs']) + @cmds.is_owner() + async def guilds(self, ctx): + paginator = cmds.Paginator() + + for guild in self.bot.guilds: + paginator.add_line(guild.name) + + for page in paginator.pages: + await ctx.send(f'**Guilds:**\n{page}') + @cmds.command(name=',status', aliases=[',presence', ',game'], hidden=True) @cmds.is_owner() async def change_status(self, ctx, *, game=None): From e0f58d9ce4e3821621e803c51dbe963126e1780a Mon Sep 17 00:00:00 2001 From: Myned Date: Sun, 14 Jan 2018 02:17:34 -0500 Subject: [PATCH 13/13] Clean prune user all command --- src/cogs/management.py | 98 +++++++++++++++--------------------------- 1 file changed, 34 insertions(+), 64 deletions(-) diff --git a/src/cogs/management.py b/src/cogs/management.py index bbcdd55..7d6b2f2 100644 --- a/src/cogs/management.py +++ b/src/cogs/management.py @@ -91,79 +91,49 @@ class Administration: @_prune_user.command(name='all', aliases=['a'], brief='Prune a user\'s messages from the guild', description='about flag centers on message 50 of 101 messages\n\npfg \{user id\} [before|after|about] [\{message id\}]\n\nExample:\npfg \{user id\} before \{message id\}', hidden=True) @cmds.is_owner() - async def _prune_user_all(self, ctx, user, when=None, reference=None): - def yes(msg): - if msg.content.lower() == 'y' and msg.channel is ctx.channel and msg.author is ctx.author: - return True - elif msg.content.lower() == 'n' and msg.channel is ctx.channel and msg.author is ctx.author: - raise exc.CheckFail - else: - return False + async def _prune_user_all(self, ctx, user: d.User): + def confirm(r, u): + if u is ctx.author: + if r.emoji == '\N{OCTAGONAL SIGN}': + raise exc.Abort + if r.emoji == '\N{THUMBS UP SIGN}': + return True + return False - channels = ctx.guild.text_channels - if reference is not None: - for channel in channels: - try: - ref = await channel.get_message(reference) - - except err.NotFound: - continue - - history = [] try: - pru_sent = await ctx.send('\N{HOURGLASS} **Pruning** <@{}>**\'s messages will take some time**'.format(user)) - ch_sent = await ctx.send('\N{FILE CABINET} **Caching channels...**') + pruning = await ctx.send(f'\N{HOURGLASS} **Pruning** {user.mention}**\'s messages might take some time.** Proceed, {ctx.author.mention}?') + await pruning.add_reaction('\N{THUMBS UP SIGN}') + await pruning.add_reaction('\N{OCTAGONAL SIGN}') + await asyncio.sleep(1) - if when is None: - for channel in channels: - async for message in channel.history(limit=None): - if message.author.id == int(user): - history.append(message) - await ch_sent.edit(content='\N{FILE CABINET} **Cached** `{}/{}` **channels**'.format(channels.index(channel) + 1, len(channels))) - await asyncio.sleep(self.RATE_LIMIT) - elif when == 'before': - for channel in channels: - async for message in channel.history(limit=None, before=ref.created_at): - if message.author.id == int(user): - history.append(message) - await ch_sent.edit(content='\N{FILE CABINET} **Cached** `{}/{}` **channels**'.format(channels.index(channel) + 1, len(channels))) - await asyncio.sleep(self.RATE_LIMIT) - elif when == 'after': - for channel in channels: - async for message in channel.history(limit=None, after=ref.created_at): - if message.author.id == int(user): - history.append(message) - await ch_sent.edit(content='\N{FILE CABINET} **Cached** `{}/{}` **channels**'.format(channels.index(channel) + 1, len(channels))) - await asyncio.sleep(self.RATE_LIMIT) - elif when == 'about': - for channel in channels: - async for message in channel.history(limit=None, about=ref.created_at): - if message.author.id == int(user): - history.append(message) - await ch_sent.edit(content='\N{FILE CABINET} **Cached** `{}/{}` **channels**'.format(channels.index(channel) + 1, len(channels))) - await asyncio.sleep(self.RATE_LIMIT) + await self.bot.wait_for('reaction_add', check=confirm, timeout=10 * 60) + + deleting = await ctx.send(f'\N{WASTEBASKET} **Deleting** {user.mention}**\'s messages...**') + await asyncio.sleep(1) - est_sent = await ctx.send('\N{STOPWATCH} **Estimated time to delete history:** `{}m {}s`'.format(int(self.RATE_LIMIT * len(history) / 60), int(self.RATE_LIMIT * len(history) % 60))) - cont_sent = await ctx.send('{} **Continue?** `Y` or `N`'.format(ctx.author.mention)) - await self.bot.wait_for('message', check=yes, timeout=10 * 60) - await cont_sent.delete() - del_sent = await ctx.send('\N{WASTEBASKET} **Deleting messages..**') - await del_sent.pin() c = 0 - for message in history: - with suppress(err.NotFound): - await message.delete() - c += 1 - await del_sent.edit(content='\N{WASTEBASKET} **Deleted** `{}/{}` **messages**'.format(history.index(message) + 1, len(history))) - await asyncio.sleep(self.RATE_LIMIT) - await del_sent.unpin() + for channel in ctx.guild.text_channels: + await deleting.edit(content=f'\N{WASTEBASKET} **Deleting** {user.mention}**\'s messages from** {channel.mention}') - await ctx.send('\N{WASTEBASKET} `{}` **of** <@{}>**\'s messages left in** {}****'.format(len(history) - c, user, ctx.guild.name)) + deleted = await channel.purge(check=lambda m: m.author.id == user.id, before=pruning, limit=None) + c += len(deleted) - except exc.CheckFail: + await asyncio.sleep(1) + + for channel in ctx.guild.text_channels: + missed = 0 + async for message in channel.history(before=pruning, limit=None): + if message.author.id == user.id: + missed += 1 + + if missed > 0: + await ctx.send(f'\N{DOUBLE EXCLAMATION MARK} `{missed}` **messages were not deleted in** {channel.mention}') + + await ctx.send(f'\N{WHITE HEAVY CHECK MARK} **Finished deleting** `{c}` **of** {user.mention}**\'s messages**') + + except exc.Abort: await ctx.send('**Deletion aborted**', delete_after=7) await ctx.message.add_reaction('\N{CROSS MARK}') - except TimeoutError: await ctx.send('**Deletion timed out**', delete_after=7) await ctx.message.add_reaction('\N{CROSS MARK}')