Skill: chyher/irene-ai-news (version 1.1.0)

--- name: daily-ai-news description: > Daily AI news aggregator. Fetches latest AI-related content from HackerNews, GitHub Trending, ArXiv papers, and web search (Brave/Tavily). Use for: (1) Getting daily AI digest, (2) Staying updated with trending AI projects, (3) Discovering latest research papers, (4) Web search for breaking news. Triggers: ai news, daily digest, what's new in ai, tech news. version: "1.1.0" user-invocable: true metadata: openclaw: emoji: 📰 requires: tools: ["web_search", "web_fetch"] bins: ["python3", "curl"] suggests: ["openclaw-tavily-search"] --- # Daily AI News - 每日 AI 资讯聚合器 自动抓取 AI 领域最新资讯，包括 HackerNews 热帖、GitHub 趋势项目、ArXiv 论文摘要，以及实时网络搜索。 ## 功能特性 - 🤖 **AI 智能筛选** - 自动识别 AI/ML 相关内容 - 📊 **多源聚合** - HackerNews + GitHub + ArXiv + 网络搜索 - 🔍 **实时搜索** - 支持 Brave Search 或 Tavily（无需信用卡） - 🕘 **定时推送** - 支持 Cron 定时任务 - 💬 **一键分享** - 直接发送到聊天频道 - 💳 **零门槛** - Tavily 方案无需绑定信用卡 ## 搜索方案选择 | 方案 | 优点 | 是否需要信用卡 | 配置方式 | |------|------|----------------|----------| | **Tavily** ⭐推荐 | 1000次/月免费，AI增强结果 | ❌ 不需要 | 安装 `openclaw-tavily-search` skill | | **Brave** | OpenClaw原生支持 | ✅ 需要 | `openclaw configure --section web` | ### Tavily 配置（推荐） 1. 安装社区skill： ```bash clawhub install openclaw-tavily-search ``` 2. 获取免费API Key： - 访问 https://tavily.com/ - 注册账号（仅需邮箱） - Dashboard复制API Key 3. 配置环境变量： ```bash echo 'TAVILY_API_KEY=tvly-你的key' >> ~/.openclaw/.env ``` ### Brave Search 配置 ```bash openclaw configure --section web # 输入 Brave Search API Key ``` ## 使用方法 ### 主动查询 ``` "今天有什么 AI 新闻" "获取最新的 AI 资讯" "显示今日 AI 摘要" "搜索 OpenAI 最新消息" ``` ### 定时推送（Cron 配置） ```json { "cron": { "jobs": [ { "schedule": "0 9 * * *", "command": "python3 ~/.openclaw/workspace/data/skills/daily-ai-news/scripts/ai_news.py --send", "channel": "feishu" } ] } } ``` ## 数据源 | 来源 | 内容类型 | 更新频率 | |------|---------|---------| | HackerNews | 技术讨论、产品发布 | 实时 | | GitHub Trending | 开源项目、工具库 | 每日 | | ArXiv | 学术论文、研究进展 | 每日 | | Web Search | 突发新闻、官方公告 | 实时 | ## 输出格式 ```markdown 📰 每日 AI 资讯 - 2026-03-02 ## 🔥 热点头条（Web搜索） • [标题] - 简短描述... [链接] • [标题] - 简短描述... [链接] ## 💬 HackerNews 热门 • [标题] - 简短描述... [链接] • [标题] - 简短描述... [链接] ## ⭐ GitHub 趋势 • [项目名] ⭐ Stars - 项目描述... [链接] • [项目名] ⭐ Stars - 项目描述... [链接] ## 📄 ArXiv 论文 • [论文标题] - 作者/机构 - 一句话摘要... [链接] • [论文标题] - 作者/机构 - 一句话摘要... [链接] --- 💡 提示: 回复 "详细+序号" 查看完整内容 ``` ## 安装 ```bash # 从 ClawHub 安装 clawhub install irene-ai-news # 或手动克隆到 workspace skills 目录 cd ~/.openclaw/workspace/data/skills git clone <repo-url> daily-ai-news ``` ## 依赖 - Python 3.8+ - curl - Tavily API Key（推荐）或 Brave Search API Key ## 配置选项 编辑 `scripts/ai_news.py` 顶部配置区： ```python CONFIG = { "max_hackernews": 5, # HN 条目数 "max_github": 5, # GitHub 项目数 "max_arxiv": 3, # 论文数 "max_web_search": 5, # 网络搜索条数 "categories": ["ai", "ml", "llm", "gpt", "neural"], # 关键词过滤 "search_provider": "auto", # auto/tavily/brave } ``` ## 手动运行测试 ```bash # 仅显示结果 python3 scripts/ai_news.py # 使用 Tavily 搜索 python3 scripts/ai_news.py --provider tavily # 发送到当前频道 python3 scripts/ai_news.py --send # 发送到指定频道 python3 scripts/ai_news.py --send --channel feishu ``` ## 技术实现 - **HackerNews**: 官方 Algolia API - **GitHub**: Trending 页面解析 - **ArXiv**: RSS Feed + 摘要提取 - **Web Search**: Brave Search API 或 Tavily API ## Changelog ### v1.1.0 (2026-03-02) - ✨ 新增 Tavily 搜索支持（无需信用卡） - 🔍 集成网络搜索获取实时AI新闻 - 📚 更新文档，添加双方案配置说明 ### v1.0.0 (2026-03-02) - 🎉 初始发布 - 📊 HackerNews + GitHub + ArXiv 聚合 ## License MIT

