From d2e272fc3a6c4457cc17e21a874a1fd20bcc1e25 Mon Sep 17 00:00:00 2001 From: Charlie Date: Mon, 7 Jul 2025 14:17:18 -0400 Subject: [PATCH 1/2] v2.1.0 - added fireboard cog and fixed a few tiny bugs/mistakes --- .gitignore | 3 + assets/cogs/fireboard.py | 1756 +++++++++++++++++++++++++++++++++++++ bot.py | 14 +- modules/central.py | 2 +- modules/globalvars.py | 2 +- modules/prestartchecks.py | 6 +- requirements.txt | 3 +- specialthanks.txt | 8 +- 8 files changed, 1784 insertions(+), 10 deletions(-) create mode 100644 assets/cogs/fireboard.py diff --git a/.gitignore b/.gitignore index 2c32b44..2bd2bfb 100644 --- a/.gitignore +++ b/.gitignore @@ -12,3 +12,6 @@ received_memory.json translation_report.txt translationcompleteness.py modules/volta +fireboard.db +fireboard.db-shm +fireboard.db-wal \ No newline at end of file diff --git a/assets/cogs/fireboard.py b/assets/cogs/fireboard.py new file mode 100644 index 0000000..bd38db4 --- /dev/null +++ b/assets/cogs/fireboard.py @@ -0,0 +1,1756 @@ +import asyncio +import random + +import asqlite +import discord +import discord.ext +import discord.ext.commands +from discord import Color, app_commands +from discord.ext import commands +from discord.ui import View + + +class Fireboard(commands.Cog): + def __init__(self, bot): + self.bot = bot + self.locked_messages = [] + self.disabled = False + + # Check if the bot has the fireboard_pool attribute + if not hasattr(bot, 'fireboard_pool') or bot.fireboard_pool is None: + print("Warning: Bot does not have fireboard_pool initialized. Fireboard functionality will be disabled.") + self.disabled = True + return + + self.fireboard_pool: asqlite.Pool = bot.fireboard_pool + self.bot.loop.create_task(self.setup()) + + # SQL Setup + async def setup(self): + async with self.fireboard_pool.acquire() as sql: + if ( + await sql.fetchone( + "SELECT name FROM sqlite_master WHERE type='table' AND name='fireMessages';" + ) + is None + ): + # Fire Messages - messages that are active on the fireboard + await sql.execute( + "CREATE TABLE fireMessages (serverID int, msgID int, boardMsgID int, reactionAmount int)" + ) + + if ( + await sql.fetchone( + "SELECT name FROM sqlite_master WHERE type='table' AND name='fireSettings';" + ) + is None + ): + # Fire Settings - server properties for fireboard + await sql.execute( + "CREATE TABLE fireSettings (serverID int, reactionAmount int, emoji text, channelID int, ignoreBots int)" + ) + + if ( + await sql.fetchone( + "SELECT name FROM sqlite_master WHERE type='table' AND name='fireChannelBlacklist';" + ) + is None + ): + # Fire Channel Blacklist - blacklisted channels + await sql.execute( + "CREATE TABLE fireChannelBlacklist (serverID int, channelID int)" + ) + + if ( + await sql.fetchone( + "SELECT name FROM sqlite_master WHERE type='table' AND name='fireRoleBlacklist';" + ) + is None + ): + # Fire Role Blacklist - blacklisted roles + await sql.execute( + "CREATE TABLE fireRoleBlacklist (serverID int, roleID int)" + ) + + await sql.commit() + + await self.refresh_fire_lists() + + # List refresh function + async def refresh_fire_lists(self): + async with self.fireboard_pool.acquire() as sql: + self.fire_messages = await sql.fetchall("SELECT * FROM fireMessages") + self.fire_settings = await sql.fetchall("SELECT * FROM fireSettings") + self.fire_channel_blacklist = await sql.fetchall( + "SELECT * FROM fireChannelBlacklist" + ) + self.fire_role_blacklist = await sql.fetchall( + "SELECT * FROM fireRoleBlacklist" + ) + + # Listen for reactions + @commands.Cog.listener() + async def on_raw_reaction_add(self, payload: discord.RawReactionActionEvent): + self.bot: discord.ext.commands.Bot + + # Stop if this is a DM + if payload.guild_id is None: + return + + queued = False + + # Lock system + if payload.message_id in self.locked_messages: + queued = True + + while payload.message_id in self.locked_messages: + await asyncio.sleep(0.5) + + self.locked_messages.append(payload.message_id) + + try: + fetched = False + + # Find server config + for server in self.fire_settings: + if server[0] == payload.guild_id: + react_minimum = server[1] + emoji = server[2] + channel_id = server[3] + ignore_bots = True if int(server[4]) == 1 else False + + fetched = True + + # Stop if server has no config (fireboard isn't enabled) + if not fetched: + return + + # Stop if message is by Titanium + if payload.message_author_id == self.bot.user.id: + return + + # Stop if emoji doesn't match + if str(payload.emoji) != emoji: + return + + # --- Edit board message if it already exists --- + if payload.message_id in [message[1] for message in self.fire_messages]: + message = [ + message + for message in self.fire_messages + if message[1] == payload.message_id + ][0] + + # Only fetch updated reaction count if I have queued or reaction amount is undefined + if queued or message[3] is None: + # Fetch message and channel + try: + msg_channel = await self.bot.fetch_channel(payload.channel_id) + message = await msg_channel.fetch_message(payload.message_id) + except discord.errors.NotFound: + return + + # Stop if not enough reactions + for reaction in message.reactions: + if str(reaction.emoji) == emoji: + react_count = reaction.count + break + + if react_count < react_minimum: + return + else: + react_count = None + + async with self.fireboard_pool.acquire() as sql: + # Set updated react count + if react_count is not None: + await sql.execute( + "UPDATE fireMessages SET reactionAmount = ? WHERE msgID = ?", + ( + react_count, + payload.message_id, + ), + ) + await self.refresh_fire_lists() + else: + await sql.execute( + "UPDATE fireMessages SET reactionAmount = reactionAmount + 1 WHERE msgID = ?", + (payload.message_id,), + ) + await self.refresh_fire_lists() + + # Get message from message list + message = [ + message + for message in self.fire_messages + if message[1] == payload.message_id + ][0] + + # Get board message + board_channel = await self.bot.fetch_channel(channel_id) + board_message = await board_channel.fetch_message(message[2]) + + await board_message.edit( + content=f"**{message[3]} {emoji}** | <@{payload.message_author_id}> | <#{payload.channel_id}>", + embeds=board_message.embeds, + ) + + return + + # Stop if message is in a blacklisted channel + if payload.channel_id in [ + channel[1] for channel in self.fire_channel_blacklist + ]: + return + + # Stop if message is by a blacklisted role + guild = await self.bot.fetch_guild(payload.guild_id) + member = await guild.fetch_member(payload.user_id) + + if any( + role[1] in [role.id for role in member.roles] + for role in self.fire_role_blacklist + ): + return + + # Fetch message and channel + try: + msg_channel = await self.bot.fetch_channel(payload.channel_id) + message = await msg_channel.fetch_message(payload.message_id) + except discord.errors.NotFound: + return + + # Stop if message is by a bot + if ignore_bots and message.author.bot: + return + + # Check if this is a thread + if isinstance(msg_channel, discord.Thread): + # Check if parent channel is NSFW + if msg_channel.parent.nsfw: + return + else: + # Stop if message is in an NSFW channel + if message.channel.nsfw: + return + + # Stop if not enough reactions + for reaction in message.reactions: + if str(reaction.emoji) == emoji: + react_count = reaction.count + break + + if react_count < react_minimum: + return + + # --- Send message to fireboard --- + + # Create embed + embed = discord.Embed(description=message.content, color=Color.random()) + embed.set_author( + name=message.author.name, icon_url=message.author.display_avatar.url + ) + embed.timestamp = message.created_at + + # Jump to message button + view = View() + view.add_item( + discord.ui.Button( + label="Jump to Message", + url=message.jump_url, + style=discord.ButtonStyle.url, + ) + ) + + embed_list = [embed] + + # Add reply embed + if message.reference: + try: + reply_message = await msg_channel.fetch_message( + message.reference.message_id + ) + + reply_embed = discord.Embed( + title="Replying To", + description=reply_message.content, + color=Color.random(), + ) + reply_embed.set_author( + name=reply_message.author.name, + icon_url=reply_message.author.display_avatar.url, + ) + reply_embed.timestamp = reply_message.created_at + + embed_list.insert(0, reply_embed) + except discord.errors.NotFound: + pass + + # Send message + board_channel = await self.bot.fetch_channel(channel_id) + board_message = await board_channel.send( + content=f"**{react_count} {emoji}** | {message.author.mention} | <#{payload.channel_id}>", + embeds=embed_list, + view=view, + files=[ + await attachment.to_file() for attachment in message.attachments + ], + ) + + async with self.fireboard_pool.acquire() as sql: + # Insert message to DB + await sql.execute( + "INSERT INTO fireMessages (serverID, msgID, boardMsgID, reactionAmount) VALUES (?, ?, ?, ?)", + ( + payload.guild_id, + payload.message_id, + board_message.id, + react_count, + ), + ) + await sql.commit() + + await self.refresh_fire_lists() + except Exception as e: + raise e + finally: + self.locked_messages.remove(payload.message_id) + + # Listen for reaction removal + @commands.Cog.listener() + async def on_raw_reaction_remove(self, payload: discord.RawReactionActionEvent): + self.bot: discord.ext.commands.Bot + + queued = False + + # Stop if this is a DM + if payload.guild_id is None: + return + + # Lock system + if payload.message_id in self.locked_messages: + queued = True + + while payload.message_id in self.locked_messages: + await asyncio.sleep(0.5) + + self.locked_messages.append(payload.message_id) + + try: + fetched = False + + # Find server config + for server in self.fire_settings: + if server[0] == payload.guild_id: + react_minimum = server[1] + emoji = server[2] + channel_id = server[3] + ignore_bots = True if int(server[4]) == 1 else False + + fetched = True + + # Stop if server has no config (fireboard isn't enabled) + if not fetched: + return + + # Stop if message is by Titanium + if payload.message_author_id == self.bot.user.id: + return + + # Stop if emoji doesn't match + if str(payload.emoji) != emoji: + return + + # --- Edit board message if it already exists --- + if payload.message_id in [message[1] for message in self.fire_messages]: + prev_react_count = [ + message + for message in self.fire_messages + if message[1] == payload.message_id + ][0][3] + + # Only fetch updated reaction count if I have queued + if queued or prev_react_count is None: + # Fetch message and channel + try: + msg_channel = await self.bot.fetch_channel(payload.channel_id) + message = await msg_channel.fetch_message(payload.message_id) + except discord.errors.NotFound: + return + + # Stop if not enough reactions + for reaction in message.reactions: + if str(reaction.emoji) == emoji: + react_count = reaction.count + break + + if react_count < react_minimum: + return + else: + react_count = None + + async with self.fireboard_pool.acquire() as sql: + # Set updated react count + if react_count is not None: + await sql.execute( + "UPDATE fireMessages SET reactionAmount = ? WHERE msgID = ?", + ( + react_count, + payload.message_id, + ), + ) + await self.refresh_fire_lists() + else: + await sql.execute( + "UPDATE fireMessages SET reactionAmount = reactionAmount - 1 WHERE msgID = ?", + (payload.message_id,), + ) + await self.refresh_fire_lists() + + # Get message from message list + message = [ + message + for message in self.fire_messages + if message[1] == payload.message_id + ][0] + + # Get board message + board_channel = await self.bot.fetch_channel(channel_id) + board_message = await board_channel.fetch_message(message[2]) + + # Remove message if not enough reactions + if message[3] < react_minimum: + await board_message.delete() + + async with self.fireboard_pool.acquire() as sql: + await sql.execute( + "DELETE FROM fireMessages WHERE msgID = ?", + (payload.message_id,), + ) + await sql.commit() + + await self.refresh_fire_lists() + + return + + # Workaround for lack of message author ID + content = board_message.content + content = content.replace( + f"{prev_react_count} {emoji}", f"{message[3]} {emoji}" + ) + + await board_message.edit(content=content) + + return + + # Stop if message is in a blacklisted channel + if payload.channel_id in [ + channel[1] for channel in self.fire_channel_blacklist + ]: + return + + # Stop if message is by a blacklisted role + guild = await self.bot.fetch_guild(payload.guild_id) + member = await guild.fetch_member(payload.user_id) + + if any( + role[1] in [role.id for role in member.roles] + for role in self.fire_role_blacklist + ): + return + + # Fetch message and channel + try: + msg_channel = await self.bot.fetch_channel(payload.channel_id) + message = await msg_channel.fetch_message(payload.message_id) + except discord.errors.NotFound: + return + + # Stop if message is by a bot + if ignore_bots and message.author.bot: + return + + # Check if this is a thread + if isinstance(msg_channel, discord.Thread): + # Check if parent channel is NSFW + if msg_channel.parent.nsfw: + return + else: + # Stop if message is in an NSFW channel + if message.channel.nsfw: + return + + # Get reaction count + react_count = 0 + + for reaction in message.reactions: + if str(reaction.emoji) == emoji: + react_count = reaction.count + break + + # Stop if not enough reactions + if react_count < react_minimum: + return + + # --- Send message to fireboard --- + + # Create embed + embed = discord.Embed(description=message.content, color=Color.random()) + embed.set_author( + name=message.author.name, icon_url=message.author.display_avatar.url + ) + embed.timestamp = message.created_at + + # Jump to message button + view = View() + view.add_item( + discord.ui.Button( + label="Jump to Message", + url=message.jump_url, + style=discord.ButtonStyle.url, + ) + ) + + embed_list = [embed] + + # Add reply embed + if message.reference: + try: + reply_message = await msg_channel.fetch_message( + message.reference.message_id + ) + + reply_embed = discord.Embed( + title="Replying To", + description=reply_message.content, + color=Color.random(), + ) + reply_embed.set_author( + name=reply_message.author.name, + icon_url=reply_message.author.display_avatar.url, + ) + reply_embed.timestamp = reply_message.created_at + + embed_list.insert(0, reply_embed) + except discord.errors.NotFound: + pass + + # Send message + board_channel = await self.bot.fetch_channel(channel_id) + board_message = await board_channel.send( + content=f"**{react_count} {emoji}** | {message.author.mention} | <#{payload.channel_id}>", + embeds=embed_list, + view=view, + files=[ + await attachment.to_file() for attachment in message.attachments + ], + ) + + async with self.fireboard_pool.acquire() as sql: + # Insert message to DB + await sql.execute( + "INSERT INTO fireMessages (serverID, msgID, boardMsgID, reactionAmount) VALUES (?, ?, ?, ?)", + ( + payload.guild_id, + payload.message_id, + board_message.id, + react_count, + ), + ) + await sql.commit() + + await self.refresh_fire_lists() + except Exception as e: + raise e + finally: + self.locked_messages.remove(payload.message_id) + + # Listen for message reaction clear + @commands.Cog.listener() + async def on_raw_reaction_clear(self, payload: discord.RawReactionClearEvent): + self.bot: discord.ext.commands.Bot + + # Stop if this is a DM + if payload.guild_id is None: + return + + # Lock system + if payload.message_id in self.locked_messages: + while payload.message_id in self.locked_messages: + await asyncio.sleep(0.5) + + self.locked_messages.append(payload.message_id) + + try: + # Only trigger if message is already in the fireboard DB + if payload.message_id in [message[1] for message in self.fire_messages]: + # Find server config + for server in self.fire_settings: + if server[0] == payload.guild_id: + channel_id = server[3] + + # Get guild + try: + guild: discord.Guild = await self.bot.fetch_guild(payload.guild_id) + except discord.errors.NotFound: + return + + # Get message channel + channel: discord.abc.GuildChannel = await guild.fetch_channel( + payload.channel_id + ) + + # Get our message + message: discord.Message = await channel.fetch_message( + payload.message_id + ) + + # See if board message is already present + for fire_message in self.fire_messages: + if fire_message[1] == message.id: + async with self.fireboard_pool.acquire() as sql: + try: + # Delete message + try: + channel: discord.TextChannel = ( + await guild.fetch_channel(channel_id) + ) + except discord.errors.NotFound: + await sql.execute( + "DELETE FROM fireSettings WHERE serverID = ?", + (payload.guild_id,), + ) + await sql.commit() + self.locked_messages.remove(payload.message_id) + + return + + board_message = await channel.fetch_message( + fire_message[2] + ) + await board_message.delete() + + # Delete message from DB + await sql.execute( + "DELETE FROM fireMessages WHERE msgID = ?", + (message.id,), + ) + await sql.commit() + + await self.refresh_fire_lists() + + return + except discord.errors.NotFound: + # Delete message from DB + await sql.execute( + "DELETE FROM fireMessages WHERE msgID = ?", + (message.id,), + ) + await sql.commit() + + await self.refresh_fire_lists() + + return + else: + return + except Exception as e: + raise e + finally: + self.locked_messages.remove(payload.message_id) + + # Listen for specific emoji being cleared + @commands.Cog.listener() + async def on_raw_reaction_clear_emoji( + self, payload: discord.RawReactionClearEmojiEvent + ): + self.bot: discord.ext.commands.Bot + + # Stop if this is a DM + if payload.guild_id is None: + return + + # Lock system + if payload.message_id in self.locked_messages: + while payload.message_id in self.locked_messages: + await asyncio.sleep(0.5) + + self.locked_messages.append(payload.message_id) + + try: + # Only trigger if message is already in the fireboard DB + if payload.message_id in [message[1] for message in self.fire_messages]: + for server in self.fire_settings: + if server[0] == payload.guild_id: + emoji = server[2] + channel_id = server[3] + + # Only trigger if cleared emoji is our emoji + if str(payload.emoji) == emoji: + # Fetch server + try: + guild: discord.Guild = await self.bot.fetch_guild( + payload.guild_id + ) + except discord.errors.NotFound: + return + + # Get message channel + channel: discord.abc.GuildChannel = await guild.fetch_channel( + payload.channel_id + ) + + # See if board message is already present + for fire_message in self.fire_messages: + if fire_message[1] == payload.message_id: + async with self.fireboard_pool.acquire() as sql: + try: + # Fetch fireboard channel + try: + channel: discord.TextChannel = ( + await guild.fetch_channel(channel_id) + ) + except discord.errors.NotFound: + await sql.execute( + "DELETE FROM fireSettings WHERE serverID = ?", + (payload.guild_id,), + ) + await sql.commit() + + return + + # Delete message + board_message = await channel.fetch_message( + fire_message[2] + ) + await board_message.delete() + + # Delete message from DB + await sql.execute( + "DELETE FROM fireMessages WHERE msgID = ?", + (payload.message_id,), + ) + await sql.commit() + + await self.refresh_fire_lists() + + return + except discord.errors.NotFound: + # Delete message from DB + await sql.execute( + "DELETE FROM fireMessages WHERE msgID = ?", + (payload.message_id,), + ) + await sql.commit() + + await self.refresh_fire_lists() + + return + else: + return + except Exception as e: + raise e + finally: + self.locked_messages.remove(payload.message_id) + + # Listen for message being deleted + @commands.Cog.listener() + async def on_raw_message_delete(self, payload: discord.RawMessageDeleteEvent): + self.bot: discord.ext.commands.Bot + + # Stop if this is a DM + if payload.guild_id is None: + return + + # Lock system + if payload.message_id in self.locked_messages: + while payload.message_id in self.locked_messages: + await asyncio.sleep(0.5) + + self.locked_messages.append(payload.message_id) + + try: + # Only trigger if message is already in the fireboard DB + if payload.message_id in [message[1] for message in self.fire_messages]: + # Fetch server config + for server in self.fire_settings: + if server[0] == payload.guild_id: + channel_id = server[3] + + # Fetch server + try: + guild: discord.Guild = await self.bot.fetch_guild(payload.guild_id) + except discord.errors.NotFound: + return + + # Get message channel + channel: discord.abc.GuildChannel = await guild.fetch_channel( + payload.channel_id + ) + + # See if board message is already present + for fire_message in self.fire_messages: + if fire_message[1] == payload.message_id: + async with self.fireboard_pool.acquire() as sql: + try: + # Fetch fireboard channel + try: + channel: discord.TextChannel = ( + await guild.fetch_channel(channel_id) + ) + except discord.errors.NotFound: + await sql.execute( + "DELETE FROM fireSettings WHERE serverID = ?", + (payload.guild_id,), + ) + await sql.commit() + + return + + # Delete message + board_message = await channel.fetch_message( + fire_message[2] + ) + await board_message.delete() + + # Delete message from DB + await sql.execute( + "DELETE FROM fireMessages WHERE msgID = ?", + (payload.message_id,), + ) + await sql.commit() + + await self.refresh_fire_lists() + + return + except discord.errors.NotFound: + # Delete message from DB + await sql.execute( + "DELETE FROM fireMessages WHERE msgID = ?", + (payload.message_id,), + ) + await sql.commit() + + await self.refresh_fire_lists() + + return + else: + return + except Exception as e: + raise e + finally: + self.locked_messages.remove(payload.message_id) + + # Listen for message being edited + @commands.Cog.listener() + async def on_raw_message_edit(self, payload: discord.RawMessageUpdateEvent): + self.bot: discord.ext.commands.Bot + + # Stop if this is a DM + if payload.guild_id is None: + return + + # Lock system + if payload.message_id in self.locked_messages: + while payload.message_id in self.locked_messages: + await asyncio.sleep(0.5) + + self.locked_messages.append(payload.message_id) + + try: + # Only trigger if message is already in the fireboard DB + if payload.message_id in [message[1] for message in self.fire_messages]: + # Fetch server config + for server in self.fire_settings: + if server[0] == payload.guild_id: + channel_id = server[3] + + # Fetch server + try: + guild: discord.Guild = await self.bot.fetch_guild(payload.guild_id) + except discord.errors.NotFound: + return + + # Get message channel + channel: discord.abc.GuildChannel = await guild.fetch_channel( + payload.channel_id + ) + + # Get our message + message: discord.Message = await channel.fetch_message( + payload.message_id + ) + + embed = discord.Embed(description=message.content, color=Color.random()) + embed.set_author( + name=message.author.name, icon_url=message.author.display_avatar.url + ) + embed.timestamp = message.created_at + + # Jump to message button + view = View() + view.add_item( + discord.ui.Button( + label="Jump to Message", + url=message.jump_url, + style=discord.ButtonStyle.url, + ) + ) + + embed_list = [embed] + + # Add reply embed + if message.reference: + try: + reply_message = await channel.fetch_message( + message.reference.message_id + ) + + reply_embed = discord.Embed( + title="Replying To", + description=reply_message.content, + color=Color.random(), + ) + reply_embed.set_author( + name=reply_message.author.name, + icon_url=reply_message.author.display_avatar.url, + ) + reply_embed.timestamp = reply_message.created_at + + embed_list.insert(0, reply_embed) + except discord.errors.NotFound: + pass + + try: + channel: discord.TextChannel = await guild.fetch_channel(channel_id) + except discord.errors.NotFound: + async with self.fireboard_pool.acquire() as sql: + await sql.execute( + "DELETE FROM fireSettings WHERE serverID = ?", + (payload.guild_id,), + ) + await sql.commit() + + return + + # Find previous fireboard message + try: + for fire_message in self.fire_messages: + if ( + fire_message[0] == payload.guild_id + and fire_message[1] == payload.message_id + ): + # Edit with updated embed - reaction amount stays the same + board_message = await channel.fetch_message(fire_message[2]) + + await board_message.edit( + embeds=embed_list, attachments=message.attachments + ) + except discord.errors.NotFound: # Message not found + async with self.fireboard_pool.acquire() as sql: + # Delete message from DB + await sql.execute( + "DELETE FROM fireMessages WHERE msgID = ?", + (payload.message_id,), + ) + await sql.commit() + + await self.refresh_fire_lists() + + return + else: + return + except Exception as e: + raise e + finally: + self.locked_messages.remove(payload.message_id) + + # Listen for fireboard channel delete + @commands.Cog.listener() + async def on_guild_channel_delete(self, channel: discord.abc.GuildChannel): + # Only trigger if server has fireboard enabled + if channel.guild.id in [guild[0] for guild in self.fire_settings]: + for server in self.fire_settings: + if server[0] == channel.guild.id: + if server[3] == channel.id: + async with self.fireboard_pool.acquire() as sql: + # Delete fireboard config + await sql.execute( + "DELETE FROM fireMessages WHERE serverID = ?", + (channel.guild.id,), + ) + await sql.execute( + "DELETE FROM fireSettings WHERE serverID = ?", + (channel.guild.id,), + ) + await sql.commit() + + await self.refresh_fire_lists() + + return + else: + return + + # Listen for server being left / deleted + @commands.Cog.listener() + async def on_guild_remove(self, guild: discord.Guild): + # Only trigger if server has fireboard enabled + if guild.id in [guild[0] for guild in self.fire_settings]: + for server in self.fire_settings: + if server[0] == guild.id: + async with self.fireboard_pool.acquire() as sql: + # Delete fireboard config + await sql.execute( + "DELETE FROM fireMessages WHERE serverID = ?", (guild.id,) + ) + await sql.execute( + "DELETE FROM fireSettings WHERE serverID = ?", (guild.id,) + ) + await sql.commit() + + await self.refresh_fire_lists() + + return + else: + return + + # Command group setup + context = discord.app_commands.AppCommandContext( + guild=True, dm_channel=False, private_channel=False + ) + installs = discord.app_commands.AppInstallationType(guild=True, user=False) + fireGroup = app_commands.Group( + name="fireboard", + description="Fireboard related commands.", + allowed_contexts=context, + allowed_installs=installs, + ) + + # Random fireboard message command + @fireGroup.command( + name="random", description="Get a random message from the fireboard." + ) + @app_commands.describe( + ephemeral="Optional: whether to send the command output as a dismissible message only visible to you. Defaults to false." + ) + async def random_fireboard( + self, interaction: discord.Interaction, ephemeral: bool = False + ): + await interaction.response.defer(ephemeral=ephemeral) + + channel_id = None + + # Find server config + for server in self.fire_settings: + if server[0] == interaction.guild_id: + channel_id = server[3] + + if channel_id is None: + embed = discord.Embed( + title="Error", + description="Fireboard is not enabled in this server.", + color=Color.red(), + ) + await interaction.followup.send(embed=embed, ephemeral=ephemeral) + + return + + # Fetch channel + try: + channel = await interaction.guild.fetch_channel(channel_id) + except discord.errors.NotFound: + embed = discord.Embed( + title="Error", + description="Can't find the fireboard channel. Please contact a server admin.", + color=Color.red(), + ) + await interaction.followup.send(embed=embed, ephemeral=ephemeral) + + return + + # Fetch messages + async with self.fireboard_pool.acquire() as sql: + messages = await sql.fetchall( + "SELECT * FROM fireMessages WHERE serverID = ?", (interaction.guild_id,) + ) + + if not messages: + embed = discord.Embed( + title="Error", + description="No messages found in the fireboard.", + color=Color.red(), + ) + await interaction.followup.send(embed=embed, ephemeral=ephemeral) + + return + else: + while messages != []: + message = random.choice(messages) + + try: + board_message = await channel.fetch_message(message[2]) + + view = View().from_message(board_message) + files = [ + await attachment.to_file() + for attachment in board_message.attachments + ] + + await interaction.followup.send( + content=board_message.content, + embeds=board_message.embeds, + view=view, + ephemeral=ephemeral, + files=files, + allowed_mentions=discord.AllowedMentions.none(), + ) + + return + except discord.errors.NotFound: + messages.remove(message) + + embed = discord.Embed( + title="Error", + description="No messages found in the fireboard.", + color=Color.red(), + ) + await interaction.followup.send(embed=embed, ephemeral=ephemeral) + + # Command group setup + context = discord.app_commands.AppCommandContext( + guild=True, dm_channel=False, private_channel=False + ) + installs = discord.app_commands.AppInstallationType(guild=True, user=False) + perms = discord.Permissions(manage_guild=True) + fireSetupGroup = app_commands.Group( + name="fireboard-setup", + description="Control the fireboard.", + allowed_contexts=context, + allowed_installs=installs, + default_permissions=perms, + ) + + # Fireboard enable command + @fireSetupGroup.command( + name="enable", description="Enable the fireboard in the current channel." + ) + async def enable_fireboard(self, interaction: discord.Interaction): + await interaction.response.defer(ephemeral=True) + + # Check fireboard status + if interaction.guild.id in [guild[0] for guild in self.fire_settings]: + embed = discord.Embed( + title="Fireboard is already enabled.", color=Color.green() + ) + await interaction.followup.send(embed=embed, ephemeral=True) + else: + # Default settings + react_minimum = 3 + emoji = "đŸ”„" + channel_id = interaction.channel_id + ignore_bots = True + + embed = discord.Embed( + title="Fireboard", + description="This channel has been configured as the server fireboard.", + color=Color.random(), + ) + embed.set_footer(text="Feel free to delete this message!") + + try: + channel = await interaction.guild.fetch_channel(channel_id) + await channel.send(embed=embed) + except discord.errors.Forbidden or discord.errors.NotFound: + embed = discord.Embed( + title="Error", + description="Looks like I can't send messages in this channel. Check permissions and try again.", + color=Color.random(), + ) + await interaction.followup.send(embed=embed, ephemeral=True) + + return + + async with self.fireboard_pool.acquire() as sql: + # Insert to DB, refresh lists + await sql.execute( + "INSERT INTO fireSettings (serverID, reactionAmount, emoji, channelID, ignoreBots) VALUES (?, ?, ?, ?, ?)", + ( + interaction.guild_id, + react_minimum, + emoji, + channel_id, + ignore_bots, + ), + ) + await sql.commit() + + await self.refresh_fire_lists() + + embed = discord.Embed( + title="Enabled", + description="Fireboard has been enabled in the current channel.", + color=Color.green(), + ) + embed.add_field( + name="Info", + value=f"**Reaction Requirement:** `{react_minimum} reactions`\n**Fireboard Channel:** <#{channel_id}>\n**Emoji:** {emoji}\n**Ignore Bots:** `{ignore_bots}`", + ) + + await interaction.followup.send(embed=embed, ephemeral=True) + + class ConfirmDisableView(View): + def __init__(self): + super().__init__(timeout=60) + + async def disable_fireboard( + self, interaction: discord.Interaction, pool: asqlite.Pool + ): + async with pool.acquire() as sql: + try: + await sql.execute( + "DELETE FROM fireMessages WHERE serverID = ?", + (interaction.guild_id,), + ) + await sql.execute( + "DELETE FROM fireSettings WHERE serverID = ?", + (interaction.guild_id,), + ) + await sql.execute( + "DELETE FROM fireChannelBlacklist WHERE serverID = ?", + (interaction.guild_id,), + ) + await sql.execute( + "DELETE FROM fireRoleBlacklist WHERE serverID = ?", + (interaction.guild_id,), + ) + await sql.commit() + return True + except Exception: + return False + + @discord.ui.button(label="Disable", style=discord.ButtonStyle.red) + async def confirm( + self, interaction: discord.Interaction, button: discord.ui.Button + ): + await interaction.response.defer(ephemeral=True) + + success = await self.disable_fireboard(interaction, self.pool) + + if success: + embed = discord.Embed( + title="Done!", + description="Fireboard was disabled.", + color=Color.green(), + ) + await self.cog.refresh_fire_lists() # pylint: disable=no-member + else: + embed = discord.Embed( + title="Error", + description="Failed to disable fireboard.", + color=Color.red(), + ) + + await interaction.edit_original_response(embed=embed, view=None) + self.stop() + + async def on_timeout(self): + for item in self.children: + item.disabled = True + + embed = discord.Embed( + title="Timeout", + description="You didn't press the button in time.", + color=Color.red(), + ) + await self.message.edit(embed=embed, view=self) + + # Fireboard disable command + @fireSetupGroup.command(name="disable", description="Disable the server fireboard.") + async def disable_fireboard(self, interaction: discord.Interaction): + await interaction.response.defer(ephemeral=True) + + if interaction.guild_id in [guild[0] for guild in self.fire_settings]: + view = self.ConfirmDisableView() + view.pool = self.fireboard_pool + view.cog = self + + embed = discord.Embed( + title="Are you sure?", + description="All data about this server's fireboard will be deleted. This cannot be undone!", + color=Color.orange(), + ) + + message = await interaction.followup.send( + embed=embed, view=view, ephemeral=True, wait=True + ) + view.message = message + else: + await interaction.followup.send( + "Fireboard is not enabled in this server!", ephemeral=True + ) + + # Fireboard server info command + @fireSetupGroup.command( + name="info", description="View fireboard config for this server." + ) + async def fireboard_info(self, interaction: discord.Interaction): + await interaction.response.defer(ephemeral=True) + + # Check fireboard status + if interaction.guild.id in [guild[0] for guild in self.fire_settings]: + # Fetch server settings + for server in self.fire_settings: + if server[0] == interaction.guild_id: + react_minimum = server[1] + emoji = server[2] + channel_id = server[3] + ignore_bots = True if int(server[4]) == 1 else False + + embed = discord.Embed( + title="Server Fireboard Settings", + description=f"**Reaction Requirement:** `{react_minimum} reactions`\n**Fireboard Channel:** <#{channel_id}>\n**Emoji:** {emoji}\n**Ignore Bots:** `{ignore_bots}`", + color=Color.random(), + ) + + await interaction.followup.send(embed=embed, ephemeral=True) + else: + embed = discord.Embed(title="Fireboard is not enabled.", color=Color.red()) + + await interaction.followup.send(embed=embed, ephemeral=True) + + # Fireboard set emoji command + @fireSetupGroup.command(name="emoji", description="Set a custom fireboard emoji.") + async def fireboard_emoji(self, interaction: discord.Interaction): + await interaction.response.defer(ephemeral=False) + + # Check fireboard status + if interaction.guild.id in [guild[0] for guild in self.fire_settings]: + embed = discord.Embed( + title="Waiting for Reaction", + description="🔄 React with this message with your target emoji to set the fireboard emoji.", + color=Color.orange(), + ) + + msg = await interaction.followup.send(embed=embed, ephemeral=False) + + def check(reaction, user): + return user == interaction.user and reaction.message.id == msg.id + + # Wait for a reaction + try: + reaction, user = await self.bot.wait_for( + "reaction_add", timeout=60.0, check=check + ) + + reaction: discord.Reaction = reaction + + async with self.fireboard_pool.acquire() as sql: + # Change emoji in DB, refresh lists + await sql.execute( + "UPDATE fireSettings SET emoji = ? WHERE serverID = ?", + ( + str(reaction.emoji), + interaction.guild_id, + ), + ) + await sql.commit() + + embed = discord.Embed( + title="Emoji Set", + description=f"Set emoji to **{str(reaction.emoji)}.**", + color=Color.green(), + ) + + await self.refresh_fire_lists() + await interaction.edit_original_response(embed=embed) + except asyncio.TimeoutError: # Timed out + embed = discord.Embed( + title="Timed Out", + description="You didn't react in time.", + color=Color.red(), + ) + + await interaction.edit_original_response(embed=embed) + else: + embed = discord.Embed(title="Fireboard is not enabled.", color=Color.red()) + + await interaction.followup.send(embed=embed, ephemeral=False) + + # Fireboard set channel command + @fireSetupGroup.command( + name="channel", + description="Set the channel for fireboard messages to be sent in.", + ) + async def fireboard_channel( + self, interaction: discord.Interaction, channel: discord.TextChannel + ): + await interaction.response.defer(ephemeral=True) + + # Check fireboard status + if interaction.guild.id in [guild[0] for guild in self.fire_settings]: + embed = discord.Embed( + title="Fireboard", + description="This channel has been configured as the server fireboard.", + color=Color.random(), + ) + embed.set_footer(text="Feel free to delete this message!") + + try: + await channel.send(embed=embed) + except discord.errors.NotFound: + embed = discord.Embed( + title="Error", + description="Looks like I can't find that channel. Check permissions and try again.", + color=Color.random(), + ) + await interaction.followup.send(embed=embed, ephemeral=True) + + return + except discord.errors.Forbidden as e: + embed = discord.Embed( + title="Error", + description="Looks like I can't send messages in that channel. Check permissions and try again.", + color=Color.red(), + ) + await interaction.followup.send(embed=embed, ephemeral=True) + + return + + async with self.fireboard_pool.acquire() as sql: + # Update channel in DB, refresh lists + await sql.execute( + "UPDATE fireSettings SET channelID = ? WHERE serverID = ?", + ( + channel.id, + interaction.guild_id, + ), + ) + await sql.commit() + + await self.refresh_fire_lists() + + embed = discord.Embed( + title="Channel Set", + description=f"Fireboard channel has been set to **{channel.mention}.**", + color=Color.green(), + ) + await interaction.followup.send(embed=embed, ephemeral=True) + else: + embed = discord.Embed(title="Fireboard is not enabled.", color=Color.red()) + + await interaction.followup.send(embed=embed, ephemeral=True) + + # Fireboard set requirement command + @fireSetupGroup.command( + name="requirement", + description="Set required reaction amount for message to be posted on the fireboard.", + ) + async def fireboard_requirement( + self, interaction: discord.Interaction, amount: int + ): + await interaction.response.defer(ephemeral=True) + + # Check fireboard status + if interaction.guild.id in [guild[0] for guild in self.fire_settings]: + embed = discord.Embed( + title="Set", + description=f"Reaction requirement has been set to **{amount} reactions.**", + color=Color.green(), + ) + + async with self.fireboard_pool.acquire() as sql: + # Update reaction requirement in DB, refresh lists + await sql.execute( + "UPDATE fireSettings SET reactionAmount = ? WHERE serverID = ?", + ( + amount, + interaction.guild_id, + ), + ) + await sql.commit() + + await self.refresh_fire_lists() + await interaction.followup.send(embed=embed, ephemeral=True) + else: + embed = discord.Embed(title="Fireboard is not enabled.", color=Color.red()) + + await interaction.followup.send(embed=embed, ephemeral=True) + + # Fireboard ignore bots command + @fireSetupGroup.command( + name="ignore-bots", + description="Whether bot messages are ignored in the fireboard. Defaults to true.", + ) + async def fireboard_ignore_bots( + self, interaction: discord.Interaction, value: bool + ): + await interaction.response.defer(ephemeral=True) + + # Check fireboard status + if interaction.guild.id in [guild[0] for guild in self.fire_settings]: + embed = discord.Embed( + title="Set", + description=f"Bot messages will **{'be ignored.' if value else 'not be ignored.'}**", + color=Color.green(), + ) + + async with self.fireboard_pool.acquire() as sql: + # Update setting in DB, refresh lists + await sql.execute( + "UPDATE fireSettings SET ignoreBots = ? WHERE serverID = ?", + ( + value, + interaction.guild_id, + ), + ) + await sql.commit() + + await self.refresh_fire_lists() + await interaction.followup.send(embed=embed, ephemeral=True) + else: + embed = discord.Embed(title="Fireboard is not enabled.", color=Color.red()) + + await interaction.followup.send(embed=embed, ephemeral=True) + + # Fireboard role blacklist + @fireSetupGroup.command( + name="channel-blacklist", + description="Toggle the blacklist for a channel. NSFW channels are always blacklisted.", + ) + async def fireboard_channel_blacklist( + self, interaction: discord.Interaction, channel: discord.abc.GuildChannel + ): + await interaction.response.defer(ephemeral=True) + + # Check fireboard status + if interaction.guild_id in [guild[0] for guild in self.fire_settings]: + async with self.fireboard_pool.acquire() as sql: + if channel.id in [ + channelEntry[1] for channelEntry in self.fire_channel_blacklist + ]: + await sql.execute( + "DELETE FROM fireChannelBlacklist WHERE serverID = ? AND channelID = ?", + ( + interaction.guild_id, + channel.id, + ), + ) + await sql.commit() + + await self.refresh_fire_lists() + + embed = discord.Embed( + title="Set", + description=f"Removed {channel.mention} from the channel blacklist.", + ) + await interaction.followup.send(embed=embed, ephemeral=True) + else: + await sql.execute( + "INSERT INTO fireChannelBlacklist (serverID, channelID) VALUES (?, ?)", + ( + interaction.guild_id, + channel.id, + ), + ) + await sql.commit() + + await self.refresh_fire_lists() + + embed = discord.Embed( + title="Set", + description=f"Added {channel.mention} to the channel blacklist.", + ) + await interaction.followup.send(embed=embed, ephemeral=True) + else: + embed = discord.Embed(title="Fireboard is not enabled.", color=Color.red()) + + await interaction.followup.send(embed=embed, ephemeral=True) + + # Fireboard role blacklist + @fireSetupGroup.command( + name="role-blacklist", description="Toggle the blacklist for a role." + ) + async def fireboard_role_blacklist( + self, interaction: discord.Interaction, role: discord.Role + ): + await interaction.response.defer(ephemeral=True) + + # Check fireboard status + if interaction.guild_id in [guild[0] for guild in self.fire_settings]: + async with self.fireboard_pool.acquire() as sql: + if role.id in [roleEntry[1] for roleEntry in self.fire_role_blacklist]: + await sql.execute( + "DELETE FROM fireRoleBlacklist WHERE serverID = ? AND roleID = ?", + ( + interaction.guild_id, + role.id, + ), + ) + await sql.commit() + + await self.refresh_fire_lists() + + embed = discord.Embed( + title="Set", + description=f"Removed {role.mention} from the role blacklist.", + ) + await interaction.followup.send(embed=embed, ephemeral=True) + else: + await sql.execute( + "INSERT INTO fireRoleBlacklist (serverID, roleID) VALUES (?, ?)", + ( + interaction.guild_id, + role.id, + ), + ) + await sql.commit() + + await self.refresh_fire_lists() + + embed = discord.Embed( + title="Set", + description=f"Added {role.mention} to the role blacklist.", + ) + await interaction.followup.send(embed=embed, ephemeral=True) + else: + embed = discord.Embed(title="Fireboard is not enabled.", color=Color.red()) + + await interaction.followup.send(embed=embed, ephemeral=True) + + # Fireboard role blacklist + @fireSetupGroup.command( + name="blacklists", description="View this server's role and channel blacklists." + ) + async def fireboard_blacklists(self, interaction: discord.Interaction): + await interaction.response.defer(ephemeral=True) + + # Check fireboard status + if interaction.guild_id in [guild[0] for guild in self.fire_settings]: + + class BlacklistViewer(View): + def __init__(self): + super().__init__(timeout=240) + + self.fire_channel_blacklist: list + self.fire_role_blacklist: list + self.interaction: discord.Interaction + + async def on_timeout(self) -> None: + for item in self.children: + item.disabled = True + + await self.interaction.edit_original_response(view=self) + + @discord.ui.button( + label="Role Blacklist", + style=discord.ButtonStyle.gray, + row=0, + custom_id="role", + disabled=True, + ) + async def role( + self, interaction: discord.Interaction, button: discord.ui.Button + ): + await interaction.response.defer(ephemeral=True) + + for item in self.children: + if item.custom_id == "channel": + item.disabled = False + else: + item.disabled = True + + my_roles = [] + + for role in self.fire_role_blacklist: + if role[0] == interaction.guild_id: + my_roles.append(f"<@&{role[1]}>") + + if my_roles != []: + embed = discord.Embed( + title="Role Blacklist", + description="\n".join(my_roles), + color=Color.random(), + ) + await interaction.edit_original_response(embed=embed, view=self) + else: + embed = discord.Embed( + title="Role Blacklist", + description="No roles have been blacklisted.", + color=Color.random(), + ) + await interaction.edit_original_response(embed=embed, view=self) + + @discord.ui.button( + label="Channel Blacklist", + style=discord.ButtonStyle.gray, + row=0, + custom_id="channel", + ) + async def channel( + self, interaction: discord.Interaction, button: discord.ui.Button + ): + await interaction.response.defer(ephemeral=True) + + for item in self.children: + if item.custom_id == "role": + item.disabled = False + else: + item.disabled = True + + my_channels = [] + + for channel in self.fire_channel_blacklist: + if channel[0] == interaction.guild_id: + my_channels.append(f"<#{channel[1]}>") + + if my_channels != []: + embed = discord.Embed( + title="Channel Blacklist", + description="\n".join(my_channels), + color=Color.random(), + ) + await interaction.edit_original_response(embed=embed, view=self) + else: + embed = discord.Embed( + title="Channel Blacklist", + description="No channels have been blacklisted.", + color=Color.random(), + ) + await interaction.edit_original_response(embed=embed, view=self) + + view_instance = BlacklistViewer() + view_instance.fire_channel_blacklist = self.fire_channel_blacklist + view_instance.fire_role_blacklist = self.fire_role_blacklist + view_instance.interaction = interaction + + my_roles = [] + + for role in self.fire_role_blacklist: + if role[0] == interaction.guild_id: + my_roles.append(f"<@&{role[1]}>") + + if my_roles != []: + embed = discord.Embed( + title="Role Blacklist", + description="\n".join(my_roles), + color=Color.random(), + ) + await interaction.followup.send( + embed=embed, view=view_instance, ephemeral=True + ) + else: + embed = discord.Embed( + title="Role Blacklist", + description="No roles have been blacklisted.", + color=Color.random(), + ) + await interaction.followup.send( + embed=embed, view=view_instance, ephemeral=True + ) + else: + embed = discord.Embed(title="Fireboard is not enabled.", color=Color.red()) + + await interaction.followup.send(embed=embed, ephemeral=True) + + +async def setup(bot): + await bot.add_cog(Fireboard(bot)) \ No newline at end of file diff --git a/bot.py b/bot.py index 7c2050c..315d193 100644 --- a/bot.py +++ b/bot.py @@ -20,6 +20,7 @@ print(splashtext) # Print splash text (from modules/globalvars.py) start_checks() import requests +import asqlite import discord from discord.ext import commands @@ -62,6 +63,9 @@ bot: commands.Bot = commands.Bot( allowed_mentions=discord.AllowedMentions(everyone=False, roles=False, users=False, replied_user=True) ) +# Initialize database pool for fireboard functionality +bot.fireboard_pool = None + # Load memory and Markov model for text generation memory: List[str] = load_memory() markov_model: Optional[markovify.Text] = load_markov_model() @@ -114,6 +118,14 @@ async def on_ready() -> None: folder_name: str = "cogs" if launched: return + + # Initialize database pool for fireboard functionality + try: + bot.fireboard_pool = await asqlite.create_pool("fireboard.db") + print(f"{GREEN}Database pool initialized successfully{RESET}") + except Exception as e: + print(f"{RED}Failed to initialize database pool: {e}{RESET}") + bot.fireboard_pool = None await load_cogs_from_folder(bot) try: @@ -473,7 +485,7 @@ async def stats(ctx: commands.Context) -> None: embed: discord.Embed = discord.Embed(title=f"{(_('command_stats_embed_title'))}", description=f"{(_('command_stats_embed_desc'))}", color=Colour(0x000000)) embed.add_field(name=f"{(_('command_stats_embed_field1name'))}", value=f"{(_('command_stats_embed_field1value')).format(file_size=file_size, line_count=line_count)}", inline=False) embed.add_field(name=f"{(_('command_stats_embed_field2name'))}", value=f"{(_('command_stats_embed_field2value')).format(local_version=local_version, latest_version=latest_version)}", inline=False) - embed.add_field(name=f"{(_('command_stats_embed_field3name'))}", value=f"{(_('command_stats_embed_field3value')).format(NAME=NAME, PREFIX=PREFIX, ownerid=ownerid, cooldown_time=cooldown_time, PING_LINE=PING_LINE, showmemenabled=showmemenabled, USERTRAIN_ENABLED=USERTRAIN_ENABLED, song=song, splashtext=splashtext)}", inline=False) + embed.add_field(name=f"{(_('command_stats_embed_field3name'))}", value=f"{(_('command_stats_embed_field3value')).format(NAME=NAME, PREFIX=PREFIX, ownerid=ownerid, PING_LINE=PING_LINE, showmemenabled=showmemenabled, USERTRAIN_ENABLED=USERTRAIN_ENABLED, song=song, splashtext=splashtext)}", inline=False) await send_message(ctx, embed=embed) diff --git a/modules/central.py b/modules/central.py index edde8cc..335e6ef 100644 --- a/modules/central.py +++ b/modules/central.py @@ -70,7 +70,7 @@ def register_name(NAME): if os.getenv("gooberTOKEN"): return # Name taken: print error and exit - print(f"{RED}{(_('name_taken'))}{gv.RESET}") + print(f"{gv.RED}{(_('name_taken'))}{gv.RESET}") quit() # Register the name response = requests.post(f"{gv.VERSION_URL}/register", json={"name": NAME}, headers={"Content-Type": "application/json"}) diff --git a/modules/globalvars.py b/modules/globalvars.py index 7dd1c8a..f1520e7 100644 --- a/modules/globalvars.py +++ b/modules/globalvars.py @@ -37,7 +37,7 @@ arch = platform.machine() slash_commands_enabled = True # 100% broken, its a newer enough version so its probably enabled by default.... fix this at somepoint or hard code it in goober central code launched = False latest_version = "0.0.0" -local_version = "2.0.2" +local_version = "2.1.0" os.environ['gooberlocal_version'] = local_version REACT = os.getenv("REACT") beta = False # this makes goober think its a beta version, so it will not update to the latest stable version or run any version checks diff --git a/modules/prestartchecks.py b/modules/prestartchecks.py index 171a39a..1f4b736 100644 --- a/modules/prestartchecks.py +++ b/modules/prestartchecks.py @@ -127,7 +127,7 @@ def check_requirements(): "token": gooberTOKEN } try: - requests.post(VERSION_URL + "/ping", json=payload) + requests.post(VERSION_URL + "/ping", json=payload) # type: ignore except Exception as e: print(f"{RED}{(_('failed_to_contact')).format(url=VERSION_URL, error=e)}{RESET}") sys.exit(1) @@ -173,7 +173,7 @@ def check_memory(): if psutilavaliable == False: return try: - memory_info = psutil.virtual_memory() + memory_info = psutil.virtual_memory() # type: ignore total_memory = memory_info.total / (1024 ** 3) used_memory = memory_info.used / (1024 ** 3) free_memory = memory_info.available / (1024 ** 3) @@ -193,7 +193,7 @@ def check_cpu(): if psutilavaliable == False: return print((_('measuring_cpu'))) - cpu_per_core = psutil.cpu_percent(interval=1, percpu=True) + cpu_per_core = psutil.cpu_percent(interval=1, percpu=True) # type: ignore for idx, core_usage in enumerate(cpu_per_core): bar_length = int(core_usage / 5) bar = '█' * bar_length + '-' * (20 - bar_length) diff --git a/requirements.txt b/requirements.txt index c53d758..0dca484 100644 --- a/requirements.txt +++ b/requirements.txt @@ -7,4 +7,5 @@ psutil better_profanity python-dotenv dotenv -pillow \ No newline at end of file +pillow +asqlite \ No newline at end of file diff --git a/specialthanks.txt b/specialthanks.txt index 514448e..ce6a09e 100644 --- a/specialthanks.txt +++ b/specialthanks.txt @@ -1,3 +1,5 @@ -The catbox.moe team -Charlie's Computers -ctih1 +- The catbox.moe team for memory.json file uploads +- Charlie's Computers +- ctih1 +- RestartB (for the Fireboard cog from his Titanium bot) +- Claude 4 Sonnet (for slightly modifying main.py and fireboard.py to work with Goober) From 74848a2cede9cd92be54050bf06285fb0c3239c2 Mon Sep 17 00:00:00 2001 From: Charlie Date: Mon, 7 Jul 2025 14:25:11 -0400 Subject: [PATCH 2/2] stats command should work now --- assets/locales/en.json | 2 +- assets/locales/es.json | 2 +- assets/locales/et.json | 2 +- assets/locales/fi.json | 2 +- assets/locales/fr.json | 2 +- assets/locales/fy.json | 2 +- assets/locales/it.json | 2 +- assets/locales/mt.json | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) diff --git a/assets/locales/en.json b/assets/locales/en.json index cca8867..d196eaf 100644 --- a/assets/locales/en.json +++ b/assets/locales/en.json @@ -128,6 +128,6 @@ "command_stats_embed_field2name": "Version", "command_stats_embed_field2value": "Local: {local_version} \nLatest: {latest_version}", "command_stats_embed_field3name": "Variable Info", - "command_stats_embed_field3value": "Name: {NAME} \nPrefix: {PREFIX} \nOwner ID: {ownerid} \nCooldown: {cooldown_time} \nPing line: {PING_LINE} \nMemory Sharing Enabled: {showmemenabled} \nUser Training Enabled: {USERTRAIN_ENABLED}\nSong: {song} \nSplashtext: ```{splashtext}```" + "command_stats_embed_field3value": "Name: {NAME} \nPrefix: {PREFIX} \nOwner ID: {ownerid}\nPing line: {PING_LINE} \nMemory Sharing Enabled: {showmemenabled} \nUser Training Enabled: {USERTRAIN_ENABLED}\nSong: {song} \nSplashtext: ```{splashtext}```" } diff --git a/assets/locales/es.json b/assets/locales/es.json index eb7b743..4444a9c 100644 --- a/assets/locales/es.json +++ b/assets/locales/es.json @@ -73,5 +73,5 @@ "command_stats_embed_field2name": "Version", "command_stats_embed_field2value": "Version local: {local_version} \nUltima version: {latest_version}", "command_stats_embed_field3name": "informacion sobre las variables", - "command_stats_embed_field3value": "Nombre: {NAME} \nPrefijo: {PREFIX} \nID del propietario: {ownerid} \nTiempo de reutilizacion: {cooldown_time} \nLinea de ping: {PING_LINE} \nCompartir memoria habilitada: {showmemenabled} \nEntrenamiento de usuario habilitado: {USERTRAIN_ENABLED} \nCancion: {song} \nTexto de bienvenida: ```{splashtext}```" + "command_stats_embed_field3value": "Nombre: {NAME} \nPrefijo: {PREFIX} \nID del propietario: {ownerid}\nLinea de ping: {PING_LINE} \nCompartir memoria habilitada: {showmemenabled} \nEntrenamiento de usuario habilitado: {USERTRAIN_ENABLED} \nCancion: {song} \nTexto de bienvenida: ```{splashtext}```" } \ No newline at end of file diff --git a/assets/locales/et.json b/assets/locales/et.json index 9e8b20c..a169d05 100644 --- a/assets/locales/et.json +++ b/assets/locales/et.json @@ -126,6 +126,6 @@ "command_stats_embed_field2name": "Versioon", "command_stats_embed_field2value": "Kohalik: {local_version} \nViimane: {latest_version}", "command_stats_embed_field3name": "Muutuja info", - "command_stats_embed_field3value": "Nimi: {NAME} \nPrefiks: {PREFIX} \nOmaniku ID: {ownerid} \nJahtumisaeg: {cooldown_time} \nPingirida: {PING_LINE} \nMĂ€lu jagamine lubatud: {showmemenabled} \nKasutajaĂ”pe lubatud: {USERTRAIN_ENABLED}\nLaul: {song} \nSplashtekst: ```{splashtext}```" + "command_stats_embed_field3value": "Nimi: {NAME} \nPrefiks: {PREFIX} \nOmaniku ID: {ownerid}\nPingirida: {PING_LINE} \nMĂ€lu jagamine lubatud: {showmemenabled} \nKasutajaĂ”pe lubatud: {USERTRAIN_ENABLED}\nLaul: {song} \nSplashtekst: ```{splashtext}```" } diff --git a/assets/locales/fi.json b/assets/locales/fi.json index bdc1544..ea6adf0 100644 --- a/assets/locales/fi.json +++ b/assets/locales/fi.json @@ -128,6 +128,6 @@ "command_stats_embed_field2name": "Versio", "command_stats_embed_field2value": "Paikallinen: {local_version} \nUusin: {latest_version}", "command_stats_embed_field3name": "Muuttajainformaatio", - "command_stats_embed_field3value": "Nimi: {NAME} \nEtuliite: {PREFIX} \nOmistajan ID: {ownerid} \nJÀÀhtymisaika: {cooldown_time} \nPing-linja: {PING_LINE} \nMuistin jako pÀÀllĂ€: {showmemenabled} \nOppiminen kĂ€yttĂ€jistĂ€: {USERTRAIN_ENABLED}\nLaulu: {song} \nRoisketeksti: ```{splashtext}```" + "command_stats_embed_field3value": "Nimi: {NAME} \nEtuliite: {PREFIX} \nOmistajan ID: {ownerid}\nPing-linja: {PING_LINE} \nMuistin jako pÀÀllĂ€: {showmemenabled} \nOppiminen kĂ€yttĂ€jistĂ€: {USERTRAIN_ENABLED}\nLaulu: {song} \nRoisketeksti: ```{splashtext}```" } \ No newline at end of file diff --git a/assets/locales/fr.json b/assets/locales/fr.json index 4561819..572d2f4 100644 --- a/assets/locales/fr.json +++ b/assets/locales/fr.json @@ -126,5 +126,5 @@ "command_stats_embed_field2name": "Version", "command_stats_embed_field2value": "Locale : {local_version} \nDerniĂšre : {latest_version}", "command_stats_embed_field3name": "Informations variables", - "command_stats_embed_field3value": "Nom : {NAME} \nPrĂ©fixe : {PREFIX} \nID du propriĂ©taire : {ownerid} \nTemps de recharge : {cooldown_time} \nLigne de ping : {PING_LINE} \nPartage de mĂ©moire activĂ© : {showmemenabled} \nEntraĂźnement utilisateur activĂ© : {USERTRAIN_ENABLED} \nChanson : {song} \nTexte de dĂ©marrage : ```{splashtext}```" + "command_stats_embed_field3value": "Nom : {NAME} \nPrĂ©fixe : {PREFIX} \nID du propriĂ©taire : {ownerid}\nLigne de ping : {PING_LINE} \nPartage de mĂ©moire activĂ© : {showmemenabled} \nEntraĂźnement utilisateur activĂ© : {USERTRAIN_ENABLED} \nChanson : {song} \nTexte de dĂ©marrage : ```{splashtext}```" } \ No newline at end of file diff --git a/assets/locales/fy.json b/assets/locales/fy.json index 2779729..5881b5b 100644 --- a/assets/locales/fy.json +++ b/assets/locales/fy.json @@ -121,5 +121,5 @@ "command_stats_embed_field2name": "Ferzje", "command_stats_embed_field2value": "Lokaal: {local_version} \nLĂȘste: {latest_version}", "command_stats_embed_field3name": "Fariabel Info", - "command_stats_embed_field3value": "Namme: {NAME} \nFoarheaksel: {PREFIX} \nEigner ID: {ownerid} \nCooldown: {cooldown_time} \nPingrigel: {PING_LINE} \nUnthĂąld Dielen Ynskeakele: {showmemenabled} \nBrĂ»kerstraining Ynskeakele: {USERTRAIN_ENABLED}\nLiet: {song} \nSplashtekst: ```{splashtext}```" + "command_stats_embed_field3value": "Namme: {NAME} \nFoarheaksel: {PREFIX} \nEigner ID: {ownerid}\nPingrigel: {PING_LINE} \nUnthĂąld Dielen Ynskeakele: {showmemenabled} \nBrĂ»kerstraining Ynskeakele: {USERTRAIN_ENABLED}\nLiet: {song} \nSplashtekst: ```{splashtext}```" } diff --git a/assets/locales/it.json b/assets/locales/it.json index ebf28aa..f86fc96 100644 --- a/assets/locales/it.json +++ b/assets/locales/it.json @@ -129,6 +129,6 @@ "command_stats_embed_field2name": "Versione", "command_stats_embed_field2value": "Locale: {local_version} \nUltima: {latest_version}", "command_stats_embed_field3name": "Informazioni sulle variabili", - "command_stats_embed_field3value": "Nome: {NAME} \nPrefisso: {PREFIX} \nID Proprietario: {ownerid} \nCooldown: {cooldown_time} \nLinea ping: {PING_LINE} \nMemoria Condivisa Abilitata: {showmemenabled} \nAddestramento Utente Abilitato: {USERTRAIN_ENABLED}\nCanzone: {song} \nSplashtext: ```{splashtext}```" + "command_stats_embed_field3value": "Nome: {NAME} \nPrefisso: {PREFIX} \nID Proprietario: {ownerid}\nLinea ping: {PING_LINE} \nMemoria Condivisa Abilitata: {showmemenabled} \nAddestramento Utente Abilitato: {USERTRAIN_ENABLED}\nCanzone: {song} \nSplashtext: ```{splashtext}```" } diff --git a/assets/locales/mt.json b/assets/locales/mt.json index 5e7e9bd..cb41722 100644 --- a/assets/locales/mt.json +++ b/assets/locales/mt.json @@ -121,5 +121,5 @@ "command_stats_embed_field2name": "VerĆŒjoni", "command_stats_embed_field2value": "Lokali: {local_version} \nÄ dida: {latest_version}", "command_stats_embed_field3name": "Informazzjoni Varjabbli", - "command_stats_embed_field3value": "Isem: {NAME} \nPrefiss: {PREFIX} \nID ta’ Sid: {ownerid} \nCooldown: {cooldown_time} \nPing line: {PING_LINE} \nImoħħar Memenja: {showmemenabled} \nUser Training Attiva: {USERTRAIN_ENABLED}\nKan ta’: {song} \nSplashtext: ```{splashtext}```" + "command_stats_embed_field3value": "Isem: {NAME} \nPrefiss: {PREFIX} \nID ta’ Sid: {ownerid}\nPing line: {PING_LINE} \nImoħħar Memenja: {showmemenabled} \nUser Training Attiva: {USERTRAIN_ENABLED}\nKan ta’: {song} \nSplashtext: ```{splashtext}```" }