(sock, chatId, message, crop = false)
| 185 | } |
| 186 | |
| 187 | async function igsCommand(sock, chatId, message, crop = false) { |
| 188 | try { |
| 189 | const text = message.message?.conversation || message.message?.extendedTextMessage?.text || ''; |
| 190 | const urlMatch = text.match(/https?:\/\/\S+/); |
| 191 | if (!urlMatch) { |
| 192 | await sock.sendMessage(chatId, { text: `Send an Instagram post/reel link.\nUsage:\n.igs <url>\n.igsc <url>` }, { quoted: message }); |
| 193 | return; |
| 194 | } |
| 195 | |
| 196 | await sock.sendMessage(chatId, { react: { text: '🔄', key: message.key } }); |
| 197 | |
| 198 | const downloadData = await igdl(urlMatch[0]).catch(() => null); |
| 199 | if (!downloadData || !downloadData.data) { |
| 200 | await sock.sendMessage(chatId, { text: '❌ Failed to fetch media from Instagram link.' }, { quoted: message }); |
| 201 | return; |
| 202 | } |
| 203 | // Raw items |
| 204 | const rawItems = (downloadData?.data || []).filter(m => m && m.url); |
| 205 | // Deduplicate by exact URL first |
| 206 | const seenUrls = new Set(); |
| 207 | const items = []; |
| 208 | for (const m of rawItems) { |
| 209 | if (!seenUrls.has(m.url)) { |
| 210 | seenUrls.add(m.url); |
| 211 | items.push(m); |
| 212 | } |
| 213 | } |
| 214 | if (items.length === 0) { |
| 215 | await sock.sendMessage(chatId, { text: '❌ No media found at the provided link.' }, { quoted: message }); |
| 216 | return; |
| 217 | } |
| 218 | |
| 219 | // Process up to 10 media items to avoid spam/timeouts |
| 220 | const maxItems = Math.min(items.length, 10); |
| 221 | const seenHashes = new Set(); |
| 222 | for (let i = 0; i < maxItems; i++) { |
| 223 | try { |
| 224 | const media = items[i]; |
| 225 | const mediaUrl = media.url; |
| 226 | const isVideo = (media?.type === 'video') || /\.(mp4|mov|avi|mkv|webm)$/i.test(mediaUrl); |
| 227 | |
| 228 | const buffer = await fetchBufferFromUrl(mediaUrl); |
| 229 | |
| 230 | // Content-based dedupe: skip if identical media already processed |
| 231 | const hash = require('crypto').createHash('sha1').update(buffer).digest('hex'); |
| 232 | if (seenHashes.has(hash)) { |
| 233 | continue; |
| 234 | } |
| 235 | seenHashes.add(hash); |
| 236 | |
| 237 | let stickerBuffer = crop |
| 238 | ? await stickercropFromBuffer(buffer, isVideo) |
| 239 | : await convertBufferToStickerWebp(buffer, isVideo, false); |
| 240 | |
| 241 | // Ensure final size under ~900KB; otherwise try a harsher mini fallback |
| 242 | let finalSticker = stickerBuffer; |
| 243 | if (finalSticker.length > 900 * 1024) { |
| 244 | try { |
nothing calls this directly
no test coverage detected