#!/bin/bash # 任务名称:每周五检查小编飞书云空间文档,清理超过2个月未修改的文档 # 执行时间:每周五 10:00 (Asia/Shanghai) # 归属 Agent:xiaobian(小编) set -e export PATH=/root/.nvm/versions/node/v24.14.0/bin:$PATH LOG_FILE="/var/log/xiaobian_weekly_cleanup.log" WORKSPACE="/root/.openclaw/workspace-xiaobian" log() { echo "[$(TZ='Asia/Shanghai' date +'%Y-%m-%d %H:%M:%S')] $1" | tee -a "$LOG_FILE" } # ====== 1. 获取 tenant_access_token ====== log "=== 任务开始:飞书云空间文档清理检查 ===" APP_ID=$(jq -r '.apps[0].appId' /root/.openclaw/credentials/xiaobian/config.json) APP_SECRET=$(jq -r '.apps[0].appSecret' /root/.openclaw/credentials/xiaobian/config.json) TOKEN=$(curl -s -X POST "https://open.feishu.cn/open-apis/auth/v3/tenant_access_token/internal" \ -H "Content-Type: application/json" \ -d "{\"app_id\":\"$APP_ID\",\"app_secret\":\"$APP_SECRET\"}" \ | jq -r '.tenant_access_token') if [ -z "$TOKEN" ] || [ "$TOKEN" = "null" ]; then log "ERROR: 获取 tenant_access_token 失败" exit 1 fi # ====== 2. 获取小编 Bot 的 open_id ====== log "正在获取 Bot 身份信息..." BOT_OPEN_ID=$(curl -s "https://open.feishu.cn/open-apis/bot/v3/info" \ -H "Authorization: Bearer $TOKEN" | jq -r '.bot.open_id') if [ -z "$BOT_OPEN_ID" ] || [ "$BOT_OPEN_ID" = "null" ]; then log "ERROR: 获取 Bot open_id 失败" exit 1 fi log "Bot open_id: $BOT_OPEN_ID" # ====== 3. 获取小编 Bot 创建的全部云文档列表 ====== log "正在获取小编创建的云文档列表..." FILES_JSON=$(curl -s "https://open.feishu.cn/open-apis/drive/v1/files?page_size=50" \ -H "Authorization: Bearer $TOKEN") # 检查是否有更多页 HAS_MORE=$(echo "$FILES_JSON" | jq -r '.data.has_more') PAGE_TOKEN=$(echo "$FILES_JSON" | jq -r '.data.page_token') # 合并所有页数据 ALL_FILES=$(echo "$FILES_JSON" | jq '.data.files') if [ "$HAS_MORE" = "true" ] && [ -n "$PAGE_TOKEN" ] && [ "$PAGE_TOKEN" != "null" ]; then PAGE2=$(curl -s "https://open.feishu.cn/open-apis/drive/v1/files?page_size=50&page_token=$PAGE_TOKEN" \ -H "Authorization: Bearer $TOKEN") ALL_FILES=$(echo "$ALL_FILES" "$(echo "$PAGE2" | jq '.data.files')" | jq -s 'add') fi # 只保留 owner_id 为 Bot 自身的文档(即小编创建的) MY_FILES=$(echo "$ALL_FILES" | jq --arg bot_id "$BOT_OPEN_ID" '[.[] | select(.owner_id == $bot_id)]') TOTAL=$(echo "$MY_FILES" | jq 'length') log "小编创建的云文档数量:$TOTAL" # ====== 4. 计算2个月前的时间戳 ====== TWO_MONTHS_AGO=$(TZ='Asia/Shanghai' date -d '2 months ago' +%s) TWO_MONTHS_AGO_STR=$(TZ='Asia/Shanghai' date -d '@'"$TWO_MONTHS_AGO" '+%Y-%m-%d %H:%M:%S') log "2个月前的时间点:$TWO_MONTHS_AGO_STR (时间戳: $TWO_MONTHS_AGO)" # ====== 5. 筛选超过2个月未修改的文档 ====== OLD_FILES=$(echo "$MY_FILES" | jq --arg threshold "$TWO_MONTHS_AGO" ' [.[] | select((.modified_time | tonumber) < ($threshold | tonumber))] ') OLD_COUNT=$(echo "$OLD_FILES" | jq 'length') log "超过2个月未修改的文档数量:$OLD_COUNT" # ====== 6. 生成完整文档清单(供参考) ====== log "=== 小编创建的文档清单 ===" echo "$MY_FILES" | jq -r '.[] | "\(.name) | \(.type) | \(.modified_time)"' | while read line; do name=$(echo "$line" | cut -d'|' -f1 | xargs) type=$(echo "$line" | cut -d'|' -f2 | xargs) ts=$(echo "$line" | cut -d'|' -f3 | xargs) mod_time=$(TZ='Asia/Shanghai' date -d "@$ts" '+%Y-%m-%d %H:%M:%S') log " [$type] $name — 最后修改: $mod_time" done # ====== 7. 如果有过期文档,发送飞书消息给胡笳 ====== if [ "$OLD_COUNT" -gt 0 ]; then log "发现 $OLD_COUNT 个超过2个月未修改的文档,准备通知胡笳..." # 构建消息内容 MSG="📋 小编云空间清理提醒 以下文档最后修改时间超过2个月($TWO_MONTHS_AGO_STR 之前): " while IFS= read -r line; do name=$(echo "$line" | jq -r '.name') type=$(echo "$line" | jq -r '.type') ts=$(echo "$line" | jq -r '.modified_time') mod_time=$(TZ='Asia/Shanghai' date -d "@$ts" '+%Y-%m-%d %H:%M') url=$(echo "$line" | jq -r '.url') MSG="${MSG}• [$type] ${name} 最后修改:${mod_time} ${url} " done < <(echo "$OLD_FILES" | jq -c '.[]') MSG="${MSG}请确认是否删除以上文档。回复「确认删除」执行删除,或回复「保留」跳过。" # 发送消息给胡笳 (user_id: b7g5c9d6) SEND_RESULT=$(curl -s -X POST "https://open.feishu.cn/open-apis/im/v1/messages?receive_id_type=user_id" \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d "$(jq -n --arg msg "$MSG" '{ receive_id: "b7g5c9d6", msg_type: "text", content: "{\"text\":$msg}" }')") SEND_CODE=$(echo "$SEND_RESULT" | jq -r '.code') if [ "$SEND_CODE" = "0" ]; then log "✅ 已发送清理提醒给胡笳" else log "❌ 发送消息失败: $SEND_RESULT" fi else log "✅ 没有超过2个月未修改的文档,无需清理" fi log "=== 任务结束 ===" exit 0