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:
<parent>/
xdk-python/
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"}'uv run xchat-bot-runThe bot connects to the Activity Stream using BEARER_TOKEN, decrypts incoming
messages using private_keys, and replies using the OAuth2 user token.
This project supports a Discord.py-like decorator style for commands/events. You
can create a separate bot module and register handlers (see
xchat_bot_python/decorator_example_bot.py):
from xchat_bot_python.decorators_bot import Context, XChatBot
bot = XChatBot(command_prefix="!")
@bot.command("ping")
async def ping(ctx: Context) -> None:
await ctx.reply_async("pong")
@bot.command("echo")
def echo(ctx: Context) -> None:
ctx.reply(" ".join(ctx.args))
@bot.event
def on_message(ctx: Context) -> None:
# Runs for every decrypted Text message (commands and non-commands).
...
bot.run()Notes:
- Commands are prefix-based. With
command_prefix="!", a message like!echo hello worldmaps to commandechowith args["hello", "world"]. - Handlers can be either
deforasync def. - Use
ctx.reply(...)(sync) orawait ctx.reply_async(...)(async).
To run the decorator example:
uv run python -m xchat_bot_python.decorator_example_botstate.jsoncontains tokens and keys. Keep it local and uncommitted.- You can override any
.envvalue with environment variables.