From 1f5fadec78c8361f39ae5614c461350051f06adf Mon Sep 17 00:00:00 2001 From: veeru153 <32760644+veeru153@users.noreply.github.com> Date: Fri, 9 Dec 2022 22:52:38 +0530 Subject: [PATCH 1/4] implemented embedraw --- cogs/embeds.py | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/cogs/embeds.py b/cogs/embeds.py index 4963045..87ab0d2 100644 --- a/cogs/embeds.py +++ b/cogs/embeds.py @@ -1,24 +1,33 @@ import discord from discord.ext import commands +import json class Embeds(commands.Cog): + def __init__(self, bot: commands.Bot) -> None: self.bot = bot - + @commands.hybrid_command() async def embed(self, ctx: commands.Context): """ Interactively construct a Discord embed. """ - raise NotImplementedError('Command requires implementation and permission set-up.') - + raise NotImplementedError( + 'Command requires implementation and permission set-up.') + @commands.command() - async def embedraw(self, ctx: commands.Context): + async def embedraw(self, ctx: commands.Context, *, embedData): """ Create a Discord embed via raw JSON input. """ - raise NotImplementedError('Command requires implementation and permission set-up.') + try: + embedBody = json.loads(embedData) + embed = discord.Embed.from_dict(embedBody) + await ctx.send(embed=embed) + except Exception as ex: + print(ex) + await ctx.send("An Error Occurred") async def setup(bot: commands.Bot): From 9d5b91f3843623cbe9812ff0c5ea2fa66d5cda54 Mon Sep 17 00:00:00 2001 From: veeru153 <32760644+veeru153@users.noreply.github.com> Date: Sat, 10 Dec 2022 16:52:19 +0530 Subject: [PATCH 2/4] Implemented embed --- cogs/embeds.py | 119 ++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 114 insertions(+), 5 deletions(-) diff --git a/cogs/embeds.py b/cogs/embeds.py index 87ab0d2..758be74 100644 --- a/cogs/embeds.py +++ b/cogs/embeds.py @@ -1,6 +1,7 @@ import discord from discord.ext import commands import json +import re class Embeds(commands.Cog): @@ -13,17 +14,125 @@ async def embed(self, ctx: commands.Context): """ Interactively construct a Discord embed. """ - raise NotImplementedError( - 'Command requires implementation and permission set-up.') + + def check(m: discord.Message): + return m.channel == ctx.channel and m.author == ctx.author + + def is_skip(msg: discord.Message): + return msg.content.strip().lower() == "skip" + + def is_done(msg: discord.Message): + return msg.content.strip().lower() == "done" + + + async def handle_color(embed: discord.Embed): + while True: + try: + color_msg: discord.Message = await self.bot.wait_for("message", check=check, timeout=timeout) + if is_skip(color_msg): + break + color_str = color_msg.content + embed.color = discord.Color.from_str(color_str) + break + except: + await ctx.send("Invalid Color. Please provide the color in hex format (e.g. `#123456`). Use `skip` to skip this.") + + async def handle_img(embed: discord.Embed): + img_url_msg: discord.Message = await self.bot.wait_for("message", check=check, timeout=timeout) + if is_skip(img_url_msg): return + img_url = img_url_msg.content.strip() + if img_url.startswith("<"): img_url = img_url[1:] + if img_url.endswith(">"): img_url = img_url[:-1] + try: + embed.set_image(url=img_url) + except: + print("Error setting image") + + async def handle_fields(embed: discord.Embed): + while True: + await ctx.send("You are now adding another field. Please use the following format: `\"Title\" \"Value\"`. You can type `done` to complete the embed.") + field_msg = await self.bot.wait_for("message", check=check, timeout=timeout) + if is_done(field_msg): + break + [title, value] = await handle_field(field_msg) + title = title[:256] + value = value[:1024] + embed.add_field(name=title, value=value) + + async def handle_field(field_msg: discord.Message): + output = [] + current = None + field_tokens = re.split(" +", field_msg.content.strip()) + + for token in field_tokens: + if token.startswith("\"") and current is None: + current = "" + + if current is not None: + current = current + token + " " + + if token.endswith("\"") and current is not None: + index = len(token) - 2 + if index < 0 or token[index] != "\\": + data = current.strip() + data = data[1:-1] + output.append(data) + current = None + + return output + + async def handle_channel(): + while True: + try: + channel_msg: discord.Message = await self.bot.wait_for("message", check=check, timeout=timeout) + if len(channel_msg.channel_mentions) is 0: + await ctx.send("Could not find that channel") + else: + channel = channel_msg.channel_mentions[0] + return channel + except TimeoutError: + await ctx.send("Reached Timeout. Discarding Embed") + break + + async def handle_replacement_msg(embed: discord.Embed, channel: discord.TextChannel): + msg_id_msg: discord.Message = await self.bot.wait_for("message", check=check, timeout=timeout) + msg_id = msg_id_msg.content.strip() + try: + msg = await channel.fetch_message(msg_id) + await msg.edit(embed=embed) + except: + await channel.send(embed=embed) + + timeout = 60 * 1000 + embed = discord.Embed() + + await ctx.send("Please type the description of the embed. Use `skip` to skip this.") + desc: discord.Message = await self.bot.wait_for("message", check=check, timeout=timeout) + if not is_skip(desc): embed.description = desc.content.strip()[:4096] + + await ctx.send("Please provide the colour in hex format (e.g. `#123456`). Use `skip` to skip this.") + await handle_color(embed) + + await ctx.send("Please provide the URL for the image you would like to display. Use `skip` to skip this.") + await handle_img(embed) + + await handle_fields(embed) + + await ctx.send("Which channel should the embed be sent in?") + channel = await handle_channel() + + await ctx.send("Should this embed replace any existing message in that channel? If yes, type the message ID, otherwise type anything else.") + await handle_replacement_msg(embed, channel) + @commands.command() - async def embedraw(self, ctx: commands.Context, *, embedData): + async def embedraw(self, ctx: commands.Context, *, embed_data): """ Create a Discord embed via raw JSON input. """ try: - embedBody = json.loads(embedData) - embed = discord.Embed.from_dict(embedBody) + embed_body = json.loads(embed_data) + embed = discord.Embed.from_dict(embed_body) await ctx.send(embed=embed) except Exception as ex: print(ex) From c5b693a25adc48cee3207a554973a8db6bc79991 Mon Sep 17 00:00:00 2001 From: veeru153 <32760644+veeru153@users.noreply.github.com> Date: Fri, 27 Jan 2023 19:23:50 +0530 Subject: [PATCH 3/4] updated embed --- cogs/embeds.py | 120 +++++++++++++++++++++++++------------------------ 1 file changed, 62 insertions(+), 58 deletions(-) diff --git a/cogs/embeds.py b/cogs/embeds.py index 758be74..d1727de 100644 --- a/cogs/embeds.py +++ b/cogs/embeds.py @@ -1,9 +1,10 @@ +import asyncio +import logging import discord from discord.ext import commands import json import re - class Embeds(commands.Cog): def __init__(self, bot: commands.Bot) -> None: @@ -15,6 +16,9 @@ async def embed(self, ctx: commands.Context): Interactively construct a Discord embed. """ + log = logging.getLogger() + INPUT_TIMED_OUT = "Input timed out." + def check(m: discord.Message): return m.channel == ctx.channel and m.author == ctx.author @@ -24,6 +28,13 @@ def is_skip(msg: discord.Message): def is_done(msg: discord.Message): return msg.content.strip().lower() == "done" + async def handle_desc(embed: discord.Embed): + try: + desc: discord.Message = await self.bot.wait_for("message", check=check, timeout=timeout) + if not is_skip(desc): + embed.description = desc.content.strip()[:4096] + except asyncio.TimeoutError: + await ctx.send(INPUT_TIMED_OUT) async def handle_color(embed: discord.Embed): while True: @@ -34,54 +45,45 @@ async def handle_color(embed: discord.Embed): color_str = color_msg.content embed.color = discord.Color.from_str(color_str) break - except: + except ValueError: await ctx.send("Invalid Color. Please provide the color in hex format (e.g. `#123456`). Use `skip` to skip this.") + except asyncio.TimeoutError: + await ctx.send(INPUT_TIMED_OUT) async def handle_img(embed: discord.Embed): - img_url_msg: discord.Message = await self.bot.wait_for("message", check=check, timeout=timeout) - if is_skip(img_url_msg): return - img_url = img_url_msg.content.strip() - if img_url.startswith("<"): img_url = img_url[1:] - if img_url.endswith(">"): img_url = img_url[:-1] try: + img_url_msg: discord.Message = await self.bot.wait_for("message", check=check, timeout=timeout) + if is_skip(img_url_msg): + return + img_url = img_url_msg.content.strip() + if img_url.startswith("<"): + img_url = img_url[1:] + if img_url.endswith(">"): + img_url = img_url[:-1] embed.set_image(url=img_url) - except: - print("Error setting image") + except asyncio.TimeoutError: + await ctx.send(INPUT_TIMED_OUT) + except Exception as ex: + log.error("[Embeds.handle_img] Error setting image", ex, exc_info=1) async def handle_fields(embed: discord.Embed): while True: - await ctx.send("You are now adding another field. Please use the following format: `\"Title\" \"Value\"`. You can type `done` to complete the embed.") - field_msg = await self.bot.wait_for("message", check=check, timeout=timeout) - if is_done(field_msg): - break - [title, value] = await handle_field(field_msg) - title = title[:256] - value = value[:1024] - embed.add_field(name=title, value=value) - - async def handle_field(field_msg: discord.Message): - output = [] - current = None - field_tokens = re.split(" +", field_msg.content.strip()) - - for token in field_tokens: - if token.startswith("\"") and current is None: - current = "" - - if current is not None: - current = current + token + " " - - if token.endswith("\"") and current is not None: - index = len(token) - 2 - if index < 0 or token[index] != "\\": - data = current.strip() - data = data[1:-1] - output.append(data) - current = None - - return output - - async def handle_channel(): + try: + await ctx.send("You are now adding another field. Please use the following format: `\"Title\" \"Value\"`. You can type `done` to complete the embed.") + field_msg = await self.bot.wait_for("message", check=check, timeout=timeout) + if is_done(field_msg): + break + match = re.search('\"(.+?)\" +\"(.+?)\"', field_msg.content.strip()) + if match is None: + await ctx.send("Invalid Format") + else: + title = match.group(1)[:256] + value = match.group(2)[:1024] + embed.add_field(name=title, value=value) + except asyncio.TimeoutError: + await ctx.send(INPUT_TIMED_OUT) + + async def get_channel(): while True: try: channel_msg: discord.Message = await self.bot.wait_for("message", check=check, timeout=timeout) @@ -90,39 +92,41 @@ async def handle_channel(): else: channel = channel_msg.channel_mentions[0] return channel - except TimeoutError: - await ctx.send("Reached Timeout. Discarding Embed") + except asyncio.TimeoutError: + await ctx.send(INPUT_TIMED_OUT) break - async def handle_replacement_msg(embed: discord.Embed, channel: discord.TextChannel): - msg_id_msg: discord.Message = await self.bot.wait_for("message", check=check, timeout=timeout) - msg_id = msg_id_msg.content.strip() + async def send_or_update_embed(embed: discord.Embed, channel: discord.TextChannel): try: + msg_id_msg: discord.Message = await self.bot.wait_for("message", check=check, timeout=timeout) + msg_id = msg_id_msg.content.strip() msg = await channel.fetch_message(msg_id) await msg.edit(embed=embed) - except: - await channel.send(embed=embed) + except discord.HTTPException as ex: + # TODO: temp. Need to confirm how to handle this correctly. + await ctx.send("An error occurred while updating the embed.") + log.error("Error updating embed", ex, exc_info=1) + except Exception as ex: + try: + await channel.send(embed=embed) + except Exception as ex: + await ctx.send("An error occurred while sending the embed.") + log.error("[Embeds.send_or_update_embed]", ex, exc_info=1) - timeout = 60 * 1000 + timeout = 60 embed = discord.Embed() await ctx.send("Please type the description of the embed. Use `skip` to skip this.") - desc: discord.Message = await self.bot.wait_for("message", check=check, timeout=timeout) - if not is_skip(desc): embed.description = desc.content.strip()[:4096] - + await handle_desc(embed) await ctx.send("Please provide the colour in hex format (e.g. `#123456`). Use `skip` to skip this.") await handle_color(embed) - await ctx.send("Please provide the URL for the image you would like to display. Use `skip` to skip this.") await handle_img(embed) - await handle_fields(embed) - await ctx.send("Which channel should the embed be sent in?") - channel = await handle_channel() - + channel = await get_channel() await ctx.send("Should this embed replace any existing message in that channel? If yes, type the message ID, otherwise type anything else.") - await handle_replacement_msg(embed, channel) + await send_or_update_embed(embed, channel) @commands.command() From d76751ac4705f0a427f95ee6caf7a3cb72564f33 Mon Sep 17 00:00:00 2001 From: veeru153 <32760644+veeru153@users.noreply.github.com> Date: Mon, 30 Jan 2023 00:58:19 +0530 Subject: [PATCH 4/4] Code review changes --- cogs/embeds.py | 130 ++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 108 insertions(+), 22 deletions(-) diff --git a/cogs/embeds.py b/cogs/embeds.py index d1727de..0798c1c 100644 --- a/cogs/embeds.py +++ b/cogs/embeds.py @@ -9,6 +9,9 @@ class Embeds(commands.Cog): def __init__(self, bot: commands.Bot) -> None: self.bot = bot + self.DESC_MAX_LEN = 4096 + self.FIELD_TITLE_MAX_LEN = 256 + self.FIELD_VALUE_MAX_LEN = 1024 @commands.hybrid_command() async def embed(self, ctx: commands.Context): @@ -32,7 +35,7 @@ async def handle_desc(embed: discord.Embed): try: desc: discord.Message = await self.bot.wait_for("message", check=check, timeout=timeout) if not is_skip(desc): - embed.description = desc.content.strip()[:4096] + embed.description = desc.content.strip()[:self.DESC_MAX_LEN] except asyncio.TimeoutError: await ctx.send(INPUT_TIMED_OUT) @@ -74,11 +77,11 @@ async def handle_fields(embed: discord.Embed): if is_done(field_msg): break match = re.search('\"(.+?)\" +\"(.+?)\"', field_msg.content.strip()) - if match is None: + if match == None: await ctx.send("Invalid Format") else: - title = match.group(1)[:256] - value = match.group(2)[:1024] + title = match.group(1)[:self.FIELD_TITLE_MAX_LEN] + value = match.group(2)[:self.FIELD_VALUE_MAX_LEN] embed.add_field(name=title, value=value) except asyncio.TimeoutError: await ctx.send(INPUT_TIMED_OUT) @@ -87,7 +90,7 @@ async def get_channel(): while True: try: channel_msg: discord.Message = await self.bot.wait_for("message", check=check, timeout=timeout) - if len(channel_msg.channel_mentions) is 0: + if len(channel_msg.channel_mentions) == 0: await ctx.send("Could not find that channel") else: channel = channel_msg.channel_mentions[0] @@ -100,18 +103,21 @@ async def send_or_update_embed(embed: discord.Embed, channel: discord.TextChanne try: msg_id_msg: discord.Message = await self.bot.wait_for("message", check=check, timeout=timeout) msg_id = msg_id_msg.content.strip() - msg = await channel.fetch_message(msg_id) - await msg.edit(embed=embed) + matches = re.match('[0-9]+', msg_id) + if matches: + msg = await channel.fetch_message(msg_id) + await msg.edit(embed=embed) + return + await channel.send(embed=embed) + except discord.NotFound as ex: + await ctx.send("No message with this ID was found.") except discord.HTTPException as ex: # TODO: temp. Need to confirm how to handle this correctly. - await ctx.send("An error occurred while updating the embed.") - log.error("Error updating embed", ex, exc_info=1) + await ctx.send("An error occurred while updating/sending the embed.") + log.error("Error updating/sending embed: %s", ex, exc_info=1) except Exception as ex: - try: - await channel.send(embed=embed) - except Exception as ex: - await ctx.send("An error occurred while sending the embed.") - log.error("[Embeds.send_or_update_embed]", ex, exc_info=1) + await ctx.send("An error occurred.") + log.error(ex) timeout = 60 embed = discord.Embed() @@ -125,23 +131,103 @@ async def send_or_update_embed(embed: discord.Embed, channel: discord.TextChanne await handle_fields(embed) await ctx.send("Which channel should the embed be sent in?") channel = await get_channel() + member_perms = channel.permissions_for(ctx.author) + if not (member_perms.send_messages and member_perms.embed_links): + await ctx.send("Missing channel permissions.") + return await ctx.send("Should this embed replace any existing message in that channel? If yes, type the message ID, otherwise type anything else.") await send_or_update_embed(embed, channel) @commands.command() - async def embedraw(self, ctx: commands.Context, *, embed_data): + async def embedraw(self, ctx: commands.Context, type, channel, *argv): """ Create a Discord embed via raw JSON input. """ - try: - embed_body = json.loads(embed_data) - embed = discord.Embed.from_dict(embed_body) - await ctx.send(embed=embed) - except Exception as ex: - print(ex) - await ctx.send("An Error Occurred") + # NOTE: This breaks if we miss type or channel as it tries to parse the JSON body in those and breaks. IDK how to fix. + + log = logging.getLogger() + embed_data = None + msg_id = None + print(type, channel, argv) + + # try: + + # if type == "create": + # if len(argv) != 1: + # await ctx.send('Invalid arguments. Please use the following format: `create <#channel> `.') + # return + # embed_data = argv[0] + # elif type == "update": + # if len(argv) != 2: + # await ctx.send('Invalid arguments. Please use the following format: `update <#channel> `.') + # return + # msg_id = argv[0].strip() + # embed_data = argv[1] + # else: + # await ctx.send('Unidentified type. Please use either `create` or `update` as the type.') + # return + + # member_perms = channel.permissions_for(ctx.author) + # if not (member_perms.send_messages and member_perms.embed_links): + # await ctx.send("Missing channel permissions.") + # return + + # raw = json.loads(embed_data) + # embed = discord.Embed() + + # if 'description' in raw: + # embed.description = raw['description'].strip()[:self.DESC_MAX_LEN] + + # if 'colour' in raw: + # try: + # embed.color = embed.color = discord.Color.from_str(raw['colour']) + # except ValueError as ex: + # await ctx.send('Invalid Color. Please provide the color in hex format (e.g. `#123456`).') + # return + + # if 'image_url' in raw: + # img_url = raw['image_url'].content.strip() + # if img_url.startswith("<"): + # img_url = img_url[1:] + # if img_url.endswith(">"): + # img_url = img_url[:-1] + # embed.set_image(url=img_url) + + # if 'fields' in raw: + # fields = raw['fields'] + # if len(fields) > 20: + # await ctx.send("") + # return + + # for tuple in fields: + # if len(tuple) > 2: + # continue + # title, value = tuple + # title = title[:self.FIELD_TITLE_MAX_LEN] + # value = value[:self.FIELD_VALUE_MAX_LEN] + # embed.add_field(name=title, value=value) + + # if type == "update": + # msg = await channel.fetch_message(msg_id) + # await msg.edit(embed=embed) + # return + + # await channel.send(embed=embed) + + # except commands.errors.MissingRequiredArgument as ex: + # await ctx.send('Invalid arguments.') + # except commands.errors.ChannelNotFound as ex: + # await ctx.send('Channel not found.') + # except discord.NotFound as ex: + # await ctx.send('Message with that ID was not found in the target channel.') + # except discord.HTTPException as ex: + # await ctx.send("An error occurred while updating/sending the embed.") + # log.error("Error updating/sending embed: %s", ex, exc_info=1) + # except ex: + # await ctx.send('An error occurred.') + # log.error(ex) async def setup(bot: commands.Bot): await bot.add_cog(Embeds(bot)) \ No newline at end of file