goat
command
Search Pinterest for images
0
views
0
likes
0
installs
Aliases: Pinterest, pin
raw source
const axios = require("axios");
const { createCanvas, loadImage } = require('canvas');
const fs = require('fs-extra');
const path = require('path');
const { getStreamFromURL } = global.utils;
async function generatePinterestCanvas(imageObjects, query, page, totalPages) {
const canvasWidth = 800;
const canvasHeight = 1600;
const canvas = createCanvas(canvasWidth, canvasHeight);
const ctx = canvas.getContext('2d');
ctx.fillStyle = '#1e1e1e';
ctx.fillRect(0, 0, canvasWidth, canvasHeight);
ctx.fillStyle = '#ffffff';
ctx.font = '24px Arial';
ctx.textAlign = 'left';
ctx.fillText('๐ Pinterest Searcher', 20, 45);
ctx.font = '16px Arial';
ctx.fillStyle = '#b0b0b0';
ctx.fillText(`Search results of "${query}", Showing up to ${imageObjects.length} images.`, 20, 75);
const numColumns = 3;
const padding = 15;
const columnWidth = (canvasWidth - (padding * (numColumns + 1))) / numColumns;
const columnHeights = Array(numColumns).fill(100);
const loadedPairs = await Promise.all(
imageObjects.map(obj =>
loadImage(obj.url)
.then(img => ({ img, originalIndex: obj.originalIndex, url: obj.url }))
.catch(e => {
console.error(`Failed to load image: ${obj.url}`, e && e.message);
return null;
})
)
);
const successful = loadedPairs.filter(x => x !== null);
if (successful.length === 0) {
ctx.fillStyle = '#ff6666';
ctx.font = '16px Arial';
ctx.fillText(`No images could be loaded for this page.`, 20, 110);
const outputPath = path.join(__dirname, 'cache', `pinterest_page_${Date.now()}.png`);
await fs.ensureDir(path.dirname(outputPath));
const buffer = canvas.toBuffer('image/png');
fs.writeFileSync(outputPath, buffer);
return { outputPath, displayedMap: [] };
}
let displayNumber = 0;
const displayedMap = [];
for (let i = 0; i < successful.length; i++) {
const { img, originalIndex } = successful[i];
const minHeight = Math.min(...columnHeights);
const columnIndex = columnHeights.indexOf(minHeight);
const x = padding + columnIndex * (columnWidth + padding);
const y = minHeight + padding;
const scale = columnWidth / img.width;
const scaledHeight = img.height * scale;
ctx.drawImage(img, x, y, columnWidth, scaledHeight);
displayNumber += 1;
displayedMap.push(originalIndex);
ctx.fillStyle = 'rgba(0, 0, 0, 0.5)';
ctx.fillRect(x, y, 50, 24);
ctx.fillStyle = '#ffffff';
ctx.font = 'bold 14px Arial';
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.fillText(`#${displayNumber}`, x + 25, y + 12);
ctx.fillStyle = '#b0b0b0';
ctx.font = '10px Arial';
ctx.textAlign = 'right';
ctx.textBaseline = 'alphabetic';
ctx.fillText(`${img.width} x ${img.height}`, x + columnWidth - 6, y + scaledHeight - 6);
columnHeights[columnIndex] += scaledHeight + padding;
}
ctx.fillStyle = '#ffffff';
ctx.font = 'bold 18px Arial';
ctx.textAlign = 'center';
const footerY = Math.max(...columnHeights) + 40;
ctx.fillText(`Anchestor - Page ${page}/${totalPages}`, canvasWidth / 2, footerY);
const outputPath = path.join(__dirname, 'cache', `pinterest_page_${Date.now()}.png`);
await fs.ensureDir(path.dirname(outputPath));
const buffer = canvas.toBuffer('image/png');
fs.writeFileSync(outputPath, buffer);
return { outputPath, displayedMap };
}
module.exports = {
config: {
name: "pinterest",
aliases: ["Pinterest", "pin"],
version: "2.2",
author: "Mahi--",
countDown: 10,
role: 0,
shortDescription: "Search Pinterest for images",
longDescription: "Search Pinterest for images, with canvas view for Browse.",
category: "Image",
guide: {
en: "{pn} query [-count]\n" +
"โข If count is used, it sends images directly.\n" +
"โข If no count, it shows an interactive canvas.\n" +
"โข Example: {pn} cute cat -5 (direct send)\n" +
"โข Example: {pn} anime wallpaper (canvas view)"
}
},
onStart: async function({ api, args, message, event }) {
let processingMessage = null;
try {
let count = null;
const countArg = args.find(arg => /^-\d+$/.test(arg));
if (countArg) {
count = parseInt(countArg.slice(1), 10);
args = args.filter(arg => arg !== countArg);
}
const query = args.join(" ").trim();
if (!query) {
return message.reply("Please provide a search query.");
}
processingMessage = await message.reply("๐ Searching on Pinterest...");
const res = await axios.get(`https://egret-driving-cattle.ngrok-free.app/api/pin?query=${encodeURIComponent(query)}&num=90`);
const allImageUrls = res.data.results || [];
if (allImageUrls.length === 0) {
if (processingMessage) await message.unsend(processingMessage.messageID).catch(() => { });
return message.reply(`No images found for "${query}".`);
}
if (count) {
const urls = allImageUrls.slice(0, count);
const streams = await Promise.all(urls.map(url => getStreamFromURL(url).catch(() => null)));
const validStreams = streams.filter(s => s);
if (processingMessage) await message.unsend(processingMessage.messageID).catch(() => { });
return message.reply({
body: `Here are ${validStreams.length} image(s) for "${query}":`,
attachment: validStreams
});
} else {
const imagesPerPage = 21;
const totalPages = Math.ceil(allImageUrls.length / imagesPerPage);
const startIndex = 0;
const endIndex = Math.min(allImageUrls.length, imagesPerPage);
const imagesForPage1 = allImageUrls.slice(startIndex, endIndex).map((url, idx) => ({
url,
originalIndex: startIndex + idx
}));
const { outputPath: canvasPath, displayedMap } = await generatePinterestCanvas(imagesForPage1, query, 1, totalPages);
const sentMessage = await message.reply({
body: `๐ผ๏ธ Found ${allImageUrls.length} images for "${query}".\nReply with a number (shown on canvas) to get that image, or "next" for more.`,
attachment: fs.createReadStream(canvasPath)
});
fs.unlink(canvasPath, (err) => {
if (err) console.error(err);
});
global.GoatBot.onReply.set(sentMessage.messageID, {
commandName: this.config.name,
author: event.senderID,
allImageUrls,
query,
imagesPerPage,
currentPage: 1,
totalPages,
displayedMap,
displayCount: Array.isArray(displayedMap) ? displayedMap.length : 0
});
if (processingMessage) await message.unsend(processingMessage.messageID).catch(() => { });
}
} catch (error) {
console.error(error);
if (processingMessage) {
try { await message.unsend(processingMessage.messageID); } catch (e) { }
}
message.reply("An error occurred. The server or API might be down.");
}
},
onReply: async function({ api, event, message, Reply }) {
try {
if (!Reply) return message.reply("Session expired. Please run the command again.");
const { author, allImageUrls, query, imagesPerPage, currentPage, totalPages, displayedMap, displayCount } = Reply;
if (event.senderID !== author) return;
const input = (event.body || "").trim().toLowerCase();
if (input === 'next') {
if (currentPage >= totalPages) {
return message.reply("This is the last page of results.");
}
const nextPage = currentPage + 1;
const startIndex = (nextPage - 1) * imagesPerPage;
const endIndex = Math.min(startIndex + imagesPerPage, allImageUrls.length);
const imagesForNextPage = allImageUrls.slice(startIndex, endIndex).map((url, idx) => ({
url,
originalIndex: startIndex + idx
}));
const processingMessage = await message.reply(`Loading page ${nextPage}...`);
const { outputPath: canvasPath, displayedMap: nextDisplayedMap } = await generatePinterestCanvas(imagesForNextPage, query, nextPage, totalPages);
const sentMessage = await message.reply({
body: `๐ผ๏ธ Page ${nextPage}/${totalPages}.\nReply with a number (shown on canvas) to get that image, or "next" for more.`,
attachment: fs.createReadStream(canvasPath)
});
fs.unlink(canvasPath, (err) => {
if (err) console.error(err);
});
await message.unsend(processingMessage.messageID).catch(() => { });
global.GoatBot.onReply.set(sentMessage.messageID, {
commandName: this.config.name,
author,
allImageUrls,
query,
imagesPerPage,
currentPage: nextPage,
totalPages,
displayedMap: nextDisplayedMap,
displayCount: Array.isArray(nextDisplayedMap) ? nextDisplayedMap.length : 0
});
} else {
const number = parseInt(input, 10);
if (!isNaN(number) && number > 0) {
if (!Array.isArray(displayedMap) || typeof displayCount !== 'number') {
return message.reply("This page's images aren't available anymore. Please run the command again or type 'next'.");
}
if (number > displayCount) {
return message.reply(`Invalid number. The current canvas shows only ${displayCount} image(s). Choose a number from 1 to ${displayCount}, or type "next" to load more images.`);
}
const originalIndex = displayedMap[number - 1];
if (originalIndex == null || originalIndex < 0 || originalIndex >= allImageUrls.length) {
return message.reply(`Could not find that image. Please try again or request a different number.`);
}
const imageUrl = allImageUrls[originalIndex];
const stream = await getStreamFromURL(imageUrl).catch(() => null);
if (!stream) return message.reply("Failed to fetch the requested image.");
await message.reply({
body: `Image #${number} for query "${query}":`,
attachment: stream
});
} else {
return message.reply(`Reply with a number (from the canvas) to get that image, or "next" for more pages.`);
}
}
} catch (error) {
console.error(error);
message.reply("An error occurred while handling your reply.");
}
}
};