Files
nxxy335 f0ecfa6de0 chore: 初始化 123yunpan skill 仓库 (v1.3.0)
- 完整 skill 本体:SKILL.md / README.md / manifest.json / config.json
- scripts/123yunpan.py:零依赖 CLI(含空间双配额、QPS 限流、零依赖 XLSX)
- references/ 参考资料
- 排除 .workbuddy/ 开发目录与 __pycache__
2026-09-27 22:41:43 +08:00

747 lines
41 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
name: 123yunpan
description: "Use when interacting with 123 Cloud Drive (123云盘) via its open API. Supports file management, upload, download, sharing, offline download, direct links, image hosting, and video transcoding."
version: 1.3.0
author: 暖心向阳335
license: MIT
metadata:
hermes:
tags: [123yunpan, cloud-drive, file-management, api, storage]
related_skills: [bulk-downloading, resource-sharing-platform]
---
# 123云盘 (123 Cloud Drive) Skill
## Agent Quick Start
**脚本路径**: `scripts/123yunpan.py`(相对于本 skill 目录)
**首次使用**: 先跑 `config check` 确认凭证有效,再操作文件
**输出格式**: 加 `--json` 获取单行 JSON,方便解析
**文件 ID**: 所有操作都基于 `file_id`(数字),先 `file list` 或 `file search` 获取 ID
**⚠️ 操作规则**:
1. **删除规则**: 用户要求删除文件时,必须先向用户展示文件列表(如"删除文件如下:A.txt、B.zip"),确认后再执行删除操作,删除后提醒"如需找回,请前往回收站恢复"
2. **下载规则**: 下载完成后告知用户保存位置,如"文件已保存到:/path/to/file.pdf"
3. **分享规则**: 分享少于 10 个文件时直接给出链接;**超过 10 个时导出 Excel 文件并发送给用户**(阈值可用 `--excel-threshold` 覆盖,或用 `--no-excel` 关闭自动导出)
4. **搜索规则**: 用户未给出具体云盘位置只给了文件名,搜索到多个匹配结果时,列出文件让用户选择,不要自行决定
5. **批量规则**: 批量操作前先告知用户数量和预计耗时,如"共 150 个文件,预计需要 2 分钟"
6. **上传规则**: 用户未指定目标文件夹时,先列出根目录让用户选择上传位置
7. **错误规则**: 遇到错误时给出解决方案,不要只报错。如"文件不存在"→提示检查 file_id 是否正确;"分享失败"→提示检查文件是否违规
```
# 1. 验证配置(--json 是全局选项,必须写在子命令之前)
python3 scripts/123yunpan.py --json config check
# 2. 查看根目录
python3 scripts/123yunpan.py --json file list --parent-id 0
# 3. 搜索文件
python3 scripts/123yunpan.py --json file search "关键词" --parent-id 0
# 4. 查看目录树(快速了解结构)
python3 scripts/123yunpan.py file tree --parent-id 0 --max-depth 2
```
## Common Workflows
### 🔍 "帮我找某个文件"
```
1. file search "关键词" --parent-id 0 --json → 获取 file_id
2. file detail --file-id {id} --json → 确认文件信息
3. file download --file-id {id} -o ./ → 下载
4. 告知用户:文件已保存到:./xxx.pdf
```
### 🔗 "把这个文件夹分享出去"
```
1. file tree --parent-id {folder_id} --max-depth 2 → 确认内容
2. 少于 10 个:share create 逐个创建,直接给出链接
3. 超过 10 个:share create-batch --parent-id {folder_id} --expire 0 -o links.xlsx → 导出 Excel
```
### ⬆️ "上传文件到云盘"
```
1. 用户未指定位置 → file list --parent-id 0 --json → 列出根目录让用户选
2. upload --file /path/to/file --parent-id {folder_id}
3. 告知用户:文件已上传到:{folder_id}/xxx.pdf
```
### ⬇️ "批量下载这些文件"
```
1. file search "关键词" --parent-id {root} --json → 获取 file_id 列表
2. file download-batch --file-ids "id1,id2,id3" -o ./downloads/ → 批量下载
```
### 📊 "看看空间占用情况"
```
1. space → 专业空间 / 标准空间明细(双配额)
2. file storage-report --parent-id 0 --top 10 → 按文件夹分析+大文件排行
```
### 🗑️ "删除文件"
```
1. file list --parent-id {folder_id} --search "关键词" --json → 找到 file_id
2. 向用户展示将删除的文件列表,格式:删除文件如下:XXX.txt、XXX.zip
3. file trash --file-ids "{id1},{id2}" → 移入回收站
4. 提醒用户:如需找回,请前往回收站恢复
```
> ⚠️ **删除操作必须先向用户展示文件列表,确认后再执行!**
### 🔄 "把配置迁移到另一台机器"
```
# 机器 A:
config export -o config.json
# 机器 B:
config import --file config.json
config check → 验证导入成功
```
## Overview
This skill provides a complete CLI wrapper for the [123 Cloud Drive Open Platform API](https://123yunpan.yuque.com/org-wiki-123yunpan-muaork/cr6ced). It covers **70+ API endpoints** across authentication, file management, upload, download, sharing, offline download, direct links, image hosting, and video transcoding.
## Prerequisites
### 1. Purchase Developer Access Pack
Visit https://www.123pan.com/member and purchase a developer access pack. You will receive `client_id` and `client_secret` via in-app message.
### 2. Configuration
The skill requires three credentials stored in environment variables:
```bash
# Required for API authentication
export TWOPAN_CLIENT_ID="your_client_id_here"
export TWOPAN_CLIENT_SECRET="your_client_secret_here"
export TWOPAN_ACCESS_TOKEN="your_access_token_here"
```
**If these are not set when the skill is first used**, the CLI will output a clear reminder:
```
⚠️ 123云盘技能需要配置以下环境变量才能使用:
export TWOPAN_CLIENT_ID="your_client_id"
export TWOPAN_CLIENT_SECRET="your_client_secret"
export TWOPAN_ACCESS_TOKEN="your_access_token"
获取方式:
1. 访问 https://www.123pan.com/member 购买开发者权益包
2. 通过 client_id + client_secret 调用 /api/v1/access_token 获取 access_token
3. 将三个值设置为环境变量
或运行: 123yunpan.py setup
```
### 3. Interactive Setup (Recommended)
Run `123yunpan.py setup` without arguments for interactive credential entry:
```
$ python3 scripts/123yunpan.py setup
⚠️ 未检测到 client_id
获取方式:访问 https://www.123pan.com/member 购买开发者权益包
请输入 client_id: █
⚠️ 未检测到 client_secret
请输入 client_secret: █
正在获取 access_token...
✅ 配置已保存!
```
If config already exists (from a previous setup or env vars), it skips prompting and uses the existing values. Non-interactive mode also works: `setup --client-id ID --client-secret SECRET`.
## CLI Usage
```bash
# === Global Options ===
# Add --no-color to disable colored output, --json to output raw JSON
# Setup / get token
python3 scripts/123yunpan.py setup --client-id YOUR_ID --client-secret YOUR_SECRET
# Check config status
python3 scripts/123yunpan.py config
python3 scripts/123yunpan.py config check # 验证 token 有效性
python3 scripts/123yunpan.py config export --output config.json
python3 scripts/123yunpan.py config import --file config.json
# === File Management ===
python3 scripts/123yunpan.py file list --parent-id 0 --limit 100
python3 scripts/123yunpan.py file list --parent-id 0 --trashed # 只看回收站
python3 scripts/123yunpan.py file list --parent-id 0 --show-trashed # 含回收站
python3 scripts/123yunpan.py file search "Photoshop" --parent-id 27172609 --max-depth 3
python3 scripts/123yunpan.py file tree --parent-id 0 --max-depth 3
python3 scripts/123yunpan.py file storage-report --parent-id 0 --top 10
python3 scripts/123yunpan.py file detail --file-id 12345
python3 scripts/123yunpan.py file details-multi --file-ids "1,2,3"
python3 scripts/123yunpan.py file list --search "keyword"
python3 scripts/123yunpan.py file mkdir --name "New Folder" --parent-id 0
python3 scripts/123yunpan.py file rename --file-id 12345 --new-name "renamed.txt"
python3 scripts/123yunpan.py file rename-batch --file-ids "1,2,3" --new-names "a.txt,b.txt,c.txt"
python3 scripts/123yunpan.py file move --file-ids "1,2,3" --target-parent-id 0
python3 scripts/123yunpan.py file copy --file-id 12345 --target-parent-id 0
python3 scripts/123yunpan.py file copy-batch --file-ids "1,2,3" --target-parent-id 0
python3 scripts/123yunpan.py file copy-progress --task-id abc123
python3 scripts/123yunpan.py file trash --file-ids "12345"
python3 scripts/123yunpan.py file recover --file-id 12345
python3 scripts/123yunpan.py file recover-by-path --file-id 12345 --target-parent-id 0 --filename "restored.txt"
python3 scripts/123yunpan.py file download --file-id 12345 --output /path/to/save
python3 scripts/123yunpan.py file download-batch --file-ids "1,2,3" --output-dir /path/to/save
# === Upload (V2 Recommended) ===
python3 scripts/123yunpan.py upload --file /path/to/file.txt --parent-id 0
# === Space / User ===
python3 scripts/123yunpan.py space # 空间占用报告(专业 + 标准,双配额)
python3 scripts/123yunpan.py --json space # 机器可读版本
python3 scripts/123yunpan.py user-info
# === Share Management ===
python3 scripts/123yunpan.py share create --name "My Share" --file-ids "1,2,3" --expire 7
# 超过 10 个链接自动生成 Excel(三列:文件名称 / 链接 / 路径)
python3 scripts/123yunpan.py share create-batch --parent-id 27172609 --expire 0 -o result.xlsx
# 覆盖自动导出阈值 / 关闭自动导出 / 断点续跑
python3 scripts/123yunpan.py share create-batch --parent-id 27172609 --excel-threshold 50
python3 scripts/123yunpan.py share create-batch --parent-id 27172609 --no-excel
python3 scripts/123yunpan.py share create-batch --parent-id 27172609 --resume
python3 scripts/123yunpan.py share list --limit 10
python3 scripts/123yunpan.py share list --all-pages
python3 scripts/123yunpan.py share stats --top 10
# 注意:--share-ids / --file-ids 只接受单个值,多个 ID 必须写成一个逗号分隔的字符串
python3 scripts/123yunpan.py share delete-batch --share-ids "1,2,3"
python3 scripts/123yunpan.py share delete-batch --all -y # 删除全部(跳过确认)
python3 scripts/123yunpan.py share update --share-ids "1,2" --traffic-switch 2
python3 scripts/123yunpan.py share export --output shares.xlsx
python3 scripts/123yunpan.py share export --output shares.json # 按扩展名自动判断格式
python3 scripts/123yunpan.py share pay-create --name "Paid Share" --file-ids "1,2" --amount 10
python3 scripts/123yunpan.py share pay-list --limit 10
python3 scripts/123yunpan.py share pay-update --share-ids "1" --traffic-switch 1
# === Offline Download ===
python3 scripts/123yunpan.py offline create --url "https://example.com/file.zip" --parent-id 0
python3 scripts/123yunpan.py offline progress --task-id abc123
python3 scripts/123yunpan.py offline list
# === Direct Link ===
python3 scripts/123yunpan.py direct-link enable --file-id 12345
python3 scripts/123yunpan.py direct-link disable --file-id 12345
python3 scripts/123yunpan.py direct-link url --file-id 12345
python3 scripts/123yunpan.py direct-link short-url --file-id 12345 # 短直链(更隐私)
python3 scripts/123yunpan.py direct-link cache-refresh --file-id 12345
python3 scripts/123yunpan.py direct-link ip-list
python3 scripts/123yunpan.py direct-link ip-switch --switch 1
python3 scripts/123yunpan.py direct-link ip-update --ips "1.1.1.1,2.2.2.2"
python3 scripts/123yunpan.py direct-link offline-logs
python3 scripts/123yunpan.py direct-link traffic-logs
# === Image Hosting (OSS) ===
python3 scripts/123yunpan.py oss list --parent-id 0 --limit 100
python3 scripts/123yunpan.py oss detail --file-id 12345
python3 scripts/123yunpan.py oss delete --file-id 12345
python3 scripts/123yunpan.py oss move --file-id 12345 --target-parent-id 0
python3 scripts/123yunpan.py oss copy --file-ids "1,2,3" --target-parent-id 0
python3 scripts/123yunpan.py oss copy-process --task-id abc123
python3 scripts/123yunpan.py oss copy-fail --task-id abc123
# === Video Transcode ===
# 注意:transcode 系列用 --parent-file-id,不是 --parent-id
python3 scripts/123yunpan.py transcode list --parent-file-id 0 --limit 100
python3 scripts/123yunpan.py transcode folder --parent-file-id 0
python3 scripts/123yunpan.py transcode resolutions --file-id 12345
python3 scripts/123yunpan.py transcode transcode --file-id 12345 --resolution 1080p
python3 scripts/123yunpan.py transcode record --file-id 12345
python3 scripts/123yunpan.py transcode result --file-id 12345
python3 scripts/123yunpan.py transcode delete --file-id 12345
python3 scripts/123yunpan.py transcode download --file-id 12345 --output /path/
python3 scripts/123yunpan.py transcode download-m3u8 --file-id 12345 --output /path/
python3 scripts/123yunpan.py transcode download-all --file-id 12345 --output /path/
python3 scripts/123yunpan.py transcode upload-cloud --file-id 12345 --parent-id 0
# === Safebox ===
python3 scripts/123yunpan.py safebox unlock --password "your_password"
# === Cache Management ===
python3 scripts/123yunpan.py cache list # 列出所有缓存文件
python3 scripts/123yunpan.py cache clear # 清除全部缓存
python3 scripts/123yunpan.py cache clear --root-id 12345 # 清除指定文件夹的缓存
python3 scripts/123yunpan.py cache info # 显示缓存目录信息
```
## JSON Output Fields (`--json`)
加 `--json` 后所有命令输出单行 JSON,以下是主要命令的字段说明:
### `file list --json`
```json
{"code":0, "data":{"fileList":[
{"fileId":12345, "filename":"报告.pdf", "size":1048576, "type":0,
"parentFileID":0, "createAt":"2026-01-01T00:00:00Z", "trashed":0}
], "lastFileId":12345}}
```
- `type`: 0=文件, 1=文件夹
- `trashed`: 0=正常, 1=已删除
- `size`: 字节数
### `file detail --json`
```json
{"fileId":12345, "filename":"报告.pdf", "size":1048576,
"md5":"abc123...", "type":0, "parentFileID":0,
"downloadUrl":"https://...", "createAt":"2026-01-01T00:00:00Z"}
```
### `space --json`
```json
{"nickname": "暖心向阳335",
"total_quota": 21474836480, "total_used": 9223372036854775807,
"professional": {"used": 1.5e10, "quota": 2.0e10, "remain_days": 0,
"permanent": true, "used_pct": 75.0},
"standard": {"used": 1.2e9, "quota": 5.0e9, "remain_days": 0,
"permanent": true, "used_pct": 24.0}}
```
- `professional` 满 → 开放平台上传 / 离线下载 / 转码入口被阻断
- `remain_days` > 0 表示官方给的清理倒计时,超出后配额不可恢复
### `user-info --json`
```json
{"uid":1812749510, "nickname":"暖心向阳335",
"spaceUsed":9223372036854775807, "spacePermanent":9223372036854775807,
"vip":1}
```
- `spaceUsed`/`spacePermanent`: 字节数,用 `human_size()` 转换
### `share create --json`
```json
{"code":0, "data":{"shareID":5193007, "shareKey":"JyAKVv-o6bI"},
"_shareUrl":"https://www.123pan.com/s/JyAKVv-o6bI"}
```
- `_shareUrl` 是 CLI 自动拼接的,不是 API 返回的
### `share list --json`
```json
{"code":0, "data":{"shareList":[
{"shareId":5193007, "shareName":"分享",
"shareKey":"JyAKVv-o6bI", "shareExpire":7,
"previewCount":10, "downloadCount":5, "saveCount":2,
"createAt":"2026-01-01T00:00:00Z"}
], "lastShareId":5193007}}
```
### `upload --json`
```json
{"code":0, "data":{"fileID":12345, "completed":true, "reuse":false}}
```
- `reuse`: true=秒传成功(文件已存在)
### 通用规则
- 所有 API 返回格式: `{"code":0, "message":"ok", "data":{...}}`
- `code=0` 表示成功,非 0 为错误
- `file list` 和 `share list` 使用 `lastFileId`/`lastShareId` 做游标分页
- 错误时返回: `{"code":错误码, "message":"错误描述"}`
## API Reference Summary
| Category | Endpoint | Method |
|----------|----------|--------|
| Cache | cache list | CLI |
| Cache | cache clear | CLI |
| Cache | cache info | CLI |
| Auth | `/api/v1/access_token` | POST |
| Auth (OAuth2) | `/api/v1/oauth2/access_token` | POST |
| File List (v2) | `/api/v2/file/list` | GET |
| File List (v1) | `/api/v1/file/list` | GET |
| File Detail | `/api/v1/file/detail` | GET |
| File Details (multi) | `/api/v1/file/infos` | POST |
| Create Dir | `/upload/v1/file/mkdir` | POST |
| Rename (single) | `/api/v1/file/name` | PUT |
| Rename (batch) | `/api/v1/file/rename` | POST |
| Move | `/api/v1/file/move` | POST |
| Copy (single) | `/api/v1/file/copy` | POST |
| Copy (batch) | `/api/v1/file/async/copy` | POST |
| Copy Progress | `/api/v1/file/async/copy/process` | GET |
| Trash | `/api/v1/file/trash` | POST |
| Recover | `/api/v1/file/recover` | POST |
| Recover (by path) | `/api/v1/file/recover/by_path` | POST |
| Download Info | `/api/v1/file/download_info` | GET |
| Upload V1 | `/upload/v1/file/create` | POST |
| Upload V2 | `/upload/v2/file/create` | POST |
| Upload Slice | `/upload/v2/file/slice` | POST |
| Upload Complete | `/upload/v2/file/upload_complete` | POST |
| Upload Domain | `/upload/v2/file/domain` | GET |
| SHA1 Reuse | `/upload/v2/file/sha1_reuse` | POST |
| Single Upload | `/upload/v2/file/single/create` | POST |
| Share Create | `/api/v1/share/create` | POST |
| Share List | `/api/v1/share/list` | GET |
| Share Delete | `/api/v1/share/delete` | POST |
| Share Update | `/api/v1/share/list/info` | PUT |
| Pay Share Create | `/api/v1/share/content-payment/create` | POST |
| Pay Share List | `/api/v1/share/payment/list` | GET |
| Pay Share Update | `/api/v1/share/list/payment/info` | PUT |
| Offline Download | `/api/v1/offline/download` | POST |
| Offline Progress | `/api/v1/offline/download/process` | GET |
| User Info | `/api/v1/user/info` | GET |
| Direct Link Enable | `/api/v1/direct-link/enable` | POST |
| Direct Link Disable | `/api/v1/direct-link/disable` | POST |
| Direct Link URL | `/api/v1/direct-link/url` | GET |
| Direct Link Cache | `/api/v1/direct-link/cache/refresh` | POST |
| IP Blacklist Switch | `/api/v1/developer/config/forbide-ip/switch` | POST |
| IP Blacklist Update | `/api/v1/developer/config/forbide-ip/update` | POST |
| IP Blacklist List | `/api/v1/developer/config/forbide-ip/list` | GET |
| Direct Link Logs | `/api/v1/direct-link/offline/logs` | GET |
| Direct Link Traffic | `/api/v1/direct-link/log` | GET |
| Image Upload Flow | `/upload/v1/oss/file/mkdir` | POST |
| Image Create Dir | `/upload/v1/oss/file/mkdir` | POST |
| Image Create | `/upload/v1/oss/file/create` | POST |
| Image Upload URL | `/upload/v1/oss/file/get_upload_url` | POST |
| Image Upload Done | `/upload/v1/oss/file/upload_complete` | POST |
| Image Upload Result | `/upload/v1/oss/file/upload_async_result` | POST |
| Image Copy | `/api/v1/oss/source/copy` | POST |
| Image Copy Process | `/api/v1/oss/source/copy/process` | GET |
| Image Copy Fail | `/api/v1/oss/source/copy/fail` | GET |
| Image Move | `/api/v1/oss/file/move` | POST |
| Image Delete | `/api/v1/oss/file/delete` | POST |
| Image Detail | `/api/v1/oss/file/detail` | GET |
| Image List | `/api/v1/oss/file/list` | POST |
| Image Offline | `/api/v1/oss/offline/download` | POST |
| Image Offline Process | `/api/v1/oss/offline/download/process` | GET |
| Video Upload Cloud | `/api/v1/transcode/upload/from_cloud_disk` | POST |
| Video Folder Info | `/api/v1/transcode/folder/info` | POST |
| Video Resolutions | `/api/v1/transcode/video/resolutions` | POST |
| Video Transcode List | `/api/v1/video/transcode/list` | GET |
| Video Transcode | `/api/v1/transcode/video` | POST |
| Video Record | `/api/v1/transcode/video/record` | POST |
| Video Result | `/api/v1/transcode/video/result` | POST |
| Video Delete | `/api/v1/transcode/delete` | POST |
| Video Download | `/api/v1/transcode/file/download` | POST |
| Video M3U8 Download | `/api/v1/transcode/m3u8_ts/download` | POST |
| Video All Download | `/api/v1/transcode/file/download/all` | POST |
| Safebox Unlock | `/api/v1/safebox/id` | GET |
## Short Direct Link (短直链)
除了分享链接和完整直链外,还可以生成**短直链** — 格式为 `https://{user_id}.v.123pan.cn/{user_id}/{file_id}`,不暴露文件名,更隐私友好。
### 生成短直链
```bash
# CLI 命令
python3 scripts/123yunpan.py direct-link short-url --file-id 12345
# 输出示例
{
"short_url": "https://1812749510.v.123pan.cn/1812749510/12345",
"user_id": 1812749510,
"file_id": 12345
}
```
### 手动拼接(Python)
```python
import base64, json
def get_user_id_from_token(token):
"""从 JWT token 解析 user_id,无需额外 API 调用"""
parts = token.split(".")
payload = parts[1] + "=" * (4 - len(parts[1]) % 4)
return json.loads(base64.b64decode(payload)).get("id", 0)
user_id = get_user_id_from_token(access_token)
short_url = f"https://{user_id}.v.123pan.cn/{user_id}/{file_id}"
```
### 三种链接格式对比
| 格式 | 示例 | 特点 |
|------|------|------|
| 分享链接 | `https://www.123pan.com/s/JyAKVv-o6bI` | 需要登录/跳转,最常用 |
| 完整直链 | `https://xxx.v.123pan.cn/xxx/xxx/filename.zip` | 直接下载,暴露文件名 |
| 短直链 | `https://1812749510.v.123pan.cn/1812749510/12345` | 直接下载,不暴露文件名 |
> ⚠️ 短直链需要文件已在开启了直链功能的文件夹中,否则无法使用。
## Share URL Construction
The share create API returns `shareID` and `shareKey` — it does **NOT** return a full `shareUrl`. You must construct the URL yourself. The CLI supports two formats via `--url-format`:
| Format | CLI 参数 | URL 模式 | 示例 |
|--------|----------|----------|------|
| `short` (默认) | `--url-format short` | `https://www.123pan.com/s/{shareKey}` | `https://www.123pan.com/s/JyAKVv-o6bI` |
| `uid` | `--url-format uid` | `https://{user_id}.share.123pan.cn/123pan/{shareKey}` | `https://666666666.share.123pan.cn/123pan/JyAKVv-o6bI` |
`uid` 格式自动从 JWT token 解析 `user_id`,无需额外 API 调用。解析失败时自动回退到 `short`。
```bash
# 默认 short 格式
python3 scripts/123yunpan.py share create --name "分享" --file-ids 123,456 --expire 7
# UID 格式
python3 scripts/123yunpan.py share create --name "分享" --file-ids 123,456 --url-format uid
# 批量分享也支持
python3 scripts/123yunpan.py share create-batch --parent-id 27172609 --url-format uid -o result.xlsx
```
Example response from `POST /api/v1/share/create`:
```json
{
"code": 0,
"message": "ok",
"data": {
"shareID": 5193007,
"shareKey": "JyAKVv-o6bI"
}
}
```
## Direct Link & Short Direct Link
The skill supports two types of direct links:
**Full direct link** (contains filename in URL):
```
https://{user_id}.v.123pan.cn/{user_id}/{file_id}/{filename}
```
**Short direct link** (privacy-friendlier, no filename):
```
https://{user_id}.v.123pan.cn/{user_id}/{file_id}
```
To generate short direct links, parse `user_id` from the JWT access token (no extra API call needed):
```python
import base64, json
parts = token.split(".")
payload = parts[1] + "=" * (4 - len(parts[1]) % 4)
user_id = json.loads(base64.b64decode(payload))["id"]
short_link = f"https://{user_id}.v.123pan.cn/{user_id}/{file_id}"
```
> See `references/competitor-upload-patterns.md` for full upload workflow, SHA1 reuse, and WebDAV patterns.
## 空间规则:专业空间 / 标准空间(双配额)
根据官方 2026-09-24 公告,账号空间已拆分为**两套独立配额**,各有各的容量池与剩余天数:
| 空间类型 | 存放内容 | 与开放平台的关系 |
|----------|----------|------------------|
| **专业空间** | 本人持有的文件;≥10GB 文件;**直链**文件;转码文件;图床文件 | **任一项超额都会阻断开放平台的上传 / 离线下载 / 转码入口** |
| **标准空间** | 转存(保存)的文件,<10GB;重复文件与回收站文件**不计入**容量 | 超额同样阻断上传 |
用 `space` 命令查看明细,CLI 会自动做两件事:
1. **使用率 ≥ 90%** → 红色预警,提示随时可能出现写入失败
2. **专业空间剩余天数 > 0**(官方给定的清理倒计时,超出后配额不可恢复)→ 明确打印倒计时天数并标红
```bash
python3 scripts/123yunpan.py --json space # 机器可读:professional / standard / total 三段
```
`config check` 与 `upload` 命令在执行前都会做空间预检,专业空间已满时会直接给出阻断提示,避免上传中途才失败。
> ⚠️ **排查要点**:上传失败、离线下载任务创建失败,先看是**专业空间**还是**标准空间**超限 —— 两者是独立的,不能互相顶替。
## 分享链接 Excel 导出(三列)
`share create-batch` 在**有效链接数 > 阈值(默认 10)**时会自动生成 Excel 文件,列结构固定为三列:
| 列名 | 含义 | 示例 |
|------|------|------|
| **文件名称** | 不含分类前缀、不含扩展名的干净文件名 | `Cinema 4D R20`(原 `Cinema 4D/Cinema 4D R20.zip`) |
| **链接** | 该文件的分享/直链 URL,单元格为可点击超链接 | `https://www.123pan.com/s/JyAKVv-o6bI` |
| **路径** | 文件在云盘内的完整相对路径(含文件名与扩展名) | `软件/office/离线安装包/2021/ProPlus2021Retail.img` |
名称解析规则(`split_name_and_path()`):
1. 去掉扩展名 → `Cinema 4D R20.zip` → `Cinema 4D R20`
2. 若去掉扩展名后的名字以「父目录名」开头,则补回父目录名避免歧义
→ `Cinema 4D/Cinema 4D R20.zip` → `Cinema 4D R20`(而不是 ` R20`)
阈值控制:
```bash
--excel-threshold N # 覆盖默认阈值(默认 10)
--no-excel # 关闭自动导出,超过阈值也只在终端打印链接
--resume # 断点续跑:读取 ~/.123yunpan_cache/share_batch_{root_id}.json,只处理未完成的文件
```
> ✅ **零依赖**:xlsx 由 `zipfile` + 手写 OOXML XML 直接生成,不再需要 `openpyxl`。任何 Python 3 标准库环境都能出文件,不会出现"静默降级成 JSON"的情况。
## 限流与重试(QPS 保护)
官方开发须知明确给出各端点 QPS 上限,CLI 内置 `QPS_RULES` 表自动做节奏控制:
| 端点 | QPS |
|------|-----|
| `/api/v2/file/list` | **1.5(最严格)** |
| `/api/v1/user/info` | 10 |
| `/api/v1/access_token` | 10 |
| `/api/v1/share/create` | 3 |
| 其他 | 默认 1.39(保守兜底) |
配套行为:
- **自动节流**:每个端点按自己的 QPS 计算最小请求间隔,超限时 sleep 等待
- **指数退避重试**:HTTP 429 或 5xx → `2^attempt` 秒后重试,最多 4 次
- **401 自动刷新 Token**:带 `client_id/secret` 时自动刷新后重试一次
- **审核驳回文件过滤**:`status > 100` 的文件无法分享,遍历阶段直接剔除,避免逐个试错
## Recursive File Listing
The `file list` API only returns **direct children** of a folder. To find all files in a multi-level directory tree, you must traverse recursively. See `references/bulk-operations.md` for BFS and layered traversal patterns.
**Quick pattern — find all files under a root folder:**
```python
from collections import deque
def find_all_files(root_id, list_folder_fn):
queue = deque([(root_id, [])])
all_files = []
while queue:
pid, path = queue.popleft()
for item in list_folder_fn(pid):
if item.get("trashed"):
continue
if item["type"] == 1: # folder
queue.append((item["fileId"], path + [item["filename"]]))
else: # file
all_files.append({
"fileId": item["fileId"],
"filename": item["filename"],
"path": "/".join(path),
})
return all_files
```
> ⚠️ Deep traversal can involve hundreds of API calls. For 100+ folders, expect 5-10 minutes. Use checkpoint/resume (save progress to JSON every N items) to handle interruptions.
## Common Pitfalls
1. **API response format** (CRITICAL): The 123yunpan API returns `{code: 0, message: "ok", data: {...}}` — NOT `{result: {...}}`. Always parse the `data` field. Use `client._extract(resp)` — the CLI script handles both formats.
2. **Token expiry & auto-refresh**: access_token expires after a period. The CLI automatically refreshes on 401 responses using client_id/secret. For manual refresh, run `setup` or call `/api/v1/access_token`.
3. **File name restrictions**: Filenames must be <256 chars and cannot contain: `\\\\\\\"/:*?|><`
4. **Upload size**: Single file upload limit is 10GB via the API.
5. **`fileIDList` must be a string** (CRITICAL): When calling the share create API directly, `fileIDList` must be a **comma-separated string** (`"123,456"`), NOT a JSON array (`[123, 456]`). Passing an array causes the API to return "文件列表不能为空". The CLI handles this internally via `",".join(str(x) for x in file_ids)`. Max 100 file IDs per share. `shareExpire` must be 0, 1, 7, or 30.
6. **Share response has no `shareUrl`**: The share create response only returns `shareID` + `shareKey`. You must construct the URL. Use `--url-format short` (default, `https://www.123pan.com/s/{shareKey}`) or `--url-format uid` (`https://{user_id}.share.123pan.cn/123pan/{shareKey}`). See "Share URL Construction" section above.
7. **Pagination**: Use `lastFileId`/`lastShareId` for cursor-based pagination. When value is `-1`, you've reached the last page.
8. **Upload API domain routing** (CRITICAL): Upload endpoints are split across **two domains**:
- Main API domain (`https://open-api.123pan.com`): `/upload/v2/file/create` (chunked create), `/upload/v1/file/create` (v1 create)
- Upload domain (`https://openapi-upload.123242.com`): `/upload/v2/file/single/create` (single upload), `/upload/v2/file/slice` (chunked slice), `/upload/v2/file/upload_complete` (chunked complete)
- Upload domain is obtained via **GET** `/upload/v2/file/domain` — it returns a **list** `["https://..."]`, not an object with `host` field.
9. **Single upload requires multipart/form-data** (CRITICAL): The `/upload/v2/file/single/create` endpoint does NOT accept JSON body. It requires `Content-Type: multipart/form-data` with fields `parentFileID`, `filename`, `etag`, `size` as text parts and the file content as a file part. Sending JSON results in a 500 server crash.
10. **`duplicate` parameter** (CRITICAL): The `duplicate` field in upload create causes "duplicate参数错误" when set to `0` (skip). **Do not send `duplicate` at all** (defaults to overwrite), or use `2` (overwrite) or `1` (rename). The value `0` is rejected by the API. The CLI now defaults to not sending this parameter (overwrite behavior).
11. **Trash behavior**: The v2 file list includes trashed files — filter by `trashed` field yourself. `trashed: 1` means the file is in trash.
12. **Batch operations rate limit**: The CLI now enforces per-endpoint QPS automatically via the built-in `QPS_RULES` table (see "限流与重试" section) — do NOT add manual `time.sleep()` on top of it, or you'll halve the throughput. For very long batches (2500+ files) still use checkpoint/resume: `--resume` reads `~/.123yunpan_cache/share_batch_{root_id}.json` and skips already-done files.
13. **Deep directory traversal**: Folders can be 4-5 levels deep. Use BFS with a queue for unknown structures, or layered traversal when structure is known. Each level requires a separate API call — 1400+ folders may take 10+ minutes. **Use BFS caching** (`~/.123yunpan_cache/`) to avoid repeated traversals — 57s → 0.15s on cache hit. CLI commands `file search` and `share create-batch` auto-use cache; add `--no-cache` to force refresh.
14. **Common error responses**:
| `code` | `message` | Meaning |
|--------|-----------|---------|
| `code` | HTTP | `message` | Meaning / 处理建议 |
|--------|------|-----------|--------------------|
| 0 | 200 | ok | Success |
| 1 | 200 | 文件列表不能为空 | `fileIDList` is empty or wrong format (must be comma-separated string) |
| 1 | 200 | duplicate参数错误 | `duplicate` field value is invalid (don't send `0`; use absent/`1`/`2`) |
| 1 | 200 | 分片MD5不能为空 | Slice MD5 missing — check field name matches API expectation |
| 1 | 200 | 该目录下文件名重复无法创建 | Filename already exists in target directory (use `--duplicate 2` to overwrite) |
| 2 | 200 | 分享ID非法 | File ID doesn't exist or has no share permission |
| 10 | 200 | 文件名已存在 | Target folder already has a file with that name |
| 16 | 200 | 文件不存在 | File ID not found |
| **20** | 200 | 分享文件涉嫌违规,禁止分享 | File content flagged by platform policy (e.g. `.alist` files) — CLI now filters `status > 100` files before sharing |
| **401** | 401 | — | access_token 无效或已过期 → 自动刷新重试一次;仍失败则 `setup` 重新获取 |
| **429** | 429 | — | 触发平台限流 → CLI 指数退避自动重试(最多 4 次),无需手动干预 |
| **5066** | 200 | — | 文件不存在(开放平台语义),检查 fileId |
| **5113** | 200 | — | 自用下载流量不足 → 升级 VIP 可获得无限流量下载 |
> CLI 内置 `ERROR_HINTS` 表,以上错误码会以中文人话 + 处置建议的形式打印,不会只丢一个裸错误码。
15. **审核驳回文件无法分享 (CRITICAL)**: Files with `status > 100` are rejected by content review and can never be shared. `share create-batch` filters them out during traversal instead of failing one-by-one. If a file you expect to share silently disappears from the result, check its `status` field first.
16. **专业空间 / 标准空间是两套独立配额 (CRITICAL)**: Per the 2026-09-24 announcement, exceeding **either** blocks open-platform uploads / offline downloads / transcoding. Standard space does **not** count duplicate files or trash files. Run `space` before a large upload to see which pool is actually full.
17. **Excel 导出是三列结构,不是「文件名 + 链接」**: Columns are fixed as 文件名称 / 链接 / 路径. The name column strips the folder prefix and extension (`Cinema 4D/Cinema 4D R20.zip` → `Cinema 4D R20`), while 路径 keeps the full relative path with filename (`软件/office/离线安装包/2021/ProPlus2021Retail.img`). Don't re-derive these columns downstream — read them as-is.
18. **Excel 导出零依赖,不要再去装 openpyxl**: xlsx is generated by hand via `zipfile` + OOXML XML (`write_xlsx()`). There is no `import openpyxl` anywhere in the script. Previously a missing openpyxl would silently degrade export to JSON — that can no longer happen.
19. **Packaging & versioning**: Version must be updated in **two places**: (1) `scripts/123yunpan.py` `VERSION = "X.Y.Z"`, (2) `SKILL.md` frontmatter (`version: X.Y.Z`), plus (3) `manifest.json` (`version`). No `zip`/`unzip` CLI — use Python's `zipfile` module. Package includes SKILL.md + README.md + scripts/ + references/ (no LICENSE per user preference). Exclude `__pycache__/`. Extraction for patching: `python3 -c "import zipfile; zipfile.ZipFile('pkg.zip').extractall('_tmp')"`. Repackaging: `cd _tmp && python3 -c "import zipfile,os; [zipfile.ZipFile('out.zip','w',zipfile.ZIP_DEFLATED).write(f, os.path.relpath(f,'.')) for r,d,fs in os.walk('.') for f in fs if '__pycache__' not in r]"`.
> ⚠️ Remember to also update the version in `scripts/123yunpan.py` `VERSION` when bumping the version in SKILL.md frontmatter and manifest.json.
20. **Argparse parameter names must match SKILL.md exactly**: Before adding/changing argparse arguments, cross-check every `--flag` in SKILL.md CLI examples. Mismatches (e.g. `--name` in code vs `--new-name` in docs) silently break user workflows. Use `parse_int_list()` (not `type=int, nargs="+"`) for all file-ID arguments to support comma-separated input (`--file-ids "1,2,3"`).
- ⚠️ `--file-ids` / `--share-ids` / `--name` **只接受一个值**。多 ID 必须写成**一个带引号的逗号分隔字符串** `--file-ids "1,2,3"`;写成 `--file-ids 1 2 3` 会报 `unrecognized arguments: 2 3`,因为 `1 2 3` 会被当成多余的位置参数。
- ⚠️ `--json` / `--no-color` 是**全局选项**,必须写在子命令**之前**(`--json config check`),写在后面报 `unrecognized arguments: --json`。
- ⚠️ `transcode` 系列用 `--parent-file-id`,与 `file` 系列的 `--parent-id` **不是同一个参数**。
- 可用 `.workbuddy/verify_doc_flags.py` 一键回归:它对文档里每一条 CLI 示例跑一遍 argparse 解析,找出「文档写了但代码没有」的选择器。
21. **`_refresh_token` uses imported names, not module paths**: The `ApiClient` methods use `Request` and `urlopen` (from `from urllib.request import ...`), NOT `urllib.request.Request`. Using the module path will crash with `NameError` since `urllib` is not imported as a module.
22. **BFS traversal optional params**: `bfs_list_files()` accepts `max_depth`, `use_cache`, `max_age` — always forward these from callers (e.g. `cmd_file_search`). Unused parameters silently cause unbounded traversal.
23. **Global options placement**: `--no-color` and `--json` must appear **before** the subcommand (e.g., `--json file list --parent-id 0`), not after. Placing them after the subcommand will cause argparse to ignore them.
24. **`file tree` vs recursive traversal**: `file tree` is a display-only convenience command that shows directory structure. For bulk operations (downloading, moving, sharing), use recursive BFS traversal or `file list` with pagination — `file tree` does not return file IDs suitable for batch operations.
25. **Share name is auto-truncated to 35 chars**: Paid shares require names < 35 characters; the CLI truncates automatically to avoid a 400 from the API. Don't pass a name longer than that expecting it round-trip.
26. **`share export` format is inferred from the file extension**: `--output shares.xlsx` → Excel, `--output shares.json` → JSON. There is **no `--format` flag** on `share export` (passing it yields `unrecognized arguments`). Both go through the same zero-dependency `write_xlsx()` / JSON path.
27. **`share pay-update` has no `--amount`**: Paid-share updates accept `--traffic-switch` / `--traffic-limit-switch` / `--traffic-limit` only. To change the price, recreate the share (or check the current API doc) rather than trying to patch `--amount`.
28. **`config import` safety**: Importing config will overwrite your current `~/.123yunpan_config.json`. Back up your existing config before importing. The CLI warns before overwriting if the file exists.
See `references/bulk-operations.md` for batch operation code patterns, BFS traversal, and background execution tips.
## Verification Checklist
- [ ] Environment variables `TWOPAN_CLIENT_ID`, `TWOPAN_CLIENT_SECRET`, `TWOPAN_ACCESS_TOKEN` are set
- [ ] Run `123yunpan.py setup` to validate credentials and fetch token
- [ ] Test with `123yunpan.py file list --parent-id 0 --limit 5`
- [ ] Confirm token refresh works by checking `~/.123yunpan_config.json`
- [ ] Run `123yunpan.py space` — two quotas (professional / standard) both reported
- [ ] Run `123yunpan.py share create-batch --parent-id {id}` with > 10 files → xlsx auto-generated, 3 columns correct
- [ ] Confirm no `openpyxl` dependency: `grep -c "import openpyxl" scripts/123yunpan.py` → `0`(脚本里只剩一句注释提到它)
## Changelog
### v1.3.0
- **适配新空间规则**: 新增 `space` 命令,按官方 2026-09-24 公告拆分为**专业空间 / 标准空间**双配额展示;使用率 ≥90% 红色预警;专业空间剩余天数倒计时提示;`config check` 与 `upload` 增加空间预检
- **改**: 分享链接 Excel 导出阈值 **20 → 10**(超过 10 个即自动生成 Excel),新增 `--excel-threshold` 覆盖与 `--no-excel` 关闭
- **改**: Excel 列结构改为**三列** —— 文件名称(去分类前缀、去扩展名)/ 链接(超链接)/ 路径(云盘内完整相对路径)
- **新增**: `--resume` 断点续跑,读取 `~/.123yunpan_cache/share_batch_{root_id}.json` 只处理未完成文件
- **重写**: Excel 写入器改为**零依赖**(`zipfile` + 手写 OOXML),彻底移除 `openpyxl` 依赖,不再有"缺库静默降级 JSON"的问题
- **重构**: API 客户端加入 `QPS_RULES` 端点级限流(`/api/v2/file/list` = 1.5 QPS 最严格)+ 429/5xx 指数退避重试(最多 4 次)+ 401 自动刷新 Token 重试一次
- **新增**: `ERROR_HINTS` 中文错误码映射(401 / 429 / 5066 / 5113 / 20 等),失败时给出原因与处置建议
- **修复**: 分享名称超 35 字符导致 API 400 → 主动截断;`status > 100`(审核驳回)文件在遍历阶段过滤,不再逐个试错
### v1.2.0 (2026-05-09)
- **新增**: `--no-color` 和 `--json` 全局选项,支持禁用彩色输出和 JSON 格式输出
- **新增**: `file tree` 命令 — 目录树展示,支持 `--parent-id` 和 `--max-depth` 参数
- **新增**: `file download-batch` 命令 — 批量下载文件,支持 `--file-ids` 和 `--output-dir` 参数
- **新增**: `file storage-report` 命令 — 存储分析,支持 `--parent-id` 和 `--top` 参数
- **新增**: `offline list` 命令 — 列出离线下载任务(占位,待完善)
- **新增**: `share export` 命令 — 导出分享记录到 Excel/JSON,支持 `--output` 和 `--format` 参数
- **新增**: `config export` 命令 — 导出配置到 JSON,支持 `--output` 参数
- **新增**: `config import` 命令 — 从 JSON 导入配置,支持 `--file` 参数
### v1.1.5 (2026-05-09)
- **修复**: `upload` 命令的 `--duplicate` 参数默认值从 `0`(跳过)改为不发送(默认覆盖),避免API报错
- **修复**: `_refresh_token` 方法使用了未导入的 `urllib.request` 模块,导致自动刷新Token时崩溃
- **修复**: `file search --max-depth` 参数未实际生效,BFS遍历无深度限制
- **修复**: 代码参数名与文档不一致(`--new-name`、`--target-parent-id`、`--ips`),统一到文档标准
- **优化**: 所有 `--file-ids`/`--share-ids` 参数同时支持逗号分隔和空格分隔格式(如 `1,2,3` 或 `1 2 3`)
- **新增**: `cache list` 命令 - 列出所有BFS缓存文件
- **新增**: `cache clear` 命令 - 清除缓存(支持指定文件夹或全部清除)
- **新增**: `cache info` 命令 - 显示缓存目录信息
- **优化**: 批量操作进度显示增加ETA(预计剩余时间)估算
- **优化**: 清理重复的import语句
### v1.1.4
- 初始版本,支持70+ API端点