From 25ea98236d28794176b40f3cc59b0bd93e5a93fb Mon Sep 17 00:00:00 2001 From: neoarz Date: Tue, 7 Oct 2025 13:33:53 -0400 Subject: [PATCH 01/11] feat(tweety): new command :) --- cogs/media/__init__.py | 47 ++++- cogs/media/tweety.py | 448 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 494 insertions(+), 1 deletion(-) create mode 100644 cogs/media/tweety.py diff --git a/cogs/media/__init__.py b/cogs/media/__init__.py index 11721aa..43f2475 100644 --- a/cogs/media/__init__.py +++ b/cogs/media/__init__.py @@ -6,6 +6,7 @@ from typing import Optional from .download import download_command from .mcquote import mcquote_command from .img2gif import img2gif_command +from .tweety import tweety_command def _require_group_prefix(context: Context) -> bool: @@ -23,6 +24,34 @@ class Media(commands.GroupCog, name="media"): self.bot = bot super().__init__() + @commands.Cog.listener() + async def on_message(self, message: discord.Message): + """Listen for bot mentions with 'tweety' command while replying to a message""" + if message.author.bot: + return + + if self.bot.user in message.mentions and message.reference and message.reference.message_id: + content = message.content.lower() + content_without_mention = content.replace(f'<@{self.bot.user.id}>', '').replace(f'<@!{self.bot.user.id}>', '').strip() + + if 'tweety' in content_without_mention: + parts = content_without_mention.split() + verified = "false" + theme = "light" + + for i, part in enumerate(parts): + if part == 'tweety': + if i + 1 < len(parts): + if parts[i + 1] in ['verified', 'true', 'yes']: + verified = "true" + if 'dark' in parts or 'night' in parts: + theme = "dark" + break + + ctx = await self.bot.get_context(message) + + await self.tweety(ctx, verified=verified, theme=theme) + @commands.group(name="media", invoke_without_command=True) async def media_group(self, context: Context): embed = discord.Embed( @@ -31,7 +60,7 @@ class Media(commands.GroupCog, name="media"): color=0x7289DA ) embed.set_author(name="Media", icon_url="https://yes.nighty.works/raw/y5SEZ9.webp") - embed.add_field(name="Available", value="download, mcquote, img2gif", inline=False) + embed.add_field(name="Available", value="download, mcquote, img2gif, tweety", inline=False) await context.send(embed=embed) async def _invoke_hybrid(self, context: Context, name: str, *args, **kwargs): @@ -44,6 +73,9 @@ class Media(commands.GroupCog, name="media"): if name == "img2gif": await self.img2gif(context, attachment=kwargs.get('attachment')) return + if name == "tweety": + await self.tweety(context, verified=kwargs.get('verified', "false"), theme=kwargs.get('theme', "light")) + return await context.send(f"Unknown media command: {name}") @media_group.command(name="download") @@ -58,6 +90,10 @@ class Media(commands.GroupCog, name="media"): async def media_group_img2gif(self, context: Context, attachment: Optional[discord.Attachment] = None): await self._invoke_hybrid(context, "img2gif", attachment=attachment) + @media_group.command(name="tweety") + async def media_group_tweety(self, context: Context, verified: str = "false", theme: str = "light"): + await self._invoke_hybrid(context, "tweety", verified=verified, theme=theme) + @commands.check(_require_group_prefix) @commands.hybrid_command( name="download", @@ -82,6 +118,14 @@ class Media(commands.GroupCog, name="media"): async def img2gif(self, context, attachment: Optional[discord.Attachment] = None): return await img2gif_command()(self, context, attachment=attachment) + @commands.check(_require_group_prefix) + @commands.hybrid_command( + name="tweety", + description="Convert a replied message to a tweet image.", + ) + async def tweety(self, context, verified: str = "false", theme: str = "light"): + return await tweety_command()(self, context, verified=verified, theme=theme) + async def setup(bot) -> None: cog = Media(bot) await bot.add_cog(cog) @@ -89,3 +133,4 @@ async def setup(bot) -> None: bot.logger.info("Loaded extension 'media.download'") bot.logger.info("Loaded extension 'media.mcquote'") bot.logger.info("Loaded extension 'media.img2gif'") + bot.logger.info("Loaded extension 'media.tweety'") diff --git a/cogs/media/tweety.py b/cogs/media/tweety.py new file mode 100644 index 0000000..10317af --- /dev/null +++ b/cogs/media/tweety.py @@ -0,0 +1,448 @@ +import asyncio +import os +import tempfile +import discord +from discord.ext import commands +from discord import app_commands +import aiohttp +import io +from datetime import datetime +from typing import Optional + + +class TweetyView(discord.ui.View): + """ API for Tweety is hosted on Vercel and made by me :) github can be found here: https://github.com/neoarz/tweety-api""" + + def __init__(self, author_id: int, original_message, tweet_data: dict, api_url: str, image_message: Optional[discord.Message] = None): + super().__init__(timeout=300) + self.author_id = author_id + self.original_message = original_message + self.tweet_data = tweet_data + self.api_url = api_url + self.is_dark = tweet_data.get("dark", False) + self.is_verified = tweet_data.get("verified", False) + self.image_message = image_message + + self.update_button_styles() + + def update_button_styles(self): + """Update button styles to reflect current state""" + self.clear_items() + + dark_button = discord.ui.Button( + label="Dark Mode" if self.is_dark else "Light Mode", + style=discord.ButtonStyle.primary if self.is_dark else discord.ButtonStyle.secondary, + emoji=discord.PartialEmoji(name="darkmode", id=1425165393751965884), + custom_id="toggle_dark" + ) + dark_button.callback = self.toggle_dark_callback + self.add_item(dark_button) + + verified_button = discord.ui.Button( + label="Verified", + style=discord.ButtonStyle.primary if self.is_verified else discord.ButtonStyle.secondary, + emoji=discord.PartialEmoji(name="TwitterVerifiedBadge", id=1425165432142172392), + custom_id="toggle_verified" + ) + verified_button.callback = self.toggle_verified_callback + self.add_item(verified_button) + + async def regenerate_tweet(self, interaction: discord.Interaction): + """Regenerate only the image message with current settings""" + await interaction.response.defer() + + try: + async with aiohttp.ClientSession() as session: + async with session.post( + f"{self.api_url}/api/render", + json=self.tweet_data, + headers={"Content-Type": "application/json"} + ) as response: + + if response.status != 200: + error_text = await response.text() + embed = discord.Embed( + title="Error", + description=f"API Error ({response.status}): {error_text}", + color=0xE02B2B, + ) + embed.set_author(name="Media", icon_url="https://yes.nighty.works/raw/y5SEZ9.webp") + await interaction.followup.send(embed=embed, ephemeral=True) + return + + image_data = await response.read() + + with tempfile.NamedTemporaryFile(delete=False, suffix=".png") as temp_file: + temp_file.write(image_data) + temp_file_path = temp_file.name + + with open(temp_file_path, 'rb') as f: + author_name = self.original_message.author.name + filename = f"tweet_{author_name}_{int(datetime.now().timestamp())}.png" + file = discord.File( + f, + filename=filename + ) + + self.update_button_styles() + + if self.image_message is not None: + await self.image_message.edit(attachments=[file], view=self) + else: + await interaction.followup.send(file=file, view=self) + + os.remove(temp_file_path) + + except Exception as e: + embed = discord.Embed( + title="Error", + description="Error regenerating tweet image", + color=0xE02B2B, + ) + embed.set_author(name="Media", icon_url="https://yes.nighty.works/raw/y5SEZ9.webp") + await interaction.followup.send(embed=embed, ephemeral=True) + + async def toggle_dark_callback(self, interaction: discord.Interaction): + """Handle dark mode toggle button click""" + if interaction.user.id != self.author_id: + embed = discord.Embed( + title="Error", + description="You can't modify someone else's tweet!", + color=0xE02B2B, + ) + embed.set_author(name="Media", icon_url="https://yes.nighty.works/raw/y5SEZ9.webp") + await interaction.response.send_message(embed=embed, ephemeral=True) + return + + self.is_dark = not self.is_dark + self.tweet_data["dark"] = self.is_dark + + await self.regenerate_tweet(interaction) + + async def toggle_verified_callback(self, interaction: discord.Interaction): + """Handle verified toggle button click""" + if interaction.user.id != self.author_id: + embed = discord.Embed( + title="Error", + description="You can't modify someone else's tweet!", + color=0xE02B2B, + ) + embed.set_author(name="Media", icon_url="https://yes.nighty.works/raw/y5SEZ9.webp") + await interaction.response.send_message(embed=embed, ephemeral=True) + return + + self.is_verified = not self.is_verified + self.tweet_data["verified"] = self.is_verified + + await self.regenerate_tweet(interaction) + + async def on_timeout(self): + """Disable buttons when view times out""" + for item in self.children: + item.disabled = True + + try: + pass + except: + pass + +def tweety_command(): + @commands.hybrid_command( + name="tweety", + description="Convert a replied message to a tweet image." + ) + @app_commands.describe( + verified="Add a verified badge to the tweet", + theme="Choose the theme for the tweet" + ) + @app_commands.choices(verified=[ + app_commands.Choice(name="No", value="false"), + app_commands.Choice(name="Yes", value="true") + ]) + @app_commands.choices(theme=[ + app_commands.Choice(name="Light", value="light"), + app_commands.Choice(name="Dark", value="dark") + ]) + @commands.cooldown(1, 10, commands.BucketType.user) + async def tweety(self, context, verified: Optional[str] = "false", theme: Optional[str] = "light"): + interaction = getattr(context, "interaction", None) + if interaction is not None and not isinstance(context.message, discord.Message): + try: + embed = discord.Embed( + title="Tweety", + description=( + "Use the prefix command: `.media tweety`\n" + f"Or reply to a message with: <@{self.bot.user.id}> tweety" + ), + color=0x7289DA, + ) + embed.set_author(name="Media", icon_url="https://yes.nighty.works/raw/y5SEZ9.webp") + if not interaction.response.is_done(): + await interaction.response.send_message(embed=embed, ephemeral=True) + else: + await interaction.followup.send(embed=embed, ephemeral=True) + except Exception: + pass + return + verified_bool = verified == "true" + theme_bool = theme == "dark" + + if not context.message.reference or not context.message.reference.message_id: + embed = discord.Embed( + title="Error", + description="You must reply to a message to use this command!", + color=0xE02B2B, + ) + embed.set_author(name="Media", icon_url="https://yes.nighty.works/raw/y5SEZ9.webp") + + interaction = getattr(context, "interaction", None) + if interaction is not None: + if not interaction.response.is_done(): + await interaction.response.send_message(embed=embed, ephemeral=True) + else: + await interaction.followup.send(embed=embed, ephemeral=True) + else: + await context.send(embed=embed, ephemeral=True) + return + + original_message = await context.channel.fetch_message(context.message.reference.message_id) + + try: + if not original_message: + embed = discord.Embed( + title="Error", + description="Could not find the original message!", + color=0xE02B2B, + ) + embed.set_author(name="Media", icon_url="https://yes.nighty.works/raw/y5SEZ9.webp") + + interaction = getattr(context, "interaction", None) + if interaction is not None: + if not interaction.response.is_done(): + await interaction.response.send_message(embed=embed, ephemeral=True) + else: + await interaction.followup.send(embed=embed, ephemeral=True) + else: + await context.send(embed=embed, ephemeral=True) + return + + if original_message.author.bot: + embed = discord.Embed( + title="Error", + description="Cannot convert bot messages to tweets!", + color=0xE02B2B, + ) + embed.set_author(name="Media", icon_url="https://yes.nighty.works/raw/y5SEZ9.webp") + + interaction = getattr(context, "interaction", None) + if interaction is not None: + if not interaction.response.is_done(): + await interaction.response.send_message(embed=embed, ephemeral=True) + else: + await interaction.followup.send(embed=embed, ephemeral=True) + else: + await context.send(embed=embed, ephemeral=True) + return + + processing_embed = discord.Embed( + title="Tweet Generator (Processing)", + description=" Generating tweet... This may take a moment.", + color=0x7289DA, + ) + processing_embed.set_author(name="Media", icon_url="https://yes.nighty.works/raw/y5SEZ9.webp") + + interaction = getattr(context, "interaction", None) + if interaction is not None: + if not interaction.response.is_done(): + await interaction.response.send_message(embed=processing_embed, ephemeral=True) + else: + await interaction.followup.send(embed=processing_embed, ephemeral=True) + else: + processing_msg = await context.send(embed=processing_embed) + + author = original_message.author + display_name = author.display_name or author.name + username = f"@{author.name}" + avatar_url = str(author.avatar.url) if author.avatar else str(author.default_avatar.url) + message_text = original_message.content + + image_url = None + if original_message.attachments: + for attachment in original_message.attachments: + if any(attachment.filename.lower().endswith(ext) for ext in ['.png', '.jpg', '.jpeg', '.gif', '.webp']): + raw_url = attachment.url + if 'cdn.discordapp.com' in raw_url: + media_url = raw_url.replace('cdn.discordapp.com', 'media.discordapp.net') + if '?' not in media_url: + media_url += f"?width={attachment.width}&height={attachment.height}" + elif 'width=' not in media_url and attachment.width: + media_url += f"&width={attachment.width}&height={attachment.height}" + image_url = media_url + else: + image_url = raw_url + break + + if not message_text.strip() and not image_url: + embed = discord.Embed( + title="Error", + description="Message must have either text content or an image/GIF to convert!", + color=0xE02B2B, + ) + embed.set_author(name="Media", icon_url="https://yes.nighty.works/raw/y5SEZ9.webp") + + interaction = getattr(context, "interaction", None) + if interaction is not None: + try: + await interaction.delete_original_response() + except: + pass + await interaction.followup.send(embed=embed, ephemeral=True) + else: + await processing_msg.delete() + await context.send(embed=embed, ephemeral=True) + return + + msg_time = original_message.created_at + timestamp = msg_time.strftime("%I:%M %p · %b %d, %Y").replace(" 0", " ") + + tweet_data = { + "name": display_name[:50], + "handle": username[:20], + "text": message_text[:300], + "avatar": avatar_url, + "timestamp": timestamp, + "verified": verified_bool, + "dark": theme_bool + } + + if image_url: + tweet_data["image"] = image_url + + API_BASE_URL = "https://tweety-api.vercel.app" + + async with aiohttp.ClientSession() as session: + try: + async with session.post( + f"{API_BASE_URL}/api/render", + json=tweet_data, + headers={"Content-Type": "application/json"} + ) as response: + + if response.status != 200: + error_text = await response.text() + embed = discord.Embed( + title="Error", + description=f"API Error ({response.status}): {error_text}", + color=0xE02B2B, + ) + embed.set_author(name="Media", icon_url="https://yes.nighty.works/raw/y5SEZ9.webp") + + interaction = getattr(context, "interaction", None) + if interaction is not None: + try: + await interaction.delete_original_response() + except: + pass + await interaction.followup.send(embed=embed, ephemeral=True) + else: + await processing_msg.delete() + await context.send(embed=embed, ephemeral=True) + return + + image_data = await response.read() + + with tempfile.NamedTemporaryFile(delete=False, suffix=".png") as temp_file: + temp_file.write(image_data) + temp_file_path = temp_file.name + + with open(temp_file_path, 'rb') as f: + file = discord.File(f, filename=f"tweet_{author.name}_{int(datetime.now().timestamp())}.png") + embed = discord.Embed( + title="Tweet Generated", + color=0x7289DA, + ) + embed.set_author(name="Media", icon_url="https://yes.nighty.works/raw/y5SEZ9.webp") + embed.set_footer( + text=f"Requested by {context.author.name}", + icon_url=context.author.display_avatar.url, + ) + + view = TweetyView( + author_id=context.author.id, + original_message=original_message, + tweet_data=tweet_data, + api_url=API_BASE_URL + ) + + interaction = getattr(context, "interaction", None) + if interaction is not None: + embed_message = await context.channel.send(embed=embed) + image_message = await context.channel.send(file=file, view=view) + view.image_message = image_message + try: + await interaction.delete_original_response() + except: + pass + else: + await processing_msg.delete() + embed_message = await context.channel.send(embed=embed) + image_message = await context.channel.send(file=file, view=view) + view.image_message = image_message + + os.remove(temp_file_path) + + except aiohttp.ClientError as e: + embed = discord.Embed( + title="Error", + description=f"Connection error: Could not reach tweet API at {API_BASE_URL}", + color=0xE02B2B, + ) + embed.set_author(name="Media", icon_url="https://yes.nighty.works/raw/y5SEZ9.webp") + + interaction = getattr(context, "interaction", None) + if interaction is not None: + try: + await interaction.delete_original_response() + except: + pass + await interaction.followup.send(embed=embed, ephemeral=True) + else: + await processing_msg.delete() + await context.send(embed=embed, ephemeral=True) + except Exception as e: + embed = discord.Embed( + title="Error", + description="Error generating tweet image", + color=0xE02B2B, + ) + embed.set_author(name="Media", icon_url="https://yes.nighty.works/raw/y5SEZ9.webp") + + interaction = getattr(context, "interaction", None) + if interaction is not None: + try: + await interaction.delete_original_response() + except: + pass + await interaction.followup.send(embed=embed, ephemeral=True) + else: + await processing_msg.delete() + await context.send(embed=embed, ephemeral=True) + + except Exception as e: + embed = discord.Embed( + title="Error", + description="Error processing the message!", + color=0xE02B2B, + ) + embed.set_author(name="Media", icon_url="https://yes.nighty.works/raw/y5SEZ9.webp") + + interaction = getattr(context, "interaction", None) + if interaction is not None: + if not interaction.response.is_done(): + await interaction.response.send_message(embed=embed, ephemeral=True) + else: + await interaction.followup.send(embed=embed, ephemeral=True) + else: + await context.send(embed=embed, ephemeral=True) + + return tweety From 70af8e24b2f7528cc0ffb369d115b800eed0aee1 Mon Sep 17 00:00:00 2001 From: neoarz Date: Tue, 7 Oct 2025 15:22:33 -0400 Subject: [PATCH 02/11] fix(tweety): slash commands show diff message --- cogs/media/tweety.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cogs/media/tweety.py b/cogs/media/tweety.py index 10317af..65ec96e 100644 --- a/cogs/media/tweety.py +++ b/cogs/media/tweety.py @@ -166,7 +166,7 @@ def tweety_command(): @commands.cooldown(1, 10, commands.BucketType.user) async def tweety(self, context, verified: Optional[str] = "false", theme: Optional[str] = "light"): interaction = getattr(context, "interaction", None) - if interaction is not None and not isinstance(context.message, discord.Message): + if interaction is not None: try: embed = discord.Embed( title="Tweety", From 7419ec5f59a5c2a8a75fad032dfa8cef54f51f2e Mon Sep 17 00:00:00 2001 From: neoarz Date: Tue, 7 Oct 2025 21:29:42 -0400 Subject: [PATCH 03/11] fix(tweety): remove theme args since buttons work Simplifies the tweety command by removing the 'verified' and 'theme' arguments from command signatures and related logic. Defaults for these options are now set internally, and toggling is handled via UI elements instead of command parameters. --- cogs/media/__init__.py | 26 ++++++-------------------- cogs/media/tweety.py | 20 +++++--------------- 2 files changed, 11 insertions(+), 35 deletions(-) diff --git a/cogs/media/__init__.py b/cogs/media/__init__.py index 43f2475..beb217c 100644 --- a/cogs/media/__init__.py +++ b/cogs/media/__init__.py @@ -35,22 +35,8 @@ class Media(commands.GroupCog, name="media"): content_without_mention = content.replace(f'<@{self.bot.user.id}>', '').replace(f'<@!{self.bot.user.id}>', '').strip() if 'tweety' in content_without_mention: - parts = content_without_mention.split() - verified = "false" - theme = "light" - - for i, part in enumerate(parts): - if part == 'tweety': - if i + 1 < len(parts): - if parts[i + 1] in ['verified', 'true', 'yes']: - verified = "true" - if 'dark' in parts or 'night' in parts: - theme = "dark" - break - ctx = await self.bot.get_context(message) - - await self.tweety(ctx, verified=verified, theme=theme) + await self.tweety(ctx) @commands.group(name="media", invoke_without_command=True) async def media_group(self, context: Context): @@ -74,7 +60,7 @@ class Media(commands.GroupCog, name="media"): await self.img2gif(context, attachment=kwargs.get('attachment')) return if name == "tweety": - await self.tweety(context, verified=kwargs.get('verified', "false"), theme=kwargs.get('theme', "light")) + await self.tweety(context) return await context.send(f"Unknown media command: {name}") @@ -91,8 +77,8 @@ class Media(commands.GroupCog, name="media"): await self._invoke_hybrid(context, "img2gif", attachment=attachment) @media_group.command(name="tweety") - async def media_group_tweety(self, context: Context, verified: str = "false", theme: str = "light"): - await self._invoke_hybrid(context, "tweety", verified=verified, theme=theme) + async def media_group_tweety(self, context: Context): + await self._invoke_hybrid(context, "tweety") @commands.check(_require_group_prefix) @commands.hybrid_command( @@ -123,8 +109,8 @@ class Media(commands.GroupCog, name="media"): name="tweety", description="Convert a replied message to a tweet image.", ) - async def tweety(self, context, verified: str = "false", theme: str = "light"): - return await tweety_command()(self, context, verified=verified, theme=theme) + async def tweety(self, context): + return await tweety_command()(self, context) async def setup(bot) -> None: cog = Media(bot) diff --git a/cogs/media/tweety.py b/cogs/media/tweety.py index 65ec96e..859d870 100644 --- a/cogs/media/tweety.py +++ b/cogs/media/tweety.py @@ -151,20 +151,8 @@ def tweety_command(): name="tweety", description="Convert a replied message to a tweet image." ) - @app_commands.describe( - verified="Add a verified badge to the tweet", - theme="Choose the theme for the tweet" - ) - @app_commands.choices(verified=[ - app_commands.Choice(name="No", value="false"), - app_commands.Choice(name="Yes", value="true") - ]) - @app_commands.choices(theme=[ - app_commands.Choice(name="Light", value="light"), - app_commands.Choice(name="Dark", value="dark") - ]) @commands.cooldown(1, 10, commands.BucketType.user) - async def tweety(self, context, verified: Optional[str] = "false", theme: Optional[str] = "light"): + async def tweety(self, context): interaction = getattr(context, "interaction", None) if interaction is not None: try: @@ -184,8 +172,10 @@ def tweety_command(): except Exception: pass return - verified_bool = verified == "true" - theme_bool = theme == "dark" + + # Default to light mode, non-verified (buttons will allow toggling) + verified_bool = False + theme_bool = False if not context.message.reference or not context.message.reference.message_id: embed = discord.Embed( From eb800a9bd54c53956ef8bfbe1c52898de9c7d604 Mon Sep 17 00:00:00 2001 From: neoarz Date: Tue, 7 Oct 2025 21:37:55 -0400 Subject: [PATCH 04/11] refactor(tweety): too much unecessary code :) --- cogs/media/tweety.py | 218 ++++++++++--------------------------------- 1 file changed, 51 insertions(+), 167 deletions(-) diff --git a/cogs/media/tweety.py b/cogs/media/tweety.py index 859d870..6510ee6 100644 --- a/cogs/media/tweety.py +++ b/cogs/media/tweety.py @@ -1,11 +1,8 @@ -import asyncio import os import tempfile import discord from discord.ext import commands -from discord import app_commands import aiohttp -import io from datetime import datetime from typing import Optional @@ -102,8 +99,8 @@ class TweetyView(discord.ui.View): embed.set_author(name="Media", icon_url="https://yes.nighty.works/raw/y5SEZ9.webp") await interaction.followup.send(embed=embed, ephemeral=True) - async def toggle_dark_callback(self, interaction: discord.Interaction): - """Handle dark mode toggle button click""" + async def _check_author(self, interaction: discord.Interaction) -> bool: + """Check if user is authorized to modify the tweet""" if interaction.user.id != self.author_id: embed = discord.Embed( title="Error", @@ -112,39 +109,29 @@ class TweetyView(discord.ui.View): ) embed.set_author(name="Media", icon_url="https://yes.nighty.works/raw/y5SEZ9.webp") await interaction.response.send_message(embed=embed, ephemeral=True) + return False + return True + + async def toggle_dark_callback(self, interaction: discord.Interaction): + """Handle dark mode toggle button click""" + if not await self._check_author(interaction): return - self.is_dark = not self.is_dark self.tweet_data["dark"] = self.is_dark - await self.regenerate_tweet(interaction) async def toggle_verified_callback(self, interaction: discord.Interaction): """Handle verified toggle button click""" - if interaction.user.id != self.author_id: - embed = discord.Embed( - title="Error", - description="You can't modify someone else's tweet!", - color=0xE02B2B, - ) - embed.set_author(name="Media", icon_url="https://yes.nighty.works/raw/y5SEZ9.webp") - await interaction.response.send_message(embed=embed, ephemeral=True) + if not await self._check_author(interaction): return - self.is_verified = not self.is_verified self.tweet_data["verified"] = self.is_verified - await self.regenerate_tweet(interaction) async def on_timeout(self): """Disable buttons when view times out""" for item in self.children: item.disabled = True - - try: - pass - except: - pass def tweety_command(): @commands.hybrid_command( @@ -153,30 +140,21 @@ def tweety_command(): ) @commands.cooldown(1, 10, commands.BucketType.user) async def tweety(self, context): - interaction = getattr(context, "interaction", None) - if interaction is not None: - try: - embed = discord.Embed( - title="Tweety", - description=( - "Use the prefix command: `.media tweety`\n" - f"Or reply to a message with: <@{self.bot.user.id}> tweety" - ), - color=0x7289DA, - ) - embed.set_author(name="Media", icon_url="https://yes.nighty.works/raw/y5SEZ9.webp") - if not interaction.response.is_done(): - await interaction.response.send_message(embed=embed, ephemeral=True) - else: - await interaction.followup.send(embed=embed, ephemeral=True) - except Exception: - pass + # Slash commands show info message only + if hasattr(context, "interaction") and context.interaction: + embed = discord.Embed( + title="Tweety", + description=( + "Use the prefix command: `.media tweety`\n" + f"Or reply to a message with: <@{self.bot.user.id}> tweety" + ), + color=0x7289DA, + ) + embed.set_author(name="Media", icon_url="https://yes.nighty.works/raw/y5SEZ9.webp") + await context.send(embed=embed, ephemeral=True) return - # Default to light mode, non-verified (buttons will allow toggling) - verified_bool = False - theme_bool = False - + # Check if replying to a message if not context.message.reference or not context.message.reference.message_id: embed = discord.Embed( title="Error", @@ -184,38 +162,13 @@ def tweety_command(): color=0xE02B2B, ) embed.set_author(name="Media", icon_url="https://yes.nighty.works/raw/y5SEZ9.webp") - - interaction = getattr(context, "interaction", None) - if interaction is not None: - if not interaction.response.is_done(): - await interaction.response.send_message(embed=embed, ephemeral=True) - else: - await interaction.followup.send(embed=embed, ephemeral=True) - else: - await context.send(embed=embed, ephemeral=True) + await context.send(embed=embed) return - original_message = await context.channel.fetch_message(context.message.reference.message_id) - try: - if not original_message: - embed = discord.Embed( - title="Error", - description="Could not find the original message!", - color=0xE02B2B, - ) - embed.set_author(name="Media", icon_url="https://yes.nighty.works/raw/y5SEZ9.webp") - - interaction = getattr(context, "interaction", None) - if interaction is not None: - if not interaction.response.is_done(): - await interaction.response.send_message(embed=embed, ephemeral=True) - else: - await interaction.followup.send(embed=embed, ephemeral=True) - else: - await context.send(embed=embed, ephemeral=True) - return + original_message = await context.channel.fetch_message(context.message.reference.message_id) + # Check if bot message if original_message.author.bot: embed = discord.Embed( title="Error", @@ -223,32 +176,17 @@ def tweety_command(): color=0xE02B2B, ) embed.set_author(name="Media", icon_url="https://yes.nighty.works/raw/y5SEZ9.webp") - - interaction = getattr(context, "interaction", None) - if interaction is not None: - if not interaction.response.is_done(): - await interaction.response.send_message(embed=embed, ephemeral=True) - else: - await interaction.followup.send(embed=embed, ephemeral=True) - else: - await context.send(embed=embed, ephemeral=True) + await context.send(embed=embed) return + # Show processing message processing_embed = discord.Embed( title="Tweet Generator (Processing)", description=" Generating tweet... This may take a moment.", color=0x7289DA, ) processing_embed.set_author(name="Media", icon_url="https://yes.nighty.works/raw/y5SEZ9.webp") - - interaction = getattr(context, "interaction", None) - if interaction is not None: - if not interaction.response.is_done(): - await interaction.response.send_message(embed=processing_embed, ephemeral=True) - else: - await interaction.followup.send(embed=processing_embed, ephemeral=True) - else: - processing_msg = await context.send(embed=processing_embed) + processing_msg = await context.send(embed=processing_embed) author = original_message.author display_name = author.display_name or author.name @@ -273,36 +211,26 @@ def tweety_command(): break if not message_text.strip() and not image_url: + await processing_msg.delete() embed = discord.Embed( title="Error", description="Message must have either text content or an image/GIF to convert!", color=0xE02B2B, ) embed.set_author(name="Media", icon_url="https://yes.nighty.works/raw/y5SEZ9.webp") - - interaction = getattr(context, "interaction", None) - if interaction is not None: - try: - await interaction.delete_original_response() - except: - pass - await interaction.followup.send(embed=embed, ephemeral=True) - else: - await processing_msg.delete() - await context.send(embed=embed, ephemeral=True) + await context.send(embed=embed) return - msg_time = original_message.created_at - timestamp = msg_time.strftime("%I:%M %p · %b %d, %Y").replace(" 0", " ") - + # Prepare tweet data + timestamp = original_message.created_at.strftime("%I:%M %p · %b %d, %Y").replace(" 0", " ") tweet_data = { "name": display_name[:50], "handle": username[:20], "text": message_text[:300], "avatar": avatar_url, "timestamp": timestamp, - "verified": verified_bool, - "dark": theme_bool + "verified": False, + "dark": False } if image_url: @@ -319,6 +247,7 @@ def tweety_command(): ) as response: if response.status != 200: + await processing_msg.delete() error_text = await response.text() embed = discord.Embed( title="Error", @@ -326,17 +255,7 @@ def tweety_command(): color=0xE02B2B, ) embed.set_author(name="Media", icon_url="https://yes.nighty.works/raw/y5SEZ9.webp") - - interaction = getattr(context, "interaction", None) - if interaction is not None: - try: - await interaction.delete_original_response() - except: - pass - await interaction.followup.send(embed=embed, ephemeral=True) - else: - await processing_msg.delete() - await context.send(embed=embed, ephemeral=True) + await context.send(embed=embed) return image_data = await response.read() @@ -345,6 +264,8 @@ def tweety_command(): temp_file.write(image_data) temp_file_path = temp_file.name + await processing_msg.delete() + with open(temp_file_path, 'rb') as f: file = discord.File(f, filename=f"tweet_{author.name}_{int(datetime.now().timestamp())}.png") embed = discord.Embed( @@ -364,75 +285,38 @@ def tweety_command(): api_url=API_BASE_URL ) - interaction = getattr(context, "interaction", None) - if interaction is not None: - embed_message = await context.channel.send(embed=embed) - image_message = await context.channel.send(file=file, view=view) - view.image_message = image_message - try: - await interaction.delete_original_response() - except: - pass - else: - await processing_msg.delete() - embed_message = await context.channel.send(embed=embed) - image_message = await context.channel.send(file=file, view=view) - view.image_message = image_message + await context.send(embed=embed) + image_message = await context.send(file=file, view=view) + view.image_message = image_message os.remove(temp_file_path) - except aiohttp.ClientError as e: + except aiohttp.ClientError: + await processing_msg.delete() embed = discord.Embed( title="Error", - description=f"Connection error: Could not reach tweet API at {API_BASE_URL}", + description=f"Connection error: Could not reach tweet API", color=0xE02B2B, ) embed.set_author(name="Media", icon_url="https://yes.nighty.works/raw/y5SEZ9.webp") - - interaction = getattr(context, "interaction", None) - if interaction is not None: - try: - await interaction.delete_original_response() - except: - pass - await interaction.followup.send(embed=embed, ephemeral=True) - else: - await processing_msg.delete() - await context.send(embed=embed, ephemeral=True) - except Exception as e: + await context.send(embed=embed) + except Exception: + await processing_msg.delete() embed = discord.Embed( title="Error", description="Error generating tweet image", color=0xE02B2B, ) embed.set_author(name="Media", icon_url="https://yes.nighty.works/raw/y5SEZ9.webp") - - interaction = getattr(context, "interaction", None) - if interaction is not None: - try: - await interaction.delete_original_response() - except: - pass - await interaction.followup.send(embed=embed, ephemeral=True) - else: - await processing_msg.delete() - await context.send(embed=embed, ephemeral=True) - - except Exception as e: + await context.send(embed=embed) + + except Exception: embed = discord.Embed( title="Error", description="Error processing the message!", color=0xE02B2B, ) embed.set_author(name="Media", icon_url="https://yes.nighty.works/raw/y5SEZ9.webp") - - interaction = getattr(context, "interaction", None) - if interaction is not None: - if not interaction.response.is_done(): - await interaction.response.send_message(embed=embed, ephemeral=True) - else: - await interaction.followup.send(embed=embed, ephemeral=True) - else: - await context.send(embed=embed, ephemeral=True) + await context.send(embed=embed) return tweety From 56818bf9126c880de2cc709a4ff8b24b8ff214da Mon Sep 17 00:00:00 2001 From: neoarz Date: Tue, 7 Oct 2025 22:02:37 -0400 Subject: [PATCH 05/11] refactor(tweety): new slash command for tweety (shows help) --- cogs/media/tweety.py | 120 +++++++++++++++++++++++++++++++++++++------ 1 file changed, 104 insertions(+), 16 deletions(-) diff --git a/cogs/media/tweety.py b/cogs/media/tweety.py index 6510ee6..3730594 100644 --- a/cogs/media/tweety.py +++ b/cogs/media/tweety.py @@ -7,8 +7,108 @@ from datetime import datetime from typing import Optional +class TweetyHelpView(discord.ui.View): + """Paginated help view for tweety command""" + + def __init__(self, user_id: int, bot): + super().__init__(timeout=180) + self.user_id = user_id + self.bot = bot + self.current_page = 0 + self.pages = [ + { + "title": "Tweety (Method 1)", + "description": "Use the prefix command `.media tweety` while replying to a message.", + "gif_url": "https://yes.nighty.works/raw/VrKX1L.gif", + "fields": [ + {"name": "How to use", "value": "1. Reply to any message\n2. Type `.media tweety`\n3. Use the buttons to customize!", "inline": False}, + ] + }, + { + "title": "Tweety (Method 2)", + "description": f"Mention <@{bot.user.id}> with `tweety` while replying to a message.", + "gif_url": "https://yes.nighty.works/raw/9XEe9j.gif", + "fields": [ + {"name": "How to use", "value": f"1. Reply to any message\n2. Type `<@{bot.user.id}> tweety`\n3. Use the buttons to customize!", "inline": False}, + ] + } + ] + self.update_buttons() + + def create_embed(self): + """Create embed for current page""" + page = self.pages[self.current_page] + embed = discord.Embed( + title=page["title"], + description=page["description"], + color=0x7289DA, + ) + embed.set_author(name="Media", icon_url="https://yes.nighty.works/raw/y5SEZ9.webp") + + for field in page["fields"]: + embed.add_field(name=field["name"], value=field["value"], inline=field["inline"]) + + embed.set_image(url=page["gif_url"]) + embed.set_footer(text=f"Page {self.current_page + 1}/{len(self.pages)}") + + return embed + + def update_buttons(self): + """Update button states based on current page""" + self.clear_items() + + prev_button = discord.ui.Button( + label="Prev", + style=discord.ButtonStyle.secondary, + emoji=discord.PartialEmoji(name="left", id=1420240344926126090), + disabled=self.current_page == 0 + ) + prev_button.callback = self.previous_page + self.add_item(prev_button) + + next_button = discord.ui.Button( + label="Next", + style=discord.ButtonStyle.secondary, + emoji=discord.PartialEmoji(name="right", id=1420240334100627456), + disabled=self.current_page == len(self.pages) - 1 + ) + next_button.callback = self.next_page + self.add_item(next_button) + + async def previous_page(self, interaction: discord.Interaction): + """Go to previous page""" + if interaction.user.id != self.user_id: + await interaction.response.send_message("You can't control someone else's help menu!", ephemeral=True) + return + + if self.current_page > 0: + self.current_page -= 1 + self.update_buttons() + await interaction.response.edit_message(embed=self.create_embed(), view=self) + else: + await interaction.response.defer() + + async def next_page(self, interaction: discord.Interaction): + """Go to next page""" + if interaction.user.id != self.user_id: + await interaction.response.send_message("You can't control someone else's help menu!", ephemeral=True) + return + + if self.current_page < len(self.pages) - 1: + self.current_page += 1 + self.update_buttons() + await interaction.response.edit_message(embed=self.create_embed(), view=self) + else: + await interaction.response.defer() + + async def on_timeout(self): + """Disable buttons when view times out""" + for item in self.children: + item.disabled = True + + class TweetyView(discord.ui.View): - """ API for Tweety is hosted on Vercel and made by me :) github can be found here: https://github.com/neoarz/tweety-api""" + """API for Tweety is hosted on Vercel and made by me :) github can be found here: https://github.com/neoarz/tweety-api""" def __init__(self, author_id: int, original_message, tweet_data: dict, api_url: str, image_message: Optional[discord.Message] = None): super().__init__(timeout=300) @@ -140,21 +240,12 @@ def tweety_command(): ) @commands.cooldown(1, 10, commands.BucketType.user) async def tweety(self, context): - # Slash commands show info message only if hasattr(context, "interaction") and context.interaction: - embed = discord.Embed( - title="Tweety", - description=( - "Use the prefix command: `.media tweety`\n" - f"Or reply to a message with: <@{self.bot.user.id}> tweety" - ), - color=0x7289DA, - ) - embed.set_author(name="Media", icon_url="https://yes.nighty.works/raw/y5SEZ9.webp") - await context.send(embed=embed, ephemeral=True) + view = TweetyHelpView(user_id=context.author.id, bot=self.bot) + embed = view.create_embed() + await context.send(embed=embed, view=view, ephemeral=True) return - # Check if replying to a message if not context.message.reference or not context.message.reference.message_id: embed = discord.Embed( title="Error", @@ -168,7 +259,6 @@ def tweety_command(): try: original_message = await context.channel.fetch_message(context.message.reference.message_id) - # Check if bot message if original_message.author.bot: embed = discord.Embed( title="Error", @@ -179,7 +269,6 @@ def tweety_command(): await context.send(embed=embed) return - # Show processing message processing_embed = discord.Embed( title="Tweet Generator (Processing)", description=" Generating tweet... This may take a moment.", @@ -221,7 +310,6 @@ def tweety_command(): await context.send(embed=embed) return - # Prepare tweet data timestamp = original_message.created_at.strftime("%I:%M %p · %b %d, %Y").replace(" 0", " ") tweet_data = { "name": display_name[:50], From 47043fb38b5eba504835027289b8dc6661d36f05 Mon Sep 17 00:00:00 2001 From: neoarz Date: Tue, 7 Oct 2025 22:10:52 -0400 Subject: [PATCH 06/11] refactor(tweet): new york time add pytz requirements --- cogs/media/tweety.py | 18 ++++++++++-------- requirements.txt | 3 ++- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/cogs/media/tweety.py b/cogs/media/tweety.py index 3730594..3bc5dce 100644 --- a/cogs/media/tweety.py +++ b/cogs/media/tweety.py @@ -1,3 +1,8 @@ +# The API used in the tweety command is made by me and can be found here: +# https://github.com/neoarz/tweety-api +# I made this out of spite since i couldnt find any free APIs for this +# Its serverless and hosted on Vercel :) + import os import tempfile import discord @@ -5,10 +10,11 @@ from discord.ext import commands import aiohttp from datetime import datetime from typing import Optional +import pytz class TweetyHelpView(discord.ui.View): - """Paginated help view for tweety command""" + """This is the help view for the slash command cuz only the pinging and prefix versions can use this command""" def __init__(self, user_id: int, bot): super().__init__(timeout=180) @@ -36,7 +42,6 @@ class TweetyHelpView(discord.ui.View): self.update_buttons() def create_embed(self): - """Create embed for current page""" page = self.pages[self.current_page] embed = discord.Embed( title=page["title"], @@ -54,7 +59,6 @@ class TweetyHelpView(discord.ui.View): return embed def update_buttons(self): - """Update button states based on current page""" self.clear_items() prev_button = discord.ui.Button( @@ -76,7 +80,6 @@ class TweetyHelpView(discord.ui.View): self.add_item(next_button) async def previous_page(self, interaction: discord.Interaction): - """Go to previous page""" if interaction.user.id != self.user_id: await interaction.response.send_message("You can't control someone else's help menu!", ephemeral=True) return @@ -89,7 +92,6 @@ class TweetyHelpView(discord.ui.View): await interaction.response.defer() async def next_page(self, interaction: discord.Interaction): - """Go to next page""" if interaction.user.id != self.user_id: await interaction.response.send_message("You can't control someone else's help menu!", ephemeral=True) return @@ -102,7 +104,6 @@ class TweetyHelpView(discord.ui.View): await interaction.response.defer() async def on_timeout(self): - """Disable buttons when view times out""" for item in self.children: item.disabled = True @@ -123,7 +124,6 @@ class TweetyView(discord.ui.View): self.update_button_styles() def update_button_styles(self): - """Update button styles to reflect current state""" self.clear_items() dark_button = discord.ui.Button( @@ -310,7 +310,9 @@ def tweety_command(): await context.send(embed=embed) return - timestamp = original_message.created_at.strftime("%I:%M %p · %b %d, %Y").replace(" 0", " ") + ny_tz = pytz.timezone('America/New_York') + msg_time_ny = original_message.created_at.astimezone(ny_tz) + timestamp = msg_time_ny.strftime("%I:%M %p · %b %d, %Y").replace(" 0", " ") tweet_data = { "name": display_name[:50], "handle": username[:20], diff --git a/requirements.txt b/requirements.txt index a9340be..3e23e11 100644 --- a/requirements.txt +++ b/requirements.txt @@ -4,4 +4,5 @@ discord.py==2.6.3 python-dotenv yt-dlp Pillow -pillow-heif \ No newline at end of file +pillow-heif +pytz \ No newline at end of file From 94d0eca8f110ba2f4edd829609f0b9f9d69c5e76 Mon Sep 17 00:00:00 2001 From: neoarz Date: Tue, 7 Oct 2025 22:24:30 -0400 Subject: [PATCH 07/11] fix(tweet): user and role mentions now show real text --- cogs/media/tweety.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/cogs/media/tweety.py b/cogs/media/tweety.py index 3bc5dce..8eb2e93 100644 --- a/cogs/media/tweety.py +++ b/cogs/media/tweety.py @@ -109,8 +109,6 @@ class TweetyHelpView(discord.ui.View): class TweetyView(discord.ui.View): - """API for Tweety is hosted on Vercel and made by me :) github can be found here: https://github.com/neoarz/tweety-api""" - def __init__(self, author_id: int, original_message, tweet_data: dict, api_url: str, image_message: Optional[discord.Message] = None): super().__init__(timeout=300) self.author_id = author_id @@ -283,6 +281,16 @@ def tweety_command(): avatar_url = str(author.avatar.url) if author.avatar else str(author.default_avatar.url) message_text = original_message.content + for mention in original_message.mentions: + message_text = message_text.replace(f'<@{mention.id}>', f'@{mention.name}') + message_text = message_text.replace(f'<@!{mention.id}>', f'@{mention.name}') + + for role in original_message.role_mentions: + message_text = message_text.replace(f'<@&{role.id}>', f'@{role.name}') + + for channel in original_message.channel_mentions: + message_text = message_text.replace(f'<#{channel.id}>', f'#{channel.name}') + image_url = None if original_message.attachments: for attachment in original_message.attachments: From fae21eed44e48e7835b9351f9fd98f325087013c Mon Sep 17 00:00:00 2001 From: neoarz Date: Tue, 7 Oct 2025 22:41:05 -0400 Subject: [PATCH 08/11] fix(tweety): only works with text no more images also now works with other bot messages --- cogs/media/tweety.py | 35 +++-------------------------------- 1 file changed, 3 insertions(+), 32 deletions(-) diff --git a/cogs/media/tweety.py b/cogs/media/tweety.py index 8eb2e93..153f12b 100644 --- a/cogs/media/tweety.py +++ b/cogs/media/tweety.py @@ -256,16 +256,6 @@ def tweety_command(): try: original_message = await context.channel.fetch_message(context.message.reference.message_id) - - if original_message.author.bot: - embed = discord.Embed( - title="Error", - description="Cannot convert bot messages to tweets!", - color=0xE02B2B, - ) - embed.set_author(name="Media", icon_url="https://yes.nighty.works/raw/y5SEZ9.webp") - await context.send(embed=embed) - return processing_embed = discord.Embed( title="Tweet Generator (Processing)", @@ -291,31 +281,15 @@ def tweety_command(): for channel in original_message.channel_mentions: message_text = message_text.replace(f'<#{channel.id}>', f'#{channel.name}') - image_url = None - if original_message.attachments: - for attachment in original_message.attachments: - if any(attachment.filename.lower().endswith(ext) for ext in ['.png', '.jpg', '.jpeg', '.gif', '.webp']): - raw_url = attachment.url - if 'cdn.discordapp.com' in raw_url: - media_url = raw_url.replace('cdn.discordapp.com', 'media.discordapp.net') - if '?' not in media_url: - media_url += f"?width={attachment.width}&height={attachment.height}" - elif 'width=' not in media_url and attachment.width: - media_url += f"&width={attachment.width}&height={attachment.height}" - image_url = media_url - else: - image_url = raw_url - break - - if not message_text.strip() and not image_url: + if not message_text.strip(): await processing_msg.delete() embed = discord.Embed( title="Error", - description="Message must have either text content or an image/GIF to convert!", + description="No text found! This command only works with text messages.", color=0xE02B2B, ) embed.set_author(name="Media", icon_url="https://yes.nighty.works/raw/y5SEZ9.webp") - await context.send(embed=embed) + await context.send(embed=embed, ephemeral=True) return ny_tz = pytz.timezone('America/New_York') @@ -331,9 +305,6 @@ def tweety_command(): "dark": False } - if image_url: - tweet_data["image"] = image_url - API_BASE_URL = "https://tweety-api.vercel.app" async with aiohttp.ClientSession() as session: From 78f5e80b9396a89f69c2bcd3bf08e76fa34364a8 Mon Sep 17 00:00:00 2001 From: neoarz Date: Tue, 7 Oct 2025 22:52:06 -0400 Subject: [PATCH 09/11] fix(tweet): dual error messages --- cogs/media/__init__.py | 2 +- cogs/media/tweety.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cogs/media/__init__.py b/cogs/media/__init__.py index beb217c..c3acee5 100644 --- a/cogs/media/__init__.py +++ b/cogs/media/__init__.py @@ -34,7 +34,7 @@ class Media(commands.GroupCog, name="media"): content = message.content.lower() content_without_mention = content.replace(f'<@{self.bot.user.id}>', '').replace(f'<@!{self.bot.user.id}>', '').strip() - if 'tweety' in content_without_mention: + if content_without_mention.strip() == 'tweety': ctx = await self.bot.get_context(message) await self.tweety(ctx) diff --git a/cogs/media/tweety.py b/cogs/media/tweety.py index 153f12b..a11ab10 100644 --- a/cogs/media/tweety.py +++ b/cogs/media/tweety.py @@ -289,7 +289,7 @@ def tweety_command(): color=0xE02B2B, ) embed.set_author(name="Media", icon_url="https://yes.nighty.works/raw/y5SEZ9.webp") - await context.send(embed=embed, ephemeral=True) + await context.send(embed=embed) return ny_tz = pytz.timezone('America/New_York') From f946efd5ce3ac797be503d454837cfc0da641c7d Mon Sep 17 00:00:00 2001 From: neoarz Date: Tue, 7 Oct 2025 23:00:33 -0400 Subject: [PATCH 10/11] chore: update readme and todo --- README.md | 2 +- TODO.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 49d3d05..7636788 100644 --- a/README.md +++ b/README.md @@ -29,7 +29,7 @@ | melonx | `help`, `transfer`, `mods`, `gamecrash`, `requirements`, `error`, `26` | | miscellaneous | `keanu`, `labubu`, `piracy`, `tryitandsee`, `rickroll`, `dontasktoask`, `support`, `depart`| | utilities | `translate`, `codepreview`, `dictionary` | -| media | `download`, `mcquote`, `img2gif` | +| media | `download`, `mcquote`, `img2gif`, `tweety` | ## Download diff --git a/TODO.md b/TODO.md index 66733cb..c317ff5 100644 --- a/TODO.md +++ b/TODO.md @@ -27,7 +27,7 @@ - [ ] Create github action -- [ ] Add video commands +- [x] Add video commands - [ ] Add git log to info From c80dca059f5d642ac9dbc8eda310bb10bab4c8a0 Mon Sep 17 00:00:00 2001 From: neoarz Date: Wed, 8 Oct 2025 00:43:21 -0400 Subject: [PATCH 11/11] fix(tweety): warning and word count --- cogs/media/tweety.py | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/cogs/media/tweety.py b/cogs/media/tweety.py index a11ab10..cbab0c8 100644 --- a/cogs/media/tweety.py +++ b/cogs/media/tweety.py @@ -13,6 +13,20 @@ from typing import Optional import pytz +def break_long_words(text: str, max_word_length: int = 50) -> str: + words = text.split(' ') + result = [] + + for word in words: + if len(word) > max_word_length: + chunks = [word[i:i+max_word_length] for i in range(0, len(word), max_word_length)] + result.append(' '.join(chunks)) + else: + result.append(word) + + return ' '.join(result) + + class TweetyHelpView(discord.ui.View): """This is the help view for the slash command cuz only the pinging and prefix versions can use this command""" @@ -292,6 +306,19 @@ def tweety_command(): await context.send(embed=embed) return + if len(message_text) > 300: + await processing_msg.delete() + embed = discord.Embed( + title="Error", + description=f"Message is too long! Maximum 300 characters allowed.\nYour message: {len(message_text)} characters", + color=0xE02B2B, + ) + embed.set_author(name="Media", icon_url="https://yes.nighty.works/raw/y5SEZ9.webp") + await context.send(embed=embed) + return + + message_text = break_long_words(message_text, max_word_length=50) + ny_tz = pytz.timezone('America/New_York') msg_time_ny = original_message.created_at.astimezone(ny_tz) timestamp = msg_time_ny.strftime("%I:%M %p · %b %d, %Y").replace(" 0", " ") @@ -339,6 +366,7 @@ def tweety_command(): file = discord.File(f, filename=f"tweet_{author.name}_{int(datetime.now().timestamp())}.png") embed = discord.Embed( title="Tweet Generated", + description=f"<:error:1424007141768822824> Tweet sometimes may look a bit broken, im gonna rewrite the API another time... (it wasnt made for Syntrel in the first place)", color=0x7289DA, ) embed.set_author(name="Media", icon_url="https://yes.nighty.works/raw/y5SEZ9.webp")