// code from goatstore // api: https://store.agi.bd const axios = require("axios"); const API_BASE = "https://eryxenx.agi.bd/api/pornpics"; const LANGS = new Set([ "en", "de", "fr", "es", "it", "pt", "pl", "nl", "ru", "jp", "zh", "cs", "fi", "sv", "no", "da" ]); const DEFAULT_LIMIT = 6; const MAX_LIMIT = 10; const TIMEOUT = 30000; const IMAGE_UA = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"; const api = axios.create({ baseURL: API_BASE, timeout: TIMEOUT }); function errMsg(err) { const data = err && err.response && err.response.data; return (data && (data.message || data.error || data.detail)) || (err && err.message) || "Unknown error"; } function truncate(text, max) { const value = String(text || "").trim(); return value.length > max ? value.slice(0, max - 1) + "โ€ฆ" : value; } function parseArgs(args) { let limit = DEFAULT_LIMIT; let lang = "en"; const words = []; for (let i = 0; i < args.length; i++) { const arg = String(args[i]); let name = null; let value = null; if (arg === "-n" || arg === "--limit") { name = "limit"; value = args[++i]; } else if (arg === "-l" || arg === "--lang") { name = "lang"; value = args[++i]; } else if (/^-n.+/.test(arg)) { name = "limit"; value = arg.slice(2); } else if (/^-l.+/.test(arg)) { name = "lang"; value = arg.slice(2); } if (name === "limit") { const parsed = parseInt(value, 10); if (Number.isFinite(parsed)) limit = parsed; } else if (name === "lang") { const code = String(value || "").toLowerCase(); if (LANGS.has(code)) lang = code; } else { words.push(arg); } } return { query: words.join(" ").trim(), limit: Math.max(1, Math.min(MAX_LIMIT, limit || DEFAULT_LIMIT)), lang }; } async function fetchPage(query, lang, limit, offset) { const { data } = await api.get("/search", { params: { q: query, lang, limit, offset } }); return data; } async function toStream(url) { const res = await axios.get(url, { responseType: "stream", timeout: TIMEOUT, headers: { "User-Agent": IMAGE_UA } }); return res.data; } async function buildAttachments(results) { const settled = await Promise.allSettled( results.map(item => toStream(item.thumbnail_large || item.thumbnail)) ); return settled .filter(entry => entry.status === "fulfilled" && entry.value) .map(entry => entry.value); } function buildBody(query, page, results, hasMore, withImageLinks) { const lines = results.map((item, index) => { const head = `${index + 1}. ${truncate(item.title, 90)}`; if (!withImageLinks) return `${head}\n${item.url}`; return `${head}\n${item.thumbnail_large || item.thumbnail}\n${item.url}`; }); return [ `๐Ÿ”ž PornPics โ€” "${query}"`, `Page ${page} ยท ${results.length} result(s)`, "", lines.join("\n\n"), "", hasMore ? 'Reply "n" for the next page, or a page number to jump.' : "That is the last page." ].join("\n"); } function remember(messageID, data) { global.GoatBot.onReply.set(messageID, data); } async function sendPage({ message, event, commandName, query, lang, limit, page, previous }) { const offset = (page - 1) * limit; const data = await fetchPage(query, lang, limit, offset); if (!data.results || !data.results.length) { return message.reply(page === 1 ? `No results for "${query}".` : `Page ${page} has no results. Highest page is ${Math.max(1, page - 1)}.`); } const attachments = await buildAttachments(data.results); const form = { body: buildBody(query, page, data.results, data.has_more, !attachments.length) }; if (attachments.length) form.attachment = attachments; await message.reply(form, (err, info) => { if (err) return message.err(err); if (previous && previous.messageID && previous.messageID !== info.messageID) message.unsend(previous.messageID); remember(info.messageID, { commandName, messageID: info.messageID, author: event.senderID, query, lang, limit, page, hasMore: Boolean(data.has_more) }); }); } module.exports = { config: { name: "pornpics", aliases: ["ppics", "pp"], version: "1.0.0", author: "EryXenX", countDown: 10, role: 0, category: "18+", description: { en: "Search PornPics galleries and browse the results page by page" }, guide: { en: " {pn} โ€” search and show the first page" + "\n {pn} -n โ€” results per page (1-10, default 6)" + "\n {pn} -l โ€” site locale, e.g. -ljp, -lde (default en)" + "\n {pn} -n8 -ljp โ€” flags can be combined" + "\n Then reply \"n\" for the next page, or a page number to jump." } }, onStart: async function ({ event, args, message, commandName }) { const { query, limit, lang } = parseArgs(args); if (!query) return message.reply("Usage: pornpics [-n] [-l]\nExample: pornpics office -n6 -len"); await message.reaction("โณ", event.messageID); try { await sendPage({ message, event, commandName, query, lang, limit, page: 1 }); await message.reaction("โœ…", event.messageID); } catch (err) { console.error("[pornpics] search failed:", errMsg(err)); await message.reaction("โŒ", event.messageID); message.reply(`Search failed: ${errMsg(err)}`); } }, onReply: async function ({ event, message, Reply, commandName }) { if (event.senderID !== Reply.author) return; const body = String(event.body || "").trim().toLowerCase(); let page; if (body === "n" || body === "next") { if (!Reply.hasMore) return message.reply("That is the last page."); page = Reply.page + 1; } else { const parsed = parseInt(body, 10); if (!Number.isFinite(parsed) || parsed < 1) return message.reply('Reply with a page number, or "n" for the next page.'); page = parsed; } await message.reaction("โณ", event.messageID); try { await sendPage({ message, event, commandName, query: Reply.query, lang: Reply.lang, limit: Reply.limit, page, previous: Reply }); await message.reaction("โœ…", event.messageID); } catch (err) { console.error("[pornpics] page failed:", errMsg(err)); await message.reaction("โŒ", event.messageID); message.reply(`Search failed: ${errMsg(err)}`); } } };