@zhin.js/adapter-weixin-ilink 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 +25 -0
- package/README.md +1 -1
- package/lib/endpoint.d.ts +2 -5
- package/lib/endpoint.js +7 -5
- package/lib/protocol.d.ts +6 -0
- package/lib/protocol.js +11 -0
- package/package.json +10 -9
- package/src/endpoint.ts +13 -5
- 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,30 @@
|
|
|
1
1
|
# @zhin.js/adapter-weixin-ilink
|
|
2
2
|
|
|
3
|
+
## 5.0.2
|
|
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@6.0.2
|
|
16
|
+
|
|
17
|
+
## 5.0.1
|
|
18
|
+
|
|
19
|
+
### Patch Changes
|
|
20
|
+
|
|
21
|
+
- Updated dependencies [c8f4d45]
|
|
22
|
+
- @zhin.js/plugin-runtime@1.1.2
|
|
23
|
+
- @zhin.js/adapter@1.1.4
|
|
24
|
+
- @zhin.js/command@1.0.6
|
|
25
|
+
- @zhin.js/core@1.5.1
|
|
26
|
+
- zhin.js@6.0.1
|
|
27
|
+
|
|
3
28
|
## 5.0.0
|
|
4
29
|
|
|
5
30
|
### Patch Changes
|
package/README.md
CHANGED
|
@@ -52,7 +52,7 @@ data/weixin-ilink/<bot-name>.json
|
|
|
52
52
|
|
|
53
53
|
回复必须携带入站时缓存的 `context_token`(按 `endpointId + peerUserId`)。若用户久未发消息导致 token 缺失,出站会拒绝并打 warn。
|
|
54
54
|
|
|
55
|
-
出站经 Runtime:`MessageGateway` → `endpoint.send({
|
|
55
|
+
出站经 Runtime:`MessageGateway` → `endpoint.send({ conversation, payload })`(`conversation` 为入站归一化出的 `ConversationRef`,kind 恒为 `private`,`id` 即对端 user_id)。
|
|
56
56
|
|
|
57
57
|
**图文限制**:微信单条消息不支持图文混排。适配器会自动:
|
|
58
58
|
|
package/lib/endpoint.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { EndpointInstance, EndpointManagement } from '@zhin.js/adapter';
|
|
1
|
+
import type { EndpointInstance, EndpointManagement, EndpointSendRequest } from '@zhin.js/adapter';
|
|
2
2
|
import type { MessageGateway } from '@zhin.js/core/runtime';
|
|
3
3
|
import type { CapabilityId } from '@zhin.js/plugin-runtime';
|
|
4
4
|
import { getUpdates, notifyStart, notifyStop } from './ilink-api.js';
|
|
@@ -33,10 +33,7 @@ export declare class WeixinIlinkEndpoint implements EndpointInstance {
|
|
|
33
33
|
open(): void;
|
|
34
34
|
close(): void;
|
|
35
35
|
stop(): Promise<void>;
|
|
36
|
-
send({
|
|
37
|
-
readonly target: string;
|
|
38
|
-
readonly payload: unknown;
|
|
39
|
-
}): Promise<string>;
|
|
36
|
+
send({ conversation, payload }: EndpointSendRequest): Promise<string>;
|
|
40
37
|
/** Test / internal: admit a parsed message when the endpoint is open. */
|
|
41
38
|
admit(msg: WeixinMessageWithMedia): void;
|
|
42
39
|
sendTypingToUser(userId: string, status: number): Promise<void>;
|
package/lib/endpoint.js
CHANGED
|
@@ -18,7 +18,7 @@ import { sendWeixinMediaFile } from './weixin-send-media.js';
|
|
|
18
18
|
import { materializeOutboundMedia } from './outbound-media.js';
|
|
19
19
|
import { MessageItemType } from './ilink-types.js';
|
|
20
20
|
import { getExtensionFromMime, sniffMimeFromBuffer } from './mime.js';
|
|
21
|
-
import { formatInboundContent, formatOutboundSegments, inboundMessageId, segmentLocalPath, sleep, } from './protocol.js';
|
|
21
|
+
import { formatInboundContent, formatOutboundSegments, inboundMessageId, segmentLocalPath, sleep, weixinIlinkInboundConversation, } from './protocol.js';
|
|
22
22
|
const logger = getLogger('weixin-ilink');
|
|
23
23
|
const MAX_CONSECUTIVE_FAILURES = 3;
|
|
24
24
|
const BACKOFF_DELAY_MS = 30_000;
|
|
@@ -130,10 +130,12 @@ export class WeixinIlinkEndpoint {
|
|
|
130
130
|
endpoint: this.#options.config.name,
|
|
131
131
|
}));
|
|
132
132
|
}
|
|
133
|
-
async send({
|
|
133
|
+
async send({ conversation, payload }) {
|
|
134
134
|
if (!this.#creds?.botToken) {
|
|
135
135
|
throw new Error('weixin-ilink bot not authenticated');
|
|
136
136
|
}
|
|
137
|
+
// 个人微信仅 private 会话,对端 user_id 即 conversation.id
|
|
138
|
+
const target = conversation.id;
|
|
137
139
|
const contextToken = getContextToken(this.#options.config.name, target);
|
|
138
140
|
if (!contextToken) {
|
|
139
141
|
logger.warn(formatCompact({
|
|
@@ -215,12 +217,12 @@ export class WeixinIlinkEndpoint {
|
|
|
215
217
|
if (msg.context_token && userId) {
|
|
216
218
|
setContextToken(this.#options.config.name, userId, msg.context_token);
|
|
217
219
|
}
|
|
220
|
+
const conversation = weixinIlinkInboundConversation(String(this.#options.id), userId);
|
|
218
221
|
void this.#options.gateway.receive({
|
|
219
|
-
|
|
220
|
-
|
|
222
|
+
conversation,
|
|
223
|
+
message: { conversation, id: inboundMessageId(msg) },
|
|
221
224
|
content: formatInboundContent(msg),
|
|
222
225
|
sender: userId,
|
|
223
|
-
id: inboundMessageId(msg),
|
|
224
226
|
metadata: Object.freeze({
|
|
225
227
|
endpoint: this.#options.config.name,
|
|
226
228
|
messageType: msg.message_type,
|
package/lib/protocol.d.ts
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
* Weixin iLink protocol helpers (no legacy Adapter/Endpoint / segment-mapper).
|
|
3
3
|
* Canonicalization is owned by gateway/core before endpoint.send.
|
|
4
4
|
*/
|
|
5
|
+
import type { ConversationRef } from '@zhin.js/im-contract';
|
|
5
6
|
import type { WeixinMessage } from './ilink-types.js';
|
|
6
7
|
/** Plugin Runtime owner config (`plugins.<instanceKey>` / schema.json). */
|
|
7
8
|
export interface WeixinIlinkAdapterConfig {
|
|
@@ -43,6 +44,11 @@ export declare function resolveWeixinIlinkConfig(config?: WeixinIlinkAdapterConf
|
|
|
43
44
|
/** Build inbound text for MessageGateway.receive (gateway owns reply routing). */
|
|
44
45
|
export declare function formatInboundContent(msg: WeixinMessageWithMedia): string;
|
|
45
46
|
export declare function inboundMessageId(msg: WeixinMessage): string;
|
|
47
|
+
/**
|
|
48
|
+
* 入站归一化 → ConversationRef:个人微信无群/频道概念,全部会话都是
|
|
49
|
+
* 与 `from_user_id` 的 private 会话(无 parent)。
|
|
50
|
+
*/
|
|
51
|
+
export declare function weixinIlinkInboundConversation(endpointId: string, userId: string): ConversationRef;
|
|
46
52
|
/**
|
|
47
53
|
* Wire-encode an already-rendered outbound payload into Weixin wire segments.
|
|
48
54
|
* Segment canonicalization is intentionally not done here.
|
package/lib/protocol.js
CHANGED
|
@@ -55,6 +55,17 @@ export function formatInboundContent(msg) {
|
|
|
55
55
|
export function inboundMessageId(msg) {
|
|
56
56
|
return String(msg.message_id ?? msg.client_id ?? msg.seq ?? Date.now());
|
|
57
57
|
}
|
|
58
|
+
/**
|
|
59
|
+
* 入站归一化 → ConversationRef:个人微信无群/频道概念,全部会话都是
|
|
60
|
+
* 与 `from_user_id` 的 private 会话(无 parent)。
|
|
61
|
+
*/
|
|
62
|
+
export function weixinIlinkInboundConversation(endpointId, userId) {
|
|
63
|
+
return {
|
|
64
|
+
endpoint: { id: endpointId, adapter: endpointId.split('\0')[0] ?? endpointId },
|
|
65
|
+
kind: 'private',
|
|
66
|
+
id: userId,
|
|
67
|
+
};
|
|
68
|
+
}
|
|
58
69
|
/**
|
|
59
70
|
* Wire-encode an already-rendered outbound payload into Weixin wire segments.
|
|
60
71
|
* Segment canonicalization is intentionally not done here.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zhin.js/adapter-weixin-ilink",
|
|
3
|
-
"version": "5.0.
|
|
3
|
+
"version": "5.0.2",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Zhin.js Weixin iLink (ClawBot) adapter for Plugin Runtime (long-poll)",
|
|
6
6
|
"main": "./lib/index.js",
|
|
@@ -48,17 +48,18 @@
|
|
|
48
48
|
}
|
|
49
49
|
},
|
|
50
50
|
"dependencies": {
|
|
51
|
-
"@zhin.js/adapter": "1.1.
|
|
52
|
-
"@zhin.js/command": "1.0.
|
|
53
|
-
"@zhin.js/core": "1.5.
|
|
51
|
+
"@zhin.js/adapter": "1.1.5",
|
|
52
|
+
"@zhin.js/command": "1.0.7",
|
|
53
|
+
"@zhin.js/core": "1.5.2",
|
|
54
|
+
"@zhin.js/im-contract": "1.0.1",
|
|
54
55
|
"@zhin.js/logger": "1.0.75",
|
|
55
|
-
"@zhin.js/plugin-runtime": "1.1.
|
|
56
|
+
"@zhin.js/plugin-runtime": "1.1.3"
|
|
56
57
|
},
|
|
57
58
|
"peerDependencies": {
|
|
58
|
-
"@zhin.js/adapter": "1.1.
|
|
59
|
-
"@zhin.js/core": "1.5.
|
|
60
|
-
"@zhin.js/plugin-runtime": "1.1.
|
|
61
|
-
"zhin.js": "6.0.
|
|
59
|
+
"@zhin.js/adapter": "1.1.5",
|
|
60
|
+
"@zhin.js/core": "1.5.2",
|
|
61
|
+
"@zhin.js/plugin-runtime": "1.1.3",
|
|
62
|
+
"zhin.js": "6.0.2"
|
|
62
63
|
},
|
|
63
64
|
"peerDependenciesMeta": {
|
|
64
65
|
"zhin.js": {
|
package/src/endpoint.ts
CHANGED
|
@@ -4,7 +4,12 @@
|
|
|
4
4
|
import crypto from 'node:crypto';
|
|
5
5
|
import fs from 'node:fs';
|
|
6
6
|
import path from 'node:path';
|
|
7
|
-
import type {
|
|
7
|
+
import type {
|
|
8
|
+
EndpointFriend,
|
|
9
|
+
EndpointInstance,
|
|
10
|
+
EndpointManagement,
|
|
11
|
+
EndpointSendRequest,
|
|
12
|
+
} from '@zhin.js/adapter';
|
|
8
13
|
import type { MessageGateway } from '@zhin.js/core/runtime';
|
|
9
14
|
import { formatCompact, getLogger } from '@zhin.js/logger';
|
|
10
15
|
import type { CapabilityId } from '@zhin.js/plugin-runtime';
|
|
@@ -44,6 +49,7 @@ import {
|
|
|
44
49
|
inboundMessageId,
|
|
45
50
|
segmentLocalPath,
|
|
46
51
|
sleep,
|
|
52
|
+
weixinIlinkInboundConversation,
|
|
47
53
|
type ResolvedWeixinIlinkConfig,
|
|
48
54
|
type WeixinMessageWithMedia,
|
|
49
55
|
type WeixinWireSegment,
|
|
@@ -197,10 +203,12 @@ export class WeixinIlinkEndpoint implements EndpointInstance {
|
|
|
197
203
|
}));
|
|
198
204
|
}
|
|
199
205
|
|
|
200
|
-
async send({
|
|
206
|
+
async send({ conversation, payload }: EndpointSendRequest): Promise<string> {
|
|
201
207
|
if (!this.#creds?.botToken) {
|
|
202
208
|
throw new Error('weixin-ilink bot not authenticated');
|
|
203
209
|
}
|
|
210
|
+
// 个人微信仅 private 会话,对端 user_id 即 conversation.id
|
|
211
|
+
const target = conversation.id;
|
|
204
212
|
const contextToken = getContextToken(this.#options.config.name, target);
|
|
205
213
|
if (!contextToken) {
|
|
206
214
|
logger.warn(formatCompact({
|
|
@@ -288,12 +296,12 @@ export class WeixinIlinkEndpoint implements EndpointInstance {
|
|
|
288
296
|
if (msg.context_token && userId) {
|
|
289
297
|
setContextToken(this.#options.config.name, userId, msg.context_token);
|
|
290
298
|
}
|
|
299
|
+
const conversation = weixinIlinkInboundConversation(String(this.#options.id), userId);
|
|
291
300
|
void this.#options.gateway.receive({
|
|
292
|
-
|
|
293
|
-
|
|
301
|
+
conversation,
|
|
302
|
+
message: { conversation, id: inboundMessageId(msg) },
|
|
294
303
|
content: formatInboundContent(msg),
|
|
295
304
|
sender: userId,
|
|
296
|
-
id: inboundMessageId(msg),
|
|
297
305
|
metadata: Object.freeze({
|
|
298
306
|
endpoint: this.#options.config.name,
|
|
299
307
|
messageType: msg.message_type,
|
package/src/protocol.ts
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
|
|
6
6
|
import { pickCredential } from '@zhin.js/adapter';
|
|
7
7
|
import { isMediaRef } from '@zhin.js/core';
|
|
8
|
+
import type { ConversationRef } from '@zhin.js/im-contract';
|
|
8
9
|
import { bodyFromItemList, isMediaItem } from './weixin-inbound.js';
|
|
9
10
|
import { DEFAULT_API_BASE_URL, DEFAULT_CDN_BASE_URL } from './ilink-meta.js';
|
|
10
11
|
import type { WeixinMessage } from './ilink-types.js';
|
|
@@ -107,6 +108,18 @@ export function inboundMessageId(msg: WeixinMessage): string {
|
|
|
107
108
|
return String(msg.message_id ?? msg.client_id ?? msg.seq ?? Date.now());
|
|
108
109
|
}
|
|
109
110
|
|
|
111
|
+
/**
|
|
112
|
+
* 入站归一化 → ConversationRef:个人微信无群/频道概念,全部会话都是
|
|
113
|
+
* 与 `from_user_id` 的 private 会话(无 parent)。
|
|
114
|
+
*/
|
|
115
|
+
export function weixinIlinkInboundConversation(endpointId: string, userId: string): ConversationRef {
|
|
116
|
+
return {
|
|
117
|
+
endpoint: { id: endpointId, adapter: endpointId.split('\0')[0] ?? endpointId },
|
|
118
|
+
kind: 'private',
|
|
119
|
+
id: userId,
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
|
|
110
123
|
/**
|
|
111
124
|
* Wire-encode an already-rendered outbound payload into Weixin wire segments.
|
|
112
125
|
* Segment canonicalization is intentionally not done here.
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|