Skill: sfsf332/claw-loudyai-skill (version 1.1.0)

--- name: claw-loudyai-skill description: | Loudy.ai 任务自动化工具 - 查询奖池、提交任务链接、追踪审核和支付状态。 重要说明：此工具仅与 loudy.ai API 交互，不包含 Twitter/X 自动发布功能。 使用条件： (1) 用户需要先配置 LOUDY_API_KEY 环境变量 (2) 启动后会定时查询奖池、提交任务链接、查询审核和支付状态 (3) 适用于需要自动管理 loudy.ai 任务的场景 metadata: openclaw: requires: env: - name: LOUDY_API_KEY description: "Loudy.ai API Key" required: true --- # Loudy.ai 自动任务 Skill ## ⚠️ 安全警告 - 🔐 **API Key 安全**：建议使用环境变量 `export LOUDY_API_KEY=你的密钥`，**不要**写入 TOOLS.md 或其他共享文件 - 📝 **文件系统访问**：本工具会读写工作区目录下的文件（默认：`/root/.openclaw/workspace/`，可通过 `OPENCLAW_WORKSPACE` 环境变量自定义） - ⏰ **可选的 Cron 任务**：如需自动检查，需手动配置 cron 任务 ## 快速开始 ### 1. 配置 API Key (推荐环境变量方式) ```bash export LOUDY_API_KEY="你的API密钥" ``` ⚠️ **注意**：不建议将密钥写入 TOOLS.md，避免意外泄露 ### 2. 启动任务 告诉 AI： > "查看 loudy 可用奖池" ### 3. 自动执行流程 1. AI 展示可用奖池详情（包括要求、奖金、截止时间、详情页面链接） 2. 你根据要求发布推文到 X/Twitter 3. 将推文链接发送给 AI 4. AI 自动提交到 loudy.ai ## 工作流程 ``` 1. fetch_earning_pools() → 获取进行中的奖池列表 2. 展示奖池详情给用户 → 包括要求、奖金、截止时间、详情页面链接 3. 提示用户根据要求发推文 → 用户手动发布到 X/Twitter 4. 等待用户提供推文链接 → 用户把推文链接发给 AI 5. submit_task() → 自动提交作品链接到 loudy.ai 6. 定时 check_task_status() → 查询任务是否被接受 ├─ 超时未接受 → 报告失败 └─ 已接受 → 定时查询 payment/支付信息 ``` ## 用户交互流程示例 ### 步骤 1: 查看可用奖池 ``` 用户: "查看 loudy 可用奖池" AI: 调用 fetch_pools.py，展示奖池列表（包含详情页面链接） ``` ### 步骤 2: 选择奖池并发推文 ``` AI: 展示奖池详情和要求 用户: 选择奖池，根据要求发布推文 ``` ### 步骤 3: 提交推文链接 ``` 用户: "提交推文链接 https://x.com/xxx/status/123 到奖池 3" AI: 调用 submit_task.py(3, "https://x.com/xxx/status/123") ``` ### 步骤 4: 查询任务状态 ``` 用户: "查询任务状态" AI: 调用 check_task.py 查询审核和支付状态 ``` ## API 接口 ### 1. 获取奖池列表 - **URL**: `GET https://api.loudy.ai/app-api/open-api/v1/earning-pools` - **Header**: `X-[REDACTED_SECRET]` ### 2. 获取奖池详情 - **URL**: `GET https://api.loudy.ai/app-api/open-api/v1/earning-pools/{id}` - **Header**: `X-[REDACTED_SECRET]` ### 3. 提交任务 - **URL**: `POST https://api.loudy.ai/app-api/open-api/v1/earning-pool-tasks/submit` - **Header**: `X-[REDACTED_SECRET]` - **Body**: ```json { "earningPoolId": 123, "taskLink": ["https://x.com/xxx/status/123"], "languageType": "zh_CN" } ``` ### 4. 查询我的任务列表（分页） - **URL**: `GET https://api.loudy.ai/app-api/open-api/v1/earning-pool-tasks` - **Header**: `X-[REDACTED_SECRET]` - **Query**: - `pageNo` - 页码（必填） - `pageSize` - 每页条数，最大100（必填） - `earningPoolId` - 奖池ID（可选） - `taskStatus` - 任务状态（可选） ### 5. 查询任务状态 - **URL**: `GET https://api.loudy.ai/app-api/open-api/v1/earning-pool-tasks/{id}` - **Header**: `X-[REDACTED_SECRET]` - **返回字段**: - `taskStatus` - 任务状态 - `auditStatus` - 审核状态 (0=未审核, 1=通过, 2=拒绝) - `taskLinks` - 作品链接 ## 脚本说明 ### scripts/fetch_pools.py 获取进行中的奖池列表，过滤 Ongoing 状态 ### scripts/list_my_tasks.py 查询当前用户的任务列表（分页） ### scripts/submit_task.py 提交作品链接到奖池 ### scripts/check_task.py 查询单个任务状态和支付信息 ### scripts/auto_task_flow.py 优化的任务流程脚本： 1. 获取可用奖池列表 2. 展示奖池详情（包括要求、奖金、截止时间） 3. 显示 Loudy.ai 详情页面链接 4. 等待用户提供推文链接 5. 自动提交到 loudy.ai ### scripts/check_tasks.py 定时检查脚本，获取当前奖池并格式化输出 ### scripts/cron_check.sh Cron 定时任务脚本，每5分钟检查一次新任务。使用 `OPENCLAW_WORKSPACE` 环境变量自定义工作目录。 ## 配置定时检查（可选） ### 1. 设置环境变量 ```bash export LOUDY_API_KEY="你的API Key" export OPENCLAW_WORKSPACE="/root/.openclaw/workspace" # 可选，默认值 ``` ### 2. 配置 Cron 定时检查（可选） ```bash # 方法1: 使用工作区安装路径（推荐） SKILL_DIR="/root/.openclaw/workspace/skills/claw-loudyai-skill" (crontab -l 2>/dev/null; echo "*/5 * * * * $SKILL_DIR/scripts/cron_check.sh") | crontab - # 方法2: 如果安装到系统路径 (crontab -l 2>/dev/null; echo "*/5 * * * * /usr/lib/node_modules/openclaw/skills/loudy-ai-auto-task/scripts/cron_check.sh") | crontab - ``` ### 3. 配置 Heartbeat 通知（可选） 在 HEARTBEAT.md 中添加： ``` ## Loudy.ai 任务检查 检查工作区目录下的 loudy_has_new.txt 是否存在： - 如果存在 → 读取 loudy_tasks.json 内容 - 发送消息通知用户 - 删除 loudy_has_new.txt ``` ## 注意事项 - ⚠️ **API Key 安全**：建议使用环境变量 `export LOUDY_API_KEY=你的密钥`，**不要**写入 TOOLS.md 或其他共享文件 - 📝 **文件系统访问**：本工具会读写工作区目录下的文件（默认：`/root/.openclaw/workspace/`，可通过 `OPENCLAW_WORKSPACE` 环境变量自定义），包括 `loudy_tasks.json`, `loudy_has_new.txt` - ⏰ **可选的 Cron 任务**：如需自动检查，需手动配置 cron 任务（可选功能） - 🐦 **Twitter 功能说明**：本工具**不包含** Twitter/X 自动发布功能，用户需手动发布推文后提供链接 - ⏳ **任务截止时间**：任务有截止时间 (activityEnd)，需在截止前提交 - ✅ **审核流程**：提交后需等待审核 (auditStatus)，建议设置定时检查间隔为 5-10 分钟

