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.
This commit is contained in:
qinze committed 2026-07-07 23:38:23 +08:00
1 parent 238f4256d3
commit a65d6f23ec
138 files changed
+7767 -1714

No files matched your search

+98 -66
View File
@@ -2,9 +2,14 @@
"""
查看今天收到的日志列表及详情
基于 `dws report inbox list` + `dws report entry get`(旧的 report list / report detail
已废弃)。inbox list 返回的 result[] 使用中文展示键:日期 / 标题 / 发送人 / 状态 / 钉钉链接;
reportId 不在 result[] 里,只在 _internalDetailCommands[].command 中,按页与 result[] 同序对应。
用法:
python report_received_today.py
python report_received_today.py --days 3 # 最近 3 天
python report_received_today.py --days 3 # 最近 3 天
python report_received_today.py --detail # 额外拉取每条正文 (entry get)
python report_received_today.py --dry-run
"""
@@ -13,7 +18,7 @@ import json
import subprocess
import argparse
from datetime import datetime, timedelta
from typing import List, Any, Optional
from typing import List, Any, Optional, Tuple
def run_dws(
@@ -37,91 +42,118 @@ def run_dws(
return None
def to_iso(dt: datetime) -> str:
return dt.strftime('%Y-%m-%dT%H:%M:%S+08:00')
def iso_start(dt: datetime) -> str:
return dt.strftime('%Y-%m-%dT00:00:00+08:00')
def iso_end(dt: datetime) -> str:
return dt.strftime('%Y-%m-%dT23:59:59+08:00')
def report_id_from_command(cmd: str) -> str:
parts = cmd.split()
if '--report-id' in parts:
i = parts.index('--report-id')
if i + 1 < len(parts):
return parts[i + 1]
return ''
def fetch_inbox(
start: str, end: str, dry_run: bool,
) -> List[Tuple[dict, str]]:
"""按 cursor 翻页拉全 inbox;返回 (result_item, reportId) 列表。"""
pairs: List[Tuple[dict, str]] = []
cursor = 0
while True:
data = run_dws([
'report', 'inbox', 'list',
'--start', start,
'--end', end,
'--cursor', str(cursor),
'--size', '20',
'--format', 'json',
], dry_run=dry_run)
if dry_run or not isinstance(data, dict):
return pairs
items = data.get('result') or []
cmds = data.get('_internalDetailCommands') or []
for idx, item in enumerate(items):
rid = ''
if idx < len(cmds):
rid = report_id_from_command(
cmds[idx].get('command', '')
)
pairs.append((item, rid))
if data.get('hasMore') and data.get('nextCursor') is not None:
cursor = data['nextCursor']
else:
break
return pairs
def print_detail(rid: str) -> None:
detail = run_dws([
'report', 'entry', 'get',
'--report-id', rid, '--format', 'json',
])
if not isinstance(detail, dict):
return
result = detail.get('result')
if not isinstance(result, dict):
return
for c in (result.get('report_content') or [])[:3]:
key = c.get('key', '')
val = c.get('value', '')
if key and val:
print(f" {key}: {str(val).strip()[:60]}")
def main():
parser = argparse.ArgumentParser(
description='查看收到的日志'
parser = argparse.ArgumentParser(description='查看收到的日志')
parser.add_argument(
'--days', type=int, default=1, help='查询天数 (默认 1,即今天)'
)
parser.add_argument(
'--days', type=int, default=1, help='查询天数 (默认 1)'
'--detail', action='store_true', help='额外拉取每条正文'
)
parser.add_argument('--dry-run', action='store_true')
args = parser.parse_args()
now = datetime.now()
start = now - timedelta(days=args.days)
start = start.replace(hour=0, minute=0, second=0)
start_dt = now - timedelta(days=max(args.days - 1, 0))
start = iso_start(start_dt)
end = iso_end(now)
label = '今天' if args.days == 1 else f'最近 {args.days} 天'
print(f'📓 查看{label}收到的日志...\n')
data = run_dws([
'report', 'list',
'--start', to_iso(start),
'--end', to_iso(now),
'--cursor', '0',
'--size', '20',
'--format', 'json',
], dry_run=args.dry_run)
print(f'查看{label}收到的日志...\n')
pairs = fetch_inbox(start, end, args.dry_run)
if args.dry_run:
return
if not data:
print('未查到日志')
if not pairs:
print(' 暂无收到的日志')
return
if isinstance(data, list):
reports = data
elif isinstance(data, dict):
inner = data.get('result', data)
if isinstance(inner, dict):
reports = inner.get('report_list',
inner.get('reports', []))
elif isinstance(inner, list):
reports = inner
else:
reports = []
else:
reports = []
if not reports:
print(' ✅ 暂无收到的日志')
return
print(f"📓 {label}日志 ({len(reports)} 条)")
print(f"{label}日志 ({len(pairs)} 条)")
print('=' * 50)
for r in reports:
if not isinstance(r, dict):
print(f"\n 📝 {r}")
continue
rid = r.get('reportId') or r.get('id', '')
creator = r.get('creatorName') or r.get('creator', '未知')
template = r.get('templateName') or r.get('template', '')
create_time = r.get('createTime', '')
if isinstance(create_time, (int, float)):
create_time = datetime.fromtimestamp(
create_time / 1000
).strftime('%Y-%m-%d %H:%M')
for item, rid in pairs:
title = item.get('标题') or '日志'
sender = item.get('发送人') or '未知'
date = item.get('日期') or ''
status = item.get('状态') or ''
link = item.get('钉钉链接') or ''
print(f"\n 📝 {template or '日志'} - {creator}")
print(f" 时间: {create_time}")
print(f" ID: {rid}")
print(f"\n {title} - {sender}")
print(f" 时间: {date}")
if status:
print(f" 状态: {status}")
if link:
print(f" 链接: {link}")
if rid:
detail = run_dws([
'report', 'detail',
'--report-id', rid, '--format', 'json',
])
if detail and isinstance(detail, dict):
contents = detail.get('contents', [])
for c in contents[:3]:
key = c.get('key') or c.get('title', '')
val = c.get('value') or c.get('content', '')
if key and val:
print(f" {key}: {str(val)[:60]}")
if args.detail and rid:
print_detail(rid)
if __name__ == '__main__':