diff --git a/README.md b/README.md index ad9d4db..f95101f 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,8 @@ v3 Cogs for Red-DiscordBot by Twentysix26. away - Originally by Paddo, written for v3 by Axas, final tests by aikaterna. Set and unset a user as being "away". +blurplefy - Make an avatar or an image upload blurple for Discord's 3rd anniversary. + chatchart - Generates a pie chart to display chat activity over the last 5000 messages. Requested by violetnyte. pingtime - Show all shards' pingtimes. diff --git a/blurplefy/__init__.py b/blurplefy/__init__.py new file mode 100644 index 0000000..6f46173 --- /dev/null +++ b/blurplefy/__init__.py @@ -0,0 +1,6 @@ +from .blurplefy import Blurplefy +from discord.ext import commands + + +def setup(bot: commands.Bot): + bot.add_cog(Blurplefy(bot)) diff --git a/blurplefy/blurplefy.py b/blurplefy/blurplefy.py new file mode 100644 index 0000000..16ff86a --- /dev/null +++ b/blurplefy/blurplefy.py @@ -0,0 +1,374 @@ +# Ported for Red v3 from: https://github.com/Rocked03/Blurplefied +# pip install python-resize-image +# pip install pillow + +import discord +from discord.ext import commands +from discord.ext.commands.cooldowns import BucketType +from PIL import Image, ImageEnhance, ImageSequence +from io import BytesIO +import aiohttp +import asyncio +import copy +import datetime +import io +import math +from resizeimage import resizeimage +from redbot.core import Config, checks + +blurple = (114, 137, 218) +blurplehex = 0x7289da +darkblurple = (78, 93, 148) +white = (255, 255, 255) + + +class Blurplefy: + def __init__(self, bot): + self.bot = bot + self.config = Config.get_conf(self, 2778931480, force_registration=True) + + default_guild = { + "role_enabled": False, + "blurple_role": None + } + + self.config.register_guild(**default_guild) + self.session = aiohttp.ClientSession() + + @commands.command() + @checks.admin_or_permissions(manage_roles=True) + async def blurplerole(self, ctx): + """Toggle a role award for having a blurple profile picture.""" + blurple_role_id = await self.config.guild(ctx.guild).blurple_role() + if blurple_role_id is None: + await ctx.send('Enter the role name to award: it needs to be a valid, already existing role.') + + def check(m): + return m.author == ctx.author + + try: + blurple_role = await ctx.bot.wait_for('message', timeout=15.0, check=check) + blurple_role_obj = discord.utils.get(ctx.guild.roles, name=blurple_role.content) + if blurple_role_obj is None: + return await ctx.send('No role with that name.') + await ctx.invoke(self.blurpleroleset, blurple_role_obj) + except asyncio.TimeoutError: + return await ctx.send('No role entered, try again later.') + + role_enabled = await self.config.guild(ctx.guild).role_enabled() + await self.config.guild(ctx.guild).role_enabled.set(not role_enabled) + if not role_enabled: + word = 'enabled' + else: + word = 'disabled' + await ctx.send('Blurple role awarding {}.'.format(word)) + + @commands.command() + @checks.admin_or_permissions(manage_roles=True) + async def blurpleroleset(self, ctx, *, role_name: discord.Role): + """Sets the role to award if blurplerole is on.""" + await self.config.guild(ctx.guild).blurple_role.set(role_name.id) + blurple_role_id = await self.config.guild(ctx.guild).blurple_role() + blurple_role_obj = discord.utils.get(ctx.guild.roles, id=blurple_role_id) + await ctx.send('Blurple award role set to: {}.'.format(blurple_role_obj.name)) + blurple_role_enabled = await self.config.guild(ctx.guild).role_enabled() + if not blurple_role_enabled: + await ctx.invoke(self.blurplerole) + + async def blurplefy(self, ctx, user: discord.Member=None): + """Blurplefy a user or image.""" + picture = None + await ctx.send('{}, starting blurple image analysis.'.format(ctx.message.author.mention)) + link = ctx.message.attachments + if user is None and not link: + picture = ctx.author.avatar_url + else: + if not user: + if len(link) != 0: + for image in link: + picture = image.url + else: + picture = user.avatar_url + try: + async with self.session.request('GET', picture) as r: + response = await r.read() + except ValueError: + await ctx.send('{}, please link a valid image URL.'.format(ctx.author.display_name)) + return + + @commands.command() + @commands.cooldown(rate=1, per=30, type=BucketType.user) + async def blurple(self, ctx, user: discord.Member=None): + """Check a user or uploaded image for blurple content.""" + picture = None + await ctx.send('{}, starting blurple image analysis.'.format(ctx.message.author.mention)) + link = ctx.message.attachments + if user is None and not link: + picture = ctx.author.avatar_url + role_check = True + elif not user: + if len(link) != 0: + for image in link: + picture = image.url + role_check = False + else: + picture = user.avatar_url + role_check = False + + try: + async with self.session.request('GET', picture) as r: + response = await r.read() + except ValueError: + await ctx.send('{}, please link a valid image URL.'.format(ctx.author.display_name)) + return + try: + im = Image.open(BytesIO(response)) + except Exception: + await ctx.send('{}, please link a valid image URL.'.format(ctx.author.display_name)) + return + + im = im.convert('RGBA') + imsize = list(im.size) + impixels = imsize[0]*imsize[1] + # 1250x1250 = 1562500 + maxpixelcount = 1562500 + + if impixels > maxpixelcount: + downsizefraction = math.sqrt(maxpixelcount/impixels) + im = resizeimage.resize_width(im, (imsize[0]*downsizefraction)) + imsize = list(im.size) + impixels = imsize[0]*imsize[1] + await ctx.send('{}, image resized smaller for easier processing.'.format(ctx.message.author.display_name)) + + image = self.blurple_imager(im, imsize) + image = discord.File(fp=image, filename='image.png') + + blurplenesspercentage = round(((nooftotalpixels/noofpixels)*100), 2) + percentblurple = round(((noofblurplepixels/noofpixels)*100), 2) + percentdblurple = round(((noofdarkblurplepixels/noofpixels)*100), 2) + percentwhite = round(((noofwhitepixels/noofpixels)*100), 2) + + embed = discord.Embed(title='', colour=0x7289DA) + embed.add_field(name='Total amount of Blurple', value='{}%'.format(blurplenesspercentage), inline=False) + embed.add_field(name='Blurple (rgb(114, 137, 218))', value='{}%'.format(percentblurple), inline=True) + embed.add_field(name='White (rgb(255, 255, 255))', value='{}\%'.format(percentwhite), inline=True) + embed.add_field(name='Dark Blurple (rgb(78, 93, 148))', value='{}\%'.format(percentdblurple), inline=True) + embed.add_field(name='Guide', value='Blurple, White, Dark Blurple = \nBlurple, White, and Dark Blurple (respectively) \nBlack = Not Blurple, White, or Dark Blurple', inline=False) + embed.set_footer(text='Please note: Discord slightly reduces quality of the images, therefore the percentages may be slightly inaccurate. | Content requested by {}'.format(ctx.author)) + embed.set_image(url='attachment://image.png') + embed.set_thumbnail(url=picture) + await ctx.send(embed=embed, file=image) + + blurple_role_enabled = await self.config.guild(ctx.guild).role_enabled() + if role_check and blurple_role_enabled: + blurple_role_id = await self.config.guild(ctx.guild).blurple_role() + blurple_role_obj = discord.utils.get(ctx.guild.roles, id=blurple_role_id) + if blurplenesspercentage > 75 and picture == ctx.author.avatar_url and blurple_role_obj not in ctx.author.roles and percentblurple > 5: + await ctx.send('{}, as your profile pic has enough blurple (over 75% in total and over 5% blurple), you have recieved the role **{}**!'.format(ctx.message.author.display_name, blurple_role_obj.name)) + await ctx.author.add_roles(blurple_role_obj) + elif picture == ctx.author.avatar_url and blurple_role_obj not in ctx.author.roles: + await ctx.send('{}, your profile pic does not have enough blurple (over 75% in total and over 5% blurple), therefore you are not eligible for the role {}.'.format(ctx.message.author.display_name, blurple_role_obj.name)) + + @commands.command() + @commands.cooldown(rate=1, per=30, type=BucketType.user) + async def blurplefy(self, ctx, user: discord.Member=None): + """Blurplefy a user or uploaded image.""" + picture = None + await ctx.send("{}, starting blurple image analysis.".format(ctx.message.author.mention)) + link = ctx.message.attachments + if user is None and not link: + picture = ctx.author.avatar_url + else: + if not user: + if len(link) != 0: + for image in link: + picture = image.url + else: + picture = user.avatar_url + try: + async with self.session.request('GET', picture) as r: + response = await r.read() + except ValueError: + await ctx.send("{}, please link a valid image URL.".format(ctx.author.display_name)) + return + try: + im = Image.open(BytesIO(response)) + except Exception: + await ctx.send("{}, please link a valid image URL.".format(ctx.author.display_name)) + return + + imsize = list(im.size) + impixels = imsize[0]*imsize[1] + # 1250x1250 = 1562500 + maxpixelcount = 1562500 + + try: + i = im.info["version"] + isgif = True + gifloop = int(im.info["loop"]) + except Exception: + isgif = False + + await ctx.send('{}, image fetched, analyzing image...'.format(ctx.message.author.display_name)) + + if impixels > maxpixelcount: + downsizefraction = math.sqrt(maxpixelcount/impixels) + im = resizeimage.resize_width(im, (imsize[0]*downsizefraction)) + imsize = list(im.size) + impixels = imsize[0]*imsize[1] + await ctx.send('{}, image resized smaller for easier processing'.format(ctx.message.author.display_name)) + + if isgif is False: + image = self.imager(im, imsize) + else: + image = self.gifimager(im, gifloop, imsize) + await ctx.send("{}, image data extracted.".format(ctx.author.display_name)) + if isgif is False: + image = discord.File(fp=image, filename='image.png') + else: + image = discord.File(fp=image, filename='image.gif') + + try: + embed = discord.Embed(title="", colour=0x7289DA) + embed.set_author(name="Blurplefier - makes your image blurple!") + if isgif is False: + embed.set_image(url="attachment://image.png") + else: + embed.set_image(url="attachment://image.gif") + embed.set_footer(text="Please note - This blurplefier is automated and therefore may not always give you the best result. | Content requested by {}".format(ctx.author)) + embed.set_thumbnail(url=picture) + await ctx.send(embed=embed, file=image) + except Exception: + await ctx.send("{}, whoops! It looks like this gif is too big to upload. Try a smaller image (less than 8mb).".format(ctx.author.name)) + + @staticmethod + def blurple_imager(im, imsize): + colourbuffer = 20 + global noofblurplepixels + noofblurplepixels = 0 + global noofwhitepixels + noofwhitepixels = 0 + global noofdarkblurplepixels + noofdarkblurplepixels = 0 + global nooftotalpixels + nooftotalpixels = 0 + global noofpixels + noofpixels = 0 + + blurple = (114, 137, 218) + darkblurple = (78, 93, 148) + white = (255, 255, 255) + + img = im.load() + for x in range(imsize[0]): + i = 1 + for y in range(imsize[1]): + pixel = img[x, y] + check = 1 + checkblurple = 1 + checkwhite = 1 + checkdarkblurple = 1 + for i in range(3): + if not(blurple[i]+colourbuffer > pixel[i] > blurple[i]-colourbuffer): + checkblurple = 0 + if not(darkblurple[i]+colourbuffer > pixel[i] > darkblurple[i]-colourbuffer): + checkdarkblurple = 0 + if not(white[i]+colourbuffer > pixel[i] > white[i]-colourbuffer): + checkwhite = 0 + if checkblurple == 0 and checkdarkblurple == 0 and checkwhite == 0: + check = 0 + if check == 0: + img[x, y] = (0, 0, 0, 255) + if check == 1: + nooftotalpixels += 1 + if checkblurple == 1: + noofblurplepixels += 1 + if checkdarkblurple == 1: + noofdarkblurplepixels += 1 + if checkwhite == 1: + noofwhitepixels += 1 + noofpixels += 1 + + image_file_object = io.BytesIO() + im.save(image_file_object, format='png') + image_file_object.seek(0) + return image_file_object + + @staticmethod + def imager(im, imsize): + im = im.convert(mode='L') + im = ImageEnhance.Contrast(im).enhance(1000) + im = im.convert(mode='RGB') + + img = im.load() + + for x in range(imsize[0]-1): + i = 1 + for y in range(imsize[1]-1): + pixel = img[x, y] + + if pixel != (255, 255, 255): + img[x, y] = (114, 137, 218) + + image_file_object = io.BytesIO() + im.save(image_file_object, format='png') + image_file_object.seek(0) + return image_file_object + + @staticmethod + def gifimager(im, gifloop, imsize): + frames = [frame.copy() for frame in ImageSequence.Iterator(im)] + newgif = [] + + for frame in frames: + frame = frame.convert(mode='L') + frame = ImageEnhance.Contrast(frame).enhance(1000) + frame = frame.convert(mode='RGB') + img = frame.load() + + for x in range(imsize[0]): + i = 1 + for y in range(imsize[1]): + pixel = img[x, y] + if pixel != (255, 255, 255): + img[x, y] = (114, 137, 218) + newgif.append(frame) + + image_file_object = io.BytesIO() + gif = newgif[0] + gif.save(image_file_object, format='gif', save_all=True, append_images=newgif[1:], loop=0) + image_file_object.seek(0) + return image_file_object + + @commands.command() + async def countdown(self, ctx): + """Countdown to Discord's 3rd Anniversary.""" + embed = discord.Embed(name="", colour=0x7289da) + timeleft = datetime.datetime(2018, 5, 13) + datetime.timedelta(hours=7) - datetime.datetime.utcnow() + embed.set_author(name='Time left until Discord\'s 3rd Anniversary') + if int(timeleft.total_seconds()) < 0: + timeleft = datetime.datetime(2019, 5, 13) + datetime.timedelta(hours=7) - datetime.datetime.utcnow() + embed.set_author(name='Time left until Discord\'s 4th Anniversary') + embed.add_field(name='Countdown to midnight, May 13, California time (UTC-7):', value=('{}'.format(self._dynamic_time(countdown_seconds)))) + await ctx.send(embed=embed) + + @staticmethod + def _dynamic_time(time): + m, s = divmod(time, 60) + h, m = divmod(m, 60) + d, h = divmod(h, 24) + + if d > 0: + msg = "{0}d {1}h" + elif d == 0 and h > 0: + msg = "{1}h {2}m" + elif d == 0 and h == 0 and m > 0: + msg = "{2}m {3}s" + elif d == 0 and h == 0 and m == 0 and s > 0: + msg = "{3}s" + else: + msg = "" + return msg.format(d, h, m, s) + + def __unload(self): + self.session.close()