Files
MoviePilot/skills/feedback-issue/scripts/prepare_feedback_issue.py

160 lines
5.1 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.
"""校验并生成 feedback-issue 提交前的预览与 payload 文件。"""
from __future__ import annotations
import argparse
from pathlib import Path
from typing import Any, Optional
from feedback_issue_common import (
ALLOWED_ENVIRONMENTS,
ALLOWED_ISSUE_TYPES,
MAX_PREVIEW_LOGS_CHARS,
MAX_TITLE_CHARS,
build_issue_body,
check_content_quality,
load_diagnostics_logs,
read_json_file,
result_payload,
runtime_file,
sanitize_logs,
truncate,
validate_enum,
write_json_file,
)
REQUIRED_DRAFT_FIELDS = (
"title",
"version",
"environment",
"issue_type",
"description",
"original_user_request",
"diagnostics_file",
)
def normalize_draft(raw: dict[str, Any]) -> tuple[dict[str, Any], list[str]]:
"""规范化草稿字段并返回缺失字段列表。"""
draft = {key: str(raw.get(key) or "").strip() for key in REQUIRED_DRAFT_FIELDS}
missing = [key for key, value in draft.items() if not value]
draft["title"] = truncate(draft["title"], MAX_TITLE_CHARS, marker="...")
return draft, missing
def validate_draft(draft: dict[str, Any], logs: str) -> Optional[str]:
"""校验草稿枚举和内容质量,返回错误信息或 None。"""
for value, allowed, field_name in (
(draft["environment"], ALLOWED_ENVIRONMENTS, "environment"),
(draft["issue_type"], ALLOWED_ISSUE_TYPES, "issue_type"),
):
error = validate_enum(value, allowed, field_name)
if error:
return error
return check_content_quality(
title=draft["title"],
description=draft["description"],
original_user_request=draft["original_user_request"],
logs=logs,
)
def build_preview_text(draft: dict[str, Any], logs: str, diagnostics: dict[str, Any]) -> str:
"""构造给用户确认的 Markdown 预览文本。"""
preview_logs = sanitize_logs(logs, MAX_PREVIEW_LOGS_CHARS) or "会话中未捕获到相关后端日志。"
source_files = diagnostics.get("source_files") or []
sources = "\n".join(f"- {item}" for item in source_files) or "- 未命中具体日志文件"
return (
"请确认是否提交以下问题反馈:\n\n"
f"标题:{draft['title']}\n"
f"版本:{draft['version']}\n"
f"环境:{draft['environment']}\n"
f"类型:{draft['issue_type']}\n\n"
"诊断来源:\n"
f"{sources}\n\n"
"问题描述:\n"
f"{draft['description'].strip()}\n\n"
"日志预览(已脱敏):\n"
f"```bash\n{preview_logs}\n```\n\n"
"如内容无误,请回复「确认」;如需调整,请回复「修改:...」。"
)
def prepare_issue(draft_file: str | Path) -> dict[str, Any]:
"""读取草稿 JSON校验后写出 payload 与 preview 文件。"""
raw = read_json_file(draft_file)
draft, missing = normalize_draft(raw)
if missing:
return {
"success": False,
"reason": "missing_fields",
"message": f"草稿缺少必填字段:{', '.join(missing)}",
}
try:
logs, diagnostics = load_diagnostics_logs(draft["diagnostics_file"])
except Exception as err:
return {
"success": False,
"reason": "diagnostics_missing",
"message": f"无法读取诊断日志文件:{err}",
}
error = validate_draft(draft, logs)
if error:
return {
"success": False,
"reason": "invalid_draft",
"message": error,
}
payload = {
**draft,
"diagnostics_file": str(draft["diagnostics_file"]),
}
payload_file = runtime_file("payload", ".json")
preview_file = runtime_file("preview", ".md")
write_json_file(payload_file, payload)
preview_text = build_preview_text(draft, logs, diagnostics)
preview_file.write_text(preview_text, encoding="utf-8")
body_preview = build_issue_body(
version=draft["version"],
environment=draft["environment"],
issue_type=draft["issue_type"],
description=draft["description"],
logs=logs,
)
return {
"success": True,
"payload_file": str(payload_file),
"preview_file": str(preview_file),
"body_chars": len(body_preview),
"log_bytes": len(logs.encode("utf-8", errors="replace")),
"log_lines": len(logs.splitlines()) if logs else 0,
"message": (
"已生成 Issue 预览和提交 payload。请把 preview_file 内容完整展示给用户,"
"等待明确「确认」后再调用 submit_feedback_issue.py。"
),
}
def parse_args() -> argparse.Namespace:
"""解析命令行参数。"""
parser = argparse.ArgumentParser(description="生成 MoviePilot 反馈 Issue 预览")
parser.add_argument("--draft-file", required=True, help="包含 Issue 草稿字段的 JSON 文件")
return parser.parse_args()
def main() -> int:
"""脚本入口:校验草稿并输出 JSON 结果。"""
args = parse_args()
result = prepare_issue(args.draft_file)
print(result_payload(**result))
return 0 if result.get("success") else 2
if __name__ == "__main__":
raise SystemExit(main())