This commit is contained in:
jbkzi
2026-02-20 22:26:49 +01:00
commit 7ddc49e24a
7 changed files with 145 additions and 0 deletions

6
Dockerfile Normal file
View File

@@ -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"]

29
bot.py Normal file
View File

@@ -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}")

71
cogs/logger.py Normal file
View File

@@ -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))

18
config.py Normal file
View File

@@ -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()

7
docker-compose.yml Normal file
View File

@@ -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=""

2
requirements.txt Normal file
View File

@@ -0,0 +1,2 @@
discord.py
python-dotenv

12
utils.py Normal file
View File

@@ -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()