@tencent-connect/openclaw-qqbot 1.5.7 → 1.6.0-alpha.2
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 +9 -2
- package/README.zh.md +7 -2
- package/package.json +1 -1
- package/scripts/upgrade-via-npm.sh +85 -115
- package/scripts/upgrade-via-source.sh +203 -35
- package/skills/qqbot-cron/SKILL.md +46 -423
- package/skills/qqbot-media/SKILL.md +29 -182
- package/src/api.ts +16 -5
- package/src/channel.ts +6 -7
- package/src/gateway.ts +510 -525
- package/src/image-server.ts +72 -10
- package/src/openclaw-plugin-sdk.d.ts +1 -1
- package/src/outbound.ts +571 -611
- package/src/ref-index-store.ts +1 -1
- package/src/slash-commands.ts +425 -0
- package/src/types.ts +18 -1
- package/src/update-checker.ts +102 -0
- package/src/user-messages.ts +73 -0
- package/src/utils/audio-convert.ts +69 -4
- package/src/utils/media-tags.ts +46 -4
- package/dist/AI/345/210/233/346/226/260/345/272/224/347/224/250/345/245/226_/347/224/263/346/212/245/344/271/246.md +0 -211
- package/dist/index.d.ts +0 -17
- package/dist/index.js +0 -22
- package/dist/src/api.d.ts +0 -138
- package/dist/src/api.js +0 -525
- package/dist/src/channel.d.ts +0 -3
- package/dist/src/channel.js +0 -337
- package/dist/src/config.d.ts +0 -25
- package/dist/src/config.js +0 -161
- package/dist/src/gateway.d.ts +0 -18
- package/dist/src/gateway.js +0 -2468
- package/dist/src/image-server.d.ts +0 -62
- package/dist/src/image-server.js +0 -401
- package/dist/src/known-users.d.ts +0 -100
- package/dist/src/known-users.js +0 -263
- package/dist/src/onboarding.d.ts +0 -10
- package/dist/src/onboarding.js +0 -203
- package/dist/src/outbound.d.ts +0 -150
- package/dist/src/outbound.js +0 -1175
- package/dist/src/proactive.d.ts +0 -170
- package/dist/src/proactive.js +0 -399
- package/dist/src/runtime.d.ts +0 -3
- package/dist/src/runtime.js +0 -10
- package/dist/src/session-store.d.ts +0 -52
- package/dist/src/session-store.js +0 -254
- package/dist/src/slash-commands.d.ts +0 -48
- package/dist/src/slash-commands.js +0 -212
- package/dist/src/types.d.ts +0 -146
- package/dist/src/types.js +0 -1
- package/dist/src/utils/audio-convert.d.ts +0 -73
- package/dist/src/utils/audio-convert.js +0 -645
- package/dist/src/utils/file-utils.d.ts +0 -46
- package/dist/src/utils/file-utils.js +0 -107
- package/dist/src/utils/image-size.d.ts +0 -51
- package/dist/src/utils/image-size.js +0 -234
- package/dist/src/utils/media-tags.d.ts +0 -14
- package/dist/src/utils/media-tags.js +0 -120
- package/dist/src/utils/payload.d.ts +0 -112
- package/dist/src/utils/payload.js +0 -186
- package/dist/src/utils/platform.d.ts +0 -126
- package/dist/src/utils/platform.js +0 -358
- package/dist/src/utils/upload-cache.d.ts +0 -34
- package/dist/src/utils/upload-cache.js +0 -93
package/src/image-server.ts
CHANGED
|
@@ -418,54 +418,116 @@ export async function ensureImageServer(publicBaseUrl?: string): Promise<string
|
|
|
418
418
|
* @param url 远程文件 URL
|
|
419
419
|
* @param destDir 目标目录
|
|
420
420
|
* @param originalFilename 原始文件名(可选,完整文件名包含扩展名)
|
|
421
|
+
* @param options 下载选项
|
|
421
422
|
* @returns 本地文件路径,失败返回 null
|
|
422
423
|
*/
|
|
423
424
|
export async function downloadFile(
|
|
424
425
|
url: string,
|
|
425
426
|
destDir: string,
|
|
426
|
-
originalFilename?: string
|
|
427
|
+
originalFilename?: string,
|
|
428
|
+
options?: {
|
|
429
|
+
/** 超时时间(毫秒),默认 120000(2分钟) */
|
|
430
|
+
timeoutMs?: number;
|
|
431
|
+
/** 最大文件大小(字节),默认 50MB */
|
|
432
|
+
maxSizeBytes?: number;
|
|
433
|
+
},
|
|
427
434
|
): Promise<string | null> {
|
|
435
|
+
const timeoutMs = options?.timeoutMs ?? 120000;
|
|
436
|
+
const maxSizeBytes = options?.maxSizeBytes ?? 50 * 1024 * 1024;
|
|
437
|
+
|
|
438
|
+
const controller = new AbortController();
|
|
439
|
+
const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
|
|
440
|
+
|
|
428
441
|
try {
|
|
429
442
|
// 确保目录存在
|
|
430
443
|
if (!fs.existsSync(destDir)) {
|
|
431
444
|
fs.mkdirSync(destDir, { recursive: true });
|
|
432
445
|
}
|
|
433
446
|
|
|
434
|
-
//
|
|
435
|
-
const response = await fetch(url);
|
|
447
|
+
// 下载文件(带超时控制)
|
|
448
|
+
const response = await fetch(url, { signal: controller.signal });
|
|
436
449
|
if (!response.ok) {
|
|
437
450
|
console.error(`[image-server] Download failed: ${response.status} ${response.statusText}`);
|
|
438
451
|
return null;
|
|
439
452
|
}
|
|
440
453
|
|
|
441
|
-
|
|
454
|
+
// Content-Length 预检
|
|
455
|
+
const contentLength = response.headers.get("content-length");
|
|
456
|
+
if (contentLength) {
|
|
457
|
+
const declaredSize = parseInt(contentLength, 10);
|
|
458
|
+
if (declaredSize > maxSizeBytes) {
|
|
459
|
+
console.error(`[image-server] Download rejected: Content-Length ${declaredSize} exceeds limit ${maxSizeBytes}`);
|
|
460
|
+
return null;
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
// 流式下载,实时监控大小
|
|
465
|
+
const reader = response.body?.getReader();
|
|
466
|
+
if (!reader) {
|
|
467
|
+
console.error(`[image-server] Download failed: no response body`);
|
|
468
|
+
return null;
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
const chunks: Uint8Array[] = [];
|
|
472
|
+
let totalSize = 0;
|
|
473
|
+
|
|
474
|
+
while (true) {
|
|
475
|
+
const { done, value } = await reader.read();
|
|
476
|
+
if (done) break;
|
|
477
|
+
totalSize += value.byteLength;
|
|
478
|
+
if (totalSize > maxSizeBytes) {
|
|
479
|
+
reader.cancel();
|
|
480
|
+
console.error(`[image-server] Download aborted: size ${totalSize} exceeds limit ${maxSizeBytes}`);
|
|
481
|
+
return null;
|
|
482
|
+
}
|
|
483
|
+
chunks.push(value);
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
const buffer = Buffer.concat(chunks);
|
|
487
|
+
|
|
488
|
+
// 从 Content-Disposition 解析文件名(如果没有提供 originalFilename)
|
|
489
|
+
if (!originalFilename) {
|
|
490
|
+
const disposition = response.headers.get("content-disposition");
|
|
491
|
+
if (disposition) {
|
|
492
|
+
const filenameMatch = disposition.match(/filename\*?=(?:UTF-8''|")?([^";]+)"?/i);
|
|
493
|
+
if (filenameMatch?.[1]) {
|
|
494
|
+
try { originalFilename = decodeURIComponent(filenameMatch[1]); } catch { /* keep undefined */ }
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
}
|
|
442
498
|
|
|
443
499
|
// 确定文件名
|
|
444
500
|
let finalFilename: string;
|
|
445
501
|
if (originalFilename) {
|
|
446
|
-
// QQ 平台返回的 filename 可能是 URL 编码的(如 %E7%AC%94%E5%A2%A8...),先解码
|
|
447
502
|
let decodedFilename = originalFilename;
|
|
448
503
|
try { decodedFilename = decodeURIComponent(originalFilename); } catch { /* keep original */ }
|
|
449
|
-
// 使用原始文件名,但添加时间戳避免冲突
|
|
450
504
|
const ext = path.extname(decodedFilename);
|
|
451
505
|
const baseName = path.basename(decodedFilename, ext);
|
|
452
506
|
const timestamp = Date.now();
|
|
453
507
|
finalFilename = `${baseName}_${timestamp}${ext}`;
|
|
454
508
|
} else {
|
|
455
|
-
//
|
|
456
|
-
|
|
509
|
+
// 没有原始文件名,尝试从 Content-Type 推导扩展名
|
|
510
|
+
const contentType = response.headers.get("content-type");
|
|
511
|
+
const ext = contentType ? (getExtFromMime(contentType.split(";")[0]?.trim() ?? "") ?? "bin") : "bin";
|
|
512
|
+
finalFilename = `${generateImageId()}.${ext}`;
|
|
457
513
|
}
|
|
458
514
|
|
|
459
515
|
const filePath = path.join(destDir, finalFilename);
|
|
460
516
|
|
|
461
517
|
// 保存文件
|
|
462
518
|
fs.writeFileSync(filePath, buffer);
|
|
463
|
-
console.log(`[image-server] Downloaded file: ${filePath}`);
|
|
519
|
+
console.log(`[image-server] Downloaded file: ${filePath} (${buffer.length} bytes, ${Date.now()}ms)`);
|
|
464
520
|
|
|
465
521
|
return filePath;
|
|
466
522
|
} catch (err) {
|
|
467
|
-
|
|
523
|
+
if (err instanceof Error && err.name === "AbortError") {
|
|
524
|
+
console.error(`[image-server] Download timeout after ${timeoutMs}ms: ${url}`);
|
|
525
|
+
} else {
|
|
526
|
+
console.error(`[image-server] Download error:`, err);
|
|
527
|
+
}
|
|
468
528
|
return null;
|
|
529
|
+
} finally {
|
|
530
|
+
clearTimeout(timeoutId);
|
|
469
531
|
}
|
|
470
532
|
}
|
|
471
533
|
|
|
@@ -215,7 +215,7 @@ declare module "openclaw/plugin-sdk" {
|
|
|
215
215
|
* 频道插件 Messaging 接口
|
|
216
216
|
*/
|
|
217
217
|
export interface ChannelPluginMessaging {
|
|
218
|
-
normalizeTarget?: (target: string) =>
|
|
218
|
+
normalizeTarget?: (target: string) => string | undefined;
|
|
219
219
|
targetResolver?: TargetResolver;
|
|
220
220
|
[key: string]: unknown;
|
|
221
221
|
}
|