diff --git a/src/modules/fedimbed.js b/src/modules/fedimbed.js index 5475eda..0c09d38 100644 --- a/src/modules/fedimbed.js +++ b/src/modules/fedimbed.js @@ -1,4 +1,4 @@ -const {Message} = require("@projectdysnomia/dysnomia"); +const {Message, PrivateChannel} = require("@projectdysnomia/dysnomia"); const fs = require("node:fs"); const httpSignature = require("@peertube/http-signature"); @@ -187,6 +187,27 @@ async function blueskyQuoteEmbed(quote) { const embeds = []; const videos = []; + let hidden = false; + let adult = false; + let spoiler = false; + const tags = []; + + for (const label of quote.labels) { + if (label.val === "!hide") { + hidden = true; + spoiler = true; + } else if ( + label.val === "porn" || + label.val === "sexual" || + label.val === "graphic-media" || + label.val === "nudity" + ) { + adult = true; + spoiler = true; + tags.push(label.val); + } + } + const mainEmbed = { color: PLATFORM_COLORS.bluesky, url: `https://bsky.app/profile/${quote.author.handle}/post/${quote.uri.substring(quote.uri.lastIndexOf("/") + 1)}`, @@ -198,6 +219,7 @@ async function blueskyQuoteEmbed(quote) { description: quote.value.text, footer: { text: "Bluesky", + icon_url: "https://bsky.app/static/apple-touch-icon.png", }, timestamp: quote.value.createdAt, }; @@ -254,7 +276,7 @@ async function blueskyQuoteEmbed(quote) { embeds.push(mainEmbed); } - return {embeds, videos}; + return {embeds, videos, adult, hidden, spoiler, tags}; } async function bluesky(msg, url, spoiler = false) { @@ -287,6 +309,25 @@ async function bluesky(msg, url, spoiler = false) { const {post} = data.thread; + const guild = msg.guildID && hf.bot.guilds.has(msg.guildID) ? hf.bot.guilds.get(msg.guildID) : null; + const channel = guild ? guild.channels.get(msg.channel.id) : msg.channel; + const channelNsfw = channel?.nsfw; + + let hidden = false; + let adult = false; + const tags = []; + + for (const label of post.labels) { + if (label.val === "!hide") { + hidden = true; + spoiler = true; + } else if (label.val === "porn" || label.val === "sexual" || label.val === "graphic-media") { + adult = true; + spoiler = true; + tags.push(label.val); + } + } + const videos = []; const embeds = []; let sendWait = false; @@ -301,6 +342,7 @@ async function bluesky(msg, url, spoiler = false) { }, footer: { text: "Bluesky", + icon_url: "https://bsky.app/static/apple-touch-icon.png", }, timestamp: post.record.createdAt, }; @@ -346,6 +388,12 @@ async function bluesky(msg, url, spoiler = false) { if (quoteData.videos.length > 0) videos.push(...quoteData.videos); embeds.push(mainEmbed, ...quoteData.embeds); + + if (quoteData.adult) adult = true; + if (quoteData.hidden) hidden = true; + if (quoteData.spoiler) spoiler = true; + if (quoteData.tags.length > 0) tags.push(...quoteData.tags); + break; } case "app.bsky.embed.recordWithMedia#view": { @@ -369,6 +417,11 @@ async function bluesky(msg, url, spoiler = false) { if (quoteData.videos.length > 0) videos.push(...quoteData.videos); embeds.push(...quoteData.embeds); + if (quoteData.adult) adult = true; + if (quoteData.hidden) hidden = true; + if (quoteData.spoiler) spoiler = true; + if (quoteData.tags.length > 0) tags.push(...quoteData.tags); + break; } default: { @@ -385,7 +438,6 @@ async function bluesky(msg, url, spoiler = false) { if (msg instanceof Message) await msg.addReaction("\uD83D\uDCE4"); } - const guild = msg.channel?.guild ?? (msg.guildID ? hf.bot.guilds.get(msg.guildID) : false); const files = []; if (videos.length > 0) { for (const attachment of videos) { @@ -417,9 +469,25 @@ async function bluesky(msg, url, spoiler = false) { } } + const warnings = []; + if (hidden) { + warnings.push(":warning: Post marked as hidden"); + } + if (adult) { + if (channelNsfw || channel instanceof PrivateChannel) { + warnings.push(`:warning: Post contains adult content: ${Array.from(new Set(tags)).join(", ")}`); + } else { + return { + response: { + content: "Not embedding post due to adult content in SFW channel", + }, + }; + } + } + return { response: { - content: spoiler ? `|| ${url} ||` : "", + content: `${warnings.length > 0 ? warnings.join("\n") + "\n" : ""}${spoiler ? `|| ${url} ||` : ""}`, embeds, attachments: files, allowedMentions: { @@ -444,7 +512,7 @@ async function processUrl(msg, url, spoiler = false) { if (invalidUrl) return {}; - if (BSKY_DOMAINS.includes(urlObj.hostname)) return await bluesky(msg, url, spoiler); + if (BSKY_DOMAINS.includes(urlObj.hostname.toLowerCase())) return await bluesky(msg, url, spoiler); // some lemmy instances have old reddit frontend subdomains // but these frontends are just frontends and dont actually expose the API