Skill: testmtcode/game-deals (version 1.0.1)

--- name: game-deals description: "获取 Steam 和 Epic Games 的限免游戏信息。触发条件：用户询问'今天有什么免费游戏'、'Steam 喜加一'、'Epic 免费游戏'、'限免游戏推送'等。支持查询当前免费游戏、即将结束的限免、以及设置定时推送。" version: 1.0.0 author: testc0de --- # Game Deals Skill 获取 Steam 和 Epic Games 平台的限免游戏信息。 ## 触发条件 当用户说以下话时，使用此技能： - "今天有什么免费游戏" - "Steam 喜加一" - "Epic 免费游戏" - "限免游戏推送" - "免费游戏推荐" - "有什么游戏在免费送" ## 功能 ### 1. 查询当前限免 **Steam 限免：** ```bash # 获取 Steam 免费游戏（需要 Steam API Key） curl -s "https://api.steampowered.com/ISteamApps/GetAppList/v2/" | jq '.applist.apps[] | select(.name | contains("Free"))' ``` 实际使用：访问 Steam 商店免费游戏页面解析 ```bash curl -s "https://store.steampowered.com/genre/Free%20to%20Play/" -H "User-Agent: Mozilla/5.0" | grep -oP 'data-ds-appid="\d+"[^>]*>[^<]*<[^>]*>[^<]*' | head -10 ``` **Epic 限免：** ```bash # Epic Games Store 免费游戏 API curl -s "https://store-site-backend-static.ak.epicgames.com/freeGamesPromotions?locale=zh-CN&country=CN&allowCountries=CN" | jq '.' ``` ### 2. 解析 Epic 响应 Epic API 返回结构： ```json { "data": { "Catalog": { "searchStore": { "elements": [ { "title": "游戏名称", "description": "游戏描述", "keyImages": [{"url": "封面图"}], "promotions": { "promotionalOffers": [{ "startDate": "2024-01-01T00:00:00Z", "endDate": "2024-01-08T00:00:00Z" }] } } ] } } } } ``` ### 3. 推送设置（可选） 使用 OpenClaw cron 设置定时检查： ```bash # 每天上午 10 点检查限免 openclaw cron add --name "game-deals-check" --schedule "0 10 * * *" --command "check-game-deals" ``` ## 使用方法 ### 手动查询 ```bash # 查询 Epic 当前限免 python3 skills/game-deals/scripts/epic_free.py # 查询 Steam 限免 python3 skills/game-deals/scripts/steam_free.py # 查询全部 python3 skills/game-deals/scripts/check_deals.py --all ``` ### 输出格式 ``` 🎮 今日限免游戏 【Epic Games】 ━━━━━━━━━━━━━━━━ 🎯 游戏名: 《XXX》 💰 原价: ¥99 📅 截止: 2024-01-08 00:00 🔗 领取: https://store.epicgames.com/zh-CN/p/xxx 【Steam】 ━━━━━━━━━━━━━━━━ 🎯 游戏名: 《YYY》 💰 原价: ¥68 → 免费 📅 截止: 限时免费开玩 🔗 领取: https://store.steampowered.com/app/xxx ``` ## 文件结构 ``` skills/game-deals/ ├── SKILL.md # 本文件 ├── scripts/ │ ├── epic_free.py # Epic 限免查询 │ ├── steam_free.py # Steam 限免查询 │ └── check_deals.py # 统一入口 ├── cache/ # 缓存目录 │ └── last_check.json # 上次检查结果 └── config.json # 配置（可选） ``` ## 依赖 - Python 3 - requests - jq (可选，用于命令行解析) 安装： ```bash pip3 install requests ``` ## 注意事项 1. Epic API 不需要 Key，但有频率限制 2. Steam 免费游戏页面需要解析 HTML，可能因页面结构变化而失效 3. 建议缓存结果，避免频繁请求 4. 部分限免可能有区域限制 ## 未来扩展 - [ ] GOG 平台支持 - [ ] Humble Bundle 限免 - [ ] 邮件/消息推送 - [ ] 游戏评分和简介 - [ ] 历史限免记录

