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