Skill: swordman20/ai-news-feishu-doc (version 0.0.1)

--- name: ai-news-digest description: 飞书AI情报 --- # AI News Digest - AI 早报生成器 自动生成 AI 行业早报，支持 RSS 订阅、智能摘要、多语言分类。 ## 快速开始 ```bash # 生成今日早报 cd scripts python generate-ai-news.py # 或 python3 generate-ai-news.py # 输出文件：AI早报_2026年2月28日.md ``` ## 功能特性 - ✅ **RSS 订阅** - 自动抓取配置源的最新文章 - ✅ **智能摘要** - 提取文章要点，生成摘要 - ✅ **关键词过滤** - 按关键词匹配优先级排序 - ✅ **多语言分类** - 自动区分中文/英文内容 - ✅ **媒体预览** - 提取文章配图 - ✅ **Markdown 输出** - 结构化早报文档 ## 配置说明 配置文件：`assets/ai-news-rss.yaml` ```yaml feeds: - name: "36氪" url: "https://www.36kr.com/feed" category: "国内AI" language: "zh" filters: keywords: - "GPT" - "Claude" - "AI" - "大模型" - "OpenAI" output: max_articles_per_feed: 5 total_max_articles: 15 ``` ## 自定义配置 1. **添加 RSS 源**：在 `feeds` 列表中添加新的源 2. **调整关键词**：修改 `filters.keywords` 匹配感兴趣的内容 3. **输出格式**：调整 `output` 参数控制文章数量 ## 定时任务 添加到 OpenClaw cron 实现每日自动推送： ```json { "name": "ai-morning-news", "schedule": "0 8 * * *", "command": "python skills/ai-news-digest/scripts/generate-ai-news.py" } ``` ## 输出示例 ```markdown # 📰 AI 早报 | 2026年2月28日 ## 🔥 头版头条 ### OpenAI 获得 1100 亿美元新投资... ## 🇨🇳 国内 AI 动态 ... ## 🌍 海外 AI 动态 ... ``` ## 依赖 - Python 3.7+ - PyYAML: `pip install pyyaml` --- 🦞 Powered by OpenClaw | v1.0.0

path: assets/ai-news-rss.yaml
content: feeds:
  # ✅ 国内AI - 可用
  - name: "36氪"
    url: "https://www.36kr.com/feed"
    category: "国内AI"
    language: "zh"
    status: "✅ 可用"
    
  # ❌ 国内AI - 暂时不可用
  - name: "机器之心"
    url: "https://www.jiqizhixin.com/rss"
    category: "国内AI"
    language: "zh"
    status: "❌ 重定向过多"
    
  - name: "量子位"
    url: "https://www.qbitai.com/feed"
    category: "国内AI"
    language: "zh"
    status: "❌ 403禁止访问"
    
  - name: "品玩"
    url: "https://www.pingwest.com/feed"
    category: "国内AI"
    language: "zh"
    status: "❌ 404不存在"
    
  # ✅ 海外AI - 可用
  - name: "Synced(机器之心英文)"
    url: "https://syncedreview.com/feed/"
    category: "海外AI"
    language: "en"
    status: "✅ 可用"
    
  - name: "TechCrunch AI"
    url: "https://techcrunch.com/category/artificial-intelligence/feed/"
    category: "海外AI"
    language: "en"
    status: "✅ 可用"
    
  - name: "OpenAI Blog"
    url: "https://openai.com/news/rss.xml"
    category: "官方发布"
    language: "en"
    status: "✅ 可用"
    
  # 备用源
  - name: "The Verge AI"
    url: "https://www.theverge.com/ai-artificial-intelligence/rss/index.xml"
    category: "海外AI"
    language: "en"
    status: "⏳ 待测试"
    
  - name: "Google AI Blog"
    url: "https://ai.googleblog.com/feeds/posts/default"
    category: "官方发布"
    language: "en"
    status: "⏳ 待测试"
    
  - name: "Anthropic News"
    url: "https://www.anthropic.com/rss.xml"
    category: "官方发布"
    language: "en"
    status: "⏳ 待测试"

# 当前可用源统计
working_feeds:
  zh: 1      # 36氪
  en: 3      # Synced, TechCrunch, OpenAI
  total: 4

