Discord has a "proper" way to do things. Create a bot application in the Developer Portal. Configure OAuth2. Set up intents. Invite the bot. Manage the token. It's clean, documented, and safe.
I didn't do any of that.
Instead, I launched Discord with a Chromium debug flag, connected to its renderer process via WebSocket, injected JavaScript to extract my user token from the live session, and used that token to hit the Discord REST API directly. Plus DOM manipulation for UI control and xdotool as a nuclear fallback.
The result: discord-thread-manager — an AI agent skill that manages Discord threads with zero setup.
I needed to scan for stale threads on my Discord server and bulk lock/archive them. The standard path:
MANAGE_THREADS permission.envAuthorization: Bot <token>That's 30+ minutes of clicking through a web UI for something I wanted to do right now on my personal server.
I already have a Discord session running on my desktop. I'm already logged in. The token is right there in the renderer process. Why can't I just... grab it?
So I did.
Discord Desktop is an Electron app — Chromium under the hood. Chromium supports --remote-debugging-port, which exposes the Chrome DevTools Protocol on localhost.
/usr/share/discord/Discord --remote-debugging-port=9222
Now http://127.0.0.1:9222/json returns debuggable targets, each with a WebSocket URL.
Connected via WebSocket, you can execute arbitrary JavaScript in Discord's renderer. Three extraction methods, tried in order:
Method 1 — localStorage (fastest):
localStorage.getItem('token')
Method 2 — Script tag parsing:
document.querySelector('script').textContent
.match(/"token"\s*:\s*"([^"]{30,})"/)
Method 3 — Webpack module cache:
// Searches webpackChunkdiscord_app for getToken() exports
With the extracted user token, the controller becomes a standard Discord REST API client. Same endpoints, same pagination, same rate limits — just using Authorization: <token> instead of Authorization: Bot <token>.
| Proper (Bot API) | Hacky (CDP) | |
|---|---|---|
| Setup | Create bot, OAuth, invite | Just launch Discord |
| Token | Bot token from dev portal | Extracted from live session |
| Auth header | Bot <token> | <token> |
| UI Control | API only | API + CDP DOM + xdotool |
| TOS Risk | None | User tokens = gray area |
| Setup time | ~30 minutes | ~0 minutes |
The project is an AI agent skill — a self-contained set of instructions and scripts that an AI coding tool can follow. Three-phase workflow: scan → report → act.
Discovers all threads in a channel — active, public archived, private archived — with full pagination. Analyzes each for inactivity, message count, locked/archived state, and auto-categorizes by issue type.
python scripts/scan_threads.py --channel-id 123456789 --min-inactive-days 30
Generates markdown with age breakdowns (30-60, 60-90, 90-180, 180+ days), issue categories (billing, bugs, API errors, performance), and full thread details.
python scripts/generate_report.py --input stale_threads.json --categorize
Bulk locks and archives stale threads. Always dry-runs first. Requires explicit user confirmation. Locks before archiving (order matters for Discord's API).
# Always dry-run first
python scripts/lock_archive_threads.py --input stale_threads.json --dry-run
# After explicit confirmation
python scripts/lock_archive_threads.py --input stale_threads.json --confirm
Because we have a full CDP connection, the controller can do things the API can't:
# Click a button in Discord's UI
ctrl.cdp.click_element('[aria-label="Thread Menu"]')
# Type into a contenteditable
ctrl.cdp.type_in_element('[contenteditable="true"]', "Closing this")
# Read visible text
title = ctrl.cdp.get_element_text('.channel-name')
And when everything else fails:
ctrl.xdo.activate_discord()
ctrl.xdo.send_keys("Hello!")
ctrl.xdo.send_key("Return")
This entire project was built and tested using Codex CLI with custom AI models. Not GPT-4. Not Claude. Two alternative models:
The Codex Launcher lets you swap OpenAI models for these alternatives. Same Codex CLI experience, powered by models that cost nothing (or significantly less).
The skill format (SKILL.md) is standard. The repo includes setup guides for five AI coding tools:
We only tested with Codex. The others should work since they all read SKILL.md the same way, but we haven't verified. PRs welcome.
For personal server management on Ubuntu Linux, this approach is ridiculously convenient. You skip 30 minutes of bot setup and go straight to managing threads. The CDP bridge is elegant — extracting the token via JS injection, then using it for REST API calls, with DOM manipulation as a bonus.
Is it "proper"? No. Will Discord eventually break it? Maybe. Was it a fun engineering challenge powered by free AI models? Absolutely.
If you're managing your own server and don't want the overhead of bot infrastructure, give it a try. Just don't use it for spam or abuse.
github.com/roman-ryzenadvanced/discord-thread-manager
MIT licensed. The core is scripts/discord_controller.py — ~500 lines of Python handling CDP connection, token extraction, REST API calls, and xdotool fallback. The rest is analysis helpers and CLI wrappers.
Everything built with Codex Launcher + free mimo 2.5 pro API on Ubuntu.
Built with Codex CLI + xiaomi mimo 2.5 pro (free API) on Ubuntu Linux.