path: README.md
content: # Daily AI News 📰

OpenClaw Skill - 每日 AI 资讯聚合器

## 功能

自动抓取并汇总 AI 领域最新动态：
- 🔥 **HackerNews** - AI 相关热门讨论
- ⭐ **GitHub** -  trending AI 开源项目  
- 📄 **ArXiv** - 最新 AI/ML 研究论文

## 快速开始

### 安装

```bash
# 方法1: 复制到 workspace skills 目录
cp -r daily-ai-news ~/.openclaw/workspace/data/skills/

# 方法2: 从 ClawHub 安装（发布后）
openclaw skill install daily-ai-news
```

### 使用

**主动查询:**
```
"今天有什么 AI 新闻"
"获取 AI 资讯"
"显示今日科技摘要"
```

**手动运行:**
```bash
# 仅显示
python3 ~/.openclaw/workspace/data/skills/daily-ai-news/scripts/ai_news.py

# 发送到当前频道
python3 ~/.openclaw/workspace/data/skills/daily-ai-news/scripts/ai_news.py --send
```

### 定时推送配置

在 `~/.openclaw/openclaw.json` 中添加 cron job:

```json
{
  "cron": {
    "jobs": [
      {
        "id": "daily-ai-news",
        "schedule": "0 9 * * *",
        "command": "python3 ~/.openclaw/workspace/data/skills/daily-ai-news/scripts/ai_news.py --send",
        "mode": "isolated",
        "delivery": {
          "target": "last"
        }
      }
    ]
  }
}
```

每天早上 9 点自动推送 AI 资讯到上次联系的频道。

## 配置

编辑 `scripts/ai_news.py` 修改配置:

```python
CONFIG = {
    "max_hackernews": 5,      # HN 条目数
    "max_github": 5,          # GitHub 项目数
    "max_arxiv": 3,           # 论文数
    "keywords": ["ai", "ml", ...],  # 关键词过滤
}
```

## 依赖

- Python 3.8+
- 无需额外 pip 包（只用标准库）
- 无需 API Key

## 输出示例

```markdown
📰 每日 AI 资讯 - 2026-03-02

## 🔥 HackerNews 热门
1. [OpenAI releases GPT-5] (128👍)
2. [New neural architecture breakthrough] (95👍)
...

## ⭐ GitHub 趋势项目
1. **awesome-llm** ⭐15,234
   A curated list of Large Language Model resources
   
2. **local-ai** ⭐8,456
   Run AI models locally with one command
...

## 📄 ArXiv 最新论文
1. [Attention Is All You Need: Revisited]
   We revisit the transformer architecture and propose improvements...
...
```

## License

MIT © 2026


path: scripts/ai_news.py
content: #!/usr/bin/env python3
"""
Daily AI News Aggregator
Fetches AI-related content from HackerNews, GitHub Trending, and ArXiv
"""

