@izhimu/qq 0.5.0 → 0.6.0
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/README.md +21 -18
- package/dist/index.d.ts +5 -11
- package/dist/index.js +9 -18
- package/dist/src/adapters/message.d.ts +15 -4
- package/dist/src/adapters/message.js +179 -124
- package/dist/src/channel.d.ts +2 -7
- package/dist/src/channel.js +247 -278
- package/dist/src/core/auth.d.ts +67 -0
- package/dist/src/core/auth.js +154 -0
- package/dist/src/core/config.d.ts +7 -8
- package/dist/src/core/config.js +9 -9
- package/dist/src/core/connection.d.ts +6 -5
- package/dist/src/core/connection.js +17 -70
- package/dist/src/core/dispatch.d.ts +7 -54
- package/dist/src/core/dispatch.js +210 -398
- package/dist/src/core/event-handler.d.ts +42 -0
- package/dist/src/core/event-handler.js +171 -0
- package/dist/src/core/request.d.ts +3 -8
- package/dist/src/core/request.js +13 -126
- package/dist/src/core/runtime.d.ts +2 -11
- package/dist/src/core/runtime.js +0 -50
- package/dist/src/runtime.d.ts +3 -0
- package/dist/src/runtime.js +3 -0
- package/dist/src/setup-surface.d.ts +2 -0
- package/dist/src/setup-surface.js +59 -0
- package/dist/src/types/index.d.ts +69 -24
- package/dist/src/types/index.js +3 -4
- package/dist/src/utils/cqcode.d.ts +0 -9
- package/dist/src/utils/cqcode.js +0 -17
- package/dist/src/utils/index.d.ts +0 -17
- package/dist/src/utils/index.js +17 -154
- package/dist/src/utils/log.js +6 -3
- package/dist/src/utils/markdown.d.ts +5 -0
- package/dist/src/utils/markdown.js +57 -5
- package/openclaw.plugin.json +3 -2
- package/package.json +9 -11
- package/dist/src/onboarding.d.ts +0 -10
- package/dist/src/onboarding.js +0 -98
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* QQ Event Handler Module
|
|
3
|
+
*
|
|
4
|
+
* 统一事件处理入口,提供:
|
|
5
|
+
* - 事件上下文构建 (buildEventContext)
|
|
6
|
+
* - 统一授权检查 (checkEventAuthorization)
|
|
7
|
+
* - 事件处理器工厂 (createQQEventHandler)
|
|
8
|
+
*/
|
|
9
|
+
import type { NapCatEvent, InboundMessage } from "../types";
|
|
10
|
+
import type { QQAccount } from "../types";
|
|
11
|
+
/**
|
|
12
|
+
* 构建入站消息
|
|
13
|
+
* @param account
|
|
14
|
+
* @param event
|
|
15
|
+
*/
|
|
16
|
+
export declare function buildInboundMessage(account: QQAccount, event: NapCatEvent): Promise<InboundMessage | null>;
|
|
17
|
+
/**
|
|
18
|
+
* 检查事件是否被授权
|
|
19
|
+
*/
|
|
20
|
+
export declare function isEventAuthorized(ctx: InboundMessage): boolean;
|
|
21
|
+
/**
|
|
22
|
+
* 创建 QQ 事件处理器
|
|
23
|
+
*
|
|
24
|
+
* 这是统一的事件处理入口点,负责:
|
|
25
|
+
* 1. 构建事件上下文
|
|
26
|
+
* 2. 执行授权检查
|
|
27
|
+
* 3. 更新状态
|
|
28
|
+
* 4. 路由到具体处理器
|
|
29
|
+
*
|
|
30
|
+
* @example
|
|
31
|
+
* ```typescript
|
|
32
|
+
* const handler = createQQEventHandler({
|
|
33
|
+
* runtime,
|
|
34
|
+
* cfg: context.cfg,
|
|
35
|
+
* accountId: context.accountId,
|
|
36
|
+
* connection,
|
|
37
|
+
* });
|
|
38
|
+
*
|
|
39
|
+
* connection.on("event", handler);
|
|
40
|
+
* ```
|
|
41
|
+
*/
|
|
42
|
+
export declare function createQQEventHandler(account: QQAccount, handler: (msg: InboundMessage) => Promise<void>): (event: NapCatEvent) => Promise<void>;
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* QQ Event Handler Module
|
|
3
|
+
*
|
|
4
|
+
* 统一事件处理入口,提供:
|
|
5
|
+
* - 事件上下文构建 (buildEventContext)
|
|
6
|
+
* - 统一授权检查 (checkEventAuthorization)
|
|
7
|
+
* - 事件处理器工厂 (createQQEventHandler)
|
|
8
|
+
*/
|
|
9
|
+
import { resolveQQCommandAuthorization, getQQConfigByChatType } from "./auth.js";
|
|
10
|
+
import { inboundMessageAdapter, formatContentToText, hasMediaContent, extractMedia, } from "../adapters/message.js";
|
|
11
|
+
import { generateMessageId, Logger as log } from "../utils/index.js";
|
|
12
|
+
/**
|
|
13
|
+
* 构建入站消息
|
|
14
|
+
* @param account
|
|
15
|
+
* @param event
|
|
16
|
+
*/
|
|
17
|
+
export async function buildInboundMessage(account, event) {
|
|
18
|
+
// 处理消息事件
|
|
19
|
+
if (event.post_type === "message") {
|
|
20
|
+
return buildMessageEventContext(event, account);
|
|
21
|
+
}
|
|
22
|
+
// 处理通知事件
|
|
23
|
+
if (event.post_type === "notice") {
|
|
24
|
+
const noticeEvent = event;
|
|
25
|
+
const isPokeEvent = noticeEvent.notice_type === "poke" ||
|
|
26
|
+
(noticeEvent.notice_type === "notify" && noticeEvent.sub_type === "poke");
|
|
27
|
+
if (isPokeEvent) {
|
|
28
|
+
return buildPokeEventContext(noticeEvent, account);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
// 不支持的事件类型
|
|
32
|
+
log.debug("event-handler", `Unhandled event type: ${event.post_type}`);
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* 构建消息事件上下文
|
|
37
|
+
*/
|
|
38
|
+
async function buildMessageEventContext(event, account) {
|
|
39
|
+
// 过滤空消息
|
|
40
|
+
if (!event.raw_message || event.raw_message.trim() === "") {
|
|
41
|
+
log.debug("event-handler", "Ignored empty message");
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
const isGroup = event.message_type === "group";
|
|
45
|
+
const senderId = event.user_id.toString();
|
|
46
|
+
const groupId = event.group_id?.toString();
|
|
47
|
+
// 获取配置并进行授权检查
|
|
48
|
+
const qqConfig = getQQConfigByChatType(isGroup, groupId, account);
|
|
49
|
+
const authorization = resolveQQCommandAuthorization({
|
|
50
|
+
senderId,
|
|
51
|
+
qqConfig,
|
|
52
|
+
});
|
|
53
|
+
// 解析消息内容
|
|
54
|
+
const content = await inboundMessageAdapter(event.message);
|
|
55
|
+
const plainText = formatContentToText(content);
|
|
56
|
+
const media = hasMediaContent(content) ? extractMedia(content) : undefined;
|
|
57
|
+
return {
|
|
58
|
+
targetId: account.accountId,
|
|
59
|
+
messageId: event.message_id?.toString() ?? generateMessageId(),
|
|
60
|
+
senderId,
|
|
61
|
+
senderName: event.sender?.nickname || event.sender?.card,
|
|
62
|
+
text: plainText,
|
|
63
|
+
timestamp: event.time * 1000,
|
|
64
|
+
isGroup,
|
|
65
|
+
groupId,
|
|
66
|
+
hasMedia: !!media,
|
|
67
|
+
media,
|
|
68
|
+
authorization: {
|
|
69
|
+
isAuthorizedSender: authorization.isAuthorizedSender,
|
|
70
|
+
denialReason: authorization.denialReason,
|
|
71
|
+
},
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* 构建戳一戳事件上下文
|
|
76
|
+
*/
|
|
77
|
+
function buildPokeEventContext(event, account) {
|
|
78
|
+
const isGroup = !!event.group_id;
|
|
79
|
+
const senderId = event.user_id.toString();
|
|
80
|
+
const groupId = event.group_id?.toString();
|
|
81
|
+
// 获取配置并进行授权检查
|
|
82
|
+
const qqConfig = getQQConfigByChatType(isGroup, groupId, account);
|
|
83
|
+
const authorization = resolveQQCommandAuthorization({
|
|
84
|
+
senderId,
|
|
85
|
+
qqConfig,
|
|
86
|
+
});
|
|
87
|
+
// 提取戳一戳动作文本
|
|
88
|
+
const actionText = extractPokeActionText(event.raw_info);
|
|
89
|
+
const pokeContent = `[动作]${actionText || "戳了戳"}`;
|
|
90
|
+
return {
|
|
91
|
+
targetId: event.target_id.toString(),
|
|
92
|
+
messageId: generateMessageId(),
|
|
93
|
+
senderId,
|
|
94
|
+
senderName: senderId,
|
|
95
|
+
text: pokeContent,
|
|
96
|
+
timestamp: event.time * 1000,
|
|
97
|
+
isGroup,
|
|
98
|
+
groupId,
|
|
99
|
+
hasMedia: false,
|
|
100
|
+
authorization: {
|
|
101
|
+
isAuthorizedSender: authorization.isAuthorizedSender,
|
|
102
|
+
denialReason: authorization.denialReason,
|
|
103
|
+
},
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* 提取戳一戳动作文本
|
|
108
|
+
*/
|
|
109
|
+
function extractPokeActionText(rawInfo) {
|
|
110
|
+
if (!rawInfo)
|
|
111
|
+
return "戳了戳";
|
|
112
|
+
const actionItem = rawInfo.find((item) => item.type === "nor" && item.txt);
|
|
113
|
+
return actionItem?.txt || "戳了戳";
|
|
114
|
+
}
|
|
115
|
+
// =============================================================================
|
|
116
|
+
// Authorization Check
|
|
117
|
+
// =============================================================================
|
|
118
|
+
/**
|
|
119
|
+
* 检查事件是否被授权
|
|
120
|
+
*/
|
|
121
|
+
export function isEventAuthorized(ctx) {
|
|
122
|
+
if (!ctx.authorization) {
|
|
123
|
+
return false;
|
|
124
|
+
}
|
|
125
|
+
if (!ctx.authorization.isAuthorizedSender) {
|
|
126
|
+
log.info("event-handler", `Authorization denied for ${ctx.senderId}: ${ctx.authorization.denialReason}`);
|
|
127
|
+
return false;
|
|
128
|
+
}
|
|
129
|
+
return true;
|
|
130
|
+
}
|
|
131
|
+
// =============================================================================
|
|
132
|
+
// Event Handler Factory
|
|
133
|
+
// =============================================================================
|
|
134
|
+
/**
|
|
135
|
+
* 创建 QQ 事件处理器
|
|
136
|
+
*
|
|
137
|
+
* 这是统一的事件处理入口点,负责:
|
|
138
|
+
* 1. 构建事件上下文
|
|
139
|
+
* 2. 执行授权检查
|
|
140
|
+
* 3. 更新状态
|
|
141
|
+
* 4. 路由到具体处理器
|
|
142
|
+
*
|
|
143
|
+
* @example
|
|
144
|
+
* ```typescript
|
|
145
|
+
* const handler = createQQEventHandler({
|
|
146
|
+
* runtime,
|
|
147
|
+
* cfg: context.cfg,
|
|
148
|
+
* accountId: context.accountId,
|
|
149
|
+
* connection,
|
|
150
|
+
* });
|
|
151
|
+
*
|
|
152
|
+
* connection.on("event", handler);
|
|
153
|
+
* ```
|
|
154
|
+
*/
|
|
155
|
+
export function createQQEventHandler(account, handler) {
|
|
156
|
+
return async (event) => {
|
|
157
|
+
log.debug("event-handler", `Received event: ${event.post_type}`);
|
|
158
|
+
// 1. 构建事件上下文
|
|
159
|
+
const msg = await buildInboundMessage(account, event);
|
|
160
|
+
if (!msg) {
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
// 2. 授权检查
|
|
164
|
+
if (!isEventAuthorized(msg)) {
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
// 3. 路由到具体处理器 - 调用 dispatchMessage
|
|
168
|
+
await handler(msg);
|
|
169
|
+
return;
|
|
170
|
+
};
|
|
171
|
+
}
|
|
@@ -1,27 +1,22 @@
|
|
|
1
|
-
import type { GetFileReq, GetFileResp, GetMsgReq, GetMsgResp, GetStatusResp, GetLoginInfoResp, NapCatResp, SendMsgReq, SendMsgResp, SetInputStatusReq, GetFriendListResp, GetGroupListResp } from "../types";
|
|
1
|
+
import type { GetFileReq, GetFileResp, GetMsgReq, GetMsgResp, GetStatusResp, GetLoginInfoResp, NapCatResp, SendMsgReq, SendMsgResp, SetInputStatusReq, GetFriendListResp, GetGroupListResp, NapCatEvent, QQAccount, InboundMessage } from "../types";
|
|
2
2
|
/**
|
|
3
3
|
* 事件监听
|
|
4
|
-
* @param event
|
|
5
4
|
*/
|
|
6
|
-
export declare function eventListener(event:
|
|
5
|
+
export declare function eventListener(account: QQAccount, event: NapCatEvent, handler: (msg: InboundMessage) => Promise<void>): Promise<void>;
|
|
7
6
|
/**
|
|
8
|
-
*
|
|
9
|
-
* @param params
|
|
7
|
+
* 发送消息
|
|
10
8
|
*/
|
|
11
9
|
export declare function sendMsg(params: SendMsgReq): Promise<NapCatResp<SendMsgResp>>;
|
|
12
10
|
/**
|
|
13
11
|
* 获取消息
|
|
14
|
-
* @param params
|
|
15
12
|
*/
|
|
16
13
|
export declare function getMsg(params: GetMsgReq): Promise<NapCatResp<GetMsgResp>>;
|
|
17
14
|
/**
|
|
18
15
|
* 获取文件
|
|
19
|
-
* @param params
|
|
20
16
|
*/
|
|
21
17
|
export declare function getFile(params: GetFileReq): Promise<NapCatResp<GetFileResp>>;
|
|
22
18
|
/**
|
|
23
19
|
* 设置输入状态
|
|
24
|
-
* @param params
|
|
25
20
|
*/
|
|
26
21
|
export declare function setInputStatus(params: SetInputStatusReq): Promise<NapCatResp<void>>;
|
|
27
22
|
/**
|
package/dist/src/core/request.js
CHANGED
|
@@ -1,171 +1,58 @@
|
|
|
1
|
-
import pLimit from 'p-limit';
|
|
2
1
|
import { Logger as log } from "../utils/index.js";
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import { failResp } from "./connection.js";
|
|
6
|
-
/**
|
|
7
|
-
* Rate limiter for sendMsg requests
|
|
8
|
-
* Limits concurrent messages to prevent API throttling
|
|
9
|
-
*/
|
|
10
|
-
const sendMsgLimiter = pLimit(1);
|
|
2
|
+
import { createQQEventHandler } from "./event-handler.js";
|
|
3
|
+
import { sendRequest } from "./connection.js";
|
|
11
4
|
/**
|
|
12
5
|
* 事件监听
|
|
13
|
-
* @param event
|
|
14
6
|
*/
|
|
15
|
-
export async function eventListener(event) {
|
|
7
|
+
export async function eventListener(account, event, handler) {
|
|
16
8
|
log.debug("request", `Received event: ${event.post_type}`);
|
|
17
|
-
|
|
18
|
-
if (!context) {
|
|
19
|
-
log.warn("request", `No gateway context`);
|
|
20
|
-
return;
|
|
21
|
-
}
|
|
22
|
-
const connection = getConnection();
|
|
23
|
-
if (!connection) {
|
|
24
|
-
log.warn("request", `No connection available`);
|
|
25
|
-
return;
|
|
26
|
-
}
|
|
27
|
-
switch (event.post_type) {
|
|
28
|
-
case "message":
|
|
29
|
-
// 过滤空消息
|
|
30
|
-
if (!event.raw_message || event.raw_message === '') {
|
|
31
|
-
log.debug("request", `Ignored empty message`);
|
|
32
|
-
break;
|
|
33
|
-
}
|
|
34
|
-
setContextStatus({
|
|
35
|
-
lastInboundAt: Date.now(),
|
|
36
|
-
});
|
|
37
|
-
if (event.message_type === "group" && event.group_id) {
|
|
38
|
-
await handleGroupMessage({
|
|
39
|
-
time: event.time,
|
|
40
|
-
self_id: event.self_id,
|
|
41
|
-
message_id: event.message_id ?? 0,
|
|
42
|
-
group_id: event.group_id,
|
|
43
|
-
user_id: event.user_id,
|
|
44
|
-
message: event.message ?? [],
|
|
45
|
-
raw_message: event.raw_message ?? '',
|
|
46
|
-
sender: event.sender,
|
|
47
|
-
});
|
|
48
|
-
}
|
|
49
|
-
else if (event.message_type === "private") {
|
|
50
|
-
await handlePrivateMessage({
|
|
51
|
-
time: event.time,
|
|
52
|
-
self_id: event.self_id,
|
|
53
|
-
message_id: event.message_id ?? 0,
|
|
54
|
-
user_id: event.user_id,
|
|
55
|
-
message: event.message ?? [],
|
|
56
|
-
raw_message: event.raw_message ?? '',
|
|
57
|
-
sender: event.sender,
|
|
58
|
-
});
|
|
59
|
-
}
|
|
60
|
-
break;
|
|
61
|
-
case "notice":
|
|
62
|
-
if (event.target_id) {
|
|
63
|
-
const isPokeEvent = event.notice_type === "poke" ||
|
|
64
|
-
(event.notice_type === "notify" && event.sub_type === "poke");
|
|
65
|
-
if (isPokeEvent) {
|
|
66
|
-
await handlePokeEvent({
|
|
67
|
-
user_id: event.user_id,
|
|
68
|
-
target_id: event.target_id,
|
|
69
|
-
group_id: event.group_id,
|
|
70
|
-
raw_info: event.raw_info,
|
|
71
|
-
});
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
break;
|
|
75
|
-
default:
|
|
76
|
-
log.debug("request", `Unhandled event type: ${event.post_type}`);
|
|
77
|
-
}
|
|
9
|
+
await createQQEventHandler(account, handler)(event);
|
|
78
10
|
}
|
|
79
11
|
/**
|
|
80
|
-
*
|
|
81
|
-
* @param params
|
|
12
|
+
* 发送消息
|
|
82
13
|
*/
|
|
83
14
|
export async function sendMsg(params) {
|
|
84
|
-
|
|
85
|
-
if (!connection) {
|
|
86
|
-
log.warn("request", `No connection available`);
|
|
87
|
-
return failResp();
|
|
88
|
-
}
|
|
89
|
-
// 使用限流器控制并发,避免触发 NapCat API 限流
|
|
90
|
-
return sendMsgLimiter(() => connection.sendRequest("send_msg", params));
|
|
15
|
+
return sendRequest("send_msg", params);
|
|
91
16
|
}
|
|
92
17
|
/**
|
|
93
18
|
* 获取消息
|
|
94
|
-
* @param params
|
|
95
19
|
*/
|
|
96
20
|
export async function getMsg(params) {
|
|
97
|
-
|
|
98
|
-
if (!connection) {
|
|
99
|
-
log.warn("request", `No connection available`);
|
|
100
|
-
return failResp();
|
|
101
|
-
}
|
|
102
|
-
return connection.sendRequest("get_msg", params);
|
|
21
|
+
return sendRequest("get_msg", params);
|
|
103
22
|
}
|
|
104
23
|
/**
|
|
105
24
|
* 获取文件
|
|
106
|
-
* @param params
|
|
107
25
|
*/
|
|
108
26
|
export async function getFile(params) {
|
|
109
|
-
|
|
110
|
-
if (!connection) {
|
|
111
|
-
log.warn("request", `No connection available`);
|
|
112
|
-
return failResp();
|
|
113
|
-
}
|
|
114
|
-
return connection.sendRequest("get_file", params);
|
|
27
|
+
return sendRequest("get_file", params);
|
|
115
28
|
}
|
|
116
29
|
/**
|
|
117
30
|
* 设置输入状态
|
|
118
|
-
* @param params
|
|
119
31
|
*/
|
|
120
32
|
export async function setInputStatus(params) {
|
|
121
|
-
|
|
122
|
-
if (!connection) {
|
|
123
|
-
log.warn("request", `No connection available`);
|
|
124
|
-
return failResp();
|
|
125
|
-
}
|
|
126
|
-
return connection.sendRequest("set_input_status", params);
|
|
33
|
+
return sendRequest("set_input_status", params);
|
|
127
34
|
}
|
|
128
35
|
/**
|
|
129
36
|
* 获取状态
|
|
130
37
|
*/
|
|
131
38
|
export async function getStatus() {
|
|
132
|
-
|
|
133
|
-
if (!connection) {
|
|
134
|
-
log.warn("request", `No connection available`);
|
|
135
|
-
return failResp();
|
|
136
|
-
}
|
|
137
|
-
return connection.sendRequest("get_status");
|
|
39
|
+
return sendRequest("get_status");
|
|
138
40
|
}
|
|
139
41
|
/**
|
|
140
42
|
* 获取登录信息
|
|
141
43
|
*/
|
|
142
44
|
export async function getLoginInfo() {
|
|
143
|
-
|
|
144
|
-
if (!connection) {
|
|
145
|
-
log.warn("request", `No connection available`);
|
|
146
|
-
return failResp();
|
|
147
|
-
}
|
|
148
|
-
return connection.sendRequest("get_login_info");
|
|
45
|
+
return sendRequest("get_login_info");
|
|
149
46
|
}
|
|
150
47
|
/**
|
|
151
48
|
* 获取好友列表
|
|
152
49
|
*/
|
|
153
50
|
export async function getFriendList() {
|
|
154
|
-
|
|
155
|
-
if (!connection) {
|
|
156
|
-
log.warn("request", `No connection available`);
|
|
157
|
-
return failResp();
|
|
158
|
-
}
|
|
159
|
-
return connection.sendRequest("get_friend_list");
|
|
51
|
+
return sendRequest("get_friend_list");
|
|
160
52
|
}
|
|
161
53
|
/**
|
|
162
54
|
* 获取群列表
|
|
163
55
|
*/
|
|
164
56
|
export async function getGroupList() {
|
|
165
|
-
|
|
166
|
-
if (!connection) {
|
|
167
|
-
log.warn("request", `No connection available`);
|
|
168
|
-
return failResp();
|
|
169
|
-
}
|
|
170
|
-
return connection.sendRequest("get_group_list");
|
|
57
|
+
return sendRequest("get_group_list");
|
|
171
58
|
}
|
|
@@ -2,21 +2,12 @@
|
|
|
2
2
|
* Plugin Runtime Storage
|
|
3
3
|
* Stores the PluginRuntime for access in gateway handlers
|
|
4
4
|
*/
|
|
5
|
-
import type {
|
|
6
|
-
import {
|
|
5
|
+
import type { HistoryEntry } from "openclaw/plugin-sdk/reply-history";
|
|
6
|
+
import { QQLoginInfo } from "../types";
|
|
7
7
|
import { ConnectionManager } from "./connection.js";
|
|
8
|
-
export declare function setRuntime(next: PluginRuntime): void;
|
|
9
|
-
export declare function getRuntime(): PluginRuntime | null;
|
|
10
|
-
export declare function setContext(next: ChannelGatewayContext<QQConfig>): void;
|
|
11
|
-
export declare function getContext(): ChannelGatewayContext<QQConfig> | null;
|
|
12
|
-
export declare function clearContext(): void;
|
|
13
|
-
export declare function setContextStatus(next: Omit<ChannelAccountSnapshot, 'accountId'>): void;
|
|
14
8
|
export declare function setConnection(next: ConnectionManager): void;
|
|
15
9
|
export declare function getConnection(): ConnectionManager | null;
|
|
16
10
|
export declare function clearConnection(): void;
|
|
17
|
-
export declare function getSession(sessionKey: string): QQSession;
|
|
18
|
-
export declare function updateSession(sessionKey: string, session: QQSession): void;
|
|
19
|
-
export declare function clearSession(sessionKey: string): void;
|
|
20
11
|
export declare function setLoginInfo(next: QQLoginInfo): void;
|
|
21
12
|
export declare function getLoginInfo(): QQLoginInfo;
|
|
22
13
|
export declare const historyCache: Map<string, HistoryEntry[]>;
|
package/dist/src/core/runtime.js
CHANGED
|
@@ -3,37 +3,6 @@
|
|
|
3
3
|
* Stores the PluginRuntime for access in gateway handlers
|
|
4
4
|
*/
|
|
5
5
|
// =============================================================================
|
|
6
|
-
// Runtime
|
|
7
|
-
// =============================================================================
|
|
8
|
-
let runtime = null;
|
|
9
|
-
export function setRuntime(next) {
|
|
10
|
-
runtime = next;
|
|
11
|
-
}
|
|
12
|
-
export function getRuntime() {
|
|
13
|
-
return runtime;
|
|
14
|
-
}
|
|
15
|
-
// =============================================================================
|
|
16
|
-
// Context
|
|
17
|
-
// =============================================================================
|
|
18
|
-
let context = null;
|
|
19
|
-
export function setContext(next) {
|
|
20
|
-
context = next;
|
|
21
|
-
}
|
|
22
|
-
export function getContext() {
|
|
23
|
-
return context;
|
|
24
|
-
}
|
|
25
|
-
export function clearContext() {
|
|
26
|
-
context = null;
|
|
27
|
-
}
|
|
28
|
-
export function setContextStatus(next) {
|
|
29
|
-
if (context) {
|
|
30
|
-
context.setStatus({
|
|
31
|
-
...context.getStatus(),
|
|
32
|
-
...next,
|
|
33
|
-
});
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
// =============================================================================
|
|
37
6
|
// Connection
|
|
38
7
|
// =============================================================================
|
|
39
8
|
let connection = null;
|
|
@@ -47,25 +16,6 @@ export function clearConnection() {
|
|
|
47
16
|
connection = null;
|
|
48
17
|
}
|
|
49
18
|
// =============================================================================
|
|
50
|
-
// Session
|
|
51
|
-
// =============================================================================
|
|
52
|
-
const sessionMap = new Map();
|
|
53
|
-
export function getSession(sessionKey) {
|
|
54
|
-
let session = sessionMap.get(sessionKey);
|
|
55
|
-
if (session) {
|
|
56
|
-
return session;
|
|
57
|
-
}
|
|
58
|
-
session = {};
|
|
59
|
-
sessionMap.set(sessionKey, session);
|
|
60
|
-
return session;
|
|
61
|
-
}
|
|
62
|
-
export function updateSession(sessionKey, session) {
|
|
63
|
-
sessionMap.set(sessionKey, session);
|
|
64
|
-
}
|
|
65
|
-
export function clearSession(sessionKey) {
|
|
66
|
-
sessionMap.delete(sessionKey);
|
|
67
|
-
}
|
|
68
|
-
// =============================================================================
|
|
69
19
|
// LoginInfo
|
|
70
20
|
// =============================================================================
|
|
71
21
|
const loginInfo = {
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { createStandardChannelSetupStatus, applySetupAccountConfigPatch } from "openclaw/plugin-sdk/setup";
|
|
2
|
+
import { QQ_CHANNEL, listQQAccountIds, resolveQQAccount } from "./core/config";
|
|
3
|
+
export const qqSetupWizard = {
|
|
4
|
+
channel: QQ_CHANNEL,
|
|
5
|
+
status: createStandardChannelSetupStatus({
|
|
6
|
+
channelLabel: "QQ Chat",
|
|
7
|
+
configuredLabel: "configured",
|
|
8
|
+
unconfiguredLabel: "needs service account",
|
|
9
|
+
configuredHint: "configured",
|
|
10
|
+
unconfiguredHint: "needs auth",
|
|
11
|
+
includeStatusLine: true,
|
|
12
|
+
resolveConfigured: ({ cfg }) => listQQAccountIds(cfg).some((accountId) => resolveQQAccount({ cfg, accountId }).token !== "none"),
|
|
13
|
+
}),
|
|
14
|
+
introNote: {
|
|
15
|
+
title: "QQ Chat setup",
|
|
16
|
+
lines: [
|
|
17
|
+
"1) 确保已安装 NapCat: https://github.com/NapNeko/NapCatQQ",
|
|
18
|
+
"2) 在 NapCat 配置中启用 WebSocket (正向 WS)",
|
|
19
|
+
"3) 默认地址: ws://localhost:3001",
|
|
20
|
+
"4) 如需访问控制,可设置 token",
|
|
21
|
+
"",
|
|
22
|
+
"NapCat 文档: https://napneko.github.io/",
|
|
23
|
+
]
|
|
24
|
+
},
|
|
25
|
+
credentials: [],
|
|
26
|
+
textInputs: [
|
|
27
|
+
{
|
|
28
|
+
inputKey: "url",
|
|
29
|
+
message: "请输入 NapCat WebSocket URL",
|
|
30
|
+
placeholder: "ws://localhost:3001",
|
|
31
|
+
shouldPrompt: ({ currentValue }) => !currentValue,
|
|
32
|
+
validate: ({ value }) => (value ? undefined : "Required"),
|
|
33
|
+
normalizeValue: ({ value }) => String(value).trim(),
|
|
34
|
+
applySet: async ({ cfg, accountId, value }) => applySetupAccountConfigPatch({
|
|
35
|
+
cfg,
|
|
36
|
+
channelKey: QQ_CHANNEL,
|
|
37
|
+
accountId,
|
|
38
|
+
patch: {
|
|
39
|
+
wsUrl: value,
|
|
40
|
+
}
|
|
41
|
+
}),
|
|
42
|
+
}, {
|
|
43
|
+
inputKey: "token",
|
|
44
|
+
message: "请输入 Access Token",
|
|
45
|
+
placeholder: "ws://localhost:3001",
|
|
46
|
+
shouldPrompt: ({ currentValue }) => !currentValue,
|
|
47
|
+
validate: ({ value }) => (value ? undefined : "Required"),
|
|
48
|
+
normalizeValue: ({ value }) => String(value).trim(),
|
|
49
|
+
applySet: async ({ cfg, accountId, value }) => applySetupAccountConfigPatch({
|
|
50
|
+
cfg,
|
|
51
|
+
channelKey: QQ_CHANNEL,
|
|
52
|
+
accountId,
|
|
53
|
+
patch: {
|
|
54
|
+
wsUrl: value,
|
|
55
|
+
}
|
|
56
|
+
}),
|
|
57
|
+
}
|
|
58
|
+
]
|
|
59
|
+
};
|