@acedatacloud/skills 2026.714.2 → 2026.714.3

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.2",
3
+ "version": "2026.714.3",
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,19 +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. Use when the user mentions Telegram, a Telegram chat/group/contact, "我的 Telegram", reading/replying/forwarding/summarizing Telegram messages, their unread Telegram, 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, 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.
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
8
  download a photo/file from a message, or take an action — send, reply,
9
- forward, edit, delete, react, send a file, or mark a chat read. This drives
10
- the user's OWN account over MTProto (not a bot), so it sees everything they see.
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.
11
12
  connections: [telegram]
12
13
  allowed_tools: [Bash]
13
14
  license: Apache-2.0
14
15
  metadata:
15
16
  author: acedatacloud
16
- version: "1.2"
17
+ version: "1.3"
17
18
  ---
18
19
 
19
20
  We drive **personal** Telegram over MTProto with [Telethon](https://docs.telethon.dev/) —
@@ -42,7 +43,7 @@ python3 "$TG" whoami
42
43
  ```
43
44
 
44
45
  Every state-changing command (`send`, `reply`, `send-file`, `forward`, `edit`, `delete`,
45
- `react`, `mark-read`) is **gated**: without a trailing `--confirm` it only DRY-RUNS (prints what
46
+ `react`, `mark-read`, `join`, `leave`) is **gated**: without a trailing `--confirm` it only DRY-RUNS (prints what
46
47
  it would do, changes nothing). Read commands run directly. `--confirm` is honored **only as the
47
48
  last argument** so a message/caption that merely contains "--confirm" can never silently confirm.
48
49
 
@@ -106,8 +107,16 @@ python3 "$TG" edit <target> <msg_id> "new text" --confirm # own messages
106
107
  python3 "$TG" delete <target> <msg_id> --confirm # destructive
107
108
  python3 "$TG" react <target> <msg_id> "👍" --confirm
108
109
  python3 "$TG" mark-read <target> --confirm # sends read receipts
110
+ python3 "$TG" join <@username|t.me/link|t.me/+invite> --confirm # join a public group/channel or a private invite
111
+ python3 "$TG" leave <target> --confirm # leave a group/channel (not private chats)
109
112
  ```
110
113
 
114
+ `join` accepts a public `@username` / `t.me/<name>` (→ JoinChannelRequest) or a private
115
+ invite link `t.me/+HASH` / `t.me/joinchat/HASH` (→ ImportChatInviteRequest). Joining someone
116
+ else's group is a real membership change on the account — treat it like any other write:
117
+ dry-run, confirm, then `--confirm`. Never auto-join then bulk-message; that gets accounts
118
+ spam-limited.
119
+
111
120
  The dry run returns `{"dry_run": true, "command": ..., "args": [...]}` — present that to the
112
121
  user verbatim as the confirmation prompt.
113
122
 
@@ -19,10 +19,10 @@ import tempfile
19
19
  import urllib.request
20
20
  from urllib.parse import urlparse
21
21
 
22
- from telethon import TelegramClient
22
+ from telethon import TelegramClient, errors, utils
23
23
  from telethon.sessions import StringSession
24
24
  from telethon.tl import functions
25
- from telethon.tl.types import ReactionEmoji
25
+ from telethon.tl.types import ReactionEmoji, User
26
26
 
27
27
  API_ID = int(os.environ["TELEGRAM_API_ID"])
28
28
  API_HASH = os.environ["TELEGRAM_API_HASH"]
@@ -35,7 +35,7 @@ CONFIRM = bool(_raw) and _raw[-1] == "--confirm"
35
35
  a = _raw[:-1] if CONFIRM else list(_raw)
36
36
  cmd = a[0] if a else "help"
37
37
  args = a[1:]
38
- GATED = {"send", "reply", "send-file", "forward", "edit", "delete", "react", "mark-read"}
38
+ GATED = {"send", "reply", "send-file", "forward", "edit", "delete", "react", "mark-read", "join", "leave"}
39
39
 
40
40
 
41
41
  def out(o):
@@ -252,6 +252,33 @@ async def run():
252
252
  await cl.send_read_acknowledge(ent)
253
253
  out({"marked_read": True})
254
254
 
255
+ elif cmd == "join":
256
+ need(1); target = args[0]
257
+ # A public @username / t.me/<user> resolves + JoinChannelRequest; a
258
+ # private invite (t.me/+HASH, joinchat/HASH, tg://join?invite=HASH)
259
+ # can't be resolved without joining, so import it by hash directly.
260
+ link_hash, is_invite = utils.parse_username(target)
261
+ try:
262
+ if is_invite:
263
+ res = await cl(functions.messages.ImportChatInviteRequest(link_hash))
264
+ chat = (getattr(res, "chats", None) or [None])[0]
265
+ out({"joined": True, "via": "invite",
266
+ "id": getattr(chat, "id", None), "title": getattr(chat, "title", None)})
267
+ else:
268
+ ent = await resolve(cl, target)
269
+ await cl(functions.channels.JoinChannelRequest(ent))
270
+ out({"joined": True, "via": "public", "id": ent.id,
271
+ "title": getattr(ent, "title", None), "username": getattr(ent, "username", None)})
272
+ except errors.UserAlreadyParticipantError:
273
+ out({"joined": True, "already_member": True, "target": target})
274
+
275
+ elif cmd == "leave":
276
+ need(1); ent = await resolve(cl, args[0])
277
+ if isinstance(ent, User):
278
+ out({"error": "leave applies to groups/channels, not private chats"}); return
279
+ await cl.delete_dialog(ent)
280
+ out({"left": True, "id": ent.id})
281
+
255
282
  else:
256
283
  out({"error": f"unknown command: {cmd}"}); sys.exit(1)
257
284