360 Secure SkillHub
360 安全技能中心

telethon-plus

核心功能涵盖消息收发编辑、聊天创建/管理、成员权限调整、媒体传输、投票操作,支持通过webhook/WebSocket推送实时事件。典型场景包括账号自动化运维、将Telegram消息同步至外部应用、批量处理聊天内容。通过JSON格式HTTP请求或MCP工具调用交互,需对接已部署并登录的实例,内置防限流机制保障账号安全,提供Bearer认证选项

psyb0t
psyb0t
热度 39.17
最近更新 2026-08-02 04:29:47
分类:自动化依赖:第三方 API / MCP 服务应用场景:社交媒体 / 任务管理 / 工作流 / 自动化脚本

安装技能

复制下面的话术发给你的 Agent,它会通过 secure-skills 检索并安装该技能。

执行命令安装此 skill: npx secure-skills-cli add telethon-plus
Metadata
name
telethon-plus
description
HTTP + MCP control plane over a REAL Telegram MTProto userbot (Telethon) — a full user account, not the Bot API. Send/read/edit/delete/forward messages, browse dialogs, resolve entities/contacts, download + send media, manage chats (create/join/leave/admin), run polls, and forward every incoming message to a webhook (TELETHON_POST_TO_URL). JSON in / JSON out on one port; MCP streamable-HTTP at /mcp/ mirrors the same tools; optional bearer auth. Built-in anti-flood throttling keeps the account under Telegram's limits. Use when the user wants an agent, script, or curl one-liner to drive their own Telegram account over HTTP/MCP — read a chat, DM someone, post to a channel, react/pin, or pipe incoming DMs into an app. Drives a real account acting AS the user with full account access.
homepage
https://github.com/psyb0t/docker-telethon-plus
user-invocable
true
metadata
{"openclaw":{"emoji":"✈️","primaryEnv":"TELETHON_PLUS_URL","requires":{"bins":["docker","curl"]}}}
permissions
{"shell":"setup-only — docker/curl for install, login, and container management (see references/setup.md); runtime API calls are plain HTTP, not shell","network":"outbound HTTP to $TELETHON_PLUS_URL (every API/MCP call), plus server-side fetch of caller-supplied file_url when present (POST /api/messages, send_file)","account_access":"full-access Telegram user account — read, write, and admin (not a sandboxed bot); every call acts as the account owner"}

telethon-plus

Your Telegram account behind an HTTP API. Wraps Telethon — the real MTProto userbot client, a full user account, not the neutered Bot API — behind a JSON HTTP API and a Model Context Protocol endpoint. Same tool registry, two doors: POST JSON at /api/..., or point an MCP-aware agent at /mcp/. Either way it talks to Telegram as you, with full account access.

Capabilities:

  • Messages — send text, send media from a URL, read history, fetch one message, edit, bulk-delete, forward, mark read.
  • Reactions / pins — react with an emoji, remove a reaction, pin / unpin.
  • Dialogs / entities — list your chats, resolve any @username / phone / t.me link / numeric ID to a profile, bulk-resolve.
  • Media — download an attachment (raw bytes over HTTP, or base64 via MCP).
  • Chats — create a supergroup/channel, delete one you own, join public / private-invite, leave, resolve a channel's linked discussion group, list participants.
  • Admin — ban / unban / kick / promote / demote in groups you administer.
  • Polls — create, vote, read results.
  • Incoming events — every new message / edit / delete / chat action streamed over WebSocket and POSTed to your webhook (TELETHON_POST_TO_URL).
  • Health / throttle — liveness, live rate-limit state, flood-risk tier, Prometheus metrics.

For installation, login, and container setup, see references/setup.md.

