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,