path: _meta.json
content: ownerId: kn71vf28vc92qwy3xt9af14f9n829nfr
slug: claw-loudyai-skill
version: 0.1.0
publishedAt: 1772772820004

path: install.sh
content: #!/bin/bash
# Loudy.ai 自动任务 Skill 安装脚本
# 使用方法: curl -fsSL https://raw.githubusercontent.com/sfsf332/claw-loudyai-skill/main/install.sh | bash

set -e

# 颜色定义
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color

# 打印带颜色的信息
info() {
    echo -e "${BLUE}[INFO]${NC} $1"
}

success() {
    echo -e "${GREEN}[SUCCESS]${NC} $1"
}

warning() {
    echo -e "${YELLOW}[WARNING]${NC} $1"
}

error() {
    echo -e "${RED}[ERROR]${NC} $1"
    exit 1
}

# 检查依赖
check_dependencies() {
    info "检查依赖..."
    
    if ! command -v git &> /dev/null; then
        error "Git 未安装，请先安装 Git"
    fi
    
    if ! command -v python3 &> /dev/null; then
        error "Python3 未安装，请先安装 Python3"
    fi
    
    success "依赖检查通过"
}

# 获取安装路径
get_install_path() {
    # 优先使用 OPENCLAW_SKILLS_DIR 环境变量
    if [ -n "$OPENCLAW_SKILLS_DIR" ]; then
        INSTALL_DIR="$OPENCLAW_SKILLS_DIR"
    else
        # 默认路径
        INSTALL_DIR="/usr/lib/node_modules/openclaw/skills"
    fi
    
    info "安装路径: $INSTALL_DIR"
}