import json
import re
import sys
import argparse
from datetime import datetime
from urllib.request import urlopen, Request
from urllib.parse import quote
from xml.etree import ElementTree as ET

# ============ 配置 ============
CONFIG = {
    "max_hackernews": 5,
    "max_github": 5,
    "max_arxiv": 3,
    "keywords": ["ai", "ml", "machine learning", "llm", "gpt", "neural", 
                 "deep learning", "transformer", "openai", "anthropic",
                 "stable diffusion", "midjourney", "langchain", "vector"],
}

# ============ 工具函数 ============
def fetch_json(url):
    """获取 JSON 数据"""
    try:
        req = Request(url, headers={'User-Agent': 'Mozilla/5.0'})
        with urlopen(req, timeout=10) as response:
            return json.loads(response.read().decode('utf-8'))
    except Exception as e:
        print(f"Error fetching {url}: {e}", file=sys.stderr)
        return None

def fetch_text(url):
    """获取文本数据"""
    try:
        req = Request(url, headers={'User-Agent': 'Mozilla/5.0'})
        with urlopen(req, timeout=10) as response:
            return response.read().decode('utf-8')
    except Exception as e:
        print(f"Error fetching {url}: {e}", file=sys.stderr)
        return None

def contains_ai_keywords(text):
    """检查文本是否包含 AI 关键词"""
    text_lower = text.lower()
    return any(kw in text_lower for kw in CONFIG["keywords"])

def truncate(text, max_len=100):
    """截断文本"""
    if len(text) <= max_len:
        return text
    return text[:max_len-3] + "..."

# ============ 数据源 ============
def fetch_hackernews():
    """获取 HackerNews AI 相关热帖"""
    # 使用 Algolia API 搜索 AI 相关内容
    query = quote("AI OR machine-learning OR LLM OR GPT")
    url = f"https://hn.algolia.com/api/v1/search_by_date?query={query}&tags=story&numericFilters=points>50&hitsPerPage={CONFIG['max_hackernews']*2}"
    
    data = fetch_json(url)
    if not data or 'hits' not in data:
        return []
    
    results = []
    for hit in data['hits'][:CONFIG['max_hackernews']]:
        title = hit.get('title', '')
        url = hit.get('url') or f"https://news.ycombinator.com/item?id={hit.get('objectID')}"
        points = hit.get('points', 0)
        
        results.append({
            'title': title,
            'url': url,
            'points': points,
            'source': 'HN'
        })
    
    return results

def fetch_github_trending():
    """获取 GitHub AI 趋势项目"""
    # GitHub trending 没有官方 API，使用搜索 API 找最近更新的热门项目
    url = "https://api.github.com/search/repositories?q=AI+OR+LLM+OR+GPT+created:>2025-12-01&sort=stars&order=desc&per_page=20"
    
    data = fetch_json(url)
    if not data or 'items' not in data:
        return []
    
    results = []
    for item in data['items'][:CONFIG['max_github']]:
        name = item.get('full_name', '')
        desc = item.get('description', '') or 'No description'
        stars = item.get('stargazers_count', 0)
        url = item.get('html_url', '')
        
        # 过滤非 AI 项目
        if not contains_ai_keywords(name + ' ' + desc):
            continue
            
        results.append({
            'name': name,
            'description': truncate(desc, 80),
            'stars': stars,
            'url': url,
            'source': 'GitHub'
        })
    
    return results[:CONFIG['max_github']]

