# MystBin ! - quote.py FONTS_DIR = "fonts" QUOTE_FONT = os.path.join(FONTS_DIR, "NudMotoya Maru W55 W2b.tff") AUTHOR_NAME_FONT = os.path.join(FONTS_DIR, "Lato-LightItalic") AUTHOR_USERNAME_FONT = os.path.join(FONTS_DIR, "Muller Narrow Light") FOOTER_FONT = os.path.join(FONTS_DIR, "NudMotoya Maru W55 W2b.tff") QUOTE_FONT_SIZE = 120 AUTHOR_NAME_FONT_SIZE = 45 AUTHOR_USERNAME_FONT_SIZE = 35 FOOTER_FONT_SIZE = 18 IMAGE_WIDTH = 800 IMAGE_HEIGHT = 450 PFP_SIZE = (400, 450) FOOTER_TEXT = "LTC" async def create_gradient_overlay(width, height): overlay = Image.new("RGBA", (width, height), (0, 0, 0, 0)) draw = ImageDraw.Draw(overlay) for x in range(width): alpha = int(255 * (x / width)) draw.line([(x, 0), (x, height)], fill=(0, 0, 0, alpha)) return overlay async def wrap_text(text, font, max_width): words = text.split() lines = [] current_line = [] for word in words: current_line.append(word) text_width = font.getbbox(' '.join(current_line))[2] if text_width > max_width: if len(current_line) == 1: lines.append(current_line[0]) current_line = [] else: current_line.pop() lines.append(' '.join(current_line)) current_line = [word] if current_line: lines.append(' '.join(current_line)) return '\n'.join(lines) async def fetch_avatar(avatar_url): async with aiohttp.ClientSession() as session: async with session.get(avatar_url) as response: return await response.read() async def generate_quote_image(text, display_name, username, avatar_url): image = Image.new("RGBA", (IMAGE_WIDTH, IMAGE_HEIGHT), "black") avatar_data = await fetch_avatar(avatar_url) avatar = Image.open(BytesIO(avatar_data)).convert("L") avatar = avatar.resize(PFP_SIZE, Image.Resampling.LANCZOS) image.paste(avatar, (0, 0)) gradient = await create_gradient_overlay(*PFP_SIZE) image.paste(gradient, (0, 0), gradient) draw = ImageDraw.Draw(image) try: font_quote = ImageFont.truetype(QUOTE_FONT, QUOTE_FONT_SIZE) font_author = ImageFont.truetype(AUTHOR_NAME_FONT, AUTHOR_NAME_FONT_SIZE) font_username = ImageFont.truetype(AUTHOR_USERNAME_FONT, AUTHOR_USERNAME_FONT_SIZE) font_footer = ImageFont.truetype(FOOTER_FONT, FOOTER_FONT_SIZE) except OSError: font_quote = ImageFont.load_default() font_author = ImageFont.load_default() font_username = ImageFont.load_default() font_footer = ImageFont.load_default() quote_x = 450 quote_y = IMAGE_HEIGHT // 2 - 150 max_width = IMAGE_WIDTH - quote_x - 50 wrapped_text = await wrap_text(text, font_quote, max_width) draw.text((quote_x, quote_y), wrapped_text, fill="white", font=font_quote) author_y = quote_y + 120 draw.text((quote_x, author_y), f"- {display_name}", fill="white", font=font_author) username_y = author_y + 50 draw.text((quote_x, username_y), f"@{username}", fill="rgba(255,255,255,180)", font=font_username) footer_x = IMAGE_WIDTH - 20 footer_y = IMAGE_HEIGHT - 20 draw.text((footer_x, footer_y), FOOTER_TEXT, fill="white", font=font_footer, anchor="rb") return image @bot.command() async def quote(ctx): if not ctx.message.reference: await ctx.send("Please reply to a message to generate a quote.") return replied_message = await ctx.channel.fetch_message(ctx.message.reference.message_id) text = replied_message.content author = replied_message.author avatar_url = author.avatar.url if author.avatar else author.default_avatar.url image = await generate_quote_image(text, author.display_name, author.name, avatar_url) buffer = BytesIO() image.save(buffer, format="PNG") buffer.seek(0) await ctx.send(file=discord.File(buffer, "quote.png"))