@napuketto/adapter 0.2.1 → 0.3.1
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/dist/gray-tip-8zPh4d9W.d.mts +1 -0
- package/dist/{raw-message-Bx18pIQS.mjs → gray-tip-B80z9KR4.mjs} +2 -12
- package/dist/gray-tip-B80z9KR4.mjs.map +1 -0
- package/dist/kurobot/index.d.mts +840 -0
- package/dist/kurobot/index.d.mts.map +1 -0
- package/dist/kurobot/index.mjs +998 -0
- package/dist/kurobot/index.mjs.map +1 -0
- package/dist/onebot11/index.d.mts +2 -2
- package/dist/onebot11/index.mjs +2 -1
- package/dist/onebot11/index.mjs.map +1 -1
- package/dist/raw-message-6KdlG8kR.mjs +13 -0
- package/dist/raw-message-6KdlG8kR.mjs.map +1 -0
- package/dist/satori/index.d.mts +2 -2
- package/dist/satori/index.mjs +2 -1
- package/dist/satori/index.mjs.map +1 -1
- package/package.json +9 -2
- package/dist/gray-tip-CcpfI1Og.d.mts +0 -1
- package/dist/raw-message-Bx18pIQS.mjs.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":[],"sources":["../../src/satori/helper/config.ts","../../src/satori/helper/error.ts","../../src/satori/helper/ids.ts","../../src/satori/helper/element/canonical.ts","../../src/satori/helper/element/parse-tag.ts","../../src/satori/helper/element/element.ts","../../src/satori/helper/element/asset.ts","../../src/satori/helper/element/media-convert.ts","../../src/satori/helper/element/element-convert.ts","../../src/satori/helper/translate.ts","../../src/satori/action/base-action.ts","../../src/satori/action/channel.ts","../../src/satori/action/friend.ts","../../src/satori/action/guild.ts","../../src/satori/action/login.ts","../../src/satori/action/message.ts","../../src/satori/action/not-implemented.ts","../../src/satori/action/reaction.ts","../../src/satori/action/user.ts","../../src/satori/action/index.ts","../../src/satori/api/satori-api.ts","../../src/satori/event/translate.ts","../../src/satori/types/event.ts","../../src/satori/transport.ts","../../src/satori/adapter.ts"],"sourcesContent":["/**\n * Satori 协议配置(zod schema,归属本包,ADR-012)\n * 协议配置 schema 在各自协议包,kernel 只提供 ConfigBase 基类。\n */\nimport { z } from \"zod\";\nimport { createHttpServerSchema, createWsServerSchema } from \"../../core/transport-schema.js\";\n\n/** HTTP RPC 服务器默认端口(satori.chat 惯例 5500 区间,取 5500)。 */\nconst DEFAULT_HTTP_PORT = 5500;\n/** WS 事件服务默认端口(satori.chat 惯例 5500 区间,取 5501)。 */\nconst DEFAULT_WS_PORT = 5501;\n\n/** HTTP RPC 服务器实例(POST /v1/{resource}.{method})。 */\nconst httpServerSchema = createHttpServerSchema(DEFAULT_HTTP_PORT);\n\n/** WS 事件服务实例(/v1/events 信令,第三方 SDK 连入)。 */\nconst wsServerSchema = createWsServerSchema(DEFAULT_WS_PORT);\n\n/** Satori 配置 schema。 */\nexport const satoriConfigSchema = z.object({\n /** 鉴权 token(全局默认,实例未指定时继承)。 */\n token: z.string().optional(),\n /** 媒体资源(img/audio/video/file)下载缓存目录。 */\n cacheDir: z.string().optional(),\n /** HTTP RPC 服务器列表。 */\n httpServers: z.array(httpServerSchema).default([]),\n /** WS 事件服务列表。 */\n wsServers: z.array(wsServerSchema).default([]),\n});\n\n/** Satori 配置类型(由 schema 推导)。 */\nexport type SatoriConfig = z.infer<typeof satoriConfigSchema>;\n","/**\n * Satori 错误映射(ADR-017):KernelErrorCode → HTTP 状态码\n * 与 OB11 的 retcode 不同,Satori 直接用 HTTP 状态码表达错误。\n * 动作未实现 → 501;API 不存在 → 404;鉴权失败 → 401。\n */\nimport type { KernelErrorCode } from \"@napuketto/kernel\";\n\n/** KernelErrorCode → HTTP 状态码映射表。 */\nexport const satoriHttpStatusMap: Record<KernelErrorCode, number> = {\n SEND_FAILED: 500,\n PERMISSION_DENIED: 403,\n NOT_FOUND: 404,\n TIMEOUT: 500,\n NOT_LOGIN: 403,\n INVALID_PARAM: 400,\n INVALID_STATE: 500,\n UNKNOWN: 500,\n};\n\n/** HTTP 状态码常量。 */\nexport const HTTP_STATUS = {\n ok: 200,\n badRequest: 400,\n unauthorized: 401,\n forbidden: 403,\n notFound: 404,\n notImplemented: 501,\n serverError: 500,\n} as const;\n","/**\n * Satori 频道/登录 ID 构造(QQ 平台语义)\n *\n * - 群聊:guild.id = channel.id = 群号,channel.type = TEXT(guild.plain)\n * - 私聊:channel.id = 对端 uin,channel.type = DIRECT,无 guild\n * - user.id = uin;message.id = NT msgId\n * - login.user.id = 机器人 uin\n */\nimport type { Channel, ChannelType, Guild, Login, User } from \"../types/resource.js\";\n\n/** 平台名(固定 qq)。 */\nexport const PLATFORM = \"qq\";\n/** 适配器名(固定 napuketto)。 */\nexport const ADAPTER = \"napuketto\";\n\n/** 平台特性(API 可用性判断;群聊频道与群组重合 → guild.plain)。 */\nexport const FEATURES = [\n \"guild.plain\", // 群组内只能存在一个消息频道\n \"message.list.from\", // message.list 支持以消息 ID 作分页令牌\n] as const;\n\n/** 构造用户(uin)。 */\nexport function toUser(id: string, name?: string, nick?: string): User {\n const user: User = { id };\n if (name !== undefined && name !== \"\") {\n user.name = name;\n }\n if (nick !== undefined && nick !== \"\") {\n user.nick = nick;\n }\n return user;\n}\n\n/** 构造群聊频道(id = 群号)。 */\nexport function toGroupChannel(groupCode: string, name?: string): Channel {\n const channel: Channel = { id: String(groupCode), type: 0 as ChannelType };\n if (name !== undefined && name !== \"\") {\n channel.name = name;\n }\n return channel;\n}\n\n/** 构造私聊频道(id = 对端 uin)。 */\nexport function toDirectChannel(uin: string, name?: string): Channel {\n const channel: Channel = { id: String(uin), type: 1 as ChannelType };\n if (name !== undefined && name !== \"\") {\n channel.name = name;\n }\n return channel;\n}\n\n/** 构造群组(id = 群号)。 */\nexport function toGuild(groupCode: string, name?: string, avatar?: string): Guild {\n const guild: Guild = { id: String(groupCode) };\n if (name !== undefined && name !== \"\") {\n guild.name = name;\n }\n if (avatar !== undefined && avatar !== \"\") {\n guild.avatar = avatar;\n }\n return guild;\n}\n\n/** 构造登录信息(login.get / READY logins)。 */\nexport function toLogin(\n self: { uin: string; nickname: string },\n sn: number,\n online: boolean,\n): Login {\n const login: Login = {\n sn,\n platform: PLATFORM,\n user: toUser(self.uin, self.nickname),\n status: online ? 1 : 0,\n adapter: ADAPTER,\n features: [...FEATURES],\n };\n return login;\n}\n\n/** 构造非登录事件的最小 login(只带 sn/user/platform,规范)。 */\nexport function toMinimalLogin(sn: number, selfUin: string): Login {\n return {\n sn,\n platform: PLATFORM,\n user: toUser(selfUin),\n status: 1,\n adapter: ADAPTER,\n };\n}\n","/**\n * 收方向转换:kernel canonical → Satori 元素(从 element-convert.ts 拆分,2026-08-08 FTA 优化)\n *\n * canonicalToSatoriElements:纯函数映射,宽容忽略 unknown(json/xml 无法表达)。\n * at 目标为 uid 时经 uidToUin 批量转换(异步,失败原样 uid)。\n */\nimport type { CanonicalElement } from \"@napuketto/kernel\";\nimport type { SatoriElement } from \"./element.js\";\n\n/** 收方向依赖(at uid 解析)。 */\nexport interface CanonicalToSatoriDeps {\n /** uid → uin(at 目标显示用;未提供原样返回 uid)。 */\n uidToUin?: (uids: string[]) => Promise<Map<string, string>>;\n}\n\n/** 收集 at uid → 批量 uidToUin(失败不阻塞,at 原样 uid)。 */\nasync function resolveAtUids(\n elements: CanonicalElement[],\n uidToUin: CanonicalToSatoriDeps[\"uidToUin\"],\n): Promise<Map<string, string>> {\n const atUids: string[] = [];\n for (const el of elements) {\n if (el.type === \"at\" && el.target !== \"all\") {\n atUids.push(el.target);\n }\n }\n if (atUids.length === 0 || uidToUin === undefined) {\n return new Map();\n }\n try {\n return await uidToUin([...new Set(atUids)]);\n } catch {\n // uid 解析失败:at 原样(uid),不阻塞事件翻译\n return new Map();\n }\n}\n\n/** canonical at → Satori at(uid → uin,name = display)。 */\nfunction atToSatori(\n el: Extract<CanonicalElement, { type: \"at\" }>,\n uinMap: Map<string, string>,\n): SatoriElement {\n if (el.target === \"all\") {\n return { type: \"at\", attrs: { id: \"all\" } };\n }\n const attrs: Record<string, string> = {\n id: uinMap.get(el.target) ?? el.target,\n };\n if (el.display !== undefined && el.display !== \"\") {\n attrs[\"name\"] = el.display;\n }\n return { type: \"at\", attrs };\n}\n\n/** canonical file → Satori file(title = name)。 */\nfunction fileToSatori(el: Extract<CanonicalElement, { type: \"file\" }>): SatoriElement {\n const attrs: Record<string, string> = { src: el.path };\n if (el.name !== undefined && el.name !== \"\") {\n attrs[\"title\"] = el.name;\n }\n return { type: \"file\", attrs };\n}\n\n/** canonical forward → Satori message(取首个消息 id)。 */\nfunction forwardToSatori(el: Extract<CanonicalElement, { type: \"forward\" }>): SatoriElement | null {\n const first = el.messageIds[0];\n if (first === undefined || first === \"\") {\n return null;\n }\n return { type: \"message\", attrs: { forward: \"\", id: first } };\n}\n\n/** 单个 canonical → Satori(null = 宽容忽略,如 json/xml/unknown)。 */\nfunction canonicalToSatori(\n el: CanonicalElement,\n uinMap: Map<string, string>,\n): SatoriElement | null {\n switch (el.type) {\n case \"text\":\n return el.text === \"\" ? null : { type: \"text\", text: el.text };\n case \"at\":\n return atToSatori(el, uinMap);\n case \"image\":\n return { type: \"img\", attrs: { src: el.url ?? el.path } };\n case \"face\":\n return { type: \"emoji\", attrs: { id: el.id } };\n case \"voice\":\n return { type: \"audio\", attrs: { src: el.path } };\n case \"video\":\n return { type: \"video\", attrs: { src: el.url ?? el.path } };\n case \"file\":\n return fileToSatori(el);\n case \"reply\":\n return { type: \"quote\", attrs: { id: el.messageId } };\n case \"forward\":\n return forwardToSatori(el);\n case \"json\":\n case \"xml\":\n case \"unknown\":\n // 无法表达:忽略(宽容)\n return null;\n }\n}\n\n/**\n * 收方向:kernel canonical 元素 → Satori 元素树(纯函数映射,宽容忽略 unknown)。\n * at 目标为 uid 时经 uidToUin 转换(异步);无转换器时原样。\n */\nexport async function canonicalToSatoriElements(\n elements: CanonicalElement[],\n deps: CanonicalToSatoriDeps = {},\n): Promise<SatoriElement[]> {\n const uinMap = await resolveAtUids(elements, deps.uidToUin);\n const out: SatoriElement[] = [];\n for (const el of elements) {\n const converted = canonicalToSatori(el, uinMap);\n if (converted !== null) {\n out.push(converted);\n }\n }\n return out;\n}\n","/**\n * Satori 标签读取(元素名 / 属性解析,从 element.ts 拆分,2026-08-08 FTA 优化)\n *\n * - decodeEntity/decodeEntities:命名/十进制/十六进制实体解码(属性值用)\n * - readTagName:读取元素名(小写字母/数字/连字符,以字母开头)\n * - readTagAttrs / readAttr / skipSpaces:标签属性区解析(无值属性 / 引号值 / 无引号值)\n * - readTag:完整标签(名 + 属性 + 自闭合指示)\n */\n\n/** 命名实体(规范规定的四个 + apos 宽容)。 */\nconst NAMED_ENTITIES: Record<string, string> = {\n quot: '\"',\n amp: \"&\",\n lt: \"<\",\n gt: \">\",\n apos: \"'\",\n};\n\n/** 解码单个实体(命名/十进制/十六进制)。 */\nfunction decodeEntity(body: string): string {\n const named = NAMED_ENTITIES[body];\n if (named !== undefined) {\n return named;\n }\n if (body.startsWith(\"#x\") || body.startsWith(\"#X\")) {\n const code = Number.parseInt(body.slice(2), 16);\n if (Number.isInteger(code)) {\n return String.fromCodePoint(code);\n }\n }\n if (body.startsWith(\"#\")) {\n const code = Number.parseInt(body.slice(1), 10);\n if (Number.isInteger(code)) {\n return String.fromCodePoint(code);\n }\n }\n // 未识别实体原样保留\n return `&${body};`;\n}\n\n/** 解码字符串中的全部实体。 */\nconst ENTITY_RE = /&([^;]+);/g;\n\n/** 解码字符串中的全部实体。 */\nfunction decodeEntities(input: string): string {\n return input.replace(ENTITY_RE, (_full, body: string) => decodeEntity(body));\n}\n\n/** 元素名:小写字母/数字/连字符,以字母开头(规范)。 */\nconst NAME_RE = /^[a-z][a-z0-9-]*/i;\n\n/** 读取元素名(pos 起);未匹配返回空名。 */\nexport function readTagName(source: string, pos: number): { name: string; end: number } {\n const rest = source.slice(pos);\n const m = NAME_RE.exec(rest);\n if (m === null) {\n return { name: \"\", end: pos };\n }\n return { name: m[0], end: pos + m[0].length };\n}\n\n/** 空白字符。 */\nconst SPACE_RE = /\\s/;\n\n/** 跳过空白;返回下一个非空白位置。 */\nfunction skipSpaces(source: string, start: number): number {\n let i = start;\n while (i < source.length && SPACE_RE.test(source.charAt(i))) {\n i += 1;\n }\n return i;\n}\n\n/** 属性名(字母数字 / 冒号(命名空间)/ 连字符 / 下划线)。 */\nconst ATTR_NAME_RE = /^[a-zA-Z0-9:_-]+/;\n/** 无引号属性值的结束字符(空白 / >)。 */\nconst ATTR_VALUE_END_RE = /[\\s>]/;\n\n/** 读取单个属性(名 / = / 值 / 无值属性),更新 attrs,返回下一个位置。 */\nfunction readAttr(source: string, start: number, attrs: Record<string, string>): number {\n // 属性名(字母数字 / 冒号(命名空间)/ 连字符 / 下划线)\n const keyMatch = ATTR_NAME_RE.exec(source.slice(start));\n if (keyMatch === null) {\n return start + 1;\n }\n const key = keyMatch[0];\n let i = skipSpaces(source, start + key.length);\n if (source.charAt(i) !== \"=\") {\n // 无值属性\n attrs[key] = \"\";\n return i;\n }\n i = skipSpaces(source, i + 1);\n const quote = source.charAt(i);\n if (quote === '\"' || quote === \"'\") {\n i += 1;\n const valueStart = i;\n while (i < source.length && source.charAt(i) !== quote) {\n i += 1;\n }\n attrs[key] = decodeEntities(source.slice(valueStart, i));\n if (i < source.length) {\n i += 1; // 跳过结束引号\n }\n } else {\n // 无引号值(到空白或 > 为止)\n const valueStart = i;\n while (i < source.length && !ATTR_VALUE_END_RE.test(source.charAt(i))) {\n i += 1;\n }\n attrs[key] = decodeEntities(source.slice(valueStart, i));\n }\n return i;\n}\n\n/** 读取标签属性区(到 > 或 /> 为止),返回属性表与闭合指示、结束位置。 */\nfunction readTagAttrs(\n source: string,\n start: number,\n): { attrs: Record<string, string>; selfClosing: boolean; end: number } {\n const attrs: Record<string, string> = {};\n let i = start;\n let selfClosing = false;\n while (i < source.length) {\n const ch = source.charAt(i);\n if (ch === \">\") {\n i += 1;\n break;\n }\n if (ch === \"/\") {\n if (source.charAt(i + 1) === \">\") {\n selfClosing = true;\n i += 2;\n break;\n }\n i += 1;\n continue;\n }\n if (SPACE_RE.test(ch)) {\n i += 1;\n continue;\n }\n i = readAttr(source, i, attrs);\n }\n return { attrs, selfClosing, end: i };\n}\n\n/** 读取完整标签(元素名 + 属性 + 闭合指示),返回属性表与结束位置。 */\nexport function readTag(\n source: string,\n pos: number,\n): { name: string; attrs: Record<string, string>; selfClosing: boolean; end: number } {\n const { name, end } = readTagName(source, pos);\n if (name === \"\") {\n return { name: \"\", attrs: {}, selfClosing: false, end: pos };\n }\n const { attrs, selfClosing, end: attrEnd } = readTagAttrs(source, end);\n return { name, attrs, selfClosing, end: attrEnd };\n}\n","/**\n * Satori 消息元素解析/渲染(协议 v1,自研实现,规范参考 satori.chat/zh-CN/protocol/message.html)\n *\n * - parseElements:XML 风格字符串 → 元素树(注释/转义/无值属性/未配对标签容错)\n * - renderElements:元素树 → 字符串(消息 content 序列化)\n *\n * canonical ↔ Satori 双向转换不在本文件:收方向(canonical → Satori)在\n * canonical.ts,发方向(Satori → canonical)在 element-convert.ts,调用方直引。\n *\n * 语法要点(对齐规范):\n * - 转义:" & < > ' ' 与十进制/十六进制数字实体\n * - 注释 <!-- --> 不渲染\n * - 无值属性(key)与 key=\"value\" / key='value' 均合法\n * - 未配对的标签视为文本内容的一部分\n * - 文本内容前后包含换行符的连续空白会被忽略\n */\nimport { readTag, readTagName } from \"./parse-tag.js\";\n\n/** Satori 消息元素(与规范 satori-parse 形状一致;type=\"text\" 为文本节点)。 */\nexport interface SatoriElement {\n type: string;\n attrs?: Record<string, string>;\n children?: SatoriElement[];\n text?: string;\n}\n\n/** 文本转义正则(& < >)。 */\nconst TEXT_ESCAPE_RE = /[&<>]/g;\n\n/** 文本转义(& < >)。 */\nfunction encodeText(input: string): string {\n return input.replace(TEXT_ESCAPE_RE, (ch) => {\n if (ch === \"&\") {\n return \"&\";\n }\n if (ch === \"<\") {\n return \"<\";\n }\n return \">\";\n });\n}\n\n/** 属性值转义正则(& \" < >)。 */\nconst ATTR_ESCAPE_RE = /[&\"<>]/g;\n\n/** 属性值转义(& \" < >)。 */\nfunction encodeAttr(input: string): string {\n return input.replace(ATTR_ESCAPE_RE, (ch) => {\n if (ch === \"&\") {\n return \"&\";\n }\n if (ch === '\"') {\n return \""\";\n }\n if (ch === \"<\") {\n return \"<\";\n }\n return \">\";\n });\n}\n\n/** 行首连续空白。 */\nconst LEADING_WS_RE = /^\\s+/;\n/** 行尾连续空白。 */\nconst TRAILING_WS_RE = /\\s+$/;\n/** 换行符。 */\nconst NEWLINE_RE = /[\\r\\n]/;\n\n/**\n * 文本规范化:开头/结尾的「包含换行符的连续空白」被忽略(规范),\n * 中间的空白与不含换行的首尾空白保留。\n */\nfunction normalizeText(raw: string): string {\n const leading = LEADING_WS_RE.exec(raw);\n const trailing = TRAILING_WS_RE.exec(raw);\n let out = raw;\n if (leading !== null && NEWLINE_RE.test(leading[0])) {\n out = out.slice(leading[0].length);\n }\n if (trailing !== null && NEWLINE_RE.test(trailing[0])) {\n out = out.slice(0, -trailing[0].length);\n }\n return out;\n}\n\n/** Satori 消息解析器状态(挂载点 / 文本缓冲 / 栈管理,拆分主循环复杂度)。 */\nclass ElementParser {\n private readonly root: SatoriElement[] = [];\n private readonly stack: SatoriElement[] = [];\n private text = \"\";\n\n /** 当前挂载数组(栈顶元素 children 或根数组)。 */\n private mountPoint(): SatoriElement[] {\n const top = this.stack[this.stack.length - 1];\n if (top === undefined) {\n return this.root;\n }\n top.children ??= [];\n return top.children;\n }\n\n /** 归一化文本落位到当前挂载点。 */\n pushText(): void {\n const normalized = normalizeText(this.text);\n this.text = \"\";\n if (normalized !== \"\") {\n this.mountPoint().push({ type: \"text\", text: normalized });\n }\n }\n\n /** 解析完成的元素树。 */\n get elements(): SatoriElement[] {\n return this.root;\n }\n\n /** 追加原始文本(解析间隙累积)。 */\n appendRaw(raw: string): void {\n this.text += raw;\n }\n\n /** 起始/自闭合标签落位(压栈或仅挂载)。 */\n openTag(name: string, attrs: Record<string, string>, selfClosing: boolean): void {\n const el: SatoriElement = { type: name, attrs };\n this.mountPoint().push(el);\n if (!selfClosing) {\n this.stack.push(el);\n }\n }\n\n /** 收尾:剩余文本落位(栈剩余未闭合元素:文本落在栈顶元素内,宽容)。 */\n flush(): void {\n this.pushText();\n }\n\n /** 注释:返回跳过后的位置;-1 = 未闭合终止解析(注释不渲染)。 */\n skipComment(source: string, pos: number): number {\n const end = source.indexOf(\"-->\", pos);\n return end === -1 ? -1 : end + 3;\n }\n\n /**\n * 结束标签:匹配栈顶则闭合,否则按文本保留。\n * @returns 下一个解析位置;-1 = 终止解析。\n */\n closeTag(source: string, lt: number, slashPos: number): number {\n const { name, end } = readTagName(source, slashPos + 1);\n if (name === \"\") {\n // 非法结束标签(</> 等):< 按文本保留\n this.text += \"<\";\n return lt + 1;\n }\n const gt = source.indexOf(\">\", end);\n if (gt === -1) {\n this.text += source.slice(lt);\n return -1;\n }\n const top = this.stack[this.stack.length - 1];\n if (top !== undefined && top.type === name) {\n // 闭合前的文本推入该元素\n this.pushText();\n this.stack.pop();\n } else {\n // 未配对:原样当文本\n this.text += source.slice(lt, gt + 1);\n }\n return gt + 1;\n }\n}\n\n/**\n * 解析 Satori 消息字符串 → 元素树。\n * 容错:未配对标签/非法标签按文本保留;解析不抛错(消息内容宽容)。\n */\nexport function parseElements(source: string): SatoriElement[] {\n const parser = new ElementParser();\n let i = 0;\n while (i < source.length) {\n const lt = source.indexOf(\"<\", i);\n if (lt === -1) {\n parser.appendRaw(source.slice(i));\n break;\n }\n parser.appendRaw(source.slice(i, lt));\n i = lt + 1;\n // 注释:不渲染\n if (source.startsWith(\"!--\", i)) {\n const next = parser.skipComment(source, i);\n if (next === -1) {\n break;\n }\n i = next;\n continue;\n }\n // 结束标签\n if (source.startsWith(\"/\", i)) {\n const next = parser.closeTag(source, lt, i);\n if (next === -1) {\n break;\n }\n i = next;\n continue;\n }\n // 起始 / 自闭合标签\n const tag = readTag(source, i);\n if (tag.name === \"\") {\n parser.appendRaw(\"<\");\n continue;\n }\n parser.pushText();\n parser.openTag(tag.name, tag.attrs, tag.selfClosing);\n i = tag.end;\n }\n parser.flush();\n return parser.elements;\n}\n\n/** 元素树 → Satori 消息字符串。 */\nexport function renderElements(elements: SatoriElement[]): string {\n let out = \"\";\n for (const el of elements) {\n if (el.type === \"text\") {\n out += encodeText(el.text ?? \"\");\n continue;\n }\n out += renderElement(el);\n }\n return out;\n}\n\n/** 渲染单个元素(含子元素递归)。 */\nfunction renderElement(el: SatoriElement): string {\n let attrs = \"\";\n for (const [key, value] of Object.entries(el.attrs ?? {})) {\n if (value === \"\") {\n // 无值属性(forward 等布尔属性)\n attrs += ` ${key}`;\n } else {\n attrs += ` ${key}=\"${encodeAttr(value)}\"`;\n }\n }\n const children = el.children ?? [];\n if (children.length === 0) {\n return `<${el.type}${attrs}/>`;\n }\n const inner = renderElements(children);\n return `<${el.type}${attrs}>${inner}</${el.type}>`;\n}\n","/**\n * Satori 发方向资源处理(从 element-convert.ts 拆分,2026-08-08 FTA 优化)\n *\n * - resolveAsset:internal: 路径 / http(s) 下载 / 本地路径原样\n * - ensureSilk 已上移到 adapter core(core/media.ts,onebot11/satori 共用)\n */\nimport { mkdir, writeFile } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { kernelError } from \"@napuketto/kernel\";\n\n/** internal:{platform}/{user.id}/{path} → 本地路径(取 _tmp 之后的路径,回落 cacheDir)。 */\nfunction resolveInternalPath(src: string): string {\n const pathPart = src.slice(\"internal:\".length);\n const slash1 = pathPart.indexOf(\"/\");\n const slash2 = slash1 === -1 ? -1 : pathPart.indexOf(\"/\", slash1 + 1);\n if (slash2 === -1) {\n return pathPart;\n }\n const rest = pathPart.slice(slash2 + 1);\n // 去掉 _tmp/ 前缀段,回落到 cacheDir\n const tmpIdx = rest.indexOf(\"_tmp/\");\n return tmpIdx === -1 ? rest : rest.slice(tmpIdx + 5);\n}\n\n/** http(s) URL 前缀。 */\nconst HTTP_URL_RE = /^https?:\\/\\//i;\n/** 文件名不安全字符(URL 尾部段转文件名时替换)。 */\nconst UNSAFE_NAME_RE = /[^a-zA-Z0-9._-]/g;\n\n/**\n * 解析资源 src:\n * - internal:{platform}/{user.id}/{path} → 本地路径(第一版仅支持 _tmp 保留路径)\n * - http(s):// → 下载到 cacheDir\n * - 其他 → 视为本地路径原样返回\n */\nexport async function resolveAsset(src: string, cacheDir: string): Promise<string> {\n if (src.startsWith(\"internal:\")) {\n return resolveInternalPath(src);\n }\n if (HTTP_URL_RE.test(src)) {\n return downloadAsset(src, cacheDir);\n }\n return src;\n}\n\n/** 下载 URL 资源到缓存目录(返回本地路径)。 */\nasync function downloadAsset(url: string, cacheDir: string): Promise<string> {\n await mkdir(cacheDir, { recursive: true });\n const res = await fetch(url);\n if (!res.ok) {\n throw kernelError(`下载资源失败: ${url}`, \"SEND_FAILED\");\n }\n const buf = Buffer.from(await res.arrayBuffer());\n const rawName = url.split(\"/\").pop() ?? `asset-${Date.now()}`;\n const safeName = rawName.replace(UNSAFE_NAME_RE, \"_\") || `asset-${Date.now()}`;\n const filePath = join(cacheDir, safeName);\n await writeFile(filePath, buf);\n return filePath;\n}\n","/**\n * Satori 媒体元素 → canonical 转换器(从 element-convert.ts 拆分,2026-08-08)\n *\n * img / audio / video / file 四个媒体类型:src → 本地路径(internal: 路径 /\n * http 下载 / 本地原样),audio 额外 silk 转码(非 silk 失败原样返回),\n * video 非 mp4 输入经 ffmpeg 归一化(2026-09-08 T6;ffmpeg 缺失/失败原样)。\n *\n * deps 类型来自 deps.js(独立契约文件,避免与 element-convert 循环引用)。\n */\nimport type { CanonicalElement } from \"@napuketto/kernel\";\nimport { transcodeVideo } from \"@napuketto/media\";\nimport { ensureSilk } from \"../../../core/media.js\";\nimport { resolveAsset } from \"./asset.js\";\nimport type { SatoriToCanonicalDeps } from \"./deps.js\";\nimport type { SatoriElement } from \"./element.js\";\n\n/** 解析元素媒体 src(空 → null 忽略),返回本地路径。 */\nasync function resolveMediaSrc(\n el: SatoriElement,\n deps: SatoriToCanonicalDeps,\n): Promise<string | null> {\n const src = el.attrs?.[\"src\"] ?? \"\";\n if (src === \"\") {\n return null;\n }\n return resolveAsset(src, deps.cacheDir);\n}\n\n/** Satori img → canonical image。 */\nasync function imgToCanonical(\n el: SatoriElement,\n deps: SatoriToCanonicalDeps,\n): Promise<CanonicalElement[]> {\n const path = await resolveMediaSrc(el, deps);\n return path === null ? [] : [{ type: \"image\", path }];\n}\n\n/** Satori audio → canonical voice(非 silk 转码)。 */\nasync function audioToCanonical(\n el: SatoriElement,\n deps: SatoriToCanonicalDeps,\n): Promise<CanonicalElement[]> {\n const path = await resolveMediaSrc(el, deps);\n return path === null ? [] : [{ type: \"voice\", path: await ensureSilk(path) }];\n}\n\n/**\n * Satori video → canonical video(非 mp4 输入 ffmpeg 归一化,fail-soft)。\n *\n * QQ 视频协议固定 mp4/H.264;非 mp4(webm/mov 等)先转码,ffmpeg 缺失或\n * 转码失败原样透传(kernel 发送侧兜底,可能发送失败但不阻断装配)。\n */\nasync function videoToCanonical(\n el: SatoriElement,\n deps: SatoriToCanonicalDeps,\n): Promise<CanonicalElement[]> {\n const path = await resolveMediaSrc(el, deps);\n if (path === null) {\n return [];\n }\n if (path.toLowerCase().endsWith(\".mp4\")) {\n return [{ type: \"video\", path }];\n }\n try {\n return [{ type: \"video\", path: await transcodeVideo(path) }];\n } catch {\n // fail-soft:ffmpeg 缺失/转码失败原样透传\n return [{ type: \"video\", path }];\n }\n}\n\n/** Satori file → canonical file(title → name)。 */\nasync function fileToCanonical(\n el: SatoriElement,\n deps: SatoriToCanonicalDeps,\n): Promise<CanonicalElement[]> {\n const path = await resolveMediaSrc(el, deps);\n if (path === null) {\n return [];\n }\n const title = el.attrs?.[\"title\"];\n const base: CanonicalElement = { type: \"file\", path };\n if (title !== undefined && title !== \"\") {\n return [{ ...base, name: title }];\n }\n return [base];\n}\n\n/** 媒体元素转换器子表(并入 element-convert 主表)。 */\nexport const MEDIA_CONVERTERS = {\n img: imgToCanonical,\n audio: audioToCanonical,\n video: videoToCanonical,\n file: fileToCanonical,\n};\n","/**\n * 发方向转换:Satori 元素 → kernel canonical(从 element.ts 拆分,2026-08-08 FTA 优化)\n *\n * - satoriToCanonicalElements:Satori 元素树 → kernel canonical(at uin→uid + 资源下载/转码)\n * - parseContentToCanonical:消息 content 字符串 → canonical(message.create 用)\n *\n * 收方向(canonical → Satori)在 canonical.ts;解析/渲染(parseElements / renderElements)\n * 留在 element.ts;资源处理(http 下载 / silk 转码 / internal 路径)在 asset.ts;\n * 媒体元素转换器(img/audio/video/file)在 media-convert.ts。\n * 本文件只做发方向元素映射。\n */\nimport type { CanonicalElement } from \"@napuketto/kernel\";\nimport type { SatoriToCanonicalDeps } from \"./deps.js\";\nimport type { SatoriElement } from \"./element.js\";\nimport { parseElements } from \"./element.js\";\nimport { MEDIA_CONVERTERS } from \"./media-convert.js\";\n\nexport type { SatoriToCanonicalDeps } from \"./deps.js\";\n\n/** 发方向:Satori 元素树 → kernel canonical(at uin→uid + 资源下载/转码)。 */\nexport async function satoriToCanonicalElements(\n elements: SatoriElement[],\n deps: SatoriToCanonicalDeps,\n): Promise<CanonicalElement[]> {\n // 预收集 at uin → 批量转换\n const atUins: string[] = [];\n const collectAt = (list: SatoriElement[]): void => {\n for (const el of list) {\n const id = el.attrs?.[\"id\"];\n if (el.type === \"at\" && id !== undefined && id !== \"all\") {\n atUins.push(id);\n }\n if (el.children !== undefined) {\n collectAt(el.children);\n }\n }\n };\n collectAt(elements);\n let uinToUidMap = new Map<string, string>();\n if (atUins.length > 0) {\n try {\n uinToUidMap = await deps.uinToUid([...new Set(atUins)]);\n } catch {\n // uin 解析失败:at 原样(uin),由 kernel 发送时兜底\n }\n }\n const out: CanonicalElement[] = [];\n for (const el of elements) {\n out.push(...(await elementToCanonical(el, deps, uinToUidMap)));\n }\n return out;\n}\n\n/** Satori at → canonical at(uin → uid,name → display)。 */\nfunction atToCanonical(el: SatoriElement, uinToUidMap: Map<string, string>): CanonicalElement[] {\n const id = el.attrs?.[\"id\"] ?? \"\";\n if (id === \"all\") {\n return [{ type: \"at\", target: \"all\" }];\n }\n if (id === \"\") {\n return [];\n }\n const target = uinToUidMap.get(id) ?? id;\n const display = el.attrs?.[\"name\"];\n const base: CanonicalElement = { type: \"at\", target };\n if (display !== undefined && display !== \"\") {\n return [{ ...base, display }];\n }\n return [base];\n}\n\n/** Satori message:forward 属性 → canonical forward;否则展开子元素。 */\nasync function messageToCanonical(\n el: SatoriElement,\n deps: SatoriToCanonicalDeps,\n uinToUidMap: Map<string, string>,\n): Promise<CanonicalElement[]> {\n if (el.attrs?.[\"forward\"] !== undefined) {\n const id = el.attrs?.[\"id\"] ?? \"\";\n return id === \"\" ? [] : [{ type: \"forward\", messageIds: [id] }];\n }\n // 普通 message 元素:展开子元素\n return flattenChildren(el.children, deps, uinToUidMap);\n}\n\n/** 元素转换器(type → 转换逻辑;修饰/排版元素无表项,fallback 展开子元素)。 */\ntype ElementConverter = (\n el: SatoriElement,\n deps: SatoriToCanonicalDeps,\n uinToUidMap: Map<string, string>,\n) => Promise<CanonicalElement[]>;\n\n/** 元素 type → 转换器(未知/修饰/排版类型走 fallback,不在此表)。 */\nconst ELEMENT_CONVERTERS: Record<string, ElementConverter> = {\n text: async (el) => {\n const text = el.text ?? \"\";\n return text === \"\" ? [] : [{ type: \"text\", text }];\n },\n at: async (el, _deps, uinToUidMap) => atToCanonical(el, uinToUidMap),\n emoji: async (el) => {\n const id = el.attrs?.[\"id\"] ?? \"\";\n return id === \"\" ? [] : [{ type: \"face\", id }];\n },\n quote: async (el) => {\n const id = el.attrs?.[\"id\"] ?? \"\";\n return id === \"\" ? [] : [{ type: \"reply\", messageId: id }];\n },\n message: async (el, deps, uinToUidMap) => messageToCanonical(el, deps, uinToUidMap),\n br: async () => [{ type: \"text\", text: \"\\n\" }],\n // QQ 不支持:忽略(button 交互第一版不做)\n author: async () => [],\n sharp: async () => [],\n a: async () => [],\n button: async () => [],\n // 媒体元素(img/audio/video/file):独立文件 media-convert.ts\n ...MEDIA_CONVERTERS,\n};\n\n/** 单个元素 → canonical(修饰/排版元素展开子元素)。 */\nasync function elementToCanonical(\n el: SatoriElement,\n deps: SatoriToCanonicalDeps,\n uinToUidMap: Map<string, string>,\n): Promise<CanonicalElement[]> {\n const converter = ELEMENT_CONVERTERS[el.type];\n if (converter !== undefined) {\n return converter(el, deps, uinToUidMap);\n }\n // 修饰(b/i/u/s/code 等)/排版(p)元素:展开子元素\n return flattenChildren(el.children, deps, uinToUidMap);\n}\n\n/** 展开子元素为 canonical(递归)。 */\nasync function flattenChildren(\n children: SatoriElement[] | undefined,\n deps: SatoriToCanonicalDeps,\n uinToUidMap: Map<string, string>,\n): Promise<CanonicalElement[]> {\n const out: CanonicalElement[] = [];\n for (const child of children ?? []) {\n out.push(...(await elementToCanonical(child, deps, uinToUidMap)));\n }\n return out;\n}\n\n/** 解析消息 content 字符串 → canonical(message.create 用)。 */\nexport async function parseContentToCanonical(\n content: string,\n deps: SatoriToCanonicalDeps,\n): Promise<CanonicalElement[]> {\n const elements = parseElements(content);\n return satoriToCanonicalElements(elements, deps);\n}\n","/**\n * Satori 资源翻译(RawMessage → Satori Message / 频道 / 群组,纯函数 + 注入转换)\n *\n * 收方向 ID 空间:canonical at.target 是 uid,Satori 规范是 uin——经\n * uidToUin 批量转换(依赖注入,与 OB11 的 ReceiveTranslateContext 同思路)。\n */\nimport { ChatType, type RawMessage, toCanonicalElements } from \"@napuketto/kernel\";\nimport type { Channel, Message } from \"../types/resource.js\";\nimport { canonicalToSatoriElements, renderElements } from \"./element/index.js\";\nimport { toDirectChannel, toGroupChannel, toGuild, toUser } from \"./ids.js\";\n\n/** 收方向翻译依赖(uid → uin 转换,缺省 at 原样 uid)。 */\nexport interface SatoriTranslateDeps {\n /** 机器人自身 uin(频道类型判定 / 私聊对端兜底)。 */\n selfUin: string;\n /** uid → uin(at 目标 / 用户 ID;缺省不转换)。 */\n uidToUin?: (uids: string[]) => Promise<Map<string, string>>;\n}\n\n/** RawMessage → Satori Message(资源提升:message 不嵌套 user/member/channel)。 */\nexport async function toSatoriMessage(\n raw: RawMessage,\n deps: SatoriTranslateDeps,\n): Promise<Message> {\n const elementDeps: { uidToUin?: (uids: string[]) => Promise<Map<string, string>> } = {};\n if (deps.uidToUin !== undefined) {\n elementDeps.uidToUin = deps.uidToUin;\n }\n const elements = await canonicalToSatoriElements(toCanonicalElements(raw), elementDeps);\n const content = renderElements(elements);\n const isGroup = raw.chatType === ChatType.GROUP;\n const peerUin = String(raw.peerUin ?? \"\");\n const senderUin = String(raw.senderUin ?? \"\");\n const nickname = raw.sendNickName ?? \"\";\n const memberName = raw.sendMemberName ?? \"\";\n\n const message: Message = { id: raw.msgId };\n if (content !== \"\") {\n message.content = content;\n }\n if (isGroup) {\n message.guild = toGuild(peerUin, raw.peerName);\n message.channel = toGroupChannel(peerUin, raw.peerName);\n message.user = toUser(senderUin, nickname);\n message.member = { user: message.user };\n if (memberName !== \"\" && memberName !== nickname) {\n message.member.nick = memberName;\n }\n } else {\n const peerId = senderUin !== \"\" ? senderUin : peerUin;\n message.channel = toDirectChannel(peerId, nickname);\n message.user = toUser(peerId, nickname);\n }\n const created = Number(raw.msgTime);\n if (Number.isFinite(created) && created > 0) {\n message.created_at = created;\n }\n return message;\n}\n\n/** 按频道 id 构造频道(isGroup 由调用方从事件/缓存得知)。 */\nexport function toChannelById(channelId: string, isGroup: boolean, name?: string): Channel {\n if (isGroup) {\n return toGroupChannel(channelId, name);\n }\n return toDirectChannel(channelId, name);\n}\n","/**\n * Satori 动作基类(响应形态与 OB11 不同:直接返回资源对象,错误用 HTTP 状态码)\n *\n * - `run(payload)`:zod 校验失败 → SatoriActionError(400);_handle 抛的\n * KernelError → satoriHttpStatusMap 映射 HTTP 状态码(ADR-017)\n * - `SatoriActionError`:带 HTTP 状态码的动作错误(transport 层直接透传状态码)\n */\nimport { isKernelError } from \"@napuketto/kernel\";\nimport type { ZodType } from \"zod\";\nimport { satoriHttpStatusMap } from \"../helper/index.js\";\n\n/** 动作错误(带 HTTP 状态码;由 transport 层透传)。 */\nexport class SatoriActionError extends Error {\n readonly status: number;\n\n constructor(status: number, message: string) {\n super(message);\n this.status = status;\n }\n}\n\n/** 校验失败默认状态码。 */\nconst INVALID_PARAM_STATUS = 400;\n/** 兜底错误状态码。 */\nconst UNKNOWN_STATUS = 500;\n\n/**\n * Satori 动作基类:校验 + 执行 + 错误映射。\n * 成功返回资源对象(或数组/分页列表),失败抛 SatoriActionError。\n */\nexport abstract class BaseSatoriAction<TPayload, TReturn> {\n /** 动作名(resource.method,如 \"message.create\")。 */\n abstract readonly name: string;\n /** 参数校验 schema。 */\n abstract readonly schema: ZodType<TPayload>;\n\n /** 校验并执行(HTTP/WS 共用)。 */\n async run(payload: unknown): Promise<TReturn> {\n const parsed = this.schema.safeParse(payload);\n if (!parsed.success) {\n throw new SatoriActionError(INVALID_PARAM_STATUS, \"参数校验失败\");\n }\n try {\n return await this._handle(parsed.data);\n } catch (err) {\n if (err instanceof SatoriActionError) {\n throw err;\n }\n if (isKernelError(err)) {\n throw new SatoriActionError(\n satoriHttpStatusMap[err.code] ?? UNKNOWN_STATUS,\n err.message,\n );\n }\n throw new SatoriActionError(\n UNKNOWN_STATUS,\n err instanceof Error ? err.message : \"未知错误\",\n );\n }\n }\n\n /** 动作实现(只抛错或返回资源;不包装响应)。 */\n protected abstract _handle(payload: TPayload): Promise<TReturn>;\n}\n","/**\n * Satori 频道动作:channel.get / channel.list\n * (channel.create / update / delete / mute:QQ 不支持 → 501,见 registry)\n */\nimport { z } from \"zod\";\nimport type { SatoriApi } from \"../api/index.js\";\nimport { toChannelById } from \"../helper/index.js\";\nimport type { Channel, List } from \"../types/index.js\";\nimport { BaseSatoriAction } from \"./base-action.js\";\n\n/** 动作依赖(Pick<SatoriApi> 视图)。 */\nexport type ChannelActionDeps = Pick<\n SatoriApi,\n \"groupApi\" | \"selfUin\" | \"resolvePeer\" | \"setChannelType\" | \"groupCache\"\n>;\n\n/** channel.get 参数。 */\nconst channelGetSchema = z.object({\n channel_id: z.string(),\n});\n\n/** 获取频道(群聊 = 群号频道;私聊 = uin 频道)。 */\nexport class ChannelGetAction extends BaseSatoriAction<z.infer<typeof channelGetSchema>, Channel> {\n readonly name = \"channel.get\";\n readonly schema = channelGetSchema;\n private readonly deps: ChannelActionDeps;\n\n constructor(deps: ChannelActionDeps) {\n super();\n this.deps = deps;\n }\n\n protected async _handle(payload: z.infer<typeof channelGetSchema>): Promise<Channel> {\n const { channel_id: channelId } = payload;\n const peer = await this.deps.resolvePeer(channelId);\n const isGroup = peer.chatType === 2;\n this.deps.setChannelType(channelId, isGroup);\n if (isGroup) {\n // 群聊频道:尝试补群名(缓存优先)\n const detail = await this.deps.groupCache?.getGroupDetail(channelId);\n return toChannelById(channelId, true, detail?.groupName);\n }\n return toChannelById(channelId, false);\n }\n}\n\n/** channel.list 参数。 */\nconst channelListSchema = z.object({\n guild_id: z.string(),\n next: z.string().optional(),\n});\n\n/** 获取群组频道列表(QQ:群组与群聊频道重合 → 单元素列表)。 */\nexport class ChannelListAction extends BaseSatoriAction<\n z.infer<typeof channelListSchema>,\n List<Channel>\n> {\n readonly name = \"channel.list\";\n readonly schema = channelListSchema;\n private readonly deps: ChannelActionDeps;\n\n constructor(deps: ChannelActionDeps) {\n super();\n this.deps = deps;\n }\n\n protected async _handle(payload: z.infer<typeof channelListSchema>): Promise<List<Channel>> {\n const { guild_id: guildId } = payload;\n // 校验群存在(不存在抛 NOT_FOUND)\n const detail = await this.deps.groupCache?.getGroupDetail(guildId);\n const name = detail?.groupName;\n return {\n data: [toChannelById(guildId, true, name)],\n };\n }\n}\n","/**\n * Satori 好友动作:friend.list / friend.delete / friend.approve\n */\nimport type { BuddyReq } from \"@napuketto/kernel\";\nimport { z } from \"zod\";\nimport type { SatoriApi } from \"../api/index.js\";\nimport { toUser } from \"../helper/index.js\";\nimport type { Friend, List } from \"../types/index.js\";\nimport { BaseSatoriAction } from \"./base-action.js\";\n\n/** 动作依赖(Pick<SatoriApi> 视图)。 */\nexport type FriendActionDeps = Pick<SatoriApi, \"friendApi\" | \"uinToUid\" | \"uidToUin\">;\n\n/** friend.list 参数。 */\nconst friendListSchema = z.object({\n next: z.string().optional(),\n});\n\n/** 获取好友列表。 */\nexport class FriendListAction extends BaseSatoriAction<\n z.infer<typeof friendListSchema>,\n List<Friend>\n> {\n readonly name = \"friend.list\";\n readonly schema = friendListSchema;\n private readonly deps: FriendActionDeps;\n\n constructor(deps: FriendActionDeps) {\n super();\n this.deps = deps;\n }\n\n protected async _handle(_payload: z.infer<typeof friendListSchema>): Promise<List<Friend>> {\n const friends = await this.deps.friendApi.getFriendList();\n const data: Friend[] = friends.map((f) => {\n const user = toUser(f.uin, f.nickname);\n const out: Friend = { user };\n if (f.remark !== \"\" && f.remark !== f.nickname) {\n out.nick = f.remark;\n }\n return out;\n });\n return { data };\n }\n}\n\n/** friend.delete 参数。 */\nconst friendDeleteSchema = z.object({\n user_id: z.string(),\n});\n\n/** 删除好友。 */\nexport class FriendDeleteAction extends BaseSatoriAction<z.infer<typeof friendDeleteSchema>, void> {\n readonly name = \"friend.delete\";\n readonly schema = friendDeleteSchema;\n private readonly deps: FriendActionDeps;\n\n constructor(deps: FriendActionDeps) {\n super();\n this.deps = deps;\n }\n\n protected async _handle(payload: z.infer<typeof friendDeleteSchema>): Promise<void> {\n const { user_id: userId } = payload;\n const uidMap = await this.deps.uinToUid([userId]);\n const uid = uidMap.get(userId) ?? userId;\n await this.deps.friendApi.deleteFriend(uid);\n }\n}\n\n/** friend.approve 参数(处理好友申请)。 */\nconst friendApproveSchema = z.object({\n message_id: z.string(),\n approve: z.boolean(),\n comment: z.string().optional(),\n});\n\n/** 处理好友申请(message_id = 申请方 uin)。 */\nexport class FriendApproveAction extends BaseSatoriAction<\n z.infer<typeof friendApproveSchema>,\n void\n> {\n readonly name = \"friend.approve\";\n readonly schema = friendApproveSchema;\n private readonly deps: FriendActionDeps;\n\n constructor(deps: FriendActionDeps) {\n super();\n this.deps = deps;\n }\n\n protected async _handle(payload: z.infer<typeof friendApproveSchema>): Promise<void> {\n const { message_id: messageId, approve } = payload;\n const notify = await findBuddyReq(this.deps, messageId);\n await this.deps.friendApi.handleFriendRequest(notify, approve);\n }\n}\n\n/** 按 message_id(申请方 uin)查找好友申请。 */\nasync function findBuddyReq(deps: FriendActionDeps, messageId: string): Promise<BuddyReq> {\n const reqs = await deps.friendApi.getBuddyReqList();\n if (reqs.length === 0) {\n throw new Error(\"好友申请不存在\");\n }\n // 批量 uid → uin(friendUid 为 uid,message_id 为 uin)\n const uids = reqs.map((r) => r.friendUid);\n const uinMap = await deps.uidToUin(uids);\n const found = reqs.find((r) => uinMap.get(r.friendUid) === messageId);\n if (found === undefined) {\n throw new Error(\"好友申请不存在\");\n }\n return found;\n}\n","/**\n * Satori 群组动作:guild.get / guild.list / guild.approve\n * + guild.member.get / list / kick / mute / approve\n * (role.set / role.unset / guild.role.*:501,见 registry)\n */\nimport type { GroupMember, GroupNotify } from \"@napuketto/kernel\";\nimport { NTGroupRequestOperateTypes } from \"@napuketto/kernel\";\nimport { z } from \"zod\";\nimport type { SatoriApi } from \"../api/index.js\";\nimport { toGuild, toUser } from \"../helper/index.js\";\nimport type { Guild, GuildMember, List } from \"../types/index.js\";\nimport { BaseSatoriAction } from \"./base-action.js\";\n\n/** 动作依赖(Pick<SatoriApi> 视图)。 */\nexport type GuildActionDeps = Pick<\n SatoriApi,\n \"groupApi\" | \"groupNotifyApi\" | \"uinToUid\" | \"uidToUin\" | \"groupCache\"\n>;\n\n/** 成员列表默认条数。 */\nconst DEFAULT_MEMBER_LIMIT = 100;\n\n/** guild.get 参数。 */\nconst guildGetSchema = z.object({\n guild_id: z.string(),\n});\n\n/** 获取群组。 */\nexport class GuildGetAction extends BaseSatoriAction<z.infer<typeof guildGetSchema>, Guild> {\n readonly name = \"guild.get\";\n readonly schema = guildGetSchema;\n private readonly deps: GuildActionDeps;\n\n constructor(deps: GuildActionDeps) {\n super();\n this.deps = deps;\n }\n\n protected async _handle(payload: z.infer<typeof guildGetSchema>): Promise<Guild> {\n const { guild_id: guildId } = payload;\n const detail = await this.deps.groupApi.getGroupInfo(guildId);\n return toGuild(detail.groupCode, detail.groupName);\n }\n}\n\n/** guild.list 参数。 */\nconst guildListSchema = z.object({\n next: z.string().optional(),\n});\n\n/** 获取群组列表。 */\nexport class GuildListAction extends BaseSatoriAction<\n z.infer<typeof guildListSchema>,\n List<Guild>\n> {\n readonly name = \"guild.list\";\n readonly schema = guildListSchema;\n private readonly deps: GuildActionDeps;\n\n constructor(deps: GuildActionDeps) {\n super();\n this.deps = deps;\n }\n\n protected async _handle(_payload: z.infer<typeof guildListSchema>): Promise<List<Guild>> {\n // 实测校准(2026-08-08):原生 getGroupList 返回值无数据,列表经\n // onGroupListUpdate 事件推送 → GroupCache 维护;缓存为空时主动刷新回填。\n const groups =\n this.deps.groupCache === undefined\n ? await this.deps.groupApi.getGroupList(true)\n : await this.deps.groupCache.listGroupsRefreshed();\n const data: Guild[] = groups.map((g) => toGuild(g.groupCode, g.groupName));\n return { data };\n }\n}\n\n/** guild.approve 参数(处理入群邀请)。 */\nconst guildApproveSchema = z.object({\n message_id: z.string(),\n approve: z.boolean(),\n comment: z.string().optional(),\n});\n\n/** 群请求 approve 基类(guild.approve / guild.member.approve 共用实现)。 */\nabstract class GroupRequestApproveBase extends BaseSatoriAction<\n z.infer<typeof guildApproveSchema>,\n void\n> {\n readonly schema = guildApproveSchema;\n protected readonly deps: GuildActionDeps;\n\n constructor(deps: GuildActionDeps) {\n super();\n this.deps = deps;\n }\n\n protected async _handle(payload: z.infer<typeof guildApproveSchema>): Promise<void> {\n const { message_id: messageId, approve, comment } = payload;\n const notify = await findGroupNotify(this.deps, messageId);\n const operateType = approve\n ? NTGroupRequestOperateTypes.KAGREE\n : NTGroupRequestOperateTypes.KREFUSE;\n await this.deps.groupNotifyApi.handleGroupRequest(\n false,\n notify,\n operateType,\n comment ?? \"\",\n );\n }\n}\n\n/** 处理群邀请(message_id = 群通知 seq)。 */\nexport class GuildApproveAction extends GroupRequestApproveBase {\n readonly name = \"guild.approve\";\n}\n\n/** guild.member.get 参数。 */\nconst guildMemberGetSchema = z.object({\n guild_id: z.string(),\n user_id: z.string(),\n});\n\n/** 获取群成员。 */\nexport class GuildMemberGetAction extends BaseSatoriAction<\n z.infer<typeof guildMemberGetSchema>,\n GuildMember\n> {\n readonly name = \"guild.member.get\";\n readonly schema = guildMemberGetSchema;\n private readonly deps: GuildActionDeps;\n\n constructor(deps: GuildActionDeps) {\n super();\n this.deps = deps;\n }\n\n protected async _handle(payload: z.infer<typeof guildMemberGetSchema>): Promise<GuildMember> {\n const { guild_id: guildId, user_id: userId } = payload;\n const uidMap = await this.deps.uinToUid([userId]);\n const uid = uidMap.get(userId) ?? userId;\n const member = await this.deps.groupCache?.getMember(guildId, uid);\n if (member !== undefined) {\n return toGuildMember(member, userId);\n }\n // 缓存未命中:直查原生(整群拉取后单查)\n const list = await this.deps.groupApi.getGroupMemberInfo(guildId, [uid]);\n const found = list.find((m) => m.uid === uid);\n if (found === undefined) {\n throw new Error(\"群成员不存在\");\n }\n return toGuildMember(found, userId);\n }\n}\n\n/** guild.member.list 参数。 */\nconst guildMemberListSchema = z.object({\n guild_id: z.string(),\n next: z.string().optional(),\n});\n\n/** 获取群成员列表。 */\nexport class GuildMemberListAction extends BaseSatoriAction<\n z.infer<typeof guildMemberListSchema>,\n List<GuildMember>\n> {\n readonly name = \"guild.member.list\";\n readonly schema = guildMemberListSchema;\n private readonly deps: GuildActionDeps;\n\n constructor(deps: GuildActionDeps) {\n super();\n this.deps = deps;\n }\n\n protected async _handle(\n payload: z.infer<typeof guildMemberListSchema>,\n ): Promise<List<GuildMember>> {\n const { guild_id: guildId } = payload;\n let members: GroupMember[];\n const cached = await this.deps.groupCache?.getMembers(guildId);\n if (cached !== undefined && cached.length > 0) {\n members = cached;\n } else {\n members = await this.deps.groupApi.getGroupMemberList(guildId, true);\n }\n const limited = members.slice(0, DEFAULT_MEMBER_LIMIT);\n // 批量 uid → uin(user.id 规范为 uin)\n const uids = limited.map((m) => m.uid);\n const uinMap = await this.deps.uidToUin(uids);\n const data: GuildMember[] = limited.map((m) =>\n toGuildMember(m, uinMap.get(m.uid) ?? m.uin),\n );\n const out: List<GuildMember> = { data };\n if (members.length > DEFAULT_MEMBER_LIMIT) {\n out.next = String(DEFAULT_MEMBER_LIMIT);\n }\n return out;\n }\n}\n\n/** guild.member.kick 参数。 */\nconst guildMemberKickSchema = z.object({\n guild_id: z.string(),\n user_id: z.string(),\n /** 是否永久踢出(无法再次加入)。 */\n permanent: z.boolean().optional(),\n});\n\n/** 踢出群成员。 */\nexport class GuildMemberKickAction extends BaseSatoriAction<\n z.infer<typeof guildMemberKickSchema>,\n void\n> {\n readonly name = \"guild.member.kick\";\n readonly schema = guildMemberKickSchema;\n private readonly deps: GuildActionDeps;\n\n constructor(deps: GuildActionDeps) {\n super();\n this.deps = deps;\n }\n\n protected async _handle(payload: z.infer<typeof guildMemberKickSchema>): Promise<void> {\n const { guild_id: guildId, user_id: userId, permanent } = payload;\n const uidMap = await this.deps.uinToUid([userId]);\n const uid = uidMap.get(userId) ?? userId;\n await this.deps.groupApi.kickMember(guildId, [uid], permanent === true);\n }\n}\n\n/** 毫秒 → 秒(QQ setMemberShutUp 单位;向下取整,0 解除禁言)。 */\nfunction msToSeconds(ms: number): number {\n return Math.max(0, Math.floor(ms / 1000));\n}\n\n/** guild.member.mute 参数。 */\nconst guildMemberMuteSchema = z.object({\n guild_id: z.string(),\n user_id: z.string(),\n /** 禁言时长(毫秒,Satori 规范);0 解除禁言。 */\n duration: z.number(),\n});\n\n/** 群成员禁言(Satori duration 毫秒 → kernel setMemberShutUp 秒)。 */\nexport class GuildMemberMuteAction extends BaseSatoriAction<\n z.infer<typeof guildMemberMuteSchema>,\n void\n> {\n readonly name = \"guild.member.mute\";\n readonly schema = guildMemberMuteSchema;\n private readonly deps: GuildActionDeps;\n\n constructor(deps: GuildActionDeps) {\n super();\n this.deps = deps;\n }\n\n protected async _handle(payload: z.infer<typeof guildMemberMuteSchema>): Promise<void> {\n const { guild_id: guildId, user_id: userId, duration } = payload;\n const uidMap = await this.deps.uinToUid([userId]);\n const uid = uidMap.get(userId) ?? userId;\n await this.deps.groupApi.setMemberShutUp(guildId, [\n { uid, duration: msToSeconds(duration) },\n ]);\n }\n}\n\n/** guild.member.approve 参数(处理加群申请)。 */\nconst guildMemberApproveSchema = z.object({\n message_id: z.string(),\n approve: z.boolean(),\n comment: z.string().optional(),\n});\n\n/** 处理加群申请(message_id = 群通知 seq)。 */\nexport class GuildMemberApproveAction extends GroupRequestApproveBase {\n readonly name = \"guild.member.approve\";\n override readonly schema = guildMemberApproveSchema;\n}\n\n/** 按 message_id(通知 seq)查找群通知。 */\nasync function findGroupNotify(deps: GuildActionDeps, messageId: string): Promise<GroupNotify> {\n const notifies = await deps.groupNotifyApi.getSingleScreenNotifies(false, 50);\n const found = notifies.find((n) => n.seq === messageId);\n if (found === undefined) {\n throw new Error(\"群通知不存在\");\n }\n return found;\n}\n\n/** GroupMember → Satori GuildMember(user.id = uin;nick = 群名片优先)。 */\nfunction toGuildMember(member: GroupMember, uin: string): GuildMember {\n const nick = member.cardName !== \"\" ? member.cardName : member.nick;\n const out: GuildMember = {\n user: toUser(uin, member.nick),\n };\n if (nick !== \"\" && nick !== member.nick) {\n out.nick = nick;\n }\n return out;\n}\n","/**\n * Satori 登录动作:login.get\n */\nimport { z } from \"zod\";\nimport type { SatoriApi } from \"../api/index.js\";\nimport { toLogin } from \"../helper/index.js\";\nimport type { Login } from \"../types/index.js\";\nimport { BaseSatoriAction } from \"./base-action.js\";\n\n/** login.get 参数(无)。 */\nconst loginGetSchema = z.object({});\n\n/** 动作依赖。 */\nexport type LoginActionDeps = Pick<SatoriApi, \"self\">;\n\n/** 获取登录信息。 */\nexport class LoginGetAction extends BaseSatoriAction<z.infer<typeof loginGetSchema>, Login> {\n readonly name = \"login.get\";\n readonly schema = loginGetSchema;\n private readonly deps: LoginActionDeps;\n\n constructor(deps: LoginActionDeps) {\n super();\n this.deps = deps;\n }\n\n protected async _handle(_payload: z.infer<typeof loginGetSchema>): Promise<Login> {\n return toLogin(this.deps.self, 0, true);\n }\n}\n","/**\n * Satori 消息动作:message.create / get / list / delete\n * (message.update:QQ 不支持编辑 → 501,见 registry)\n */\nimport { z } from \"zod\";\nimport type { SatoriApi } from \"../api/index.js\";\nimport { parseContentToCanonical } from \"../helper/element/index.js\";\nimport { toChannelById, toSatoriMessage } from \"../helper/index.js\";\nimport type { BidiList, Message } from \"../types/index.js\";\nimport { BaseSatoriAction } from \"./base-action.js\";\n\n/** 消息列表默认条数(规范推荐 50)。 */\nconst DEFAULT_LIST_LIMIT = 50;\n\n/** 动作依赖(Pick<SatoriApi> 视图)。 */\nexport type MessageActionDeps = Pick<\n SatoriApi,\n \"msgApi\" | \"selfUin\" | \"uidToUin\" | \"resolvePeer\" | \"setChannelType\" | \"toCanonicalDeps\"\n>;\n\n/** message.create 参数。 */\nconst messageCreateSchema = z.object({\n channel_id: z.string(),\n content: z.string(),\n /** 被动请求来源(第一版透传忽略,QQ 不区分主动/被动)。 */\n referrer: z.unknown().optional(),\n});\n\n/** 发送消息:返回 Message 数组(规范)。 */\nexport class MessageCreateAction extends BaseSatoriAction<\n z.infer<typeof messageCreateSchema>,\n Message[]\n> {\n readonly name = \"message.create\";\n readonly schema = messageCreateSchema;\n private readonly deps: MessageActionDeps;\n\n constructor(deps: MessageActionDeps) {\n super();\n this.deps = deps;\n }\n\n protected async _handle(payload: z.infer<typeof messageCreateSchema>): Promise<Message[]> {\n const { channel_id: channelId, content } = payload;\n const peer = await this.deps.resolvePeer(channelId);\n this.deps.setChannelType(channelId, peer.chatType === 2);\n const elements = await parseContentToCanonical(content, this.deps.toCanonicalDeps());\n const { msgId } = await this.deps.msgApi.sendMessage(peer, elements);\n const message: Message = {\n id: msgId,\n channel: toChannelById(channelId, peer.chatType === 2),\n };\n // content 回显原始输入(发送成功即已按协议解析;规范化回显留待后续)\n message.content = content;\n return [message];\n }\n}\n\n/** message.get 参数。 */\nconst messageGetSchema = z.object({\n channel_id: z.string(),\n message_id: z.string(),\n});\n\n/** 获取单条消息。 */\nexport class MessageGetAction extends BaseSatoriAction<z.infer<typeof messageGetSchema>, Message> {\n readonly name = \"message.get\";\n readonly schema = messageGetSchema;\n private readonly deps: MessageActionDeps;\n\n constructor(deps: MessageActionDeps) {\n super();\n this.deps = deps;\n }\n\n protected async _handle(payload: z.infer<typeof messageGetSchema>): Promise<Message> {\n const { channel_id: channelId, message_id: messageId } = payload;\n const peer = await this.deps.resolvePeer(channelId);\n const msgs = await this.deps.msgApi.fetchMsgsByMsgId(peer, [messageId]);\n const first = msgs[0];\n if (first === undefined) {\n throw new Error(\"消息不存在\");\n }\n this.deps.setChannelType(channelId, peer.chatType === 2);\n return toSatoriMessage(first, {\n selfUin: this.deps.selfUin,\n uidToUin: (uids) => this.deps.uidToUin(uids),\n });\n }\n}\n\n/** message.list 参数。 */\nconst messageListSchema = z.object({\n channel_id: z.string(),\n /** 分页令牌(缺省从最新开始)。 */\n next: z.string().optional(),\n /** 查询方向(缺省 before)。 */\n direction: z.enum([\"before\", \"after\", \"around\"]).optional(),\n /** 消息数量限制(缺省平台默认,推荐 50)。 */\n limit: z.number().int().optional(),\n /** 排序(缺省 asc)。 */\n order: z.enum([\"asc\", \"desc\"]).optional(),\n});\n\n/** 获取消息列表(双向分页;第一版仅支持 before 方向)。 */\nexport class MessageListAction extends BaseSatoriAction<\n z.infer<typeof messageListSchema>,\n BidiList<Message>\n> {\n readonly name = \"message.list\";\n readonly schema = messageListSchema;\n private readonly deps: MessageActionDeps;\n\n constructor(deps: MessageActionDeps) {\n super();\n this.deps = deps;\n }\n\n protected async _handle(\n payload: z.infer<typeof messageListSchema>,\n ): Promise<BidiList<Message>> {\n const { channel_id: channelId, next, direction, limit, order } = payload;\n if (direction === \"around\") {\n throw new Error(\"暂不支持 around 方向\");\n }\n const peer = await this.deps.resolvePeer(channelId);\n const count = limit ?? DEFAULT_LIST_LIMIT;\n const raw = await this.deps.msgApi.fetchMessages(peer, {\n count,\n msgId: next ?? \"\",\n });\n this.deps.setChannelType(channelId, peer.chatType === 2);\n // QQ getMsgs 返回时间倒序(最新在前);Satori 默认 asc(旧在前)\n const asc = order !== \"desc\";\n const ordered = asc ? [...raw].reverse() : raw;\n const messages: Message[] = [];\n for (const msg of ordered) {\n messages.push(\n await toSatoriMessage(msg, {\n selfUin: this.deps.selfUin,\n uidToUin: (uids) => this.deps.uidToUin(uids),\n }),\n );\n }\n // 分页令牌:asc 时下一页为更早消息 → 首条(最早)的 msgId;desc 时下一批更新 → 末条 msgId\n const last = messages[messages.length - 1];\n const out: BidiList<Message> = { data: messages };\n if (last !== undefined && raw.length >= count) {\n out.next = last.id;\n }\n return out;\n }\n}\n\n/** message.delete 参数。 */\nconst messageDeleteSchema = z.object({\n channel_id: z.string(),\n message_id: z.string(),\n});\n\n/** 撤回消息。 */\nexport class MessageDeleteAction extends BaseSatoriAction<\n z.infer<typeof messageDeleteSchema>,\n void\n> {\n readonly name = \"message.delete\";\n readonly schema = messageDeleteSchema;\n private readonly deps: MessageActionDeps;\n\n constructor(deps: MessageActionDeps) {\n super();\n this.deps = deps;\n }\n\n protected async _handle(payload: z.infer<typeof messageDeleteSchema>): Promise<void> {\n const { channel_id: channelId, message_id: messageId } = payload;\n const peer = await this.deps.resolvePeer(channelId);\n await this.deps.msgApi.recallMessage(peer, [messageId]);\n }\n}\n","/**\n * 未实现动作(501):平台支持但适配器未实现(规范:501 而非 404)。\n */\nimport { z } from \"zod\";\nimport { BaseSatoriAction, SatoriActionError } from \"./base-action.js\";\n\n/** 未实现动作:schema 接受任意对象,执行抛 501。 */\nexport class NotImplementedAction extends BaseSatoriAction<unknown, never> {\n readonly name: string;\n readonly schema = z.object({}).passthrough();\n\n constructor(name: string) {\n super();\n this.name = name;\n }\n\n protected async _handle(_payload: unknown): Promise<never> {\n throw new SatoriActionError(501, `平台不支持该操作: ${this.name}`);\n }\n}\n","/**\n * Satori 表态动作:reaction.create / reaction.delete\n * (reaction.clear / reaction.list:501,见 registry)\n */\nimport { z } from \"zod\";\nimport type { SatoriApi } from \"../api/index.js\";\nimport { BaseSatoriAction } from \"./base-action.js\";\n\n/** 动作依赖(Pick<SatoriApi> 视图)。 */\nexport type ReactionActionDeps = Pick<SatoriApi, \"msgApi\" | \"resolvePeer\">;\n\n/** 表情 ID 默认类型(QQ 表情 id 为 qq 表情索引)。 */\nconst EMOJI_TYPE = \"1\";\n\n/** reaction.create 参数。 */\nconst reactionCreateSchema = z.object({\n channel_id: z.string(),\n message_id: z.string(),\n emoji_id: z.string(),\n});\n\n/** 添加表态(QQ 表情表态,message_id = NT msgId)。 */\nexport class ReactionCreateAction extends BaseSatoriAction<\n z.infer<typeof reactionCreateSchema>,\n void\n> {\n readonly name = \"reaction.create\";\n readonly schema = reactionCreateSchema;\n private readonly deps: ReactionActionDeps;\n\n constructor(deps: ReactionActionDeps) {\n super();\n this.deps = deps;\n }\n\n protected async _handle(payload: z.infer<typeof reactionCreateSchema>): Promise<void> {\n const { channel_id: channelId, message_id: messageId, emoji_id: emojiId } = payload;\n const peer = await this.deps.resolvePeer(channelId);\n await this.deps.msgApi.setMsgEmojiLike(peer, {\n msgSeq: messageId,\n emojiId,\n emojiType: EMOJI_TYPE,\n like: true,\n });\n }\n}\n\n/** reaction.delete 参数。 */\nconst reactionDeleteSchema = z.object({\n channel_id: z.string(),\n message_id: z.string(),\n emoji_id: z.string(),\n /** 删除指定用户的表态(缺省删除自己的)。 */\n user_id: z.string().optional(),\n});\n\n/** 删除表态(第一版仅支持删除自己的,与 QQ 表情表态能力对齐)。 */\nexport class ReactionDeleteAction extends BaseSatoriAction<\n z.infer<typeof reactionDeleteSchema>,\n void\n> {\n readonly name = \"reaction.delete\";\n readonly schema = reactionDeleteSchema;\n private readonly deps: ReactionActionDeps;\n\n constructor(deps: ReactionActionDeps) {\n super();\n this.deps = deps;\n }\n\n protected async _handle(payload: z.infer<typeof reactionDeleteSchema>): Promise<void> {\n const {\n channel_id: channelId,\n message_id: messageId,\n emoji_id: emojiId,\n user_id: userId,\n } = payload;\n if (userId !== undefined) {\n throw new Error(\"暂不支持删除他人表态\");\n }\n const peer = await this.deps.resolvePeer(channelId);\n await this.deps.msgApi.setMsgEmojiLike(peer, {\n msgSeq: messageId,\n emojiId,\n emojiType: EMOJI_TYPE,\n like: false,\n });\n }\n}\n","/**\n * Satori 用户动作:user.get / user.channel.create\n */\nimport { z } from \"zod\";\nimport type { SatoriApi } from \"../api/index.js\";\nimport { toChannelById, toUser } from \"../helper/index.js\";\nimport type { Channel, User } from \"../types/index.js\";\nimport { BaseSatoriAction } from \"./base-action.js\";\n\n/** 动作依赖(Pick<SatoriApi> 视图)。 */\nexport type UserActionDeps = Pick<SatoriApi, \"profileApi\" | \"groupApi\" | \"groupCache\">;\n\n/** user.get 参数。 */\nconst userGetSchema = z.object({\n user_id: z.string(),\n});\n\n/** 获取用户信息(QQ 平台 user.id = uin)。 */\nexport class UserGetAction extends BaseSatoriAction<z.infer<typeof userGetSchema>, User> {\n readonly name = \"user.get\";\n readonly schema = userGetSchema;\n private readonly deps: UserActionDeps;\n\n constructor(deps: UserActionDeps) {\n super();\n this.deps = deps;\n }\n\n protected async _handle(payload: z.infer<typeof userGetSchema>): Promise<User> {\n const { user_id: userId } = payload;\n // 尝试陌生人信息补昵称(失败退化为基础 User)\n let nickname: string | undefined;\n try {\n const info = await this.deps.profileApi.getStrangerInfo(userId);\n nickname = info.nickname ?? info.long_nick ?? undefined;\n } catch {\n // 陌生人信息不可用(非好友等):仅 ID\n }\n return toUser(userId, nickname);\n }\n}\n\n/** user.channel.create 参数。 */\nconst userChannelCreateSchema = z.object({\n user_id: z.string(),\n guild_id: z.string().optional(),\n});\n\n/** 创建私聊频道(QQ:私聊频道 id = 对端 uin,无需实际创建)。 */\nexport class UserChannelCreateAction extends BaseSatoriAction<\n z.infer<typeof userChannelCreateSchema>,\n Channel\n> {\n readonly name = \"user.channel.create\";\n readonly schema = userChannelCreateSchema;\n\n protected async _handle(payload: z.infer<typeof userChannelCreateSchema>): Promise<Channel> {\n const { user_id: userId } = payload;\n // 私聊频道 id = 对端 uin(与消息事件/消息动作的私聊频道一致)\n return toChannelById(userId, false);\n }\n}\n","/**\n * Satori 动作注册表(ADR-013 延伸):动作依赖收敛为 { api: SatoriApi } 单聚合对象。\n *\n * 501 动作:规范「平台支持但适配器未实现 → 501」——QQ 平台不支持的\n * 能力(消息编辑/频道管理/角色管理等)统一注册 NotImplementedAction。\n */\n\nimport { ActionRegistry } from \"../../core/action-registry.js\";\nimport type { SatoriApi } from \"../api/index.js\";\nimport type { BaseSatoriAction } from \"./base-action.js\";\nimport { type ChannelActionDeps, ChannelGetAction, ChannelListAction } from \"./channel.js\";\nimport {\n type FriendActionDeps,\n FriendApproveAction,\n FriendDeleteAction,\n FriendListAction,\n} from \"./friend.js\";\nimport {\n type GuildActionDeps,\n GuildApproveAction,\n GuildGetAction,\n GuildListAction,\n GuildMemberApproveAction,\n GuildMemberGetAction,\n GuildMemberKickAction,\n GuildMemberListAction,\n GuildMemberMuteAction,\n} from \"./guild.js\";\nimport { type LoginActionDeps, LoginGetAction } from \"./login.js\";\nimport {\n type MessageActionDeps,\n MessageCreateAction,\n MessageDeleteAction,\n MessageGetAction,\n MessageListAction,\n} from \"./message.js\";\nimport { NotImplementedAction } from \"./not-implemented.js\";\nimport { type ReactionActionDeps, ReactionCreateAction, ReactionDeleteAction } from \"./reaction.js\";\nimport type { SatoriActionRegistry } from \"./registry.js\";\nimport type { UserActionDeps } from \"./user.js\";\nimport { UserChannelCreateAction, UserGetAction } from \"./user.js\";\n\n/** 动作注册依赖(单聚合对象)。 */\nexport interface SatoriActionDeps {\n api: SatoriApi;\n}\n\n/** QQ 平台不支持的 Satori 标准动作(501)。 */\nconst NOT_IMPLEMENTED = [\n \"message.update\", // QQ 不支持编辑消息\n \"channel.create\", // QQ 不支持创建频道\n \"channel.update\",\n \"channel.delete\",\n \"channel.mute\",\n \"guild.member.role.set\",\n \"guild.member.role.unset\",\n \"guild.role.list\",\n \"guild.role.create\",\n \"guild.role.update\",\n \"guild.role.delete\",\n \"reaction.clear\",\n \"reaction.list\",\n] as const;\n\n/** 创建 Satori 动作注册表。 */\nexport function createSatoriActionRegistry(deps: SatoriActionDeps): SatoriActionRegistry {\n const registry = new ActionRegistry<BaseSatoriAction<unknown, unknown>>();\n const { api } = deps;\n\n const messageDeps: MessageActionDeps = api;\n registry.register(new MessageCreateAction(messageDeps));\n registry.register(new MessageGetAction(messageDeps));\n registry.register(new MessageListAction(messageDeps));\n registry.register(new MessageDeleteAction(messageDeps));\n\n const channelDeps: ChannelActionDeps = api;\n registry.register(new ChannelGetAction(channelDeps));\n registry.register(new ChannelListAction(channelDeps));\n\n const userDeps: UserActionDeps = api;\n registry.register(new UserGetAction(userDeps));\n registry.register(new UserChannelCreateAction());\n\n const guildDeps: GuildActionDeps = api;\n registry.register(new GuildGetAction(guildDeps));\n registry.register(new GuildListAction(guildDeps));\n registry.register(new GuildApproveAction(guildDeps));\n registry.register(new GuildMemberGetAction(guildDeps));\n registry.register(new GuildMemberListAction(guildDeps));\n registry.register(new GuildMemberKickAction(guildDeps));\n registry.register(new GuildMemberMuteAction(guildDeps));\n registry.register(new GuildMemberApproveAction(guildDeps));\n\n const friendDeps: FriendActionDeps = api;\n registry.register(new FriendListAction(friendDeps));\n registry.register(new FriendDeleteAction(friendDeps));\n registry.register(new FriendApproveAction(friendDeps));\n\n const loginDeps: LoginActionDeps = api;\n registry.register(new LoginGetAction(loginDeps));\n\n const reactionDeps: ReactionActionDeps = api;\n registry.register(new ReactionCreateAction(reactionDeps));\n registry.register(new ReactionDeleteAction(reactionDeps));\n\n for (const name of NOT_IMPLEMENTED) {\n registry.register(new NotImplementedAction(name));\n }\n\n return registry;\n}\n","/**\n * SatoriApi:Satori 动作的统一依赖聚合(对齐 OB11 的 OneBotApi,P2-16 模式)\n *\n * 单类持有 kernel apis + 协议层共享状态(self / cacheDir / 元素转换依赖),\n * 动作只依赖一个聚合对象(或 Pick 视图),装配面只出现一个聚合对象。\n *\n * - 便捷方法 uinToUid / uidToUin 委托 groupApi(at 转换 / 私聊发送用)。\n * - cacheDir 供消息元素发方向资源下载(img/audio/video/file 的 http(s) src)。\n */\nimport type {\n FriendApi,\n GroupApi,\n GroupCache,\n GroupNotifyApi,\n MsgApi,\n ProfileApi,\n} from \"@napuketto/kernel\";\nimport { ChatType, type Peer } from \"@napuketto/kernel\";\nimport type { SatoriToCanonicalDeps } from \"../helper/element/index.js\";\n\n/** SatoriApi 构造参数。 */\nexport interface SatoriApiOptions {\n msgApi: MsgApi;\n groupApi: GroupApi;\n groupNotifyApi: GroupNotifyApi;\n friendApi: FriendApi;\n profileApi: ProfileApi;\n /** 登录身份(login.get / READY logins 用)。 */\n self: { uin: string; nickname: string };\n /** 媒体资源下载缓存目录(发方向 img/audio/video/file 的 http(s) src)。 */\n cacheDir: string;\n /** 群/成员缓存(ADR-008;guild.member.get / user.get 读缓存用)。 */\n groupCache?: GroupCache;\n}\n\n/** Satori 动作统一依赖聚合。 */\nexport class SatoriApi {\n readonly msgApi: MsgApi;\n readonly groupApi: GroupApi;\n readonly groupNotifyApi: GroupNotifyApi;\n readonly friendApi: FriendApi;\n readonly profileApi: ProfileApi;\n readonly self: { uin: string; nickname: string };\n readonly selfUin: string;\n readonly cacheDir: string;\n readonly groupCache: GroupCache | undefined;\n\n /**\n * 频道类型缓存:channel_id → isGroup(群聊频道 id 与私聊 uin 无法从数字\n * 规则区分,需从事件/查询回填——QQ 平台群号与用户 uin 同为数字)。\n */\n private readonly channelTypeCache = new Map<string, boolean>();\n\n constructor(opts: SatoriApiOptions) {\n this.msgApi = opts.msgApi;\n this.groupApi = opts.groupApi;\n this.groupNotifyApi = opts.groupNotifyApi;\n this.friendApi = opts.friendApi;\n this.profileApi = opts.profileApi;\n this.self = opts.self;\n this.selfUin = opts.self.uin;\n this.cacheDir = opts.cacheDir;\n this.groupCache = opts.groupCache;\n }\n\n /** 记录频道类型(消息事件翻译时回填:chatType GROUP → true)。 */\n setChannelType(channelId: string, isGroup: boolean): void {\n this.channelTypeCache.set(channelId, isGroup);\n }\n\n /**\n * 解析 channel_id → Peer(消息动作共用)。\n * 缓存命中直接取类型;未命中先试群号(getGroupInfo 成功 = 群聊),\n * 失败按私聊(uin → uid)。\n */\n async resolvePeer(channelId: string): Promise<Peer> {\n const cached = this.channelTypeCache.get(channelId);\n if (cached === true) {\n return { chatType: ChatType.GROUP, peerUid: channelId };\n }\n if (cached === false) {\n return this.toC2cPeer(channelId);\n }\n const isGroup = await this.groupApi\n .getGroupInfo(channelId)\n .then(() => true)\n .catch(() => false);\n this.channelTypeCache.set(channelId, isGroup);\n if (isGroup) {\n return { chatType: ChatType.GROUP, peerUid: channelId };\n }\n return this.toC2cPeer(channelId);\n }\n\n /** uin → 私聊 Peer(uin→uid 转换;转换失败原样 uin 兜底)。 */\n private async toC2cPeer(uin: string): Promise<Peer> {\n const map = await this.groupApi.uinToUid([uin]);\n const uid = map.get(uin) ?? uin;\n return { chatType: ChatType.C2C, peerUid: uid };\n }\n\n /** uin → uid(委托 groupApi;私聊发送 / at 目标用)。 */\n uinToUid(uins: string[]): Promise<Map<string, string>> {\n return this.groupApi.uinToUid(uins);\n }\n\n /** uid → uin(委托 groupApi;收方向 at / 用户 ID 用)。 */\n uidToUin(uids: string[]): Promise<Map<string, string>> {\n return this.groupApi.uidToUin(uids);\n }\n\n /** 发方向元素转换依赖(at uin→uid + 资源下载目录)。 */\n toCanonicalDeps(): SatoriToCanonicalDeps {\n return {\n uinToUid: (uins) => this.groupApi.uinToUid(uins),\n cacheDir: this.cacheDir,\n };\n }\n}\n","/**\n * Satori 事件翻译(kernel RawMessage → Satori Event 内容,纯函数 + 注入转换)\n *\n * 第一版支持:\n * - message-created:普通消息(Msg/onRecvMsg)\n * - message-deleted:grayTip REVOKE(撤回)\n * - guild-member-added / guild-member-removed:grayTip GROUP(TipGroupElement)\n * (friend-request / guild-request 事件留待后续:grayTip 的 jsonGrayTipElement\n * 结构待运行时探测,OB11 走主动查询 get_group_system_msg 不广播。)\n *\n * 收方向 ID 空间:uid → uin 批量转换(deps.uidToUin,与消息翻译同依赖)。\n */\nimport { ChatType, GrayTipSubType, type RawMessage, TipGroupElementType } from \"@napuketto/kernel\";\nimport { collectGrayTipUids } from \"../../core/gray-tip.js\";\nimport { type SatoriTranslateDeps, toSatoriMessage, toUser } from \"../helper/index.js\";\nimport type { Channel, Guild, GuildMember, Message, User } from \"../types/resource.js\";\n\n/** 事件内容(不含 sn/login,由 adapter 包一层完整 Event)。 */\nexport interface SatoriEventContent {\n type: string;\n timestamp: number;\n channel?: Channel;\n guild?: Guild;\n message?: Message;\n user?: User;\n member?: GuildMember;\n operator?: User;\n}\n\n/** 事件翻译依赖(selfUin + uidToUin)。 */\nexport interface SatoriEventDeps extends SatoriTranslateDeps {}\n\n/** 普通消息 → message-created 事件。 */\nexport async function toSatoriMessageEvent(\n raw: RawMessage,\n deps: SatoriEventDeps,\n): Promise<SatoriEventContent> {\n const message = await toSatoriMessage(raw, deps);\n const content: SatoriEventContent = {\n type: \"message-created\",\n timestamp: Number(raw.msgTime),\n };\n if (message.channel !== undefined) {\n content.channel = message.channel;\n }\n if (message.guild !== undefined) {\n content.guild = message.guild;\n }\n if (message.user !== undefined) {\n content.user = message.user;\n }\n if (message.member !== undefined) {\n content.member = message.member;\n }\n content.message = message;\n return content;\n}\n\n/** 检查消息是否含可翻译的 grayTip(撤回 / 群成员变动)。 */\nexport function hasSatoriGrayTip(raw: RawMessage): boolean {\n for (const el of raw.elements) {\n const g = el.grayTipElement;\n if (g === undefined) {\n continue;\n }\n if (\n g.subElementType === GrayTipSubType.REVOKE ||\n g.subElementType === GrayTipSubType.GROUP\n ) {\n return true;\n }\n }\n return false;\n}\n\n/** 收集 grayTip 涉及的 uid(批量 uidToUin 用,core 共享实现)。 */\nexport const collectSatoriGrayTipUids = collectGrayTipUids;\n\n/** 判断消息是否为群聊 grayTip(peerUid 是群号)。 */\nfunction isGroupMessage(raw: RawMessage): boolean {\n return raw.chatType === ChatType.GROUP;\n}\n\n/** grayTip → 事件(撤回 / 群成员变动;无匹配返回 null)。 */\nexport async function toSatoriGrayTipEvent(\n raw: RawMessage,\n uidToUinMap: Map<string, string>,\n): Promise<SatoriEventContent | null> {\n for (const el of raw.elements) {\n const g = el.grayTipElement;\n if (g === undefined) {\n continue;\n }\n if (g.subElementType === GrayTipSubType.REVOKE) {\n const revoked = toRevokeEvent(raw, uidToUinMap);\n if (revoked !== null) {\n return revoked;\n }\n }\n if (g.subElementType === GrayTipSubType.GROUP && isGroupMessage(raw)) {\n const member = toMemberChangeEvent(raw, uidToUinMap);\n if (member !== null) {\n return member;\n }\n }\n }\n return null;\n}\n\n/** uid → uin(映射缺省退化为 uid)。 */\nfunction toUin(uid: string, uidToUinMap: Map<string, string>): string {\n return uidToUinMap.get(uid) ?? uid;\n}\n\n/** 撤回 → message-deleted 事件。 */\nfunction toRevokeEvent(\n raw: RawMessage,\n uidToUinMap: Map<string, string>,\n): SatoriEventContent | null {\n for (const el of raw.elements) {\n const revoke = el.grayTipElement?.revokeElement;\n if (revoke === undefined) {\n continue;\n }\n const operatorUin = toUin(revoke.operatorUid, uidToUinMap);\n const peerUin = String(raw.peerUin ?? \"\");\n const content: SatoriEventContent = {\n type: \"message-deleted\",\n timestamp: Number(raw.msgTime),\n };\n content.channel = {\n id: peerUin,\n type: isGroupMessage(raw) ? 0 : 1,\n };\n if (isGroupMessage(raw)) {\n content.guild = { id: peerUin, name: raw.peerName };\n }\n // 撤回消息的 id:NT 侧仅 grayTip 的 msgSeq 可用(对齐 OB11 message_id 语义)\n content.message = { id: raw.msgSeq };\n content.operator = toUser(operatorUin, revoke.operatorNick);\n return content;\n }\n return null;\n}\n\n/** 群成员变动 → guild-member-added / guild-member-removed 事件。 */\nfunction toMemberChangeEvent(\n raw: RawMessage,\n uidToUinMap: Map<string, string>,\n): SatoriEventContent | null {\n for (const el of raw.elements) {\n const grp = el.grayTipElement?.groupElement;\n if (grp === undefined || grp.type === undefined) {\n continue;\n }\n const type = memberChangeType(grp.type);\n if (type === null) {\n return null;\n }\n const memberUid = grp.memberUid ?? \"\";\n const adminUid = grp.adminUid ?? \"\";\n const peerUin = String(raw.peerUin ?? \"\");\n const content: SatoriEventContent = {\n type,\n timestamp: Number(raw.msgTime),\n guild: toGroupGuild(peerUin, raw.peerName),\n channel: { id: peerUin, type: 0 },\n member: toMember(memberUid, grp.memberNick, grp.memberRemark, uidToUinMap),\n user: toUser(toUin(memberUid, uidToUinMap), grp.memberNick),\n };\n if (adminUid !== \"\" && adminUid !== memberUid) {\n content.operator = toUser(toUin(adminUid, uidToUinMap), grp.adminNick);\n }\n return content;\n }\n return null;\n}\n\n/** 成员变动类型(MEMBER_ADD=added / QUIT=removed;其余 null)。 */\nfunction memberChangeType(\n type: TipGroupElementType,\n): \"guild-member-added\" | \"guild-member-removed\" | null {\n if (type === TipGroupElementType.MEMBER_ADD) {\n return \"guild-member-added\";\n }\n if (type === TipGroupElementType.QUIT) {\n return \"guild-member-removed\";\n }\n return null;\n}\n\n/** 构造群组(带可选群名)。 */\nfunction toGroupGuild(id: string, name: string | undefined): Guild {\n const guild: Guild = { id };\n if (name !== undefined && name !== \"\") {\n guild.name = name;\n }\n return guild;\n}\n\n/** 构造群成员(user + 可选备注 nick)。 */\nfunction toMember(\n uid: string,\n nick: string | undefined,\n remark: string | undefined,\n uidToUinMap: Map<string, string>,\n): GuildMember {\n const member: GuildMember = { user: toUser(toUin(uid, uidToUinMap), nick) };\n if (remark !== undefined && remark !== \"\" && remark !== nick) {\n member.nick = remark;\n }\n return member;\n}\n","/**\n * Satori 事件与信令类型(协议 v1,规范参考 satori.chat/zh-CN/protocol/events.html)\n *\n * - Opcode:信令类型(EVENT=0 收 / PING=1 发 / PONG=2 收 / IDENTIFY=3 发 / READY=4 收 / META=5 收)\n * - Signal:信令外壳 { op, body }\n * - Event:事件对象(资源提升:message 不嵌套 user/member/channel,平铺顶层)\n */\nimport type {\n Channel,\n Emoji,\n Friend,\n Guild,\n GuildMember,\n GuildRole,\n Login,\n Message,\n User,\n} from \"./resource.js\";\n\n/** 信令类型。 */\nexport const Opcode = {\n EVENT: 0, // 接收:事件\n PING: 1, // 发送:心跳\n PONG: 2, // 接收:心跳回复\n IDENTIFY: 3, // 发送:鉴权\n READY: 4, // 接收:鉴权成功\n META: 5, // 接收:元信息更新(实验性)\n} as const;\nexport type Opcode = (typeof Opcode)[keyof typeof Opcode];\n\n/** 信令外壳。 */\nexport interface Signal<T = unknown> {\n op: Opcode;\n body?: T;\n}\n\n/** IDENTIFY 信令 body(鉴权 + 可选会话恢复)。 */\nexport interface IdentifyBody {\n token?: string;\n sn?: number;\n}\n\n/** READY 信令 body。 */\nexport interface ReadyBody {\n logins: Login[];\n proxy_urls: string[];\n}\n\n/** META 信令 body(不反映登录状态,不含 logins)。 */\nexport interface MetaBody {\n proxy_urls: string[];\n}\n\n/** 交互指令(interaction 事件)。 */\nexport interface Argv {\n name?: string;\n arguments?: string;\n options?: Record<string, string>;\n}\n\n/** 交互按钮。 */\nexport interface Button {\n id?: string;\n}\n\n/** 事件对象(type 为事件类型;各资源字段按规范可选)。 */\nexport interface Event {\n /** 事件序列号(会话内递增,1 起)。 */\n sn: number;\n /** 事件类型(message-created 等)。 */\n type: string;\n /** 事件时间戳(毫秒)。 */\n timestamp: number;\n /** 登录信息(非登录事件只带 sn/user/platform)。 */\n login: Login;\n /** 交互指令(button 等)。 */\n argv?: Argv;\n /** 交互按钮。 */\n button?: Button;\n /** 事件所属频道。 */\n channel?: Channel;\n /** 事件的表情(reaction 事件)。 */\n emoji?: Emoji;\n /** 事件的好友(friend 事件)。 */\n friend?: Friend;\n /** 事件所属群组。 */\n guild?: Guild;\n /** 事件的目标成员。 */\n member?: GuildMember;\n /** 事件的消息。 */\n message?: Message;\n /** 事件的操作者。 */\n operator?: User;\n /** 事件的目标角色。 */\n role?: GuildRole;\n /** 事件的目标用户。 */\n user?: User;\n}\n","/**\n * Satori 传输装配(HTTP RPC + WS 事件服务)\n *\n * - HTTP RPC:HttpServer 的 onPathRequest(network 协议无关扩展)按\n * `/v1/{resource}.{method}` 分发 → 动作注册表 → 响应(错误用 HTTP 状态码)。\n * - WS 事件服务:WsServer(path=/v1/events)处理信令:\n * IDENTIFY → READY(token 校验);PING → PONG;EVENT 经 broadcaster 推送。\n *\n * 错误语义(规范):标准 API 平台不支持 → 404;平台支持但未实现 → 501;\n * 非标准 API → 404;鉴权失败 → 401。\n */\nimport type {\n AuthorizeHook,\n EventBroadcaster,\n HttpRouteContext,\n Respond,\n TransportAdapter,\n} from \"@napuketto/network\";\nimport { HttpServer, WsServer } from \"@napuketto/network\";\nimport { SatoriActionError } from \"./action/base-action.js\";\nimport type { SatoriActionRegistry } from \"./action/registry.js\";\nimport { HTTP_STATUS, type SatoriConfig } from \"./helper/index.js\";\nimport { type Event, type IdentifyBody, type Login, Opcode } from \"./types/index.js\";\n\n/** WS 事件服务路径(规范固定)。 */\nconst WS_EVENTS_PATH = \"/v1/events\";\n/** HTTP RPC 版本前缀。 */\nconst V1_PREFIX = \"/v1/\";\n\n/** Authorization 头前缀。 */\nconst BEARER_PREFIX = \"Bearer \";\n\n/** resource.method 路径模式(resource 与 method 均为字母开头的小写标识符,可含连字符/数字)。 */\nconst ACTION_PATH_RE = /^([a-z][a-z0-9-]*)(?:\\.[a-z][a-z0-9-]*)+$/i;\n\n/** 传输装配结果。 */\nexport interface SatoriTransportSet {\n /** 传输适配器(HTTP RPC + WS 事件服务,均已注册 broadcaster)。 */\n adapters: TransportAdapter[];\n /** 停止:关闭全部传输(幂等)。 */\n close: () => Promise<void>;\n}\n\n/** 传输装配选项。 */\nexport interface AssembleSatoriTransportsOptions {\n /** Satori 配置。 */\n config: SatoriConfig;\n /** 事件广播(WS 事件服务注册到它,EVENT 信令经 broadcastEvent 推送)。 */\n broadcaster: EventBroadcaster;\n /** 动作注册表(HTTP RPC 分发用)。 */\n registry: SatoriActionRegistry;\n /** 登录信息构造(READY logins / meta 用)。 */\n login: () => Login;\n}\n\n/** 按配置装配 Satori 传输。 */\nexport function assembleSatoriTransports(\n opts: AssembleSatoriTransportsOptions,\n): SatoriTransportSet {\n const { config, broadcaster, registry, login } = opts;\n const adapters: TransportAdapter[] = [];\n\n for (const item of config.httpServers) {\n if (!item.enabled) {\n continue;\n }\n const token = item.token ?? config.token;\n const server = new HttpServer({\n host: item.host,\n port: item.port,\n ...(token !== undefined && token !== \"\"\n ? { authorize: makeBearerAuthorize(token) }\n : {}),\n });\n server.onPathRequest = (ctx, req, respond) => {\n handleHttpRequest(ctx, req, respond, registry, login);\n };\n adapters.push(server);\n }\n\n for (const item of config.wsServers) {\n if (!item.enabled) {\n continue;\n }\n const token = item.token ?? config.token;\n const server = new WsServer({\n host: item.host,\n port: item.port,\n path: WS_EVENTS_PATH,\n });\n server.onRequest = (req, respond) => {\n handleWsSignal(req, respond, token, login);\n };\n adapters.push(server);\n }\n\n // 注册事件广播(HTTP server 的 send 为 no-op,注册无害;WS 事件服务接收 EVENT 信令)\n for (const adapter of adapters) {\n broadcaster.register(adapter);\n }\n\n return {\n adapters,\n close: async () => {\n await Promise.all(adapters.map((a) => a.close()));\n for (const adapter of adapters) {\n broadcaster.unregister(adapter);\n }\n },\n };\n}\n\n/** Bearer token 鉴权钩子。 */\nfunction makeBearerAuthorize(token: string): AuthorizeHook {\n return (ctx) => {\n const auth = ctx.headers[\"authorization\"] ?? ctx.headers[\"Authorization\"];\n if (auth?.startsWith(BEARER_PREFIX)) {\n return auth.slice(BEARER_PREFIX.length) === token;\n }\n return false;\n };\n}\n\n/** 解析路径:/v1/{resource}.{method} → 动作名;不匹配返回 null。 */\nfunction parseActionPath(path: string): string | null {\n if (!path.startsWith(V1_PREFIX)) {\n return null;\n }\n const rest = path.slice(V1_PREFIX.length);\n const m = ACTION_PATH_RE.exec(rest);\n if (m === null) {\n return null;\n }\n return rest;\n}\n\n/** HTTP RPC 分发。 */\nfunction handleHttpRequest(\n ctx: HttpRouteContext,\n req: unknown,\n respond: Respond,\n registry: SatoriActionRegistry,\n login: () => Login,\n): void {\n if (ctx.method !== \"POST\") {\n respond({ error: \"method not allowed\" }, 405);\n return;\n }\n const action = parseActionPath(ctx.path);\n if (action === null) {\n respond({ error: \"api not found\" }, HTTP_STATUS.notFound);\n return;\n }\n // 元信息(无需平台头)\n if (action === \"meta\") {\n respond({ logins: [login()], proxy_urls: [] }, HTTP_STATUS.ok);\n return;\n }\n const act = registry.get(action);\n if (act === undefined) {\n respond({ error: \"api not found\" }, HTTP_STATUS.notFound);\n return;\n }\n act.run(req)\n .then((data) => {\n respond(data ?? {}, HTTP_STATUS.ok);\n })\n .catch((err: unknown) => {\n if (err instanceof SatoriActionError) {\n respond({ error: err.message }, err.status);\n return;\n }\n respond(\n { error: err instanceof Error ? err.message : \"internal error\" },\n HTTP_STATUS.serverError,\n );\n });\n}\n\n/** WS 信令处理(IDENTIFY → READY;PING → PONG)。 */\nfunction handleWsSignal(\n signal: unknown,\n respond: Respond,\n token: string | undefined,\n login: () => Login,\n): void {\n const parsed = (signal ?? {}) as { op?: unknown; body?: unknown };\n const op = parsed.op;\n if (op === Opcode.IDENTIFY) {\n const body = (parsed.body ?? {}) as Partial<IdentifyBody>;\n // 鉴权:配置了 token 时校验 IDENTIFY token;失败不响应(客户端超时断开)\n if (token !== undefined && token !== \"\" && body.token !== token) {\n return;\n }\n // 会话恢复(body.sn):第一版跳过,直接 READY 当前状态\n respond({\n op: Opcode.READY,\n body: { logins: [login()], proxy_urls: [] },\n });\n return;\n }\n if (op === Opcode.PING) {\n respond({ op: Opcode.PONG, body: null });\n }\n // 其他信令忽略(EVENT/META 为 SDK 侧接收)\n}\n\n/** 事件信令(adapter 广播用)。 */\nexport function toEventSignal(event: Event): { op: number; body: Event } {\n return { op: Opcode.EVENT, body: event };\n}\n","/**\n * NapukettoSatoriAdapter:Satori 协议适配器(P5,2026-08-08)\n *\n * - 收链路:订阅 kernel 消息事件通道 → 翻译 Satori 事件(message-created /\n * message-deleted / guild-member-added / guild-member-removed)→ WS EVENT 信令广播\n * - 发链路:HTTP RPC(/v1/{resource}.{method})→ 动作注册表 → kernel apis\n *\n * 生命周期走 BaseProtocolAdapter 骨架(start 校验配置 → onStart 装配传输 →\n * onStop 清理)。事件 sn 从 1 递增;非登录事件 login 只带最小字段(规范)。\n */\nimport type { MsgEventChannel, RawMessage } from \"@napuketto/kernel\";\nimport type { EventBroadcaster } from \"@napuketto/network\";\nimport { BaseProtocolAdapter, type ProtocolConfig } from \"../core/index.js\";\nimport { forEachRawMessage } from \"../core/raw-message.js\";\nimport { createSatoriActionRegistry } from \"./action/index.js\";\nimport type { SatoriApiOptions } from \"./api/index.js\";\nimport { SatoriApi } from \"./api/index.js\";\nimport {\n collectSatoriGrayTipUids,\n hasSatoriGrayTip,\n type SatoriEventContent,\n toSatoriGrayTipEvent,\n toSatoriMessageEvent,\n} from \"./event/index.js\";\nimport type { SatoriConfig } from \"./helper/index.js\";\nimport { toLogin, toMinimalLogin } from \"./helper/index.js\";\nimport type { SatoriTransportSet } from \"./transport.js\";\nimport { assembleSatoriTransports, toEventSignal } from \"./transport.js\";\nimport type { Event, Login } from \"./types/index.js\";\n\n/** 适配器构造参数。 */\nexport interface SatoriAdapterOptions extends SatoriApiOptions {\n /** 协议配置(zod 校验 + seed 装配)。 */\n config: ProtocolConfig<SatoriConfig>;\n /** network 事件广播(注册传输后 emit 推给第三方)。 */\n broadcaster: EventBroadcaster;\n /** kernel 消息事件通道(消息收链路入口)。 */\n msgChannel: MsgEventChannel;\n}\n\n/** Satori 协议适配器。 */\nexport class NapukettoSatoriAdapter extends BaseProtocolAdapter<SatoriConfig> {\n readonly protocol = \"satori\";\n\n private readonly msgChannel: MsgEventChannel;\n private readonly api: SatoriApi;\n private readonly registry: ReturnType<typeof createSatoriActionRegistry>;\n private transports: SatoriTransportSet | null = null;\n private unsubscribe: (() => void) | null = null;\n private eventSn = 0;\n\n constructor(opts: SatoriAdapterOptions) {\n super({\n config: opts.config,\n broadcaster: opts.broadcaster,\n hooks: {\n onStart: (config) => this.startTransports(config as SatoriConfig),\n onStop: () => this.stopAll(),\n onReload: (config) => this.reloadTransports(config as SatoriConfig),\n },\n });\n this.msgChannel = opts.msgChannel;\n this.api = new SatoriApi(opts);\n // 动作注册表(HTTP RPC 分发)\n this.registry = createSatoriActionRegistry({ api: this.api });\n }\n\n /** 启动传输:装配(HTTP RPC + WS 事件服务)+ 打开 server + 订阅消息事件。 */\n private async startTransports(config: SatoriConfig): Promise<void> {\n const broadcaster = this.getBroadcaster();\n if (broadcaster !== undefined) {\n // 装配传输(onPathRequest / onRequest 分发已挂 registry)\n this.transports = assembleSatoriTransports({\n config,\n broadcaster,\n registry: this.registry,\n login: () => this.currentLogin(),\n });\n await Promise.all(this.transports.adapters.map((t) => t.open()));\n }\n this.subscribe();\n // 广播 login-updated(启动 → 在线)\n this.broadcastLoginUpdated(true);\n }\n\n /** 当前登录信息(READY logins / meta / login.get 用)。 */\n private currentLogin(): Login {\n return toLogin(this.api.self, 0, true);\n }\n\n /**\n * 配置热更新(2026-09-08 实现):stop 旧传输/退订(广播 login-updated 离线)→\n * 按新配置重建(广播在线)。Satori 无 IPC 桥模式,无传输时同样走重建路径。\n */\n private async reloadTransports(config: SatoriConfig): Promise<void> {\n await this.stopAll();\n await this.startTransports(config);\n }\n\n /** 广播 login-updated 事件(适配器启停时)。 */\n private broadcastLoginUpdated(online: boolean): void {\n const login = toLogin(this.api.self, 0, online);\n this.broadcastEvent(\n toEventSignal({\n sn: this.nextSn(),\n type: \"login-updated\",\n timestamp: Date.now(),\n login,\n }),\n );\n }\n\n /** 停止:退订 + 关闭传输 + 广播 login-updated(离线)。 */\n private async stopAll(): Promise<void> {\n this.unsubscribeAll();\n if (this.transports !== null) {\n await this.transports.close();\n this.transports = null;\n }\n this.broadcastLoginUpdated(false);\n }\n\n /** 订阅 kernel 消息事件(幂等)。 */\n private subscribe(): void {\n if (this.unsubscribe !== null) {\n return;\n }\n this.unsubscribe = this.msgChannel.on(\"Msg/onRecvMsg\", (msgs) => {\n forEachRawMessage(msgs, (msg) => {\n // grayTip(系统事件)→ 撤回/群成员变动事件;否则 → 消息事件\n if (hasSatoriGrayTip(msg)) {\n this.broadcastGrayTip(msg).catch(() => {\n // grayTip 翻译失败静默(结构探测期宽容)\n });\n return;\n }\n void this.broadcastMessageCreated(msg);\n });\n });\n }\n\n /** 广播消息事件(message-created)。 */\n private async broadcastMessageCreated(msg: RawMessage): Promise<void> {\n const content = await toSatoriMessageEvent(msg, {\n selfUin: this.api.selfUin,\n uidToUin: (uids) => this.api.uidToUin(uids),\n });\n // 回填频道类型缓存(收方向已知 chatType)\n this.api.setChannelType(String(msg.peerUin ?? \"\"), msg.chatType === 2);\n this.broadcastContent(content);\n }\n\n /** 广播 grayTip 事件(message-deleted / guild-member-added / removed)。 */\n private async broadcastGrayTip(msg: RawMessage): Promise<void> {\n const uids = collectSatoriGrayTipUids(msg);\n let uidToUin = new Map<string, string>();\n if (uids.length > 0) {\n uidToUin = await this.api.uidToUin(uids);\n }\n const content = await toSatoriGrayTipEvent(msg, uidToUin);\n if (content !== null) {\n this.broadcastContent(content);\n }\n }\n\n /** 广播事件内容(sn 递增 + 最小 login 包装 + EVENT 信令)。 */\n private broadcastContent(content: SatoriEventContent): void {\n this.broadcastEvent(\n toEventSignal({\n sn: this.nextSn(),\n type: content.type,\n timestamp: content.timestamp,\n login: toMinimalLogin(this.eventSn, this.api.selfUin),\n ...pickEventResources(content),\n }),\n );\n }\n\n /** 事件 sn(递增,从 1 起)。 */\n private nextSn(): number {\n this.eventSn += 1;\n return this.eventSn;\n }\n\n /** 退订(幂等)。 */\n private unsubscribeAll(): void {\n this.unsubscribe?.();\n this.unsubscribe = null;\n }\n}\n\n/** 从事件内容提取可选资源字段(channel/guild/message/user/member/operator)。 */\nfunction pickEventResources(\n content: SatoriEventContent,\n): Pick<Event, \"channel\" | \"guild\" | \"message\" | \"user\" | \"member\" | \"operator\"> {\n const out: Pick<Event, \"channel\" | \"guild\" | \"message\" | \"user\" | \"member\" | \"operator\"> = {};\n if (content.channel !== undefined) {\n out.channel = content.channel;\n }\n if (content.guild !== undefined) {\n out.guild = content.guild;\n }\n if (content.message !== undefined) {\n out.message = content.message;\n }\n if (content.user !== undefined) {\n out.user = content.user;\n }\n if (content.member !== undefined) {\n out.member = content.member;\n }\n if (content.operator !== undefined) {\n out.operator = content.operator;\n }\n return out;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../../src/satori/helper/config.ts","../../src/satori/helper/error.ts","../../src/satori/helper/ids.ts","../../src/satori/helper/element/canonical.ts","../../src/satori/helper/element/parse-tag.ts","../../src/satori/helper/element/element.ts","../../src/satori/helper/element/asset.ts","../../src/satori/helper/element/media-convert.ts","../../src/satori/helper/element/element-convert.ts","../../src/satori/helper/translate.ts","../../src/satori/action/base-action.ts","../../src/satori/action/channel.ts","../../src/satori/action/friend.ts","../../src/satori/action/guild.ts","../../src/satori/action/login.ts","../../src/satori/action/message.ts","../../src/satori/action/not-implemented.ts","../../src/satori/action/reaction.ts","../../src/satori/action/user.ts","../../src/satori/action/index.ts","../../src/satori/api/satori-api.ts","../../src/satori/event/translate.ts","../../src/satori/types/event.ts","../../src/satori/transport.ts","../../src/satori/adapter.ts"],"sourcesContent":["/**\n * Satori 协议配置(zod schema,归属本包,ADR-012)\n * 协议配置 schema 在各自协议包,kernel 只提供 ConfigBase 基类。\n */\nimport { z } from \"zod\";\nimport { createHttpServerSchema, createWsServerSchema } from \"../../core/transport-schema.js\";\n\n/** HTTP RPC 服务器默认端口(satori.chat 惯例 5500 区间,取 5500)。 */\nconst DEFAULT_HTTP_PORT = 5500;\n/** WS 事件服务默认端口(satori.chat 惯例 5500 区间,取 5501)。 */\nconst DEFAULT_WS_PORT = 5501;\n\n/** HTTP RPC 服务器实例(POST /v1/{resource}.{method})。 */\nconst httpServerSchema = createHttpServerSchema(DEFAULT_HTTP_PORT);\n\n/** WS 事件服务实例(/v1/events 信令,第三方 SDK 连入)。 */\nconst wsServerSchema = createWsServerSchema(DEFAULT_WS_PORT);\n\n/** Satori 配置 schema。 */\nexport const satoriConfigSchema = z.object({\n /** 鉴权 token(全局默认,实例未指定时继承)。 */\n token: z.string().optional(),\n /** 媒体资源(img/audio/video/file)下载缓存目录。 */\n cacheDir: z.string().optional(),\n /** HTTP RPC 服务器列表。 */\n httpServers: z.array(httpServerSchema).default([]),\n /** WS 事件服务列表。 */\n wsServers: z.array(wsServerSchema).default([]),\n});\n\n/** Satori 配置类型(由 schema 推导)。 */\nexport type SatoriConfig = z.infer<typeof satoriConfigSchema>;\n","/**\n * Satori 错误映射(ADR-017):KernelErrorCode → HTTP 状态码\n * 与 OB11 的 retcode 不同,Satori 直接用 HTTP 状态码表达错误。\n * 动作未实现 → 501;API 不存在 → 404;鉴权失败 → 401。\n */\nimport type { KernelErrorCode } from \"@napuketto/kernel\";\n\n/** KernelErrorCode → HTTP 状态码映射表。 */\nexport const satoriHttpStatusMap: Record<KernelErrorCode, number> = {\n SEND_FAILED: 500,\n PERMISSION_DENIED: 403,\n NOT_FOUND: 404,\n TIMEOUT: 500,\n NOT_LOGIN: 403,\n INVALID_PARAM: 400,\n INVALID_STATE: 500,\n UNKNOWN: 500,\n};\n\n/** HTTP 状态码常量。 */\nexport const HTTP_STATUS = {\n ok: 200,\n badRequest: 400,\n unauthorized: 401,\n forbidden: 403,\n notFound: 404,\n notImplemented: 501,\n serverError: 500,\n} as const;\n","/**\n * Satori 频道/登录 ID 构造(QQ 平台语义)\n *\n * - 群聊:guild.id = channel.id = 群号,channel.type = TEXT(guild.plain)\n * - 私聊:channel.id = 对端 uin,channel.type = DIRECT,无 guild\n * - user.id = uin;message.id = NT msgId\n * - login.user.id = 机器人 uin\n */\nimport type { Channel, ChannelType, Guild, Login, User } from \"../types/resource.js\";\n\n/** 平台名(固定 qq)。 */\nexport const PLATFORM = \"qq\";\n/** 适配器名(固定 napuketto)。 */\nexport const ADAPTER = \"napuketto\";\n\n/** 平台特性(API 可用性判断;群聊频道与群组重合 → guild.plain)。 */\nexport const FEATURES = [\n \"guild.plain\", // 群组内只能存在一个消息频道\n \"message.list.from\", // message.list 支持以消息 ID 作分页令牌\n] as const;\n\n/** 构造用户(uin)。 */\nexport function toUser(id: string, name?: string, nick?: string): User {\n const user: User = { id };\n if (name !== undefined && name !== \"\") {\n user.name = name;\n }\n if (nick !== undefined && nick !== \"\") {\n user.nick = nick;\n }\n return user;\n}\n\n/** 构造群聊频道(id = 群号)。 */\nexport function toGroupChannel(groupCode: string, name?: string): Channel {\n const channel: Channel = { id: String(groupCode), type: 0 as ChannelType };\n if (name !== undefined && name !== \"\") {\n channel.name = name;\n }\n return channel;\n}\n\n/** 构造私聊频道(id = 对端 uin)。 */\nexport function toDirectChannel(uin: string, name?: string): Channel {\n const channel: Channel = { id: String(uin), type: 1 as ChannelType };\n if (name !== undefined && name !== \"\") {\n channel.name = name;\n }\n return channel;\n}\n\n/** 构造群组(id = 群号)。 */\nexport function toGuild(groupCode: string, name?: string, avatar?: string): Guild {\n const guild: Guild = { id: String(groupCode) };\n if (name !== undefined && name !== \"\") {\n guild.name = name;\n }\n if (avatar !== undefined && avatar !== \"\") {\n guild.avatar = avatar;\n }\n return guild;\n}\n\n/** 构造登录信息(login.get / READY logins)。 */\nexport function toLogin(\n self: { uin: string; nickname: string },\n sn: number,\n online: boolean,\n): Login {\n const login: Login = {\n sn,\n platform: PLATFORM,\n user: toUser(self.uin, self.nickname),\n status: online ? 1 : 0,\n adapter: ADAPTER,\n features: [...FEATURES],\n };\n return login;\n}\n\n/** 构造非登录事件的最小 login(只带 sn/user/platform,规范)。 */\nexport function toMinimalLogin(sn: number, selfUin: string): Login {\n return {\n sn,\n platform: PLATFORM,\n user: toUser(selfUin),\n status: 1,\n adapter: ADAPTER,\n };\n}\n","/**\n * 收方向转换:kernel canonical → Satori 元素(从 element-convert.ts 拆分,2026-08-08 FTA 优化)\n *\n * canonicalToSatoriElements:纯函数映射,宽容忽略 unknown(json/xml 无法表达)。\n * at 目标为 uid 时经 uidToUin 批量转换(异步,失败原样 uid)。\n */\nimport type { CanonicalElement } from \"@napuketto/kernel\";\nimport type { SatoriElement } from \"./element.js\";\n\n/** 收方向依赖(at uid 解析)。 */\nexport interface CanonicalToSatoriDeps {\n /** uid → uin(at 目标显示用;未提供原样返回 uid)。 */\n uidToUin?: (uids: string[]) => Promise<Map<string, string>>;\n}\n\n/** 收集 at uid → 批量 uidToUin(失败不阻塞,at 原样 uid)。 */\nasync function resolveAtUids(\n elements: CanonicalElement[],\n uidToUin: CanonicalToSatoriDeps[\"uidToUin\"],\n): Promise<Map<string, string>> {\n const atUids: string[] = [];\n for (const el of elements) {\n if (el.type === \"at\" && el.target !== \"all\") {\n atUids.push(el.target);\n }\n }\n if (atUids.length === 0 || uidToUin === undefined) {\n return new Map();\n }\n try {\n return await uidToUin([...new Set(atUids)]);\n } catch {\n // uid 解析失败:at 原样(uid),不阻塞事件翻译\n return new Map();\n }\n}\n\n/** canonical at → Satori at(uid → uin,name = display)。 */\nfunction atToSatori(\n el: Extract<CanonicalElement, { type: \"at\" }>,\n uinMap: Map<string, string>,\n): SatoriElement {\n if (el.target === \"all\") {\n return { type: \"at\", attrs: { id: \"all\" } };\n }\n const attrs: Record<string, string> = {\n id: uinMap.get(el.target) ?? el.target,\n };\n if (el.display !== undefined && el.display !== \"\") {\n attrs[\"name\"] = el.display;\n }\n return { type: \"at\", attrs };\n}\n\n/** canonical file → Satori file(title = name)。 */\nfunction fileToSatori(el: Extract<CanonicalElement, { type: \"file\" }>): SatoriElement {\n const attrs: Record<string, string> = { src: el.path };\n if (el.name !== undefined && el.name !== \"\") {\n attrs[\"title\"] = el.name;\n }\n return { type: \"file\", attrs };\n}\n\n/** canonical forward → Satori message(取首个消息 id)。 */\nfunction forwardToSatori(el: Extract<CanonicalElement, { type: \"forward\" }>): SatoriElement | null {\n const first = el.messageIds[0];\n if (first === undefined || first === \"\") {\n return null;\n }\n return { type: \"message\", attrs: { forward: \"\", id: first } };\n}\n\n/** 单个 canonical → Satori(null = 宽容忽略,如 json/xml/unknown)。 */\nfunction canonicalToSatori(\n el: CanonicalElement,\n uinMap: Map<string, string>,\n): SatoriElement | null {\n switch (el.type) {\n case \"text\":\n return el.text === \"\" ? null : { type: \"text\", text: el.text };\n case \"at\":\n return atToSatori(el, uinMap);\n case \"image\":\n return { type: \"img\", attrs: { src: el.url ?? el.path } };\n case \"face\":\n return { type: \"emoji\", attrs: { id: el.id } };\n case \"voice\":\n return { type: \"audio\", attrs: { src: el.path } };\n case \"video\":\n return { type: \"video\", attrs: { src: el.url ?? el.path } };\n case \"file\":\n return fileToSatori(el);\n case \"reply\":\n return { type: \"quote\", attrs: { id: el.messageId } };\n case \"forward\":\n return forwardToSatori(el);\n case \"json\":\n case \"xml\":\n case \"unknown\":\n // 无法表达:忽略(宽容)\n return null;\n }\n}\n\n/**\n * 收方向:kernel canonical 元素 → Satori 元素树(纯函数映射,宽容忽略 unknown)。\n * at 目标为 uid 时经 uidToUin 转换(异步);无转换器时原样。\n */\nexport async function canonicalToSatoriElements(\n elements: CanonicalElement[],\n deps: CanonicalToSatoriDeps = {},\n): Promise<SatoriElement[]> {\n const uinMap = await resolveAtUids(elements, deps.uidToUin);\n const out: SatoriElement[] = [];\n for (const el of elements) {\n const converted = canonicalToSatori(el, uinMap);\n if (converted !== null) {\n out.push(converted);\n }\n }\n return out;\n}\n","/**\n * Satori 标签读取(元素名 / 属性解析,从 element.ts 拆分,2026-08-08 FTA 优化)\n *\n * - decodeEntity/decodeEntities:命名/十进制/十六进制实体解码(属性值用)\n * - readTagName:读取元素名(小写字母/数字/连字符,以字母开头)\n * - readTagAttrs / readAttr / skipSpaces:标签属性区解析(无值属性 / 引号值 / 无引号值)\n * - readTag:完整标签(名 + 属性 + 自闭合指示)\n */\n\n/** 命名实体(规范规定的四个 + apos 宽容)。 */\nconst NAMED_ENTITIES: Record<string, string> = {\n quot: '\"',\n amp: \"&\",\n lt: \"<\",\n gt: \">\",\n apos: \"'\",\n};\n\n/** 解码单个实体(命名/十进制/十六进制)。 */\nfunction decodeEntity(body: string): string {\n const named = NAMED_ENTITIES[body];\n if (named !== undefined) {\n return named;\n }\n if (body.startsWith(\"#x\") || body.startsWith(\"#X\")) {\n const code = Number.parseInt(body.slice(2), 16);\n if (Number.isInteger(code)) {\n return String.fromCodePoint(code);\n }\n }\n if (body.startsWith(\"#\")) {\n const code = Number.parseInt(body.slice(1), 10);\n if (Number.isInteger(code)) {\n return String.fromCodePoint(code);\n }\n }\n // 未识别实体原样保留\n return `&${body};`;\n}\n\n/** 解码字符串中的全部实体。 */\nconst ENTITY_RE = /&([^;]+);/g;\n\n/** 解码字符串中的全部实体。 */\nfunction decodeEntities(input: string): string {\n return input.replace(ENTITY_RE, (_full, body: string) => decodeEntity(body));\n}\n\n/** 元素名:小写字母/数字/连字符,以字母开头(规范)。 */\nconst NAME_RE = /^[a-z][a-z0-9-]*/i;\n\n/** 读取元素名(pos 起);未匹配返回空名。 */\nexport function readTagName(source: string, pos: number): { name: string; end: number } {\n const rest = source.slice(pos);\n const m = NAME_RE.exec(rest);\n if (m === null) {\n return { name: \"\", end: pos };\n }\n return { name: m[0], end: pos + m[0].length };\n}\n\n/** 空白字符。 */\nconst SPACE_RE = /\\s/;\n\n/** 跳过空白;返回下一个非空白位置。 */\nfunction skipSpaces(source: string, start: number): number {\n let i = start;\n while (i < source.length && SPACE_RE.test(source.charAt(i))) {\n i += 1;\n }\n return i;\n}\n\n/** 属性名(字母数字 / 冒号(命名空间)/ 连字符 / 下划线)。 */\nconst ATTR_NAME_RE = /^[a-zA-Z0-9:_-]+/;\n/** 无引号属性值的结束字符(空白 / >)。 */\nconst ATTR_VALUE_END_RE = /[\\s>]/;\n\n/** 读取单个属性(名 / = / 值 / 无值属性),更新 attrs,返回下一个位置。 */\nfunction readAttr(source: string, start: number, attrs: Record<string, string>): number {\n // 属性名(字母数字 / 冒号(命名空间)/ 连字符 / 下划线)\n const keyMatch = ATTR_NAME_RE.exec(source.slice(start));\n if (keyMatch === null) {\n return start + 1;\n }\n const key = keyMatch[0];\n let i = skipSpaces(source, start + key.length);\n if (source.charAt(i) !== \"=\") {\n // 无值属性\n attrs[key] = \"\";\n return i;\n }\n i = skipSpaces(source, i + 1);\n const quote = source.charAt(i);\n if (quote === '\"' || quote === \"'\") {\n i += 1;\n const valueStart = i;\n while (i < source.length && source.charAt(i) !== quote) {\n i += 1;\n }\n attrs[key] = decodeEntities(source.slice(valueStart, i));\n if (i < source.length) {\n i += 1; // 跳过结束引号\n }\n } else {\n // 无引号值(到空白或 > 为止)\n const valueStart = i;\n while (i < source.length && !ATTR_VALUE_END_RE.test(source.charAt(i))) {\n i += 1;\n }\n attrs[key] = decodeEntities(source.slice(valueStart, i));\n }\n return i;\n}\n\n/** 读取标签属性区(到 > 或 /> 为止),返回属性表与闭合指示、结束位置。 */\nfunction readTagAttrs(\n source: string,\n start: number,\n): { attrs: Record<string, string>; selfClosing: boolean; end: number } {\n const attrs: Record<string, string> = {};\n let i = start;\n let selfClosing = false;\n while (i < source.length) {\n const ch = source.charAt(i);\n if (ch === \">\") {\n i += 1;\n break;\n }\n if (ch === \"/\") {\n if (source.charAt(i + 1) === \">\") {\n selfClosing = true;\n i += 2;\n break;\n }\n i += 1;\n continue;\n }\n if (SPACE_RE.test(ch)) {\n i += 1;\n continue;\n }\n i = readAttr(source, i, attrs);\n }\n return { attrs, selfClosing, end: i };\n}\n\n/** 读取完整标签(元素名 + 属性 + 闭合指示),返回属性表与结束位置。 */\nexport function readTag(\n source: string,\n pos: number,\n): { name: string; attrs: Record<string, string>; selfClosing: boolean; end: number } {\n const { name, end } = readTagName(source, pos);\n if (name === \"\") {\n return { name: \"\", attrs: {}, selfClosing: false, end: pos };\n }\n const { attrs, selfClosing, end: attrEnd } = readTagAttrs(source, end);\n return { name, attrs, selfClosing, end: attrEnd };\n}\n","/**\n * Satori 消息元素解析/渲染(协议 v1,自研实现,规范参考 satori.chat/zh-CN/protocol/message.html)\n *\n * - parseElements:XML 风格字符串 → 元素树(注释/转义/无值属性/未配对标签容错)\n * - renderElements:元素树 → 字符串(消息 content 序列化)\n *\n * canonical ↔ Satori 双向转换不在本文件:收方向(canonical → Satori)在\n * canonical.ts,发方向(Satori → canonical)在 element-convert.ts,调用方直引。\n *\n * 语法要点(对齐规范):\n * - 转义:" & < > ' ' 与十进制/十六进制数字实体\n * - 注释 <!-- --> 不渲染\n * - 无值属性(key)与 key=\"value\" / key='value' 均合法\n * - 未配对的标签视为文本内容的一部分\n * - 文本内容前后包含换行符的连续空白会被忽略\n */\nimport { readTag, readTagName } from \"./parse-tag.js\";\n\n/** Satori 消息元素(与规范 satori-parse 形状一致;type=\"text\" 为文本节点)。 */\nexport interface SatoriElement {\n type: string;\n attrs?: Record<string, string>;\n children?: SatoriElement[];\n text?: string;\n}\n\n/** 文本转义正则(& < >)。 */\nconst TEXT_ESCAPE_RE = /[&<>]/g;\n\n/** 文本转义(& < >)。 */\nfunction encodeText(input: string): string {\n return input.replace(TEXT_ESCAPE_RE, (ch) => {\n if (ch === \"&\") {\n return \"&\";\n }\n if (ch === \"<\") {\n return \"<\";\n }\n return \">\";\n });\n}\n\n/** 属性值转义正则(& \" < >)。 */\nconst ATTR_ESCAPE_RE = /[&\"<>]/g;\n\n/** 属性值转义(& \" < >)。 */\nfunction encodeAttr(input: string): string {\n return input.replace(ATTR_ESCAPE_RE, (ch) => {\n if (ch === \"&\") {\n return \"&\";\n }\n if (ch === '\"') {\n return \""\";\n }\n if (ch === \"<\") {\n return \"<\";\n }\n return \">\";\n });\n}\n\n/** 行首连续空白。 */\nconst LEADING_WS_RE = /^\\s+/;\n/** 行尾连续空白。 */\nconst TRAILING_WS_RE = /\\s+$/;\n/** 换行符。 */\nconst NEWLINE_RE = /[\\r\\n]/;\n\n/**\n * 文本规范化:开头/结尾的「包含换行符的连续空白」被忽略(规范),\n * 中间的空白与不含换行的首尾空白保留。\n */\nfunction normalizeText(raw: string): string {\n const leading = LEADING_WS_RE.exec(raw);\n const trailing = TRAILING_WS_RE.exec(raw);\n let out = raw;\n if (leading !== null && NEWLINE_RE.test(leading[0])) {\n out = out.slice(leading[0].length);\n }\n if (trailing !== null && NEWLINE_RE.test(trailing[0])) {\n out = out.slice(0, -trailing[0].length);\n }\n return out;\n}\n\n/** Satori 消息解析器状态(挂载点 / 文本缓冲 / 栈管理,拆分主循环复杂度)。 */\nclass ElementParser {\n private readonly root: SatoriElement[] = [];\n private readonly stack: SatoriElement[] = [];\n private text = \"\";\n\n /** 当前挂载数组(栈顶元素 children 或根数组)。 */\n private mountPoint(): SatoriElement[] {\n const top = this.stack[this.stack.length - 1];\n if (top === undefined) {\n return this.root;\n }\n top.children ??= [];\n return top.children;\n }\n\n /** 归一化文本落位到当前挂载点。 */\n pushText(): void {\n const normalized = normalizeText(this.text);\n this.text = \"\";\n if (normalized !== \"\") {\n this.mountPoint().push({ type: \"text\", text: normalized });\n }\n }\n\n /** 解析完成的元素树。 */\n get elements(): SatoriElement[] {\n return this.root;\n }\n\n /** 追加原始文本(解析间隙累积)。 */\n appendRaw(raw: string): void {\n this.text += raw;\n }\n\n /** 起始/自闭合标签落位(压栈或仅挂载)。 */\n openTag(name: string, attrs: Record<string, string>, selfClosing: boolean): void {\n const el: SatoriElement = { type: name, attrs };\n this.mountPoint().push(el);\n if (!selfClosing) {\n this.stack.push(el);\n }\n }\n\n /** 收尾:剩余文本落位(栈剩余未闭合元素:文本落在栈顶元素内,宽容)。 */\n flush(): void {\n this.pushText();\n }\n\n /** 注释:返回跳过后的位置;-1 = 未闭合终止解析(注释不渲染)。 */\n skipComment(source: string, pos: number): number {\n const end = source.indexOf(\"-->\", pos);\n return end === -1 ? -1 : end + 3;\n }\n\n /**\n * 结束标签:匹配栈顶则闭合,否则按文本保留。\n * @returns 下一个解析位置;-1 = 终止解析。\n */\n closeTag(source: string, lt: number, slashPos: number): number {\n const { name, end } = readTagName(source, slashPos + 1);\n if (name === \"\") {\n // 非法结束标签(</> 等):< 按文本保留\n this.text += \"<\";\n return lt + 1;\n }\n const gt = source.indexOf(\">\", end);\n if (gt === -1) {\n this.text += source.slice(lt);\n return -1;\n }\n const top = this.stack[this.stack.length - 1];\n if (top !== undefined && top.type === name) {\n // 闭合前的文本推入该元素\n this.pushText();\n this.stack.pop();\n } else {\n // 未配对:原样当文本\n this.text += source.slice(lt, gt + 1);\n }\n return gt + 1;\n }\n}\n\n/**\n * 解析 Satori 消息字符串 → 元素树。\n * 容错:未配对标签/非法标签按文本保留;解析不抛错(消息内容宽容)。\n */\nexport function parseElements(source: string): SatoriElement[] {\n const parser = new ElementParser();\n let i = 0;\n while (i < source.length) {\n const lt = source.indexOf(\"<\", i);\n if (lt === -1) {\n parser.appendRaw(source.slice(i));\n break;\n }\n parser.appendRaw(source.slice(i, lt));\n i = lt + 1;\n // 注释:不渲染\n if (source.startsWith(\"!--\", i)) {\n const next = parser.skipComment(source, i);\n if (next === -1) {\n break;\n }\n i = next;\n continue;\n }\n // 结束标签\n if (source.startsWith(\"/\", i)) {\n const next = parser.closeTag(source, lt, i);\n if (next === -1) {\n break;\n }\n i = next;\n continue;\n }\n // 起始 / 自闭合标签\n const tag = readTag(source, i);\n if (tag.name === \"\") {\n parser.appendRaw(\"<\");\n continue;\n }\n parser.pushText();\n parser.openTag(tag.name, tag.attrs, tag.selfClosing);\n i = tag.end;\n }\n parser.flush();\n return parser.elements;\n}\n\n/** 元素树 → Satori 消息字符串。 */\nexport function renderElements(elements: SatoriElement[]): string {\n let out = \"\";\n for (const el of elements) {\n if (el.type === \"text\") {\n out += encodeText(el.text ?? \"\");\n continue;\n }\n out += renderElement(el);\n }\n return out;\n}\n\n/** 渲染单个元素(含子元素递归)。 */\nfunction renderElement(el: SatoriElement): string {\n let attrs = \"\";\n for (const [key, value] of Object.entries(el.attrs ?? {})) {\n if (value === \"\") {\n // 无值属性(forward 等布尔属性)\n attrs += ` ${key}`;\n } else {\n attrs += ` ${key}=\"${encodeAttr(value)}\"`;\n }\n }\n const children = el.children ?? [];\n if (children.length === 0) {\n return `<${el.type}${attrs}/>`;\n }\n const inner = renderElements(children);\n return `<${el.type}${attrs}>${inner}</${el.type}>`;\n}\n","/**\n * Satori 发方向资源处理(从 element-convert.ts 拆分,2026-08-08 FTA 优化)\n *\n * - resolveAsset:internal: 路径 / http(s) 下载 / 本地路径原样\n * - ensureSilk 已上移到 adapter core(core/media.ts,onebot11/satori 共用)\n */\nimport { mkdir, writeFile } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { kernelError } from \"@napuketto/kernel\";\n\n/** internal:{platform}/{user.id}/{path} → 本地路径(取 _tmp 之后的路径,回落 cacheDir)。 */\nfunction resolveInternalPath(src: string): string {\n const pathPart = src.slice(\"internal:\".length);\n const slash1 = pathPart.indexOf(\"/\");\n const slash2 = slash1 === -1 ? -1 : pathPart.indexOf(\"/\", slash1 + 1);\n if (slash2 === -1) {\n return pathPart;\n }\n const rest = pathPart.slice(slash2 + 1);\n // 去掉 _tmp/ 前缀段,回落到 cacheDir\n const tmpIdx = rest.indexOf(\"_tmp/\");\n return tmpIdx === -1 ? rest : rest.slice(tmpIdx + 5);\n}\n\n/** http(s) URL 前缀。 */\nconst HTTP_URL_RE = /^https?:\\/\\//i;\n/** 文件名不安全字符(URL 尾部段转文件名时替换)。 */\nconst UNSAFE_NAME_RE = /[^a-zA-Z0-9._-]/g;\n\n/**\n * 解析资源 src:\n * - internal:{platform}/{user.id}/{path} → 本地路径(第一版仅支持 _tmp 保留路径)\n * - http(s):// → 下载到 cacheDir\n * - 其他 → 视为本地路径原样返回\n */\nexport async function resolveAsset(src: string, cacheDir: string): Promise<string> {\n if (src.startsWith(\"internal:\")) {\n return resolveInternalPath(src);\n }\n if (HTTP_URL_RE.test(src)) {\n return downloadAsset(src, cacheDir);\n }\n return src;\n}\n\n/** 下载 URL 资源到缓存目录(返回本地路径)。 */\nasync function downloadAsset(url: string, cacheDir: string): Promise<string> {\n await mkdir(cacheDir, { recursive: true });\n const res = await fetch(url);\n if (!res.ok) {\n throw kernelError(`下载资源失败: ${url}`, \"SEND_FAILED\");\n }\n const buf = Buffer.from(await res.arrayBuffer());\n const rawName = url.split(\"/\").pop() ?? `asset-${Date.now()}`;\n const safeName = rawName.replace(UNSAFE_NAME_RE, \"_\") || `asset-${Date.now()}`;\n const filePath = join(cacheDir, safeName);\n await writeFile(filePath, buf);\n return filePath;\n}\n","/**\n * Satori 媒体元素 → canonical 转换器(从 element-convert.ts 拆分,2026-08-08)\n *\n * img / audio / video / file 四个媒体类型:src → 本地路径(internal: 路径 /\n * http 下载 / 本地原样),audio 额外 silk 转码(非 silk 失败原样返回),\n * video 非 mp4 输入经 ffmpeg 归一化(2026-09-08 T6;ffmpeg 缺失/失败原样)。\n *\n * deps 类型来自 deps.js(独立契约文件,避免与 element-convert 循环引用)。\n */\nimport type { CanonicalElement } from \"@napuketto/kernel\";\nimport { transcodeVideo } from \"@napuketto/media\";\nimport { ensureSilk } from \"../../../core/media.js\";\nimport { resolveAsset } from \"./asset.js\";\nimport type { SatoriToCanonicalDeps } from \"./deps.js\";\nimport type { SatoriElement } from \"./element.js\";\n\n/** 解析元素媒体 src(空 → null 忽略),返回本地路径。 */\nasync function resolveMediaSrc(\n el: SatoriElement,\n deps: SatoriToCanonicalDeps,\n): Promise<string | null> {\n const src = el.attrs?.[\"src\"] ?? \"\";\n if (src === \"\") {\n return null;\n }\n return resolveAsset(src, deps.cacheDir);\n}\n\n/** Satori img → canonical image。 */\nasync function imgToCanonical(\n el: SatoriElement,\n deps: SatoriToCanonicalDeps,\n): Promise<CanonicalElement[]> {\n const path = await resolveMediaSrc(el, deps);\n return path === null ? [] : [{ type: \"image\", path }];\n}\n\n/** Satori audio → canonical voice(非 silk 转码)。 */\nasync function audioToCanonical(\n el: SatoriElement,\n deps: SatoriToCanonicalDeps,\n): Promise<CanonicalElement[]> {\n const path = await resolveMediaSrc(el, deps);\n return path === null ? [] : [{ type: \"voice\", path: await ensureSilk(path) }];\n}\n\n/**\n * Satori video → canonical video(非 mp4 输入 ffmpeg 归一化,fail-soft)。\n *\n * QQ 视频协议固定 mp4/H.264;非 mp4(webm/mov 等)先转码,ffmpeg 缺失或\n * 转码失败原样透传(kernel 发送侧兜底,可能发送失败但不阻断装配)。\n */\nasync function videoToCanonical(\n el: SatoriElement,\n deps: SatoriToCanonicalDeps,\n): Promise<CanonicalElement[]> {\n const path = await resolveMediaSrc(el, deps);\n if (path === null) {\n return [];\n }\n if (path.toLowerCase().endsWith(\".mp4\")) {\n return [{ type: \"video\", path }];\n }\n try {\n return [{ type: \"video\", path: await transcodeVideo(path) }];\n } catch {\n // fail-soft:ffmpeg 缺失/转码失败原样透传\n return [{ type: \"video\", path }];\n }\n}\n\n/** Satori file → canonical file(title → name)。 */\nasync function fileToCanonical(\n el: SatoriElement,\n deps: SatoriToCanonicalDeps,\n): Promise<CanonicalElement[]> {\n const path = await resolveMediaSrc(el, deps);\n if (path === null) {\n return [];\n }\n const title = el.attrs?.[\"title\"];\n const base: CanonicalElement = { type: \"file\", path };\n if (title !== undefined && title !== \"\") {\n return [{ ...base, name: title }];\n }\n return [base];\n}\n\n/** 媒体元素转换器子表(并入 element-convert 主表)。 */\nexport const MEDIA_CONVERTERS = {\n img: imgToCanonical,\n audio: audioToCanonical,\n video: videoToCanonical,\n file: fileToCanonical,\n};\n","/**\n * 发方向转换:Satori 元素 → kernel canonical(从 element.ts 拆分,2026-08-08 FTA 优化)\n *\n * - satoriToCanonicalElements:Satori 元素树 → kernel canonical(at uin→uid + 资源下载/转码)\n * - parseContentToCanonical:消息 content 字符串 → canonical(message.create 用)\n *\n * 收方向(canonical → Satori)在 canonical.ts;解析/渲染(parseElements / renderElements)\n * 留在 element.ts;资源处理(http 下载 / silk 转码 / internal 路径)在 asset.ts;\n * 媒体元素转换器(img/audio/video/file)在 media-convert.ts。\n * 本文件只做发方向元素映射。\n */\nimport type { CanonicalElement } from \"@napuketto/kernel\";\nimport type { SatoriToCanonicalDeps } from \"./deps.js\";\nimport type { SatoriElement } from \"./element.js\";\nimport { parseElements } from \"./element.js\";\nimport { MEDIA_CONVERTERS } from \"./media-convert.js\";\n\nexport type { SatoriToCanonicalDeps } from \"./deps.js\";\n\n/** 发方向:Satori 元素树 → kernel canonical(at uin→uid + 资源下载/转码)。 */\nexport async function satoriToCanonicalElements(\n elements: SatoriElement[],\n deps: SatoriToCanonicalDeps,\n): Promise<CanonicalElement[]> {\n // 预收集 at uin → 批量转换\n const atUins: string[] = [];\n const collectAt = (list: SatoriElement[]): void => {\n for (const el of list) {\n const id = el.attrs?.[\"id\"];\n if (el.type === \"at\" && id !== undefined && id !== \"all\") {\n atUins.push(id);\n }\n if (el.children !== undefined) {\n collectAt(el.children);\n }\n }\n };\n collectAt(elements);\n let uinToUidMap = new Map<string, string>();\n if (atUins.length > 0) {\n try {\n uinToUidMap = await deps.uinToUid([...new Set(atUins)]);\n } catch {\n // uin 解析失败:at 原样(uin),由 kernel 发送时兜底\n }\n }\n const out: CanonicalElement[] = [];\n for (const el of elements) {\n out.push(...(await elementToCanonical(el, deps, uinToUidMap)));\n }\n return out;\n}\n\n/** Satori at → canonical at(uin → uid,name → display)。 */\nfunction atToCanonical(el: SatoriElement, uinToUidMap: Map<string, string>): CanonicalElement[] {\n const id = el.attrs?.[\"id\"] ?? \"\";\n if (id === \"all\") {\n return [{ type: \"at\", target: \"all\" }];\n }\n if (id === \"\") {\n return [];\n }\n const target = uinToUidMap.get(id) ?? id;\n const display = el.attrs?.[\"name\"];\n const base: CanonicalElement = { type: \"at\", target };\n if (display !== undefined && display !== \"\") {\n return [{ ...base, display }];\n }\n return [base];\n}\n\n/** Satori message:forward 属性 → canonical forward;否则展开子元素。 */\nasync function messageToCanonical(\n el: SatoriElement,\n deps: SatoriToCanonicalDeps,\n uinToUidMap: Map<string, string>,\n): Promise<CanonicalElement[]> {\n if (el.attrs?.[\"forward\"] !== undefined) {\n const id = el.attrs?.[\"id\"] ?? \"\";\n return id === \"\" ? [] : [{ type: \"forward\", messageIds: [id] }];\n }\n // 普通 message 元素:展开子元素\n return flattenChildren(el.children, deps, uinToUidMap);\n}\n\n/** 元素转换器(type → 转换逻辑;修饰/排版元素无表项,fallback 展开子元素)。 */\ntype ElementConverter = (\n el: SatoriElement,\n deps: SatoriToCanonicalDeps,\n uinToUidMap: Map<string, string>,\n) => Promise<CanonicalElement[]>;\n\n/** 元素 type → 转换器(未知/修饰/排版类型走 fallback,不在此表)。 */\nconst ELEMENT_CONVERTERS: Record<string, ElementConverter> = {\n text: async (el) => {\n const text = el.text ?? \"\";\n return text === \"\" ? [] : [{ type: \"text\", text }];\n },\n at: async (el, _deps, uinToUidMap) => atToCanonical(el, uinToUidMap),\n emoji: async (el) => {\n const id = el.attrs?.[\"id\"] ?? \"\";\n return id === \"\" ? [] : [{ type: \"face\", id }];\n },\n quote: async (el) => {\n const id = el.attrs?.[\"id\"] ?? \"\";\n return id === \"\" ? [] : [{ type: \"reply\", messageId: id }];\n },\n message: async (el, deps, uinToUidMap) => messageToCanonical(el, deps, uinToUidMap),\n br: async () => [{ type: \"text\", text: \"\\n\" }],\n // QQ 不支持:忽略(button 交互第一版不做)\n author: async () => [],\n sharp: async () => [],\n a: async () => [],\n button: async () => [],\n // 媒体元素(img/audio/video/file):独立文件 media-convert.ts\n ...MEDIA_CONVERTERS,\n};\n\n/** 单个元素 → canonical(修饰/排版元素展开子元素)。 */\nasync function elementToCanonical(\n el: SatoriElement,\n deps: SatoriToCanonicalDeps,\n uinToUidMap: Map<string, string>,\n): Promise<CanonicalElement[]> {\n const converter = ELEMENT_CONVERTERS[el.type];\n if (converter !== undefined) {\n return converter(el, deps, uinToUidMap);\n }\n // 修饰(b/i/u/s/code 等)/排版(p)元素:展开子元素\n return flattenChildren(el.children, deps, uinToUidMap);\n}\n\n/** 展开子元素为 canonical(递归)。 */\nasync function flattenChildren(\n children: SatoriElement[] | undefined,\n deps: SatoriToCanonicalDeps,\n uinToUidMap: Map<string, string>,\n): Promise<CanonicalElement[]> {\n const out: CanonicalElement[] = [];\n for (const child of children ?? []) {\n out.push(...(await elementToCanonical(child, deps, uinToUidMap)));\n }\n return out;\n}\n\n/** 解析消息 content 字符串 → canonical(message.create 用)。 */\nexport async function parseContentToCanonical(\n content: string,\n deps: SatoriToCanonicalDeps,\n): Promise<CanonicalElement[]> {\n const elements = parseElements(content);\n return satoriToCanonicalElements(elements, deps);\n}\n","/**\n * Satori 资源翻译(RawMessage → Satori Message / 频道 / 群组,纯函数 + 注入转换)\n *\n * 收方向 ID 空间:canonical at.target 是 uid,Satori 规范是 uin——经\n * uidToUin 批量转换(依赖注入,与 OB11 的 ReceiveTranslateContext 同思路)。\n */\nimport { ChatType, type RawMessage, toCanonicalElements } from \"@napuketto/kernel\";\nimport type { Channel, Message } from \"../types/resource.js\";\nimport { canonicalToSatoriElements, renderElements } from \"./element/index.js\";\nimport { toDirectChannel, toGroupChannel, toGuild, toUser } from \"./ids.js\";\n\n/** 收方向翻译依赖(uid → uin 转换,缺省 at 原样 uid)。 */\nexport interface SatoriTranslateDeps {\n /** 机器人自身 uin(频道类型判定 / 私聊对端兜底)。 */\n selfUin: string;\n /** uid → uin(at 目标 / 用户 ID;缺省不转换)。 */\n uidToUin?: (uids: string[]) => Promise<Map<string, string>>;\n}\n\n/** RawMessage → Satori Message(资源提升:message 不嵌套 user/member/channel)。 */\nexport async function toSatoriMessage(\n raw: RawMessage,\n deps: SatoriTranslateDeps,\n): Promise<Message> {\n const elementDeps: { uidToUin?: (uids: string[]) => Promise<Map<string, string>> } = {};\n if (deps.uidToUin !== undefined) {\n elementDeps.uidToUin = deps.uidToUin;\n }\n const elements = await canonicalToSatoriElements(toCanonicalElements(raw), elementDeps);\n const content = renderElements(elements);\n const isGroup = raw.chatType === ChatType.GROUP;\n const peerUin = String(raw.peerUin ?? \"\");\n const senderUin = String(raw.senderUin ?? \"\");\n const nickname = raw.sendNickName ?? \"\";\n const memberName = raw.sendMemberName ?? \"\";\n\n const message: Message = { id: raw.msgId };\n if (content !== \"\") {\n message.content = content;\n }\n if (isGroup) {\n message.guild = toGuild(peerUin, raw.peerName);\n message.channel = toGroupChannel(peerUin, raw.peerName);\n message.user = toUser(senderUin, nickname);\n message.member = { user: message.user };\n if (memberName !== \"\" && memberName !== nickname) {\n message.member.nick = memberName;\n }\n } else {\n const peerId = senderUin !== \"\" ? senderUin : peerUin;\n message.channel = toDirectChannel(peerId, nickname);\n message.user = toUser(peerId, nickname);\n }\n const created = Number(raw.msgTime);\n if (Number.isFinite(created) && created > 0) {\n message.created_at = created;\n }\n return message;\n}\n\n/** 按频道 id 构造频道(isGroup 由调用方从事件/缓存得知)。 */\nexport function toChannelById(channelId: string, isGroup: boolean, name?: string): Channel {\n if (isGroup) {\n return toGroupChannel(channelId, name);\n }\n return toDirectChannel(channelId, name);\n}\n","/**\n * Satori 动作基类(响应形态与 OB11 不同:直接返回资源对象,错误用 HTTP 状态码)\n *\n * - `run(payload)`:zod 校验失败 → SatoriActionError(400);_handle 抛的\n * KernelError → satoriHttpStatusMap 映射 HTTP 状态码(ADR-017)\n * - `SatoriActionError`:带 HTTP 状态码的动作错误(transport 层直接透传状态码)\n */\nimport { isKernelError } from \"@napuketto/kernel\";\nimport type { ZodType } from \"zod\";\nimport { satoriHttpStatusMap } from \"../helper/index.js\";\n\n/** 动作错误(带 HTTP 状态码;由 transport 层透传)。 */\nexport class SatoriActionError extends Error {\n readonly status: number;\n\n constructor(status: number, message: string) {\n super(message);\n this.status = status;\n }\n}\n\n/** 校验失败默认状态码。 */\nconst INVALID_PARAM_STATUS = 400;\n/** 兜底错误状态码。 */\nconst UNKNOWN_STATUS = 500;\n\n/**\n * Satori 动作基类:校验 + 执行 + 错误映射。\n * 成功返回资源对象(或数组/分页列表),失败抛 SatoriActionError。\n */\nexport abstract class BaseSatoriAction<TPayload, TReturn> {\n /** 动作名(resource.method,如 \"message.create\")。 */\n abstract readonly name: string;\n /** 参数校验 schema。 */\n abstract readonly schema: ZodType<TPayload>;\n\n /** 校验并执行(HTTP/WS 共用)。 */\n async run(payload: unknown): Promise<TReturn> {\n const parsed = this.schema.safeParse(payload);\n if (!parsed.success) {\n throw new SatoriActionError(INVALID_PARAM_STATUS, \"参数校验失败\");\n }\n try {\n return await this._handle(parsed.data);\n } catch (err) {\n if (err instanceof SatoriActionError) {\n throw err;\n }\n if (isKernelError(err)) {\n throw new SatoriActionError(\n satoriHttpStatusMap[err.code] ?? UNKNOWN_STATUS,\n err.message,\n );\n }\n throw new SatoriActionError(\n UNKNOWN_STATUS,\n err instanceof Error ? err.message : \"未知错误\",\n );\n }\n }\n\n /** 动作实现(只抛错或返回资源;不包装响应)。 */\n protected abstract _handle(payload: TPayload): Promise<TReturn>;\n}\n","/**\n * Satori 频道动作:channel.get / channel.list\n * (channel.create / update / delete / mute:QQ 不支持 → 501,见 registry)\n */\nimport { z } from \"zod\";\nimport type { SatoriApi } from \"../api/index.js\";\nimport { toChannelById } from \"../helper/index.js\";\nimport type { Channel, List } from \"../types/index.js\";\nimport { BaseSatoriAction } from \"./base-action.js\";\n\n/** 动作依赖(Pick<SatoriApi> 视图)。 */\nexport type ChannelActionDeps = Pick<\n SatoriApi,\n \"groupApi\" | \"selfUin\" | \"resolvePeer\" | \"setChannelType\" | \"groupCache\"\n>;\n\n/** channel.get 参数。 */\nconst channelGetSchema = z.object({\n channel_id: z.string(),\n});\n\n/** 获取频道(群聊 = 群号频道;私聊 = uin 频道)。 */\nexport class ChannelGetAction extends BaseSatoriAction<z.infer<typeof channelGetSchema>, Channel> {\n readonly name = \"channel.get\";\n readonly schema = channelGetSchema;\n private readonly deps: ChannelActionDeps;\n\n constructor(deps: ChannelActionDeps) {\n super();\n this.deps = deps;\n }\n\n protected async _handle(payload: z.infer<typeof channelGetSchema>): Promise<Channel> {\n const { channel_id: channelId } = payload;\n const peer = await this.deps.resolvePeer(channelId);\n const isGroup = peer.chatType === 2;\n this.deps.setChannelType(channelId, isGroup);\n if (isGroup) {\n // 群聊频道:尝试补群名(缓存优先)\n const detail = await this.deps.groupCache?.getGroupDetail(channelId);\n return toChannelById(channelId, true, detail?.groupName);\n }\n return toChannelById(channelId, false);\n }\n}\n\n/** channel.list 参数。 */\nconst channelListSchema = z.object({\n guild_id: z.string(),\n next: z.string().optional(),\n});\n\n/** 获取群组频道列表(QQ:群组与群聊频道重合 → 单元素列表)。 */\nexport class ChannelListAction extends BaseSatoriAction<\n z.infer<typeof channelListSchema>,\n List<Channel>\n> {\n readonly name = \"channel.list\";\n readonly schema = channelListSchema;\n private readonly deps: ChannelActionDeps;\n\n constructor(deps: ChannelActionDeps) {\n super();\n this.deps = deps;\n }\n\n protected async _handle(payload: z.infer<typeof channelListSchema>): Promise<List<Channel>> {\n const { guild_id: guildId } = payload;\n // 校验群存在(不存在抛 NOT_FOUND)\n const detail = await this.deps.groupCache?.getGroupDetail(guildId);\n const name = detail?.groupName;\n return {\n data: [toChannelById(guildId, true, name)],\n };\n }\n}\n","/**\n * Satori 好友动作:friend.list / friend.delete / friend.approve\n */\nimport type { BuddyReq } from \"@napuketto/kernel\";\nimport { z } from \"zod\";\nimport type { SatoriApi } from \"../api/index.js\";\nimport { toUser } from \"../helper/index.js\";\nimport type { Friend, List } from \"../types/index.js\";\nimport { BaseSatoriAction } from \"./base-action.js\";\n\n/** 动作依赖(Pick<SatoriApi> 视图)。 */\nexport type FriendActionDeps = Pick<SatoriApi, \"friendApi\" | \"uinToUid\" | \"uidToUin\">;\n\n/** friend.list 参数。 */\nconst friendListSchema = z.object({\n next: z.string().optional(),\n});\n\n/** 获取好友列表。 */\nexport class FriendListAction extends BaseSatoriAction<\n z.infer<typeof friendListSchema>,\n List<Friend>\n> {\n readonly name = \"friend.list\";\n readonly schema = friendListSchema;\n private readonly deps: FriendActionDeps;\n\n constructor(deps: FriendActionDeps) {\n super();\n this.deps = deps;\n }\n\n protected async _handle(_payload: z.infer<typeof friendListSchema>): Promise<List<Friend>> {\n const friends = await this.deps.friendApi.getFriendList();\n const data: Friend[] = friends.map((f) => {\n const user = toUser(f.uin, f.nickname);\n const out: Friend = { user };\n if (f.remark !== \"\" && f.remark !== f.nickname) {\n out.nick = f.remark;\n }\n return out;\n });\n return { data };\n }\n}\n\n/** friend.delete 参数。 */\nconst friendDeleteSchema = z.object({\n user_id: z.string(),\n});\n\n/** 删除好友。 */\nexport class FriendDeleteAction extends BaseSatoriAction<z.infer<typeof friendDeleteSchema>, void> {\n readonly name = \"friend.delete\";\n readonly schema = friendDeleteSchema;\n private readonly deps: FriendActionDeps;\n\n constructor(deps: FriendActionDeps) {\n super();\n this.deps = deps;\n }\n\n protected async _handle(payload: z.infer<typeof friendDeleteSchema>): Promise<void> {\n const { user_id: userId } = payload;\n const uidMap = await this.deps.uinToUid([userId]);\n const uid = uidMap.get(userId) ?? userId;\n await this.deps.friendApi.deleteFriend(uid);\n }\n}\n\n/** friend.approve 参数(处理好友申请)。 */\nconst friendApproveSchema = z.object({\n message_id: z.string(),\n approve: z.boolean(),\n comment: z.string().optional(),\n});\n\n/** 处理好友申请(message_id = 申请方 uin)。 */\nexport class FriendApproveAction extends BaseSatoriAction<\n z.infer<typeof friendApproveSchema>,\n void\n> {\n readonly name = \"friend.approve\";\n readonly schema = friendApproveSchema;\n private readonly deps: FriendActionDeps;\n\n constructor(deps: FriendActionDeps) {\n super();\n this.deps = deps;\n }\n\n protected async _handle(payload: z.infer<typeof friendApproveSchema>): Promise<void> {\n const { message_id: messageId, approve } = payload;\n const notify = await findBuddyReq(this.deps, messageId);\n await this.deps.friendApi.handleFriendRequest(notify, approve);\n }\n}\n\n/** 按 message_id(申请方 uin)查找好友申请。 */\nasync function findBuddyReq(deps: FriendActionDeps, messageId: string): Promise<BuddyReq> {\n const reqs = await deps.friendApi.getBuddyReqList();\n if (reqs.length === 0) {\n throw new Error(\"好友申请不存在\");\n }\n // 批量 uid → uin(friendUid 为 uid,message_id 为 uin)\n const uids = reqs.map((r) => r.friendUid);\n const uinMap = await deps.uidToUin(uids);\n const found = reqs.find((r) => uinMap.get(r.friendUid) === messageId);\n if (found === undefined) {\n throw new Error(\"好友申请不存在\");\n }\n return found;\n}\n","/**\n * Satori 群组动作:guild.get / guild.list / guild.approve\n * + guild.member.get / list / kick / mute / approve\n * (role.set / role.unset / guild.role.*:501,见 registry)\n */\nimport type { GroupMember, GroupNotify } from \"@napuketto/kernel\";\nimport { NTGroupRequestOperateTypes } from \"@napuketto/kernel\";\nimport { z } from \"zod\";\nimport type { SatoriApi } from \"../api/index.js\";\nimport { toGuild, toUser } from \"../helper/index.js\";\nimport type { Guild, GuildMember, List } from \"../types/index.js\";\nimport { BaseSatoriAction } from \"./base-action.js\";\n\n/** 动作依赖(Pick<SatoriApi> 视图)。 */\nexport type GuildActionDeps = Pick<\n SatoriApi,\n \"groupApi\" | \"groupNotifyApi\" | \"uinToUid\" | \"uidToUin\" | \"groupCache\"\n>;\n\n/** 成员列表默认条数。 */\nconst DEFAULT_MEMBER_LIMIT = 100;\n\n/** guild.get 参数。 */\nconst guildGetSchema = z.object({\n guild_id: z.string(),\n});\n\n/** 获取群组。 */\nexport class GuildGetAction extends BaseSatoriAction<z.infer<typeof guildGetSchema>, Guild> {\n readonly name = \"guild.get\";\n readonly schema = guildGetSchema;\n private readonly deps: GuildActionDeps;\n\n constructor(deps: GuildActionDeps) {\n super();\n this.deps = deps;\n }\n\n protected async _handle(payload: z.infer<typeof guildGetSchema>): Promise<Guild> {\n const { guild_id: guildId } = payload;\n const detail = await this.deps.groupApi.getGroupInfo(guildId);\n return toGuild(detail.groupCode, detail.groupName);\n }\n}\n\n/** guild.list 参数。 */\nconst guildListSchema = z.object({\n next: z.string().optional(),\n});\n\n/** 获取群组列表。 */\nexport class GuildListAction extends BaseSatoriAction<\n z.infer<typeof guildListSchema>,\n List<Guild>\n> {\n readonly name = \"guild.list\";\n readonly schema = guildListSchema;\n private readonly deps: GuildActionDeps;\n\n constructor(deps: GuildActionDeps) {\n super();\n this.deps = deps;\n }\n\n protected async _handle(_payload: z.infer<typeof guildListSchema>): Promise<List<Guild>> {\n // 实测校准(2026-08-08):原生 getGroupList 返回值无数据,列表经\n // onGroupListUpdate 事件推送 → GroupCache 维护;缓存为空时主动刷新回填。\n const groups =\n this.deps.groupCache === undefined\n ? await this.deps.groupApi.getGroupList(true)\n : await this.deps.groupCache.listGroupsRefreshed();\n const data: Guild[] = groups.map((g) => toGuild(g.groupCode, g.groupName));\n return { data };\n }\n}\n\n/** guild.approve 参数(处理入群邀请)。 */\nconst guildApproveSchema = z.object({\n message_id: z.string(),\n approve: z.boolean(),\n comment: z.string().optional(),\n});\n\n/** 群请求 approve 基类(guild.approve / guild.member.approve 共用实现)。 */\nabstract class GroupRequestApproveBase extends BaseSatoriAction<\n z.infer<typeof guildApproveSchema>,\n void\n> {\n readonly schema = guildApproveSchema;\n protected readonly deps: GuildActionDeps;\n\n constructor(deps: GuildActionDeps) {\n super();\n this.deps = deps;\n }\n\n protected async _handle(payload: z.infer<typeof guildApproveSchema>): Promise<void> {\n const { message_id: messageId, approve, comment } = payload;\n const notify = await findGroupNotify(this.deps, messageId);\n const operateType = approve\n ? NTGroupRequestOperateTypes.KAGREE\n : NTGroupRequestOperateTypes.KREFUSE;\n await this.deps.groupNotifyApi.handleGroupRequest(\n false,\n notify,\n operateType,\n comment ?? \"\",\n );\n }\n}\n\n/** 处理群邀请(message_id = 群通知 seq)。 */\nexport class GuildApproveAction extends GroupRequestApproveBase {\n readonly name = \"guild.approve\";\n}\n\n/** guild.member.get 参数。 */\nconst guildMemberGetSchema = z.object({\n guild_id: z.string(),\n user_id: z.string(),\n});\n\n/** 获取群成员。 */\nexport class GuildMemberGetAction extends BaseSatoriAction<\n z.infer<typeof guildMemberGetSchema>,\n GuildMember\n> {\n readonly name = \"guild.member.get\";\n readonly schema = guildMemberGetSchema;\n private readonly deps: GuildActionDeps;\n\n constructor(deps: GuildActionDeps) {\n super();\n this.deps = deps;\n }\n\n protected async _handle(payload: z.infer<typeof guildMemberGetSchema>): Promise<GuildMember> {\n const { guild_id: guildId, user_id: userId } = payload;\n const uidMap = await this.deps.uinToUid([userId]);\n const uid = uidMap.get(userId) ?? userId;\n const member = await this.deps.groupCache?.getMember(guildId, uid);\n if (member !== undefined) {\n return toGuildMember(member, userId);\n }\n // 缓存未命中:直查原生(整群拉取后单查)\n const list = await this.deps.groupApi.getGroupMemberInfo(guildId, [uid]);\n const found = list.find((m) => m.uid === uid);\n if (found === undefined) {\n throw new Error(\"群成员不存在\");\n }\n return toGuildMember(found, userId);\n }\n}\n\n/** guild.member.list 参数。 */\nconst guildMemberListSchema = z.object({\n guild_id: z.string(),\n next: z.string().optional(),\n});\n\n/** 获取群成员列表。 */\nexport class GuildMemberListAction extends BaseSatoriAction<\n z.infer<typeof guildMemberListSchema>,\n List<GuildMember>\n> {\n readonly name = \"guild.member.list\";\n readonly schema = guildMemberListSchema;\n private readonly deps: GuildActionDeps;\n\n constructor(deps: GuildActionDeps) {\n super();\n this.deps = deps;\n }\n\n protected async _handle(\n payload: z.infer<typeof guildMemberListSchema>,\n ): Promise<List<GuildMember>> {\n const { guild_id: guildId } = payload;\n let members: GroupMember[];\n const cached = await this.deps.groupCache?.getMembers(guildId);\n if (cached !== undefined && cached.length > 0) {\n members = cached;\n } else {\n members = await this.deps.groupApi.getGroupMemberList(guildId, true);\n }\n const limited = members.slice(0, DEFAULT_MEMBER_LIMIT);\n // 批量 uid → uin(user.id 规范为 uin)\n const uids = limited.map((m) => m.uid);\n const uinMap = await this.deps.uidToUin(uids);\n const data: GuildMember[] = limited.map((m) =>\n toGuildMember(m, uinMap.get(m.uid) ?? m.uin),\n );\n const out: List<GuildMember> = { data };\n if (members.length > DEFAULT_MEMBER_LIMIT) {\n out.next = String(DEFAULT_MEMBER_LIMIT);\n }\n return out;\n }\n}\n\n/** guild.member.kick 参数。 */\nconst guildMemberKickSchema = z.object({\n guild_id: z.string(),\n user_id: z.string(),\n /** 是否永久踢出(无法再次加入)。 */\n permanent: z.boolean().optional(),\n});\n\n/** 踢出群成员。 */\nexport class GuildMemberKickAction extends BaseSatoriAction<\n z.infer<typeof guildMemberKickSchema>,\n void\n> {\n readonly name = \"guild.member.kick\";\n readonly schema = guildMemberKickSchema;\n private readonly deps: GuildActionDeps;\n\n constructor(deps: GuildActionDeps) {\n super();\n this.deps = deps;\n }\n\n protected async _handle(payload: z.infer<typeof guildMemberKickSchema>): Promise<void> {\n const { guild_id: guildId, user_id: userId, permanent } = payload;\n const uidMap = await this.deps.uinToUid([userId]);\n const uid = uidMap.get(userId) ?? userId;\n await this.deps.groupApi.kickMember(guildId, [uid], permanent === true);\n }\n}\n\n/** 毫秒 → 秒(QQ setMemberShutUp 单位;向下取整,0 解除禁言)。 */\nfunction msToSeconds(ms: number): number {\n return Math.max(0, Math.floor(ms / 1000));\n}\n\n/** guild.member.mute 参数。 */\nconst guildMemberMuteSchema = z.object({\n guild_id: z.string(),\n user_id: z.string(),\n /** 禁言时长(毫秒,Satori 规范);0 解除禁言。 */\n duration: z.number(),\n});\n\n/** 群成员禁言(Satori duration 毫秒 → kernel setMemberShutUp 秒)。 */\nexport class GuildMemberMuteAction extends BaseSatoriAction<\n z.infer<typeof guildMemberMuteSchema>,\n void\n> {\n readonly name = \"guild.member.mute\";\n readonly schema = guildMemberMuteSchema;\n private readonly deps: GuildActionDeps;\n\n constructor(deps: GuildActionDeps) {\n super();\n this.deps = deps;\n }\n\n protected async _handle(payload: z.infer<typeof guildMemberMuteSchema>): Promise<void> {\n const { guild_id: guildId, user_id: userId, duration } = payload;\n const uidMap = await this.deps.uinToUid([userId]);\n const uid = uidMap.get(userId) ?? userId;\n await this.deps.groupApi.setMemberShutUp(guildId, [\n { uid, duration: msToSeconds(duration) },\n ]);\n }\n}\n\n/** guild.member.approve 参数(处理加群申请)。 */\nconst guildMemberApproveSchema = z.object({\n message_id: z.string(),\n approve: z.boolean(),\n comment: z.string().optional(),\n});\n\n/** 处理加群申请(message_id = 群通知 seq)。 */\nexport class GuildMemberApproveAction extends GroupRequestApproveBase {\n readonly name = \"guild.member.approve\";\n override readonly schema = guildMemberApproveSchema;\n}\n\n/** 按 message_id(通知 seq)查找群通知。 */\nasync function findGroupNotify(deps: GuildActionDeps, messageId: string): Promise<GroupNotify> {\n const notifies = await deps.groupNotifyApi.getSingleScreenNotifies(false, 50);\n const found = notifies.find((n) => n.seq === messageId);\n if (found === undefined) {\n throw new Error(\"群通知不存在\");\n }\n return found;\n}\n\n/** GroupMember → Satori GuildMember(user.id = uin;nick = 群名片优先)。 */\nfunction toGuildMember(member: GroupMember, uin: string): GuildMember {\n const nick = member.cardName !== \"\" ? member.cardName : member.nick;\n const out: GuildMember = {\n user: toUser(uin, member.nick),\n };\n if (nick !== \"\" && nick !== member.nick) {\n out.nick = nick;\n }\n return out;\n}\n","/**\n * Satori 登录动作:login.get\n */\nimport { z } from \"zod\";\nimport type { SatoriApi } from \"../api/index.js\";\nimport { toLogin } from \"../helper/index.js\";\nimport type { Login } from \"../types/index.js\";\nimport { BaseSatoriAction } from \"./base-action.js\";\n\n/** login.get 参数(无)。 */\nconst loginGetSchema = z.object({});\n\n/** 动作依赖。 */\nexport type LoginActionDeps = Pick<SatoriApi, \"self\">;\n\n/** 获取登录信息。 */\nexport class LoginGetAction extends BaseSatoriAction<z.infer<typeof loginGetSchema>, Login> {\n readonly name = \"login.get\";\n readonly schema = loginGetSchema;\n private readonly deps: LoginActionDeps;\n\n constructor(deps: LoginActionDeps) {\n super();\n this.deps = deps;\n }\n\n protected async _handle(_payload: z.infer<typeof loginGetSchema>): Promise<Login> {\n return toLogin(this.deps.self, 0, true);\n }\n}\n","/**\n * Satori 消息动作:message.create / get / list / delete\n * (message.update:QQ 不支持编辑 → 501,见 registry)\n */\nimport { z } from \"zod\";\nimport type { SatoriApi } from \"../api/index.js\";\nimport { parseContentToCanonical } from \"../helper/element/index.js\";\nimport { toChannelById, toSatoriMessage } from \"../helper/index.js\";\nimport type { BidiList, Message } from \"../types/index.js\";\nimport { BaseSatoriAction } from \"./base-action.js\";\n\n/** 消息列表默认条数(规范推荐 50)。 */\nconst DEFAULT_LIST_LIMIT = 50;\n\n/** 动作依赖(Pick<SatoriApi> 视图)。 */\nexport type MessageActionDeps = Pick<\n SatoriApi,\n \"msgApi\" | \"selfUin\" | \"uidToUin\" | \"resolvePeer\" | \"setChannelType\" | \"toCanonicalDeps\"\n>;\n\n/** message.create 参数。 */\nconst messageCreateSchema = z.object({\n channel_id: z.string(),\n content: z.string(),\n /** 被动请求来源(第一版透传忽略,QQ 不区分主动/被动)。 */\n referrer: z.unknown().optional(),\n});\n\n/** 发送消息:返回 Message 数组(规范)。 */\nexport class MessageCreateAction extends BaseSatoriAction<\n z.infer<typeof messageCreateSchema>,\n Message[]\n> {\n readonly name = \"message.create\";\n readonly schema = messageCreateSchema;\n private readonly deps: MessageActionDeps;\n\n constructor(deps: MessageActionDeps) {\n super();\n this.deps = deps;\n }\n\n protected async _handle(payload: z.infer<typeof messageCreateSchema>): Promise<Message[]> {\n const { channel_id: channelId, content } = payload;\n const peer = await this.deps.resolvePeer(channelId);\n this.deps.setChannelType(channelId, peer.chatType === 2);\n const elements = await parseContentToCanonical(content, this.deps.toCanonicalDeps());\n const { msgId } = await this.deps.msgApi.sendMessage(peer, elements);\n const message: Message = {\n id: msgId,\n channel: toChannelById(channelId, peer.chatType === 2),\n };\n // content 回显原始输入(发送成功即已按协议解析;规范化回显留待后续)\n message.content = content;\n return [message];\n }\n}\n\n/** message.get 参数。 */\nconst messageGetSchema = z.object({\n channel_id: z.string(),\n message_id: z.string(),\n});\n\n/** 获取单条消息。 */\nexport class MessageGetAction extends BaseSatoriAction<z.infer<typeof messageGetSchema>, Message> {\n readonly name = \"message.get\";\n readonly schema = messageGetSchema;\n private readonly deps: MessageActionDeps;\n\n constructor(deps: MessageActionDeps) {\n super();\n this.deps = deps;\n }\n\n protected async _handle(payload: z.infer<typeof messageGetSchema>): Promise<Message> {\n const { channel_id: channelId, message_id: messageId } = payload;\n const peer = await this.deps.resolvePeer(channelId);\n const msgs = await this.deps.msgApi.fetchMsgsByMsgId(peer, [messageId]);\n const first = msgs[0];\n if (first === undefined) {\n throw new Error(\"消息不存在\");\n }\n this.deps.setChannelType(channelId, peer.chatType === 2);\n return toSatoriMessage(first, {\n selfUin: this.deps.selfUin,\n uidToUin: (uids) => this.deps.uidToUin(uids),\n });\n }\n}\n\n/** message.list 参数。 */\nconst messageListSchema = z.object({\n channel_id: z.string(),\n /** 分页令牌(缺省从最新开始)。 */\n next: z.string().optional(),\n /** 查询方向(缺省 before)。 */\n direction: z.enum([\"before\", \"after\", \"around\"]).optional(),\n /** 消息数量限制(缺省平台默认,推荐 50)。 */\n limit: z.number().int().optional(),\n /** 排序(缺省 asc)。 */\n order: z.enum([\"asc\", \"desc\"]).optional(),\n});\n\n/** 获取消息列表(双向分页;第一版仅支持 before 方向)。 */\nexport class MessageListAction extends BaseSatoriAction<\n z.infer<typeof messageListSchema>,\n BidiList<Message>\n> {\n readonly name = \"message.list\";\n readonly schema = messageListSchema;\n private readonly deps: MessageActionDeps;\n\n constructor(deps: MessageActionDeps) {\n super();\n this.deps = deps;\n }\n\n protected async _handle(\n payload: z.infer<typeof messageListSchema>,\n ): Promise<BidiList<Message>> {\n const { channel_id: channelId, next, direction, limit, order } = payload;\n if (direction === \"around\") {\n throw new Error(\"暂不支持 around 方向\");\n }\n const peer = await this.deps.resolvePeer(channelId);\n const count = limit ?? DEFAULT_LIST_LIMIT;\n const raw = await this.deps.msgApi.fetchMessages(peer, {\n count,\n msgId: next ?? \"\",\n });\n this.deps.setChannelType(channelId, peer.chatType === 2);\n // QQ getMsgs 返回时间倒序(最新在前);Satori 默认 asc(旧在前)\n const asc = order !== \"desc\";\n const ordered = asc ? [...raw].reverse() : raw;\n const messages: Message[] = [];\n for (const msg of ordered) {\n messages.push(\n await toSatoriMessage(msg, {\n selfUin: this.deps.selfUin,\n uidToUin: (uids) => this.deps.uidToUin(uids),\n }),\n );\n }\n // 分页令牌:asc 时下一页为更早消息 → 首条(最早)的 msgId;desc 时下一批更新 → 末条 msgId\n const last = messages[messages.length - 1];\n const out: BidiList<Message> = { data: messages };\n if (last !== undefined && raw.length >= count) {\n out.next = last.id;\n }\n return out;\n }\n}\n\n/** message.delete 参数。 */\nconst messageDeleteSchema = z.object({\n channel_id: z.string(),\n message_id: z.string(),\n});\n\n/** 撤回消息。 */\nexport class MessageDeleteAction extends BaseSatoriAction<\n z.infer<typeof messageDeleteSchema>,\n void\n> {\n readonly name = \"message.delete\";\n readonly schema = messageDeleteSchema;\n private readonly deps: MessageActionDeps;\n\n constructor(deps: MessageActionDeps) {\n super();\n this.deps = deps;\n }\n\n protected async _handle(payload: z.infer<typeof messageDeleteSchema>): Promise<void> {\n const { channel_id: channelId, message_id: messageId } = payload;\n const peer = await this.deps.resolvePeer(channelId);\n await this.deps.msgApi.recallMessage(peer, [messageId]);\n }\n}\n","/**\n * 未实现动作(501):平台支持但适配器未实现(规范:501 而非 404)。\n */\nimport { z } from \"zod\";\nimport { BaseSatoriAction, SatoriActionError } from \"./base-action.js\";\n\n/** 未实现动作:schema 接受任意对象,执行抛 501。 */\nexport class NotImplementedAction extends BaseSatoriAction<unknown, never> {\n readonly name: string;\n readonly schema = z.object({}).passthrough();\n\n constructor(name: string) {\n super();\n this.name = name;\n }\n\n protected async _handle(_payload: unknown): Promise<never> {\n throw new SatoriActionError(501, `平台不支持该操作: ${this.name}`);\n }\n}\n","/**\n * Satori 表态动作:reaction.create / reaction.delete\n * (reaction.clear / reaction.list:501,见 registry)\n */\nimport { z } from \"zod\";\nimport type { SatoriApi } from \"../api/index.js\";\nimport { BaseSatoriAction } from \"./base-action.js\";\n\n/** 动作依赖(Pick<SatoriApi> 视图)。 */\nexport type ReactionActionDeps = Pick<SatoriApi, \"msgApi\" | \"resolvePeer\">;\n\n/** 表情 ID 默认类型(QQ 表情 id 为 qq 表情索引)。 */\nconst EMOJI_TYPE = \"1\";\n\n/** reaction.create 参数。 */\nconst reactionCreateSchema = z.object({\n channel_id: z.string(),\n message_id: z.string(),\n emoji_id: z.string(),\n});\n\n/** 添加表态(QQ 表情表态,message_id = NT msgId)。 */\nexport class ReactionCreateAction extends BaseSatoriAction<\n z.infer<typeof reactionCreateSchema>,\n void\n> {\n readonly name = \"reaction.create\";\n readonly schema = reactionCreateSchema;\n private readonly deps: ReactionActionDeps;\n\n constructor(deps: ReactionActionDeps) {\n super();\n this.deps = deps;\n }\n\n protected async _handle(payload: z.infer<typeof reactionCreateSchema>): Promise<void> {\n const { channel_id: channelId, message_id: messageId, emoji_id: emojiId } = payload;\n const peer = await this.deps.resolvePeer(channelId);\n await this.deps.msgApi.setMsgEmojiLike(peer, {\n msgSeq: messageId,\n emojiId,\n emojiType: EMOJI_TYPE,\n like: true,\n });\n }\n}\n\n/** reaction.delete 参数。 */\nconst reactionDeleteSchema = z.object({\n channel_id: z.string(),\n message_id: z.string(),\n emoji_id: z.string(),\n /** 删除指定用户的表态(缺省删除自己的)。 */\n user_id: z.string().optional(),\n});\n\n/** 删除表态(第一版仅支持删除自己的,与 QQ 表情表态能力对齐)。 */\nexport class ReactionDeleteAction extends BaseSatoriAction<\n z.infer<typeof reactionDeleteSchema>,\n void\n> {\n readonly name = \"reaction.delete\";\n readonly schema = reactionDeleteSchema;\n private readonly deps: ReactionActionDeps;\n\n constructor(deps: ReactionActionDeps) {\n super();\n this.deps = deps;\n }\n\n protected async _handle(payload: z.infer<typeof reactionDeleteSchema>): Promise<void> {\n const {\n channel_id: channelId,\n message_id: messageId,\n emoji_id: emojiId,\n user_id: userId,\n } = payload;\n if (userId !== undefined) {\n throw new Error(\"暂不支持删除他人表态\");\n }\n const peer = await this.deps.resolvePeer(channelId);\n await this.deps.msgApi.setMsgEmojiLike(peer, {\n msgSeq: messageId,\n emojiId,\n emojiType: EMOJI_TYPE,\n like: false,\n });\n }\n}\n","/**\n * Satori 用户动作:user.get / user.channel.create\n */\nimport { z } from \"zod\";\nimport type { SatoriApi } from \"../api/index.js\";\nimport { toChannelById, toUser } from \"../helper/index.js\";\nimport type { Channel, User } from \"../types/index.js\";\nimport { BaseSatoriAction } from \"./base-action.js\";\n\n/** 动作依赖(Pick<SatoriApi> 视图)。 */\nexport type UserActionDeps = Pick<SatoriApi, \"profileApi\" | \"groupApi\" | \"groupCache\">;\n\n/** user.get 参数。 */\nconst userGetSchema = z.object({\n user_id: z.string(),\n});\n\n/** 获取用户信息(QQ 平台 user.id = uin)。 */\nexport class UserGetAction extends BaseSatoriAction<z.infer<typeof userGetSchema>, User> {\n readonly name = \"user.get\";\n readonly schema = userGetSchema;\n private readonly deps: UserActionDeps;\n\n constructor(deps: UserActionDeps) {\n super();\n this.deps = deps;\n }\n\n protected async _handle(payload: z.infer<typeof userGetSchema>): Promise<User> {\n const { user_id: userId } = payload;\n // 尝试陌生人信息补昵称(失败退化为基础 User)\n let nickname: string | undefined;\n try {\n const info = await this.deps.profileApi.getStrangerInfo(userId);\n nickname = info.nickname ?? info.long_nick ?? undefined;\n } catch {\n // 陌生人信息不可用(非好友等):仅 ID\n }\n return toUser(userId, nickname);\n }\n}\n\n/** user.channel.create 参数。 */\nconst userChannelCreateSchema = z.object({\n user_id: z.string(),\n guild_id: z.string().optional(),\n});\n\n/** 创建私聊频道(QQ:私聊频道 id = 对端 uin,无需实际创建)。 */\nexport class UserChannelCreateAction extends BaseSatoriAction<\n z.infer<typeof userChannelCreateSchema>,\n Channel\n> {\n readonly name = \"user.channel.create\";\n readonly schema = userChannelCreateSchema;\n\n protected async _handle(payload: z.infer<typeof userChannelCreateSchema>): Promise<Channel> {\n const { user_id: userId } = payload;\n // 私聊频道 id = 对端 uin(与消息事件/消息动作的私聊频道一致)\n return toChannelById(userId, false);\n }\n}\n","/**\n * Satori 动作注册表(ADR-013 延伸):动作依赖收敛为 { api: SatoriApi } 单聚合对象。\n *\n * 501 动作:规范「平台支持但适配器未实现 → 501」——QQ 平台不支持的\n * 能力(消息编辑/频道管理/角色管理等)统一注册 NotImplementedAction。\n */\n\nimport { ActionRegistry } from \"../../core/action-registry.js\";\nimport type { SatoriApi } from \"../api/index.js\";\nimport type { BaseSatoriAction } from \"./base-action.js\";\nimport { type ChannelActionDeps, ChannelGetAction, ChannelListAction } from \"./channel.js\";\nimport {\n type FriendActionDeps,\n FriendApproveAction,\n FriendDeleteAction,\n FriendListAction,\n} from \"./friend.js\";\nimport {\n type GuildActionDeps,\n GuildApproveAction,\n GuildGetAction,\n GuildListAction,\n GuildMemberApproveAction,\n GuildMemberGetAction,\n GuildMemberKickAction,\n GuildMemberListAction,\n GuildMemberMuteAction,\n} from \"./guild.js\";\nimport { type LoginActionDeps, LoginGetAction } from \"./login.js\";\nimport {\n type MessageActionDeps,\n MessageCreateAction,\n MessageDeleteAction,\n MessageGetAction,\n MessageListAction,\n} from \"./message.js\";\nimport { NotImplementedAction } from \"./not-implemented.js\";\nimport { type ReactionActionDeps, ReactionCreateAction, ReactionDeleteAction } from \"./reaction.js\";\nimport type { SatoriActionRegistry } from \"./registry.js\";\nimport type { UserActionDeps } from \"./user.js\";\nimport { UserChannelCreateAction, UserGetAction } from \"./user.js\";\n\n/** 动作注册依赖(单聚合对象)。 */\nexport interface SatoriActionDeps {\n api: SatoriApi;\n}\n\n/** QQ 平台不支持的 Satori 标准动作(501)。 */\nconst NOT_IMPLEMENTED = [\n \"message.update\", // QQ 不支持编辑消息\n \"channel.create\", // QQ 不支持创建频道\n \"channel.update\",\n \"channel.delete\",\n \"channel.mute\",\n \"guild.member.role.set\",\n \"guild.member.role.unset\",\n \"guild.role.list\",\n \"guild.role.create\",\n \"guild.role.update\",\n \"guild.role.delete\",\n \"reaction.clear\",\n \"reaction.list\",\n] as const;\n\n/** 创建 Satori 动作注册表。 */\nexport function createSatoriActionRegistry(deps: SatoriActionDeps): SatoriActionRegistry {\n const registry = new ActionRegistry<BaseSatoriAction<unknown, unknown>>();\n const { api } = deps;\n\n const messageDeps: MessageActionDeps = api;\n registry.register(new MessageCreateAction(messageDeps));\n registry.register(new MessageGetAction(messageDeps));\n registry.register(new MessageListAction(messageDeps));\n registry.register(new MessageDeleteAction(messageDeps));\n\n const channelDeps: ChannelActionDeps = api;\n registry.register(new ChannelGetAction(channelDeps));\n registry.register(new ChannelListAction(channelDeps));\n\n const userDeps: UserActionDeps = api;\n registry.register(new UserGetAction(userDeps));\n registry.register(new UserChannelCreateAction());\n\n const guildDeps: GuildActionDeps = api;\n registry.register(new GuildGetAction(guildDeps));\n registry.register(new GuildListAction(guildDeps));\n registry.register(new GuildApproveAction(guildDeps));\n registry.register(new GuildMemberGetAction(guildDeps));\n registry.register(new GuildMemberListAction(guildDeps));\n registry.register(new GuildMemberKickAction(guildDeps));\n registry.register(new GuildMemberMuteAction(guildDeps));\n registry.register(new GuildMemberApproveAction(guildDeps));\n\n const friendDeps: FriendActionDeps = api;\n registry.register(new FriendListAction(friendDeps));\n registry.register(new FriendDeleteAction(friendDeps));\n registry.register(new FriendApproveAction(friendDeps));\n\n const loginDeps: LoginActionDeps = api;\n registry.register(new LoginGetAction(loginDeps));\n\n const reactionDeps: ReactionActionDeps = api;\n registry.register(new ReactionCreateAction(reactionDeps));\n registry.register(new ReactionDeleteAction(reactionDeps));\n\n for (const name of NOT_IMPLEMENTED) {\n registry.register(new NotImplementedAction(name));\n }\n\n return registry;\n}\n","/**\n * SatoriApi:Satori 动作的统一依赖聚合(对齐 OB11 的 OneBotApi,P2-16 模式)\n *\n * 单类持有 kernel apis + 协议层共享状态(self / cacheDir / 元素转换依赖),\n * 动作只依赖一个聚合对象(或 Pick 视图),装配面只出现一个聚合对象。\n *\n * - 便捷方法 uinToUid / uidToUin 委托 groupApi(at 转换 / 私聊发送用)。\n * - cacheDir 供消息元素发方向资源下载(img/audio/video/file 的 http(s) src)。\n */\nimport type {\n FriendApi,\n GroupApi,\n GroupCache,\n GroupNotifyApi,\n MsgApi,\n ProfileApi,\n} from \"@napuketto/kernel\";\nimport { ChatType, type Peer } from \"@napuketto/kernel\";\nimport type { SatoriToCanonicalDeps } from \"../helper/element/index.js\";\n\n/** SatoriApi 构造参数。 */\nexport interface SatoriApiOptions {\n msgApi: MsgApi;\n groupApi: GroupApi;\n groupNotifyApi: GroupNotifyApi;\n friendApi: FriendApi;\n profileApi: ProfileApi;\n /** 登录身份(login.get / READY logins 用)。 */\n self: { uin: string; nickname: string };\n /** 媒体资源下载缓存目录(发方向 img/audio/video/file 的 http(s) src)。 */\n cacheDir: string;\n /** 群/成员缓存(ADR-008;guild.member.get / user.get 读缓存用)。 */\n groupCache?: GroupCache;\n}\n\n/** Satori 动作统一依赖聚合。 */\nexport class SatoriApi {\n readonly msgApi: MsgApi;\n readonly groupApi: GroupApi;\n readonly groupNotifyApi: GroupNotifyApi;\n readonly friendApi: FriendApi;\n readonly profileApi: ProfileApi;\n readonly self: { uin: string; nickname: string };\n readonly selfUin: string;\n readonly cacheDir: string;\n readonly groupCache: GroupCache | undefined;\n\n /**\n * 频道类型缓存:channel_id → isGroup(群聊频道 id 与私聊 uin 无法从数字\n * 规则区分,需从事件/查询回填——QQ 平台群号与用户 uin 同为数字)。\n */\n private readonly channelTypeCache = new Map<string, boolean>();\n\n constructor(opts: SatoriApiOptions) {\n this.msgApi = opts.msgApi;\n this.groupApi = opts.groupApi;\n this.groupNotifyApi = opts.groupNotifyApi;\n this.friendApi = opts.friendApi;\n this.profileApi = opts.profileApi;\n this.self = opts.self;\n this.selfUin = opts.self.uin;\n this.cacheDir = opts.cacheDir;\n this.groupCache = opts.groupCache;\n }\n\n /** 记录频道类型(消息事件翻译时回填:chatType GROUP → true)。 */\n setChannelType(channelId: string, isGroup: boolean): void {\n this.channelTypeCache.set(channelId, isGroup);\n }\n\n /**\n * 解析 channel_id → Peer(消息动作共用)。\n * 缓存命中直接取类型;未命中先试群号(getGroupInfo 成功 = 群聊),\n * 失败按私聊(uin → uid)。\n */\n async resolvePeer(channelId: string): Promise<Peer> {\n const cached = this.channelTypeCache.get(channelId);\n if (cached === true) {\n return { chatType: ChatType.GROUP, peerUid: channelId };\n }\n if (cached === false) {\n return this.toC2cPeer(channelId);\n }\n const isGroup = await this.groupApi\n .getGroupInfo(channelId)\n .then(() => true)\n .catch(() => false);\n this.channelTypeCache.set(channelId, isGroup);\n if (isGroup) {\n return { chatType: ChatType.GROUP, peerUid: channelId };\n }\n return this.toC2cPeer(channelId);\n }\n\n /** uin → 私聊 Peer(uin→uid 转换;转换失败原样 uin 兜底)。 */\n private async toC2cPeer(uin: string): Promise<Peer> {\n const map = await this.groupApi.uinToUid([uin]);\n const uid = map.get(uin) ?? uin;\n return { chatType: ChatType.C2C, peerUid: uid };\n }\n\n /** uin → uid(委托 groupApi;私聊发送 / at 目标用)。 */\n uinToUid(uins: string[]): Promise<Map<string, string>> {\n return this.groupApi.uinToUid(uins);\n }\n\n /** uid → uin(委托 groupApi;收方向 at / 用户 ID 用)。 */\n uidToUin(uids: string[]): Promise<Map<string, string>> {\n return this.groupApi.uidToUin(uids);\n }\n\n /** 发方向元素转换依赖(at uin→uid + 资源下载目录)。 */\n toCanonicalDeps(): SatoriToCanonicalDeps {\n return {\n uinToUid: (uins) => this.groupApi.uinToUid(uins),\n cacheDir: this.cacheDir,\n };\n }\n}\n","/**\n * Satori 事件翻译(kernel RawMessage → Satori Event 内容,纯函数 + 注入转换)\n *\n * 第一版支持:\n * - message-created:普通消息(Msg/onRecvMsg)\n * - message-deleted:grayTip REVOKE(撤回)\n * - guild-member-added / guild-member-removed:grayTip GROUP(TipGroupElement)\n * (friend-request / guild-request 事件留待后续:grayTip 的 jsonGrayTipElement\n * 结构待运行时探测,OB11 走主动查询 get_group_system_msg 不广播。)\n *\n * 收方向 ID 空间:uid → uin 批量转换(deps.uidToUin,与消息翻译同依赖)。\n */\nimport { ChatType, GrayTipSubType, type RawMessage, TipGroupElementType } from \"@napuketto/kernel\";\nimport { collectGrayTipUids } from \"../../core/gray-tip.js\";\nimport { type SatoriTranslateDeps, toSatoriMessage, toUser } from \"../helper/index.js\";\nimport type { Channel, Guild, GuildMember, Message, User } from \"../types/resource.js\";\n\n/** 事件内容(不含 sn/login,由 adapter 包一层完整 Event)。 */\nexport interface SatoriEventContent {\n type: string;\n timestamp: number;\n channel?: Channel;\n guild?: Guild;\n message?: Message;\n user?: User;\n member?: GuildMember;\n operator?: User;\n}\n\n/** 事件翻译依赖(selfUin + uidToUin)。 */\nexport interface SatoriEventDeps extends SatoriTranslateDeps {}\n\n/** 普通消息 → message-created 事件。 */\nexport async function toSatoriMessageEvent(\n raw: RawMessage,\n deps: SatoriEventDeps,\n): Promise<SatoriEventContent> {\n const message = await toSatoriMessage(raw, deps);\n const content: SatoriEventContent = {\n type: \"message-created\",\n timestamp: Number(raw.msgTime),\n };\n if (message.channel !== undefined) {\n content.channel = message.channel;\n }\n if (message.guild !== undefined) {\n content.guild = message.guild;\n }\n if (message.user !== undefined) {\n content.user = message.user;\n }\n if (message.member !== undefined) {\n content.member = message.member;\n }\n content.message = message;\n return content;\n}\n\n/** 检查消息是否含可翻译的 grayTip(撤回 / 群成员变动)。 */\nexport function hasSatoriGrayTip(raw: RawMessage): boolean {\n for (const el of raw.elements) {\n const g = el.grayTipElement;\n if (g === undefined) {\n continue;\n }\n if (\n g.subElementType === GrayTipSubType.REVOKE ||\n g.subElementType === GrayTipSubType.GROUP\n ) {\n return true;\n }\n }\n return false;\n}\n\n/** 收集 grayTip 涉及的 uid(批量 uidToUin 用,core 共享实现)。 */\nexport const collectSatoriGrayTipUids = collectGrayTipUids;\n\n/** 判断消息是否为群聊 grayTip(peerUid 是群号)。 */\nfunction isGroupMessage(raw: RawMessage): boolean {\n return raw.chatType === ChatType.GROUP;\n}\n\n/** grayTip → 事件(撤回 / 群成员变动;无匹配返回 null)。 */\nexport async function toSatoriGrayTipEvent(\n raw: RawMessage,\n uidToUinMap: Map<string, string>,\n): Promise<SatoriEventContent | null> {\n for (const el of raw.elements) {\n const g = el.grayTipElement;\n if (g === undefined) {\n continue;\n }\n if (g.subElementType === GrayTipSubType.REVOKE) {\n const revoked = toRevokeEvent(raw, uidToUinMap);\n if (revoked !== null) {\n return revoked;\n }\n }\n if (g.subElementType === GrayTipSubType.GROUP && isGroupMessage(raw)) {\n const member = toMemberChangeEvent(raw, uidToUinMap);\n if (member !== null) {\n return member;\n }\n }\n }\n return null;\n}\n\n/** uid → uin(映射缺省退化为 uid)。 */\nfunction toUin(uid: string, uidToUinMap: Map<string, string>): string {\n return uidToUinMap.get(uid) ?? uid;\n}\n\n/** 撤回 → message-deleted 事件。 */\nfunction toRevokeEvent(\n raw: RawMessage,\n uidToUinMap: Map<string, string>,\n): SatoriEventContent | null {\n for (const el of raw.elements) {\n const revoke = el.grayTipElement?.revokeElement;\n if (revoke === undefined) {\n continue;\n }\n const operatorUin = toUin(revoke.operatorUid, uidToUinMap);\n const peerUin = String(raw.peerUin ?? \"\");\n const content: SatoriEventContent = {\n type: \"message-deleted\",\n timestamp: Number(raw.msgTime),\n };\n content.channel = {\n id: peerUin,\n type: isGroupMessage(raw) ? 0 : 1,\n };\n if (isGroupMessage(raw)) {\n content.guild = { id: peerUin, name: raw.peerName };\n }\n // 撤回消息的 id:NT 侧仅 grayTip 的 msgSeq 可用(对齐 OB11 message_id 语义)\n content.message = { id: raw.msgSeq };\n content.operator = toUser(operatorUin, revoke.operatorNick);\n return content;\n }\n return null;\n}\n\n/** 群成员变动 → guild-member-added / guild-member-removed 事件。 */\nfunction toMemberChangeEvent(\n raw: RawMessage,\n uidToUinMap: Map<string, string>,\n): SatoriEventContent | null {\n for (const el of raw.elements) {\n const grp = el.grayTipElement?.groupElement;\n if (grp === undefined || grp.type === undefined) {\n continue;\n }\n const type = memberChangeType(grp.type);\n if (type === null) {\n return null;\n }\n const memberUid = grp.memberUid ?? \"\";\n const adminUid = grp.adminUid ?? \"\";\n const peerUin = String(raw.peerUin ?? \"\");\n const content: SatoriEventContent = {\n type,\n timestamp: Number(raw.msgTime),\n guild: toGroupGuild(peerUin, raw.peerName),\n channel: { id: peerUin, type: 0 },\n member: toMember(memberUid, grp.memberNick, grp.memberRemark, uidToUinMap),\n user: toUser(toUin(memberUid, uidToUinMap), grp.memberNick),\n };\n if (adminUid !== \"\" && adminUid !== memberUid) {\n content.operator = toUser(toUin(adminUid, uidToUinMap), grp.adminNick);\n }\n return content;\n }\n return null;\n}\n\n/** 成员变动类型(MEMBER_ADD=added / QUIT=removed;其余 null)。 */\nfunction memberChangeType(\n type: TipGroupElementType,\n): \"guild-member-added\" | \"guild-member-removed\" | null {\n if (type === TipGroupElementType.MEMBER_ADD) {\n return \"guild-member-added\";\n }\n if (type === TipGroupElementType.QUIT) {\n return \"guild-member-removed\";\n }\n return null;\n}\n\n/** 构造群组(带可选群名)。 */\nfunction toGroupGuild(id: string, name: string | undefined): Guild {\n const guild: Guild = { id };\n if (name !== undefined && name !== \"\") {\n guild.name = name;\n }\n return guild;\n}\n\n/** 构造群成员(user + 可选备注 nick)。 */\nfunction toMember(\n uid: string,\n nick: string | undefined,\n remark: string | undefined,\n uidToUinMap: Map<string, string>,\n): GuildMember {\n const member: GuildMember = { user: toUser(toUin(uid, uidToUinMap), nick) };\n if (remark !== undefined && remark !== \"\" && remark !== nick) {\n member.nick = remark;\n }\n return member;\n}\n","/**\n * Satori 事件与信令类型(协议 v1,规范参考 satori.chat/zh-CN/protocol/events.html)\n *\n * - Opcode:信令类型(EVENT=0 收 / PING=1 发 / PONG=2 收 / IDENTIFY=3 发 / READY=4 收 / META=5 收)\n * - Signal:信令外壳 { op, body }\n * - Event:事件对象(资源提升:message 不嵌套 user/member/channel,平铺顶层)\n */\nimport type {\n Channel,\n Emoji,\n Friend,\n Guild,\n GuildMember,\n GuildRole,\n Login,\n Message,\n User,\n} from \"./resource.js\";\n\n/** 信令类型。 */\nexport const Opcode = {\n EVENT: 0, // 接收:事件\n PING: 1, // 发送:心跳\n PONG: 2, // 接收:心跳回复\n IDENTIFY: 3, // 发送:鉴权\n READY: 4, // 接收:鉴权成功\n META: 5, // 接收:元信息更新(实验性)\n} as const;\nexport type Opcode = (typeof Opcode)[keyof typeof Opcode];\n\n/** 信令外壳。 */\nexport interface Signal<T = unknown> {\n op: Opcode;\n body?: T;\n}\n\n/** IDENTIFY 信令 body(鉴权 + 可选会话恢复)。 */\nexport interface IdentifyBody {\n token?: string;\n sn?: number;\n}\n\n/** READY 信令 body。 */\nexport interface ReadyBody {\n logins: Login[];\n proxy_urls: string[];\n}\n\n/** META 信令 body(不反映登录状态,不含 logins)。 */\nexport interface MetaBody {\n proxy_urls: string[];\n}\n\n/** 交互指令(interaction 事件)。 */\nexport interface Argv {\n name?: string;\n arguments?: string;\n options?: Record<string, string>;\n}\n\n/** 交互按钮。 */\nexport interface Button {\n id?: string;\n}\n\n/** 事件对象(type 为事件类型;各资源字段按规范可选)。 */\nexport interface Event {\n /** 事件序列号(会话内递增,1 起)。 */\n sn: number;\n /** 事件类型(message-created 等)。 */\n type: string;\n /** 事件时间戳(毫秒)。 */\n timestamp: number;\n /** 登录信息(非登录事件只带 sn/user/platform)。 */\n login: Login;\n /** 交互指令(button 等)。 */\n argv?: Argv;\n /** 交互按钮。 */\n button?: Button;\n /** 事件所属频道。 */\n channel?: Channel;\n /** 事件的表情(reaction 事件)。 */\n emoji?: Emoji;\n /** 事件的好友(friend 事件)。 */\n friend?: Friend;\n /** 事件所属群组。 */\n guild?: Guild;\n /** 事件的目标成员。 */\n member?: GuildMember;\n /** 事件的消息。 */\n message?: Message;\n /** 事件的操作者。 */\n operator?: User;\n /** 事件的目标角色。 */\n role?: GuildRole;\n /** 事件的目标用户。 */\n user?: User;\n}\n","/**\n * Satori 传输装配(HTTP RPC + WS 事件服务)\n *\n * - HTTP RPC:HttpServer 的 onPathRequest(network 协议无关扩展)按\n * `/v1/{resource}.{method}` 分发 → 动作注册表 → 响应(错误用 HTTP 状态码)。\n * - WS 事件服务:WsServer(path=/v1/events)处理信令:\n * IDENTIFY → READY(token 校验);PING → PONG;EVENT 经 broadcaster 推送。\n *\n * 错误语义(规范):标准 API 平台不支持 → 404;平台支持但未实现 → 501;\n * 非标准 API → 404;鉴权失败 → 401。\n */\nimport type {\n AuthorizeHook,\n EventBroadcaster,\n HttpRouteContext,\n Respond,\n TransportAdapter,\n} from \"@napuketto/network\";\nimport { HttpServer, WsServer } from \"@napuketto/network\";\nimport { SatoriActionError } from \"./action/base-action.js\";\nimport type { SatoriActionRegistry } from \"./action/registry.js\";\nimport { HTTP_STATUS, type SatoriConfig } from \"./helper/index.js\";\nimport { type Event, type IdentifyBody, type Login, Opcode } from \"./types/index.js\";\n\n/** WS 事件服务路径(规范固定)。 */\nconst WS_EVENTS_PATH = \"/v1/events\";\n/** HTTP RPC 版本前缀。 */\nconst V1_PREFIX = \"/v1/\";\n\n/** Authorization 头前缀。 */\nconst BEARER_PREFIX = \"Bearer \";\n\n/** resource.method 路径模式(resource 与 method 均为字母开头的小写标识符,可含连字符/数字)。 */\nconst ACTION_PATH_RE = /^([a-z][a-z0-9-]*)(?:\\.[a-z][a-z0-9-]*)+$/i;\n\n/** 传输装配结果。 */\nexport interface SatoriTransportSet {\n /** 传输适配器(HTTP RPC + WS 事件服务,均已注册 broadcaster)。 */\n adapters: TransportAdapter[];\n /** 停止:关闭全部传输(幂等)。 */\n close: () => Promise<void>;\n}\n\n/** 传输装配选项。 */\nexport interface AssembleSatoriTransportsOptions {\n /** Satori 配置。 */\n config: SatoriConfig;\n /** 事件广播(WS 事件服务注册到它,EVENT 信令经 broadcastEvent 推送)。 */\n broadcaster: EventBroadcaster;\n /** 动作注册表(HTTP RPC 分发用)。 */\n registry: SatoriActionRegistry;\n /** 登录信息构造(READY logins / meta 用)。 */\n login: () => Login;\n}\n\n/** 按配置装配 Satori 传输。 */\nexport function assembleSatoriTransports(\n opts: AssembleSatoriTransportsOptions,\n): SatoriTransportSet {\n const { config, broadcaster, registry, login } = opts;\n const adapters: TransportAdapter[] = [];\n\n for (const item of config.httpServers) {\n if (!item.enabled) {\n continue;\n }\n const token = item.token ?? config.token;\n const server = new HttpServer({\n host: item.host,\n port: item.port,\n ...(token !== undefined && token !== \"\"\n ? { authorize: makeBearerAuthorize(token) }\n : {}),\n });\n server.onPathRequest = (ctx, req, respond) => {\n handleHttpRequest(ctx, req, respond, registry, login);\n };\n adapters.push(server);\n }\n\n for (const item of config.wsServers) {\n if (!item.enabled) {\n continue;\n }\n const token = item.token ?? config.token;\n const server = new WsServer({\n host: item.host,\n port: item.port,\n path: WS_EVENTS_PATH,\n });\n server.onRequest = (req, respond) => {\n handleWsSignal(req, respond, token, login);\n };\n adapters.push(server);\n }\n\n // 注册事件广播(HTTP server 的 send 为 no-op,注册无害;WS 事件服务接收 EVENT 信令)\n for (const adapter of adapters) {\n broadcaster.register(adapter);\n }\n\n return {\n adapters,\n close: async () => {\n await Promise.all(adapters.map((a) => a.close()));\n for (const adapter of adapters) {\n broadcaster.unregister(adapter);\n }\n },\n };\n}\n\n/** Bearer token 鉴权钩子。 */\nfunction makeBearerAuthorize(token: string): AuthorizeHook {\n return (ctx) => {\n const auth = ctx.headers[\"authorization\"] ?? ctx.headers[\"Authorization\"];\n if (auth?.startsWith(BEARER_PREFIX)) {\n return auth.slice(BEARER_PREFIX.length) === token;\n }\n return false;\n };\n}\n\n/** 解析路径:/v1/{resource}.{method} → 动作名;不匹配返回 null。 */\nfunction parseActionPath(path: string): string | null {\n if (!path.startsWith(V1_PREFIX)) {\n return null;\n }\n const rest = path.slice(V1_PREFIX.length);\n const m = ACTION_PATH_RE.exec(rest);\n if (m === null) {\n return null;\n }\n return rest;\n}\n\n/** HTTP RPC 分发。 */\nfunction handleHttpRequest(\n ctx: HttpRouteContext,\n req: unknown,\n respond: Respond,\n registry: SatoriActionRegistry,\n login: () => Login,\n): void {\n if (ctx.method !== \"POST\") {\n respond({ error: \"method not allowed\" }, 405);\n return;\n }\n const action = parseActionPath(ctx.path);\n if (action === null) {\n respond({ error: \"api not found\" }, HTTP_STATUS.notFound);\n return;\n }\n // 元信息(无需平台头)\n if (action === \"meta\") {\n respond({ logins: [login()], proxy_urls: [] }, HTTP_STATUS.ok);\n return;\n }\n const act = registry.get(action);\n if (act === undefined) {\n respond({ error: \"api not found\" }, HTTP_STATUS.notFound);\n return;\n }\n act.run(req)\n .then((data) => {\n respond(data ?? {}, HTTP_STATUS.ok);\n })\n .catch((err: unknown) => {\n if (err instanceof SatoriActionError) {\n respond({ error: err.message }, err.status);\n return;\n }\n respond(\n { error: err instanceof Error ? err.message : \"internal error\" },\n HTTP_STATUS.serverError,\n );\n });\n}\n\n/** WS 信令处理(IDENTIFY → READY;PING → PONG)。 */\nfunction handleWsSignal(\n signal: unknown,\n respond: Respond,\n token: string | undefined,\n login: () => Login,\n): void {\n const parsed = (signal ?? {}) as { op?: unknown; body?: unknown };\n const op = parsed.op;\n if (op === Opcode.IDENTIFY) {\n const body = (parsed.body ?? {}) as Partial<IdentifyBody>;\n // 鉴权:配置了 token 时校验 IDENTIFY token;失败不响应(客户端超时断开)\n if (token !== undefined && token !== \"\" && body.token !== token) {\n return;\n }\n // 会话恢复(body.sn):第一版跳过,直接 READY 当前状态\n respond({\n op: Opcode.READY,\n body: { logins: [login()], proxy_urls: [] },\n });\n return;\n }\n if (op === Opcode.PING) {\n respond({ op: Opcode.PONG, body: null });\n }\n // 其他信令忽略(EVENT/META 为 SDK 侧接收)\n}\n\n/** 事件信令(adapter 广播用)。 */\nexport function toEventSignal(event: Event): { op: number; body: Event } {\n return { op: Opcode.EVENT, body: event };\n}\n","/**\n * NapukettoSatoriAdapter:Satori 协议适配器(P5,2026-08-08)\n *\n * - 收链路:订阅 kernel 消息事件通道 → 翻译 Satori 事件(message-created /\n * message-deleted / guild-member-added / guild-member-removed)→ WS EVENT 信令广播\n * - 发链路:HTTP RPC(/v1/{resource}.{method})→ 动作注册表 → kernel apis\n *\n * 生命周期走 BaseProtocolAdapter 骨架(start 校验配置 → onStart 装配传输 →\n * onStop 清理)。事件 sn 从 1 递增;非登录事件 login 只带最小字段(规范)。\n */\nimport type { MsgEventChannel, RawMessage } from \"@napuketto/kernel\";\nimport type { EventBroadcaster } from \"@napuketto/network\";\nimport { BaseProtocolAdapter, type ProtocolConfig } from \"../core/index.js\";\nimport { forEachRawMessage } from \"../core/raw-message.js\";\nimport { createSatoriActionRegistry } from \"./action/index.js\";\nimport type { SatoriApiOptions } from \"./api/index.js\";\nimport { SatoriApi } from \"./api/index.js\";\nimport {\n collectSatoriGrayTipUids,\n hasSatoriGrayTip,\n type SatoriEventContent,\n toSatoriGrayTipEvent,\n toSatoriMessageEvent,\n} from \"./event/index.js\";\nimport type { SatoriConfig } from \"./helper/index.js\";\nimport { toLogin, toMinimalLogin } from \"./helper/index.js\";\nimport type { SatoriTransportSet } from \"./transport.js\";\nimport { assembleSatoriTransports, toEventSignal } from \"./transport.js\";\nimport type { Event, Login } from \"./types/index.js\";\n\n/** 适配器构造参数。 */\nexport interface SatoriAdapterOptions extends SatoriApiOptions {\n /** 协议配置(zod 校验 + seed 装配)。 */\n config: ProtocolConfig<SatoriConfig>;\n /** network 事件广播(注册传输后 emit 推给第三方)。 */\n broadcaster: EventBroadcaster;\n /** kernel 消息事件通道(消息收链路入口)。 */\n msgChannel: MsgEventChannel;\n}\n\n/** Satori 协议适配器。 */\nexport class NapukettoSatoriAdapter extends BaseProtocolAdapter<SatoriConfig> {\n readonly protocol = \"satori\";\n\n private readonly msgChannel: MsgEventChannel;\n private readonly api: SatoriApi;\n private readonly registry: ReturnType<typeof createSatoriActionRegistry>;\n private transports: SatoriTransportSet | null = null;\n private unsubscribe: (() => void) | null = null;\n private eventSn = 0;\n\n constructor(opts: SatoriAdapterOptions) {\n super({\n config: opts.config,\n broadcaster: opts.broadcaster,\n hooks: {\n onStart: (config) => this.startTransports(config as SatoriConfig),\n onStop: () => this.stopAll(),\n onReload: (config) => this.reloadTransports(config as SatoriConfig),\n },\n });\n this.msgChannel = opts.msgChannel;\n this.api = new SatoriApi(opts);\n // 动作注册表(HTTP RPC 分发)\n this.registry = createSatoriActionRegistry({ api: this.api });\n }\n\n /** 启动传输:装配(HTTP RPC + WS 事件服务)+ 打开 server + 订阅消息事件。 */\n private async startTransports(config: SatoriConfig): Promise<void> {\n const broadcaster = this.getBroadcaster();\n if (broadcaster !== undefined) {\n // 装配传输(onPathRequest / onRequest 分发已挂 registry)\n this.transports = assembleSatoriTransports({\n config,\n broadcaster,\n registry: this.registry,\n login: () => this.currentLogin(),\n });\n await Promise.all(this.transports.adapters.map((t) => t.open()));\n }\n this.subscribe();\n // 广播 login-updated(启动 → 在线)\n this.broadcastLoginUpdated(true);\n }\n\n /** 当前登录信息(READY logins / meta / login.get 用)。 */\n private currentLogin(): Login {\n return toLogin(this.api.self, 0, true);\n }\n\n /**\n * 配置热更新(2026-09-08 实现):stop 旧传输/退订(广播 login-updated 离线)→\n * 按新配置重建(广播在线)。Satori 无 IPC 桥模式,无传输时同样走重建路径。\n */\n private async reloadTransports(config: SatoriConfig): Promise<void> {\n await this.stopAll();\n await this.startTransports(config);\n }\n\n /** 广播 login-updated 事件(适配器启停时)。 */\n private broadcastLoginUpdated(online: boolean): void {\n const login = toLogin(this.api.self, 0, online);\n this.broadcastEvent(\n toEventSignal({\n sn: this.nextSn(),\n type: \"login-updated\",\n timestamp: Date.now(),\n login,\n }),\n );\n }\n\n /** 停止:退订 + 关闭传输 + 广播 login-updated(离线)。 */\n private async stopAll(): Promise<void> {\n this.unsubscribeAll();\n if (this.transports !== null) {\n await this.transports.close();\n this.transports = null;\n }\n this.broadcastLoginUpdated(false);\n }\n\n /** 订阅 kernel 消息事件(幂等)。 */\n private subscribe(): void {\n if (this.unsubscribe !== null) {\n return;\n }\n this.unsubscribe = this.msgChannel.on(\"Msg/onRecvMsg\", (msgs) => {\n forEachRawMessage(msgs, (msg) => {\n // grayTip(系统事件)→ 撤回/群成员变动事件;否则 → 消息事件\n if (hasSatoriGrayTip(msg)) {\n this.broadcastGrayTip(msg).catch(() => {\n // grayTip 翻译失败静默(结构探测期宽容)\n });\n return;\n }\n void this.broadcastMessageCreated(msg);\n });\n });\n }\n\n /** 广播消息事件(message-created)。 */\n private async broadcastMessageCreated(msg: RawMessage): Promise<void> {\n const content = await toSatoriMessageEvent(msg, {\n selfUin: this.api.selfUin,\n uidToUin: (uids) => this.api.uidToUin(uids),\n });\n // 回填频道类型缓存(收方向已知 chatType)\n this.api.setChannelType(String(msg.peerUin ?? \"\"), msg.chatType === 2);\n this.broadcastContent(content);\n }\n\n /** 广播 grayTip 事件(message-deleted / guild-member-added / removed)。 */\n private async broadcastGrayTip(msg: RawMessage): Promise<void> {\n const uids = collectSatoriGrayTipUids(msg);\n let uidToUin = new Map<string, string>();\n if (uids.length > 0) {\n uidToUin = await this.api.uidToUin(uids);\n }\n const content = await toSatoriGrayTipEvent(msg, uidToUin);\n if (content !== null) {\n this.broadcastContent(content);\n }\n }\n\n /** 广播事件内容(sn 递增 + 最小 login 包装 + EVENT 信令)。 */\n private broadcastContent(content: SatoriEventContent): void {\n this.broadcastEvent(\n toEventSignal({\n sn: this.nextSn(),\n type: content.type,\n timestamp: content.timestamp,\n login: toMinimalLogin(this.eventSn, this.api.selfUin),\n ...pickEventResources(content),\n }),\n );\n }\n\n /** 事件 sn(递增,从 1 起)。 */\n private nextSn(): number {\n this.eventSn += 1;\n return this.eventSn;\n }\n\n /** 退订(幂等)。 */\n private unsubscribeAll(): void {\n this.unsubscribe?.();\n this.unsubscribe = null;\n }\n}\n\n/** 从事件内容提取可选资源字段(channel/guild/message/user/member/operator)。 */\nfunction pickEventResources(\n content: SatoriEventContent,\n): Pick<Event, \"channel\" | \"guild\" | \"message\" | \"user\" | \"member\" | \"operator\"> {\n const out: Pick<Event, \"channel\" | \"guild\" | \"message\" | \"user\" | \"member\" | \"operator\"> = {};\n if (content.channel !== undefined) {\n out.channel = content.channel;\n }\n if (content.guild !== undefined) {\n out.guild = content.guild;\n }\n if (content.message !== undefined) {\n out.message = content.message;\n }\n if (content.user !== undefined) {\n out.user = content.user;\n }\n if (content.member !== undefined) {\n out.member = content.member;\n }\n if (content.operator !== undefined) {\n out.operator = content.operator;\n }\n return out;\n}\n"],"mappings":"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"}
|