Minimal X Chat bot example with a login, unlock, and run flow. Everything the bot needs (tokens, keys, env) lives in this directory.
This example expects the XDK repos to be sibling directories (since chat-xdk is not released yet):
<parent>/
chat-xdk/
xchat-bot-python/
- Python 3.10+
uv- X app credentials (OAuth2 client id/secret)
- Activity Stream bearer token
From the parent directory:
cd xchat-bot-python
cp env.template .envEdit .env with:
BEARER_TOKENOAUTH_CLIENT_IDOAUTH_CLIENT_SECRETOAUTH_REDIRECT_URIOAUTH_SCOPES
Install dependencies:
uv syncRun the login command and follow the prompt:
uv run xchat-bot-loginThis stores the OAuth2 token in state.json.
Fetch public keys, prompt for PIN, and store private keys locally:
uv run xchat-bot-unlockThis uses /2/users/:id/public_keys and stores:
private_keyssigning_key_versionuser_id
All are saved in state.json.
Create a chat.received subscription for the authenticated user:
xurl -X POST --auth oauth2 "/2/activity/subscriptions" -d \
'{"event_type": "chat.received", "filter": {"user_id": "{id}"}, "tag": "bot received messages"}'Bare-bones echo bot:
uv run xchat-bot-runGrok-powered bot (requires Grok API token):
uv run xchat-bot-run-grokReaction-only bot (👀 read-receipt reaction):
uv run xchat-bot-run-reactThe xchat_bot_js/ directory contains a JavaScript equivalent using the WASM-based chat-xdk.
Install dependencies:
cd xchat_bot_js
npm installRun the bot:
npm run botThe JS bot uses the same XChatBot / XChatBotConfig interface from chat-xdk/bot. Configuration is provided inline via the XChatBotConfig constructor. See xchat_bot_js/decorator_example_bot.js for a full example.
xchat-bot-run(Python): replies withreceived <text>xchat-bot-run-grok(Python): replies with a Grok responsexchat-bot-run-react(Python): reacts with 👀 to messages it would reply to (uses message sequence_id)npm run bot(JS): replies via Grok with smart routing (text, image generation, or video generation)
Each bot connects to the Activity Stream using BEARER_TOKEN, decrypts incoming
messages using private_keys, and replies using the OAuth2 user token.
state.jsoncontains tokens and keys. Keep it local and uncommitted.- You can override any
.envvalue with environment variables.