@zhin.js/adapter-icqq 6.0.1 → 6.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +59 -0
- package/README.md +32 -437
- package/adapters/icqq.ts +30 -0
- package/agent/tools/announce.ts +21 -0
- package/agent/tools/essence.ts +22 -0
- package/agent/tools/friend_list.ts +19 -0
- package/agent/tools/group_files.ts +27 -0
- package/agent/tools/group_invite.ts +20 -0
- package/agent/tools/group_list.ts +20 -0
- package/agent/tools/group_sign.ts +19 -0
- package/agent/tools/list_muted.ts +22 -0
- package/agent/tools/poke.ts +20 -0
- package/agent/tools/send_user_like.ts +22 -0
- package/agent/tools/set_anonymous.ts +22 -0
- package/agent/tools/set_title.ts +25 -0
- package/lib/agent-prompt.d.ts +0 -1
- package/lib/agent-prompt.js +0 -1
- package/lib/cq-message.d.ts +0 -1
- package/lib/cq-message.js +0 -1
- package/lib/endpoint.d.ts +45 -97
- package/lib/endpoint.js +357 -628
- package/lib/forward-msg.d.ts +1 -2
- package/lib/forward-msg.js +4 -2
- package/lib/icqq-agent-deps.d.ts +25 -0
- package/lib/icqq-agent-deps.js +32 -0
- package/lib/icqq-guild.d.ts +1 -2
- package/lib/icqq-guild.js +4 -2
- package/lib/icqq-inbound.d.ts +10 -1
- package/lib/icqq-inbound.js +27 -3
- package/lib/index.d.ts +6 -18
- package/lib/index.js +6 -50
- package/lib/ipc-client.d.ts +0 -1
- package/lib/ipc-client.js +0 -1
- package/lib/outbound-media.d.ts +0 -1
- package/lib/outbound-media.js +0 -1
- package/lib/protocol.d.ts +73 -2
- package/lib/protocol.js +124 -2
- package/lib/types.d.ts +0 -1
- package/lib/types.js +0 -1
- package/package.json +51 -43
- package/plugin.ts +32 -0
- package/schema.json +35 -0
- package/src/agent-prompt.ts +1 -2
- package/src/endpoint.ts +399 -800
- package/src/forward-msg.ts +2 -2
- package/src/icqq-agent-deps.ts +57 -0
- package/src/icqq-guild.ts +1 -2
- package/src/icqq-inbound.ts +31 -2
- package/src/index.ts +35 -69
- package/src/protocol.ts +191 -1
- package/client/ICQQManagement.tsx +0 -147
- package/client/LoginAssistPanel.tsx +0 -300
- package/client/index.tsx +0 -11
- package/client/tsconfig.json +0 -7
- package/client/utils/api.ts +0 -30
- package/dist/index.js +0 -34
- package/lib/adapter.d.ts +0 -46
- package/lib/adapter.d.ts.map +0 -1
- package/lib/adapter.js +0 -128
- package/lib/adapter.js.map +0 -1
- package/lib/agent-prompt.d.ts.map +0 -1
- package/lib/agent-prompt.js.map +0 -1
- package/lib/cq-message.d.ts.map +0 -1
- package/lib/cq-message.js.map +0 -1
- package/lib/endpoint.d.ts.map +0 -1
- package/lib/endpoint.js.map +0 -1
- package/lib/forward-msg.d.ts.map +0 -1
- package/lib/forward-msg.js.map +0 -1
- package/lib/get-msg.d.ts +0 -3
- package/lib/get-msg.d.ts.map +0 -1
- package/lib/get-msg.js +0 -46
- package/lib/get-msg.js.map +0 -1
- package/lib/icqq-guild.d.ts.map +0 -1
- package/lib/icqq-guild.js.map +0 -1
- package/lib/icqq-inbound.d.ts.map +0 -1
- package/lib/icqq-inbound.js.map +0 -1
- package/lib/icqq-login-assist-bridge.d.ts +0 -5
- package/lib/icqq-login-assist-bridge.d.ts.map +0 -1
- package/lib/icqq-login-assist-bridge.js +0 -148
- package/lib/icqq-login-assist-bridge.js.map +0 -1
- package/lib/icqq-side-events.d.ts +0 -34
- package/lib/icqq-side-events.d.ts.map +0 -1
- package/lib/icqq-side-events.js +0 -194
- package/lib/icqq-side-events.js.map +0 -1
- package/lib/index.d.ts.map +0 -1
- package/lib/index.js.map +0 -1
- package/lib/ipc-client.d.ts.map +0 -1
- package/lib/ipc-client.js.map +0 -1
- package/lib/login-assist-routes.d.ts +0 -7
- package/lib/login-assist-routes.d.ts.map +0 -1
- package/lib/login-assist-routes.js +0 -29
- package/lib/login-assist-routes.js.map +0 -1
- package/lib/login-ipc-contract.d.ts +0 -44
- package/lib/login-ipc-contract.d.ts.map +0 -1
- package/lib/login-ipc-contract.js +0 -35
- package/lib/login-ipc-contract.js.map +0 -1
- package/lib/outbound-media.d.ts.map +0 -1
- package/lib/outbound-media.js.map +0 -1
- package/lib/protocol.d.ts.map +0 -1
- package/lib/protocol.js.map +0 -1
- package/lib/routes.d.ts +0 -8
- package/lib/routes.d.ts.map +0 -1
- package/lib/routes.js +0 -67
- package/lib/routes.js.map +0 -1
- package/lib/segment-mapper.d.ts +0 -2
- package/lib/segment-mapper.d.ts.map +0 -1
- package/lib/segment-mapper.js +0 -2
- package/lib/segment-mapper.js.map +0 -1
- package/lib/tools/index.d.ts +0 -10
- package/lib/tools/index.d.ts.map +0 -1
- package/lib/tools/index.js +0 -341
- package/lib/tools/index.js.map +0 -1
- package/lib/types.d.ts.map +0 -1
- package/lib/types.js.map +0 -1
- package/plugin.yml +0 -3
- package/src/adapter.ts +0 -151
- package/src/get-msg.ts +0 -56
- package/src/icqq-login-assist-bridge.ts +0 -196
- package/src/icqq-side-events.ts +0 -228
- package/src/login-assist-routes.ts +0 -39
- package/src/login-ipc-contract.ts +0 -52
- package/src/routes.ts +0 -83
- package/src/segment-mapper.ts +0 -1
- package/src/tools/index.ts +0 -412
- /package/{skills/icqq → agent}/PERMITS.md +0 -0
package/lib/forward-msg.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* 合并转发(聊天记录)解析:从 json/xml 提取 resid,经 IPC get_forward_msg 拉取正文。
|
|
3
3
|
*/
|
|
4
|
-
import type
|
|
4
|
+
import { type MessageSegment, type QuotedMessagePayload } from 'zhin.js';
|
|
5
5
|
import type { IpcClient } from "./ipc-client.js";
|
|
6
6
|
import { type IcqqMessageElement } from "./icqq-inbound.js";
|
|
7
7
|
/** 从 QQ 合并转发 json 元素提取 resid */
|
|
@@ -24,4 +24,3 @@ export declare function formatForwardMsgResponse(data: unknown): string;
|
|
|
24
24
|
export declare function fetchForwardMsgText(ipc: IpcClient, id: string): Promise<string>;
|
|
25
25
|
/** 若 payload 含合并转发,拉取并追加到 content */
|
|
26
26
|
export declare function enrichQuotedPayloadWithForward(ipc: IpcClient | null | undefined, payload: QuotedMessagePayload, ipcRaw?: unknown): Promise<QuotedMessagePayload>;
|
|
27
|
-
//# sourceMappingURL=forward-msg.d.ts.map
|
package/lib/forward-msg.js
CHANGED
|
@@ -1,4 +1,7 @@
|
|
|
1
|
-
|
|
1
|
+
/**
|
|
2
|
+
* 合并转发(聊天记录)解析:从 json/xml 提取 resid,经 IPC get_forward_msg 拉取正文。
|
|
3
|
+
*/
|
|
4
|
+
import { segment } from 'zhin.js';
|
|
2
5
|
import { Actions } from "./protocol.js";
|
|
3
6
|
import { icqqElementsToSegments, } from "./icqq-inbound.js";
|
|
4
7
|
import { parseCqMessage } from "./cq-message.js";
|
|
@@ -384,4 +387,3 @@ export async function enrichQuotedPayloadWithForward(ipc, payload, ipcRaw) {
|
|
|
384
387
|
}
|
|
385
388
|
return payload;
|
|
386
389
|
}
|
|
387
|
-
//# sourceMappingURL=forward-msg.js.map
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agent tool deps for icqq.
|
|
3
|
+
* Endpoints register themselves on start; tools look up by config name / QQ uin.
|
|
4
|
+
*/
|
|
5
|
+
import type { IpcFriendInfo, IpcGroupInfo } from './types.js';
|
|
6
|
+
import type { IpcResponse } from './protocol.js';
|
|
7
|
+
export interface IcqqAgentEndpoint {
|
|
8
|
+
readonly name: string;
|
|
9
|
+
readonly ipc: {
|
|
10
|
+
request(action: string, params?: Record<string, unknown>): Promise<IpcResponse>;
|
|
11
|
+
};
|
|
12
|
+
readonly friends: Map<number, IpcFriendInfo>;
|
|
13
|
+
readonly groups: Map<number, IpcGroupInfo>;
|
|
14
|
+
request(action: string, params?: Record<string, unknown>): Promise<IpcResponse>;
|
|
15
|
+
}
|
|
16
|
+
export interface IcqqAgentDeps {
|
|
17
|
+
getEndpoint: (endpointId: string) => IcqqAgentEndpoint;
|
|
18
|
+
getAdapter?: () => {
|
|
19
|
+
getEndpoint: (id: string) => IcqqAgentEndpoint;
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
export declare function registerIcqqAgentEndpoint(endpointId: string, endpoint: IcqqAgentEndpoint): () => void;
|
|
23
|
+
/** Optional override used by tests / transitional callers. Pass `null` to clear. */
|
|
24
|
+
export declare function setIcqqAgentDeps(deps: IcqqAgentDeps | null): void;
|
|
25
|
+
export declare function getIcqqAgentDeps(): IcqqAgentDeps;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agent tool deps for icqq.
|
|
3
|
+
* Endpoints register themselves on start; tools look up by config name / QQ uin.
|
|
4
|
+
*/
|
|
5
|
+
const endpoints = new Map();
|
|
6
|
+
let override = null;
|
|
7
|
+
export function registerIcqqAgentEndpoint(endpointId, endpoint) {
|
|
8
|
+
endpoints.set(endpointId, endpoint);
|
|
9
|
+
return () => {
|
|
10
|
+
if (endpoints.get(endpointId) === endpoint) {
|
|
11
|
+
endpoints.delete(endpointId);
|
|
12
|
+
}
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
/** Optional override used by tests / transitional callers. Pass `null` to clear. */
|
|
16
|
+
export function setIcqqAgentDeps(deps) {
|
|
17
|
+
override = deps;
|
|
18
|
+
}
|
|
19
|
+
function lookup(endpointId) {
|
|
20
|
+
const registered = endpoints.get(endpointId);
|
|
21
|
+
if (!registered)
|
|
22
|
+
throw new Error(`Endpoint ${endpointId} 不存在`);
|
|
23
|
+
return registered;
|
|
24
|
+
}
|
|
25
|
+
export function getIcqqAgentDeps() {
|
|
26
|
+
if (override)
|
|
27
|
+
return override;
|
|
28
|
+
return {
|
|
29
|
+
getEndpoint: lookup,
|
|
30
|
+
getAdapter: () => ({ getEndpoint: lookup }),
|
|
31
|
+
};
|
|
32
|
+
}
|
package/lib/icqq-guild.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* ICQQ guild (QQ 频道) catalog sync, inbound normalize, Console channel list.
|
|
3
3
|
*/
|
|
4
|
-
import type
|
|
4
|
+
import { type MessageSegment } from "zhin.js";
|
|
5
5
|
import { type IpcGuildMessageEventData } from "./protocol.js";
|
|
6
6
|
import type { IpcClient } from "./ipc-client.js";
|
|
7
7
|
export interface IcqqGuildChannelEntry {
|
|
@@ -45,4 +45,3 @@ export declare class IcqqGuildCatalog {
|
|
|
45
45
|
}
|
|
46
46
|
export declare function isIcqqGuildIpcEvent(eventName: string | undefined): boolean;
|
|
47
47
|
export declare function normalizeIcqqGuildInboundMessage(data: IpcGuildMessageEventData & Record<string, unknown>): NormalizedIcqqGuildInbound | null;
|
|
48
|
-
//# sourceMappingURL=icqq-guild.d.ts.map
|
package/lib/icqq-guild.js
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ICQQ guild (QQ 频道) catalog sync, inbound normalize, Console channel list.
|
|
3
|
+
*/
|
|
4
|
+
import { toCanonicalSegments } from "zhin.js";
|
|
1
5
|
import { Actions } from "./protocol.js";
|
|
2
6
|
import { parseCqMessage } from "./cq-message.js";
|
|
3
|
-
import { toCanonicalSegments } from "./segment-mapper.js";
|
|
4
7
|
export class IcqqGuildCatalog {
|
|
5
8
|
channels = new Map();
|
|
6
9
|
guilds = new Map();
|
|
@@ -112,4 +115,3 @@ export function normalizeIcqqGuildInboundMessage(data) {
|
|
|
112
115
|
raw: data,
|
|
113
116
|
};
|
|
114
117
|
}
|
|
115
|
-
//# sourceMappingURL=icqq-guild.js.map
|
package/lib/icqq-inbound.d.ts
CHANGED
|
@@ -91,6 +91,16 @@ export interface NormalizedIcqqInbound {
|
|
|
91
91
|
export declare function parseIcqqSenderGroupRole(sender: {
|
|
92
92
|
role?: unknown;
|
|
93
93
|
} | undefined): GroupRole | undefined;
|
|
94
|
+
/**
|
|
95
|
+
* 判断入站消息是否 @ 了本机账号(uin = endpoint 配置 name,即 QQ 号)。
|
|
96
|
+
* 新 Plugin Runtime 的 Message.content 为纯文本,at 信息只能经 metadata 传递,
|
|
97
|
+
* endpoint 在 admit 时用本函数标注 `metadata.mentioned`(供 Agent Host 的 @ 触发)。
|
|
98
|
+
*/
|
|
99
|
+
export declare function isIcqqBotMentioned(input: {
|
|
100
|
+
readonly uin: string;
|
|
101
|
+
readonly content?: readonly MessageSegment[] | null;
|
|
102
|
+
readonly rawMessage?: string;
|
|
103
|
+
}): boolean;
|
|
94
104
|
/** 从 IPC 事件壳中取出 OneBot/icqq 载荷(兼容 data 嵌套或字段在根级) */
|
|
95
105
|
export declare function unwrapIcqqIpcEventPayload(event: {
|
|
96
106
|
data?: unknown;
|
|
@@ -127,4 +137,3 @@ export declare class InboundMessageDeduper {
|
|
|
127
137
|
shouldProcess(messageId: string): boolean;
|
|
128
138
|
clear(): void;
|
|
129
139
|
}
|
|
130
|
-
//# sourceMappingURL=icqq-inbound.d.ts.map
|
package/lib/icqq-inbound.js
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* icqq 守护进程 IPC 入站事件归一化(post_type / message_type / message 段)
|
|
3
3
|
*/
|
|
4
|
-
import { Message } from "zhin.js";
|
|
4
|
+
import { Message, toCanonicalSegments } from "zhin.js";
|
|
5
5
|
import { parseCqMessage } from "./cq-message.js";
|
|
6
|
-
import { toCanonicalSegments } from "./segment-mapper.js";
|
|
7
6
|
import { extractForwardResidFromJsonElement } from "./forward-msg.js";
|
|
8
7
|
const DEDUPE_TTL_MS = 120_000;
|
|
9
8
|
const ICQQ_GROUP_ROLES = new Set(["owner", "admin", "member"]);
|
|
@@ -15,6 +14,32 @@ export function parseIcqqSenderGroupRole(sender) {
|
|
|
15
14
|
const role = raw.trim().toLowerCase();
|
|
16
15
|
return ICQQ_GROUP_ROLES.has(role) ? role : undefined;
|
|
17
16
|
}
|
|
17
|
+
/**
|
|
18
|
+
* 判断入站消息是否 @ 了本机账号(uin = endpoint 配置 name,即 QQ 号)。
|
|
19
|
+
* 新 Plugin Runtime 的 Message.content 为纯文本,at 信息只能经 metadata 传递,
|
|
20
|
+
* endpoint 在 admit 时用本函数标注 `metadata.mentioned`(供 Agent Host 的 @ 触发)。
|
|
21
|
+
*/
|
|
22
|
+
export function isIcqqBotMentioned(input) {
|
|
23
|
+
const uin = String(input.uin ?? "").trim();
|
|
24
|
+
if (!uin)
|
|
25
|
+
return false;
|
|
26
|
+
for (const seg of input.content ?? []) {
|
|
27
|
+
if (!seg || (seg.type !== "at" && seg.type !== "mention"))
|
|
28
|
+
continue;
|
|
29
|
+
const data = (seg.data ?? {});
|
|
30
|
+
const target = String(data.qq ?? data.user_id ?? data.id ?? data.target ?? "").trim();
|
|
31
|
+
if (target && target !== "all" && Number(target) === Number(uin)) {
|
|
32
|
+
return true;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
const raw = input.rawMessage ?? "";
|
|
36
|
+
if (!raw)
|
|
37
|
+
return false;
|
|
38
|
+
const escaped = uin.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
39
|
+
// OneBot CQ 码 `[CQ:at,qq=uin]`;QQ 频道 markdown `<@!uin>`
|
|
40
|
+
return new RegExp(`\\[CQ:at,qq=${escaped}\\]`, "u").test(raw)
|
|
41
|
+
|| new RegExp(`<@!?${escaped}>`, "u").test(raw);
|
|
42
|
+
}
|
|
18
43
|
const MESSAGE_POST_TYPES = new Set(["message"]);
|
|
19
44
|
function isMessagePostType(postType) {
|
|
20
45
|
if (typeof postType !== "string" || postType === "")
|
|
@@ -538,4 +563,3 @@ export class InboundMessageDeduper {
|
|
|
538
563
|
this.seen.clear();
|
|
539
564
|
}
|
|
540
565
|
}
|
|
541
|
-
//# sourceMappingURL=icqq-inbound.js.map
|
package/lib/index.d.ts
CHANGED
|
@@ -1,18 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
web: PageManager;
|
|
8
|
-
router: Router;
|
|
9
|
-
}
|
|
10
|
-
}
|
|
11
|
-
interface Adapters {
|
|
12
|
-
icqq: IcqqAdapter;
|
|
13
|
-
}
|
|
14
|
-
}
|
|
15
|
-
export * from "./types.js";
|
|
16
|
-
export { IcqqEndpoint } from "./endpoint.js";
|
|
17
|
-
export { IcqqAdapter } from "./adapter.js";
|
|
18
|
-
//# sourceMappingURL=index.d.ts.map
|
|
1
|
+
export { Actions, formatInboundContent, formatInboundTarget, formatOutboundBody, parseSendTarget, resolveIcqqConfig, type IcqqAdapterConfig, type IcqqInboundMessage, type IcqqWireSegment, type IpcEvent, type IpcMessage, type IpcRequest, type IpcResponse, type ParsedIcqqSendTarget, type ResolvedIcqqConfig, } from './protocol.js';
|
|
2
|
+
export { IcqqIpcEndpoint, type CreateIcqqIpc, type IcqqEndpointOptions, type IcqqIpcTransport, } from './endpoint.js';
|
|
3
|
+
export * from './types.js';
|
|
4
|
+
export { getIcqqAgentDeps, registerIcqqAgentEndpoint, setIcqqAgentDeps, type IcqqAgentDeps, type IcqqAgentEndpoint, } from './icqq-agent-deps.js';
|
|
5
|
+
export { IpcClient } from './ipc-client.js';
|
|
6
|
+
export { resolveIcqqInboundMessageId, } from './icqq-inbound.js';
|
package/lib/index.js
CHANGED
|
@@ -1,50 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
import { createIcqqAgentPromptContributor } from "./agent-prompt.js";
|
|
8
|
-
import { IcqqAdapter } from "./adapter.js";
|
|
9
|
-
import { registerTools } from "./tools/index.js";
|
|
10
|
-
import { registerRoutes } from "./routes.js";
|
|
11
|
-
import { registerLoginAssistRoutes } from "./login-assist-routes.js";
|
|
12
|
-
export * from "./types.js";
|
|
13
|
-
export { IcqqEndpoint } from "./endpoint.js";
|
|
14
|
-
export { IcqqAdapter } from "./adapter.js";
|
|
15
|
-
const plugin = usePlugin();
|
|
16
|
-
const { provide, useContext, addCommand, root } = plugin;
|
|
17
|
-
// ── 适配器注册 ─────────────────────────────────────────────────────
|
|
18
|
-
provide({
|
|
19
|
-
name: "icqq",
|
|
20
|
-
description: "ICQQ Adapter",
|
|
21
|
-
mounted: async (p) => {
|
|
22
|
-
registerAgentPromptContributor(createIcqqAgentPromptContributor());
|
|
23
|
-
const adapter = new IcqqAdapter(p);
|
|
24
|
-
await adapter.start();
|
|
25
|
-
return adapter;
|
|
26
|
-
},
|
|
27
|
-
dispose: async (adapter) => {
|
|
28
|
-
unregisterAgentPromptContributor("icqq");
|
|
29
|
-
await adapter.stop();
|
|
30
|
-
},
|
|
31
|
-
});
|
|
32
|
-
// ── AI 工具注册 ────────────────────────────────────────────────────
|
|
33
|
-
useContext("tool", "icqq", (toolService, icqq) => {
|
|
34
|
-
return registerTools(toolService, icqq, plugin.name);
|
|
35
|
-
});
|
|
36
|
-
// ── Web 控制台入口 ─────────────────────────────────────────────────
|
|
37
|
-
useContext("web", (pageManager) => {
|
|
38
|
-
pageManager.addEntry({
|
|
39
|
-
id: "icqq",
|
|
40
|
-
development: path.resolve(import.meta.dirname, "../client/index.tsx"),
|
|
41
|
-
production: path.resolve(import.meta.dirname, "../dist/index.js"),
|
|
42
|
-
meta: { name: "ICQQ" },
|
|
43
|
-
});
|
|
44
|
-
});
|
|
45
|
-
// ── HTTP 路由 ──────────────────────────────────────────────────────
|
|
46
|
-
useContext("router", "icqq", async (router, icqq) => {
|
|
47
|
-
registerRoutes(router, icqq, root);
|
|
48
|
-
registerLoginAssistRoutes(router, root);
|
|
49
|
-
});
|
|
50
|
-
//# sourceMappingURL=index.js.map
|
|
1
|
+
export { Actions, formatInboundContent, formatInboundTarget, formatOutboundBody, parseSendTarget, resolveIcqqConfig, } from './protocol.js';
|
|
2
|
+
export { IcqqIpcEndpoint, } from './endpoint.js';
|
|
3
|
+
export * from './types.js';
|
|
4
|
+
export { getIcqqAgentDeps, registerIcqqAgentEndpoint, setIcqqAgentDeps, } from './icqq-agent-deps.js';
|
|
5
|
+
export { IpcClient } from './ipc-client.js';
|
|
6
|
+
export { resolveIcqqInboundMessageId, } from './icqq-inbound.js';
|
package/lib/ipc-client.d.ts
CHANGED
package/lib/ipc-client.js
CHANGED
package/lib/outbound-media.d.ts
CHANGED
|
@@ -11,4 +11,3 @@ export declare function resolveIcqqOutboundMediaMode(config: Pick<IcqqEndpointCo
|
|
|
11
11
|
* base64 模式:不改动(由 toCqString 生成 base64://,供异机守护进程读取)。
|
|
12
12
|
*/
|
|
13
13
|
export declare function materializeOutboundBase64(content: SendContent, mode?: IcqqOutboundMediaMode): SendContent;
|
|
14
|
-
//# sourceMappingURL=outbound-media.d.ts.map
|
package/lib/outbound-media.js
CHANGED
package/lib/protocol.d.ts
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
* 2. 认证通过后发送 IpcRequest → 等待 IpcResponse
|
|
10
10
|
* 3. 认证通过后自动接收 icqq 事件推送(IpcEvent),断开连接自动停止
|
|
11
11
|
*
|
|
12
|
-
* 登录相关:**推送**走 IpcEvent(`system.login
|
|
12
|
+
* 登录相关:**推送**走 IpcEvent(`system.login.*`)。
|
|
13
13
|
*/
|
|
14
14
|
/** CLI → Daemon 请求 */
|
|
15
15
|
export type IpcRequest = {
|
|
@@ -195,4 +195,75 @@ export declare const Actions: {
|
|
|
195
195
|
readonly SET_NOTIFY: "set_notify";
|
|
196
196
|
readonly GET_NOTIFY: "get_notify";
|
|
197
197
|
};
|
|
198
|
-
|
|
198
|
+
/** Plugin Runtime owner config (`plugins.<instanceKey>` / schema.json). */
|
|
199
|
+
export interface IcqqAdapterConfig {
|
|
200
|
+
readonly name?: string;
|
|
201
|
+
readonly autoReconnect?: boolean;
|
|
202
|
+
readonly outboundMedia?: 'file' | 'base64';
|
|
203
|
+
readonly rpc?: {
|
|
204
|
+
readonly host?: string;
|
|
205
|
+
readonly port?: number;
|
|
206
|
+
readonly token?: string;
|
|
207
|
+
};
|
|
208
|
+
/** Transitional: legacy root `endpoints[]` with `context: icqq`. */
|
|
209
|
+
readonly endpoints?: ReadonlyArray<{
|
|
210
|
+
readonly context?: string;
|
|
211
|
+
readonly name?: string;
|
|
212
|
+
readonly autoReconnect?: boolean;
|
|
213
|
+
readonly outboundMedia?: 'file' | 'base64';
|
|
214
|
+
readonly rpc?: {
|
|
215
|
+
readonly host?: string;
|
|
216
|
+
readonly port?: number;
|
|
217
|
+
readonly token?: string;
|
|
218
|
+
};
|
|
219
|
+
}>;
|
|
220
|
+
}
|
|
221
|
+
export interface ResolvedIcqqConfig {
|
|
222
|
+
readonly context: 'icqq';
|
|
223
|
+
readonly name: string;
|
|
224
|
+
readonly autoReconnect: boolean;
|
|
225
|
+
readonly outboundMedia?: 'file' | 'base64';
|
|
226
|
+
readonly rpc?: {
|
|
227
|
+
readonly host: string;
|
|
228
|
+
readonly port: number;
|
|
229
|
+
readonly token: string;
|
|
230
|
+
};
|
|
231
|
+
}
|
|
232
|
+
export interface IcqqWireSegment {
|
|
233
|
+
readonly type: string;
|
|
234
|
+
readonly data?: Record<string, unknown>;
|
|
235
|
+
}
|
|
236
|
+
export interface IcqqInboundMessage {
|
|
237
|
+
readonly id: string;
|
|
238
|
+
readonly target: string;
|
|
239
|
+
readonly content: string;
|
|
240
|
+
readonly sender: string;
|
|
241
|
+
readonly channelType: 'private' | 'group' | 'channel';
|
|
242
|
+
readonly metadata?: Record<string, unknown>;
|
|
243
|
+
}
|
|
244
|
+
export type ParsedIcqqSendTarget = {
|
|
245
|
+
readonly kind: 'private';
|
|
246
|
+
readonly userId: number;
|
|
247
|
+
} | {
|
|
248
|
+
readonly kind: 'group';
|
|
249
|
+
readonly groupId: number;
|
|
250
|
+
} | {
|
|
251
|
+
readonly kind: 'temp';
|
|
252
|
+
readonly groupId: number;
|
|
253
|
+
readonly userId: number;
|
|
254
|
+
} | {
|
|
255
|
+
readonly kind: 'channel';
|
|
256
|
+
readonly guildId: string;
|
|
257
|
+
readonly channelId: string;
|
|
258
|
+
};
|
|
259
|
+
export declare function resolveIcqqConfig(config?: IcqqAdapterConfig): ResolvedIcqqConfig;
|
|
260
|
+
/** Gateway reply target: `private:uid` / `group:gid` / `temp:gid:uid` / `channel:guild:channel`. */
|
|
261
|
+
export declare function formatInboundTarget(input: {
|
|
262
|
+
readonly channelType: 'private' | 'group' | 'channel';
|
|
263
|
+
readonly channelId: string;
|
|
264
|
+
readonly channelParentGroupId?: string;
|
|
265
|
+
readonly guildId?: string;
|
|
266
|
+
}): string;
|
|
267
|
+
export declare function parseSendTarget(target: string): ParsedIcqqSendTarget;
|
|
268
|
+
export declare function formatOutboundBody(payload: unknown): string;
|
|
269
|
+
export declare function formatInboundContent(rawMessage: string): string;
|
package/lib/protocol.js
CHANGED
|
@@ -9,8 +9,9 @@
|
|
|
9
9
|
* 2. 认证通过后发送 IpcRequest → 等待 IpcResponse
|
|
10
10
|
* 3. 认证通过后自动接收 icqq 事件推送(IpcEvent),断开连接自动停止
|
|
11
11
|
*
|
|
12
|
-
* 登录相关:**推送**走 IpcEvent(`system.login
|
|
12
|
+
* 登录相关:**推送**走 IpcEvent(`system.login.*`)。
|
|
13
13
|
*/
|
|
14
|
+
import { pickCredential } from '@zhin.js/adapter';
|
|
14
15
|
/** 从 IPC 入站 payload 解析可用于引用/撤回的消息 ID */
|
|
15
16
|
export { resolveIcqqInboundMessageId } from "./icqq-inbound.js";
|
|
16
17
|
/**
|
|
@@ -184,4 +185,125 @@ export const Actions = {
|
|
|
184
185
|
SET_NOTIFY: "set_notify",
|
|
185
186
|
GET_NOTIFY: "get_notify",
|
|
186
187
|
};
|
|
187
|
-
|
|
188
|
+
export function resolveIcqqConfig(config = {}) {
|
|
189
|
+
const entry = config.endpoints?.find((item) => item.context === 'icqq');
|
|
190
|
+
const name = pickCredential(config.name, entry?.name, process.env.ICQQ_ACCOUNT);
|
|
191
|
+
if (!/^\d+$/.test(name)) {
|
|
192
|
+
throw new TypeError('ICQQ adapter requires numeric name (QQ uin) via plugins.<key>.name or ICQQ_ACCOUNT');
|
|
193
|
+
}
|
|
194
|
+
const autoReconnect = config.autoReconnect ?? entry?.autoReconnect ?? true;
|
|
195
|
+
const outboundMedia = config.outboundMedia ?? entry?.outboundMedia;
|
|
196
|
+
const rpcRaw = config.rpc ?? entry?.rpc;
|
|
197
|
+
const rpc = rpcRaw?.host && rpcRaw.port != null && rpcRaw.token
|
|
198
|
+
? { host: rpcRaw.host, port: Number(rpcRaw.port), token: rpcRaw.token }
|
|
199
|
+
: undefined;
|
|
200
|
+
return {
|
|
201
|
+
context: 'icqq',
|
|
202
|
+
name,
|
|
203
|
+
autoReconnect,
|
|
204
|
+
...(outboundMedia ? { outboundMedia } : {}),
|
|
205
|
+
...(rpc ? { rpc } : {}),
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
/** Gateway reply target: `private:uid` / `group:gid` / `temp:gid:uid` / `channel:guild:channel`. */
|
|
209
|
+
export function formatInboundTarget(input) {
|
|
210
|
+
if (input.channelType === 'private' && input.channelParentGroupId) {
|
|
211
|
+
return `temp:${input.channelParentGroupId}:${input.channelId}`;
|
|
212
|
+
}
|
|
213
|
+
if (input.channelType === 'channel' && input.guildId) {
|
|
214
|
+
return `channel:${input.guildId}:${input.channelId}`;
|
|
215
|
+
}
|
|
216
|
+
return `${input.channelType}:${input.channelId}`;
|
|
217
|
+
}
|
|
218
|
+
export function parseSendTarget(target) {
|
|
219
|
+
const trimmed = target.trim();
|
|
220
|
+
if (trimmed.startsWith('temp:')) {
|
|
221
|
+
const rest = trimmed.slice(5);
|
|
222
|
+
const [groupId, userId] = rest.split(':');
|
|
223
|
+
if (!groupId || !userId)
|
|
224
|
+
throw new TypeError(`Invalid icqq temp target: ${target}`);
|
|
225
|
+
return { kind: 'temp', groupId: Number(groupId), userId: Number(userId) };
|
|
226
|
+
}
|
|
227
|
+
if (trimmed.startsWith('channel:')) {
|
|
228
|
+
const rest = trimmed.slice(8);
|
|
229
|
+
const idx = rest.indexOf(':');
|
|
230
|
+
if (idx <= 0)
|
|
231
|
+
throw new TypeError(`Invalid icqq channel target: ${target}`);
|
|
232
|
+
return {
|
|
233
|
+
kind: 'channel',
|
|
234
|
+
guildId: rest.slice(0, idx),
|
|
235
|
+
channelId: rest.slice(idx + 1),
|
|
236
|
+
};
|
|
237
|
+
}
|
|
238
|
+
if (trimmed.startsWith('private:')) {
|
|
239
|
+
return { kind: 'private', userId: Number(trimmed.slice(8)) };
|
|
240
|
+
}
|
|
241
|
+
if (trimmed.startsWith('group:')) {
|
|
242
|
+
return { kind: 'group', groupId: Number(trimmed.slice(6)) };
|
|
243
|
+
}
|
|
244
|
+
// Bare numeric id → private (common reply path)
|
|
245
|
+
if (/^\d+$/.test(trimmed)) {
|
|
246
|
+
return { kind: 'private', userId: Number(trimmed) };
|
|
247
|
+
}
|
|
248
|
+
throw new TypeError(`Invalid icqq send target: ${target}`);
|
|
249
|
+
}
|
|
250
|
+
export function formatOutboundBody(payload) {
|
|
251
|
+
if (typeof payload === 'string') {
|
|
252
|
+
const text = payload.trim();
|
|
253
|
+
return text || '\u200b';
|
|
254
|
+
}
|
|
255
|
+
if (payload == null)
|
|
256
|
+
return '\u200b';
|
|
257
|
+
if (!Array.isArray(payload)) {
|
|
258
|
+
// 单个段对象(非数组)按单元素数组处理,杜绝 String(object) → '[object Object]'
|
|
259
|
+
if (isWireSegment(payload)) {
|
|
260
|
+
return formatOutboundBody([payload]);
|
|
261
|
+
}
|
|
262
|
+
if (typeof payload === 'object' && payload !== null && 'text' in payload) {
|
|
263
|
+
const text = String(payload.text ?? '').trim();
|
|
264
|
+
return text || '\u200b';
|
|
265
|
+
}
|
|
266
|
+
const text = String(payload).trim();
|
|
267
|
+
return text || '\u200b';
|
|
268
|
+
}
|
|
269
|
+
const joined = payload.map((seg) => {
|
|
270
|
+
if (typeof seg === 'string')
|
|
271
|
+
return seg;
|
|
272
|
+
const item = seg;
|
|
273
|
+
switch (item.type) {
|
|
274
|
+
case 'text':
|
|
275
|
+
return String(item.data?.text ?? '');
|
|
276
|
+
case 'at':
|
|
277
|
+
case 'mention':
|
|
278
|
+
return `[at:${item.data?.qq ?? item.data?.id ?? item.data?.target ?? ''}]`;
|
|
279
|
+
case 'image':
|
|
280
|
+
return `[image:${item.data?.file || item.data?.url || base64Media(item.data) || ''}]`;
|
|
281
|
+
case 'face':
|
|
282
|
+
return `[face:${item.data?.id ?? ''}]`;
|
|
283
|
+
case 'reply':
|
|
284
|
+
return `[reply:${item.data?.message_id ?? item.data?.id ?? ''}]`;
|
|
285
|
+
case 'record':
|
|
286
|
+
case 'audio':
|
|
287
|
+
return `[record:${item.data?.file || item.data?.url || base64Media(item.data) || ''}]`;
|
|
288
|
+
case 'video':
|
|
289
|
+
return `[video:${item.data?.file || item.data?.url || base64Media(item.data) || ''}]`;
|
|
290
|
+
default:
|
|
291
|
+
return String(item.data?.text ?? '');
|
|
292
|
+
}
|
|
293
|
+
}).join('').trim();
|
|
294
|
+
return joined || '\u200b';
|
|
295
|
+
}
|
|
296
|
+
export function formatInboundContent(rawMessage) {
|
|
297
|
+
return rawMessage;
|
|
298
|
+
}
|
|
299
|
+
/** base64 媒体回退:CQ `base64://` 由守护进程解码(异机 RPC 场景)。 */
|
|
300
|
+
function base64Media(data) {
|
|
301
|
+
const b64 = data?.base64;
|
|
302
|
+
return typeof b64 === 'string' && b64 ? `base64://${b64}` : undefined;
|
|
303
|
+
}
|
|
304
|
+
/** 段对象判定:`{ type: string, data? }`(非数组 payload 的防御入口)。 */
|
|
305
|
+
function isWireSegment(value) {
|
|
306
|
+
return typeof value === 'object'
|
|
307
|
+
&& value !== null
|
|
308
|
+
&& typeof value.type === 'string';
|
|
309
|
+
}
|
package/lib/types.d.ts
CHANGED
package/lib/types.js
CHANGED
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zhin.js/adapter-icqq",
|
|
3
|
-
"version": "6.0.
|
|
4
|
-
"description": "Zhin.js adapter for
|
|
3
|
+
"version": "6.0.3",
|
|
4
|
+
"description": "Zhin.js ICQQ adapter for Plugin Runtime (IPC daemon client)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./lib/index.js",
|
|
7
7
|
"types": "./lib/index.d.ts",
|
|
@@ -33,56 +33,49 @@
|
|
|
33
33
|
"type": "git",
|
|
34
34
|
"directory": "plugins/adapters/icqq"
|
|
35
35
|
},
|
|
36
|
-
"
|
|
37
|
-
"
|
|
38
|
-
"
|
|
39
|
-
"
|
|
40
|
-
"
|
|
41
|
-
"skills",
|
|
42
|
-
"plugin.yml",
|
|
43
|
-
"README.md",
|
|
44
|
-
"CHANGELOG.md"
|
|
45
|
-
],
|
|
46
|
-
"dependencies": {},
|
|
47
|
-
"devDependencies": {
|
|
48
|
-
"@radix-ui/themes": "^3.2.1",
|
|
49
|
-
"@types/react": "^19.2.17",
|
|
50
|
-
"@types/react-dom": "^19.2.3",
|
|
51
|
-
"lucide-react": "^1.22.0",
|
|
52
|
-
"radix-ui": "^1.6.2",
|
|
53
|
-
"typescript": "^6.0.3",
|
|
54
|
-
"@zhin.js/agent": "1.0.2",
|
|
55
|
-
"@zhin.js/cli": "1.0.92",
|
|
56
|
-
"@zhin.js/client": "2.0.4",
|
|
57
|
-
"@zhin.js/contract": "1.0.2",
|
|
58
|
-
"@zhin.js/core": "1.3.3",
|
|
59
|
-
"@zhin.js/host-api": "2.0.4",
|
|
60
|
-
"@zhin.js/host-router": "2.0.2",
|
|
61
|
-
"zhin.js": "4.1.1"
|
|
36
|
+
"dependencies": {
|
|
37
|
+
"@zhin.js/adapter": "1.0.1",
|
|
38
|
+
"@zhin.js/core": "1.3.5",
|
|
39
|
+
"@zhin.js/logger": "1.0.75",
|
|
40
|
+
"@zhin.js/plugin-runtime": "1.0.1"
|
|
62
41
|
},
|
|
63
42
|
"peerDependencies": {
|
|
64
|
-
"
|
|
65
|
-
"@zhin.js/
|
|
66
|
-
"@zhin.js/
|
|
67
|
-
"@zhin.js/
|
|
68
|
-
"@zhin.js/
|
|
69
|
-
"
|
|
70
|
-
"zhin.js": "4.1.1"
|
|
43
|
+
"zod": "^4.0.0",
|
|
44
|
+
"@zhin.js/adapter": "1.0.1",
|
|
45
|
+
"@zhin.js/agent": "1.0.4",
|
|
46
|
+
"@zhin.js/core": "1.3.5",
|
|
47
|
+
"@zhin.js/plugin-runtime": "1.0.1",
|
|
48
|
+
"zhin.js": "4.1.3"
|
|
71
49
|
},
|
|
72
50
|
"peerDependenciesMeta": {
|
|
73
|
-
"
|
|
74
|
-
"optional": true
|
|
75
|
-
},
|
|
76
|
-
"@zhin.js/client": {
|
|
51
|
+
"zhin.js": {
|
|
77
52
|
"optional": true
|
|
78
53
|
},
|
|
79
|
-
"@zhin.js/
|
|
54
|
+
"@zhin.js/agent": {
|
|
80
55
|
"optional": true
|
|
81
56
|
},
|
|
82
|
-
"
|
|
57
|
+
"zod": {
|
|
83
58
|
"optional": true
|
|
84
59
|
}
|
|
85
60
|
},
|
|
61
|
+
"devDependencies": {
|
|
62
|
+
"@types/node": "^26.1.0",
|
|
63
|
+
"typescript": "^6.0.3",
|
|
64
|
+
"vitest": "^4.1.10",
|
|
65
|
+
"zod": "^4.4.3",
|
|
66
|
+
"@zhin.js/agent": "1.0.4",
|
|
67
|
+
"zhin.js": "4.1.3"
|
|
68
|
+
},
|
|
69
|
+
"files": [
|
|
70
|
+
"adapters",
|
|
71
|
+
"plugin.ts",
|
|
72
|
+
"schema.json",
|
|
73
|
+
"src",
|
|
74
|
+
"lib",
|
|
75
|
+
"agent",
|
|
76
|
+
"README.md",
|
|
77
|
+
"CHANGELOG.md"
|
|
78
|
+
],
|
|
86
79
|
"publishConfig": {
|
|
87
80
|
"access": "public",
|
|
88
81
|
"registry": "https://registry.npmjs.org"
|
|
@@ -90,8 +83,23 @@
|
|
|
90
83
|
"engines": {
|
|
91
84
|
"node": "^20.19.0 || >=22.12.0"
|
|
92
85
|
},
|
|
86
|
+
"zhin": {
|
|
87
|
+
"protocol": 1,
|
|
88
|
+
"type": "plugin",
|
|
89
|
+
"entry": "./plugin.ts",
|
|
90
|
+
"engine": "^1.0.0",
|
|
91
|
+
"runtime": "trusted",
|
|
92
|
+
"features": [
|
|
93
|
+
{
|
|
94
|
+
"package": "@zhin.js/adapter",
|
|
95
|
+
"api": "^1.0.0"
|
|
96
|
+
}
|
|
97
|
+
],
|
|
98
|
+
"plugins": []
|
|
99
|
+
},
|
|
93
100
|
"scripts": {
|
|
94
|
-
"build": "
|
|
95
|
-
"clean": "rimraf lib"
|
|
101
|
+
"build": "tsc",
|
|
102
|
+
"clean": "rimraf lib",
|
|
103
|
+
"test": "NODE_OPTIONS=--experimental-strip-types vitest run --root ../../.. plugins/adapters/icqq/tests"
|
|
96
104
|
}
|
|
97
105
|
}
|