Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b660dc4613 | ||
|
|
8a6aacc70d | ||
|
|
9fefe96f7b | ||
|
|
819640c683 | ||
|
|
fcdf837435 | ||
|
|
fa9f552d73 | ||
|
|
7e88e142b2 | ||
|
|
b60bb219ec | ||
|
|
3e870e1e37 | ||
|
|
3721fd383a | ||
|
|
8142074b3c | ||
|
|
5a2e2a5e9c | ||
|
|
cb9b320996 | ||
|
|
3b845ce6b5 | ||
|
|
a0941eff63 | ||
|
|
7e6805ca87 | ||
|
|
4b89a57889 | ||
|
|
5627cbef60 | ||
|
|
57dc2c319c | ||
|
|
84543b12b6 | ||
|
|
3584efb09b | ||
|
|
f357e920b7 | ||
|
|
dca03c4f0c | ||
|
|
576137a62f | ||
|
|
a896f15c44 | ||
|
|
f78eead5fd | ||
|
|
6fe8f03f16 | ||
|
|
8e7b35385b | ||
|
|
f2698ba2f0 | ||
|
|
5a07c02ad0 | ||
|
|
ce0baddc78 | ||
|
|
0271066e27 | ||
|
|
f2e4ce36de | ||
|
|
cce09de022 | ||
|
|
9e37d4449f | ||
|
|
aff39c1e1b | ||
|
|
6c22566079 | ||
|
|
cfb48a8c25 |
@@ -1,23 +0,0 @@
|
||||
name: Auto Refresh
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: '*/20 0-17,22,23 * * *'
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
release:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: lts/*
|
||||
|
||||
- name: Refresh
|
||||
env:
|
||||
JWT_TOKEN: ${{ secrets.JWT_TOKEN }}
|
||||
run: npx tsx ./scripts/refresh.ts
|
||||
@@ -1,4 +1,4 @@
|
||||
name: Publish Docker image
|
||||
name: Publish Docker image to Gitea
|
||||
|
||||
on:
|
||||
push:
|
||||
@@ -8,11 +8,12 @@ on:
|
||||
|
||||
jobs:
|
||||
build-docker:
|
||||
name: Push Docker image to multiple registries
|
||||
name: Build and push to Gitea Registry
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
packages: write
|
||||
packages: write # 必须:允许写入软件包
|
||||
contents: read
|
||||
|
||||
steps:
|
||||
- name: Check out the repo
|
||||
uses: actions/checkout@v4
|
||||
@@ -23,18 +24,23 @@ jobs:
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Log in to the Container registry
|
||||
- name: Log in to Gitea Registry
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ghcr.io
|
||||
# 修改点1: 这里填你的 Gitea 域名
|
||||
registry: git.chgr.cc
|
||||
# 修改点2: 使用触发 Action 的 Gitea 用户名
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GGITHUB_TOKEN }}
|
||||
# 修改点3: 直接使用 Gitea 内置的 Token,无需额外配置 Secret!
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Extract metadata (tags, labels) for Docker
|
||||
id: meta
|
||||
uses: docker/metadata-action@v5
|
||||
with:
|
||||
images: ghcr.io/${{ github.repository }}
|
||||
# 修改点4: 这里的格式是 "域名/用户名/仓库名"
|
||||
# 注意:Gitea 镜像名通常要求全小写
|
||||
images: git.chgr.cc/${{ github.repository }}
|
||||
|
||||
- name: Build and push
|
||||
uses: docker/build-push-action@v5
|
||||
@@ -46,4 +52,4 @@ jobs:
|
||||
linux/arm64
|
||||
push: true
|
||||
tags: ${{ steps.meta.outputs.tags }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
@@ -2,7 +2,6 @@ name: Release
|
||||
|
||||
on:
|
||||
push:
|
||||
branch: main
|
||||
tags:
|
||||
- 'v*'
|
||||
|
||||
@@ -14,12 +13,28 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
fetch-depth: 0 # 必须抓取完整历史才能生成日志
|
||||
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: lts/*
|
||||
|
||||
- run: npx changelogithub
|
||||
# 步骤 1: 本地生成 Changelog (不依赖 GitHub API)
|
||||
- name: Generate Changelog
|
||||
run: |
|
||||
npx conventional-changelog-cli -p angular -i CHANGELOG.md -s
|
||||
# 说明: 这会根据你的 commit 记录生成 CHANGELOG.md 文件
|
||||
|
||||
# 步骤 2: 发布 Release 到 Gitea
|
||||
- name: Create Release
|
||||
uses: softprops/action-gh-release@v1
|
||||
with:
|
||||
body_path: CHANGELOG.md # 把生成的内容作为 Release 的描述
|
||||
files: | # 如果你有构建产物想上传,写在这里
|
||||
# dist/app.js
|
||||
# build/*.zip
|
||||
env:
|
||||
GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
# 关键: 如果 softprops 无法自动识别 Gitea,有时需要指定 URL,
|
||||
# 但通常 Gitea Runner 会自动注入 GITHUB_API_URL 环境变量,
|
||||
# 所以大多数情况下直接用即可。
|
||||
@@ -15,6 +15,7 @@ services:
|
||||
- JWT_SECRET=
|
||||
- INIT_TABLE=true
|
||||
- ENABLE_CACHE=true
|
||||
- PRODUCTHUNT_API_TOKEN=
|
||||
|
||||
volumes:
|
||||
newsnow_data:
|
||||
|
||||
+2
-1
@@ -2,4 +2,5 @@ G_CLIENT_ID=
|
||||
G_CLIENT_SECRET=
|
||||
JWT_SECRET=
|
||||
INIT_TABLE=true
|
||||
ENABLE_CACHE=true
|
||||
ENABLE_CACHE=true
|
||||
PRODUCTHUNT_API_TOKEN=
|
||||
+5
-5
@@ -15,14 +15,14 @@
|
||||
<meta property="og:title" content="NewsNow - 优雅的新闻聚合阅读器" />
|
||||
<meta property="og:description" content="实时新闻聚合阅读器,汇集全球热点新闻,提供优雅的阅读体验" />
|
||||
<meta property="og:type" content="website" />
|
||||
<meta property="og:url" content="https://newsnow.busiyi.world" />
|
||||
<meta property="og:image" content="https://newsnow.busiyi.world/og-image.png" />
|
||||
<meta property="og:url" content="https://hot.chgr.cc" />
|
||||
<meta property="og:image" content="https://hot.chgr.cc/og-image.png" />
|
||||
|
||||
<!-- Twitter Card Meta Tags -->
|
||||
<meta name="twitter:card" content="summary_large_image" />
|
||||
<meta name="twitter:title" content="NewsNow - 优雅的新闻聚合阅读器" />
|
||||
<meta name="twitter:description" content="实时新闻聚合阅读器,汇集全球热点新闻,提供优雅的阅读体验" />
|
||||
<meta name="twitter:image" content="https://newsnow.busiyi.world/og-image.svg" />
|
||||
<meta name="twitter:image" content="https://hot.chgr.cc/og-image.svg" />
|
||||
|
||||
<meta name="theme-color" content="#F14D42" />
|
||||
<link rel="preload" href="/Baloo2-Bold.subset.ttf" as="font" type="font/ttf" crossorigin>
|
||||
@@ -34,8 +34,8 @@
|
||||
"@context": "https://schema.org",
|
||||
"@type": "WebSite",
|
||||
"name": "NewsNow",
|
||||
"url": "https://newsnow.busiyi.world",
|
||||
"description": "实时新闻聚合阅读器,汇集全球热点新闻,提供优雅的阅读体验",
|
||||
"url": "https://hot.chgr.cc",
|
||||
"description": "实时新闻聚合阅读器,汇集全球热点新闻,提供优雅的阅读体验"
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "newsnow",
|
||||
"type": "module",
|
||||
"version": "0.0.34",
|
||||
"version": "0.0.37",
|
||||
"private": true,
|
||||
"packageManager": "[email protected]+sha512.ad27a79641b49c3e481a16a805baa71817a04bbe06a38d17e60e2eaee83f6a146c6a688125f5792e48dd5ba30e7da52a5cda4c3992b9ccf333f9ce223af84748",
|
||||
"author": {
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 5.6 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 38 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 3.4 KiB |
+1
-1
@@ -1,7 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
|
||||
<url>
|
||||
<loc>https://newsnow.busiyi.world/</loc>
|
||||
<loc>https://hot.chgr.cc/</loc>
|
||||
<lastmod>2025-01-18</lastmod>
|
||||
<changefreq>always</changefreq>
|
||||
<priority>1.0</priority>
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
import sources from "../shared/sources.json"
|
||||
|
||||
Promise.all(Object.keys(sources).map(id =>
|
||||
fetch(`https://newsnow.busiyi.world/api/s?id=${id}`),
|
||||
)).catch(console.error)
|
||||
Vendored
+4
@@ -8,6 +8,7 @@ declare module 'glob:./sources/{*.ts,**/index.ts}' {
|
||||
export const chongbuluo: typeof import('./sources/chongbuluo')
|
||||
export const cls: typeof import('./sources/cls/index')
|
||||
export const coolapk: typeof import('./sources/coolapk/index')
|
||||
export const douban: typeof import('./sources/douban')
|
||||
export const douyin: typeof import('./sources/douyin')
|
||||
export const fastbull: typeof import('./sources/fastbull')
|
||||
export const gelonghui: typeof import('./sources/gelonghui')
|
||||
@@ -22,6 +23,7 @@ declare module 'glob:./sources/{*.ts,**/index.ts}' {
|
||||
export const kaopu: typeof import('./sources/kaopu')
|
||||
export const kuaishou: typeof import('./sources/kuaishou')
|
||||
export const linuxdo: typeof import('./sources/linuxdo')
|
||||
export const idcflare: typeof import('./sources/idcflare')
|
||||
export const mktnews: typeof import('./sources/mktnews')
|
||||
export const nowcoder: typeof import('./sources/nowcoder')
|
||||
export const pcbeta: typeof import('./sources/pcbeta')
|
||||
@@ -30,6 +32,8 @@ declare module 'glob:./sources/{*.ts,**/index.ts}' {
|
||||
export const solidot: typeof import('./sources/solidot')
|
||||
export const sputniknewscn: typeof import('./sources/sputniknewscn')
|
||||
export const sspai: typeof import('./sources/sspai')
|
||||
export const steam: typeof import('./sources/steam')
|
||||
export const tencent: typeof import('./sources/tencent')
|
||||
export const thepaper: typeof import('./sources/thepaper')
|
||||
export const tieba: typeof import('./sources/tieba')
|
||||
export const toutiao: typeof import('./sources/toutiao')
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
interface HotMoviesRes {
|
||||
category: string
|
||||
tags: []
|
||||
items: MovieItem[]
|
||||
recommend_tags: []
|
||||
total: number
|
||||
type: string
|
||||
}
|
||||
|
||||
interface MovieItem {
|
||||
rating: {
|
||||
count: number
|
||||
max: number
|
||||
star_count: number
|
||||
value: number
|
||||
}
|
||||
title: string
|
||||
pic: {
|
||||
large: string
|
||||
normal: string
|
||||
}
|
||||
is_new: boolean
|
||||
uri: string
|
||||
episodes_info: string
|
||||
card_subtitle: string
|
||||
type: string
|
||||
id: string
|
||||
}
|
||||
|
||||
export default defineSource(async () => {
|
||||
const baseURL = "https://m.douban.com/rexxar/api/v2/subject/recent_hot/movie"
|
||||
const res: HotMoviesRes = await myFetch(baseURL, {
|
||||
headers: {
|
||||
Referer: "https://movie.douban.com/",
|
||||
Accept: "application/json, text/plain, */*",
|
||||
},
|
||||
})
|
||||
return res.items.map(movie => ({
|
||||
id: movie.id,
|
||||
title: movie.title,
|
||||
url: `https://movie.douban.com/subject/${movie.id}`,
|
||||
extra: {
|
||||
info: movie.card_subtitle.split(" / ").slice(0, 3).join(" / "),
|
||||
hover: movie.card_subtitle,
|
||||
},
|
||||
}))
|
||||
})
|
||||
@@ -0,0 +1,61 @@
|
||||
interface Res {
|
||||
topic_list: {
|
||||
can_create_topic: boolean
|
||||
more_topics_url: string
|
||||
per_page: number
|
||||
top_tags: string[]
|
||||
topics: {
|
||||
id: number
|
||||
title: string
|
||||
fancy_title: string
|
||||
posts_count: number
|
||||
reply_count: number
|
||||
highest_post_number: number
|
||||
image_url: null | string
|
||||
created_at: Date
|
||||
last_posted_at: Date
|
||||
bumped: boolean
|
||||
bumped_at: Date
|
||||
unseen: boolean
|
||||
pinned: boolean
|
||||
excerpt?: string
|
||||
visible: boolean
|
||||
closed: boolean
|
||||
archived: boolean
|
||||
like_count: number
|
||||
has_summary: boolean
|
||||
last_poster_username: string
|
||||
category_id: number
|
||||
pinned_globally: boolean
|
||||
}[]
|
||||
}
|
||||
}
|
||||
|
||||
const hot = defineSource(async () => {
|
||||
const res = await myFetch<Res>("https://n8n.chgr.cc/webhook/api?name=idcflare")
|
||||
return res.topic_list.topics
|
||||
.filter(k => k.visible && !k.archived && !k.pinned)
|
||||
.map(k => ({
|
||||
id: k.id,
|
||||
title: k.title,
|
||||
url: `https://idcflare.com/t/topic/${k.id}`,
|
||||
}))
|
||||
})
|
||||
|
||||
const latest = defineSource(async () => {
|
||||
const res = await myFetch<Res>("https://idcflare.com/latest.json?order=created")
|
||||
return res.topic_list.topics
|
||||
.filter(k => k.visible && !k.archived && !k.pinned)
|
||||
.map(k => ({
|
||||
id: k.id,
|
||||
title: k.title,
|
||||
pubDate: new Date(k.created_at).valueOf(),
|
||||
url: `https://idcflare.com/t/topic/${k.id}`,
|
||||
}))
|
||||
})
|
||||
|
||||
export default defineSource({
|
||||
"idcflare": latest,
|
||||
"idcflare-latest": latest,
|
||||
"idcflare-hot": hot,
|
||||
})
|
||||
@@ -2,17 +2,17 @@ type Res = {
|
||||
description: string
|
||||
link: string
|
||||
// Date
|
||||
pubDate: string
|
||||
pub_date: string
|
||||
publisher: string
|
||||
title: string
|
||||
}[]
|
||||
export default defineSource(async () => {
|
||||
const res = await Promise.all(["https://kaopucdn.azureedge.net/jsondata/news_list_beta_hans_0.json", "https://kaopucdn.azureedge.net/jsondata/news_list_beta_hans_1.json"].map(url => myFetch(url) as Promise<Res>))
|
||||
return res.flat().filter(k => ["财新", "公视"].every(h => k.publisher !== h)).map((k) => {
|
||||
const res: Res = await $fetch("https://kaopustorage.blob.core.windows.net/news-prod/news_list_hans_0.json")
|
||||
return res.filter(k => ["财新", "公视"].every(h => k.publisher !== h)).map((k) => {
|
||||
return {
|
||||
id: k.link,
|
||||
title: k.title,
|
||||
pubDate: k.pubDate,
|
||||
pubDate: k.pub_date,
|
||||
extra: {
|
||||
hover: k.description,
|
||||
info: k.publisher,
|
||||
|
||||
@@ -41,17 +41,21 @@ const flash = defineSource(async () => {
|
||||
const typeMap = { policy: "Policy", AI: "AI", financial: "Financial" } as const
|
||||
|
||||
const allReports = categories.flatMap((category) => {
|
||||
const flash_list = res.data.find(item => item.name === category)?.child[0]?.flash_list || []
|
||||
return flash_list.map(item => ({ ...item, type: typeMap[category] }))
|
||||
const categoryData = res.data.find(item => item.name === category)
|
||||
if (!categoryData?.child) return []
|
||||
|
||||
return categoryData.child.flatMap(subCategory =>
|
||||
(subCategory.flash_list || []).map(item => ({ ...item, type: typeMap[category] })),
|
||||
)
|
||||
})
|
||||
|
||||
return allReports
|
||||
.sort((a, b) => b.time.localeCompare(a.time))
|
||||
.map(item => ({
|
||||
id: item.id,
|
||||
title: item.data.title || item.data.content,
|
||||
title: item.data.title || item.data.content.match(/^【([^】]*)】(.*)$/)?.[1] || item.data.content,
|
||||
pubDate: item.time,
|
||||
extra: { info: item.type },
|
||||
extra: { info: item.type, hover: item.data.content },
|
||||
url: `https://mktnews.net/flashDetail.html?id=${item.id}`,
|
||||
}))
|
||||
})
|
||||
|
||||
@@ -1,28 +1,56 @@
|
||||
import * as cheerio from "cheerio"
|
||||
import process from "node:process"
|
||||
import type { NewsItem } from "@shared/types"
|
||||
|
||||
export default defineSource(async () => {
|
||||
const baseURL = "https://www.producthunt.com"
|
||||
const html: any = await myFetch(baseURL)
|
||||
const $ = cheerio.load(html)
|
||||
const $main = $("[data-test=homepage-section-0] [data-test^=post-item]")
|
||||
const apiToken = process.env.PRODUCTHUNT_API_TOKEN
|
||||
const token = `Bearer ${apiToken}`
|
||||
if (!apiToken) {
|
||||
throw new Error("PRODUCTHUNT_API_TOKEN is not set")
|
||||
}
|
||||
const query = `
|
||||
query {
|
||||
posts(first: 30, order: VOTES) {
|
||||
edges {
|
||||
node {
|
||||
id
|
||||
name
|
||||
tagline
|
||||
votesCount
|
||||
url
|
||||
slug
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
const response: any = await myFetch("https://api.producthunt.com/v2/api/graphql", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Authorization": token,
|
||||
"Content-Type": "application/json",
|
||||
"Accept": "application/json",
|
||||
},
|
||||
body: JSON.stringify({ query }),
|
||||
})
|
||||
|
||||
const news: NewsItem[] = []
|
||||
$main.each((_, el) => {
|
||||
const a = $(el).find("a").first()
|
||||
const url = a.attr("href")
|
||||
const title = $(el).find("a[data-test^=post-name]").text().replace(/^\d+\.\s*/, "")
|
||||
const id = $(el).attr("data-test")?.replace("post-item-", "")
|
||||
const vote = $(el).find("[data-test=vote-button]").text()
|
||||
if (url && id && title) {
|
||||
const posts = response?.data?.posts?.edges || []
|
||||
|
||||
for (const edge of posts) {
|
||||
const post = edge.node
|
||||
if (post.id && post.name) {
|
||||
news.push({
|
||||
url: `${baseURL}${url}`,
|
||||
title,
|
||||
id,
|
||||
id: post.id,
|
||||
title: post.name,
|
||||
url: post.url || `https://www.producthunt.com/posts/${post.slug}`,
|
||||
extra: {
|
||||
info: `△︎ ${vote}`,
|
||||
info: ` △︎ ${post.votesCount || 0}`,
|
||||
hover: post.tagline,
|
||||
},
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
return news
|
||||
})
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
import * as cheerio from "cheerio"
|
||||
import type { NewsItem } from "@shared/types"
|
||||
|
||||
export default defineSource(async () => {
|
||||
const response: any = await myFetch("https://store.steampowered.com/stats/stats/")
|
||||
const $ = cheerio.load(response)
|
||||
const $rows = $("#detailStats tr.player_count_row")
|
||||
const news: NewsItem[] = []
|
||||
|
||||
$rows.each((_, el) => {
|
||||
const $el = $(el)
|
||||
const $a = $el.find("a.gameLink")
|
||||
const url = $a.attr("href")
|
||||
const gameName = $a.text().trim()
|
||||
const currentPlayers = $el.find("td:first-child .currentServers").text().trim()
|
||||
|
||||
if (url && gameName && currentPlayers) {
|
||||
const title = gameName
|
||||
news.push({
|
||||
url,
|
||||
title,
|
||||
id: url,
|
||||
pubDate: Date.now(),
|
||||
extra: {
|
||||
info: currentPlayers,
|
||||
},
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
return news
|
||||
})
|
||||
@@ -0,0 +1,63 @@
|
||||
import { myFetch } from "#/utils/fetch"
|
||||
import { defineSource } from "#/utils/source"
|
||||
|
||||
interface WapRes {
|
||||
ret: number
|
||||
msg: string
|
||||
data: {
|
||||
id: number
|
||||
name: string
|
||||
lead: string
|
||||
cover?: string
|
||||
shareTitle: string
|
||||
shareAbstract: string
|
||||
sharePic: string
|
||||
is724: boolean
|
||||
is724Paper: boolean
|
||||
head_cmsid: string
|
||||
feed_style: number
|
||||
head_article: {
|
||||
live_info: string
|
||||
title: string
|
||||
img: string
|
||||
pub_time: string
|
||||
media_name: string
|
||||
}
|
||||
paperInfo: any
|
||||
tabs: {
|
||||
id: string
|
||||
channel_id: string
|
||||
name: string
|
||||
source: string
|
||||
type: string
|
||||
articleList: any[]
|
||||
article_count: number
|
||||
sub_tab: string
|
||||
}[]
|
||||
banner: string
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 综合早报
|
||||
*/
|
||||
const comprehensiveNews = defineSource(async () => {
|
||||
const url = "https://i.news.qq.com/web_backend/v2/getTagInfo?tagId=aEWqxLtdgmQ%3D"
|
||||
const res = await myFetch<WapRes>(url, {
|
||||
headers: {
|
||||
Referer: "https://news.qq.com/",
|
||||
},
|
||||
})
|
||||
return res.data.tabs[0].articleList.map(news => ({
|
||||
id: news.id,
|
||||
title: news.title,
|
||||
url: news.link_info.url,
|
||||
extra: {
|
||||
hover: news.desc,
|
||||
},
|
||||
}))
|
||||
})
|
||||
|
||||
export default defineSource({
|
||||
"tencent-hot": comprehensiveNews,
|
||||
})
|
||||
+47
-139
@@ -1,144 +1,52 @@
|
||||
export interface Root {
|
||||
ok: number
|
||||
data: Data
|
||||
}
|
||||
|
||||
export interface Data {
|
||||
mapi: any[]
|
||||
cards: Card[]
|
||||
cardlistInfo: CardlistInfo
|
||||
scheme: string
|
||||
showAppTips: number
|
||||
}
|
||||
|
||||
export interface Card {
|
||||
card_group: CardGroup[]
|
||||
card_type: number
|
||||
show_type: number
|
||||
itemid?: string
|
||||
title?: string
|
||||
}
|
||||
|
||||
export interface CardGroup {
|
||||
actionlog?: Actionlog
|
||||
card_type: number
|
||||
pic?: string
|
||||
itemid?: string
|
||||
icon?: string
|
||||
icon_width?: number
|
||||
icon_height?: number
|
||||
desc?: string
|
||||
scheme: string
|
||||
desc_extr: any
|
||||
promotion?: Promotion
|
||||
display_arrow?: number
|
||||
show_type?: number
|
||||
title?: string
|
||||
is_show_arrow?: number
|
||||
left_tag_img?: string
|
||||
sub_title?: string
|
||||
}
|
||||
|
||||
export interface Actionlog {
|
||||
luicode: string
|
||||
lfid: string
|
||||
uicode: string
|
||||
act_code: number
|
||||
act_type: number
|
||||
fid: string
|
||||
ext: string
|
||||
}
|
||||
|
||||
export interface Promotion {
|
||||
monitor_url: MonitorUrl[]
|
||||
}
|
||||
|
||||
export interface MonitorUrl {
|
||||
third_party_click: string
|
||||
third_party_show: string
|
||||
type: string
|
||||
monitor_name?: string
|
||||
}
|
||||
|
||||
export interface CardlistInfo {
|
||||
select_id: string
|
||||
title_top: string
|
||||
show_style: number
|
||||
starttime: number
|
||||
can_shared: number
|
||||
v_p: string
|
||||
containerid: string
|
||||
page_type: string
|
||||
nick: string
|
||||
cardlist_head_cards: CardlistHeadCard[]
|
||||
enable_load_imge_scrolling: number
|
||||
page_title: string
|
||||
search_request_id: string
|
||||
load_more_threshold: number
|
||||
pagesize: number
|
||||
page_common_ext: string
|
||||
cardlist_menus: any[]
|
||||
total: number
|
||||
headbg_animation: string
|
||||
page_size: number
|
||||
page: any
|
||||
}
|
||||
|
||||
export interface CardlistHeadCard {
|
||||
title_top?: string
|
||||
show_menu: boolean
|
||||
head_type_name: string
|
||||
head_type: number
|
||||
head_data?: HeadData
|
||||
channel_list?: ChannelList[]
|
||||
}
|
||||
|
||||
export interface HeadData {
|
||||
cover_url: string
|
||||
show_navi_mask: boolean
|
||||
show_title: boolean
|
||||
data_type: number
|
||||
scheme: string
|
||||
}
|
||||
|
||||
export interface ChannelList {
|
||||
actionlog: Actionlog2
|
||||
scheme: string
|
||||
containerid: string
|
||||
name: string
|
||||
default_add: number
|
||||
id: number
|
||||
must_show: number
|
||||
replaced_name?: string
|
||||
}
|
||||
|
||||
export interface Actionlog2 {
|
||||
luicode: string
|
||||
lfid: string
|
||||
uicode: string
|
||||
act_code: number
|
||||
act_type: number
|
||||
fid: string
|
||||
ext: string
|
||||
}
|
||||
import * as cheerio from "cheerio"
|
||||
|
||||
export default defineSource(async () => {
|
||||
const url = "https://m.weibo.cn/api/container/getIndex?containerid=106003type%3D25%26t%3D3%26disable_hot%3D1%26filter_type%3Drealtimehot"
|
||||
const res: Root = await myFetch(url)
|
||||
return res.data.cards[0].card_group
|
||||
.filter((k, i) => i !== 0 && k.desc && !k.actionlog?.ext.includes("ads_word"))
|
||||
.map((k) => {
|
||||
return {
|
||||
id: k.desc!,
|
||||
title: k.desc!,
|
||||
extra: {
|
||||
icon: k.icon && {
|
||||
url: proxyPicture(k.icon),
|
||||
scale: 1.5,
|
||||
const baseurl = "https://s.weibo.com"
|
||||
const url = `${baseurl}/top/summary?cate=realtimehot`
|
||||
|
||||
const html = await myFetch(url, {
|
||||
headers: {
|
||||
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36",
|
||||
// https://github.com/v5tech/weibo-trending-hot-search
|
||||
"Cookie": "SUB=_2AkMWIuNSf8NxqwJRmP8dy2rhaoV2ygrEieKgfhKJJRMxHRl-yT9jqk86tRB6PaLNvQZR6zYUcYVT1zSjoSreQHidcUq7",
|
||||
"referer": url,
|
||||
},
|
||||
})
|
||||
|
||||
const $ = cheerio.load(html)
|
||||
|
||||
const rows = $("#pl_top_realtimehot table tbody tr").slice(1)
|
||||
|
||||
const hotNews: NewsItem[] = []
|
||||
|
||||
rows.each((_, row) => {
|
||||
const $row = $(row)
|
||||
const $link = $row.find("td.td-02 a").filter((_, el) => {
|
||||
const href = $(el).attr("href")
|
||||
return !!(href && !href.includes("javascript:void(0);"))
|
||||
}).first()
|
||||
|
||||
if ($link.length) {
|
||||
const title = $link.text().trim()
|
||||
const href = $link.attr("href")
|
||||
|
||||
if (title && href) {
|
||||
const $flag = $row.find("td.td-03").text().trim()
|
||||
const flagUrl = {
|
||||
新: "https://simg.s.weibo.com/moter/flags/1_0.png",
|
||||
热: "https://simg.s.weibo.com/moter/flags/2_0.png",
|
||||
}[$flag]
|
||||
hotNews.push({
|
||||
id: title,
|
||||
title,
|
||||
url: `${baseurl}${href}`,
|
||||
mobileUrl: `${baseurl}${href}`,
|
||||
extra: {
|
||||
icon: flagUrl ? { url: proxyPicture(flagUrl), scale: 1.5 } : undefined,
|
||||
},
|
||||
},
|
||||
url: `https://s.weibo.com/weibo?q=${encodeURIComponent(`#${k.desc}#`)}`,
|
||||
mobileUrl: k.scheme,
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
return hotNews
|
||||
})
|
||||
|
||||
+10
-2
@@ -36,10 +36,18 @@
|
||||
"kaopu": "kaopuxinwen",
|
||||
"jin10": "jinshishuju",
|
||||
"baidu": "baiduresou",
|
||||
"linuxdo-latest": "LINUX DO-zuixin",
|
||||
"linuxdo-hot": "LINUX DO-jinrizuire",
|
||||
"idcflare-latest": "IDC Flare-zuixin",
|
||||
"idcflare-hot": "IDC Flare-jinrizuire",
|
||||
"nowcoder": "niuke",
|
||||
"sspai": "shaoshupai",
|
||||
"juejin": "xitujuejin",
|
||||
"ifeng": "fenghuangwang-redianzixun",
|
||||
"chongbuluo-latest": "chongbuluo-zuixin",
|
||||
"chongbuluo-hot": "chongbuluo-zuire"
|
||||
}
|
||||
"chongbuluo-hot": "chongbuluo-zuire",
|
||||
"douban": "douban-remendianying",
|
||||
"steam": "Steam-zaixianrenshu",
|
||||
"tencent-hot": "tengxunxinwen-zonghezaobao"
|
||||
}
|
||||
|
||||
|
||||
+49
-4
@@ -336,7 +336,6 @@ export const originSources = {
|
||||
column: "tech",
|
||||
color: "slate",
|
||||
home: "https://linux.do/",
|
||||
disable: true,
|
||||
sub: {
|
||||
latest: {
|
||||
title: "最新",
|
||||
@@ -349,6 +348,24 @@ export const originSources = {
|
||||
home: "https://linux.do/hot",
|
||||
},
|
||||
},
|
||||
},
|
||||
"idcflare": {
|
||||
name: "IDC Flare",
|
||||
column: "tech",
|
||||
color: "slate",
|
||||
home: "https://idcflare.com/",
|
||||
sub: {
|
||||
latest: {
|
||||
title: "最新",
|
||||
home: "https://idcflare.com/latest",
|
||||
},
|
||||
hot: {
|
||||
title: "今日最热",
|
||||
type: "hottest",
|
||||
interval: Time.Common,
|
||||
home: "https://idcflare.com/hot",
|
||||
},
|
||||
},
|
||||
},
|
||||
"ghxi": {
|
||||
name: "果核剥壳",
|
||||
@@ -413,6 +430,36 @@ export const originSources = {
|
||||
},
|
||||
},
|
||||
},
|
||||
"douban": {
|
||||
name: "豆瓣",
|
||||
column: "china",
|
||||
title: "热门电影",
|
||||
color: "green",
|
||||
type: "hottest",
|
||||
home: "https://www.douban.com",
|
||||
},
|
||||
"steam": {
|
||||
name: "Steam",
|
||||
column: "world",
|
||||
title: "在线人数",
|
||||
color: "blue",
|
||||
type: "hottest",
|
||||
home: "https://store.steampowered.com",
|
||||
},
|
||||
"tencent": {
|
||||
name: "腾讯新闻",
|
||||
column: "china",
|
||||
color: "blue",
|
||||
home: "https://news.qq.com",
|
||||
sub: {
|
||||
hot: {
|
||||
title: "综合早报",
|
||||
type: "hottest",
|
||||
interval: Time.Common,
|
||||
home: "https://news.qq.com/tag/aEWqxLtdgmQ=",
|
||||
},
|
||||
},
|
||||
},
|
||||
} as const satisfies Record<string, OriginSource>
|
||||
|
||||
export function genSources() {
|
||||
@@ -461,10 +508,8 @@ export function genSources() {
|
||||
_.filter(([_, v]) => {
|
||||
if (v.disable === "cf" && process.env.CF_PAGES) {
|
||||
return false
|
||||
} else if (v.disable === true) {
|
||||
return false
|
||||
} else {
|
||||
return true
|
||||
return v.disable !== true
|
||||
}
|
||||
}),
|
||||
)
|
||||
|
||||
+56
-1
@@ -414,6 +414,24 @@
|
||||
"color": "blue",
|
||||
"interval": 600000
|
||||
},
|
||||
"linuxdo-hot": {
|
||||
"name": "LINUX DO",
|
||||
"type": "hottest",
|
||||
"column": "tech",
|
||||
"home": "https://linux.do",
|
||||
"color": "gray",
|
||||
"interval": 600000,
|
||||
"title": "今日最热"
|
||||
},
|
||||
"idcflare-hot": {
|
||||
"name": "IDC Flare",
|
||||
"type": "hottest",
|
||||
"column": "tech",
|
||||
"home": "https://idcflare.com",
|
||||
"color": "gray",
|
||||
"interval": 600000,
|
||||
"title": "今日最热"
|
||||
},
|
||||
"nowcoder": {
|
||||
"name": "牛客",
|
||||
"type": "hottest",
|
||||
@@ -472,5 +490,42 @@
|
||||
"color": "green",
|
||||
"interval": 1800000,
|
||||
"title": "最热"
|
||||
},
|
||||
"douban": {
|
||||
"title": "热门电影",
|
||||
"name": "豆瓣",
|
||||
"type": "hottest",
|
||||
"column": "china",
|
||||
"home": "https://www.douban.com",
|
||||
"color": "green",
|
||||
"interval": 600000
|
||||
},
|
||||
"steam": {
|
||||
"title": "在线人数",
|
||||
"name": "Steam",
|
||||
"type": "hottest",
|
||||
"column": "world",
|
||||
"home": "https://store.steampowered.com",
|
||||
"color": "blue",
|
||||
"interval": 600000
|
||||
},
|
||||
"tencent": {
|
||||
"redirect": "tencent-hot",
|
||||
"name": "腾讯新闻",
|
||||
"type": "hottest",
|
||||
"column": "china",
|
||||
"home": "https://news.qq.com/tag/aEWqxLtdgmQ=",
|
||||
"color": "blue",
|
||||
"interval": 1800000,
|
||||
"title": "综合早报"
|
||||
},
|
||||
"tencent-hot": {
|
||||
"name": "腾讯新闻",
|
||||
"type": "hottest",
|
||||
"column": "china",
|
||||
"home": "https://news.qq.com/tag/aEWqxLtdgmQ=",
|
||||
"color": "blue",
|
||||
"interval": 1800000,
|
||||
"title": "综合早报"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -236,11 +236,11 @@ function NewsListHot({ items }: { items: NewsItem[] }) {
|
||||
key={item.id}
|
||||
title={item.extra?.hover}
|
||||
className={$(
|
||||
"flex gap-2 items-start items-stretch relative cursor-pointer [&_*]:cursor-pointer transition-all",
|
||||
"flex gap-2 items-center items-stretch relative cursor-pointer [&_*]:cursor-pointer transition-all",
|
||||
"hover:bg-neutral-400/10 rounded-md pr-1 visited:(text-neutral-400)",
|
||||
)}
|
||||
>
|
||||
<span className={$("bg-neutral-400/10 min-w-6 flex justify-center items-center rounded-md text-sm h-6 flex-shrink-0")}>
|
||||
<span className={$("bg-neutral-400/10 min-w-6 flex justify-center items-center rounded-md text-sm")}>
|
||||
{i + 1}
|
||||
</span>
|
||||
{!!item.extra?.diff && <DiffNumber diff={item.extra.diff} />}
|
||||
|
||||
Reference in New Issue
Block a user