const discord = require("discord.js"); require("dotenv").config(); // for some reason, node.js's built-in fetch function doesn't work for this // also node-fetch doesn't like being required const fetch = (...args) => import("node-fetch").then(n => n.default(...args)); const client = new discord.Client({ intents: [ discord.GatewayIntentBits.Guilds, discord.GatewayIntentBits.GuildMessages, discord.GatewayIntentBits.MessageContent, ], sweepers: { messages: { lifetime: 3600, interval: 3600, } } }); // grab data from an instagram url async function fetchData(url) { const res = await fetch(url, { headers: { 'User-Agent': 'curl/8.1.0', 'Accept': '*/*' }, credentials: "omit", }); if(!res.ok) { console.log("oops. fetching instagram link returned bad status", res.statusText); return; } const text = await res.text(); // here's where the wacky stuff happens // okay so instagram for some reason includes a full JSON-LD dump of // the post in the html returned, in this script tag // i just need to find the boundaries and parse the json let index = text.indexOf(''); if(index < 0) { console.log("couldn't find end of json data in html"); return; } json = json.substring(0, index+1); let data; try { data = JSON.parse(json); } catch(err) { console.log("couldn't parse json", err); return; } return data; } client.on("messageCreate", async message => { if(message.author.bot) { return; } if(!/^https?:\/\/(www\.)?instagram\.com\/reel/.test(message.content)) { return; } const url = message.content.split(" ")[0]; console.log("Fetching content for URL: " + url); let data; try { data = await fetchData(url); } catch(err) { console.log("Error fetching, retrying:", err); try { data = await fetchData(url); } catch(err) { console.log("Error fetching URL: " + url); console.log("Error:", err); message.channel.send("Failed to fetch data for link"); return; } } if(!data) { console.log("data is empty"); message.channel.send("Link had no data, or something went wrong"); return; } const sendObj = { embeds: [{ author: { name: data?.author?.alternateName, icon_url: data?.author?.image, }, title: data?.author?.name, description: data?.headline, }], files: (data.video || []).map(d => d.contentUrl) }; const interval = setInterval(() => message.channel.sendTyping().catch(() => {}), 3000); let success = true; try { console.log("posting..."); message.channel.sendTyping().catch(() => {}); await message.channel.send(sendObj); } catch(err) { console.log("Error posting to discord, attempting backup:", err); sendObj.content = sendObj.files.join(" "); delete sendObj.files; try { await message.channel.send(sendObj); } catch(err) { console.log("error posting backup", err); success = false; } } if(success) console.log("posted!"); clearInterval(interval); }); client.login(process.env.TOKEN).then(() => console.log("Logged into Discord as", client.user.username + "#" + client.user.discriminator));