@alemonjs/qq-bot 2.1.33 → 2.1.34
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 +46 -8
- package/dist/assets/index.css +1 -1
- package/dist/assets/index.js +12 -5
- package/dist/index.html +1 -1
- package/lib/config.d.ts +44 -19
- package/lib/config.js +39 -35
- package/lib/desktop.d.ts +4 -1
- package/lib/desktop.js +53 -57
- package/lib/format.d.ts +26 -5
- package/lib/format.js +84 -101
- package/lib/hook.d.ts +82 -65
- package/lib/hook.js +35 -32
- package/lib/index.d.ts +6 -4
- package/lib/index.js +13 -16
- package/lib/index.webhook.d.ts +4 -1
- package/lib/index.webhook.js +12 -14
- package/lib/index.websoket.d.ts +6 -3
- package/lib/index.websoket.js +131 -41
- package/lib/message/AT_MESSAGE_CREATE.d.ts +36 -33
- package/lib/message/AT_MESSAGE_CREATE.js +1 -1
- package/lib/message/CHANNEL_CREATE.d.ts +17 -14
- package/lib/message/CHANNEL_CREATE.js +1 -1
- package/lib/message/CHANNEL_DELETE.d.ts +22 -14
- package/lib/message/CHANNEL_DELETE.js +1 -1
- package/lib/message/CHANNEL_UPDATE.d.ts +22 -14
- package/lib/message/CHANNEL_UPDATE.js +1 -1
- package/lib/message/DIRECT_MESSAGE_CREATE.d.ts +35 -27
- package/lib/message/DIRECT_MESSAGE_CREATE.js +1 -1
- package/lib/message/DIRECT_MESSAGE_DELETE.d.ts +23 -15
- package/lib/message/DIRECT_MESSAGE_DELETE.js +1 -1
- package/lib/message/ERROR.d.ts +4 -1
- package/lib/message/ERROR.js +1 -1
- package/lib/message/GUILD_CREATE.d.ts +22 -14
- package/lib/message/GUILD_CREATE.js +1 -1
- package/lib/message/GUILD_DELETE.d.ts +22 -14
- package/lib/message/GUILD_DELETE.js +1 -1
- package/lib/message/GUILD_MEMBER_ADD.d.ts +21 -13
- package/lib/message/GUILD_MEMBER_ADD.js +1 -1
- package/lib/message/GUILD_MEMBER_REMOVE.d.ts +21 -13
- package/lib/message/GUILD_MEMBER_REMOVE.js +1 -1
- package/lib/message/GUILD_MEMBER_UPDATE.d.ts +21 -13
- package/lib/message/GUILD_MEMBER_UPDATE.js +1 -1
- package/lib/message/GUILD_UPDATE.d.ts +22 -14
- package/lib/message/GUILD_UPDATE.js +1 -1
- package/lib/message/INTERACTION_CREATE.d.ts +52 -44
- package/lib/message/INTERACTION_CREATE.js +1 -1
- package/lib/message/MESSAGE_CREATE.d.ts +12 -1
- package/lib/message/MESSAGE_CREATE.js +1 -1
- package/lib/message/MESSAGE_DELETE.d.ts +12 -1
- package/lib/message/MESSAGE_DELETE.js +1 -1
- package/lib/message/MESSAGE_REACTION_ADD.d.ts +15 -12
- package/lib/message/MESSAGE_REACTION_ADD.js +1 -1
- package/lib/message/MESSAGE_REACTION_REMOVE.d.ts +15 -12
- package/lib/message/MESSAGE_REACTION_REMOVE.js +1 -1
- package/lib/message/PUBLIC_MESSAGE_DELETE.d.ts +20 -13
- package/lib/message/PUBLIC_MESSAGE_DELETE.js +1 -1
- package/lib/message/READY.d.ts +11 -5
- package/lib/message/READY.js +1 -1
- package/lib/message/group/C2C_MESSAGE_CREATE.d.ts +18 -15
- package/lib/message/group/C2C_MESSAGE_CREATE.js +1 -1
- package/lib/message/group/C2C_MSG_RECEIVE.d.ts +13 -3
- package/lib/message/group/C2C_MSG_RECEIVE.js +1 -1
- package/lib/message/group/C2C_MSG_REJECT.d.ts +9 -3
- package/lib/message/group/C2C_MSG_REJECT.js +1 -1
- package/lib/message/group/FRIEND_ADD.d.ts +13 -3
- package/lib/message/group/FRIEND_ADD.js +1 -1
- package/lib/message/group/FRIEND_DEL.d.ts +9 -3
- package/lib/message/group/FRIEND_DEL.js +1 -1
- package/lib/message/group/GROUP_ADD_ROBOT.d.ts +23 -4
- package/lib/message/group/GROUP_ADD_ROBOT.js +1 -1
- package/lib/message/group/GROUP_AT_MESSAGE_CREATE.d.ts +38 -32
- package/lib/message/group/GROUP_AT_MESSAGE_CREATE.js +1 -1
- package/lib/message/group/GROUP_DEL_ROBOT.d.ts +19 -4
- package/lib/message/group/GROUP_DEL_ROBOT.js +1 -1
- package/lib/message/group/GROUP_JOIN_REQUEST.d.ts +83 -22
- package/lib/message/group/GROUP_JOIN_REQUEST.js +1 -1
- package/lib/message/group/GROUP_MEMBER_ADD.d.ts +8 -5
- package/lib/message/group/GROUP_MEMBER_ADD.js +1 -1
- package/lib/message/group/GROUP_MEMBER_REMOVE.d.ts +8 -5
- package/lib/message/group/GROUP_MEMBER_REMOVE.js +1 -1
- package/lib/message/group/GROUP_MESSAGE_CREATE.d.ts +38 -32
- package/lib/message/group/GROUP_MESSAGE_CREATE.js +1 -1
- package/lib/message/group/GROUP_MSG_RECEIVE.d.ts +14 -4
- package/lib/message/group/GROUP_MSG_RECEIVE.js +1 -1
- package/lib/message/group/GROUP_MSG_REJECT.d.ts +10 -4
- package/lib/message/group/GROUP_MSG_REJECT.js +1 -1
- package/lib/message/group/MESSAGE_AUDIT_PASS.d.ts +12 -6
- package/lib/message/group/MESSAGE_AUDIT_PASS.js +1 -1
- package/lib/message/group/MESSAGE_AUDIT_REJECT.d.ts +12 -6
- package/lib/message/group/MESSAGE_AUDIT_REJECT.js +1 -1
- package/lib/register.d.ts +12 -9
- package/lib/register.js +1246 -1404
- package/lib/sdk/api.d.ts +937 -306
- package/lib/sdk/api.js +1481 -893
- package/lib/sdk/client.webhook.d.ts +25 -8
- package/lib/sdk/client.webhook.js +185 -189
- package/lib/sdk/client.websoket.d.ts +31 -9
- package/lib/sdk/client.websoket.js +369 -392
- package/lib/sdk/config.d.ts +6 -2
- package/lib/sdk/config.js +9 -6
- package/lib/sdk/instance.d.ts +11 -3
- package/lib/sdk/instance.js +72 -84
- package/lib/sdk/intents.d.ts +89 -3
- package/lib/sdk/intents.js +116 -33
- package/lib/sdk/message.d.ts +7 -4
- package/lib/sdk/message.group.d.ts +38 -35
- package/lib/sdk/message.group.js +1 -1
- package/lib/sdk/message.guild.d.ts +40 -37
- package/lib/sdk/message.guild.js +1 -1
- package/lib/sdk/message.js +1 -1
- package/lib/sdk/message.public.d.ts +8 -5
- package/lib/sdk/message.public.js +1 -1
- package/lib/sdk/qr-auth.d.ts +55 -0
- package/lib/sdk/qr-auth.js +169 -0
- package/lib/sdk/registry.d.ts +34 -24
- package/lib/sdk/registry.js +134 -114
- package/lib/sdk/session.d.ts +19 -15
- package/lib/sdk/session.js +51 -54
- package/lib/sdk/typing.d.ts +410 -184
- package/lib/sdk/typing.js +1 -1
- package/lib/sdk/webhook.secret.d.ts +33 -13
- package/lib/sdk/webhook.secret.js +50 -36
- package/lib/sends.d.ts +42 -25
- package/lib/sends.js +519 -541
- package/lib/types.d.ts +52 -41
- package/lib/types.js +1 -1
- package/lib/upload.d.ts +47 -18
- package/lib/upload.js +80 -51
- package/lib/utils.d.ts +9 -1
- package/lib/utils.js +10 -5
- package/package.json +9 -6
package/lib/sends.js
CHANGED
|
@@ -1,599 +1,577 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import
|
|
4
|
-
import {
|
|
5
|
-
import
|
|
1
|
+
import { getQQBotConfig } from "./config.js";
|
|
2
|
+
import { buttonsToText, dataEnumToText, markdownToText } from "./format.js";
|
|
3
|
+
import { ResultCode, createResult, logger } from "alemonjs";
|
|
4
|
+
import { readFileSync } from "fs";
|
|
5
|
+
import axios from "axios";
|
|
6
6
|
|
|
7
|
+
//#region src/sends.ts
|
|
8
|
+
/** QQ Bot 平台按钮限制:最多 5 行,每行最多 5 个按钮 */
|
|
7
9
|
const MAX_BUTTON_ROWS = 5;
|
|
8
10
|
const MAX_BUTTONS_PER_ROW = 5;
|
|
11
|
+
/**
|
|
12
|
+
* QQ-Bot 按钮样式(render_data.style):
|
|
13
|
+
* 0 灰色线框 / 1 蓝色线框 / 3 红框 / 4 蓝底白字
|
|
14
|
+
*/
|
|
9
15
|
const BUTTON_STYLE_MAP = {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
16
|
+
gray: 0,
|
|
17
|
+
blue: 1,
|
|
18
|
+
red: 3,
|
|
19
|
+
"blue-fill": 4
|
|
14
20
|
};
|
|
21
|
+
/** 解析按钮样式:支持样式名与数字(含数字字符串),默认灰框 0 */
|
|
15
22
|
const resolveButtonStyle = (style) => {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
}
|
|
23
|
-
if (style.trim() !== '' && !Number.isNaN(Number(style))) {
|
|
24
|
-
return Number(style);
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
return 0;
|
|
23
|
+
if (typeof style === "number") return style;
|
|
24
|
+
if (typeof style === "string") {
|
|
25
|
+
if (BUTTON_STYLE_MAP[style] !== void 0) return BUTTON_STYLE_MAP[style];
|
|
26
|
+
if (style.trim() !== "" && !Number.isNaN(Number(style))) return Number(style);
|
|
27
|
+
}
|
|
28
|
+
return 0;
|
|
28
29
|
};
|
|
29
30
|
const createButtonsData = (rows, startId = 0) => {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
})
|
|
83
|
-
})),
|
|
84
|
-
nextId: id
|
|
85
|
-
};
|
|
31
|
+
let id = startId;
|
|
32
|
+
return {
|
|
33
|
+
rows: rows.slice(0, MAX_BUTTON_ROWS).map((row) => ({ buttons: row.value.slice(0, MAX_BUTTONS_PER_ROW).map((button) => {
|
|
34
|
+
const value = button.value;
|
|
35
|
+
const options = button.options;
|
|
36
|
+
id++;
|
|
37
|
+
const typing = options?.type ?? "command";
|
|
38
|
+
const typeMap = {
|
|
39
|
+
command: 2,
|
|
40
|
+
link: 0,
|
|
41
|
+
call: 1
|
|
42
|
+
};
|
|
43
|
+
const rowData = options?.rawData ?? {};
|
|
44
|
+
const data = options?.data;
|
|
45
|
+
const modal = options?.modal ?? (typeof data === "object" && data ? {
|
|
46
|
+
content: data.click,
|
|
47
|
+
confirmText: data.confirm,
|
|
48
|
+
cancelText: data.cancel
|
|
49
|
+
} : void 0);
|
|
50
|
+
const action = {
|
|
51
|
+
type: typeMap[typing],
|
|
52
|
+
permission: {
|
|
53
|
+
type: typeof options.permission?.type === "undefined" ? 2 : options?.permission?.type,
|
|
54
|
+
specify_user_ids: options?.permission?.userIds,
|
|
55
|
+
specify_role_ids: options?.permission?.roleIds
|
|
56
|
+
},
|
|
57
|
+
unsupport_tips: options?.toolTip ?? "",
|
|
58
|
+
data: typeof data === "string" ? data : "",
|
|
59
|
+
at_bot_show_channel_list: options?.atBotShowChannelList ?? false,
|
|
60
|
+
enter: options?.autoEnter ?? false,
|
|
61
|
+
reply: options?.reply ?? false,
|
|
62
|
+
anchor: options?.anchor ?? 0,
|
|
63
|
+
click_limit: options?.clickLimit
|
|
64
|
+
};
|
|
65
|
+
if (modal) action.modal = {
|
|
66
|
+
content: modal.content ?? "是否确认操作?",
|
|
67
|
+
confirm_text: modal.confirmText ?? "是",
|
|
68
|
+
cancel_text: modal.cancelText ?? "否"
|
|
69
|
+
};
|
|
70
|
+
return {
|
|
71
|
+
id: String(id),
|
|
72
|
+
render_data: {
|
|
73
|
+
label: value,
|
|
74
|
+
visited_label: value,
|
|
75
|
+
style: resolveButtonStyle(options?.style)
|
|
76
|
+
},
|
|
77
|
+
action,
|
|
78
|
+
...rowData
|
|
79
|
+
};
|
|
80
|
+
}) })),
|
|
81
|
+
nextId: id
|
|
82
|
+
};
|
|
86
83
|
};
|
|
87
84
|
const createArkCardData = (value) => ({
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
85
|
+
template_id: 24,
|
|
86
|
+
kv: [
|
|
87
|
+
{
|
|
88
|
+
key: "#DESC#",
|
|
89
|
+
value: value.decs
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
key: "#PROMPT#",
|
|
93
|
+
value: value.prompt
|
|
94
|
+
},
|
|
95
|
+
{
|
|
96
|
+
key: "#TITLE#",
|
|
97
|
+
value: value.title
|
|
98
|
+
},
|
|
99
|
+
{
|
|
100
|
+
key: "#METADESC#",
|
|
101
|
+
value: value.metadecs
|
|
102
|
+
},
|
|
103
|
+
{
|
|
104
|
+
key: "#IMG#",
|
|
105
|
+
value: value.cover
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
key: "#LINK#",
|
|
109
|
+
value: value.link
|
|
110
|
+
},
|
|
111
|
+
{
|
|
112
|
+
key: "#SUBTITLE#",
|
|
113
|
+
value: value.subtitle
|
|
114
|
+
}
|
|
115
|
+
]
|
|
98
116
|
});
|
|
99
117
|
const createArkBigCardData = (value) => ({
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
118
|
+
template_id: 37,
|
|
119
|
+
kv: [
|
|
120
|
+
{
|
|
121
|
+
key: "#PROMPT#",
|
|
122
|
+
value: value.prompt
|
|
123
|
+
},
|
|
124
|
+
{
|
|
125
|
+
key: "#METATITLE#",
|
|
126
|
+
value: value.title
|
|
127
|
+
},
|
|
128
|
+
{
|
|
129
|
+
key: "#METASUBTITLE#",
|
|
130
|
+
value: value.subtitle
|
|
131
|
+
},
|
|
132
|
+
{
|
|
133
|
+
key: "#METACOVER#",
|
|
134
|
+
value: value.cover
|
|
135
|
+
},
|
|
136
|
+
{
|
|
137
|
+
key: "#METAURL#",
|
|
138
|
+
value: value.link
|
|
139
|
+
}
|
|
140
|
+
]
|
|
108
141
|
});
|
|
109
142
|
const createArkListData = (value) => {
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
143
|
+
const [tip, data] = value;
|
|
144
|
+
return {
|
|
145
|
+
template_id: 23,
|
|
146
|
+
kv: [
|
|
147
|
+
{
|
|
148
|
+
key: "#DESC#",
|
|
149
|
+
value: tip.value.desc
|
|
150
|
+
},
|
|
151
|
+
{
|
|
152
|
+
key: "#PROMPT#",
|
|
153
|
+
value: tip.value.prompt
|
|
154
|
+
},
|
|
155
|
+
{
|
|
156
|
+
key: "#LIST#",
|
|
157
|
+
obj: data.value.map((item) => {
|
|
158
|
+
const v = item.value;
|
|
159
|
+
if (typeof v === "string") return { obj_kv: [{
|
|
160
|
+
key: "desc",
|
|
161
|
+
value: v
|
|
162
|
+
}] };
|
|
163
|
+
return { obj_kv: [{
|
|
164
|
+
key: "desc",
|
|
165
|
+
value: v.title
|
|
166
|
+
}, {
|
|
167
|
+
key: "link",
|
|
168
|
+
value: v.link
|
|
169
|
+
}] };
|
|
170
|
+
})
|
|
171
|
+
}
|
|
172
|
+
]
|
|
173
|
+
};
|
|
133
174
|
};
|
|
134
175
|
const mdFormatters = {
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
return '<qqbot-at-everyone />';
|
|
165
|
-
}
|
|
166
|
-
return `<qqbot-at-user id="${value}" />`;
|
|
167
|
-
},
|
|
168
|
-
'MD.content': value => `${value}`,
|
|
169
|
-
'MD.button': (title, options) => {
|
|
170
|
-
const { data, autoEnter } = options || {};
|
|
171
|
-
if (autoEnter) {
|
|
172
|
-
return `<qqbot-cmd-enter text="${data}" show="${title}" />`;
|
|
173
|
-
}
|
|
174
|
-
return `<qqbot-cmd-input text="${data}" show="${title}" />`;
|
|
175
|
-
}
|
|
176
|
+
"MD.title": (value) => `# ${value} `,
|
|
177
|
+
"MD.subtitle": (value) => `## ${value} `,
|
|
178
|
+
"MD.text": (value) => `${value} `,
|
|
179
|
+
"MD.bold": (value) => `**${value}** `,
|
|
180
|
+
"MD.divider": () => "\n***\n",
|
|
181
|
+
"MD.italic": (value) => `__${value}__ `,
|
|
182
|
+
"MD.italicStar": (value) => `*${value}* `,
|
|
183
|
+
"MD.strikethrough": (value) => `~~${value}~~ `,
|
|
184
|
+
"MD.blockquote": (value) => `\n> ${value} `,
|
|
185
|
+
"MD.newline": () => "\n",
|
|
186
|
+
"MD.link": (value) => {
|
|
187
|
+
if (!value?.text && !value?.url) return "";
|
|
188
|
+
if (!value?.text || !value?.url) return `<${value?.url ?? value?.text}> `;
|
|
189
|
+
return `[🔗${value?.text}](${value?.url}) `;
|
|
190
|
+
},
|
|
191
|
+
"MD.image": (value, options) => `\n\n`,
|
|
192
|
+
"MD.mention": (value, options) => {
|
|
193
|
+
const { belong } = options || {};
|
|
194
|
+
if (belong === "channel") return "";
|
|
195
|
+
if (belong === "user") return `<qqbot-at-user id="${value}" />`;
|
|
196
|
+
if (value === "everyone") return "<qqbot-at-everyone />";
|
|
197
|
+
return `<qqbot-at-user id="${value}" />`;
|
|
198
|
+
},
|
|
199
|
+
"MD.content": (value) => `${value}`,
|
|
200
|
+
"MD.button": (title, options) => {
|
|
201
|
+
const { data, autoEnter } = options || {};
|
|
202
|
+
if (autoEnter) return `<qqbot-cmd-enter text="${data}" show="${title}" />`;
|
|
203
|
+
return `<qqbot-cmd-input text="${data}" show="${title}" />`;
|
|
204
|
+
}
|
|
176
205
|
};
|
|
177
206
|
const createMarkdownText = (data) => {
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
});
|
|
187
|
-
return `${listStr.join('')}\n`;
|
|
188
|
-
}
|
|
189
|
-
if (mdItem.type === 'MD.code') {
|
|
190
|
-
const language = mdItem?.options?.language || '';
|
|
191
|
-
return `\n\`\`\`${language}\n${mdItem.value}\n\`\`\`\n`;
|
|
192
|
-
}
|
|
193
|
-
return String(mdItem['value'] || '');
|
|
194
|
-
})
|
|
195
|
-
.join('');
|
|
207
|
+
return data.map((mdItem) => {
|
|
208
|
+
if (mdFormatters[mdItem.type]) return mdFormatters[mdItem.type](mdItem?.value, mdItem?.options);
|
|
209
|
+
if (mdItem.type === "MD.list" && typeof mdItem.value !== "string") return `${mdItem.value.map((listItem) => {
|
|
210
|
+
return typeof listItem.value === "object" ? `\n${listItem.value.index}. ${listItem.value.text}` : `\n- ${listItem.value}`;
|
|
211
|
+
}).join("")}\n`;
|
|
212
|
+
if (mdItem.type === "MD.code") return `\n\`\`\`${mdItem?.options?.language || ""}\n${mdItem.value}\n\`\`\`\n`;
|
|
213
|
+
return String(mdItem["value"] || "");
|
|
214
|
+
}).join("");
|
|
196
215
|
};
|
|
197
216
|
const formatMention = (item, mode) => {
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
if (item.options?.belong === 'channel') {
|
|
211
|
-
return `<#${value}>`;
|
|
212
|
-
}
|
|
213
|
-
return '';
|
|
214
|
-
}
|
|
215
|
-
if (isEmptyMention) {
|
|
216
|
-
return '';
|
|
217
|
-
}
|
|
218
|
-
if (item.options?.belong === 'user') {
|
|
219
|
-
return `<@${value}>`;
|
|
220
|
-
}
|
|
221
|
-
return '';
|
|
217
|
+
if (mode === "guild-direct") return "";
|
|
218
|
+
const value = item.value;
|
|
219
|
+
const isEmptyMention = value === "everyone" || value === "all" || value === "" || typeof value !== "string";
|
|
220
|
+
if (mode === "guild-public") {
|
|
221
|
+
if (isEmptyMention) return "@everyone";
|
|
222
|
+
if (item.options?.belong === "user") return `<@!${value}>`;
|
|
223
|
+
if (item.options?.belong === "channel") return `<#${value}>`;
|
|
224
|
+
return "";
|
|
225
|
+
}
|
|
226
|
+
if (isEmptyMention) return "";
|
|
227
|
+
if (item.options?.belong === "user") return `<@${value}>`;
|
|
228
|
+
return "";
|
|
222
229
|
};
|
|
230
|
+
/** 从消息数据中提取文本内容 */
|
|
223
231
|
const extractContent = (val, mode) => {
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
}
|
|
251
|
-
if (item.type === 'Text') {
|
|
252
|
-
return item.value;
|
|
253
|
-
}
|
|
254
|
-
return '';
|
|
255
|
-
})
|
|
256
|
-
.join('');
|
|
257
|
-
const config = getQQBotConfig();
|
|
258
|
-
const hide = config.hideUnsupported;
|
|
259
|
-
const fallbackText = val
|
|
260
|
-
.filter(item => !nativeTypes.has(item.type))
|
|
261
|
-
.map(item => dataEnumToText(item, hide))
|
|
262
|
-
.filter(Boolean)
|
|
263
|
-
.join('\n');
|
|
264
|
-
return [nativeText, fallbackText].filter(Boolean).join('\n');
|
|
232
|
+
const nativeTypes = /* @__PURE__ */ new Set([
|
|
233
|
+
"Mention",
|
|
234
|
+
"Text",
|
|
235
|
+
"Link",
|
|
236
|
+
"Image",
|
|
237
|
+
"ImageFile",
|
|
238
|
+
"ImageURL",
|
|
239
|
+
"Audio",
|
|
240
|
+
"Video",
|
|
241
|
+
"Attachment",
|
|
242
|
+
"Markdown",
|
|
243
|
+
"MarkdownOriginal",
|
|
244
|
+
"BT.group",
|
|
245
|
+
"ButtonTemplate",
|
|
246
|
+
"Ark.list",
|
|
247
|
+
"Ark.Card",
|
|
248
|
+
"Ark.BigCard"
|
|
249
|
+
]);
|
|
250
|
+
const nativeText = val.filter((item) => item.type === "Mention" || item.type === "Text" || item.type === "Link").map((item) => {
|
|
251
|
+
if (item.type === "Link") return `[${item.value}](${item?.options?.link})`;
|
|
252
|
+
if (item.type === "Mention") return formatMention(item, mode);
|
|
253
|
+
if (item.type === "Text") return item.value;
|
|
254
|
+
return "";
|
|
255
|
+
}).join("");
|
|
256
|
+
const hide = getQQBotConfig().hideUnsupported;
|
|
257
|
+
return [nativeText, val.filter((item) => !nativeTypes.has(item.type)).map((item) => dataEnumToText(item, hide)).filter(Boolean).join("\n")].filter(Boolean).join("\n");
|
|
265
258
|
};
|
|
259
|
+
/**
|
|
260
|
+
* notice 事件中平台支持以 event_id 被动回复的 tag
|
|
261
|
+
* (群聊:GROUP_ADD_ROBOT / GROUP_MSG_RECEIVE;单聊:C2C_MSG_RECEIVE / FRIEND_ADD)
|
|
262
|
+
*/
|
|
263
|
+
const NOTICE_EVENT_REPLY_TAGS = /* @__PURE__ */ new Set([
|
|
264
|
+
"GROUP_ADD_ROBOT",
|
|
265
|
+
"GROUP_MSG_RECEIVE",
|
|
266
|
+
"C2C_MSG_RECEIVE",
|
|
267
|
+
"FRIEND_ADD"
|
|
268
|
+
]);
|
|
269
|
+
/** 构建 baseParams(event_id 或 msg_id) */
|
|
266
270
|
const buildBaseParams = (tag, messageId, interactionTag) => {
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
}
|
|
270
|
-
return { msg_id: messageId };
|
|
271
|
+
if (tag && NOTICE_EVENT_REPLY_TAGS.has(tag) || tag === interactionTag) return { event_id: messageId };
|
|
272
|
+
return { msg_id: messageId };
|
|
271
273
|
};
|
|
274
|
+
/** 构建 Markdown 和按钮参数 */
|
|
272
275
|
const buildMdAndButtonsParams = (val) => {
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
if (params['markdown']?.content) {
|
|
312
|
-
params['markdown'].content += content;
|
|
313
|
-
}
|
|
314
|
-
else {
|
|
315
|
-
params['markdown'] = { content };
|
|
316
|
-
}
|
|
317
|
-
}
|
|
318
|
-
}
|
|
319
|
-
else if (item.type === 'MarkdownOriginal' && typeof item?.value === 'string') {
|
|
320
|
-
if (params['markdown']?.content) {
|
|
321
|
-
params['markdown'].content += '\n' + item.value;
|
|
322
|
-
}
|
|
323
|
-
else {
|
|
324
|
-
params['markdown'] = { content: item.value };
|
|
325
|
-
}
|
|
326
|
-
}
|
|
327
|
-
}
|
|
328
|
-
if (keyboardExtra && params['keyboard']?.content) {
|
|
329
|
-
if (keyboardExtra.smallButton) {
|
|
330
|
-
params['keyboard'].content.style = { font_size: 'small' };
|
|
331
|
-
}
|
|
332
|
-
if (keyboardExtra.rawData) {
|
|
333
|
-
Object.assign(params['keyboard'].content, keyboardExtra.rawData);
|
|
334
|
-
}
|
|
335
|
-
}
|
|
336
|
-
if (params['keyboard']?.content?.nextId !== undefined) {
|
|
337
|
-
delete params['keyboard'].content.nextId;
|
|
338
|
-
}
|
|
339
|
-
return params;
|
|
276
|
+
const items = val.filter((item) => item.type === "Markdown" || item.type === "MarkdownOriginal" || item.type === "BT.group" || item.type === "ButtonTemplate");
|
|
277
|
+
if (items.length === 0) return null;
|
|
278
|
+
const params = {};
|
|
279
|
+
let keyboardExtra;
|
|
280
|
+
for (const item of items) if (item.type === "ButtonTemplate") {
|
|
281
|
+
if (item?.value) params["keyboard"] = { id: item.value };
|
|
282
|
+
} else if (item.type === "BT.group" && typeof item?.value !== "string") {
|
|
283
|
+
const groupOptions = item?.options ?? {};
|
|
284
|
+
if (groupOptions.smallButton || groupOptions.rawData) keyboardExtra = {
|
|
285
|
+
smallButton: keyboardExtra?.smallButton || groupOptions.smallButton,
|
|
286
|
+
rawData: groupOptions.rawData
|
|
287
|
+
};
|
|
288
|
+
if (params["keyboard"]?.content?.rows) {
|
|
289
|
+
const existingRows = params["keyboard"].content.rows;
|
|
290
|
+
const currentId = params["keyboard"].content.nextId ?? existingRows.length;
|
|
291
|
+
const remaining = MAX_BUTTON_ROWS - existingRows.length;
|
|
292
|
+
if (remaining > 0) {
|
|
293
|
+
const { rows: newRows, nextId } = createButtonsData(item.value.slice(0, remaining), currentId);
|
|
294
|
+
existingRows.push(...newRows);
|
|
295
|
+
params["keyboard"].content.nextId = nextId;
|
|
296
|
+
}
|
|
297
|
+
} else params["keyboard"] = { content: createButtonsData(item.value) };
|
|
298
|
+
} else if (item.type === "Markdown" && typeof item?.value !== "string") {
|
|
299
|
+
const content = createMarkdownText(item.value);
|
|
300
|
+
if (content) {
|
|
301
|
+
if (params["markdown"]?.content) params["markdown"].content += content;
|
|
302
|
+
else params["markdown"] = { content };
|
|
303
|
+
}
|
|
304
|
+
} else if (item.type === "MarkdownOriginal" && typeof item?.value === "string") {
|
|
305
|
+
if (params["markdown"]?.content) params["markdown"].content += "\n" + item.value;
|
|
306
|
+
else params["markdown"] = { content: item.value };
|
|
307
|
+
}
|
|
308
|
+
if (keyboardExtra && params["keyboard"]?.content) {
|
|
309
|
+
if (keyboardExtra.smallButton) params["keyboard"].content.style = { font_size: "small" };
|
|
310
|
+
if (keyboardExtra.rawData) Object.assign(params["keyboard"].content, keyboardExtra.rawData);
|
|
311
|
+
}
|
|
312
|
+
if (params["keyboard"]?.content?.nextId !== void 0) delete params["keyboard"].content.nextId;
|
|
313
|
+
return params;
|
|
340
314
|
};
|
|
315
|
+
/** 构建 Ark 参数 */
|
|
341
316
|
const buildArkParams = (val) => {
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
params['ark'] = createArkCardData(item.value);
|
|
350
|
-
}
|
|
351
|
-
else if (item.type === 'Ark.BigCard' && typeof item?.value !== 'string') {
|
|
352
|
-
params['ark'] = createArkBigCardData(item.value);
|
|
353
|
-
}
|
|
354
|
-
else if (item.type === 'Ark.list' && typeof item?.value !== 'string') {
|
|
355
|
-
params['ark'] = createArkListData(item.value);
|
|
356
|
-
}
|
|
357
|
-
}
|
|
358
|
-
return params;
|
|
317
|
+
const items = val.filter((item) => item.type === "Ark.BigCard" || item.type === "Ark.Card" || item.type === "Ark.list");
|
|
318
|
+
if (items.length === 0) return null;
|
|
319
|
+
const params = {};
|
|
320
|
+
for (const item of items) if (item.type === "Ark.Card" && typeof item?.value !== "string") params["ark"] = createArkCardData(item.value);
|
|
321
|
+
else if (item.type === "Ark.BigCard" && typeof item?.value !== "string") params["ark"] = createArkBigCardData(item.value);
|
|
322
|
+
else if (item.type === "Ark.list" && typeof item?.value !== "string") params["ark"] = createArkListData(item.value);
|
|
323
|
+
return params;
|
|
359
324
|
};
|
|
325
|
+
/**
|
|
326
|
+
* 富媒体类型 → QQ file_type 映射
|
|
327
|
+
* 1 图片 / 2 视频 / 3 语音 / 4 文件
|
|
328
|
+
*/
|
|
360
329
|
const MEDIA_FILE_TYPE = {
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
330
|
+
Image: 1,
|
|
331
|
+
ImageFile: 1,
|
|
332
|
+
ImageURL: 1,
|
|
333
|
+
Video: 2,
|
|
334
|
+
Audio: 3,
|
|
335
|
+
Attachment: 4
|
|
367
336
|
};
|
|
337
|
+
/** 过滤富媒体数据(图片/视频/音频/文件) */
|
|
368
338
|
const filterMedia = (val) => {
|
|
369
|
-
|
|
339
|
+
return val.filter((item) => MEDIA_FILE_TYPE[item.type] !== void 0);
|
|
370
340
|
};
|
|
341
|
+
/** 过滤图片数据(仅图片,频道路径使用) */
|
|
371
342
|
const filterImages = (val) => {
|
|
372
|
-
|
|
343
|
+
return val.filter((item) => item.type === "Image" || item.type === "ImageFile" || item.type === "ImageURL");
|
|
373
344
|
};
|
|
345
|
+
/** 将媒体数据解析为 base64(支持 URL / file:// / base64:// / buffer:// / Buffer / 裸 base64) */
|
|
374
346
|
const resolveFileData = async (item) => {
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
if (value.startsWith('buffer://')) {
|
|
387
|
-
return value.replace('buffer://', '');
|
|
388
|
-
}
|
|
389
|
-
if (value.startsWith('file://')) {
|
|
390
|
-
return readFileSync(value.replace('file://', ''), 'base64');
|
|
391
|
-
}
|
|
392
|
-
if (value.startsWith('base64://')) {
|
|
393
|
-
return value.replace('base64://', '');
|
|
394
|
-
}
|
|
395
|
-
return value;
|
|
396
|
-
}
|
|
397
|
-
if (Buffer.isBuffer(value)) {
|
|
398
|
-
return value.toString('base64');
|
|
399
|
-
}
|
|
400
|
-
return undefined;
|
|
347
|
+
if (item.type === "ImageURL") return await axios.get(item.value, { responseType: "arraybuffer" }).then((res) => Buffer.from(res.data, "binary").toString("base64"));
|
|
348
|
+
if (item.type === "ImageFile") return readFileSync(item.value, "base64");
|
|
349
|
+
const value = item.value;
|
|
350
|
+
if (typeof value === "string") {
|
|
351
|
+
if (value.startsWith("https://") || value.startsWith("http://")) return await axios.get(value, { responseType: "arraybuffer" }).then((res) => Buffer.from(res.data, "binary").toString("base64"));
|
|
352
|
+
if (value.startsWith("buffer://")) return value.replace("buffer://", "");
|
|
353
|
+
if (value.startsWith("file://")) return readFileSync(value.replace("file://", ""), "base64");
|
|
354
|
+
if (value.startsWith("base64://")) return value.replace("base64://", "");
|
|
355
|
+
return value;
|
|
356
|
+
}
|
|
357
|
+
if (Buffer.isBuffer(value)) return value.toString("base64");
|
|
401
358
|
};
|
|
359
|
+
/**
|
|
360
|
+
* 通过富媒体上传获取 file_info(图片/视频/音频/文件)
|
|
361
|
+
* 按 MEDIA_FILE_TYPE 自动选择 file_type,超出 10MB 的文件自动走分片上传
|
|
362
|
+
*/
|
|
402
363
|
const resolveMediaUrl = async (media, uploadMedia) => {
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
364
|
+
for (const item of media) {
|
|
365
|
+
const fileData = await resolveFileData(item);
|
|
366
|
+
if (fileData) {
|
|
367
|
+
const fileInfo = await uploadMedia({
|
|
368
|
+
file_type: MEDIA_FILE_TYPE[item.type] ?? 1,
|
|
369
|
+
file_data: fileData
|
|
370
|
+
}).then((res) => res?.file_info);
|
|
371
|
+
if (fileInfo) return fileInfo;
|
|
372
|
+
}
|
|
373
|
+
}
|
|
413
374
|
};
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
.replace(/\[视频\]/g, '')
|
|
418
|
-
.replace(/\n{2,}/g, '\n')
|
|
419
|
-
.trim();
|
|
375
|
+
/** 移除富媒体占位符(dataEnumToText 降级产生的 [视频]/[音频]/[附件]) */
|
|
376
|
+
const stripMediaPlaceholders = (text) => text.replace(/\[附件[^\]]*\]/g, "").replace(/\[音频\]/g, "").replace(/\[视频\]/g, "").replace(/\n{2,}/g, "\n").trim();
|
|
377
|
+
/** 当 markdownToText 选项开启时,将 Markdown 和按钮降级为纯文本并追加到 content */
|
|
420
378
|
const flattenMdToText = (content, val) => {
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
}
|
|
428
|
-
}
|
|
429
|
-
for (const item of btnItems) {
|
|
430
|
-
if (item.type === 'BT.group' && typeof item.value !== 'string') {
|
|
431
|
-
parts.push(buttonsToText(item.value));
|
|
432
|
-
}
|
|
433
|
-
}
|
|
434
|
-
return parts
|
|
435
|
-
.filter(Boolean)
|
|
436
|
-
.join('\n')
|
|
437
|
-
.replace(/^[^\S\n\r]+|[^\S\n\r]+$/g, '');
|
|
379
|
+
const mdItems = val.filter((item) => item.type === "Markdown");
|
|
380
|
+
const btnItems = val.filter((item) => item.type === "BT.group");
|
|
381
|
+
const parts = [content];
|
|
382
|
+
for (const item of mdItems) if (item.type === "Markdown" && typeof item.value !== "string") parts.push(markdownToText(item.value));
|
|
383
|
+
for (const item of btnItems) if (item.type === "BT.group" && typeof item.value !== "string") parts.push(buttonsToText(item.value));
|
|
384
|
+
return parts.filter(Boolean).join("\n").replace(/^[^\S\n\r]+|[^\S\n\r]+$/g, "");
|
|
438
385
|
};
|
|
386
|
+
/** Open API 通用发送逻辑(群组 / C2C) */
|
|
439
387
|
const sendOpenApiMessage = async (content, val, baseParams, uploadMedia, sendMessage, label, options) => {
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
388
|
+
const config = getQQBotConfig();
|
|
389
|
+
const mdToText = config.markdownToText === true;
|
|
390
|
+
const media = filterMedia(val);
|
|
391
|
+
if (media.length > 0) {
|
|
392
|
+
const fileInfo = await resolveMediaUrl(media, uploadMedia);
|
|
393
|
+
if (!fileInfo) return [createResult(ResultCode.Fail, "媒体上传失败", null)];
|
|
394
|
+
const res = await sendMessage({
|
|
395
|
+
content: stripMediaPlaceholders(flattenMdToText(content, val)),
|
|
396
|
+
media: { file_info: fileInfo },
|
|
397
|
+
msg_type: 7,
|
|
398
|
+
...baseParams
|
|
399
|
+
});
|
|
400
|
+
return [createResult(ResultCode.Ok, label, { id: res.id })];
|
|
401
|
+
}
|
|
402
|
+
if (config.hideUnsupported === true && !content && !buildMdAndButtonsParams(val) && !buildArkParams(val)) {
|
|
403
|
+
logger.info("[qq-bot] hideUnsupported: 消息内容转换后为空,跳过发送");
|
|
404
|
+
return [];
|
|
405
|
+
}
|
|
406
|
+
if (mdToText) {
|
|
407
|
+
const textContent = flattenMdToText(content, val);
|
|
408
|
+
if (textContent) {
|
|
409
|
+
const res = await sendMessage({
|
|
410
|
+
content: textContent,
|
|
411
|
+
msg_type: 0,
|
|
412
|
+
...baseParams
|
|
413
|
+
});
|
|
414
|
+
return [createResult(ResultCode.Ok, label, { id: res.id })];
|
|
415
|
+
}
|
|
416
|
+
return [];
|
|
417
|
+
}
|
|
418
|
+
const mdParams = buildMdAndButtonsParams(val);
|
|
419
|
+
if (mdParams) {
|
|
420
|
+
if (mdParams.markdown?.content && content) mdParams.markdown.content = content + "\n" + mdParams.markdown.content;
|
|
421
|
+
const res = await sendMessage({
|
|
422
|
+
content,
|
|
423
|
+
msg_type: 2,
|
|
424
|
+
...mdParams,
|
|
425
|
+
...baseParams,
|
|
426
|
+
force_verify_image_resource: options?.forceVerifyImageResource
|
|
427
|
+
});
|
|
428
|
+
return [createResult(ResultCode.Ok, label, { id: res.id })];
|
|
429
|
+
}
|
|
430
|
+
const arkParams = buildArkParams(val);
|
|
431
|
+
if (arkParams) {
|
|
432
|
+
const res = await sendMessage({
|
|
433
|
+
content,
|
|
434
|
+
msg_type: 3,
|
|
435
|
+
...arkParams,
|
|
436
|
+
...baseParams
|
|
437
|
+
});
|
|
438
|
+
return [createResult(ResultCode.Ok, label, { id: res.id })];
|
|
439
|
+
}
|
|
440
|
+
if (content) {
|
|
441
|
+
const res = await sendMessage({
|
|
442
|
+
content,
|
|
443
|
+
msg_type: 0,
|
|
444
|
+
...baseParams
|
|
445
|
+
});
|
|
446
|
+
return [createResult(ResultCode.Ok, label, { id: res.id })];
|
|
447
|
+
}
|
|
448
|
+
return [];
|
|
487
449
|
};
|
|
450
|
+
/** 将图片数据解析为 Buffer */
|
|
488
451
|
const resolveImageBuffer = async (images) => {
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
return Buffer.from(item.value.replace('base64://', ''), 'base64');
|
|
502
|
-
}
|
|
503
|
-
if (item.value.startsWith('file://')) {
|
|
504
|
-
return readFileSync(item.value.replace('file://', ''));
|
|
505
|
-
}
|
|
506
|
-
return Buffer.from(item.value, 'base64');
|
|
507
|
-
}
|
|
508
|
-
if (Buffer.isBuffer(item.value)) {
|
|
509
|
-
return item.value;
|
|
510
|
-
}
|
|
511
|
-
}
|
|
512
|
-
return null;
|
|
452
|
+
for (const item of images) {
|
|
453
|
+
if (item.type === "ImageURL") return await axios.get(item.value, { responseType: "arraybuffer" }).then((res) => res?.data);
|
|
454
|
+
if (item.type === "ImageFile") return readFileSync(item.value);
|
|
455
|
+
if (typeof item.value === "string") {
|
|
456
|
+
if (item.value.startsWith("https://") || item.value.startsWith("http://")) return await axios.get(item.value, { responseType: "arraybuffer" }).then((res) => res?.data);
|
|
457
|
+
if (item.value.startsWith("base64://")) return Buffer.from(item.value.replace("base64://", ""), "base64");
|
|
458
|
+
if (item.value.startsWith("file://")) return readFileSync(item.value.replace("file://", ""));
|
|
459
|
+
return Buffer.from(item.value, "base64");
|
|
460
|
+
}
|
|
461
|
+
if (Buffer.isBuffer(item.value)) return item.value;
|
|
462
|
+
}
|
|
463
|
+
return null;
|
|
513
464
|
};
|
|
465
|
+
/** Guild API 通用发送逻辑(频道公聊 / 频道私聊) */
|
|
514
466
|
const sendGuildMessage = async (content, val, baseParams, sendMessage, label) => {
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
467
|
+
const config = getQQBotConfig();
|
|
468
|
+
const mdToText = config.markdownToText === true;
|
|
469
|
+
const images = filterImages(val);
|
|
470
|
+
if (images.length > 0) {
|
|
471
|
+
const imageBuffer = await resolveImageBuffer(images);
|
|
472
|
+
const res = await sendMessage({
|
|
473
|
+
content: flattenMdToText(content, val),
|
|
474
|
+
...baseParams
|
|
475
|
+
}, imageBuffer);
|
|
476
|
+
return [createResult(ResultCode.Ok, label, { id: res?.id })];
|
|
477
|
+
}
|
|
478
|
+
if (config.hideUnsupported && !content && !buildMdAndButtonsParams(val) && !buildArkParams(val)) {
|
|
479
|
+
logger.info("[qq-bot] hideUnsupported: 消息内容转换后为空,跳过发送");
|
|
480
|
+
return [];
|
|
481
|
+
}
|
|
482
|
+
if (mdToText) {
|
|
483
|
+
const textContent = flattenMdToText(content, val);
|
|
484
|
+
if (textContent) {
|
|
485
|
+
const res = await sendMessage({
|
|
486
|
+
content: textContent,
|
|
487
|
+
...baseParams
|
|
488
|
+
});
|
|
489
|
+
return [createResult(ResultCode.Ok, label, { id: res?.id })];
|
|
490
|
+
}
|
|
491
|
+
return [];
|
|
492
|
+
}
|
|
493
|
+
const mdParams = buildMdAndButtonsParams(val);
|
|
494
|
+
if (mdParams) {
|
|
495
|
+
if (mdParams.markdown?.content && content) mdParams.markdown.content = content + "\n" + mdParams.markdown.content;
|
|
496
|
+
const res = await sendMessage({
|
|
497
|
+
content: "",
|
|
498
|
+
...mdParams,
|
|
499
|
+
...baseParams
|
|
500
|
+
});
|
|
501
|
+
return [createResult(ResultCode.Ok, label, { id: res.id })];
|
|
502
|
+
}
|
|
503
|
+
const arkParams = buildArkParams(val);
|
|
504
|
+
if (arkParams) {
|
|
505
|
+
const res = await sendMessage({
|
|
506
|
+
content,
|
|
507
|
+
...arkParams,
|
|
508
|
+
...baseParams
|
|
509
|
+
});
|
|
510
|
+
return [createResult(ResultCode.Ok, label, { id: res.id })];
|
|
511
|
+
}
|
|
512
|
+
if (content) {
|
|
513
|
+
const res = await sendMessage({
|
|
514
|
+
content,
|
|
515
|
+
...baseParams
|
|
516
|
+
});
|
|
517
|
+
return [createResult(ResultCode.Ok, label, { id: res?.id })];
|
|
518
|
+
}
|
|
519
|
+
return [];
|
|
554
520
|
};
|
|
521
|
+
/**
|
|
522
|
+
* 群组消息
|
|
523
|
+
*/
|
|
555
524
|
const GROUP_AT_MESSAGE_CREATE = async (client, event, val, options) => {
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
}
|
|
525
|
+
const baseParams = buildBaseParams(event._tag, event.MessageId, "INTERACTION_CREATE_GROUP");
|
|
526
|
+
const content = extractContent(val, "group");
|
|
527
|
+
try {
|
|
528
|
+
return await sendOpenApiMessage(content, val, baseParams, (data) => client.postRichMediaByGroup(event.ChannelId, data), (data) => client.groupOpenMessages(event.ChannelId, data), "client.groupOpenMessages", options);
|
|
529
|
+
} catch (err) {
|
|
530
|
+
return [createResult(ResultCode.Fail, err?.response?.data ?? err?.message ?? err, null)];
|
|
531
|
+
}
|
|
564
532
|
};
|
|
533
|
+
/**
|
|
534
|
+
* 私聊消息
|
|
535
|
+
*/
|
|
565
536
|
const C2C_MESSAGE_CREATE = async (client, event, val, options) => {
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
}
|
|
537
|
+
const baseParams = buildBaseParams(event._tag, event.MessageId, "INTERACTION_CREATE_C2C");
|
|
538
|
+
const content = extractContent(val, "group");
|
|
539
|
+
try {
|
|
540
|
+
return await sendOpenApiMessage(content, val, baseParams, (data) => client.postRichMediaByUser(event.UserId, data), (data) => client.usersOpenMessages(event.UserId, data), "client.usersOpenMessages", options);
|
|
541
|
+
} catch (err) {
|
|
542
|
+
return [createResult(ResultCode.Fail, err?.response?.data ?? err?.message ?? err, null)];
|
|
543
|
+
}
|
|
574
544
|
};
|
|
545
|
+
/**
|
|
546
|
+
* 频道私聊
|
|
547
|
+
*/
|
|
575
548
|
const DIRECT_MESSAGE_CREATE = async (client, event, val) => {
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
}
|
|
549
|
+
const baseParams = buildBaseParams(event._tag, event.MessageId, "INTERACTION_CREATE_GUILD");
|
|
550
|
+
const content = extractContent(val, "guild-direct");
|
|
551
|
+
try {
|
|
552
|
+
return await sendGuildMessage(content, val, baseParams, (data, buf) => client.dmsMessages(event.UserId, data, buf), "client.dmsMessage");
|
|
553
|
+
} catch (err) {
|
|
554
|
+
return [createResult(ResultCode.Fail, err?.response?.data ?? err?.message ?? err, null)];
|
|
555
|
+
}
|
|
584
556
|
};
|
|
557
|
+
/**
|
|
558
|
+
* 频道公聊
|
|
559
|
+
*/
|
|
585
560
|
const MESSAGE_CREATE = async (client, event, val) => {
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
}
|
|
561
|
+
const baseParams = buildBaseParams(event._tag, event.MessageId, "INTERACTION_CREATE_GUILD");
|
|
562
|
+
const content = extractContent(val, "guild-public");
|
|
563
|
+
try {
|
|
564
|
+
return await sendGuildMessage(content, val, baseParams, (data, buf) => client.channelsMessages(event.ChannelId, data, buf), "client.channelsMessagesPost");
|
|
565
|
+
} catch (err) {
|
|
566
|
+
return [createResult(ResultCode.Fail, err?.response?.data ?? err?.message ?? err, null)];
|
|
567
|
+
}
|
|
594
568
|
};
|
|
569
|
+
/**
|
|
570
|
+
* 频道公聊 @
|
|
571
|
+
*/
|
|
595
572
|
const AT_MESSAGE_CREATE = (client, event, val) => {
|
|
596
|
-
|
|
573
|
+
return MESSAGE_CREATE(client, event, val);
|
|
597
574
|
};
|
|
598
575
|
|
|
599
|
-
|
|
576
|
+
//#endregion
|
|
577
|
+
export { AT_MESSAGE_CREATE, C2C_MESSAGE_CREATE, DIRECT_MESSAGE_CREATE, GROUP_AT_MESSAGE_CREATE, MESSAGE_CREATE };
|