@zhin.js/core 1.3.3 → 1.3.4
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/lib/adapter.js +2 -2
- package/lib/built/ai-outbound/parse.js +26 -12
- package/lib/built/component.d.ts +1 -2
- package/lib/built/component.js +1 -1
- package/lib/built/database.d.ts +1 -2
- package/lib/built/database.js +1 -1
- package/lib/built/dispatcher.d.ts +1 -1
- package/lib/built/endpoint-lifecycle-service.js +2 -1
- package/lib/built/endpoint-lifecycle.js +4 -3
- package/lib/built/html-to-text.js +15 -14
- package/lib/built/inbound-pipeline.d.ts +1 -1
- package/lib/built/inbound-pipeline.js +1 -1
- package/lib/built/interactive-segments/action.js +16 -1
- package/lib/built/message-enrich.d.ts +1 -1
- package/lib/built/permit-check.d.ts +1 -1
- package/lib/built/schedule.d.ts +1 -2
- package/lib/built/schedule.js +1 -1
- package/lib/component.js +28 -11
- package/lib/index.d.ts +1 -0
- package/lib/index.js +1 -0
- package/lib/notice.d.ts +15 -58
- package/lib/notice.js +1 -3
- package/lib/plugin.d.ts +2 -4
- package/lib/plugin.js +12 -8
- package/lib/request.d.ts +16 -59
- package/lib/request.js +0 -3
- package/lib/side-event/base.d.ts +45 -0
- package/lib/side-event/base.js +30 -0
- package/lib/side-event/index.d.ts +3 -0
- package/lib/side-event/index.js +3 -0
- package/lib/side-event/normalize.d.ts +40 -0
- package/lib/side-event/normalize.js +117 -0
- package/lib/side-event/types.d.ts +25 -0
- package/lib/side-event/types.js +55 -0
- package/package.json +5 -5
package/lib/adapter.js
CHANGED
|
@@ -194,7 +194,7 @@ export class Adapter extends EventEmitter {
|
|
|
194
194
|
if (!endpoint)
|
|
195
195
|
throw new Error(`Endpoint ${options.endpoint} not found`);
|
|
196
196
|
assertOutbound(endpoint);
|
|
197
|
-
this.logger.
|
|
197
|
+
this.logger.debug(formatCompact({
|
|
198
198
|
send: `${options.type}(${options.id})`,
|
|
199
199
|
endpoint: options.endpoint,
|
|
200
200
|
preview: truncatePreview(segment.raw(options.content)),
|
|
@@ -231,7 +231,7 @@ export class Adapter extends EventEmitter {
|
|
|
231
231
|
content: options.content,
|
|
232
232
|
});
|
|
233
233
|
await editable.$editMessage({ ...options, content: rendered.content });
|
|
234
|
-
this.logger.
|
|
234
|
+
this.logger.debug(formatCompact({
|
|
235
235
|
edit: `${options.type}(${options.id})`,
|
|
236
236
|
endpoint: options.endpoint,
|
|
237
237
|
messageId: options.messageId,
|
|
@@ -21,19 +21,17 @@ export function parseOutboundSegment(raw) {
|
|
|
21
21
|
/** 去掉嵌入 JSON 候选末尾的 markdown 围栏(模型常见 ```json … ``` 误输出)。 */
|
|
22
22
|
export function trimTrailingMarkdownFence(raw) {
|
|
23
23
|
let s = raw.trim();
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
s = trailingFence[1].trim();
|
|
24
|
+
if (s.endsWith('```'))
|
|
25
|
+
s = s.slice(0, -3).trimEnd();
|
|
27
26
|
return s;
|
|
28
27
|
}
|
|
29
28
|
export function unwrapAiOutboundJsonCandidate(raw) {
|
|
30
29
|
const trimmed = raw.trim();
|
|
31
|
-
const fenced = trimmed
|
|
32
|
-
if (fenced)
|
|
33
|
-
return fenced
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
return trimTrailingMarkdownFence(embeddedFence[1].trim());
|
|
30
|
+
const fenced = readMarkdownFence(trimmed);
|
|
31
|
+
if (fenced && fenced.full)
|
|
32
|
+
return fenced.body.trim();
|
|
33
|
+
if (fenced && trimmed.indexOf('{') >= 0) {
|
|
34
|
+
return trimTrailingMarkdownFence(fenced.body.trim());
|
|
37
35
|
}
|
|
38
36
|
return trimTrailingMarkdownFence(trimmed);
|
|
39
37
|
}
|
|
@@ -42,12 +40,12 @@ export function extractEmbeddedAiOutboundJson(plain) {
|
|
|
42
40
|
const trimmed = plain.trim();
|
|
43
41
|
if (!trimmed.includes('{'))
|
|
44
42
|
return null;
|
|
45
|
-
const fenced = trimmed
|
|
43
|
+
const fenced = readMarkdownFence(trimmed);
|
|
46
44
|
if (fenced) {
|
|
47
|
-
const jsonRaw = trimTrailingMarkdownFence(fenced
|
|
45
|
+
const jsonRaw = trimTrailingMarkdownFence(fenced.body.trim());
|
|
48
46
|
const payload = parseAiOutboundJson(jsonRaw);
|
|
49
47
|
if (payload?.mentions?.length && payload.text?.trim()) {
|
|
50
|
-
const prose = trimmed.slice(0, fenced.
|
|
48
|
+
const prose = trimmed.slice(0, fenced.start).trim();
|
|
51
49
|
return { prose, jsonRaw };
|
|
52
50
|
}
|
|
53
51
|
}
|
|
@@ -62,6 +60,22 @@ export function extractEmbeddedAiOutboundJson(plain) {
|
|
|
62
60
|
}
|
|
63
61
|
return null;
|
|
64
62
|
}
|
|
63
|
+
function readMarkdownFence(text) {
|
|
64
|
+
const start = text.indexOf('```');
|
|
65
|
+
if (start < 0)
|
|
66
|
+
return null;
|
|
67
|
+
let bodyStart = start + 3;
|
|
68
|
+
const langEnd = text.indexOf('\n', bodyStart);
|
|
69
|
+
if (langEnd >= 0) {
|
|
70
|
+
const lang = text.slice(bodyStart, langEnd).trim().toLowerCase();
|
|
71
|
+
if (lang === '' || lang === 'json')
|
|
72
|
+
bodyStart = langEnd + 1;
|
|
73
|
+
}
|
|
74
|
+
const end = text.indexOf('```', bodyStart);
|
|
75
|
+
if (end < 0)
|
|
76
|
+
return null;
|
|
77
|
+
return { start, body: text.slice(bodyStart, end), full: start === 0 && text.slice(end + 3).trim() === '' };
|
|
78
|
+
}
|
|
65
79
|
export function parseAiOutboundJson(raw) {
|
|
66
80
|
const candidate = unwrapAiOutboundJsonCandidate(raw);
|
|
67
81
|
if (!candidate.startsWith('{'))
|
package/lib/built/component.d.ts
CHANGED
|
@@ -2,9 +2,8 @@
|
|
|
2
2
|
* ComponentFeature
|
|
3
3
|
* 管理所有插件注册的组件,继承自 Feature 抽象类
|
|
4
4
|
*/
|
|
5
|
-
import { Feature, FeatureJSON } from
|
|
5
|
+
import { Feature, FeatureJSON, type PluginLike } from '@zhin.js/kernel';
|
|
6
6
|
import { Component } from "../component.js";
|
|
7
|
-
import type { PluginLike } from '@zhin.js/kernel';
|
|
8
7
|
/**
|
|
9
8
|
* ComponentContext 扩展方法类型
|
|
10
9
|
*/
|
package/lib/built/component.js
CHANGED
package/lib/built/database.d.ts
CHANGED
|
@@ -4,8 +4,7 @@
|
|
|
4
4
|
*/
|
|
5
5
|
import { Definition, Database } from "@zhin.js/database";
|
|
6
6
|
import { DatabaseConfig, Models } from "../types.js";
|
|
7
|
-
import { Feature, FeatureJSON } from
|
|
8
|
-
import type { PluginLike } from '@zhin.js/kernel';
|
|
7
|
+
import { Feature, FeatureJSON, type PluginLike } from '@zhin.js/kernel';
|
|
9
8
|
/**
|
|
10
9
|
* 模型定义记录
|
|
11
10
|
*/
|
package/lib/built/database.js
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
*/
|
|
5
5
|
import { Registry } from "@zhin.js/database";
|
|
6
6
|
import { formatCompact } from '@zhin.js/logger';
|
|
7
|
-
import { Feature } from
|
|
7
|
+
import { Feature } from '@zhin.js/kernel';
|
|
8
8
|
import { SystemLogDefinition } from "../models/system-log.js";
|
|
9
9
|
import { UserDefinition } from "../models/user.js";
|
|
10
10
|
const databaseAfterStartHooks = [];
|
|
@@ -27,8 +27,8 @@
|
|
|
27
27
|
* 默认路由为 exclusive(命令与 AI 互斥);需双轨时请显式 dualRoute.mode: 'dual'。
|
|
28
28
|
*/
|
|
29
29
|
import { Message } from '../message.js';
|
|
30
|
+
import { type Context } from '../plugin.js';
|
|
30
31
|
import type { MessageMiddleware, RegisteredAdapter, MaybePromise, SendContent, OutboundReplySource, OutboundPolishMiddleware, OutboundReplyStore } from '../types.js';
|
|
31
|
-
import type { Context } from '../plugin.js';
|
|
32
32
|
export declare function getOutboundReplyStore(): OutboundReplyStore | undefined;
|
|
33
33
|
/**
|
|
34
34
|
* 路由判定结果(互斥模式 legacy)
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { formatDisplayPath } from '@zhin.js/logger';
|
|
1
2
|
import { Adapter } from '../adapter.js';
|
|
2
3
|
import { segment } from '../utils.js';
|
|
3
4
|
import { connectEndpointInstance, disconnectEndpointInstance } from './connect-endpoint-instance.js';
|
|
@@ -81,7 +82,7 @@ export class EndpointLifecycleService {
|
|
|
81
82
|
const loader = configService.configs.get(configService.primaryFile);
|
|
82
83
|
const endpoints = readAllEndpoints(this.root);
|
|
83
84
|
await this.persistEndpoints(endpoints);
|
|
84
|
-
const configPath = loader.resolvedPath;
|
|
85
|
+
const configPath = formatDisplayPath(loader.resolvedPath);
|
|
85
86
|
const names = endpoints.map((e) => `${e.context}/${e.name}`).join(', ') || '(无)';
|
|
86
87
|
return {
|
|
87
88
|
message: `✅ 已将 ${endpoints.length} 个 endpoint 写入 \`${configPath}\`:${names}`,
|
|
@@ -26,9 +26,10 @@ export async function emitEndpointLifecycle(plugin, adapter, endpoint, kind, det
|
|
|
26
26
|
$id: `endpoint-lifecycle:${endpoint.$id}:${kind}:${Date.now()}`,
|
|
27
27
|
$adapter: adapter.name,
|
|
28
28
|
$endpoint: endpoint.$id,
|
|
29
|
-
$type: '
|
|
30
|
-
$
|
|
31
|
-
$
|
|
29
|
+
$type: 'notice',
|
|
30
|
+
$scene_id: endpoint.$id,
|
|
31
|
+
$scene_type: 'endpoint',
|
|
32
|
+
$sub_type: kind,
|
|
32
33
|
$timestamp: Date.now(),
|
|
33
34
|
});
|
|
34
35
|
adapter.emit('notice.receive', notice);
|
|
@@ -79,20 +79,21 @@ function stripRemainingTags(html) {
|
|
|
79
79
|
return out;
|
|
80
80
|
}
|
|
81
81
|
function decodeCommonEntities(text) {
|
|
82
|
-
return text
|
|
83
|
-
.
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
82
|
+
return text.replace(/&(#x[0-9a-fA-F]+|#\d+|nbsp|amp|lt|gt|quot|apos);/gi, (entity) => {
|
|
83
|
+
const key = entity.slice(1, -1).toLowerCase();
|
|
84
|
+
if (key === 'nbsp')
|
|
85
|
+
return ' ';
|
|
86
|
+
if (key === 'amp')
|
|
87
|
+
return '&';
|
|
88
|
+
if (key === 'lt')
|
|
89
|
+
return '<';
|
|
90
|
+
if (key === 'gt')
|
|
91
|
+
return '>';
|
|
92
|
+
if (key === 'quot')
|
|
93
|
+
return '"';
|
|
94
|
+
if (key === 'apos')
|
|
95
|
+
return "'";
|
|
96
|
+
const n = key.startsWith('#x') ? parseInt(key.slice(2), 16) : Number(key.slice(1));
|
|
96
97
|
return Number.isFinite(n) && n >= 0 && n <= 0x10ffff ? String.fromCodePoint(n) : '';
|
|
97
98
|
});
|
|
98
99
|
}
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Extracted from Adapter.emit() override to improve testability and decoupling.
|
|
5
5
|
* Handles: event routing, logging, backpressure, pipeline execution, observer emission.
|
|
6
6
|
*/
|
|
7
|
-
import type
|
|
7
|
+
import { type Logger } from '@zhin.js/logger';
|
|
8
8
|
import { type Message as MessageType } from '../message.js';
|
|
9
9
|
import type { Plugin } from '../plugin.js';
|
|
10
10
|
export interface InboundPipelineOptions {
|
|
@@ -54,7 +54,7 @@ export class InboundMessagePipeline {
|
|
|
54
54
|
return result;
|
|
55
55
|
}
|
|
56
56
|
logIncoming(message) {
|
|
57
|
-
this.options.logger.
|
|
57
|
+
this.options.logger.debug(formatCompact({
|
|
58
58
|
recv: `${message.$channel.type}(${message.$channel.id})`,
|
|
59
59
|
endpoint: message.$endpoint,
|
|
60
60
|
preview: truncatePreview(segment.raw(message.$content)),
|
|
@@ -19,11 +19,26 @@ const PAYLOAD_PATTERN = /^[a-z0-9_]+:[^:\s]+:[a-z0-9_-]+$/i;
|
|
|
19
19
|
/** 去掉 @bot、XML at 段、首尾空白(QQ 指令预填文本归一化) */
|
|
20
20
|
export function stripInteractiveCommandText(raw) {
|
|
21
21
|
let text = raw.trim();
|
|
22
|
-
text = text
|
|
22
|
+
text = stripXmlAtSegments(text);
|
|
23
23
|
text = text.replace(/^@\S+\s+/u, '');
|
|
24
24
|
text = text.replace(/\s+/g, ' ').trim();
|
|
25
25
|
return text;
|
|
26
26
|
}
|
|
27
|
+
function stripXmlAtSegments(text) {
|
|
28
|
+
let out = '';
|
|
29
|
+
let cursor = 0;
|
|
30
|
+
while (cursor < text.length) {
|
|
31
|
+
const start = text.toLowerCase().indexOf('<at ', cursor);
|
|
32
|
+
if (start < 0)
|
|
33
|
+
break;
|
|
34
|
+
const end = text.indexOf('/>', start + 4);
|
|
35
|
+
if (end < 0)
|
|
36
|
+
break;
|
|
37
|
+
out += text.slice(cursor, start) + ' ';
|
|
38
|
+
cursor = end + 2;
|
|
39
|
+
}
|
|
40
|
+
return out + text.slice(cursor);
|
|
41
|
+
}
|
|
27
42
|
/** 从文本降级输入解析 payload(需在 active session 的 fallback.map 中查找) */
|
|
28
43
|
export function resolveTextFallbackPayload(raw, map) {
|
|
29
44
|
const trimmed = raw.trim();
|
|
@@ -5,7 +5,7 @@ import type { Plugin } from '../plugin.js';
|
|
|
5
5
|
import { Message, type MessageChannel } from '../message.js';
|
|
6
6
|
import type { Adapters } from '../adapter.js';
|
|
7
7
|
import type { SendContent } from '../types.js';
|
|
8
|
-
import type
|
|
8
|
+
import { type SenderRole } from './roles.js';
|
|
9
9
|
/** Agent turn 可挂载在 Message 扩展字段上的元数据 */
|
|
10
10
|
export type AgentTurnMessage = Message<{
|
|
11
11
|
extra?: Record<string, unknown>;
|
|
@@ -2,6 +2,6 @@
|
|
|
2
2
|
* 内置 permit 同步校验(adapter/group/private/channel/user/role)
|
|
3
3
|
*/
|
|
4
4
|
import type { Message } from '../message.js';
|
|
5
|
-
import type
|
|
5
|
+
import { type SenderRole } from './roles.js';
|
|
6
6
|
export declare function checkBuiltinPermit(name: string, message: Message<any>, roles: readonly SenderRole[]): boolean;
|
|
7
7
|
export declare function checkBuiltinPermitList(permits: readonly string[], message: Message<any>, roles: readonly SenderRole[]): boolean;
|
package/lib/built/schedule.d.ts
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* ScheduleFeature — 管理插件注册的内存定时任务
|
|
3
3
|
*/
|
|
4
|
-
import { Feature, FeatureJSON, type ScheduleKind } from '@zhin.js/kernel';
|
|
5
|
-
import type { HolidayInput, ScatterInput } from '@zhin.js/kernel';
|
|
4
|
+
import { Feature, FeatureJSON, type ScheduleKind, type HolidayInput, type ScatterInput } from '@zhin.js/kernel';
|
|
6
5
|
export interface ScheduleDescriptor {
|
|
7
6
|
kind: ScheduleKind | 'every' | 'at';
|
|
8
7
|
/** 6 段 cron(solar/lunar/workday/freeDay/holiday) */
|
package/lib/built/schedule.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* ScheduleFeature — 管理插件注册的内存定时任务
|
|
3
3
|
*/
|
|
4
|
-
import { Feature, ScheduleEngine, getScheduleEngine, setScheduleEngine
|
|
4
|
+
import { Feature, ScheduleEngine, getScheduleEngine, setScheduleEngine } from '@zhin.js/kernel';
|
|
5
5
|
function ensureEngine() {
|
|
6
6
|
let engine = getScheduleEngine();
|
|
7
7
|
if (!engine) {
|
package/lib/component.js
CHANGED
|
@@ -176,24 +176,41 @@ function parseAttributeValue(str, startIndex, context) {
|
|
|
176
176
|
function evaluateQuotedExpression(quotedValue, context) {
|
|
177
177
|
if (!context)
|
|
178
178
|
return quotedValue;
|
|
179
|
-
|
|
180
|
-
let
|
|
181
|
-
const
|
|
182
|
-
|
|
183
|
-
while ((match = expressionRegex.exec(quotedValue)) !== null) {
|
|
184
|
-
const expr = match[1];
|
|
179
|
+
let result = '';
|
|
180
|
+
let cursor = 0;
|
|
181
|
+
for (const match of readBraceExpressions(quotedValue)) {
|
|
182
|
+
result += quotedValue.slice(cursor, match.start);
|
|
185
183
|
try {
|
|
186
|
-
const value = context.getValue(expr);
|
|
187
|
-
if (value !== undefined && value !== expr) {
|
|
184
|
+
const value = context.getValue(match.expr);
|
|
185
|
+
if (value !== undefined && value !== match.expr) {
|
|
188
186
|
const stringValue = typeof value === 'string' ? value : JSON.stringify(value);
|
|
189
|
-
result
|
|
187
|
+
result += stringValue;
|
|
188
|
+
}
|
|
189
|
+
else {
|
|
190
|
+
result += quotedValue.slice(match.start, match.end);
|
|
190
191
|
}
|
|
191
192
|
}
|
|
192
193
|
catch (error) {
|
|
193
|
-
|
|
194
|
+
result += quotedValue.slice(match.start, match.end);
|
|
194
195
|
}
|
|
196
|
+
cursor = match.end;
|
|
195
197
|
}
|
|
196
|
-
return result;
|
|
198
|
+
return result + quotedValue.slice(cursor);
|
|
199
|
+
}
|
|
200
|
+
function readBraceExpressions(value) {
|
|
201
|
+
const matches = [];
|
|
202
|
+
let cursor = 0;
|
|
203
|
+
while (cursor < value.length) {
|
|
204
|
+
const start = value.indexOf('{', cursor);
|
|
205
|
+
if (start < 0)
|
|
206
|
+
break;
|
|
207
|
+
const end = value.indexOf('}', start + 1);
|
|
208
|
+
if (end < 0)
|
|
209
|
+
break;
|
|
210
|
+
matches.push({ start, end: end + 1, expr: value.slice(start + 1, end) });
|
|
211
|
+
cursor = end + 1;
|
|
212
|
+
}
|
|
213
|
+
return matches;
|
|
197
214
|
}
|
|
198
215
|
/**
|
|
199
216
|
* 解析表达式值
|
package/lib/index.d.ts
CHANGED
|
@@ -8,6 +8,7 @@ export * from './message.js';
|
|
|
8
8
|
export * from './im-scene.js';
|
|
9
9
|
export * from './notice.js';
|
|
10
10
|
export * from './request.js';
|
|
11
|
+
export * from './side-event/index.js';
|
|
11
12
|
export * from './prompt.js';
|
|
12
13
|
export * from './types.js';
|
|
13
14
|
export * from './agent-prompt.js';
|
package/lib/index.js
CHANGED
|
@@ -9,6 +9,7 @@ export * from './message.js';
|
|
|
9
9
|
export * from './im-scene.js';
|
|
10
10
|
export * from './notice.js';
|
|
11
11
|
export * from './request.js';
|
|
12
|
+
export * from './side-event/index.js';
|
|
12
13
|
export * from './prompt.js';
|
|
13
14
|
export * from './types.js';
|
|
14
15
|
export * from './agent-prompt.js';
|
package/lib/notice.d.ts
CHANGED
|
@@ -1,81 +1,38 @@
|
|
|
1
|
-
import { Adapters } from './adapter.js';
|
|
2
|
-
import type {
|
|
1
|
+
import type { Adapters } from './adapter.js';
|
|
2
|
+
import type { NoticeKind } from './side-event/types.js';
|
|
3
|
+
import type { SideEventBase } from './side-event/base.js';
|
|
4
|
+
export type { NoticeKind, ComposedNoticeName, NoticeType } from './side-event/types.js';
|
|
5
|
+
export type { SideEventBase } from './side-event/base.js';
|
|
6
|
+
export { composeSideEventName, formatSideEventName, matchesSideEventName, parseSideEventName, sideEventSendChannel, } from './side-event/base.js';
|
|
3
7
|
/**
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* 常见 IM 通知事件分类:
|
|
7
|
-
* - group_member_increase: 群成员增加(入群)
|
|
8
|
-
* - group_member_decrease: 群成员减少(退群/被踢)
|
|
9
|
-
* - group_admin_change: 群管理员变动
|
|
10
|
-
* - group_ban: 群禁言
|
|
11
|
-
* - group_recall: 群消息撤回
|
|
12
|
-
* - friend_recall: 好友消息撤回
|
|
13
|
-
* - friend_add: 新增好友
|
|
14
|
-
* - group_poke: 群戳一戳
|
|
15
|
-
* - friend_poke: 好友戳一戳
|
|
16
|
-
* - group_transfer: 群转让
|
|
17
|
-
* - endpoint.lifecycle: Endpoint 连接/断开/错误($subType: connect | disconnect | error)
|
|
18
|
-
*
|
|
19
|
-
* 适配器可自行扩展更多子类型
|
|
20
|
-
*/
|
|
21
|
-
export type NoticeType = 'group_member_increase' | 'group_member_decrease' | 'group_admin_change' | 'group_ban' | 'group_recall' | 'friend_recall' | 'friend_add' | 'group_poke' | 'friend_poke' | 'group_transfer' | (string & {});
|
|
22
|
-
/**
|
|
23
|
-
* 通知频道信息
|
|
24
|
-
*/
|
|
25
|
-
export interface NoticeChannel {
|
|
26
|
-
id: string;
|
|
27
|
-
type: 'group' | 'private' | 'channel';
|
|
28
|
-
}
|
|
29
|
-
/**
|
|
30
|
-
* 通知基础结构
|
|
31
|
-
*
|
|
32
|
-
* 与 MessageBase 同构设计,所有通知共享以下字段。
|
|
33
|
-
* 适配器通过 `Notice.from(raw, base)` 合并平台原始数据和标准字段。
|
|
8
|
+
* 通知基础结构 — 继承 SideEventBase。
|
|
34
9
|
*
|
|
35
10
|
* @example
|
|
36
11
|
* ```typescript
|
|
37
|
-
* // 适配器中格式化通知
|
|
38
12
|
* const notice = Notice.from(rawEvent, {
|
|
39
13
|
* $id: rawEvent.id,
|
|
40
14
|
* $adapter: 'icqq',
|
|
41
15
|
* $endpoint: endpointId,
|
|
42
|
-
* $type: '
|
|
43
|
-
* $
|
|
44
|
-
* $
|
|
45
|
-
* $
|
|
16
|
+
* $type: 'notice',
|
|
17
|
+
* $scene_id: groupId,
|
|
18
|
+
* $scene_type: 'group',
|
|
19
|
+
* $sub_type: 'member_decrease',
|
|
20
|
+
* $actor: { id: operatorId, name: '管理员' },
|
|
46
21
|
* $target: { id: userId, name: '被踢者' },
|
|
47
22
|
* $timestamp: Date.now(),
|
|
48
23
|
* });
|
|
24
|
+
* // formatSideEventName(notice) === 'notice.group.member_decrease'
|
|
49
25
|
* this.adapter.emit('notice.receive', notice);
|
|
50
26
|
* ```
|
|
51
27
|
*/
|
|
52
|
-
export interface NoticeBase {
|
|
53
|
-
/** 通知唯一 ID(平台提供或自生成) */
|
|
54
|
-
$id: string;
|
|
55
|
-
/** 适配器名称 */
|
|
28
|
+
export interface NoticeBase extends SideEventBase {
|
|
56
29
|
$adapter: keyof Adapters;
|
|
57
|
-
|
|
58
|
-
$endpoint: string;
|
|
59
|
-
/** 通知类型 */
|
|
60
|
-
$type: NoticeType;
|
|
61
|
-
/** 通知子类型(如 leave/kick、set/unset) */
|
|
62
|
-
$subType?: string;
|
|
63
|
-
/** 通知发生的频道/群/会话 */
|
|
64
|
-
$channel: NoticeChannel;
|
|
65
|
-
/** 操作者信息(如管理员) */
|
|
66
|
-
$operator?: MessageSender;
|
|
67
|
-
/** 被操作目标信息(如被踢的成员) */
|
|
68
|
-
$target?: MessageSender;
|
|
69
|
-
/** 通知时间戳 */
|
|
70
|
-
$timestamp: number;
|
|
30
|
+
$type: NoticeKind;
|
|
71
31
|
}
|
|
72
32
|
/**
|
|
73
33
|
* 完整通知类型,支持平台原始数据扩展
|
|
74
34
|
*/
|
|
75
35
|
export type Notice<T extends object = {}> = NoticeBase & T;
|
|
76
36
|
export declare namespace Notice {
|
|
77
|
-
/**
|
|
78
|
-
* 工具方法:合并自定义字段与基础通知结构
|
|
79
|
-
*/
|
|
80
37
|
function from<T extends object>(input: T, format: NoticeBase): Notice<T>;
|
|
81
38
|
}
|
package/lib/notice.js
CHANGED
|
@@ -1,8 +1,6 @@
|
|
|
1
|
+
export { composeSideEventName, formatSideEventName, matchesSideEventName, parseSideEventName, sideEventSendChannel, } from './side-event/base.js';
|
|
1
2
|
export var Notice;
|
|
2
3
|
(function (Notice) {
|
|
3
|
-
/**
|
|
4
|
-
* 工具方法:合并自定义字段与基础通知结构
|
|
5
|
-
*/
|
|
6
4
|
function from(input, format) {
|
|
7
5
|
return Object.assign(input, format);
|
|
8
6
|
}
|
package/lib/plugin.d.ts
CHANGED
|
@@ -6,12 +6,10 @@
|
|
|
6
6
|
* - 生命周期事件类型安全(Plugin.Lifecycle)
|
|
7
7
|
* - 中间件系统、适配器管理、useContext 等
|
|
8
8
|
*/
|
|
9
|
-
import type
|
|
9
|
+
import { MessageMiddleware, RegisteredAdapter, MaybePromise, ArrayItem, SendOptions, MessageSendPayload, type PluginManifest } from './types.js';
|
|
10
10
|
import type { InteractiveHandler } from "./built/interactive-segments/types.js";
|
|
11
|
-
import { MessageMiddleware, RegisteredAdapter, MaybePromise, ArrayItem, SendOptions, MessageSendPayload } from "./types.js";
|
|
12
11
|
import { Adapter, Adapters } from "./adapter.js";
|
|
13
|
-
import { PluginBase, PluginBaseLifecycle } from
|
|
14
|
-
import type { PluginLike } from "@zhin.js/kernel";
|
|
12
|
+
import { PluginBase, PluginBaseLifecycle, type PluginLike } from '@zhin.js/kernel';
|
|
15
13
|
export { getPlugin } from "./plugin-context.js";
|
|
16
14
|
export { storage, getCurrentFile } from "./plugin-context.js";
|
|
17
15
|
export { setHostRootPlugin, getHostRootPlugin } from "./host-plugin-registry.js";
|
package/lib/plugin.js
CHANGED
|
@@ -13,8 +13,7 @@ import { formatCompact } from "@zhin.js/logger";
|
|
|
13
13
|
import { compose, remove, resolveEntry } from "./utils.js";
|
|
14
14
|
import { ensureInteractiveMiddleware, registerInteractiveHandler as registerInteractiveHandlerCore, } from "./built/interactive-segments/handlers.js";
|
|
15
15
|
import { Adapter } from "./adapter.js";
|
|
16
|
-
import { Feature } from
|
|
17
|
-
import { PluginBase, resolvePluginResolveDir as _resolvePluginResolveDir, pluginCreateRequire as _pluginCreateRequire, getFileHash, watchFile, registerExtension, unregisterExtensions, installExtensionProxy, } from "@zhin.js/kernel";
|
|
16
|
+
import { Feature, PluginBase, resolvePluginResolveDir as _resolvePluginResolveDir, pluginCreateRequire as _pluginCreateRequire, getFileHash, watchFile, registerExtension, unregisterExtensions, installExtensionProxy } from '@zhin.js/kernel';
|
|
18
17
|
import { storage, getCurrentFile } from "./plugin-context.js";
|
|
19
18
|
// Re-export getPlugin from plugin-context for backward compatibility
|
|
20
19
|
export { getPlugin } from "./plugin-context.js";
|
|
@@ -121,15 +120,20 @@ export class Plugin extends PluginBase {
|
|
|
121
120
|
get name() {
|
|
122
121
|
if (this.#cachedName)
|
|
123
122
|
return this.#cachedName;
|
|
124
|
-
|
|
123
|
+
let name = path
|
|
125
124
|
.relative(process.cwd(), this.filePath)
|
|
126
125
|
.replace(/\?t=\d+$/, "")
|
|
127
126
|
.replace(/\\/g, "/")
|
|
128
127
|
.replace(/\/index\.(js|ts)x?$/, "")
|
|
129
|
-
.replace(/\/(lib|src|dist)$/, "")
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
.
|
|
128
|
+
.replace(/\/(lib|src|dist)$/, "");
|
|
129
|
+
const nodeModulesIndex = name.indexOf("node_modules/");
|
|
130
|
+
if (nodeModulesIndex !== -1)
|
|
131
|
+
name = name.slice(nodeModulesIndex + "node_modules/".length);
|
|
132
|
+
const slash = name.lastIndexOf("/");
|
|
133
|
+
if (slash !== -1)
|
|
134
|
+
name = name.slice(slash + 1);
|
|
135
|
+
name = name.replace(/\.(js|ts)x?$/, "");
|
|
136
|
+
this.#cachedName = name;
|
|
133
137
|
return this.#cachedName;
|
|
134
138
|
}
|
|
135
139
|
/**
|
|
@@ -426,7 +430,7 @@ export class Plugin extends PluginBase {
|
|
|
426
430
|
installExtensionProxy(Plugin.prototype);
|
|
427
431
|
for (const [name, fn] of Object.entries(context.extensions)) {
|
|
428
432
|
if (typeof fn === 'function') {
|
|
429
|
-
registerExtension(name, fn);
|
|
433
|
+
registerExtension(name, fn);
|
|
430
434
|
}
|
|
431
435
|
}
|
|
432
436
|
}
|
package/lib/request.d.ts
CHANGED
|
@@ -1,84 +1,41 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import type {
|
|
1
|
+
import type { MaybePromise } from './types.js';
|
|
2
|
+
import type { Adapters } from './adapter.js';
|
|
3
|
+
import type { RequestKind } from './side-event/types.js';
|
|
4
|
+
import type { SideEventBase } from './side-event/base.js';
|
|
5
|
+
export type { RequestKind, ComposedRequestName, RequestType } from './side-event/types.js';
|
|
3
6
|
/**
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* 常见 IM 请求事件分类:
|
|
7
|
-
* - friend_add: 好友添加请求
|
|
8
|
-
* - group_add: 主动申请入群
|
|
9
|
-
* - group_invite: 邀请入群请求
|
|
10
|
-
*
|
|
11
|
-
* 适配器可自行扩展更多子类型
|
|
12
|
-
*/
|
|
13
|
-
export type RequestType = 'friend_add' | 'group_add' | 'group_invite' | (string & {});
|
|
14
|
-
/**
|
|
15
|
-
* 请求频道信息
|
|
16
|
-
*/
|
|
17
|
-
export interface RequestChannel {
|
|
18
|
-
id: string;
|
|
19
|
-
type: 'group' | 'private' | 'channel';
|
|
20
|
-
}
|
|
21
|
-
/**
|
|
22
|
-
* 请求基础结构
|
|
23
|
-
*
|
|
24
|
-
* 与 MessageBase / NoticeBase 同构设计。
|
|
25
|
-
* 核心区别:Request 提供 `$approve()` 和 `$reject()` 方法,用于快速处理请求。
|
|
7
|
+
* 请求基础结构 — 继承 SideEventBase,附加审批方法。
|
|
26
8
|
*
|
|
27
9
|
* @example
|
|
28
10
|
* ```typescript
|
|
29
|
-
* // 适配器中格式化请求
|
|
30
11
|
* const request = Request.from(rawEvent, {
|
|
31
12
|
* $id: rawEvent.flag,
|
|
32
13
|
* $adapter: 'icqq',
|
|
33
14
|
* $endpoint: endpointId,
|
|
34
|
-
* $type: '
|
|
35
|
-
* $
|
|
36
|
-
* $
|
|
15
|
+
* $type: 'request',
|
|
16
|
+
* $scene_id: groupId,
|
|
17
|
+
* $scene_type: 'group',
|
|
18
|
+
* $sub_type: 'invite',
|
|
19
|
+
* $actor: { id: userId, name: '邀请者' },
|
|
37
20
|
* $comment: '请求加群消息',
|
|
38
21
|
* $timestamp: Date.now(),
|
|
39
22
|
* $approve: async (remark?) => { await api.approve(flag, remark); },
|
|
40
23
|
* $reject: async (reason?) => { await api.reject(flag, reason); },
|
|
41
24
|
* });
|
|
25
|
+
* // formatSideEventName(request) === 'request.group.invite'
|
|
42
26
|
* this.adapter.emit('request.receive', request);
|
|
43
27
|
* ```
|
|
44
28
|
*/
|
|
45
|
-
export interface RequestBase {
|
|
46
|
-
/** 请求唯一 ID / flag(平台提供的请求标识,用于后续处理) */
|
|
47
|
-
$id: string;
|
|
48
|
-
/** 适配器名称 */
|
|
29
|
+
export interface RequestBase extends SideEventBase {
|
|
49
30
|
$adapter: keyof Adapters;
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
$type: RequestType;
|
|
54
|
-
/** 请求子类型 */
|
|
55
|
-
$subType?: string;
|
|
56
|
-
/** 请求发生的频道/群/会话 */
|
|
57
|
-
$channel: RequestChannel;
|
|
58
|
-
/** 请求发送者 */
|
|
59
|
-
$sender: MessageSender;
|
|
60
|
-
/** 请求附言/验证消息 */
|
|
31
|
+
$type: RequestKind;
|
|
32
|
+
/** 主参与者(必填) */
|
|
33
|
+
$actor: NonNullable<SideEventBase['$actor']>;
|
|
61
34
|
$comment?: string;
|
|
62
|
-
/** 请求时间戳 */
|
|
63
|
-
$timestamp: number;
|
|
64
|
-
/**
|
|
65
|
-
* 同意请求
|
|
66
|
-
* @param remark 备注信息(如好友备注)
|
|
67
|
-
*/
|
|
68
35
|
$approve(remark?: string): MaybePromise<void>;
|
|
69
|
-
/**
|
|
70
|
-
* 拒绝请求
|
|
71
|
-
* @param reason 拒绝原因
|
|
72
|
-
*/
|
|
73
36
|
$reject(reason?: string): MaybePromise<void>;
|
|
74
37
|
}
|
|
75
|
-
/**
|
|
76
|
-
* 完整请求类型,支持平台原始数据扩展
|
|
77
|
-
*/
|
|
78
38
|
export type Request<T extends object = {}> = RequestBase & T;
|
|
79
39
|
export declare namespace Request {
|
|
80
|
-
/**
|
|
81
|
-
* 工具方法:合并自定义字段与基础请求结构
|
|
82
|
-
*/
|
|
83
40
|
function from<T extends object>(input: T, format: RequestBase): Request<T>;
|
|
84
41
|
}
|
package/lib/request.js
CHANGED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import type { Adapters } from '../adapter.js';
|
|
2
|
+
import type { MessageSender } from '../types.js';
|
|
3
|
+
/**
|
|
4
|
+
* Side Event 共享基础结构(Notice 与 Request 共用)。
|
|
5
|
+
* 字段统一为 `$foo_bar` 命名。
|
|
6
|
+
*
|
|
7
|
+
* 完整事件名:`${$type}.${$scene_type}.${$sub_type}`(见 `formatSideEventName`)。
|
|
8
|
+
*/
|
|
9
|
+
export interface SideEventBase {
|
|
10
|
+
/** 平台侧去重键 / flag(非 DB 行 id) */
|
|
11
|
+
$id: string;
|
|
12
|
+
$adapter: keyof Adapters;
|
|
13
|
+
$endpoint: string;
|
|
14
|
+
/** 命名空间:`notice` 或 `request` */
|
|
15
|
+
$type: string;
|
|
16
|
+
/** 平台场景 ID(群号、用户号、频道 ID 等) */
|
|
17
|
+
$scene_id: string;
|
|
18
|
+
/** 场景域(如 group、friend、channel、endpoint) */
|
|
19
|
+
$scene_type?: string;
|
|
20
|
+
/** 动作子类型(如 member_increase、add、poke) */
|
|
21
|
+
$sub_type?: string;
|
|
22
|
+
/** 主参与者(原 Notice.$operator / Request.$sender) */
|
|
23
|
+
$actor?: MessageSender;
|
|
24
|
+
/** 被操作对象 */
|
|
25
|
+
$target?: MessageSender;
|
|
26
|
+
/** 毫秒时间戳 */
|
|
27
|
+
$timestamp: number;
|
|
28
|
+
}
|
|
29
|
+
/** 组合 Side Event 完整类型名 */
|
|
30
|
+
export declare function composeSideEventName(type: string, sceneType: string | undefined, subType: string | undefined): string;
|
|
31
|
+
/** 从 Side Event 字段生成完整类型名 */
|
|
32
|
+
export declare function formatSideEventName(event: Pick<SideEventBase, '$type' | '$scene_type' | '$sub_type'>): string;
|
|
33
|
+
/** 判断 Side Event 是否匹配完整类型名(如 `notice.group.member_increase`) */
|
|
34
|
+
export declare function matchesSideEventName(event: Pick<SideEventBase, '$type' | '$scene_type' | '$sub_type'>, fullName: string): boolean;
|
|
35
|
+
/** 解析完整类型名为三段 */
|
|
36
|
+
export declare function parseSideEventName(fullName: string): {
|
|
37
|
+
type: string;
|
|
38
|
+
scene_type?: string;
|
|
39
|
+
sub_type?: string;
|
|
40
|
+
};
|
|
41
|
+
/** 将 Side Event 场景映射为 IM 发送通道(group-suite 等消费者用) */
|
|
42
|
+
export declare function sideEventSendChannel(event: Pick<SideEventBase, '$scene_type' | '$scene_id'>): {
|
|
43
|
+
id: string;
|
|
44
|
+
type: 'group' | 'private' | 'channel';
|
|
45
|
+
};
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/** 组合 Side Event 完整类型名 */
|
|
2
|
+
export function composeSideEventName(type, sceneType, subType) {
|
|
3
|
+
return [type, sceneType, subType].filter((p) => !!p).join('.');
|
|
4
|
+
}
|
|
5
|
+
/** 从 Side Event 字段生成完整类型名 */
|
|
6
|
+
export function formatSideEventName(event) {
|
|
7
|
+
return composeSideEventName(event.$type, event.$scene_type, event.$sub_type);
|
|
8
|
+
}
|
|
9
|
+
/** 判断 Side Event 是否匹配完整类型名(如 `notice.group.member_increase`) */
|
|
10
|
+
export function matchesSideEventName(event, fullName) {
|
|
11
|
+
return formatSideEventName(event) === fullName;
|
|
12
|
+
}
|
|
13
|
+
/** 解析完整类型名为三段 */
|
|
14
|
+
export function parseSideEventName(fullName) {
|
|
15
|
+
const parts = fullName.split('.');
|
|
16
|
+
return {
|
|
17
|
+
type: parts[0] ?? '',
|
|
18
|
+
scene_type: parts[1],
|
|
19
|
+
sub_type: parts.slice(2).join('.') || undefined,
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
/** 将 Side Event 场景映射为 IM 发送通道(group-suite 等消费者用) */
|
|
23
|
+
export function sideEventSendChannel(event) {
|
|
24
|
+
const domain = event.$scene_type ?? 'private';
|
|
25
|
+
if (domain === 'group')
|
|
26
|
+
return { id: event.$scene_id, type: 'group' };
|
|
27
|
+
if (domain === 'channel')
|
|
28
|
+
return { id: event.$scene_id, type: 'channel' };
|
|
29
|
+
return { id: event.$scene_id, type: 'private' };
|
|
30
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { Notice, type NoticeBase } from '../notice.js';
|
|
2
|
+
import { Request, type RequestBase } from '../request.js';
|
|
3
|
+
import { type ComposedNoticeName, type ComposedRequestName, type SideEventParts } from './types.js';
|
|
4
|
+
export type SideEventPlatform = 'onebot' | 'icqq' | 'napcat' | 'kook' | (string & {});
|
|
5
|
+
export interface OneBotLikeRawEvent {
|
|
6
|
+
notice_type?: string;
|
|
7
|
+
request_type?: string;
|
|
8
|
+
sub_type?: string;
|
|
9
|
+
group_id?: number | string;
|
|
10
|
+
user_id?: number | string;
|
|
11
|
+
operator_id?: number | string;
|
|
12
|
+
flag?: string;
|
|
13
|
+
time?: number;
|
|
14
|
+
self_id?: number | string;
|
|
15
|
+
[key: string]: unknown;
|
|
16
|
+
}
|
|
17
|
+
/** 去重用:notice / request 无稳定 id 时组合键 */
|
|
18
|
+
export declare function resolveSideEventDedupeKey(data: OneBotLikeRawEvent, kind: 'notice' | 'request'): string;
|
|
19
|
+
/**
|
|
20
|
+
* 将平台原始 notice_type 映射为标准 `$scene_type` + `$sub_type`。
|
|
21
|
+
*/
|
|
22
|
+
export declare function mapNoticeParts(platform: SideEventPlatform, raw: string, options?: {
|
|
23
|
+
sub_type?: string;
|
|
24
|
+
is_group?: boolean;
|
|
25
|
+
notify_handler?: (subType: string | undefined, isGroup: boolean) => SideEventParts;
|
|
26
|
+
}): SideEventParts;
|
|
27
|
+
/**
|
|
28
|
+
* 将平台原始 request_type 映射为标准 `$scene_type` + `$sub_type`。
|
|
29
|
+
*/
|
|
30
|
+
export declare function mapRequestParts(_platform: SideEventPlatform, raw: string, subType?: string): SideEventParts;
|
|
31
|
+
/** 组合完整 Notice 类型名(兼容旧 API) */
|
|
32
|
+
export declare function mapNoticeType(platform: SideEventPlatform, raw: string, options?: Parameters<typeof mapNoticeParts>[2]): ComposedNoticeName;
|
|
33
|
+
/** 组合完整 Request 类型名(兼容旧 API) */
|
|
34
|
+
export declare function mapRequestType(platform: SideEventPlatform, raw: string, subType?: string): ComposedRequestName;
|
|
35
|
+
export declare function senderFromId(id: unknown, name?: string): {
|
|
36
|
+
id: string;
|
|
37
|
+
name: string;
|
|
38
|
+
} | undefined;
|
|
39
|
+
export declare function buildNotice<T extends object>(input: T, format: NoticeBase): Notice<T>;
|
|
40
|
+
export declare function buildRequest<T extends object>(input: T, format: RequestBase): Request<T>;
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { Notice } from '../notice.js';
|
|
2
|
+
import { Request } from '../request.js';
|
|
3
|
+
import { composeSideEventName, parseSideEventName, } from './base.js';
|
|
4
|
+
import { KOOK_NOTICE_PARTS_MAP, ONEBOT_NOTICE_PARTS_MAP, SLACK_NOTICE_PARTS_MAP } from './types.js';
|
|
5
|
+
/** 去重用:notice / request 无稳定 id 时组合键 */
|
|
6
|
+
export function resolveSideEventDedupeKey(data, kind) {
|
|
7
|
+
if (data.flag != null && String(data.flag) !== '') {
|
|
8
|
+
return `${kind}:${data.flag}`;
|
|
9
|
+
}
|
|
10
|
+
const time = data.time ?? 0;
|
|
11
|
+
const type = data.notice_type ?? data.request_type ?? kind;
|
|
12
|
+
const scope = data.group_id ?? data.user_id ?? '';
|
|
13
|
+
const sub = data.sub_type ?? '';
|
|
14
|
+
return `${kind}:${time}_${type}_${scope}_${sub}`;
|
|
15
|
+
}
|
|
16
|
+
function resolveNotifyNoticeParts(subType, isGroup) {
|
|
17
|
+
if (subType === 'poke') {
|
|
18
|
+
return isGroup
|
|
19
|
+
? { scene_type: 'group', sub_type: 'poke' }
|
|
20
|
+
: { scene_type: 'friend', sub_type: 'poke' };
|
|
21
|
+
}
|
|
22
|
+
if (subType === 'input_status')
|
|
23
|
+
return { scene_type: 'notify', sub_type: 'input_status' };
|
|
24
|
+
if (subType === 'title')
|
|
25
|
+
return { scene_type: 'notify', sub_type: 'title_change' };
|
|
26
|
+
if (subType === 'profile_like')
|
|
27
|
+
return { scene_type: 'notify', sub_type: 'profile_like' };
|
|
28
|
+
return { scene_type: 'notify', sub_type: subType ?? 'unknown' };
|
|
29
|
+
}
|
|
30
|
+
function resolveEssenceNoticeParts(subType) {
|
|
31
|
+
return subType === 'add'
|
|
32
|
+
? { scene_type: 'group', sub_type: 'essence_add' }
|
|
33
|
+
: { scene_type: 'group', sub_type: 'essence_delete' };
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* 将平台原始 notice_type 映射为标准 `$scene_type` + `$sub_type`。
|
|
37
|
+
*/
|
|
38
|
+
export function mapNoticeParts(platform, raw, options) {
|
|
39
|
+
const subType = options?.sub_type;
|
|
40
|
+
const isGroup = options?.is_group ?? false;
|
|
41
|
+
if (platform === 'kook') {
|
|
42
|
+
return KOOK_NOTICE_PARTS_MAP[raw] ?? { scene_type: 'kook', sub_type: raw };
|
|
43
|
+
}
|
|
44
|
+
if (platform === 'slack') {
|
|
45
|
+
return SLACK_NOTICE_PARTS_MAP[raw] ?? { scene_type: 'slack', sub_type: raw };
|
|
46
|
+
}
|
|
47
|
+
if (raw === 'notify') {
|
|
48
|
+
const handler = options?.notify_handler ?? resolveNotifyNoticeParts;
|
|
49
|
+
return handler(subType, isGroup);
|
|
50
|
+
}
|
|
51
|
+
if (raw === 'essence') {
|
|
52
|
+
return resolveEssenceNoticeParts(subType);
|
|
53
|
+
}
|
|
54
|
+
if (ONEBOT_NOTICE_PARTS_MAP[raw]) {
|
|
55
|
+
return ONEBOT_NOTICE_PARTS_MAP[raw];
|
|
56
|
+
}
|
|
57
|
+
if (raw.startsWith('notice.')) {
|
|
58
|
+
const parsed = parseSideEventName(raw);
|
|
59
|
+
if (parsed.scene_type && parsed.sub_type) {
|
|
60
|
+
return { scene_type: parsed.scene_type, sub_type: parsed.sub_type };
|
|
61
|
+
}
|
|
62
|
+
const suffix = raw.slice('notice.'.length).replace(/\./g, '_');
|
|
63
|
+
if (ONEBOT_NOTICE_PARTS_MAP[suffix])
|
|
64
|
+
return ONEBOT_NOTICE_PARTS_MAP[suffix];
|
|
65
|
+
return { scene_type: parsed.scene_type ?? 'unknown', sub_type: parsed.sub_type ?? suffix };
|
|
66
|
+
}
|
|
67
|
+
if (platform === 'icqq' || platform === 'onebot' || platform === 'napcat') {
|
|
68
|
+
const dotted = raw.replace(/_/g, '.');
|
|
69
|
+
const parsed = parseSideEventName(`notice.${dotted}`);
|
|
70
|
+
return {
|
|
71
|
+
scene_type: parsed.scene_type ?? dotted.split('.')[0] ?? 'unknown',
|
|
72
|
+
sub_type: parsed.sub_type ?? (dotted.split('.').slice(1).join('.') || raw),
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
return { scene_type: platform, sub_type: raw };
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* 将平台原始 request_type 映射为标准 `$scene_type` + `$sub_type`。
|
|
79
|
+
*/
|
|
80
|
+
export function mapRequestParts(_platform, raw, subType) {
|
|
81
|
+
if (raw === 'friend' || raw.startsWith('friend')) {
|
|
82
|
+
return { scene_type: 'friend', sub_type: 'add' };
|
|
83
|
+
}
|
|
84
|
+
if (raw === 'group' || raw.startsWith('group')) {
|
|
85
|
+
return { scene_type: 'group', sub_type: subType === 'invite' ? 'invite' : 'add' };
|
|
86
|
+
}
|
|
87
|
+
if (raw.startsWith('request.')) {
|
|
88
|
+
const parsed = parseSideEventName(raw);
|
|
89
|
+
return {
|
|
90
|
+
scene_type: parsed.scene_type ?? 'unknown',
|
|
91
|
+
sub_type: parsed.sub_type ?? 'unknown',
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
return { scene_type: raw, sub_type: subType ?? 'unknown' };
|
|
95
|
+
}
|
|
96
|
+
/** 组合完整 Notice 类型名(兼容旧 API) */
|
|
97
|
+
export function mapNoticeType(platform, raw, options) {
|
|
98
|
+
const parts = mapNoticeParts(platform, raw, options);
|
|
99
|
+
return composeSideEventName('notice', parts.scene_type, parts.sub_type);
|
|
100
|
+
}
|
|
101
|
+
/** 组合完整 Request 类型名(兼容旧 API) */
|
|
102
|
+
export function mapRequestType(platform, raw, subType) {
|
|
103
|
+
const parts = mapRequestParts(platform, raw, subType);
|
|
104
|
+
return composeSideEventName('request', parts.scene_type, parts.sub_type);
|
|
105
|
+
}
|
|
106
|
+
export function senderFromId(id, name) {
|
|
107
|
+
if (id == null || id === '')
|
|
108
|
+
return undefined;
|
|
109
|
+
const s = String(id);
|
|
110
|
+
return { id: s, name: name ?? s };
|
|
111
|
+
}
|
|
112
|
+
export function buildNotice(input, format) {
|
|
113
|
+
return Notice.from(input, format);
|
|
114
|
+
}
|
|
115
|
+
export function buildRequest(input, format) {
|
|
116
|
+
return Request.from(input, format);
|
|
117
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Side Event 标准类型。
|
|
3
|
+
* 运行时 `$type` 仅存命名空间(`notice` / `request`);
|
|
4
|
+
* 完整名由 `${$type}.${$scene_type}.${$sub_type}` 组合。
|
|
5
|
+
*/
|
|
6
|
+
export type SideEventKind = 'notice' | 'request';
|
|
7
|
+
export type NoticeKind = 'notice';
|
|
8
|
+
export type RequestKind = 'request';
|
|
9
|
+
/** 完整组合名(测试 / 文档 / 消费者匹配用) */
|
|
10
|
+
export type ComposedNoticeName = 'notice.group.member_increase' | 'notice.group.member_decrease' | 'notice.group.admin_change' | 'notice.group.ban' | 'notice.group.recall' | 'notice.group.poke' | 'notice.group.transfer' | 'notice.group.upload' | 'notice.group.emoji_reaction' | 'notice.group.card_change' | 'notice.group.essence_add' | 'notice.group.essence_delete' | 'notice.friend.recall' | 'notice.friend.increase' | 'notice.friend.poke' | 'notice.endpoint.lifecycle' | (string & {});
|
|
11
|
+
export type ComposedRequestName = 'request.friend.add' | 'request.group.add' | 'request.group.invite' | (string & {});
|
|
12
|
+
/** @deprecated 使用 ComposedNoticeName + formatSideEventName */
|
|
13
|
+
export type NoticeType = ComposedNoticeName;
|
|
14
|
+
/** @deprecated 使用 ComposedRequestName + formatSideEventName */
|
|
15
|
+
export type RequestType = ComposedRequestName;
|
|
16
|
+
export interface SideEventParts {
|
|
17
|
+
scene_type: string;
|
|
18
|
+
sub_type: string;
|
|
19
|
+
}
|
|
20
|
+
/** OneBot / icqq 等平台 notice_type 原始值 → 标准 scene_type + sub_type */
|
|
21
|
+
export declare const ONEBOT_NOTICE_PARTS_MAP: Record<string, SideEventParts>;
|
|
22
|
+
/** KOOK extra.type 原始值 → 标准 scene_type + sub_type */
|
|
23
|
+
export declare const KOOK_NOTICE_PARTS_MAP: Record<string, SideEventParts>;
|
|
24
|
+
/** Slack event type 原始值 → 标准 scene_type + sub_type */
|
|
25
|
+
export declare const SLACK_NOTICE_PARTS_MAP: Record<string, SideEventParts>;
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Side Event 标准类型。
|
|
3
|
+
* 运行时 `$type` 仅存命名空间(`notice` / `request`);
|
|
4
|
+
* 完整名由 `${$type}.${$scene_type}.${$sub_type}` 组合。
|
|
5
|
+
*/
|
|
6
|
+
/** OneBot / icqq 等平台 notice_type 原始值 → 标准 scene_type + sub_type */
|
|
7
|
+
export const ONEBOT_NOTICE_PARTS_MAP = {
|
|
8
|
+
group_increase: { scene_type: 'group', sub_type: 'member_increase' },
|
|
9
|
+
group_decrease: { scene_type: 'group', sub_type: 'member_decrease' },
|
|
10
|
+
group_admin: { scene_type: 'group', sub_type: 'admin_change' },
|
|
11
|
+
group_ban: { scene_type: 'group', sub_type: 'ban' },
|
|
12
|
+
group_recall: { scene_type: 'group', sub_type: 'recall' },
|
|
13
|
+
friend_recall: { scene_type: 'friend', sub_type: 'recall' },
|
|
14
|
+
friend_add: { scene_type: 'friend', sub_type: 'increase' },
|
|
15
|
+
group_upload: { scene_type: 'group', sub_type: 'upload' },
|
|
16
|
+
group_transfer: { scene_type: 'group', sub_type: 'transfer' },
|
|
17
|
+
group_card: { scene_type: 'group', sub_type: 'card_change' },
|
|
18
|
+
group_msg_emoji_like: { scene_type: 'group', sub_type: 'emoji_reaction' },
|
|
19
|
+
'group.increase': { scene_type: 'group', sub_type: 'member_increase' },
|
|
20
|
+
'group.decrease': { scene_type: 'group', sub_type: 'member_decrease' },
|
|
21
|
+
'group.admin': { scene_type: 'group', sub_type: 'admin_change' },
|
|
22
|
+
'group.ban': { scene_type: 'group', sub_type: 'ban' },
|
|
23
|
+
'group.recall': { scene_type: 'group', sub_type: 'recall' },
|
|
24
|
+
'friend.increase': { scene_type: 'friend', sub_type: 'increase' },
|
|
25
|
+
'friend.decrease': { scene_type: 'friend', sub_type: 'recall' },
|
|
26
|
+
'friend.recall': { scene_type: 'friend', sub_type: 'recall' },
|
|
27
|
+
};
|
|
28
|
+
/** KOOK extra.type 原始值 → 标准 scene_type + sub_type */
|
|
29
|
+
export const KOOK_NOTICE_PARTS_MAP = {
|
|
30
|
+
joined_guild: { scene_type: 'group', sub_type: 'member_increase' },
|
|
31
|
+
exited_guild: { scene_type: 'group', sub_type: 'member_decrease' },
|
|
32
|
+
deleted_message: { scene_type: 'group', sub_type: 'recall' },
|
|
33
|
+
deleted_private_message: { scene_type: 'friend', sub_type: 'recall' },
|
|
34
|
+
added_reaction: { scene_type: 'group', sub_type: 'emoji_reaction' },
|
|
35
|
+
deleted_reaction: { scene_type: 'group', sub_type: 'emoji_reaction' },
|
|
36
|
+
private_added_reaction: { scene_type: 'group', sub_type: 'emoji_reaction' },
|
|
37
|
+
private_deleted_reaction: { scene_type: 'group', sub_type: 'emoji_reaction' },
|
|
38
|
+
};
|
|
39
|
+
/** Slack event type 原始值 → 标准 scene_type + sub_type */
|
|
40
|
+
export const SLACK_NOTICE_PARTS_MAP = {
|
|
41
|
+
member_joined_channel: { scene_type: 'group', sub_type: 'member_increase' },
|
|
42
|
+
member_left_channel: { scene_type: 'group', sub_type: 'member_decrease' },
|
|
43
|
+
reaction_added: { scene_type: 'group', sub_type: 'emoji_reaction' },
|
|
44
|
+
reaction_removed: { scene_type: 'group', sub_type: 'emoji_reaction' },
|
|
45
|
+
message_deleted: { scene_type: 'group', sub_type: 'recall' },
|
|
46
|
+
channel_archive: { scene_type: 'slack', sub_type: 'channel_archive' },
|
|
47
|
+
channel_unarchive: { scene_type: 'slack', sub_type: 'channel_unarchive' },
|
|
48
|
+
channel_rename: { scene_type: 'slack', sub_type: 'channel_rename' },
|
|
49
|
+
channel_created: { scene_type: 'slack', sub_type: 'channel_created' },
|
|
50
|
+
channel_deleted: { scene_type: 'slack', sub_type: 'channel_deleted' },
|
|
51
|
+
pin_added: { scene_type: 'slack', sub_type: 'pin_added' },
|
|
52
|
+
pin_removed: { scene_type: 'slack', sub_type: 'pin_removed' },
|
|
53
|
+
app_home_opened: { scene_type: 'slack', sub_type: 'app_home_opened' },
|
|
54
|
+
team_join: { scene_type: 'friend', sub_type: 'increase' },
|
|
55
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zhin.js/core",
|
|
3
|
-
"version": "1.3.
|
|
3
|
+
"version": "1.3.4",
|
|
4
4
|
"description": "Zhin机器人核心框架",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./lib/index.js",
|
|
@@ -45,9 +45,9 @@
|
|
|
45
45
|
"segment-matcher": "^1.0.5",
|
|
46
46
|
"smol-toml": "^1.7.0",
|
|
47
47
|
"yaml": "^2.9.0",
|
|
48
|
-
"@zhin.js/
|
|
49
|
-
"@zhin.js/
|
|
50
|
-
"@zhin.js/logger": "1.0.
|
|
48
|
+
"@zhin.js/kernel": "1.0.3",
|
|
49
|
+
"@zhin.js/database": "1.0.76",
|
|
50
|
+
"@zhin.js/logger": "1.0.74",
|
|
51
51
|
"@zhin.js/schema": "1.0.71"
|
|
52
52
|
},
|
|
53
53
|
"peerDependencies": {
|
|
@@ -62,7 +62,7 @@
|
|
|
62
62
|
"@types/node": "^26.1.0",
|
|
63
63
|
"@types/qrcode": "^1.5.5",
|
|
64
64
|
"typescript": "^6.0.3",
|
|
65
|
-
"@zhin.js/ai": "1.4.
|
|
65
|
+
"@zhin.js/ai": "1.4.4"
|
|
66
66
|
},
|
|
67
67
|
"repository": {
|
|
68
68
|
"type": "git",
|