@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.
|
|
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",
|
package/skills/telegram/SKILL.md
CHANGED
|
@@ -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,
|
|
10
|
-
the user's OWN account over MTProto (not a
|
|
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.
|
|
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
|
|