rednote-cli 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (81) hide show
  1. rednote_cli/__init__.py +5 -0
  2. rednote_cli/_runtime/__init__.py +0 -0
  3. rednote_cli/_runtime/common/__init__.py +0 -0
  4. rednote_cli/_runtime/common/app_utils.py +77 -0
  5. rednote_cli/_runtime/common/config.py +83 -0
  6. rednote_cli/_runtime/common/enums.py +17 -0
  7. rednote_cli/_runtime/common/errors.py +22 -0
  8. rednote_cli/_runtime/core/__init__.py +0 -0
  9. rednote_cli/_runtime/core/account_manager.py +349 -0
  10. rednote_cli/_runtime/core/browser/__init__.py +0 -0
  11. rednote_cli/_runtime/core/browser/manager.py +247 -0
  12. rednote_cli/_runtime/core/database/__init__.py +0 -0
  13. rednote_cli/_runtime/core/database/manager.py +334 -0
  14. rednote_cli/_runtime/platforms/__init__.py +0 -0
  15. rednote_cli/_runtime/platforms/base.py +62 -0
  16. rednote_cli/_runtime/platforms/factory.py +55 -0
  17. rednote_cli/_runtime/platforms/publishing/__init__.py +12 -0
  18. rednote_cli/_runtime/platforms/publishing/media.py +275 -0
  19. rednote_cli/_runtime/platforms/publishing/models.py +59 -0
  20. rednote_cli/_runtime/platforms/publishing/validator.py +124 -0
  21. rednote_cli/_runtime/services/__init__.py +1 -0
  22. rednote_cli/_runtime/services/scraper_service.py +235 -0
  23. rednote_cli/adapters/__init__.py +1 -0
  24. rednote_cli/adapters/output/__init__.py +1 -0
  25. rednote_cli/adapters/output/event_stream.py +29 -0
  26. rednote_cli/adapters/output/formatter_json.py +23 -0
  27. rednote_cli/adapters/output/formatter_table.py +39 -0
  28. rednote_cli/adapters/output/writer.py +17 -0
  29. rednote_cli/adapters/persistence/__init__.py +1 -0
  30. rednote_cli/adapters/persistence/file_account_repo.py +51 -0
  31. rednote_cli/adapters/platform/__init__.py +1 -0
  32. rednote_cli/adapters/platform/rednote/__init__.py +1 -0
  33. rednote_cli/adapters/platform/rednote/extractor.py +65 -0
  34. rednote_cli/adapters/platform/rednote/publisher.py +26 -0
  35. rednote_cli/adapters/platform/rednote/runtime_extractor.py +818 -0
  36. rednote_cli/adapters/platform/rednote/runtime_publisher.py +373 -0
  37. rednote_cli/adapters/platform/rednote/runtime_registration.py +20 -0
  38. rednote_cli/application/__init__.py +1 -0
  39. rednote_cli/application/dto/__init__.py +1 -0
  40. rednote_cli/application/dto/input_models.py +121 -0
  41. rednote_cli/application/dto/output_models.py +78 -0
  42. rednote_cli/application/use_cases/__init__.py +1 -0
  43. rednote_cli/application/use_cases/account_list.py +9 -0
  44. rednote_cli/application/use_cases/account_mutation.py +22 -0
  45. rednote_cli/application/use_cases/auth_login.py +64 -0
  46. rednote_cli/application/use_cases/auth_status.py +96 -0
  47. rednote_cli/application/use_cases/doctor.py +49 -0
  48. rednote_cli/application/use_cases/init_runtime.py +20 -0
  49. rednote_cli/application/use_cases/note_get.py +22 -0
  50. rednote_cli/application/use_cases/note_search.py +26 -0
  51. rednote_cli/application/use_cases/publish_note.py +25 -0
  52. rednote_cli/application/use_cases/user_get.py +18 -0
  53. rednote_cli/application/use_cases/user_search.py +8 -0
  54. rednote_cli/application/use_cases/user_self.py +8 -0
  55. rednote_cli/cli/__init__.py +1 -0
  56. rednote_cli/cli/__main__.py +5 -0
  57. rednote_cli/cli/commands/__init__.py +1 -0
  58. rednote_cli/cli/commands/account.py +204 -0
  59. rednote_cli/cli/commands/doctor.py +20 -0
  60. rednote_cli/cli/commands/init.py +20 -0
  61. rednote_cli/cli/commands/note.py +101 -0
  62. rednote_cli/cli/commands/publish.py +147 -0
  63. rednote_cli/cli/commands/search.py +185 -0
  64. rednote_cli/cli/commands/user.py +113 -0
  65. rednote_cli/cli/main.py +163 -0
  66. rednote_cli/cli/options.py +13 -0
  67. rednote_cli/cli/runtime.py +142 -0
  68. rednote_cli/cli/utils.py +74 -0
  69. rednote_cli/domain/__init__.py +1 -0
  70. rednote_cli/domain/errors.py +50 -0
  71. rednote_cli/domain/note_search_filters.py +155 -0
  72. rednote_cli/infra/__init__.py +1 -0
  73. rednote_cli/infra/exit_codes.py +30 -0
  74. rednote_cli/infra/logger.py +11 -0
  75. rednote_cli/infra/paths.py +31 -0
  76. rednote_cli/infra/platforms.py +4 -0
  77. rednote_cli-0.1.0.dist-info/METADATA +81 -0
  78. rednote_cli-0.1.0.dist-info/RECORD +81 -0
  79. rednote_cli-0.1.0.dist-info/WHEEL +5 -0
  80. rednote_cli-0.1.0.dist-info/entry_points.txt +2 -0
  81. rednote_cli-0.1.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,101 @@
