From b72ddd96142877394feeea9f37dd7eb096ef9132 Mon Sep 17 00:00:00 2001 From: Cynthia Foxwell Date: Sat, 5 Apr 2025 12:25:12 -0600 Subject: [PATCH] fedimbed: attempt to resolve fedi users from bridgy bsky users --- src/modules/fedimbed.js | 201 +++++++++++++++++++++++++++++----------- 1 file changed, 149 insertions(+), 52 deletions(-) diff --git a/src/modules/fedimbed.js b/src/modules/fedimbed.js index 7264b6e..aa4134d 100644 --- a/src/modules/fedimbed.js +++ b/src/modules/fedimbed.js @@ -143,11 +143,69 @@ async function resolvePlatform(url) { function normalizePlatform(platform) { return platform .replace("gotosocial", "GoToSocial") - .replace("birdsitelive", '"Twitter" (BirdsiteLive)') + .replace("birdsitelive", "BirdsiteLive") .replace(/^(.)/, (_, c) => c.toUpperCase()) .replace("Cohost", "cohost"); } +async function getCrawledData(url, color, platformName) { + const urlObj = new URL(url); + + let headTag, + lastIconSize = 0, + icon; + try { + const page = await fetch(url, { + headers: { + "User-Agent": FRIENDLY_USERAGENT, + }, + }) + .then((res) => res.text()) + .catch(() => {}); + + if (page) { + headTag = pageParser.parse(page)?.html?.head; + } + } catch { + // noop + } + + if (headTag) { + for (const tag of headTag.link) { + if (tag.$rel == "icon" || tag.$rel == "apple-touch-icon") { + if (tag.$sizes) { + const [w, h] = tag.$sizes.split("x").map((x) => parseInt(x)); + const size = w * h; + if (size > lastIconSize) { + lastIconSize = size; + icon = tag.$href; + } + } else { + icon = tag.$href; + } + } + } + + for (const tag of headTag.meta) { + if (tag.$property == "og:site_name" && tag.$content != platformName) { + if (platformName == "") { + platformName = tag.$content; + } else { + platformName = `${tag.$content} (${platformName})`; + } + } else if (!color && tag.$name == "theme-color") { + color = parseInt(tag.$content.replace("^#", "0x")); + } + } + } + + if (icon && icon.startsWith("/")) { + icon = urlObj.origin + icon; + } + + return {icon, color, platformName}; +} + const keyId = "https://hf.c7.pm/actor#main-key"; const privKey = fs.readFileSync(require.resolve("#root/priv/private.pem")); async function signedFetch(url, options) { @@ -280,6 +338,48 @@ async function blueskyQuoteEmbed(quote) { timestamp: quote.value.createdAt, }; + if (quote.author.handle.endsWith(".ap.brid.gy")) { + const handle = quote.author.handle.replace(".ap.brid.gy", ""); + const split = handle.split("."); + + const username = split.shift(); + const domain = split.join("."); + const authorUrl = `https://${domain}/users/${username}`; + + const authorData = await signedFetch(authorUrl, { + headers: { + "User-Agent": FRIENDLY_USERAGENT, + Accept: "application/activity+json", + }, + }) + .then((res) => res.json()) + .catch((err) => { + logger.error("fedimbed", `Failed to get author for "${authorUrl}": ${err}`); + }); + + if (authorData) { + const platform = (await resolvePlatform(authorUrl)) ?? ""; + let color = PLATFORM_COLORS[platform]; + let platformName = normalizePlatform(platform); + + const crawled = getCrawledData(authorUrl, color, platformName); + if (!color && crawled?.color) { + color = crawled.color; + } + if (crawled?.platformName) { + platformName = crawled.platformName; + } + platformName += " [via Bluesky via Bridgy]"; + + mainEmbed.footer = { + text: platformName, + icon_url: crawled?.icon, + }; + mainEmbed.title = `${authorData.name} (${authorData.preferredUsername}@${domain})`; + mainEmbed.color = color; + } + } + if (quote.value.facets?.length > 0) { mainEmbed.description = processBlueskyFacets(mainEmbed.description, quote.value.facets); } @@ -353,7 +453,6 @@ async function blueskyQuoteEmbed(quote) { async function bluesky(msg, url, spoiler = false) { const quoteOnly = await hasFlag(msg.guildID, "bskyQuoteOnly"); - // really... if (url.includes("bsky.brid.gy")) url = url.replace("bsky.brid.gy/r/https://", ""); const urlObj = new URL(url); @@ -429,6 +528,48 @@ async function bluesky(msg, url, spoiler = false) { timestamp: post.record.createdAt, }; + if (post.author.handle.endsWith(".ap.brid.gy")) { + const handle = post.author.handle.replace(".ap.brid.gy", ""); + const split = handle.split("."); + + const username = split.shift(); + const domain = split.join("."); + const authorUrl = `https://${domain}/@${username}`; + + const authorData = await signedFetch(authorUrl, { + headers: { + "User-Agent": FRIENDLY_USERAGENT, + Accept: "application/activity+json", + }, + }) + .then((res) => res.json()) + .catch((err) => { + logger.error("fedimbed", `Failed to get author for "${authorUrl}": ${err}`); + }); + + if (authorData) { + const platform = (await resolvePlatform(authorUrl)) ?? ""; + let color = PLATFORM_COLORS[platform]; + let platformName = normalizePlatform(platform); + + const crawled = getCrawledData(authorUrl, color, platformName); + if (!color && crawled?.color) { + color = crawled.color; + } + if (crawled?.platformName) { + platformName = crawled.platformName; + } + platformName += " [via Bluesky via Bridgy]"; + + mainEmbed.footer = { + text: platformName, + icon_url: crawled?.icon, + }; + mainEmbed.title = `${authorData.name} (${authorData.preferredUsername}@${domain})`; + mainEmbed.color = color; + } + } + if (post.record.facets?.length > 0) { mainEmbed.description = processBlueskyFacets(mainEmbed.description, post.record.facets); } @@ -1220,56 +1361,12 @@ async function processUrl(msg, url, spoiler = false, command = false) { const user = author.name ? `${author.name} (${author.handle})` : author.handle; - let headTag, - lastIconSize = 0, - icon; - try { - const page = await fetch(url, { - headers: { - "User-Agent": FRIENDLY_USERAGENT, - }, - }) - .then((res) => res.text()) - .catch(() => {}); - - if (page) { - headTag = pageParser.parse(page)?.html?.head; - } - } catch { - // noop + const crawled = await getCrawledData(url, color, platformName); + if (!color && crawled?.color) { + color = crawled.color; } - - if (headTag) { - for (const tag of headTag.link) { - if (tag.$rel == "icon" || tag.$rel == "apple-touch-icon") { - if (tag.$sizes) { - const [w, h] = tag.$sizes.split("x").map((x) => parseInt(x)); - const size = w * h; - if (size > lastIconSize) { - lastIconSize = size; - icon = tag.$href; - } - } else { - icon = tag.$href; - } - } - } - - for (const tag of headTag.meta) { - if (tag.$property == "og:site_name" && tag.$content != platformName) { - if (platformName == "") { - platformName = tag.$content; - } else { - platformName = `${tag.$content} (${platformName})`; - } - } else if (!color && tag.$name == "theme-color") { - color = parseInt(tag.$content.replace("^#", "0x")); - } - } - } - - if (icon && icon.startsWith("/")) { - icon = urlObj.origin + icon; + if (crawled?.platformName) { + platformName = crawled.platformName; } const baseEmbed = { @@ -1291,7 +1388,7 @@ async function processUrl(msg, url, spoiler = false, command = false) { } : null, footer: { - icon_url: icon, + icon_url: crawled?.icon, text: platformName, }, thumbnail: {