chattool 5.2.0__tar.gz → 5.3.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.
Files changed (79) hide show
  1. {chattool-5.2.0/src/chattool.egg-info → chattool-5.3.0}/PKG-INFO +84 -107
  2. chattool-5.3.0/README.md +154 -0
  3. {chattool-5.2.0 → chattool-5.3.0}/pyproject.toml +4 -1
  4. {chattool-5.2.0 → chattool-5.3.0}/src/chattool/__init__.py +7 -1
  5. {chattool-5.2.0 → chattool-5.3.0}/src/chattool/cli/chatenv.py +98 -54
  6. {chattool-5.2.0 → chattool-5.3.0}/src/chattool/cli/client/__init__.py +2 -0
  7. chattool-5.3.0/src/chattool/cli/client/lark.py +387 -0
  8. {chattool-5.2.0 → chattool-5.3.0}/src/chattool/cli/main.py +6 -2
  9. {chattool-5.2.0 → chattool-5.3.0}/src/chattool/cli/service/__init__.py +2 -0
  10. chattool-5.3.0/src/chattool/cli/service/lark_serve.py +177 -0
  11. chattool-5.3.0/src/chattool/cli/test_cmd.py +28 -0
  12. {chattool-5.2.0 → chattool-5.3.0}/src/chattool/config/__init__.py +4 -1
  13. {chattool-5.2.0 → chattool-5.3.0}/src/chattool/config/elements.py +14 -0
  14. chattool-5.3.0/src/chattool/config/main.py +135 -0
  15. {chattool-5.2.0 → chattool-5.3.0}/src/chattool/tools/__init__.py +4 -0
  16. chattool-5.3.0/src/chattool/tools/lark/__init__.py +5 -0
  17. chattool-5.3.0/src/chattool/tools/lark/bot.py +642 -0
  18. chattool-5.3.0/src/chattool/tools/lark/context.py +128 -0
  19. chattool-5.3.0/src/chattool/tools/lark/elements.py +119 -0
  20. chattool-5.3.0/src/chattool/tools/lark/session.py +116 -0
  21. {chattool-5.2.0 → chattool-5.3.0/src/chattool.egg-info}/PKG-INFO +84 -107
  22. {chattool-5.2.0 → chattool-5.3.0}/src/chattool.egg-info/SOURCES.txt +7 -0
  23. {chattool-5.2.0 → chattool-5.3.0}/src/chattool.egg-info/requires.txt +2 -0
  24. chattool-5.2.0/README.md +0 -179
  25. chattool-5.2.0/src/chattool/cli/test_cmd.py +0 -76
  26. chattool-5.2.0/src/chattool/config/main.py +0 -45
  27. {chattool-5.2.0 → chattool-5.3.0}/LICENSE +0 -0
  28. {chattool-5.2.0 → chattool-5.3.0}/setup.cfg +0 -0
  29. {chattool-5.2.0 → chattool-5.3.0}/src/chattool/_all.py +0 -0
  30. {chattool-5.2.0 → chattool-5.3.0}/src/chattool/application/kb/__init__.py +0 -0
  31. {chattool-5.2.0 → chattool-5.3.0}/src/chattool/application/kb/cli.py +0 -0
  32. {chattool-5.2.0 → chattool-5.3.0}/src/chattool/application/kb/ingest.py +0 -0
  33. {chattool-5.2.0 → chattool-5.3.0}/src/chattool/application/kb/manager.py +0 -0
  34. {chattool-5.2.0 → chattool-5.3.0}/src/chattool/application/kb/storage.py +0 -0
  35. {chattool-5.2.0 → chattool-5.3.0}/src/chattool/cli/__init__.py +0 -0
  36. {chattool-5.2.0 → chattool-5.3.0}/src/chattool/cli/client/cert_client.py +0 -0
  37. {chattool-5.2.0 → chattool-5.3.0}/src/chattool/cli/client/cert_updater.py +0 -0
  38. {chattool-5.2.0 → chattool-5.3.0}/src/chattool/cli/client/dns_updater.py +0 -0
  39. {chattool-5.2.0 → chattool-5.3.0}/src/chattool/cli/client/mcp.py +0 -0
  40. {chattool-5.2.0 → chattool-5.3.0}/src/chattool/cli/client/network.py +0 -0
  41. {chattool-5.2.0 → chattool-5.3.0}/src/chattool/cli/service/capture.py +0 -0
  42. {chattool-5.2.0 → chattool-5.3.0}/src/chattool/cli/service/cert_server.py +0 -0
  43. {chattool-5.2.0 → chattool-5.3.0}/src/chattool/const.py +0 -0
  44. {chattool-5.2.0 → chattool-5.3.0}/src/chattool/llm/__init__.py +0 -0
  45. {chattool-5.2.0 → chattool-5.3.0}/src/chattool/llm/chattype.py +0 -0
  46. {chattool-5.2.0 → chattool-5.3.0}/src/chattool/llm/response.py +0 -0
  47. {chattool-5.2.0 → chattool-5.3.0}/src/chattool/mcp/__init__.py +0 -0
  48. {chattool-5.2.0 → chattool-5.3.0}/src/chattool/mcp/dns.py +0 -0
  49. {chattool-5.2.0 → chattool-5.3.0}/src/chattool/mcp/network.py +0 -0
  50. {chattool-5.2.0 → chattool-5.3.0}/src/chattool/mcp/server.py +0 -0
  51. {chattool-5.2.0 → chattool-5.3.0}/src/chattool/mcp/zulip.py +0 -0
  52. {chattool-5.2.0 → chattool-5.3.0}/src/chattool/tools/cert/__init__.py +0 -0
  53. {chattool-5.2.0 → chattool-5.3.0}/src/chattool/tools/cert/acme_dns_tiny.py +0 -0
  54. {chattool-5.2.0 → chattool-5.3.0}/src/chattool/tools/cert/cert_server.py +0 -0
  55. {chattool-5.2.0 → chattool-5.3.0}/src/chattool/tools/cert/cert_updater.py +0 -0
  56. {chattool-5.2.0 → chattool-5.3.0}/src/chattool/tools/dns/__init__.py +0 -0
  57. {chattool-5.2.0 → chattool-5.3.0}/src/chattool/tools/dns/aliyun.py +0 -0
  58. {chattool-5.2.0 → chattool-5.3.0}/src/chattool/tools/dns/base.py +0 -0
  59. {chattool-5.2.0 → chattool-5.3.0}/src/chattool/tools/dns/ip_updater.py +0 -0
  60. {chattool-5.2.0 → chattool-5.3.0}/src/chattool/tools/dns/tencent.py +0 -0
  61. {chattool-5.2.0 → chattool-5.3.0}/src/chattool/tools/dns/utils.py +0 -0
  62. {chattool-5.2.0 → chattool-5.3.0}/src/chattool/tools/githubclient.py +0 -0
  63. {chattool-5.2.0 → chattool-5.3.0}/src/chattool/tools/interact.py +0 -0
  64. {chattool-5.2.0 → chattool-5.3.0}/src/chattool/tools/network/__init__.py +0 -0
  65. {chattool-5.2.0 → chattool-5.3.0}/src/chattool/tools/network/scanner.py +0 -0
  66. {chattool-5.2.0 → chattool-5.3.0}/src/chattool/tools/zulip/__init__.py +0 -0
  67. {chattool-5.2.0 → chattool-5.3.0}/src/chattool/tools/zulip/client.py +0 -0
  68. {chattool-5.2.0 → chattool-5.3.0}/src/chattool/tools/zulip/legacy.py +0 -0
  69. {chattool-5.2.0 → chattool-5.3.0}/src/chattool/utils/__init__.py +0 -0
  70. {chattool-5.2.0 → chattool-5.3.0}/src/chattool/utils/basic.py +0 -0
  71. {chattool-5.2.0 → chattool-5.3.0}/src/chattool/utils/custom_logger.py +0 -0
  72. {chattool-5.2.0 → chattool-5.3.0}/src/chattool/utils/fastobj.py +0 -0
  73. {chattool-5.2.0 → chattool-5.3.0}/src/chattool/utils/httpclient.py +0 -0
  74. {chattool-5.2.0 → chattool-5.3.0}/src/chattool/utils/urltool.py +0 -0
  75. {chattool-5.2.0 → chattool-5.3.0}/src/chattool.egg-info/dependency_links.txt +0 -0
  76. {chattool-5.2.0 → chattool-5.3.0}/src/chattool.egg-info/entry_points.txt +0 -0
  77. {chattool-5.2.0 → chattool-5.3.0}/src/chattool.egg-info/top_level.txt +0 -0
  78. {chattool-5.2.0 → chattool-5.3.0}/tests/test_chatenv.py +0 -0
  79. {chattool-5.2.0 → chattool-5.3.0}/tests/test_import.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: chattool
