Spaces:
Sleeping
Sleeping
Upload 2 files
Browse files- app.py +34 -39
- news_sources.json +29 -0
app.py
CHANGED
|
@@ -91,43 +91,38 @@ UNSPLASH_POOL = [
|
|
| 91 |
# ─────────────────────────────────────────────────────────────────
|
| 92 |
# 新聞來源定義
|
| 93 |
# ─────────────────────────────────────────────────────────────────
|
| 94 |
-
|
| 95 |
-
|
| 96 |
{"name": "科技新報", "type": "rss", "url": "https://technews.tw/feed/"},
|
| 97 |
{"name": "癮科技", "type": "rss", "url": "https://chinese.engadget.com/rss.xml"},
|
| 98 |
{"name": "數位時代", "type": "rss", "url": "https://www.bnext.com/rss"},
|
| 99 |
{"name": "Yahoo 新聞", "type": "rss", "url": "https://hk.news.yahoo.com/rss/"},
|
| 100 |
-
{"name": "
|
| 101 |
-
|
| 102 |
-
{"name": "
|
| 103 |
-
{"name": "
|
| 104 |
-
{"name": "明日科學", "type": "rss", "url": "https://tomorrowsci.com/feed/"},
|
| 105 |
-
{"name": "Gizmodo", "type": "rss", "url": "https://gizmodo.com/rss"},
|
| 106 |
-
{"name": "Hackaday", "type": "rss", "url": "https://hackaday.com/feed/"},
|
| 107 |
-
{"name": "地球圖輯隊", "type": "rss", "url": "https://world.yam.com/rss.php"},
|
| 108 |
-
{"name": "冷知識", "type": "rss", "url": "https://misstwocm.com/feed/"},
|
| 109 |
-
# ── 新增:國際/財經類 ──
|
| 110 |
-
{"name": "鉅亨網 國際政經", "type": "rss", "url": "https://news.cnyes.com/rss/div/global_macro"},
|
| 111 |
-
{"name": "阿斯達克財經網", "type": "rss", "url": "https://www.aastocks.com/tc/resources/rss.ashx?type=1"},
|
| 112 |
-
{"name": "RTHK 財經新聞", "type": "rss", "url": "https://rthk.hk/rthk/news/rss/c_expressnews_cfinance.xml"},
|
| 113 |
-
{"name": "RTHK 本地新聞", "type": "rss", "url": "https://rthk.hk/rthk/news/rss/c_expressnews_clocal.xml"},
|
| 114 |
-
{"name": "RTHK 國際新聞", "type": "rss", "url": "https://rthk.hk/rthk/news/rss/c_expressnews_cinternational.xml"},
|
| 115 |
-
# ── 新增:材料/工業類 ──
|
| 116 |
-
{"name": "Plastics Today", "type": "rss", "url": "https://www.plasticstoday.com/rss.xml"},
|
| 117 |
-
{"name": "Metal Miner", "type": "rss", "url": "https://agmetalminer.com/feed/"},
|
| 118 |
-
{"name": "AZoM 材料科學", "type": "rss", "url": "https://www.azom.com/azom-news-feed.xml"},
|
| 119 |
-
# ── 新增:旅遊/生活類 ──
|
| 120 |
-
{"name": "Wave 流行潮流", "type": "rss", "url": "https://www.wavetv.tw/feed/"},
|
| 121 |
-
{"name": "U Travel 旅遊", "type": "rss", "url": "https://utravel.com.hk/rss"},
|
| 122 |
-
{"name": "Yahoo 旅遊 港台", "type": "rss", "url": "https://travel.yahoo.com.tw/rss/headline/"},
|
| 123 |
-
{"name": "Yahoo 旅遊 香港", "type": "rss", "url": "https://hk.news.yahoo.com/rss/travel"},
|
| 124 |
-
{"name": "港生活 北上", "type": "rss", "url": "https://hk.ulifestyle.com.hk/rss/travel-main"},
|
| 125 |
-
{"name": "香港01 大灣區", "type": "rss", "url": "https://www.hk01.com/rss/category/877"},
|
| 126 |
-
# ── 新增:熱搜趨勢類 ──
|
| 127 |
-
{"name": "Google Trends 香港", "type": "rss", "url": "https://trends.google.com.hk/trending/rss?geo=HK"},
|
| 128 |
-
{"name": "Google Trends 美國", "type": "rss", "url": "https://trends.google.com/trending/rss?geo=US"},
|
| 129 |
]
|
| 130 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 131 |
# ─────────────���───────────────────────────────────────────────────
|
| 132 |
# 圖片處理輔助函式
|
| 133 |
# ─────────────────────────────────────────────────────────────────
|
|
@@ -569,13 +564,13 @@ SYSTEM_PROMPT = """你是一個專業的繁體中文新聞編輯秘書。你將
|
|
| 569 |
請為每一篇文章完成以下任務:
|
| 570 |
|
| 571 |
1. **語言判斷**:先判斷原文 title 和 content 的主要語言。
|
| 572 |
-
- 若為**中文**:直接產出繁體中文的 title 和 summary(單語模式)。
|
| 573 |
- 若為**英文或其他外語**:自動切換為「中英對照模式」(見第 2 點)。
|
| 574 |
|
| 575 |
2. **中英對照模式**(僅適用於外語原文):
|
| 576 |
title 和 summary 必須用換行符號 `\n` 分隔上下兩半段:
|
| 577 |
-
- **上半段**:流暢、道地的繁體中文翻譯(使用香港/台灣常用表達方式)。
|
| 578 |
-
- **下半段**:對應的英文原文(title 對照英文原標題;summary 對照由 AI 生成的流暢英文摘要,
|
| 579 |
|
| 580 |
對照模式輸出範例:
|
| 581 |
{
|
|
@@ -597,7 +592,7 @@ SYSTEM_PROMPT = """你是一個專業的繁體中文新聞編輯秘書。你將
|
|
| 597 |
[
|
| 598 |
{
|
| 599 |
"title": "繁體中文標題(外語時:中文\nEnglish)",
|
| 600 |
-
"summary": "
|
| 601 |
"category": "科技",
|
| 602 |
"image_url": "https://...",
|
| 603 |
"source": "來源名稱"
|
|
@@ -606,8 +601,8 @@ SYSTEM_PROMPT = """你是一個專業的繁體中文新聞編輯秘書。你將
|
|
| 606 |
]
|
| 607 |
|
| 608 |
注意事項:
|
| 609 |
-
- 中文模式摘要必須在
|
| 610 |
-
- 對照模式上半段繁體中文摘要同樣
|
| 611 |
- category 必須是列出的 9 個選項之一,不可自行發明
|
| 612 |
- 所有繁體中文必須使用 zh-TW 習慣用語
|
| 613 |
- 不可遺漏任何一篇文章
|
|
@@ -700,7 +695,7 @@ def summarize_by_deepseek(articles: List[Dict]) -> List[Dict]:
|
|
| 700 |
a = articles[i]
|
| 701 |
completed.append({
|
| 702 |
"title": a["title"],
|
| 703 |
-
"summary": clean_html(a.get("content", a["title"]))[:
|
| 704 |
"category": guess_category(a.get("source", "")),
|
| 705 |
"image_url": a["image_url"],
|
| 706 |
"source": a["source"],
|
|
@@ -724,7 +719,7 @@ def summarize_by_deepseek(articles: List[Dict]) -> List[Dict]:
|
|
| 724 |
for a in articles:
|
| 725 |
fallback.append({
|
| 726 |
"title": a["title"],
|
| 727 |
-
"summary": clean_html(a.get("content", a["title"]))[:
|
| 728 |
"category": guess_category(a.get("source", "")),
|
| 729 |
"image_url": a["image_url"],
|
| 730 |
"source": a["source"],
|
|
|
|
| 91 |
# ─────────────────────────────────────────────────────────────────
|
| 92 |
# 新聞來源定義
|
| 93 |
# ─────────────────────────────────────────────────────────────────
|
| 94 |
+
# ── 備用清單(當 news_sources.json 無法讀取時使用)──
|
| 95 |
+
FALLBACK_SOURCES: List[Dict] = [
|
| 96 |
{"name": "科技新報", "type": "rss", "url": "https://technews.tw/feed/"},
|
| 97 |
{"name": "癮科技", "type": "rss", "url": "https://chinese.engadget.com/rss.xml"},
|
| 98 |
{"name": "數位時代", "type": "rss", "url": "https://www.bnext.com/rss"},
|
| 99 |
{"name": "Yahoo 新聞", "type": "rss", "url": "https://hk.news.yahoo.com/rss/"},
|
| 100 |
+
{"name": "RTHK 本地新聞", "type": "rss", "url": "https://rthk.hk/rthk/news/rss/c_expressnews_clocal.xml"},
|
| 101 |
+
{"name": "RTHK 國際新聞", "type": "rss", "url": "https://rthk.hk/rthk/news/rss/c_expressnews_cinternational.xml"},
|
| 102 |
+
{"name": "Gizmodo", "type": "rss", "url": "https://gizmodo.com/rss"},
|
| 103 |
+
{"name": "Hackaday", "type": "rss", "url": "https://hackaday.com/feed/"},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 104 |
]
|
| 105 |
|
| 106 |
+
|
| 107 |
+
def load_news_sources() -> List[Dict]:
|
| 108 |
+
"""從 news_sources.json 載入新聞來源清單;失敗時回退到備用清單。"""
|
| 109 |
+
import os as _os
|
| 110 |
+
config_path = _os.path.join(_os.path.dirname(_os.path.abspath(__file__)), "news_sources.json")
|
| 111 |
+
try:
|
| 112 |
+
with open(config_path, "r", encoding="utf-8") as f:
|
| 113 |
+
sources = json.load(f)
|
| 114 |
+
if isinstance(sources, list) and len(sources) > 0:
|
| 115 |
+
print(f"📋 成功載入 news_sources.json({len(sources)} 個來源)", flush=True)
|
| 116 |
+
return sources
|
| 117 |
+
else:
|
| 118 |
+
raise ValueError("news_sources.json 格式無效(需為非空陣列)")
|
| 119 |
+
except Exception as e:
|
| 120 |
+
print(f"⚠️ 無法讀取 news_sources.json:{e},使用備用清單({len(FALLBACK_SOURCES)} 個來源)", flush=True)
|
| 121 |
+
return FALLBACK_SOURCES
|
| 122 |
+
|
| 123 |
+
|
| 124 |
+
NEWS_SOURCES: List[Dict] = load_news_sources()
|
| 125 |
+
|
| 126 |
# ─────────────���───────────────────────────────────────────────────
|
| 127 |
# 圖片處理輔助函式
|
| 128 |
# ─────────────────────────────────────────────────────────────────
|
|
|
|
| 564 |
請為每一篇文章完成以下任務:
|
| 565 |
|
| 566 |
1. **語言判斷**:先判斷原文 title 和 content 的主要語言。
|
| 567 |
+
- 若為**中文**:直接產出繁體中文的 title 和 summary(單語模式,摘要 80~200 字)。
|
| 568 |
- 若為**英文或其他外語**:自動切換為「中英對照模式」(見第 2 點)。
|
| 569 |
|
| 570 |
2. **中英對照模式**(僅適用於外語原文):
|
| 571 |
title 和 summary 必須用換行符號 `\n` 分隔上下兩半段:
|
| 572 |
+
- **上半段**:流暢、道地的繁體中文翻譯(使用香港/台灣常用表達方式),摘要 80~200 字。
|
| 573 |
+
- **下半段**:對應的英文原文(title 對照英文原標題;summary 對照由 AI 生成的流暢英文摘要,40~70 個英文字)。
|
| 574 |
|
| 575 |
對照模式輸出範例:
|
| 576 |
{
|
|
|
|
| 592 |
[
|
| 593 |
{
|
| 594 |
"title": "繁體中文標題(外語時:中文\nEnglish)",
|
| 595 |
+
"summary": "80~200字繁體中文摘要(外語時:中文摘要\nEnglish summary)",
|
| 596 |
"category": "科技",
|
| 597 |
"image_url": "https://...",
|
| 598 |
"source": "來源名稱"
|
|
|
|
| 601 |
]
|
| 602 |
|
| 603 |
注意事項:
|
| 604 |
+
- 中文模式摘要必須在 80~200 個中文字之間,不可過短,也不可超過 200 字
|
| 605 |
+
- 對照模式上半段繁體中文摘要同樣 80~200 字,下半段英文摘要 40~70 個英文字
|
| 606 |
- category 必須是列出的 9 個選項之一,不可自行發明
|
| 607 |
- 所有繁體中文必須使用 zh-TW 習慣用語
|
| 608 |
- 不可遺漏任何一篇文章
|
|
|
|
| 695 |
a = articles[i]
|
| 696 |
completed.append({
|
| 697 |
"title": a["title"],
|
| 698 |
+
"summary": clean_html(a.get("content", a["title"]))[:200],
|
| 699 |
"category": guess_category(a.get("source", "")),
|
| 700 |
"image_url": a["image_url"],
|
| 701 |
"source": a["source"],
|
|
|
|
| 719 |
for a in articles:
|
| 720 |
fallback.append({
|
| 721 |
"title": a["title"],
|
| 722 |
+
"summary": clean_html(a.get("content", a["title"]))[:200],
|
| 723 |
"category": guess_category(a.get("source", "")),
|
| 724 |
"image_url": a["image_url"],
|
| 725 |
"source": a["source"],
|
news_sources.json
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
[
|
| 2 |
+
{"name": "科技新報", "type": "rss", "url": "https://technews.tw/feed/", "category": "科技/AI"},
|
| 3 |
+
{"name": "癮科技", "type": "rss", "url": "https://chinese.engadget.com/rss.xml", "category": "科技/3C"},
|
| 4 |
+
{"name": "數位時代", "type": "rss", "url": "https://www.bnext.com/rss", "category": "科技/商業"},
|
| 5 |
+
{"name": "HKEPC", "type": "html", "url": "https://www.hkepc.com/", "category": "科技/硬體"},
|
| 6 |
+
{"name": "QbitAI 機器之心", "type": "rss", "url": "https://www.jiqizhixin.com/rss", "category": "科技/AI"},
|
| 7 |
+
{"name": "Inside 科技趨勢", "type": "rss", "url": "https://www.inside.com.tw/feed", "category": "科技/趨勢"},
|
| 8 |
+
{"name": "明日科學", "type": "rss", "url": "https://tomorrowsci.com/feed/", "category": "科學/未來"},
|
| 9 |
+
{"name": "Gizmodo", "type": "rss", "url": "https://gizmodo.com/rss", "category": "科技/極客"},
|
| 10 |
+
{"name": "Hackaday", "type": "rss", "url": "https://hackaday.com/feed/", "category": "科技/DIY"},
|
| 11 |
+
{"name": "地球圖輯隊", "type": "rss", "url": "https://world.yam.com/rss.php", "category": "國際/圖文"},
|
| 12 |
+
{"name": "冷知識", "type": "rss", "url": "https://misstwocm.com/feed/", "category": "生活/趣味"},
|
| 13 |
+
{"name": "鉅亨網 國際政經", "type": "rss", "url": "https://news.cnyes.com/rss/div/global_macro", "category": "財經/國際"},
|
| 14 |
+
{"name": "阿斯達克財經網", "type": "rss", "url": "https://www.aastocks.com/tc/resources/rss.ashx?type=1", "category": "財經/港股"},
|
| 15 |
+
{"name": "RTHK 財經新聞", "type": "rss", "url": "https://rthk.hk/rthk/news/rss/c_expressnews_cfinance.xml", "category": "財經/香港"},
|
| 16 |
+
{"name": "RTHK 本地新聞", "type": "rss", "url": "https://rthk.hk/rthk/news/rss/c_expressnews_clocal.xml", "category": "時事/香港"},
|
| 17 |
+
{"name": "RTHK 國際新聞", "type": "rss", "url": "https://rthk.hk/rthk/news/rss/c_expressnews_cinternational.xml", "category": "國際/時事"},
|
| 18 |
+
{"name": "Plastics Today", "type": "rss", "url": "https://www.plasticstoday.com/rss.xml", "category": "材料工業/塑膠"},
|
| 19 |
+
{"name": "Metal Miner", "type": "rss", "url": "https://agmetalminer.com/feed/", "category": "材料工業/金屬"},
|
| 20 |
+
{"name": "AZoM 材料科學", "type": "rss", "url": "https://www.azom.com/azom-news-feed.xml", "category": "材料工業/科學"},
|
| 21 |
+
{"name": "Wave 流行潮流", "type": "rss", "url": "https://www.wavetv.tw/feed/", "category": "生活/潮流"},
|
| 22 |
+
{"name": "U Travel 旅遊", "type": "rss", "url": "https://utravel.com.hk/rss", "category": "旅遊/香港"},
|
| 23 |
+
{"name": "Yahoo 旅遊 港台", "type": "rss", "url": "https://travel.yahoo.com.tw/rss/headline/", "category": "旅遊/港台"},
|
| 24 |
+
{"name": "Yahoo 旅遊 香港", "type": "rss", "url": "https://hk.news.yahoo.com/rss/travel", "category": "旅遊/香港"},
|
| 25 |
+
{"name": "港生活 北上", "type": "rss", "url": "https://hk.ulifestyle.com.hk/rss/travel-main", "category": "旅遊/深圳"},
|
| 26 |
+
{"name": "香港01 大灣區", "type": "rss", "url": "https://www.hk01.com/rss/category/877", "category": "旅遊/大灣區"},
|
| 27 |
+
{"name": "Google Trends 香港","type": "rss", "url": "https://trends.google.com.hk/trending/rss?geo=HK", "category": "熱門趨勢/香港"},
|
| 28 |
+
{"name": "Google Trends 美國","type": "rss", "url": "https://trends.google.com/trending/rss?geo=US", "category": "熱門趨勢/全球"}
|
| 29 |
+
]
|