From 9aaad019a527170303828deb6d246fbc29ee29b1 Mon Sep 17 00:00:00 2001 From: Azeem Fidahusein Date: Mon, 11 Aug 2025 01:10:36 +0100 Subject: [PATCH] added new bot script --- scripts/bot.py | 141 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 141 insertions(+) create mode 100644 scripts/bot.py diff --git a/scripts/bot.py b/scripts/bot.py new file mode 100644 index 0000000..f59f935 --- /dev/null +++ b/scripts/bot.py @@ -0,0 +1,141 @@ +# discord_export_bot_v2.py +# This bot connects to a Discord server and exports the entire message +# history from every accessible text channel into separate CSV files. +# This version uses a more robust task-based approach to prevent hanging. + +import discord +import csv +import os +import asyncio + +# --- Configuration --- +BOT_TOKEN = "___" +OUTPUT_DIRECTORY = "discord_chat_logs" +# Optional: If you want to lock the bot to one server +# ALLOWED_SERVER_ID = 123456789012345678 +# ------------------- + +# --- Bot Setup --- +# The intents MUST be enabled in the Discord Developer Portal. +intents = discord.Intents.default() +intents.guilds = True +intents.messages = True +intents.message_content = True # This is the most important one! + +client = discord.Client(intents=intents) + +async def export_channel_history(channel): + """ + Asynchronously fetches all messages from a given text channel + and saves them to a CSV file. + """ + print(f"-> Starting export for channel: #{channel.name}") + + sanitized_channel_name = "".join(c if c.isalnum() else '_' for c in channel.name) + file_path = os.path.join(OUTPUT_DIRECTORY, f"{sanitized_channel_name}.csv") + + try: + message_count = 0 + with open(file_path, 'w', newline='', encoding='utf-8') as csvfile: + header = [ + 'message_id', 'timestamp_utc', 'author_id', 'author_name', + 'author_nickname', 'content', 'attachment_urls', 'embeds' + ] + writer = csv.DictWriter(csvfile, fieldnames=header) + writer.writeheader() + + # This is the part that fails without the Message Content Intent + async for message in channel.history(limit=None): + message_count += 1 + if message_count % 250 == 0: # Log progress less frequently + print(f" ... processed {message_count} messages in #{channel.name}") + + attachment_urls = ", ".join([att.url for att in message.attachments]) + embeds_str = ", ".join([str(embed.to_dict()) for embed in message.embeds]) + + # Handle nickname - only Member objects have nick attribute, not User objects + author_nickname = getattr(message.author, 'nick', None) or message.author.display_name + + writer.writerow({ + 'message_id': message.id, + 'timestamp_utc': message.created_at, + 'author_id': message.author.id, + 'author_name': message.author.name, + 'author_nickname': author_nickname, + 'content': message.content, + 'attachment_urls': attachment_urls, + 'embeds': embeds_str + }) + + if message_count > 0: + print(f"✅ Finished exporting {message_count} messages from #{channel.name}.") + else: + print(f"⚠️ Channel #{channel.name} is empty or unreadable. 0 messages exported.") + return True + + except discord.errors.Forbidden: + print(f"❌ ERROR: Permission denied for channel #{channel.name}. Check bot permissions. Skipping.") + return False + except Exception as e: + print(f"❌ An unexpected error occurred for channel #{channel.name}: {e}") + return False + +async def main_export_task(): + """ + The main logic for the bot's export process. + This is run as a background task to avoid blocking. + """ + # Wait until the bot is fully ready before starting + await client.wait_until_ready() + + print('------') + print("Bot is ready. Starting export process...") + + # Create the output directory if it doesn't exist + if not os.path.exists(OUTPUT_DIRECTORY): + os.makedirs(OUTPUT_DIRECTORY) + print(f"Created output directory: {OUTPUT_DIRECTORY}") + + # Use the first guild the bot is in. For specific server, use client.get_guild(ALLOWED_SERVER_ID) + if not client.guilds: + print("Error: Bot does not appear to be in any server.") + await client.close() + return + + guild = client.guilds[0] + print(f"Targeting server: {guild.name} (ID: {guild.id})") + + text_channels = [channel for channel in guild.text_channels] + print(f"Found {len(text_channels)} text channels to export.") + + for channel in text_channels: + await export_channel_history(channel) + await asyncio.sleep(1) + + print('------') + print("All channels have been processed. The bot will now shut down.") + + # This properly closes the bot's connection. + await client.close() + +@client.event +async def on_ready(): + """ + This event is triggered once the bot has successfully connected. + It now only prints a ready message and starts the main task. + """ + print(f'Logged in as: {client.user.name} (ID: {client.user.id})') + # Schedule the main task to run in the background + client.loop.create_task(main_export_task()) + +# --- Run the Bot --- +if __name__ == "__main__": + if BOT_TOKEN == "YOUR_BOT_TOKEN_HERE": + print("!!! ERROR: Please replace 'YOUR_BOT_TOKEN_HERE' with your actual bot token in the script.") + else: + try: + client.run(BOT_TOKEN) + except discord.errors.LoginFailure: + print("!!! ERROR: Login failed. The token is likely invalid or incorrect.") + except Exception as e: + print(f"!!! An error occurred while running the bot: {e}")