output:
  max_articles_per_feed: 5
  total_max_articles: 15
  format: "markdown"

media:
  enabled: true
  max_images: 3
  extract_video_thumbnail: true

summary:
  enabled: true
  max_length: 200

filters:
  keywords:
    - "GPT"
    - "Claude"
    - "AI"
    - "大模型"
    - "OpenAI"
    - "阿里"
    - "字节"
    - "腾讯"
    - "百度"
    - "千问"
    - "豆包"
    - "融资"
    - "Agent"
    - "眼镜"
    - "硬件"
  blacklist:
    - "广告"
    - "招聘"
    - "推广"

path: scripts/generate-ai-news.py
content: #!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
AI 早报生成器
功能：RSS订阅 + 智能摘要 + 多语言支持 + 媒体预览
"""

import os
import sys
import re
import yaml
from datetime import datetime
from xml.etree import ElementTree as ET
import urllib.request
import urllib.error


def get_rss_feed(url):
    """获取 RSS 订阅内容"""
    try:
        req = urllib.request.Request(url, headers={'User-Agent': 'Mozilla/5.0'})
        with urllib.request.urlopen(req, timeout=30) as response:
            return response.read()
    except Exception as e:
        print(f"警告: 无法获取 RSS: {url} - {e}")
        return None


def extract_images(html_content, max_images=3):
    """从 HTML 提取图片"""
    images = []
    pattern = r'<img[^>]+src="([^"]+)"[^>]*>'
    matches = re.findall(pattern, html_content)
    
    for img_url in matches[:max_images]:
        if not re.search(r'icon|logo|avatar|placeholder|1x1', img_url, re.I):
            images.append(img_url)
    
    return images


def get_smart_summary(content, max_length=200):
    """生成智能摘要"""
    # 清理 HTML 标签
    text = re.sub(r'<[^>]+>', '', content)
    text = text.replace('&nbsp;', ' ')
    text = text.replace('&quot;', '"')
    text = text.replace('&amp;', '&')
    
    if len(text) > max_length:
        return text[:max_length] + "..."
    return text.strip()


def test_keyword_match(title, description, keywords):
    """关键词匹配评分"""
    text = f"{title} {description}".lower()
    score = 0
    for keyword in keywords:
        if keyword.lower() in text:
            score += 1
    return score


def parse_rss(xml_content):
    """解析 RSS XML"""
    articles = []
    try:
        root = ET.fromstring(xml_content)
        # 处理 RSS 2.0
        if root.tag == 'rss':
            channel = root.find('channel')
            if channel is not None:
                for item in channel.findall('item'):
                    title = item.findtext('title', '')
                    link = item.findtext('link', '')
                    pub_date = item.findtext('pubDate', '')
                    desc = item.findtext('description', '')
                    articles.append({
                        'title': title,
                        'link': link,
                        'pubDate': pub_date,
                        'description': desc
                    })
        # 处理 Atom
        elif 'feed' in root.tag:
            for entry in root.findall('{http://www.w3.org/2005/Atom}entry'):
                title = entry.findtext('{http://www.w3.org/2005/Atom}title', '')
                link = entry.find('{http://www.w3.org/2005/Atom}link')
                link_href = link.get('href', '') if link is not None else ''
                pub_date = entry.findtext('{http://www.w3.org/2005/Atom}updated', '')
                desc = entry.findtext('{http://www.w3.org/2005/Atom}summary', '')
                articles.append({
                    'title': title,
                    'link': link_href,
                    'pubDate': pub_date,
                    'description': desc
                })
    except Exception as e:
        print(f"解析 RSS 失败: {e}")
    
    return articles


def main():
    script_dir = os.path.dirname(os.path.abspath(__file__))
    config_path = os.path.join(script_dir, '..', 'assets', 'ai-news-rss.yaml')
    
    today = datetime.now().strftime('%Y年%m月%d日')
    output_path = os.path.join(script_dir, f'AI早报_{today}.md')
    
    # 读取配置
    with open(config_path, 'r', encoding='utf-8') as f:
        config = yaml.safe_load(f)
    
    print("🦞 AI 早报生成器启动...\n")
    
    all_articles = []
    
    for feed in config.get('feeds', []):
        print(f"📡 正在获取: {feed['name']} ...", end='')
        
        xml_content = get_rss_feed(feed['url'])
        if xml_content:
            articles = parse_rss(xml_content)
            max_per_feed = config.get('output', {}).get('max_articles_per_feed', 5)
            
            for article in articles[:max_per_feed]:
                # 解析日期
                pub_date = article.get('pubDate', '')
                try:
                    # 尝试解析 RSS 日期格式
                    dt = datetime.strptime(pub_date[:16], '%a, %d %b %Y')
                    pub_date = dt.strftime('%m-%d')
                except:
                    pub_date = '未知时间'
                
                title = article.get('title', '')
                desc = article.get('description', '')
                
                # 关键词匹配
                keywords = config.get('filters', {}).get('keywords', [])
                score = test_keyword_match(title, desc, keywords)
                
                # 提取图片
                images = extract_images(desc, config.get('media', {}).get('max_images', 3))
                
                # 生成摘要
                summary = get_smart_summary(desc, config.get('summary', {}).get('max_length', 200))
                
                all_articles.append({
                    'title': title,
                    'link': article.get('link', ''),
                    'pubDate': pub_date,
                    'summary': summary,
                    'images': images,
                    'source': feed['name'],
                    'category': feed.get('category', ''),
                    'language': feed.get('language', 'zh'),
                    'score': score
                })
            
            print(f" ✅ 获取 {len(articles[:max_per_feed])} 篇")
        else:
            print(" ❌ 失败")
    
    print(f"\n📝 共获取 {len(all_articles)} 篇文章，正在生成早报...")
    
    # 排序并选取 Top N
    all_articles.sort(key=lambda x: x['score'], reverse=True)
    total_max = config.get('output', {}).get('total_max_articles', 15)
    top_articles = all_articles[:total_max]
    
    # 分类
    cn_articles = [a for a in top_articles if a['language'] == 'zh']
    en_articles = [a for a in top_articles if a['language'] in ('en', 'mixed')]
    
    # 生成 Markdown
    content = f"""# 📰 AI 早报 | {today}

