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