Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/trustlessmatt/discord-exporter-bot/llms.txt

Use this file to discover all available pages before exploring further.

Functions for exporting Discord messages, serializing message data, and calculating export statistics.

export_channel_messages()

async def export_channel_messages(
    channel,
    hours: int,
    config: Config
) -> list:
    """Export messages from a channel within the last X hours."""
Exports all messages from a specific Discord channel within the specified time range.

Parameters

channel
discord.TextChannel
required
Discord text channel object to export messages from.
hours
int
required
Number of hours to look back from current time. Messages older than this are excluded.
config
Config
required
Bot configuration object containing timezone and other settings.

Returns

return
list[dict]
List of serialized message dictionaries. Empty list if channel is inaccessible or on error.
Message Dictionary Structure: See serialize_message() for complete message structure.

Behavior

  • Uses channel.history() with after parameter for efficient filtering
  • Automatically handles pagination for channels with many messages
  • Returns empty list on permission errors (logs warning)
  • Returns empty list on other errors (logs error)
  • Converts all timestamps to configured timezone
This function handles Discord API rate limits automatically through discord.py’s built-in rate limiter.

perform_export()

async def perform_export(
    bot: commands.Bot,
    config: Config,
    hours: int
) -> Optional[dict]:
    """Perform the export operation across all channels concurrently."""
Performs a complete export across all text channels in the configured guild, saves to JSON file, and returns statistics.

Parameters

bot
commands.Bot
required
Discord bot instance with active connection.
config
Config
required
Bot configuration containing guild_id, exports_dir, and timezone settings.
hours
int
required
Time range in hours to export messages from.

Returns

return
Optional[dict]
Export result dictionary with statistics and data, or None if export failed.
Success Response Structure:
{
  "filename": "exports/discord_export_20260304_120000_ET.json",
  "total_messages": 1547,
  "channel_count": 8,
  "export_data": {
    "guild_name": "My Server",
    "export_time_eastern": "2026-03-04T12:00:00-05:00",
    "export_time_utc": "2026-03-04T17:00:00+00:00",
    "timezone": "America/New_York (Eastern Time - auto DST)",
    "time_range_hours": 24,
    "channels": {
      "general": {
        "channel_id": "1234567890",
        "messages": [...]
      }
    }
  },
  "stats": {
    "total_messages": 1547,
    "active_channels": 8,
    "contributors": {"user1", "user2"},
    "contributor_count": 15
  }
}

Behavior

  • Exports from all text channels concurrently using asyncio.gather()
  • Creates exports directory if it doesn’t exist
  • Generates timestamped JSON filename with Eastern Time
  • Only includes channels with messages in the time range
  • Returns None if guild not found or save fails
Use concurrent export for better performance. The function processes all channels in parallel rather than sequentially.

serialize_message()

def serialize_message(
    message,
    guild,
    config: Config
) -> dict:
    """Convert a Discord message to a serializable dictionary."""
Converts a Discord message object into a JSON-serializable dictionary with cleaned content and metadata.

Parameters

message
discord.Message
required
Discord message object to serialize.
guild
discord.Guild
required
Guild object for resolving mentions to readable names.
config
Config
required
Configuration containing timezone settings for timestamp conversion.

Returns

return
dict
Serialized message dictionary with all metadata and cleaned content.
Message Dictionary Structure:
{
    "message_id": "1234567890123456789",
    "author": {
        "name": "username",
        "display_name": "Display Name",
        "id": "9876543210987654321",
        "bot": False
    },
    "content": "Hey <@123456> check <#789012>",  # Raw content
    "content_clean": "Hey @username check #general",  # Cleaned mentions
    "timestamp": "2026-03-04T12:34:56-05:00",  # Eastern Time
    "timestamp_utc": "2026-03-04T17:34:56+00:00",
    "edited_timestamp": "2026-03-04T12:35:00-05:00",  # or None
    "attachments": [
        {
            "filename": "image.png",
            "url": "https://cdn.discord.com/...",
            "size": 245678
        }
    ],
    "embeds": 2,  # Count of embeds
    "reactions": [
        {"emoji": "👍", "count": 5},
        {"emoji": "❤️", "count": 3}
    ],
    "mentions": [
        {"id": "123456", "name": "username", "display_name": "Display Name"}
    ],
    "channel_mentions": [
        {"id": "789012", "name": "general"}
    ],
    "thread": "Thread Name"  # or None
}

Content Cleaning

The function converts Discord mentions from internal format to readable names:
  • <@123456>@username
  • <#789012>#channel-name
  • <@&456789>@role-name
If a mentioned user, channel, or role no longer exists, the original mention format is preserved.

calculate_export_stats()

def calculate_export_stats(export_data: dict) -> dict:
    """Calculate statistics from export data."""
Calculates summary statistics from export data including message counts, active channels, and unique contributors.

Parameters

export_data
dict
required
Export data dictionary containing channels and messages. Must have structure from perform_export().

Returns

return
dict
Statistics dictionary with message counts and contributor information.
Statistics Structure:
{
    "total_messages": 1547,          # Total message count across all channels
    "active_channels": 8,            # Number of channels with messages
    "contributors": {                # Set of unique display names (excludes bots)
        "Alice",
        "Bob",
        "Charlie"
    },
    "contributor_count": 15          # Number of unique human contributors
}

Behavior

  • Counts all messages across all channels
  • Only counts channels that have at least one message
  • Excludes bot messages from contributor calculation
  • Uses display names for contributor identification
  • Returns set for contributors to ensure uniqueness
Bot messages (where author.bot is True) are excluded from contributor statistics but included in total message count.

Integration Example

Complete workflow using all export functions:
from discord.ext import commands
from bot import Config, perform_export, calculate_export_stats
import json

# Initialize
config = Config.from_env()
bot = commands.Bot(command_prefix="!", intents=intents)

@bot.command()
async def export(ctx, hours: int = 24):
    """Export command handler"""
    # Perform full export
    result = await perform_export(bot, config, hours)
    
    if not result:
        await ctx.send("Export failed!")
        return
    
    # Access the data
    export_data = result["export_data"]
    stats = result["stats"]
    
    # Report results
    await ctx.send(
        f"Exported {stats['total_messages']} messages\n"
        f"From {stats['active_channels']} channels\n"
        f"By {stats['contributor_count']} contributors\n"
        f"Saved to: {result['filename']}"
    )
    
    # Load the saved file
    with open(result['filename'], 'r') as f:
        saved_data = json.load(f)
    
    # Process channels
    for channel_name, channel_data in saved_data['channels'].items():
        message_count = len(channel_data['messages'])
        print(f"#{channel_name}: {message_count} messages")

Build docs developers (and LLMs) love