3
- Version: 5.2.0
3
+ Version: 5.3.0
4
4
  Summary: Toolkit for Chat API
5
5
  Author-email: Rex Wang <1073853456@qq.com>
6
6
  License: MIT license
@@ -40,6 +40,8 @@ Requires-Dist: alibabacloud_alidns20150109>=3.5.10; extra == "tools"
40
40
  Requires-Dist: alibabacloud_tea_openapi>=0.3.0; extra == "tools"
41
41
  Requires-Dist: tencentcloud-sdk-python; extra == "tools"
42
42
  Requires-Dist: netifaces; extra == "tools"
43
+ Requires-Dist: lark-oapi==1.5.3; extra == "tools"
44
+ Requires-Dist: flask; extra == "tools"
43
45
  Provides-Extra: tests
44
46
  Requires-Dist: coverage; extra == "tests"
45
47
  Requires-Dist: pytest>=3; extra == "tests"
@@ -81,161 +83,136 @@ Dynamic: license-file
81
83
  [English](README_en.md) | [简体中文](README.md)
82
84
  </div>
83
85
 
84
- 基于 OpenAI API `Chat` 对象,支持多轮对话以及异步处理数据等。
86
+ 集成 LLM 对话、飞书机器人、DNS 管理、SSL 证书等工具的 Python 开发套件。
85
87
 
