rsshub 1.0.0-master.fb6d4f3 → 1.0.0-master.fb7e42c
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/assets/logo.svg +1 -0
- package/lib/config.ts +10 -0
- package/lib/errors/index.test.ts +15 -5
- package/lib/errors/types/captcha.ts +5 -0
- package/lib/middleware/cache.ts +4 -0
- package/lib/middleware/parameter.ts +1 -1
- package/lib/registry.ts +6 -2
- package/lib/routes/10000link/info.ts +1 -1
- package/lib/routes/18comic/album.ts +1 -0
- package/lib/routes/18comic/blogs.ts +1 -0
- package/lib/routes/18comic/search.ts +1 -0
- package/lib/routes/199it/index.ts +1 -1
- package/lib/routes/21caijing/channel.ts +1 -1
- package/lib/routes/51cto/utils.ts +1 -1
- package/lib/routes/6v123/index.ts +487 -0
- package/lib/routes/6v123/namespace.ts +1 -0
- package/lib/routes/8kcos/cat.ts +3 -0
- package/lib/routes/8kcos/latest.ts +1 -0
- package/lib/routes/8kcos/tag.ts +1 -0
- package/lib/routes/91porn/author.ts +1 -0
- package/lib/routes/95mm/category.ts +1 -0
- package/lib/routes/95mm/tag.ts +1 -0
- package/lib/routes/aa1/60s.ts +189 -0
- package/lib/routes/{xinhuanet → aa1}/namespace.ts +3 -3
- package/lib/routes/abc/index.ts +1 -1
- package/lib/routes/abskoop/index.ts +3 -0
- package/lib/routes/abskoop/nsfw.ts +3 -0
- package/lib/routes/acgvinyl/namespace.ts +6 -0
- package/lib/routes/acgvinyl/news.ts +86 -0
- package/lib/routes/ally/rail.ts +1 -1
- package/lib/routes/amazfitwatchfaces/index.ts +1 -2
- package/lib/routes/anthropic/news.ts +13 -11
- package/lib/routes/apnews/mobile-api.ts +1 -1
- package/lib/routes/apnews/sitemap.ts +1 -1
- package/lib/routes/app-sales/index.ts +1 -1
- package/lib/routes/apple/apps.ts +2 -2
- package/lib/routes/apple/podcast.ts +58 -25
- package/lib/routes/apple/security-releases.ts +2 -2
- package/lib/routes/asiafruitchina/categories.ts +1 -1
- package/lib/routes/asiantolick/index.ts +3 -0
- package/lib/routes/asmr-200/index.ts +1 -0
- package/lib/routes/bandisoft/history.ts +2 -2
- package/lib/routes/bangumi.tv/group/reply.ts +1 -1
- package/lib/routes/banshujiang/index.ts +1 -1
- package/lib/routes/banyuetan/index.ts +1 -1
- package/lib/routes/baozimh/index.ts +21 -3
- package/lib/routes/baselang/index.ts +117 -0
- package/lib/routes/baselang/namespace.ts +6 -0
- package/lib/routes/bbc/learningenglish.ts +35 -20
- package/lib/routes/bestofjs/monthly.ts +137 -0
- package/lib/routes/bestofjs/namespace.ts +7 -0
- package/lib/routes/bestofjs/templates/description.art +36 -0
- package/lib/routes/bilibili/cache.ts +17 -1
- package/lib/routes/bilibili/dynamic.ts +34 -8
- package/lib/routes/bilibili/page.ts +1 -1
- package/lib/routes/bilibili/ranking.ts +25 -17
- package/lib/routes/bilibili/wasm-exec.ts +1 -1
- package/lib/routes/bilibili/weekly-recommend.ts +22 -6
- package/lib/routes/bjp/apod.ts +1 -1
- package/lib/routes/bnext/index.ts +58 -0
- package/lib/routes/bnext/namespace.ts +7 -0
- package/lib/routes/bookwalker/namespace.ts +9 -0
- package/lib/routes/bookwalker/search.ts +114 -0
- package/lib/routes/bookwalker/templates/description.art +13 -0
- package/lib/routes/booru/mmda.ts +1 -0
- package/lib/routes/buaa/jiaowu.ts +1 -1
- package/lib/routes/bullionvault/gold-news.ts +4 -4
- package/lib/routes/cast/index.ts +1 -1
- package/lib/routes/cbndata/information.ts +250 -0
- package/lib/routes/cbndata/namespace.ts +9 -0
- package/lib/routes/cbndata/templates/description.art +17 -0
- package/lib/routes/cccmc/index.ts +1 -1
- package/lib/routes/ccg/index.ts +181 -0
- package/lib/routes/ccg/namespace.ts +9 -0
- package/lib/routes/ccg/templates/description.art +21 -0
- package/lib/routes/chikubi/category.ts +1 -0
- package/lib/routes/chikubi/navigation.ts +1 -0
- package/lib/routes/chikubi/nipple-video-category.ts +1 -0
- package/lib/routes/chikubi/nipple-video-maker.ts +1 -0
- package/lib/routes/chikubi/search.ts +1 -0
- package/lib/routes/chinadaily/language.ts +1 -1
- package/lib/routes/chinaratings/credit-research.ts +1 -1
- package/lib/routes/chub/characters.ts +3 -0
- package/lib/routes/civitai/discussions.ts +1 -0
- package/lib/routes/civitai/models.ts +1 -0
- package/lib/routes/civitai/user.ts +3 -0
- package/lib/routes/cline/blog.ts +34 -23
- package/lib/routes/coolapk/utils.ts +5 -4
- package/lib/routes/coolbuy/index.ts +106 -0
- package/lib/routes/coolbuy/namespace.ts +9 -0
- package/lib/routes/coolbuy/templates/description.art +48 -0
- package/lib/routes/coolidge/film-guide.ts +60 -0
- package/lib/routes/coolidge/namespace.ts +7 -0
- package/lib/routes/coolidge/news.ts +65 -0
- package/lib/routes/coolidge/templates/description.art +4 -0
- package/lib/routes/coomer/index.ts +4 -6
- package/lib/routes/copymanga/comic.ts +2 -1
- package/lib/routes/cosplaytele/category.ts +1 -0
- package/lib/routes/cosplaytele/latest.ts +1 -0
- package/lib/routes/cosplaytele/popular.ts +1 -0
- package/lib/routes/cosplaytele/tag.ts +1 -0
- package/lib/routes/cpta/handler.ts +1 -1
- package/lib/routes/creative-comic/book.ts +1 -1
- package/lib/routes/crush/index.ts +77 -0
- package/lib/routes/crush/namespace.ts +7 -0
- package/lib/routes/cursor/blog.ts +84 -0
- package/lib/routes/daum/potplayer.ts +3 -3
- package/lib/routes/dbaplus/new.ts +392 -0
- package/lib/routes/dbaplus/templates/description.art +21 -0
- package/lib/routes/deepl/blog.ts +2 -3
- package/lib/routes/diariofruticola/filtro.ts +1 -1
- package/lib/routes/digitalpolicyalert/activity-tracker.ts +1 -1
- package/lib/routes/dlsite/campaign.ts +1 -0
- package/lib/routes/dlsite/ci-en/article.ts +1 -0
- package/lib/routes/dlsite/new.ts +1 -0
- package/lib/routes/dlsite/z-index/index.ts +3 -0
- package/lib/routes/dockerhub/utils.ts +1 -1
- package/lib/routes/dora-world/article.ts +2 -2
- package/lib/routes/dytt/index.ts +1 -2
- package/lib/routes/eeo/kuaixun.ts +154 -0
- package/lib/routes/eeo/namespace.ts +9 -0
- package/lib/routes/eeo/templates/description.art +7 -0
- package/lib/routes/ehentai/ehapi.ts +3 -3
- package/lib/routes/ehentai/favorites.ts +1 -0
- package/lib/routes/ehentai/search.ts +1 -0
- package/lib/routes/elecfans/article.ts +64 -0
- package/lib/routes/elecfans/namespace.ts +7 -0
- package/lib/routes/elecfans/soft.ts +69 -0
- package/lib/routes/engineering/namespace.ts +8 -0
- package/lib/routes/engineering/tag.ts +55 -0
- package/lib/routes/eventbrite/events.ts +152 -0
- package/lib/routes/eventbrite/namespace.ts +7 -0
- package/lib/routes/eventernote/actors.ts +61 -64
- package/lib/routes/everia/category.ts +1 -0
- package/lib/routes/everia/latest.ts +1 -0
- package/lib/routes/everia/search.ts +1 -0
- package/lib/routes/everia/tag.ts +1 -0
- package/lib/routes/expats/czech-news.ts +1 -1
- package/lib/routes/fangchan/list.ts +1 -1
- package/lib/routes/fansly/post.ts +1 -0
- package/lib/routes/fantia/search.ts +1 -0
- package/lib/routes/fantia/user.ts +1 -0
- package/lib/routes/finology/tag.ts +1 -1
- package/lib/routes/furaffinity/art.ts +1 -0
- package/lib/routes/furaffinity/browse.ts +1 -0
- package/lib/routes/furaffinity/commissions.ts +1 -0
- package/lib/routes/furaffinity/home.ts +1 -0
- package/lib/routes/furaffinity/journal-comments.ts +1 -0
- package/lib/routes/furaffinity/journals.ts +1 -0
- package/lib/routes/furaffinity/search.ts +1 -0
- package/lib/routes/furaffinity/shouts.ts +1 -0
- package/lib/routes/furaffinity/status.ts +1 -0
- package/lib/routes/furaffinity/submission-comments.ts +1 -0
- package/lib/routes/furaffinity/user.ts +1 -0
- package/lib/routes/furaffinity/watchers.ts +1 -0
- package/lib/routes/furaffinity/watching.ts +1 -0
- package/lib/routes/gamebase/news.ts +2 -2
- package/lib/routes/gamer/gnn-index.ts +8 -3
- package/lib/routes/gcores/categories.ts +1 -1
- package/lib/routes/gcores/collections.ts +1 -1
- package/lib/routes/gcores/tags.ts +1 -1
- package/lib/routes/gcores/topics.ts +1 -1
- package/lib/routes/gdufs/xwxy/index.ts +196 -0
- package/lib/routes/gelbooru/post.ts +1 -0
- package/lib/routes/genossenschaften/index.ts +2 -3
- package/lib/routes/github/activity.ts +1 -1
- package/lib/routes/gitstar-ranking/index.ts +1 -1
- package/lib/routes/go/jihs/idwr.ts +3 -4
- package/lib/routes/google/jules.ts +63 -0
- package/lib/routes/gov/mem/namespace.ts +7 -0
- package/lib/routes/gov/mem/zfxxgkpt.ts +96 -0
- package/lib/routes/gov/moa/gjs.ts +1 -1
- package/lib/routes/gov/mot/index.ts +157 -53
- package/lib/routes/grainoil/category.ts +1 -2
- package/lib/routes/gxmzu/lib.ts +2 -2
- package/lib/routes/gxmzu/utils/index.ts +2 -2
- package/lib/routes/hameln/chapter.ts +1 -1
- package/lib/routes/hanime1/previews.ts +1 -0
- package/lib/routes/hanime1/search.ts +1 -0
- package/lib/routes/hex-rays/index.ts +1 -1
- package/lib/routes/hit/hitgs.ts +1 -1
- package/lib/routes/hit/namespace.ts +1 -1
- package/lib/routes/hpoi/banner-item.ts +28 -11
- package/lib/routes/hpoi/info.ts +1 -1
- package/lib/routes/hudsonrivertrading/index.ts +122 -0
- package/lib/routes/hudsonrivertrading/namespace.ts +7 -0
- package/lib/routes/huggingface/daily-papers.ts +45 -9
- package/lib/routes/huijin-inv/namespace.ts +7 -0
- package/lib/routes/huijin-inv/news.ts +83 -0
- package/lib/routes/hupu/index.ts +170 -74
- package/lib/routes/hupu/types.ts +163 -0
- package/lib/routes/hust/gs.ts +1 -1
- package/lib/routes/hust/mse.ts +1 -1
- package/lib/routes/huxiu/util.ts +2 -2
- package/lib/routes/immich/cursed-knowledge.ts +49 -0
- package/lib/routes/immich/namespace.ts +7 -0
- package/lib/routes/infoq/presentations.ts +1 -1
- package/lib/routes/instagram/common-utils.ts +3 -3
- package/lib/routes/investor/index.ts +362 -148
- package/lib/routes/itch/devlog.ts +7 -3
- package/lib/routes/iwara/index.ts +3 -0
- package/lib/routes/iwara/subscriptions.ts +1 -0
- package/lib/routes/javbus/index.ts +1 -1
- package/lib/routes/javdb/actors.ts +1 -0
- package/lib/routes/javdb/index.ts +3 -0
- package/lib/routes/javdb/lists.ts +3 -0
- package/lib/routes/javdb/makers.ts +1 -0
- package/lib/routes/javdb/rankings.ts +1 -0
- package/lib/routes/javdb/search.ts +1 -0
- package/lib/routes/javdb/series.ts +1 -0
- package/lib/routes/javdb/videocodes.ts +1 -0
- package/lib/routes/javlibrary/bestrated.ts +3 -0
- package/lib/routes/javlibrary/bestreviews.ts +1 -0
- package/lib/routes/javlibrary/genre.ts +3 -0
- package/lib/routes/javlibrary/maker.ts +1 -0
- package/lib/routes/javlibrary/mostwanted.ts +3 -0
- package/lib/routes/javlibrary/newentries.ts +3 -0
- package/lib/routes/javlibrary/newrelease.ts +3 -0
- package/lib/routes/javlibrary/update.ts +3 -0
- package/lib/routes/javlibrary/user.ts +3 -0
- package/lib/routes/javlibrary/utils.ts +1 -1
- package/lib/routes/javtrailers/categories.ts +3 -0
- package/lib/routes/javtrailers/studios.ts +3 -0
- package/lib/routes/jbma/namespace.ts +17 -0
- package/lib/routes/jbma/report.ts +471 -0
- package/lib/routes/jetbrains/comments.ts +1 -1
- package/lib/routes/jingzhengu/utils.ts +1 -1
- package/lib/routes/jou/utils/index.ts +2 -2
- package/lib/routes/jpxgmn/search.ts +3 -0
- package/lib/routes/jpxgmn/weekly.ts +3 -0
- package/lib/routes/juejin/aicoding.ts +102 -0
- package/lib/routes/juejin/trending.ts +70 -20
- package/lib/routes/juejin/utils.ts +36 -51
- package/lib/routes/jumeili/home.ts +1 -1
- package/lib/routes/kakuyomu/works.ts +1 -1
- package/lib/routes/kemono/index.ts +11 -3
- package/lib/routes/kemono/templates/discord.art +1 -1
- package/lib/routes/kisskiss/blog.ts +1 -0
- package/lib/routes/komiic/comic.ts +2 -1
- package/lib/routes/koyso/index.ts +339 -0
- package/lib/routes/koyso/namespace.ts +9 -0
- package/lib/routes/koyso/templates/description.art +13 -0
- package/lib/routes/kpopping/kpics.ts +3 -4
- package/lib/routes/kpopping/news.ts +2 -2
- package/lib/routes/laimanhua/index.ts +1 -0
- package/lib/routes/letterboxd/index.ts +65 -0
- package/lib/routes/letterboxd/namespace.ts +8 -0
- package/lib/routes/lhratings/research.ts +1 -1
- package/lib/routes/literotica/category.ts +3 -0
- package/lib/routes/lovelive-anime/topics.ts +5 -6
- package/lib/routes/maccms/index.ts +1 -1
- package/lib/routes/mangadex/index.ts +3 -0
- package/lib/routes/mangadex/mdlist/feed.ts +1 -0
- package/lib/routes/mangadex/user/feed.ts +1 -0
- package/lib/routes/mangadex/user/follows.ts +1 -0
- package/lib/routes/manhuagui/comic.ts +1 -0
- package/lib/routes/manhuagui/subscribe.ts +1 -0
- package/lib/routes/manyvids/video.ts +3 -0
- package/lib/routes/mercari/util.ts +1 -1
- package/lib/routes/mihoyo/sr/news.ts +2 -1
- package/lib/routes/mihoyo/zzz/news.ts +2 -1
- package/lib/routes/mingpao/index.ts +1 -1
- package/lib/routes/musify/index.ts +2 -3
- package/lib/routes/mycard520/news.ts +2 -2
- package/lib/routes/nankai/ai-notice.ts +142 -0
- package/lib/routes/nankai/graduate-notice.ts +162 -0
- package/lib/routes/natgeo/natgeo.ts +3 -2
- package/lib/routes/naturalism/namespace.ts +7 -0
- package/lib/routes/naturalism/new.ts +59 -0
- package/lib/routes/netflav/index.ts +3 -0
- package/lib/routes/nhentai/search.ts +1 -0
- package/lib/routes/nhk/news-web-easy.ts +1 -1
- package/lib/routes/nicovideo/mylist.ts +39 -0
- package/lib/routes/nicovideo/types.ts +27 -0
- package/lib/routes/nicovideo/utils.ts +27 -1
- package/lib/routes/nikkei/cn/index.ts +1 -4
- package/lib/routes/nio/nioradio.ts +1 -1
- package/lib/routes/njxzc/utils/index.ts +2 -2
- package/lib/routes/notion/namespace.ts +2 -2
- package/lib/routes/now/news.ts +1 -1
- package/lib/routes/nytimes/index.ts +1 -1
- package/lib/routes/olevod/vod.ts +3 -0
- package/lib/routes/olevod/vodlist.ts +3 -0
- package/lib/routes/oreno3d/main.ts +1 -0
- package/lib/routes/oschina/column.ts +1 -2
- package/lib/routes/oschina/event.ts +1 -1
- package/lib/routes/osu/beatmaps/latest-ranked.ts +2 -3
- package/lib/routes/papers/category.ts +3 -4
- package/lib/routes/patreon/feed.ts +1 -0
- package/lib/routes/picuki/profile.ts +13 -5
- package/lib/routes/pixiv/bookmarks.ts +1 -0
- package/lib/routes/pixiv/illustfollow.ts +1 -0
- package/lib/routes/pixiv/novel-api/user-novels/sfw.ts +1 -1
- package/lib/routes/pixiv/novel-series.ts +17 -3
- package/lib/routes/pixiv/novels.ts +1 -0
- package/lib/routes/pixiv/ranking.ts +1 -0
- package/lib/routes/pixiv/search.ts +1 -0
- package/lib/routes/pixiv/user.ts +1 -0
- package/lib/routes/pixivision/utils.ts +1 -1
- package/lib/routes/pku/hr.ts +1 -1
- package/lib/routes/pku/scc/recruit.ts +1 -1
- package/lib/routes/pornhub/category-url.ts +1 -0
- package/lib/routes/pornhub/category.ts +1 -0
- package/lib/routes/pornhub/pornstar.ts +1 -0
- package/lib/routes/pornhub/search.ts +1 -0
- package/lib/routes/pornhub/users.ts +1 -0
- package/lib/routes/producthunt/templates/description.art +2 -2
- package/lib/routes/producthunt/today.ts +17 -8
- package/lib/routes/ps/trophy.ts +1 -1
- package/lib/routes/pubscholar/utils.ts +14 -1
- package/lib/routes/qq/lol/news.ts +1 -1
- package/lib/routes/qweather/3days.ts +14 -14
- package/lib/routes/qweather/now.ts +12 -10
- package/lib/routes/qweather/util.tsx +89 -0
- package/lib/routes/qwenlm/blog.ts +75 -0
- package/lib/routes/qwenlm/namespace.ts +6 -0
- package/lib/routes/radio-canada/latest.ts +30 -17
- package/lib/routes/rawkuma/manga.ts +1 -0
- package/lib/routes/ruc/ai.ts +1 -1
- package/lib/routes/ruc/hr.ts +1 -1
- package/lib/routes/samrdprc/index.ts +241 -0
- package/lib/routes/samrdprc/namespace.ts +1 -1
- package/lib/routes/scientificamerican/podcast.ts +2 -2
- package/lib/routes/scitechvista/index.ts +99 -0
- package/lib/routes/scitechvista/namespace.ts +7 -0
- package/lib/routes/scitechvista/templates/description.art +6 -0
- package/lib/routes/scoop/apps.ts +2 -4
- package/lib/routes/sdo/ff14risingstones/api.ts +78 -0
- package/lib/routes/sdo/ff14risingstones/constant.ts +338 -0
- package/lib/routes/sdo/ff14risingstones/posts.ts +80 -0
- package/lib/routes/sdo/ff14risingstones/strats.ts +75 -0
- package/lib/routes/sdo/ff14risingstones/templates/duties-party.art +41 -0
- package/lib/routes/sdo/ff14risingstones/templates/fc-party.art +26 -0
- package/lib/routes/sdo/ff14risingstones/templates/novice-network-party.art +9 -0
- package/lib/routes/sdo/ff14risingstones/templates/rp-party.art +15 -0
- package/lib/routes/sdo/ff14risingstones/timeline.ts +31 -0
- package/lib/routes/sdo/ff14risingstones/types/dynamic.ts +50 -0
- package/lib/routes/sdo/ff14risingstones/types/index.ts +3 -0
- package/lib/routes/sdo/ff14risingstones/types/other.ts +57 -0
- package/lib/routes/sdo/ff14risingstones/types/party.ts +111 -0
- package/lib/routes/sdo/ff14risingstones/user-dynamics.ts +32 -0
- package/lib/routes/sdo/ff14risingstones/user-posts.ts +32 -0
- package/lib/routes/sdo/ff14risingstones/user-resently.ts +38 -0
- package/lib/routes/sdo/ff14risingstones/user-strats.ts +32 -0
- package/lib/routes/sdo/ff14risingstones/utils.ts +215 -0
- package/lib/routes/sdo/namespace.ts +7 -0
- package/lib/routes/secretsanfrancisco/namespace.ts +7 -0
- package/lib/routes/secretsanfrancisco/rss.ts +101 -0
- package/lib/routes/secretsanfrancisco/templates/description.art +9 -0
- package/lib/routes/sehuatang/user.ts +1 -0
- package/lib/routes/showstart/utils.ts +1 -1
- package/lib/routes/shu/global.ts +6 -5
- package/lib/routes/shu/xxgk.ts +107 -0
- package/lib/routes/shuiguopai/index.ts +3 -0
- package/lib/routes/sis001/author.ts +1 -0
- package/lib/routes/sjtu/jwc.ts +1 -1
- package/lib/routes/skeb/following-creators.ts +1 -0
- package/lib/routes/skeb/following-works.ts +1 -0
- package/lib/routes/skeb/friend-works.ts +1 -0
- package/lib/routes/skeb/index.ts +1 -0
- package/lib/routes/skeb/search.ts +1 -0
- package/lib/routes/skeb/works.ts +44 -25
- package/lib/routes/skebetter/illust.ts +1 -0
- package/lib/routes/skebetter/index.ts +1 -0
- package/lib/routes/skebetter/manga.ts +1 -0
- package/lib/routes/sohu/mp.ts +1 -1
- package/lib/routes/sotwe/user.ts +1 -1
- package/lib/routes/sspai/index.ts +5 -1
- package/lib/routes/stcn/index.ts +1 -2
- package/lib/routes/stcn/rank.ts +1 -2
- package/lib/routes/surfshark/blog.ts +273 -77
- package/lib/routes/surfshark/templates/description.art +16 -4
- package/lib/routes/sustainabilitymag/articles.ts +1 -1
- package/lib/routes/syosetu/dev.ts +1 -1
- package/lib/routes/syosetu/ranking-isekai.ts +1 -1
- package/lib/routes/szse/disclosure/listed-notice.ts +44 -6
- package/lib/routes/t66y/post.ts +1 -0
- package/lib/routes/techcrunch/news.ts +1 -0
- package/lib/routes/techpowerup/index.ts +11 -11
- package/lib/routes/techpowerup/namespace.ts +1 -1
- package/lib/routes/techpowerup/review.ts +12 -12
- package/lib/routes/techpowerup/utils.ts +2 -2
- package/lib/routes/telegram/channel-media.ts +249 -0
- package/lib/routes/telegram/channel.ts +5 -4
- package/lib/routes/telegram/stories.ts +130 -0
- package/lib/routes/telegram/tglib/channel.ts +146 -117
- package/lib/routes/telegram/tglib/client.ts +37 -139
- package/lib/routes/tesla/cx.ts +1 -1
- package/lib/routes/test/index.ts +4 -0
- package/lib/routes/thegadgetflow/namespace.ts +7 -0
- package/lib/routes/thegadgetflow/rss.ts +99 -0
- package/lib/routes/thegadgetflow/templates/description.art +9 -0
- package/lib/routes/theverge/index.ts +20 -6
- package/lib/routes/threads/utils.ts +7 -3
- package/lib/routes/tidb/blog.ts +2 -2
- package/lib/routes/tmtpost/column.ts +1 -1
- package/lib/routes/toutiao/user.ts +2 -2
- package/lib/routes/trendforce/namespace.ts +9 -0
- package/lib/routes/trendforce/new.ts +113 -0
- package/lib/routes/tsinghua/news.ts +87 -0
- package/lib/routes/twitter/api/mobile-api/api.ts +1 -1
- package/lib/routes/twitter/api/web-api/api.ts +5 -1
- package/lib/routes/txrjy/fornumtopic.ts +2 -2
- package/lib/routes/typst/universe.ts +1 -1
- package/lib/routes/uber/blog.ts +87 -46
- package/lib/routes/uraaka-joshi/uraaka-joshi-user.ts +1 -0
- package/lib/routes/uraaka-joshi/uraaka-joshi.ts +3 -0
- package/lib/routes/wainao/topics.ts +1 -1
- package/lib/routes/wdfxw/bookfree.ts +1 -1
- package/lib/routes/weibo/utils.ts +17 -9
- package/lib/routes/wikipedia/current-events.ts +411 -0
- package/lib/routes/wikipedia/namespace.ts +7 -0
- package/lib/routes/wnacg/category.ts +3 -0
- package/lib/routes/wnacg/index.ts +3 -0
- package/lib/routes/wnacg/tag.ts +3 -0
- package/lib/routes/wohnnet/index.ts +2 -2
- package/lib/routes/xiaohongshu/user.ts +2 -2
- package/lib/routes/xiaohongshu/util.ts +16 -9
- package/lib/routes/xjtu/ee-jzxx.ts +1 -1
- package/lib/routes/xjtu/zs.ts +1 -1
- package/lib/routes/xsijishe/forum.ts +1 -0
- package/lib/routes/xsijishe/rank.ts +1 -0
- package/lib/routes/xwenming/index.ts +1 -1
- package/lib/routes/yahoo/news/utils.ts +7 -3
- package/lib/routes/ymgal/article.ts +1 -1
- package/lib/routes/yoasobi-music/media.ts +1 -1
- package/lib/routes/youtube/api/google.ts +20 -6
- package/lib/routes/youtube/api/subtitles.ts +86 -0
- package/lib/routes/youtube/api/youtubei.ts +7 -51
- package/lib/routes/youtube/channel.ts +1 -1
- package/lib/routes/youtube/community.ts +4 -4
- package/lib/routes/youtube/user.ts +1 -1
- package/lib/routes/yuanliao/index.ts +1 -1
- package/lib/routes/zaimanhua/comic.ts +1 -0
- package/lib/routes/zaimanhua/update.ts +1 -0
- package/lib/routes/zaker/utils.ts +1 -1
- package/lib/routes/zaobao/interactive.ts +18 -7
- package/lib/routes/zaobao/other.ts +3 -2
- package/lib/routes/zaobao/realtime.ts +3 -2
- package/lib/routes/zaobao/util.tsx +185 -0
- package/lib/routes/zaobao/zaobao.tsx +23 -0
- package/lib/routes/zaobao/znews.ts +3 -2
- package/lib/routes/zhihu/daily-section.ts +19 -24
- package/lib/routes/zhihu/daily.ts +8 -11
- package/lib/routes/zju/cse/index.ts +102 -0
- package/lib/routes/zodgame/forum.ts +1 -0
- package/lib/server.ts +1 -1
- package/lib/types.ts +1 -1
- package/lib/utils/puppeteer-utils.test.ts +5 -5
- package/lib/utils/puppeteer.test.ts +1 -1
- package/lib/views/index.tsx +4 -4
- package/package.json +55 -57
- package/lib/routes/gdufs/xwxy/xwxy-news.ts +0 -90
- package/lib/routes/hit/jwc.ts +0 -86
- package/lib/routes/picuki/utils.ts +0 -19
- package/lib/routes/qweather/templates/3days.art +0 -22
- package/lib/routes/qweather/templates/now.art +0 -16
- package/lib/routes/xinhuanet/app.ts +0 -109
- package/lib/routes/zaobao/templates/zaobao.art +0 -17
- package/lib/routes/zaobao/util.ts +0 -198
|
@@ -1,160 +1,191 @@
|
|
|
1
|
-
|
|
2
|
-
import { client, decodeMedia, getClient, getFilename, getMediaLink, streamDocument, streamThumbnail } from './client';
|
|
3
|
-
import { returnBigInt } from 'telegram/Helpers.js';
|
|
4
|
-
import { HTMLParser } from 'telegram/extensions/html.js';
|
|
1
|
+
/* eslint-disable no-await-in-loop */
|
|
5
2
|
import { DataItem } from '@/types';
|
|
6
|
-
import
|
|
3
|
+
import { Context } from 'hono';
|
|
4
|
+
import { Api } from 'telegram';
|
|
5
|
+
import { HTMLParser } from 'telegram/extensions/html.js';
|
|
6
|
+
import { getClient, getDocument, getFilename, unwrapMedia } from './client';
|
|
7
|
+
import { getDisplayName } from 'telegram/Utils.js';
|
|
8
|
+
import cache from '@/utils/cache';
|
|
9
|
+
import { returnBigInt } from 'telegram/Helpers.js';
|
|
7
10
|
|
|
8
|
-
function
|
|
9
|
-
|
|
10
|
-
|
|
11
|
+
export function getGeoLink(geo: Api.GeoPoint) {
|
|
12
|
+
return `<a href="https://www.google.com/maps/search/?api=1&query=${geo.lat}%2C${geo.long}" target="_blank">Geo LatLon: ${geo.lat}, ${geo.long}</a>`;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export async function getPollResults(client, message, m: Api.MessageMediaPoll) {
|
|
16
|
+
const resultsUpdateResponse = await client.invoke(new Api.messages.GetPollResults({ peer: message.peerId, msgId: message.id }));
|
|
17
|
+
let results: Api.PollResults;
|
|
18
|
+
if (resultsUpdateResponse?.updates[0] instanceof Api.UpdateMessagePoll) {
|
|
19
|
+
results = resultsUpdateResponse.updates[0].results as Api.PollResults;
|
|
11
20
|
}
|
|
12
|
-
const
|
|
13
|
-
|
|
14
|
-
|
|
21
|
+
const txt = `<h4>${m.poll.quiz ? 'Quiz' : 'Poll'}: ${m.poll.question.text}</h4>
|
|
22
|
+
<div><ul>${m.poll.answers
|
|
23
|
+
.map((a) => {
|
|
24
|
+
let answerTxt = a.text.text;
|
|
25
|
+
const result = results.results?.find((r) => r.option.buffer === a.option.buffer);
|
|
26
|
+
if (result && results.totalVoters) {
|
|
27
|
+
answerTxt = `<strong>${Math.round((result.voters / results.totalVoters) * 100)}%</strong>: ${answerTxt}`;
|
|
28
|
+
}
|
|
29
|
+
return `<li>${answerTxt}</li>`;
|
|
30
|
+
})
|
|
31
|
+
.join('')}</ul></div>
|
|
32
|
+
`;
|
|
33
|
+
return txt;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function getMediaLink(src: string, m: Api.TypeMessageMedia) {
|
|
37
|
+
const doc = getDocument(m);
|
|
38
|
+
const mime = doc ? doc.mimeType : '';
|
|
39
|
+
|
|
40
|
+
if (m instanceof Api.MessageMediaPhoto || mime.startsWith('image/')) {
|
|
41
|
+
return `<img src="${src}" alt=""/>`;
|
|
15
42
|
}
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
.map((elem) => returnBigInt(elem));
|
|
23
|
-
if (range.length < 2) {
|
|
24
|
-
if (seg.startsWith('-')) {
|
|
25
|
-
range.unshift(0);
|
|
26
|
-
} else {
|
|
27
|
-
range.push(length);
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
parsedSegs.push(range);
|
|
43
|
+
if (doc && mime.startsWith('video/')) {
|
|
44
|
+
const vid = (doc.attributes.find((t) => t instanceof Api.DocumentAttributeVideo) ?? { w: 1080, h: 720 }) as { w: number; h: number };
|
|
45
|
+
return `<video controls preload="metadata" poster="${src}?thumb" width="${vid.w / 2}" height="${vid.h / 2}"><source src="${src}" type="${mime}"></video>`;
|
|
46
|
+
}
|
|
47
|
+
if (doc && mime.startsWith('audio/')) {
|
|
48
|
+
return `<div>${getAudioTitle(m)}</div><div><audio src="${src}"></audio></div>`;
|
|
31
49
|
}
|
|
32
|
-
return parsedSegs;
|
|
33
|
-
}
|
|
34
50
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
51
|
+
if (doc && mime.startsWith('application/')) {
|
|
52
|
+
let linkText = `${getFilename(m)} (${humanFileSize(doc.size.valueOf())})`;
|
|
53
|
+
if (mime.endsWith('x-tgsticker')) {
|
|
54
|
+
linkText = ''; // remove filename, it's only an animated sticker
|
|
55
|
+
}
|
|
56
|
+
if ((doc.thumbs?.length ?? 0) > 0) {
|
|
57
|
+
linkText = `<div><img src="${src}?thumb" alt=""/></div><div>${linkText}</div>`;
|
|
58
|
+
}
|
|
59
|
+
return `<a href="${src}" target="_blank">${linkText}</a>`;
|
|
60
|
+
}
|
|
61
|
+
if ((m instanceof Api.MessageMediaGeo || m instanceof Api.MessageMediaGeoLive) && m.geo instanceof Api.GeoPoint) {
|
|
62
|
+
return getGeoLink(m.geo);
|
|
63
|
+
}
|
|
64
|
+
if (m instanceof Api.MessageMediaWebPage) {
|
|
65
|
+
return ''; // a link without a document attach, usually is in the message text, so we can skip here
|
|
66
|
+
}
|
|
67
|
+
if (m instanceof Api.MessageMediaContact) {
|
|
68
|
+
return `Contact: <a href="tel:${m.phoneNumber}" target="_blank">${m.firstName} ${m.lastName} ${m.phoneNumber}</a>`;
|
|
69
|
+
// TODO: download vCard as media ?
|
|
40
70
|
}
|
|
41
|
-
if (
|
|
42
|
-
|
|
43
|
-
|
|
71
|
+
if (m instanceof Api.MessageMediaInvoice) {
|
|
72
|
+
let description = m.description;
|
|
73
|
+
if (m.photo?.url) {
|
|
74
|
+
description = `<img src="${m.photo?.url}" /><br />${description}`;
|
|
75
|
+
}
|
|
76
|
+
return `<h4>${m.test ? 'TEST ' : ''}Invoice: ${m.title}</h4><div>${description}</div>`;
|
|
44
77
|
}
|
|
45
78
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
let stream;
|
|
49
|
-
if ('thumb' in ctx.req.query()) {
|
|
50
|
-
try {
|
|
51
|
-
stream = streamThumbnail(media);
|
|
52
|
-
ctx.set('Content-Type', 'image/jpeg');
|
|
53
|
-
} catch {
|
|
54
|
-
ctx.status = 404;
|
|
55
|
-
return ctx.res.end();
|
|
56
|
-
}
|
|
57
|
-
} else {
|
|
58
|
-
ctx.set('Content-Type', media.document.mimeType);
|
|
59
|
-
|
|
60
|
-
ctx.set('Accept-Ranges', 'bytes');
|
|
61
|
-
const range = parseRange(ctx.get('Range'), media.document.size - 1);
|
|
62
|
-
if (range.length > 1) {
|
|
63
|
-
ctx.status = 416; // range not satisfiable
|
|
64
|
-
return ctx.res.end();
|
|
65
|
-
}
|
|
66
|
-
if (range.length === 1) {
|
|
67
|
-
// console.log(`${ctx.method} ${ctx.req.url} Range: ${ctx.get('Range')}`);
|
|
68
|
-
ctx.status = 206; // partial content
|
|
69
|
-
const [offset, limit] = range[0];
|
|
70
|
-
ctx.set('Content-Length', limit - offset + 1);
|
|
71
|
-
ctx.set('Content-Range', `bytes ${offset}-${limit}/${media.document.size}`);
|
|
72
|
-
|
|
73
|
-
const stream = streamDocument(media.document, '', offset, limit);
|
|
74
|
-
for await (const chunk of stream) {
|
|
75
|
-
ctx.res.write(chunk);
|
|
76
|
-
if (ctx.res.closed) {
|
|
77
|
-
break;
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
return ctx.res.end();
|
|
81
|
-
}
|
|
79
|
+
return m.className;
|
|
80
|
+
}
|
|
82
81
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
stream = streamDocument(media.document);
|
|
88
|
-
}
|
|
89
|
-
// const addr = JSON.stringify(ctx.res.socket.address());
|
|
90
|
-
// console.log(`streaming ${ctx.req.param('media')} to ${addr}`);
|
|
82
|
+
function humanFileSize(size: number) {
|
|
83
|
+
const i = size === 0 ? 0 : Math.floor(Math.log(size) / Math.log(1024));
|
|
84
|
+
return (size / Math.pow(1024, i)).toFixed(2) + ' ' + ['B', 'kB', 'MB', 'GB', 'TB'][i];
|
|
85
|
+
}
|
|
91
86
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
}
|
|
97
|
-
// console.log(`writing ${chunk.length / 1024} to ${addr}`);
|
|
98
|
-
ctx.res.write(chunk);
|
|
99
|
-
}
|
|
100
|
-
if ('close' in stream) {
|
|
101
|
-
stream.close();
|
|
87
|
+
export function getAudioTitle(x: Api.TypeMessageMedia) {
|
|
88
|
+
if (x instanceof Api.MessageMediaDocument && x.document instanceof Api.Document) {
|
|
89
|
+
const attr = x.document.attributes.find((x) => x instanceof Api.DocumentAttributeAudio);
|
|
90
|
+
if (attr) {
|
|
91
|
+
return `${attr.performer} - ${attr.title} (${humanDuration(attr.duration)})`;
|
|
102
92
|
}
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
93
|
+
}
|
|
94
|
+
return getFilename(x);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export function humanDuration(seconds: number) {
|
|
98
|
+
const hours = Math.floor(seconds / 3600);
|
|
99
|
+
const minutes = Math.floor((seconds % 3600) / 60);
|
|
100
|
+
const remainingSeconds = seconds % 60;
|
|
101
|
+
|
|
102
|
+
// Format time components with leading zeros if necessary
|
|
103
|
+
const paddedMinutes = String(minutes).padStart(2, '0');
|
|
104
|
+
const paddedSeconds = String(remainingSeconds).padStart(2, '0');
|
|
105
|
+
|
|
106
|
+
// Construct the time string conditionally
|
|
107
|
+
if (hours > 0) {
|
|
108
|
+
return `${hours}:${paddedMinutes}:${paddedSeconds}`; // Show hours, minutes, and seconds
|
|
109
|
+
} else if (minutes > 0) {
|
|
110
|
+
return `${minutes}:${paddedSeconds}`; // Show minutes and seconds
|
|
108
111
|
} else {
|
|
109
|
-
|
|
110
|
-
ctx.write(media.className);
|
|
112
|
+
return `0:${paddedSeconds}`; // Show only seconds
|
|
111
113
|
}
|
|
112
|
-
return ctx.res.end();
|
|
113
114
|
}
|
|
114
115
|
|
|
115
|
-
export default async function handler(ctx) {
|
|
116
|
-
const { username } = ctx.req.param();
|
|
116
|
+
export default async function handler(ctx: Context) {
|
|
117
117
|
const client = await getClient();
|
|
118
|
+
const username = ctx.req.param('username');
|
|
118
119
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
throw new Error(`${username} is not a channel`);
|
|
120
|
+
let peerCache = await cache.get(`telegram:inputEntity:${username}`);
|
|
121
|
+
if (!peerCache) {
|
|
122
|
+
const p = await client.getInputEntity(username);
|
|
123
|
+
peerCache = JSON.stringify(p.toJSON());
|
|
124
|
+
await cache.set(`telegram:inputEntity:${username}`, peerCache);
|
|
125
125
|
}
|
|
126
|
+
const peerData = JSON.parse(peerCache, (k, v) => (k === 'channelId' || k === 'accessHash' ? returnBigInt(v) : v));
|
|
127
|
+
const peer = new Api.InputPeerChannel(peerData);
|
|
128
|
+
|
|
129
|
+
const entity = await client.getEntity(peer);
|
|
126
130
|
|
|
127
131
|
let attachments: string[] = [];
|
|
128
|
-
const messages = await client.getMessages(
|
|
132
|
+
const messages = await client.getMessages(peer, { limit: 50 });
|
|
129
133
|
|
|
134
|
+
let i = 0;
|
|
135
|
+
const item: DataItem[] = [];
|
|
130
136
|
for (const message of messages) {
|
|
131
|
-
|
|
137
|
+
let text = message.text; // must not be HTML
|
|
138
|
+
|
|
139
|
+
if (message.fwdFrom?.fromId) {
|
|
140
|
+
const fwdFrom = await client.getEntity(message.fwdFrom.fromId);
|
|
141
|
+
text = `Forwarded From: ${getDisplayName(fwdFrom)}: ${text}`;
|
|
142
|
+
}
|
|
143
|
+
const media = await unwrapMedia(message.media, message.peerId);
|
|
144
|
+
if (message.media instanceof Api.MessageMediaStory && media) {
|
|
145
|
+
// if successfully loaded the story
|
|
146
|
+
const storyFrom = await client.getEntity(message.media.peer);
|
|
147
|
+
text = `Story From: ${getDisplayName(storyFrom)}: ${text}`;
|
|
148
|
+
}
|
|
149
|
+
if (media) {
|
|
150
|
+
if (media instanceof Api.MessageMediaPoll) {
|
|
151
|
+
attachments.push(await getPollResults(client, message, media));
|
|
152
|
+
continue;
|
|
153
|
+
}
|
|
132
154
|
// messages that have no text are shown as if they're one post
|
|
133
155
|
// because in TG only 1 attachment per message is possible
|
|
134
|
-
|
|
156
|
+
const src = `${new URL(ctx.req.url).origin}/telegram/media/${username}/${message.id}`;
|
|
157
|
+
attachments.push(getMediaLink(src, media));
|
|
135
158
|
}
|
|
136
|
-
if (message.
|
|
137
|
-
|
|
159
|
+
if (message.replyMarkup instanceof Api.ReplyInlineMarkup) {
|
|
160
|
+
for (const buttonRow of message.replyMarkup.rows) {
|
|
161
|
+
for (const button of buttonRow.buttons) {
|
|
162
|
+
if (button instanceof Api.KeyboardButtonUrl) {
|
|
163
|
+
attachments.push(`<div><a href="${button.url}" target="_blank">${button.text}</a></div>`);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
if (text !== '' || ++i === messages.length - 1) {
|
|
169
|
+
let description = attachments.join('<br/>\n');
|
|
138
170
|
attachments = []; // emitting these, buffer other ones
|
|
139
171
|
|
|
140
|
-
if (
|
|
172
|
+
if (text) {
|
|
141
173
|
description += `<p>${HTMLParser.unparse(message.message, message.entities).replaceAll('\n', '<br/>')}</p>`;
|
|
142
174
|
}
|
|
143
175
|
|
|
144
176
|
const title = message.text ? message.text.slice(0, 80) + (message.text.length > 80 ? '...' : '') : new Date(message.date * 1000).toUTCString();
|
|
145
|
-
|
|
146
177
|
item.push({
|
|
147
178
|
title,
|
|
148
179
|
description,
|
|
149
180
|
pubDate: new Date(message.date * 1000).toUTCString(),
|
|
150
181
|
link: `https://t.me/s/${username}/${message.id}`,
|
|
151
|
-
author:
|
|
182
|
+
author: getDisplayName(message.sender ?? entity),
|
|
152
183
|
});
|
|
153
184
|
}
|
|
154
185
|
}
|
|
155
186
|
|
|
156
187
|
return {
|
|
157
|
-
title:
|
|
188
|
+
title: getDisplayName(entity),
|
|
158
189
|
language: null,
|
|
159
190
|
link: `https://t.me/${username}`,
|
|
160
191
|
item,
|
|
@@ -162,5 +193,3 @@ export default async function handler(ctx) {
|
|
|
162
193
|
description: `@${username} on Telegram`,
|
|
163
194
|
};
|
|
164
195
|
}
|
|
165
|
-
|
|
166
|
-
export { getMedia };
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { Api, TelegramClient } from 'telegram';
|
|
2
2
|
import { UserAuthParams } from 'telegram/client/auth';
|
|
3
3
|
import { StringSession } from 'telegram/sessions/index.js';
|
|
4
|
-
import { getAppropriatedPartSize } from 'telegram/Utils.js';
|
|
5
4
|
|
|
6
5
|
import { config } from '@/config';
|
|
7
6
|
import ConfigNotFoundError from '@/errors/types/config-not-found';
|
|
@@ -34,160 +33,59 @@ export async function getClient(authParams?: UserAuthParams, session?: string) {
|
|
|
34
33
|
: undefined,
|
|
35
34
|
});
|
|
36
35
|
|
|
37
|
-
await client.
|
|
36
|
+
await client.start(
|
|
37
|
+
Object.assign(authParams ?? {}, {
|
|
38
|
+
onError: (err: Error) => {
|
|
39
|
+
throw new Error('Cannot start TG: ' + err);
|
|
40
|
+
},
|
|
41
|
+
}) as any
|
|
42
|
+
);
|
|
38
43
|
return client;
|
|
39
44
|
}
|
|
40
45
|
|
|
41
|
-
function
|
|
42
|
-
const i = size === 0 ? 0 : Math.floor(Math.log(size) / Math.log(1024));
|
|
43
|
-
return (size / Math.pow(1024, i)).toFixed(2) * 1 + ' ' + ['B', 'kB', 'MB', 'GB', 'TB'][i];
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
/**
|
|
47
|
-
* https://core.telegram.org/api/files#stripped-thumbnails
|
|
48
|
-
* @param bytes Buffer
|
|
49
|
-
* @returns Buffer jpeg
|
|
50
|
-
*/
|
|
51
|
-
function ExpandInlineBytes(bytes) {
|
|
52
|
-
if (bytes.length < 3 || bytes[0] !== 0x1) {
|
|
53
|
-
return [];
|
|
54
|
-
}
|
|
55
|
-
const header = Buffer.from([
|
|
56
|
-
0xFF, 0xD8, 0xFF, 0xE0, 0x00, 0x10, 0x4A, 0x46, 0x49, 0x46, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0xFF, 0xDB, 0x00, 0x43, 0x00, 0x28, 0x1C, 0x1E, 0x23, 0x1E, 0x19, 0x28, 0x23, 0x21, 0x23, 0x2D, 0x2B,
|
|
57
|
-
0x28, 0x30, 0x3C, 0x64, 0x41, 0x3C, 0x37, 0x37, 0x3C, 0x7B, 0x58, 0x5D, 0x49, 0x64, 0x91, 0x80, 0x99, 0x96, 0x8F, 0x80, 0x8C, 0x8A, 0xA0, 0xB4, 0xE6, 0xC3, 0xA0, 0xAA, 0xDA, 0xAD, 0x8A, 0x8C, 0xC8, 0xFF, 0xCB, 0xDA, 0xEE,
|
|
58
|
-
0xF5, 0xFF, 0xFF, 0xFF, 0x9B, 0xC1, 0xFF, 0xFF, 0xFF, 0xFA, 0xFF, 0xE6, 0xFD, 0xFF, 0xF8, 0xFF, 0xDB, 0x00, 0x43, 0x01, 0x2B, 0x2D, 0x2D, 0x3C, 0x35, 0x3C, 0x76, 0x41, 0x41, 0x76, 0xF8, 0xA5, 0x8C, 0xA5, 0xF8, 0xF8, 0xF8,
|
|
59
|
-
0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8,
|
|
60
|
-
0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xFF, 0xC0, 0x00, 0x11, 0x08, 0x00, 0x00, 0x00, 0x00, 0x03, 0x01, 0x22, 0x00, 0x02, 0x11, 0x01, 0x03, 0x11, 0x01, 0xFF, 0xC4, 0x00, 0x1F, 0x00, 0x00, 0x01, 0x05,
|
|
61
|
-
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0xFF, 0xC4, 0x00, 0xB5, 0x10, 0x00, 0x02, 0x01, 0x03, 0x03, 0x02, 0x04,
|
|
62
|
-
0x03, 0x05, 0x05, 0x04, 0x04, 0x00, 0x00, 0x01, 0x7D, 0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12, 0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, 0x07, 0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xA1, 0x08, 0x23, 0x42, 0xB1, 0xC1,
|
|
63
|
-
0x15, 0x52, 0xD1, 0xF0, 0x24, 0x33, 0x62, 0x72, 0x82, 0x09, 0x0A, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A,
|
|
64
|
-
0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x92, 0x93, 0x94, 0x95, 0x96,
|
|
65
|
-
0x97, 0x98, 0x99, 0x9A, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7,
|
|
66
|
-
0xD8, 0xD9, 0xDA, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0xFF, 0xC4, 0x00, 0x1F, 0x01, 0x00, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
|
|
67
|
-
0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0xFF, 0xC4, 0x00, 0xB5, 0x11, 0x00, 0x02, 0x01, 0x02, 0x04, 0x04, 0x03, 0x04, 0x07, 0x05, 0x04, 0x04, 0x00,
|
|
68
|
-
0x01, 0x02, 0x77, 0x00, 0x01, 0x02, 0x03, 0x11, 0x04, 0x05, 0x21, 0x31, 0x06, 0x12, 0x41, 0x51, 0x07, 0x61, 0x71, 0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91, 0xA1, 0xB1, 0xC1, 0x09, 0x23, 0x33, 0x52, 0xF0, 0x15, 0x62,
|
|
69
|
-
0x72, 0xD1, 0x0A, 0x16, 0x24, 0x34, 0xE1, 0x25, 0xF1, 0x17, 0x18, 0x19, 0x1A, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x53, 0x54, 0x55, 0x56, 0x57,
|
|
70
|
-
0x58, 0x59, 0x5A, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A,
|
|
71
|
-
0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xE2,
|
|
72
|
-
0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0xFF, 0xDA, 0x00, 0x0C, 0x03, 0x01, 0x00, 0x02, 0x11, 0x03, 0x11, 0x00, 0x3F, 0x00,
|
|
73
|
-
]);
|
|
74
|
-
const footer = Buffer.from([0xFF, 0xD9]);
|
|
75
|
-
const real = Buffer.alloc(header.length + bytes.length + footer.length);
|
|
76
|
-
header.copy(real);
|
|
77
|
-
bytes.copy(real, header.length, 3);
|
|
78
|
-
bytes.copy(real, 164, 1, 2);
|
|
79
|
-
bytes.copy(real, 166, 2, 3);
|
|
80
|
-
footer.copy(real, header.length + bytes.length, 0);
|
|
81
|
-
return real;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
function getMediaLink(ctx, channel: Api.InputPeerChannel, channelName: string, message: Api.Message) {
|
|
85
|
-
const base = `${ctx.protocol}://${ctx.host}/telegram/channel/${channelName}`;
|
|
86
|
-
const src = base + `${channel.channelId}_${message.id}`;
|
|
87
|
-
|
|
88
|
-
const x = message.media;
|
|
89
|
-
if (x instanceof Api.MessageMediaPhoto || (x instanceof Api.MessageMediaDocument && x.document.mimeType.startsWith('image/'))) {
|
|
90
|
-
return `<img src="${src}" alt=""/>`;
|
|
91
|
-
}
|
|
92
|
-
if (x instanceof Api.MessageMediaDocument && x.document.mimeType.startsWith('video/')) {
|
|
93
|
-
const vid = x.document.attributes.find((t) => t.className === 'DocumentAttributeVideo') ?? { w: 1080, h: 720 };
|
|
94
|
-
return `<video controls preload="metadata" poster="${src}?thumb" width="${vid.w / 2}" height="${vid.h / 2}"><source src="${src}" type="${x.document.mimeType}"></video>`;
|
|
95
|
-
}
|
|
96
|
-
if (x instanceof Api.MessageMediaDocument && x.document.mimeType.startsWith('audio/')) {
|
|
97
|
-
return `<audio src="${src}"></audio>`;
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
let linkText = getFilename(x);
|
|
101
|
-
if (x instanceof Api.MessageMediaDocument) {
|
|
102
|
-
linkText += ` (${humanFileSize(x.document.size)})`;
|
|
103
|
-
return `<a href="${src}" target="_blank"><img src="${src}?thumb" alt=""/><br/>${linkText}</a>`;
|
|
104
|
-
}
|
|
105
|
-
return '';
|
|
106
|
-
}
|
|
107
|
-
function getFilename(x) {
|
|
46
|
+
export function getFilename(x: Api.TypeMessageMedia) {
|
|
108
47
|
if (x instanceof Api.MessageMediaDocument) {
|
|
109
|
-
const
|
|
110
|
-
|
|
111
|
-
|
|
48
|
+
for (const a of (x.document as Api.Document).attributes) {
|
|
49
|
+
if (a instanceof Api.DocumentAttributeFilename) {
|
|
50
|
+
return a.fileName;
|
|
51
|
+
}
|
|
112
52
|
}
|
|
113
53
|
}
|
|
114
54
|
return x.className;
|
|
115
55
|
}
|
|
116
56
|
|
|
117
|
-
function
|
|
118
|
-
if (
|
|
119
|
-
return
|
|
57
|
+
export function getDocument(m: Api.TypeMessageMedia) {
|
|
58
|
+
if (m instanceof Api.MessageMediaDocument && m.document && !(m.document instanceof Api.DocumentEmpty)) {
|
|
59
|
+
return m.document;
|
|
120
60
|
}
|
|
121
|
-
if (
|
|
122
|
-
return
|
|
61
|
+
if (m instanceof Api.MessageMediaWebPage && m.webpage instanceof Api.WebPage && m.webpage.document instanceof Api.Document) {
|
|
62
|
+
return m.webpage.document;
|
|
123
63
|
}
|
|
124
|
-
if (thumb instanceof Api.PhotoSize) {
|
|
125
|
-
return thumb.size;
|
|
126
|
-
}
|
|
127
|
-
if (thumb instanceof Api.PhotoSizeProgressive) {
|
|
128
|
-
return Math.max(...thumb.sizes);
|
|
129
|
-
}
|
|
130
|
-
if (thumb instanceof Api.VideoSize) {
|
|
131
|
-
return thumb.size;
|
|
132
|
-
}
|
|
133
|
-
return 0;
|
|
134
64
|
}
|
|
135
65
|
|
|
136
|
-
function
|
|
137
|
-
|
|
138
|
-
|
|
66
|
+
export async function getStory(entity: Api.TypeEntityLike, id: number) {
|
|
67
|
+
const result = await (
|
|
68
|
+
await getClient()
|
|
69
|
+
).invoke(
|
|
70
|
+
new Api.stories.GetStoriesByID({
|
|
71
|
+
id: [id],
|
|
72
|
+
peer: entity,
|
|
73
|
+
})
|
|
74
|
+
);
|
|
75
|
+
return result.stories[0] as Api.StoryItem;
|
|
139
76
|
}
|
|
140
77
|
|
|
141
|
-
function
|
|
142
|
-
if (
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
return (function* () {
|
|
146
|
-
yield ExpandInlineBytes(size.bytes);
|
|
147
|
-
})();
|
|
78
|
+
export async function unwrapMedia(media: Api.TypeMessageMedia | undefined, backupPeerId?: Api.TypePeer) {
|
|
79
|
+
if (media instanceof Api.MessageMediaStory) {
|
|
80
|
+
if (media.story instanceof Api.StoryItem && media.story.media) {
|
|
81
|
+
return media.story.media;
|
|
148
82
|
}
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
async function decodeMedia(channelName, x, retry = false) {
|
|
155
|
-
const [channel, msg] = x.split('_');
|
|
156
|
-
|
|
157
|
-
try {
|
|
158
|
-
const msgs = await client.getMessages(channel, {
|
|
159
|
-
ids: [Number(msg)],
|
|
160
|
-
});
|
|
161
|
-
return msgs[0]?.media;
|
|
162
|
-
} catch (error) {
|
|
163
|
-
if (!retry) {
|
|
164
|
-
// channel likely not seen before, we need to resolve ID and retry
|
|
165
|
-
await client.getInputEntity(channelName);
|
|
166
|
-
return decodeMedia(channelName, x, true);
|
|
83
|
+
let storyItem = await getStory(media.peer, media.id);
|
|
84
|
+
if (!storyItem?.media && backupPeerId) {
|
|
85
|
+
// it's possible the story got hidden by the original user, but we've saved it into Saved Messages - we can still get it
|
|
86
|
+
storyItem = await getStory(backupPeerId, media.id);
|
|
167
87
|
}
|
|
168
|
-
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
function streamDocument(obj, thumbSize = '', offset, limit) {
|
|
173
|
-
const chunkSize = (obj.size ? getAppropriatedPartSize(obj.size) : 64) * 1024;
|
|
174
|
-
const iterFileParams = {
|
|
175
|
-
file: new Api.InputDocumentFileLocation({
|
|
176
|
-
id: obj.id,
|
|
177
|
-
accessHash: obj.accessHash,
|
|
178
|
-
fileReference: obj.fileReference,
|
|
179
|
-
thumbSize,
|
|
180
|
-
}),
|
|
181
|
-
chunkSize,
|
|
182
|
-
dcId: obj.dcId,
|
|
183
|
-
};
|
|
184
|
-
if (offset) {
|
|
185
|
-
iterFileParams.offset = offset;
|
|
186
|
-
}
|
|
187
|
-
if (limit) {
|
|
188
|
-
iterFileParams.limit = limit;
|
|
88
|
+
return storyItem?.media;
|
|
189
89
|
}
|
|
190
|
-
return
|
|
90
|
+
return media;
|
|
191
91
|
}
|
|
192
|
-
|
|
193
|
-
export { client, getMediaLink, decodeMedia, getFilename, streamDocument, streamThumbnail };
|
package/lib/routes/tesla/cx.ts
CHANGED
|
@@ -147,7 +147,7 @@ async function handler(ctx) {
|
|
|
147
147
|
alt: item.venueName ?? item.title,
|
|
148
148
|
}
|
|
149
149
|
: undefined,
|
|
150
|
-
description: item.description?.
|
|
150
|
+
description: item.description?.replaceAll(/\["|"]/g, '') ?? undefined,
|
|
151
151
|
data: item.parkingLocationId
|
|
152
152
|
? {
|
|
153
153
|
title: item.venueName ?? item.title,
|
package/lib/routes/test/index.ts
CHANGED
|
@@ -6,6 +6,7 @@ import cache from '@/utils/cache';
|
|
|
6
6
|
import { fetchArticle } from '@/utils/wechat-mp';
|
|
7
7
|
import ConfigNotFoundError from '@/errors/types/config-not-found';
|
|
8
8
|
import InvalidParameterError from '@/errors/types/invalid-parameter';
|
|
9
|
+
import CaptchaError from '@/errors/types/captcha';
|
|
9
10
|
|
|
10
11
|
let cacheIndex = 0;
|
|
11
12
|
|
|
@@ -32,6 +33,9 @@ async function handler(ctx) {
|
|
|
32
33
|
if (ctx.req.param('id') === 'invalid-parameter-error') {
|
|
33
34
|
throw new InvalidParameterError('Test invalid parameter error');
|
|
34
35
|
}
|
|
36
|
+
if (ctx.req.param('id') === 'captcha-error') {
|
|
37
|
+
throw new CaptchaError('Test captcha error');
|
|
38
|
+
}
|
|
35
39
|
if (ctx.req.param('id') === 'redirect') {
|
|
36
40
|
ctx.set('redirect', '/test/1');
|
|
37
41
|
return;
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { Route } from '@/types';
|
|
2
|
+
import got from '@/utils/got';
|
|
3
|
+
import { parseDate } from '@/utils/parse-date';
|
|
4
|
+
import { art } from '@/utils/render';
|
|
5
|
+
import path from 'node:path';
|
|
6
|
+
import { load } from 'cheerio';
|
|
7
|
+
import cache from '@/utils/cache';
|
|
8
|
+
|
|
9
|
+
export const route: Route = {
|
|
10
|
+
path: '/:category?',
|
|
11
|
+
categories: ['shopping'],
|
|
12
|
+
example: '/thegadgetflow/cool-gadgets-gifts',
|
|
13
|
+
parameters: { category: 'category name, can be found in url' },
|
|
14
|
+
features: {
|
|
15
|
+
requireConfig: false,
|
|
16
|
+
requirePuppeteer: false,
|
|
17
|
+
antiCrawler: false,
|
|
18
|
+
supportBT: false,
|
|
19
|
+
supportPodcast: false,
|
|
20
|
+
supportScihub: false,
|
|
21
|
+
},
|
|
22
|
+
radar: [
|
|
23
|
+
{
|
|
24
|
+
source: ['thegadgetflow.com/categories/:category'],
|
|
25
|
+
target: '/:category',
|
|
26
|
+
},
|
|
27
|
+
],
|
|
28
|
+
name: 'Category',
|
|
29
|
+
maintainers: ['EthanWng97'],
|
|
30
|
+
handler,
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
async function handler(ctx) {
|
|
34
|
+
const baseUrl = 'https://thegadgetflow.com';
|
|
35
|
+
const categoryApiPath = '/wp-json/wp/v2/categories';
|
|
36
|
+
const postApiPath = '/wp-json/wp/v2/posts';
|
|
37
|
+
|
|
38
|
+
// get category number
|
|
39
|
+
const categorySlug = ctx.req.param('category') || '';
|
|
40
|
+
|
|
41
|
+
let category;
|
|
42
|
+
if (categorySlug) {
|
|
43
|
+
category = await cache.tryGet(`${baseUrl}${categoryApiPath}`, async () => {
|
|
44
|
+
const { data } = await got(`${baseUrl}${categoryApiPath}`, {
|
|
45
|
+
searchParams: { slug: categorySlug },
|
|
46
|
+
});
|
|
47
|
+
if (!data || data.length === 0) {
|
|
48
|
+
throw new Error(`Category "${categorySlug}" not found`);
|
|
49
|
+
}
|
|
50
|
+
return data[0];
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const categoryId = category?.id;
|
|
55
|
+
const categoryName = category?.name;
|
|
56
|
+
const categoryLink = category?.link;
|
|
57
|
+
|
|
58
|
+
// get posts
|
|
59
|
+
const postsUrl = `${baseUrl}${postApiPath}`;
|
|
60
|
+
const postsResponse = await got(postsUrl, {
|
|
61
|
+
searchParams: {
|
|
62
|
+
per_page: ctx.req.query('limit') ? Number.parseInt(ctx.req.query('limit')) : 10,
|
|
63
|
+
_embed: '',
|
|
64
|
+
...(categoryId && { categories: categoryId }),
|
|
65
|
+
},
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
const items = postsResponse.data.map((item) => {
|
|
69
|
+
const featuredMedia = item._embedded?.['wp:featuredmedia']?.find((v) => v.id === item.featured_media);
|
|
70
|
+
const image = featuredMedia?.source_url;
|
|
71
|
+
const altText = featuredMedia?.alt_text || featuredMedia?.title?.rendered;
|
|
72
|
+
let caption;
|
|
73
|
+
if (featuredMedia?.caption?.rendered) {
|
|
74
|
+
caption = load(featuredMedia?.caption?.rendered);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const single = {
|
|
78
|
+
title: item.title.rendered,
|
|
79
|
+
description: art(path.join(__dirname, 'templates/description.art'), {
|
|
80
|
+
content: item.content.rendered,
|
|
81
|
+
image,
|
|
82
|
+
altText,
|
|
83
|
+
caption: caption?.text() || '',
|
|
84
|
+
}),
|
|
85
|
+
link: item.link,
|
|
86
|
+
pubDate: parseDate(item.date_gmt),
|
|
87
|
+
updated: parseDate(item.modified_gmt),
|
|
88
|
+
// image,
|
|
89
|
+
author: item._embedded.author[0].name,
|
|
90
|
+
};
|
|
91
|
+
return single;
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
return {
|
|
95
|
+
title: categoryName ? `Gadget Flow - ${categoryName}` : 'Gadget Flow',
|
|
96
|
+
link: categoryLink || `${baseUrl}/${categorySlug}`,
|
|
97
|
+
item: items,
|
|
98
|
+
};
|
|
99
|
+
}
|