كود حزمه يجيب لك حزكة ملصقات من بنترست
javascriptv1MITنُشر ٢٨ يوليو ٢٠٢٦· ♥ 0 · ⬇ 1 · 💬 0
تحميل ملصقات
// plugins/stickers-pack.js
// 🕷️ Spider-Man - حزمة ملصقات Pinterest 📦
import fetch from 'node-fetch'
import { writeFileSync, unlinkSync, readFileSync, existsSync } from 'fs'
import { execSync } from 'child_process'
import crypto from 'crypto'
import https from 'https'
import axios from 'axios'
const PINTEREST_BASE = 'https://www.pinterest.com'
const PINTEREST_SEARCH_PATH = '/resource/BaseSearchResource/get/'
const PINTEREST_HEADERS = {
accept: 'application/json, text/javascript, */*, q=0.01',
referer: 'https://www.pinterest.com/',
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36',
'x-app-version': 'a9522f',
'x-pinterest-appstate': 'active',
'x-pinterest-pws-handler': 'www/[username]/[slug].js',
'x-requested-with': 'XMLHttpRequest'
}
async function getPinterestCookies() {
try {
const res = await axios.get(PINTEREST_BASE, { timeout: 15000 })
const setCookies = res.headers['set-cookie']
if (!setCookies) return null
return setCookies.map(c => c.split(';')[0].trim()).join('; ')
} catch {
return null
}
}
async function searchPinterest(query, needed = 100) {
const images = { static: [], animated: [] }
const cookies = await getPinterestCookies()
if (!cookies) return images
let bookmarks = ['']
let attempts = 0
const maxAttempts = 10
while (images.static.length + images.animated.length < needed && bookmarks.length && attempts < maxAttempts) {
attempts++
try {
const params = {
source_url: `/search/pins/?q=${encodeURIComponent(query)}`,
data: JSON.stringify({
options: { isPrefetch: false, query, scope: 'pins', bookmarks, page_size: 50 },
context: {}
}),
_: Date.now()
}
const { data } = await axios.get(`${PINTEREST_BASE}${PINTEREST_SEARCH_PATH}`, {
headers: { ...PINTEREST_HEADERS, cookie: cookies },
params,
timeout: 15000
})
const results = data?.resource_response?.data?.results?.filter(v => v?.images?.orig?.url) || []
if (!results.length) break
for (const r of results) {
const url = r.images.orig.url
if (url.endsWith('.gif')) {
images.animated.push(url)
} else {
images.static.push(url)
}
if (images.static.length + images.animated.length >= needed) break
}
const nextBookmark = data?.resource_response?.bookmark
if (!nextBookmark || nextBookmark === '-end-') break
bookmarks = [nextBookmark]
} catch {
break
}
}
return {
static: [...new Set(images.static)].slice(0, needed),
animated: [...new Set(images.animated)].slice(0, needed)
}
}
async function getSharp() {
try {
const mod = await import('sharp')
return mod.default
} catch {
throw new Error('❌ مكتبة sharp غير مثبتة\n📦 npm i sharp')
}
}
async function makeTrayWebp(buffer) {
const sharp = await getSharp()
return sharp(buffer, { animated: false }).resize(252, 252, { fit: 'cover' }).webp().toBuffer()
}
async function makeBlankTrayWebp() {
const sharp = await getSharp()
return sharp({ create: { width: 252, height: 252, channels: 4, background: { r: 0, g: 0, b: 0, alpha: 0 } } }).webp().toBuffer()
}
async function makeThumbnailJpeg(buffer) {
const sharp = await getSharp()
return sharp(buffer).resize(252, 252, { fit: 'cover' }).jpeg().toBuffer()
}
function sha256(buffer) {
return crypto.createHash('sha256').update(buffer).digest()
}
function toB64Url(buffer) {
return Buffer.from(buffer).toString('base64').replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/g, '')
}
async function uploadToServer(conn, buffer, { hkdf, mediaPath, mediaKey = crypto.randomBytes(32) }) {
const expanded = Buffer.from(crypto.hkdfSync('sha256', mediaKey, Buffer.alloc(32), Buffer.from(hkdf), 112))
const iv = expanded.subarray(0, 16)
const cipherKey = expanded.subarray(16, 48)
const macKey = expanded.subarray(48, 80)
const cipher = crypto.createCipheriv('aes-256-cbc', cipherKey, iv)
const encrypted = Buffer.concat([cipher.update(buffer), cipher.final()])
const mac = crypto.createHmac('sha256', macKey).update(iv).update(encrypted).digest().subarray(0, 10)
const encBuffer = Buffer.concat([encrypted, mac])
const fileSha256 = sha256(buffer)
const fileEncSha256 = sha256(encBuffer)
const iq = await conn.query({
tag: 'iq', attrs: { id: conn.generateMessageTag?.() ?? Date.now().toString(), to: 's.whatsapp.net', type: 'set', xmlns: 'w:m' },
content: [{ tag: 'media_conn', attrs: {} }]
})
const mediaConn = iq.content?.find(v => v.tag === 'media_conn')
if (!mediaConn) throw new Error('media_conn غير موجود')
const auth = mediaConn.attrs?.auth
if (!auth) throw new Error('auth غير موجود')
const hosts = (mediaConn.content || []).filter(v => v.tag === 'host').map(v => v.attrs?.hostname).filter(Boolean)
if (!hosts.length) throw new Error('لا يوجد host للرفع')
const token = encodeURIComponent(fileEncSha256.toString('base64').replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/g, ''))
for (const host of hosts) {
try {
const json = await new Promise((resolve, reject) => {
const url = new URL(`https://${host}${mediaPath}/${token}?auth=${encodeURIComponent(auth)}&token=${token}`)
const req = https.request({
hostname: url.hostname, port: 443, path: url.pathname + url.search, method: 'POST',
headers: { Origin: 'https://web.whatsapp.com', Referer: 'https://web.whatsapp.com/', 'Content-Type': 'application/octet-stream', 'Content-Length': encBuffer.length }
}, (res) => {
let body = ''
res.on('data', c => body += c)
res.on('end', () => {
if (res.statusCode < 200 || res.statusCode >= 300) return reject(new Error(`فشل الرفع ${res.statusCode}`))
try { resolve(JSON.parse(body)) } catch { reject(new Error('رد غير JSON')) }
})
})
req.on('error', reject)
req.write(encBuffer)
req.end()
})
const directPath = json.direct_path ?? json.directPath ?? json.url ?? json.path
if (!directPath) throw new Error('directPath غير موجود')
return { mediaKey, fileLength: buffer.length, fileSha256, fileEncSha256, directPath, ...json }
} catch (e) { lastError = e }
}
throw lastError ?? new Error('جميع محاولات الرفع فشلت')
}
async function sendStickerPack(conn, m, pack, query) {
const JSZip = (await import('jszip')).default
const zip = new JSZip()
const stickersMetadata = []
for (const item of pack) {
const fileName = `${toB64Url(sha256(item.buffer))}.webp`
zip.file(fileName, item.buffer)
stickersMetadata.push({
fileName,
isAnimated: item.isAnimated,
emojis: [''],
accessibilityLabel: '',
isLottie: false,
mimetype: 'image/webp'
})
}
const trayIconFileName = 'tray_icon.webp'
const traySource = pack.find(v => !v.isAnimated)?.buffer || pack[0]?.buffer
const trayBuffer = traySource ? await makeTrayWebp(traySource) : await makeBlankTrayWebp()
zip.file(trayIconFileName, trayBuffer)
const archive = await zip.generateAsync({ type: 'nodebuffer', compression: 'STORE' })
const packUpload = await uploadToServer(conn, archive, { hkdf: 'WhatsApp Sticker Pack Keys', mediaPath: '/mms/sticker-pack' })
const thumbnailBuffer = await makeThumbnailJpeg(trayBuffer)
const thumbUpload = await uploadToServer(conn, thumbnailBuffer, { hkdf: 'WhatsApp Sticker Pack Thumbnail Keys', mediaPath: '/mms/thumbnail-sticker-pack', mediaKey: packUpload.mediaKey })
await conn.relayMessage(m.chat, {
messageContextInfo: { messageSecret: crypto.randomBytes(32) },
stickerPackMessage: {
stickerPackId: 'Pack_' + crypto.randomBytes(8).toString('hex'),
name: '🕷️ Spider-Man',
publisher: '🕷️ Spider-Man',
packDescription: `🕷️ ${query}`,
stickers: stickersMetadata,
fileLength: packUpload.fileLength,
fileSha256: packUpload.fileSha256,
fileEncSha256: packUpload.fileEncSha256,
mediaKey: packUpload.mediaKey,
directPath: packUpload.directPath,
mediaKeyTimestamp: Math.floor(Date.now() / 1000),
stickerPackSize: packUpload.fileLength,
stickerPackOrigin: 2,
trayIconFileName,
thumbnailDirectPath: thumbUpload.directPath,
thumbnailSha256: thumbUpload.fileSha256,
thumbnailEncSha256: thumbUpload.fileEncSha256,
thumbnailHeight: 252,
thumbnailWidth: 252,
imageDataHash: thumbUpload.fileSha256.toString('base64')
}
}, { quoted: m })
}
async function makeWebp(buffer, index) {
const ts = Date.now()
const inputPath = `./tmp_pk_${ts}_${index}.jpg`
const outputPath = `./tmp_pk_${ts}_${index}.webp`
try {
writeFileSync(inputPath, buffer)
execSync(`ffmpeg -y -i "${inputPath}" -vf "scale=512:512:force_original_aspect_ratio=decrease,pad=512:512:(ow-iw)/2:(oh-ih)/2:color=0x00000000" -q:v 80 "${outputPath}"`, { stdio: 'pipe' })
let result = readFileSync(outputPath)
return { buffer: result, isAnimated: false }
} finally {
try { if (existsSync(inputPath)) unlinkSync(inputPath) } catch {}
try { if (existsSync(outputPath)) unlinkSync(outputPath) } catch {}
}
}
async function makeAnimatedWebp(buffer, index) {
const ts = Date.now()
const inputPath = `./tmp_pk_a_${ts}_${index}.gif`
const outputPath = `./tmp_pk_a_${ts}_${index}.webp`
try {
writeFileSync(inputPath, buffer)
execSync(`ffmpeg -y -i "${inputPath}" -vf "scale=512:512:force_original_aspect_ratio=decrease,pad=512:512:(ow-iw)/2:(oh-ih)/2:color=0x00000000,fps=15" -loop 0 -q:v 80 -preset default -an "${outputPath}"`, { stdio: 'pipe' })
let result = readFileSync(outputPath)
return { buffer: result, isAnimated: true }
} finally {
try { if (existsSync(inputPath)) unlinkSync(inputPath) } catch {}
try { if (existsSync(outputPath)) unlinkSync(outputPath) } catch {}
}
}
let handler = async (m, { conn, text, usedPrefix, command }) => {
if (!text) {
return m.reply(`🕷️ ${usedPrefix + command} <بحث> <عدد>\n🕷️ مثال: ${usedPrefix + command} anime 10`)
}
let query = text
let count = 10
const parts = text.split(' ')
const lastPart = parts[parts.length - 1]
if (!isNaN(lastPart) && parts.length > 1) {
count = Math.min(100, Math.max(1, parseInt(lastPart)))
query = parts.slice(0, -1).join(' ')
}
let statusMsg = await m.reply(`🕷️ ${m.pushName} بحث عن ${query}`)
try {
const images = await searchPinterest(query, count)
const allStatic = images.static
const allAnimated = images.animated
if (allStatic.length === 0 && allAnimated.length === 0) {
return m.reply(`🕷️ لا توجد نتائج لـ ${query}`)
}
const pack = []
const UA = 'Mozilla/5.0 (Linux; Android 10) AppleWebKit/537.36'
for (let i = 0; i < allStatic.length && pack.length < count; i++) {
try {
const res = await fetch(allStatic[i], { headers: { 'User-Agent': UA }, timeout: 10000 })
if (!res.ok) continue
const buffer = Buffer.from(await res.arrayBuffer())
const webp = await makeWebp(buffer, i)
pack.push(webp)
} catch {}
}
for (let i = 0; i < allAnimated.length && pack.length < count; i++) {
try {
const res = await fetch(allAnimated[i], { headers: { 'User-Agent': UA }, timeout: 15000 })
if (!res.ok) continue
const buffer = Buffer.from(await res.arrayBuffer())
const webp = await makeAnimatedWebp(buffer, i)
pack.push(webp)
} catch {}
}
if (pack.length === 0) {
return m.reply(`🕷️ تعذر معالجة اي ملصق`)
}
try { await conn.sendMessage(m.chat, { delete: statusMsg.key }) } catch {}
await sendStickerPack(conn, m, pack, query)
} catch (e) {
console.error(e)
try { await conn.sendMessage(m.chat, { delete: statusMsg.key }) } catch {}
m.reply(`🕷️ خطأ: ${e.message}`)
}
}
handler.command = ['ملصقات', 'باكج', 'stickerpack', 'pack']
handler.help = ['ملصقات [بحث] [عدد]']
handler.tags = ['sticker']
export default handler
التعليقات (0)
سجّل الدخول للتعليق.