From f71d14fbcbd4fe838ee467c9123ab4c9839d9618 Mon Sep 17 00:00:00 2001 From: Tony Date: Fri, 17 Jul 2026 09:31:34 +0800 Subject: [PATCH] feat(route): add ltn (#22741) --- lib/routes/ltn/def.ts | 109 ++++++++++++++++++++++++++++++++++++ lib/routes/ltn/namespace.ts | 7 +++ 2 files changed, 116 insertions(+) create mode 100644 lib/routes/ltn/def.ts create mode 100644 lib/routes/ltn/namespace.ts diff --git a/lib/routes/ltn/def.ts b/lib/routes/ltn/def.ts new file mode 100644 index 000000000..f95b76277 --- /dev/null +++ b/lib/routes/ltn/def.ts @@ -0,0 +1,109 @@ +import { load } from 'cheerio'; +import type { Context } from 'hono'; + +import type { DataItem, Route } from '@/types'; +import cache from '@/utils/cache'; +import ofetch from '@/utils/ofetch'; +import { parseDate } from '@/utils/parse-date'; +import timezone from '@/utils/timezone'; + +const channels = { + breakingnewslist: '軍情動態', + 'list/10': '國際軍情', + 'list/11': '台海軍情', + 'list/22': '軍情看板', + mitlist: '國防MIT', + 'list/12': '國機國造', + 'list/13': '國艦國造', + 'list/14': '潛艦國造', + 'list/23': '飛彈', + 'list/24': '戰甲車國造', + 'list/15': '國防產業', + 'list/25': '其他裝備', + pedialist: '軍武百科', + 'list/16': '圖解軍武', + 'list/17': '陸用裝備', + 'list/18': '海軍系統', + 'list/19': '空軍系統', + historylist: '國防祕辛', + stylelist: '軍風尚', + 'list/27': '將軍官邸故事', + 'list/28': '軍事風餐廳', + 'list/29': '軍風文創', + 'list/30': '軍風世界', + filelist: '軍武書摘', + forumlist: '自由講武堂', + 'list/20': '投書', + 'list/21': '論壇', + peoplelist: '軍情人物', +}; + +export const route: Route = { + path: '/def/:channel{.+}?', + categories: ['traditional-media'], + example: '/ltn/def/breakingnewslist', + parameters: { + channel: { + description: 'Channel, see the table below', + options: Object.entries(channels).map(([value, label]) => ({ value, label })), + default: 'breakingnewslist', + }, + }, + radar: [ + { + source: ['def.ltn.com.tw/:channel'], + target: '/def/:channel', + }, + { + source: ['def.ltn.com.tw/list/:id'], + target: '/def/list/:id', + }, + ], + name: '自由軍武頻道', + maintainers: ['TonyRL'], + handler, + url: 'def.ltn.com.tw', +}; + +async function handler(ctx: Context) { + const { channel = 'breakingnewslist' } = ctx.req.param(); + const baseUrl = 'https://def.ltn.com.tw'; + + const response = await ofetch(`${baseUrl}/ajax/${channel}/1`, { responseType: 'json' }); + + const list: DataItem[] = response.map((item) => ({ + title: item.title, + link: item.url, + pubDate: timezone(parseDate(item.createTime, 'YYYYMMDDHHmmss'), 8), + image: item.url_b, + })); + + const items = await Promise.all( + list.map((item) => + cache.tryGet(item.link!, async () => { + const response = await ofetch(item.link!); + const $ = load(response); + + const content = $('.whitecon:not(.template) div[data-desc="內文"]'); + content.find('.before_ir, .after_ir, [id^="ad"], .appE1121').remove(); + content.find('img[data-src]').each((_, img) => { + $(img).attr('src', $(img).attr('data-src')!).removeAttr('data-src'); + }); + content.find('iframe[src^="https://www.youtube.com/embed/"]').each((_, iframe) => { + $(iframe).attr('referrerpolicy', 'strict-origin-when-cross-origin').removeAttr('allow'); + }); + + item.description = content.html()?.trim(); + return item; + }) + ) + ); + + return { + title: `${channels[channel] ?? ''} - 自由軍武頻道`, + link: `${baseUrl}/${channel}`, + language: 'zh-TW' as const, + image: `${baseUrl}/assets/images/1200_def.png`, + item: items, + }; +} diff --git a/lib/routes/ltn/namespace.ts b/lib/routes/ltn/namespace.ts new file mode 100644 index 000000000..751831642 --- /dev/null +++ b/lib/routes/ltn/namespace.ts @@ -0,0 +1,7 @@ +import type { Namespace } from '@/types'; + +export const namespace: Namespace = { + name: '自由時報', + url: 'ltn.com.tw', + lang: 'zh-TW', +};