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.
- {chattool-5.2.0/src/chattool.egg-info → chattool-5.3.0}/PKG-INFO +84 -107
- chattool-5.3.0/README.md +154 -0
- {chattool-5.2.0 → chattool-5.3.0}/pyproject.toml +4 -1
- {chattool-5.2.0 → chattool-5.3.0}/src/chattool/__init__.py +7 -1
- {chattool-5.2.0 → chattool-5.3.0}/src/chattool/cli/chatenv.py +98 -54
- {chattool-5.2.0 → chattool-5.3.0}/src/chattool/cli/client/__init__.py +2 -0
- chattool-5.3.0/src/chattool/cli/client/lark.py +387 -0
- {chattool-5.2.0 → chattool-5.3.0}/src/chattool/cli/main.py +6 -2
- {chattool-5.2.0 → chattool-5.3.0}/src/chattool/cli/service/__init__.py +2 -0
- chattool-5.3.0/src/chattool/cli/service/lark_serve.py +177 -0
- chattool-5.3.0/src/chattool/cli/test_cmd.py +28 -0
- {chattool-5.2.0 → chattool-5.3.0}/src/chattool/config/__init__.py +4 -1
- {chattool-5.2.0 → chattool-5.3.0}/src/chattool/config/elements.py +14 -0
- chattool-5.3.0/src/chattool/config/main.py +135 -0
- {chattool-5.2.0 → chattool-5.3.0}/src/chattool/tools/__init__.py +4 -0
- chattool-5.3.0/src/chattool/tools/lark/__init__.py +5 -0
- chattool-5.3.0/src/chattool/tools/lark/bot.py +642 -0
- chattool-5.3.0/src/chattool/tools/lark/context.py +128 -0
- chattool-5.3.0/src/chattool/tools/lark/elements.py +119 -0
- chattool-5.3.0/src/chattool/tools/lark/session.py +116 -0
- {chattool-5.2.0 → chattool-5.3.0/src/chattool.egg-info}/PKG-INFO +84 -107
- {chattool-5.2.0 → chattool-5.3.0}/src/chattool.egg-info/SOURCES.txt +7 -0
- {chattool-5.2.0 → chattool-5.3.0}/src/chattool.egg-info/requires.txt +2 -0
- chattool-5.2.0/README.md +0 -179
- chattool-5.2.0/src/chattool/cli/test_cmd.py +0 -76
- chattool-5.2.0/src/chattool/config/main.py +0 -45
- {chattool-5.2.0 → chattool-5.3.0}/LICENSE +0 -0
- {chattool-5.2.0 → chattool-5.3.0}/setup.cfg +0 -0
- {chattool-5.2.0 → chattool-5.3.0}/src/chattool/_all.py +0 -0
- {chattool-5.2.0 → chattool-5.3.0}/src/chattool/application/kb/__init__.py +0 -0
- {chattool-5.2.0 → chattool-5.3.0}/src/chattool/application/kb/cli.py +0 -0
- {chattool-5.2.0 → chattool-5.3.0}/src/chattool/application/kb/ingest.py +0 -0
- {chattool-5.2.0 → chattool-5.3.0}/src/chattool/application/kb/manager.py +0 -0
- {chattool-5.2.0 → chattool-5.3.0}/src/chattool/application/kb/storage.py +0 -0
- {chattool-5.2.0 → chattool-5.3.0}/src/chattool/cli/__init__.py +0 -0
- {chattool-5.2.0 → chattool-5.3.0}/src/chattool/cli/client/cert_client.py +0 -0
- {chattool-5.2.0 → chattool-5.3.0}/src/chattool/cli/client/cert_updater.py +0 -0
- {chattool-5.2.0 → chattool-5.3.0}/src/chattool/cli/client/dns_updater.py +0 -0
- {chattool-5.2.0 → chattool-5.3.0}/src/chattool/cli/client/mcp.py +0 -0
- {chattool-5.2.0 → chattool-5.3.0}/src/chattool/cli/client/network.py +0 -0
- {chattool-5.2.0 → chattool-5.3.0}/src/chattool/cli/service/capture.py +0 -0
- {chattool-5.2.0 → chattool-5.3.0}/src/chattool/cli/service/cert_server.py +0 -0
- {chattool-5.2.0 → chattool-5.3.0}/src/chattool/const.py +0 -0
- {chattool-5.2.0 → chattool-5.3.0}/src/chattool/llm/__init__.py +0 -0
- {chattool-5.2.0 → chattool-5.3.0}/src/chattool/llm/chattype.py +0 -0
- {chattool-5.2.0 → chattool-5.3.0}/src/chattool/llm/response.py +0 -0
- {chattool-5.2.0 → chattool-5.3.0}/src/chattool/mcp/__init__.py +0 -0
- {chattool-5.2.0 → chattool-5.3.0}/src/chattool/mcp/dns.py +0 -0
- {chattool-5.2.0 → chattool-5.3.0}/src/chattool/mcp/network.py +0 -0
- {chattool-5.2.0 → chattool-5.3.0}/src/chattool/mcp/server.py +0 -0
- {chattool-5.2.0 → chattool-5.3.0}/src/chattool/mcp/zulip.py +0 -0
- {chattool-5.2.0 → chattool-5.3.0}/src/chattool/tools/cert/__init__.py +0 -0
- {chattool-5.2.0 → chattool-5.3.0}/src/chattool/tools/cert/acme_dns_tiny.py +0 -0
- {chattool-5.2.0 → chattool-5.3.0}/src/chattool/tools/cert/cert_server.py +0 -0
- {chattool-5.2.0 → chattool-5.3.0}/src/chattool/tools/cert/cert_updater.py +0 -0
- {chattool-5.2.0 → chattool-5.3.0}/src/chattool/tools/dns/__init__.py +0 -0
- {chattool-5.2.0 → chattool-5.3.0}/src/chattool/tools/dns/aliyun.py +0 -0
- {chattool-5.2.0 → chattool-5.3.0}/src/chattool/tools/dns/base.py +0 -0
- {chattool-5.2.0 → chattool-5.3.0}/src/chattool/tools/dns/ip_updater.py +0 -0
- {chattool-5.2.0 → chattool-5.3.0}/src/chattool/tools/dns/tencent.py +0 -0
- {chattool-5.2.0 → chattool-5.3.0}/src/chattool/tools/dns/utils.py +0 -0
- {chattool-5.2.0 → chattool-5.3.0}/src/chattool/tools/githubclient.py +0 -0
- {chattool-5.2.0 → chattool-5.3.0}/src/chattool/tools/interact.py +0 -0
- {chattool-5.2.0 → chattool-5.3.0}/src/chattool/tools/network/__init__.py +0 -0
- {chattool-5.2.0 → chattool-5.3.0}/src/chattool/tools/network/scanner.py +0 -0
- {chattool-5.2.0 → chattool-5.3.0}/src/chattool/tools/zulip/__init__.py +0 -0
- {chattool-5.2.0 → chattool-5.3.0}/src/chattool/tools/zulip/client.py +0 -0
- {chattool-5.2.0 → chattool-5.3.0}/src/chattool/tools/zulip/legacy.py +0 -0
- {chattool-5.2.0 → chattool-5.3.0}/src/chattool/utils/__init__.py +0 -0
- {chattool-5.2.0 → chattool-5.3.0}/src/chattool/utils/basic.py +0 -0
- {chattool-5.2.0 → chattool-5.3.0}/src/chattool/utils/custom_logger.py +0 -0
- {chattool-5.2.0 → chattool-5.3.0}/src/chattool/utils/fastobj.py +0 -0
- {chattool-5.2.0 → chattool-5.3.0}/src/chattool/utils/httpclient.py +0 -0
- {chattool-5.2.0 → chattool-5.3.0}/src/chattool/utils/urltool.py +0 -0
- {chattool-5.2.0 → chattool-5.3.0}/src/chattool.egg-info/dependency_links.txt +0 -0
- {chattool-5.2.0 → chattool-5.3.0}/src/chattool.egg-info/entry_points.txt +0 -0
- {chattool-5.2.0 → chattool-5.3.0}/src/chattool.egg-info/top_level.txt +0 -0
- {chattool-5.2.0 → chattool-5.3.0}/tests/test_chatenv.py +0 -0
- {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.
|
|
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
|
-
|
|
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
|
-
|
|
98
|
+
集中式配置管理,密码类字段自动脱敏显示,交互模式隐藏敏感输入。
|
|
97
99
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
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
|
-
|
|
111
|
-
|
|
108
|
+
# 查看全部配置(敏感值自动打码)
|
|
109
|
+
chatenv cat
|
|
112
110
|
|
|
113
|
-
|
|
114
|
-
|
|
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
|
-
|
|
121
|
-
|
|
122
|
-
|
|
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
|
-
|
|
128
|
-
|
|
129
|
-
|
|
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
|
-
|
|
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
|
-
|
|
133
|
+
chat.get_response()
|
|
134
|
+
chat.save("chat.json", mode="w")
|
|
146
135
|
|
|
147
|
-
#
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
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
|
-
|
|
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
|
-
|
|
150
|
+
一行命令发消息、启动 Echo/AI 机器人,支持文本、图片、文件、富文本等消息类型。
|
|
173
151
|
|
|
174
|
-
```
|
|
175
|
-
|
|
176
|
-
|
|
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
|
-
|
|
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
|
-
|
|
161
|
+
# 启动 AI 对话机器人
|
|
162
|
+
chattool serve lark ai --model gpt-4o
|
|
197
163
|
|
|
198
|
-
|
|
164
|
+
# 查看机器人信息和权限
|
|
165
|
+
chattool lark info
|
|
166
|
+
chattool lark scopes
|
|
167
|
+
```
|
|
199
168
|
|
|
200
169
|
```python
|
|
201
|
-
from chattool.tools.
|
|
170
|
+
from chattool.tools.lark import LarkBot
|
|
202
171
|
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
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
|
-
|
|
209
|
-
|
|
176
|
+
@bot.on_message
|
|
177
|
+
def handle(ctx):
|
|
178
|
+
ctx.reply_text(f"收到: {ctx.text}")
|
|
179
|
+
|
|
180
|
+
bot.start()
|
|
210
181
|
```
|
|
211
182
|
|
|
212
|
-
|
|
183
|
+
### 4. DNS 管理 (`chattool dns`)
|
|
213
184
|
|
|
214
|
-
|
|
185
|
+
统一的 DNS 接口,支持阿里云和腾讯云,提供 DDNS 动态更新和 SSL 证书自动续期。
|
|
215
186
|
|
|
216
187
|
```bash
|
|
217
|
-
#
|
|
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
|
|
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
|
-
|
|
211
|
+
MIT License
|
|
236
212
|
|
|
237
213
|
## 更新日志
|
|
238
214
|
|
|
239
|
-
-
|
|
240
|
-
-
|
|
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
|
+
- 更早版本请参考仓库提交记录
|
chattool-5.3.0/README.md
ADDED
|
@@ -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.
|
|
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
|
-
|
|
33
|
-
|
|
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
|
-
|
|
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
|
-
#
|
|
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
|
-
|
|
98
|
+
if filter_keys is None:
|
|
99
|
+
click.echo(line)
|
|
60
100
|
continue
|
|
61
|
-
|
|
62
|
-
if '=' in line:
|
|
63
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
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
|
]
|