From acedatacloud-content
Controls a personal Telegram account via MTProto (Telethon): list/search chats, read/summarize history, send/reply/forward/edit/delete/react to messages, download media, mark read, join/leave groups, and post in forum topics.
How this skill is triggered — by the user, by Claude, or both
Slash command
/acedatacloud-content:telegramWhen to use
Trigger for anything on the user's personal Telegram account: list recent conversations or just the unread ones, read / summarize a chat or group, search one chat or across all chats, look up a contact or a chat's info, download a photo/file from a message, list a forum group's topics, or take an action — send, reply, forward, edit, delete, react, send a file, mark a chat read, post into a forum topic, or join / leave a group or channel. This drives the user's OWN account over MTProto (not a bot), so it sees everything they see.
The summary Claude sees in its skill listing — used to decide when to auto-load this skill
We drive **personal** Telegram over MTProto with [Telethon](https://docs.telethon.dev/) —
We drive personal Telegram over MTProto with Telethon — this acts as the user's own account (a "userbot"), so unlike the Bot API it can read full history, list every conversation, and act on anyone the user can reach.
Credentials are injected as env vars by the connector:
TELEGRAM_API_ID — app idTELEGRAM_API_HASH — app hash — secret, never echoTELEGRAM_SESSION_STRING — Telethon StringSession = full account access. Never log,
echo, or print it. Treat it like the account password.The skill ships scripts/tg.py — self-contained (the only third-party dep is
telethon, preinstalled in the sandbox). Point a var at the shipped path and call it; no heredoc
to re-create per turn, so a multi-step flow (dry-run → confirm) can't lose the helper between calls:
# $SKILL_DIR can point at another skill loaded this turn — anchor on our own
# script (re-run this at the top of every fresh-shell Bash block below).
TG="$SKILL_DIR/scripts/tg.py"; [ -f "$TG" ] || TG=$(find /tmp -maxdepth 8 -path '*/skills/*/scripts/tg.py' 2>/dev/null | head -1)
[ -f "$TG" ] || { echo "telegram script not found (SKILL_DIR=$SKILL_DIR)" >&2; exit 1; }
python3 "$TG" whoami
Every state-changing command (send, reply, send-file, forward, edit, delete,
react, mark-read, join, leave) is gated: without a trailing --confirm it only DRY-RUNS (prints what
it would do, changes nothing). Read commands run directly. --confirm is honored only as the
last argument so a message/caption that merely contains "--confirm" can never silently confirm.
python3 "$TG" whoami
# → {"id": 8367450178, "username": "GermeyAce", "name": "Germey", "phone": "..."}
On an auth/session error the stored session is dead — tell the user to reconnect at https://auth.acedata.cloud/user/connections.
| Goal | Command |
|---|---|
| Recent conversations | python3 "$TG" list-chats 20 |
| Only chats with unread (ranked) | python3 "$TG" unread |
| A chat's history (oldest→newest) | python3 "$TG" get-messages <target> 50 |
| Search inside one chat | python3 "$TG" search <target> "kw" 30 |
| Search across ALL chats | python3 "$TG" search-global "kw" 30 |
| List contacts | python3 "$TG" contacts |
| Info about a chat/user | python3 "$TG" chat-info <target> |
| List a forum group's topics (threads) | python3 "$TG" list-topics <target> |
| t.me link to a message | python3 "$TG" message-link <target> <msg_id> |
<target> = numeric id (most reliable — from list-chats), @username, phone, or exact chat
name. In message rows, out:true = sent by the user; media:true = has an attachment.
Summarize-unread pattern: unread → pick the chats that matter → get-messages <id> N on
each → summarize. Don't dump 20k messages; sample the most-unread / most-relevant.
# Download an attachment from a message → returns the saved path
python3 "$TG" download-media <target> <msg_id> ./tg_downloads
# Send a local file OR an http(s) URL (optional caption) — GATED
python3 "$TG" send-file <target> /path/or/https-url "caption" --confirm
An http(s) URL is downloaded to a real local file first (with the right
extension from the URL / Content-Type) and then uploaded, so a remote image
lands as a photo — not a document, and not a silent failure. This is the
reliable way to "发图": pass the CDN URL straight to send-file.
To hand a downloaded file back to the user as a link, upload it to the CDN (see the
cos-upload skill) after download-media.
--confirm)Sending/editing/deleting acts as the real user. Always run the dry run first, show the user
exactly what will happen, get an explicit "yes", then re-run with --confirm as the last
argument. Never bulk-send.
python3 "$TG" send <target> "text" # → dry_run; add --confirm to send
python3 "$TG" send <target> "text" --topic <top_message> --confirm # into a forum topic thread
python3 "$TG" reply <target> <msg_id> "text" --confirm
python3 "$TG" forward <from_target> <msg_id> <to_target> --confirm
python3 "$TG" edit <target> <msg_id> "new text" --confirm # own messages
python3 "$TG" delete <target> <msg_id> --confirm # destructive
python3 "$TG" react <target> <msg_id> "👍" --confirm
python3 "$TG" mark-read <target> --confirm # sends read receipts
python3 "$TG" join <@username|t.me/link|t.me/+invite> --confirm # join a public group/channel or a private invite
python3 "$TG" leave <target> --confirm # leave a group/channel (not private chats)
join accepts a public @username / t.me/<name> (→ JoinChannelRequest) or a private
invite link t.me/+HASH / t.me/joinchat/HASH (→ ImportChatInviteRequest). Joining someone
else's group is a real membership change on the account — treat it like any other write:
dry-run, confirm, then --confirm. Never auto-join then bulk-message; that gets accounts
spam-limited.
Posting into a forum group. Some supergroups are forums: messages live in topics
(threads), and a plain send to the group root is rejected with TOPIC_CLOSED. Run
list-topics <target> first, pick a topic with "closed": false (an open chat/offtopic
thread — often titled Беседка/Флуд/Chat/Offtopic/General), then post with
send <target> "text" --topic <top_message> --confirm (its top_message id is the thread
anchor).
The dry run returns {"dry_run": true, "command": ..., "args": [...]} — present that to the
user verbatim as the confirmation prompt.
FloodWaitError: Telegram rate-limits userbots. On a flood-wait of N seconds, tell the
user to retry after N — never loop/retry aggressively (escalates toward a ban).AuthKeyError/unauthorized, reconnect the connector (don't retry).TELEGRAM_SESSION_STRING / TELEGRAM_API_HASH — full-account secrets.id from list-chats (the helper recovers its access hash by
scanning dialogs); names need an exact match, usernames need a leading @.message-link only works for public channels/supergroups; private 1:1 / basic groups
return an error (no shareable link exists).TOPIC_CLOSED on send = a forum supergroup; you can't post to the root. list-topics,
pick an open ("closed": false) thread, and send ... --topic <top_message>.ChatWriteForbiddenError on send = usually a channel's linked discussion group: you
must be subscribed to the parent channel (join <parent_channel>) before you can write in
its comment group, even though the group itself doesn't ban posting.edit/delete generally only apply to the user's own messages (admins can delete others
in groups they manage).After you successfully publish and obtain the live result URL, call the built-in
publish_artifact tool ONCE so the user can track this deliverable in My Outputs:
publish_artifact(kind="message", channel="telegram", title="<title>", url="<the REAL returned URL>", status="delivered")
Use the real returned URL — never fabricate one. Call it once per published item,
only after delivery is confirmed; skip it (or use status="failed") if publishing failed.
See _shared/artifacts.md.
npx claudepluginhub acedatacloud/skills --plugin acedatacloud-contentProvides Telegram bot tools to send messages, media, locations, contacts, and polls; edit, delete, forward, copy, and pin messages; resolve chats, members, and files. Use when the user mentions Telegram or wants a bot to post or manage content.
Telegram CLI for reading, searching, and sending messages. Use when the user asks about Telegram messages, wants to check inbox, search chats, send messages, or look up contacts and groups.
user wants to send a text message on Telegram as their personal account via MTProto, text someone, or message a contact by username, phone, or.