@acedatacloud/skills 2026.714.3 → 2026.714.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@acedatacloud/skills",
3
- "version": "2026.714.3",
3
+ "version": "2026.714.4",
4
4
  "description": "Agent Skills for AceDataCloud AI services — music, image, video generation, LLM chat, web search. Compatible with Claude Code, GitHub Copilot, Gemini CLI, OpenAI Codex, and 30+ AI coding agents.",
5
5
  "keywords": [
6
6
  "agent-skills",
@@ -1,20 +1,20 @@
1
1
  ---
2
2
  name: telegram
3
- description: Full personal Telegram control over MTProto (Telethon) with the user's own account — list/search chats, read & summarize history, see unread, look up contacts & chat info, download media, and send / reply / forward / edit / delete / react / send files / mark read / join & leave groups. Use when the user mentions Telegram, a Telegram chat/group/contact, "我的 Telegram", reading/replying/forwarding/summarizing Telegram messages, their unread Telegram, joining or leaving a Telegram group/channel, or sending a file/message on Telegram.
3
+ description: Full personal Telegram control over MTProto (Telethon) with the user's own account — list/search chats, read & summarize history, see unread, look up contacts & chat info, list a forum group's topics, download media, and send / reply / forward / edit / delete / react / send files / mark read / join & leave groups, including posting into a forum topic thread. Use when the user mentions Telegram, a Telegram chat/group/contact, "我的 Telegram", reading/replying/forwarding/summarizing Telegram messages, their unread Telegram, joining or leaving a Telegram group/channel, posting into a forum group's topic, or sending a file/message on Telegram.
4
4
  when_to_use: |
5
5
  Trigger for anything on the user's personal Telegram account: list recent
6
6
  conversations or just the unread ones, read / summarize a chat or group,
7
7
  search one chat or across all chats, look up a contact or a chat's info,
8
- download a photo/file from a message, or take an action — send, reply,
9
- forward, edit, delete, react, send a file, mark a chat read, or join / leave
10
- a group or channel. This drives the user's OWN account over MTProto (not a
11
- bot), so it sees everything they see.
8
+ download a photo/file from a message, list a forum group's topics, or take an
9
+ action — send, reply, forward, edit, delete, react, send a file, mark a chat
10
+ read, post into a forum topic, or join / leave a group or channel. This drives
11
+ the user's OWN account over MTProto (not a bot), so it sees everything they see.
12
12
  connections: [telegram]
13
13
  allowed_tools: [Bash]
14
14
  license: Apache-2.0
15
15
  metadata:
16
16
  author: acedatacloud
17
- version: "1.3"
17
+ version: "1.4"
18
18
  ---
19
19
 
