Files
dingtalk-workspace-cli/skills/mono/scripts/drive_tree_list.py
T
qinze a65d6f23ec fix: resolve real-machine QA findings across CLI, scripts and skill docs
Fix CLI command bugs surfaced by full real-machine QA:
- aitable: make chart/dashboard share update --enabled a string flag so
  "--enabled false" disables instead of silently enabling (bool flag +
  space-syntax help example inverted the action); clarify chart update
  requires --config; make form get filter by view-id client-side so it
  returns a single form; drop inline // comments from chart JSON examples
- chat: resolve conversation-info --user to openDingTalkId, register
  --id/--conversation-id/--chat aliases; cap list-all-conversations
  --limit at 100 and reject larger values instead of silent truncation;
  detect webhook errcode failures instead of wrapping them as success;
  remove duplicate group/members subcommand registration in help
- contact: register --dept/--depts as the primary dept flags to match
  the RunE parsing (were only registered as --id/--ids)
- sheet: emit clean JSON for media-upload and export under --format json,
  suppressing progress lines that leaked onto stdout
- wiki: correct node create --type enum (drop unsupported asheet, add
  axls/able/appt/adraw/amind)
- ding: default message list --type to ALL since the server rejects an
  empty type

Fix helper scripts (mono and multi):
- aitable import/export flag names and tableId length regex
- mail search --limit, contact dept response keys and userInfo nesting
- attendance_my_record whoami compatibility, calendar_schedule_meeting
  event id unwrapping, drive_tree_list recursion via fileId, report
  scripts migrated off deprecated report list/detail

Sync skill docs (mono and multi) to real-machine behavior across all
products: command indexes, flag names, enums, return-structure keys, and
cross-product intent routing; annotate genuinely server-side limitations
and the no-op global --jq/--fields flags.
2026-07-07 23:38:23 +08:00

138 lines
4.2 KiB
Python
Raw 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.
#!/usr/bin/env python3
"""
递归列出钉盘目录树结构(可指定深度)
用法:
python drive_tree_list.py # 列出根目录
python drive_tree_list.py --depth 2 # 递归 2 层
python drive_tree_list.py --folder <id> # 指定目录 (传 drive list 返回的 fileId)
python drive_tree_list.py --dry-run
说明:
`dws drive list` 返回的每个 item 有两个 ID:
- dentryId:纯数字串,`--folder` 不接受,别用它递归;
- fileId:字母数字串,即 CLI 所称 dentryUuid,`--folder` 只认它。
递归子目录必须用 fileId 作为 `--folder` 的值。
"""
import sys
import json
import subprocess
import argparse
from typing import List, Any, Optional
def run_dws(
args: List[str], dry_run: bool = False,
) -> Optional[Any]:
cmd = ['dws'] + args
if dry_run:
print(f"[dry-run] {' '.join(cmd)}")
return None
try:
result = subprocess.run(
cmd, capture_output=True, text=True, timeout=60
)
if result.returncode != 0:
print(f"错误:{result.stderr.strip()}", file=sys.stderr)
return None
return json.loads(result.stdout)
except (subprocess.TimeoutExpired, json.JSONDecodeError,
FileNotFoundError) as e:
print(f"错误:{e}", file=sys.stderr)
return None
def list_dir(
folder: str = '', dry_run: bool = False,
) -> list:
cmd_args = [
'drive', 'list', '--limit', '50', '--format', 'json',
]
if folder:
cmd_args.extend(['--folder', folder])
data = run_dws(cmd_args, dry_run=dry_run)
if not data:
return []
if isinstance(data, list):
return data
if isinstance(data, dict):
inner = data.get('result', data)
if isinstance(inner, dict):
return inner.get('items', inner.get('dentryList', []))
if isinstance(inner, list):
return inner
return []
def print_tree(
items: list, depth: int, max_depth: int,
prefix: str = '', dry_run: bool = False,
):
for i, item in enumerate(items):
is_last = (i == len(items) - 1)
connector = '└── ' if is_last else '├── '
name = item.get('name') or item.get('fileName', '?')
item_type = item.get('type') or item.get('dentryType', '')
is_dir = str(item_type).lower() in (
'folder', 'directory', '1',
)
icon = '📁' if is_dir else '📄'
size_str = ''
size = item.get('size') or item.get('fileSize')
if size and not is_dir:
size = int(size)
if size > 1024 * 1024:
size_str = f" ({size / 1024 / 1024:.1f}MB)"
elif size > 1024:
size_str = f" ({size / 1024:.1f}KB)"
else:
size_str = f" ({size}B)"
print(f"{prefix}{connector}{icon} {name}{size_str}")
if is_dir and depth < max_depth:
child_prefix = prefix + (' ' if is_last else '│ ')
# `--folder` 只认 fileId (dentryUuid),不认纯数字 dentryId
folder_id = item.get('fileId', '')
if folder_id:
children = list_dir(folder_id, dry_run=dry_run)
print_tree(
children, depth + 1, max_depth,
child_prefix, dry_run,
)
def main():
parser = argparse.ArgumentParser(
description='递归列出钉盘目录树'
)
parser.add_argument(
'--folder', default='',
help='起始目录 ID (传 drive list 返回的 fileId)',
)
parser.add_argument(
'--depth', type=int, default=1,
help='递归深度 (默认 1, 最大 5)',
)
parser.add_argument('--dry-run', action='store_true')
args = parser.parse_args()
args.depth = min(args.depth, 5)
root_name = args.folder or '我的文件'
print(f"📁 {root_name}")
items = list_dir(args.folder, dry_run=args.dry_run)
if args.dry_run:
return
if not items:
print(' (空目录)')
return
print_tree(items, 0, args.depth, '', args.dry_run)
print(f"\n共 {len(items)} 个项目 (根目录)")
if __name__ == '__main__':
main()