koishi-plugin-smmcat-gptimage2 0.1.21 → 0.1.23
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/chatSend.d.ts +55 -5
- package/lib/index.js +212 -50
- package/lib/keyboards.d.ts +46 -0
- package/package.json +1 -1
package/lib/chatSend.d.ts
CHANGED
|
@@ -1,17 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 内容发送模块(Markdown 优先 + 纯文本降级)
|
|
3
|
+
*
|
|
4
|
+
* 功能定位:
|
|
5
|
+
* - 统一封装向 QQ 平台发送消息:优先走 QQ 官方 Markdown 接口(useMd=true),
|
|
6
|
+
* 失败或非 QQ 平台时降级为纯文本 fallbackText。
|
|
7
|
+
*
|
|
8
|
+
* 核心算法(msg_seq 递增):
|
|
9
|
+
* - QQ 平台限制同一 msg_id 的被动回复次数;本模块以 msg_id 为键维护计数,
|
|
10
|
+
* 连续发送时递增 msg_seq;超过 5 次后尝试切换到 middleware 捕获的最新
|
|
11
|
+
* messageId,仍不可用则直接降级发纯文本。
|
|
12
|
+
*
|
|
13
|
+
* 注意事项:
|
|
14
|
+
* - 私聊场景会先移除 <qqbot-at-user> 标签(私聊不支持 @)。
|
|
15
|
+
* - chache/messageIdTemp 为有界内存缓存(分别 40/150 条,FIFO 淘汰)。
|
|
16
|
+
* - 调用方应同时提供 Markdown 内容与等价纯文本,保证降级后信息不丢失。
|
|
17
|
+
*/
|
|
1
18
|
import { Context, Session } from "koishi";
|
|
2
19
|
import { Config } from ".";
|
|
20
|
+
import { QQKeyboard } from "./keyboards";
|
|
21
|
+
export interface ChatSendResult {
|
|
22
|
+
sent: true;
|
|
23
|
+
mode: 'markdown' | 'text';
|
|
24
|
+
}
|
|
25
|
+
type PassiveSource = {
|
|
26
|
+
type: 'msg_id';
|
|
27
|
+
id: string;
|
|
28
|
+
payload: {
|
|
29
|
+
msg_id: string;
|
|
30
|
+
};
|
|
31
|
+
} | {
|
|
32
|
+
type: 'event_id';
|
|
33
|
+
id: string;
|
|
34
|
+
payload: {
|
|
35
|
+
event_id: string;
|
|
36
|
+
};
|
|
37
|
+
};
|
|
3
38
|
/** 内容发送模块 */
|
|
4
39
|
export declare const Chat: {
|
|
5
40
|
config: Config;
|
|
6
41
|
ctx: Context;
|
|
7
|
-
chache:
|
|
8
|
-
messageIdTemp:
|
|
42
|
+
chache: Record<string, number>;
|
|
43
|
+
messageIdTemp: Record<string, string>;
|
|
44
|
+
buttonEventIdTemp: Record<string, string>;
|
|
9
45
|
init(config: Config, ctx: Context): Promise<void>;
|
|
10
|
-
/**
|
|
11
|
-
|
|
46
|
+
/**
|
|
47
|
+
* 发送消息(Markdown 优先,失败自动降级)。
|
|
48
|
+
*/
|
|
49
|
+
send(session: Session, content: string, fallbackText?: string, keyboard?: QQKeyboard): Promise<ChatSendResult>;
|
|
12
50
|
isUseMd(session: Session): boolean;
|
|
13
|
-
/**
|
|
51
|
+
/** 优先取当前 session 的 msg_id;如果是按钮事件,则取 event_id */
|
|
52
|
+
getCurrentPassiveSource(session: Session): PassiveSource | null;
|
|
53
|
+
/**
|
|
54
|
+
* 超过被动 MD 上限时:
|
|
55
|
+
* 1. 优先找 message 事件中缓存的新 msg_id
|
|
56
|
+
* 2. 没有新 msg_id,再找按钮事件缓存的 event_id
|
|
57
|
+
*/
|
|
58
|
+
getNextPassiveSource(userId: string, current: PassiveSource): PassiveSource | null;
|
|
59
|
+
getCacheKey(source: PassiveSource): string;
|
|
60
|
+
/** 清除一条 msg_id/event_id 计数记录 */
|
|
14
61
|
clearChache(): void;
|
|
15
62
|
/** 清除一条 messageId 缓存 */
|
|
16
63
|
clearMessageIdTemp(): void;
|
|
64
|
+
/** 清除一条按钮 event_id 缓存 */
|
|
65
|
+
clearButtonEventIdTemp(): void;
|
|
17
66
|
};
|
|
67
|
+
export {};
|
package/lib/index.js
CHANGED
|
@@ -47,70 +47,169 @@ var Chat = {
|
|
|
47
47
|
config: {},
|
|
48
48
|
ctx: {},
|
|
49
49
|
chache: {},
|
|
50
|
+
// message 事件缓存:userId -> msg_id
|
|
50
51
|
messageIdTemp: {},
|
|
52
|
+
// 按钮事件缓存:userId -> event_id
|
|
53
|
+
buttonEventIdTemp: {},
|
|
51
54
|
async init(config, ctx) {
|
|
52
55
|
Chat.config = config;
|
|
53
56
|
Chat.ctx = ctx;
|
|
57
|
+
Chat.config.deBug && console.log(Chat.chache);
|
|
54
58
|
ctx.on("message", (session) => {
|
|
55
|
-
if (!
|
|
56
|
-
Chat.messageIdTemp[session.userId] = {};
|
|
57
|
-
}
|
|
59
|
+
if (!session.userId || !session.messageId) return;
|
|
58
60
|
Chat.messageIdTemp[session.userId] = session.messageId;
|
|
59
61
|
Chat.clearMessageIdTemp();
|
|
60
62
|
}, true);
|
|
63
|
+
ctx.on("interaction/button", (session) => {
|
|
64
|
+
const eventId = session.event?._data?.id;
|
|
65
|
+
if (!session.userId || !eventId) {
|
|
66
|
+
Chat.config.deBug && console.log("[Chat] button interaction has no event_id:", session.event?._data);
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
Chat.buttonEventIdTemp[session.userId] = eventId;
|
|
70
|
+
Chat.clearButtonEventIdTemp();
|
|
71
|
+
Chat.config.deBug && console.log("[Chat] cached button event_id:", eventId);
|
|
72
|
+
});
|
|
61
73
|
},
|
|
62
|
-
/**
|
|
63
|
-
|
|
74
|
+
/**
|
|
75
|
+
* 发送消息(Markdown 优先,失败自动降级)。
|
|
76
|
+
*/
|
|
77
|
+
async send(session, content, fallbackText = content, keyboard2) {
|
|
64
78
|
if (session.platform !== "qq") {
|
|
65
|
-
await session.send(
|
|
66
|
-
return;
|
|
79
|
+
await session.send(fallbackText);
|
|
80
|
+
return { sent: true, mode: "text" };
|
|
67
81
|
}
|
|
68
|
-
if (Chat.config.useMd) {
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
82
|
+
if (!Chat.config.useMd) {
|
|
83
|
+
await session.send(fallbackText);
|
|
84
|
+
return { sent: true, mode: "text" };
|
|
85
|
+
}
|
|
86
|
+
const userId = session.userId;
|
|
87
|
+
let source = Chat.getCurrentPassiveSource(session);
|
|
88
|
+
if (!source) {
|
|
89
|
+
Chat.config.deBug && console.log("[Chat.send] no msg_id/event_id, fallback to text");
|
|
90
|
+
await session.send(fallbackText);
|
|
91
|
+
return { sent: true, mode: "text" };
|
|
92
|
+
}
|
|
93
|
+
const cacheKey = Chat.getCacheKey(source);
|
|
94
|
+
if (Chat.chache[cacheKey] == void 0) {
|
|
95
|
+
Chat.chache[cacheKey] = 0;
|
|
96
|
+
}
|
|
97
|
+
let seq = ++Chat.chache[cacheKey];
|
|
98
|
+
let msgSeq = seq === 1 ? 0 : seq;
|
|
99
|
+
if (seq > 1) {
|
|
100
|
+
Chat.config.deBug && console.log(`检测到 MD 连续发送,尝试递增 msg_seq;当前第 ${seq} 次。`);
|
|
101
|
+
}
|
|
102
|
+
if (seq > 5) {
|
|
103
|
+
const nextSource = Chat.getNextPassiveSource(userId, source);
|
|
104
|
+
if (nextSource) {
|
|
105
|
+
source = nextSource;
|
|
106
|
+
const nextCacheKey = Chat.getCacheKey(source);
|
|
107
|
+
if (Chat.chache[nextCacheKey] == void 0) {
|
|
108
|
+
Chat.chache[nextCacheKey] = 0;
|
|
90
109
|
}
|
|
110
|
+
const nextSeq = ++Chat.chache[nextCacheKey];
|
|
111
|
+
msgSeq = nextSeq === 1 ? 0 : nextSeq;
|
|
112
|
+
Chat.config.deBug && console.log(`[Chat.send] 被动 MD 超过上限,切换到 ${source.type}: ${source.id}`);
|
|
113
|
+
} else {
|
|
114
|
+
await session.send(fallbackText);
|
|
115
|
+
Chat.clearChache();
|
|
116
|
+
return { sent: true, mode: "text" };
|
|
91
117
|
}
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
}
|
|
100
|
-
|
|
118
|
+
}
|
|
119
|
+
const mdContent = {
|
|
120
|
+
...source.payload,
|
|
121
|
+
msg_type: 2,
|
|
122
|
+
msg_seq: msgSeq,
|
|
123
|
+
markdown: {
|
|
124
|
+
content
|
|
125
|
+
},
|
|
126
|
+
...keyboard2 ? { keyboard: keyboard2 } : {}
|
|
127
|
+
};
|
|
128
|
+
try {
|
|
129
|
+
if (session.guildId) {
|
|
130
|
+
await session.bot.internal.sendMessage(session.channelId, mdContent);
|
|
131
|
+
} else {
|
|
132
|
+
mdContent.markdown.content = mdContent.markdown.content.replace(/<qqbot-at-user[^>]*\/>/g, "");
|
|
133
|
+
await session.bot.internal.sendPrivateMessage(session.userId, mdContent);
|
|
101
134
|
}
|
|
102
135
|
Chat.clearChache();
|
|
103
|
-
|
|
104
|
-
|
|
136
|
+
return { sent: true, mode: "markdown" };
|
|
137
|
+
} catch (error) {
|
|
138
|
+
Chat.config.deBug && console.log("[Chat.send] Markdown 发送失败,回退普通文本", error);
|
|
139
|
+
try {
|
|
140
|
+
await session.send(fallbackText);
|
|
141
|
+
Chat.clearChache();
|
|
142
|
+
return { sent: true, mode: "text" };
|
|
143
|
+
} catch {
|
|
144
|
+
Chat.clearChache();
|
|
145
|
+
throw new Error("消息发送失败");
|
|
146
|
+
}
|
|
105
147
|
}
|
|
106
148
|
},
|
|
107
|
-
// 当前 session 是否可以使用MD
|
|
149
|
+
// 当前 session 是否可以使用 MD
|
|
108
150
|
isUseMd(session) {
|
|
109
151
|
if (session.platform !== "qq") return false;
|
|
110
|
-
|
|
111
|
-
|
|
152
|
+
const source = Chat.getCurrentPassiveSource(session);
|
|
153
|
+
if (!source) return false;
|
|
154
|
+
const cacheKey = Chat.getCacheKey(source);
|
|
155
|
+
if (Chat.chache[cacheKey] == void 0) return true;
|
|
156
|
+
return Chat.chache[cacheKey] <= 5;
|
|
112
157
|
},
|
|
113
|
-
/**
|
|
158
|
+
/** 优先取当前 session 的 msg_id;如果是按钮事件,则取 event_id */
|
|
159
|
+
getCurrentPassiveSource(session) {
|
|
160
|
+
if (session.messageId) {
|
|
161
|
+
return {
|
|
162
|
+
type: "msg_id",
|
|
163
|
+
id: session.messageId,
|
|
164
|
+
payload: {
|
|
165
|
+
msg_id: session.messageId
|
|
166
|
+
}
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
const eventId = session.event?._data?.id;
|
|
170
|
+
if (eventId) {
|
|
171
|
+
return {
|
|
172
|
+
type: "event_id",
|
|
173
|
+
id: eventId,
|
|
174
|
+
payload: {
|
|
175
|
+
event_id: eventId
|
|
176
|
+
}
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
return null;
|
|
180
|
+
},
|
|
181
|
+
/**
|
|
182
|
+
* 超过被动 MD 上限时:
|
|
183
|
+
* 1. 优先找 message 事件中缓存的新 msg_id
|
|
184
|
+
* 2. 没有新 msg_id,再找按钮事件缓存的 event_id
|
|
185
|
+
*/
|
|
186
|
+
getNextPassiveSource(userId, current) {
|
|
187
|
+
const cachedMessageId = Chat.messageIdTemp[userId];
|
|
188
|
+
if (cachedMessageId && !(current.type === "msg_id" && current.id === cachedMessageId)) {
|
|
189
|
+
return {
|
|
190
|
+
type: "msg_id",
|
|
191
|
+
id: cachedMessageId,
|
|
192
|
+
payload: {
|
|
193
|
+
msg_id: cachedMessageId
|
|
194
|
+
}
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
const cachedButtonEventId = Chat.buttonEventIdTemp[userId];
|
|
198
|
+
if (cachedButtonEventId && !(current.type === "event_id" && current.id === cachedButtonEventId)) {
|
|
199
|
+
return {
|
|
200
|
+
type: "event_id",
|
|
201
|
+
id: cachedButtonEventId,
|
|
202
|
+
payload: {
|
|
203
|
+
event_id: cachedButtonEventId
|
|
204
|
+
}
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
return null;
|
|
208
|
+
},
|
|
209
|
+
getCacheKey(source) {
|
|
210
|
+
return `${source.type}:${source.id}`;
|
|
211
|
+
},
|
|
212
|
+
/** 清除一条 msg_id/event_id 计数记录 */
|
|
114
213
|
clearChache() {
|
|
115
214
|
const chache = Object.keys(Chat.chache);
|
|
116
215
|
if (chache.length >= 40) {
|
|
@@ -123,9 +222,45 @@ var Chat = {
|
|
|
123
222
|
if (messageIdTempList.length > 150) {
|
|
124
223
|
delete Chat.messageIdTemp[messageIdTempList[0]];
|
|
125
224
|
}
|
|
225
|
+
},
|
|
226
|
+
/** 清除一条按钮 event_id 缓存 */
|
|
227
|
+
clearButtonEventIdTemp() {
|
|
228
|
+
const buttonEventIdTempList = Object.keys(Chat.buttonEventIdTemp);
|
|
229
|
+
if (buttonEventIdTempList.length > 150) {
|
|
230
|
+
delete Chat.buttonEventIdTemp[buttonEventIdTempList[0]];
|
|
231
|
+
}
|
|
126
232
|
}
|
|
127
233
|
};
|
|
128
234
|
|
|
235
|
+
// src/keyboards.ts
|
|
236
|
+
var UNSUPPORT_TIPS = "请升级QQ版本";
|
|
237
|
+
function cmdButton(label, data, style = 0, permission = { type: 2 }, visitedLabel = label, enter = true) {
|
|
238
|
+
return {
|
|
239
|
+
render_data: { label, visited_label: visitedLabel, style },
|
|
240
|
+
action: { type: 2, permission, data, enter, unsupport_tips: UNSUPPORT_TIPS }
|
|
241
|
+
};
|
|
242
|
+
}
|
|
243
|
+
__name(cmdButton, "cmdButton");
|
|
244
|
+
function callbackButton(label, data, style = 0, permission = { type: 2 }, visitedLabel = label, id) {
|
|
245
|
+
return {
|
|
246
|
+
id,
|
|
247
|
+
render_data: { label, visited_label: visitedLabel, style },
|
|
248
|
+
action: { type: 1, permission, data, unsupport_tips: UNSUPPORT_TIPS }
|
|
249
|
+
};
|
|
250
|
+
}
|
|
251
|
+
__name(callbackButton, "callbackButton");
|
|
252
|
+
function linkButton(label, url, style = 0) {
|
|
253
|
+
return {
|
|
254
|
+
render_data: { label, visited_label: label, style },
|
|
255
|
+
action: { type: 0, permission: { type: 2 }, data: url, unsupport_tips: UNSUPPORT_TIPS }
|
|
256
|
+
};
|
|
257
|
+
}
|
|
258
|
+
__name(linkButton, "linkButton");
|
|
259
|
+
function keyboard(...rows) {
|
|
260
|
+
return { content: { rows: rows.map((buttons) => ({ buttons })) } };
|
|
261
|
+
}
|
|
262
|
+
__name(keyboard, "keyboard");
|
|
263
|
+
|
|
129
264
|
// src/index.ts
|
|
130
265
|
var name = "smmcat-gptimage2";
|
|
131
266
|
var inject = ["canvas", "database"];
|
|
@@ -380,7 +515,7 @@ function apply(ctx, config) {
|
|
|
380
515
|
};
|
|
381
516
|
config.deBug && console.log(body);
|
|
382
517
|
const response = await ctx.http.post(body.images ? modelConfig.use.url : modelConfig.use.no_img_url, body, {
|
|
383
|
-
headers: { "Authorization": config.token }
|
|
518
|
+
headers: { "Authorization": config.token, "Content-Type": "application/json" }
|
|
384
519
|
});
|
|
385
520
|
config.deBug && console.log(response);
|
|
386
521
|
const data = response.data;
|
|
@@ -544,9 +679,18 @@ function apply(ctx, config) {
|
|
|
544
679
|
if (!newData) {
|
|
545
680
|
await Chat.send(session, `获取失败,稍后再试!`);
|
|
546
681
|
}
|
|
547
|
-
await Chat.send(session, VideoGpt.formatMsg(newData));
|
|
682
|
+
await Chat.send(session, VideoGpt.formatMsg(newData), VideoGpt.formatMsg(newData), VideoGpt.formatButton(newData, session));
|
|
548
683
|
}
|
|
549
684
|
},
|
|
685
|
+
formatButton(data, session) {
|
|
686
|
+
return keyboard(
|
|
687
|
+
[
|
|
688
|
+
session.guildId ? callbackButton("更新进度", data.task_id, 0, { type: 0, specify_user_ids: [session.userId] }, "查询完成", "check_video_task") : callbackButton("更新进度", data.task_id, 0, { type: 2 }, "查询完成", "check_video_task")
|
|
689
|
+
],
|
|
690
|
+
[cmdButton("查询 teak_id", "/查视频进度 " + data.task_id, 0)],
|
|
691
|
+
data.webUrl ? [linkButton("查看视频", data.webUrl, 0)] : []
|
|
692
|
+
);
|
|
693
|
+
},
|
|
550
694
|
formatMsg(data) {
|
|
551
695
|
console.log(data);
|
|
552
696
|
const video = data?.url ? import_koishi.h.video((0, import_node_url.pathToFileURL)(import_path.default.join(ctx.baseDir, config.download_videoPath, data.url)).href) : "";
|
|
@@ -555,10 +699,9 @@ function apply(ctx, config) {
|
|
|
555
699
|
const status = VideoGpt.statusDict[data.status] || "未知";
|
|
556
700
|
const model = data.model;
|
|
557
701
|
const progress = data.progress;
|
|
558
|
-
console.log(data.url);
|
|
559
702
|
if (config.useMd) {
|
|
560
703
|
return `
|
|
561
|
-
|
|
704
|
+
### 视频生成进度查询
|
|
562
705
|
|
|
563
706
|
\`\`\` 内容
|
|
564
707
|
${data.prompt}
|
|
@@ -568,8 +711,7 @@ function apply(ctx, config) {
|
|
|
568
711
|
**进度** ${progress}
|
|
569
712
|
**使用模型** ${model}
|
|
570
713
|
**创建时间** ${createdTime}
|
|
571
|
-
|
|
572
|
-
${data?.webUrl ? `[查看信息](${data.webUrl})` : ""}`;
|
|
714
|
+
`;
|
|
573
715
|
} else {
|
|
574
716
|
return `teak_id:${data.task_id}
|
|
575
717
|
内容:${prompt}
|
|
@@ -764,6 +906,26 @@ tip:您现在使用的是 ${videoConfig.use.type} 进行处理`);
|
|
|
764
906
|
await VideoGpt.getVideoResult(session, task_id);
|
|
765
907
|
});
|
|
766
908
|
}
|
|
909
|
+
const cmdDict = {
|
|
910
|
+
"check_video_task": /* @__PURE__ */ __name((btnData) => {
|
|
911
|
+
return `查视频进度 ${btnData.data}`;
|
|
912
|
+
}, "check_video_task")
|
|
913
|
+
};
|
|
914
|
+
ctx.on("interaction/button", async (session) => {
|
|
915
|
+
if (session.platform !== "qq") return;
|
|
916
|
+
const button = session.event?.button;
|
|
917
|
+
const buttonKey = button?.id;
|
|
918
|
+
console.log(buttonKey);
|
|
919
|
+
if (!buttonKey) return;
|
|
920
|
+
const interactionId = session.event?._data?.id;
|
|
921
|
+
if (interactionId && session.bot.internal?.acknowledgeInteraction) {
|
|
922
|
+
void Promise.resolve(session.bot.internal.acknowledgeInteraction(interactionId, { code: 0 })).catch(() => {
|
|
923
|
+
});
|
|
924
|
+
}
|
|
925
|
+
if (buttonKey && cmdDict[buttonKey]) {
|
|
926
|
+
await session.execute(cmdDict[buttonKey](button));
|
|
927
|
+
}
|
|
928
|
+
});
|
|
767
929
|
}
|
|
768
930
|
__name(apply, "apply");
|
|
769
931
|
// Annotate the CommonJS export names for ESM import in node:
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* QQ Markdown 下挂按钮(keyboard)组件
|
|
3
|
+
*
|
|
4
|
+
* 结构、样式与交互规范来自 robloxmdbutton.md / QQ_MARKDOWN_BUTTON_GUIDE.md:
|
|
5
|
+
* - action.type:0 链接 / 1 回调 / 2 指令
|
|
6
|
+
* - render_data.style:0 灰线框 / 1 蓝线框 / 2 蓝底 / 3 红色警示 / 4 主操作
|
|
7
|
+
* - permission.type:0 指定用户 / 1 管理者 / 2 所有人
|
|
8
|
+
* - 每行最多 4 个按钮,最多 5 行
|
|
9
|
+
*/
|
|
10
|
+
export interface QQButtonRenderData {
|
|
11
|
+
label: string;
|
|
12
|
+
visited_label?: string;
|
|
13
|
+
style: 0 | 1 | 2 | 3 | 4;
|
|
14
|
+
}
|
|
15
|
+
export interface QQButtonPermission {
|
|
16
|
+
type: 0 | 1 | 2;
|
|
17
|
+
specify_user_ids?: string[];
|
|
18
|
+
}
|
|
19
|
+
export interface QQButtonAction {
|
|
20
|
+
type: 0 | 1 | 2;
|
|
21
|
+
permission: QQButtonPermission;
|
|
22
|
+
data: string;
|
|
23
|
+
enter?: boolean;
|
|
24
|
+
reply?: boolean;
|
|
25
|
+
unsupport_tips?: string;
|
|
26
|
+
}
|
|
27
|
+
export interface QQButton {
|
|
28
|
+
id?: string;
|
|
29
|
+
render_data: QQButtonRenderData;
|
|
30
|
+
action: QQButtonAction;
|
|
31
|
+
}
|
|
32
|
+
export interface QQKeyboardRow {
|
|
33
|
+
buttons: QQButton[];
|
|
34
|
+
}
|
|
35
|
+
export interface QQKeyboard {
|
|
36
|
+
content: {
|
|
37
|
+
rows: QQKeyboardRow[];
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
/** 指令按钮(action.type=2):以点击者身份发送指令 */
|
|
41
|
+
export declare function cmdButton(label: string, data: string, style?: QQButtonRenderData['style'], permission?: QQButtonPermission, visitedLabel?: string, enter?: boolean): QQButton;
|
|
42
|
+
/** 回调按钮(action.type=1):触发 interaction/button 事件 */
|
|
43
|
+
export declare function callbackButton(label: string, data: string, style?: QQButtonRenderData['style'], permission?: QQButtonPermission, visitedLabel?: string, id?: string): QQButton;
|
|
44
|
+
/** 链接按钮(action.type=0) */
|
|
45
|
+
export declare function linkButton(label: string, url: string, style?: QQButtonRenderData['style']): QQButton;
|
|
46
|
+
export declare function keyboard(...rows: QQButton[][]): QQKeyboard;
|