Security & safety

  • Declared capabilities (see permissions: in the frontmatter): shell (setup-only), network (runtime HTTP), and full Telegram account access. Setup-only shell vs. runtime API is a hard line — docker run / docker compose / curl in references/setup.md are one-time operator commands to stand up the container; every runtime call this skill makes afterward is a plain HTTP request (curl as an HTTP client, or MCP), never a new shell/docker invocation against the host.
  • Outbound HTTP — every API call is a curl/HTTP request to the telethon-plus server ($TELETHON_PLUS_URL), and the server itself makes outbound calls to Telegram's MTProto servers and, when file_url is used, to whatever URL is given (see the SSRF note below).
  • Drives a full-access Telegram user account — read AND write AND admin, not a sandboxed bot. Any call this skill makes acts as the account owner (see "Authorized / responsible use" below for the account-level rules).
  • Destructive / admin operations require explicit user confirmation naming the exact target (chat, message IDs, user) before running — see the per-endpoint warnings in the API tables below (DELETE /api/chats, ban/kick/promote, bulk DELETE /api/messages).
  • Deployment: bind the server to localhost/loopback or put it behind TLS on a reverse proxy, set TELETHON_AUTH_KEY (empty = no auth, wide open), and never expose /mcp/ or /api/ to untrusted agents/networks — either surface hands out full account control. See references/setup.md for the full deployment + auth guidance.

Authorized / responsible use

This drives a REAL Telegram user account, not a bot. Whoever holds the session string is the account owner — full read/write access to every DM, group, and channel that account can reach. Treat it like the account's password.

  • Only ever point this skill at an instance the user runs and owns, driving the user's own account (or an account they are explicitly authorized to operate).
  • Do not send spam, mass-DM strangers, scrape at volume, auto-join channels en masse, or run any automation that violates Telegram's Terms of Service — that gets the account banned (the throttle layer reduces flood risk, it does not make abuse safe).
  • Never provision, escalate, or log in a new account from here. This skill is consumer-only: it talks to an already-running, already-authorized instance. First-time login is an operator-side step (see setup) and is out of scope for the agent.
  • Writes are destructive and act as the user. DELETE /api/chats is irreversible; deleting / editing messages and banning users are visible actions others see. Confirm intent before running writes on shared chats.
  • Never print, log, or echo the session string, TELETHON_API_HASH, TELETHON_AUTH_KEY, phone numbers, or message contents beyond what the task needs.

When To Use

  • Read recent messages from a chat, or search a chat's history.
  • Send a DM, post to a channel/group, or drop a note in Saved Messages (chat=me).
  • Send a file/photo to a chat by URL, or download an attachment someone sent.
  • React to, pin, edit, forward, or delete messages.
  • List the account's dialogs, resolve a handle to a profile, or list a group's members.
  • Create / join / leave a chat; run group admin actions on chats the account administers.
  • Create or tally a poll.
  • Wire incoming Telegram messages into an app via the outbound webhook or the WS stream.

When NOT To Use

  • You don't have an instance the user owns — this is consumer-only; it never stands up or logs in an account.
  • Anything the user isn't authorized to do with that account — spam, mass outreach, bulk scraping, ToS-violating automation.
  • Bot-API-style multi-tenant bots — this is one user account per container, not a bot framework. If you need a bot token surface, use the Bot API instead.
  • Real-time streaming reads — history reads are request/response. For live events use the WS stream or the webhook, not a poll loop.
  • Bulk resolving cold numeric IDs — MTProto needs an access_hash. Numeric IDs only resolve for entities the session has already seen (via a prior @username / t.me lookup, a dialog list, or an incoming message). Resolve by @username first.

Setup

The container should already be running and logged in (operator-side — see setup). Set the base URL:

export TELETHON_PLUS_URL=http://localhost:8080

If the server was started with TELETHON_AUTH_KEY, export it too — every request except /healthz and /metrics then needs the bearer header:

export TELETHON_AUTH_KEY=<the-key>
# add to every call below:  -H "Authorization: Bearer $TELETHON_AUTH_KEY"

Verify:

curl -s $TELETHON_PLUS_URL/healthz
# {"status": "ok", "authorized": true}

If the session is dead (bad/revoked string, or Telegram unreachable) the container fails to start rather than booting with authorized: false — the process exits and /healthz never comes up. Check docker logs and re-login. /healthz is always public.

For install / first-time login / env vars / ports, see references/setup.md.

