Discord Backup Bot
Archives a single Discord server's text channels in full fidelity: messages, attachments, embeds, reactions, reply/thread structure, edits, and deletions. Stores everything in a local SQLite database with attachment files on disk. Resumes cleanly after crashes or restarts.
Setup
1. Create a Discord bot
- Open https://discord.com/developers/applications and create a new application.
- Under Bot, click Reset Token and copy the token somewhere safe.
- Under Privileged Gateway Intents, enable:
- Message Content Intent (required)
- Under OAuth2 → URL Generator:
- Scopes:
bot - Bot Permissions:
View Channels,Read Message History
- Scopes:
- Open the generated URL and add the bot to your server.
2. Get the IDs
Enable Developer Mode in Discord: User Settings → Advanced → Developer Mode.
Right-click your server icon → Copy Server ID (this is guildId).
Right-click each channel you want backed up → Copy Channel ID.
3. Install and configure
Requires Node.js 22.5 or newer (uses the built-in node:sqlite module — no native compilation).
npm install
Copy config.example.json to config.json and fill in:
{
"token": "your bot token",
"guildId": "your server id",
"channelIds": ["channel id 1", "channel id 2"],
"dataDir": "./data",
"attachmentConcurrency": 3
}
4. Run
npm start
The bot will log in, attach its live listener, start downloading attachments in the background, and backfill each configured channel (walking backward through old messages and forward to catch anything missed during downtime). Leave it running 24/7. Stop with Ctrl+C — next start resumes from the last checkpoint.
What gets captured
- Messages (content, author, timestamps, reply target)
- Edits — full edit history from when the bot is present
- Deletions — soft-delete flag with timestamp
- Attachments — downloaded to
data/attachments/<channel_id>/<message_id>/<filename> - Embeds — link previews, bot embeds (stored as JSON)
- Reactions — per user, with add/remove timestamps
- Threads — active and public archived; new threads via live events
- Author info — username, display name, avatar URL snapshot
Limitations
- Pre-bot edits and deletions are unrecoverable. Discord's API does not expose edit/deletion history — the bot only sees them live.
- Reactions removed before the bot joined are lost. Reactions still present at backfill time are captured.
- Private archived threads are not backfilled (requires extra permissions). Private threads created while the bot is live are captured.
- Signed attachment URLs expire after ~24 hours. If the bot is offline that long after a message is posted, its attachment URL may 403/410 before the downloader can fetch it — metadata is preserved but the file is unrecoverable.
- Gateway reconnects that don't trigger a fresh
readydon't re-run backfill. Restart the bot to catch up after long disconnects.
Storage
data/
├── backup.db # SQLite (WAL mode; -shm / -wal files appear while running)
└── attachments/
└── <channel_id>/<message_id>/<attachment_id>/<filename>
Inspecting the archive
sqlite3 data/backup.db
Useful queries:
-- Messages per channel (including threads)
SELECT c.name, COUNT(*) AS count
FROM messages m JOIN channels c ON c.id = m.channel_id
GROUP BY c.id ORDER BY count DESC;
-- Backfill progress
SELECT name, is_thread, backfill_complete,
oldest_fetched_id, newest_fetched_id
FROM channels;
-- Pending / failed attachments
SELECT
SUM(CASE WHEN downloaded = 1 THEN 1 ELSE 0 END) AS done,
SUM(CASE WHEN downloaded = 0 AND download_failed = 0 THEN 1 ELSE 0 END) AS pending,
SUM(download_failed) AS failed
FROM attachments;
-- Messages with edit history
SELECT m.id, m.content, COUNT(e.message_id) AS edits
FROM messages m LEFT JOIN message_edits e ON e.message_id = m.id
GROUP BY m.id HAVING edits > 0;
-- Deleted messages
SELECT id, author_id, content, deleted_at FROM messages
WHERE deleted_at IS NOT NULL ORDER BY deleted_at DESC LIMIT 20;