From 8fcbaac983b6818d2f1119be485bd93d5258b2a3 Mon Sep 17 00:00:00 2001 From: PolarisStarnor <113868681+PolarisStarnor@users.noreply.github.com> Date: Tue, 2 Dec 2025 22:13:09 -0700 Subject: [PATCH] feat(route/tumblr): Add Tumblr tagged route (#20576) * tumblr.com: Code to access tagged/ endpoint * tumblr.com: Change processPost for tagged endpoint * Tumblr.com: "Tagged" response is a list of posts * tumblr.com: removed unnecessary link * tumblr.com: fixing api key auth as per https://www.tumblr.com/docs/en/api/v2#authentication * tumblr.com: user query params properly * tumblr.com: compliance with Script Standard * tumblr.com: ensuring `api_key` works for posts.ts * tumblr: don't mess up the types this time * tumblr/tagged/:tag Feed Description + Link * /tumblr Can now parse blog answers * /tumblr "answer" is the correct type * /tumblr: Better format for blog answers * /tumblr: fixed misplaced

tag * lib/routes/tumblr/tagged.ts: Removed unnecessary template literals Co-authored-by: Tony * /tumblr/tagged: Only one category * /tumblr: Fixed how the asker's url is handled * Ran ESLint --------- Co-authored-by: Tony --- lib/routes/tumblr/posts.ts | 4 +-- lib/routes/tumblr/tagged.ts | 71 +++++++++++++++++++++++++++++++++++++ lib/routes/tumblr/utils.ts | 12 +++++-- 3 files changed, 82 insertions(+), 5 deletions(-) create mode 100644 lib/routes/tumblr/tagged.ts diff --git a/lib/routes/tumblr/posts.ts b/lib/routes/tumblr/posts.ts index 908fafa68..02b73bfd7 100644 --- a/lib/routes/tumblr/posts.ts +++ b/lib/routes/tumblr/posts.ts @@ -38,7 +38,7 @@ export const route: Route = { supportScihub: false, }, name: 'Posts', - maintainers: ['Rakambda'], + maintainers: ['Rakambda', 'PolarisStarnor'], description: `::: tip Tumblr provides official RSS feeds for non "dashboard only" blogs, for instance [https://biketouring-nearby.tumblr.com](https://biketouring-nearby.tumblr.com/rss). :::`, @@ -55,7 +55,7 @@ async function handler(ctx: Context): Promise { const response = await got.get(`https://api.tumblr.com/v2/blog/${blogIdentifier}/posts`, { searchParams: { - ...utils.generateAuthParams(), + api_key: utils.generateAuthParams(), limit, }, headers: await utils.generateAuthHeaders(), diff --git a/lib/routes/tumblr/tagged.ts b/lib/routes/tumblr/tagged.ts new file mode 100644 index 000000000..b2e21ce6d --- /dev/null +++ b/lib/routes/tumblr/tagged.ts @@ -0,0 +1,71 @@ +import type { Context } from 'hono'; + +import { config } from '@/config'; +import ConfigNotFoundError from '@/errors/types/config-not-found'; +import type { Data, Route } from '@/types'; +import got from '@/utils/got'; +import { fallback, queryToInteger } from '@/utils/readable-social'; + +import utils from './utils'; + +export const route: Route = { + path: '/tagged/:tag', + categories: ['social-media'], + example: '/tumblr/tagged/nature', + parameters: { + tag: 'Tag name (see `https://www.tumblr.com/docs/en/api/v2#tagged--get-posts-with-tag`)', + }, + radar: [], + features: { + requireConfig: [ + { + name: 'TUMBLR_CLIENT_ID', + description: 'Please see above for details.', + }, + { + name: 'TUMBLR_CLIENT_SECRET', + description: 'Please see above for details.', + }, + { + name: 'TUMBLR_REFRESH_TOKEN', + description: 'Please see above for details.', + }, + ], + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: 'Tagged Posts', + maintainers: ['PolarisStarnor'], + handler, +}; + +async function handler(ctx: Context): Promise { + if (!config.tumblr || !config.tumblr.clientId) { + throw new ConfigNotFoundError('Tumblr RSS is disabled due to the lack of relevant config'); + } + + const tag = ctx.req.param('tag'); + const limit = fallback(undefined, queryToInteger(ctx.req.query('limit')), 20); + + const response = await got.get('https://api.tumblr.com/v2/tagged', { + searchParams: { + tag, + api_key: utils.generateAuthParams(), + limit, + }, + headers: await utils.generateAuthHeaders(), + }); + + const posts = response.data.response.map((post: any) => utils.processPost(post)); + + return { + title: `Tumblr - ${tag}`, + description: `Tumblr posts tagged #${tag}`, + link: `https://tumblr.com/tagged/${tag}`, + item: posts, + allowEmpty: true, + }; +} diff --git a/lib/routes/tumblr/utils.ts b/lib/routes/tumblr/utils.ts index 8ac2eb26c..b04dd104f 100644 --- a/lib/routes/tumblr/utils.ts +++ b/lib/routes/tumblr/utils.ts @@ -31,9 +31,7 @@ const generateAuthHeaders: () => Promise<{ Authorization?: string }> = async () }; }; -const generateAuthParams: () => { apiKey?: string } = () => ({ - apiKey: config.tumblr.clientId, -}); +const generateAuthParams: () => string = () => config.tumblr.clientId!; const processPost: (post: any) => DataItem = (post) => { let description = ''; @@ -42,6 +40,13 @@ const processPost: (post: any) => DataItem = (post) => { case 'text': description = post.body; break; + case 'answer': + description += post.asking_url === null ? `

${post.asking_name} asks:

` : `

${post.asking_name} asks:

`; + description += post.question; + description += '
'; + description += `

${post.blog_name} answers:

`; + description += post.answer; + break; case 'photo': for (const photo of post.photos ?? []) { description += `
`; @@ -58,6 +63,7 @@ const processPost: (post: any) => DataItem = (post) => { } return { + author: post.blog_name, id: post.id_string, title: post.summary ?? `New post from ${post.blog_name}`, link: post.post_url,