fedimbed: attempt to resolve fedi users from bridgy bsky users
This commit is contained in:
parent
12a2e222b7
commit
b72ddd9614
1 changed files with 149 additions and 52 deletions
|
@ -143,11 +143,69 @@ async function resolvePlatform(url) {
|
||||||
function normalizePlatform(platform) {
|
function normalizePlatform(platform) {
|
||||||
return platform
|
return platform
|
||||||
.replace("gotosocial", "GoToSocial")
|
.replace("gotosocial", "GoToSocial")
|
||||||
.replace("birdsitelive", '"Twitter" (BirdsiteLive)')
|
.replace("birdsitelive", "BirdsiteLive")
|
||||||
.replace(/^(.)/, (_, c) => c.toUpperCase())
|
.replace(/^(.)/, (_, c) => c.toUpperCase())
|
||||||
.replace("Cohost", "cohost");
|
.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 == "<no nodeinfo>") {
|
||||||
|
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 keyId = "https://hf.c7.pm/actor#main-key";
|
||||||
const privKey = fs.readFileSync(require.resolve("#root/priv/private.pem"));
|
const privKey = fs.readFileSync(require.resolve("#root/priv/private.pem"));
|
||||||
async function signedFetch(url, options) {
|
async function signedFetch(url, options) {
|
||||||
|
@ -280,6 +338,48 @@ async function blueskyQuoteEmbed(quote) {
|
||||||
timestamp: quote.value.createdAt,
|
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)) ?? "<no nodeinfo>";
|
||||||
|
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) {
|
if (quote.value.facets?.length > 0) {
|
||||||
mainEmbed.description = processBlueskyFacets(mainEmbed.description, quote.value.facets);
|
mainEmbed.description = processBlueskyFacets(mainEmbed.description, quote.value.facets);
|
||||||
}
|
}
|
||||||
|
@ -353,7 +453,6 @@ async function blueskyQuoteEmbed(quote) {
|
||||||
async function bluesky(msg, url, spoiler = false) {
|
async function bluesky(msg, url, spoiler = false) {
|
||||||
const quoteOnly = await hasFlag(msg.guildID, "bskyQuoteOnly");
|
const quoteOnly = await hasFlag(msg.guildID, "bskyQuoteOnly");
|
||||||
|
|
||||||
// really...
|
|
||||||
if (url.includes("bsky.brid.gy")) url = url.replace("bsky.brid.gy/r/https://", "");
|
if (url.includes("bsky.brid.gy")) url = url.replace("bsky.brid.gy/r/https://", "");
|
||||||
|
|
||||||
const urlObj = new URL(url);
|
const urlObj = new URL(url);
|
||||||
|
@ -429,6 +528,48 @@ async function bluesky(msg, url, spoiler = false) {
|
||||||
timestamp: post.record.createdAt,
|
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)) ?? "<no nodeinfo>";
|
||||||
|
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) {
|
if (post.record.facets?.length > 0) {
|
||||||
mainEmbed.description = processBlueskyFacets(mainEmbed.description, post.record.facets);
|
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;
|
const user = author.name ? `${author.name} (${author.handle})` : author.handle;
|
||||||
|
|
||||||
let headTag,
|
const crawled = await getCrawledData(url, color, platformName);
|
||||||
lastIconSize = 0,
|
if (!color && crawled?.color) {
|
||||||
icon;
|
color = crawled.color;
|
||||||
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 (crawled?.platformName) {
|
||||||
if (headTag) {
|
platformName = crawled.platformName;
|
||||||
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 == "<no nodeinfo>") {
|
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const baseEmbed = {
|
const baseEmbed = {
|
||||||
|
@ -1291,7 +1388,7 @@ async function processUrl(msg, url, spoiler = false, command = false) {
|
||||||
}
|
}
|
||||||
: null,
|
: null,
|
||||||
footer: {
|
footer: {
|
||||||
icon_url: icon,
|
icon_url: crawled?.icon,
|
||||||
text: platformName,
|
text: platformName,
|
||||||
},
|
},
|
||||||
thumbnail: {
|
thumbnail: {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue