@zhin.js/adapter-telegram 5.0.0 → 5.0.2
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/CHANGELOG.md +30 -0
- package/README.md +9 -0
- package/{skills/telegram → agent}/PERMITS.md +1 -1
- package/agent/tools/create_invite.ts +20 -0
- package/agent/tools/list_admins.ts +26 -0
- package/agent/tools/member_count.ts +18 -0
- package/agent/tools/pin_message.ts +21 -0
- package/agent/tools/react.ts +20 -0
- package/agent/tools/send_poll.ts +34 -0
- package/agent/tools/send_sticker.ts +19 -0
- package/agent/tools/set_description.ts +19 -0
- package/agent/tools/set_permissions.ts +33 -0
- package/agent/tools/unpin_message.ts +21 -0
- package/dist/index.js +2 -2
- package/lib/agent/tools/create_invite.js +20 -0
- package/lib/agent/tools/create_invite.js.map +1 -0
- package/lib/agent/tools/list_admins.js +26 -0
- package/lib/agent/tools/list_admins.js.map +1 -0
- package/lib/agent/tools/member_count.js +18 -0
- package/lib/agent/tools/member_count.js.map +1 -0
- package/lib/agent/tools/pin_message.js +21 -0
- package/lib/agent/tools/pin_message.js.map +1 -0
- package/lib/agent/tools/react.js +20 -0
- package/lib/agent/tools/react.js.map +1 -0
- package/lib/agent/tools/send_poll.js +32 -0
- package/lib/agent/tools/send_poll.js.map +1 -0
- package/lib/agent/tools/send_sticker.js +19 -0
- package/lib/agent/tools/send_sticker.js.map +1 -0
- package/lib/agent/tools/set_description.js +19 -0
- package/lib/agent/tools/set_description.js.map +1 -0
- package/lib/agent/tools/set_permissions.js +34 -0
- package/lib/agent/tools/set_permissions.js.map +1 -0
- package/lib/agent/tools/unpin_message.js +21 -0
- package/lib/agent/tools/unpin_message.js.map +1 -0
- package/lib/{adapter.js → src/adapter.js} +6 -5
- package/lib/src/adapter.js.map +1 -0
- package/lib/{endpoint.js → src/endpoint.js} +57 -8
- package/lib/src/endpoint.js.map +1 -0
- package/lib/src/index.js +215 -0
- package/lib/src/index.js.map +1 -0
- package/lib/{platform-permit.js → src/platform-permit.js} +5 -2
- package/lib/src/platform-permit.js.map +1 -0
- package/lib/src/segment-mapper.js +2 -0
- package/lib/src/segment-mapper.js.map +1 -0
- package/lib/src/telegram-agent-deps.js +10 -0
- package/lib/src/telegram-agent-deps.js.map +1 -0
- package/lib/src/types.js.map +1 -0
- package/package.json +27 -17
- package/src/adapter.ts +6 -5
- package/src/endpoint.ts +68 -7
- package/src/index.ts +13 -263
- package/src/platform-permit.ts +3 -4
- package/src/segment-mapper.ts +1 -0
- package/src/telegram-agent-deps.ts +22 -0
- package/lib/adapter.d.ts +0 -20
- package/lib/adapter.d.ts.map +0 -1
- package/lib/adapter.js.map +0 -1
- package/lib/endpoint.d.ts +0 -154
- package/lib/endpoint.d.ts.map +0 -1
- package/lib/endpoint.js.map +0 -1
- package/lib/index.d.ts +0 -18
- package/lib/index.d.ts.map +0 -1
- package/lib/index.js +0 -462
- package/lib/index.js.map +0 -1
- package/lib/platform-permit.d.ts +0 -19
- package/lib/platform-permit.d.ts.map +0 -1
- package/lib/platform-permit.js.map +0 -1
- package/lib/types.d.ts +0 -29
- package/lib/types.d.ts.map +0 -1
- package/lib/types.js.map +0 -1
- /package/{skills/telegram/SKILL.md → agent/skills/telegram.md} +0 -0
- /package/lib/{types.js → src/types.js} +0 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zhin.js/adapter-telegram",
|
|
3
|
-
"version": "5.0.
|
|
3
|
+
"version": "5.0.2",
|
|
4
4
|
"description": "Zhin.js adapter for Telegram",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./lib/index.js",
|
|
@@ -35,28 +35,35 @@
|
|
|
35
35
|
"telegraf": "^4.16.3"
|
|
36
36
|
},
|
|
37
37
|
"devDependencies": {
|
|
38
|
-
"@types/node": "^
|
|
38
|
+
"@types/node": "^26.1.0",
|
|
39
39
|
"@types/react": "^19.2.17",
|
|
40
40
|
"@types/react-dom": "^19.2.3",
|
|
41
|
-
"lucide-react": "^1.
|
|
41
|
+
"lucide-react": "^1.22.0",
|
|
42
42
|
"typescript": "^6.0.3",
|
|
43
|
-
"
|
|
44
|
-
"@zhin.js/
|
|
45
|
-
"@zhin.js/host-
|
|
46
|
-
"@zhin.js/
|
|
47
|
-
"@zhin.js/host-
|
|
48
|
-
"zhin.js": "
|
|
49
|
-
"@zhin.js/logger": "1.0.
|
|
43
|
+
"zod": "^4.4.3",
|
|
44
|
+
"@zhin.js/contract": "1.0.3",
|
|
45
|
+
"@zhin.js/host-router": "2.0.3",
|
|
46
|
+
"@zhin.js/client": "2.0.5",
|
|
47
|
+
"@zhin.js/host-api": "2.0.5",
|
|
48
|
+
"@zhin.js/cli": "1.0.93",
|
|
49
|
+
"@zhin.js/logger": "1.0.74",
|
|
50
|
+
"zhin.js": "4.1.2",
|
|
51
|
+
"@zhin.js/agent": "1.0.3"
|
|
50
52
|
},
|
|
51
53
|
"peerDependencies": {
|
|
52
|
-
"
|
|
53
|
-
"@zhin.js/
|
|
54
|
-
"zhin.js": "
|
|
55
|
-
"@zhin.js/
|
|
56
|
-
"@zhin.js/host-
|
|
57
|
-
"@zhin.js/
|
|
54
|
+
"zod": "^4.0.0",
|
|
55
|
+
"@zhin.js/host-api": "2.0.5",
|
|
56
|
+
"@zhin.js/contract": "1.0.3",
|
|
57
|
+
"@zhin.js/client": "2.0.5",
|
|
58
|
+
"@zhin.js/host-router": "2.0.3",
|
|
59
|
+
"@zhin.js/agent": "1.0.3",
|
|
60
|
+
"@zhin.js/logger": "1.0.74",
|
|
61
|
+
"zhin.js": "4.1.2"
|
|
58
62
|
},
|
|
59
63
|
"peerDependenciesMeta": {
|
|
64
|
+
"@zhin.js/agent": {
|
|
65
|
+
"optional": true
|
|
66
|
+
},
|
|
60
67
|
"@zhin.js/client": {
|
|
61
68
|
"optional": true
|
|
62
69
|
},
|
|
@@ -65,6 +72,9 @@
|
|
|
65
72
|
},
|
|
66
73
|
"@zhin.js/host-api": {
|
|
67
74
|
"optional": true
|
|
75
|
+
},
|
|
76
|
+
"zod": {
|
|
77
|
+
"optional": true
|
|
68
78
|
}
|
|
69
79
|
},
|
|
70
80
|
"files": [
|
|
@@ -72,7 +82,7 @@
|
|
|
72
82
|
"lib",
|
|
73
83
|
"client",
|
|
74
84
|
"dist",
|
|
75
|
-
"
|
|
85
|
+
"agent",
|
|
76
86
|
"plugin.yml",
|
|
77
87
|
"README.md",
|
|
78
88
|
"CHANGELOG.md"
|
package/src/adapter.ts
CHANGED
|
@@ -9,6 +9,7 @@ import type { TelegramEndpointConfig } from "./types.js";
|
|
|
9
9
|
export class TelegramAdapter extends Adapter<TelegramEndpoint> {
|
|
10
10
|
static override readonly capabilities = ['inbound', 'outbound'] as const;
|
|
11
11
|
static override outboundRichSegmentPolicy = OUTBOUND_RICH_SEGMENT_POLICY_IM_FULL;
|
|
12
|
+
static override interactivePolicy = 'native' as const;
|
|
12
13
|
|
|
13
14
|
constructor(plugin: Plugin) {
|
|
14
15
|
super(plugin, "telegram", []);
|
|
@@ -18,9 +19,9 @@ export class TelegramAdapter extends Adapter<TelegramEndpoint> {
|
|
|
18
19
|
return new TelegramEndpoint(this, config);
|
|
19
20
|
}
|
|
20
21
|
|
|
21
|
-
// ──
|
|
22
|
+
// ── ISceneManagement 标准群管方法 ──────────────────────────────────
|
|
22
23
|
|
|
23
|
-
async
|
|
24
|
+
async removeMember(endpointId: string, sceneId: string, userId: string) {
|
|
24
25
|
const endpoint = this.endpoints.get(endpointId);
|
|
25
26
|
if (!endpoint) throw new Error(`Endpoint ${endpointId} 不存在`);
|
|
26
27
|
return endpoint.kickMember(Number(sceneId), Number(userId));
|
|
@@ -38,19 +39,19 @@ export class TelegramAdapter extends Adapter<TelegramEndpoint> {
|
|
|
38
39
|
return endpoint.muteMember(Number(sceneId), Number(userId), duration);
|
|
39
40
|
}
|
|
40
41
|
|
|
41
|
-
async
|
|
42
|
+
async setModerator(endpointId: string, sceneId: string, userId: string, enable = true) {
|
|
42
43
|
const endpoint = this.endpoints.get(endpointId);
|
|
43
44
|
if (!endpoint) throw new Error(`Endpoint ${endpointId} 不存在`);
|
|
44
45
|
return endpoint.setAdmin(Number(sceneId), Number(userId), enable);
|
|
45
46
|
}
|
|
46
47
|
|
|
47
|
-
async
|
|
48
|
+
async renameScene(endpointId: string, sceneId: string, name: string) {
|
|
48
49
|
const endpoint = this.endpoints.get(endpointId);
|
|
49
50
|
if (!endpoint) throw new Error(`Endpoint ${endpointId} 不存在`);
|
|
50
51
|
return endpoint.setChatTitle(Number(sceneId), name);
|
|
51
52
|
}
|
|
52
53
|
|
|
53
|
-
async
|
|
54
|
+
async getSceneInfo(endpointId: string, sceneId: string) {
|
|
54
55
|
const endpoint = this.endpoints.get(endpointId);
|
|
55
56
|
if (!endpoint) throw new Error(`Endpoint ${endpointId} 不存在`);
|
|
56
57
|
return endpoint.getChatInfo(Number(sceneId));
|
package/src/endpoint.ts
CHANGED
|
@@ -10,10 +10,12 @@ import {
|
|
|
10
10
|
SendContent,
|
|
11
11
|
MessageSegment,
|
|
12
12
|
segment,
|
|
13
|
+
expandInteractiveSegmentsInContent,
|
|
13
14
|
type QuotedMessagePayload,} from 'zhin.js';
|
|
14
15
|
import type { TelegramEndpointConfig, TelegramSenderInfo } from "./types.js";
|
|
15
16
|
import type { TelegramAdapter } from "./adapter.js";
|
|
16
17
|
import { normalizeTelegramChatMember } from "./platform-permit.js";
|
|
18
|
+
import { fromCanonicalSegments, toCanonicalSegments } from './segment-mapper.js';
|
|
17
19
|
|
|
18
20
|
export class TelegramEndpoint extends Telegraf implements Endpoint<TelegramEndpointConfig, TelegramMessage> {
|
|
19
21
|
$connected: boolean = false;
|
|
@@ -43,8 +45,12 @@ export class TelegramEndpoint extends Telegraf implements Endpoint<TelegramEndpo
|
|
|
43
45
|
|
|
44
46
|
// Set up callback query handler (for inline buttons)
|
|
45
47
|
this.on("callback_query", async (ctx) => {
|
|
46
|
-
// Handle callback queries as special messages
|
|
47
48
|
if (ctx.callbackQuery && "data" in ctx.callbackQuery) {
|
|
49
|
+
try {
|
|
50
|
+
await ctx.answerCbQuery();
|
|
51
|
+
} catch {
|
|
52
|
+
// already answered
|
|
53
|
+
}
|
|
48
54
|
const message = this.$formatCallbackQuery(ctx);
|
|
49
55
|
this.adapter.emit("message.receive", message);
|
|
50
56
|
this.pluginLogger.debug(
|
|
@@ -169,9 +175,10 @@ export class TelegramEndpoint extends Telegraf implements Endpoint<TelegramEndpo
|
|
|
169
175
|
const channelId = msg.chat.id.toString();
|
|
170
176
|
|
|
171
177
|
// Parse message content
|
|
172
|
-
const
|
|
173
|
-
const quoteId = Message.quoteIdFromContent(
|
|
174
|
-
Message.alignReplySegments(
|
|
178
|
+
const wire = this.parseMessageContent(msg);
|
|
179
|
+
const quoteId = Message.quoteIdFromContent(wire);
|
|
180
|
+
Message.alignReplySegments(wire, quoteId);
|
|
181
|
+
const content = toCanonicalSegments(wire);
|
|
175
182
|
|
|
176
183
|
const result = Message.from(msg, {
|
|
177
184
|
$id: msg.message_id.toString(),
|
|
@@ -271,7 +278,14 @@ export class TelegramEndpoint extends Telegraf implements Endpoint<TelegramEndpo
|
|
|
271
278
|
id: channelId,
|
|
272
279
|
type: channelType,
|
|
273
280
|
},
|
|
274
|
-
$content: [{
|
|
281
|
+
$content: [{
|
|
282
|
+
type: "action",
|
|
283
|
+
data: {
|
|
284
|
+
id: query.id,
|
|
285
|
+
payload: query.data,
|
|
286
|
+
sourceMessageId: msg && "message_id" in msg ? String(msg.message_id) : undefined,
|
|
287
|
+
},
|
|
288
|
+
}],
|
|
275
289
|
$raw: query.data,
|
|
276
290
|
$timestamp: Date.now(),
|
|
277
291
|
$recall: async () => {
|
|
@@ -508,7 +522,9 @@ export class TelegramEndpoint extends Telegraf implements Endpoint<TelegramEndpo
|
|
|
508
522
|
async $sendMessage(options: SendOptions): Promise<string> {
|
|
509
523
|
try {
|
|
510
524
|
const chatId = parseInt(options.id);
|
|
511
|
-
const
|
|
525
|
+
const canonical = expandInteractiveSegmentsInContent(options.content);
|
|
526
|
+
const wire = fromCanonicalSegments(canonical);
|
|
527
|
+
const result = await this.sendContentToChat(chatId, wire);
|
|
512
528
|
this.messageChatMap.set(result.message_id.toString(), options.id);
|
|
513
529
|
this.pluginLogger.debug(
|
|
514
530
|
`${this.$config.name} send ${options.type}(${options.id}): ${segment.raw(options.content)}`
|
|
@@ -520,6 +536,38 @@ export class TelegramEndpoint extends Telegraf implements Endpoint<TelegramEndpo
|
|
|
520
536
|
}
|
|
521
537
|
}
|
|
522
538
|
|
|
539
|
+
async $editMessage(options: import('zhin.js').EditMessageOptions): Promise<void> {
|
|
540
|
+
const chatId = parseInt(options.id);
|
|
541
|
+
const messageId = parseInt(options.messageId);
|
|
542
|
+
const content = Array.isArray(options.content) ? options.content : [options.content];
|
|
543
|
+
let textContent = "";
|
|
544
|
+
let keyboard: { inline_keyboard: Array<Array<{ text: string; callback_data: string }>> } | undefined;
|
|
545
|
+
for (const seg of content) {
|
|
546
|
+
if (typeof seg === "string") {
|
|
547
|
+
textContent += seg;
|
|
548
|
+
continue;
|
|
549
|
+
}
|
|
550
|
+
if (seg.type === "text") textContent += seg.data.text || "";
|
|
551
|
+
if (seg.type === "keyboard") {
|
|
552
|
+
keyboard = {
|
|
553
|
+
inline_keyboard: (seg.data.rows ?? []).map((row: Array<{ label: string; payload: string; disabled?: boolean }>) =>
|
|
554
|
+
row.map((btn) => ({
|
|
555
|
+
text: btn.label,
|
|
556
|
+
callback_data: String(btn.payload).slice(0, 64),
|
|
557
|
+
})),
|
|
558
|
+
),
|
|
559
|
+
};
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
await this.telegram.editMessageText(
|
|
563
|
+
chatId,
|
|
564
|
+
messageId,
|
|
565
|
+
undefined,
|
|
566
|
+
textContent.trim() || " ",
|
|
567
|
+
keyboard ? { reply_markup: keyboard } : {},
|
|
568
|
+
);
|
|
569
|
+
}
|
|
570
|
+
|
|
523
571
|
private async sendContentToChat(
|
|
524
572
|
chatId: number,
|
|
525
573
|
content: SendContent,
|
|
@@ -691,6 +739,20 @@ export class TelegramEndpoint extends Telegraf implements Endpoint<TelegramEndpo
|
|
|
691
739
|
reply_parameters: { message_id: parseInt(data.id) },
|
|
692
740
|
...extraOptions,
|
|
693
741
|
});
|
|
742
|
+
|
|
743
|
+
case "keyboard": {
|
|
744
|
+
const rows = (data.rows ?? []).map((row: Array<{ label: string; payload: string; disabled?: boolean }>) =>
|
|
745
|
+
row.map((btn) => ({
|
|
746
|
+
text: btn.label,
|
|
747
|
+
callback_data: String(btn.payload).slice(0, 64),
|
|
748
|
+
})),
|
|
749
|
+
);
|
|
750
|
+
return await this.telegram.sendMessage(chatId, textContent.trim() || " ", {
|
|
751
|
+
reply_markup: { inline_keyboard: rows },
|
|
752
|
+
...extraOptions,
|
|
753
|
+
});
|
|
754
|
+
}
|
|
755
|
+
|
|
694
756
|
default:
|
|
695
757
|
// Unknown segment type, add as text
|
|
696
758
|
textContent += data.text || `[${type}]`;
|
|
@@ -707,7 +769,6 @@ export class TelegramEndpoint extends Telegraf implements Endpoint<TelegramEndpo
|
|
|
707
769
|
}
|
|
708
770
|
|
|
709
771
|
async $recallMessage(id: string): Promise<void> {
|
|
710
|
-
// Telegram requires both chat_id and message_id to delete a message
|
|
711
772
|
// The Endpoint interface only provides message_id, making recall impossible
|
|
712
773
|
// Users should use message.$recall() instead, which has the full context
|
|
713
774
|
throw new Error(
|
package/src/index.ts
CHANGED
|
@@ -2,14 +2,14 @@
|
|
|
2
2
|
* Telegram 适配器入口:类型扩展、导出、注册
|
|
3
3
|
*/
|
|
4
4
|
import path from "node:path";
|
|
5
|
-
import { usePlugin, type Plugin, type Context, type
|
|
5
|
+
import { usePlugin, type Plugin, type Context, type ISceneManagement, createSceneManagementTools, type ToolFeature } from "zhin.js";
|
|
6
6
|
import { TelegramAdapter } from "./adapter.js";
|
|
7
7
|
import { PageManager } from "@zhin.js/host-api";
|
|
8
8
|
import {
|
|
9
|
-
platformPermit,
|
|
10
9
|
registerTelegramPlatformPermitChecker,
|
|
11
10
|
telegramGroupPermitResolver,
|
|
12
11
|
} from "./platform-permit.js";
|
|
12
|
+
import { setTelegramAgentDeps } from "./telegram-agent-deps.js";
|
|
13
13
|
|
|
14
14
|
declare module "zhin.js" {
|
|
15
15
|
namespace Plugin {
|
|
@@ -48,270 +48,20 @@ provide({
|
|
|
48
48
|
useContext('tool', 'telegram', (toolService: ToolFeature, telegram: TelegramAdapter) => {
|
|
49
49
|
const disposers: (() => void)[] = [];
|
|
50
50
|
disposers.push(registerTelegramPlatformPermitChecker());
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
'telegram',
|
|
54
|
-
{ permitResolver: telegramGroupPermitResolver, registerChecker: false },
|
|
55
|
-
);
|
|
56
|
-
disposers.push(...groupTools.map(t => toolService.addTool(t, plugin.name)));
|
|
57
|
-
|
|
58
|
-
disposers.push(toolService.addTool({
|
|
59
|
-
name: 'telegram_pin_message',
|
|
60
|
-
description: '置顶 Telegram 群组消息',
|
|
61
|
-
parameters: {
|
|
62
|
-
type: 'object',
|
|
63
|
-
properties: {
|
|
64
|
-
endpoint_id: { type: 'string', description: 'Endpoint 名称', contextKey: 'endpointId' },
|
|
65
|
-
chat_id: { type: 'string', description: '聊天 ID' },
|
|
66
|
-
message_id: { type: 'string', description: '消息 ID' },
|
|
67
|
-
},
|
|
68
|
-
required: ['endpoint_id', 'chat_id', 'message_id'],
|
|
69
|
-
},
|
|
70
|
-
platforms: ['telegram'],
|
|
71
|
-
tags: ['telegram'],
|
|
72
|
-
permissions: [platformPermit('pin_messages')],
|
|
73
|
-
execute: async (args: Record<string, any>) => {
|
|
74
|
-
const endpoint = telegram.endpoints.get(args.endpoint_id);
|
|
75
|
-
if (!endpoint) throw new Error(`Endpoint ${args.endpoint_id} 不存在`);
|
|
76
|
-
const success = await endpoint.pinMessage(Number(args.chat_id), Number(args.message_id));
|
|
77
|
-
return { success, message: success ? '消息已置顶' : '操作失败' };
|
|
78
|
-
},
|
|
79
|
-
}, plugin.name));
|
|
80
|
-
|
|
81
|
-
disposers.push(toolService.addTool({
|
|
82
|
-
name: 'telegram_unpin_message',
|
|
83
|
-
description: '取消置顶 Telegram 群组消息',
|
|
84
|
-
parameters: {
|
|
85
|
-
type: 'object',
|
|
86
|
-
properties: {
|
|
87
|
-
endpoint_id: { type: 'string', description: 'Endpoint 名称', contextKey: 'endpointId' },
|
|
88
|
-
chat_id: { type: 'string', description: '聊天 ID' },
|
|
89
|
-
message_id: { type: 'string', description: '消息 ID(可选,不提供则取消所有置顶)' },
|
|
90
|
-
},
|
|
91
|
-
required: ['endpoint_id', 'chat_id'],
|
|
92
|
-
},
|
|
93
|
-
platforms: ['telegram'],
|
|
94
|
-
tags: ['telegram'],
|
|
95
|
-
permissions: [platformPermit('pin_messages')],
|
|
96
|
-
execute: async (args: Record<string, any>) => {
|
|
97
|
-
const endpoint = telegram.endpoints.get(args.endpoint_id);
|
|
98
|
-
if (!endpoint) throw new Error(`Endpoint ${args.endpoint_id} 不存在`);
|
|
99
|
-
const success = await endpoint.unpinMessage(Number(args.chat_id), args.message_id ? Number(args.message_id) : undefined);
|
|
100
|
-
return { success, message: success ? '已取消置顶' : '操作失败' };
|
|
101
|
-
},
|
|
102
|
-
}, plugin.name));
|
|
103
|
-
|
|
104
|
-
disposers.push(toolService.addTool({
|
|
105
|
-
name: 'telegram_list_admins',
|
|
106
|
-
description: '获取 Telegram 群组管理员列表',
|
|
107
|
-
parameters: {
|
|
108
|
-
type: 'object',
|
|
109
|
-
properties: {
|
|
110
|
-
endpoint_id: { type: 'string', description: 'Endpoint 名称', contextKey: 'endpointId' },
|
|
111
|
-
chat_id: { type: 'string', description: '聊天 ID' },
|
|
112
|
-
},
|
|
113
|
-
required: ['endpoint_id', 'chat_id'],
|
|
114
|
-
},
|
|
115
|
-
platforms: ['telegram'],
|
|
116
|
-
tags: ['telegram'],
|
|
117
|
-
execute: async (args: Record<string, any>) => {
|
|
118
|
-
const endpoint = telegram.endpoints.get(args.endpoint_id);
|
|
119
|
-
if (!endpoint) throw new Error(`Endpoint ${args.endpoint_id} 不存在`);
|
|
120
|
-
const admins = await endpoint.getChatAdmins(Number(args.chat_id));
|
|
121
|
-
return {
|
|
122
|
-
admins: admins.map((a: any) => ({
|
|
123
|
-
user_id: a.user.id,
|
|
124
|
-
username: a.user.username,
|
|
125
|
-
first_name: a.user.first_name,
|
|
126
|
-
status: a.status,
|
|
127
|
-
})),
|
|
128
|
-
count: admins.length,
|
|
129
|
-
};
|
|
130
|
-
},
|
|
131
|
-
}, plugin.name));
|
|
132
|
-
|
|
133
|
-
disposers.push(toolService.addTool({
|
|
134
|
-
name: 'telegram_member_count',
|
|
135
|
-
description: '获取 Telegram 群组成员数量',
|
|
136
|
-
parameters: {
|
|
137
|
-
type: 'object',
|
|
138
|
-
properties: {
|
|
139
|
-
endpoint_id: { type: 'string', description: 'Endpoint 名称', contextKey: 'endpointId' },
|
|
140
|
-
chat_id: { type: 'string', description: '聊天 ID' },
|
|
141
|
-
},
|
|
142
|
-
required: ['endpoint_id', 'chat_id'],
|
|
143
|
-
},
|
|
144
|
-
platforms: ['telegram'],
|
|
145
|
-
tags: ['telegram'],
|
|
146
|
-
execute: async (args: Record<string, any>) => {
|
|
147
|
-
const endpoint = telegram.endpoints.get(args.endpoint_id);
|
|
148
|
-
if (!endpoint) throw new Error(`Endpoint ${args.endpoint_id} 不存在`);
|
|
149
|
-
const count = await endpoint.getChatMemberCount(Number(args.chat_id));
|
|
150
|
-
return { count, message: `群组共有 ${count} 名成员` };
|
|
151
|
-
},
|
|
152
|
-
}, plugin.name));
|
|
153
|
-
|
|
154
|
-
disposers.push(toolService.addTool({
|
|
155
|
-
name: 'telegram_create_invite',
|
|
156
|
-
description: '创建 Telegram 群组邀请链接',
|
|
157
|
-
parameters: {
|
|
158
|
-
type: 'object',
|
|
159
|
-
properties: {
|
|
160
|
-
endpoint_id: { type: 'string', description: 'Endpoint 名称', contextKey: 'endpointId' },
|
|
161
|
-
chat_id: { type: 'string', description: '聊天 ID' },
|
|
162
|
-
},
|
|
163
|
-
required: ['endpoint_id', 'chat_id'],
|
|
164
|
-
},
|
|
165
|
-
platforms: ['telegram'],
|
|
166
|
-
tags: ['telegram'],
|
|
167
|
-
permissions: [platformPermit('chat_administrator')],
|
|
168
|
-
execute: async (args: Record<string, any>) => {
|
|
169
|
-
const endpoint = telegram.endpoints.get(args.endpoint_id);
|
|
170
|
-
if (!endpoint) throw new Error(`Endpoint ${args.endpoint_id} 不存在`);
|
|
171
|
-
const link = await endpoint.createInviteLink(Number(args.chat_id));
|
|
172
|
-
return { invite_link: link, message: `邀请链接: ${link}` };
|
|
173
|
-
},
|
|
174
|
-
}, plugin.name));
|
|
175
|
-
|
|
176
|
-
disposers.push(toolService.addTool({
|
|
177
|
-
name: 'telegram_send_poll',
|
|
178
|
-
description: '在 Telegram 群组中发起投票',
|
|
179
|
-
parameters: {
|
|
180
|
-
type: 'object',
|
|
181
|
-
properties: {
|
|
182
|
-
endpoint_id: { type: 'string', description: 'Endpoint 名称', contextKey: 'endpointId' },
|
|
183
|
-
chat_id: { type: 'string', description: '聊天 ID' },
|
|
184
|
-
question: { type: 'string', description: '投票问题' },
|
|
185
|
-
options: { type: 'string', description: '选项 JSON 数组,如 ["A","B","C"]' },
|
|
186
|
-
is_anonymous: { type: 'boolean', description: '是否匿名投票,默认 true' },
|
|
187
|
-
allows_multiple: { type: 'boolean', description: '是否允许多选,默认 false' },
|
|
188
|
-
},
|
|
189
|
-
required: ['endpoint_id', 'chat_id', 'question', 'options'],
|
|
190
|
-
},
|
|
191
|
-
platforms: ['telegram'],
|
|
192
|
-
tags: ['telegram'],
|
|
193
|
-
execute: async (args: Record<string, any>) => {
|
|
194
|
-
const endpoint = telegram.endpoints.get(args.endpoint_id);
|
|
195
|
-
if (!endpoint) throw new Error(`Endpoint ${args.endpoint_id} 不存在`);
|
|
196
|
-
let optList: string[];
|
|
197
|
-
try {
|
|
198
|
-
optList = JSON.parse(args.options);
|
|
199
|
-
} catch {
|
|
200
|
-
return { success: false, message: 'options 格式错误,应为 JSON 数组' };
|
|
201
|
-
}
|
|
202
|
-
if (!Array.isArray(optList) || optList.length < 2) {
|
|
203
|
-
return { success: false, message: '至少需要 2 个选项' };
|
|
204
|
-
}
|
|
205
|
-
const result = await endpoint.sendPoll(
|
|
206
|
-
Number(args.chat_id), args.question, optList,
|
|
207
|
-
args.is_anonymous ?? true, args.allows_multiple ?? false,
|
|
208
|
-
);
|
|
209
|
-
return { success: true, message_id: result.message_id, message: '投票已发送' };
|
|
210
|
-
},
|
|
211
|
-
}, plugin.name));
|
|
212
|
-
|
|
213
|
-
disposers.push(toolService.addTool({
|
|
214
|
-
name: 'telegram_react',
|
|
215
|
-
description: '对 Telegram 消息添加表情反应',
|
|
216
|
-
parameters: {
|
|
217
|
-
type: 'object',
|
|
218
|
-
properties: {
|
|
219
|
-
endpoint_id: { type: 'string', description: 'Endpoint 名称', contextKey: 'endpointId' },
|
|
220
|
-
chat_id: { type: 'string', description: '聊天 ID' },
|
|
221
|
-
message_id: { type: 'string', description: '消息 ID' },
|
|
222
|
-
reaction: { type: 'string', description: '反应表情(如 👍、❤️、🔥)' },
|
|
223
|
-
},
|
|
224
|
-
required: ['endpoint_id', 'chat_id', 'message_id', 'reaction'],
|
|
225
|
-
},
|
|
226
|
-
platforms: ['telegram'],
|
|
227
|
-
tags: ['telegram'],
|
|
228
|
-
execute: async (args: Record<string, any>) => {
|
|
229
|
-
const endpoint = telegram.endpoints.get(args.endpoint_id);
|
|
230
|
-
if (!endpoint) throw new Error(`Endpoint ${args.endpoint_id} 不存在`);
|
|
231
|
-
const success = await endpoint.setMessageReaction(Number(args.chat_id), Number(args.message_id), args.reaction);
|
|
232
|
-
return { success, message: success ? `已添加反应 ${args.reaction}` : '操作失败' };
|
|
233
|
-
},
|
|
234
|
-
}, plugin.name));
|
|
235
|
-
|
|
236
|
-
disposers.push(toolService.addTool({
|
|
237
|
-
name: 'telegram_send_sticker',
|
|
238
|
-
description: '发送 Telegram 贴纸',
|
|
239
|
-
parameters: {
|
|
240
|
-
type: 'object',
|
|
241
|
-
properties: {
|
|
242
|
-
endpoint_id: { type: 'string', description: 'Endpoint 名称', contextKey: 'endpointId' },
|
|
243
|
-
chat_id: { type: 'string', description: '聊天 ID' },
|
|
244
|
-
sticker: { type: 'string', description: '贴纸 file_id 或 URL' },
|
|
245
|
-
},
|
|
246
|
-
required: ['endpoint_id', 'chat_id', 'sticker'],
|
|
247
|
-
},
|
|
248
|
-
platforms: ['telegram'],
|
|
249
|
-
tags: ['telegram'],
|
|
250
|
-
execute: async (args: Record<string, any>) => {
|
|
251
|
-
const endpoint = telegram.endpoints.get(args.endpoint_id);
|
|
252
|
-
if (!endpoint) throw new Error(`Endpoint ${args.endpoint_id} 不存在`);
|
|
253
|
-
const result = await endpoint.sendStickerMessage(Number(args.chat_id), args.sticker);
|
|
254
|
-
return { success: true, message_id: result.message_id, message: '贴纸已发送' };
|
|
255
|
-
},
|
|
256
|
-
}, plugin.name));
|
|
257
|
-
|
|
258
|
-
disposers.push(toolService.addTool({
|
|
259
|
-
name: 'telegram_set_permissions',
|
|
260
|
-
description: '设置 Telegram 群组的默认成员权限',
|
|
261
|
-
parameters: {
|
|
262
|
-
type: 'object',
|
|
263
|
-
properties: {
|
|
264
|
-
endpoint_id: { type: 'string', description: 'Endpoint 名称', contextKey: 'endpointId' },
|
|
265
|
-
chat_id: { type: 'string', description: '聊天 ID' },
|
|
266
|
-
can_send_messages: { type: 'boolean', description: '是否可以发消息' },
|
|
267
|
-
can_send_photos: { type: 'boolean', description: '是否可以发图片' },
|
|
268
|
-
can_send_videos: { type: 'boolean', description: '是否可以发视频' },
|
|
269
|
-
can_send_polls: { type: 'boolean', description: '是否可以发投票' },
|
|
270
|
-
can_send_other_messages: { type: 'boolean', description: '是否可以发贴纸/GIF等' },
|
|
271
|
-
can_add_web_page_previews: { type: 'boolean', description: '是否可以添加网页预览' },
|
|
272
|
-
can_change_info: { type: 'boolean', description: '是否可以改群信息' },
|
|
273
|
-
can_invite_users: { type: 'boolean', description: '是否可以邀请用户' },
|
|
274
|
-
can_pin_messages: { type: 'boolean', description: '是否可以置顶消息' },
|
|
275
|
-
},
|
|
276
|
-
required: ['endpoint_id', 'chat_id'],
|
|
277
|
-
},
|
|
278
|
-
platforms: ['telegram'],
|
|
279
|
-
tags: ['telegram'],
|
|
280
|
-
permissions: [platformPermit('manage_chat')],
|
|
281
|
-
execute: async (args: Record<string, any>) => {
|
|
282
|
-
const { endpoint: endpointId, chat_id, ...perms } = args;
|
|
51
|
+
setTelegramAgentDeps({
|
|
52
|
+
getEndpoint: (endpointId) => {
|
|
283
53
|
const endpoint = telegram.endpoints.get(endpointId);
|
|
284
54
|
if (!endpoint) throw new Error(`Endpoint ${endpointId} 不存在`);
|
|
285
|
-
|
|
286
|
-
for (const [k, v] of Object.entries(perms)) {
|
|
287
|
-
if (typeof v === 'boolean') permissions[k] = v;
|
|
288
|
-
}
|
|
289
|
-
const success = await endpoint.setChatPermissionsAll(Number(chat_id), permissions);
|
|
290
|
-
return { success, message: success ? '群权限已更新' : '操作失败' };
|
|
291
|
-
},
|
|
292
|
-
}, plugin.name));
|
|
293
|
-
|
|
294
|
-
disposers.push(toolService.addTool({
|
|
295
|
-
name: 'telegram_set_description',
|
|
296
|
-
description: '设置 Telegram 群组描述',
|
|
297
|
-
parameters: {
|
|
298
|
-
type: 'object',
|
|
299
|
-
properties: {
|
|
300
|
-
endpoint_id: { type: 'string', description: 'Endpoint 名称', contextKey: 'endpointId' },
|
|
301
|
-
chat_id: { type: 'string', description: '聊天 ID' },
|
|
302
|
-
description: { type: 'string', description: '群描述文字' },
|
|
303
|
-
},
|
|
304
|
-
required: ['endpoint_id', 'chat_id', 'description'],
|
|
55
|
+
return endpoint;
|
|
305
56
|
},
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
}, plugin.name));
|
|
57
|
+
getAdapter: () => telegram,
|
|
58
|
+
});
|
|
59
|
+
const sceneTools = createSceneManagementTools(
|
|
60
|
+
telegram as unknown as ISceneManagement,
|
|
61
|
+
'telegram',
|
|
62
|
+
{ permitResolver: telegramGroupPermitResolver, registerChecker: false },
|
|
63
|
+
);
|
|
64
|
+
disposers.push(...sceneTools.map(t => toolService.addTool(t, plugin.name)));
|
|
315
65
|
|
|
316
66
|
return () => disposers.forEach(d => d());
|
|
317
67
|
});
|
package/src/platform-permit.ts
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Telegram platform permit — ChatMember 状态 + 细粒度权限
|
|
3
3
|
*/
|
|
4
|
-
import type
|
|
5
|
-
import { registerPlatformPermitChecker } from 'zhin.js';
|
|
4
|
+
import { registerPlatformPermitChecker, type Message } from 'zhin.js';
|
|
6
5
|
|
|
7
6
|
const ADAPTER = 'telegram';
|
|
8
7
|
|
|
@@ -11,8 +10,8 @@ export function platformPermit(perm: string): string {
|
|
|
11
10
|
}
|
|
12
11
|
|
|
13
12
|
const FACTORY_PERM_MAP: Record<string, string> = {
|
|
14
|
-
|
|
15
|
-
|
|
13
|
+
scene_admin: 'chat_administrator',
|
|
14
|
+
scene_owner: 'chat_creator',
|
|
16
15
|
};
|
|
17
16
|
|
|
18
17
|
export function telegramGroupPermitResolver(logicalPerm: string): string {
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { toCanonicalSegments, fromCanonicalSegments } from 'zhin.js';
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared runtime deps for telegram agent/ authoring tools.
|
|
3
|
+
* Set once from plugin index during init — never call getPlugin() from tool execute.
|
|
4
|
+
*/
|
|
5
|
+
import type { TelegramAdapter } from './adapter.js';
|
|
6
|
+
import type { TelegramEndpoint } from './endpoint.js';
|
|
7
|
+
|
|
8
|
+
export interface TelegramAgentDeps {
|
|
9
|
+
getEndpoint: (endpointId: string) => TelegramEndpoint;
|
|
10
|
+
getAdapter: () => TelegramAdapter;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
let _deps: TelegramAgentDeps | null = null;
|
|
14
|
+
|
|
15
|
+
export function setTelegramAgentDeps(deps: TelegramAgentDeps): void {
|
|
16
|
+
_deps = deps;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function getTelegramAgentDeps(): TelegramAgentDeps {
|
|
20
|
+
if (!_deps) throw new Error('telegram agent deps not initialized');
|
|
21
|
+
return _deps;
|
|
22
|
+
}
|
package/lib/adapter.d.ts
DELETED
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Telegram 适配器
|
|
3
|
-
*/
|
|
4
|
-
import { Adapter, Plugin } from 'zhin.js';
|
|
5
|
-
import { TelegramEndpoint } from "./endpoint.js";
|
|
6
|
-
import type { TelegramEndpointConfig } from "./types.js";
|
|
7
|
-
export declare class TelegramAdapter extends Adapter<TelegramEndpoint> {
|
|
8
|
-
static readonly capabilities: readonly ["inbound", "outbound"];
|
|
9
|
-
static outboundRichSegmentPolicy: import("zhin.js").OutboundRichSegmentPolicy;
|
|
10
|
-
constructor(plugin: Plugin);
|
|
11
|
-
createEndpoint(config: TelegramEndpointConfig): TelegramEndpoint;
|
|
12
|
-
kickMember(endpointId: string, sceneId: string, userId: string): Promise<boolean>;
|
|
13
|
-
unbanMember(endpointId: string, sceneId: string, userId: string): Promise<boolean>;
|
|
14
|
-
muteMember(endpointId: string, sceneId: string, userId: string, duration?: number): Promise<boolean>;
|
|
15
|
-
setAdmin(endpointId: string, sceneId: string, userId: string, enable?: boolean): Promise<boolean>;
|
|
16
|
-
setGroupName(endpointId: string, sceneId: string, name: string): Promise<boolean>;
|
|
17
|
-
getGroupInfo(endpointId: string, sceneId: string): Promise<any>;
|
|
18
|
-
start(): Promise<void>;
|
|
19
|
-
}
|
|
20
|
-
//# sourceMappingURL=adapter.d.ts.map
|
package/lib/adapter.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../src/adapter.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,EAAE,OAAO,EACd,MAAM,EAAwC,MAAM,SAAS,CAAC;AAChE,OAAO,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AACjD,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,YAAY,CAAC;AAEzD,qBAAa,eAAgB,SAAQ,OAAO,CAAC,gBAAgB,CAAC;IAC5D,gBAAyB,YAAY,mCAAoC;IACzE,OAAgB,yBAAyB,8CAAwC;gBAErE,MAAM,EAAE,MAAM;IAI1B,cAAc,CAAC,MAAM,EAAE,sBAAsB,GAAG,gBAAgB;IAM1D,UAAU,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;IAM9D,WAAW,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;IAM/D,UAAU,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,SAAM;IAM9E,QAAQ,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,UAAO;IAM3E,YAAY,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM;IAM9D,YAAY,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM;IAQhD,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CAG7B"}
|
package/lib/adapter.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"adapter.js","sourceRoot":"","sources":["../src/adapter.ts"],"names":[],"mappings":"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"}
|