@zhin.js/adapter-napcat 1.1.22 → 1.1.23
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 +34 -0
- package/lib/http-endpoint.js +6 -1
- package/lib/napcat-inbound.d.ts +2 -5
- package/lib/napcat-inbound.js +7 -21
- package/lib/onebot-get-msg.js +3 -2
- package/lib/protocol.d.ts +5 -0
- package/lib/protocol.js +21 -0
- package/lib/ws-endpoint.js +7 -1
- package/lib/ws-types.d.ts +1 -0
- package/lib/wss-endpoint.js +7 -1
- package/package.json +19 -19
- package/src/http-endpoint.ts +6 -0
- package/src/napcat-inbound.ts +4 -21
- package/src/onebot-get-msg.ts +6 -4
- package/src/protocol.ts +19 -0
- package/src/ws-endpoint.ts +7 -0
- package/src/ws-types.ts +1 -0
- package/src/wss-endpoint.ts +7 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,39 @@
|
|
|
1
1
|
# @zhin.js/adapter-napcat
|
|
2
2
|
|
|
3
|
+
## 1.1.23
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- bc6ccb1: Deep audit bug fixes and 7 architectural improvements:
|
|
8
|
+
- Sink Agent Turn ingress pipeline from `@zhin.js/cli` into `@zhin.js/agent/runtime` (`plugin-runtime/ingress/`) and decompose `ingress-route.ts` into modular pipeline stages.
|
|
9
|
+
- Bind Session State Plane (`JsxButtonRegistry`, `InteractionRuntime`, `RuntimeInteractiveRouter`) to `PluginIncarnationRef` so active buttons and interactions survive unrelated-plugin HMR while expiring when the owning plugin reloads, and support per-conversation fallback numbering (`numberingScope: 'conversation'`).
|
|
10
|
+
- Establish `conversation_events` as the canonical message SSOT and reference `conversation_event_id`, `conversation_key`, `platform_message_id`, and `message_key` in `agent_messages` (`AGENT_MESSAGE_QUERY_VERSION = 2`), threading `conversationSequence` across inbound and outbound delivery receipts.
|
|
11
|
+
- Push down `DatabaseContextRepository` and `DatabaseScheduleJobStorage` full-session/full-table memory scans to indexed SQL queries.
|
|
12
|
+
- Add kernel-level inbound `replyTo` and `mentioned` auto-promotion (`promoteInboundMessagePayload`) plus reusable `InboundDeduper` and `TtlLookupCache` primitives in `@zhin.js/adapter`.
|
|
13
|
+
- Add `MiddlewareContext.dispatch` (`InboundDispatchSummary`) and `onAfterDispatch` hooks for `after-dispatch` middlewares, and support incremental rendering of in-place `envelope.payload` array mutations in `OutboundDeliveryRuntime`.
|
|
14
|
+
- Unify `HistoryWindowTracker` and `AgentCompactionRuntime` token/message budgets (`resolveUnifiedHistoryAndCompactionBudget`).
|
|
15
|
+
- Fix schema validation, database transaction connection cleanup and SQLite WAL mode, schedule timer wheel head-of-line blocking, security policy bash read paths and subagent guard propagation, group merge window permission carrier handoff, micro-compact namespaced tool matching, and WebSocket force-close / inbound segment mapping across adapters.
|
|
16
|
+
- Updated dependencies [3a8504b]
|
|
17
|
+
- Updated dependencies [6163abc]
|
|
18
|
+
- Updated dependencies [fed2c27]
|
|
19
|
+
- Updated dependencies [bc6ccb1]
|
|
20
|
+
- Updated dependencies [72b5a12]
|
|
21
|
+
- Updated dependencies [aa41889]
|
|
22
|
+
- Updated dependencies [4427e72]
|
|
23
|
+
- Updated dependencies [22bd14a]
|
|
24
|
+
- @zhin.js/agent-feature@1.1.14
|
|
25
|
+
- @zhin.js/core@1.1.57
|
|
26
|
+
- @zhin.js/client@1.1.25
|
|
27
|
+
- @zhin.js/page@1.1.15
|
|
28
|
+
- @zhin.js/im-contract@1.1.12
|
|
29
|
+
- @zhin.js/adapter@1.1.29
|
|
30
|
+
- @zhin.js/host-http@1.1.15
|
|
31
|
+
- @zhin.js/feature-kit@1.1.13
|
|
32
|
+
- @zhin.js/tool@1.1.16
|
|
33
|
+
- @zhin.js/adapter-onebot-events@0.0.2
|
|
34
|
+
- @zhin.js/permission@1.1.13
|
|
35
|
+
- @zhin.js/skill@1.1.14
|
|
36
|
+
|
|
3
37
|
## 1.1.22
|
|
4
38
|
|
|
5
39
|
### Patch Changes
|
package/lib/http-endpoint.js
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { EndpointDeliveryError } from '@zhin.js/im-contract';
|
|
2
|
+
import { toCanonicalSegments } from '@zhin.js/core';
|
|
2
3
|
import { createEndpointSpecification, endpointSpec, createRecallEndpointControl, } from 'zhin.js/adapter';
|
|
3
4
|
import { formatCompact, getAdapterLogger } from '@zhin.js/logger';
|
|
4
5
|
import { createNapCatEndpointManagement } from './endpoint-management.js';
|
|
5
6
|
import { createNapcatSelfProfile } from './self-profile.js';
|
|
6
7
|
import { InboundMessageDeduper, isNapCatBotMentioned, isSelfMessage, normalizeMessage, } from './napcat-inbound.js';
|
|
7
|
-
import { buildSendAction, callNapCatHttpAction, formatInboundContent, formatOutboundSegments, isMessageEvent, napcatInboundConversation, napcatOutboundTarget, senderNickname, senderUserId, } from './protocol.js';
|
|
8
|
+
import { buildSendAction, callNapCatHttpAction, extractQuoteId, formatInboundContent, formatOutboundSegments, isMessageEvent, napcatInboundConversation, napcatOutboundTarget, senderNickname, senderUserId, } from './protocol.js';
|
|
8
9
|
import { receiveNapCatSideEvent } from './side-event-dispatch.js';
|
|
9
10
|
import { createNapCatContentPort } from './onebot-get-msg.js';
|
|
10
11
|
import { NapcatClient } from './client.js';
|
|
@@ -120,12 +121,15 @@ export class NapCatHttpEndpoint extends NapcatClient {
|
|
|
120
121
|
const conversation = { ...napcatInboundConversation(String(this.#options.id), ev), endpoint: this[endpointSpec].identity };
|
|
121
122
|
const nickname = senderNickname(ev);
|
|
122
123
|
const mentioned = isNapCatBotMentioned(ev);
|
|
124
|
+
const quoteId = extractQuoteId(ev);
|
|
125
|
+
const segments = Array.isArray(ev.message) ? toCanonicalSegments(ev.message) : undefined;
|
|
123
126
|
void this[endpointSpec].events.message({
|
|
124
127
|
event: nativeEvent,
|
|
125
128
|
...(typeof nativeEvent.time === 'number' ? { platformSentAt: nativeEvent.time * 1000 } : {}),
|
|
126
129
|
conversation,
|
|
127
130
|
message: { conversation, id: msgId },
|
|
128
131
|
content: formatInboundContent(ev),
|
|
132
|
+
...(segments?.length ? { segments } : {}),
|
|
129
133
|
sender: {
|
|
130
134
|
id: senderUserId(ev),
|
|
131
135
|
name: nickname,
|
|
@@ -133,6 +137,7 @@ export class NapCatHttpEndpoint extends NapcatClient {
|
|
|
133
137
|
},
|
|
134
138
|
endpointId: this.#options.config.id,
|
|
135
139
|
...(mentioned ? { mentioned: true } : {}),
|
|
140
|
+
...(quoteId ? { replyTo: { id: quoteId } } : {}),
|
|
136
141
|
metadata: Object.freeze({
|
|
137
142
|
message_type: ev.message_type,
|
|
138
143
|
user_id: ev.user_id != null ? String(ev.user_id) : undefined,
|
package/lib/napcat-inbound.d.ts
CHANGED
|
@@ -1,12 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* NapCat 入站消息治理:去重、自发过滤、消息归一化
|
|
3
3
|
*/
|
|
4
|
+
import { InboundMessageDeduper } from 'zhin.js/adapter';
|
|
4
5
|
import type { NapCatMessageEvent, MessageSegment } from './protocol.js';
|
|
5
|
-
export
|
|
6
|
-
private readonly seen;
|
|
7
|
-
shouldProcess(messageId: string): boolean;
|
|
8
|
-
clear(): void;
|
|
9
|
-
}
|
|
6
|
+
export { InboundMessageDeduper };
|
|
10
7
|
/** 判断是否为 bot 自身发出的消息 */
|
|
11
8
|
export declare function isSelfMessage(event: NapCatMessageEvent | {
|
|
12
9
|
post_type?: string;
|
package/lib/napcat-inbound.js
CHANGED
|
@@ -1,21 +1,8 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
for (const [id, t] of this.seen) {
|
|
7
|
-
if (now - t > DEDUPE_TTL_MS)
|
|
8
|
-
this.seen.delete(id);
|
|
9
|
-
}
|
|
10
|
-
if (this.seen.has(messageId))
|
|
11
|
-
return false;
|
|
12
|
-
this.seen.set(messageId, now);
|
|
13
|
-
return true;
|
|
14
|
-
}
|
|
15
|
-
clear() {
|
|
16
|
-
this.seen.clear();
|
|
17
|
-
}
|
|
18
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* NapCat 入站消息治理:去重、自发过滤、消息归一化
|
|
3
|
+
*/
|
|
4
|
+
import { InboundMessageDeduper, promoteInboundMessagePayload } from 'zhin.js/adapter';
|
|
5
|
+
export { InboundMessageDeduper };
|
|
19
6
|
/** 判断是否为 bot 自身发出的消息 */
|
|
20
7
|
export function isSelfMessage(event) {
|
|
21
8
|
if (event.post_type === 'message_sent')
|
|
@@ -53,9 +40,8 @@ export function isNapCatBotMentioned(ev) {
|
|
|
53
40
|
if (!uin)
|
|
54
41
|
return false;
|
|
55
42
|
const segments = normalizeMessage(ev.message ?? []);
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
return true;
|
|
43
|
+
if (promoteInboundMessagePayload({ segments }, uin).mentioned === true) {
|
|
44
|
+
return true;
|
|
59
45
|
}
|
|
60
46
|
const text = segments
|
|
61
47
|
.map((seg) => (seg.type === 'text' ? String(seg.data?.text ?? '') : ''))
|
package/lib/onebot-get-msg.js
CHANGED
|
@@ -67,9 +67,10 @@ export function createNapCatContentPort(callApi) {
|
|
|
67
67
|
},
|
|
68
68
|
});
|
|
69
69
|
}
|
|
70
|
+
import { toCanonicalSegments } from '@zhin.js/core';
|
|
70
71
|
function canonicalSegments(content) {
|
|
71
|
-
return Object.freeze(content.map((segment) => Object.freeze({
|
|
72
|
-
|
|
72
|
+
return Object.freeze(toCanonicalSegments(content).map((segment) => Object.freeze({
|
|
73
|
+
...segment,
|
|
73
74
|
data: Object.freeze(segment.data ?? {}),
|
|
74
75
|
})));
|
|
75
76
|
}
|
package/lib/protocol.d.ts
CHANGED
|
@@ -119,6 +119,11 @@ export declare function formatInboundContent(ev: NapCatEvent): string;
|
|
|
119
119
|
export declare function senderUserId(ev: NapCatEvent): string;
|
|
120
120
|
/** 显示名(群名片 card 优先于 nickname),没有则 undefined。 */
|
|
121
121
|
export declare function senderNickname(ev: NapCatEvent): string | undefined;
|
|
122
|
+
/**
|
|
123
|
+
* 回复引用 id:优先取 message 数组中的 {type:'reply', data:{id}} 段;
|
|
124
|
+
* 兼容部分实现把 reply 放在事件顶层(标量或带 message_id 的对象)。
|
|
125
|
+
*/
|
|
126
|
+
export declare function extractQuoteId(ev: NapCatEvent): string | undefined;
|
|
122
127
|
/**
|
|
123
128
|
* canonical MediaRef → OneBot `file` 参数:url 直传、base64 → `base64://`、
|
|
124
129
|
* 本地路径 → `file://`(路径在 OneBot 实现侧解析)、kind=file 不透明引用原样透传。
|
package/lib/protocol.js
CHANGED
|
@@ -157,6 +157,27 @@ export function senderNickname(ev) {
|
|
|
157
157
|
const name = ev.sender?.card || ev.sender?.nickname;
|
|
158
158
|
return typeof name === 'string' && name ? name : undefined;
|
|
159
159
|
}
|
|
160
|
+
/**
|
|
161
|
+
* 回复引用 id:优先取 message 数组中的 {type:'reply', data:{id}} 段;
|
|
162
|
+
* 兼容部分实现把 reply 放在事件顶层(标量或带 message_id 的对象)。
|
|
163
|
+
*/
|
|
164
|
+
export function extractQuoteId(ev) {
|
|
165
|
+
if (Array.isArray(ev.message)) {
|
|
166
|
+
for (const seg of ev.message) {
|
|
167
|
+
if (seg?.type === 'reply' && seg.data?.id != null)
|
|
168
|
+
return String(seg.data.id);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
const reply = ev.reply;
|
|
172
|
+
if (typeof reply === 'number' || typeof reply === 'string')
|
|
173
|
+
return String(reply);
|
|
174
|
+
if (reply && typeof reply === 'object' && 'message_id' in reply) {
|
|
175
|
+
const id = reply.message_id;
|
|
176
|
+
if (id != null)
|
|
177
|
+
return String(id);
|
|
178
|
+
}
|
|
179
|
+
return undefined;
|
|
180
|
+
}
|
|
160
181
|
/**
|
|
161
182
|
* canonical MediaRef → OneBot `file` 参数:url 直传、base64 → `base64://`、
|
|
162
183
|
* 本地路径 → `file://`(路径在 OneBot 实现侧解析)、kind=file 不透明引用原样透传。
|
package/lib/ws-endpoint.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { EndpointDeliveryError } from '@zhin.js/im-contract';
|
|
2
|
+
import { toCanonicalSegments } from '@zhin.js/core';
|
|
2
3
|
/**
|
|
3
4
|
* NapCat WS client endpoint — outbound connect to NapCat.
|
|
4
5
|
*/
|
|
@@ -8,7 +9,7 @@ import { formatCompact, getAdapterLogger } from '@zhin.js/logger';
|
|
|
8
9
|
import { createNapCatEndpointManagement } from './endpoint-management.js';
|
|
9
10
|
import { createNapcatSelfProfile } from './self-profile.js';
|
|
10
11
|
import { InboundMessageDeduper, isNapCatBotMentioned, isSelfMessage, normalizeMessage, } from './napcat-inbound.js';
|
|
11
|
-
import { buildSendAction, buildWsConnectOptions, formatInboundContent, formatOutboundSegments, isMessageEvent, napcatInboundConversation, napcatOutboundTarget, senderNickname, senderUserId, } from './protocol.js';
|
|
12
|
+
import { buildSendAction, buildWsConnectOptions, extractQuoteId, formatInboundContent, formatOutboundSegments, isMessageEvent, napcatInboundConversation, napcatOutboundTarget, senderNickname, senderUserId, } from './protocol.js';
|
|
12
13
|
import { receiveNapCatSideEvent } from './side-event-dispatch.js';
|
|
13
14
|
import { createNapCatContentPort } from './onebot-get-msg.js';
|
|
14
15
|
import { NapcatClient } from './client.js';
|
|
@@ -134,12 +135,15 @@ export class NapCatWsEndpoint extends NapcatClient {
|
|
|
134
135
|
const content = formatInboundContent(ev);
|
|
135
136
|
const nickname = senderNickname(ev);
|
|
136
137
|
const mentioned = isNapCatBotMentioned(ev);
|
|
138
|
+
const quoteId = extractQuoteId(ev);
|
|
139
|
+
const segments = Array.isArray(ev.message) ? toCanonicalSegments(ev.message) : undefined;
|
|
137
140
|
void this[endpointSpec].events.message({
|
|
138
141
|
event: nativeEvent,
|
|
139
142
|
...(typeof nativeEvent.time === 'number' ? { platformSentAt: nativeEvent.time * 1000 } : {}),
|
|
140
143
|
conversation,
|
|
141
144
|
message: { conversation, id: msgId },
|
|
142
145
|
content,
|
|
146
|
+
...(segments?.length ? { segments } : {}),
|
|
143
147
|
sender: {
|
|
144
148
|
id: senderUserId(ev),
|
|
145
149
|
name: nickname,
|
|
@@ -147,6 +151,7 @@ export class NapCatWsEndpoint extends NapcatClient {
|
|
|
147
151
|
},
|
|
148
152
|
endpointId: this.#options.config.id,
|
|
149
153
|
...(mentioned ? { mentioned: true } : {}),
|
|
154
|
+
...(quoteId ? { replyTo: { id: quoteId } } : {}),
|
|
150
155
|
metadata: Object.freeze({
|
|
151
156
|
message_type: ev.message_type,
|
|
152
157
|
user_id: ev.user_id != null ? String(ev.user_id) : undefined,
|
|
@@ -178,6 +183,7 @@ export class NapCatWsEndpoint extends NapcatClient {
|
|
|
178
183
|
}
|
|
179
184
|
try {
|
|
180
185
|
ws.close();
|
|
186
|
+
ws.terminate?.();
|
|
181
187
|
}
|
|
182
188
|
catch {
|
|
183
189
|
/* ignore */
|
package/lib/ws-types.d.ts
CHANGED
|
@@ -3,6 +3,7 @@ export interface NapCatWsSocket {
|
|
|
3
3
|
readonly readyState: number;
|
|
4
4
|
send(data: string): void;
|
|
5
5
|
close(code?: number, reason?: string): void;
|
|
6
|
+
terminate?(): void;
|
|
6
7
|
ping?(): void;
|
|
7
8
|
on(event: 'open' | 'message' | 'close' | 'error' | 'pong', listener: (...args: unknown[]) => void): void;
|
|
8
9
|
}
|
package/lib/wss-endpoint.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { EndpointDeliveryError } from '@zhin.js/im-contract';
|
|
2
|
+
import { toCanonicalSegments } from '@zhin.js/core';
|
|
2
3
|
/**
|
|
3
4
|
* NapCat reverse WSS endpoint — accepts inbound WebSocket from NapCat.
|
|
4
5
|
*/
|
|
@@ -7,7 +8,7 @@ import { formatCompact, getAdapterLogger } from '@zhin.js/logger';
|
|
|
7
8
|
import { createNapCatEndpointManagement } from './endpoint-management.js';
|
|
8
9
|
import { createNapcatSelfProfile } from './self-profile.js';
|
|
9
10
|
import { InboundMessageDeduper, isNapCatBotMentioned, isSelfMessage, normalizeMessage, } from './napcat-inbound.js';
|
|
10
|
-
import { buildSendAction, formatInboundContent, formatOutboundSegments, isMessageEvent, napcatInboundConversation, napcatOutboundTarget, senderNickname, senderUserId, } from './protocol.js';
|
|
11
|
+
import { buildSendAction, extractQuoteId, formatInboundContent, formatOutboundSegments, isMessageEvent, napcatInboundConversation, napcatOutboundTarget, senderNickname, senderUserId, } from './protocol.js';
|
|
11
12
|
import { receiveNapCatSideEvent } from './side-event-dispatch.js';
|
|
12
13
|
import { createNapCatContentPort } from './onebot-get-msg.js';
|
|
13
14
|
import { NapcatClient } from './client.js';
|
|
@@ -147,12 +148,15 @@ export class NapCatWssEndpoint extends NapcatClient {
|
|
|
147
148
|
const conversation = { ...napcatInboundConversation(String(this.#options.id), ev), endpoint: this[endpointSpec].identity };
|
|
148
149
|
const nickname = senderNickname(ev);
|
|
149
150
|
const mentioned = isNapCatBotMentioned(ev);
|
|
151
|
+
const quoteId = extractQuoteId(ev);
|
|
152
|
+
const segments = Array.isArray(ev.message) ? toCanonicalSegments(ev.message) : undefined;
|
|
150
153
|
void this[endpointSpec].events.message({
|
|
151
154
|
event: nativeEvent,
|
|
152
155
|
...(typeof nativeEvent.time === 'number' ? { platformSentAt: nativeEvent.time * 1000 } : {}),
|
|
153
156
|
conversation,
|
|
154
157
|
message: { conversation, id: msgId },
|
|
155
158
|
content: formatInboundContent(ev),
|
|
159
|
+
...(segments?.length ? { segments } : {}),
|
|
156
160
|
sender: {
|
|
157
161
|
id: senderUserId(ev),
|
|
158
162
|
name: nickname,
|
|
@@ -160,6 +164,7 @@ export class NapCatWssEndpoint extends NapcatClient {
|
|
|
160
164
|
},
|
|
161
165
|
endpointId: this.#options.config.id,
|
|
162
166
|
...(mentioned ? { mentioned: true } : {}),
|
|
167
|
+
...(quoteId ? { replyTo: { id: quoteId } } : {}),
|
|
163
168
|
metadata: Object.freeze({
|
|
164
169
|
message_type: ev.message_type,
|
|
165
170
|
user_id: ev.user_id != null ? String(ev.user_id) : undefined,
|
|
@@ -197,6 +202,7 @@ export class NapCatWssEndpoint extends NapcatClient {
|
|
|
197
202
|
}
|
|
198
203
|
try {
|
|
199
204
|
socket.close();
|
|
205
|
+
socket.terminate?.();
|
|
200
206
|
}
|
|
201
207
|
catch {
|
|
202
208
|
/* ignore */
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zhin.js/adapter-napcat",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.23",
|
|
4
4
|
"description": "Zhin.js NapCat adapter for Plugin Runtime (WebSocket client, OneBot11 + NapCat extensions)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./lib/index.js",
|
|
@@ -45,20 +45,20 @@
|
|
|
45
45
|
},
|
|
46
46
|
"dependencies": {
|
|
47
47
|
"ws": "^8.21.1",
|
|
48
|
-
"@zhin.js/adapter-onebot-events": "0.0.
|
|
49
|
-
"@zhin.js/adapter": "1.1.
|
|
50
|
-
"@zhin.js/agent-feature": "1.1.
|
|
51
|
-
"@zhin.js/client": "1.1.
|
|
48
|
+
"@zhin.js/adapter-onebot-events": "0.0.2",
|
|
49
|
+
"@zhin.js/adapter": "1.1.29",
|
|
50
|
+
"@zhin.js/agent-feature": "1.1.14",
|
|
51
|
+
"@zhin.js/client": "1.1.25",
|
|
52
52
|
"@zhin.js/console-contract": "1.1.3",
|
|
53
|
-
"@zhin.js/core": "1.1.
|
|
54
|
-
"@zhin.js/feature-kit": "1.1.
|
|
55
|
-
"@zhin.js/host-http": "1.1.
|
|
56
|
-
"@zhin.js/im-contract": "1.1.
|
|
53
|
+
"@zhin.js/core": "1.1.57",
|
|
54
|
+
"@zhin.js/feature-kit": "1.1.13",
|
|
55
|
+
"@zhin.js/host-http": "1.1.15",
|
|
56
|
+
"@zhin.js/im-contract": "1.1.12",
|
|
57
57
|
"@zhin.js/logger": "1.1.2",
|
|
58
|
-
"@zhin.js/page": "1.1.
|
|
59
|
-
"@zhin.js/permission": "1.1.
|
|
60
|
-
"@zhin.js/skill": "1.1.
|
|
61
|
-
"@zhin.js/tool": "1.1.
|
|
58
|
+
"@zhin.js/page": "1.1.15",
|
|
59
|
+
"@zhin.js/permission": "1.1.13",
|
|
60
|
+
"@zhin.js/skill": "1.1.14",
|
|
61
|
+
"@zhin.js/tool": "1.1.16"
|
|
62
62
|
},
|
|
63
63
|
"devDependencies": {
|
|
64
64
|
"@types/node": "^26.1.2",
|
|
@@ -66,15 +66,15 @@
|
|
|
66
66
|
"typescript": "^6.0.3",
|
|
67
67
|
"vitest": "^4.1.10",
|
|
68
68
|
"zod": "^4.4.3",
|
|
69
|
-
"@zhin.js/host-http": "1.1.
|
|
70
|
-
"zhin.js": "1.1.
|
|
69
|
+
"@zhin.js/host-http": "1.1.15",
|
|
70
|
+
"zhin.js": "1.1.22"
|
|
71
71
|
},
|
|
72
72
|
"peerDependencies": {
|
|
73
73
|
"zod": "^4.0.0",
|
|
74
|
-
"@zhin.js/adapter": "^1.1.
|
|
75
|
-
"@zhin.js/command": "^1.1.
|
|
76
|
-
"@zhin.js/core": "^1.1.
|
|
77
|
-
"zhin.js": "^1.1.
|
|
74
|
+
"@zhin.js/adapter": "^1.1.29",
|
|
75
|
+
"@zhin.js/command": "^1.1.15",
|
|
76
|
+
"@zhin.js/core": "^1.1.57",
|
|
77
|
+
"zhin.js": "^1.1.22"
|
|
78
78
|
},
|
|
79
79
|
"peerDependenciesMeta": {
|
|
80
80
|
"@zhin.js/command": {
|
package/src/http-endpoint.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { EndpointDeliveryError } from '@zhin.js/im-contract';
|
|
2
|
+
import { toCanonicalSegments } from '@zhin.js/core';
|
|
2
3
|
/**
|
|
3
4
|
* NapCat HTTP endpoint — POST inbound events + HTTP API outbound.
|
|
4
5
|
*/
|
|
@@ -25,6 +26,7 @@ import {
|
|
|
25
26
|
import {
|
|
26
27
|
buildSendAction,
|
|
27
28
|
callNapCatHttpAction,
|
|
29
|
+
extractQuoteId,
|
|
28
30
|
formatInboundContent,
|
|
29
31
|
formatOutboundSegments,
|
|
30
32
|
isMessageEvent,
|
|
@@ -171,12 +173,15 @@ export class NapCatHttpEndpoint extends NapcatClient implements Endpoint {
|
|
|
171
173
|
const conversation = { ...napcatInboundConversation(String(this.#options.id), ev), endpoint: this[endpointSpec].identity };
|
|
172
174
|
const nickname = senderNickname(ev);
|
|
173
175
|
const mentioned = isNapCatBotMentioned(ev);
|
|
176
|
+
const quoteId = extractQuoteId(ev);
|
|
177
|
+
const segments = Array.isArray(ev.message) ? toCanonicalSegments(ev.message) : undefined;
|
|
174
178
|
void this[endpointSpec].events.message({
|
|
175
179
|
event: nativeEvent,
|
|
176
180
|
...(typeof nativeEvent.time === 'number' ? { platformSentAt: nativeEvent.time * 1000 } : {}),
|
|
177
181
|
conversation,
|
|
178
182
|
message: { conversation, id: msgId },
|
|
179
183
|
content: formatInboundContent(ev),
|
|
184
|
+
...(segments?.length ? { segments } : {}),
|
|
180
185
|
sender: {
|
|
181
186
|
id: senderUserId(ev),
|
|
182
187
|
name: nickname,
|
|
@@ -184,6 +189,7 @@ export class NapCatHttpEndpoint extends NapcatClient implements Endpoint {
|
|
|
184
189
|
},
|
|
185
190
|
endpointId: this.#options.config.id,
|
|
186
191
|
...(mentioned ? { mentioned: true } : {}),
|
|
192
|
+
...(quoteId ? { replyTo: { id: quoteId } } : {}),
|
|
187
193
|
metadata: Object.freeze({
|
|
188
194
|
message_type: ev.message_type,
|
|
189
195
|
user_id: ev.user_id != null ? String(ev.user_id) : undefined,
|
package/src/napcat-inbound.ts
CHANGED
|
@@ -1,27 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* NapCat 入站消息治理:去重、自发过滤、消息归一化
|
|
3
3
|
*/
|
|
4
|
+
import { InboundMessageDeduper, promoteInboundMessagePayload } from 'zhin.js/adapter';
|
|
4
5
|
import type { NapCatMessageEvent, MessageSegment } from './protocol.js';
|
|
5
6
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
export class InboundMessageDeduper {
|
|
9
|
-
private readonly seen = new Map<string, number>();
|
|
10
|
-
|
|
11
|
-
shouldProcess(messageId: string): boolean {
|
|
12
|
-
const now = Date.now();
|
|
13
|
-
for (const [id, t] of this.seen) {
|
|
14
|
-
if (now - t > DEDUPE_TTL_MS) this.seen.delete(id);
|
|
15
|
-
}
|
|
16
|
-
if (this.seen.has(messageId)) return false;
|
|
17
|
-
this.seen.set(messageId, now);
|
|
18
|
-
return true;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
clear(): void {
|
|
22
|
-
this.seen.clear();
|
|
23
|
-
}
|
|
24
|
-
}
|
|
7
|
+
export { InboundMessageDeduper };
|
|
25
8
|
|
|
26
9
|
/** 判断是否为 bot 自身发出的消息 */
|
|
27
10
|
export function isSelfMessage(event: NapCatMessageEvent | {
|
|
@@ -67,8 +50,8 @@ export function isNapCatBotMentioned(ev: {
|
|
|
67
50
|
const uin = String(ev.self_id).trim();
|
|
68
51
|
if (!uin) return false;
|
|
69
52
|
const segments = normalizeMessage(ev.message ?? []);
|
|
70
|
-
|
|
71
|
-
|
|
53
|
+
if (promoteInboundMessagePayload<{ segments: MessageSegment[]; mentioned?: boolean }>({ segments }, uin).mentioned === true) {
|
|
54
|
+
return true;
|
|
72
55
|
}
|
|
73
56
|
const text = segments
|
|
74
57
|
.map((seg) => (seg.type === 'text' ? String(seg.data?.text ?? '') : ''))
|
package/src/onebot-get-msg.ts
CHANGED
|
@@ -82,9 +82,13 @@ export function createNapCatContentPort(
|
|
|
82
82
|
});
|
|
83
83
|
}
|
|
84
84
|
|
|
85
|
+
import { toCanonicalSegments } from '@zhin.js/core';
|
|
86
|
+
import type { PendingRpcRequest, EndpointContentPort, EndpointContentResolveContext } from 'zhin.js/adapter';
|
|
87
|
+
import type { ConversationMessage, ConversationReference, ForwardEntry } from '@zhin.js/im-contract';
|
|
88
|
+
|
|
85
89
|
function canonicalSegments(content: Array<{ type: string; data?: Record<string, unknown> }>) {
|
|
86
|
-
return Object.freeze(content.map((segment) => Object.freeze({
|
|
87
|
-
|
|
90
|
+
return Object.freeze(toCanonicalSegments(content).map((segment) => Object.freeze({
|
|
91
|
+
...segment,
|
|
88
92
|
data: Object.freeze(segment.data ?? {}),
|
|
89
93
|
})));
|
|
90
94
|
}
|
|
@@ -93,5 +97,3 @@ function toMillis(value: unknown): number {
|
|
|
93
97
|
const time = Number(value);
|
|
94
98
|
return Number.isFinite(time) ? (time < 10_000_000_000 ? time * 1000 : time) : Date.now();
|
|
95
99
|
}
|
|
96
|
-
import type { PendingRpcRequest, EndpointContentPort, EndpointContentResolveContext } from 'zhin.js/adapter';
|
|
97
|
-
import type { ConversationMessage, ConversationReference, ForwardEntry } from '@zhin.js/im-contract';
|
package/src/protocol.ts
CHANGED
|
@@ -283,6 +283,25 @@ export function senderNickname(ev: NapCatEvent): string | undefined {
|
|
|
283
283
|
return typeof name === 'string' && name ? name : undefined;
|
|
284
284
|
}
|
|
285
285
|
|
|
286
|
+
/**
|
|
287
|
+
* 回复引用 id:优先取 message 数组中的 {type:'reply', data:{id}} 段;
|
|
288
|
+
* 兼容部分实现把 reply 放在事件顶层(标量或带 message_id 的对象)。
|
|
289
|
+
*/
|
|
290
|
+
export function extractQuoteId(ev: NapCatEvent): string | undefined {
|
|
291
|
+
if (Array.isArray(ev.message)) {
|
|
292
|
+
for (const seg of ev.message) {
|
|
293
|
+
if (seg?.type === 'reply' && seg.data?.id != null) return String(seg.data.id);
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
const reply = ev.reply;
|
|
297
|
+
if (typeof reply === 'number' || typeof reply === 'string') return String(reply);
|
|
298
|
+
if (reply && typeof reply === 'object' && 'message_id' in reply) {
|
|
299
|
+
const id = (reply as { message_id?: unknown }).message_id;
|
|
300
|
+
if (id != null) return String(id);
|
|
301
|
+
}
|
|
302
|
+
return undefined;
|
|
303
|
+
}
|
|
304
|
+
|
|
286
305
|
/**
|
|
287
306
|
* canonical MediaRef → OneBot `file` 参数:url 直传、base64 → `base64://`、
|
|
288
307
|
* 本地路径 → `file://`(路径在 OneBot 实现侧解析)、kind=file 不透明引用原样透传。
|
package/src/ws-endpoint.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { EndpointDeliveryError } from '@zhin.js/im-contract';
|
|
2
|
+
import { toCanonicalSegments } from '@zhin.js/core';
|
|
2
3
|
/**
|
|
3
4
|
* NapCat WS client endpoint — outbound connect to NapCat.
|
|
4
5
|
*/
|
|
@@ -27,6 +28,7 @@ import {
|
|
|
27
28
|
import {
|
|
28
29
|
buildSendAction,
|
|
29
30
|
buildWsConnectOptions,
|
|
31
|
+
extractQuoteId,
|
|
30
32
|
formatInboundContent,
|
|
31
33
|
formatOutboundSegments,
|
|
32
34
|
isMessageEvent,
|
|
@@ -190,12 +192,15 @@ export class NapCatWsEndpoint extends NapcatClient implements Endpoint {
|
|
|
190
192
|
const content = formatInboundContent(ev);
|
|
191
193
|
const nickname = senderNickname(ev);
|
|
192
194
|
const mentioned = isNapCatBotMentioned(ev);
|
|
195
|
+
const quoteId = extractQuoteId(ev);
|
|
196
|
+
const segments = Array.isArray(ev.message) ? toCanonicalSegments(ev.message) : undefined;
|
|
193
197
|
void this[endpointSpec].events.message({
|
|
194
198
|
event: nativeEvent,
|
|
195
199
|
...(typeof nativeEvent.time === 'number' ? { platformSentAt: nativeEvent.time * 1000 } : {}),
|
|
196
200
|
conversation,
|
|
197
201
|
message: { conversation, id: msgId },
|
|
198
202
|
content,
|
|
203
|
+
...(segments?.length ? { segments } : {}),
|
|
199
204
|
sender: {
|
|
200
205
|
id: senderUserId(ev),
|
|
201
206
|
name: nickname,
|
|
@@ -203,6 +208,7 @@ export class NapCatWsEndpoint extends NapcatClient implements Endpoint {
|
|
|
203
208
|
},
|
|
204
209
|
endpointId: this.#options.config.id,
|
|
205
210
|
...(mentioned ? { mentioned: true } : {}),
|
|
211
|
+
...(quoteId ? { replyTo: { id: quoteId } } : {}),
|
|
206
212
|
metadata: Object.freeze({
|
|
207
213
|
message_type: ev.message_type,
|
|
208
214
|
user_id: ev.user_id != null ? String(ev.user_id) : undefined,
|
|
@@ -237,6 +243,7 @@ export class NapCatWsEndpoint extends NapcatClient implements Endpoint {
|
|
|
237
243
|
}
|
|
238
244
|
try {
|
|
239
245
|
ws.close();
|
|
246
|
+
ws.terminate?.();
|
|
240
247
|
} catch {
|
|
241
248
|
/* ignore */
|
|
242
249
|
}
|
package/src/ws-types.ts
CHANGED
|
@@ -3,6 +3,7 @@ export interface NapCatWsSocket {
|
|
|
3
3
|
readonly readyState: number;
|
|
4
4
|
send(data: string): void;
|
|
5
5
|
close(code?: number, reason?: string): void;
|
|
6
|
+
terminate?(): void;
|
|
6
7
|
ping?(): void;
|
|
7
8
|
on(event: 'open' | 'message' | 'close' | 'error' | 'pong', listener: (...args: unknown[]) => void): void;
|
|
8
9
|
}
|
package/src/wss-endpoint.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { EndpointDeliveryError } from '@zhin.js/im-contract';
|
|
2
|
+
import { toCanonicalSegments } from '@zhin.js/core';
|
|
2
3
|
/**
|
|
3
4
|
* NapCat reverse WSS endpoint — accepts inbound WebSocket from NapCat.
|
|
4
5
|
*/
|
|
@@ -25,6 +26,7 @@ import {
|
|
|
25
26
|
} from './napcat-inbound.js';
|
|
26
27
|
import {
|
|
27
28
|
buildSendAction,
|
|
29
|
+
extractQuoteId,
|
|
28
30
|
formatInboundContent,
|
|
29
31
|
formatOutboundSegments,
|
|
30
32
|
isMessageEvent,
|
|
@@ -195,12 +197,15 @@ export class NapCatWssEndpoint extends NapcatClient implements Endpoint {
|
|
|
195
197
|
const conversation = { ...napcatInboundConversation(String(this.#options.id), ev), endpoint: this[endpointSpec].identity };
|
|
196
198
|
const nickname = senderNickname(ev);
|
|
197
199
|
const mentioned = isNapCatBotMentioned(ev);
|
|
200
|
+
const quoteId = extractQuoteId(ev);
|
|
201
|
+
const segments = Array.isArray(ev.message) ? toCanonicalSegments(ev.message) : undefined;
|
|
198
202
|
void this[endpointSpec].events.message({
|
|
199
203
|
event: nativeEvent,
|
|
200
204
|
...(typeof nativeEvent.time === 'number' ? { platformSentAt: nativeEvent.time * 1000 } : {}),
|
|
201
205
|
conversation,
|
|
202
206
|
message: { conversation, id: msgId },
|
|
203
207
|
content: formatInboundContent(ev),
|
|
208
|
+
...(segments?.length ? { segments } : {}),
|
|
204
209
|
sender: {
|
|
205
210
|
id: senderUserId(ev),
|
|
206
211
|
name: nickname,
|
|
@@ -208,6 +213,7 @@ export class NapCatWssEndpoint extends NapcatClient implements Endpoint {
|
|
|
208
213
|
},
|
|
209
214
|
endpointId: this.#options.config.id,
|
|
210
215
|
...(mentioned ? { mentioned: true } : {}),
|
|
216
|
+
...(quoteId ? { replyTo: { id: quoteId } } : {}),
|
|
211
217
|
metadata: Object.freeze({
|
|
212
218
|
message_type: ev.message_type,
|
|
213
219
|
user_id: ev.user_id != null ? String(ev.user_id) : undefined,
|
|
@@ -246,6 +252,7 @@ export class NapCatWssEndpoint extends NapcatClient implements Endpoint {
|
|
|
246
252
|
}
|
|
247
253
|
try {
|
|
248
254
|
socket.close();
|
|
255
|
+
socket.terminate?.();
|
|
249
256
|
} catch {
|
|
250
257
|
/* ignore */
|
|
251
258
|
}
|