> 自动采集 {len(config.get('feeds', []))} 个信源 | 精选 {len(top_articles)} 篇 | 由 OpenClaw 驱动

---

## 🔥 头版头条

"""
    
    # 头版头条
    for article in top_articles[:3]:
        content += f"""### {article['title']}
📍 **{article['source']}** | 🕐 {article['pubDate']}

💡 {article['summary']}

🔗 [阅读全文]({article['link']})

"""
        if article['images']:
            for img in article['images']:
                content += f"![图片]({img})\n"
            content += "\n"
    
    # 国内 AI 动态
    if cn_articles:
        content += "---\n\n## 🇨🇳 国内 AI 动态\n\n"
        for article in cn_articles[:5]:
            content += f"""### {article['title']}
**来源：** {article['source']} | **时间：** {article['pubDate']}

{article['summary']}

🔗 [阅读原文]({article['link']})

"""
    
    # 海外 AI 动态
    if en_articles:
        content += "---\n\n## 🌍 海外 AI 动态\n\n"
        for article in en_articles[:5]:
            content += f"""### {article['title']}
**Source:** {article['source']} | **Time:** {article['pubDate']}

{article['summary']}

🔗 [Read Original]({article['link']})

"""
    
    # 总结
    if top_articles:
        content += f"""---

## 🎯 今日总结

> 今日 AI 圈最值得关注的是：**{top_articles[0]['title']}**

---

*🦞 由 大龙虾 AI 早报系统自动生成 | {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}*
"""
    
    # 保存文件
    with open(output_path, 'w', encoding='utf-8') as f:
        f.write(content)
    
    print(f"\n✅ 早报已生成: {output_path}")
    print(f"📊 统计信息:")
    print(f"   - 国内文章: {len(cn_articles)} 篇")
    print(f"   - 海外文章: {len(en_articles)} 篇")
    print(f"   - 头版头条: 3 篇")
    
    return output_path


if __name__ == '__main__':
    main()


Security scan verdicts — ClawScan: clean | static analysis: clean | VirusTotal: clean | SkillSpector: clean (LOW)