86
- ## 安装方法
88
+ ## 安装
87
89
 
88
90
  ```bash
89
91
  pip install chattool --upgrade
90
92
  ```
91
93
 
92
- ## 使用方法
94
+ ## 功能概览
93
95
 
94
- ### 环境变量配置
96
+ ### 1. 环境变量管理 (`chatenv`)
95
97
 
96
- ChatTool 使用集中式配置管理,支持`.env`文件、环境变量和默认值。
98
+ 集中式配置管理,密码类字段自动脱敏显示,交互模式隐藏敏感输入。
97
99
 
98
- 1. **CLI 管理配置**(推荐):
99
- ```bash
100
- # 交互式初始化(引导设置各项配置)
101
- chatenv init -i
102
-
103
- # 设置配置项
104
- chatenv set OPENAI_API_KEY=sk-xxx
105
-
106
- # 查看配置
107
- chatenv cat
108
- ```
100
+ ```bash
101
+ # 交互式初始化(敏感字段自动隐藏输入)
102
+ chatenv init -i
103
+
104
+ # 仅初始化指定服务配置
105
+ chatenv init -i -t openai
106
+ chatenv init -i -t feishu
109
107
 
110
- 2. **手动配置**:
111
- 你可以手动创建 `.env` 文件或设置环境变量。
108
+ # 查看全部配置(敏感值自动打码)
109
+ chatenv cat
112
110
 
113
- **OpenAI 配置**
114
- ```bash
115
- export OPENAI_API_KEY="sk-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
116
- export OPENAI_API_BASE="https://api.example.com/v1"
117
- export OPENAI_API_MODEL="gpt-3.5-turbo"
118
- ```
111
+ # 按类型过滤查看
112
+ chatenv cat -t feishu
119
113
 
120
- **阿里云 DNS 配置**
121
- ```bash
122
- export ALIBABA_CLOUD_ACCESS_KEY_ID="your-access-key-id"
123
- export ALIBABA_CLOUD_ACCESS_KEY_SECRET="your-access-key-secret"
124
- export ALIBABA_CLOUD_REGION_ID="cn-hangzhou"
125
- ```
114
+ # 设置 / 查看单项
115
+ chatenv set OPENAI_API_KEY=sk-xxx
116
+ chatenv get OPENAI_API_KEY
126
117
 
127
- **腾讯云 DNS 配置**
128
- ```bash
129
- export TENCENT_SECRET_ID="your-secret-id"
130
- export TENCENT_SECRET_KEY="your-secret-key"
131
- export TENCENT_REGION_ID="ap-guangzhou"
132
- ```
118
+ # 多配置 profile 管理
119
+ chatenv save work && chatenv use work
120
+ ```
133
121
 
134
- ### Chat 对象使用
122
+ ### 2. Chat 对话 (`chattool.Chat`)
135
123
 
136
- 示例1,多轮对话:
124
+ 基于 OpenAI API 的对话对象,支持多轮对话、批量处理、异步并发和流式输出。
137
125
 
138
126
  ```python
139
- # 初次对话
127
+ from chattool import Chat
128
+
129
+ # 多轮对话
140
130
  chat = Chat("Hello!")
141
131
  resp = chat.get_response()
142
-
143
- # 继续对话
144
132
  chat.user("How are you?")
145
- next_resp = chat.get_response()
133
+ chat.get_response()
134
+ chat.save("chat.json", mode="w")
146
135
 
147
- # 人为添加返回内容
148
- chat.user("What's your name?")
149
- chat.assistant("My name is GPT-3.5.")
150
-
151
- # 保存对话内容
152
- chat.save("chat.json", mode="w") # 默认为 "a"
153
-
154
- # 打印对话历史
155
- chat.print_log()
136
+ # 异步并发
137
+ import asyncio
138
+ base = Chat().system("你是一个有用的助手")
139
+ tasks = [base.copy().user(f"主题 {i}").async_get_response() for i in range(5)]
140
+ responses = asyncio.run(asyncio.gather(*tasks))
141
+
142
+ # 流式输出
143
+ async for chunk in Chat().user("写一首诗").async_get_response_stream():
144
+ if chunk.delta_content:
145
+ print(chunk.delta_content, end="", flush=True)
156
146
  ```
