@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.
Files changed (72) hide show
  1. package/CHANGELOG.md +30 -0
  2. package/README.md +9 -0
  3. package/{skills/telegram → agent}/PERMITS.md +1 -1
  4. package/agent/tools/create_invite.ts +20 -0
  5. package/agent/tools/list_admins.ts +26 -0
  6. package/agent/tools/member_count.ts +18 -0
  7. package/agent/tools/pin_message.ts +21 -0
  8. package/agent/tools/react.ts +20 -0
  9. package/agent/tools/send_poll.ts +34 -0
  10. package/agent/tools/send_sticker.ts +19 -0
  11. package/agent/tools/set_description.ts +19 -0
  12. package/agent/tools/set_permissions.ts +33 -0
  13. package/agent/tools/unpin_message.ts +21 -0
  14. package/dist/index.js +2 -2
  15. package/lib/agent/tools/create_invite.js +20 -0
  16. package/lib/agent/tools/create_invite.js.map +1 -0
  17. package/lib/agent/tools/list_admins.js +26 -0
  18. package/lib/agent/tools/list_admins.js.map +1 -0
  19. package/lib/agent/tools/member_count.js +18 -0
  20. package/lib/agent/tools/member_count.js.map +1 -0
  21. package/lib/agent/tools/pin_message.js +21 -0
  22. package/lib/agent/tools/pin_message.js.map +1 -0
  23. package/lib/agent/tools/react.js +20 -0
  24. package/lib/agent/tools/react.js.map +1 -0
  25. package/lib/agent/tools/send_poll.js +32 -0
  26. package/lib/agent/tools/send_poll.js.map +1 -0
  27. package/lib/agent/tools/send_sticker.js +19 -0
  28. package/lib/agent/tools/send_sticker.js.map +1 -0
  29. package/lib/agent/tools/set_description.js +19 -0
  30. package/lib/agent/tools/set_description.js.map +1 -0
  31. package/lib/agent/tools/set_permissions.js +34 -0
  32. package/lib/agent/tools/set_permissions.js.map +1 -0
  33. package/lib/agent/tools/unpin_message.js +21 -0
  34. package/lib/agent/tools/unpin_message.js.map +1 -0
  35. package/lib/{adapter.js → src/adapter.js} +6 -5
  36. package/lib/src/adapter.js.map +1 -0
  37. package/lib/{endpoint.js → src/endpoint.js} +57 -8
  38. package/lib/src/endpoint.js.map +1 -0
  39. package/lib/src/index.js +215 -0
  40. package/lib/src/index.js.map +1 -0
  41. package/lib/{platform-permit.js → src/platform-permit.js} +5 -2
  42. package/lib/src/platform-permit.js.map +1 -0
  43. package/lib/src/segment-mapper.js +2 -0
  44. package/lib/src/segment-mapper.js.map +1 -0
  45. package/lib/src/telegram-agent-deps.js +10 -0
  46. package/lib/src/telegram-agent-deps.js.map +1 -0
  47. package/lib/src/types.js.map +1 -0
  48. package/package.json +27 -17
  49. package/src/adapter.ts +6 -5
  50. package/src/endpoint.ts +68 -7
  51. package/src/index.ts +13 -263
  52. package/src/platform-permit.ts +3 -4
  53. package/src/segment-mapper.ts +1 -0
  54. package/src/telegram-agent-deps.ts +22 -0
  55. package/lib/adapter.d.ts +0 -20
  56. package/lib/adapter.d.ts.map +0 -1
  57. package/lib/adapter.js.map +0 -1
  58. package/lib/endpoint.d.ts +0 -154
  59. package/lib/endpoint.d.ts.map +0 -1
  60. package/lib/endpoint.js.map +0 -1
  61. package/lib/index.d.ts +0 -18
  62. package/lib/index.d.ts.map +0 -1
  63. package/lib/index.js +0 -462
  64. package/lib/index.js.map +0 -1
  65. package/lib/platform-permit.d.ts +0 -19
  66. package/lib/platform-permit.d.ts.map +0 -1
  67. package/lib/platform-permit.js.map +0 -1
  68. package/lib/types.d.ts +0 -29
  69. package/lib/types.d.ts.map +0 -1
  70. package/lib/types.js.map +0 -1
  71. /package/{skills/telegram/SKILL.md → agent/skills/telegram.md} +0 -0
  72. /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.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": "^25.9.2",
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.21.0",
41
+ "lucide-react": "^1.22.0",
42
42
  "typescript": "^6.0.3",
43
- "@zhin.js/client": "2.0.3",
44
- "@zhin.js/cli": "1.0.91",
45
- "@zhin.js/host-api": "2.0.3",
46
- "@zhin.js/contract": "1.0.1",
47
- "@zhin.js/host-router": "2.0.1",
48
- "zhin.js": "4.1.0",
49
- "@zhin.js/logger": "1.0.72"
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
- "@zhin.js/client": "2.0.3",
53
- "@zhin.js/contract": "1.0.1",
54
- "zhin.js": "4.1.0",
55
- "@zhin.js/host-router": "2.0.1",
56
- "@zhin.js/host-api": "2.0.3",
57
- "@zhin.js/logger": "1.0.72"
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
- "skills",
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
- // ── IGroupManagement 标准群管方法 ──────────────────────────────────
22
+ // ── ISceneManagement 标准群管方法 ──────────────────────────────────
22
23
 
23
- async kickMember(endpointId: string, sceneId: string, userId: string) {
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 setAdmin(endpointId: string, sceneId: string, userId: string, enable = true) {
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 setGroupName(endpointId: string, sceneId: string, name: string) {
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 getGroupInfo(endpointId: string, sceneId: string) {
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 content = this.parseMessageContent(msg);
173
- const quoteId = Message.quoteIdFromContent(content);
174
- Message.alignReplySegments(content, quoteId);
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: [{ type: "text", data: { text: query.data } }],
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 result = await this.sendContentToChat(chatId, options.content);
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 IGroupManagement, createGroupManagementTools, type ToolFeature } from "zhin.js";
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
- const groupTools = createGroupManagementTools(
52
- telegram as unknown as IGroupManagement,
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
- const permissions: any = {};
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
- platforms: ['telegram'],
307
- tags: ['telegram'],
308
- execute: async (args: Record<string, any>) => {
309
- const endpoint = telegram.endpoints.get(args.endpoint_id);
310
- if (!endpoint) throw new Error(`Endpoint ${args.endpoint_id} 不存在`);
311
- const success = await endpoint.setChatDescription(Number(args.chat_id), args.description);
312
- return { success, message: success ? '群描述已更新' : '操作失败' };
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
  });
@@ -1,8 +1,7 @@
1
1
  /**
2
2
  * Telegram platform permit — ChatMember 状态 + 细粒度权限
3
3
  */
4
- import type { Message } from 'zhin.js';
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
- group_admin: 'chat_administrator',
15
- group_owner: 'chat_creator',
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
@@ -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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"adapter.js","sourceRoot":"","sources":["../src/adapter.ts"],"names":[],"mappings":"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"}