From 9b15262fc982a0da791edc2cf07fb946eedf2ec7 Mon Sep 17 00:00:00 2001 From: Qiang Huang Date: Sun, 8 Jun 2025 01:45:57 -0700 Subject: [PATCH] feat: add route smartlink.bio (#19329) --- lib/routes/smartlink/index.ts | 74 +++++++++++++++++++++++++++++++ lib/routes/smartlink/namespace.ts | 7 +++ 2 files changed, 81 insertions(+) create mode 100644 lib/routes/smartlink/index.ts create mode 100644 lib/routes/smartlink/namespace.ts diff --git a/lib/routes/smartlink/index.ts b/lib/routes/smartlink/index.ts new file mode 100644 index 000000000..6aeff223b --- /dev/null +++ b/lib/routes/smartlink/index.ts @@ -0,0 +1,74 @@ +import { Route } from '@/types'; +import { toTitleCase } from '@/utils/common-utils'; +import ofetch from '@/utils/ofetch'; + +function parseTitle(smartlinkUrl: string): string { + try { + const url = new URL(smartlinkUrl); + const pathSegments = url.pathname.split('/').filter((segment) => segment.length > 0); + + // Find the segment after the date (YYYY-MM-DD pattern) + const dateRegex = /^\d{4}-\d{2}-\d{2}$/; + const dateIndex = pathSegments.findIndex((segment) => dateRegex.test(segment)); + + // Use the segment after the date if found, otherwise use the last path segment + const titleSlug = dateIndex !== -1 && dateIndex < pathSegments.length - 1 ? pathSegments[dateIndex + 1] : pathSegments.at(-1) || ''; + + // Convert hyphens to spaces and capitalize each word + return toTitleCase(titleSlug.replaceAll('-', ' ')); + } catch { + // If URL parsing fails, return the URL without query parameters as fallback + return smartlinkUrl.split('?')[0]; + } +} + +function getTitle(item: any): string { + return item.type === 'video' + ? item.smartlink.split('?')[0] // For video items, use the full YouTube URL (without query parameters) + : parseTitle(item.smartlink); // Use existing parseTitle for photo items +} + +export const route: Route = { + path: '/:site', + categories: ['social-media'], + example: '/smartlink/bloombergpursuits', + parameters: { site: 'the site attached to smartlink.bio/' }, + radar: [ + { + source: ['smartlink.bio/'], + }, + ], + features: { + requireConfig: false, + requirePuppeteer: false, + antiCrawler: false, + supportBT: false, + supportPodcast: false, + supportScihub: false, + }, + name: 'Posts', + maintainers: ['nickyfoto'], + handler, + description: 'smartlink.bio link in bio takes your audience from Instagram and TikTok to your website in one easy step.', +}; + +async function handler(ctx) { + const site = ctx.req.param('site'); + const link = `https://smartlink.bio/api/instagram/${site}/posts`; + const data = await ofetch(link); + // the API returns text/plain as MIME type + // Parse the JSON string into an array + const parsedData = JSON.parse(data); + const items = parsedData.map((item) => ({ + title: getTitle(item), + link: item.smartlink.split('?')[0], + description: `

`, + pubDate: item.created, + guid: item.id, + })); + return { + title: `@${site} SmartLink`, + link: `https://smartlink.bio/${site}`, + item: items, + }; +} diff --git a/lib/routes/smartlink/namespace.ts b/lib/routes/smartlink/namespace.ts new file mode 100644 index 000000000..2b20468b6 --- /dev/null +++ b/lib/routes/smartlink/namespace.ts @@ -0,0 +1,7 @@ +import type { Namespace } from '@/types'; + +export const namespace: Namespace = { + name: 'SmartLink', + url: 'smartlink.bio', + lang: 'en', +};