feat(route): bluesky user posts (#14130)

This commit is contained in:
Tony 2023-12-28 21:01:23 +00:00 committed by GitHub
parent 526d1ea94d
commit 2eefc7fecc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 121 additions and 0 deletions

View File

@ -1,3 +1,4 @@
module.exports = {
'/keyword/:keyword': ['untitaker'],
'/profile/:handle': ['TonyRL'],
};

42
lib/v2/bsky/posts.js Normal file
View File

@ -0,0 +1,42 @@
const { parseDate } = require('@/utils/parse-date');
const { resolveHandle, getProfile, getAuthorFeed } = require('./utils');
const { art } = require('@/utils/render');
const { join } = require('path');
module.exports = async (ctx) => {
const { handle } = ctx.params;
const DID = await resolveHandle(handle, ctx.cache.tryGet);
const profile = await getProfile(DID, ctx.cache.tryGet);
const authorFeed = await getAuthorFeed(DID, ctx.cache.tryGet);
const items = authorFeed.feed.map(({ post }) => ({
title: post.record.text.split('\n')[0],
description: art(join(__dirname, 'templates/post.art'), {
text: post.record.text.replace(/\n/g, '<br>'),
embed: post.embed,
// embed.$type "app.bsky.embed.record#view" and "app.bsky.embed.recordWithMedia#view"
// are not handled
}),
author: post.author.displayName,
pubDate: parseDate(post.record.createdAt),
link: `https://bsky.app/profile/${post.author.handle}/post/${post.uri.split('app.bsky.feed.post/')[1]}`,
upvotes: post.likeCount,
comments: post.replyCount,
}));
ctx.state.data = {
title: `${profile.displayName} (@${profile.handle}) — Bluesky`,
description: profile.description.replace(/\n/g, ' '),
link: `https://bsky.app/profile/${profile.handle}`,
image: profile.banner,
icon: profile.avatar,
logo: profile.avatar,
item: items,
};
ctx.state.json = {
DID,
profile,
authorFeed,
};
};

View File

@ -8,6 +8,12 @@ module.exports = {
source: '/search',
target: (params, url) => `/bsky/keyword/${new URL(url).searchParams.get('q')}`,
},
{
title: 'Post',
docs: 'https://docs.rsshub.app/routes/social-media#bluesky-bsky',
source: '/profile/:handle',
target: '/bsky/profile/:handle',
},
],
},
};

View File

@ -1,3 +1,4 @@
module.exports = (router) => {
router.get('/keyword/:keyword', require('./keyword'));
router.get('/profile/:handle', require('./posts'));
};

View File

@ -0,0 +1,15 @@
{{ if text }}
{{@ text }}<br>
{{ /if }}
{{ if embed }}
{{ if embed.$type == 'app.bsky.embed.images#view'}}
{{ each embed.images i }}
<img src="{{ i.fullsize }}" alt="{{ i.alt }}"><br>
{{ /each }}
{{ else if embed.$type == 'app.bsky.embed.external#view' }}
<a href="{{ embed.external.uri }}"><b>{{ embed.external.title }}</b><br>
{{ embed.external.description }}
</a>
{{ /if }}
{{ /if }}

52
lib/v2/bsky/utils.js Normal file
View File

@ -0,0 +1,52 @@
const got = require('@/utils/got');
const config = require('@/config').value;
/**
* docs: https://atproto.com/lexicons/app-bsky
*/
// https://github.com/bluesky-social/atproto/blob/main/lexicons/com/atproto/identity/resolveHandle.json
const resolveHandle = (handle, tryGet) =>
tryGet(`bsky:${handle}`, async () => {
const { data } = await got('https://public.api.bsky.app/xrpc/com.atproto.identity.resolveHandle', {
searchParams: {
handle,
},
});
return data.did;
});
// https://github.com/bluesky-social/atproto/blob/main/lexicons/app/bsky/actor/getProfile.json
const getProfile = (did, tryGet) =>
tryGet(`bsky:profile:${did}`, async () => {
const { data } = await got('https://public.api.bsky.app/xrpc/app.bsky.actor.getProfile', {
searchParams: {
actor: did,
},
});
return data;
});
// https://github.com/bluesky-social/atproto/blob/main/lexicons/app/bsky/feed/getAuthorFeed.json
const getAuthorFeed = (did, tryGet) =>
tryGet(
`bsky:authorFeed:${did}`,
async () => {
const { data } = await got('https://public.api.bsky.app/xrpc/app.bsky.feed.getAuthorFeed', {
searchParams: {
actor: did,
filter: 'posts_and_author_threads',
limit: 30,
},
});
return data;
},
config.cache.routeExpire,
false
);
module.exports = {
resolveHandle,
getProfile,
getAuthorFeed,
};

View File

@ -339,6 +339,10 @@
## Bluesky (bsky) {#bluesky-bsky}
### Post {#bluesky-bsky-post}
<Route author="TonyRL" example="/bsky/profile/bsky.app" path="/bsky/profile/:handle" paramsDesc={['User handle, can be found in URL']} radar="1" />
### Keywords {#bluesky-bsky-keywords}
<Route author="untitaker" example="/bsky/keyword/hello" path="/bsky/keyword/:keyword" radar="1" />