20
20
  We drive **personal** Telegram over MTProto with [Telethon](https://docs.telethon.dev/) —
@@ -68,6 +68,7 @@ https://auth.acedata.cloud/user/connections.
68
68
  | Search across ALL chats | `python3 "$TG" search-global "kw" 30` |
69
69
  | List contacts | `python3 "$TG" contacts` |
70
70
  | Info about a chat/user | `python3 "$TG" chat-info <target>` |
71
+ | List a forum group's topics (threads) | `python3 "$TG" list-topics <target>` |
71
72
  | t.me link to a message | `python3 "$TG" message-link <target> <msg_id>` |
72
73
 
73
74
  `<target>` = numeric id (most reliable — from `list-chats`), `@username`, phone, or exact chat
@@ -101,6 +102,7 @@ argument**. Never bulk-send.
101
102
 
102
103
  ```sh
103
104
  python3 "$TG" send <target> "text" # → dry_run; add --confirm to send
105
+ python3 "$TG" send <target> "text" --topic <top_message> --confirm # into a forum topic thread
104
106
  python3 "$TG" reply <target> <msg_id> "text" --confirm
105
107
  python3 "$TG" forward <from_target> <msg_id> <to_target> --confirm
106
108
  python3 "$TG" edit <target> <msg_id> "new text" --confirm # own messages
@@ -117,6 +119,13 @@ else's group is a real membership change on the account — treat it like any ot
117
119
  dry-run, confirm, then `--confirm`. Never auto-join then bulk-message; that gets accounts
118
120
  spam-limited.
119
121
 
122
+ **Posting into a forum group.** Some supergroups are *forums*: messages live in topics
123
+ (threads), and a plain `send` to the group root is rejected with `TOPIC_CLOSED`. Run
124
+ `list-topics <target>` first, pick a topic with `"closed": false` (an open chat/offtopic
125
+ thread — often titled `Беседка`/`Флуд`/`Chat`/`Offtopic`/`General`), then post with
126
+ `send <target> "text" --topic <top_message> --confirm` (its `top_message` id is the thread
127
+ anchor).
128
+
120
129
  The dry run returns `{"dry_run": true, "command": ..., "args": [...]}` — present that to the
121
130
  user verbatim as the confirmation prompt.
122
131
 
@@ -132,6 +141,11 @@ user verbatim as the confirmation prompt.
132
141
  scanning dialogs); names need an exact match, usernames need a leading `@`.
133
142
  - **`message-link`** only works for public channels/supergroups; private 1:1 / basic groups
134
143
  return an error (no shareable link exists).
144
+ - **`TOPIC_CLOSED` on send** = a forum supergroup; you can't post to the root. `list-topics`,
145
+ pick an open (`"closed": false`) thread, and `send ... --topic <top_message>`.
146
+ - **`ChatWriteForbiddenError` on send** = usually a channel's linked *discussion* group: you
147
+ must be subscribed to the parent channel (`join <parent_channel>`) before you can write in
148
+ its comment group, even though the group itself doesn't ban posting.
135
149
  - **`edit`/`delete`** generally only apply to the user's own messages (admins can delete others
136
150
  in groups they manage).
137
151
 
@@ -117,6 +117,19 @@ def need(n):
117
117
  raise ValueError(f"{cmd} needs {n} argument(s), got {len(args)}")
118
118
 
119
119
 
120
+ def _pop_opt(argv, name):
121
+ """Extract `--name VALUE` from argv; return (value_or_None, remaining_argv).
122
+
123
+ Only a standalone `--name` token matches, so a message that merely contains
124
+ the flag text as part of one argument is left untouched.
125
+ """
126
+ if name in argv:
127
+ i = argv.index(name)
128
+ if i + 1 < len(argv):
129
+ return argv[i + 1], argv[:i] + argv[i + 2:]
130
+ return None, argv
131
+
132
+
120
133
  async def run():
121
134
  if cmd in GATED and not CONFIRM:
122
135
  out({"dry_run": True, "command": cmd, "args": args,
@@ -185,6 +198,18 @@ async def run():
185
198
  pass
186
199
  out(info)
187
200
 
201
+ elif cmd == "list-topics":
202
+ # Forum-style supergroups organize messages into topics (threads).
203
+ # A plain send to the group root is rejected (TOPIC_CLOSED); you must
204
+ # post into an open topic via `send --topic <top_message>`.
205
+ need(1); ent = await resolve(cl, args[0])
206
+ limit = int(args[1]) if len(args) > 1 else 100
207
+ res = await cl(functions.messages.GetForumTopicsRequest(
208
+ channel=ent, offset_date=0, offset_id=0, offset_topic=0, limit=limit))
209
+ out([{"id": getattr(t, "id", None), "title": getattr(t, "title", None),
210
+ "closed": getattr(t, "closed", None), "hidden": getattr(t, "hidden", None),
211
+ "top_message": getattr(t, "top_message", None)} for t in res.topics])
212
+
188
213
  elif cmd == "message-link":
189
214
  need(2); ent = await resolve(cl, args[0]); mid = int(args[1])
190
215
  try:
@@ -205,9 +230,15 @@ async def run():
205
230
 
206
231
  # ---- gated writes (need trailing --confirm) ----
207
232
  elif cmd == "send":
208
- need(2); ent = await resolve(cl, args[0])
209
- m = await cl.send_message(ent, args[1])
210
- out({"sent": True, "id": m.id})
233
+ # `--topic <top_message>` posts into a forum topic thread (reply_to);
234
+ # required for forum-style supergroups where a root send is rejected.
235
+ topic, sargs = _pop_opt(args, "--topic")
236
+ if len(sargs) < 2:
237
+ raise ValueError("send needs <target> <text> (optional --topic <id>)")
238
+ ent = await resolve(cl, sargs[0])
239
+ kw = {"reply_to": int(topic)} if topic is not None else {}
240
+ m = await cl.send_message(ent, sargs[1], **kw)
241
+ out({"sent": True, "id": m.id, "topic": int(topic) if topic is not None else None})
211
242
 
212
243
  elif cmd == "reply":
213
244
  need(3); ent = await resolve(cl, args[0])