path: README.md
content: # Game Deals Skill for OpenClaw

获取 Epic Games 和 Steam 限免/免费游戏信息。

## 安装

### 通过 ClawHub（推荐）
```bash
clawhub install game-deals
```

### 手动安装
```bash
git clone https://github.com/你的用户名/game-deals.git ./skills/game-deals
```

## 使用

```bash
# 查询所有平台
python3 skills/game-deals/scripts/check_deals.py

# 单独查询 Epic
python3 skills/game-deals/scripts/epic_free.py

# 单独查询 Steam
python3 skills/game-deals/scripts/steam_free.py
```

## 触发词

在 OpenClaw 中，说以下话会自动触发：
- "今天有什么免费游戏"
- "Steam 喜加一"
- "Epic 免费游戏"
- "限免游戏"

## 依赖

- Python 3
- requests

```bash
pip3 install requests
```

## 作者

帕西法尔


path: scripts/check_deals.py
content: #!/usr/bin/env python3
"""
统一入口：检查所有平台限免游戏
"""

import sys
import os
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))

from epic_free import get_epic_free_games, format_output as format_epic
from steam_free import get_steam_free_games, format_output as format_steam

def check_all():
    """检查所有平台"""
    print("🔍 正在查询限免游戏...\n")
    
    # Epic
    print(format_epic(get_epic_free_games()))
    print("\n")
    
    # Steam
    print(format_steam(get_steam_free_games()))
    print("\n✅ 查询完成！")

if __name__ == "__main__":
    check_all()


path: scripts/epic_free.py
content: #!/usr/bin/env python3
"""
获取 Epic Games Store 当前限免游戏
"""

import requests
import json
from datetime import datetime

def get_epic_free_games():
    """获取 Epic 限免游戏"""
    url = "https://store-site-backend-static.ak.epicgames.com/freeGamesPromotions"
    params = {
        "locale": "zh-CN",
        "country": "CN",
        "allowCountries": "CN"
    }
    
    try:
        response = requests.get(url, params=params, timeout=30)
        response.raise_for_status()
        data = response.json()
        
        games = []
        
        # 处理错误情况，API 可能返回 errors 但仍有数据
        catalog = data.get("data", {}).get("Catalog", {}) or {}
        search_store = catalog.get("searchStore", {}) or {}
        elements = search_store.get("elements", []) or []
        
        for game in elements:
            # 检查是否有促销活动
            promotions = game.get("promotions", {}) or {}
            promo_offers = promotions.get("promotionalOffers", [])
            
            # 只处理有促销的游戏（discountPercentage 为 0 表示免费）
            if promo_offers and len(promo_offers) > 0:
                offer_list = promo_offers[0].get("promotionalOffers", [])
                if offer_list:
                    offer = offer_list[0]
                    discount_pct = offer.get("discountSetting", {}).get("discountPercentage", 100)
                    
                    # 100% 折扣 = 免费
                    if discount_pct == 0:
                        start_date = offer.get("startDate", "")
                        end_date = offer.get("endDate", "")
                        
                        # 获取封面图
                        images = game.get("keyImages", [])
                        image_url = ""
                        for img in images:
                            if img.get("type") == "OfferImageWide":
                                image_url = img.get("url", "")
                                break
                        
                        # 获取价格信息
                        price_info = game.get("price", {}).get("totalPrice", {})
                        original_price = price_info.get("originalPrice", 0)
                        currency = price_info.get("currencyCode", "CNY")
                        
                        game_info = {
                            "title": game.get("title", "未知"),
                            "description": game.get("description", ""),
                            "image": image_url,
                            "startDate": start_date,
                            "endDate": end_date,
                            "originalPrice": original_price / 100 if original_price else 0,  # 转换为元
                            "currency": currency,
                            "url": f"https://store.epicgames.com/zh-CN/p/{game.get('productSlug', game.get('urlSlug', game.get('id', '')))}"
                        }
                        games.append(game_info)
        
        return games
    
    except Exception as e:
        return {"error": str(e)}

