From 7ddc49e24a42db14ba0f69b51d6dffbf8ff1f518 Mon Sep 17 00:00:00 2001 From: jbkzi Date: Fri, 20 Feb 2026 22:26:49 +0100 Subject: [PATCH] worky --- Dockerfile | 6 ++++ bot.py | 29 +++++++++++++++++++ cogs/logger.py | 71 ++++++++++++++++++++++++++++++++++++++++++++++ config.py | 18 ++++++++++++ docker-compose.yml | 7 +++++ requirements.txt | 2 ++ utils.py | 12 ++++++++ 7 files changed, 145 insertions(+) create mode 100644 Dockerfile create mode 100644 bot.py create mode 100644 cogs/logger.py create mode 100644 config.py create mode 100644 docker-compose.yml create mode 100644 requirements.txt create mode 100644 utils.py diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..85ffb06 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,6 @@ +FROM python:3.10-slim +WORKDIR /app +COPY requirements.txt . +RUN pip install --no-cache-dir -r requirements.txt +COPY . . +CMD ["python", "bot.py"] \ No newline at end of file diff --git a/bot.py b/bot.py new file mode 100644 index 0000000..676e096 --- /dev/null +++ b/bot.py @@ -0,0 +1,29 @@ +import discord +import asyncio +from discord.ext import commands +from config import Config + +class RealBot(commands.Bot): + def __init__(self): + intents = discord.Intents.default() + intents.messages = True + intents.message_content = True + intents.members = True + super().__init__(command_prefix="!", intents=intents) + + async def setup_hook(self): + # Loads the extension from the 'cogs' folder + # Note the dot notation: cogs.logger + await self.load_extension("cogs.logger") + print("Logger extension loaded.") + + async def on_ready(self): + print(f"Logged in as {self.user} (ID: {self.user.id})") + print("---------------------------------------------") + +if __name__ == "__main__": + bot = RealBot() + try: + bot.run(Config.BOT_TOKEN) + except Exception as e: + print(f"Error: {e}") \ No newline at end of file diff --git a/cogs/logger.py b/cogs/logger.py new file mode 100644 index 0000000..ae067d1 --- /dev/null +++ b/cogs/logger.py @@ -0,0 +1,71 @@ +import discord +from discord.ext import commands +from config import Config +import utils + +class ChatLogger(commands.Cog): + def __init__(self, bot): + self.bot = bot + + @commands.Cog.listener() + async def on_message_delete(self, message: discord.Message): + # 1. Validation + if message.author.bot: return + + # 2. Build Embed + embed = discord.Embed( + description=f"**Message sent by {message.author.mention} was deleted**", + color=Config.COLOR_DELETE, + timestamp=utils.get_timestamp() + ) + + embed.set_author( + name=f"{message.author}", + icon_url=message.author.avatar.url if message.author.avatar else None + ) + + content = message.content if message.content else "[No Text Content]" + embed.add_field(name="Deleted Text:", value=utils.truncate(content), inline=False) + + # 3. Secure Image Handling + if message.attachments: + links = [f"[{att.filename}]({att.proxy_url})" for att in message.attachments] + embed.add_field(name="Evidence:", value="\n".join(links), inline=False) + embed.set_image(url=message.attachments[0].proxy_url) + + embed.set_footer(text="Caught in 4K") # Optional flavor text + + # 4. Send to the SAME channel + await message.channel.send(embed=embed) + + @commands.Cog.listener() + async def on_message_edit(self, before: discord.Message, after: discord.Message): + # 1. Validation + if before.author.bot: return + if before.content == after.content: return + + # 2. Build Embed + embed = discord.Embed( + description=f"**Message edited by {before.author.mention}** [Jump to Message]({after.jump_url})", + color=Config.COLOR_EDIT, + timestamp=utils.get_timestamp() + ) + + embed.set_author( + name=f"{before.author}", + icon_url=before.author.avatar.url if before.author.avatar else None + ) + + original = before.content if before.content else "[No Text Content]" + # We don't necessarily need the "Edited to" field here since the + # actual edited message is visible right above this log, but it's good for history. + + embed.add_field(name="Original:", value=utils.truncate(original), inline=False) + + embed.set_footer(text="Edit History") + + # 3. Send to the SAME channel + await before.channel.send(embed=embed) + +async def setup(bot): + await bot.add_cog(ChatLogger(bot)) diff --git a/config.py b/config.py new file mode 100644 index 0000000..3b3a51f --- /dev/null +++ b/config.py @@ -0,0 +1,18 @@ +import os +import discord +from dotenv import load_dotenv + +# Load .env file (if it exists) for local testing +load_dotenv() + +class Config: + # Secrets + BOT_TOKEN = os.getenv("DISCORD_TOKEN", "YOUR_FALLBACK_TOKEN_HERE") + + # Settings + LOG_CHANNEL_NAME = "real-chat-logs" + EMBED_LIMIT = 1000 + + # Colors + COLOR_DELETE = discord.Color.red() + COLOR_EDIT = discord.Color.orange() \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..4eafa07 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,7 @@ +services: + discord-bot: + build: . + container_name: real_chat_bot + restart: always # If the bot crashes or server reboots, this auto-restarts it + environment: + - DISCORD_TOKEN="" diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..95004ae --- /dev/null +++ b/requirements.txt @@ -0,0 +1,2 @@ +discord.py +python-dotenv \ No newline at end of file diff --git a/utils.py b/utils.py new file mode 100644 index 0000000..c3cfc93 --- /dev/null +++ b/utils.py @@ -0,0 +1,12 @@ +import datetime +from config import Config + +def truncate(text: str, limit: int = Config.EMBED_LIMIT) -> str: + """Truncates text to ensure it fits in an Embed field.""" + if len(text) > limit: + return text[:limit] + "..." + return text + +def get_timestamp(): + """Returns the current timestamp.""" + return datetime.datetime.now() \ No newline at end of file