Conventions

  • Response shape: every 2xx returns the resource directly — no {"result": ...} envelope. Lists are JSON arrays, singles are JSON objects. Errors return {"detail": ...} (FastAPI standard).
  • Chat references (chat, from_chat, to_chat) accept whatever Telethon accepts: @username, phone +1234567890, https://t.me/name, numeric ID 123456789, supergroup/channel ID -1001234567890, or me (your Saved Messages). In {chat} path segments, phone and t.me links must be URL-encoded (+ → %2B); usernames and IDs work inline.
  • Errors: 400 bad JSON / validation / bad input · 401 missing-or-wrong bearer (only when TELETHON_AUTH_KEY set) · 403 write attempted in read-only mode · 404 unknown endpoint / admin action · 502 Telegram RPC error, body {"telegram_error": "...", "message": "..."}.

API — GET /api/me

The authorized account's own profile.

curl -s $TELETHON_PLUS_URL/api/me | jq
{ "id": 123456789, "type": "User", "username": "someone", "first_name": "Some", "phone": "+..." }

API — GET /api/entities

Resolve any chat reference to a full profile.

Param Type Default Notes
chat string required @username / phone / t.me link / numeric ID
curl -s "$TELETHON_PLUS_URL/api/entities?chat=@telegram" | jq
# { "id": 1234567, "type": "Channel", "username": "telegram", "title": "Telegram" }

API — POST /api/entities/bulk

Resolve many references at once, honoring the resolve-username bucket.

Field Type Default Notes
chats list[string] required References to resolve

API — GET /api/dialogs

List the account's dialogs, newest activity first.

Param Type Default Notes
limit int 20 1–200
archived bool false Include archived chats
search string — Case-insensitive substring on title / username
curl -s "$TELETHON_PLUS_URL/api/dialogs?limit=10&archived=false" | jq

Each dialog carries id, type, username, first_name/title, unread_count, pinned, and a last_message object.

API — GET /api/messages

Read recent messages from a chat (newest first).

Param Type Default Notes
chat string required Chat to read from
limit int 20 1–200
offset_id int 0 Start before this message ID (pagination)
search string — Full-text filter
curl -s "$TELETHON_PLUS_URL/api/messages?chat=me&limit=5&search=hello" | jq
[
  { "id": 4242, "date": "2026-04-29T12:00:00+00:00", "chat_id": 12345,
    "sender_id": 67890, "text": "hello", "out": false,
    "reply_to_msg_id": null, "media": false, "media_type": null }
]

Returns [] if nothing matches.

API — GET /api/messages/{id}

Fetch a single message. chat is a required query param.

curl -s "$TELETHON_PLUS_URL/api/messages/4242?chat=me" | jq

API — POST /api/messages

Send a message. One endpoint, two flavors — if the body has file_url, that URL is fetched and sent as media (with text as the caption); otherwise text is sent as a plain message.

SSRF note: file_url is fetched server-side — the container makes the HTTP request, not the caller. An attacker-controlled file_url can be used to probe internal/private network addresses reachable from the container. Don't pass arbitrary caller-supplied URLs through as file_url. Restrict it to https scheme and a small set of trusted, publicly-known domains you control or explicitly trust — never build file_url from untrusted input (a message someone else sent, a scraped page, an LLM-generated guess). Prefer local file upload over server-side URL fetches: fetch the file yourself (client-side, where you can validate it) and send it as a direct upload instead of handing the container a URL to fetch blind.

Field Type Default Notes
chat string required Target chat
text string required if no file_url Message text or caption (1–4096 chars)
file_url string null HTTPS URL to fetch and send as media
parse_mode string null md / markdown / html / null
reply_to int null Message ID to reply to
silent bool false Send without notification
link_preview bool true Show link previews (text only)
schedule string null Future ISO datetime to schedule the send
force_document bool false With file_url: send as generic document
max_bytes int 52428800 With file_url: reject files larger than this (max 2 GB)
# Text.
curl -s -X POST $TELETHON_PLUS_URL/api/messages \
  -H 'Content-Type: application/json' \
  -d '{"chat":"me","text":"**hello** from a container","parse_mode":"md","silent":true}' | jq

