// code from goatstore // api: https://store.agi.bd const axios = require("axios"); const API_BASE = "https://rx.agi.bd/pinterest"; async function readErrorBody(err) { const stream = err.response?.data; if (!stream || typeof stream.on !== "function") return null; const chunks = []; for await (const chunk of stream) chunks.push(chunk); try { const parsed = JSON.parse(Buffer.concat(chunks).toString()); return typeof parsed.detail === "string" ? parsed.detail : JSON.stringify(parsed.detail); } catch { return null; } } async function fetchAttachment(url, filename) { const res = await axios.get(url, { responseType: "stream", timeout: 90000, validateStatus: (s) => s === 200 }); res.data.path = filename; return res.data; } async function searchPinterest(mode, query, limit) { const endpoint = mode === "video" ? "video" : "img"; const { data } = await axios.get(`${API_BASE}/${endpoint}`, { params: { quary: query, limit }, timeout: 60000 }); if (!data || data.status === false) { throw new Error(data?.message || data?.detail || "API returned an error"); } return Array.isArray(data.result) ? data.result : []; } async function downloadVideo({ api, item, choice, message, editMessageID }) { const text = choice ? `⬇️ Downloading video ${choice}...` : `⬇️ Downloading video...`; let waitMsg = null; if (editMessageID) { try { api.editMessage(text, editMessageID); } catch {} } else { waitMsg = await message.reply(text); } try { const attachment = await fetchAttachment(item.download_url, `${item.id}.mp4`); await message.reply({ attachment }); } catch (e) { const reason = await readErrorBody(e) || e.message; await message.reply(`❌ Failed to download video${choice ? ` ${choice}` : ""}.\nReason: ${reason}`); } finally { if (waitMsg && waitMsg.messageID) { try { api.unsendMessage(waitMsg.messageID); } catch {} } } } module.exports = { config: { name: "Pinterest", aliases: ["pin"], version: "2.0.0", author: "rX", countDown: 5, role: 0, shortDescription: "Search Pinterest images/videos", longDescription: "Search Pinterest via rx.agi.bd API and send images or videos (with sound).", category: "media", guide: { en: "{pn} [-N] (image, default)\n{pn} -v [-N] (video)\nEx: {pn} flowers -3\nEx: {pn} -v sunset -7" } }, onStart: async function ({ api, event, args, message }) { if (!args.length) return message.reply("⚠️ Usage: !pin [-N] | !pin -v [-N]"); let mode = "image"; let num = 10; const queryParts = []; for (const arg of args) { const lower = arg.toLowerCase(); if (lower === "-v" || lower === "-video" || lower === "video") { mode = "video"; } else if (lower === "-i" || lower === "-image" || lower === "image") { mode = "image"; } else if (/^-\d+$/.test(arg)) { num = Math.min(10, Math.max(1, parseInt(arg.slice(1), 10))); } else { queryParts.push(arg); } } const query = queryParts.join(" ").trim(); if (!query) return message.reply("⚠️ Please provide a search query."); const waitMsg = await message.reply(`🔍 Searching Pinterest for "${query}" (${mode}, ${num})...`); let keepWaitMsg = false; try { let results = await searchPinterest(mode, query, num); if (!results.length) { results = await searchPinterest(mode, query, num); } if (!results.length) { return message.reply(`❌ No ${mode} results found for "${query}".`); } if (mode === "image") { const attachments = []; for (const item of results) { try { attachments.push(await fetchAttachment(item.download_url, `${item.id}.jpg`)); } catch {} } if (!attachments.length) return message.reply("❌ Failed to fetch images."); return message.reply({ body: `✅ Found ${attachments.length} image(s) for "${query}"`, attachment: attachments }); } if (results.length === 1) { keepWaitMsg = true; return downloadVideo({ api, item: results[0], message, editMessageID: waitMsg.messageID }); } const thumbs = []; const list = []; results.forEach((item, i) => { const dur = item.duration_ms ? ` (${Math.round(item.duration_ms / 1000)}s)` : ""; list.push(`${i + 1}.${dur} ${item.alt || "No title"}`.slice(0, 80)); }); for (const item of results) { try { thumbs.push(await fetchAttachment(item.thumbnail, `${item.id}.jpg`)); } catch {} } if (!thumbs.length) return message.reply("❌ Failed to load thumbnails."); const pickMsg = await message.reply({ body: `🎬 Found ${thumbs.length} video(s) for "${query}"\n\n${list.join("\n")}\n\n👉 Reply with a number (1-${thumbs.length}) to download`, attachment: thumbs }); global.GoatBot.onReply.set(pickMsg.messageID, { commandName: this.config.name, messageID: pickMsg.messageID, author: event.senderID, results }); } catch (err) { const reason = await readErrorBody(err) || err.message; return message.reply(`❌ Error: ${reason}`); } finally { if (!keepWaitMsg && waitMsg && waitMsg.messageID) { try { api.unsendMessage(waitMsg.messageID); } catch {} } } }, onReply: async function ({ api, event, Reply, message }) { if (event.senderID !== Reply.author) return; const choice = parseInt(event.body?.trim(), 10); const item = Reply.results[choice - 1]; if (!choice || !item) { return message.reply(`⚠️ Please reply with a number between 1 and ${Reply.results.length}.`); } global.GoatBot.onReply.delete(Reply.messageID); return downloadVideo({ api, item, choice, message, editMessageID: Reply.messageID }); } };