157
147
 
158
- 示例2,批量处理数据(串行),并使用缓存文件 `chat.jsonl`:
159
-
160
- ```python
161
- # 串行处理(按需保存)
162
- msgs = ["1", "2", "3", "4", "5", "6", "7", "8", "9"]
163
- results = []
164
- for m in msgs:
165
- chat = Chat()
166
- chat.system("你是一个熟练的数字翻译家。")
167
- resp = chat.user(f"请将该数字翻译为罗马数字:{m}").get_response()
168
- results.append(resp.content)
169
- chat.save("chat.jsonl", mode="a")
170
- ```
148
+ ### 3. 飞书/Lark 机器人 (`chattool lark`)
171
149
 
172
- 示例3,异步并发与流式输出:
150
+ 一行命令发消息、启动 Echo/AI 机器人,支持文本、图片、文件、富文本等消息类型。
173
151
 
174
- ```python
175
- import asyncio
176
- from chattool import Chat
152
+ ```bash
153
+ # 发送消息
154
+ chattool lark send -r USER_ID -m "Hello"
155
+ chattool lark send -r USER_ID --image photo.png
156
+ chattool lark send -r USER_ID --file report.pdf
177
157
 
178
- async def run():
179
- # 并发问答
180
- base = Chat().system("你是一个有用的助手")
181
- tasks = [base.copy().user(f"请解释:主题 {i}").async_get_response() for i in range(2)]
182
- responses = await asyncio.gather(*tasks)
183
- for r in responses:
184
- print(r.content)
185
-
186
- # 流式输出
187
- print("流式: ", end="")
188
- async for chunk in Chat().user("写一首关于春天的短诗").async_get_response_stream():
189
- if chunk.delta_content:
190
- print(chunk.delta_content, end="", flush=True)
191
- print()
192
-
193
- asyncio.run(run())
194
- ```
158
+ # 启动 Echo 机器人(WebSocket)
159
+ chattool serve lark echo
195
160
 
196
- ### DNS 工具箱
161
+ # 启动 AI 对话机器人
162
+ chattool serve lark ai --model gpt-4o
197
163
 
198
- ChatTool 提供了统一的 DNS 管理接口,支持阿里云和腾讯云。
164
+ # 查看机器人信息和权限
165
+ chattool lark info
166
+ chattool lark scopes
167
+ ```
199
168
 
200
169
  ```python
201
- from chattool.tools.dns import create_dns_client
170
+ from chattool.tools.lark import LarkBot
202
171
 
203
- # 创建阿里云客户端
204
- aliyun = create_dns_client("aliyun")
205
- aliyun.add_domain_record("example.com", "www", "A", "1.1.1.1")
172
+ bot = LarkBot()
173
+ bot.send_text("ou_xxx", "open_id", "Hello!")
174
+ bot.send_image_file("ou_xxx", "open_id", "photo.png")
206
175
 
207
- # 创建腾讯云客户端
208
- tencent = create_dns_client("tencent")
209
- tencent.add_domain_record("example.com", "www", "A", "1.1.1.1")
176
+ @bot.on_message
177
+ def handle(ctx):
178
+ ctx.reply_text(f"收到: {ctx.text}")
179
+
180
+ bot.start()
210
181
  ```
211
182
 
212
- **命令行工具 (CLI)**
183
+ ### 4. DNS 管理 (`chattool dns`)
213
184
 
214
- 提供了便捷的 DDNS(动态域名解析)更新工具和 SSL 证书管理:
185
+ 统一的 DNS 接口,支持阿里云和腾讯云,提供 DDNS 动态更新和 SSL 证书自动续期。
215
186
 
216
187
  ```bash
217
- # 获取 DNS 记录
188
+ # 查询 / 设置 DNS 记录
218
189
  chattool dns get test.example.com
219
-
220
- # 设置 DNS 记录
221
190
  chattool dns set test.example.com -v 1.2.3.4
222
191
 
223
- # DDNS 动态域名更新 (公网 IP)
192
+ # DDNS 动态域名 (公网 / 局域网)
224
193
  chattool dns ddns -d example.com -r home --monitor
225
-
226
- # DDNS 动态域名更新 (局域网 IP)
227
194
  chattool dns ddns -d example.com -r nas --ip-type local --local-ip-cidr 192.168.1.0/24
228
195
 
229
196
  # SSL 证书自动更新
230
197
  chattool dns cert-update -d example.com -e admin@example.com --cert-dir ./certs
231
198
  ```