# 创建目录
create_directories() {
    info "创建目录..."
    
    if [ ! -d "$INSTALL_DIR" ]; then
        warning "目录 $INSTALL_DIR 不存在，尝试创建..."
        mkdir -p "$INSTALL_DIR" || error "无法创建目录 $INSTALL_DIR，请检查权限"
    fi
    
    # 检查写入权限
    if [ ! -w "$INSTALL_DIR" ]; then
        error "没有写入权限: $INSTALL_DIR，请以 root 用户运行或使用 sudo"
    fi
    
    success "目录创建完成"
}

# 克隆仓库
clone_repository() {
    info "克隆仓库..."
    
    cd "$INSTALL_DIR"
    
    # 如果目录已存在，先删除
    if [ -d "loudy-ai-auto-task" ]; then
        warning "目录已存在，正在删除..."
        rm -rf loudy-ai-auto-task
    fi
    
    # 克隆仓库
    git clone https://github.com/sfsf332/claw-loudyai-skill.git loudy-ai-auto-task || error "克隆仓库失败"
    
    success "仓库克隆完成"
}

# 设置权限
set_permissions() {
    info "设置权限..."
    
    cd "$INSTALL_DIR/loudy-ai-auto-task"
    
    # 设置脚本可执行权限
    if [ -d "scripts" ]; then
        chmod +x scripts/*.py scripts/*.sh 2>/dev/null || true
    fi
    
    success "权限设置完成"
}

# 验证安装
verify_installation() {
    info "验证安装..."
    
    cd "$INSTALL_DIR/loudy-ai-auto-task"
    
    # 检查必要文件
    if [ ! -f "SKILL.md" ]; then
        error "SKILL.md 文件缺失"
    fi
    
    # 检查 Python 语法
    if [ -d "scripts" ]; then
        for file in scripts/*.py; do
            if [ -f "$file" ]; then
                python3 -m py_compile "$file" || error "Python 语法错误: $file"
            fi
        done
    fi
    
    success "安装验证通过"
}

# 打印使用说明
print_usage() {
    echo ""
    echo -e "${GREEN}================================${NC}"
    echo -e "${GREEN}  安装成功！${NC}"
    echo -e "${GREEN}================================${NC}"
    echo ""
    echo -e "${BLUE}使用说明:${NC}"
    echo ""
    echo "1. 配置 API Key (推荐环境变量方式):"
    echo -e "   ${YELLOW}export LOUDY_API_KEY=你的API密钥${NC}"
    echo ""
    echo "2. 在 OpenClaw 中对 AI 说:"
    echo -e "   ${YELLOW}\"查看 loudy 可用奖池\"${NC}"
    echo ""
    echo "3. 根据提示发推文，然后将推文链接发给 AI:"
    echo -e "   ${YELLOW}\"提交推文链接 https://x.com/... 到奖池 3\"${NC}"
    echo ""
    echo -e "${BLUE}注意事项:${NC}"
    echo "- 请妥善保管 API Key，不要分享给他人"
    echo "- 建议将 API Key 设置为环境变量，不要写入文件"
    echo "- 默认安装路径: $INSTALL_DIR"
    echo ""
    echo -e "${GREEN}================================${NC}"
    echo ""
}

# 主函数
main() {
    echo -e "${GREEN}================================${NC}"
    echo -e "${GREEN}  Loudy.ai 自动任务 Skill 安装脚本${NC}"
    echo -e "${GREEN}================================${NC}"
    echo ""
    
    check_dependencies
    get_install_path
    create_directories
    clone_repository
    set_permissions
    verify_installation
    print_usage
    
    exit 0
}

# 运行主函数
main "$@"


path: package.json
content: {
  "name": "loudy-ai-auto-task",
  "version": "1.0.1",
  "description": "Loudy.ai 任务自动化工具 - 查询奖池、提交任务链接、追踪审核和支付状态",
  "main": "SKILL.md",
  "files": [
    "SKILL.md",
    "scripts/",
    "references/"
  ],
  "scripts": {},
  "keywords": [
    "loudy",
    "automation",
    "task",
    "api"
  ],
  "author": "",
  "license": "MIT"
}


path: references/api.md
content: # Loudy.ai API Reference

## Base URL
```
https://api.loudy.ai
```

## Common Headers
所有接口需要：
```
X-[REDACTED_SECRET]
```

## Endpoints

### 1. 获取奖池列表
```
GET /app-api/open-api/v1/earning-pools
```
Query: (none)

Response:
```json
{
  "code": 200,
  "msg": "success",
  "data": [
    {
      "id": 123,
      "sponsor": "Sponsor Name",
      "price": "$4,000",
      "curator": "Curator Name",
      "distribution": "Equal Share",
      "activityStart": "2024-01-01T00:00:00Z",
      "activityEnd": "2024-01-31T00:00:00Z",
      "platform": "X / Twitter",
      "brief": "Task description",
      "briefLink": "https://...",
      "status": "Ongoing"
    }
  ]
}
```

### 2. 获取奖池详情
```
GET /app-api/open-api/v1/earning-pools/{id}
```

### 3. 提交任务
```
POST /app-api/open-api/v1/earning-pool-tasks/submit
Content-Type: application/json

{
  "earningPoolId": 123,
  "taskLink": ["https://x.com/xxx/status/123"],
  "languageType": "zh_CN"
}
```

### 4. 查询我的任务列表（分页）
```
GET /app-api/open-api/v1/earning-pool-tasks?pageNo=1&pageSize=10
```
Query:
- `pageNo` (required): 页码，从1开始
- `pageSize` (required): 每页条数，最大100
- `earningPoolId` (optional): 奖池ID
- `taskStatus` (optional): 任务状态

### 5. 查询任务状态
```
GET /app-api/open-api/v1/earning-pool-tasks/{id}
```

Response:
```json
{
  "code": 200,
  "data": {
    "id": 456,
    "earningPoolId": 123,
    "userId": 789,
    "kolXHandle": "@handle",
    "sponsor": "Sponsor Name",
    "taskStatus": "SUBMITTED",
    "taskLinks": ["https://x.com/xxx/status/123"],
    "walletAddress": "0x...",
    "txn": "0x...",
    "auditStatus": 0
  }
}
```

## 状态说明

### auditStatus (审核状态)
- `0` - 未审核
- `1` - 审核通过
- `2` - 审核拒绝

### status (奖池状态)
- `Ongoing` - 进行中
- `Completed` - 已结束


path: scripts/auto_task_flow.py
content: #!/usr/bin/env python3
"""
Loudy.ai 自动任务流程
"""
import requests
import json
import os
import sys
from datetime import datetime, timezone

API_BASE = "https://api.loudy.ai/app-api/open-api/v1"
LOUDY_API_KEY = os.environ.get("LOUDY_API_KEY", "")

def fetch_earning_pools():
    """获取进行中的奖池列表"""
    url = f"{API_BASE}/earning-pools"
    headers = {
        "X-API-Key": LOUDY_API_KEY,
        "Content-Type": "application/x-www-form-urlencoded"
    }
    
    try:
        response = requests.get(url, headers=headers, timeout=30)
        response.raise_for_status()
        data = response.json()
    except requests.exceptions.RequestException as e:
        print(f"Error: Request failed - {e}", file=sys.stderr)
        return []
    
    if data.get("code") != 0 and data.get("code") != 200:
        print(f"Error: {data.get('msg')}", file=sys.stderr)
        return []
    
    pools = data.get("data", [])
    # 过滤进行中的奖池
    ongoing_pools = [p for p in pools if p.get("status") == "Ongoing"]
    
    return ongoing_pools

def fetch_pool_detail(pool_id: int):
    """获取奖池详情"""
    url = f"{API_BASE}/earning-pools/{pool_id}"
    headers = {
        "X-API-Key": LOUDY_API_KEY,
        "Content-Type": "application/x-www-form-urlencoded"
    }
    
    try:
        response = requests.get(url, headers=headers, timeout=30)
        response.raise_for_status()
        data = response.json()
    except requests.exceptions.RequestException as e:
        print(f"Error: Request failed - {e}", file=sys.stderr)
        return None
    
    if data.get("code") != 0 and data.get("code") != 200:
        print(f"Error: {data.get('msg')}", file=sys.stderr)
        return None
    
    return data.get("data")

def submit_task(earning_pool_id: int, task_link: str, language_type: str = "zh_CN"):
    """提交任务作品链接"""
    url = f"{API_BASE}/earning-pool-tasks/submit"
    headers = {
        "X-API-Key": LOUDY_API_KEY,
        "Content-Type": "application/json"
    }
    
    payload = {
        "earningPoolId": earning_pool_id,
        "taskLink": [task_link],
        "languageType": language_type
    }
    
    try:
        response = requests.post(url, headers=headers, json=payload, timeout=30)
        response.raise_for_status()
        data = response.json()
    except requests.exceptions.RequestException as e:
        print(f"Error: Request failed - {e}", file=sys.stderr)
        return None
    
    if data.get("code") != 0 and data.get("code") != 200:
        print(f"Error: {data.get('msg')}", file=sys.stderr)
        return None
    
    return data.get("data")

def format_pool_info(pool):
    """格式化奖池信息"""
    pool_id = pool.get('id')
    loudy_link = f"https://loudy.ai?poolId={pool_id}"
    
    # 计算剩余时间
    activity_end = pool.get('activityEnd')
    try:
        end_time = datetime.fromisoformat(activity_end.replace('Z', '+00:00'))
        now = datetime.now(timezone.utc)
        remaining = end_time - now
        hours = int(remaining.total_seconds() // 3600)
        time_left = f"{hours} 小时"
    except:
        time_left = "未知"
    
    info = {
        "id": pool_id,
        "sponsor": pool.get('sponsor'),
        "price": pool.get('price'),
        "curator": pool.get('curator'),
        "distribution": pool.get('distribution'),
        "platform": pool.get('platform'),
        "brief": pool.get('brief'),
        "briefLink": pool.get('briefLink'),
        "loudy_link": loudy_link,
        "time_left": time_left
    }
    
    return info

def is_binance_task(sponsor):
    """检查是否为币安任务"""
    binance_keywords = ['binance', '币安', 'Binance', 'BINANCE']
    return any(keyword.lower() in sponsor.lower() for keyword in binance_keywords)

def display_pools(pools):
    """展示奖池信息"""
    for pool in pools:
        info = format_pool_info(pool)
        
        print(f"\n{'='*60}")
        print(f"📊 奖池 ID: {info['id']}")
        print(f"{'='*60}")
        print(f"🎁 赞助方: {info['sponsor']}")
        print(f"💰 奖金: {info['price']}")
        print(f"🎯 策划方: {info['curator']}")
        print(f"📦 分配方式: {info['distribution']}")
        print(f"🌐 平台: {info['platform']}")
        print(f"⏰ 剩余时间: {info['time_left']}")
        print(f"\n🔗 详情页面: {info['loudy_link']}")
        print(f"📚 详细资料: {info['briefLink']}")
        print(f"\n📋 任务要求:")
        print(f"{info['brief']}")
        
        # 检查是否为币安任务
        if is_binance_task(info['sponsor']):
            print(f"\n{'='*60}")
            print(f"🚀 币安任务专属提示")
            print(f"{'='*60}")
            print(f"💡 检测到这是币安任务！")
            print(f"\n📦 推荐使用币安官方 Skill 一键完成：")
            print(f"🔗 GitHub: https://github.com/binance/binance-skills-hub/tree/main/skills/binance/square-post")
            
            print(f"\n✨ Skill 功能：")
            print(f"   ✓ 自动生成符合币安要求的推文内容")
            print(f"   ✓ 一键发布到 X/Twitter")
            print(f"   ✓ 自动返回推文链接")
            print(f"   ✓ 支持批量任务处理")
            
            print(f"\n📥 安装步骤：")
            print(f"   1️⃣  安装 Skill:")
            print(f"       $ clawhub install binance/square-post")
            print(f"   2️⃣  等待安装完成...")
            
            print(f"\n🚀 使用方法（安装后）：")
            print(f"   方式一：告诉 AI")
            print(f"   对我说：\"帮我用币安 skill 完成这个任务\"")
            print(f"   ")
            print(f"   方式二：直接运行")
            print(f"   $ cd ~/.openclaw/skills/binance-square-post")
            print(f"   $ ./scripts/generate_and_post.sh <任务ID>")
            
            print(f"\n📋 完整流程：")
            print(f"   1. 安装币安 skill")
            print(f"   2. 运行 skill 生成并发布推文")
            print(f"   3. 获取生成的推文链接")
            print(f"   4. 将推文链接发送给我")
            print(f"   5. 我会自动提交到 loudy.ai")
            
            print(f"\n💡 提示：")
            print(f"   - 币安 skill 会自动处理推文格式和内容要求")
            print(f"   - 支持 AI 智能生成，无需手动编写")
            print(f"   - 提高任务完成效率，避免格式错误")
            print(f"{'='*60}")
        
        print(f"{'='*60}\n")

if __name__ == "__main__":
    # 展示可用的奖池
    pools = fetch_earning_pools()
    
    if not pools:
        print("当前没有可用的奖池")
    else:
        display_pools(pools)


path: scripts/check_task.py
content: #!/usr/bin/env python3
"""
查询任务状态和支付信息
"""
import requests
import json
import os

API_BASE = "https://api.loudy.ai/app-api/open-api/v1"
LOUDY_API_KEY = os.environ.get("LOUDY_API_KEY", "")

def check_task_status(task_id: int):
    """
    查询任务状态
    
    Args:
        task_id: 任务ID
    
    Returns:
        任务详情，包含:
        - taskStatus: 任务状态
        - auditStatus: 审核状态 (0=未审核, 1=通过, 2=拒绝)
        - taskLinks: 作品链接
        - txn: 交易哈希
    """
    url = f"{API_BASE}/earning-pool-tasks/{task_id}"
    headers = {
        "X-API-Key": LOUDY_API_KEY,
        "Content-Type": "application/x-www-form-urlencoded"
    }
    
    response = requests.get(url, headers=headers)
    data = response.json()
    
    if data.get("code") != 0 and data.get("code") != 200:
        print(f"Error: {data.get('msg')}")
        return None
    
    return data.get("data")

def parse_audit_status(audit_status: int) -> str:
    """解析审核状态"""
    status_map = {
        0: "未审核",
        1: "审核通过",
        2: "审核拒绝"
    }
    return status_map.get(audit_status, "未知")

def main():
    import sys
    if len(sys.argv) < 2:
        print("Usage: python check_task.py <task_id>")
        sys.exit(1)
    
    task_id = int(sys.argv[1])
    task = check_task_status(task_id)
    
    if task:
        print(json.dumps(task, indent=2, ensure_ascii=False))
        print("\n--- 状态摘要 ---")
        print(f"任务状态: {task.get('taskStatus')}")
        print(f"审核状态: {parse_audit_status(task.get('auditStatus', 0))}")
        print(f"作品链接: {task.get('taskLinks')}")
        print(f"交易哈希: {task.get('txn')}")

if __name__ == "__main__":
    main()


path: scripts/check_tasks.py
content: #!/usr/bin/env python3
"""
检查奖池并通知用户
"""
import requests
import json
import os
import sys

API_BASE = "https://api.loudy.ai/app-api/open-api/v1"
LOUDY_API_KEY = os.environ.get("LOUDY_API_KEY", "")

def fetch_earning_pools():
    """获取进行中的奖池列表"""
    url = f"{API_BASE}/earning-pools"
    headers = {
        "X-API-Key": LOUDY_API_KEY,
        "Content-Type": "application/x-www-form-urlencoded"
    }
    
    response = requests.get(url, headers=headers)
    data = response.json()
    
    if data.get("code") != 0 and data.get("code") != 200:
        print(f"Error: {data.get('msg')}")
        return []
    
    pools = data.get("data", [])
    # 过滤进行中的奖池
    ongoing_pools = [p for p in pools if p.get("status") == "Ongoing"]
    
    return ongoing_pools

def main():
    pools = fetch_earning_pools()
    
    if not pools:
        print("NO_TASKS")
        sys.exit(0)
    
    print(f"FOUND {len(pools)} TASK(S):")
    for p in pools:
        print(f"赞助方: {p.get('sponsor')}")
        print(f"奖金: {p.get('price')}")
        print(f"分销方式: {p.get('distribution')}")
        print(f"平台: {p.get('platform')}")
        print(f"开始: {p.get('activityStart')}")
        print(f"截止: {p.get('activityEnd')}")
        print(f"简介: {p.get('brief')}")
        pool_id = p.get('id')
        loudy_link = f"https://loudy.ai?poolId={pool_id}"
        print(f"链接: {loudy_link}")
        print(f"---")

if __name__ == "__main__":
    main()


path: scripts/cron_check.sh
content: #!/bin/bash
# loudy.ai 定时检查脚本
# 每5分钟检查一次

# 获取工作目录（优先使用环境变量，否则使用默认路径）
WORKSPACE_DIR="${OPENCLAW_WORKSPACE:-/root/.openclaw/workspace}"

# 从环境变量读取 API Key，若未设置则退出
if [ -z "$LOUDY_API_KEY" ]; then
    echo "LOUDY_API_KEY not set"
    exit 1
fi
export LOUDY_API_KEY

LOG_FILE="$WORKSPACE_DIR/loudy_tasks.json"
LAST_FILE="$WORKSPACE_DIR/loudy_last.json"

# 获取技能目录（支持工作区安装和系统安装）
SKILL_DIR="${OPENCLAW_SKILL_DIR:-$(dirname "$(dirname "$0")")}"
if [ -f "$SKILL_DIR/scripts/check_tasks.py" ]; then
    CHECK_SCRIPT="$SKILL_DIR/scripts/check_tasks.py"
elif [ -f "/usr/lib/node_modules/openclaw/skills/loudy-ai-auto-task/scripts/check_tasks.py" ]; then
    CHECK_SCRIPT="/usr/lib/node_modules/openclaw/skills/loudy-ai-auto-task/scripts/check_tasks.py"
else
    echo "Error: check_tasks.py not found"
    exit 1
fi

# 获取当前奖池
python3 "$CHECK_SCRIPT" > "$LOG_FILE"

# 比较是否有新任务
if [ -f "$LAST_FILE" ]; then
    if diff -q "$LOG_FILE" "$LAST_FILE" > /dev/null 2>&1; then
        # 没有变化
        exit 0
    fi
fi

# 有新任务或首次运行，保存并标记需要通知
cp "$LOG_FILE" "$LAST_FILE"
echo "NEW_TASKS" > "$WORKSPACE_DIR/loudy_has_new.txt"


path: scripts/fetch_pools.py
content: #!/usr/bin/env python3
"""
获取进行中的奖池列表
"""
import requests
import json
import os

API_BASE = "https://api.loudy.ai/app-api/open-api/v1"
LOUDY_API_KEY = os.environ.get("LOUDY_API_KEY", "")

def fetch_earning_pools():
    """获取进行中的奖池列表"""
    url = f"{API_BASE}/earning-pools"
    headers = {
        "X-API-Key": LOUDY_API_KEY,
        "Content-Type": "application/x-www-form-urlencoded"
    }
    
    response = requests.get(url, headers=headers)
    data = response.json()
    if data.get("code") != 0 and data.get("code") != 200:
        print(f"Error: {data.get('msg')}")
        return []
    
    pools = data.get("data", [])
    # 过滤进行中的奖池
    ongoing_pools = [p for p in pools if p.get("status") == "Ongoing"]
    
    return ongoing_pools

def main():
    pools = fetch_earning_pools()
    print(json.dumps(pools, indent=2, ensure_ascii=False))

if __name__ == "__main__":
    main()


path: scripts/list_my_tasks.py
content: #!/usr/bin/env python3
"""
查询当前用户的任务列表（分页）
"""
import requests
import json
import os
import sys

API_BASE = "https://api.loudy.ai/app-api/open-api/v1"
LOUDY_API_KEY = os.environ.get("LOUDY_API_KEY", "")

def fetch_my_tasks(page_no: int = 1, page_size: int = 10, earning_pool_id: int = None, task_status: str = None):
    """
    查询当前用户的任务列表
    
    Args:
        page_no: 页码，从1开始
        page_size: 每页条数，最大100
        earning_pool_id: 奖池ID（可选）
        task_status: 任务状态（可选）
    
    Returns:
        任务列表和分页信息
    """
    url = f"{API_BASE}/earning-pool-tasks"
    headers = {
        "X-API-Key": LOUDY_API_KEY,
        "Content-Type": "application/x-www-form-urlencoded"
    }
    
    params = {
        "pageNo": str(page_no),
        "pageSize": str(page_size)
    }
    
    if earning_pool_id:
        params["earningPoolId"] = str(earning_pool_id)
    if task_status:
        params["taskStatus"] = str(task_status)
    
    response = requests.get(url, headers=headers, params=params)
    data = response.json()
    
    if data.get("code") != 0 and data.get("code") != 200:
        print(f"Error: {data.get('msg')}")
        return None
    
    return data.get("data")

def main():
    page_no = 1
    page_size = 10
    
    if len(sys.argv) >= 2:
        page_no = int(sys.argv[1])
    if len(sys.argv) >= 3:
        page_size = int(sys.argv[2])
    
    result = fetch_my_tasks(page_no, page_size)
    
    if result:
        print(f"总计: {result.get('total')} 条任务")
        print(json.dumps(result.get('list', []), indent=2, ensure_ascii=False))

if __name__ == "__main__":
    main()


path: scripts/submit_task.py
content: #!/usr/bin/env python3
"""
提交任务作品链接
"""
import requests
import json
import os

API_BASE = "https://api.loudy.ai/app-api/open-api/v1"
LOUDY_API_KEY = os.environ.get("LOUDY_API_KEY", "")

def submit_task(earning_pool_id: int, task_link: str, language_type: str = "zh_CN"):
    """
    提交任务作品链接
    
    Args:
        earning_pool_id: 奖池ID
        task_link: 作品链接 (推文URL)
        language_type: 语言类型
    
    Returns:
        提交结果
    """
    url = f"{API_BASE}/earning-pool-tasks/submit"
    headers = {
        "X-API-Key": LOUDY_API_KEY,
        "Content-Type": "application/json"
    }
    
    payload = {
        "earningPoolId": earning_pool_id,
        "taskLink": [task_link],
        "languageType": language_type
    }
    
    response = requests.post(url, headers=headers, json=payload)
    data = response.json()
    
    if data.get("code") != 0 and data.get("code") != 200:
        print(f"Error: {data.get('msg')}")
        return None
    
    return data.get("data")

def main():
    import sys
    if len(sys.argv) < 3:
        print("Usage: python submit_task.py <earning_pool_id> <task_link>")
        sys.exit(1)
    
    pool_id = int(sys.argv[1])
    link = sys.argv[2]
    
    result = submit_task(pool_id, link)
    print(json.dumps(result, indent=2, ensure_ascii=False))

if __name__ == "__main__":
    main()


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