# Media from a URL (text becomes the caption).
curl -s -X POST $TELETHON_PLUS_URL/api/messages \
  -H 'Content-Type: application/json' \
  -d '{"chat":"me","file_url":"https://example.com/photo.jpg","text":"a caption"}' | jq

Returns the created message object.

API — PATCH /api/messages/{id}

Edit a message you sent. ID in the URL, rest in the body.

Field Type Default Notes
chat string required Chat containing the message
text string required New text (1–4096 chars)
parse_mode string null md / html / null
link_preview bool true Show link previews
curl -s -X PATCH $TELETHON_PLUS_URL/api/messages/4242 \
  -H 'Content-Type: application/json' \
  -d '{"chat":"me","text":"fixed version","parse_mode":"md"}' | jq

API — DELETE /api/messages

Bulk delete by ID (body carries the list).

Field Type Default Notes
chat string required Chat containing the messages
message_ids list[int] required IDs to delete (max 100)
revoke bool true Delete for everyone, not just yourself

Irreversible, and with revoke: true (the default) it deletes for everyone in the chat, not just you. Confirm the exact chat and message_ids with the user before calling this on a shared chat.

curl -s -X DELETE $TELETHON_PLUS_URL/api/messages \
  -H 'Content-Type: application/json' \
  -d '{"chat":"me","message_ids":[4242,4243],"revoke":true}' | jq
# { "deleted": 2, "requested": 2 }

API — POST /api/messages/forward

Field Type Default Notes
from_chat string required Source chat
to_chat string required Destination chat
message_ids list[int] required IDs to forward (max 100)
silent bool false Forward without notification

API — POST /api/messages/read

Mark messages as read.

Field Type Default Notes
chat string required Chat to mark read
max_id int 0 Mark up to this message ID. 0 = all.
curl -s -X POST $TELETHON_PLUS_URL/api/messages/read \
  -H 'Content-Type: application/json' -d '{"chat":"me","max_id":0}' | jq
# { "ok": true }

API — GET /api/messages/{id}/media

Download a message's attachment as raw bytes (binary stream). Content-Type comes from Telegram; Content-Disposition: attachment; filename=... is set. chat is a required query param.

Param Type Default Notes
chat string required Chat containing the message
max_bytes int 52428800 Reject files larger than this
curl -s "$TELETHON_PLUS_URL/api/messages/4242/media?chat=me" -o attachment.bin

MCP clients: use the download_media tool instead — it returns base64 in the JSON result.

API — reactions & pins

Method + path Body / params What it does
POST /api/messages/{id}/reactions chat, emoji, big? React to a message
DELETE /api/messages/{id}/reactions chat Remove your reaction
POST /api/messages/{id}/pin chat, silent?, pm_oneside? Pin
POST /api/messages/{id}/unpin chat Unpin
curl -s -X POST $TELETHON_PLUS_URL/api/messages/4242/reactions \
  -H 'Content-Type: application/json' -d '{"chat":"me","emoji":"🔥"}' | jq

API — GET /api/participants

List members of a group or channel.

Param Type Default Notes
chat string required Group or channel
limit int 100 1–1000
search string — Filter by name

Large public channels may return a limited set or require admin rights.

API — chats

Method + path Body / params What it does
POST /api/chats title, megagroup? (default true) Create a supergroup (true) or broadcast channel (false)
DELETE /api/chats chat Delete a supergroup/channel you own — irreversible
POST /api/chats/join chat Join a public channel/group
POST /api/chats/invite invite Join a private chat via a t.me/+hash link
POST /api/chats/leave chat Leave a channel/group
GET /api/chats/{chat}/linked — Resolve a channel's linked discussion group

Destructive. DELETE /api/chats permanently deletes the chat for everyone in it and cannot be undone. Require explicit user confirmation naming the exact chat before calling it — prefer leaving it disabled/unused unless the task genuinely needs it.

curl -s -X POST $TELETHON_PLUS_URL/api/chats \
  -H 'Content-Type: application/json' -d '{"title":"my-group","megagroup":true}' | jq
# { "id": 1234567890, "type": "Channel", "title": "my-group" }

API — chat admin

