feat: Add support for Bluesky card previews#161
feat: Add support for Bluesky card previews#161pdubroy wants to merge 1 commit intohumanwhocodes:mainfrom
Conversation
|
Thanks for the pull request. I need to take some time to think about this because |
|
Ok, makes sense. FYI I have a little script that uses async function fetchOgData(url) {
const res = await fetch(url, {
headers: {'User-Agent': 'crosspost-bot/1.0'},
redirect: 'follow',
signal: AbortSignal.timeout(10000),
});
const html = await res.text();
const og = name => {
const m =
html.match(
new RegExp(`<meta[^>]+property=["']og:${name}["'][^>]+content=["']([^"']+)["']`, 'i')
) ??
html.match(
new RegExp(`<meta[^>]+content=["']([^"']+)["'][^>]+property=["']og:${name}["']`, 'i')
);
return m?.[1] ?? null;
};
return {
uri: url,
title: og('title') ?? '',
description: og('description') ?? '',
thumb_url: og('image') ?? null,
};
}Obviously doing it with a RegEx is bit nasty. Here's what it would look like using metascraper: import metascraper from 'metascraper';
import metascraperDescription from 'metascraper-description';
import metascraperImage from 'metascraper-image';
import metascraperTitle from 'metascraper-title';
const scraper = metascraper([
metascraperDescription(),
metascraperImage(),
metascraperTitle(),
]);
export async function fetchOgData(url) {
const res = await fetch(url, {
headers: {'User-Agent': 'og-fetch/1.0'},
redirect: 'follow',
});
const html = await res.text();
const metadata = await scraper({html, url});
return {
uri: url,
title: metadata.title ?? '',
description: metadata.description ?? '',
thumb_url: metadata.image ?? null,
};
}Seems pretty clean. (And ~72KB of dependencies in total, in case it matters.) If you'd prefer to do it that way, I'm happy to submit another PR that adds this to lookup the embed info for the first link in the post. That should cover 90% of use cases. |
I noticed that when my post has a link, Bluesky doesn't automatically add an embed card. For example, compare these two posts:
This PR adds support for Bluesky embed cards, via
embedwith$type: "app.bsky.embed.external"as described here.Here's a test post that was generated via the code in this PR: https://bsky.app/profile/ohmjs.org/post/3meog7agbhw25