sermo 0.3.2__tar.gz → 0.5.0__tar.gz
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.
- {sermo-0.3.2 → sermo-0.5.0}/PKG-INFO +39 -10
- {sermo-0.3.2 → sermo-0.5.0}/README.md +37 -8
- {sermo-0.3.2 → sermo-0.5.0}/pyproject.toml +2 -2
- {sermo-0.3.2 → sermo-0.5.0}/sermo.egg-info/PKG-INFO +39 -10
- {sermo-0.3.2 → sermo-0.5.0}/sermo.egg-info/SOURCES.txt +4 -1
- {sermo-0.3.2 → sermo-0.5.0}/sermo_cli/__init__.py +1 -1
- {sermo-0.3.2 → sermo-0.5.0}/sermo_cli/cli.py +169 -2
- sermo-0.5.0/sermo_cli/interactive.py +167 -0
- sermo-0.5.0/sermo_cli/tui.py +647 -0
- sermo-0.5.0/tests/test_interactive.py +25 -0
- sermo-0.5.0/tests/test_tui.py +76 -0
- sermo-0.3.2/sermo_cli/tui.py +0 -365
- {sermo-0.3.2 → sermo-0.5.0}/sermo.egg-info/dependency_links.txt +0 -0
- {sermo-0.3.2 → sermo-0.5.0}/sermo.egg-info/entry_points.txt +0 -0
- {sermo-0.3.2 → sermo-0.5.0}/sermo.egg-info/top_level.txt +0 -0
- {sermo-0.3.2 → sermo-0.5.0}/sermo_cli/__main__.py +0 -0
- {sermo-0.3.2 → sermo-0.5.0}/sermo_cli/api.py +0 -0
- {sermo-0.3.2 → sermo-0.5.0}/sermo_cli/formatting.py +0 -0
- {sermo-0.3.2 → sermo-0.5.0}/sermo_cli/store.py +0 -0
- {sermo-0.3.2 → sermo-0.5.0}/setup.cfg +0 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: sermo
|
|
3
|
-
Version: 0.
|
|
4
|
-
Summary: Command-line Sermo
|
|
3
|
+
Version: 0.5.0
|
|
4
|
+
Summary: Command-line client for Sermo 言浪
|
|
5
5
|
Author: Sermo
|
|
6
6
|
Project-URL: Homepage, https://sermo.jyonn.space
|
|
7
7
|
Keywords: sermo,chat,cli
|
|
@@ -18,16 +18,17 @@ Classifier: Topic :: Communications :: Chat
|
|
|
18
18
|
Requires-Python: >=3.10
|
|
19
19
|
Description-Content-Type: text/markdown
|
|
20
20
|
|
|
21
|
-
#
|
|
21
|
+
# Sermo 言浪 CLI
|
|
22
22
|
|
|
23
|
-
Sermo
|
|
23
|
+
Sermo 言浪的命令行客户端。它直接接入 `sermo-backend` API,适合在终端里快速查看会话、发送文字消息和检查好友信息。
|
|
24
24
|
|
|
25
25
|
当前版本覆盖日常终端使用:
|
|
26
26
|
|
|
27
27
|
- 支持进入空间并保存登录状态。
|
|
28
28
|
- 支持查看当前账户、空间成员、聊天列表、好友列表、好友申请。
|
|
29
29
|
- 支持查看聊天记录、发送文字消息、持续刷新新消息、监听全局新消息。
|
|
30
|
-
-
|
|
30
|
+
- 默认提供不清屏的局部窗口选择器,会话、好友、成员和申请列表只展示光标附近 5 项。
|
|
31
|
+
- 保留 `sermo tui` 全屏终端聊天界面,会话列表、消息流和输入框集中展示。
|
|
31
32
|
- 支持好友申请处理、好友邀请链接、私聊打开、群聊创建和成员管理。
|
|
32
33
|
- 支持账号资料、联系方式绑定、通知偏好、空间创建和管理员基础操作。
|
|
33
34
|
- 图片、视频、语音、文件等多媒体消息在终端中展示为 `[图片]`、`[视频]`、`[语音]`、`[文件]`;也支持上传发送图片、视频和语音文件。
|
|
@@ -92,6 +93,20 @@ sermo me
|
|
|
92
93
|
sermo chats
|
|
93
94
|
```
|
|
94
95
|
|
|
96
|
+
终端中会显示光标附近 5 个会话。使用 `↑/↓` 选择、`Enter` 打开、`Esc` 返回;也支持 `j/k` 和 `Ctrl+N/Ctrl+P`。
|
|
97
|
+
|
|
98
|
+
直接输出完整列表,适合管道和脚本:
|
|
99
|
+
|
|
100
|
+
```bash
|
|
101
|
+
sermo chats --list
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
调整局部窗口行数:
|
|
105
|
+
|
|
106
|
+
```bash
|
|
107
|
+
sermo chats --window 7
|
|
108
|
+
```
|
|
109
|
+
|
|
95
110
|
打开 TUI:
|
|
96
111
|
|
|
97
112
|
```bash
|
|
@@ -100,11 +115,14 @@ sermo tui
|
|
|
100
115
|
|
|
101
116
|
在 TUI 里:
|
|
102
117
|
|
|
103
|
-
-
|
|
104
|
-
-
|
|
105
|
-
- `
|
|
106
|
-
- `
|
|
107
|
-
-
|
|
118
|
+
- `Tab/Shift+Tab` 在会话、消息和输入框之间切换焦点。
|
|
119
|
+
- 会话焦点下使用 `↑/↓` 或 `j/k` 切换会话。
|
|
120
|
+
- 消息焦点下使用 `↑/↓` 滚动,`PageUp/PageDown` 翻页,滑到顶部自动加载历史。
|
|
121
|
+
- 查看历史时不会强制跳到底部;标题会提示新消息数量,`End` 回到最新消息。
|
|
122
|
+
- 输入框支持左右移动、`Home/End`、退格与 Delete,并按会话保存草稿。
|
|
123
|
+
- 发送消息会立即展示“发送中”;失败后保留消息,在消息焦点按 `r` 重试。
|
|
124
|
+
- `Enter` 发送,`F5` 手动刷新,`Ctrl+U` 清空输入。
|
|
125
|
+
- `Esc` 或 `F10` 退出。
|
|
108
126
|
|
|
109
127
|
打开聊天:
|
|
110
128
|
|
|
@@ -136,6 +154,17 @@ sermo chat 1 --follow
|
|
|
136
154
|
sermo friends
|
|
137
155
|
```
|
|
138
156
|
|
|
157
|
+
好友、空间成员、好友申请和群聊邀请使用同一套局部窗口交互:
|
|
158
|
+
|
|
159
|
+
```bash
|
|
160
|
+
sermo friends
|
|
161
|
+
sermo users
|
|
162
|
+
sermo requests
|
|
163
|
+
sermo group invites
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
这些命令也都支持 `--list` 和 `--window`。
|
|
167
|
+
|
|
139
168
|
查看好友申请:
|
|
140
169
|
|
|
141
170
|
```bash
|
|
@@ -1,13 +1,14 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Sermo 言浪 CLI
|
|
2
2
|
|
|
3
|
-
Sermo
|
|
3
|
+
Sermo 言浪的命令行客户端。它直接接入 `sermo-backend` API,适合在终端里快速查看会话、发送文字消息和检查好友信息。
|
|
4
4
|
|
|
5
5
|
当前版本覆盖日常终端使用:
|
|
6
6
|
|
|
7
7
|
- 支持进入空间并保存登录状态。
|
|
8
8
|
- 支持查看当前账户、空间成员、聊天列表、好友列表、好友申请。
|
|
9
9
|
- 支持查看聊天记录、发送文字消息、持续刷新新消息、监听全局新消息。
|
|
10
|
-
-
|
|
10
|
+
- 默认提供不清屏的局部窗口选择器,会话、好友、成员和申请列表只展示光标附近 5 项。
|
|
11
|
+
- 保留 `sermo tui` 全屏终端聊天界面,会话列表、消息流和输入框集中展示。
|
|
11
12
|
- 支持好友申请处理、好友邀请链接、私聊打开、群聊创建和成员管理。
|
|
12
13
|
- 支持账号资料、联系方式绑定、通知偏好、空间创建和管理员基础操作。
|
|
13
14
|
- 图片、视频、语音、文件等多媒体消息在终端中展示为 `[图片]`、`[视频]`、`[语音]`、`[文件]`;也支持上传发送图片、视频和语音文件。
|
|
@@ -72,6 +73,20 @@ sermo me
|
|
|
72
73
|
sermo chats
|
|
73
74
|
```
|
|
74
75
|
|
|
76
|
+
终端中会显示光标附近 5 个会话。使用 `↑/↓` 选择、`Enter` 打开、`Esc` 返回;也支持 `j/k` 和 `Ctrl+N/Ctrl+P`。
|
|
77
|
+
|
|
78
|
+
直接输出完整列表,适合管道和脚本:
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
sermo chats --list
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
调整局部窗口行数:
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
sermo chats --window 7
|
|
88
|
+
```
|
|
89
|
+
|
|
75
90
|
打开 TUI:
|
|
76
91
|
|
|
77
92
|
```bash
|
|
@@ -80,11 +95,14 @@ sermo tui
|
|
|
80
95
|
|
|
81
96
|
在 TUI 里:
|
|
82
97
|
|
|
83
|
-
-
|
|
84
|
-
-
|
|
85
|
-
- `
|
|
86
|
-
- `
|
|
87
|
-
-
|
|
98
|
+
- `Tab/Shift+Tab` 在会话、消息和输入框之间切换焦点。
|
|
99
|
+
- 会话焦点下使用 `↑/↓` 或 `j/k` 切换会话。
|
|
100
|
+
- 消息焦点下使用 `↑/↓` 滚动,`PageUp/PageDown` 翻页,滑到顶部自动加载历史。
|
|
101
|
+
- 查看历史时不会强制跳到底部;标题会提示新消息数量,`End` 回到最新消息。
|
|
102
|
+
- 输入框支持左右移动、`Home/End`、退格与 Delete,并按会话保存草稿。
|
|
103
|
+
- 发送消息会立即展示“发送中”;失败后保留消息,在消息焦点按 `r` 重试。
|
|
104
|
+
- `Enter` 发送,`F5` 手动刷新,`Ctrl+U` 清空输入。
|
|
105
|
+
- `Esc` 或 `F10` 退出。
|
|
88
106
|
|
|
89
107
|
打开聊天:
|
|
90
108
|
|
|
@@ -116,6 +134,17 @@ sermo chat 1 --follow
|
|
|
116
134
|
sermo friends
|
|
117
135
|
```
|
|
118
136
|
|
|
137
|
+
好友、空间成员、好友申请和群聊邀请使用同一套局部窗口交互:
|
|
138
|
+
|
|
139
|
+
```bash
|
|
140
|
+
sermo friends
|
|
141
|
+
sermo users
|
|
142
|
+
sermo requests
|
|
143
|
+
sermo group invites
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
这些命令也都支持 `--list` 和 `--window`。
|
|
147
|
+
|
|
119
148
|
查看好友申请:
|
|
120
149
|
|
|
121
150
|
```bash
|
|
@@ -4,8 +4,8 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "sermo"
|
|
7
|
-
version = "0.
|
|
8
|
-
description = "Command-line Sermo
|
|
7
|
+
version = "0.5.0"
|
|
8
|
+
description = "Command-line client for Sermo 言浪"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.10"
|
|
11
11
|
dependencies = []
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: sermo
|
|
3
|
-
Version: 0.
|
|
4
|
-
Summary: Command-line Sermo
|
|
3
|
+
Version: 0.5.0
|
|
4
|
+
Summary: Command-line client for Sermo 言浪
|
|
5
5
|
Author: Sermo
|
|
6
6
|
Project-URL: Homepage, https://sermo.jyonn.space
|
|
7
7
|
Keywords: sermo,chat,cli
|
|
@@ -18,16 +18,17 @@ Classifier: Topic :: Communications :: Chat
|
|
|
18
18
|
Requires-Python: >=3.10
|
|
19
19
|
Description-Content-Type: text/markdown
|
|
20
20
|
|
|
21
|
-
#
|
|
21
|
+
# Sermo 言浪 CLI
|
|
22
22
|
|
|
23
|
-
Sermo
|
|
23
|
+
Sermo 言浪的命令行客户端。它直接接入 `sermo-backend` API,适合在终端里快速查看会话、发送文字消息和检查好友信息。
|
|
24
24
|
|
|
25
25
|
当前版本覆盖日常终端使用:
|
|
26
26
|
|
|
27
27
|
- 支持进入空间并保存登录状态。
|
|
28
28
|
- 支持查看当前账户、空间成员、聊天列表、好友列表、好友申请。
|
|
29
29
|
- 支持查看聊天记录、发送文字消息、持续刷新新消息、监听全局新消息。
|
|
30
|
-
-
|
|
30
|
+
- 默认提供不清屏的局部窗口选择器,会话、好友、成员和申请列表只展示光标附近 5 项。
|
|
31
|
+
- 保留 `sermo tui` 全屏终端聊天界面,会话列表、消息流和输入框集中展示。
|
|
31
32
|
- 支持好友申请处理、好友邀请链接、私聊打开、群聊创建和成员管理。
|
|
32
33
|
- 支持账号资料、联系方式绑定、通知偏好、空间创建和管理员基础操作。
|
|
33
34
|
- 图片、视频、语音、文件等多媒体消息在终端中展示为 `[图片]`、`[视频]`、`[语音]`、`[文件]`;也支持上传发送图片、视频和语音文件。
|
|
@@ -92,6 +93,20 @@ sermo me
|
|
|
92
93
|
sermo chats
|
|
93
94
|
```
|
|
94
95
|
|
|
96
|
+
终端中会显示光标附近 5 个会话。使用 `↑/↓` 选择、`Enter` 打开、`Esc` 返回;也支持 `j/k` 和 `Ctrl+N/Ctrl+P`。
|
|
97
|
+
|
|
98
|
+
直接输出完整列表,适合管道和脚本:
|
|
99
|
+
|
|
100
|
+
```bash
|
|
101
|
+
sermo chats --list
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
调整局部窗口行数:
|
|
105
|
+
|
|
106
|
+
```bash
|
|
107
|
+
sermo chats --window 7
|
|
108
|
+
```
|
|
109
|
+
|
|
95
110
|
打开 TUI:
|
|
96
111
|
|
|
97
112
|
```bash
|
|
@@ -100,11 +115,14 @@ sermo tui
|
|
|
100
115
|
|
|
101
116
|
在 TUI 里:
|
|
102
117
|
|
|
103
|
-
-
|
|
104
|
-
-
|
|
105
|
-
- `
|
|
106
|
-
- `
|
|
107
|
-
-
|
|
118
|
+
- `Tab/Shift+Tab` 在会话、消息和输入框之间切换焦点。
|
|
119
|
+
- 会话焦点下使用 `↑/↓` 或 `j/k` 切换会话。
|
|
120
|
+
- 消息焦点下使用 `↑/↓` 滚动,`PageUp/PageDown` 翻页,滑到顶部自动加载历史。
|
|
121
|
+
- 查看历史时不会强制跳到底部;标题会提示新消息数量,`End` 回到最新消息。
|
|
122
|
+
- 输入框支持左右移动、`Home/End`、退格与 Delete,并按会话保存草稿。
|
|
123
|
+
- 发送消息会立即展示“发送中”;失败后保留消息,在消息焦点按 `r` 重试。
|
|
124
|
+
- `Enter` 发送,`F5` 手动刷新,`Ctrl+U` 清空输入。
|
|
125
|
+
- `Esc` 或 `F10` 退出。
|
|
108
126
|
|
|
109
127
|
打开聊天:
|
|
110
128
|
|
|
@@ -136,6 +154,17 @@ sermo chat 1 --follow
|
|
|
136
154
|
sermo friends
|
|
137
155
|
```
|
|
138
156
|
|
|
157
|
+
好友、空间成员、好友申请和群聊邀请使用同一套局部窗口交互:
|
|
158
|
+
|
|
159
|
+
```bash
|
|
160
|
+
sermo friends
|
|
161
|
+
sermo users
|
|
162
|
+
sermo requests
|
|
163
|
+
sermo group invites
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
这些命令也都支持 `--list` 和 `--window`。
|
|
167
|
+
|
|
139
168
|
查看好友申请:
|
|
140
169
|
|
|
141
170
|
```bash
|
|
@@ -10,6 +10,7 @@ from typing import Any
|
|
|
10
10
|
|
|
11
11
|
from .api import ApiClient, SermoApiError
|
|
12
12
|
from .formatting import chat_title, format_time, message_text, print_header, style, user_name
|
|
13
|
+
from .interactive import choose
|
|
13
14
|
from .store import SessionStore
|
|
14
15
|
|
|
15
16
|
|
|
@@ -151,6 +152,30 @@ def command_chats(args: argparse.Namespace) -> int:
|
|
|
151
152
|
if not chats:
|
|
152
153
|
print("暂无会话。")
|
|
153
154
|
return 0
|
|
155
|
+
if sys.stdin.isatty() and not args.list:
|
|
156
|
+
def render_chat_choice(chat: dict[str, Any]) -> str:
|
|
157
|
+
unread = f" · {chat.get('unread_count')} 条新消息" if chat.get("unread_count") else ""
|
|
158
|
+
preview = message_text(chat.get("last_message") or {}) if chat.get("last_message") else "暂无消息"
|
|
159
|
+
return f"{chat_title(chat, self_user_id)}{unread} {preview}"
|
|
160
|
+
|
|
161
|
+
selected = choose(
|
|
162
|
+
chats,
|
|
163
|
+
render_chat_choice,
|
|
164
|
+
title="选择会话",
|
|
165
|
+
window_size=args.window,
|
|
166
|
+
)
|
|
167
|
+
if selected is None:
|
|
168
|
+
return 0
|
|
169
|
+
print(style.muted(f"进入 {chat_title(selected, self_user_id)}"))
|
|
170
|
+
return command_chat(argparse.Namespace(**{
|
|
171
|
+
**vars(args),
|
|
172
|
+
"chat_id": int(selected["chat_id"]),
|
|
173
|
+
"limit": args.limit,
|
|
174
|
+
"before": None,
|
|
175
|
+
"follow": False,
|
|
176
|
+
"interval": 5.0,
|
|
177
|
+
"no_mark_read": False,
|
|
178
|
+
}))
|
|
154
179
|
print_header("聊天")
|
|
155
180
|
for chat in chats:
|
|
156
181
|
last = chat.get("last_message")
|
|
@@ -268,11 +293,43 @@ def command_send(args: argparse.Namespace) -> int:
|
|
|
268
293
|
|
|
269
294
|
def command_friends(args: argparse.Namespace) -> int:
|
|
270
295
|
client = build_client(args)
|
|
271
|
-
require_session(client)
|
|
296
|
+
session = require_session(client)
|
|
272
297
|
friends = client.friends()
|
|
273
298
|
if not friends:
|
|
274
299
|
print("暂无好友。")
|
|
275
300
|
return 0
|
|
301
|
+
if sys.stdin.isatty() and not args.list:
|
|
302
|
+
selected = choose(
|
|
303
|
+
friends,
|
|
304
|
+
lambda friend: f"{user_name(friend)} {'在线' if friend.get('is_alive') else '离线'}",
|
|
305
|
+
title="选择好友",
|
|
306
|
+
window_size=args.window,
|
|
307
|
+
)
|
|
308
|
+
if selected is None:
|
|
309
|
+
return 0
|
|
310
|
+
action = choose(
|
|
311
|
+
("chat", "profile", "back"),
|
|
312
|
+
lambda item: {"chat": "发消息", "profile": "查看资料", "back": "返回"}[item],
|
|
313
|
+
title=user_name(selected),
|
|
314
|
+
window_size=3,
|
|
315
|
+
)
|
|
316
|
+
if action == "chat":
|
|
317
|
+
chat = client.direct_chat(int(selected["user_id"]))
|
|
318
|
+
return command_chat(argparse.Namespace(**{
|
|
319
|
+
**vars(args),
|
|
320
|
+
"chat_id": int(chat["chat_id"]),
|
|
321
|
+
"limit": 30,
|
|
322
|
+
"before": None,
|
|
323
|
+
"follow": False,
|
|
324
|
+
"interval": 5.0,
|
|
325
|
+
"no_mark_read": False,
|
|
326
|
+
}))
|
|
327
|
+
if action == "profile":
|
|
328
|
+
print_header(user_name(selected), "好友")
|
|
329
|
+
print(f"用户 ID: {selected.get('user_id')}")
|
|
330
|
+
print(f"状态: {'在线' if selected.get('is_alive') else '离线'}")
|
|
331
|
+
print(f"认证: {'已认证' if selected.get('verified') else '未认证'}")
|
|
332
|
+
return 0
|
|
276
333
|
print_header("好友")
|
|
277
334
|
for friend in friends:
|
|
278
335
|
online = style.green("在线") if friend.get("is_alive") else style.muted("离线")
|
|
@@ -286,6 +343,44 @@ def command_requests(args: argparse.Namespace) -> int:
|
|
|
286
343
|
payload = client.friend_requests()
|
|
287
344
|
incoming = payload.get("incoming") or []
|
|
288
345
|
outgoing = payload.get("outgoing") or []
|
|
346
|
+
if sys.stdin.isatty() and not args.list and (incoming or outgoing):
|
|
347
|
+
rows = [("incoming", item) for item in incoming] + [("outgoing", item) for item in outgoing]
|
|
348
|
+
selected = choose(
|
|
349
|
+
rows,
|
|
350
|
+
lambda row: (
|
|
351
|
+
f"{'收到' if row[0] == 'incoming' else '发出'} · "
|
|
352
|
+
f"{user_name(row[1].get('from_user') if row[0] == 'incoming' else row[1].get('to_user'))}"
|
|
353
|
+
),
|
|
354
|
+
title="好友申请",
|
|
355
|
+
window_size=args.window,
|
|
356
|
+
)
|
|
357
|
+
if selected is None:
|
|
358
|
+
return 0
|
|
359
|
+
direction, item = selected
|
|
360
|
+
user = item.get("from_user") if direction == "incoming" else item.get("to_user")
|
|
361
|
+
user_id = int((user or {}).get("user_id"))
|
|
362
|
+
actions = ("accept", "ignore", "back") if direction == "incoming" else ("withdraw", "back")
|
|
363
|
+
action = choose(
|
|
364
|
+
actions,
|
|
365
|
+
lambda value: {
|
|
366
|
+
"accept": "接受申请",
|
|
367
|
+
"ignore": "忽略申请",
|
|
368
|
+
"withdraw": "撤回申请",
|
|
369
|
+
"back": "返回",
|
|
370
|
+
}[value],
|
|
371
|
+
title=user_name(user),
|
|
372
|
+
window_size=3,
|
|
373
|
+
)
|
|
374
|
+
if action == "accept":
|
|
375
|
+
client.respond_friend_request(user_id, True)
|
|
376
|
+
print("已接受好友申请。")
|
|
377
|
+
elif action == "ignore":
|
|
378
|
+
client.respond_friend_request(user_id, False)
|
|
379
|
+
print("已忽略好友申请。")
|
|
380
|
+
elif action == "withdraw":
|
|
381
|
+
client.remove_friend(user_id)
|
|
382
|
+
print("已撤回好友申请。")
|
|
383
|
+
return 0
|
|
289
384
|
print_header("好友申请")
|
|
290
385
|
print(style.bold("收到的"))
|
|
291
386
|
if incoming:
|
|
@@ -309,6 +404,46 @@ def command_users(args: argparse.Namespace) -> int:
|
|
|
309
404
|
if not users:
|
|
310
405
|
print("没有找到用户。")
|
|
311
406
|
return 0
|
|
407
|
+
if sys.stdin.isatty() and not args.list:
|
|
408
|
+
selected = choose(
|
|
409
|
+
users,
|
|
410
|
+
lambda user: f"{user_name(user)} {'在线' if user.get('is_alive') else '离线'} {'已认证' if user.get('verified') else '未认证'}",
|
|
411
|
+
title="选择空间成员",
|
|
412
|
+
window_size=args.window,
|
|
413
|
+
)
|
|
414
|
+
if selected is None:
|
|
415
|
+
return 0
|
|
416
|
+
action = choose(
|
|
417
|
+
("chat", "friend", "profile", "back"),
|
|
418
|
+
lambda value: {
|
|
419
|
+
"chat": "发起私聊",
|
|
420
|
+
"friend": "发送好友申请",
|
|
421
|
+
"profile": "查看资料",
|
|
422
|
+
"back": "返回",
|
|
423
|
+
}[value],
|
|
424
|
+
title=user_name(selected),
|
|
425
|
+
window_size=4,
|
|
426
|
+
)
|
|
427
|
+
if action == "chat":
|
|
428
|
+
chat = client.direct_chat(int(selected["user_id"]))
|
|
429
|
+
return command_chat(argparse.Namespace(**{
|
|
430
|
+
**vars(args),
|
|
431
|
+
"chat_id": int(chat["chat_id"]),
|
|
432
|
+
"limit": 30,
|
|
433
|
+
"before": None,
|
|
434
|
+
"follow": False,
|
|
435
|
+
"interval": 5.0,
|
|
436
|
+
"no_mark_read": False,
|
|
437
|
+
}))
|
|
438
|
+
if action == "friend":
|
|
439
|
+
client.create_friend_request(int(selected["user_id"]))
|
|
440
|
+
print("好友申请已发送。")
|
|
441
|
+
elif action == "profile":
|
|
442
|
+
print_header(user_name(selected), "空间成员")
|
|
443
|
+
print(f"用户 ID: {selected.get('user_id')}")
|
|
444
|
+
print(f"状态: {'在线' if selected.get('is_alive') else '离线'}")
|
|
445
|
+
print(f"认证: {'已认证' if selected.get('verified') else '未认证'}")
|
|
446
|
+
return 0
|
|
312
447
|
print_header("空间成员")
|
|
313
448
|
for user in users:
|
|
314
449
|
online = style.green("在线") if user.get("is_alive") else style.muted("离线")
|
|
@@ -486,6 +621,27 @@ def command_group_invites(args: argparse.Namespace) -> int:
|
|
|
486
621
|
if not invites:
|
|
487
622
|
print("暂无群聊邀请。")
|
|
488
623
|
return 0
|
|
624
|
+
if sys.stdin.isatty() and not args.list:
|
|
625
|
+
selected = choose(
|
|
626
|
+
invites,
|
|
627
|
+
lambda item: chat_title(item.get("chat") if isinstance(item, dict) and item.get("chat") else item),
|
|
628
|
+
title="群聊邀请",
|
|
629
|
+
window_size=args.window,
|
|
630
|
+
)
|
|
631
|
+
if selected is None:
|
|
632
|
+
return 0
|
|
633
|
+
chat = selected.get("chat") if isinstance(selected, dict) else None
|
|
634
|
+
chat_id = selected.get("chat_id") or (chat or {}).get("chat_id")
|
|
635
|
+
action = choose(
|
|
636
|
+
("accept", "reject", "back"),
|
|
637
|
+
lambda value: {"accept": "接受邀请", "reject": "拒绝邀请", "back": "返回"}[value],
|
|
638
|
+
title=chat_title(chat or selected),
|
|
639
|
+
window_size=3,
|
|
640
|
+
)
|
|
641
|
+
if action in {"accept", "reject"}:
|
|
642
|
+
client.respond_group_invite(int(chat_id), action == "accept")
|
|
643
|
+
print("已接受群聊邀请。" if action == "accept" else "已拒绝群聊邀请。")
|
|
644
|
+
return 0
|
|
489
645
|
print_header("群聊邀请")
|
|
490
646
|
for item in invites:
|
|
491
647
|
chat = item.get("chat") if isinstance(item, dict) else None
|
|
@@ -770,7 +926,7 @@ def command_admin_logout(args: argparse.Namespace) -> int:
|
|
|
770
926
|
|
|
771
927
|
|
|
772
928
|
def build_parser() -> argparse.ArgumentParser:
|
|
773
|
-
parser = argparse.ArgumentParser(prog="sermo", description="Sermo
|
|
929
|
+
parser = argparse.ArgumentParser(prog="sermo", description="Sermo 言浪命令行客户端")
|
|
774
930
|
parser.add_argument("--api", help="Sermo API 地址,默认读取 SERMO_API_BASE 或 https://api.sermo.jyonn.space")
|
|
775
931
|
subparsers = parser.add_subparsers(dest="command", required=True)
|
|
776
932
|
|
|
@@ -788,6 +944,9 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
788
944
|
me.set_defaults(func=command_me)
|
|
789
945
|
|
|
790
946
|
chats = subparsers.add_parser("chats", help="查看聊天列表")
|
|
947
|
+
chats.add_argument("--list", action="store_true", help="直接输出完整列表,不进入选择器")
|
|
948
|
+
chats.add_argument("--window", type=int, default=5, help="选择器可见行数")
|
|
949
|
+
chats.add_argument("-n", "--limit", type=int, default=30, help="进入会话后加载的消息数")
|
|
791
950
|
chats.set_defaults(func=command_chats)
|
|
792
951
|
|
|
793
952
|
chat = subparsers.add_parser("chat", help="打开聊天并发送文字")
|
|
@@ -810,9 +969,13 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
810
969
|
send.set_defaults(func=command_send)
|
|
811
970
|
|
|
812
971
|
friends = subparsers.add_parser("friends", help="查看好友")
|
|
972
|
+
friends.add_argument("--list", action="store_true", help="直接输出完整列表")
|
|
973
|
+
friends.add_argument("--window", type=int, default=5, help="选择器可见行数")
|
|
813
974
|
friends.set_defaults(func=command_friends)
|
|
814
975
|
|
|
815
976
|
requests = subparsers.add_parser("requests", help="查看好友申请")
|
|
977
|
+
requests.add_argument("--list", action="store_true", help="直接输出完整列表")
|
|
978
|
+
requests.add_argument("--window", type=int, default=5, help="选择器可见行数")
|
|
816
979
|
requests.set_defaults(func=command_requests)
|
|
817
980
|
|
|
818
981
|
users = subparsers.add_parser("users", help="搜索/查看空间成员")
|
|
@@ -820,6 +983,8 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
820
983
|
users.add_argument("--online", action="store_true", help="只看在线成员")
|
|
821
984
|
users.add_argument("--limit", type=int, default=50)
|
|
822
985
|
users.add_argument("--offset", type=int, default=0)
|
|
986
|
+
users.add_argument("--list", action="store_true", help="直接输出完整列表")
|
|
987
|
+
users.add_argument("--window", type=int, default=5, help="选择器可见行数")
|
|
823
988
|
users.set_defaults(func=command_users)
|
|
824
989
|
|
|
825
990
|
direct = subparsers.add_parser("direct", help="打开一个私聊")
|
|
@@ -892,6 +1057,8 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
892
1057
|
group_delete.add_argument("-y", "--yes", action="store_true")
|
|
893
1058
|
group_delete.set_defaults(func=command_group_delete)
|
|
894
1059
|
group_invites = group_sub.add_parser("invites", help="查看群聊邀请")
|
|
1060
|
+
group_invites.add_argument("--list", action="store_true", help="直接输出完整列表")
|
|
1061
|
+
group_invites.add_argument("--window", type=int, default=5, help="选择器可见行数")
|
|
895
1062
|
group_invites.set_defaults(func=command_group_invites)
|
|
896
1063
|
group_accept = group_sub.add_parser("accept", help="接受群聊邀请")
|
|
897
1064
|
group_accept.add_argument("chat_id", type=int)
|