rocket.chat.picsearcher/PicSearcherApp.js
2024-12-09 23:12:07 +08:00

234 lines
8.6 KiB
JavaScript

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.PicSearcherApp = void 0;
exports.processMessageImages = processMessageImages;
exports.getFileBlob = getFileBlob;
const App_1 = require("@rocket.chat/apps-engine/definition/App");
const defaultSauceNAOResponse = {
header: {
user_id: '',
account_type: '',
short_limit: '',
long_limit: '',
long_remaining: 0,
short_remaining: 0,
status: 0,
results_requested: 0,
index: {},
search_depth: '',
minimum_similarity: 0,
query_image_display: '',
query_image: '',
results_returned: 0,
},
results: [],
};
let myLogger;
class PicSearcherApp extends App_1.App {
constructor(info, logger, accessors) {
super(info, logger, accessors);
this.rootUrl = '';
myLogger = logger;
}
async initialize(configurationExtend, environmentRead) {
this.rootUrl = await environmentRead.getEnvironmentVariables().getValueByName('ROOT_URL');
myLogger.log('rootUrl:', this.rootUrl);
return super.initialize(configurationExtend, environmentRead);
}
checkPostMessageSent(message, read, http) {
return Promise.resolve(true);
}
async executePostMessageSent(message, read, http, persistence, modify) {
const author = await read.getUserReader().getAppUser();
if (!message.attachments || message.attachments.length <= 0) {
return Promise.resolve();
}
myLogger.info('message.attachments:', message.attachments);
const imageBlobs = await processMessageImages(message, http, read);
const result = await searchImageOnSauceNAO(imageBlobs[0].blob, imageBlobs[0].suffix);
await sendSearchResults(modify, message.room, result, author);
}
async sendMessage(textMessage, room, author, modify) {
const messageBuilder = modify.getCreator().startMessage({
text: textMessage,
});
messageBuilder.setSender(author);
messageBuilder.setRoom(room);
return modify.getCreator().finish(messageBuilder);
}
}
exports.PicSearcherApp = PicSearcherApp;
async function processMessageImages(message, http, context) {
const blobs = [];
if (!message.attachments || message.attachments.length === 0) {
throw new Error('No attachments found in the message.');
}
for (const attachment of message.attachments) {
if (attachment.imageUrl) {
try {
const fileId = attachment.imageUrl.split('/')[2];
const r = {
blob: await getFileBlob(fileId, context),
suffix: attachment.imageUrl.split('.').pop() || '',
};
blobs.push(r);
}
catch (error) {
throw new Error(`Error fetching image content: ${error.message}`);
}
}
}
return blobs;
}
async function getFileBlob(fileId, read) {
try {
const buffer = await read.getUploadReader().getBufferById(fileId);
return new Blob([buffer]);
}
catch (error) {
myLogger.error(`Error fetching file content: ${error.message}`);
}
return new Blob();
}
async function searchImageOnSauceNAO(image, suffix) {
const formData = new FormData();
formData.append('file', image, 'image.' + suffix);
try {
const response = await fetch('https://saucenao.com/search.php', {
method: 'POST',
body: formData,
});
if (response.ok) {
const results = [];
const html = await response.text();
myLogger.info('HTML:', html);
const resultBlocks = html.match(/<div class="result">\s*<table class="resulttable">([\s\S]*?)<\/table>\s*<\/div>/g) || [];
resultBlocks.forEach((block) => {
const result = {
similarity: '',
title: '',
thumbnailUrl: '',
sourceUrl: '',
creator: '',
creatorUrl: '',
source: '',
id: '',
};
const similarityMatch = block.match(/<div class="resultsimilarityinfo">(\d+\.\d+)%<\/div>/);
if (similarityMatch) {
result.similarity = similarityMatch[1];
}
const titleMatch = block.match(/<div class="resulttitle"><strong>(.*?)<\/strong>/);
if (titleMatch) {
result.title = titleMatch[1];
}
const thumbnailMatch = block.match(/src="(https:\/\/img\d\.saucenao\.com\/[^"]+)"/);
if (thumbnailMatch) {
result.thumbnailUrl = thumbnailMatch[1];
}
const sourceMatch = block.match(/<strong>(?:pixiv ID|dA ID):\s*<\/strong><a href="([^"]+)"[^>]*>(\d+)<\/a>/);
if (sourceMatch) {
result.sourceUrl = sourceMatch[1];
result.id = sourceMatch[2];
}
const creatorMatch = block.match(/<strong>(?:Member|Author):\s*<\/strong><a href="([^"]+)"[^>]*>(.*?)<\/a>/);
if (creatorMatch) {
result.creatorUrl = creatorMatch[1];
result.creator = creatorMatch[2];
}
if (block.includes('pixiv ID:')) {
result.source = 'Pixiv';
}
else if (block.includes('dA ID:')) {
result.source = 'DeviantArt';
}
else if (block.includes('E-Hentai')) {
result.source = 'E-Hentai';
}
results.push(result);
});
return results;
}
else {
console.error('请求失败:', response.status, response.statusText);
}
}
catch (error) {
console.error('搜索请求出错:', error);
}
return [];
}
function getInterpolatedColor(similarity) {
const clampedSimilarity = Math.max(80, Math.min(90, similarity));
const t = (clampedSimilarity - 80) / 10;
const red = Math.round(255 * (1 - t));
const green = Math.round(255 * t);
const redHex = red.toString(16).padStart(2, '0');
const greenHex = green.toString(16).padStart(2, '0');
return `#${redHex}${greenHex}00`;
}
async function sendSearchResults(modify, room, results, appUser) {
const attachments = results.map((result, index) => {
var _a, _b;
const similarityValue = parseFloat(result.similarity.replace('%', ''));
const color = getInterpolatedColor(similarityValue);
let textStr = result.source + ': ' + result.id;
if (result.sourceUrl) {
textStr += '\n' + result.sourceUrl;
}
const attachmentObject = {
title: {
value: `${result.title} (${result.similarity}%)`,
link: result.sourceUrl,
},
author: {
name: result.creator,
link: result.creatorUrl,
},
text: textStr,
thumbnailUrl: result.thumbnailUrl,
collapsed: index !== 0,
color,
};
if (!result.sourceUrl) {
(_a = attachmentObject.title) === null || _a === void 0 ? true : delete _a.link;
}
if (!result.creatorUrl) {
(_b = attachmentObject.author) === null || _b === void 0 ? true : delete _b.link;
}
if (!result.thumbnailUrl) {
delete attachmentObject.thumbnailUrl;
}
if (textStr === ': ') {
delete attachmentObject.text;
delete attachmentObject.collapsed;
}
return attachmentObject;
});
const text = results.length > 0 ? '以下是查找到的结果:' : '没有找到相关结果';
const messageBuilder = modify.getCreator().startMessage()
.setRoom(room)
.setUsernameAlias(appUser.username)
.setText(text)
.setAttachments(attachments);
await modify.getCreator().finish(messageBuilder);
}
async function urlToBlob(url) {
try {
const response = await fetch(url);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return await response.blob();
}
catch (error) {
throw new Error(`Failed to fetch image: ${error}`);
}
}
async function test() {
const blob = await urlToBlob('https://www.z4a.net/images/2024/12/09/69054578_p0.jpg');
const result = await searchImageOnSauceNAO(blob, 'jpg');
console.log(result);
}
exports.default = PicSearcherApp;