worky
This commit is contained in:
6
Dockerfile
Normal file
6
Dockerfile
Normal 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
29
bot.py
Normal 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
71
cogs/logger.py
Normal 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
18
config.py
Normal 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
7
docker-compose.yml
Normal 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
2
requirements.txt
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
discord.py
|
||||||
|
python-dotenv
|
||||||
12
utils.py
Normal file
12
utils.py
Normal 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()
|
||||||
Reference in New Issue
Block a user