| name | telegram-wiki-bots |
| description | Deploy Telegram bots as a frontend to LLM Wiki — query bot for knowledge retrieval, admin bot for content ingestion. Long-polling, systemd-managed, no webhook needed. |
| version | 1.0.0 |
| author | 3号传声筒 |
| metadata | {"hermes":{"tags":["telegram","wiki","bot","knowledge-base","long-polling"],"category":"telegram-bots","related_skills":["llm-wiki"]}} |
Telegram Wiki Bots
Two Telegram bots serving as a user-facing frontend to the LLM Wiki via the Hermes API server.
Architecture
┌─────────────────┐ ┌──────────────────────┐ ┌─────────────────┐
│ Telegram User │────▶│ Wiki Bot (query) │────▶│ Hermes API │
│ │ │ @bot /query │ │ 127.0.0.1:8642 │
│ │────▶│ Admin Bot (ingest) │───▶│ /llm_wiki │
│ │ │ @bot /ingest │ │ │
└─────────────────┘ └──────────────────────┘ └─────────────────┘
- Wiki Bot (
wiki_bot.py) — query side: users ask questions, get answers + .md file
- Wiki Admin Bot (
wiki_admin_bot.py) — ingestion side: users send content, bot catalogs it into the wiki
- Both communicate with Hermes API server at
127.0.0.1:8642/v1/chat/completions
- Both use long-polling (no webhook setup needed)
- Both managed by systemd for auto-restart
Prerequisites
- Hermes API server running on
127.0.0.1:8642
- LLM Wiki skill installed and configured
- Python 3.8+ with
requests library
- Two Telegram bot tokens (obtain from @BotFather)
Wiki Bot (Query Bot)
Trigger Rules
- Private chat: always responds to any text
- Group chat: only responds when
@mentioned, replied to, or /query command used
/query <question> slash command
Flow
- React with 👀 to acknowledge the message
- Show typing indicator
- Send query to API:
/llm_wiki query <question>
- System prompt constrains: "你是一个 wiki 查询助手。只回答用户的问题,不要建议创建 wiki 页面、更新 index 或修改任何文件。使用 Wiki 内容完整回答用户问题,不要出现'请参考...'或'基于...页面'这类引导式语句。直接给出自包含的完整答案,必要时将引用内容自然地融入答案中。回答结束后停止。"
- Save answer as timestamped
.md file in OUTPUT_DIR
- Format answer as Telegram HTML and send
- Send
.md file as document attachment
- Fallback to plain text if HTML send fails
Key Functions
def should_respond(msg: dict) -> bool:
"""Private: always. Group: @mention, /query, or reply to bot."""
def extract_query(msg: str) -> str
def query_wiki(query: str) -> str
def format_for_telegram(text: str) -> str
def save_markdown(query: str, answer: str) -> str
Config Constants
BOT_TOKEN = "your-query-bot-token"
API_URL = "http://127.0.0.1:8642/v1/chat/completions"
OUTPUT_DIR = Path("/root/wiki_bot_output")
POLL_TIMEOUT = 30
REQUEST_TIMEOUT = 300
Registered Commands
Wiki Admin Bot (Ingestion Bot)
Trigger Rules
@mentioned in group (passes full message as-is to ingest)
/ingest <content> slash command
Flow
- React with 👀 to acknowledge
- Show typing indicator
- Send to API:
/llm_wiki Ingest """<full_content>"""
- System prompt: "你是一个 wiki 内容摄入助手...完成后只需回复「处理完成」"
- Reply "处理完成" on success, error message on failure
Key Functions
def should_respond(msg: dict) -> bool
def extract_content(msg: dict) -> str
def ingest_content(content: str) -> bool
Config Constants
BOT_TOKEN = "your-admin-bot-token"
API_URL = "http://127.0.0.1:8642/v1/chat/completions"
POLL_TIMEOUT = 30
REQUEST_TIMEOUT = 300
Registered Commands
/ingest — "摄入内容到 wiki 知识库"
Deployment
1. Create Bot Files
Place wiki_bot.py and wiki_admin_bot.py in /root/ (or any directory).
2. Systemd Services
[Unit]
Description=Wiki Bot (@<username>) - Telegram wiki query bot
After=network.target
[Service]
Type=simple
WorkingDirectory=/root
ExecStart=/usr/bin/python3 /root/wiki_bot.py
Restart=always
RestartSec=5
StandardOutput=append:/root/wiki_bot.log
StandardError=append:/root/wiki_bot.log
[Install]
WantedBy=multi-user.target
[Unit]
Description=Wiki Admin Bot (@<username>) - Telegram wiki ingestion bot
After=network.target
[Service]
Type=simple
WorkingDirectory=/root
ExecStart=/usr/bin/python3 /root/wiki_admin_bot.py
Restart=always
RestartSec=5
StandardOutput=append:/root/wiki_admin_bot.log
StandardError=append:/root/wiki_admin_bot.log
[Install]
WantedBy=multi-user.target
3. Enable & Start
sudo systemctl daemon-reload
sudo systemctl enable --now wiki-bot wiki-admin-bot
sudo systemctl status wiki-bot wiki-admin-bot
4. Monitor
journalctl -u wiki-bot -f
journalctl -u wiki-admin-bot -f
tail -f /root/wiki_bot.log
tail -f /root/wiki_admin_bot.log
ps -ef | grep wiki
Common Pitfalls
- Duplicate processes: hermes terminal wrappers can leave zombie processes. Check
ps -ef | grep wiki and kill any with a hermes bash wrapper parent PID. Clean instances should have PPID=1.
- No webhook: these bots use long-polling. Don't set a webhook with
setWebhook — it will conflict. The admin bot explicitly calls deleteWebhook on startup to clear pending updates.
- Telegram HTML parsing: the
format_for_telegram function is critical. Without it, characters like <, > in code blocks break the HTML parse. Always protect code blocks before html.escape().
- API timeout: ingestion can take up to 5 minutes for large content. The admin bot sets
REQUEST_TIMEOUT=300. The query bot uses 120s.
- Token conflict: two bots cannot share the same token. Use separate bot tokens for query and admin.
- System prompt constraints: the query bot's system prompt explicitly says "不要建议创建 wiki 页面、更新 index 或修改任何文件" — this prevents the bot from trying to modify wiki files when it should only be reading.
Extending
Adding New Actions (e.g., lint, create)
To add a new slash command like /lint:
- Add the command to the bot's
setMyCommands list
- Add trigger detection in
should_respond() (for /lint prefix)
- Add content extraction in
extract_query() or extract_content()
- Add the API call pattern:
def lint_wiki() -> str:
payload = {
"messages": [
{"role": "system", "content": "你是一个 wiki 审计助手..."},
{"role": "user", "content": "/llm_wiki lint"}
]
}
- Add dispatch logic in the main loop's
for update in updates block
Multi-bot Pattern
This two-bot architecture (read + write) is a clean pattern for any skill-based bot:
- Read bot: queries knowledge, returns formatted answers + .md files
- Write bot: ingests/modifies content, returns confirmation
- Separate tokens, separate triggers, same API backend
HTML Formatting Module
The format_for_telegram() function is reusable. Extract it into a shared module if building multiple Telegram bots. Key steps:
- Extract and protect code blocks (triple-backtick and inline)
html.escape() the remaining text
- Restore code blocks
- Convert markdown → HTML tags
- Truncate at 4000 chars
File Checklist
/root/
├── wiki_bot.py # Query bot
├── wiki_admin_bot.py # Ingestion bot
├── wiki_bot.log # Query bot log
├── wiki_admin_bot.log # Admin bot log
└── wiki_bot_output/ # .md files from queries
/etc/systemd/system/
├── wiki-bot.service
└── wiki-admin-bot.service