"""This file contains the DailySketchySketch Discord Cog extension. """ import os, discord, requests from discord import NotFound from discord.ext import commands from asyncio import sleep from bs4 import BeautifulSoup as bs from collections import OrderedDict as odict from py_files.sketchy_sketch import sketchySketch class DailySketchySketch(commands.Cog): """ Instantiates the dailySketchySketch class. """ def __init__(self, bot): self.bot = bot ### === BOT SETTINGS === ### self.dir_path = r'C:\Users\haose\OneDrive\Desktop\irl_references' # self.dir_path = r'C:\Users\haose\OneDrive\Desktop\test_refs' self._cache_size = 50 # only channels listed here are authorized to use this class self._authorized_channels = \ { 'hoes-hoes-๐๐๐', 'hoes-hoes-2-๐๐๐', 'testing' } self._reactions = \ { 'crossmark':'โ', 'checkmark':'โ ', 'bookmark':'๐', 'leftarrow':'โฌ ๏ธ', 'rightarrow':'โก๏ธ' } ### === END === ### self._sketch = sketchySketch(self.dir_path) self._secret_channel = None # caches sent messages, saves { message id : file information } self._sent_img = odict() self._sent_bookmarked = odict() self._sent_deleted = odict() @commands.command(name='gimmesum') async def random_pic(self, ctx, num=1): """ Sends a random image to the text channel. The text channel must be an authorized text channel. """ # check if channel is authorized if ctx.message.channel.name not in self._authorized_channels: await ctx.send('No.') return # remove oldest message if (len(self._sent_img) > self._cache_size): self._sent_img.popitem(last=False) # max number of requests max_request = 20 if num > max_request: await ctx.send("That's too much!") num = max_request # send image(s) to the text channel for _ in range(num): file_path = self._sketch.random_pic() prev_msg = await ctx.send(file=discord.File(file_path)) # cache sent image self._sent_img[prev_msg.id] = file_path # add reaction to message await prev_msg.add_reaction(self._reactions.get('crossmark')) await prev_msg.add_reaction(self._reactions.get('bookmark')) await sleep(0.5) @commands.Cog.listener() async def on_reaction_add(self, reaction, user): """ Performs operations based on reactions in the text channel. """ message = reaction.message # check if author is a bot or if reaction is added in authorized channels if ((user == self.bot.user) or (message.channel.name not in self._authorized_channels)): return # === REMOVES USER REACTION ON REACT === # if (message.id in self._sent_bookmarked or message.id in self._sent_deleted): try: await reaction.remove(user) except NotFound: pass # === ADDS IMG ON REACT === # # Adds the image to directory if reacted with checkmark. if (reaction.emoji == self._reactions.get('checkmark')): for msg_attachment in message.attachments: if 'image' in msg_attachment.content_type: ### TODO: Check for duplicate file names ### IDEA: Use message id as file name instead self._sketch.add_pic(msg_attachment.filename) await msg_attachment.save(os.path.join(self.dir_path, msg_attachment.filename)) # === REMOVES IMG ON REACT === # # Only removes images sent in the previous %gimmesum call. if ((message.id in self._sent_img) and (reaction.emoji == self._reactions.get('crossmark'))): # move deleted image to recycling bin delete_imgs = set() for msg_attachment in message.attachments: delete_imgs.add(msg_attachment.filename) self._sketch.remove_pic(self._msg_attachment.filename) # send status message await message.channel.send('Deleted!') await message.delete() # === BOOKMARKS IMG ON REACT === # # Only bookmarks images sent in the previous %gimmesum call. if ((message.id in self._sent_img) and (reaction.emoji == self._reactions.get('bookmark'))): for msg_attachment in message.attachments: self._sketch.bookmark_pic(file_name=msg_attachment.filename) # send status message await message.channel.send('Bookmarked!') # === TURNS PAGES & UNBOOKMARK FOR BOOKMARK EMBEDS === # if (message.id in self._sent_bookmarked): # self._sent_bookmarked: {message.id:[embed_pages, index], ...} # embed_pages = [(file_name, disc_embed), ...] bookmarked = self._sent_bookmarked.get(message.id) embed_pages = bookmarked[0] index = bookmarked[1] # prev page if (reaction.emoji == self._reactions.get('leftarrow')): index = (index - 1) % len(embed_pages) # update embed await message.edit(embed=embed_pages[index][1]) # next page elif (reaction.emoji == self._reactions.get('rightarrow')): index = (index + 1) % len(embed_pages) # update embed await message.edit(embed=embed_pages[index][1]) # unbookmark item elif (reaction.emoji == self._reactions.get('bookmark')): self._sketch.unbookmark_pic(embed_pages[index][0]) # delete item elif (reaction.emoji == self._reactions.get('crossmark')): self._sketch.unbookmark_pic(embed_pages[index][0]) self._sketch.remove_pic(embed_pages[index][0]) # update index self._sent_bookmarked.get(message.id)[1] = index @commands.Cog.listener() async def on_message(self, message): """ Adds the image file sent in the previous message to the directory. """ # check if author is a bot or if message is sent in authorized channels if (message.author.bot or (message.channel.name not in self._authorized_channels)): return # check if message contains an image for msg_attachment in message.attachments: if 'image' in msg_attachment.content_type: # content is image await message.add_reaction(self._reactions.get('checkmark')) @commands.command(name='gimmebookies') async def send_bookmarked(self, ctx): """ Sends bookmarked pictures as embeds. """ # remove oldest message if len(self._sent_bookmarked) > self._cache_size: self._sent_bookmarked.popitem(last=False) # send status message await ctx.send("Retrieving bookmarked images!") # create secret channel - not designed with multiple guilds in mind await self._update_secret_channel(ctx.guild) # get embeds embed_pages = await self._embed_pages(self._sketch.list_bookmarked(), title='Bookmarked Images') if (len(embed_pages) > 0): # send first page of bookmarked sent_msg = await ctx.send(embed=embed_pages[0][1]) await sent_msg.add_reaction(self._reactions.get('leftarrow')) await sent_msg.add_reaction(self._reactions.get('rightarrow')) await sent_msg.add_reaction(self._reactions.get('bookmark')) await sent_msg.add_reaction(self._reactions.get('crossmark')) # update bookmarked dict, stores list of embeds and current page index self._sent_bookmarked[sent_msg.id] = [embed_pages, 0] else: await ctx.send("There are no bookmarked images.") async def _update_secret_channel(self, guild): """ Update the TextChannel for YorBot's secret channel from a given Guild. """ # check if secret channel already exists tc_name = 'yor-secret-channel' guild_tc = guild.text_channels for i in range(len(guild_tc)): if (tc_name == str(guild_tc[i])): self._secret_channel = guild_tc[i] return # new channel settings tc_overwrites = { guild.default_role: discord.PermissionOverwrite(read_messages=False), guild.me: discord.PermissionOverwrite(read_messages=True) } # send temp imgs to retrieve url self._secret_channel = await guild.create_text_channel(name=tc_name, overwrite=tc_overwrites) async def _embed_pages(self, file_list, title): """ Returns a list of formatted embeds from the list of file and title. """ pages = [] # stores bookmarked pages for page_num, file_path in enumerate(file_list): # grab information on file file_name = os.path.split(file_path)[-1] pic_info = self._sketch.pic_info(file_name) # check if image url exists or is valid ### BROKE BEGIN response = requests.get(pic_info['url']) url_dne = False if (response.status_code == 404): url_dne = True elif (response.status_code == 200): err_msg = 'This XML file does not appear to have any style information\ associated with it. The document tree is shown below.' soup = bs(response.content, 'html.parser').find(class_='header') # header class exists if soup: url_dne = True ### BROKE END # update image url if (not pic_info['url'] or url_dne): # url does not exist or is invalid temp_msg = await self._secret_channel.send(file=discord.File(file_path)) # get attachment from temp file img_url = temp_msg.attachments[0].url self._sketch.update_url(file_name, img_url) await sleep(0.5) # update file info pic_info = self._sketch.pic_info(file_name) # format embed embed_desc = f'**rating: **{pic_info["rating"]}/10\n' + \ f'**tags: **{str(pic_info["tags"])}\n' disc_embed = discord.Embed.from_dict({'title':title, 'description':embed_desc}) disc_embed.set_image(url=pic_info['url']) # add footer & color disc_embed.set_footer(text=f'{str(page_num + 1)}/{str(len(file_list))}') disc_embed.color = discord.Color.from_rgb(255, 214, 232) # pastel pink # add to embed pages pages.append((file_name, disc_embed)) return pages @commands.command(name='tag') async def tag_pic(self, ctx): """ Adds the given tag to the previously sent image. """ pass @commands.Cog.listener() async def clean_up(self): pass async def setup(bot): await bot.add_cog(DailySketchySketch(bot))