Files
gxlydlyf e0ca337e16 修复图片无法下载(原因:在js文件中使用ts语法)
修复预览图无法显示(原因:在downloadPic使用了forEach,因为是异步的,所以导致thumbPath在没有被修改之前就发送到了前端)
2024-11-03 10:07:45 +08:00

146 lines
4.4 KiB
JavaScript

//大部分摘自LLOneBot
const { RkeyManager } = require("./rkeyManager.js");
const http = require("http");
const https = require("https");
const fs = require("fs");
const path = require("path");
const IMAGE_HTTP_HOST = "https://gchat.qpic.cn";
const IMAGE_HTTP_HOST_NT = "https://multimedia.nt.qq.com.cn";
class ImgDownloader {
constructor() {
this.rkeyManager = new RkeyManager("https://llob.linyuchen.net/rkey");
}
async getImageUrl(element) {
if (!element) {
return "";
}
const url = element.originImageUrl; // 没有域名
const md5HexStr = element.md5HexStr;
if (url) {
const parsedUrl = new URL(IMAGE_HTTP_HOST + url); //临时解析拼接
const imageAppid = parsedUrl.searchParams.get("appid");
const isNewPic = imageAppid && ["1406", "1407"].includes(imageAppid);
if (isNewPic) {
let rkey = parsedUrl.searchParams.get("rkey");
if (rkey) {
return IMAGE_HTTP_HOST_NT + url;
}
const rkeyData = await this.rkeyManager.getRkey();
rkey =
imageAppid === "1406" ? rkeyData.private_rkey : rkeyData.group_rkey;
return IMAGE_HTTP_HOST_NT + url + rkey;
} else {
// 老的图片url,不需要rkey
return IMAGE_HTTP_HOST + url;
}
} else if (md5HexStr) {
// 没有url,需要自己拼接
return `${IMAGE_HTTP_HOST}/gchatpic_new/0/0-0-${md5HexStr.toUpperCase()}/0`;
}
this.output("Pic url get error:", element);
return "";
}
// 下载被撤回的图片(抄自Lite-Tools)
async downloadPic(msgItem) {
if (!Array.isArray(msgItem?.elements)){
return;
}
for (let el of msgItem.elements){
if (el?.picElement) {
const pic = el.picElement;
const thumbMap = new Map([
[
0,
pic.sourcePath,
],
[
198,
pic.sourcePath,
],
[
720,
pic.sourcePath,
],
]);
const picUrl = await this.getImageUrl(el.picElement);
this.output(
"Download lost pic(s)... url=",
picUrl,
"msgId=",
msgItem.msgId,
"to=",
pic.sourcePath
);
let pictureRequireDownload = false;
try{
pictureRequireDownload = statSync(pic.sourcePath).size <= 100;//错误的图片
}catch (e) {
}
if (!fs.existsSync(pic.sourcePath) || pictureRequireDownload) {
this.output("Download pic:", `${picUrl}`, " to ", pic.sourcePath);
const body = await this.request(`${picUrl}`);
try {
JSON.parse(body);
this.output('Picture already expired.', picUrl, pic.sourcePath);//过期
} catch (e) {
fs.mkdirSync(path.dirname(pic.sourcePath), { recursive: true });
fs.writeFileSync(pic.sourcePath, body);
}
} else {
this.output("Pic already existed, skip.", pic.sourcePath);
}
//需要复制原图到预览图目录
// thumbMap.forEach(async (el, key) => {
// if (!fs.existsSync(el)) {
// this.output("Copy thumbs:", `source: ${pic.sourcePath} to ${el}`);
// fs.copyFile(pic.sourcePath, el);
// }
// });
// 修复本地数据中的错误
if (
pic?.thumbPath &&
(pic.thumbPath instanceof Array || pic.thumbPath instanceof Object)
) {
pic.thumbPath = thumbMap;
}
}
}
}
async request(url) {
return new Promise((resolve, reject) => {
const protocol = url.startsWith("https") ? https : http;
const req = protocol.get(url);
req.on("error", (error) => {
this.output("Download error", error);
reject(error);
});
req.on("response", (res) => {
// 发生跳转就继续请求
if (res.statusCode >= 300 && res.statusCode <= 399) {
return resolve(this.request(res.headers.location));
}
const chunks = [];
res.on("error", (error) => {
this.output("Download error", error);
reject(error);
});
res.on("data", (chunk) => chunks.push(chunk));
res.on("end", () => resolve(Buffer.concat(chunks)));
});
});
}
output(...args) {
console.log("\x1b[32m%s\x1b[0m", "Anti-Recall:", ...args);
}
}
module.exports.ImgDownloader = ImgDownloader;