1
+ from __future__ import annotations
2
+
3
+ import typer
4
+
5
+ from rednote_cli.application.dto.input_models import NoteGetInput
6
+ from rednote_cli.application.use_cases.note_get import execute_note_get
7
+ from rednote_cli.cli.options import CliContext
8
+ from rednote_cli.cli.runtime import run_async_command
9
+ from rednote_cli.cli.utils import all_option_params_are_default, load_json_input, pick_cli_or_input
10
+
11
+ app = typer.Typer(help="笔记详情查询", no_args_is_help=False)
12
+
13
+
14
+ @app.callback(invoke_without_command=True)
15
+ def note_get(
16
+ ctx: typer.Context,
17
+ note_id: str | None = typer.Option(None, "--note-id", help="笔记 ID;可由 --input 提供"),
18
+ xsec_token: str | None = typer.Option(None, "--xsec-token", help="xsec_token,可选"),
19
+ xsec_source: str = typer.Option("pc_feed", "--xsec-source", help="xsec 来源,默认 pc_feed"),
20
+ comment_size: int = typer.Option(10, "--comment-size", help="一级评论数量(1..100)"),
21
+ sub_comment_size: int = typer.Option(5, "--sub-comment-size", help="二级评论数量(1..50)"),
22
+ account: str | None = typer.Option(None, "--account", help="账号唯一标识(user_no)"),
23
+ input_file: str | None = typer.Option(
24
+ None,
25
+ "--input",
26
+ help="JSON 输入文件路径或 '-',字段示例:note_id,xsec_token,xsec_source,comment_size,sub_comment_size,account_uid",
27
+ ),
28
+ ):
29
+ """
30
+ 获取单条笔记详情与评论。
31
+
32
+ 参数优先级:显式 CLI 参数 > --input JSON > 默认值。
33
+ """
34
+ if all_option_params_are_default(ctx=ctx):
35
+ typer.echo(ctx.get_help())
36
+ raise typer.Exit(code=0)
37
+
38
+ cli_ctx: CliContext = ctx.obj
39
+ payload = load_json_input(input_file)
40
+ account_uid = pick_cli_or_input(
41
+ ctx=ctx,
42
+ param_name="account",
43
+ cli_value=account,
44
+ payload=payload,
45
+ payload_key="account_uid",
46
+ )
47
+
48
+ async def _run():
49
+ validated = NoteGetInput(
50
+ note_id=pick_cli_or_input(
51
+ ctx=ctx,
52
+ param_name="note_id",
53
+ cli_value=note_id,
54
+ payload=payload,
55
+ payload_key="note_id",
56
+ ),
57
+ xsec_token=pick_cli_or_input(
58
+ ctx=ctx,
59
+ param_name="xsec_token",
60
+ cli_value=xsec_token,
61
+ payload=payload,
62
+ payload_key="xsec_token",
63
+ ),
64
+ xsec_source=pick_cli_or_input(
65
+ ctx=ctx,
66
+ param_name="xsec_source",
67
+ cli_value=xsec_source,
68
+ payload=payload,
69
+ payload_key="xsec_source",
70
+ ),
71
+ comment_size=pick_cli_or_input(
72
+ ctx=ctx,
73
+ param_name="comment_size",
74
+ cli_value=comment_size,
75
+ payload=payload,
76
+ payload_key="comment_size",
77
+ ),
78
+ sub_comment_size=pick_cli_or_input(
79
+ ctx=ctx,
80
+ param_name="sub_comment_size",
81
+ cli_value=sub_comment_size,
82
+ payload=payload,
83
+ payload_key="sub_comment_size",
84
+ ),
85
+ account_uid=account_uid,
86
+ )
87
+ return await execute_note_get(
88
+ note_id=validated.note_id,
89
+ xsec_token=validated.xsec_token,
90
+ xsec_source=validated.xsec_source,
91
+ comment_size=validated.comment_size,
92
+ sub_comment_size=validated.sub_comment_size,
93
+ account_uid=validated.account_uid,
94
+ )
95
+
96
+ run_async_command(
97
+ ctx=cli_ctx,
98
+ command="note.get",
99
+ func=_run,
100
+ account_uid=account_uid,
101
+ )
@@ -0,0 +1,147 @@
1
+ from __future__ import annotations
2
+
3
+ import typer
4
+
5
+ from rednote_cli.application.dto.input_models import PublishNoteInput
6
+ from rednote_cli.application.use_cases.publish_note import execute_publish_note
7
+ from rednote_cli.cli.options import CliContext
8
+ from rednote_cli.cli.runtime import run_async_command
9
+ from rednote_cli.cli.utils import all_option_params_are_default, load_json_input, parse_csv, pick_cli_or_input
10
+ from rednote_cli.domain.errors import InvalidArgsError
11
+
12
+
13
+ app = typer.Typer(help="发布相关命令(图文/视频)", no_args_is_help=False)
14
+
15
+
16
+ @app.callback(invoke_without_command=True)
17
+ def publish(
18
+ ctx: typer.Context,
19
+ target: str | None = typer.Option("image", "--target", help="发布类型:image | video | article"),
20
+ image_list: str | None = typer.Option(None, "--image-list", help="图片路径/URL 列表(逗号分隔,target=image 时必填)"),
21
+ video: str | None = typer.Option(None, "--video", help="视频路径/URL(target=video 时必填)"),
22
+ title: str | None = typer.Option(None, "--title", help="标题;可由 --input 提供"),
23
+ content: str | None = typer.Option(None, "--content", help="正文;可由 --input 提供"),
24
+ tags: str | None = typer.Option(None, "--tags", help="标签列表(逗号分隔),例如 科技,AI"),
25
+ schedule_at: str | None = typer.Option(None, "--schedule-at", help="定时发布时间(RFC3339),为空表示立即发布"),
26
+ account: str | None = typer.Option(None, "--account", help="账号唯一标识(user_no),必填"),
27
+ input_file: str | None = typer.Option(
28
+ None,
29
+ "--input",
30
+ help="JSON 输入文件路径或 '-',字段示例:target,image_list,video,title,content,tags,schedule_at",
31
+ ),
32
+ ):
33
+ """
34
+ 发布内容(图文/视频)。
35
+
36
+ 参数优先级:显式 CLI 参数 > --input JSON > 默认值。
37
+ """
38
+ if all_option_params_are_default(ctx=ctx):
39
+ typer.echo(ctx.get_help())
40
+ raise typer.Exit(code=0)
41
+
42
+ cli_ctx: CliContext = ctx.obj
43
+ payload = load_json_input(input_file)
44
+ account_uid = (account or "").strip()
45
+
46
+ async def _run():
47
+ if not account_uid:
48
+ raise InvalidArgsError("`--account` 为必填参数")
49
+
50
+ resolved_target = pick_cli_or_input(
51
+ ctx=ctx,
52
+ param_name="target",
53
+ cli_value=target,
54
+ payload=payload,
55
+ payload_key="target",
56
+ )
57
+
58
+ image_source = pick_cli_or_input(
59
+ ctx=ctx,
60
+ param_name="image_list",
61
+ cli_value=image_list,
62
+ payload=payload,
63
+ payload_key="image_list",
64
+ )
65
+ if isinstance(image_source, list):
66
+ raw_images = image_source
67
+ else:
68
+ raw_images = parse_csv(image_source)
69
+
70
+ video_source = pick_cli_or_input(
71
+ ctx=ctx,
72
+ param_name="video",
73
+ cli_value=video,
74
+ payload=payload,
75
+ payload_key="video",
76
+ )
77
+ normalized_video = video_source
78
+ if isinstance(resolved_target, str) and resolved_target.strip().lower() == "video":
79
+ if (normalized_video is None or normalized_video == "") and len(raw_images) == 1:
80
+ normalized_video = raw_images[0]
81
+
82
+ tags_source = pick_cli_or_input(
83
+ ctx=ctx,
84
+ param_name="tags",
85
+ cli_value=tags,
86
+ payload=payload,
87
+ payload_key="tags",
88
+ )
89
+ if isinstance(tags_source, list):
90
+ normalized_tags = tags_source
91
+ else:
92
+ normalized_tags = parse_csv(tags_source)
93
+
94
+ validated = PublishNoteInput(
95
+ target=resolved_target,
96
+ image_list=raw_images,
97
+ video=normalized_video,
98
+ title=pick_cli_or_input(
99
+ ctx=ctx,
100
+ param_name="title",
101
+ cli_value=title,
102
+ payload=payload,
103
+ payload_key="title",
104
+ )
105
+ or "",
106
+ content=pick_cli_or_input(
107
+ ctx=ctx,
108
+ param_name="content",
109
+ cli_value=content,
110
+ payload=payload,
111
+ payload_key="content",
112
+ )
113
+ or "",
114
+ tags=normalized_tags,
115
+ schedule_at=pick_cli_or_input(
116
+ ctx=ctx,
117
+ param_name="schedule_at",
118
+ cli_value=schedule_at,
119
+ payload=payload,
120
+ payload_key="schedule_at",
121
+ ),
122
+ account_uid=account_uid,
123
+ )
124
+ target_text = validated.target.strip().lower()
125
+ if target_text == "video":
126
+ media_list = [validated.video] if validated.video else []
127
+ elif target_text == "image":
128
+ media_list = validated.image_list
129
+ else:
130
+ media_list = validated.image_list
131
+
132
+ return await execute_publish_note(
133
+ target=validated.target,
134
+ image_list=media_list,
135
+ title=validated.title,
136
+ content=validated.content,
137
+ tags=validated.tags,
138
+ schedule_at=validated.schedule_at,
139
+ account_uid=validated.account_uid,
140
+ )
141
+
142
+ run_async_command(
143
+ ctx=cli_ctx,
144
+ command="publish",
145
+ func=_run,
146
+ account_uid=account_uid,
147
+ )
@@ -0,0 +1,185 @@
1
+ from __future__ import annotations
2
+
3
+ import typer
4
+
5
+ from rednote_cli.application.dto.input_models import NoteSearchInput, UserSearchInput
6
+ from rednote_cli.application.use_cases.note_search import execute_note_search
7
+ from rednote_cli.application.use_cases.user_search import execute_user_search
8
+ from rednote_cli.cli.options import CliContext
9
+ from rednote_cli.cli.runtime import run_async_command
10
+ from rednote_cli.cli.utils import all_option_params_are_default, load_json_input, pick_cli_or_input
11
+
12
+ app = typer.Typer(help="统一搜索入口(笔记/用户)", no_args_is_help=True)
13
+ note_app = typer.Typer(help="搜索笔记", no_args_is_help=False)
14
+ user_app = typer.Typer(help="搜索用户", no_args_is_help=False)
15
+
16
+
17
+ @note_app.callback(invoke_without_command=True)
18
+ def search_note(
19
+ ctx: typer.Context,
20
+ keyword: str | None = typer.Option(None, "--keyword", help="关键词(1..n);可由 --input 提供"),
21
+ size: int | None = typer.Option(20, "--size", help="返回数量(1..100)"),
22
+ sort_by: str | None = typer.Option(
23
+ None,
24
+ "--sort-by",
25
+ help="排序:comprehensive|latest|most_liked|most_commented|most_favorited(支持中文别名)",
26
+ ),
27
+ note_type: str | None = typer.Option(None, "--note-type", help="类型:all|video|image_text(支持中文别名)"),
28
+ publish_time: str | None = typer.Option(None, "--publish-time", help="发布时间:all|day|week|half_year(支持中文别名)"),
29
+ search_scope: str | None = typer.Option(None, "--search-scope", help="搜索范围:all|viewed|unviewed|following(支持中文别名)"),
30
+ location: str | None = typer.Option(None, "--location", help="位置:all|local|nearby(支持中文别名)"),
31
+ account: str | None = typer.Option(None, "--account", help="账号唯一标识(user_no)"),
32
+ input_file: str | None = typer.Option(
33
+ None,
34
+ "--input",
35
+ help="JSON 输入文件路径或 '-'(stdin),字段示例:keyword,size,sort_by,note_type,publish_time,search_scope,location,account_uid",
36
+ ),
37
+ ):
38
+ """搜索笔记列表。"""
39
+ if all_option_params_are_default(ctx=ctx):
40
+ typer.echo(ctx.get_help())
41
+ raise typer.Exit(code=0)
42
+
43
+ cli_ctx: CliContext = ctx.obj
44
+ payload = load_json_input(input_file)
45
+ account_uid = pick_cli_or_input(
46
+ ctx=ctx,
47
+ param_name="account",
48
+ cli_value=account,
49
+ payload=payload,
50
+ payload_key="account_uid",
51
+ )
52
+
53
+ async def _run():
54
+ validated = NoteSearchInput(
55
+ keyword=pick_cli_or_input(
56
+ ctx=ctx,
57
+ param_name="keyword",
58
+ cli_value=keyword,
59
+ payload=payload,
60
+ payload_key="keyword",
61
+ ),
62
+ size=pick_cli_or_input(
63
+ ctx=ctx,
64
+ param_name="size",
65
+ cli_value=size,
66
+ payload=payload,
67
+ payload_key="size",
68
+ ),
69
+ sort_by=pick_cli_or_input(
70
+ ctx=ctx,
71
+ param_name="sort_by",
72
+ cli_value=sort_by,
73
+ payload=payload,
74
+ payload_key="sort_by",
75
+ ),
76
+ note_type=pick_cli_or_input(
77
+ ctx=ctx,
78
+ param_name="note_type",
79
+ cli_value=note_type,
80
+ payload=payload,
81
+ payload_key="note_type",
82
+ ),
83
+ publish_time=pick_cli_or_input(
84
+ ctx=ctx,
85
+ param_name="publish_time",
86
+ cli_value=publish_time,
87
+ payload=payload,
88
+ payload_key="publish_time",
89
+ ),
90
+ search_scope=pick_cli_or_input(
91
+ ctx=ctx,
92
+ param_name="search_scope",
93
+ cli_value=search_scope,
94
+ payload=payload,
95
+ payload_key="search_scope",
96
+ ),
97
+ location=pick_cli_or_input(
98
+ ctx=ctx,
99
+ param_name="location",
100
+ cli_value=location,
101
+ payload=payload,
102
+ payload_key="location",
103
+ ),
104
+ account_uid=account_uid,
105
+ )
106
+ return await execute_note_search(
107
+ keyword=validated.keyword,
108
+ size=validated.size,
109
+ sort_by=validated.sort_by,
110
+ note_type=validated.note_type,
111
+ publish_time=validated.publish_time,
112
+ search_scope=validated.search_scope,
113
+ location=validated.location,
114
+ account_uid=validated.account_uid,
115
+ )
116
+
117
+ run_async_command(
118
+ ctx=cli_ctx,
119
+ command="note.search",
120
+ func=_run,
121
+ account_uid=account_uid,
122
+ )
123
+
124
+
125
+ @user_app.callback(invoke_without_command=True)
126
+ def search_user(
127
+ ctx: typer.Context,
128
+ keyword: str | None = typer.Option(None, "--keyword", help="关键词(1..n);可由 --input 提供"),
129
+ size: int | None = typer.Option(20, "--size", help="返回数量(1..100)"),
130
+ account: str | None = typer.Option(None, "--account", help="账号唯一标识(user_no)"),
131
+ input_file: str | None = typer.Option(
132
+ None,
133
+ "--input",
134
+ help="JSON 输入文件路径或 '-'(stdin),字段示例:keyword,size,account_uid",
135
+ ),
136
+ ):
137
+ """搜索用户列表。"""
138
+ if all_option_params_are_default(ctx=ctx):
139
+ typer.echo(ctx.get_help())
140
+ raise typer.Exit(code=0)
141
+
142
+ cli_ctx: CliContext = ctx.obj
143
+ payload = load_json_input(input_file)
144
+ account_uid = pick_cli_or_input(
145
+ ctx=ctx,
146
+ param_name="account",
147
+ cli_value=account,
148
+ payload=payload,
149
+ payload_key="account_uid",
150
+ )
151
+
152
+ async def _run():
153
+ validated = UserSearchInput(
154
+ keyword=pick_cli_or_input(
155
+ ctx=ctx,
156
+ param_name="keyword",
157
+ cli_value=keyword,
158
+ payload=payload,
159
+ payload_key="keyword",
160
+ ),
161
+ size=pick_cli_or_input(
162
+ ctx=ctx,
163
+ param_name="size",
164
+ cli_value=size,
165
+ payload=payload,
166
+ payload_key="size",
167
+ ),
168
+ account_uid=account_uid,
169
+ )
170
+ return await execute_user_search(
171
+ keyword=validated.keyword,
172
+ size=validated.size,
173
+ account_uid=validated.account_uid,
174
+ )
175
+
176
+ run_async_command(
177
+ ctx=cli_ctx,
178
+ command="user.search",
179
+ func=_run,
180
+ account_uid=account_uid,
181
+ )
182
+
183
+
184
+ app.add_typer(note_app, name="note")
185
+ app.add_typer(user_app, name="user")
@@ -0,0 +1,113 @@
1
+ from __future__ import annotations
2
+
3
+ import typer
4
+
5
+ from rednote_cli.application.dto.input_models import UserGetInput
6
+ from rednote_cli.application.use_cases.user_get import execute_user_get
7
+ from rednote_cli.application.use_cases.user_self import execute_user_self
8
+ from rednote_cli.cli.options import CliContext
9
+ from rednote_cli.cli.runtime import run_async_command
10
+ from rednote_cli.cli.utils import all_option_params_are_default, load_json_input, pick_cli_or_input
11
+ from rednote_cli.domain.errors import InvalidArgsError
12
+
13
+ app = typer.Typer(help="用户主页详情查询", no_args_is_help=False)
14
+
15
+
16
+ @app.callback(invoke_without_command=True)
17
+ def user_get(
18
+ ctx: typer.Context,
19
+ user_id: str | None = typer.Option(None, "--user-id", help="用户 ID;可由 --input 提供"),
20
+ xsec_token: str | None = typer.Option(None, "--xsec-token", help="xsec_token,可选"),
21
+ xsec_source: str = typer.Option("pc_feed", "--xsec-source", help="xsec 来源,默认 pc_feed"),
22
+ account: str | None = typer.Option(None, "--account", help="账号唯一标识(user_no)"),
23
+ input_file: str | None = typer.Option(
24
+ None,
25
+ "--input",
26
+ help="JSON 输入文件路径或 '-',字段示例:user_id,xsec_token,xsec_source,account_uid",
27
+ ),
28
+ ):
29
+ """
30
+ 获取单个用户主页详情。
31
+
32
+ 参数优先级:显式 CLI 参数 > --input JSON > 默认值。
33
+ """
34
+ if ctx.invoked_subcommand:
35
+ return
36
+ if all_option_params_are_default(ctx=ctx):
37
+ typer.echo(ctx.get_help())
38
+ raise typer.Exit(code=0)
39
+
40
+ cli_ctx: CliContext = ctx.obj
41
+ payload = load_json_input(input_file)
42
+ account_uid = pick_cli_or_input(
43
+ ctx=ctx,
44
+ param_name="account",
45
+ cli_value=account,
46
+ payload=payload,
47
+ payload_key="account_uid",
48
+ )
49
+
50
+ async def _run():
51
+ validated = UserGetInput(
52
+ user_id=pick_cli_or_input(
53
+ ctx=ctx,
54
+ param_name="user_id",
55
+ cli_value=user_id,
56
+ payload=payload,
57
+ payload_key="user_id",
58
+ ),
59
+ xsec_token=pick_cli_or_input(
60
+ ctx=ctx,
61
+ param_name="xsec_token",
62
+ cli_value=xsec_token,
63
+ payload=payload,
64
+ payload_key="xsec_token",
65
+ ),
66
+ xsec_source=pick_cli_or_input(
67
+ ctx=ctx,
68
+ param_name="xsec_source",
69
+ cli_value=xsec_source,
70
+ payload=payload,
71
+ payload_key="xsec_source",
72
+ ),
73
+ account_uid=account_uid,
74
+ )
75
+ return await execute_user_get(
76
+ user_id=validated.user_id,
77
+ xsec_token=validated.xsec_token,
78
+ xsec_source=validated.xsec_source,
79
+ account_uid=validated.account_uid,
80
+ )
81
+
82
+ run_async_command(
83
+ ctx=cli_ctx,
84
+ command="user.get",
85
+ func=_run,
86
+ account_uid=account_uid,
87
+ )
88
+
89
+
90
+ @app.command("self")
91
+ def user_self(
92
+ ctx: typer.Context,
93
+ account: str | None = typer.Option(None, "--account", help="账号唯一标识(user_no)"),
94
+ ):
95
+ """查询当前登录账号自己的用户信息。"""
96
+ if all_option_params_are_default(ctx=ctx):
97
+ typer.echo(ctx.get_help())
98
+ raise typer.Exit(code=0)
99
+
100
+ cli_ctx: CliContext = ctx.obj
101
+ account_uid = (account or "").strip()
102
+
103
+ async def _run():
104
+ if not account_uid:
105
+ raise InvalidArgsError("`--account` 为必填参数")
106
+ return await execute_user_self(account_uid=account_uid)
107
+
108
+ run_async_command(
109
+ ctx=cli_ctx,
110
+ command="user.self",
111
+ func=_run,
112
+ account_uid=account_uid,
113
+ )