232
199
 
200
+ ### 5. 其他工具
201
+
202
+ | 工具 | 命令 | 说明 |
203
+ |------|------|------|
204
+ | 网络扫描 | `chattool network scan` | 扫描局域网活跃主机和 SSH 端口 |
205
+ | MCP 服务 | `chattool mcp inspect` | MCP Server 能力检查 |
206
+ | 截图服务 | `chattool serve capture` | 本地网页截图服务 |
207
+ | 证书分发 | `chattool serve cert` / `chattool client cert` | SSL 证书集中管理与客户端拉取 |
208
+
233
209
  ## 开源协议
234
210
 
235
- 使用 MIT 协议开源。
211
+ MIT License
236
212
 
237
213
  ## 更新日志
238
214
 
239
- - 当前版本 `4.1.0`,统一 `Chat` API(同步/异步/流式),默认环境变量配置,改进重试与调试工具
240
- - 历史:`2.x-3.x` 阶段逐步完善异步处理与批量用法
241
- - 更早版本沿革请参考仓库提交记录
215
+ - `5.3.0` — 飞书机器人(消息收发、事件路由、AI 对话)、CLI 工具链(`chattool lark`、`chattool serve lark`)
216
+ - `5.0.0` — DNS 管理(阿里云/腾讯云)、DDNS、SSL 证书自动续期、环境变量集中管理
217
+ - `4.1.0` — 统一 `Chat` API(同步/异步/流式),默认环境变量配置
218
+ - 更早版本请参考仓库提交记录
@@ -0,0 +1,154 @@
1
+ <div align="center">
2
+ <a href="https://pypi.python.org/pypi/chattool">
3
+ <img src="https://img.shields.io/pypi/v/chattool.svg" alt="PyPI version" />
4
+ </a>
5
+ <a href="https://github.com/cubenlp/chattool/actions/workflows/ci.yml">
6
+ <img src="https://github.com/cubenlp/chattool/actions/workflows/ci.yml/badge.svg" alt="Tests" />
7
+ </a>
8
+ <a href="https://chattool.wzhecnu.cn">
9
+ <img src="https://img.shields.io/badge/docs-github_pages-blue.svg" alt="Documentation Status" />
10
+ </a>
11
+ <a href="https://codecov.io/gh/cubenlp/chattool">
12
+ <img src="https://codecov.io/gh/cubenlp/chattool/branch/master/graph/badge.svg" alt="Coverage" />
13
+ </a>
14
+ </div>
15
+
16
+ <div align="center">
17
+ <img src="https://qiniu.wzhecnu.cn/PicBed6/picgo/chattool.jpeg" alt="ChatAPI Toolkit" width="360", style="border-radius: 20px;">
18
+
19
+ [English](README_en.md) | [简体中文](README.md)
20
+ </div>
21
+
22
+ 集成 LLM 对话、飞书机器人、DNS 管理、SSL 证书等工具的 Python 开发套件。
23
+
24
+ ## 安装
25
+
26
+ ```bash
27
+ pip install chattool --upgrade
28
+ ```
29
+
30
+ ## 功能概览
31
+
32
+ ### 1. 环境变量管理 (`chatenv`)
33
+
34
+ 集中式配置管理,密码类字段自动脱敏显示,交互模式隐藏敏感输入。
35
+
36
+ ```bash
37
+ # 交互式初始化(敏感字段自动隐藏输入)
38
+ chatenv init -i
39
+
40
+ # 仅初始化指定服务配置
41
+ chatenv init -i -t openai
42
+ chatenv init -i -t feishu
43
+
44
+ # 查看全部配置(敏感值自动打码)
45
+ chatenv cat
46
+
47
+ # 按类型过滤查看
48
+ chatenv cat -t feishu
49
+
50
+ # 设置 / 查看单项
51
+ chatenv set OPENAI_API_KEY=sk-xxx
52
+ chatenv get OPENAI_API_KEY
53
+
54
+ # 多配置 profile 管理
55
+ chatenv save work && chatenv use work
56
+ ```
57
+
58
+ ### 2. Chat 对话 (`chattool.Chat`)
59
+
60
+ 基于 OpenAI API 的对话对象,支持多轮对话、批量处理、异步并发和流式输出。
61
+
62
+ ```python
63
+ from chattool import Chat
64
+
65
+ # 多轮对话
66
+ chat = Chat("Hello!")
67
+ resp = chat.get_response()
68
+ chat.user("How are you?")
69
+ chat.get_response()
70
+ chat.save("chat.json", mode="w")
71
+
72
+ # 异步并发
73
+ import asyncio
74
+ base = Chat().system("你是一个有用的助手")
75
+ tasks = [base.copy().user(f"主题 {i}").async_get_response() for i in range(5)]
76
+ responses = asyncio.run(asyncio.gather(*tasks))
77
+
78
+ # 流式输出
79
+ async for chunk in Chat().user("写一首诗").async_get_response_stream():
80
+ if chunk.delta_content:
81
+ print(chunk.delta_content, end="", flush=True)
82
+ ```
83
+
84
+ ### 3. 飞书/Lark 机器人 (`chattool lark`)
85
+
86
+ 一行命令发消息、启动 Echo/AI 机器人,支持文本、图片、文件、富文本等消息类型。
87
+
88
+ ```bash
89
+ # 发送消息
90
+ chattool lark send -r USER_ID -m "Hello"
91
+ chattool lark send -r USER_ID --image photo.png
92
+ chattool lark send -r USER_ID --file report.pdf
93
+
94
+ # 启动 Echo 机器人(WebSocket)
95
+ chattool serve lark echo
96
+
97
+ # 启动 AI 对话机器人
98
+ chattool serve lark ai --model gpt-4o
99
+
100
+ # 查看机器人信息和权限
101
+ chattool lark info
102
+ chattool lark scopes
103
+ ```
104
+
105
+ ```python
106
+ from chattool.tools.lark import LarkBot
107
+
108
+ bot = LarkBot()
109
+ bot.send_text("ou_xxx", "open_id", "Hello!")
110
+ bot.send_image_file("ou_xxx", "open_id", "photo.png")
111
+
112
+ @bot.on_message
113
+ def handle(ctx):
114
+ ctx.reply_text(f"收到: {ctx.text}")
115
+
116
+ bot.start()
117
+ ```
118
+
119
+ ### 4. DNS 管理 (`chattool dns`)
120
+
121
+ 统一的 DNS 接口,支持阿里云和腾讯云,提供 DDNS 动态更新和 SSL 证书自动续期。
122
+
123
+ ```bash
124
+ # 查询 / 设置 DNS 记录
125
+ chattool dns get test.example.com
126
+ chattool dns set test.example.com -v 1.2.3.4
127
+
128
+ # DDNS 动态域名 (公网 / 局域网)
129
+ chattool dns ddns -d example.com -r home --monitor
130
+ chattool dns ddns -d example.com -r nas --ip-type local --local-ip-cidr 192.168.1.0/24
131
+
132
+ # SSL 证书自动更新
133
+ chattool dns cert-update -d example.com -e admin@example.com --cert-dir ./certs
134
+ ```
135
+
136
+ ### 5. 其他工具
137
+
138
+ | 工具 | 命令 | 说明 |
139
+ |------|------|------|
140
+ | 网络扫描 | `chattool network scan` | 扫描局域网活跃主机和 SSH 端口 |
141
+ | MCP 服务 | `chattool mcp inspect` | MCP Server 能力检查 |
142
+ | 截图服务 | `chattool serve capture` | 本地网页截图服务 |
143
+ | 证书分发 | `chattool serve cert` / `chattool client cert` | SSL 证书集中管理与客户端拉取 |
144
+
145
+ ## 开源协议
146
+
147
+ MIT License
148
+
149
+ ## 更新日志
150
+
151
+ - `5.3.0` — 飞书机器人(消息收发、事件路由、AI 对话)、CLI 工具链(`chattool lark`、`chattool serve lark`)
152
+ - `5.0.0` — DNS 管理(阿里云/腾讯云)、DDNS、SSL 证书自动续期、环境变量集中管理
153
+ - `4.1.0` — 统一 `Chat` API(同步/异步/流式),默认环境变量配置
154
+ - 更早版本请参考仓库提交记录
@@ -50,6 +50,8 @@ tools = [
50
50
  "alibabacloud_tea_openapi>=0.3.0",
51
51
  "tencentcloud-sdk-python",
52
52
  "netifaces",
53
+ "lark-oapi==1.5.3",
54
+ "flask",
53
55
  ]
