@alemonjs/qq-bot 2.1.30 → 2.1.32
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 +14 -0
- package/dist/assets/index.css +1 -1
- package/dist/assets/index.js +3 -3
- package/dist/index.html +1 -1
- package/lib/config.d.ts +8 -0
- package/lib/config.js +23 -1
- package/lib/desktop.js +8 -1
- package/lib/index.js +5 -8
- package/lib/index.webhook.js +3 -0
- package/lib/index.websoket.d.ts +2 -0
- package/lib/index.websoket.js +18 -16
- package/lib/message/group/C2C_MESSAGE_CREATE.d.ts +7 -0
- package/lib/message/group/GROUP_AT_MESSAGE_CREATE.d.ts +7 -0
- package/lib/message/group/GROUP_MESSAGE_CREATE.d.ts +7 -0
- package/lib/register.d.ts +10 -1
- package/lib/register.js +260 -9
- package/lib/sdk/api.d.ts +58 -0
- package/lib/sdk/api.js +252 -14
- package/lib/sdk/client.webhook.js +14 -14
- package/lib/sdk/client.websoket.d.ts +2 -1
- package/lib/sdk/client.websoket.js +321 -110
- package/lib/sdk/config.d.ts +3 -1
- package/lib/sdk/config.js +6 -2
- package/lib/sdk/registry.d.ts +25 -0
- package/lib/sdk/registry.js +116 -0
- package/lib/sdk/session.d.ts +18 -0
- package/lib/sdk/session.js +56 -0
- package/lib/sdk/typing.d.ts +2 -0
- package/lib/sends.js +25 -4
- package/package.json +5 -3
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { readFile, mkdir, writeFile, rename, unlink } from 'node:fs/promises';
|
|
2
|
+
import { randomUUID } from 'node:crypto';
|
|
3
|
+
import { join, dirname } from 'node:path';
|
|
4
|
+
|
|
5
|
+
class FileSessionStore {
|
|
6
|
+
root;
|
|
7
|
+
#queues = new Map();
|
|
8
|
+
#generations = new Map();
|
|
9
|
+
constructor(root = join(process.cwd(), '.data', 'qq-bot', 'sessions')) {
|
|
10
|
+
this.root = root;
|
|
11
|
+
}
|
|
12
|
+
#path(botId) {
|
|
13
|
+
return join(this.root, `${encodeURIComponent(botId)}.json`);
|
|
14
|
+
}
|
|
15
|
+
async load(botId) {
|
|
16
|
+
try {
|
|
17
|
+
const value = JSON.parse(await readFile(this.#path(botId), 'utf8'));
|
|
18
|
+
return value?.botId === botId && typeof value.sessionId === 'string' && Number.isFinite(value.sequence) ? value : null;
|
|
19
|
+
}
|
|
20
|
+
catch {
|
|
21
|
+
return null;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
async save(session) {
|
|
25
|
+
const generation = this.#generations.get(session.botId) || 0;
|
|
26
|
+
await this.#enqueue(session.botId, async () => {
|
|
27
|
+
if (generation !== (this.#generations.get(session.botId) || 0))
|
|
28
|
+
return;
|
|
29
|
+
const path = this.#path(session.botId);
|
|
30
|
+
const temp = `${path}.${process.pid}.${randomUUID()}.tmp`;
|
|
31
|
+
await mkdir(dirname(path), { recursive: true });
|
|
32
|
+
await writeFile(temp, JSON.stringify(session), 'utf8');
|
|
33
|
+
await rename(temp, path);
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
async clear(botId) {
|
|
37
|
+
this.#generations.set(botId, (this.#generations.get(botId) || 0) + 1);
|
|
38
|
+
await this.#enqueue(botId, async () => {
|
|
39
|
+
await unlink(this.#path(botId)).catch(() => undefined);
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
#enqueue(botId, task) {
|
|
43
|
+
const previous = this.#queues.get(botId) || Promise.resolve();
|
|
44
|
+
const next = previous.catch(() => undefined).then(task);
|
|
45
|
+
this.#queues.set(botId, next);
|
|
46
|
+
void next
|
|
47
|
+
.finally(() => {
|
|
48
|
+
if (this.#queues.get(botId) === next)
|
|
49
|
+
this.#queues.delete(botId);
|
|
50
|
+
})
|
|
51
|
+
.catch(() => undefined);
|
|
52
|
+
return next;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export { FileSessionStore };
|
package/lib/sdk/typing.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { IntentsEnum } from './intents';
|
|
2
|
+
import type { SessionStore } from './session.js';
|
|
2
3
|
export type MessageType = 0 | 1 | 2 | 3 | 4 | 7;
|
|
3
4
|
export type FileType = 1 | 2 | 3 | 4;
|
|
4
5
|
export interface ButtonType {
|
|
@@ -68,4 +69,5 @@ export interface Options {
|
|
|
68
69
|
gatewayURL?: string;
|
|
69
70
|
base_url_gateway?: string;
|
|
70
71
|
base_url_app_access_token?: string;
|
|
72
|
+
sessionStore?: SessionStore;
|
|
71
73
|
}
|
package/lib/sends.js
CHANGED
|
@@ -188,6 +188,9 @@ const extractContent = (val, mode) => {
|
|
|
188
188
|
'Image',
|
|
189
189
|
'ImageFile',
|
|
190
190
|
'ImageURL',
|
|
191
|
+
'Audio',
|
|
192
|
+
'Video',
|
|
193
|
+
'Attachment',
|
|
191
194
|
'Markdown',
|
|
192
195
|
'MarkdownOriginal',
|
|
193
196
|
'BT.group',
|
|
@@ -301,14 +304,15 @@ const buildArkParams = (val) => {
|
|
|
301
304
|
const filterImages = (val) => {
|
|
302
305
|
return val.filter(item => item.type === 'Image' || item.type === 'ImageFile' || item.type === 'ImageURL');
|
|
303
306
|
};
|
|
304
|
-
const resolveRichMediaUrl = async (
|
|
305
|
-
for (const item of
|
|
307
|
+
const resolveRichMediaUrl = async (items, uploadMedia) => {
|
|
308
|
+
for (const item of items) {
|
|
306
309
|
let fileData;
|
|
307
310
|
let fileInfo;
|
|
311
|
+
const fileType = item.type === 'Video' ? 2 : item.type === 'Audio' ? 3 : item.type === 'Attachment' ? 4 : 1;
|
|
308
312
|
if (item.type === 'ImageURL') {
|
|
309
313
|
fileData = await axios.get(item.value, { responseType: 'arraybuffer' }).then(res => Buffer.from(res.data, 'binary').toString('base64'));
|
|
310
314
|
}
|
|
311
|
-
else if (item.type === 'Image') {
|
|
315
|
+
else if (item.type === 'Image' || item.type === 'Audio' || item.type === 'Video' || item.type === 'Attachment') {
|
|
312
316
|
if (typeof item.value === 'string' && (item.value.startsWith('https://') || item.value.startsWith('http://'))) {
|
|
313
317
|
fileData = await axios.get(item.value, { responseType: 'arraybuffer' }).then(res => Buffer.from(res.data, 'binary').toString('base64'));
|
|
314
318
|
}
|
|
@@ -322,6 +326,9 @@ const resolveRichMediaUrl = async (images, uploadMedia) => {
|
|
|
322
326
|
else if (Buffer.isBuffer(item.value)) {
|
|
323
327
|
fileData = item.value.toString('base64');
|
|
324
328
|
}
|
|
329
|
+
else if (typeof item.value === 'string' && item.value.startsWith('buffer://')) {
|
|
330
|
+
fileData = item.value.replace('buffer://', '');
|
|
331
|
+
}
|
|
325
332
|
else if (typeof item.value === 'string') {
|
|
326
333
|
fileData = item.value;
|
|
327
334
|
}
|
|
@@ -330,7 +337,7 @@ const resolveRichMediaUrl = async (images, uploadMedia) => {
|
|
|
330
337
|
fileData = readFileSync(item.value, 'base64');
|
|
331
338
|
}
|
|
332
339
|
if (fileData) {
|
|
333
|
-
fileInfo = await uploadMedia({ file_type:
|
|
340
|
+
fileInfo = await uploadMedia({ file_type: fileType, file_data: fileData }).then(res => res?.file_info);
|
|
334
341
|
}
|
|
335
342
|
if (fileInfo) {
|
|
336
343
|
return fileInfo;
|
|
@@ -361,6 +368,7 @@ const sendOpenApiMessage = async (content, val, baseParams, uploadMedia, sendMes
|
|
|
361
368
|
const config = getQQBotConfig();
|
|
362
369
|
const mdToText = config.markdownToText === true;
|
|
363
370
|
const images = filterImages(val);
|
|
371
|
+
const richMedia = val.filter(item => item.type === 'Audio' || item.type === 'Video' || item.type === 'Attachment');
|
|
364
372
|
if (images.length > 0) {
|
|
365
373
|
const url = await resolveRichMediaUrl(images, uploadMedia);
|
|
366
374
|
if (!url) {
|
|
@@ -375,6 +383,19 @@ const sendOpenApiMessage = async (content, val, baseParams, uploadMedia, sendMes
|
|
|
375
383
|
});
|
|
376
384
|
return [createResult(ResultCode.Ok, label, { id: res.id })];
|
|
377
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
|
+
}
|
|
378
399
|
if (config.hideUnsupported === true && !content && !buildMdAndButtonsParams(val) && !buildArkParams(val)) {
|
|
379
400
|
logger.info('[qq-bot] hideUnsupported: 消息内容转换后为空,跳过发送');
|
|
380
401
|
return [];
|
package/package.json
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@alemonjs/qq-bot",
|
|
3
|
-
"version": "2.1.
|
|
3
|
+
"version": "2.1.32",
|
|
4
4
|
"description": "阿柠檬qq-bot平台连接",
|
|
5
5
|
"author": "lemonade",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"type": "module",
|
|
8
8
|
"main": "lib/index.js",
|
|
9
9
|
"scripts": {
|
|
10
|
-
"build": "lvy build"
|
|
10
|
+
"build": "lvy build",
|
|
11
|
+
"test:ws": "yarn build && node tests/websocket.integration.mjs"
|
|
11
12
|
},
|
|
12
13
|
"dependencies": {
|
|
13
14
|
"@noble/curves": "^1.7.0",
|
|
@@ -22,6 +23,7 @@
|
|
|
22
23
|
"mime-types": "^2.1.35",
|
|
23
24
|
"public-ip": "^6.0.1",
|
|
24
25
|
"qrcode": "^1.5.3",
|
|
26
|
+
"uuid": "^14.0.1",
|
|
25
27
|
"ws": "^8.13.0"
|
|
26
28
|
},
|
|
27
29
|
"peerDependencies": {
|
|
@@ -92,5 +94,5 @@
|
|
|
92
94
|
"type": "git",
|
|
93
95
|
"url": "https://github.com/lemonade-lab/alemonjs.git"
|
|
94
96
|
},
|
|
95
|
-
"gitHead": "
|
|
97
|
+
"gitHead": "ebea3f64e13a92c671a0ecd8af856c17ca44846d"
|
|
96
98
|
}
|