Files
skill-template/content-manager/content_manager/cli/app.py
2026-04-04 10:35:02 +08:00

262 lines
10 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.
"""CLI 入口argparse 装配与分发Controller"""
from __future__ import annotations
import argparse
import os
import sys
from typing import List, Optional
from content_manager.services import article_service, image_service, video_service
from content_manager.services.article_service import resolve_publish_platform
from content_manager.util.argparse_zh import ZhArgumentParser
def _print_root_usage_zh() -> None:
print(
"""内容管理:请指定资源类型子命令。
python main.py article list
python main.py article get 1
python main.py article add --title "标题" --body-file 文章.md
python main.py article generate 豆包 搜狐号 RPA降本增效
python main.py image add --file D:\\\\a.png [--title "说明"]
python main.py video add --file D:\\\\a.mp4 [--title "说明"] [--duration-ms 120000]
查看完整说明python main.py -h"""
)
def _handle_article_add(args: argparse.Namespace) -> None:
if args.body_file:
fp = os.path.abspath(args.body_file)
try:
with open(fp, encoding="utf-8") as f:
body = f.read()
except OSError as e:
print(f"❌ 无法读取正文文件:{fp}\n原因:{e}")
sys.exit(1)
else:
body = args.body or ""
article_service.cmd_add(args.title, body, source="manual")
def _handle_article_import(args: argparse.Namespace) -> None:
article_service.cmd_import_json(args.path)
def _handle_article_generate(args: argparse.Namespace) -> None:
raw_parts = [str(x).strip() for x in (args.generate_args or []) if str(x).strip()]
if not raw_parts:
print("❌ 缺少主题或关键词。")
print("示例python main.py article generate 豆包 搜狐号 RPA降本增效")
sys.exit(1)
platform_guess = resolve_publish_platform(raw_parts[0])
if platform_guess and len(raw_parts) == 1:
print("❌ 缺少主题或关键词。")
sys.exit(1)
if platform_guess and len(raw_parts) >= 2:
publish_platform = platform_guess
topic = " ".join(raw_parts[1:]).strip()
else:
publish_platform = "common"
topic = " ".join(raw_parts).strip()
if not topic:
print("❌ 主题或关键词不能为空。")
sys.exit(1)
article_service.cmd_generate(
args.llm_target,
topic,
publish_platform=publish_platform,
title=getattr(args, "title", None),
)
def _handle_article_feedback(args: argparse.Namespace) -> None:
article_service.cmd_feedback(args.article_id, args.status, args.account_id, args.error_msg)
def _handle_article_save_legacy(args: argparse.Namespace) -> None:
article_service.cmd_save(args.legacy_id, args.legacy_title, args.legacy_content)
def _handle_image_add(args: argparse.Namespace) -> None:
image_service.cmd_add(args.file, title=getattr(args, "title", None))
def _handle_image_feedback(args: argparse.Namespace) -> None:
image_service.cmd_feedback(args.image_id, args.status, args.account_id, args.error_msg)
def _handle_video_add(args: argparse.Namespace) -> None:
video_service.cmd_add(
args.file,
title=getattr(args, "title", None),
duration_ms=getattr(args, "duration_ms", None),
)
def _handle_video_feedback(args: argparse.Namespace) -> None:
video_service.cmd_feedback(args.video_id, args.status, args.account_id, args.error_msg)
def build_parser() -> ZhArgumentParser:
fmt = argparse.RawDescriptionHelpFormatter
p = ZhArgumentParser(
prog="main.py",
description="内容管理:文章(正文在库内)与图片/视频(文件在数据目录,库内仅存路径)。",
epilog="示例见各子命令 -h一级分组article / image / video",
formatter_class=fmt,
)
sub = p.add_subparsers(
dest="resource",
required=True,
metavar="资源类型",
help="article 文章 | image 图片 | video 视频",
parser_class=ZhArgumentParser,
)
# ----- article -----
art = sub.add_parser("article", help="文章:正文与元数据在 SQLite", formatter_class=fmt)
art_sub = art.add_subparsers(
dest="article_cmd",
required=True,
metavar="子命令",
parser_class=ZhArgumentParser,
)
sp = art_sub.add_parser("list", help="列出文章")
sp.add_argument("--limit", type=int, default=10)
sp.add_argument("--max-chars", type=int, default=50)
sp.set_defaults(handler=lambda a: article_service.cmd_list(limit=a.limit, max_chars=a.max_chars))
sp = art_sub.add_parser("get", help="按 id 输出 JSON")
sp.add_argument("article_id", metavar="文章id")
sp.set_defaults(handler=lambda a: article_service.cmd_get(a.article_id))
sp = art_sub.add_parser(
"add",
help="新增文章",
formatter_class=fmt,
epilog="示例python main.py article add --title \"标题\" --body \"正文\"",
)
sp.add_argument("--title", required=True)
g = sp.add_mutually_exclusive_group(required=True)
g.add_argument("--body-file", metavar="路径")
g.add_argument("--body", metavar="正文")
sp.set_defaults(handler=_handle_article_add)
sp = art_sub.add_parser("import-json", help="从 JSON 批量导入")
sp.add_argument("path", metavar="JSON路径")
sp.set_defaults(handler=_handle_article_import)
sp = art_sub.add_parser("generate", help="调用 llm-manager 生成并入库", formatter_class=fmt)
sp.add_argument("llm_target", metavar="大模型目标")
sp.add_argument("generate_args", nargs="+", metavar="生成参数")
sp.add_argument("--title", metavar="标题", default=None)
sp.set_defaults(handler=_handle_article_generate)
sp = art_sub.add_parser("prompt-list", help="查看提示词模板")
sp.add_argument("platform", nargs="?", default=None, metavar="发布平台")
sp.add_argument("--limit", type=int, default=30)
sp.set_defaults(handler=lambda a: article_service.cmd_prompt_list(a.platform, a.limit))
sp = art_sub.add_parser("delete", help="删除文章")
sp.add_argument("article_id", metavar="文章id")
sp.set_defaults(handler=lambda a: article_service.cmd_delete(a.article_id))
sp = art_sub.add_parser("feedback", help="回写发布状态", formatter_class=fmt)
sp.add_argument("article_id", metavar="文章id")
sp.add_argument("status", metavar="状态")
sp.add_argument("account_id", nargs="?", default=None, metavar="账号")
sp.add_argument("error_msg", nargs="?", default=None, metavar="错误说明")
sp.set_defaults(handler=_handle_article_feedback)
sp = art_sub.add_parser("save", help="旧版单行正文保存", formatter_class=fmt)
sp.add_argument("legacy_id", metavar="id")
sp.add_argument("legacy_title", metavar="标题")
sp.add_argument("legacy_content", metavar="正文一行")
sp.set_defaults(handler=_handle_article_save_legacy)
# ----- image -----
img = sub.add_parser("image", help="图片文件在数据目录images 表存相对路径", formatter_class=fmt)
img_sub = img.add_subparsers(
dest="image_cmd",
required=True,
metavar="子命令",
parser_class=ZhArgumentParser,
)
sp = img_sub.add_parser("list", help="列出图片")
sp.add_argument("--limit", type=int, default=20)
sp.add_argument("--max-chars", type=int, default=80)
sp.set_defaults(handler=lambda a: image_service.cmd_list(limit=a.limit, max_chars=a.max_chars))
sp = img_sub.add_parser("get", help="按 id 输出 JSON含 absolute_path")
sp.add_argument("image_id", metavar="图片id")
sp.set_defaults(handler=lambda a: image_service.cmd_get(a.image_id))
sp = img_sub.add_parser("add", help="从本地文件复制入库", formatter_class=fmt)
sp.add_argument("--file", required=True, metavar="文件", help="源图片路径")
sp.add_argument("--title", default=None, metavar="标题", help="可选说明")
sp.set_defaults(handler=_handle_image_add)
sp = img_sub.add_parser("delete", help="删除记录与磁盘目录")
sp.add_argument("image_id", metavar="图片id")
sp.set_defaults(handler=lambda a: image_service.cmd_delete(a.image_id))
sp = img_sub.add_parser("feedback", help="回写状态", formatter_class=fmt)
sp.add_argument("image_id", metavar="图片id")
sp.add_argument("status", metavar="状态")
sp.add_argument("account_id", nargs="?", default=None, metavar="账号")
sp.add_argument("error_msg", nargs="?", default=None, metavar="错误说明")
sp.set_defaults(handler=_handle_image_feedback)
# ----- video -----
vid = sub.add_parser("video", help="视频文件在数据目录videos 表存相对路径", formatter_class=fmt)
vid_sub = vid.add_subparsers(
dest="video_cmd",
required=True,
metavar="子命令",
parser_class=ZhArgumentParser,
)
sp = vid_sub.add_parser("list", help="列出视频")
sp.add_argument("--limit", type=int, default=20)
sp.add_argument("--max-chars", type=int, default=80)
sp.set_defaults(handler=lambda a: video_service.cmd_list(limit=a.limit, max_chars=a.max_chars))
sp = vid_sub.add_parser("get", help="按 id 输出 JSON")
sp.add_argument("video_id", metavar="视频id")
sp.set_defaults(handler=lambda a: video_service.cmd_get(a.video_id))
sp = vid_sub.add_parser("add", help="从本地文件复制入库", formatter_class=fmt)
sp.add_argument("--file", required=True, metavar="文件")
sp.add_argument("--title", default=None, metavar="标题")
sp.add_argument("--duration-ms", type=int, default=None, metavar="毫秒", help="可选时长")
sp.set_defaults(handler=_handle_video_add)
sp = vid_sub.add_parser("delete", help="删除记录与磁盘目录")
sp.add_argument("video_id", metavar="视频id")
sp.set_defaults(handler=lambda a: video_service.cmd_delete(a.video_id))
sp = vid_sub.add_parser("feedback", help="回写状态", formatter_class=fmt)
sp.add_argument("video_id", metavar="视频id")
sp.add_argument("status", metavar="状态")
sp.add_argument("account_id", nargs="?", default=None, metavar="账号")
sp.add_argument("error_msg", nargs="?", default=None, metavar="错误说明")
sp.set_defaults(handler=_handle_video_feedback)
return p
def main(argv: Optional[List[str]] = None) -> int:
argv = argv if argv is not None else sys.argv[1:]
if not argv:
_print_root_usage_zh()
return 1
parser = build_parser()
args = parser.parse_args(argv)
args.handler(args)
return 0