diff --git a/src/main/cogs/booru.py b/src/main/cogs/booru.py
index a22f730..821eda2 100644
--- a/src/main/cogs/booru.py
+++ b/src/main/cogs/booru.py
@@ -1,48 +1,44 @@
import json
try:
- with open('global_blacklist.json') as infile:
- global_blacklist = json.load(infile)
- print('\"global_blacklist.json\" loaded.')
+ with open('blacklists.json') as infile:
+ blacklists = json.load(infile)
+ print('\"blacklists.json\" loaded.')
except FileNotFoundError:
- with open('global_blacklist.json', 'w+') as iofile:
- print('Blacklist file not found: \"global_blacklist.json\" created.')
- json.dump([], iofile, indent=4, sort_keys=True)
+ with open('blacklists.json', 'w+') as iofile:
+ print('Blacklists file not found: \"blacklists.json\" created and loaded.')
+ json.dump({'global_blacklist': [], 'guild_blacklist': {}, 'user_blacklist': {}}, iofile, indent=4, sort_keys=True)
iofile.seek(0)
- global_blacklist = json.load(iofile)
+ blacklists = json.load(iofile)
try:
- with open('guild_blacklist.json') as infile:
- guild_blacklist = json.load(infile)
- print('\"guild_blacklist.json\" loaded.')
+ with open('aliases.json') as infile:
+ aliases = json.load(infile)
+ print('\"aliases.json\" loaded.')
except FileNotFoundError:
- with open('guild_blacklist.json', 'w+') as iofile:
- print('Blacklist file not found: \"guild_blacklist.json\" created.')
- json.dump({}, iofile, indent=4, sort_keys=True)
+ with open('aliases.json', 'w+') as iofile:
+ print('Aliases file not found: \"aliases.json\" created and loaded.')
+ json.dump({'global_blacklist': {}, 'guild_blacklist': {}, 'user_blacklist': {}}, iofile, indent=4, sort_keys=True)
iofile.seek(0)
- guild_blacklist = json.load(iofile)
-try:
- with open('user_blacklist.json') as infile:
- user_blacklist = json.load(infile)
- print('\"user_blacklist.json\" loaded.')
-except FileNotFoundError:
- with open('user_blacklist.json', 'w+') as iofile:
- print('Blacklist file not found: \"user_blacklist.json\" created.')
- json.dump({}, iofile, indent=4, sort_keys=True)
- iofile.seek(0)
- user_blacklist = json.load(iofile)
+ aliases = json.load(iofile)
import asyncio
-import discord
+import discord as d
import requests
-import traceback
+import traceback as tb
+import discord as d
from discord import reaction
from discord.ext import commands
-from discord.ext.commands import errors
+from discord.ext.commands import errors as errext
+from discord import errors as err
+from cogs import tools
from misc import checks
from misc import exceptions as exc
from utils import formatter, scraper
+from utils import utils as u
-last_command = {}
+HEADERS = {'user-agent': 'Modumind/0.0.1 (Myned)'}
+
+# temp_urls = {}
class MsG:
@@ -69,8 +65,23 @@ class MsG:
except exc.ImageError:
await ctx.send('❌ ' + ctx.message.author.mention + ' **No image found.**')
except Exception:
- await ctx.send(exc.base)
- traceback.print_exc(limit=1)
+ await ctx.send('{}\n```{}```'.format(exc.base, tb.format_exc(limit=1)))
+ tb.print_exc()
+
+ # Tag aliases
+ @commands.command(aliases=['alias', 'a'], brief='e621 Tag aliases', description='e621 | NSFW\nSearch aliases for given tag')
+ @checks.del_ctx()
+ async def aliases(self, ctx, tag):
+ global HEADERS
+ aliases = []
+ try:
+ alias_request = requests.get('https://e621.net/tag_alias/index.json?aliased_to=' + tag + '&approved=true', headers=HEADERS).json()
+ for dic in alias_request:
+ aliases.append(dic['name'])
+ await ctx.send('✅ `' + tag + '` **aliases:**\n```' + formatter.tostring(aliases) + '```')
+ except Exception:
+ await ctx.send('{}\n```{}```'.format(exc.base, tb.format_exc(limit=1)))
+ tb.print_exc()
# Reverse image searches a linked image using the public iqdb
@commands.command(name='reverse', aliases=['rev', 'ris'], brief='e621 Reverse image search', description='e621 | NSFW\nReverse-search an image with given URL')
@@ -78,106 +89,201 @@ class MsG:
async def reverse_image_search(self, ctx, url):
try:
await ctx.trigger_typing()
- await ctx.send('✅ ' + ctx.message.author.mention + ' **Probable match:**\n' + scraper.check_match('http://iqdb.harry.lu/?url=' + url))
+ await ctx.send('✅ ' + ctx.message.author.mention + ' **Probable match:**\n' + scraper.check_match('http://iqdb.harry.lu/?url={}'.format(url)))
except exc.MatchError:
- await ctx.send('❌ ' + ctx.message.author.mention + ' **No probable match.**')
+ await ctx.send('❌ ' + ctx.message.author.mention + ' **No probable match.**', delete_after=10)
except Exception:
- await ctx.send(exc.base)
- traceback.print_exc(limit=1)
+ await ctx.send('{}\n```{}```'.format(exc.base, tb.format_exc(limit=1)))
+ tb.print_exc()
+
+ @commands.command(name='e621p', aliases=['e6p', '6p'])
+ @checks.del_ctx()
+ @checks.is_nsfw()
+ async def e621_paginator(self, ctx, *args):
+ def react(reaction, user):
+ if reaction.emoji == '⬅' and reaction.message.content == paginator.content and user is ctx.message.author: raise exc.Left
+ elif reaction.emoji == '🚫' and reaction.message.content == paginator.content and user is ctx.message.author: raise exc.Abort
+ elif reaction.emoji == '📁' and reaction.message.content == paginator.content and user is ctx.message.author: raise exc.Save
+ elif reaction.emoji == '➡' and reaction.message.content == paginator.content and user is ctx.message.author: raise exc.Right
+ else: return False
+
+ user = ctx.message.author
+ args = list(args)
+ limit = 100
+
+ try:
+ await ctx.trigger_typing()
+
+ c = 1
+ posts = self.check_return_posts(ctx=ctx, booru='e621', tags=args, limit=limit)
+ starred = []
+
+ embed = d.Embed(title='/post/{}'.format(list(posts.keys())[c-1]), url='https://e621.net/post/show/{}'.format(list(posts.keys())[c-1]), color=ctx.me.color).set_image(url=list(posts.values())[c-1])
+ embed.set_author(name=formatter.tostring(args, random=True), url='https://e621.net/post?tags={}'.format(','.join(args)), icon_url=user.avatar_url)
+ embed.set_footer(text='e621', icon_url='http://ndl.mgccw.com/mu3/app/20141013/18/1413204353554/icon/icon_xl.png')
+ paginator = await ctx.send(embed=embed)
+
+ await paginator.add_reaction('⬅')
+ await paginator.add_reaction('🚫')
+ await paginator.add_reaction('📁')
+ await paginator.add_reaction('➡')
+ await asyncio.sleep(1)
+
+ while True:
+ try:
+ await self.bot.wait_for('reaction_add', check=react, timeout=5*60)
+ except exc.Left:
+ if c > 1:
+ c -= 1
+ embed.title = '/post/{}'.format(list(posts.keys())[c-1])
+ embed.url = 'https://e621.net/post/show/{}'.format(list(posts.keys())[c-1])
+ embed.set_image(url=list(posts.values())[c-1])
+ await paginator.edit(embed=embed)
+ except exc.Save:
+ if list(posts.values())[c-1] not in starred:
+ starred.append(list(posts.values())[c-1])
+ except exc.Right:
+ if c % limit == 0:
+ await ctx.trigger_typing()
+ try: posts.update(self.check_return_posts(ctx=ctx, booru='e621', tags=args, limit=limit, previous=posts))
+ except exc.NotFound:
+ await paginator.edit(content='❌ **No more images found.**')
+
+ c += 1
+ embed.title = '/post/{}'.format(list(posts.keys())[c-1])
+ embed.url = 'https://e621.net/post/show/{}'.format(list(posts.keys())[c-1])
+ embed.set_image(url=list(posts.values())[c-1])
+ await paginator.edit(embed=embed)
+ except exc.Abort: await paginator.edit(content='🚫 **Exited paginator.**')
+ except exc.TagBlacklisted as e: await ctx.send('❌ `{}` **blacklisted.**'.format(e), delete_after=10)
+ except exc.TagBoundsError as e: await ctx.send('❌ `{}` **out of bounds.** Tags limited to 5, currently.'.format(e), delete_after=10)
+ except exc.Timeout: await ctx.send('❌ **Request timed out.**')
+ except asyncio.TimeoutError: await paginator.edit(content='❌ **Paginator timed out.**')
+ except Exception:
+ await ctx.send('{}\n```{}```'.format(exc.base, tb.format_exc(limit=1)))
+ tb.print_exc()
+ finally:
+ if starred:
+ for url in starred: await user.send(url)
+
+ @e621_paginator.error
+ async def e621_paginator_error(self, ctx, error):
+ if isinstance(error, errext.CheckFailure):
+ return await ctx.send('❌ <#' + str(ctx.message.channel.id) + '> **is not an NSFW channel.**', delete_after=10)
# Searches for and returns images from e621.net given tags when not blacklisted
@commands.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.del_ctx()
@checks.is_nsfw()
async def e621(self, ctx, *args):
- global global_blacklist, guild_blacklist, user_blacklist
+ # global temp_urls
+
args = list(args)
+ limit = 1
+
try:
await ctx.trigger_typing()
- await self.check_send_urls(ctx, 'e621', args)
- except exc.TagBlacklisted as e:
- await ctx.send('❌ `' + str(e) + '` **blacklisted.**', delete_after=10)
- except exc.BoundsError as e:
- await ctx.send('❌ `' + str(e) + '` **out of bounds.**', delete_after=10)
- except exc.TagBoundsError as e:
- await ctx.send('❌ `' + str(e) + '` **out of bounds.** Tags limited to 5, currently.', delete_after=10)
+ # Checks for, defines, and removes limit from args
+ for arg in args:
+ if len(arg) == 1:
+ if int(arg) <= 6 and int(arg) >= 1:
+ limit = int(arg)
+ args.remove(arg)
+ else: raise exc.BoundsError(arg)
+ posts = self.check_return_posts(ctx=ctx, booru='e621', tags=args, limit=limit)#, previous=temp_urls.get(ctx.message.author.id, []))
+ for ident, url in posts.items():
+ embed = d.Embed(title='/post/{}'.format(ident), url='https://e621.net/post/show/{}'.format(ident), color=ctx.me.color).set_image(url=url)
+ embed.set_author(name=formatter.tostring(args, random=True), url='https://e621.net/post?tags={}'.format(','.join(args)), icon_url=ctx.message.author.avatar_url)
+ embed.set_footer(text='e621', icon_url='http://ndl.mgccw.com/mu3/app/20141013/18/1413204353554/icon/icon_xl.png')
+ await ctx.send(embed=embed)
+ # temp_urls.setdefault(ctx.message.author.id, []).extend(posts.values())
+ except exc.TagBlacklisted as e: await ctx.send('❌ `' + str(e) + '` **blacklisted.**', delete_after=10)
+ except exc.BoundsError as e: await ctx.send('❌ `' + str(e) + '` **out of bounds.**', delete_after=10)
+ except exc.TagBoundsError as e: await ctx.send('❌ `' + str(e) + '` **out of bounds.** Tags limited to 5, currently.', delete_after=10)
+ except exc.NotFound as e: await ctx.send('❌ `' + str(e) + '` **not found.**', delete_after=10)
+ except exc.Timeout: await ctx.send('❌ **Request timed out.**')
except Exception:
- await ctx.send(exc.base)
- traceback.print_exc()
+ await ctx.send('{}\n```{}```'.format(exc.base, tb.format_exc(limit=1)))
+ tb.print_exc()
+ tools.command_dict.setdefault(str(ctx.message.author.id), {}).update({'command': ctx.command, 'args': ctx.args})
@e621.error
async def e621_error(self, ctx, error):
- if isinstance(error, errors.CheckFailure):
+ if isinstance(error, errext.CheckFailure):
return await ctx.send('❌ <#' + str(ctx.message.channel.id) + '> **is not an NSFW channel.**', delete_after=10)
# Searches for and returns images from e926.net given tags when not blacklisted
@commands.command(aliases=['e9', '9'], brief='e926 | SFW', description='e926 | SFW\nTag-based search for e926.net\n\nYou can only search 5 tags and 6 images at once for now.\ne9 [tags...] ([# of images])')
@checks.del_ctx()
async def e926(self, ctx, *args):
- global global_blacklist, guild_blacklist, user_blacklist
+ # global temp_urls
+
args = list(args)
+ limit = 1
+
try:
await ctx.trigger_typing()
- await self.check_send_urls(ctx, 'e926', args)
- except exc.TagBlacklisted as e:
- await ctx.send('❌ `' + str(e) + '` **blacklisted.**', delete_after=10)
- except exc.BoundsError as e:
- await ctx.send('❌ `' + str(e) + '` **out of bounds.**', delete_after=10)
- except exc.TagBoundsError as e:
- await ctx.send('❌ `' + str(e) + '` **out of bounds.** Tags limited to 5, currently.', delete_after=10)
+ # Checks for, defines, and removes limit from args
+ for arg in args:
+ if len(arg) == 1:
+ if int(arg) <= 6 and int(arg) >= 1:
+ limit = int(arg)
+ args.remove(arg)
+ else: raise exc.BoundsError(arg)
+ posts = self.check_return_posts(ctx=ctx, booru='e926', tags=args, limit=limit)#, previous=temp_urls.get(ctx.message.author.id, []))
+ for ident, url in posts.items():
+ embed = d.Embed(title='/post/{}'.format(ident), url='https://e926.net/post/show/{}'.format(ident), color=ctx.me.color).set_image(url=url)
+ embed.set_author(name=formatter.tostring(args, random=True), url='https://e621.net/post?tags={}'.format(','.join(args)), icon_url=ctx.message.author.avatar_url)
+ embed.set_footer(text='e926', icon_url='http://ndl.mgccw.com/mu3/app/20141013/18/1413204353554/icon/icon_xl.png')
+ await ctx.send(embed=embed)
+ # temp_urls.setdefault(ctx.message.author.id, []).extend(posts.values())
+ except exc.TagBlacklisted as e: await ctx.send('❌ `' + str(e) + '` **blacklisted.**', delete_after=10)
+ except exc.BoundsError as e: await ctx.send('❌ `' + str(e) + '` **out of bounds.**', delete_after=10)
+ except exc.TagBoundsError as e: await ctx.send('❌ `' + str(e) + '` **out of bounds.** Tags limited to 5, currently.', delete_after=10)
+ except exc.NotFound as e: await ctx.send('❌ `' + str(e) + '` **not found.**', delete_after=10)
+ except exc.Timeout: await ctx.send('❌ **Request timed out.**')
except Exception:
- await ctx.send(exc.base)
- traceback.print_exc(limit=1)
+ await ctx.send('{}\n```{}```'.format(exc.base, tb.format_exc(limit=1)))
+ tb.print_exc()
# Messy code that checks image limit and tags in blacklists
- async def check_send_urls(self, ctx, booru, args):
- global global_blacklist, guild_blacklist, user_blacklist
- if isinstance(ctx.message.guild, discord.Guild):
- guild = ctx.message.guild
- else:
- guild = ctx.message.channel
+ def check_return_posts(self, *, ctx, booru='e621', tags=[], limit=1, previous=[]):
+ global blacklists, aliases, HEADERS
+
+ if isinstance(ctx.message.guild, d.Guild): guild = ctx.message.guild
+ else: guild = ctx.message.channel
channel = ctx.message.channel
user = ctx.message.author
- urls = []
- limit = 1
- # Checks if tags are in the file blacklists
- if args:
- for tag in args:
- if tag == 'swf' or tag == 'webm' or tag in global_blacklist or tag in guild_blacklist.get(str(guild.id), {}).get(str(channel.id), []) or tag in user_blacklist.get(str(user.id), []):
- raise exc.TagBlacklisted(tag)
- if len(args) > 5:
- raise exc.TagBoundsError(formatter.tostring(args[5:]))
- # Checks for, defines, and removes limit from end of args
- if args and len(args[-1]) == 1:
- if int(args[-1]) <= 6 and int(args[-1]) >= 1:
- limit = int(args[-1])
- args.pop()
- else:
- raise exc.BoundsError(args[-1])
+ blacklist = []
+
+ # Creates temp blacklist based on context
+ for k, v in aliases['global_blacklist'].items(): blacklist.extend([k] + v)
+ for k, v in aliases['guild_blacklist'].get(str(guild.id), {}).get(str(channel.id), {}).items(): blacklist.extend([k] + v)
+ for k, v in aliases['user_blacklist'].get(str(user.id), {}).items(): blacklist.extend([k] + v)
+ # Checks if tags are in local blacklists
+ if tags:
+ if len(tags) > 5: raise exc.TagBoundsError(formatter.tostring(tags[5:]))
+ for tag in tags:
+ if tag == 'swf' or tag == 'webm' or tag in blacklist: raise exc.TagBlacklisted(tag)
+
# Checks for blacklisted tags in endpoint blacklists - try/except is for continuing the parent loop
- while len(urls) < limit:
- request = requests.get('https://' + booru + '.net/post/index.json?limit=6&tags=order:random' + formatter.tostring_commas(args)).json()
+ posts = {}
+ c = 0
+ while len(posts) < limit:
+ if c == 50 + limit: raise exc.Timeout
+ request = requests.get('https://{}.net/post/index.json?tags={}'.format(booru, ','.join(['order:random'] + tags)), headers=HEADERS).json()
+ if len(request) == 0: raise exc.NotFound(formatter.tostring(tags))
+ if len(request) < limit: limit = len(request)
for post in request:
- if 'swf' in post['file_ext'] or 'webm' in post['file_ext']:
- continue
+ if 'swf' in post['file_ext'] or 'webm' in post['file_ext']: continue
try:
- for tag in global_blacklist:
- if tag in post['tags']:
- raise exc.Continue
- for tag in guild_blacklist.get(str(guild.id), {}).get(str(channel.id), []):
- if tag in post['tags']:
- raise exc.Continue
- for tag in user_blacklist.get(str(user.id), []):
- if tag in post['tags']:
- raise exc.Continue
- except exc.Continue:
- continue
- if post['file_url'] not in urls:
- urls.append(post['file_url'])
- if len(urls) == limit:
- break
- for url in urls:
- await ctx.send('`' + formatter.tostring(args) + '`\n' + url)
+ for tag in blacklist:
+ if tag in post['tags']: raise exc.Continue
+ except exc.Continue: continue
+ if post['file_url'] not in posts.values() and post['file_url'] not in previous: posts[post['id']] = post['file_url']
+ if len(posts) == limit: break
+ c += 1
+ return posts
# Umbrella command structure to manage global, channel, and user blacklists
@commands.group(aliases=['bl', 'b'], brief='Manage blacklists', description='Blacklist base command for managing blacklists\n\n`bl get [blacklist]` to show a blacklist\n`bl set [blacklist] [tags]` to replace a blacklist\n`bl clear [blacklist]` to clear a blacklist\n`bl add [blacklist] [tags]` to add tags to a blacklist\n`bl remove [blacklist] [tags]` to remove tags from a blacklist', usage='[flag] [blacklist] ([tags])')
@@ -185,238 +291,254 @@ class MsG:
async def blacklist(self, ctx):
if ctx.invoked_subcommand is None:
await ctx.send('❌ **Use a flag to manage blacklists.**\n*Type* `' + ctx.prefix + 'help bl` *for more info.*', delete_after=10)
-
@blacklist.error
async def blacklist_error(self, ctx, error):
if isinstance(error, commands.CheckFailure):
- return await ctx.send('❌ **Insufficient permissions.**', delete_after=10)
- if isinstance(error, exc.TagExists):
- return await ctx.send('❌ `' + str(exc.TagExists) + '` **already in blacklist.**', delete_after=10)
- if isinstance(error, exc.TagError):
- return await ctx.send('❌ `' + str(exc.TagError) + '` **not in blacklist.**', delete_after=10)
+ return await ctx.send('❌ **Insufficient permissions.**')
if isinstance(error, KeyError):
return await ctx.send('❌ **Blacklist does not exist.**', delete_after=10)
- @blacklist.command(name='update', aliases=['upd', 'up'])
- async def _update_blacklists(self, ctx):
- global global_blacklist, guild_blacklist, user_blacklist
- with open('global_blacklist.json', 'w') as outfile:
- json.dump(global_blacklist, outfile, indent=4, sort_keys=True)
- with open('guild_blacklist.json', 'w') as outfile:
- json.dump(guild_blacklist, outfile, indent=4, sort_keys=True)
- with open('user_blacklist.json', 'w') as outfile:
- json.dump(user_blacklist, outfile, indent=4, sort_keys=True)
- await ctx.send('✅ **Blacklists updated.**')
-
@blacklist.group(name='get', aliases=['g'])
async def _get_blacklist(self, ctx):
if ctx.invoked_subcommand is None:
await ctx.send('❌ **Invalid blacklist.**')
-
@_get_blacklist.command(name='global', aliases=['gl', 'g'])
async def __get_global_blacklist(self, ctx):
- global global_blacklist
- await ctx.send('🚫 **Global blacklist:**\n```' + formatter.tostring(global_blacklist) + '```')
+ global blacklists
+ await ctx.send('🚫 **Global blacklist:**\n```' + formatter.tostring(blacklists['global_blacklist']) + '```')
@_get_blacklist.command(name='channel', aliases=['ch', 'c'])
async def __get_channel_blacklist(self, ctx):
- global guild_blacklist
- if isinstance(ctx.message.guild, discord.Guild):
+ global blacklists
+ if isinstance(ctx.message.guild, d.Guild):
guild = ctx.message.guild
else:
guild = ctx.message.channel
channel = ctx.message.channel
- await ctx.send('🚫 <#' + str(channel.id) + '> **blacklist:**\n```' + formatter.tostring(guild_blacklist.get(str(guild.id), {}).get(str(channel.id), [])) + '```')
+ await ctx.send('🚫 <#' + str(channel.id) + '> **blacklist:**\n```' + formatter.tostring(blacklists['guild_blacklist'].get(str(guild.id), {}).get(str(channel.id), [])) + '```')
@_get_blacklist.command(name='me', aliases=['m'])
async def __get_user_blacklist(self, ctx):
- global user_blacklist
+ global blacklists
user = ctx.message.author
- await ctx.send('🚫 ' + user.mention + '**\'s blacklist:**\n```' + formatter.tostring(user_blacklist.get(str(user.id), [])) + '```', delete_after=10)
+ await ctx.send('🚫 ' + user.mention + '**\'s blacklist:**\n```' + formatter.tostring(blacklists['user_blacklist'].get(str(user.id), [])) + '```', delete_after=10)
@_get_blacklist.command(name='here', aliases=['h'])
async def __get_here_blacklists(self, ctx):
- global global_blacklist, guild_blacklist
- if isinstance(ctx.message.guild, discord.Guild):
+ global blacklists
+ if isinstance(ctx.message.guild, d.Guild):
guild = ctx.message.guild
else:
guild = ctx.message.channel
channel = ctx.message.channel
- await ctx.send('🚫 **__Blacklisted:__**\n\n**Global:**\n```' + formatter.tostring(global_blacklist) + '```\n**<#' + str(channel.id) + '>:**\n```' + formatter.tostring(guild_blacklist.get(str(guild.id), {}).get(str(channel.id), [])) + '```')
+ await ctx.send('🚫 **__Blacklisted:__**\n\n**Global:**\n```' + formatter.tostring(blacklists['global_blacklist']) + '```\n**<#' + str(channel.id) + '>:**\n```' + formatter.tostring(blacklists['guild_blacklist'].get(str(guild.id), {}).get(str(channel.id), [])) + '```')
@_get_blacklist.group(name='all', aliases=['a'])
async def __get_all_blacklists(self, ctx):
if ctx.invoked_subcommand is None:
await ctx.send('❌ **Invalid blacklist.**')
-
@__get_all_blacklists.command(name='guild', aliases=['g'])
@commands.has_permissions(manage_channels=True)
async def ___get_all_guild_blacklists(self, ctx):
- global guild_blacklist
- if isinstance(ctx.message.guild, discord.Guild):
+ global blacklists
+ if isinstance(ctx.message.guild, d.Guild):
guild = ctx.message.guild
else:
guild = ctx.message.channel
- await ctx.send('🚫 **__' + guild.name + ' blacklists:__**\n\n' + formatter.dict_tostring(guild_blacklist.get(str(guild.id), {})))
+ await ctx.send('🚫 **__' + guild.name + ' blacklists:__**\n\n' + formatter.dict_tostring(blacklists['guild_blacklist'].get(str(guild.id), {})))
@__get_all_blacklists.command(name='user', aliases=['u', 'member', 'm'])
@commands.is_owner()
async def ___get_all_user_blacklists(self, ctx):
- global user_blacklist
- await ctx.send('🚫 **__User blacklists:__**\n\n' + formatter.dict_tostring(user_blacklist))
+ global blacklists
+ await ctx.send('🚫 **__User blacklists:__**\n\n' + formatter.dict_tostring(blacklists['user_blacklist']))
@blacklist.group(name='add', aliases=['a'])
async def _add_tags(self, ctx):
if ctx.invoked_subcommand is None:
await ctx.send('❌ **Invalid blacklist.**')
-
@_add_tags.command(name='global', aliases=['gl', 'g'])
@commands.is_owner()
async def __add_global_tags(self, ctx, *tags):
- global global_blacklist
- for tag in tags:
- if tag in global_blacklist:
- raise exc.TagExists(tag)
- global_blacklist.extend(tags)
- with open('global_blacklist.json', 'w') as outfile:
- json.dump(global_blacklist, outfile, indent=4, sort_keys=True)
- await ctx.send('✅ **Added to global blacklist:**\n```' + formatter.tostring(tags) + '```', delete_after=5)
+ global blacklists, aliases, HEADERS
+ try:
+ for tag in tags:
+ if tag in blacklists['global_blacklist']:
+ raise exc.TagExists(tag)
+ blacklists['global_blacklist'].extend(tags)
+ for tag in tags:
+ alias_request = requests.get('https://e621.net/tag_alias/index.json?aliased_to=' + tag + '&approved=true', headers=HEADERS).json()
+ for dic in alias_request:
+ aliases['global_blacklist'].setdefault(tag, []).append(dic['name'])
+ with open('blacklists.json', 'w') as outfile:
+ json.dump(blacklists, outfile, indent=4, sort_keys=True)
+ with open('aliases.json', 'w') as outfile:
+ json.dump(aliases, outfile, indent=4, sort_keys=True)
+ await ctx.send('✅ **Added to global blacklist:**\n```' + formatter.tostring(tags) + '```', delete_after=5)
+ except exc.TagExists as e:
+ await ctx.send('❌ `' + str(e) + '` **already in blacklist.**', delete_after=10)
+ except Exception:
+ await ctx.send('{}\n```{}```'.format(exc.base, tb.format_exc(limit=1)))
+ tb.print_exc()
@_add_tags.command(name='channel', aliases=['ch', 'c'])
@commands.has_permissions(manage_channels=True)
async def __add_channel_tags(self, ctx, *tags):
- global guild_blacklist
- if isinstance(ctx.message.guild, discord.Guild):
+ global blacklists, aliases, HEADERS
+ if isinstance(ctx.message.guild, d.Guild):
guild = ctx.message.guild
else:
guild = ctx.message.channel
channel = ctx.message.channel
- for tag in tags:
- if tag in guild_blacklist.get(str(guild.id), {}).get(str(channel.id), []):
- raise exc.TagExists(tag)
- guild_blacklist.setdefault(str(guild.id), {}).setdefault(str(channel.id), []).extend(tags)
- with open('guild_blacklist.json', 'w') as outfile:
- json.dump(guild_blacklist, outfile, indent=4, sort_keys=True)
- await ctx.send('✅ **Added to** <#' + str(channel.id) + '> **blacklist:**\n```' + formatter.tostring(tags) + '```', delete_after=5)
+ try:
+ for tag in tags:
+ if tag in blacklists['guild_blacklist'].get(str(guild.id), {}).get(str(channel.id), []):
+ raise exc.TagExists(tag)
+ blacklists['guild_blacklist'].setdefault(str(guild.id), {}).setdefault(str(channel.id), []).extend(tags)
+ for tag in tags:
+ alias_request = requests.get('https://e621.net/tag_alias/index.json?aliased_to=' + tag + '&approved=true', headers=HEADERS).json()
+ for dic in alias_request:
+ aliases['guild_blacklist'].setdefault(str(guild.id), {}).setdefault(str(channel.id), {}).setdefault(tag, []).append(dic['name'])
+ with open('blacklists.json', 'w') as outfile:
+ json.dump(blacklists, outfile, indent=4, sort_keys=True)
+ with open('aliases.json', 'w') as outfile:
+ json.dump(aliases, outfile, indent=4, sort_keys=True)
+ await ctx.send('✅ **Added to** <#' + str(channel.id) + '> **blacklist:**\n```' + formatter.tostring(tags) + '```', delete_after=5)
+ except exc.TagExists as e:
+ await ctx.send('❌ `' + str(e) + '` **already in blacklist.**', delete_after=10)
+ except Exception:
+ await ctx.send('{}\n```{}```'.format(exc.base, tb.format_exc(limit=1)))
+ tb.print_exc()
@_add_tags.command(name='me', aliases=['m'])
async def __add_user_tags(self, ctx, *tags):
- global user_blacklist
+ global blacklists, aliases, HEADERS
user = ctx.message.author
- for tag in tags:
- if tag in user_blacklist.get(str(user.id), []):
- raise exc.TagExists(tag)
- user_blacklist.setdefault(str(user.id), []).extend(tags)
- with open('user_blacklist.json', 'w') as outfile:
- json.dump(user_blacklist, outfile, indent=4, sort_keys=True)
- await ctx.send('✅ ' + user.mention + ' **added:**\n```' + formatter.tostring(tags) + '```', delete_after=5)
+ try:
+ for tag in tags:
+ if tag in blacklists['user_blacklist'].get(str(user.id), []):
+ raise exc.TagExists(tag)
+ blacklists['user_blacklist'].setdefault(str(user.id), []).extend(tags)
+ for tag in tags:
+ alias_request = requests.get('https://e621.net/tag_alias/index.json?aliased_to=' + tag + '&approved=true', headers=HEADERS).json()
+ for dic in alias_request:
+ aliases['user_blacklist'].setdefault(str(user.id), {}).setdefault(tag, []).append(dic['name'])
+ with open('blacklists.json', 'w') as outfile:
+ json.dump(blacklists, outfile, indent=4, sort_keys=True)
+ with open('aliases.json', 'w') as outfile:
+ json.dump(aliases, outfile, indent=4, sort_keys=True)
+ await ctx.send('✅ ' + user.mention + ' **added:**\n```' + formatter.tostring(tags) + '```', delete_after=5)
+ except exc.TagExists as e:
+ await ctx.send('❌ `' + str(e) + '` **already in blacklist.**', delete_after=10)
+ except Exception:
+ await ctx.send('{}\n```{}```'.format(exc.base, tb.format_exc(limit=1)))
+ tb.print_exc()
@blacklist.group(name='remove', aliases=['rm', 'r'])
async def _remove_tags(self, ctx):
if ctx.invoked_subcommand is None:
await ctx.send('❌ **Invalid blacklist.**')
-
@_remove_tags.command(name='global', aliases=['gl', 'g'])
@commands.is_owner()
async def __remove_global_tags(self, ctx, *tags):
- global global_blacklist
- for tag in tags:
- if tag in global_blacklist:
- global_blacklist.remove(tag)
- else:
- raise exc.TagError(tag)
- with open('global_blacklist.json', 'w') as outfile:
- json.dump(global_blacklist, outfile, indent=4, sort_keys=True)
- await ctx.send('✅ **Removed from global blacklist:**\n```' + formatter.tostring(tags) + '```', delete_after=5)
+ global blacklists, aliases
+ try:
+ for tag in tags:
+ if tag in blacklists['global_blacklist']:
+ blacklists['global_blacklist'].remove(tag)
+ del aliases['global_blacklist'][tag]
+ else:
+ raise exc.TagError(tag)
+ with open('blacklists.json', 'w') as outfile:
+ json.dump(blacklists, outfile, indent=4, sort_keys=True)
+ with open('aliases.json', 'w') as outfile:
+ json.dump(aliases, outfile, indent=4, sort_keys=True)
+ await ctx.send('✅ **Removed from global blacklist:**\n```' + formatter.tostring(tags) + '```', delete_after=5)
+ except exc.TagError as e:
+ await ctx.send('❌ `' + str(e) + '` **not in blacklist.**', delete_after=10)
+ except Exception:
+ await ctx.send('{}\n```{}```'.format(exc.base, tb.format_exc(limit=1)))
+ tb.print_exc()
@_remove_tags.command(name='channel', aliases=['ch', 'c'])
@commands.has_permissions(manage_channels=True)
async def __remove_channel_tags(self, ctx, *tags):
- global guild_blacklist
- if isinstance(ctx.message.guild, discord.Guild):
+ global blacklists, aliases
+ if isinstance(ctx.message.guild, d.Guild):
guild = ctx.message.guild
else:
guild = ctx.message.channel
channel = ctx.message.channel
- for tag in tags:
- if tag in guild_blacklist.get(str(guild.id), {}).get(str(channel.id), []):
- guild_blacklist.get(str(guild.id), {})[str(channel.id)].remove(tag)
- else:
- raise exc.TagError(tag)
- with open('guild_blacklist.json', 'w') as outfile:
- json.dump(guild_blacklist, outfile, indent=4, sort_keys=True)
- await ctx.send('✅ **Removed from** <#' + str(channel.id) + '> **blacklist:**\n```' + formatter.tostring(tags) + '```', delete_after=5)
+ try:
+ for tag in tags:
+ if tag in blacklists['guild_blacklist'][str(guild.id)][str(channel.id)]:
+ blacklists['guild_blacklist'][str(guild.id)][str(channel.id)].remove(tag)
+ del aliases['guild_blacklist'][str(guild.id)][str(channel.id)][tag]
+ else:
+ raise exc.TagError(tag)
+ with open('blacklists.json', 'w') as outfile:
+ json.dump(blacklists, outfile, indent=4, sort_keys=True)
+ with open('aliases.json', 'w') as outfile:
+ json.dump(aliases, outfile, indent=4, sort_keys=True)
+ await ctx.send('✅ **Removed from** <#' + str(channel.id) + '> **blacklist:**\n```' + formatter.tostring(tags) + '```', delete_after=5)
+ except exc.TagError as e:
+ await ctx.send('❌ `' + str(e) + '` **not in blacklist.**', delete_after=10)
+ except Exception:
+ await ctx.send('{}\n```{}```'.format(exc.base, tb.format_exc(limit=1)))
+ tb.print_exc()
@_remove_tags.command(name='me', aliases=['m'])
async def __remove_user_tags(self, ctx, *tags):
- global user_blacklist
+ global blacklists, aliases
user = ctx.message.author
- for tag in tags:
- if tag in user_blacklist.get(str(user.id), []):
- user_blacklist.get[str(user.id)].remove(tag)
- else:
- raise exc.TagError(tag)
- with open('user_blacklist.json', 'w') as outfile:
- json.dump(user_blacklist, outfile, indent=4, sort_keys=True)
- await ctx.send('✅ ' + user.mention + ' **removed:**\n```' + formatter.tostring(tags) + '```', delete_after=5)
-
- @blacklist.group(name='set', aliases=['s'])
- async def _set_blacklist(self, ctx):
- if ctx.invoked_subcommand is None:
- await ctx.send('❌ **Invalid blacklist.**')
-
- @_set_blacklist.command(name='global', aliases=['gl', 'g'])
- @commands.is_owner()
- async def __set_global_blacklist(self, ctx, *tags):
- global global_blacklist
- global_blacklist = tags[:]
- with open('global_blacklist.json', 'w') as outfile:
- json.dump(global_blacklist, outfile, indent=4, sort_keys=True)
- await ctx.send('✅ **Global blacklist set to:**\n```' + formatter.tostring(global_blacklist) + '```', delete_after=10)
- @_set_blacklist.command(name='channel', aliases=['ch', 'c'])
- @commands.has_permissions(manage_channels=True)
- async def __set_channel_blacklist(self, ctx, *tags):
- global guild_blacklist
- if isinstance(ctx.message.guild, discord.Guild):
- guild = ctx.message.guild
- else:
- guild = ctx.message.channel
- channel = ctx.message.channel
- guild_blacklist.setdefault(str(guild.id), {})[str(channel.id)] = tags[:]
- with open('guild_blacklist.json', 'w') as outfile:
- json.dump(guild_blacklist, outfile, indent=4, sort_keys=True)
- await ctx.send('✅ <#' + str(channel.id) + '> **blacklist set to:**\n```' + formatter.tostring(guild_blacklist.get(str(guild.id), {}).get(str(channel.id), [])) + '```', delete_after=10)
- @_set_blacklist.command(name='me', aliases=['m'])
- async def __set_user_blacklist(self, ctx, *tags):
- global user_blacklist
- user = ctx.message.author
- user_blacklist[str(user.id)] = tags[:]
- with open('user_blacklist.json', 'w') as outfile:
- json.dump(user_blacklist, outfile, indent=4, sort_keys=True)
- await ctx.send('✅ ' + user.mention + '**\'s blacklist set to:**\n```' + formatter.tostring(user_blacklist.get(str(user.id), [])) + '```', delete_after=10)
+ try:
+ for tag in tags:
+ if tag in blacklists['user_blacklist'][str(user.id)]:
+ blacklists['user_blacklist'][str(user.id)].remove(tag)
+ del aliases['user_blacklist'][str(user.id)][tag]
+ else:
+ raise exc.TagError(tag)
+ with open('blacklists.json', 'w') as outfile:
+ json.dump(blacklists, outfile, indent=4, sort_keys=True)
+ with open('aliases.json', 'w') as outfile:
+ json.dump(aliases, outfile, indent=4, sort_keys=True)
+ await ctx.send('✅ ' + user.mention + ' **removed:**\n```' + formatter.tostring(tags) + '```', delete_after=5)
+ except exc.TagError as e:
+ await ctx.send('❌ `' + str(e) + '` **not in blacklist.**', delete_after=10)
+ except Exception:
+ await ctx.send('{}\n```{}```'.format(exc.base, tb.format_exc(limit=1)))
+ tb.print_exc()
@blacklist.group(name='clear', aliases=['cl', 'c'])
async def _clear_blacklist(self, ctx):
if ctx.invoked_subcommand is None:
await ctx.send('❌ **Invalid blacklist.**')
-
@_clear_blacklist.command(name='global', aliases=['gl', 'g'])
@commands.is_owner()
async def __clear_global_blacklist(self, ctx):
- global global_blacklist
- del global_blacklist
- with open('global_blacklist.json', 'w') as outfile:
- json.dump(global_blacklist, outfile, indent=4, sort_keys=True)
+ global blacklists, aliases
+ del blacklists['global_blacklist']
+ del aliases['global_blacklist']
+ with open('blacklists.json', 'w') as outfile:
+ json.dump(blacklists, outfile, indent=4, sort_keys=True)
+ with open('aliases.json', 'w') as outfile:
+ json.dump(aliases, outfile, indent=4, sort_keys=True)
await ctx.send('✅ **Global blacklist cleared.**', delete_after=5)
@_clear_blacklist.command(name='channel', aliases=['ch', 'c'])
@commands.has_permissions(manage_channels=True)
async def __clear_channel_blacklist(self, ctx):
- global guild_blacklist
- if isinstance(ctx.message.guild, discord.Guild):
+ global blacklists, aliases
+ if isinstance(ctx.message.guild, d.Guild):
guild = ctx.message.guild
else:
guild = ctx.message.channel
channel = ctx.message.channel
- del guild_blacklist.get(str(guild.id), {})[str(channel.id)]
- with open('guild_blacklist.json', 'w') as outfile:
- json.dump(guild_blacklist, outfile, indent=4, sort_keys=True)
+ del blacklists['guild_blacklist'][str(guild.id)][str(channel.id)]
+ del aliases['guild_blacklist'][str(guild.id)][str(channel.id)]
+ with open('blacklists.json', 'w') as outfile:
+ json.dump(blacklists, outfile, indent=4, sort_keys=True)
+ with open('aliases.json', 'w') as outfile:
+ json.dump(aliases, outfile, indent=4, sort_keys=True)
await ctx.send('✅ <#' + str(channel.id) + '> **blacklist cleared.**', delete_after=5)
@_clear_blacklist.command(name='me', aliases=['m'])
async def __clear_user_blacklist(self, ctx):
- global user_blacklist
+ global blacklists, aliases
user = ctx.message.author
- del user_blacklist[str(user.id)]
- with open('user_blacklist.json', 'w') as outfile:
- json.dump(user_blacklist, outfile, indent=4, sort_keys=True)
+ del blacklists['user_blacklist'][str(user.id)]
+ del aliases['user_blacklist'][str(user.id)]
+ with open('blacklists.json', 'w') as outfile:
+ json.dump(blacklists, outfile, indent=4, sort_keys=True)
+ with open('aliases.json', 'w') as outfile:
+ json.dump(aliases, outfile, indent=4, sort_keys=True)
await ctx.send('✅ ' + user.mention + '**\'s blacklist cleared.**', delete_after=5)
diff --git a/src/main/cogs/management.py b/src/main/cogs/management.py
new file mode 100644
index 0000000..8fb4d7a
--- /dev/null
+++ b/src/main/cogs/management.py
@@ -0,0 +1,176 @@
+import asyncio
+import discord as d
+import traceback
+from discord.ext import commands
+from misc import checks
+from misc import exceptions as exc
+from utils import utils as u
+
+RATE_LIMIT = 2.1
+
+class Administration:
+
+ def __init__(self, bot):
+ self.bot = bot
+ self.queue = asyncio.Queue()
+
+ if u.background.get('management', {}):
+ if u.background['management'].get('auto_delete', {}):
+ for channel in u.background['management']['auto_delete']:
+ temp = self.bot.get_channel(channel)
+ self.bot.loop.create_task(self.on_message(temp))
+ self.bot.loop.create_task(self.delete())
+ print('Looping {}'.format(temp.id))
+
+ # @commands.group(aliases=['pr', 'clear', 'cl'])
+ # @commands.is_owner()
+ # @checks.del_ctx()
+ # async def prune(self, ctx):
+ # pass
+ #
+ # @prune.group(name='all', aliases=['a'])
+ # async def _all(self, ctx):
+ # pass
+ # @_all.group(name='user')
+ # async def __user(self, ctx, user: d.Member):
+ # channels = ctx.message.guild.text_channels
+ # bulk_history = {}
+ # bulk = {}
+ # history = []
+ # c = 0
+ # if ctx.invoked_subcommand is None:
+ # for channel in channels:
+ # bulk_history[channel] = await channel.history(limit=None, after=dt.datetime.utcnow() - dt.timedelta(days=14)).flatten()
+ # await ch_sent.edit(content='🗄 **Cached** `' + str(channels.index(channel) + 1) + '/' + str(len(channels)) + '` **channels.**')
+ # await asyncio.sleep(RATE_LIMIT)
+ # for channel, messages in bulk_history.items():
+ # bulk[channel] = [message for message in messages if message.author.id == int(uid)]
+ # for channel, messages in bulk_history.items():
+ # bulk[channel] = [bulk[channel][i:i+100] for i in range(0, len(bulk[channel]), 100)]
+ # await ctx.send('⏱ **Estimated time to delete `bulk-history`:** `' + str(int(RATE_LIMIT * sum([len(v) for v in bulk.values()]) / 60)) + ' mins ' + str(int(RATE_LIMIT * sum([len(v) for v in bulk.values()]) % 60)) + ' secs`')
+ # check = await ctx.send(ctx.author.mention + ' **Continue?** `Y` or `N`')
+ # await self.bot.wait_for('message', check=yes, timeout=60)
+ # del_sent = await ctx.send('🗑 **Deleting messages...**')
+ # for channel, messages in bulk.items():
+ # for chunk in messages:
+ # c += len(chunk)
+ # await channel.delete_messages(chunk)
+ # await del_sent.edit(content='🗑 **Deleted** `' + str(c) + '/' + str(sum([len(v) for v in bulk.values()])) + '` **messages.**')
+ # await asyncio.sleep(5)
+ # await ctx.send('✅ `' + str(sum([len(v) for v in bulk.values()])) + '` **of** <@' + uid + '>**\'s messages deleted from** ' + ctx.message.guild.name + '**.**')
+ # for channel in channels:
+ # history.extend(await channel.history(limit=None, before=dt.datetime.utcnow() - dt.timedelta(days=14)).flatten())
+ # await ch_sent.edit(content='🗄 **Cached** `' + str(channels.index(channel) + 1) + '/' + str(len(channels)) + '` **channels.**')
+ # await asyncio.sleep(RATE_LIMIT)
+
+ @commands.command(name=',prunefromguild', aliases=[',pfg', ',prunefromserver', ',pfs'], 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\}')
+ @commands.is_owner()
+ @checks.del_ctx()
+ async def prune_all_user(self, ctx, uid, when=None, reference=None):
+ global RATE_LIMIT
+
+ def yes(msg):
+ if msg.content.lower() == 'y' and msg.channel is ctx.message.channel and msg.author is ctx.message.author:
+ return True
+ elif msg.content.lower() == 'n' and msg.channel is ctx.message.channel and msg.author is ctx.message.author:
+ raise exc.CheckFail
+ else:
+ return False
+
+ channels = ctx.message.guild.text_channels
+ if reference is not None:
+ for channel in channels:
+ try:
+ ref = await channel.get_message(reference)
+ except d.errors.NotFound:
+ continue
+ history = []
+ try:
+ pru_sent = await ctx.send('⌛️ **Pruning** <@{}>**\'s messages will take some time.**'.format(uid))
+ ch_sent = await ctx.send('🗄 **Caching channels...**')
+
+ if when is None:
+ for channel in channels:
+ history.extend(await channel.history(limit=None).flatten())
+ await ch_sent.edit(content='🗄 **Cached** `{}/{}` **channels.**'.format(channels.index(channel) + 1, len(channels)))
+ await asyncio.sleep(RATE_LIMIT)
+ elif when =='before':
+ for channel in channels:
+ history.extend(await channel.history(limit=None, before=ref.created_at).flatten())
+ await ch_sent.edit(content='🗄 **Cached** `{}/{}` **channels.**'.format(channels.index(channel) + 1, len(channels)))
+ await asyncio.sleep(RATE_LIMIT)
+ elif when == 'after':
+ for channel in channels:
+ history.extend(await channel.history(limit=None, after=ref.created_at).flatten())
+ await ch_sent.edit(content='🗄 **Cached** `{}/{}` **channels.**'.format(channels.index(channel) + 1, len(channels)))
+ await asyncio.sleep(RATE_LIMIT)
+ elif when == 'about':
+ for channel in channels:
+ history.extend(await channel.history(limit=101, about=ref.created_at).flatten())
+ await ch_sent.edit(content='🗄 **Cached** `{}/{}` **channels.**'.format(channels.index(channel) + 1, len(channels)))
+ await asyncio.sleep(RATE_LIMIT)
+
+ history = [message for message in history if message.author.id == int(uid)]
+ est_sent = await ctx.send('⏱ **Estimated time to delete history:** `{}m {}s`'.format(int(RATE_LIMIT * len(history) / 60), int(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=60)
+ await cont_sent.delete()
+ del_sent = await ctx.send('🗑 **Deleting messages...**')
+ for message in history:
+ try: await message.delete()
+ except d.NotFound: pass
+ # print('Deleted {}/{} messages.'.format(history.index(message) + 1, len(history)))
+ await del_sent.edit(content='🗑 **Deleted** `{}/{}` **messages.**'.format(history.index(message) + 1, len(history)))
+ await asyncio.sleep(RATE_LIMIT)
+ await del_sent.edit(content='🗑 `{}` **of** <@{}>**\'s messages deleted from** {}**.**'.format(len(history), uid, ctx.message.guild.name))
+ except exc.CheckFail:
+ await ctx.send('❌ **Deletion aborted.**', delete_after=10)
+ except TimeoutError:
+ await ctx.send('❌ **Deletion timed out.**', delete_after=10)
+ except Exception:
+ await ctx.send('{}\n```{}```'.format(exc.base, traceback.format_exc(limit=1)))
+ traceback.print_exc(limit=1)
+
+ async def delete(self):
+ while True:
+ message = await self.queue.get()
+ await asyncio.sleep(RATE_LIMIT)
+ await message.delete()
+
+ async def on_message(self, channel):
+ def check(msg):
+ if msg.content == 'stop' and msg.channel is channel and msg.author.guild_permissions.administrator:
+ raise exc.Abort
+ elif msg.channel is channel and not msg.pinned:
+ return True
+ else:
+ return False
+
+ try:
+ while True:
+ message = await self.bot.wait_for('message', check=check)
+ await self.queue.put(message)
+ except d.errors.NotFound:
+ pass
+ except exc.Abort:
+ u.background['management']['auto_delete'].remove(channel.id)
+ u.update(u.background, 'background.json')
+ print('Stopped looping {}'.format(channel.id))
+ await channel.send('✅ **Stopped deleting messages in** {}**.**'.format(channel.mention), delete_after=5)
+ except AttributeError:
+ pass
+ except Exception:
+ await channel.send(exc.base + '\n```' + traceback.format_exc(limit=1) + '```')
+ traceback.print_exc(limit=1)
+
+ @commands.command(name='autodelete', aliases=['autodel', 'ad'])
+ @commands.has_permissions(administrator=True)
+ @checks.del_ctx()
+ async def auto_delete(self, ctx):
+ channel = ctx.message.channel
+ u.background.setdefault('management', {}).setdefault('auto_delete', []).append(channel.id)
+ u.update(u.background, 'background.json')
+ self.bot.loop.create_task(self.on_message(channel))
+ self.bot.loop.create_task(self.delete())
+ print('Looping {}'.format(channel.id))
+ await ctx.send('✅ **Auto-deleting all messages in this channel.**', delete_after=5)
diff --git a/src/main/cogs/owner.py b/src/main/cogs/owner.py
new file mode 100644
index 0000000..a0495a8
--- /dev/null
+++ b/src/main/cogs/owner.py
@@ -0,0 +1,98 @@
+import asyncio
+import code
+import io
+import pyrasite as pyr
+import re
+import sys
+import traceback as tb
+
+import discord as d
+from discord.ext import commands
+
+from misc import checks
+from misc import exceptions as exc
+
+nl = re.compile('\n')
+
+class Tools:
+
+ def __init__(self, bot):
+ self.bot = bot
+
+ def format(self, i='', o=''):
+ if len(o) > 1: return '>>> {}\n{}'.format(i, o)
+ else: return '>>> {}'.format(i)
+ async def generate(self, d, i='', o=''):
+ return await d.send('```python\n{}```'.format(self.format(i, o)))
+ async def refresh(self, m, i='', o=''):
+ global nl
+ output = m.content[10:-3]
+ if len(nl.findall(output)) <= 20: await m.edit(content='```python\n{}\n{}\n>>>```'.format(output, self.format(i, o)))
+ else: await m.edit(content='```python\n{}```'.format(self.format(i, o)))
+
+ async def generate_err(self, d, o=''):
+ return await d.send('```\n{}```'.format(o))
+ async def refresh_err(self, m, o=''):
+ await m.edit(content='```\n{}```'.format(o))
+
+ @commands.command(name=',console', aliases=[',con', ',c'], hidden=True)
+ @commands.is_owner()
+ @checks.del_ctx()
+ async def console(self, ctx):
+ def execute(msg):
+ if msg.content == ',exit' and msg.author is ctx.message.author:
+ raise exc.CheckFail
+ elif msg.author is ctx.message.author and msg.channel is ctx.message.channel: return True
+ else: return False
+
+ try:
+ console = await self.generate(ctx)
+ exception = await self.generate_err(ctx)
+ while True:
+ exe = await self.bot.wait_for('message', check=execute)
+ await exe.delete()
+ sys.stdout = io.StringIO()
+ sys.stderr = io.StringIO()
+ try: exec(exe.content)
+ except Exception: tb.print_exc(limit=1)
+ await self.refresh(console, exe.content, sys.stdout.getvalue())
+ await self.refresh_err(exception, sys.stderr.getvalue())
+ await ctx.send(console.content[10:-3])
+ sys.stdout = sys.__stdout__
+ sys.stderr = sys.__stderr__
+ except exc.CheckFail:
+ await ctx.send('↩️ **Exited console.**')
+ except Exception:
+ await ctx.send('{}\n```{}```'.format(exc.base, tb.format_exc(limit=1)))
+ tb.print_exc(limit=1, file=sys.__stderr__)
+ finally:
+ sys.stdout = sys.__stdout__
+ sys.stderr = sys.__stderr__
+ print('Reset sys output.')
+
+ @commands.command(name='arbitrary', aliases=[',arbit', ',ar'])
+ @commands.is_owner()
+ @checks.del_ctx()
+ async def arbitrary(self, ctx, *, exe):
+ try:
+ sys.stdout = io.StringIO()
+ exec(exe)
+ await self.generate(ctx, exe, sys.stdout.getvalue())
+ except Exception:
+ await ctx.send('{}\n```{}```'.format(exc.base, tb.format_exc(limit=1)))
+ tb.print_exc(limit=1)
+ finally:
+ sys.stdout = sys.__stdout__
+ print('Reset stdout.')
+
+ @commands.group(aliases=[',db'], hidden=True)
+ @commands.is_owner()
+ @checks.del_ctx()
+ async def debug(self, ctx):
+ console = await self.generate(ctx)
+ @debug.command(name='inject', aliases=['inj'])
+ async def _inject(self, ctx, *, input_):
+ pass
+ @debug.command(name='inspect', aliases=['ins'])
+ async def _inspect(self, ctx, *, input_):
+ pass
diff --git a/src/main/cogs/tools.py b/src/main/cogs/tools.py
index 93ccf0c..875b88d 100644
--- a/src/main/cogs/tools.py
+++ b/src/main/cogs/tools.py
@@ -1,58 +1,116 @@
import asyncio
+import datetime as dt
import discord
+import httplib2
+import mimetypes
+import os
+import requests_oauthlib as ro
+import tempfile
import traceback
+import webbrowser
from discord.ext import commands
+#from run import config
from cogs import booru
from misc import checks
from misc import exceptions as exc
from utils import formatter
+from apiclient.discovery import build
+from apiclient import http
+from oauth2client.client import flow_from_clientsecrets
+youtube = None
+
+tempfile.tempdir = os.getcwd()
+
+command_dict = {}
+
class Utils:
def __init__(self, bot):
self.bot = bot
- def last():
- pass
-
@commands.command(name='last', aliases=['l', ','], brief='Reinvokes last command', description='Reinvokes previous command executed', hidden=True)
async def last_command(self, ctx):
+ global command_dict
try:
- # await ctx.invoke(command, args)
- await ctx.send('`' + booru.last_command[ctx.message.author.id] + '`')
+ if command_dict.get(str(ctx.message.author.id), {}).get('args', None) is not None:
+ args = command_dict.get(str(ctx.message.author.id), {})['args']
+ print(command_dict)
+ await ctx.invoke(command_dict.get(str(ctx.message.author.id), {}).get('command', None), args)
except Exception:
- await ctx.send(exceptions.base)
+ await ctx.send(exc.base + '\n```' + traceback.format_exc(limit=1) + '```')
traceback.print_exc(limit=1)
# [prefix]ping -> Pong!
@commands.command(aliases=['p'], brief='Pong!', description='Returns latency from bot to Discord servers, not to user')
@checks.del_ctx()
async def ping(self, ctx):
+ global command_dict
try:
await ctx.send(ctx.message.author.mention + ' 🏓 `' + str(int(self.bot.latency * 1000)) + 'ms`', delete_after=5)
except Exception:
- await ctx.send(exceptions.base)
+ await ctx.send(exc.base + '\n```' + traceback.format_exc(limit=1) + '```')
traceback.print_exc(limit=1)
+ command_dict.setdefault(str(ctx.message.author.id), {}).update({'command': ctx.command})
@commands.command(aliases=['pre'], brief='List bot prefixes', description='Shows all used prefixes')
@checks.del_ctx()
async def prefix(self, ctx):
try:
- await ctx.send('**Prefix:** `,` or ' + ctx.me.mention)
+ await ctx.send('**Prefix:** `,`')
except Exception:
- await ctx.send(exceptions.base)
+ await ctx.send(exc.base + '\n```' + traceback.format_exc(limit=1) + '```')
traceback.print_exc(limit=1)
@commands.group(name=',send', aliases=[',s'], hidden=True)
@commands.is_owner()
+ @checks.del_ctx()
async def send(self, ctx):
pass
@send.command(name='guild', aliases=['g', 'server', 's'])
- @checks.del_ctx()
async def send_guild(self, ctx, guild, channel, *message):
await discord.utils.get(self.bot.get_all_channels(), guild__name=guild, name=channel).send(formatter.tostring(message))
@send.command(name='user', aliases=['u', 'member', 'm'])
- @checks.del_ctx()
async def send_user(self, ctx, user, *message):
await discord.utils.get(self.bot.get_all_members(), id=int(user)).send(formatter.tostring(message))
+
+ @commands.command(aliases=['authenticateupload', 'authupload', 'authup', 'auth'])
+ async def authenticate_upload(self, ctx):
+ global youtube
+ flow = flow_from_clientsecrets('client_secrets.json', scope='https://www.googleapis.com/auth/youtube.upload', login_hint='botmyned@gmail.com', redirect_uri='urn:ietf:wg:oauth:2.0:oob')
+ flow.params['access_type'] = 'offline'
+ webbrowser.open_new_tab(flow.step1_get_authorize_url())
+ credentials = flow.step2_exchange(input('Authorization code: '))
+ youtube = build('youtube', 'v3', http=credentials.authorize(http.build_http()))
+ print('Service built.')
+ @commands.command(aliases=['up', 'u', 'vid', 'v'])
+ @checks.is_listed()
+ async def upload(self, ctx):
+ global youtube
+ attachments = ctx.message.attachments
+ try:
+ if not attachments:
+ raise exc.MissingAttachment
+ if len(attachments) > 1:
+ raise exc.TooManyAttachments(len(attachments))
+ mime = mimetypes.guess_type(attachments[0].filename)[0]
+ if 'video/' in mime:
+ with tempfile.NamedTemporaryFile() as temp:
+ await attachments[0].save(temp)
+ else:
+ raise exc.InvalidVideoFile(mime)
+ print('https://www.youtube.com/watch?v=' + youtube.videos().insert(part='snippet', body={'categoryId': '24', 'title': 'Test'}, media_body=http.MediaFileUpload(temp.name, chunksize=-1)))
+ except exc.InvalidVideoFile as e:
+ await ctx.send('❌ `' + str(e) + '` **not valid video type.**', delete_after=10)
+ except exc.TooManyAttachments as e:
+ await ctx.send('❌ `' + str(e) + '` **too many attachments.** Only one attachment is permitted to upload.', delete_after=10)
+ except exc.MissingAttachment:
+ await ctx.send('❌ **Missing attachment.**', delete_after=10)
+ except Exception:
+ await ctx.send(exc.base + '\n```' + traceback.format_exc(limit=1) + '```')
+ traceback.print_exc(limit=1)
+ @upload.error
+ async def upload_error(self, ctx, error):
+ pass
+# http.
diff --git a/src/main/misc/checks.py b/src/main/misc/checks.py
index 7376cc6..6876964 100644
--- a/src/main/misc/checks.py
+++ b/src/main/misc/checks.py
@@ -9,6 +9,7 @@ with open('config.json') as infile:
config = json.load(infile)
owner_id = config['owner_id']
+listed_ids = config['listed_ids']
def is_owner():
async def predicate(ctx):
@@ -22,6 +23,10 @@ def is_mod():
def predicate(ctx):
return ctx.message.author.guild_permissions.ban_members
return commands.check(predicate)
+def is_listed():
+ def predicate(ctx):
+ return ctx.message.author.id in listed_ids
+ return commands.check(predicate)
def owner(ctx):
return ctx.message.author.id == owner_id
diff --git a/src/main/misc/exceptions.py b/src/main/misc/exceptions.py
index a56802a..4c3e403 100644
--- a/src/main/misc/exceptions.py
+++ b/src/main/misc/exceptions.py
@@ -1,24 +1,23 @@
-base = '‼️ **An internal error has occurred.** Please notify my master! 🐺'
+base = '⚠️ **An internal error has occurred.** Please notify my master! 🐺'
-class PostError(Exception):
- pass
-class ImageError(Exception):
- pass
-class MatchError(Exception):
- pass
-class TagBlacklisted(Exception):
- pass
-class BoundsError(Exception):
- pass
-class TagBoundsError(Exception):
- pass
-class TagExists(Exception):
- pass
-class TagError(Exception):
- pass
-class FlagError(Exception):
- pass
-class BlacklistError(Exception):
- pass
-class Continue(Exception):
- pass
+class Left(Exception): pass
+class Right(Exception): pass
+class Save(Exception): pass
+class PostError(Exception): pass
+class ImageError(Exception): pass
+class MatchError(Exception): pass
+class TagBlacklisted(Exception): pass
+class BoundsError(Exception): pass
+class TagBoundsError(Exception): pass
+class TagExists(Exception): pass
+class TagError(Exception): pass
+class FlagError(Exception): pass
+class BlacklistError(Exception): pass
+class NotFound(Exception): pass
+class Timeout(Exception): pass
+class InvalidVideoFile(Exception): pass
+class MissingAttachment(Exception): pass
+class TooManyAttachments(Exception): pass
+class CheckFail(Exception): pass
+class Abort(Exception): pass
+class Continue(Exception): pass
diff --git a/src/main/run.py b/src/main/run.py
index 671a978..0e68add 100644
--- a/src/main/run.py
+++ b/src/main/run.py
@@ -6,76 +6,114 @@ try:
print('\"config.json\" loaded.')
except FileNotFoundError:
with open('config.json', 'w') as outfile:
- json.dump({'client_id': 0, 'owner_id': 0, 'permissions': 388160, 'prefix': ',', 'shutdown_channel': 0, 'startup_channel': 0, 'token': 'str'}, outfile, indent=4, sort_keys=True)
+ json.dump({'client_id': 0, 'listed_ids': [0], 'owner_id': 0, 'permissions': 388160, 'prefix': ',', 'shutdown_channel': 0, 'startup_channel': 0, 'token': 'str'}, outfile, indent=4, sort_keys=True)
raise FileNotFoundError('Config file not found: \"config.json\" created with abstract values. Restart \"run.py\" with correct values.')
import asyncio
-import discord
+import datetime as dt
+import discord as d
+import os
+import subprocess
+import sys
import traceback
from discord import utils
from discord.ext import commands
-from cogs import booru, info, tools
+from cogs import booru, info, owner, management, tools
from misc import checks
from misc import exceptions as exc
+from utils import utils as u
-bot = commands.Bot(command_prefix=commands.when_mentioned_or(config['prefix']), description='Experimental booru bot')
+import logging
+logging.basicConfig(level=logging.INFO)
+
+print('PID {}'.format(os.getpid()))
+
+bot = commands.Bot(command_prefix=config['prefix'], description='Experimental booru bot')
# Send and print ready message to #testing and console after logon
@bot.event
async def on_ready():
- if isinstance(bot.get_channel(config['startup_channel']), discord.TextChannel):
- await bot.get_channel(config['startup_channel']).send('Hello how are? **Have day.** 🌈\n')
- print('Connected.')
- print('Username: ' + bot.user.name)
+ global bot
+
+ bot.add_cog(tools.Utils(bot))
+ bot.add_cog(owner.Tools(bot))
+ bot.add_cog(management.Administration(bot))
+ bot.add_cog(info.Info(bot))
+ bot.add_cog(booru.MsG(bot))
+
+ # bot.loop.create_task(u.clear(booru.temp_urls, 30*60))
+
+ if isinstance(bot.get_channel(config['startup_channel']), d.TextChannel):
+ await bot.get_channel(config['startup_channel']).send('**Started.** ☀️')
+ print('CONNECTED')
+ print(bot.user.name)
print('-------')
# Close connection to Discord - immediate offline
-@bot.command(name=',die', aliases=[',d', ',close', ',kill'], brief='Kills the bot', description='BOT OWNER ONLY\nCloses the connection to Discord', hidden=True)
-@checks.del_ctx()
+@bot.command(name=',die', aliases=[',d'], brief='Kills the bot', description='BOT OWNER ONLY\nCloses the connection to Discord', hidden=True)
@commands.is_owner()
+@checks.del_ctx()
async def die(ctx):
try:
- await bot.get_channel(config['shutdown_channel']).send('Am go bye. **Have night.** 💤\n')
+ if isinstance(bot.get_channel(config['startup_channel']), d.TextChannel):
+ await bot.get_channel(config['shutdown_channel']).send('**Shutting down...** 🌙')
await bot.close()
print('-------')
- print('Closed.')
+ print('CLOSED')
except Exception:
- await ctx.send(exc.base)
+ await ctx.send(exc.base + '\n```' + traceback.format_exc(limit=1) + '```')
+ traceback.print_exc(limit=1)
+
+@bot.command(name=',restart', aliases=[',res', ',r'], hidden=True)
+@commands.is_owner()
+@checks.del_ctx()
+async def restart(ctx):
+ try:
+ print('RESTARTING')
+ print('-------')
+ if isinstance(bot.get_channel(config['startup_channel']), d.TextChannel):
+ await bot.get_channel(config['shutdown_channel']).send('**Restarting...** 💤')
+ os.execl(sys.executable, 'python3', 'run.py')
+ except Exception:
+ await ctx.send('{}\n```{}```'.format(exc.base, traceback.format_exc(limit=1)))
traceback.print_exc(limit=1)
# Invite bot to bot owner's server
@bot.command(name=',invite', aliases=[',inv', ',link'], brief='Invite the bot', description='BOT OWNER ONLY\nInvite the bot to a server (Requires admin)', hidden=True)
-@checks.del_ctx()
@commands.is_owner()
+@checks.del_ctx()
async def invite(ctx):
try:
- await ctx.send('🔗 https://discordapp.com/oauth2/authorize?&client_id=' + str(config['client_id']) + '&scope=bot&permissions=' + str(config['permissions']))
+ await ctx.send('🔗 https://discordapp.com/oauth2/authorize?&client_id={}&scope=bot&permissions={}'.format(config['client_id'], config['permissions']), delete_after=10)
except Exception:
- await ctx.send(exc.base)
+ await ctx.send('{}\n```{}```'.format(exc.base, traceback.format_exc(limit=1)))
traceback.print_exc(limit=1)
@bot.command(brief='[IN TESTING]', description='[IN TESTING]', hidden=True)
async def hi(ctx):
+ user = ctx.message.author
try:
- hello = 'Hello, ' + ctx.message.author.mention + '.'
- if ctx.message.author.id == checks.owner_id:
+ hello = 'Hewwo, {}.'.format(user.mention)
+ if user.id == checks.owner_id:
hello += '.. ***Master.*** uwu'
- elif ctx.message.author.guild_permissions.administrator:
- hello = hello[:7] + '**Admin** ' + hello[7:]
- elif ctx.message.author.guild_permissions.ban_members:
- hello = hello[:7] + '**Mod** ' + hello[7:]
+ elif user.guild_permissions.administrator:
+ hello = '{} **Admin** {}'.format(hello[:7], hello[7:])
+ elif user.guild_permissions.ban_members:
+ hello = '{} **Mod** {}'.format(hello[:7], hello[7:])
await ctx.send(hello)
except Exception:
- await ctx.send(exc.base)
+ await ctx.send('{}\n```{}```'.format(exc.base, traceback.format_exc(limit=1)))
traceback.print_exc(limit=1)
-@bot.command(hidden=True)
+@bot.command(name=',test', hidden=True)
+@commands.is_owner()
@checks.del_ctx()
async def test(ctx):
- pass
-
-bot.add_cog(info.Info(bot))
-bot.add_cog(tools.Utils(bot))
-bot.add_cog(booru.MsG(bot))
+ embed = d.Embed(title='/post/xxxxxx', url='https://static1.e621.net/data/4b/3e/4b3ec0c2e8580f418e4ce019dfd5ac32.png', timestamp=dt.datetime.utcnow(), color=ctx.me.color)
+ embed.set_image(url='https://static1.e621.net/data/27/0f/270fd28caa5e6d8bf542a76515848e02.png')
+ embed.set_footer(text='e621', icon_url='http://ndl.mgccw.com/mu3/app/20141013/18/1413204353554/icon/icon_xl.png')
+ embed.set_author(name='tags', url=ctx.message.author.avatar_url, icon_url=ctx.message.author.avatar_url)
+ embed.add_field(name='Link', value='https://static1.e621.net/data/c2/55/c255792b5a307ee6efa51d6bb3edf878.jpg')
+ await ctx.send(embed=embed)
bot.run(config['token'])
diff --git a/src/main/utils/formatter.py b/src/main/utils/formatter.py
index faa36c6..f8ed334 100644
--- a/src/main/utils/formatter.py
+++ b/src/main/utils/formatter.py
@@ -1,9 +1,13 @@
-def tostring(i):
- o = ' '
+def tostring(i, *, random=False):
+ o = ''
if i:
for v in i:
o += v + ' '
- o = o[1:-1]
+ o = o[:-1]
+ elif random is True:
+ o += 'order:random'
+ else:
+ o = ' '
return o
def tostring_commas(i):
diff --git a/src/main/utils/utils.py b/src/main/utils/utils.py
new file mode 100644
index 0000000..eb007c8
--- /dev/null
+++ b/src/main/utils/utils.py
@@ -0,0 +1,36 @@
+import json
+
+try:
+ with open('background.json') as infile:
+ background = json.load(infile)
+ print('\"background.json\" loaded.')
+except FileNotFoundError:
+ with open('background.json', 'w+') as iofile:
+ print('Background file not found: \"background.json\" created and loaded.')
+ json.dump({}, iofile, indent=4, sort_keys=True)
+ iofile.seek(0)
+ background = json.load(iofile)
+
+with open('config.json') as infile:
+ config = json.load(infile)
+
+def update(out, file):
+ with open(file, 'w') as outfile:
+ json.dump(out, outfile, indent=4, sort_keys=True)
+
+import asyncio
+
+async def clear(obj, interval=10*60, replace=None):
+ if replace is None:
+ if type(obj) is list:
+ replace = []
+ elif type(obj) is dict:
+ replace = {}
+ elif type(obj) is int:
+ replace = 0
+ elif type(obj) is str:
+ replace = ''
+
+ while True:
+ obj = replace
+ asyncio.sleep(interval)