"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(/
\s*([\s\S]*?)<\/table>\s*<\/div>/g) || []; resultBlocks.forEach((block) => { const result = { similarity: '', title: '', thumbnailUrl: '', sourceUrl: '', creator: '', creatorUrl: '', source: '', id: '', }; const similarityMatch = block.match(/
(\d+\.\d+)%<\/div>/); if (similarityMatch) { result.similarity = similarityMatch[1]; } const titleMatch = block.match(/
(.*?)<\/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(/(?:pixiv ID|dA ID):\s*<\/strong>]*>(\d+)<\/a>/); if (sourceMatch) { result.sourceUrl = sourceMatch[1]; result.id = sourceMatch[2]; } const creatorMatch = block.match(/(?:Member|Author):\s*<\/strong>]*>(.*?)<\/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;