aboutsummaryrefslogtreecommitdiffstats
path: root/bot/exts/pride
diff options
context:
space:
mode:
Diffstat (limited to 'bot/exts/pride')
-rw-r--r--bot/exts/pride/__init__.py0
-rw-r--r--bot/exts/pride/drag_queen_name.py33
-rw-r--r--bot/exts/pride/pride_anthem.py58
-rw-r--r--bot/exts/pride/pride_avatar.py145
-rw-r--r--bot/exts/pride/pride_facts.py107
5 files changed, 343 insertions, 0 deletions
diff --git a/bot/exts/pride/__init__.py b/bot/exts/pride/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/bot/exts/pride/__init__.py
diff --git a/bot/exts/pride/drag_queen_name.py b/bot/exts/pride/drag_queen_name.py
new file mode 100644
index 00000000..43813fbd
--- /dev/null
+++ b/bot/exts/pride/drag_queen_name.py
@@ -0,0 +1,33 @@
+import json
+import logging
+import random
+from pathlib import Path
+
+from discord.ext import commands
+
+log = logging.getLogger(__name__)
+
+
+class DragNames(commands.Cog):
+ """Gives a random drag queen name!"""
+
+ def __init__(self, bot: commands.Bot):
+ self.bot = bot
+ self.names = self.load_names()
+
+ @staticmethod
+ def load_names() -> list:
+ """Loads a list of drag queen names."""
+ with open(Path("bot/resources/pride/drag_queen_names.json"), "r", encoding="utf-8") as f:
+ return json.load(f)
+
+ @commands.command(name="dragname", aliases=["dragqueenname", "queenme"])
+ async def dragname(self, ctx: commands.Context) -> None:
+ """Sends a message with a drag queen name."""
+ await ctx.send(random.choice(self.names))
+
+
+def setup(bot: commands.Bot) -> None:
+ """Cog loader for drag queen name generator."""
+ bot.add_cog(DragNames(bot))
+ log.info("Drag queen name generator cog loaded!")
diff --git a/bot/exts/pride/pride_anthem.py b/bot/exts/pride/pride_anthem.py
new file mode 100644
index 00000000..b0c6d34e
--- /dev/null
+++ b/bot/exts/pride/pride_anthem.py
@@ -0,0 +1,58 @@
+import json
+import logging
+import random
+from pathlib import Path
+
+from discord.ext import commands
+
+log = logging.getLogger(__name__)
+
+
+class PrideAnthem(commands.Cog):
+ """Embed a random youtube video for a gay anthem!"""
+
+ def __init__(self, bot: commands.Bot):
+ self.bot = bot
+ self.anthems = self.load_vids()
+
+ def get_video(self, genre: str = None) -> dict:
+ """
+ Picks a random anthem from the list.
+
+ If `genre` is supplied, it will pick from videos attributed with that genre.
+ If none can be found, it will log this as well as provide that information to the user.
+ """
+ if not genre:
+ return random.choice(self.anthems)
+ else:
+ songs = [song for song in self.anthems if genre.casefold() in song["genre"]]
+ try:
+ return random.choice(songs)
+ except IndexError:
+ log.info("No videos for that genre.")
+
+ @staticmethod
+ def load_vids() -> list:
+ """Loads a list of videos from the resources folder as dictionaries."""
+ with open(Path("bot/resources/pride/anthems.json"), "r", encoding="utf-8") as f:
+ anthems = json.load(f)
+ return anthems
+
+ @commands.command(name="prideanthem", aliases=["anthem", "pridesong"])
+ async def prideanthem(self, ctx: commands.Context, genre: str = None) -> None:
+ """
+ Sends a message with a video of a random pride anthem.
+
+ If `genre` is supplied, it will select from that genre only.
+ """
+ anthem = self.get_video(genre)
+ if anthem:
+ await ctx.send(anthem["url"])
+ else:
+ await ctx.send("I couldn't find a video, sorry!")
+
+
+def setup(bot: commands.Bot) -> None:
+ """Cog loader for pride anthem."""
+ bot.add_cog(PrideAnthem(bot))
+ log.info("Pride anthems cog loaded!")
diff --git a/bot/exts/pride/pride_avatar.py b/bot/exts/pride/pride_avatar.py
new file mode 100644
index 00000000..85e49d5c
--- /dev/null
+++ b/bot/exts/pride/pride_avatar.py
@@ -0,0 +1,145 @@
+import logging
+from io import BytesIO
+from pathlib import Path
+
+import discord
+from PIL import Image, ImageDraw
+from discord.ext import commands
+
+from bot.constants import Colours
+
+log = logging.getLogger(__name__)
+
+OPTIONS = {
+ "agender": "agender",
+ "androgyne": "androgyne",
+ "androgynous": "androgyne",
+ "aromantic": "aromantic",
+ "aro": "aromantic",
+ "ace": "asexual",
+ "asexual": "asexual",
+ "bigender": "bigender",
+ "bisexual": "bisexual",
+ "bi": "bisexual",
+ "demiboy": "demiboy",
+ "demigirl": "demigirl",
+ "demi": "demisexual",
+ "demisexual": "demisexual",
+ "gay": "gay",
+ "lgbt": "gay",
+ "queer": "gay",
+ "homosexual": "gay",
+ "fluid": "genderfluid",
+ "genderfluid": "genderfluid",
+ "genderqueer": "genderqueer",
+ "intersex": "intersex",
+ "lesbian": "lesbian",
+ "non-binary": "nonbinary",
+ "enby": "nonbinary",
+ "nb": "nonbinary",
+ "nonbinary": "nonbinary",
+ "omnisexual": "omnisexual",
+ "omni": "omnisexual",
+ "pansexual": "pansexual",
+ "pan": "pansexual",
+ "pangender": "pangender",
+ "poly": "polysexual",
+ "polysexual": "polysexual",
+ "polyamory": "polyamory",
+ "polyamorous": "polyamory",
+ "transgender": "transgender",
+ "trans": "transgender",
+ "trigender": "trigender"
+}
+
+
+class PrideAvatar(commands.Cog):
+ """Put an LGBT spin on your avatar!"""
+
+ def __init__(self, bot: commands.Bot):
+ self.bot = bot
+
+ @staticmethod
+ def crop_avatar(avatar: Image) -> Image:
+ """This crops the avatar into a circle."""
+ mask = Image.new("L", avatar.size, 0)
+ draw = ImageDraw.Draw(mask)
+ draw.ellipse((0, 0) + avatar.size, fill=255)
+ avatar.putalpha(mask)
+ return avatar
+
+ @staticmethod
+ def crop_ring(ring: Image, px: int) -> Image:
+ """This crops the ring into a circle."""
+ mask = Image.new("L", ring.size, 0)
+ draw = ImageDraw.Draw(mask)
+ draw.ellipse((0, 0) + ring.size, fill=255)
+ draw.ellipse((px, px, 1024-px, 1024-px), fill=0)
+ ring.putalpha(mask)
+ return ring
+
+ @commands.group(aliases=["avatarpride", "pridepfp", "prideprofile"], invoke_without_command=True)
+ async def prideavatar(self, ctx: commands.Context, option: str = "lgbt", pixels: int = 64) -> None:
+ """
+ This surrounds an avatar with a border of a specified LGBT flag.
+
+ This defaults to the LGBT rainbow flag if none is given.
+ The amount of pixels can be given which determines the thickness of the flag border.
+ This has a maximum of 512px and defaults to a 64px border.
+ The full image is 1024x1024.
+ """
+ pixels = 0 if pixels < 0 else 512 if pixels > 512 else pixels
+
+ option = option.lower()
+
+ if option not in OPTIONS.keys():
+ return await ctx.send("I don't have that flag!")
+
+ flag = OPTIONS[option]
+
+ async with ctx.typing():
+
+ # Get avatar bytes
+ image_bytes = await ctx.author.avatar_url.read()
+ avatar = Image.open(BytesIO(image_bytes))
+ avatar = avatar.convert("RGBA").resize((1024, 1024))
+
+ avatar = self.crop_avatar(avatar)
+
+ ring = Image.open(Path(f"bot/resources/pride/flags/{flag}.png")).resize((1024, 1024))
+ ring = ring.convert("RGBA")
+ ring = self.crop_ring(ring, pixels)
+
+ avatar.alpha_composite(ring, (0, 0))
+ bufferedio = BytesIO()
+ avatar.save(bufferedio, format="PNG")
+ bufferedio.seek(0)
+
+ file = discord.File(bufferedio, filename="pride_avatar.png") # Creates file to be used in embed
+ embed = discord.Embed(
+ name="Your Lovely Pride Avatar",
+ description=f"Here is your lovely avatar, surrounded by\n a beautiful {option} flag. Enjoy :D"
+ )
+ embed.set_image(url="attachment://pride_avatar.png")
+ embed.set_footer(text=f"Made by {ctx.author.display_name}", icon_url=ctx.author.avatar_url)
+
+ await ctx.send(file=file, embed=embed)
+
+ @prideavatar.command()
+ async def flags(self, ctx: commands.Context) -> None:
+ """This lists the flags that can be used with the prideavatar command."""
+ choices = sorted(set(OPTIONS.values()))
+ options = "• " + "\n• ".join(choices)
+ embed = discord.Embed(
+ title="I have the following flags:",
+ description=options,
+ colour=Colours.soft_red
+ )
+
+ await ctx.send(embed=embed)
+
+
+def setup(bot: commands.Bot) -> None:
+ """Cog load."""
+ bot.add_cog(PrideAvatar(bot))
+ log.info("PrideAvatar cog loaded")
diff --git a/bot/exts/pride/pride_facts.py b/bot/exts/pride/pride_facts.py
new file mode 100644
index 00000000..2db8f5c2
--- /dev/null
+++ b/bot/exts/pride/pride_facts.py
@@ -0,0 +1,107 @@
+import json
+import logging
+import random
+from datetime import datetime
+from pathlib import Path
+from typing import Union
+
+import dateutil.parser
+import discord
+from discord.ext import commands
+
+from bot.constants import Channels, Colours, Month
+from bot.utils.decorators import seasonal_task
+
+log = logging.getLogger(__name__)
+
+Sendable = Union[commands.Context, discord.TextChannel]
+
+
+class PrideFacts(commands.Cog):
+ """Provides a new fact every day during the Pride season!"""
+
+ def __init__(self, bot: commands.Bot):
+ self.bot = bot
+ self.facts = self.load_facts()
+
+ self.daily_fact_task = self.bot.loop.create_task(self.send_pride_fact_daily())
+
+ @staticmethod
+ def load_facts() -> dict:
+ """Loads a dictionary of years mapping to lists of facts."""
+ with open(Path("bot/resources/pride/facts.json"), "r", encoding="utf-8") as f:
+ return json.load(f)
+
+ @seasonal_task(Month.june)
+ async def send_pride_fact_daily(self) -> None:
+ """Background task to post the daily pride fact every day."""
+ await self.bot.wait_until_ready()
+
+ channel = self.bot.get_channel(Channels.seasonalbot_commands)
+ await self.send_select_fact(channel, datetime.utcnow())
+
+ async def send_random_fact(self, ctx: commands.Context) -> None:
+ """Provides a fact from any previous day, or today."""
+ now = datetime.utcnow()
+ previous_years_facts = (self.facts[x] for x in self.facts.keys() if int(x) < now.year)
+ current_year_facts = self.facts.get(str(now.year), [])[:now.day]
+ previous_facts = current_year_facts + [x for y in previous_years_facts for x in y]
+ try:
+ await ctx.send(embed=self.make_embed(random.choice(previous_facts)))
+ except IndexError:
+ await ctx.send("No facts available")
+
+ async def send_select_fact(self, target: Sendable, _date: Union[str, datetime]) -> None:
+ """Provides the fact for the specified day, if the day is today, or is in the past."""
+ now = datetime.utcnow()
+ if isinstance(_date, str):
+ try:
+ date = dateutil.parser.parse(_date, dayfirst=False, yearfirst=False, fuzzy=True)
+ except (ValueError, OverflowError) as err:
+ await target.send(f"Error parsing date: {err}")
+ return
+ else:
+ date = _date
+ if date.year < now.year or (date.year == now.year and date.day <= now.day):
+ try:
+ await target.send(embed=self.make_embed(self.facts[str(date.year)][date.day - 1]))
+ except KeyError:
+ await target.send(f"The year {date.year} is not yet supported")
+ return
+ except IndexError:
+ await target.send(f"Day {date.day} of {date.year} is not yet support")
+ return
+ else:
+ await target.send("The fact for the selected day is not yet available.")
+
+ @commands.command(name="pridefact", aliases=["pridefacts"])
+ async def pridefact(self, ctx: commands.Context) -> None:
+ """
+ Sends a message with a pride fact of the day.
+
+ If "random" is given as an argument, a random previous fact will be provided.
+
+ If a date is given as an argument, and the date is in the past, the fact from that day
+ will be provided.
+ """
+ message_body = ctx.message.content[len(ctx.invoked_with) + 2:]
+ if message_body == "":
+ await self.send_select_fact(ctx, datetime.utcnow())
+ elif message_body.lower().startswith("rand"):
+ await self.send_random_fact(ctx)
+ else:
+ await self.send_select_fact(ctx, message_body)
+
+ def make_embed(self, fact: str) -> discord.Embed:
+ """Makes a nice embed for the fact to be sent."""
+ return discord.Embed(
+ colour=Colours.pink,
+ title="Pride Fact!",
+ description=fact
+ )
+
+
+def setup(bot: commands.Bot) -> None:
+ """Cog loader for pride facts."""
+ bot.add_cog(PrideFacts(bot))
+ log.info("Pride facts cog loaded!")