54
56
  tests = [
55
57
  "coverage",
@@ -95,7 +97,8 @@ include-package-data = true
95
97
  markers = [
96
98
  "integration: mark test as integration test",
97
99
  "dns: mark test as dns test",
98
- "mcp: mark test as mcp test"
100
+ "mcp: mark test as mcp test",
101
+ "lark: mark test as lark test"
99
102
  ]
100
103
  filterwarnings = [
101
104
  "ignore::DeprecationWarning:alibabacloud_tea_openapi.*",
@@ -2,7 +2,7 @@
2
2
 
3
3
  __author__ = """Rex Wang"""
4
4
  __email__ = '1073853456@qq.com'
5
- __version__ = '5.2.0'
5
+ __version__ = '5.3.0'
6
6
 
7
7
  from dotenv import load_dotenv
8
8
 
@@ -14,6 +14,7 @@ from .utils import (
14
14
  )
15
15
  from .const import CHATTOOL_REPO_DIR
16
16
  from .config import OpenAIConfig, AzureConfig, AliyunConfig, TencentConfig, ZulipConfig
17
+ from .tools import LarkBot, AliyunDNSClient, TencentDNSClient, DynamicIPUpdater, SSLCertUpdater
17
18
 
18
19
  setup_jupyter_async()
19
20
  load_dotenv(CHATTOOL_REPO_DIR / '.env')
@@ -34,4 +35,9 @@ __all__ = [
34
35
  "AliyunConfig",
35
36
  "TencentConfig",
36
37
  "ZulipConfig",
38
+ "LarkBot",
39
+ "AliyunDNSClient",
40
+ "TencentDNSClient",
41
+ "DynamicIPUpdater",
42
+ "SSLCertUpdater",
37
43
  ]
@@ -26,11 +26,33 @@ def profiles():
26
26
  else:
27
27
  click.echo(f"No profiles found in {CHATTOOL_ENV_DIR}")
28
28
 
29
+ def _resolve_config_types(config_types):
30
+ """Resolve -t filter to matching config classes. Returns None if no filter."""
31
+ if not config_types:
32
+ return None
33
+ normalized = [t.lower() for t in config_types]
34
+ matched = [
35
+ cls for cls in BaseEnvConfig._registry
36
+ if cls._title.lower() in normalized
37
+ or any(a.lower() in normalized for a in getattr(cls, '_aliases', []))
38
+ ]
39
+ return matched
40
+
41
+
29
42
  @cli.command(name='cat')
30
43
  @click.argument('name', required=False)
31
44
  @click.option('--no-mask', is_flag=True, help='Show values in plain text without masking.')
32
- def cat_env(name, no_mask):
33
- """Print the content of an environment profile or current .env."""
45
+ @click.option('--type', '-t', 'config_types', multiple=True,
46
+ help='Filter configuration types (e.g. openai, feishu, aliyun).')
47
+ def cat_env(name, no_mask, config_types):
48
+ """Print the content of an environment profile or current .env.
49
+
50
+ \b
51
+ Examples:
52
+ chatenv cat # show all
53
+ chatenv cat -t feishu # only show Feishu config
54
+ chatenv cat -t openai -t feishu
55
+ """
34
56
  if name:
35
57
  config_path = CHATTOOL_ENV_DIR / f'{name}.env'
36
58
  else:
@@ -40,52 +62,67 @@ def cat_env(name, no_mask):
40
62
  click.echo(f"Error: Environment file '{config_path}' not found.", err=True)
41
63
  return
42
64
 
43
- content = config_path.read_text()
44
- if no_mask:
45
- click.echo(content)
46
- return
47
-
48
- # Identify sensitive keys
65
+ # Build lookup sets
49
66
  sensitive_keys = set()
50
67
  for config_cls in BaseEnvConfig._registry:
51
68
  for _, field in config_cls.get_fields().items():
52
69
  if field.is_sensitive:
53
70
  sensitive_keys.add(field.env_key)
54
71
 
55
- # Line-by-line processing for masking
72
+ # If -t is specified, only show keys belonging to those configs
73
+ filter_keys = None
74
+ if config_types:
75
+ matched = _resolve_config_types(config_types)
76
+ if not matched:
77
+ click.echo(f"No configuration types matched: {', '.join(config_types)}")
78
+ click.echo("Available types (and aliases):")
79
+ for cls in BaseEnvConfig._registry:
80
+ aliases = getattr(cls, '_aliases', [])
81
+ alias_str = f" ({', '.join(aliases)})" if aliases else ""
82
+ click.echo(f" - {cls._title}{alias_str}")
83
+ return
84
+ filter_keys = set()
85
+ for cls in matched:
86
+ for _, field in cls.get_fields().items():
87
+ filter_keys.add(field.env_key)
88
+
89
+ content = config_path.read_text()
90
+ if no_mask and filter_keys is None:
91
+ click.echo(content)
92
+ return
93
+
56
94
  for line in content.splitlines():
57
95
  line_strip = line.strip()
96
+
58
97
  if not line_strip or line_strip.startswith('#'):
59
- click.echo(line)
98
+ if filter_keys is None:
99
+ click.echo(line)
60
100
  continue
61
-
62
- if '=' in line:
63
- key, value = line.split('=', 1)
64
- key = key.strip()
65
-
66
- if key in sensitive_keys:
67
- val_part = value.strip()
68
- quote = ''
69
- raw_val = val_part
70
-
71
- # Handle quotes
72
- if len(val_part) >= 2:
73
- if (val_part.startswith("'") and val_part.endswith("'")) or \
74
- (val_part.startswith('"') and val_part.endswith('"')):
75
- quote = val_part[0]
76
- raw_val = val_part[1:-1]
77
-
78
- # Masking logic
79
- masked = mask_secret(raw_val)
80
-
81
- # Reconstruct line with masked value
82
- # We try to preserve the key part as is (from split), but we lost the whitespace around =
83
- # Since we don't know the exact whitespace, we just output KEY=VALUE
84
- click.echo(f"{key}={quote}{masked}{quote}")
85
- else:
101
+
102
+ if '=' not in line:
103
+ if filter_keys is None:
86
104
  click.echo(line)
87
- else:
105
+ continue
106
+
107
+ key, value = line.split('=', 1)
108
+ key = key.strip()
109
+
110
+ if filter_keys is not None and key not in filter_keys:
111
+ continue
112
+
113
+ if no_mask or key not in sensitive_keys:
88
114
  click.echo(line)
115
+ else:
116
+ val_part = value.strip()
117
+ quote = ''
118
+ raw_val = val_part
119
+ if len(val_part) >= 2:
120
+ if (val_part.startswith("'") and val_part.endswith("'")) or \
121
+ (val_part.startswith('"') and val_part.endswith('"')):
122
+ quote = val_part[0]
123
+ raw_val = val_part[1:-1]
124
+ masked = mask_secret(raw_val)
125
+ click.echo(f"{key}={quote}{masked}{quote}")
89
126
 
90
127
  @cli.command(name='save')
91
128
  @click.argument('name')
@@ -141,18 +178,12 @@ def init(interactive, config_types):
141
178
  - Aliyun: ali, aliyun, alidns
142
179
  - Tencent: tencent, tx, tencent-dns
143
180
  - Zulip: zulip
181
+ - Feishu: feishu, lark
144
182
  """
145
183
 
146
- # Filter config classes if types are specified
147
184
  target_configs = BaseEnvConfig._registry
148
185
  if config_types:
149
- normalized_types = [t.lower() for t in config_types]
150
- target_configs = [
151
- cls for cls in BaseEnvConfig._registry
152
- if cls._title.lower() in normalized_types or
153
- any(alias.lower() in normalized_types for alias in getattr(cls, '_aliases', []))
154
- ]
155
-
186
+ target_configs = _resolve_config_types(config_types)
156
187
  if not target_configs:
157
188
  click.echo(f"No configuration types matched: {', '.join(config_types)}")
158
189
  click.echo("Available types (and aliases):")
@@ -178,16 +209,29 @@ def init(interactive, config_types):
178
209
  prompt_text += f" ({field.desc})"
179
210
 
180
211
  default_val = field.value if field.value is not None else field.default
181
-
182
- new_val = click.prompt(
183
- prompt_text,
184
- default=default_val if default_val is not None else "",
185
- show_default=True,
186
- type=str
187
- )
188
-
189
- if new_val:
190
- field.value = new_val
212
+
213
+ if field.is_sensitive:
214
+ hint = mask_secret(default_val) if default_val else ""
215
+ if hint:
216
+ prompt_text += f" [{hint}]"
217
+ new_val = click.prompt(
218
+ prompt_text,
219
+ default="",
220
+ show_default=False,
221
+ hide_input=True,
222
+ type=str,
223
+ )
224
+ if new_val:
225
+ field.value = new_val
226
+ else:
227
+ new_val = click.prompt(
228
+ prompt_text,
229
+ default=default_val if default_val is not None else "",
230
+ show_default=True,
231
+ type=str,
232
+ )
233
+ if new_val:
234
+ field.value = new_val
191
235
 
192
236
  BaseEnvConfig.save_env_file(str(CHATTOOL_ENV_FILE), __version__)
193
237
  click.echo(f"Configuration saved to {CHATTOOL_ENV_FILE}")
@@ -3,6 +3,7 @@ from .cert_updater import main as ssl_updater_main
3
3
  from .dns_updater import cli as dns_updater_cli
4
4
  from .mcp import cli as mcp_cli
5
5
  from .network import network as network_cli
6
+ from .lark import cli as lark_cli
6
7
 
7
8
  __all__ = [
8
9
  "cert_client",
@@ -10,4 +11,5 @@ __all__ = [
10
11
  "dns_updater_cli",
11
12
  "mcp_cli",
12
13
  "network_cli",
14
+ "lark_cli",
13
15
  ]