def format_output(games):
    """格式化输出"""
    if isinstance(games, dict) and "error" in games:
        return f"❌ 获取失败: {games['error']}"
    
    if not games:
        return "🎮 Epic 当前没有限免游戏"
    
    output = ["🎮 Epic Games 限免游戏", "=" * 40]
    
    for game in games:
        output.append(f"\n🎯 {game['title']}")
        if game['originalPrice'] > 0:
            output.append(f"💰 原价: ¥{game['originalPrice']:.0f}")
        output.append(f"📅 截止: {game['endDate'][:10] if game['endDate'] else '未知'}")
        output.append(f"🔗 {game['url']}")
        if game['description']:
            desc = game['description'][:100] + "..." if len(game['description']) > 100 else game['description']
            output.append(f"📝 {desc}")
        output.append("-" * 40)
    
    return "\n".join(output)

if __name__ == "__main__":
    games = get_epic_free_games()
    print(format_output(games))


path: scripts/steam_free.py
content: #!/usr/bin/env python3
"""
获取 Steam 限免/免费游戏信息
"""

import requests
import json

def get_steam_free_games():
    """获取 Steam 免费游戏 - 使用搜索 API"""
    games = []
    
    headers = {
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36",
        "Accept": "application/json, text/plain, */*",
        "Referer": "https://store.steampowered.com/"
    }
    
    try:
        # Steam 搜索 API - 搜索免费游戏
        url = "https://store.steampowered.com/api/search"
        params = {
            "term": "",
            "max_results": 20,
            "filters": "price:free"
        }
        
        response = requests.get(url, headers=headers, params=params, timeout=30)
        
        if response.status_code == 200:
            data = response.json()
            items = data.get("items", [])
            
            for item in items:
                games.append({
                    "appid": item.get("id"),
                    "name": item.get("name"),
                    "url": f"https://store.steampowered.com/app/{item.get('id')}/",
                    "type": "免费游戏"
                })
        
        # 如果没有结果，提供常用免费游戏列表
        if not games:
            games = [
                {"appid": "730", "name": "Counter-Strike 2", "url": "https://store.steampowered.com/app/730/", "type": "免费开玩"},
                {"appid": "570", "name": "Dota 2", "url": "https://store.steampowered.com/app/570/", "type": "免费开玩"},
                {"appid": "440", "name": "Team Fortress 2", "url": "https://store.steampowered.com/app/440/", "type": "免费开玩"},
                {"appid": "1172470", "name": "Apex Legends", "url": "https://store.steampowered.com/app/1172470/", "type": "免费开玩"},
                {"appid": "1085660", "name": "Destiny 2", "url": "https://store.steampowered.com/app/1085660/", "type": "免费开玩"},
            ]
                
    except Exception as e:
        # 出错时返回常用免费游戏
        games = [
            {"appid": "730", "name": "Counter-Strike 2", "url": "https://store.steampowered.com/app/730/", "type": "免费开玩"},
            {"appid": "570", "name": "Dota 2", "url": "https://store.steampowered.com/app/570/", "type": "免费开玩"},
            {"appid": "440", "name": "Team Fortress 2", "url": "https://store.steampowered.com/app/440/", "type": "免费开玩"},
        ]
    
    return games[:10]

def format_output(games):
    """格式化输出"""
    if isinstance(games, dict) and "error" in games:
        return f"❌ 获取失败: {games['error']}"
    
    if not games:
        return "🎮 Steam 暂无特别限免游戏"
    
    output = ["🎮 Steam 免费游戏推荐", "=" * 40]
    
    for game in games:
        output.append(f"\n🎯 {game['name']}")
        output.append(f"📌 {game['type']}")
        output.append(f"🔗 {game['url']}")
        output.append("-" * 40)
    
    output.append("\n更多免费游戏：https://store.steampowered.com/genre/Free%20to%20Play/")
    
    return "\n".join(output)

if __name__ == "__main__":
    games = get_steam_free_games()
    print(format_output(games))


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