@zhin.js/adapter-icqq 3.0.3 → 3.0.5
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 +26 -0
- package/README.md +1 -1
- package/dist/index.js +1 -1
- package/lib/bot.d.ts +56 -12
- package/lib/bot.d.ts.map +1 -1
- package/lib/bot.js +416 -136
- package/lib/bot.js.map +1 -1
- package/lib/cq-message.d.ts +10 -0
- package/lib/cq-message.d.ts.map +1 -0
- package/lib/cq-message.js +119 -0
- package/lib/cq-message.js.map +1 -0
- package/lib/forward-msg.d.ts +27 -0
- package/lib/forward-msg.d.ts.map +1 -0
- package/lib/forward-msg.js +387 -0
- package/lib/forward-msg.js.map +1 -0
- package/lib/get-msg.d.ts +3 -0
- package/lib/get-msg.d.ts.map +1 -0
- package/lib/get-msg.js +46 -0
- package/lib/get-msg.js.map +1 -0
- package/lib/icqq-inbound.d.ts +114 -0
- package/lib/icqq-inbound.d.ts.map +1 -0
- package/lib/icqq-inbound.js +495 -0
- package/lib/icqq-inbound.js.map +1 -0
- package/lib/icqq-side-events.d.ts +34 -0
- package/lib/icqq-side-events.d.ts.map +1 -0
- package/lib/icqq-side-events.js +194 -0
- package/lib/icqq-side-events.js.map +1 -0
- package/lib/index.d.ts +2 -0
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +1 -0
- package/lib/index.js.map +1 -1
- package/lib/ipc-client.d.ts +7 -2
- package/lib/ipc-client.d.ts.map +1 -1
- package/lib/ipc-client.js +74 -16
- package/lib/ipc-client.js.map +1 -1
- package/lib/protocol.d.ts +3 -10
- package/lib/protocol.d.ts.map +1 -1
- package/lib/protocol.js +2 -0
- package/lib/protocol.js.map +1 -1
- package/lib/types.d.ts +44 -0
- package/lib/types.d.ts.map +1 -1
- package/lib/typing-indicator-example.d.ts +108 -0
- package/lib/typing-indicator-example.d.ts.map +1 -0
- package/lib/typing-indicator-example.js +220 -0
- package/lib/typing-indicator-example.js.map +1 -0
- package/lib/typing-indicator.d.ts +89 -0
- package/lib/typing-indicator.d.ts.map +1 -0
- package/lib/typing-indicator.js +237 -0
- package/lib/typing-indicator.js.map +1 -0
- package/package.json +14 -10
- package/src/bot.ts +524 -149
- package/src/cq-message.ts +120 -0
- package/src/forward-msg.ts +433 -0
- package/src/get-msg.ts +56 -0
- package/src/icqq-inbound.ts +616 -0
- package/src/icqq-side-events.ts +228 -0
- package/src/index.ts +8 -0
- package/src/ipc-client.ts +76 -16
- package/src/protocol.ts +4 -10
- package/src/types.ts +45 -0
- package/src/typing-indicator-example.ts +269 -0
- package/src/typing-indicator.ts +334 -0
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* icqq IPC 非 message 入站:notice / request / meta(对齐 OneBot 与 @icqqjs/icqq EventMap)
|
|
3
|
+
* @see https://icqq.pages.dev/interfaces/EventMap.html
|
|
4
|
+
*/
|
|
5
|
+
import { Notice, Request, formatCompact } from "zhin.js";
|
|
6
|
+
import type { NoticeType } from "zhin.js";
|
|
7
|
+
import type { RequestType } from "zhin.js";
|
|
8
|
+
import { Actions } from "./protocol.js";
|
|
9
|
+
import type { IpcClient } from "./ipc-client.js";
|
|
10
|
+
|
|
11
|
+
/** OneBot / icqq 守护进程推送的通用事件字段 */
|
|
12
|
+
export interface IcqqIpcRawEvent {
|
|
13
|
+
post_type?: string;
|
|
14
|
+
time?: number;
|
|
15
|
+
self_id?: number;
|
|
16
|
+
notice_type?: string;
|
|
17
|
+
request_type?: string;
|
|
18
|
+
sub_type?: string;
|
|
19
|
+
meta_event_type?: string;
|
|
20
|
+
group_id?: number;
|
|
21
|
+
user_id?: number;
|
|
22
|
+
operator_id?: number;
|
|
23
|
+
flag?: string;
|
|
24
|
+
comment?: string;
|
|
25
|
+
[key: string]: unknown;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function resolveIcqqEventPostType(
|
|
29
|
+
data: Record<string, unknown>,
|
|
30
|
+
): string | undefined {
|
|
31
|
+
const pt = data.post_type;
|
|
32
|
+
if (typeof pt === "string" && pt !== "") return pt;
|
|
33
|
+
if (data.notice_type != null) return "notice";
|
|
34
|
+
if (data.request_type != null) return "request";
|
|
35
|
+
if (data.meta_event_type != null) return "meta_event";
|
|
36
|
+
return undefined;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function isIcqqNoticePayload(data: unknown): data is IcqqIpcRawEvent {
|
|
40
|
+
if (!data || typeof data !== "object") return false;
|
|
41
|
+
const pt = resolveIcqqEventPostType(data as Record<string, unknown>);
|
|
42
|
+
return pt === "notice" || (typeof pt === "string" && pt.startsWith("notice."));
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export function isIcqqRequestPayload(data: unknown): data is IcqqIpcRawEvent {
|
|
46
|
+
if (!data || typeof data !== "object") return false;
|
|
47
|
+
const pt = resolveIcqqEventPostType(data as Record<string, unknown>);
|
|
48
|
+
return pt === "request" || (typeof pt === "string" && pt.startsWith("request."));
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export function isIcqqMetaPayload(data: unknown): data is IcqqIpcRawEvent {
|
|
52
|
+
if (!data || typeof data !== "object") return false;
|
|
53
|
+
const pt = resolveIcqqEventPostType(data as Record<string, unknown>);
|
|
54
|
+
return pt === "meta_event" || (typeof pt === "string" && pt.startsWith("system."));
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/** 去重用:notice / request 无稳定 id 时组合键 */
|
|
58
|
+
export function resolveSideEventDedupeKey(
|
|
59
|
+
data: IcqqIpcRawEvent,
|
|
60
|
+
kind: "notice" | "request" | "meta",
|
|
61
|
+
): string {
|
|
62
|
+
if (data.flag != null && String(data.flag) !== "") {
|
|
63
|
+
return `${kind}:${data.flag}`;
|
|
64
|
+
}
|
|
65
|
+
const time = data.time ?? 0;
|
|
66
|
+
const type =
|
|
67
|
+
data.notice_type ?? data.request_type ?? data.meta_event_type ?? kind;
|
|
68
|
+
const scope = data.group_id ?? data.user_id ?? "";
|
|
69
|
+
const sub = data.sub_type ?? "";
|
|
70
|
+
return `${kind}:${time}_${type}_${scope}_${sub}`;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const NOTICE_TYPE_MAP: Record<string, NoticeType> = {
|
|
74
|
+
group_increase: "group_member_increase",
|
|
75
|
+
group_decrease: "group_member_decrease",
|
|
76
|
+
group_admin: "group_admin_change",
|
|
77
|
+
group_ban: "group_ban",
|
|
78
|
+
group_recall: "group_recall",
|
|
79
|
+
friend_recall: "friend_recall",
|
|
80
|
+
friend_add: "friend_add",
|
|
81
|
+
group_upload: "group_upload",
|
|
82
|
+
group_sign: "group_sign",
|
|
83
|
+
group_transfer: "group_transfer",
|
|
84
|
+
// icqq EventMap 命名 → Zhin NoticeType
|
|
85
|
+
"group.increase": "group_member_increase",
|
|
86
|
+
"group.decrease": "group_member_decrease",
|
|
87
|
+
"group.admin": "group_admin_change",
|
|
88
|
+
"group.ban": "group_ban",
|
|
89
|
+
"group.recall": "group_recall",
|
|
90
|
+
"friend.increase": "friend_add",
|
|
91
|
+
"friend.decrease": "friend_recall",
|
|
92
|
+
"friend.recall": "friend_recall",
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
function resolveNoticeZhinType(event: IcqqIpcRawEvent): NoticeType {
|
|
96
|
+
const raw = event.notice_type ?? "";
|
|
97
|
+
if (NOTICE_TYPE_MAP[raw]) return NOTICE_TYPE_MAP[raw];
|
|
98
|
+
if (raw === "notify") {
|
|
99
|
+
if (event.sub_type === "poke") {
|
|
100
|
+
return event.group_id != null ? "group_poke" : "friend_poke";
|
|
101
|
+
}
|
|
102
|
+
return `notify_${event.sub_type ?? "unknown"}` as NoticeType;
|
|
103
|
+
}
|
|
104
|
+
if (raw.startsWith("notice.")) {
|
|
105
|
+
const suffix = raw.slice("notice.".length).replace(/\./g, "_");
|
|
106
|
+
return (NOTICE_TYPE_MAP[suffix] ?? suffix) as NoticeType;
|
|
107
|
+
}
|
|
108
|
+
return raw as NoticeType;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function senderFromId(id: unknown, name?: string) {
|
|
112
|
+
if (id == null) return undefined;
|
|
113
|
+
const s = String(id);
|
|
114
|
+
return { id: s, name: name ?? s };
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
export function formatIcqqNotice(
|
|
118
|
+
event: IcqqIpcRawEvent,
|
|
119
|
+
botName: string,
|
|
120
|
+
): ReturnType<typeof Notice.from<IcqqIpcRawEvent>> {
|
|
121
|
+
const $type = resolveNoticeZhinType(event);
|
|
122
|
+
const isGroup = event.group_id != null;
|
|
123
|
+
const channelId = String(
|
|
124
|
+
event.group_id ?? event.user_id ?? event.operator_id ?? "",
|
|
125
|
+
);
|
|
126
|
+
const tsSec = event.time ?? Math.floor(Date.now() / 1000);
|
|
127
|
+
|
|
128
|
+
return Notice.from(event, {
|
|
129
|
+
$id: resolveSideEventDedupeKey(event, "notice"),
|
|
130
|
+
$adapter: "icqq",
|
|
131
|
+
$bot: botName,
|
|
132
|
+
$type,
|
|
133
|
+
$subType: event.sub_type,
|
|
134
|
+
$channel: {
|
|
135
|
+
id: channelId,
|
|
136
|
+
type: isGroup ? "group" : "private",
|
|
137
|
+
},
|
|
138
|
+
$operator: senderFromId(event.operator_id),
|
|
139
|
+
$target: senderFromId(event.user_id),
|
|
140
|
+
$timestamp: tsSec * 1000,
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
function resolveRequestZhinType(event: IcqqIpcRawEvent): RequestType {
|
|
145
|
+
const rt = event.request_type ?? "";
|
|
146
|
+
if (rt === "friend" || rt.startsWith("friend")) return "friend_add";
|
|
147
|
+
if (rt === "group" || rt.startsWith("group")) {
|
|
148
|
+
return event.sub_type === "invite" ? "group_invite" : "group_add";
|
|
149
|
+
}
|
|
150
|
+
return rt as RequestType;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
export function formatIcqqRequest(
|
|
154
|
+
event: IcqqIpcRawEvent,
|
|
155
|
+
botName: string,
|
|
156
|
+
ipc: IpcClient,
|
|
157
|
+
): ReturnType<typeof Request.from<IcqqIpcRawEvent>> {
|
|
158
|
+
const $type = resolveRequestZhinType(event);
|
|
159
|
+
const isGroup = event.group_id != null;
|
|
160
|
+
const channelId = String(event.group_id ?? event.user_id ?? "");
|
|
161
|
+
const tsSec = event.time ?? Math.floor(Date.now() / 1000);
|
|
162
|
+
const flag = event.flag ?? resolveSideEventDedupeKey(event, "request");
|
|
163
|
+
|
|
164
|
+
const isFriend =
|
|
165
|
+
event.request_type === "friend" ||
|
|
166
|
+
(typeof event.request_type === "string" &&
|
|
167
|
+
event.request_type.startsWith("friend"));
|
|
168
|
+
|
|
169
|
+
return Request.from(event, {
|
|
170
|
+
$id: String(flag),
|
|
171
|
+
$adapter: "icqq",
|
|
172
|
+
$bot: botName,
|
|
173
|
+
$type,
|
|
174
|
+
$subType: event.sub_type,
|
|
175
|
+
$channel: {
|
|
176
|
+
id: channelId,
|
|
177
|
+
type: isGroup ? "group" : "private",
|
|
178
|
+
},
|
|
179
|
+
$sender: senderFromId(event.user_id) ?? { id: "", name: "" },
|
|
180
|
+
$comment: event.comment,
|
|
181
|
+
$timestamp: tsSec * 1000,
|
|
182
|
+
$approve: async (remark?: string) => {
|
|
183
|
+
const action = isFriend
|
|
184
|
+
? Actions.HANDLE_FRIEND_REQUEST
|
|
185
|
+
: Actions.HANDLE_GROUP_REQUEST;
|
|
186
|
+
const params: Record<string, unknown> = {
|
|
187
|
+
flag: event.flag ?? flag,
|
|
188
|
+
approve: true,
|
|
189
|
+
};
|
|
190
|
+
if (remark) params.remark = remark;
|
|
191
|
+
if (!isFriend && event.sub_type) params.sub_type = event.sub_type;
|
|
192
|
+
const resp = await ipc.request(action, params);
|
|
193
|
+
if (!resp.ok) throw new Error(resp.error ?? "同意请求失败");
|
|
194
|
+
},
|
|
195
|
+
$reject: async (reason?: string) => {
|
|
196
|
+
const action = isFriend
|
|
197
|
+
? Actions.HANDLE_FRIEND_REQUEST
|
|
198
|
+
: Actions.HANDLE_GROUP_REQUEST;
|
|
199
|
+
const params: Record<string, unknown> = {
|
|
200
|
+
flag: event.flag ?? flag,
|
|
201
|
+
approve: false,
|
|
202
|
+
};
|
|
203
|
+
if (reason) params.reason = reason;
|
|
204
|
+
if (!isFriend && event.sub_type) params.sub_type = event.sub_type;
|
|
205
|
+
const resp = await ipc.request(action, params);
|
|
206
|
+
if (!resp.ok) throw new Error(resp.error ?? "拒绝请求失败");
|
|
207
|
+
},
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/** meta_event / system.*:返回是否建议在上线后刷新好友/群列表 */
|
|
212
|
+
export function shouldRefreshListsOnMeta(event: IcqqIpcRawEvent): boolean {
|
|
213
|
+
if (event.meta_event_type === "lifecycle") {
|
|
214
|
+
const sub = event.sub_type;
|
|
215
|
+
return sub === "connect" || sub === "enable";
|
|
216
|
+
}
|
|
217
|
+
const pt = event.post_type ?? "";
|
|
218
|
+
if (pt === "system.online" || pt.endsWith(".online")) return true;
|
|
219
|
+
return false;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
export function formatIcqqMetaLog(event: IcqqIpcRawEvent): string {
|
|
223
|
+
return formatCompact({
|
|
224
|
+
meta: event.meta_event_type ?? event.post_type ?? "meta",
|
|
225
|
+
sub_type: event.sub_type,
|
|
226
|
+
time: event.time,
|
|
227
|
+
});
|
|
228
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -32,6 +32,14 @@ declare module "zhin.js" {
|
|
|
32
32
|
export * from "./types.js";
|
|
33
33
|
export { IcqqBot } from "./bot.js";
|
|
34
34
|
export { IcqqAdapter } from "./adapter.js";
|
|
35
|
+
export {
|
|
36
|
+
ICQQTypingIndicatorManager,
|
|
37
|
+
createICQQTypingIndicatorManager,
|
|
38
|
+
startICQQTypingIndicator,
|
|
39
|
+
enableTypingIndicator,
|
|
40
|
+
type ICQQTypingIndicatorConfig,
|
|
41
|
+
} from "./typing-indicator.js";
|
|
42
|
+
export type { TypingIndicatorConfig } from "./types.js";
|
|
35
43
|
|
|
36
44
|
const plugin = usePlugin();
|
|
37
45
|
const { provide, useContext, addCommand, root } = plugin;
|
package/src/ipc-client.ts
CHANGED
|
@@ -38,6 +38,40 @@ function getRpcPortPath(uin: number): string {
|
|
|
38
38
|
return path.join(getIcqqHome(), String(uin), "daemon.rpc");
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
+
function asRecord(value: unknown): Record<string, unknown> | null {
|
|
42
|
+
return value && typeof value === "object"
|
|
43
|
+
? (value as Record<string, unknown>)
|
|
44
|
+
: null;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function isChatMessageEvent(
|
|
48
|
+
event: IpcEvent,
|
|
49
|
+
type: "private" | "group",
|
|
50
|
+
id: number,
|
|
51
|
+
): boolean {
|
|
52
|
+
if (!event.event.startsWith("message.")) return false;
|
|
53
|
+
const data = asRecord(event.data);
|
|
54
|
+
if (!data) return false;
|
|
55
|
+
|
|
56
|
+
const msgType = String(data.message_type ?? "");
|
|
57
|
+
if (msgType !== type) return false;
|
|
58
|
+
|
|
59
|
+
if (type === "private") {
|
|
60
|
+
const from = Number(data.from_id ?? data.user_id);
|
|
61
|
+
return Number.isFinite(from) && from === id;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const groupId = Number(data.group_id);
|
|
65
|
+
return Number.isFinite(groupId) && groupId === id;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function isGuildChannelMessageEvent(event: IpcEvent, channelId: string): boolean {
|
|
69
|
+
if (!event.event.startsWith("message.guild")) return false;
|
|
70
|
+
const data = asRecord(event.data);
|
|
71
|
+
if (!data) return false;
|
|
72
|
+
return String(data.channel_id ?? "") === channelId;
|
|
73
|
+
}
|
|
74
|
+
|
|
41
75
|
export class IpcClient {
|
|
42
76
|
private socket: net.Socket;
|
|
43
77
|
private buffer = "";
|
|
@@ -260,8 +294,15 @@ export class IpcClient {
|
|
|
260
294
|
try {
|
|
261
295
|
const msg = JSON.parse(line) as IpcMessage;
|
|
262
296
|
if ("event" in msg) {
|
|
263
|
-
const
|
|
264
|
-
|
|
297
|
+
const event = msg as IpcEvent;
|
|
298
|
+
if (event.id === "*") {
|
|
299
|
+
for (const handler of this.eventHandlers.values()) {
|
|
300
|
+
handler(event);
|
|
301
|
+
}
|
|
302
|
+
} else {
|
|
303
|
+
const handler = this.eventHandlers.get(event.id);
|
|
304
|
+
handler?.(event);
|
|
305
|
+
}
|
|
265
306
|
} else {
|
|
266
307
|
const p = this.pending.get(msg.id);
|
|
267
308
|
if (p) {
|
|
@@ -275,6 +316,18 @@ export class IpcClient {
|
|
|
275
316
|
}
|
|
276
317
|
}
|
|
277
318
|
|
|
319
|
+
/**
|
|
320
|
+
* 注册 icqq 事件回调。
|
|
321
|
+
* 新版守护进程在认证后会自动广播事件(event.id = "*")。
|
|
322
|
+
*/
|
|
323
|
+
onEvent(onEvent: (event: IpcEvent) => void): () => void {
|
|
324
|
+
const id = randomUUID();
|
|
325
|
+
this.eventHandlers.set(id, onEvent);
|
|
326
|
+
return () => {
|
|
327
|
+
this.eventHandlers.delete(id);
|
|
328
|
+
};
|
|
329
|
+
}
|
|
330
|
+
|
|
278
331
|
/**
|
|
279
332
|
* 发送 IPC 请求并等待响应。
|
|
280
333
|
*/
|
|
@@ -313,31 +366,38 @@ export class IpcClient {
|
|
|
313
366
|
}
|
|
314
367
|
|
|
315
368
|
/**
|
|
316
|
-
*
|
|
369
|
+
* @deprecated 兼容旧调用方式:服务端已自动推送事件,type/id 过滤在客户端完成。
|
|
317
370
|
* @returns 订阅句柄,包含 unsubscribe() 方法
|
|
318
371
|
*/
|
|
319
372
|
subscribe(
|
|
320
|
-
|
|
373
|
+
_action: string,
|
|
321
374
|
params: Record<string, unknown>,
|
|
322
375
|
onEvent: (event: IpcEvent) => void,
|
|
323
376
|
): { id: string; unsubscribe: () => Promise<void> } {
|
|
377
|
+
let off: () => void;
|
|
378
|
+
if (params.type === "private" || params.type === "group") {
|
|
379
|
+
const chatType: "private" | "group" = params.type;
|
|
380
|
+
const sessionId = Number(params.id);
|
|
381
|
+
off = Number.isFinite(sessionId) && sessionId > 0
|
|
382
|
+
? this.onEvent((event) => {
|
|
383
|
+
if (isChatMessageEvent(event, chatType, sessionId)) onEvent(event);
|
|
384
|
+
})
|
|
385
|
+
: this.onEvent(onEvent);
|
|
386
|
+
} else if (params.type === "guild" && params.id != null && params.id !== "") {
|
|
387
|
+
const channelId = String(params.id);
|
|
388
|
+
off = this.onEvent((event) => {
|
|
389
|
+
if (isGuildChannelMessageEvent(event, channelId)) onEvent(event);
|
|
390
|
+
});
|
|
391
|
+
} else {
|
|
392
|
+
off = this.onEvent(onEvent);
|
|
393
|
+
}
|
|
394
|
+
|
|
324
395
|
const id = randomUUID();
|
|
325
|
-
this.eventHandlers.set(id, onEvent);
|
|
326
|
-
const req: IpcRequest = { id, action, params };
|
|
327
|
-
this.socket.write(JSON.stringify(req) + "\n");
|
|
328
396
|
|
|
329
397
|
return {
|
|
330
398
|
id,
|
|
331
399
|
unsubscribe: async () => {
|
|
332
|
-
|
|
333
|
-
if (!this._closed) {
|
|
334
|
-
const unsub: IpcRequest = {
|
|
335
|
-
id: randomUUID(),
|
|
336
|
-
action: "unsubscribe",
|
|
337
|
-
params,
|
|
338
|
-
};
|
|
339
|
-
this.socket.write(JSON.stringify(unsub) + "\n");
|
|
340
|
-
}
|
|
400
|
+
off();
|
|
341
401
|
},
|
|
342
402
|
};
|
|
343
403
|
}
|
package/src/protocol.ts
CHANGED
|
@@ -29,16 +29,10 @@ export type IpcEvent = {
|
|
|
29
29
|
|
|
30
30
|
export type IpcMessage = IpcResponse | IpcEvent;
|
|
31
31
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
user_id: number;
|
|
37
|
-
nickname: string;
|
|
38
|
-
raw_message: string;
|
|
39
|
-
time: number;
|
|
40
|
-
group_id?: number;
|
|
41
|
-
}
|
|
32
|
+
export type { IcqqIpcMessageEvent } from "./icqq-inbound.js";
|
|
33
|
+
|
|
34
|
+
/** 从 IPC 入站 payload 解析可用于引用/撤回的消息 ID */
|
|
35
|
+
export { resolveIcqqInboundMessageId } from "./icqq-inbound.js";
|
|
42
36
|
|
|
43
37
|
/** Guild 消息事件数据 */
|
|
44
38
|
export interface IpcGuildMessageEventData {
|
package/src/types.ts
CHANGED
|
@@ -17,6 +17,46 @@ export interface IcqqSenderInfo {
|
|
|
17
17
|
title?: string;
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
+
/**
|
|
21
|
+
* Typing Indicator 配置
|
|
22
|
+
*/
|
|
23
|
+
export interface TypingIndicatorConfig {
|
|
24
|
+
/** 是否启用(默认 true) */
|
|
25
|
+
enabled?: boolean;
|
|
26
|
+
/** 默认表情(默认 '⏳') */
|
|
27
|
+
defaultEmoji?: string;
|
|
28
|
+
/** 是否在完成后自动移除(默认 true) */
|
|
29
|
+
autoRemove?: boolean;
|
|
30
|
+
/** 自动移除延迟,毫秒(默认 5000) */
|
|
31
|
+
removeDelay?: number;
|
|
32
|
+
/** 私聊配置 */
|
|
33
|
+
privateConfig?: {
|
|
34
|
+
/** 提示类型 */
|
|
35
|
+
type: 'message' | 'reaction';
|
|
36
|
+
/** 消息内容(type='message' 时使用) */
|
|
37
|
+
message?: string;
|
|
38
|
+
/** 表情符号(type='reaction' 时使用) */
|
|
39
|
+
emoji?: string;
|
|
40
|
+
/** 是否自动移除 */
|
|
41
|
+
autoRemove?: boolean;
|
|
42
|
+
/** 自动移除延迟 */
|
|
43
|
+
removeDelay?: number;
|
|
44
|
+
};
|
|
45
|
+
/** 群聊配置 */
|
|
46
|
+
groupConfig?: {
|
|
47
|
+
/** 提示类型 */
|
|
48
|
+
type: 'message' | 'reaction';
|
|
49
|
+
/** 消息内容(type='message' 时使用) */
|
|
50
|
+
message?: string;
|
|
51
|
+
/** 表情符号(type='reaction' 时使用) */
|
|
52
|
+
emoji?: string;
|
|
53
|
+
/** 是否自动移除 */
|
|
54
|
+
autoRemove?: boolean;
|
|
55
|
+
/** 自动移除延迟 */
|
|
56
|
+
removeDelay?: number;
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
|
|
20
60
|
/**
|
|
21
61
|
* Bot 配置:支持本地 IPC 和远程 RPC 两种连接模式。
|
|
22
62
|
*
|
|
@@ -41,6 +81,11 @@ export interface IcqqBotConfig {
|
|
|
41
81
|
* 默认 true;设为 false 则断开后仅将 `$connected` 置为 false,需手动重连。
|
|
42
82
|
*/
|
|
43
83
|
autoReconnect?: boolean;
|
|
84
|
+
/**
|
|
85
|
+
* Typing Indicator 配置
|
|
86
|
+
* 用于在 AI 处理消息时提示用户正在处理中
|
|
87
|
+
*/
|
|
88
|
+
typingIndicator?: TypingIndicatorConfig;
|
|
44
89
|
}
|
|
45
90
|
|
|
46
91
|
/** IPC 返回的好友信息 */
|