@zhin.js/adapter-wecom 4.0.1 → 4.0.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/CHANGELOG.md +24 -0
- package/README.md +2 -2
- package/lib/endpoint.d.ts +2 -5
- package/lib/endpoint.js +21 -9
- package/lib/protocol.d.ts +6 -0
- package/lib/protocol.js +11 -0
- package/package.json +15 -14
- package/src/endpoint.ts +22 -9
- package/src/protocol.ts +13 -0
- /package/commands/endpoint/add/{[name:string].js → [name].js} +0 -0
- /package/commands/endpoint/add/{[name:string].ts → [name].ts} +0 -0
- /package/commands/endpoint/remove/{[name:string].js → [name].js} +0 -0
- /package/commands/endpoint/remove/{[name:string].ts → [name].ts} +0 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,29 @@
|
|
|
1
1
|
# @zhin.js/adapter-wecom
|
|
2
2
|
|
|
3
|
+
## 4.0.3
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- Updated dependencies [afc0e66]
|
|
8
|
+
- Updated dependencies [2e41ad5]
|
|
9
|
+
- Updated dependencies [9f57124]
|
|
10
|
+
- @zhin.js/core@1.5.2
|
|
11
|
+
- @zhin.js/adapter@1.1.5
|
|
12
|
+
- @zhin.js/im-contract@1.0.1
|
|
13
|
+
- @zhin.js/plugin-runtime@1.1.3
|
|
14
|
+
- @zhin.js/command@1.0.7
|
|
15
|
+
- @zhin.js/host-http@1.0.6
|
|
16
|
+
- @zhin.js/agent@1.1.3
|
|
17
|
+
- zhin.js@6.0.2
|
|
18
|
+
|
|
19
|
+
## 4.0.2
|
|
20
|
+
|
|
21
|
+
### Patch Changes
|
|
22
|
+
|
|
23
|
+
- Updated dependencies [696ab1b]
|
|
24
|
+
- @zhin.js/agent@1.1.2
|
|
25
|
+
- zhin.js@6.0.1
|
|
26
|
+
|
|
3
27
|
## 4.0.1
|
|
4
28
|
|
|
5
29
|
### Patch Changes
|
package/README.md
CHANGED
|
@@ -23,8 +23,8 @@ pnpm add @zhin.js/adapter-wecom
|
|
|
23
23
|
- `@zhin.js/plugin-runtime` — `plugin.ts`(`definePlugin`)
|
|
24
24
|
- 配置经插件 `schema.json` 落到 `plugins.<instanceKey>`
|
|
25
25
|
|
|
26
|
-
入站:`gateway.receive({
|
|
27
|
-
出站:`send({
|
|
26
|
+
入站:`gateway.receive({ conversation, message: { conversation, id: MsgId }, content: text, sender, metadata })`(`conversation` 为 `ConversationRef`,`@chatroom` → kind `group`,其余 → kind `private`)
|
|
27
|
+
出站:`send({ conversation, payload })` → 企业微信 `message/send` API
|
|
28
28
|
|
|
29
29
|
入站 `metadata.mentioned`:**未接线**。企业微信应用消息回调的 XML 事件不含 mentions/@ 字段,回调里的 `ToUserName` 是 CorpID(企业 ID)而非可比较的 bot 用户 id,配置中也没有 bot id/name 可作可靠判据,故无法可靠识别 @ 机器人。
|
|
30
30
|
|
package/lib/endpoint.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* WecomEndpoint — lifecycle, outbound send, inbound admit, OpenAPI helpers for agent tools.
|
|
3
3
|
*/
|
|
4
|
-
import type { EndpointInstance } from '@zhin.js/adapter';
|
|
4
|
+
import type { EndpointInstance, EndpointSendRequest } from '@zhin.js/adapter';
|
|
5
5
|
import type { MessageGateway } from '@zhin.js/core/runtime';
|
|
6
6
|
import type { HttpHost } from '@zhin.js/host-http';
|
|
7
7
|
import type { CapabilityId } from '@zhin.js/plugin-runtime';
|
|
@@ -38,10 +38,7 @@ export declare class WecomEndpoint implements EndpointInstance {
|
|
|
38
38
|
open(): void;
|
|
39
39
|
close(): void;
|
|
40
40
|
stop(): Promise<void>;
|
|
41
|
-
send({
|
|
42
|
-
readonly target: string;
|
|
43
|
-
readonly payload: unknown;
|
|
44
|
-
}): Promise<string>;
|
|
41
|
+
send({ conversation, payload }: EndpointSendRequest): Promise<string>;
|
|
45
42
|
recallMessage(messageId: string): Promise<void>;
|
|
46
43
|
/** Test / internal: admit a parsed message when open (non-webhook path). */
|
|
47
44
|
admit(msg: WecomMessage): void;
|
package/lib/endpoint.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { formatCompact, getLogger } from '@zhin.js/logger';
|
|
2
2
|
import { registerWecomAgentEndpoint } from './wecom-agent-deps.js';
|
|
3
3
|
import { buildMediaUploadForm, readOutboundImageMedia, resolveMediaBinary, } from './media-upload.js';
|
|
4
|
-
import { buildSendRequestBody, formatInboundContent, formatOutboundBody, resolveChatType, } from './protocol.js';
|
|
4
|
+
import { buildSendRequestBody, formatInboundContent, formatOutboundBody, resolveChatType, wecomInboundConversation, } from './protocol.js';
|
|
5
5
|
import { registerWecomWebhookRoutes } from './webhook.js';
|
|
6
6
|
const logger = getLogger('wecom');
|
|
7
7
|
/**
|
|
@@ -64,10 +64,10 @@ export class WecomEndpoint {
|
|
|
64
64
|
this.#started = false;
|
|
65
65
|
logger.debug(formatCompact({ op: 'disconnect', endpoint: this.#options.config.name }));
|
|
66
66
|
}
|
|
67
|
-
async send({
|
|
67
|
+
async send({ conversation, payload }) {
|
|
68
68
|
const materialized = await this.#materializeOutboundMedia(payload);
|
|
69
69
|
const content = formatOutboundBody(materialized);
|
|
70
|
-
const body = buildSendRequestBody(
|
|
70
|
+
const body = buildSendRequestBody(conversation.id, content,
|
|
71
71
|
// Legacy field: historically written into agentid (preserved for cutover).
|
|
72
72
|
this.#options.config.agentSecret);
|
|
73
73
|
const data = await this.#request('/cgi-bin/message/send', {
|
|
@@ -77,7 +77,11 @@ export class WecomEndpoint {
|
|
|
77
77
|
if (data.errcode !== 0) {
|
|
78
78
|
throw new Error(`Failed to send message: ${data.errmsg} (${data.errcode})`);
|
|
79
79
|
}
|
|
80
|
-
logger.debug(formatCompact({
|
|
80
|
+
logger.debug(formatCompact({
|
|
81
|
+
op: 'send',
|
|
82
|
+
endpoint: this.#options.config.name,
|
|
83
|
+
to: `${conversation.kind}:${conversation.id}`,
|
|
84
|
+
}));
|
|
81
85
|
return data.msgid || `${Date.now()}`;
|
|
82
86
|
}
|
|
83
87
|
async recallMessage(messageId) {
|
|
@@ -150,12 +154,12 @@ export class WecomEndpoint {
|
|
|
150
154
|
if (!this.#open)
|
|
151
155
|
return;
|
|
152
156
|
const chatType = resolveChatType(msg.FromUserName);
|
|
157
|
+
const conversation = wecomInboundConversation(String(this.#options.id), msg);
|
|
153
158
|
void this.#options.gateway.receive({
|
|
154
|
-
|
|
155
|
-
|
|
159
|
+
conversation,
|
|
160
|
+
message: { conversation, id: msg.MsgId || `${msg.CreateTime}` },
|
|
156
161
|
content: formatInboundContent(msg),
|
|
157
162
|
sender: msg.FromUserName,
|
|
158
|
-
id: msg.MsgId || `${msg.CreateTime}`,
|
|
159
163
|
metadata: Object.freeze({
|
|
160
164
|
msgType: msg.MsgType,
|
|
161
165
|
event: msg.Event,
|
|
@@ -167,7 +171,7 @@ export class WecomEndpoint {
|
|
|
167
171
|
}).catch((err) => {
|
|
168
172
|
logger.warn(formatCompact({
|
|
169
173
|
op: 'wecom_gateway_receive_failed',
|
|
170
|
-
target:
|
|
174
|
+
target: `${conversation.kind}:${conversation.id}`,
|
|
171
175
|
error: err instanceof Error ? err.message : String(err),
|
|
172
176
|
}));
|
|
173
177
|
});
|
|
@@ -216,7 +220,15 @@ export class WecomEndpoint {
|
|
|
216
220
|
}
|
|
217
221
|
async sendTextMessage(userId, content) {
|
|
218
222
|
try {
|
|
219
|
-
|
|
223
|
+
const endpointId = String(this.#options.id);
|
|
224
|
+
await this.send({
|
|
225
|
+
conversation: {
|
|
226
|
+
endpoint: { id: endpointId, adapter: endpointId.split('\0')[0] ?? endpointId },
|
|
227
|
+
kind: resolveChatType(userId),
|
|
228
|
+
id: userId,
|
|
229
|
+
},
|
|
230
|
+
payload: content,
|
|
231
|
+
});
|
|
220
232
|
return true;
|
|
221
233
|
}
|
|
222
234
|
catch (error) {
|
package/lib/protocol.d.ts
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
* Canonicalization is owned by gateway/core before endpoint.send.
|
|
4
4
|
*/
|
|
5
5
|
import type { IncomingMessage } from 'node:http';
|
|
6
|
+
import type { ConversationRef } from '@zhin.js/im-contract';
|
|
6
7
|
/** Plugin Runtime owner config (`plugins.<instanceKey>` / schema.json). */
|
|
7
8
|
export interface WecomAdapterConfig {
|
|
8
9
|
readonly name?: string;
|
|
@@ -86,6 +87,11 @@ export declare function parseXmlMessage(xml: string): WecomMessage | null;
|
|
|
86
87
|
/** Build inbound text for MessageGateway.receive. */
|
|
87
88
|
export declare function formatInboundContent(msg: WecomMessage): string;
|
|
88
89
|
export declare function resolveChatType(fromUserName: string): 'group' | 'private';
|
|
90
|
+
/**
|
|
91
|
+
* 入站归一化 → ConversationRef:WeCom 无 guild/channel 容器概念,
|
|
92
|
+
* `@chatroom` 群会话 → kind 'group',其余(应用消息/单聊)→ kind 'private'。
|
|
93
|
+
*/
|
|
94
|
+
export declare function wecomInboundConversation(endpointId: string, msg: WecomMessage): ConversationRef;
|
|
89
95
|
/**
|
|
90
96
|
* Wire-encode an already-rendered outbound payload into WeCom message/send body parts.
|
|
91
97
|
*/
|
package/lib/protocol.js
CHANGED
|
@@ -155,6 +155,17 @@ export function formatInboundContent(msg) {
|
|
|
155
155
|
export function resolveChatType(fromUserName) {
|
|
156
156
|
return fromUserName.endsWith('@chatroom') ? 'group' : 'private';
|
|
157
157
|
}
|
|
158
|
+
/**
|
|
159
|
+
* 入站归一化 → ConversationRef:WeCom 无 guild/channel 容器概念,
|
|
160
|
+
* `@chatroom` 群会话 → kind 'group',其余(应用消息/单聊)→ kind 'private'。
|
|
161
|
+
*/
|
|
162
|
+
export function wecomInboundConversation(endpointId, msg) {
|
|
163
|
+
return {
|
|
164
|
+
endpoint: { id: endpointId, adapter: endpointId.split('\0')[0] ?? endpointId },
|
|
165
|
+
kind: resolveChatType(msg.FromUserName),
|
|
166
|
+
id: msg.FromUserName,
|
|
167
|
+
};
|
|
168
|
+
}
|
|
158
169
|
/**
|
|
159
170
|
* Wire-encode an already-rendered outbound payload into WeCom message/send body parts.
|
|
160
171
|
*/
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zhin.js/adapter-wecom",
|
|
3
|
-
"version": "4.0.
|
|
3
|
+
"version": "4.0.3",
|
|
4
4
|
"description": "Zhin.js WeCom (企业微信) adapter for Plugin Runtime (HTTP webhook)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./lib/index.js",
|
|
@@ -34,21 +34,22 @@
|
|
|
34
34
|
"directory": "plugins/adapters/wecom"
|
|
35
35
|
},
|
|
36
36
|
"dependencies": {
|
|
37
|
-
"@zhin.js/adapter": "1.1.
|
|
38
|
-
"@zhin.js/command": "1.0.
|
|
39
|
-
"@zhin.js/core": "1.5.
|
|
40
|
-
"@zhin.js/host-http": "1.0.
|
|
37
|
+
"@zhin.js/adapter": "1.1.5",
|
|
38
|
+
"@zhin.js/command": "1.0.7",
|
|
39
|
+
"@zhin.js/core": "1.5.2",
|
|
40
|
+
"@zhin.js/host-http": "1.0.6",
|
|
41
|
+
"@zhin.js/im-contract": "1.0.1",
|
|
41
42
|
"@zhin.js/logger": "1.0.75",
|
|
42
|
-
"@zhin.js/plugin-runtime": "1.1.
|
|
43
|
+
"@zhin.js/plugin-runtime": "1.1.3"
|
|
43
44
|
},
|
|
44
45
|
"peerDependencies": {
|
|
45
46
|
"zod": "^4.0.0",
|
|
46
|
-
"@zhin.js/adapter": "1.1.
|
|
47
|
-
"@zhin.js/agent": "1.1.
|
|
48
|
-
"@zhin.js/core": "1.5.
|
|
49
|
-
"@zhin.js/host-http": "1.0.
|
|
50
|
-
"@zhin.js/plugin-runtime": "1.1.
|
|
51
|
-
"zhin.js": "6.0.
|
|
47
|
+
"@zhin.js/adapter": "1.1.5",
|
|
48
|
+
"@zhin.js/agent": "1.1.3",
|
|
49
|
+
"@zhin.js/core": "1.5.2",
|
|
50
|
+
"@zhin.js/host-http": "1.0.6",
|
|
51
|
+
"@zhin.js/plugin-runtime": "1.1.3",
|
|
52
|
+
"zhin.js": "6.0.2"
|
|
52
53
|
},
|
|
53
54
|
"peerDependenciesMeta": {
|
|
54
55
|
"zhin.js": {
|
|
@@ -66,8 +67,8 @@
|
|
|
66
67
|
"typescript": "^6.0.3",
|
|
67
68
|
"vitest": "^4.1.10",
|
|
68
69
|
"zod": "^4.4.3",
|
|
69
|
-
"@zhin.js/agent": "1.1.
|
|
70
|
-
"zhin.js": "6.0.
|
|
70
|
+
"@zhin.js/agent": "1.1.3",
|
|
71
|
+
"zhin.js": "6.0.2"
|
|
71
72
|
},
|
|
72
73
|
"files": [
|
|
73
74
|
"adapters",
|
package/src/endpoint.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* WecomEndpoint — lifecycle, outbound send, inbound admit, OpenAPI helpers for agent tools.
|
|
3
3
|
*/
|
|
4
|
-
import type { EndpointInstance } from '@zhin.js/adapter';
|
|
4
|
+
import type { EndpointInstance, EndpointSendRequest } from '@zhin.js/adapter';
|
|
5
5
|
import type { MessageGateway } from '@zhin.js/core/runtime';
|
|
6
6
|
import type { HttpHost, HttpRouteRegistration } from '@zhin.js/host-http';
|
|
7
7
|
import { formatCompact, getLogger } from '@zhin.js/logger';
|
|
@@ -18,6 +18,7 @@ import {
|
|
|
18
18
|
formatInboundContent,
|
|
19
19
|
formatOutboundBody,
|
|
20
20
|
resolveChatType,
|
|
21
|
+
wecomInboundConversation,
|
|
21
22
|
type AccessToken,
|
|
22
23
|
type ResolvedWecomConfig,
|
|
23
24
|
type WecomApiResponse,
|
|
@@ -114,11 +115,11 @@ export class WecomEndpoint implements EndpointInstance {
|
|
|
114
115
|
logger.debug(formatCompact({ op: 'disconnect', endpoint: this.#options.config.name }));
|
|
115
116
|
}
|
|
116
117
|
|
|
117
|
-
async send({
|
|
118
|
+
async send({ conversation, payload }: EndpointSendRequest): Promise<string> {
|
|
118
119
|
const materialized = await this.#materializeOutboundMedia(payload);
|
|
119
120
|
const content = formatOutboundBody(materialized);
|
|
120
121
|
const body = buildSendRequestBody(
|
|
121
|
-
|
|
122
|
+
conversation.id,
|
|
122
123
|
content,
|
|
123
124
|
// Legacy field: historically written into agentid (preserved for cutover).
|
|
124
125
|
this.#options.config.agentSecret,
|
|
@@ -130,7 +131,11 @@ export class WecomEndpoint implements EndpointInstance {
|
|
|
130
131
|
if (data.errcode !== 0) {
|
|
131
132
|
throw new Error(`Failed to send message: ${data.errmsg} (${data.errcode})`);
|
|
132
133
|
}
|
|
133
|
-
logger.debug(formatCompact({
|
|
134
|
+
logger.debug(formatCompact({
|
|
135
|
+
op: 'send',
|
|
136
|
+
endpoint: this.#options.config.name,
|
|
137
|
+
to: `${conversation.kind}:${conversation.id}`,
|
|
138
|
+
}));
|
|
134
139
|
return (data.msgid as string) || `${Date.now()}`;
|
|
135
140
|
}
|
|
136
141
|
|
|
@@ -200,12 +205,12 @@ export class WecomEndpoint implements EndpointInstance {
|
|
|
200
205
|
admit(msg: WecomMessage): void {
|
|
201
206
|
if (!this.#open) return;
|
|
202
207
|
const chatType = resolveChatType(msg.FromUserName);
|
|
208
|
+
const conversation = wecomInboundConversation(String(this.#options.id), msg);
|
|
203
209
|
void this.#options.gateway.receive({
|
|
204
|
-
|
|
205
|
-
|
|
210
|
+
conversation,
|
|
211
|
+
message: { conversation, id: msg.MsgId || `${msg.CreateTime}` },
|
|
206
212
|
content: formatInboundContent(msg),
|
|
207
213
|
sender: msg.FromUserName,
|
|
208
|
-
id: msg.MsgId || `${msg.CreateTime}`,
|
|
209
214
|
metadata: Object.freeze({
|
|
210
215
|
msgType: msg.MsgType,
|
|
211
216
|
event: msg.Event,
|
|
@@ -217,7 +222,7 @@ export class WecomEndpoint implements EndpointInstance {
|
|
|
217
222
|
}).catch((err) => {
|
|
218
223
|
logger.warn(formatCompact({
|
|
219
224
|
op: 'wecom_gateway_receive_failed',
|
|
220
|
-
target:
|
|
225
|
+
target: `${conversation.kind}:${conversation.id}`,
|
|
221
226
|
error: err instanceof Error ? err.message : String(err),
|
|
222
227
|
}));
|
|
223
228
|
});
|
|
@@ -264,7 +269,15 @@ export class WecomEndpoint implements EndpointInstance {
|
|
|
264
269
|
|
|
265
270
|
async sendTextMessage(userId: string, content: string): Promise<boolean> {
|
|
266
271
|
try {
|
|
267
|
-
|
|
272
|
+
const endpointId = String(this.#options.id);
|
|
273
|
+
await this.send({
|
|
274
|
+
conversation: {
|
|
275
|
+
endpoint: { id: endpointId, adapter: endpointId.split('\0')[0] ?? endpointId },
|
|
276
|
+
kind: resolveChatType(userId),
|
|
277
|
+
id: userId,
|
|
278
|
+
},
|
|
279
|
+
payload: content,
|
|
280
|
+
});
|
|
268
281
|
return true;
|
|
269
282
|
} catch (error) {
|
|
270
283
|
logger.error('Failed to send text message:', error);
|
package/src/protocol.ts
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
|
|
6
6
|
import { createHash, createDecipheriv, timingSafeEqual } from 'node:crypto';
|
|
7
7
|
import type { IncomingMessage } from 'node:http';
|
|
8
|
+
import type { ConversationRef } from '@zhin.js/im-contract';
|
|
8
9
|
|
|
9
10
|
/** Plugin Runtime owner config (`plugins.<instanceKey>` / schema.json). */
|
|
10
11
|
export interface WecomAdapterConfig {
|
|
@@ -255,6 +256,18 @@ export function resolveChatType(fromUserName: string): 'group' | 'private' {
|
|
|
255
256
|
return fromUserName.endsWith('@chatroom') ? 'group' : 'private';
|
|
256
257
|
}
|
|
257
258
|
|
|
259
|
+
/**
|
|
260
|
+
* 入站归一化 → ConversationRef:WeCom 无 guild/channel 容器概念,
|
|
261
|
+
* `@chatroom` 群会话 → kind 'group',其余(应用消息/单聊)→ kind 'private'。
|
|
262
|
+
*/
|
|
263
|
+
export function wecomInboundConversation(endpointId: string, msg: WecomMessage): ConversationRef {
|
|
264
|
+
return {
|
|
265
|
+
endpoint: { id: endpointId, adapter: endpointId.split('\0')[0] ?? endpointId },
|
|
266
|
+
kind: resolveChatType(msg.FromUserName),
|
|
267
|
+
id: msg.FromUserName,
|
|
268
|
+
};
|
|
269
|
+
}
|
|
270
|
+
|
|
258
271
|
/**
|
|
259
272
|
* Wire-encode an already-rendered outbound payload into WeCom message/send body parts.
|
|
260
273
|
*/
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|