POST /api/chats/{chat}/admin/{action} where {action} is one of ban, unban, kick, promote, demote. Requires admin rights in the chat.

Action Body Notes
ban user, until_seconds? Ban; until_seconds=0 = permanent — visible, disruptive
unban user Lift a ban
kick user Ban + immediate unban (they can rejoin via invite) — visible, disruptive
promote user, title? (≤16 chars), plus booleans change_info, post_messages, edit_messages, delete_messages, ban_users, invite_users, pin_messages, add_admins, anonymous, manage_call (all default false) Grant admin rights — pass the specific booleans you want, omitting all of them grants admin with no permissions — privilege escalation, irreversible without another admin demoting them back
demote user Strip admin rights

Admin ops act on other people's membership/permissions and are visible to the whole chat. Require explicit user confirmation naming the exact chat and target user before calling ban, kick, or promote.

curl -s -X POST "$TELETHON_PLUS_URL/api/chats/-1001234567890/admin/ban" \
  -H 'Content-Type: application/json' -d '{"user":"@baduser"}' | jq

API — polls

Method + path Body / params What it does
POST /api/polls chat, question, options, multiple_choice?, quiz?, correct_option?, solution? Create a poll
POST /api/polls/{id}/vote chat, options (0-based indices) Vote
GET /api/polls/{id}/results chat Current vote counts

API — status & observability

Path What it returns
GET /healthz {"status": "ok", "authorized": <bool>} — always public
GET /api/throttle/status Live bucket usage, adaptive multiplier, recent flood events, cache stats
GET /api/account/health {"authorized", "risk", "multiplier", "flood_events_1h", ...}; risk is ok / warning / high
GET /metrics Prometheus exposition (public even when auth is on)

Every /api/... response also carries X-Throttle-Multiplier, X-Throttle-Flood-Events-1h, and X-RateLimit-Remaining-<bucket> headers, so clients can self-throttle without polling.

The server ships conservative anti-flood throttling on by default (per-method token buckets, per-chat send/read intervals, global gap + jitter, adaptive backoff on FLOOD_WAIT, plus a persistent entity cache). If a call seems slow, it may be the throttle protecting the account — check GET /api/throttle/status. Tuning the buckets is an operator concern (see setup); don't try to defeat them.

Incoming-message webhook (TELETHON_POST_TO_URL)

Data exfiltration warning: enabling TELETHON_POST_TO_URL forwards every incoming Telegram event — full message content, sender metadata, and account activity — to that external endpoint, for as long as the container runs. Point it ONLY at a trusted HTTPS endpoint you control. Anyone who controls that endpoint gets a live copy of everything the account receives.

When the operator sets TELETHON_POST_TO_URL on the server, every incoming Telegram event (new message, edit, delete, chat action) is POSTed to that URL as JSON — fire-and-forget, so a slow or failing webhook never blocks Telethon's loop. This is the way to pipe incoming DMs/messages into a separate app or worker that can't hold a WebSocket open.

Event payload:

{
  "type": "NewMessage",
  "message": {
    "id": 4242, "date": "2026-04-29T12:00:00+00:00", "text": "hi",
    "out": false, "sender_id": 12345, "chat_id": -1001234567890,
    "reply_to_msg_id": null, "media": false, "media_type": null
  },
  "chat_id": -1001234567890
}

type is NewMessage, MessageEdited, MessageDeleted, or ChatAction — same shape, fewer fields on the non-message ones.

