Pipeline: scout → draft → human approves → publisher.
python3 and curl are available in your environment (declared in requires.bins). Run all commands yourself using exec/ bash tool. NEVER ask the user to run commands for you.
When you load this skill, run the preflight check FIRST:
bash
python3 {baseDir}/scripts/tgcm.py --workspace {workspace} check
After check — act on results, don't ask:
[ok] → proceed with the user's task silently[fail] Bot token → ask the user for the token, then save it: tgcm.py config set bot-token <token>. Do NOT ask where the token is or offer choices[warn] SEARXNG_URL → ask the user for the URL, then save it: tgcm.py config set searxng-url <url>. Proceed without it — scout won't work but other commands will[fail] Channel → report which channel failed and why, include the fix from the output[warn] No channels → mention it, but proceed — the user may want to init oneSettings saved via config set persist in tgcm/.config.json and are used by all subsequent commands.
NEVER ask follow-up questions about check results. Report what's wrong and the fix from the output. If the user hasn't specified a task — just report the check status, nothing else.
All commands: python3 {baseDir}/scripts/tgcm.py --workspace {workspace} <cmd>
CommandWhat it doesinit <name>Create a channellistShow all channelsbind <name> --channel-id IDBind channel to Telegraminfo <name> [--chat] [--subscribers] [--permissions] [--admins] [--all]Channel statusget-id <@username|ID>Resolve @username or numeric ID → full channel info (id, type, title)checkPreflight: verify bot token, channels, env varsconfig set <key> <value>Save setting locally (keys: bot-token, searxng-url)config get <key>Read a saved settingconfig listShow all saved settingsfetch-posts <name> [--limit N] [--dry-run]Load channel posts into dedup index (requires public channel with @username)connect --channel-id ID [--channel-title T]Handle #tgcm connect event
Bot token is auto-resolved: --bot-token arg → $BOT_TOKEN env → openclaw.json (auto-search) → tgcm/.config.json. Just call tgcm.py get-id @username without --bot-token — the script finds the token itself. If auto-detection fails, save it once: tgcm.py config set bot-token <token>.
Channel name validation: ^[a-z0-9][a-z0-9_-]{0,62}$.
User saysDo this«узнай/определи channel-id»tgcm.py get-id @username«подключи канал»Recipe: Connect a channel (ниже)«какие каналы / список»tgcm.py list«статус канала X»tgcm.py info X«что в очереди»cat tgcm/<name>/content-queue.md«загрузи посты / rebuild index»tgcm.py fetch-posts <name>
python3 {baseDir}/scripts/tgcm.py get-id @username
Token is found automatically. Returns channel id, type, and title.
python3 {baseDir}/scripts/tgcm.py get-id @usernamepython3 {baseDir}/scripts/tgcm.py --workspace {workspace} init <name>python3 {baseDir}/scripts/tgcm.py --workspace {workspace} bind <name> --channel-id <id>skills.entries["tg-channel-manager"].config in openclaw.json{baseDir}/references/cron-setup.md)python3 {baseDir}/scripts/tgcm.py --workspace {workspace} fetch-posts <name>
Fetches posts from the channel's public page (t.me/s/) and adds them to content-index.json.
Options: --limit N (max pages, default 5), --dry-run (preview only).
Requires: channel must be public (have a @username).
tgcm.py listtgcm.py info <name>cat tgcm/<name>/content-queue.mdcheck shows what's wrongcheck — report errors and the fix commands, don't offer choicesget-id returns the type field, this skill is for channels onlytext
tgcm/
channels.json <- [{"name", "channelId", "status", "createdAt"}, ...]
{channel-name}/
channel.json <- per-channel metadata
content-index.json <- dedup index
content-queue.md <- post queue
A channel is bound when channelId is set and status is "connected".
Parameters are read from openclaw.json → skills.entries["tg-channel-manager"]:
ParameterTypeDescriptionconfig.channelIdstringTelegram channel ID for publishingconfig.chatIdstringChannel community chat ID (optional)
ParameterTypeDescriptionconfig.maxPostsPerDaynumberMaximum posts per dayconfig.maxDraftsPerRunnumberMaximum drafts per scout runconfig.timezonestringSchedule timezone (IANA format)config.languagestringPost language (ru, en, ...)config.cronScoutTimesstring[]Scout schedules (cron format)config.cronPublisherTimesstring[]Publisher schedules (cron format)
ParameterTypeDescriptionconfig.rubricsarrayRubrics: [{id, emoji, name}, ...]``config.searchQueriesstring[]Search queries for SearXNGconfig.searchIncludestringWhat to look for (filter description)config.searchExcludestringWhat to discard (filter description)config.evergreenstring[]Topics for articles when there are no news
ParameterTypeDescriptionconfig.postStyle.minCharsnumberMinimum characters per postconfig.postStyle.maxCharsnumberMaximum characters per postconfig.postStyle.emojiTitlebooleanEmoji before the titleconfig.postStyle.boldTitlebooleanBold titleconfig.postStyle.signaturestringPost signatureconfig.postStyle.newsFooterstringExtra text for news posts (empty = none)config.postStyle.articleFooterstringExtra text for articles (empty = none)
ParameterTypeDescriptionenv.SEARXNG_URLstringSearXNG instance URL
VariableHow to resolve{workspace}Your CWD. Run pwd or use --workspace .``{baseDir}``{workspace}/skills/tg-channel-manager
In sandbox mode ( workspaceAccess: "none"), the workspace is under ~/.openclaw/sandboxes, not ~/.openclaw/workspace. Always use CWD-relative paths.
Cron setup: see {baseDir}/references/cron-setup.md.
markdown
### <number>
- **Status:** draft | pending
- **Rubric:** <emoji> <name> (from config.rubrics)
- **Topic:** <topic>
- **Source:** <url> (for news)
- **Text:**
<post text>
Statuses:
After publishing, the entry is removed from content-queue.md.
Before every draft — mandatory check:
bash
python3 {baseDir}/scripts/dedup-check.py --base-dir <workspace> --topic "topic" --links "url1" "url2"
After publishing — add to index:
bash
python3 {baseDir}/scripts/dedup-check.py --base-dir <workspace> --add <msgId> --topic "topic" --links "url"
Rebuild index (via Telegram search):
bash
python3 {baseDir}/scripts/dedup-check.py --base-dir <workspace> --rebuild --channel-id <config.channelId>
Index is stored in <workspace>/content-index.json (or per-channel: tgcm/<name>/content-index.json).