koishi-plugin-nitter-rss
Version:
订阅 X (Twitter) 内容,使用 nitter.cz,支持ChatGPT与Gradio Chatbot翻译
202 lines (201 loc) • 7.98 kB
JavaScript
;
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.parseLinkInfo = void 0;
const koishi_1 = require("koishi");
const puppeteer_1 = require("./puppeteer");
const GradioChatBot_1 = require("./translate/GradioChatBot");
const ChatGPT_1 = require("./translate/ChatGPT");
const text2image_1 = require("./text2image");
const fs = __importStar(require("fs"));
const logger = new koishi_1.Logger('nitter-rss-parseLinkInfo');
async function parseLinkInfo(ctx, parsedTwitterLink, config, translate, forceTranslate = false) {
let finalText = '';
let content;
// 获取推文内容
try {
content = await (0, puppeteer_1.capturehtml)(config.nitterUrl, ctx, parsedTwitterLink.account, parsedTwitterLink.id, config.screenshot, config.sendImage, 480);
finalText += content.fullname + '\n' + content.timeText;
}
catch (e) {
logger.error(e);
return ([`获取推文内容失败`]);
}
// 翻译
const translationPromise = new Promise(async (resolve, reject) => {
const translateTextPath = `./data/cache/nitter-rss/${parsedTwitterLink.account}/status/${parsedTwitterLink.id}_translate.txt`;
try {
let parsedText = '';
if (fs.existsSync(translateTextPath) && !forceTranslate) {
logger.info('使用翻译文本缓存');
parsedText = fs.readFileSync(translateTextPath, 'utf8'); // 确保正确读取文件内容
}
else if (config.translateType === 'gradio-chatbot') {
parsedText = await (0, GradioChatBot_1.GradioChatBotParse)(`${config.GradioChatBotPrompt}\n${content.extractedContent}`, config);
fs.writeFileSync(translateTextPath, parsedText);
}
// ChatGPT
else if (config.translateType === 'ChatGPT') {
parsedText = await (0, ChatGPT_1.ChatGPTParse)(`${config.ChatGPTPrompt}\n${content.extractedContent}`, config.ChatGPTKey, config.ChatGPTBaseUrl, config.ChatGPTModule);
fs.writeFileSync(translateTextPath, parsedText);
}
// 不翻译
else {
parsedText = content.extractedContent;
}
resolve(parsedText);
}
catch (e) {
reject(e);
}
});
// 定义一个超时标志
let isTimeout = false;
// 设置超时 Promise
const timeoutPromise = new Promise((resolve, reject) => {
setTimeout(() => {
isTimeout = true; // 设置超时标志
reject(new Error('翻译超时'));
}, config.translateTimeout * 1000); // 超时时间
});
//如果 content.extractedContent 只有空格和换行符,直接返回
if (content.extractedContent.replace(/\s/g, '') == '') {
finalText += `\n`;
}
else if (translate) {
try {
const parsedText = await Promise.race([translationPromise, timeoutPromise]);
if (!isTimeout) {
finalText += `\n翻译结果:\n${parsedText}`;
}
}
catch (e) {
if (!isTimeout) { // 如果不是因为超时导致的错误
logger.error(e);
finalText += `\n翻译失败:${e.message}\n原文:\n${content.extractedContent}`;
}
}
}
else {
finalText += `\n原文:\n${content.extractedContent}`;
}
let final = [];
if (config.text2image) {
const text2imagePath = `./data/cache/nitter-rss/${parsedTwitterLink.account}/status/${parsedTwitterLink.id}_translate.png`;
const ImageOptions = {
text: finalText,
width: 480,
backgroundColor: '#161616',
fontSize: 16,
font: `Helvetica, Arial, sans-serif`,
color: '#ffffff',
padding: 15,
};
// 如果已经有图片
let text2imageBuffer;
if (fs.existsSync(text2imagePath) && !forceTranslate) {
logger.info('使用翻译图片缓存');
text2imageBuffer = fs.readFileSync(text2imagePath);
}
else {
text2imageBuffer = fs.readFileSync(await (0, text2image_1.Text2Image)(ctx, ImageOptions, text2imagePath));
}
const concatImagesBuffer = await concatImages(ctx, [text2imageBuffer, content.screenshot]);
final = [koishi_1.h.image(concatImagesBuffer, 'image/png')];
}
else {
final = [finalText, koishi_1.h.image(content.screenshot, 'image/png')];
}
if (content.images.length > 0) {
for (let i = 0; i < content.images.length; i++) {
final.push(koishi_1.h.image(content.images[i], 'image/png'));
}
}
if (config.sendLink) {
final.push(`https://twitter.com/${parsedTwitterLink.account}/status/${parsedTwitterLink.id}`);
}
// 发送消息
logger.success(`处理完成${parsedTwitterLink.account}/status/${parsedTwitterLink.id}`);
return final;
}
exports.parseLinkInfo = parseLinkInfo;
const concatImages = async (ctx, imageBuffers) => {
const page = await ctx.puppeteer.page();
// 创建一个包含所有图片的HTML内容
const imagesHtml = imageBuffers.map((buffer, index) => {
const base64Image = buffer.toString('base64');
return `<img src="data:image/png;base64,${base64Image}" style="width: 100%; margin: 0; padding: 0; border: none; display: block;">`;
}).join('');
// 设置页面的HTML内容
await page.setContent(`
<style>
* {
margin: 0;
padding: 0;
}
body {
margin: 0;
padding: 0;
background: transparent;
width: 100vw;
}
</style>
<div style="width: 100%; background: transparent;">
${imagesHtml}
</div>
`, { waitUntil: 'load' });
// 等待所有图片加载完成
await page.evaluate(() => new Promise((resolve) => {
let images = document.querySelectorAll('img');
let loaded = images.length;
images.forEach((image) => {
if (image.complete) {
loaded--;
}
else {
image.addEventListener('load', () => {
loaded--;
if (loaded === 0)
resolve();
});
}
});
if (loaded === 0)
resolve();
}));
// 设置一个非常高的高度以确保所有内容被包括
await page.setViewport({
width: 600, // 假设所有图片都有600px宽
height: 10000, // 临时设置一个足够大的值
deviceScaleFactor: 1,
});
// 选择包含所有图片的div元素
const element = await page.$('div');
// 对选定的元素进行截图,Puppeteer将自动裁剪
const screenshotBuffer = await element.screenshot({
omitBackground: true // 确保背景透明
});
return screenshotBuffer;
};