Closed3
RSSHUBでBlueskyFeedsの中身を更に取得する
マウント先
- ./docker/app/ttrss/feeds.js:/app/dist/feeds-CBPLXXDU.js:ro # Bsky Feeds
- ./docker/app/ttrss/post.art:/app/dist/templates/post-7b0043bb.art:ro # BskyPost template
feeds.js
import { __dirname, init_esm_shims } from './esm-shims-Dqvxr0BZ.js';
import './config-BpwDbAkH.js';
import './logger-B3QfaIfn.js';
import { parseDate } from './parse-date-D4osZfpm.js';
import './dist-BPTOq7kU.js';
import './helpers-nWIDkd0K.js';
import { cache_default as cache } from './cache-cV3Kcqf-.js';
import { art } from './render-DOz85fGU.js';
import './ofetch-ByTBixob.js';
import './got-D-bXW_9k.js';
import { ViewType } from './types-A5bA50Mg.js';
import { getFeed, getFeedGenerator, resolveHandle } from './utils-D7AD0nLZ.js';
import path from 'node:path';
init_esm_shims();
// 変更済み
export const route = {
path: '/profile/:handle/feed/:space/:routeParams?',
categories: ['social-media'],
view: ViewType.SocialMedia,
example: '/bsky.app/profile/jaz.bsky.social/feed/cv:cat',
parameters: {
handle: 'User handle, can be found in URL',
space: 'Space ID, can be found in URL',
},
features: {
requireConfig: false,
requirePuppeteer: false,
antiCrawler: false,
supportBT: false,
supportPodcast: false,
supportScihub: false,
},
name: 'Feeds',
maintainers: ['FerrisChi'],
handler,
};
async function handler(ctx) {
const handle = ctx.req.param('handle');
const space = ctx.req.param('space');
const DID = await resolveHandle(handle, cache.tryGet);
const uri = `at://${DID}/app.bsky.feed.generator/${space}`;
const profile = await getFeedGenerator(uri, cache.tryGet);
const feeds = await getFeed(uri, cache.tryGet);
const items = feeds.feed.map(({ post }) => {
// 安全なデータクリーンアップ
const safeEmbed = post.embed ? {
...post.embed,
// recordタイプの場合、authorが存在しない場合はrecord自体をnullに
record: (post.embed.record && post.embed.record.author) ? {
...post.embed.record,
author: {
handle: post.embed.record.author.handle || 'unknown',
displayName: post.embed.record.author.displayName || post.embed.record.author.handle || 'Unknown Author'
},
value: post.embed.record.value || { text: '' },
uri: post.embed.record.uri || ''
} : null
} : null;
return {
title: post.record?.text?.split('\n')[0] || 'Untitled Post',
description: art(path.join(__dirname, 'templates/post-7b0043bb.art'), {
text: post.record?.text?.replaceAll('\n', '<br>') || '',
embed: safeEmbed,
}),
author: post.author?.displayName || post.author?.handle || 'Unknown Author',
pubDate: parseDate(post.record?.createdAt),
link: (() => {
const defaultLink = `https://bsky.app/profile/${post.author?.handle || 'unknown'}/post/${post.uri?.split('app.bsky.feed.post/')[1] || ''}`;
const postText = post.record?.text || '';
const sizuMeRegex = /(https:\/\/sizu\.me\/[^\s]+)/;
const match = postText.match(sizuMeRegex);
if (match && match[1]) {
return match[1];
}
// embed.external の場合も考慮する
if (post.embed && post.embed.$type === 'app.bsky.embed.external#view' &&
post.embed.external && post.embed.external.uri?.includes('https://sizu.me/')) {
return post.embed.external.uri;
}
return defaultLink;
})(),
upvotes: post.likeCount || 0,
comments: post.replyCount || 0,
};
});
ctx.set('json', {
DID,
profile,
feeds,
});
return {
title: `${profile.view?.displayName || 'Unknown'} — Bluesky`,
description: profile.view?.description?.replaceAll('\n', ' ') || '',
link: `https://bsky.app/profile/${handle}/feed/${space}`,
image: profile.view?.avatar,
icon: profile.view?.avatar,
logo: profile.view?.avatar,
item: items,
allowEmpty: true,
};
}
post.art
{{ 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.video#view' }}
<video
controls
poster="{{ embed.thumbnail }}"
style="max-width: 100%; height: auto;"
preload="metadata">
<source src="{{ embed.playlist }}" type="application/x-mpegURL">
Your browser does not support HTML5 video playback.
</video><br>
{{ else if embed.$type === 'app.bsky.embed.external#view' }}
<a href="{{ embed.external.uri }}"><b>{{ embed.external.title }}</b><br>
{{ embed.external.description }}
</a>
{{ else if embed.$type === 'app.bsky.embed.record#view' || embed.$type === 'app.bsky.embed.recordWithMedia#view' }}
{{ if embed.record }}
<p>引用ポスト:</p>
{{ if embed.record.author }}
<a href="https://bsky.app/profile/{{ embed.record.author.handle }}/post/{{ embed.record.uri.split('app.bsky.feed.post/')[1] }}">
<b>{{ embed.record.author.displayName || embed.record.author.handle }}</b><br>
{{ if embed.record.value }}
{{ embed.record.value.text.split('\n')[0] }}
{{ /if }}
</a>
{{ else }}
<p>作成者情報が利用できません</p>
{{ /if }}
{{ /if }}
{{ /if }}
{{ /if }}
このスクラップは18日前にクローズされました