The same events also stream over WebSocket at GET /ws/updates (pass ?token=<TELETHON_AUTH_KEY> when auth is on — browsers can't set the Authorization header). Multiple subscribers each get their own queue; slow consumers drop events at TELETHON_UPDATES_BUFFER_SIZE (default 256). Updates are only registered when TELETHON_UPDATES_ENABLED=true (the default).

MCP Endpoint

An MCP server is mounted at /mcp/ using the streamable HTTP transport, on the same port as the HTTP API, behind the same bearer auth. It's stateless — every request is independent, no session juggling. Every tool in the registry (same names, same schemas as the endpoints above) is exposed automatically.

Point an MCP-aware agent at:

http://your-host:8080/mcp/

Wire into Claude Code:

claude mcp add --transport http telethon-plus $TELETHON_PLUS_URL/mcp/
# with auth:
claude mcp add --transport http telethon-plus $TELETHON_PLUS_URL/mcp/ \
  --header "Authorization: Bearer $TELETHON_AUTH_KEY"

The 34 tools, grouped: get_me, get_entity, bulk_resolve, get_dialogs, get_messages, get_message, send_message, send_file, edit_message, delete_messages, forward_messages, mark_read, download_media, set_reaction, remove_reaction, pin_message, unpin_message, get_participants, create_group, delete_chat, join_chat, leave_chat, join_via_invite, get_linked_chat, ban_user, unban_user, kick_user, promote_user, demote_user, create_poll, vote_poll, get_poll_results, throttle_status, account_health.

send_message and send_file are separate MCP tools (the POST /api/messages route dispatches between them by whether file_url is present). download_media returns base64 in data_base64 (the HTTP /media route streams raw bytes instead).

Raw JSON-RPC

The transport requires Accept: application/json, text/event-stream. For debugging or non-MCP callers, hit it directly:

# tools/list
curl -s $TELETHON_PLUS_URL/mcp/ \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'

# tools/call — send a message
curl -s $TELETHON_PLUS_URL/mcp/ \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -d '{
    "jsonrpc":"2.0","id":2,"method":"tools/call",
    "params":{
      "name":"send_message",
      "arguments":{"chat":"me","text":"hi from mcp"}
    }
  }'

MCP tool errors come back inside the result (not as HTTP status): read-only → {"error":"read_only",...}, Telegram RPC → {"error":"telegram_rpc","type":...}, bad args → {"error":"invalid_argument",...}.

Bearer-Token Auth

If TELETHON_AUTH_KEY is set on the server, every route except /healthz and /metrics requires Authorization: Bearer <key> (constant-time compared). Missing/wrong → 401 {"detail":"unauthorized"}. The WebSocket takes the key as ?token=. Empty/unset key = wide open — fine on a private network; for untrusted networks pair the key with a reverse proxy doing TLS.

curl -s -H "Authorization: Bearer $TELETHON_AUTH_KEY" $TELETHON_PLUS_URL/api/me | jq

Typical Workflows

Read the latest from a chat

curl -s "$TELETHON_PLUS_URL/api/messages?chat=@somechannel&limit=10" \
  | jq -r '.[] | "\(.sender_id): \(.text)"'

Send a DM / post to a channel

curl -s -X POST $TELETHON_PLUS_URL/api/messages \
  -H 'Content-Type: application/json' \
  -d '{"chat":"@someone","text":"heads up — deploy is done"}' | jq

Note to self (Saved Messages)

curl -s -X POST $TELETHON_PLUS_URL/api/messages \
  -H 'Content-Type: application/json' -d '{"chat":"me","text":"remember this"}' | jq

Resolve-then-refer (numeric IDs need a warm session)

# Resolve by @username once (caches the access_hash) ...
curl -s "$TELETHON_PLUS_URL/api/entities?chat=@somebot" | jq .id
# ... then subsequent calls can use the numeric ID.

Send → verify → clean up (self-test roundtrip)

MARKER="ping-$(date +%s)"
ID=$(curl -s -X POST $TELETHON_PLUS_URL/api/messages \
  -H 'Content-Type: application/json' \
  -d "{\"chat\":\"me\",\"text\":\"$MARKER\"}" | jq .id)

curl -s "$TELETHON_PLUS_URL/api/messages?chat=me&search=$MARKER" | jq -r '.[].text'

curl -s -X DELETE $TELETHON_PLUS_URL/api/messages \
  -H 'Content-Type: application/json' \
  -d "{\"chat\":\"me\",\"message_ids\":[$ID]}" | jq

Check flood risk before a burst of writes

curl -s $TELETHON_PLUS_URL/api/account/health | jq '{risk, multiplier, flood_events_1h}'
# risk "ok" → go; "warning"/"high" → back off, the account is being rate-limited.

For a small send/read wrapper over TELETHON_PLUS_URL, see scripts/telethon.sh.