Automatically provide context to OpenClaw that is specific to the current Telegram topic or Discord channel that you are writing in. The plugin is most useful when one OpenClaw agent serves many Telegram topics or Discord channels/threads and each conversation needs a small, durable context file loaded automatically.
- Loads per-topic/per-channel Markdown context into the agent system prompt via
before_prompt_build. - Supports Telegram forum topics.
- Supports Discord channels and threads.
- For Discord threads, loads parent-channel context first and thread-specific context second when both are mapped.
- Supports one file (
file) or ordered multiple files (files) per topic map entry. - Sends optional status notices with direct Telegram/Discord APIs after the normal outbound message is sent, keeping notices out of the agent transcript.
Copy this directory somewhere OpenClaw can load plugins from, then add the path to your OpenClaw config:
{
"plugins": {
"load": {
"paths": [
"/path/to/openclaw-plugin-topic-context"
]
},
"entries": {
"topic-context": {
"enabled": true,
"config": {
"statusMessages": "after_message_sent"
}
}
}
}
}Restart OpenClaw after adding a new plugin path.
Plugin config lives under plugins.entries.topic-context.config.
{
"statusMessages": "after_message_sent",
"loadedMessageTemplate": "📂 Context loaded: \"{name}\" ({file})",
"workspaceDir": "/path/to/openclaw/workspace"
}Options:
workspaceDiroptional string. Directory containingtopics/topic-map.json. Defaults to the active agent workspace when available, then~/.openclaw/workspace.statusMessagesoptional string:after_message_sentdefault. Send feedback notices after the normal outbound reply is sent.off. Do not send feedback notices.
loadedMessageTemplateoptional string. Template for successful context-load notices.- Default:
📂 Context loaded: "{name}" ({file}) - Placeholders:
{name},{file},{path}
- Default:
All default messages are in English. Override loadedMessageTemplate if you want localized feedback, for example:
{
"loadedMessageTemplate": "📂 Contesto caricato: \"{name}\" ({file})"
}Create topics/topic-map.json in the workspace.
{
"5": {
"name": "Product",
"file": "topics/product.md"
},
"10": {
"name": "Engineering",
"files": [
"topics/company.md",
"topics/engineering.md"
]
},
"1482791553054277662": {
"name": "Discord channel",
"file": "topics/discord-channel.md"
}
}Keys can be:
- Telegram topic ids.
- Discord channel ids.
- Discord thread ids.
Entries:
nameoptional display name used in feedback notices.filesingle context file path, relative toworkspaceDirunless absolute.filesordered list of context files.
Topics/channels not present in the map are ignored silently.
For Discord threads the plugin tries to load both:
- the parent channel mapping;
- the thread mapping, if present and not identical.
Parent resolution tries:
- old-style session keys that include both parent and thread ids;
- Discord API
GET /channels/{threadId}to readparent_id.
If only the parent channel is mapped, that mapping is reused for fresh thread sessions.
Feedback notices are shown only when a session/topic is considered new.
The plugin stores markers in:
topics/.state/topic-<id>-session.txt
For Discord threads, the marker is sticky per thread/topic to avoid repeated notices when runtime session ids churn. /new and /reset clear the marker for the current topic via before_reset.
The plugin sends feedback notices directly through provider APIs.
Token lookup order:
channels.discord.accounts[accountId].botTokenchannels.discord.accounts[accountId].tokenchannels.discord.accounts[accountId].tokenFilechannels.discord.botTokenchannels.discord.tokenchannels.discord.tokenFileDISCORD_BOT_TOKEN
The Discord bot needs permission to post messages in the target channel/thread.
Token lookup order:
channels.telegram.accounts[accountId].botTokenchannels.telegram.accounts[accountId].tokenFilechannels.telegram.botTokenchannels.telegram.tokenFileTELEGRAM_BOT_TOKEN_<ACCOUNT_ID>TELEGRAM_BOT_TOKENfor the default account
The plugin respects channels.telegram.accounts[accountId].apiRoot or channels.telegram.apiRoot when configured.
- Context is not injected:
- Check that
topics/topic-map.jsonexists in the resolved workspace directory. - Check that the current Discord/Telegram id is mapped.
- Check that mapped files exist and are readable.
- Check that
- Context is injected but no notice appears:
- Ensure
statusMessagesis notoff. - Ensure the bot token is available to the gateway process.
- Ensure the bot can post in the Discord channel/thread or Telegram topic.
- Remember notices appear only for new sessions/topics unless
/newor/resetclears the marker.
- Ensure
- Discord thread inherits no parent context:
- Ensure the Discord bot token is configured so the plugin can call
GET /channels/{threadId}. - Alternatively map the thread id directly.
- Ensure the Discord bot token is configured so the plugin can call
The old hook-style approach sent feedback during bootstrap. On Discord threads this could modify delivery/session state while the first prompt was still being built, causing races such as session-file-change errors.
This plugin separates the two phases:
before_prompt_buildinjects context only.message_sentsends feedback notices directly through the provider API after the normal reply has left the transcript-critical path.
Because notices are sent by the plugin runtime rather than by the agent through the message tool, they do not become assistant turns in the transcript.
node --check index.js
python3 -m json.tool openclaw.plugin.json >/dev/nullFor integration testing, create a temporary workspace with topics/topic-map.json, load the plugin, trigger before_prompt_build, then trigger message_sent with a Discord or Telegram target.