def fetch_arxiv_papers():
    """获取 ArXiv AI 论文"""
    # 搜索 CS.AI 和 CS.LG 类别的最新论文
    categories = "cat:cs.AI+OR+cat:cs.LG+OR+cat:cs.CL"
    url = f"http://export.arxiv.org/api/query?search_query={categories}&sortBy=submittedDate&sortOrder=descending&max_results={CONFIG['max_arxiv']*2}"
    
    xml_data = fetch_text(url)
    if not xml_data:
        return []
    
    try:
        root = ET.fromstring(xml_data)
        ns = {'atom': 'http://www.w3.org/2005/Atom'}
        
        results = []
        for entry in root.findall('atom:entry', ns)[:CONFIG['max_arxiv']*2]:
            title = entry.find('atom:title', ns).text.strip() if entry.find('atom:title', ns) is not None else ''
            summary = entry.find('atom:summary', ns).text.strip() if entry.find('atom:summary', ns) is not None else ''
            link = entry.find('atom:id', ns).text.strip() if entry.find('atom:id', ns) is not None else ''
            
            # 过滤非 AI 论文
            if not contains_ai_keywords(title + ' ' + summary):
                continue
            
            # 清理标题中的换行符
            title = title.replace('\n', ' ').strip()
            
            results.append({
                'title': truncate(title, 80),
                'summary': truncate(summary, 100),
                'url': link,
                'source': 'ArXiv'
            })
            
            if len(results) >= CONFIG['max_arxiv']:
                break
        
        return results
    except Exception as e:
        print(f"Error parsing ArXiv XML: {e}", file=sys.stderr)
        return []

# ============ 格式化输出 ============
def format_digest(hn_items, gh_items, arxiv_items):
    """格式化日报"""
    today = datetime.now().strftime("%Y-%m-%d")
    
    lines = [
        f"📰 每日 AI 资讯 - {today}",
        "",
        "## 🔥 HackerNews 热门",
    ]
    
    if hn_items:
        for i, item in enumerate(hn_items, 1):
            lines.append(f"{i}. [{item['title']}]({item['url']}) ({item['points']}👍)")
    else:
        lines.append("暂无数据")
    
    lines.extend(["", "## ⭐ GitHub 趋势项目"])
    
    if gh_items:
        for i, item in enumerate(gh_items, 1):
            lines.append(f"{i}. **{item['name']}** ⭐{item['stars']:,}")
            lines.append(f"   {item['description']} [链接]({item['url']})")
    else:
        lines.append("暂无数据")
    
    lines.extend(["", "## 📄 ArXiv 最新论文"])
    
    if arxiv_items:
        for i, item in enumerate(arxiv_items, 1):
            lines.append(f"{i}. [{item['title']}]({item['url']})")
            lines.append(f"   {item['summary']}")
    else:
        lines.append("暂无数据")
    
    lines.extend([
        "",
        "---",
        "🤖 由 OpenClaw daily-ai-news Skill 生成"
    ])
    
    return '\n'.join(lines)

def send_to_openclaw(message, channel=None):
    """通过 OpenClaw 发送消息"""
    import subprocess
    
    cmd = ["openclaw", "message", "send"]
    if channel:
        cmd.extend(["--channel", channel])
    
    # 使用 stdin 传递消息
    result = subprocess.run(
        cmd,
        input=message,
        capture_output=True,
        text=True
    )
    
    if result.returncode != 0:
        print(f"Error sending message: {result.stderr}", file=sys.stderr)
        return False
    
    return True

# ============ 主函数 ============
def main():
    parser = argparse.ArgumentParser(description='Daily AI News Aggregator')
    parser.add_argument('--send', action='store_true', help='Send to OpenClaw channel')
    parser.add_argument('--channel', type=str, help='Target channel (e.g., feishu, telegram)')
    parser.add_argument('--json', action='store_true', help='Output as JSON')
    args = parser.parse_args()
    
    print("正在抓取 AI 资讯...", file=sys.stderr)
    
    # 并行抓取（简化版，实际可用 asyncio）
    hn_items = fetch_hackernews()
    gh_items = fetch_github_trending()
    arxiv_items = fetch_arxiv_papers()
    
    if args.json:
        output = json.dumps({
            'hackernews': hn_items,
            'github': gh_items,
            'arxiv': arxiv_items,
            'timestamp': datetime.now().isoformat()
        }, ensure_ascii=False, indent=2)
    else:
        output = format_digest(hn_items, gh_items, arxiv_items)
    
    if args.send:
        success = send_to_openclaw(output, args.channel)
        if success:
            print("✅ 已发送到频道", file=sys.stderr)
        else:
            print("❌ 发送失败", file=sys.stderr)
            sys.exit(1)
    else:
        print(output)

if __name__ == '__main__':
    main()


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