From 263a4e3c1a85ae723b9037aaf1bbaf6759d63331 Mon Sep 17 00:00:00 2001 From: aikaterna <20862007+aikaterna@users.noreply.github.com> Date: Mon, 21 Sep 2020 21:47:52 -0400 Subject: [PATCH] [RSS] An overengineered color related addition --- rss/color.py | 84 +++++++++++++++++++++++++++++++++++++++++++++++++++ rss/info.json | 2 +- rss/rss.py | 46 ++++++++++++++++++++-------- 3 files changed, 119 insertions(+), 13 deletions(-) create mode 100644 rss/color.py diff --git a/rss/color.py b/rss/color.py new file mode 100644 index 0000000..06004d1 --- /dev/null +++ b/rss/color.py @@ -0,0 +1,84 @@ +import discord +from scipy.spatial import KDTree +import webcolors + + +class Color: + """Helper for color handling.""" + + @staticmethod + async def _color_converter(hex_code_or_color_word: str): + """ + Used for user input on rss embed color + Input: discord.Color name, CSS3 color name, 0xFFFFFF, #FFFFFF, FFFFFF + Output: 0xFFFFFF + """ + # #FFFFFF to 0xFFFFFF + hex_code = hex_code_or_color_word.replace("#", "0x") + + # FFFFFF to 0xFFFFFF + # 0xFFFFFF checking + try: + int(hex_code, 16) + if hex_code[:2] != "0x": + if len(hex_code) == 6: + hex_code = f"0x{hex_code}" + except ValueError: + pass + + # discord.Color checking + if hasattr(discord.Color, hex_code_or_color_word): + hex_code = str(getattr(discord.Color, hex_code_or_color_word)()) + hex_code = hex_code.replace("#", "0x") + return hex_code + + # CSS3 color name checking + try: + hex_code = webcolors.name_to_hex(hex_code_or_color_word, spec="css3") + hex_code = hex_code.replace("#", "0x") + return hex_code + except ValueError: + pass + + return hex_code + + async def _hex_to_css3_name(self, hex_code: str): + """ + Input: 0xFFFFFF + Output: CSS3 color name string closest match + """ + hex_code = await self._hex_validator(hex_code) + rgb_tuple = await self._hex_to_rgb(hex_code) + + names = [] + positions = [] + + for hex, name in webcolors.css3_hex_to_names.items(): + names.append(name) + positions.append(webcolors.hex_to_rgb(hex)) + + spacedb = KDTree(positions) + dist, index = spacedb.query(rgb_tuple) + + return names[index] + + async def _hex_to_rgb(self, hex_code: str): + """ + Input: 0xFFFFFF + Output: (255, 255, 255) + """ + return webcolors.hex_to_rgb(hex_code) + + async def _hex_validator(self, hex_code: str): + """ + Input: 0xFFFFFF + Output: #FFFFFF or None + """ + if hex_code[:2] == "0x": + hex_code = hex_code.replace("0x", "#") + try: + # just a check to make sure it's a real color hex code + hex_code = webcolors.normalize_hex(hex_code) + except ValueError: + hex_code = None + return hex_code diff --git a/rss/info.json b/rss/info.json index 48b1b85..ada3e84 100644 --- a/rss/info.json +++ b/rss/info.json @@ -5,6 +5,6 @@ "description": "Read RSS feeds", "tags": ["rss"], "permissions": ["embed_links"], - "requirements": ["feedparser>=6.0.0", "bs4"], + "requirements": ["bs4", "feedparser>=6.0.0", "scipy", "webcolors==1.3"], "min_bot_version" : "3.4.0" } diff --git a/rss/rss.py b/rss/rss.py index 6bcebbe..0c4f2fc 100644 --- a/rss/rss.py +++ b/rss/rss.py @@ -17,6 +17,7 @@ from urllib.parse import urlparse from redbot.core import checks, commands, Config from redbot.core.utils.chat_formatting import bold, box, escape, pagify +from .color import Color from .quiet_template import QuietTemplate from .rss_feed import RssFeed from .tag_type import INTERNAL_TAGS, VALID_IMAGES, TagType @@ -24,7 +25,7 @@ from .tag_type import INTERNAL_TAGS, VALID_IMAGES, TagType log = logging.getLogger("red.aikaterna.rss") -__version__ = "1.1.0" +__version__ = "1.1.1" class RSS(commands.Cog): @@ -343,12 +344,13 @@ class RSS(commands.Cog): pass @_rss_embed.command(name="color", aliases=["colour"]) - async def _rss_embed_color(self, ctx, feed_name: str, *, color: discord.Color = None): + async def _rss_embed_color(self, ctx, feed_name: str, *, color: str): """ Set an embed color for a feed. Use this command with no color to reset to the default. - `color` must be a hex code like #990000 or a [Discord color name](https://discordpy.readthedocs.io/en/latest/api.html#colour) + `color` must be a hex code like #990000, a [Discord color name](https://discordpy.readthedocs.io/en/latest/api.html#colour), + or a [CSS3 color name](https://www.w3.org/TR/2018/REC-css-color-3-20180619/#svg-color). """ rss_feed = await self.config.channel(ctx.channel).feeds.get_raw(feed_name, default=None) if not rss_feed: @@ -370,13 +372,23 @@ class RSS(commands.Cog): f"{embed_state_message}The color for {bold(feed_name)} has been reset. " "Use this command with a color argument to set a color for this feed." ) - return - async with self.config.channel(ctx.channel).feeds() as feed_data: - feed_data[feed_name]["embed_color"] = f"0x{str(color).lstrip('#')}" + color = color.replace(" ", "_") + hex_code = await Color()._color_converter(color) + user_facing_hex = hex_code.replace("0x", "#") + color_name = await Color()._hex_to_css3_name(hex_code) - await ctx.send(f"Embed color for {bold(feed_name)} set to {str(color)}.") + # 0xFFFFFF actually doesn't show up as white in an embed + # so let's make it close enough to count + if hex_code == "0xFFFFFF": + hex_code = "0xFFFFFE" + + async with self.config.channel(ctx.channel).feeds() as feed_data: + # data is always a 0xFFFFFF style value + feed_data[feed_name]["embed_color"] = hex_code + + await ctx.send(f"Embed color for {bold(feed_name)} set to {user_facing_hex} ({color_name}).") @_rss_embed.command(name="image") async def _rss_embed_image(self, ctx, feed_name: str, image_tag_name: str = None): @@ -563,21 +575,31 @@ class RSS(commands.Cog): return space = "\N{SPACE}" - embed_toggle = "[ ] Embed off" if not rss_feed["embed"] else "[X] Embed on" + embed_toggle = f"[ ] Embed:{space*16}Off" if not rss_feed["embed"] else f"[X] Embed:{space*16}On" embed_image = ( - "[ ] Embed image off" + f"[ ] Embed image tag:{space*6}None" if not rss_feed["embed_image"] else f"[X] Embed image tag:{space*6}${rss_feed['embed_image']}" ) embed_thumbnail = ( - "[ ] Embed thumbnail off" + f"[ ] Embed thumbnail tag:{space*2}None" if not rss_feed["embed_thumbnail"] else f"[X] Embed thumbnail tag:{space*2}${rss_feed['embed_thumbnail']}" ) + hex_color = rss_feed.get("embed_color", None) + if hex_color: + color_name = await Color()._hex_to_css3_name(hex_color) + hex_color = hex_color.lstrip("0x") + embed_color = ( + f"[ ] Embed hex color:{space*6}None" + if not hex_color + else f"[X] Embed hex color:{space*6}{hex_color} ({color_name})" + ) - template_info = f"{embed_toggle}\n{embed_image}\n{embed_thumbnail}" + embed_settings = f"{embed_toggle}\n{embed_color}\n{embed_image}\n{embed_thumbnail}" rss_template = rss_feed["template"].replace("\n", "\\n").replace("\t", "\\t") - await ctx.send(f"Template for {bold(feed_name)}:\n\n`{rss_template}`\n{box(template_info, lang='ini')}") + + await ctx.send(f"Template for {bold(feed_name)}:\n\n`{rss_template}`\n{box(embed_settings, lang='ini')}") @rss.command(name="template") async def _rss_template(self, ctx, feed_name: str, *, template: str):