diff --git a/README.md b/README.md
index 6f962d137..a4bb81971 100644
--- a/README.md
+++ b/README.md
@@ -1,15 +1,15 @@
# 简介
-> chatgpt-on-wechat(简称CoW)项目是基于大模型的智能对话机器人,支持微信公众号、企业微信应用、微信、飞书、钉钉接入,可选择GPT3.5/GPT4.0/Claude/Gemini/LinkAI/ChatGLM/KIMI/文心一言/讯飞星火/通义千问/LinkAI,能处理文本、语音和图片,通过插件访问操作系统和互联网等外部资源,支持基于自有知识库定制企业AI应用。
+> chatgpt-on-wechat(简称 CoW)项目是基于大模型的智能对话机器人,支持微信公众号、企业微信应用、微信、飞书、钉钉接入,可选择 GPT3.5/GPT4.0/Claude/文心一言/讯飞星火/通义千问/Gemini/LinkAI/ChatGLM/KIMI,能处理文本、语音和图片,通过插件访问操作系统和互联网等外部资源,支持基于自有知识库定制企业 AI 应用。
最新版本支持的功能如下:
-- ✅ **多端部署:** 有多种部署方式可选择且功能完备,目前已支持微信生态下公众号、企业微信应用、飞书、钉钉等部署方式
-- ✅ **基础对话:** 私聊及群聊的消息智能回复,支持多轮会话上下文记忆,支持 GPT-3.5, GPT-4, Claude-3, Gemini, 文心一言, 讯飞星火, 通义千问,ChatGLM-4,Kimi(月之暗面)
-- ✅ **语音能力:** 可识别语音消息,通过文字或语音回复,支持 azure, baidu, google, openai(whisper/tts) 等多种语音模型
-- ✅ **图像能力:** 支持图片生成、图片识别、图生图(如照片修复),可选择 Dall-E-3, stable diffusion, replicate, midjourney, CogView-3, vision模型
-- ✅ **丰富插件:** 支持个性化插件扩展,已实现多角色切换、文字冒险、敏感词过滤、聊天记录总结、文档总结和对话、联网搜索等插件
-- ✅ **知识库:** 通过上传知识库文件自定义专属机器人,可作为数字分身、智能客服、私域助手使用,基于 [LinkAI](https://link-ai.tech) 实现
+- [x] **多端部署:** 有多种部署方式可选择且功能完备,目前已支持微信生态下公众号、企业微信应用、飞书、钉钉等部署方式
+- [x] **基础对话:** 私聊及群聊的消息智能回复,支持多轮会话上下文记忆,支持 GPT-3.5, GPT-4, Claude-3, Gemini, 文心一言,讯飞星火,通义千问,ChatGLM-4,Kimi(月之暗面)
+- [x] **语音能力:** 可识别语音消息,通过文字或语音回复,支持 azure, baidu, google, openai(whisper/tts) 等多种语音模型
+- [x] **图像能力:** 支持图片生成、图片识别、图生图(如照片修复),可选择 Dall-E-3, stable diffusion, replicate, midjourney, CogView-3, vision 模型
+- [x] **丰富插件:** 支持个性化插件扩展,已实现多角色切换、文字冒险、敏感词过滤、聊天记录总结、文档总结和对话、联网搜索等插件
+- [x] **知识库:** 通过上传知识库文件自定义专属机器人,可作为数字分身、智能客服、私域助手使用,基于 [LinkAI](https://link-ai.tech) 实现
## 演示
@@ -23,15 +23,15 @@ https://github.com/zhayujie/chatgpt-on-wechat/assets/26161723/d5154020-36e3-41db
-# 企业服务
+# 商业服务
-
+
-> [LinkAI](https://link-ai.tech/) 是面向企业和开发者的一站式AI应用平台,聚合多模态大模型、知识库、Agent 插件、工作流等能力,支持一键接入主流平台并进行管理,支持SaaS、私有化部署多种模式。
+> 我们还提供企业级的 **一站式 AI 应用搭建与接入平台** - [LinkAI](https://link-ai.tech/),聚合多模态大模型、知识库、Agent 插件、工作流等能力,并支持一键接入主流 IM 和办公协同平台并进行管理,支持 SaaS、私有化部署、稳定托管接入多种模式。
>
-> LinkAI 目前 已在私域运营、智能客服、企业效率助手等场景积累了丰富的 AI 解决方案, 在电商、文教、健康、新消费、科技制造等各行业沉淀了大模型落地应用的最佳实践,致力于帮助更多企业和开发者拥抱 AI 生产力。
+> LinkAI 目前 已在私域运营、智能客服、企业效率助手等场景积累了丰富的 AI 解决方案,在电商、文教、健康、新消费、科技制造等各行业沉淀了大模型落地应用的最佳实践,致力于帮助更多团队和企业拥抱 AI 生产力。
-**企业服务和产品咨询** 可联系产品顾问:
+**企业服务和商用咨询** 可联系产品顾问:
@@ -39,19 +39,19 @@ https://github.com/zhayujie/chatgpt-on-wechat/assets/26161723/d5154020-36e3-41db
# 🏷 更新日志
->**2024.04.26:** [1.6.0版本](https://github.com/zhayujie/chatgpt-on-wechat/releases/tag/1.6.0),新增 Kimi 接入、gpt-4-turbo版本升级、文件总结和语音识别问题修复
+>**2024.04.26:** [1.6.0 版本](https://github.com/zhayujie/chatgpt-on-wechat/releases/tag/1.6.0),新增 Kimi 接入、gpt-4-turbo 版本升级、文件总结和语音识别问题修复
->**2024.03.26:** [1.5.8版本](https://github.com/zhayujie/chatgpt-on-wechat/releases/tag/1.5.8) 和 [1.5.7版本](https://github.com/zhayujie/chatgpt-on-wechat/releases/tag/1.5.7),新增 GLM-4、Claude-3 模型,edge-tts 语音支持
+>**2024.03.26:** [1.5.8 版本](https://github.com/zhayujie/chatgpt-on-wechat/releases/tag/1.5.8) 和 [1.5.7 版本](https://github.com/zhayujie/chatgpt-on-wechat/releases/tag/1.5.7),新增 GLM-4、Claude-3 模型,edge-tts 语音支持
->**2024.01.26:** [1.5.6版本](https://github.com/zhayujie/chatgpt-on-wechat/releases/tag/1.5.6) 和 [1.5.5版本](https://github.com/zhayujie/chatgpt-on-wechat/releases/tag/1.5.5),钉钉接入,tool插件升级,4-turbo模型更新
+>**2024.01.26:** [1.5.6 版本](https://github.com/zhayujie/chatgpt-on-wechat/releases/tag/1.5.6) 和 [1.5.5 版本](https://github.com/zhayujie/chatgpt-on-wechat/releases/tag/1.5.5),钉钉接入,tool 插件升级,4-turbo 模型更新
->**2023.11.11:** [1.5.3版本](https://github.com/zhayujie/chatgpt-on-wechat/releases/tag/1.5.3) 和 [1.5.4版本](https://github.com/zhayujie/chatgpt-on-wechat/releases/tag/1.5.4),新增通义千问模型、Google Gemini
+>**2023.11.11:** [1.5.3 版本](https://github.com/zhayujie/chatgpt-on-wechat/releases/tag/1.5.3) 和 [1.5.4 版本](https://github.com/zhayujie/chatgpt-on-wechat/releases/tag/1.5.4),新增通义千问模型、Google Gemini
->**2023.11.10:** [1.5.2版本](https://github.com/zhayujie/chatgpt-on-wechat/releases/tag/1.5.2),新增飞书通道、图像识别对话、黑名单配置
+>**2023.11.10:** [1.5.2 版本](https://github.com/zhayujie/chatgpt-on-wechat/releases/tag/1.5.2),新增飞书通道、图像识别对话、黑名单配置
->**2023.11.10:** [1.5.0版本](https://github.com/zhayujie/chatgpt-on-wechat/releases/tag/1.5.0),新增 `gpt-4-turbo`, `dall-e-3`, `tts` 模型接入,完善图像理解&生成、语音识别&生成的多模态能力
+>**2023.11.10:** [1.5.0 版本](https://github.com/zhayujie/chatgpt-on-wechat/releases/tag/1.5.0),新增 `gpt-4-turbo`, `dall-e-3`, `tts` 模型接入,完善图像理解&生成、语音识别&生成的多模态能力
->**2023.10.16:** 支持通过意图识别使用LinkAI联网搜索、数学计算、网页访问等插件,参考[插件文档](https://docs.link-ai.tech/platform/plugins)
+>**2023.10.16:** 支持通过意图识别使用 LinkAI 联网搜索、数学计算、网页访问等插件,参考[插件文档](https://docs.link-ai.tech/platform/plugins)
>**2023.09.26:** 插件增加 文件/文章链接 一键总结和对话的功能,使用参考:[插件说明](https://github.com/zhayujie/chatgpt-on-wechat/tree/master/plugins/linkai#3%E6%96%87%E6%A1%A3%E6%80%BB%E7%BB%93%E5%AF%B9%E8%AF%9D%E5%8A%9F%E8%83%BD)
@@ -59,7 +59,7 @@ https://github.com/zhayujie/chatgpt-on-wechat/assets/26161723/d5154020-36e3-41db
>**2023.06.12:** 接入 [LinkAI](https://link-ai.tech/console) 平台,可在线创建领域知识库,打造专属客服机器人。使用参考 [接入文档](https://link-ai.tech/platform/link-app/wechat)。
-更早更新日志查看: [归档日志](/docs/version/old-version.md)
+更早更新日志查看:[归档日志](/docs/version/old-version.md)
@@ -71,18 +71,18 @@ https://github.com/zhayujie/chatgpt-on-wechat/assets/26161723/d5154020-36e3-41db
### 1. 账号注册
-项目默认使用OpenAI接口,需前往 [OpenAI注册页面](https://beta.openai.com/signup) 创建账号,创建完账号则前往 [API管理页面](https://beta.openai.com/account/api-keys) 创建一个 API Key 并保存下来,后面需要在项目中配置这个key。接口需要海外网络访问及绑定信用卡支付。
+项目默认使用 OpenAI 接口,需前往 [OpenAI 注册页面](https://beta.openai.com/signup) 创建账号,创建完账号则前往 [API 管理页面](https://beta.openai.com/account/api-keys) 创建一个 API Key 并保存下来,后面需要在项目中配置这个 key。接口需要海外网络访问及绑定信用卡支付。
-> 默认对话模型是 openai 的 gpt-3.5-turbo,计费方式是约每 1000tokens (约750个英文单词 或 500汉字,包含请求和回复) 消耗 $0.002,图片生成是Dell E模型,每张消耗 $0.016。
+> 默认对话模型是 openai 的 gpt-3.5-turbo,计费方式是约每 1000tokens (约 750 个英文单词 或 500 汉字,包含请求和回复) 消耗 $0.002,图片生成是 Dell E 模型,每张消耗 $0.016。
-项目同时也支持使用 LinkAI 接口,无需代理,可使用 文心、讯飞、GPT-3、GPT-4 等模型,支持 定制化知识库、联网搜索、MJ绘图、文档总结和对话等能力。修改配置即可一键切换,参考 [接入文档](https://link-ai.tech/platform/link-app/wechat)。
+项目同时也支持使用 LinkAI 接口,无需代理,可使用 文心、讯飞、GPT-3、GPT-4 等模型,支持 定制化知识库、联网搜索、MJ 绘图、文档总结和对话等能力。修改配置即可一键切换,参考 [接入文档](https://link-ai.tech/platform/link-app/wechat)。
### 2.运行环境
-支持 Linux、MacOS、Windows 系统(可在Linux服务器上长期运行),同时需安装 `Python`。
-> 建议Python版本在 3.7.1~3.9.X 之间,推荐3.8版本,3.10及以上版本在 MacOS 可用,其他系统上不确定能否正常运行。
+支持 Linux、MacOS、Windows 系统(可在 Linux 服务器上长期运行),同时需安装 `Python`。
+> 建议 Python 版本在 3.7.1~3.9.X 之间,推荐 3.8 版本,3.10 及以上版本在 MacOS 可用,其他系统上不确定能否正常运行。
-> 注意:Docker 或 Railway 部署无需安装python环境和下载源码,可直接快进到下一节。
+> 注意:Docker 或 Railway 部署无需安装 python 环境和下载源码,可直接快进到下一节。
**(1) 克隆项目代码:**
@@ -91,7 +91,7 @@ git clone https://github.com/zhayujie/chatgpt-on-wechat
cd chatgpt-on-wechat/
```
-注: 如遇到网络问题可选择国内镜像 https://gitee.com/zhayujie/chatgpt-on-wechat
+注:如遇到网络问题可选择国内镜像 https://gitee.com/zhayujie/chatgpt-on-wechat
**(2) 安装核心依赖 (必选):**
> 能够使用`itchat`创建机器人,并具有文字交流功能所需的最小依赖集合。
@@ -114,7 +114,7 @@ pip3 install -r requirements-optional.txt
cp config-template.json config.json
```
-然后在`config.json`中填入配置,以下是对默认配置的说明,可根据需要进行自定义修改(注意实际使用时请去掉注释,保证JSON格式的完整):
+然后在`config.json`中填入配置,以下是对默认配置的说明,可根据需要进行自定义修改(注意实际使用时请去掉注释,保证 JSON 格式的完整):
```bash
# config.json文件内容示例
@@ -145,40 +145,40 @@ pip3 install -r requirements-optional.txt
**1.个人聊天**
+ 个人聊天中,需要以 "bot"或"@bot" 为开头的内容触发机器人,对应配置项 `single_chat_prefix` (如果不需要以前缀触发可以填写 `"single_chat_prefix": [""]`)
-+ 机器人回复的内容会以 "[bot] " 作为前缀, 以区分真人,对应的配置项为 `single_chat_reply_prefix` (如果不需要前缀可以填写 `"single_chat_reply_prefix": ""`)
++ 机器人回复的内容会以 "[bot] " 作为前缀,以区分真人,对应的配置项为 `single_chat_reply_prefix` (如果不需要前缀可以填写 `"single_chat_reply_prefix": ""`)
**2.群组聊天**
+ 群组聊天中,群名称需配置在 `group_name_white_list ` 中才能开启群聊自动回复。如果想对所有群聊生效,可以直接填写 `"group_name_white_list": ["ALL_GROUP"]`
+ 默认只要被人 @ 就会触发机器人自动回复;另外群聊天中只要检测到以 "@bot" 开头的内容,同样会自动回复(方便自己触发),这对应配置项 `group_chat_prefix`
-+ 可选配置: `group_name_keyword_white_list`配置项支持模糊匹配群名称,`group_chat_keyword`配置项则支持模糊匹配群消息内容,用法与上述两个配置项相同。(Contributed by [evolay](https://github.com/evolay))
++ 可选配置:`group_name_keyword_white_list`配置项支持模糊匹配群名称,`group_chat_keyword`配置项则支持模糊匹配群消息内容,用法与上述两个配置项相同。(Contributed by [evolay](https://github.com/evolay))
+ `group_chat_in_one_session`:使群聊共享一个会话上下文,配置 `["ALL_GROUP"]` 则作用于所有群聊
**3.语音识别**
-+ 添加 `"speech_recognition": true` 将开启语音识别,默认使用openai的whisper模型识别为文字,同时以文字回复,该参数仅支持私聊 (注意由于语音消息无法匹配前缀,一旦开启将对所有语音自动回复,支持语音触发画图);
-+ 添加 `"group_speech_recognition": true` 将开启群组语音识别,默认使用openai的whisper模型识别为文字,同时以文字回复,参数仅支持群聊 (会匹配group_chat_prefix和group_chat_keyword, 支持语音触发画图);
-+ 添加 `"voice_reply_voice": true` 将开启语音回复语音(同时作用于私聊和群聊),但是需要配置对应语音合成平台的key,由于itchat协议的限制,只能发送语音mp3文件,若使用wechaty则回复的是微信语音。
++ 添加 `"speech_recognition": true` 将开启语音识别,默认使用 openai 的 whisper 模型识别为文字,同时以文字回复,该参数仅支持私聊 (注意由于语音消息无法匹配前缀,一旦开启将对所有语音自动回复,支持语音触发画图);
++ 添加 `"group_speech_recognition": true` 将开启群组语音识别,默认使用 openai 的 whisper 模型识别为文字,同时以文字回复,参数仅支持群聊 (会匹配 group_chat_prefix 和 group_chat_keyword, 支持语音触发画图);
++ 添加 `"voice_reply_voice": true` 将开启语音回复语音(同时作用于私聊和群聊),但是需要配置对应语音合成平台的 key,由于 itchat 协议的限制,只能发送语音 mp3 文件,若使用 wechaty 则回复的是微信语音。
**4.其他配置**
-+ `model`: 模型名称,目前支持 `gpt-3.5-turbo`, `text-davinci-003`, `gpt-4`, `gpt-4-32k`, `wenxin` , `claude` , `xunfei`(其中gpt-4 api暂未完全开放,申请通过后可使用)
-+ `temperature`,`frequency_penalty`,`presence_penalty`: Chat API接口参数,详情参考[OpenAI官方文档。](https://platform.openai.com/docs/api-reference/chat)
++ `model`: 模型名称,目前支持 `gpt-3.5-turbo`, `text-davinci-003`, `gpt-4`, `gpt-4-32k`, `wenxin` , `claude` , `xunfei`(其中 gpt-4 api 暂未完全开放,申请通过后可使用)
++ `temperature`,`frequency_penalty`,`presence_penalty`: Chat API 接口参数,详情参考[OpenAI 官方文档。](https://platform.openai.com/docs/api-reference/chat)
+ `proxy`:由于目前 `openai` 接口国内无法访问,需配置代理客户端的地址,详情参考 [#351](https://github.com/zhayujie/chatgpt-on-wechat/issues/351)
+ 对于图像生成,在满足个人或群组触发条件外,还需要额外的关键词前缀来触发,对应配置 `image_create_prefix `
-+ 关于OpenAI对话及图片接口的参数配置(内容自由度、回复字数限制、图片大小等),可以参考 [对话接口](https://beta.openai.com/docs/api-reference/completions) 和 [图像接口](https://beta.openai.com/docs/api-reference/completions) 文档,在[`config.py`](https://github.com/zhayujie/chatgpt-on-wechat/blob/master/config.py)中检查哪些参数在本项目中是可配置的。
++ 关于 OpenAI 对话及图片接口的参数配置(内容自由度、回复字数限制、图片大小等),可以参考 [对话接口](https://beta.openai.com/docs/api-reference/completions) 和 [图像接口](https://beta.openai.com/docs/api-reference/completions) 文档,在[`config.py`](https://github.com/zhayujie/chatgpt-on-wechat/blob/master/config.py)中检查哪些参数在本项目中是可配置的。
+ `conversation_max_tokens`:表示能够记忆的上下文最大字数(一问一答为一组对话,如果累积的对话字数超出限制,就会优先移除最早的一组对话)
+ `rate_limit_chatgpt`,`rate_limit_dalle`:每分钟最高问答速率、画图速率,超速后排队按序处理。
+ `clear_memory_commands`: 对话内指令,主动清空前文记忆,字符串数组可自定义指令别名。
+ `hot_reload`: 程序退出后,暂存微信扫码状态,默认关闭。
+ `character_desc` 配置中保存着你对机器人说的一段话,他会记住这段话并作为他的设定,你可以为他定制任何人格 (关于会话上下文的更多内容参考该 [issue](https://github.com/zhayujie/chatgpt-on-wechat/issues/43))
-+ `subscribe_msg`:订阅消息,公众号和企业微信channel中请填写,当被订阅时会自动回复, 可使用特殊占位符。目前支持的占位符有{trigger_prefix},在程序中它会自动替换成bot的触发词。
++ `subscribe_msg`:订阅消息,公众号和企业微信 channel 中请填写,当被订阅时会自动回复,可使用特殊占位符。目前支持的占位符有{trigger_prefix},在程序中它会自动替换成 bot 的触发词。
-**5.LinkAI配置 (可选)**
+**5.LinkAI 配置 (可选)**
-+ `use_linkai`: 是否使用LinkAI接口,开启后可国内访问,使用知识库和 `Midjourney` 绘画, 参考 [文档](https://link-ai.tech/platform/link-app/wechat)
++ `use_linkai`: 是否使用 LinkAI 接口,开启后可国内访问,使用知识库和 `Midjourney` 绘画,参考 [文档](https://link-ai.tech/platform/link-app/wechat)
+ `linkai_api_key`: LinkAI Api Key,可在 [控制台](https://link-ai.tech/console/interface) 创建
-+ `linkai_app_code`: LinkAI 应用code,选填
++ `linkai_app_code`: LinkAI 应用 code,选填
**本说明文档可能会未及时更新,当前所有可选的配置项均在该[`config.py`](https://github.com/zhayujie/chatgpt-on-wechat/blob/master/config.py)中列出。**
@@ -196,7 +196,7 @@ python3 app.py # windows环境下该命令通
### 2.服务器部署
-使用nohup命令在后台运行程序:
+使用 nohup 命令在后台运行程序:
```bash
nohup python3 app.py & tail -f nohup.out # 在后台运行程序并通过日志输出二维码
@@ -208,11 +208,11 @@ nohup python3 app.py & tail -f nohup.out # 在后台运行程序并通
> **特殊指令:** 用户向机器人发送 **#reset** 即可清空该用户的上下文记忆。
-### 3.Docker部署
+### 3.Docker 部署
-> 使用docker部署无需下载源码和安装依赖,只需要获取 docker-compose.yml 配置文件并启动容器即可。
+> 使用 docker 部署无需下载源码和安装依赖,只需要获取 docker-compose.yml 配置文件并启动容器即可。
-> 前提是需要安装好 `docker` 及 `docker-compose`,安装成功的表现是执行 `docker -v` 和 `docker-compose version` (或 docker compose version) 可以查看到版本号,可前往 [docker官网](https://docs.docker.com/engine/install/) 进行下载。
+> 前提是需要安装好 `docker` 及 `docker-compose`,安装成功的表现是执行 `docker -v` 和 `docker-compose version` (或 docker compose version) 可以查看到版本号,可前往 [docker 官网](https://docs.docker.com/engine/install/) 进行下载。
**(1) 下载 docker-compose.yml 文件**
@@ -245,23 +245,23 @@ sudo docker logs -f chatgpt-on-wechat
**(3) 插件使用**
-如果需要在docker容器中修改插件配置,可通过挂载的方式完成,将 [插件配置文件](https://github.com/zhayujie/chatgpt-on-wechat/blob/master/plugins/config.json.template)
-重命名为 `config.json`,放置于 `docker-compose.yml` 相同目录下,并在 `docker-compose.yml` 中的 `chatgpt-on-wechat` 部分下添加 `volumes` 映射:
+如果需要在 docker 容器中修改插件配置,可通过挂载的方式完成,将 [插件配置文件](https://github.com/zhayujie/chatgpt-on-wechat/blob/master/plugins/config.json.template)
+重命名为 `config.json`,放置于 `docker-compose.yml` 相同目录下,并在 `docker-compose.yml` 中的 `chatgpt-on-wechat` 部分下添加 `volumes` 映射:
```
volumes:
- ./config.json:/app/plugins/config.json
```
-### 4. Railway部署
+### 4. Railway 部署
-> Railway 每月提供5刀和最多500小时的免费额度。 (07.11更新: 目前大部分账号已无法免费部署)
+> Railway 每月提供 5 刀和最多 500 小时的免费额度。 (07.11 更新:目前大部分账号已无法免费部署)
1. 进入 [Railway](https://railway.app/template/qApznZ?referralCode=RC3znh)
2. 点击 `Deploy Now` 按钮。
3. 设置环境变量来重载程序运行的参数,例如`open_ai_api_key`, `character_desc`。
-**一键部署:**
+**一键部署:**
[![Deploy on Railway](https://railway.app/button.svg)](https://railway.app/template/qApznZ?referralCode=RC3znh)
@@ -269,17 +269,17 @@ volumes:
# 🔎 常见问题
-FAQs:
+FAQs:
或直接在线咨询 [项目小助手](https://link-ai.tech/app/Kv2fXJcH) (语料持续完善中,回复仅供参考)
# 🛠️ 开发
-欢迎接入更多应用,参考 [Terminal代码](https://github.com/zhayujie/chatgpt-on-wechat/blob/master/channel/terminal/terminal_channel.py) 实现接收和发送消息逻辑即可接入。 同时欢迎增加新的插件,参考 [插件说明文档](https://github.com/zhayujie/chatgpt-on-wechat/tree/master/plugins)。
+欢迎接入更多应用,参考 [Terminal 代码](https://github.com/zhayujie/chatgpt-on-wechat/blob/master/channel/terminal/terminal_channel.py) 实现接收和发送消息逻辑即可接入。同时欢迎增加新的插件,参考 [插件说明文档](https://github.com/zhayujie/chatgpt-on-wechat/tree/master/plugins)。
# ✉ 联系
-欢迎提交PR、Issues,以及Star支持一下。程序运行遇到问题可以查看 [常见问题列表](https://github.com/zhayujie/chatgpt-on-wechat/wiki/FAQs) ,其次前往 [Issues](https://github.com/zhayujie/chatgpt-on-wechat/issues) 中搜索。个人开发者可加入开源交流群参与更多讨论,企业用户可联系[产品顾问](https://img-1317903499.cos.ap-guangzhou.myqcloud.com/docs/product-manager-qrcode.jpg)咨询。
+欢迎提交 PR、Issues,以及 Star 支持一下。程序运行遇到问题可以查看 [常见问题列表](https://github.com/zhayujie/chatgpt-on-wechat/wiki/FAQs) ,其次前往 [Issues](https://github.com/zhayujie/chatgpt-on-wechat/issues) 中搜索。个人开发者可加入开源交流群参与更多讨论,企业用户可联系[产品顾问](https://img-1317903499.cos.ap-guangzhou.myqcloud.com/docs/product-manager-qrcode.jpg)咨询。
# 🌟 贡献者
diff --git a/bot/ali/ali_qwen_bot.py b/bot/ali/ali_qwen_bot.py
index ae9d7674b..7f870f7c3 100644
--- a/bot/ali/ali_qwen_bot.py
+++ b/bot/ali/ali_qwen_bot.py
@@ -90,7 +90,7 @@ def reply(self, query, context=None):
return reply
else:
- reply = Reply(ReplyType.ERROR, "Bot不支持处理{}类型的消息".format(context.type))
+ reply = Reply(ReplyType.ERROR, "Bot 不支持处理{}类型的消息".format(context.type))
return reply
def reply_text(self, session: AliQwenSession, retry_count=0) -> dict:
@@ -103,7 +103,7 @@ def reply_text(self, session: AliQwenSession, retry_count=0) -> dict:
try:
prompt, history = self.convert_messages_format(session.messages)
self.update_api_key_if_expired()
- # NOTE 阿里百炼的call()函数未提供temperature参数,考虑到temperature和top_p参数作用相同,取两者较小的值作为top_p参数传入,详情见文档 https://help.aliyun.com/document_detail/2587502.htm
+ # NOTE 阿里百炼的 call() 函数未提供 temperature 参数,考虑到 temperature 和 top_p 参数作用相同,取两者较小的值作为 top_p 参数传入,详情见文档 https://help.aliyun.com/document_detail/2587502.htm
response = broadscope_bailian.Completions().call(app_id=self.app_id(), prompt=prompt, history=history, top_p=min(self.temperature(), self.top_p()))
completion_content = self.get_completion_content(response, self.node_id())
completion_tokens, total_tokens = self.calc_tokens(session.messages, completion_content)
@@ -173,7 +173,7 @@ def convert_messages_format(self, messages) -> Tuple[str, List[ChatQaMessage]]:
if user_content == '':
raise Exception('no user message')
if system_content != '':
- # NOTE 模拟系统消息,测试发现人格描述以"你需要扮演ChatGPT"开头能够起作用,而以"你是ChatGPT"开头模型会直接否认
+ # NOTE 模拟系统消息,测试发现人格描述以"你需要扮演 ChatGPT"开头能够起作用,而以"你是 ChatGPT"开头模型会直接否认
system_qa = ChatQaMessage(system_content, '好的,我会严格按照你的设定回答问题')
history.insert(0, system_qa)
logger.debug("[QWEN] converted qa messages: {}".format([item.to_dict() for item in history]))
@@ -186,7 +186,7 @@ def get_completion_content(self, response, node_id):
text = response['Data']['Text']
if node_id == '':
return text
- # TODO: 当使用流程编排创建大模型应用时,响应结构如下,最终结果在['finalResult'][node_id]['response']['text']中,暂时先这么写
+ # TODO: 当使用流程编排创建大模型应用时,响应结构如下,最终结果在 ['finalResult'][node_id]['response']['text'] 中,暂时先这么写
# {
# 'Success': True,
# 'Code': None,
@@ -194,7 +194,7 @@ def get_completion_content(self, response, node_id):
# 'Data': {
# 'ResponseId': '9822f38dbacf4c9b8daf5ca03a2daf15',
# 'SessionId': 'session_id',
- # 'Text': '{"finalResult":{"LLM_T7islK":{"params":{"modelId":"qwen-plus-v1","prompt":"${systemVars.query}${bizVars.Text}"},"response":{"text":"作为一个AI语言模型,我没有年龄,因为我没有生日。\n我只是一个程序,没有生命和身体。"}}}}',
+ # 'Text': '{"finalResult":{"LLM_T7islK":{"params":{"modelId":"qwen-plus-v1","prompt":"${systemVars.query}${bizVars.Text}"},"response":{"text":"作为一个 AI 语言模型,我没有年龄,因为我没有生日。\n我只是一个程序,没有生命和身体。"}}}}',
# 'Thoughts': [],
# 'Debug': {},
# 'DocReferences': []
diff --git a/bot/ali/ali_qwen_session.py b/bot/ali/ali_qwen_session.py
index 0eb1c4a1e..f5965c4d2 100644
--- a/bot/ali/ali_qwen_session.py
+++ b/bot/ali/ali_qwen_session.py
@@ -53,9 +53,9 @@ def calc_tokens(self):
def num_tokens_from_messages(messages, model):
"""Returns the number of tokens used by a list of messages."""
- # 官方token计算规则:"对于中文文本来说,1个token通常对应一个汉字;对于英文文本来说,1个token通常对应3至4个字母或1个单词"
+ # 官方 token 计算规则:"对于中文文本来说,1 个 token 通常对应一个汉字;对于英文文本来说,1 个 token 通常对应 3 至 4 个字母或 1 个单词"
# 详情请产看文档:https://help.aliyun.com/document_detail/2586397.html
- # 目前根据字符串长度粗略估计token数,不影响正常使用
+ # 目前根据字符串长度粗略估计 token 数,不影响正常使用
tokens = 0
for msg in messages:
tokens += len(msg["content"])
diff --git a/bot/baidu/baidu_unit_bot.py b/bot/baidu/baidu_unit_bot.py
index f7714e4f4..bcfdedb71 100644
--- a/bot/baidu/baidu_unit_bot.py
+++ b/bot/baidu/baidu_unit_bot.py
@@ -6,7 +6,7 @@
from bridge.reply import Reply, ReplyType
-# Baidu Unit对话接口 (可用, 但能力较弱)
+# Baidu Unit 对话接口 (可用,但能力较弱)
class BaiduUnitBot(Bot):
def reply(self, query, context=None):
token = self.get_token()
diff --git a/bot/baidu/baidu_wenxin.py b/bot/baidu/baidu_wenxin.py
index f35e0fa38..b7cc6dd22 100644
--- a/bot/baidu/baidu_wenxin.py
+++ b/bot/baidu/baidu_wenxin.py
@@ -94,13 +94,13 @@ def reply_text(self, session: BaiduWenxinSession, retry_count=0):
logger.warn("[BAIDU] Exception: {}".format(e))
need_retry = False
self.sessions.clear_session(session.session_id)
- result = {"completion_tokens": 0, "content": "出错了: {}".format(e)}
+ result = {"completion_tokens": 0, "content": "出错了:{}".format(e)}
return result
def get_access_token(self):
"""
使用 AK,SK 生成鉴权签名(Access Token)
- :return: access_token,或是None(如果错误)
+ :return: access_token,或是 None(如果错误)
"""
url = "https://aip.baidubce.com/oauth/2.0/token"
params = {"grant_type": "client_credentials", "client_id": BAIDU_API_KEY, "client_secret": BAIDU_SECRET_KEY}
diff --git a/bot/baidu/baidu_wenxin_session.py b/bot/baidu/baidu_wenxin_session.py
index 5ba2f1787..54e7bc655 100644
--- a/bot/baidu/baidu_wenxin_session.py
+++ b/bot/baidu/baidu_wenxin_session.py
@@ -14,7 +14,7 @@ class BaiduWenxinSession(Session):
def __init__(self, session_id, system_prompt=None, model="gpt-3.5-turbo"):
super().__init__(session_id, system_prompt)
self.model = model
- # 百度文心不支持system prompt
+ # 百度文心不支持 system prompt
# self.reset()
def discard_exceeding(self, max_tokens, cur_tokens=None):
@@ -47,7 +47,7 @@ def num_tokens_from_messages(messages, model):
"""Returns the number of tokens used by a list of messages."""
tokens = 0
for msg in messages:
- # 官方token计算规则暂不明确: "大约为 token数为 "中文字 + 其他语种单词数 x 1.3"
+ # 官方 token 计算规则暂不明确: "大约为 token 数为 "中文字 + 其他语种单词数 x 1.3"
# 这里先直接根据字数粗略估算吧,暂不影响正常使用,仅在判断是否丢弃历史会话的时候会有偏差
tokens += len(msg["content"])
return tokens
diff --git a/bot/bot_factory.py b/bot/bot_factory.py
index 3b31af02b..43d875698 100644
--- a/bot/bot_factory.py
+++ b/bot/bot_factory.py
@@ -12,19 +12,19 @@ def create_bot(bot_type):
:return: bot instance
"""
if bot_type == const.BAIDU:
- # 替换Baidu Unit为Baidu文心千帆对话接口
+ # 替换 Baidu Unit 为 Baidu 文心千帆对话接口
# from bot.baidu.baidu_unit_bot import BaiduUnitBot
# return BaiduUnitBot()
from bot.baidu.baidu_wenxin import BaiduWenxinBot
return BaiduWenxinBot()
elif bot_type == const.CHATGPT:
- # ChatGPT 网页端web接口
+ # ChatGPT 网页端 web 接口
from bot.chatgpt.chat_gpt_bot import ChatGPTBot
return ChatGPTBot()
elif bot_type == const.OPEN_AI:
- # OpenAI 官方对话模型API
+ # OpenAI 官方对话模型 API
from bot.openai.open_ai_bot import OpenAIBot
return OpenAIBot()
diff --git a/bot/chatgpt/chat_gpt_bot.py b/bot/chatgpt/chat_gpt_bot.py
index 979ce4c4e..3683b83ed 100644
--- a/bot/chatgpt/chat_gpt_bot.py
+++ b/bot/chatgpt/chat_gpt_bot.py
@@ -17,7 +17,7 @@
from config import conf, load_config
-# OpenAI对话模型API (可用)
+# OpenAI 对话模型 API (可用)
class ChatGPTBot(Bot, OpenAIImage):
def __init__(self):
super().__init__()
@@ -34,12 +34,12 @@ def __init__(self):
self.sessions = SessionManager(ChatGPTSession, model=conf().get("model") or "gpt-3.5-turbo")
self.args = {
"model": conf().get("model") or "gpt-3.5-turbo", # 对话模型的名称
- "temperature": conf().get("temperature", 0.9), # 值在[0,1]之间,越大表示回复越具有不确定性
+ "temperature": conf().get("temperature", 0.9), # 值在 [0,1] 之间,越大表示回复越具有不确定性
# "max_tokens":4096, # 回复最大的字符数
"top_p": conf().get("top_p", 1),
- "frequency_penalty": conf().get("frequency_penalty", 0.0), # [-2,2]之间,该值越大则更倾向于产生不同的内容
- "presence_penalty": conf().get("presence_penalty", 0.0), # [-2,2]之间,该值越大则更倾向于产生不同的内容
- "request_timeout": conf().get("request_timeout", None), # 请求超时时间,openai接口默认设置为600,对于难问题一般需要较长时间
+ "frequency_penalty": conf().get("frequency_penalty", 0.0), # [-2,2] 之间,该值越大则更倾向于产生不同的内容
+ "presence_penalty": conf().get("presence_penalty", 0.0), # [-2,2] 之间,该值越大则更倾向于产生不同的内容
+ "request_timeout": conf().get("request_timeout", None), # 请求超时时间,openai 接口默认设置为 600,对于难问题一般需要较长时间
"timeout": conf().get("request_timeout", None), # 重试超时时间,在这个时间内,将会自动重试
}
@@ -103,7 +103,7 @@ def reply(self, query, context=None):
reply = Reply(ReplyType.ERROR, retstring)
return reply
else:
- reply = Reply(ReplyType.ERROR, "Bot不支持处理{}类型的消息".format(context.type))
+ reply = Reply(ReplyType.ERROR, "Bot 不支持处理{}类型的消息".format(context.type))
return reply
def reply_text(self, session: ChatGPTSession, api_key=None, args=None, retry_count=0) -> dict:
diff --git a/bot/claude/claude_ai_bot.py b/bot/claude/claude_ai_bot.py
index faad274cb..ee5c7dd12 100644
--- a/bot/claude/claude_ai_bot.py
+++ b/bot/claude/claude_ai_bot.py
@@ -47,7 +47,7 @@ def reply(self, query, context: Context = None) -> Reply:
reply = Reply(ReplyType.ERROR, res)
return reply
else:
- reply = Reply(ReplyType.ERROR, "Bot不支持处理{}类型的消息".format(context.type))
+ reply = Reply(ReplyType.ERROR, "Bot 不支持处理{}类型的消息".format(context.type))
return reply
def get_organization_id(self):
@@ -71,10 +71,10 @@ def get_organization_id(self):
except:
if "App unavailable" in response.text:
logger.error("IP error: The IP is not allowed to be used on Claude")
- self.error = "ip所在地区不被claude支持"
+ self.error = "ip 所在地区不被 claude 支持"
elif "Invalid authorization" in response.text:
logger.error("Cookie error: Invalid authorization of claude, check cookie please.")
- self.error = "无法通过claude身份验证,请检查cookie"
+ self.error = "无法通过 claude 身份验证,请检查 cookie"
return None
return uuid
@@ -93,8 +93,8 @@ def check_cookie(self):
def create_new_chat(self, con_uuid):
"""
- 新建claude对话实体
- :param con_uuid: 对话id
+ 新建 claude 对话实体
+ :param con_uuid: 对话 id
:return:
"""
url = f"https://claude.ai/api/organizations/{self.org_uuid}/chat_conversations"
@@ -193,7 +193,7 @@ def _chat(self, query, context, retry_count=0) -> Reply:
if "rate limi" in reply_content:
logger.error("rate limit error: The conversation has reached the system speed limit and is synchronized with Cladue. Please go to the official website to check the lifting time")
- return Reply(ReplyType.ERROR, "对话达到系统速率限制,与cladue同步,请进入官网查看解除限制时间")
+ return Reply(ReplyType.ERROR, "对话达到系统速率限制,与 cladue 同步,请进入官网查看解除限制时间")
logger.info(f"[CLAUDE] reply={reply_content}, total_tokens=invisible")
self.sessions.session_reply(reply_content, session_id, 100)
return Reply(ReplyType.TEXT, reply_content)
diff --git a/bot/claude/claude_ai_session.py b/bot/claude/claude_ai_session.py
index ede9e51d4..7f7417e2c 100644
--- a/bot/claude/claude_ai_session.py
+++ b/bot/claude/claude_ai_session.py
@@ -5,5 +5,5 @@ class ClaudeAiSession(Session):
def __init__(self, session_id, system_prompt=None, model="claude"):
super().__init__(session_id, system_prompt)
self.model = model
- # claude逆向不支持role prompt
+ # claude 逆向不支持 role prompt
# self.reset()
diff --git a/bot/claudeapi/claude_api_bot.py b/bot/claudeapi/claude_api_bot.py
index dfb4b061a..14cb3a728 100644
--- a/bot/claudeapi/claude_api_bot.py
+++ b/bot/claudeapi/claude_api_bot.py
@@ -19,7 +19,7 @@
user_session = dict()
-# OpenAI对话模型API (可用)
+# OpenAI 对话模型 API (可用)
class ClaudeAPIBot(Bot, OpenAIImage):
def __init__(self):
super().__init__()
diff --git a/bot/dashscope/dashscope_bot.py b/bot/dashscope/dashscope_bot.py
index 07554c4d2..cff37955d 100644
--- a/bot/dashscope/dashscope_bot.py
+++ b/bot/dashscope/dashscope_bot.py
@@ -19,7 +19,7 @@
"qwen-max": dashscope.Generation.Models.qwen_max,
"qwen-bailian-v1": dashscope.Generation.Models.bailian_v1
}
-# ZhipuAI对话模型API
+# ZhipuAI 对话模型 API
class DashscopeBot(Bot):
def __init__(self):
super().__init__()
@@ -70,7 +70,7 @@ def reply(self, query, context=None):
logger.debug("[DASHSCOPE] reply {} used 0 tokens.".format(reply_content))
return reply
else:
- reply = Reply(ReplyType.ERROR, "Bot不支持处理{}类型的消息".format(context.type))
+ reply = Reply(ReplyType.ERROR, "Bot 不支持处理{}类型的消息".format(context.type))
return reply
def reply_text(self, session: DashscopeSession, retry_count=0) -> dict:
diff --git a/bot/gemini/google_gemini_bot.py b/bot/gemini/google_gemini_bot.py
index 6132b7856..880a6f944 100644
--- a/bot/gemini/google_gemini_bot.py
+++ b/bot/gemini/google_gemini_bot.py
@@ -16,13 +16,13 @@
from bot.baidu.baidu_wenxin_session import BaiduWenxinSession
-# OpenAI对话模型API (可用)
+# OpenAI 对话模型 API (可用)
class GoogleGeminiBot(Bot):
def __init__(self):
super().__init__()
self.api_key = conf().get("gemini_api_key")
- # 复用文心的token计算方式
+ # 复用文心的 token 计算方式
self.sessions = SessionManager(BaiduWenxinSession, model=conf().get("model") or "gpt-3.5-turbo")
def reply(self, query, context: Context = None) -> Reply:
diff --git a/bot/linkai/link_ai_bot.py b/bot/linkai/link_ai_bot.py
index 3fe813164..6f74df95d 100644
--- a/bot/linkai/link_ai_bot.py
+++ b/bot/linkai/link_ai_bot.py
@@ -41,7 +41,7 @@ def reply(self, query, context: Context = None) -> Reply:
reply = Reply(ReplyType.ERROR, res)
return reply
else:
- reply = Reply(ReplyType.ERROR, "Bot不支持处理{}类型的消息".format(context.type))
+ reply = Reply(ReplyType.ERROR, "Bot 不支持处理{}类型的消息".format(context.type))
return reply
def _chat(self, query, context, retry_count=0) -> Reply:
@@ -86,11 +86,11 @@ def _chat(self, query, context, retry_count=0) -> Reply:
body = {
"app_code": app_code,
"messages": session_message,
- "model": model, # 对话模型的名称, 支持 gpt-3.5-turbo, gpt-3.5-turbo-16k, gpt-4, wenxin, xunfei
+ "model": model, # 对话模型的名称,支持 gpt-3.5-turbo, gpt-3.5-turbo-16k, gpt-4, wenxin, xunfei
"temperature": conf().get("temperature"),
"top_p": conf().get("top_p", 1),
- "frequency_penalty": conf().get("frequency_penalty", 0.0), # [-2,2]之间,该值越大则更倾向于产生不同的内容
- "presence_penalty": conf().get("presence_penalty", 0.0), # [-2,2]之间,该值越大则更倾向于产生不同的内容
+ "frequency_penalty": conf().get("frequency_penalty", 0.0), # [-2,2] 之间,该值越大则更倾向于产生不同的内容
+ "presence_penalty": conf().get("presence_penalty", 0.0), # [-2,2] 之间,该值越大则更倾向于产生不同的内容
"session_id": session_id,
"sender_id": session_id,
"channel_type": conf().get("channel_type", "wx")
@@ -250,11 +250,11 @@ def reply_text(self, session: ChatGPTSession, app_code="", retry_count=0) -> dic
body = {
"app_code": app_code,
"messages": session.messages,
- "model": conf().get("model") or "gpt-3.5-turbo", # 对话模型的名称, 支持 gpt-3.5-turbo, gpt-3.5-turbo-16k, gpt-4, wenxin, xunfei
+ "model": conf().get("model") or "gpt-3.5-turbo", # 对话模型的名称,支持 gpt-3.5-turbo, gpt-3.5-turbo-16k, gpt-4, wenxin, xunfei
"temperature": conf().get("temperature"),
"top_p": conf().get("top_p", 1),
- "frequency_penalty": conf().get("frequency_penalty", 0.0), # [-2,2]之间,该值越大则更倾向于产生不同的内容
- "presence_penalty": conf().get("presence_penalty", 0.0), # [-2,2]之间,该值越大则更倾向于产生不同的内容
+ "frequency_penalty": conf().get("frequency_penalty", 0.0), # [-2,2] 之间,该值越大则更倾向于产生不同的内容
+ "presence_penalty": conf().get("presence_penalty", 0.0), # [-2,2] 之间,该值越大则更倾向于产生不同的内容
}
if self.args.get("max_tokens"):
body["max_tokens"] = self.args.get("max_tokens")
diff --git a/bot/moonshot/moonshot_bot.py b/bot/moonshot/moonshot_bot.py
index 7d2589cda..b75c67003 100644
--- a/bot/moonshot/moonshot_bot.py
+++ b/bot/moonshot/moonshot_bot.py
@@ -14,7 +14,7 @@
import requests
-# ZhipuAI对话模型API
+# ZhipuAI 对话模型 API
class MoonshotBot(Bot):
def __init__(self):
super().__init__()
@@ -76,7 +76,7 @@ def reply(self, query, context=None):
logger.debug("[MOONSHOT_AI] reply {} used 0 tokens.".format(reply_content))
return reply
else:
- reply = Reply(ReplyType.ERROR, "Bot不支持处理{}类型的消息".format(context.type))
+ reply = Reply(ReplyType.ERROR, "Bot 不支持处理{}类型的消息".format(context.type))
return reply
def reply_text(self, session: MoonshotSession, args=None, retry_count=0) -> dict:
@@ -121,7 +121,7 @@ def reply_text(self, session: MoonshotSession, args=None, retry_count=0) -> dict
logger.warn(f"[MOONSHOT_AI] do retry, times={retry_count}")
need_retry = retry_count < 2
elif res.status_code == 401:
- result["content"] = "授权失败,请检查API Key是否正确"
+ result["content"] = "授权失败,请检查 API Key 是否正确"
elif res.status_code == 429:
result["content"] = "请求过于频繁,请稍后再试"
need_retry = retry_count < 2
diff --git a/bot/openai/open_ai_bot.py b/bot/openai/open_ai_bot.py
index 160562526..790a5281a 100644
--- a/bot/openai/open_ai_bot.py
+++ b/bot/openai/open_ai_bot.py
@@ -17,7 +17,7 @@
user_session = dict()
-# OpenAI对话模型API (可用)
+# OpenAI 对话模型 API (可用)
class OpenAIBot(Bot, OpenAIImage):
def __init__(self):
super().__init__()
@@ -31,12 +31,12 @@ def __init__(self):
self.sessions = SessionManager(OpenAISession, model=conf().get("model") or "text-davinci-003")
self.args = {
"model": conf().get("model") or "text-davinci-003", # 对话模型的名称
- "temperature": conf().get("temperature", 0.9), # 值在[0,1]之间,越大表示回复越具有不确定性
+ "temperature": conf().get("temperature", 0.9), # 值在 [0,1] 之间,越大表示回复越具有不确定性
"max_tokens": 1200, # 回复最大的字符数
"top_p": 1,
- "frequency_penalty": conf().get("frequency_penalty", 0.0), # [-2,2]之间,该值越大则更倾向于产生不同的内容
- "presence_penalty": conf().get("presence_penalty", 0.0), # [-2,2]之间,该值越大则更倾向于产生不同的内容
- "request_timeout": conf().get("request_timeout", None), # 请求超时时间,openai接口默认设置为600,对于难问题一般需要较长时间
+ "frequency_penalty": conf().get("frequency_penalty", 0.0), # [-2,2] 之间,该值越大则更倾向于产生不同的内容
+ "presence_penalty": conf().get("presence_penalty", 0.0), # [-2,2] 之间,该值越大则更倾向于产生不同的内容
+ "request_timeout": conf().get("request_timeout", None), # 请求超时时间,openai 接口默认设置为 600,对于难问题一般需要较长时间
"timeout": conf().get("request_timeout", None), # 重试超时时间,在这个时间内,将会自动重试
"stop": ["\n\n\n"],
}
diff --git a/bot/openai/open_ai_image.py b/bot/openai/open_ai_image.py
index 3ff56c175..2a1f3a824 100644
--- a/bot/openai/open_ai_image.py
+++ b/bot/openai/open_ai_image.py
@@ -8,7 +8,7 @@
from config import conf
-# OPENAI提供的画图接口
+# OPENAI 提供的画图接口
class OpenAIImage(object):
def __init__(self):
openai.api_key = conf().get("open_ai_api_key")
@@ -25,7 +25,7 @@ def create_img(self, query, retry_count=0, api_key=None, api_base=None):
prompt=query, # 图片描述
n=1, # 每次生成图片的数量
model=conf().get("text_to_image") or "dall-e-2",
- # size=conf().get("image_create_size", "256x256"), # 图片大小,可选有 256x256, 512x512, 1024x1024
+ # size=conf().get("image_create_size", "256x256"), # 图片大小,可选有 256x256, 512x512, 1024x1024
)
image_url = response["data"][0]["url"]
logger.info("[OPEN_AI] image_url={}".format(image_url))
diff --git a/bot/session_manager.py b/bot/session_manager.py
index a6e89f956..9e6c43449 100644
--- a/bot/session_manager.py
+++ b/bot/session_manager.py
@@ -48,15 +48,15 @@ def __init__(self, sessioncls, **session_args):
def build_session(self, session_id, system_prompt=None):
"""
- 如果session_id不在sessions中,创建一个新的session并添加到sessions中
- 如果system_prompt不会空,会更新session的system_prompt并重置session
+ 如果 session_id 不在 sessions 中,创建一个新的 session 并添加到 sessions 中
+ 如果 system_prompt 不会空,会更新 session 的 system_prompt 并重置 session
"""
if session_id is None:
return self.sessioncls(session_id, system_prompt, **self.session_args)
if session_id not in self.sessions:
self.sessions[session_id] = self.sessioncls(session_id, system_prompt, **self.session_args)
- elif system_prompt is not None: # 如果有新的system_prompt,更新并重置session
+ elif system_prompt is not None: # 如果有新的 system_prompt,更新并重置 session
self.sessions[session_id].set_system_prompt(system_prompt)
session = self.sessions[session_id]
return session
diff --git a/bot/xunfei/xunfei_spark_bot.py b/bot/xunfei/xunfei_spark_bot.py
index 9ca6b964a..b00849e31 100644
--- a/bot/xunfei/xunfei_spark_bot.py
+++ b/bot/xunfei/xunfei_spark_bot.py
@@ -40,18 +40,18 @@ def __init__(self):
self.app_id = conf().get("xunfei_app_id")
self.api_key = conf().get("xunfei_api_key")
self.api_secret = conf().get("xunfei_api_secret")
- # 默认使用v2.0版本: "generalv2"
- # v1.5版本为 "general"
- # v3.0版本为: "generalv3"
+ # 默认使用 v2.0 版本:"generalv2"
+ # v1.5 版本为 "general"
+ # v3.0 版本为:"generalv3"
self.domain = "generalv3"
- # 默认使用v2.0版本: "ws://spark-api.xf-yun.com/v2.1/chat"
- # v1.5版本为: "ws://spark-api.xf-yun.com/v1.1/chat"
- # v3.0版本为: "ws://spark-api.xf-yun.com/v3.1/chat"
- # v3.5版本为: "wss://spark-api.xf-yun.com/v3.5/chat"
+ # 默认使用 v2.0 版本:"ws://spark-api.xf-yun.com/v2.1/chat"
+ # v1.5 版本为:"ws://spark-api.xf-yun.com/v1.1/chat"
+ # v3.0 版本为:"ws://spark-api.xf-yun.com/v3.1/chat"
+ # v3.5 版本为:"wss://spark-api.xf-yun.com/v3.5/chat"
self.spark_url = "wss://spark-api.xf-yun.com/v3.5/chat"
self.host = urlparse(self.spark_url).netloc
self.path = urlparse(self.spark_url).path
- # 和wenxin使用相同的session机制
+ # 和 wenxin 使用相同的 session 机制
self.sessions = SessionManager(BaiduWenxinSession, model=const.XUNFEI)
def reply(self, query, context: Context = None) -> Reply:
@@ -99,7 +99,7 @@ def reply(self, query, context: Context = None) -> Reply:
return reply
else:
reply = Reply(ReplyType.ERROR,
- "Bot不支持处理{}类型的消息".format(context.type))
+ "Bot 不支持处理{}类型的消息".format(context.type))
return reply
def create_web_socket(self, prompt, session_id, temperature=0.5):
@@ -124,9 +124,9 @@ def gen_request_id(self, session_id: str):
return session_id + "_" + str(int(time.time())) + "" + str(
random.randint(0, 100))
- # 生成url
+ # 生成 url
def create_url(self):
- # 生成RFC1123格式的时间戳
+ # 生成 RFC1123 格式的时间戳
now = datetime.now()
date = format_date_time(mktime(now.timetuple()))
@@ -135,7 +135,7 @@ def create_url(self):
signature_origin += "date: " + date + "\n"
signature_origin += "GET " + self.path + " HTTP/1.1"
- # 进行hmac-sha256进行加密
+ # 进行 hmac-sha256 进行加密
signature_sha = hmac.new(self.api_secret.encode('utf-8'),
signature_origin.encode('utf-8'),
digestmod=hashlib.sha256).digest()
@@ -151,14 +151,14 @@ def create_url(self):
# 将请求的鉴权参数组合为字典
v = {"authorization": authorization, "date": date, "host": self.host}
- # 拼接鉴权参数,生成url
+ # 拼接鉴权参数,生成 url
url = self.spark_url + '?' + urlencode(v)
- # 此处打印出建立连接时候的url,参考本demo的时候可取消上方打印的注释,比对相同参数时生成的url与自己代码生成的url是否一致
+ # 此处打印出建立连接时候的 url,参考本 demo 的时候可取消上方打印的注释,比对相同参数时生成的 url 与自己代码生成的 url 是否一致
return url
def gen_params(self, appid, domain, question):
"""
- 通过appid和用户的提问来生成请参数
+ 通过 appid 和用户的提问来生成请参数
"""
data = {
"header": {
@@ -189,18 +189,18 @@ def __init__(self, reply, usage=None, is_end=False):
self.usage = usage
-# 收到websocket错误的处理
+# 收到 websocket 错误的处理
def on_error(ws, error):
logger.error(f"[XunFei] error: {str(error)}")
-# 收到websocket关闭的处理
+# 收到 websocket 关闭的处理
def on_close(ws, one, two):
data_queue = queue_map.get(ws.session_id)
data_queue.put("END")
-# 收到websocket连接建立的处理
+# 收到 websocket 连接建立的处理
def on_open(ws):
logger.info(f"[XunFei] Start websocket, session_id={ws.session_id}")
thread.start_new_thread(run, (ws, ))
@@ -216,12 +216,12 @@ def run(ws, *args):
# Websocket 操作
-# 收到websocket消息的处理
+# 收到 websocket 消息的处理
def on_message(ws, message):
data = json.loads(message)
code = data['header']['code']
if code != 0:
- logger.error(f'请求错误: {code}, {data}')
+ logger.error(f'请求错误:{code}, {data}')
ws.close()
else:
choices = data["payload"]["choices"]
@@ -243,7 +243,7 @@ def on_message(ws, message):
def gen_params(appid, domain, question, temperature=0.5):
"""
- 通过appid和用户的提问来生成请参数
+ 通过 appid 和用户的提问来生成请参数
"""
data = {
"header": {
diff --git a/bot/zhipuai/zhipu_ai_image.py b/bot/zhipuai/zhipu_ai_image.py
index 84eb5671e..bd1a266c0 100644
--- a/bot/zhipuai/zhipu_ai_image.py
+++ b/bot/zhipuai/zhipu_ai_image.py
@@ -2,7 +2,7 @@
from config import conf
-# ZhipuAI提供的画图接口
+# ZhipuAI 提供的画图接口
class ZhipuAIImage(object):
def __init__(self):
@@ -18,7 +18,7 @@ def create_img(self, query, retry_count=0, api_key=None, api_base=None):
prompt=query,
n=1, # 每次生成图片的数量
model=conf().get("text_to_image") or "cogview-3",
- size=conf().get("image_create_size", "1024x1024"), # 图片大小,可选有 256x256, 512x512, 1024x1024
+ size=conf().get("image_create_size", "1024x1024"), # 图片大小,可选有 256x256, 512x512, 1024x1024
quality="standard",
)
image_url = response.data[0].url
diff --git a/bot/zhipuai/zhipuai_bot.py b/bot/zhipuai/zhipuai_bot.py
index d8eed4d35..819b29332 100644
--- a/bot/zhipuai/zhipuai_bot.py
+++ b/bot/zhipuai/zhipuai_bot.py
@@ -15,15 +15,15 @@
from zhipuai import ZhipuAI
-# ZhipuAI对话模型API
+# ZhipuAI 对话模型 API
class ZHIPUAIBot(Bot, ZhipuAIImage):
def __init__(self):
super().__init__()
self.sessions = SessionManager(ZhipuAISession, model=conf().get("model") or "ZHIPU_AI")
self.args = {
"model": conf().get("model") or "glm-4", # 对话模型的名称
- "temperature": conf().get("temperature", 0.9), # 值在(0,1)之间(智谱AI 的温度不能取 0 或者 1)
- "top_p": conf().get("top_p", 0.7), # 值在(0,1)之间(智谱AI 的 top_p 不能取 0 或者 1)
+ "temperature": conf().get("temperature", 0.9), # 值在 (0,1) 之间 (智谱 AI 的温度不能取 0 或者 1)
+ "top_p": conf().get("top_p", 0.7), # 值在 (0,1) 之间 (智谱 AI 的 top_p 不能取 0 或者 1)
}
self.client = ZhipuAI(api_key=conf().get("zhipu_ai_api_key"))
@@ -87,7 +87,7 @@ def reply(self, query, context=None):
return reply
else:
- reply = Reply(ReplyType.ERROR, "Bot不支持处理{}类型的消息".format(context.type))
+ reply = Reply(ReplyType.ERROR, "Bot 不支持处理{}类型的消息".format(context.type))
return reply
def reply_text(self, session: ZhipuAISession, api_key=None, args=None, retry_count=0) -> dict:
diff --git a/bridge/bridge.py b/bridge/bridge.py
index 6733701e3..b775676ef 100644
--- a/bridge/bridge.py
+++ b/bridge/bridge.py
@@ -91,6 +91,6 @@ def find_chat_bot(self, bot_type: str):
def reset_bot(self):
"""
- 重置bot路由
+ 重置 bot 路由
"""
self.__init__()
diff --git a/bridge/reply.py b/bridge/reply.py
index f2293bdfb..3caeab9c4 100644
--- a/bridge/reply.py
+++ b/bridge/reply.py
@@ -7,10 +7,10 @@ class ReplyType(Enum):
TEXT = 1 # 文本
VOICE = 2 # 音频文件
IMAGE = 3 # 图片文件
- IMAGE_URL = 4 # 图片URL
- VIDEO_URL = 5 # 视频URL
+ IMAGE_URL = 4 # 图片 URL
+ VIDEO_URL = 5 # 视频 URL
FILE = 6 # 文件
- CARD = 7 # 微信名片,仅支持ntchat
+ CARD = 7 # 微信名片,仅支持 ntchat
INVITE_ROOM = 8 # 邀请好友进群
INFO = 9
ERROR = 10
diff --git a/channel/channel.py b/channel/channel.py
index c22534273..f8dab2d5e 100644
--- a/channel/channel.py
+++ b/channel/channel.py
@@ -24,7 +24,7 @@ def handle_text(self, msg):
"""
raise NotImplementedError
- # 统一的发送函数,每个Channel自行实现,根据reply的type字段发送不同类型的消息
+ # 统一的发送函数,每个 Channel 自行实现,根据 reply 的 type 字段发送不同类型的消息
def send(self, reply: Reply, context: Context):
"""
send message to user
diff --git a/channel/chat_channel.py b/channel/chat_channel.py
index b3f8d1aea..aaf3becbb 100644
--- a/channel/chat_channel.py
+++ b/channel/chat_channel.py
@@ -20,32 +20,32 @@
handler_pool = ThreadPoolExecutor(max_workers=8) # 处理消息的线程池
-# 抽象类, 它包含了与消息通道无关的通用处理逻辑
+# 抽象类,它包含了与消息通道无关的通用处理逻辑
class ChatChannel(Channel):
name = None # 登录的用户名
- user_id = None # 登录的用户id
- futures = {} # 记录每个session_id提交到线程池的future对象, 用于重置会话时把没执行的future取消掉,正在执行的不会被取消
- sessions = {} # 用于控制并发,每个session_id同时只能有一个context在处理
- lock = threading.Lock() # 用于控制对sessions的访问
+ user_id = None # 登录的用户 id
+ futures = {} # 记录每个 session_id 提交到线程池的 future 对象,用于重置会话时把没执行的 future 取消掉,正在执行的不会被取消
+ sessions = {} # 用于控制并发,每个 session_id 同时只能有一个 context 在处理
+ lock = threading.Lock() # 用于控制对 sessions 的访问
def __init__(self):
_thread = threading.Thread(target=self.consume)
_thread.setDaemon(True)
_thread.start()
- # 根据消息构造context,消息内容相关的触发项写在这里
+ # 根据消息构造 context,消息内容相关的触发项写在这里
def _compose_context(self, ctype: ContextType, content, **kwargs):
context = Context(ctype, content)
context.kwargs = kwargs
- # context首次传入时,origin_ctype是None,
- # 引入的起因是:当输入语音时,会嵌套生成两个context,第一步语音转文本,第二步通过文本生成文字回复。
- # origin_ctype用于第二步文本回复时,判断是否需要匹配前缀,如果是私聊的语音,就不需要匹配前缀
+ # context 首次传入时,origin_ctype 是 None,
+ # 引入的起因是:当输入语音时,会嵌套生成两个 context,第一步语音转文本,第二步通过文本生成文字回复。
+ # origin_ctype 用于第二步文本回复时,判断是否需要匹配前缀,如果是私聊的语音,就不需要匹配前缀
if "origin_ctype" not in context:
context["origin_ctype"] = ctype
- # context首次传入时,receiver是None,根据类型设置receiver
+ # context 首次传入时,receiver 是 None,根据类型设置 receiver
first_in = "receiver" not in context
- # 群名匹配过程,设置session_id和receiver
- if first_in: # context首次传入时,receiver是None,根据类型设置receiver
+ # 群名匹配过程,设置 session_id 和 receiver
+ if first_in: # context 首次传入时,receiver 是 None,根据类型设置 receiver
config = conf()
cmsg = context["msg"]
user_data = conf().get_user_data(cmsg.from_user_id)
@@ -89,7 +89,7 @@ def _compose_context(self, ctype: ContextType, content, **kwargs):
logger.debug("[WX]self message skipped")
return None
- # 消息内容匹配过程,并处理content
+ # 消息内容匹配过程,并处理 content
if ctype == ContextType.TEXT:
if first_in and "」\n- - - - - - -" in content: # 初次匹配 过滤引用消息
logger.debug(content)
@@ -140,7 +140,7 @@ def _compose_context(self, ctype: ContextType, content, **kwargs):
return None
match_prefix = check_prefix(content, conf().get("single_chat_prefix", [""]))
- if match_prefix is not None: # 判断如果匹配到自定义前缀,则返回过滤掉前缀+空格后的内容
+ if match_prefix is not None: # 判断如果匹配到自定义前缀,则返回过滤掉前缀 + 空格后的内容
content = content.replace(match_prefix, "", 1).strip()
elif context["origin_ctype"] == ContextType.VOICE: # 如果源消息是私聊的语音消息,允许不匹配前缀,放宽条件
pass
@@ -166,16 +166,16 @@ def _handle(self, context: Context):
if context is None or not context.content:
return
logger.debug("[WX] ready to handle context: {}".format(context))
- # reply的构建步骤
+ # reply 的构建步骤
reply = self._generate_reply(context)
logger.debug("[WX] ready to decorate reply: {}".format(reply))
- # reply的包装步骤
+ # reply 的包装步骤
if reply and reply.content:
reply = self._decorate_reply(context, reply)
- # reply的发送步骤
+ # reply 的发送步骤
self._send_reply(context, reply)
def _generate_reply(self, context: Context, reply: Reply = Reply()) -> Reply:
@@ -198,7 +198,7 @@ def _generate_reply(self, context: Context, reply: Reply = Reply()) -> Reply:
wav_path = os.path.splitext(file_path)[0] + ".wav"
try:
any_to_wav(file_path, wav_path)
- except Exception as e: # 转换失败,直接使用mp3,对于某些api,mp3也可以识别
+ except Exception as e: # 转换失败,直接使用 mp3,对于某些 api,mp3 也可以识别
logger.warning("[WX]any to wav error, use raw path. " + str(e))
wav_path = file_path
# 语音识别
@@ -246,7 +246,7 @@ def _decorate_reply(self, context: Context, reply: Reply) -> Reply:
if reply.type in self.NOT_SUPPORT_REPLYTYPE:
logger.error("[WX]reply type not support: " + str(reply.type))
reply.type = ReplyType.ERROR
- reply.content = "不支持发送的消息类型: " + str(reply.type)
+ reply.content = "不支持发送的消息类型:" + str(reply.type)
if reply.type == ReplyType.TEXT:
reply_text = reply.content
@@ -356,7 +356,7 @@ def consume(self):
semaphore.release()
time.sleep(0.1)
- # 取消session_id对应的所有任务,只能取消排队的消息和已提交线程池但未执行的任务
+ # 取消 session_id 对应的所有任务,只能取消排队的消息和已提交线程池但未执行的任务
def cancel_session(self, session_id):
with self.lock:
if session_id in self.sessions:
diff --git a/channel/chat_message.py b/channel/chat_message.py
index ac0e5c2be..882d8bb42 100644
--- a/channel/chat_message.py
+++ b/channel/chat_message.py
@@ -1,32 +1,32 @@
"""
-本类表示聊天消息,用于对itchat和wechaty的消息进行统一的封装。
+本类表示聊天消息,用于对 itchat 和 wechaty 的消息进行统一的封装。
-填好必填项(群聊6个,非群聊8个),即可接入ChatChannel,并支持插件,参考TerminalChannel
+填好必填项 (群聊 6 个,非群聊 8 个),即可接入 ChatChannel,并支持插件,参考 TerminalChannel
ChatMessage
-msg_id: 消息id (必填)
+msg_id: 消息 id (必填)
create_time: 消息创建时间
ctype: 消息类型 : ContextType (必填)
-content: 消息内容, 如果是声音/图片,这里是文件路径 (必填)
+content: 消息内容,如果是声音/图片,这里是文件路径 (必填)
-from_user_id: 发送者id (必填)
+from_user_id: 发送者 id (必填)
from_user_nickname: 发送者昵称
-to_user_id: 接收者id (必填)
+to_user_id: 接收者 id (必填)
to_user_nickname: 接收者昵称
-other_user_id: 对方的id,如果你是发送者,那这个就是接收者id,如果你是接收者,那这个就是发送者id,如果是群消息,那这一直是群id (必填)
+other_user_id: 对方的 id,如果你是发送者,那这个就是接收者 id,如果你是接收者,那这个就是发送者 id,如果是群消息,那这一直是群 id (必填)
other_user_nickname: 同上
is_group: 是否是群消息 (群聊必填)
-is_at: 是否被at
+is_at: 是否被 at
-- (群消息时,一般会存在实际发送者,是群内某个成员的id和昵称,下列项仅在群消息时存在)
-actual_user_id: 实际发送者id (群聊必填)
+- (群消息时,一般会存在实际发送者,是群内某个成员的 id 和昵称,下列项仅在群消息时存在)
+actual_user_id: 实际发送者 id (群聊必填)
actual_user_nickname:实际发送者昵称
self_display_name: 自身的展示名,设置群昵称时,该字段表示群昵称
-_prepare_fn: 准备函数,用于准备消息的内容,比如下载图片等,
+_prepare_fn: 准备函数,用于准备消息的内容,比如下载图片等,
_prepared: 是否已经调用过准备函数
_rawmsg: 原始消息对象
diff --git a/channel/dingtalk/dingtalk_channel.py b/channel/dingtalk/dingtalk_channel.py
index 22ef889bb..c5609a879 100644
--- a/channel/dingtalk/dingtalk_channel.py
+++ b/channel/dingtalk/dingtalk_channel.py
@@ -38,7 +38,7 @@ def __init__(self):
super().__init__()
super(dingtalk_stream.ChatbotHandler, self).__init__()
self.logger = self.setup_logger()
- # 历史消息id暂存,用于幂等控制
+ # 历史消息 id 暂存,用于幂等控制
self.receivedMsgs = ExpiredDict(60 * 60 * 7.1)
logger.info("[dingtalk] client_id={}, client_secret={} ".format(
self.dingtalk_client_id, self.dingtalk_client_secret))
diff --git a/channel/feishu/feishu_channel.py b/channel/feishu/feishu_channel.py
index 76fbbf1b6..36f21db09 100644
--- a/channel/feishu/feishu_channel.py
+++ b/channel/feishu/feishu_channel.py
@@ -34,7 +34,7 @@ class FeiShuChanel(ChatChannel):
def __init__(self):
super().__init__()
- # 历史消息id暂存,用于幂等控制
+ # 历史消息 id 暂存,用于幂等控制
self.receivedMsgs = ExpiredDict(60 * 60 * 7.1)
logger.info("[FeiShu] app_id={}, app_secret={} verification_token={}".format(
self.feishu_app_id, self.feishu_app_secret, self.feishu_token))
diff --git a/channel/feishu/feishu_message.py b/channel/feishu/feishu_message.py
index e2054c127..278ff1f14 100644
--- a/channel/feishu/feishu_message.py
+++ b/channel/feishu/feishu_message.py
@@ -31,7 +31,7 @@ def __init__(self, event: dict, is_group=False, access_token=None):
self.content = TmpDir().path() + file_key + "." + utils.get_path_suffix(file_name)
def _download_file():
- # 如果响应状态码是200,则将响应内容写入本地文件
+ # 如果响应状态码是 200,则将响应内容写入本地文件
url = f"https://open.feishu.cn/open-apis/im/v1/messages/{self.msg_id}/resources/{file_key}"
headers = {
"Authorization": "Bearer " + access_token,
diff --git a/channel/wechat/wechat_channel.py b/channel/wechat/wechat_channel.py
index b681e1243..aef4e3d91 100644
--- a/channel/wechat/wechat_channel.py
+++ b/channel/wechat/wechat_channel.py
@@ -56,7 +56,7 @@ def wrapper(self, cmsg: ChatMessage):
return
self.receivedMsgs[msgId] = True
create_time = cmsg.create_time # 消息时间戳
- if conf().get("hot_reload") == True and int(create_time) < int(time.time()) - 60: # 跳过1分钟前的历史消息
+ if conf().get("hot_reload") == True and int(create_time) < int(time.time()) - 60: # 跳过 1 分钟前的历史消息
logger.debug("[WX]history message {} skipped".format(msgId))
return
if cmsg.my_msg and not cmsg.is_group:
@@ -151,17 +151,17 @@ def loginCallback(self):
logger.debug("Login success")
_send_login_success()
- # handle_* 系列函数处理收到的消息后构造Context,然后传入produce函数中处理Context和发送回复
- # Context包含了消息的所有信息,包括以下属性
- # type 消息类型, 包括TEXT、VOICE、IMAGE_CREATE
- # content 消息内容,如果是TEXT类型,content就是文本内容,如果是VOICE类型,content就是语音文件名,如果是IMAGE_CREATE类型,content就是图片生成命令
- # kwargs 附加参数字典,包含以下的key:
- # session_id: 会话id
+ # handle_* 系列函数处理收到的消息后构造 Context,然后传入 produce 函数中处理 Context 和发送回复
+ # Context 包含了消息的所有信息,包括以下属性
+ # type 消息类型,包括 TEXT、VOICE、IMAGE_CREATE
+ # content 消息内容,如果是 TEXT 类型,content 就是文本内容,如果是 VOICE 类型,content 就是语音文件名,如果是 IMAGE_CREATE 类型,content 就是图片生成命令
+ # kwargs 附加参数字典,包含以下的 key:
+ # session_id: 会话 id
# isgroup: 是否是群聊
# receiver: 需要回复的对象
- # msg: ChatMessage消息对象
+ # msg: ChatMessage 消息对象
# origin_ctype: 原始消息类型,语音转文字后,私聊时如果匹配前缀失败,会根据初始消息是否是语音来放宽触发规则
- # desire_rtype: 希望回复类型,默认是文本回复,设置为ReplyType.VOICE是语音回复
+ # desire_rtype: 希望回复类型,默认是文本回复,设置为 ReplyType.VOICE 是语音回复
@time_checker
@_check
def handle_single(self, cmsg: ChatMessage):
@@ -206,7 +206,7 @@ def handle_group(self, cmsg: ChatMessage):
if context:
self.produce(context)
- # 统一的发送函数,每个Channel自行实现,根据reply的type字段发送不同类型的消息
+ # 统一的发送函数,每个 Channel 自行实现,根据 reply 的 type 字段发送不同类型的消息
def send(self, reply: Reply, context: Context):
receiver = context["receiver"]
if reply.type == ReplyType.TEXT:
@@ -244,7 +244,7 @@ def send(self, reply: Reply, context: Context):
video_storage = reply.content
itchat.send_video(video_storage, toUserName=receiver)
logger.info("[WX] sendFile, receiver={}".format(receiver))
- elif reply.type == ReplyType.VIDEO_URL: # 新增视频URL回复类型
+ elif reply.type == ReplyType.VIDEO_URL: # 新增视频 URL 回复类型
video_url = reply.content
logger.debug(f"[WX] start download video, video_url={video_url}")
video_res = requests.get(video_url, stream=True)
diff --git a/channel/wechat/wechat_message.py b/channel/wechat/wechat_message.py
index b8b1d91c5..7c1be9d35 100644
--- a/channel/wechat/wechat_message.py
+++ b/channel/wechat/wechat_message.py
@@ -19,15 +19,15 @@ def __init__(self, itchat_msg, is_group=False):
self.content = itchat_msg["Text"]
elif itchat_msg["Type"] == VOICE:
self.ctype = ContextType.VOICE
- self.content = TmpDir().path() + itchat_msg["FileName"] # content直接存临时目录路径
+ self.content = TmpDir().path() + itchat_msg["FileName"] # content 直接存临时目录路径
self._prepare_fn = lambda: itchat_msg.download(self.content)
elif itchat_msg["Type"] == PICTURE and itchat_msg["MsgType"] == 3:
self.ctype = ContextType.IMAGE
- self.content = TmpDir().path() + itchat_msg["FileName"] # content直接存临时目录路径
+ self.content = TmpDir().path() + itchat_msg["FileName"] # content 直接存临时目录路径
self._prepare_fn = lambda: itchat_msg.download(self.content)
elif itchat_msg["Type"] == NOTE and itchat_msg["MsgType"] == 10000:
if is_group and ("加入群聊" in itchat_msg["Content"] or "加入了群聊" in itchat_msg["Content"]):
- # 这里只能得到nickname, actual_user_id还是机器人的id
+ # 这里只能得到 nickname,actual_user_id 还是机器人的 id
if "加入了群聊" in itchat_msg["Content"]:
self.ctype = ContextType.JOIN_GROUP
self.content = itchat_msg["Content"]
@@ -54,7 +54,7 @@ def __init__(self, itchat_msg, is_group=False):
raise NotImplementedError("Unsupported note message: " + itchat_msg["Content"])
elif itchat_msg["Type"] == ATTACHMENT:
self.ctype = ContextType.FILE
- self.content = TmpDir().path() + itchat_msg["FileName"] # content直接存临时目录路径
+ self.content = TmpDir().path() + itchat_msg["FileName"] # content 直接存临时目录路径
self._prepare_fn = lambda: itchat_msg.download(self.content)
elif itchat_msg["Type"] == SHARING:
self.ctype = ContextType.SHARING
@@ -69,14 +69,14 @@ def __init__(self, itchat_msg, is_group=False):
user_id = itchat.instance.storageClass.userName
nickname = itchat.instance.storageClass.nickName
- # 虽然from_user_id和to_user_id用的少,但是为了保持一致性,还是要填充一下
+ # 虽然 from_user_id 和 to_user_id 用的少,但是为了保持一致性,还是要填充一下
# 以下很繁琐,一句话总结:能填的都填了。
if self.from_user_id == user_id:
self.from_user_nickname = nickname
if self.to_user_id == user_id:
self.to_user_nickname = nickname
- try: # 陌生人时候, User字段可能不存在
- # my_msg 为True是表示是自己发送的消息
+ try: # 陌生人时候,User 字段可能不存在
+ # my_msg 为 True 是表示是自己发送的消息
self.my_msg = itchat_msg["ToUserName"] == itchat_msg["User"]["UserName"] and \
itchat_msg["ToUserName"] != itchat_msg["FromUserName"]
self.other_user_id = itchat_msg["User"]["UserName"]
diff --git a/channel/wechat/wechaty_channel.py b/channel/wechat/wechaty_channel.py
index 051a9cf10..458d488b1 100644
--- a/channel/wechat/wechaty_channel.py
+++ b/channel/wechat/wechaty_channel.py
@@ -43,7 +43,7 @@ def startup(self):
async def main(self):
loop = asyncio.get_event_loop()
- # 将asyncio的loop传入处理线程
+ # 将 asyncio 的 loop 传入处理线程
self.handler_pool._initializer = lambda: asyncio.set_event_loop(loop)
self.bot = Wechaty()
self.bot.on("login", self.on_login)
@@ -55,7 +55,7 @@ async def on_login(self, contact: Contact):
self.name = contact.name
logger.info("[WX] login user={}".format(contact))
- # 统一的发送函数,每个Channel自行实现,根据reply的type字段发送不同类型的消息
+ # 统一的发送函数,每个 Channel 自行实现,根据 reply 的 type 字段发送不同类型的消息
def send(self, reply: Reply, context: Context):
receiver_id = context["receiver"]
loop = asyncio.get_event_loop()
@@ -120,7 +120,7 @@ async def on_message(self, msg: Message):
logger.exception("[WX] {}".format(e))
return
logger.debug("[WX] message:{}".format(cmsg))
- room = msg.room() # 获取消息来自的群聊. 如果消息不是来自群聊, 则返回None
+ room = msg.room() # 获取消息来自的群聊。如果消息不是来自群聊,则返回 None
isgroup = room is not None
ctype = cmsg.ctype
context = self._compose_context(ctype, cmsg.content, isgroup=isgroup, msg=cmsg)
diff --git a/channel/wechat/wechaty_message.py b/channel/wechat/wechaty_message.py
index cdb41ddf2..6a060c279 100644
--- a/channel/wechat/wechaty_message.py
+++ b/channel/wechat/wechaty_message.py
@@ -41,7 +41,7 @@ async def __init__(self, wechaty_msg: Message):
elif wechaty_msg.type() == MessageType.MESSAGE_TYPE_AUDIO:
self.ctype = ContextType.VOICE
voice_file = await wechaty_msg.to_file_box()
- self.content = TmpDir().path() + voice_file.name # content直接存临时目录路径
+ self.content = TmpDir().path() + voice_file.name # content 直接存临时目录路径
def func():
loop = asyncio.get_event_loop()
@@ -56,10 +56,10 @@ def func():
self.from_user_id = from_contact.contact_id
self.from_user_nickname = from_contact.name
- # group中的from和to,wechaty跟itchat含义不一样
- # wecahty: from是消息实际发送者, to:所在群
- # itchat: 如果是你发送群消息,from和to是你自己和所在群,如果是别人发群消息,from和to是所在群和你自己
- # 但这个差别不影响逻辑,group中只使用到:1.用from来判断是否是自己发的,2.actual_user_id来判断实际发送用户
+ # group 中的 from 和 to,wechaty 跟 itchat 含义不一样
+ # wecahty: from 是消息实际发送者,to:所在群
+ # itchat: 如果是你发送群消息,from 和 to 是你自己和所在群,如果是别人发群消息,from 和 to 是所在群和你自己
+ # 但这个差别不影响逻辑,group 中只使用到:1.用 from 来判断是否是自己发的,2.actual_user_id 来判断实际发送用户
if self.is_group:
self.to_user_id = room.room_id
@@ -69,14 +69,14 @@ def func():
self.to_user_id = to_contact.contact_id
self.to_user_nickname = to_contact.name
- if self.is_group or wechaty_msg.is_self(): # 如果是群消息,other_user设置为群,如果是私聊消息,而且自己发的,就设置成对方。
+ if self.is_group or wechaty_msg.is_self(): # 如果是群消息,other_user 设置为群,如果是私聊消息,而且自己发的,就设置成对方。
self.other_user_id = self.to_user_id
self.other_user_nickname = self.to_user_nickname
else:
self.other_user_id = self.from_user_id
self.other_user_nickname = self.from_user_nickname
- if self.is_group: # wechaty群聊中,实际发送用户就是from_user
+ if self.is_group: # wechaty 群聊中,实际发送用户就是 from_user
self.is_at = await wechaty_msg.mention_self()
if not self.is_at: # 有时候复制粘贴的消息,不算做@,但是内容里面会有@xxx,这里做一下兼容
name = wechaty_msg.wechaty.user_self().name
diff --git a/channel/wechatcom/README.md b/channel/wechatcom/README.md
index 2f54a79fd..be14ed556 100644
--- a/channel/wechatcom/README.md
+++ b/channel/wechatcom/README.md
@@ -1,18 +1,18 @@
-# 企业微信应用号channel
+# 企业微信应用号 channel
-企业微信官方提供了客服、应用等API,本channel使用的是企业微信的自建应用API的能力。
+企业微信官方提供了客服、应用等 API,本 channel 使用的是企业微信的自建应用 API 的能力。
-因为未来可能还会开发客服能力,所以本channel的类型名叫作`wechatcom_app`。
+因为未来可能还会开发客服能力,所以本 channel 的类型名叫作`wechatcom_app`。
-`wechatcom_app` channel支持插件系统和图片声音交互等能力,除了无法加入群聊,作为个人使用的私人助理已绰绰有余。
+`wechatcom_app` channel 支持插件系统和图片声音交互等能力,除了无法加入群聊,作为个人使用的私人助理已绰绰有余。
## 开始之前
- 在企业中确认自己拥有在企业内自建应用的权限。
- 如果没有权限或者是个人用户,也可创建未认证的企业。操作方式:登录手机企业微信,选择`创建/加入企业`来创建企业,类型请选择企业,企业名称可随意填写。
- 未认证的企业有100人的服务人数上限,其他功能与认证企业没有差异。
+ 未认证的企业有 100 人的服务人数上限,其他功能与认证企业没有差异。
-本channel需安装的依赖与公众号一致,需要安装`wechatpy`和`web.py`,它们包含在`requirements-optional.txt`中。
+本 channel 需安装的依赖与公众号一致,需要安装`wechatpy`和`web.py`,它们包含在`requirements-optional.txt`中。
此外,如果你是`Linux`系统,除了`ffmpeg`还需要安装`amr`编码器,否则会出现找不到编码器的错误,无法正常使用语音功能。
@@ -28,34 +28,34 @@ apt-get install libavcodec-extra
## 使用方法
-1.查看企业ID
+1.查看企业 ID
- 扫码登陆[企业微信后台](https://work.weixin.qq.com)
- 选择`我的企业`,点击`企业信息`,记住该`企业ID`
2.创建自建应用
-- 选择应用管理, 在自建区选创建应用来创建企业自建应用
-- 上传应用logo,填写应用名称等项
+- 选择应用管理,在自建区选创建应用来创建企业自建应用
+- 上传应用 logo,填写应用名称等项
- 创建应用后进入应用详情页面,记住`AgentId`和`Secert`
3.配置应用
-- 在详情页点击`企业可信IP`的配置(没看到可以不管),填入你服务器的公网IP,如果不知道可以先不填
-- 点击`接收消息`下的启用API接收消息
-- `URL`填写格式为`http://url:port/wxcomapp`,`port`是程序监听的端口,默认是9898
- 如果是未认证的企业,url可直接使用服务器的IP。如果是认证企业,需要使用备案的域名,可使用二级域名。
+- 在详情页点击`企业可信IP`的配置 (没看到可以不管),填入你服务器的公网 IP,如果不知道可以先不填
+- 点击`接收消息`下的启用 API 接收消息
+- `URL`填写格式为`http://url:port/wxcomapp`,`port`是程序监听的端口,默认是 9898
+ 如果是未认证的企业,url 可直接使用服务器的 IP。如果是认证企业,需要使用备案的域名,可使用二级域名。
- `Token`可随意填写,停留在这个页面
- 在程序根目录`config.json`中增加配置(**去掉注释**),`wechatcomapp_aes_key`是当前页面的`wechatcomapp_aes_key`
```python
"channel_type": "wechatcom_app",
- "wechatcom_corp_id": "", # 企业微信公司的corpID
- "wechatcomapp_token": "", # 企业微信app的token
- "wechatcomapp_port": 9898, # 企业微信app的服务端口, 不需要端口转发
- "wechatcomapp_secret": "", # 企业微信app的secret
- "wechatcomapp_agent_id": "", # 企业微信app的agent_id
- "wechatcomapp_aes_key": "", # 企业微信app的aes_key
+ "wechatcom_corp_id": "", # 企业微信公司的 corpID
+ "wechatcomapp_token": "", # 企业微信 app 的 token
+ "wechatcomapp_port": 9898, # 企业微信 app 的服务端口,不需要端口转发
+ "wechatcomapp_secret": "", # 企业微信 app 的 secret
+ "wechatcomapp_agent_id": "", # 企业微信 app 的 agent_id
+ "wechatcomapp_aes_key": "", # 企业微信 app 的 aes_key
```
- 运行程序,在页面中点击保存,保存成功说明验证成功
@@ -64,22 +64,22 @@ apt-get install libavcodec-extra
选择`我的企业`,点击`微信插件`,下面有个邀请关注的二维码。微信扫码后,即可在微信中看到对应企业,在这里你便可以和机器人沟通。
-向机器人发送消息,如果日志里出现报错:
+向机器人发送消息,如果日志里出现报错:
```bash
Error code: 60020, message: "not allow to access from your ip, ...from ip: xx.xx.xx.xx"
```
-意思是IP不可信,需要参考上一步的`企业可信IP`配置,把这里的IP加进去。
+意思是 IP 不可信,需要参考上一步的`企业可信IP`配置,把这里的 IP 加进去。
-~~### Railway部署方式~~(2023-06-08已失效)
+~~### Railway 部署方式~~(2023-06-08 已失效)
~~公众号不能在`Railway`上部署,但企业微信应用[可以](https://railway.app/template/-FHS--?referralCode=RC3znh)!~~
-~~填写配置后,将部署完成后的网址```**.railway.app/wxcomapp```,填写在上一步的URL中。发送信息后观察日志,把报错的IP加入到可信IP。(每次重启后都需要加入可信IP)~~
+~~填写配置后,将部署完成后的网址```**.railway.app/wxcomapp```,填写在上一步的 URL 中。发送信息后观察日志,把报错的 IP 加入到可信 IP。(每次重启后都需要加入可信 IP)~~
## 测试体验
-AIGC开放社区中已经部署了多个可免费使用的Bot,扫描下方的二维码会自动邀请你来体验。
+AIGC 开放社区中已经部署了多个可免费使用的 Bot,扫描下方的二维码会自动邀请你来体验。
diff --git a/channel/wechatcom/wechatcomapp_channel.py b/channel/wechatcom/wechatcomapp_channel.py
index 1a0859690..e10a04f8d 100644
--- a/channel/wechatcom/wechatcomapp_channel.py
+++ b/channel/wechatcom/wechatcomapp_channel.py
@@ -59,7 +59,7 @@ def send(self, reply: Reply, context: Context):
for i, text in enumerate(texts):
self.client.message.send_text(self.agent_id, receiver, text)
if i != len(texts) - 1:
- time.sleep(0.5) # 休眠0.5秒,防止发送过快乱序
+ time.sleep(0.5) # 休眠 0.5 秒,防止发送过快乱序
logger.info("[wechatcom] Do send text to {}: {}".format(receiver, reply_text))
elif reply.type == ReplyType.VOICE:
try:
diff --git a/channel/wechatcom/wechatcomapp_client.py b/channel/wechatcom/wechatcomapp_client.py
index c0feb7a18..b4aea92f0 100644
--- a/channel/wechatcom/wechatcomapp_client.py
+++ b/channel/wechatcom/wechatcomapp_client.py
@@ -9,7 +9,7 @@ def __init__(self, corp_id, secret, access_token=None, session=None, timeout=Non
super(WechatComAppClient, self).__init__(corp_id, secret, access_token, session, timeout, auto_retry)
self.fetch_access_token_lock = threading.Lock()
- def fetch_access_token(self): # 重载父类方法,加锁避免多线程重复获取access_token
+ def fetch_access_token(self): # 重载父类方法,加锁避免多线程重复获取 access_token
with self.fetch_access_token_lock:
access_token = self.session.get(self.access_token_key)
if access_token:
diff --git a/channel/wechatcom/wechatcomapp_message.py b/channel/wechatcom/wechatcomapp_message.py
index a70f7556e..8fde3f193 100644
--- a/channel/wechatcom/wechatcomapp_message.py
+++ b/channel/wechatcom/wechatcomapp_message.py
@@ -18,10 +18,10 @@ def __init__(self, msg, client: WeChatClient, is_group=False):
self.content = msg.content
elif msg.type == "voice":
self.ctype = ContextType.VOICE
- self.content = TmpDir().path() + msg.media_id + "." + msg.format # content直接存临时目录路径
+ self.content = TmpDir().path() + msg.media_id + "." + msg.format # content 直接存临时目录路径
def download_voice():
- # 如果响应状态码是200,则将响应内容写入本地文件
+ # 如果响应状态码是 200,则将响应内容写入本地文件
response = client.media.download(msg.media_id)
if response.status_code == 200:
with open(self.content, "wb") as f:
@@ -32,10 +32,10 @@ def download_voice():
self._prepare_fn = download_voice
elif msg.type == "image":
self.ctype = ContextType.IMAGE
- self.content = TmpDir().path() + msg.media_id + ".png" # content直接存临时目录路径
+ self.content = TmpDir().path() + msg.media_id + ".png" # content 直接存临时目录路径
def download_image():
- # 如果响应状态码是200,则将响应内容写入本地文件
+ # 如果响应状态码是 200,则将响应内容写入本地文件
response = client.media.download(msg.media_id)
if response.status_code == 200:
with open(self.content, "wb") as f:
diff --git a/channel/wechatmp/README.md b/channel/wechatmp/README.md
index 8d753d8a8..a87049ee1 100644
--- a/channel/wechatmp/README.md
+++ b/channel/wechatmp/README.md
@@ -1,14 +1,14 @@
-# 微信公众号channel
+# 微信公众号 channel
-鉴于个人微信号在服务器上通过itchat登录有封号风险,这里新增了微信公众号channel,提供无风险的服务。
+鉴于个人微信号在服务器上通过 itchat 登录有封号风险,这里新增了微信公众号 channel,提供无风险的服务。
目前支持订阅号和服务号两种类型的公众号,它们都支持文本交互,语音和图片输入。其中个人主体的微信订阅号由于无法通过微信认证,存在回复时间限制,每天的图片和声音回复次数也有限制。
## 使用方法(订阅号,服务号类似)
-在开始部署前,你需要一个拥有公网IP的服务器,以提供微信服务器和我们自己服务器的连接。或者你需要进行内网穿透,否则微信服务器无法将消息发送给我们的服务器。
+在开始部署前,你需要一个拥有公网 IP 的服务器,以提供微信服务器和我们自己服务器的连接。或者你需要进行内网穿透,否则微信服务器无法将消息发送给我们的服务器。
-此外,需要在我们的服务器上安装python的web框架web.py和wechatpy。
-以ubuntu为例(在ubuntu 22.04上测试):
+此外,需要在我们的服务器上安装 python 的 web 框架 web.py 和 wechatpy。
+以 ubuntu 为例 (在 ubuntu 22.04 上测试):
```
pip3 install web.py
pip3 install wechatpy
@@ -16,7 +16,7 @@ pip3 install wechatpy
然后在[微信公众平台](https://mp.weixin.qq.com)注册一个自己的公众号,类型选择订阅号,主体为个人即可。
-然后根据[接入指南](https://developers.weixin.qq.com/doc/offiaccount/Basic_Information/Access_Overview.html)的说明,在[微信公众平台](https://mp.weixin.qq.com)的“设置与开发”-“基本配置”-“服务器配置”中填写服务器地址`URL`和令牌`Token`。`URL`填写格式为`http://url/wx`,可使用IP(成功几率看脸),`Token`是你自己编的一个特定的令牌。消息加解密方式如果选择了需要加密的模式,需要在配置中填写`wechatmp_aes_key`。
+然后根据[接入指南](https://developers.weixin.qq.com/doc/offiaccount/Basic_Information/Access_Overview.html)的说明,在[微信公众平台](https://mp.weixin.qq.com)的“设置与开发” - “基本配置” - “服务器配置”中填写服务器地址`URL`和令牌`Token`。`URL`填写格式为`http://url/wx`,可使用 IP(成功几率看脸),`Token`是你自己编的一个特定的令牌。消息加解密方式如果选择了需要加密的模式,需要在配置中填写`wechatmp_aes_key`。
相关的服务器验证代码已经写好,你不需要再添加任何代码。你只需要在本项目根目录的`config.json`中添加
```
@@ -30,19 +30,19 @@ pip3 install wechatpy
"single_chat_reply_prefix": "", # 推荐设置,回复不设置前缀
"plugin_trigger_prefix": "&", # 推荐设置,在手机微信客户端中,$%^等符号与中文连在一起时会自动显示一段较大的间隔,用户体验不好。请不要使用管理员指令前缀"#",这会造成未知问题。
```
-然后运行`python3 app.py`启动web服务器。这里会默认监听8080端口,但是微信公众号的服务器配置只支持80/443端口,有两种方法来解决这个问题。第一个是推荐的方法,使用端口转发命令将80端口转发到8080端口:
+然后运行`python3 app.py`启动 web 服务器。这里会默认监听 8080 端口,但是微信公众号的服务器配置只支持 80/443 端口,有两种方法来解决这个问题。第一个是推荐的方法,使用端口转发命令将 80 端口转发到 8080 端口:
```
sudo iptables -t nat -A PREROUTING -p tcp --dport 80 -j REDIRECT --to-port 8080
sudo iptables-save > /etc/iptables/rules.v4
```
-第二个方法是让python程序直接监听80端口,在配置文件中设置`"wechatmp_port": 80` ,在linux上需要使用`sudo python3 app.py`启动程序。然而这会导致一系列环境和权限问题,因此不是推荐的方法。
+第二个方法是让 python 程序直接监听 80 端口,在配置文件中设置`"wechatmp_port": 80` ,在 linux 上需要使用`sudo python3 app.py`启动程序。然而这会导致一系列环境和权限问题,因此不是推荐的方法。
-443端口同理,注意需要支持SSL,也就是https的访问,在`wechatmp_channel.py`中需要修改相应的证书路径。
+443 端口同理,注意需要支持 SSL,也就是 https 的访问,在`wechatmp_channel.py`中需要修改相应的证书路径。
程序启动并监听端口后,在刚才的“服务器配置”中点击`提交`即可验证你的服务器。
-随后在[微信公众平台](https://mp.weixin.qq.com)启用服务器,关闭手动填写规则的自动回复,即可实现ChatGPT的自动回复。
+随后在[微信公众平台](https://mp.weixin.qq.com)启用服务器,关闭手动填写规则的自动回复,即可实现 ChatGPT 的自动回复。
-之后需要在公众号开发信息下将本机IP加入到IP白名单。
+之后需要在公众号开发信息下将本机 IP 加入到 IP 白名单。
不然在启用后,发送语音、图片等消息可能会遇到如下报错:
```
@@ -51,12 +51,12 @@ sudo iptables-save > /etc/iptables/rules.v4
## 个人微信公众号的限制
-由于人微信公众号不能通过微信认证,所以没有客服接口,因此公众号无法主动发出消息,只能被动回复。而微信官方对被动回复有5秒的时间限制,最多重试2次,因此最多只有15秒的自动回复时间窗口。因此如果问题比较复杂或者我们的服务器比较忙,ChatGPT的回答就没办法及时回复给用户。为了解决这个问题,这里做了回答缓存,它需要你在回复超时后,再次主动发送任意文字(例如1)来尝试拿到回答缓存。为了优化使用体验,目前设置了两分钟(120秒)的timeout,用户在至多两分钟后即可得到查询到回复或者错误原因。
+由于人微信公众号不能通过微信认证,所以没有客服接口,因此公众号无法主动发出消息,只能被动回复。而微信官方对被动回复有 5 秒的时间限制,最多重试 2 次,因此最多只有 15 秒的自动回复时间窗口。因此如果问题比较复杂或者我们的服务器比较忙,ChatGPT 的回答就没办法及时回复给用户。为了解决这个问题,这里做了回答缓存,它需要你在回复超时后,再次主动发送任意文字(例如 1)来尝试拿到回答缓存。为了优化使用体验,目前设置了两分钟(120 秒)的 timeout,用户在至多两分钟后即可得到查询到回复或者错误原因。
-另外,由于微信官方的限制,自动回复有长度限制。因此这里将ChatGPT的回答进行了拆分,以满足限制。
+另外,由于微信官方的限制,自动回复有长度限制。因此这里将 ChatGPT 的回答进行了拆分,以满足限制。
-## 私有api_key
-公共api有访问频率限制(免费账号每分钟最多3次ChatGPT的API调用),这在服务多人的时候会遇到问题。因此这里多加了一个设置私有api_key的功能。目前通过godcmd插件的命令来设置私有api_key。
+## 私有 api_key
+公共 api 有访问频率限制(免费账号每分钟最多 3 次 ChatGPT 的 API 调用),这在服务多人的时候会遇到问题。因此这里多加了一个设置私有 api_key 的功能。目前通过 godcmd 插件的命令来设置私有 api_key。
## 语音输入
利用微信自带的语音识别功能,提供语音输入能力。需要在公众号管理页面的“设置与开发”->“接口权限”页面开启“接收语音识别结果”。
@@ -75,22 +75,22 @@ sudo iptables-save > /etc/iptables/rules.v4
"text_to_voice": "pytts"
```
-pytts是本地的语音合成引擎。还支持baidu,azure,这些你需要自行配置相关的依赖和key。
+pytts 是本地的语音合成引擎。还支持 baidu,azure,这些你需要自行配置相关的依赖和 key。
-如果使用pytts,在ubuntu上需要安装如下依赖:
+如果使用 pytts,在 ubuntu 上需要安装如下依赖:
```
sudo apt update
sudo apt install espeak
sudo apt install ffmpeg
python3 -m pip install pyttsx3
```
-不是很建议开启pytts语音回复,因为它是离线本地计算,算的慢会拖垮服务器,且声音不好听。
+不是很建议开启 pytts 语音回复,因为它是离线本地计算,算的慢会拖垮服务器,且声音不好听。
## 图片回复
-现在认证公众号和非认证公众号都可以实现的图片和语音回复。但是非认证公众号使用了永久素材接口,每天有1000次的调用上限(每个月有10次重置机会,程序中已设定遇到上限会自动重置),且永久素材库存也有上限。因此对于非认证公众号,我们会在回复图片或者语音消息后的10秒内从永久素材库存内删除该素材。
+现在认证公众号和非认证公众号都可以实现的图片和语音回复。但是非认证公众号使用了永久素材接口,每天有 1000 次的调用上限(每个月有 10 次重置机会,程序中已设定遇到上限会自动重置),且永久素材库存也有上限。因此对于非认证公众号,我们会在回复图片或者语音消息后的 10 秒内从永久素材库存内删除该素材。
## 测试
-目前在`RoboStyle`这个公众号上进行了测试(基于[wechatmp分支](https://github.com/JS00000/chatgpt-on-wechat/tree/wechatmp)),感兴趣的可以关注并体验。开启了godcmd, Banwords, role, dungeon, finish这五个插件,其他的插件还没有详尽测试。百度的接口暂未测试。[wechatmp-stable分支](https://github.com/JS00000/chatgpt-on-wechat/tree/wechatmp-stable)是较稳定的上个版本,但也缺少最新的功能支持。
+目前在`RoboStyle`这个公众号上进行了测试(基于[wechatmp 分支](https://github.com/JS00000/chatgpt-on-wechat/tree/wechatmp)),感兴趣的可以关注并体验。开启了 godcmd, Banwords, role, dungeon, finish 这五个插件,其他的插件还没有详尽测试。百度的接口暂未测试。[wechatmp-stable 分支](https://github.com/JS00000/chatgpt-on-wechat/tree/wechatmp-stable)是较稳定的上个版本,但也缺少最新的功能支持。
## TODO
- [x] 语音输入
diff --git a/channel/wechatmp/passive_reply.py b/channel/wechatmp/passive_reply.py
index d03efc4d1..e7331a1fe 100644
--- a/channel/wechatmp/passive_reply.py
+++ b/channel/wechatmp/passive_reply.py
@@ -71,7 +71,7 @@ def POST(self):
reply_text = textwrap.dedent(
f"""\
请输入'{trigger_prefix}'接你想说的话跟我说话。
- 例如:
+ 例如:
{trigger_prefix}你好,很高兴见到你。"""
)
else:
diff --git a/channel/wechatmp/wechatmp_channel.py b/channel/wechatmp/wechatmp_channel.py
index 2ae88228f..b7d0490b0 100644
--- a/channel/wechatmp/wechatmp_channel.py
+++ b/channel/wechatmp/wechatmp_channel.py
@@ -149,7 +149,7 @@ def send(self, reply: Reply, context: Context):
for i, text in enumerate(texts):
self.client.message.send_text(receiver, text)
if i != len(texts) - 1:
- time.sleep(0.5) # 休眠0.5秒,防止发送过快乱序
+ time.sleep(0.5) # 休眠 0.5 秒,防止发送过快乱序
logger.info("[wechatmp] Do send text to {}: {}".format(receiver, reply_text))
elif reply.type == ReplyType.VOICE:
try:
diff --git a/channel/wechatmp/wechatmp_client.py b/channel/wechatmp/wechatmp_client.py
index 19dca3219..9b8ce63cd 100644
--- a/channel/wechatmp/wechatmp_client.py
+++ b/channel/wechatmp/wechatmp_client.py
@@ -21,7 +21,7 @@ def clear_quota(self):
def clear_quota_v2(self):
return self.post("clear_quota/v2", params={"appid": self.appid, "appsecret": self.secret})
- def fetch_access_token(self): # 重载父类方法,加锁避免多线程重复获取access_token
+ def fetch_access_token(self): # 重载父类方法,加锁避免多线程重复获取 access_token
with self.fetch_access_token_lock:
access_token = self.session.get(self.access_token_key)
if access_token:
@@ -32,7 +32,7 @@ def fetch_access_token(self): # 重载父类方法,加锁避免多线程重
return access_token
return super().fetch_access_token()
- def _request(self, method, url_or_endpoint, **kwargs): # 重载父类方法,遇到API限流时,清除quota后重试
+ def _request(self, method, url_or_endpoint, **kwargs): # 重载父类方法,遇到 API 限流时,清除 quota 后重试
try:
return super()._request(method, url_or_endpoint, **kwargs)
except APILimitedException as e:
diff --git a/channel/wechatmp/wechatmp_message.py b/channel/wechatmp/wechatmp_message.py
index 27c9fbb85..0af7c24a0 100644
--- a/channel/wechatmp/wechatmp_message.py
+++ b/channel/wechatmp/wechatmp_message.py
@@ -19,10 +19,10 @@ def __init__(self, msg, client=None):
elif msg.type == "voice":
if msg.recognition == None:
self.ctype = ContextType.VOICE
- self.content = TmpDir().path() + msg.media_id + "." + msg.format # content直接存临时目录路径
+ self.content = TmpDir().path() + msg.media_id + "." + msg.format # content 直接存临时目录路径
def download_voice():
- # 如果响应状态码是200,则将响应内容写入本地文件
+ # 如果响应状态码是 200,则将响应内容写入本地文件
response = client.media.download(msg.media_id)
if response.status_code == 200:
with open(self.content, "wb") as f:
@@ -36,10 +36,10 @@ def download_voice():
self.content = msg.recognition
elif msg.type == "image":
self.ctype = ContextType.IMAGE
- self.content = TmpDir().path() + msg.media_id + ".png" # content直接存临时目录路径
+ self.content = TmpDir().path() + msg.media_id + ".png" # content 直接存临时目录路径
def download_image():
- # 如果响应状态码是200,则将响应内容写入本地文件
+ # 如果响应状态码是 200,则将响应内容写入本地文件
response = client.media.download(msg.media_id)
if response.status_code == 200:
with open(self.content, "wb") as f:
diff --git a/channel/wework/wework_channel.py b/channel/wework/wework_channel.py
index 102026105..b74fae9c0 100644
--- a/channel/wework/wework_channel.py
+++ b/channel/wework/wework_channel.py
@@ -28,7 +28,7 @@ def get_wxid_by_name(room_members, group_wxid, name):
for member in room_members[group_wxid]['member_list']:
if member['room_nickname'] == name or member['username'] == name:
return member['user_id']
- return None # 如果没有找到对应的group_wxid或name,则返回None
+ return None # 如果没有找到对应的 group_wxid 或 name,则返回 None
def download_and_compress_image(url, filename, quality=30):
@@ -79,7 +79,7 @@ def download_video(url, filename):
for block in response.iter_content(1024):
total_size += len(block)
- # 如果视频的总大小超过30MB (30 * 1024 * 1024 bytes),则停止下载并返回
+ # 如果视频的总大小超过 30MB (30 * 1024 * 1024 bytes),则停止下载并返回
if total_size > 30 * 1024 * 1024:
logger.info("[WX] Video is larger than 30MB, skipping...")
return None
@@ -111,7 +111,7 @@ def wrapper(self, cmsg: ChatMessage):
create_time = cmsg.create_time # 消息时间戳
if create_time is None:
return func(self, cmsg)
- if int(create_time) < int(time.time()) - 60: # 跳过1分钟前的历史消息
+ if int(create_time) < int(time.time()) - 60: # 跳过 1 分钟前的历史消息
logger.debug("[WX]history message {} skipped".format(msgId))
return
return func(self, cmsg)
@@ -122,16 +122,16 @@ def wrapper(self, cmsg: ChatMessage):
@wework.msg_register(
[ntwork.MT_RECV_TEXT_MSG, ntwork.MT_RECV_IMAGE_MSG, 11072, ntwork.MT_RECV_LINK_CARD_MSG,ntwork.MT_RECV_FILE_MSG, ntwork.MT_RECV_VOICE_MSG])
def all_msg_handler(wework_instance: ntwork.WeWork, message):
- logger.debug(f"收到消息: {message}")
+ logger.debug(f"收到消息:{message}")
if 'data' in message:
- # 首先查找conversation_id,如果没有找到,则查找room_conversation_id
+ # 首先查找 conversation_id,如果没有找到,则查找 room_conversation_id
conversation_id = message['data'].get('conversation_id', message['data'].get('room_conversation_id'))
if conversation_id is not None:
is_group = "R:" in conversation_id
try:
cmsg = create_message(wework_instance=wework_instance, message=message, is_group=is_group)
except NotImplementedError as e:
- logger.error(f"[WX]{message.get('MsgId', 'unknown')} 跳过: {e}")
+ logger.error(f"[WX]{message.get('MsgId', 'unknown')} 跳过:{e}")
return None
delay = random.randint(1, 2)
timer = threading.Timer(delay, handle_message, args=(cmsg, is_group))
@@ -188,18 +188,18 @@ def startup(self):
self.user_id = login_info['user_id']
self.name = login_info['nickname']
logger.info(f"登录信息:>>>user_id:{self.user_id}>>>>>>>>name:{self.name}")
- logger.info("静默延迟60s,等待客户端刷新数据,请勿进行任何操作······")
+ logger.info("静默延迟 60s,等待客户端刷新数据,请勿进行任何操作······")
time.sleep(60)
contacts = get_with_retry(wework.get_external_contacts)
rooms = get_with_retry(wework.get_rooms)
directory = os.path.join(os.getcwd(), "tmp")
if not contacts or not rooms:
- logger.error("获取contacts或rooms失败,程序退出")
+ logger.error("获取 contacts 或 rooms 失败,程序退出")
ntwork.exit_()
os.exit(0)
if not os.path.exists(directory):
os.makedirs(directory)
- # 将contacts保存到json文件中
+ # 将 contacts 保存到 json 文件中
with open(os.path.join(directory, 'wework_contacts.json'), 'w', encoding='utf-8') as f:
json.dump(contacts, f, ensure_ascii=False, indent=4)
with open(os.path.join(directory, 'wework_rooms.json'), 'w', encoding='utf-8') as f:
@@ -209,7 +209,7 @@ def startup(self):
# 遍历列表中的每个字典
for room in rooms['room_list']:
- # 获取聊天室ID
+ # 获取聊天室 ID
room_wxid = room['conversation_id']
# 获取聊天室成员
@@ -218,10 +218,10 @@ def startup(self):
# 将聊天室成员保存到结果字典中
result[room_wxid] = room_members
- # 将结果保存到json文件中
+ # 将结果保存到 json 文件中
with open(os.path.join(directory, 'wework_room_members.json'), 'w', encoding='utf-8') as f:
json.dump(result, f, ensure_ascii=False, indent=4)
- logger.info("wework程序初始化完成········")
+ logger.info("wework 程序初始化完成········")
run.forever()
@time_checker
@@ -265,7 +265,7 @@ def handle_group(self, cmsg: ChatMessage):
if context:
self.produce(context)
- # 统一的发送函数,每个Channel自行实现,根据reply的type字段发送不同类型的消息
+ # 统一的发送函数,每个 Channel 自行实现,根据 reply 的 type 字段发送不同类型的消息
def send(self, reply: Reply, context: Context):
logger.debug(f"context: {context}")
receiver = context["receiver"]
diff --git a/channel/wework/wework_message.py b/channel/wework/wework_message.py
index 0d9e96ef7..89b879e73 100644
--- a/channel/wework/wework_message.py
+++ b/channel/wework/wework_message.py
@@ -28,10 +28,10 @@ def get_room_info(wework, conversation_id):
logger.debug(f"传入的 conversation_id: {conversation_id}")
rooms = wework.get_rooms()
if not rooms or 'room_list' not in rooms:
- logger.error(f"获取群聊信息失败: {rooms}")
+ logger.error(f"获取群聊信息失败:{rooms}")
return None
time.sleep(1)
- logger.debug(f"获取到的群聊信息: {rooms}")
+ logger.debug(f"获取到的群聊信息:{rooms}")
for room in rooms['room_list']:
if room['conversation_id'] == conversation_id:
return room
@@ -51,9 +51,9 @@ def cdn_download(wework, message, file_name):
if "url" in data["cdn"].keys() and "auth_key" in data["cdn"].keys():
url = data["cdn"]["url"]
auth_key = data["cdn"]["auth_key"]
- # result = wework.wx_cdn_download(url, auth_key, aes_key, file_size, save_path) # ntwork库本身接口有问题,缺失了aes_key这个参数
+ # result = wework.wx_cdn_download(url, auth_key, aes_key, file_size, save_path) # ntwork 库本身接口有问题,缺失了 aes_key 这个参数
"""
- 下载wx类型的cdn文件,以https开头
+ 下载 wx 类型的 cdn 文件,以 https 开头
"""
data = {
'url': url,
@@ -62,7 +62,7 @@ def cdn_download(wework, message, file_name):
'size': file_size,
'save_path': save_path
}
- result = wework._WeWork__send_sync(send_type.MT_WXCDN_DOWNLOAD_MSG, data) # 直接用wx_cdn_download的接口内部实现来调用
+ result = wework._WeWork__send_sync(send_type.MT_WXCDN_DOWNLOAD_MSG, data) # 直接用 wx_cdn_download 的接口内部实现来调用
elif "file_id" in data["cdn"].keys():
if message["type"] == 11042:
file_type = 2
@@ -90,12 +90,12 @@ def c2c_download_and_convert(wework, message, file_name):
result = wework.c2c_cdn_download(file_id, aes_key, file_size, file_type, save_path)
logger.debug(result)
- # 在下载完SILK文件之后,立即将其转换为WAV文件
+ # 在下载完 SILK 文件之后,立即将其转换为 WAV 文件
base_name, _ = os.path.splitext(save_path)
wav_file = base_name + ".wav"
pilk.silk_to_wav(save_path, wav_file, rate=24000)
- # 删除SILK文件
+ # 删除 SILK 文件
try:
os.remove(save_path)
except Exception as e:
@@ -107,7 +107,7 @@ def __init__(self, wework_msg, wework, is_group=False):
try:
super().__init__(wework_msg)
self.msg_id = wework_msg['data'].get('conversation_id', wework_msg['data'].get('room_conversation_id'))
- # 使用.get()防止 'send_time' 键不存在时抛出错误
+ # 使用.get() 防止 'send_time' 键不存在时抛出错误
self.create_time = wework_msg['data'].get("send_time")
self.is_group = is_group
self.wework = wework
@@ -156,7 +156,7 @@ def __init__(self, wework_msg, wework, is_group=False):
else:
result = {}
for room in rooms['room_list']:
- # 获取聊天室ID
+ # 获取聊天室 ID
room_wxid = room['conversation_id']
# 获取聊天室成员
diff --git a/common/linkai_client.py b/common/linkai_client.py
index 3329f74fc..71c96313f 100644
--- a/common/linkai_client.py
+++ b/common/linkai_client.py
@@ -29,7 +29,7 @@ def on_message(self, push_msg: PushMsg):
def on_config(self, config: dict):
if not self.client_id:
return
- logger.info(f"[LinkAI] 从客户端管理加载远程配置: {config}")
+ logger.info(f"[LinkAI] 从客户端管理加载远程配置:{config}")
if config.get("enabled") != "Y":
return
diff --git a/common/time_check.py b/common/time_check.py
index 5c2dacba6..9269fe447 100644
--- a/common/time_check.py
+++ b/common/time_check.py
@@ -13,7 +13,7 @@ def _time_checker(self, *args, **kwargs):
if chat_time_module:
chat_start_time = _config.get("chat_start_time", "00:00")
chat_stopt_time = _config.get("chat_stop_time", "24:00")
- time_regex = re.compile(r"^([01]?[0-9]|2[0-4])(:)([0-5][0-9])$") # 时间匹配,包含24:00
+ time_regex = re.compile(r"^([01]?[0-9]|2[0-4])(:)([0-5][0-9])$") # 时间匹配,包含 24:00
starttime_format_check = time_regex.match(chat_start_time) # 检查停止时间格式
stoptime_format_check = time_regex.match(chat_stopt_time) # 检查停止时间格式
@@ -21,9 +21,9 @@ def _time_checker(self, *args, **kwargs):
# 时间格式检查
if not (starttime_format_check and stoptime_format_check and chat_time_check):
- logger.warn("时间格式不正确,请在config.json中修改您的CHAT_START_TIME/CHAT_STOP_TIME,否则可能会影响您正常使用,开始({})-结束({})".format(starttime_format_check, stoptime_format_check))
+ logger.warn("时间格式不正确,请在 config.json 中修改您的 CHAT_START_TIME/CHAT_STOP_TIME,否则可能会影响您正常使用,开始 ({})-结束 ({})".format(starttime_format_check, stoptime_format_check))
if chat_start_time > "23:59":
- logger.error("启动时间可能存在问题,请修改!")
+ logger.error("启动时间可能存在问题,请修改!")
# 服务时间检查
now_time = time.strftime("%H:%M", time.localtime())
@@ -34,7 +34,7 @@ def _time_checker(self, *args, **kwargs):
if args[0]["Content"] == "#更新配置": # 不在服务时间内也可以更新配置
f(self, *args, **kwargs)
else:
- logger.info("非服务时间内,不接受访问")
+ logger.info("非服务时间内,不接受访问")
return None
else:
f(self, *args, **kwargs) # 未开启时间模块则直接回答
diff --git a/common/token_bucket.py b/common/token_bucket.py
index 23901b67e..6fe8b187b 100644
--- a/common/token_bucket.py
+++ b/common/token_bucket.py
@@ -5,7 +5,7 @@
class TokenBucket:
def __init__(self, tpm, timeout=None):
self.capacity = int(tpm) # 令牌桶容量
- self.tokens = 0 # 初始令牌数为0
+ self.tokens = 0 # 初始令牌数为 0
self.rate = int(tpm) / 60 # 令牌每秒生成速率
self.timeout = timeout # 等待令牌超时时间
self.cond = threading.Condition() # 条件变量
@@ -37,7 +37,7 @@ def close(self):
if __name__ == "__main__":
- token_bucket = TokenBucket(20, None) # 创建一个每分钟生产20个tokens的令牌桶
+ token_bucket = TokenBucket(20, None) # 创建一个每分钟生产 20 个 tokens 的令牌桶
# token_bucket = TokenBucket(20, 0.1)
for i in range(3):
if token_bucket.get_token():
diff --git a/config-template.json b/config-template.json
index d0268d3b1..fdf5b9b9e 100644
--- a/config-template.json
+++ b/config-template.json
@@ -17,8 +17,8 @@
"@bot"
],
"group_name_white_list": [
- "ChatGPT测试群",
- "ChatGPT测试群2"
+ "ChatGPT 测试群",
+ "ChatGPT 测试群 2"
],
"image_create_prefix": [
"画"
@@ -28,9 +28,9 @@
"voice_reply_voice": false,
"conversation_max_tokens": 2500,
"expires_in_seconds": 3600,
- "character_desc": "你是基于大语言模型的AI智能助手,旨在回答并解决人们的任何问题,并且可以使用多种语言与人交流。",
+ "character_desc": "你是 ChatGPT, 一个由 OpenAI 训练的大型语言模型,你旨在回答并解决人们的任何问题,并且可以使用多种语言与人交流。",
"temperature": 0.7,
- "subscribe_msg": "感谢您的关注!\n这里是AI智能助手,可以自由对话。\n支持语音对话。\n支持图片输入。\n支持图片输出,画字开头的消息将按要求创作图片。\n支持tool、角色扮演和文字冒险等丰富的插件。\n输入{trigger_prefix}#help 查看详细指令。",
+ "subscribe_msg": "感谢您的关注!\n这里是 AI 智能助手,可以自由对话。\n支持语音对话。\n支持图片输入。\n支持图片输出,画字开头的消息将按要求创作图片。\n支持 tool、角色扮演和文字冒险等丰富的插件。\n输入{trigger_prefix}#help 查看详细指令。",
"use_linkai": false,
"linkai_api_key": "",
"linkai_app_code": ""
diff --git a/config.py b/config.py
index 7d07379b3..465991fd6 100644
--- a/config.py
+++ b/config.py
@@ -7,20 +7,20 @@
from common.log import logger
-# 将所有可用的配置项写在字典里, 请使用小写字母
-# 此处的配置值无实际意义,程序不会读取此处的配置,仅用于提示格式,请将配置加入到config.json中
+# 将所有可用的配置项写在字典里,请使用小写字母
+# 此处的配置值无实际意义,程序不会读取此处的配置,仅用于提示格式,请将配置加入到 config.json 中
available_setting = {
- # openai api配置
+ # openai api 配置
"open_ai_api_key": "", # openai api key
- # openai apibase,当use_azure_chatgpt为true时,需要设置对应的api base
+ # openai apibase,当 use_azure_chatgpt 为 true 时,需要设置对应的 api base
"open_ai_api_base": "https://api.openai.com/v1",
- "proxy": "", # openai使用的代理
- # chatgpt模型, 当use_azure_chatgpt为true时,其名称为Azure上model deployment名称
+ "proxy": "", # openai 使用的代理
+ # chatgpt 模型,当 use_azure_chatgpt 为 true 时,其名称为 Azure 上 model deployment 名称
"model": "gpt-3.5-turbo", # 还支持 gpt-4, gpt-4-turbo, wenxin, xunfei, qwen
- "use_azure_chatgpt": False, # 是否使用azure的chatgpt
+ "use_azure_chatgpt": False, # 是否使用 azure 的 chatgpt
"azure_deployment_id": "", # azure 模型部署名称
- "azure_api_version": "", # azure api版本
- # Bot触发配置
+ "azure_api_version": "", # azure api 版本
+ # Bot 触发配置
"single_chat_prefix": ["bot", "@bot"], # 私聊时文本需要包含该前缀才能触发机器人回复
"single_chat_reply_prefix": "[bot] ", # 私聊时自动回复的前缀,用于区分真人
"single_chat_reply_suffix": "", # 私聊时自动回复的后缀,\n 可以换行
@@ -28,40 +28,40 @@
"group_chat_reply_prefix": "", # 群聊时自动回复的前缀
"group_chat_reply_suffix": "", # 群聊时自动回复的后缀,\n 可以换行
"group_chat_keyword": [], # 群聊时包含该关键词则会触发机器人回复
- "group_at_off": False, # 是否关闭群聊时@bot的触发
- "group_name_white_list": ["ChatGPT测试群", "ChatGPT测试群2"], # 开启自动回复的群名称列表
+ "group_at_off": False, # 是否关闭群聊时@bot 的触发
+ "group_name_white_list": ["ChatGPT 测试群", "ChatGPT 测试群 2"], # 开启自动回复的群名称列表
"group_name_keyword_white_list": [], # 开启自动回复的群名称关键词列表
- "group_chat_in_one_session": ["ChatGPT测试群"], # 支持会话上下文共享的群名称
+ "group_chat_in_one_session": ["ChatGPT 测试群"], # 支持会话上下文共享的群名称
"nick_name_black_list": [], # 用户昵称黑名单
"group_welcome_msg": "", # 配置新人进群固定欢迎语,不配置则使用随机风格欢迎
"trigger_by_self": False, # 是否允许机器人触发
"text_to_image": "dall-e-2", # 图片生成模型,可选 dall-e-2, dall-e-3
- "image_proxy": True, # 是否需要图片代理,国内访问LinkAI时需要
+ "image_proxy": True, # 是否需要图片代理,国内访问 LinkAI 时需要
"image_create_prefix": ["画", "看", "找"], # 开启图片回复的前缀
- "concurrency_in_session": 1, # 同一会话最多有多少条消息在处理中,大于1可能乱序
- "image_create_size": "256x256", # 图片大小,可选有 256x256, 512x512, 1024x1024 (dall-e-3默认为1024x1024)
+ "concurrency_in_session": 1, # 同一会话最多有多少条消息在处理中,大于 1 可能乱序
+ "image_create_size": "256x256", # 图片大小,可选有 256x256, 512x512, 1024x1024 (dall-e-3 默认为 1024x1024)
"group_chat_exit_group": False,
- # chatgpt会话参数
+ # chatgpt 会话参数
"expires_in_seconds": 3600, # 无操作会话的过期时间
# 人格描述
- "character_desc": "你是ChatGPT, 一个由OpenAI训练的大型语言模型, 你旨在回答并解决人们的任何问题,并且可以使用多种语言与人交流。",
+ "character_desc": "你是 ChatGPT, 一个由 OpenAI 训练的大型语言模型,你旨在回答并解决人们的任何问题,并且可以使用多种语言与人交流。",
"conversation_max_tokens": 1000, # 支持上下文记忆的最多字符数
- # chatgpt限流配置
- "rate_limit_chatgpt": 20, # chatgpt的调用频率限制
- "rate_limit_dalle": 50, # openai dalle的调用频率限制
- # chatgpt api参数 参考https://platform.openai.com/docs/api-reference/chat/create
+ # chatgpt 限流配置
+ "rate_limit_chatgpt": 20, # chatgpt 的调用频率限制
+ "rate_limit_dalle": 50, # openai dalle 的调用频率限制
+ # chatgpt api 参数 参考 https://platform.openai.com/docs/api-reference/chat/create
"temperature": 0.9,
"top_p": 1,
"frequency_penalty": 0,
"presence_penalty": 0,
- "request_timeout": 180, # chatgpt请求超时时间,openai接口默认设置为600,对于难问题一般需要较长时间
- "timeout": 120, # chatgpt重试超时时间,在这个时间内,将会自动重试
+ "request_timeout": 180, # chatgpt 请求超时时间,openai 接口默认设置为 600,对于难问题一般需要较长时间
+ "timeout": 120, # chatgpt 重试超时时间,在这个时间内,将会自动重试
# Baidu 文心一言参数
- "baidu_wenxin_model": "eb-instant", # 默认使用ERNIE-Bot-turbo模型
+ "baidu_wenxin_model": "eb-instant", # 默认使用 ERNIE-Bot-turbo 模型
"baidu_wenxin_api_key": "", # Baidu api key
"baidu_wenxin_secret_key": "", # Baidu secret key
- # 讯飞星火API
- "xunfei_app_id": "", # 讯飞应用ID
+ # 讯飞星火 API
+ "xunfei_app_id": "", # 讯飞应用 ID
"xunfei_api_key": "", # 讯飞 API key
"xunfei_api_secret": "", # 讯飞 API secret
# claude 配置
@@ -69,84 +69,84 @@
"claude_uuid": "",
# claude api key
"claude_api_key":"",
- # 通义千问API, 获取方式查看文档 https://help.aliyun.com/document_detail/2587494.html
+ # 通义千问 API, 获取方式查看文档 https://help.aliyun.com/document_detail/2587494.html
"qwen_access_key_id": "",
"qwen_access_key_secret": "",
"qwen_agent_key": "",
"qwen_app_id": "",
- "qwen_node_id": "", # 流程编排模型用到的id,如果没有用到qwen_node_id,请务必保持为空字符串
- # 阿里灵积模型api key
+ "qwen_node_id": "", # 流程编排模型用到的 id,如果没有用到 qwen_node_id,请务必保持为空字符串
+ # 阿里灵积模型 api key
"dashscope_api_key": "",
# Google Gemini Api Key
"gemini_api_key": "",
- # wework的通用配置
- "wework_smart": True, # 配置wework是否使用已登录的企业微信,False为多开
+ # wework 的通用配置
+ "wework_smart": True, # 配置 wework 是否使用已登录的企业微信,False 为多开
# 语音设置
"speech_recognition": True, # 是否开启语音识别
"group_speech_recognition": False, # 是否开启群组语音识别
- "voice_reply_voice": False, # 是否使用语音回复语音,需要设置对应语音合成引擎的api key
+ "voice_reply_voice": False, # 是否使用语音回复语音,需要设置对应语音合成引擎的 api key
"always_reply_voice": False, # 是否一直使用语音回复
- "voice_to_text": "openai", # 语音识别引擎,支持openai,baidu,google,azure
- "text_to_voice": "openai", # 语音合成引擎,支持openai,baidu,google,pytts(offline),azure,elevenlabs,edge(online)
+ "voice_to_text": "openai", # 语音识别引擎,支持 openai,baidu,google,azure
+ "text_to_voice": "openai", # 语音合成引擎,支持 openai,baidu,google,pytts(offline),azure,elevenlabs,edge(online)
"text_to_voice_model": "tts-1",
"tts_voice_id": "alloy",
- # baidu 语音api配置, 使用百度语音识别和语音合成时需要
+ # baidu 语音 api 配置,使用百度语音识别和语音合成时需要
"baidu_app_id": "",
"baidu_api_key": "",
"baidu_secret_key": "",
- # 1536普通话(支持简单的英文识别) 1737英语 1637粤语 1837四川话 1936普通话远场
+ # 1536 普通话 (支持简单的英文识别) 1737 英语 1637 粤语 1837 四川话 1936 普通话远场
"baidu_dev_pid": "1536",
- # azure 语音api配置, 使用azure语音识别和语音合成时需要
+ # azure 语音 api 配置,使用 azure 语音识别和语音合成时需要
"azure_voice_api_key": "",
"azure_voice_region": "japaneast",
- # elevenlabs 语音api配置
- "xi_api_key": "", #获取ap的方法可以参考https://docs.elevenlabs.io/api-reference/quick-start/authentication
- "xi_voice_id": "", #ElevenLabs提供了9种英式、美式等英语发音id,分别是“Adam/Antoni/Arnold/Bella/Domi/Elli/Josh/Rachel/Sam”
- # 服务时间限制,目前支持itchat
+ # elevenlabs 语音 api 配置
+ "xi_api_key": "", #获取 ap 的方法可以参考 https://docs.elevenlabs.io/api-reference/quick-start/authentication
+ "xi_voice_id": "", #ElevenLabs 提供了 9 种英式、美式等英语发音 id,分别是“Adam/Antoni/Arnold/Bella/Domi/Elli/Josh/Rachel/Sam”
+ # 服务时间限制,目前支持 itchat
"chat_time_module": False, # 是否开启服务时间限制
"chat_start_time": "00:00", # 服务开始时间
"chat_stop_time": "24:00", # 服务结束时间
- # 翻译api
- "translate": "baidu", # 翻译api,支持baidu
- # baidu翻译api的配置
- "baidu_translate_app_id": "", # 百度翻译api的appid
- "baidu_translate_app_key": "", # 百度翻译api的秘钥
- # itchat的配置
+ # 翻译 api
+ "translate": "baidu", # 翻译 api,支持 baidu
+ # baidu 翻译 api 的配置
+ "baidu_translate_app_id": "", # 百度翻译 api 的 appid
+ "baidu_translate_app_key": "", # 百度翻译 api 的秘钥
+ # itchat 的配置
"hot_reload": False, # 是否开启热重载
- # wechaty的配置
- "wechaty_puppet_service_token": "", # wechaty的token
- # wechatmp的配置
- "wechatmp_token": "", # 微信公众平台的Token
- "wechatmp_port": 8080, # 微信公众平台的端口,需要端口转发到80或443
- "wechatmp_app_id": "", # 微信公众平台的appID
- "wechatmp_app_secret": "", # 微信公众平台的appsecret
- "wechatmp_aes_key": "", # 微信公众平台的EncodingAESKey,加密模式需要
- # wechatcom的通用配置
- "wechatcom_corp_id": "", # 企业微信公司的corpID
- # wechatcomapp的配置
- "wechatcomapp_token": "", # 企业微信app的token
- "wechatcomapp_port": 9898, # 企业微信app的服务端口,不需要端口转发
- "wechatcomapp_secret": "", # 企业微信app的secret
- "wechatcomapp_agent_id": "", # 企业微信app的agent_id
- "wechatcomapp_aes_key": "", # 企业微信app的aes_key
+ # wechaty 的配置
+ "wechaty_puppet_service_token": "", # wechaty 的 token
+ # wechatmp 的配置
+ "wechatmp_token": "", # 微信公众平台的 Token
+ "wechatmp_port": 8080, # 微信公众平台的端口,需要端口转发到 80 或 443
+ "wechatmp_app_id": "", # 微信公众平台的 appID
+ "wechatmp_app_secret": "", # 微信公众平台的 appsecret
+ "wechatmp_aes_key": "", # 微信公众平台的 EncodingAESKey,加密模式需要
+ # wechatcom 的通用配置
+ "wechatcom_corp_id": "", # 企业微信公司的 corpID
+ # wechatcomapp 的配置
+ "wechatcomapp_token": "", # 企业微信 app 的 token
+ "wechatcomapp_port": 9898, # 企业微信 app 的服务端口,不需要端口转发
+ "wechatcomapp_secret": "", # 企业微信 app 的 secret
+ "wechatcomapp_agent_id": "", # 企业微信 app 的 agent_id
+ "wechatcomapp_aes_key": "", # 企业微信 app 的 aes_key
# 飞书配置
- "feishu_port": 80, # 飞书bot监听端口
- "feishu_app_id": "", # 飞书机器人应用APP Id
- "feishu_app_secret": "", # 飞书机器人APP secret
+ "feishu_port": 80, # 飞书 bot 监听端口
+ "feishu_app_id": "", # 飞书机器人应用 APP Id
+ "feishu_app_secret": "", # 飞书机器人 APP secret
"feishu_token": "", # 飞书 verification token
"feishu_bot_name": "", # 飞书机器人的名字
# 钉钉配置
- "dingtalk_client_id": "", # 钉钉机器人Client ID
- "dingtalk_client_secret": "", # 钉钉机器人Client Secret
+ "dingtalk_client_id": "", # 钉钉机器人 Client ID
+ "dingtalk_client_secret": "", # 钉钉机器人 Client Secret
- # chatgpt指令自定义触发词
+ # chatgpt 指令自定义触发词
"clear_memory_commands": ["#清除记忆"], # 重置会话指令,必须以#开头
- # channel配置
+ # channel 配置
"channel_type": "wx", # 通道类型,支持:{wx,wxy,terminal,wechatmp,wechatmp_service,wechatcom_app}
- "subscribe_msg": "", # 订阅消息, 支持: wechatmp, wechatmp_service, wechatcom_app
- "debug": False, # 是否开启debug模式,开启后会打印更多日志
+ "subscribe_msg": "", # 订阅消息,支持:wechatmp, wechatmp_service, wechatcom_app
+ "debug": False, # 是否开启 debug 模式,开启后会打印更多日志
"appdata_dir": "", # 数据目录
# 插件配置
"plugin_trigger_prefix": "$", # 规范插件提供聊天相关指令的前缀,建议不要和管理员指令前缀"#"冲突
@@ -154,16 +154,16 @@
"use_global_plugin_config": False,
"max_media_send_count": 3, # 单次最大发送媒体资源的个数
"media_send_interval": 1, # 发送图片的事件间隔,单位秒
- # 智谱AI 平台配置
+ # 智谱 AI 平台配置
"zhipu_ai_api_key": "",
"zhipu_ai_api_base": "https://open.bigmodel.cn/api/paas/v4",
"moonshot_api_key": "",
"moonshot_base_url":"https://api.moonshot.cn/v1/chat/completions",
- # LinkAI平台配置
+ # LinkAI 平台配置
"use_linkai": False,
"linkai_api_key": "",
"linkai_app_code": "",
- "linkai_api_base": "https://api.link-ai.tech", # linkAI服务地址
+ "linkai_api_base": "https://api.link-ai.tech", # linkAI 服务地址
}
@@ -174,7 +174,7 @@ def __init__(self, d=None):
d = {}
for k, v in d.items():
self[k] = v
- # user_datas: 用户数据,key为用户名,value为用户数据,也是dict
+ # user_datas: 用户数据,key 为用户名,value 为用户数据,也是 dict
self.user_datas = {}
def __getitem__(self, key):
@@ -228,13 +228,13 @@ def load_config():
global config
config_path = "./config.json"
if not os.path.exists(config_path):
- logger.info("配置文件不存在,将使用config-template.json模板")
+ logger.info("配置文件不存在,将使用 config-template.json 模板")
config_path = "./config-template.json"
config_str = read_file(config_path)
logger.debug("[INIT] config str: {}".format(config_str))
- # 将json字符串反序列化为dict类型
+ # 将 json 字符串反序列化为 dict 类型
config = Config(json.loads(config_str))
# override config with environment variables.
diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml
index 8dbb1e41e..102b5f527 100644
--- a/docker/docker-compose.yml
+++ b/docker/docker-compose.yml
@@ -12,11 +12,11 @@ services:
SINGLE_CHAT_PREFIX: '["bot", "@bot"]'
SINGLE_CHAT_REPLY_PREFIX: '"[bot] "'
GROUP_CHAT_PREFIX: '["@bot"]'
- GROUP_NAME_WHITE_LIST: '["ChatGPT测试群", "ChatGPT测试群2"]'
+ GROUP_NAME_WHITE_LIST: '["ChatGPT 测试群", "ChatGPT 测试群 2"]'
IMAGE_CREATE_PREFIX: '["画", "看", "找"]'
CONVERSATION_MAX_TOKENS: 1000
SPEECH_RECOGNITION: 'False'
- CHARACTER_DESC: '你是ChatGPT, 一个由OpenAI训练的大型语言模型, 你旨在回答并解决人们的任何问题,并且可以使用多种语言与人交流。'
+ CHARACTER_DESC: '你是 ChatGPT, 一个由 OpenAI 训练的大型语言模型,你旨在回答并解决人们的任何问题,并且可以使用多种语言与人交流。'
EXPIRES_IN_SECONDS: 3600
USE_GLOBAL_PLUGIN_CONFIG: 'True'
USE_LINKAI: 'False'
diff --git a/docs/version/old-version.md b/docs/version/old-version.md
index ec719cd2b..6afbd1137 100644
--- a/docs/version/old-version.md
+++ b/docs/version/old-version.md
@@ -1,13 +1,13 @@
## 归档更新日志
-2023.04.26: 支持企业微信应用号部署,兼容插件,并支持语音图片交互,私人助理理想选择,使用文档。(contributed by @lanvent in #944)
+2023.04.26:支持企业微信应用号部署,兼容插件,并支持语音图片交互,私人助理理想选择,使用文档。(contributed by @lanvent in #944)
-2023.04.05: 支持微信公众号部署,兼容插件,并支持语音图片交互,使用文档。(contributed by @JS00000 in #686)
+2023.04.05:支持微信公众号部署,兼容插件,并支持语音图片交互,使用文档。(contributed by @JS00000 in #686)
-2023.04.05: 增加能让ChatGPT使用工具的tool插件,使用文档。工具相关issue可反馈至chatgpt-tool-hub。(contributed by @goldfishh in #663)
+2023.04.05:增加能让 ChatGPT 使用工具的 tool 插件,使用文档。工具相关 issue 可反馈至 chatgpt-tool-hub。(contributed by @goldfishh in #663)
-2023.03.25: 支持插件化开发,目前已实现 多角色切换、文字冒险游戏、管理员指令、Stable Diffusion等插件,使用参考 #578。(contributed by @lanvent in #565)
+2023.03.25:支持插件化开发,目前已实现 多角色切换、文字冒险游戏、管理员指令、Stable Diffusion 等插件,使用参考 #578。(contributed by @lanvent in #565)
-2023.03.09: 基于 whisper API(后续已接入更多的语音API服务) 实现对微信语音消息的解析和回复,添加配置项 "speech_recognition":true 即可启用,使用参考 #415。(contributed by wanggang1987 in #385)
+2023.03.09:基于 whisper API(后续已接入更多的语音 API 服务) 实现对微信语音消息的解析和回复,添加配置项 "speech_recognition":true 即可启用,使用参考 #415。(contributed by wanggang1987 in #385)
-2023.02.09: 扫码登录存在账号限制风险,请谨慎使用,参考#58
\ No newline at end of file
+2023.02.09:扫码登录存在账号限制风险,请谨慎使用,参考#58
\ No newline at end of file
diff --git a/lib/itchat/returnvalues.py b/lib/itchat/returnvalues.py
index f42f4e866..baf7d0d3c 100644
--- a/lib/itchat/returnvalues.py
+++ b/lib/itchat/returnvalues.py
@@ -55,7 +55,7 @@ def __repr__(self):
TRANSLATION = {
'Chinese': {
- -1000: u'返回值不带BaseResponse',
+ -1000: u'返回值不带 BaseResponse',
-1001: u'无法找到对应的成员',
-1002: u'文件位置错误',
-1003: u'服务器拒绝连接',
diff --git a/plugins/banwords/README.md b/plugins/banwords/README.md
index 39517f681..6e9239020 100644
--- a/plugins/banwords/README.md
+++ b/plugins/banwords/README.md
@@ -19,9 +19,9 @@
在以上配置项中:
- `action`: 对用户消息的默认处理行为
-- `reply_filter`: 是否对ChatGPT的回复也进行敏感词过滤
+- `reply_filter`: 是否对 ChatGPT 的回复也进行敏感词过滤
- `reply_action`: 如果开启了回复过滤,对回复的默认处理行为
## 致谢
-搜索功能实现来自https://github.com/toolgood/ToolGood.Words
\ No newline at end of file
+搜索功能实现来自 https://github.com/toolgood/ToolGood.Words
\ No newline at end of file
diff --git a/plugins/banwords/banwords.py b/plugins/banwords/banwords.py
index 2a33a5aff..1383ea270 100644
--- a/plugins/banwords/banwords.py
+++ b/plugins/banwords/banwords.py
@@ -71,7 +71,7 @@ def on_handle_context(self, e_context: EventContext):
return
elif self.action == "replace":
if self.searchr.ContainsAny(content):
- reply = Reply(ReplyType.INFO, "发言中包含敏感词,请重试: \n" + self.searchr.Replace(content))
+ reply = Reply(ReplyType.INFO, "发言中包含敏感词,请重试:\n" + self.searchr.Replace(content))
e_context["reply"] = reply
e_context.action = EventAction.BREAK_PASS
return
@@ -91,7 +91,7 @@ def on_decorate_reply(self, e_context: EventContext):
return
elif self.reply_action == "replace":
if self.searchr.ContainsAny(content):
- reply = Reply(ReplyType.INFO, "已替换回复中的敏感词: \n" + self.searchr.Replace(content))
+ reply = Reply(ReplyType.INFO, "已替换回复中的敏感词:\n" + self.searchr.Replace(content))
e_context["reply"] = reply
e_context.action = EventAction.CONTINUE
return
diff --git a/plugins/banwords/lib/WordsSearch.py b/plugins/banwords/lib/WordsSearch.py
index d41d6e7f2..b58b809d4 100644
--- a/plugins/banwords/lib/WordsSearch.py
+++ b/plugins/banwords/lib/WordsSearch.py
@@ -5,7 +5,7 @@
# Licensed under the Apache License 2.0
# 更新日志
# 2020.04.06 第一次提交
-# 2020.05.16 修改,支持大于0xffff的字符
+# 2020.05.16 修改,支持大于 0xffff 的字符
__all__ = ['WordsSearch']
__author__ = 'Lin Zhijun'
diff --git a/voice/ali/ali_api.py b/voice/ali/ali_api.py
index cac0c8c13..d05a5dace 100644
--- a/voice/ali/ali_api.py
+++ b/voice/ali/ali_api.py
@@ -26,14 +26,14 @@ def text_to_speech_aliyun(url, text, appkey, token):
"""
使用阿里云的文本转语音服务将文本转换为语音。
- 参数:
- - url (str): 阿里云文本转语音服务的端点URL。
+ 参数:
+ - url (str): 阿里云文本转语音服务的端点 URL。
- text (str): 要转换为语音的文本。
- - appkey (str): 您的阿里云appkey。
- - token (str): 阿里云API的认证令牌。
+ - appkey (str): 您的阿里云 appkey。
+ - token (str): 阿里云 API 的认证令牌。
- 返回值:
- - str: 成功时输出音频文件的路径,否则为None。
+ 返回值:
+ - str: 成功时输出音频文件的路径,否则为 None。
"""
headers = {
"Content-Type": "application/json",
@@ -55,8 +55,8 @@ def text_to_speech_aliyun(url, text, appkey, token):
file.write(response.content)
logger.debug(f"音频文件保存成功,文件名:{output_file}")
else:
- logger.debug("响应状态码: {}".format(response.status_code))
- logger.debug("响应内容: {}".format(response.text))
+ logger.debug("响应状态码:{}".format(response.status_code))
+ logger.debug("响应内容:{}".format(response.text))
output_file = None
return output_file
@@ -66,8 +66,8 @@ class AliyunTokenGenerator:
"""
用于生成阿里云服务认证令牌的类。
- 属性:
- - access_key_id (str): 您的阿里云访问密钥ID。
+ 属性:
+ - access_key_id (str): 您的阿里云访问密钥 ID。
- access_key_secret (str): 您的阿里云访问密钥秘密。
"""
@@ -79,10 +79,10 @@ def sign_request(self, parameters):
"""
为阿里云服务签名请求。
- 参数:
+ 参数:
- parameters (dict): 请求的参数字典。
- 返回值:
+ 返回值:
- str: 请求的签名签章。
"""
# 将参数按照字典顺序排序
@@ -94,9 +94,9 @@ def sign_request(self, parameters):
canonicalized_query_string += '&' + self.percent_encode(k) + '=' + self.percent_encode(v)
# 构造用于签名的字符串
- string_to_sign = 'GET&%2F&' + self.percent_encode(canonicalized_query_string[1:]) # 使用GET方法
+ string_to_sign = 'GET&%2F&' + self.percent_encode(canonicalized_query_string[1:]) # 使用 GET 方法
- # 使用HMAC算法计算签名
+ # 使用 HMAC 算法计算签名
h = hmac.new((self.access_key_secret + "&").encode('utf-8'), string_to_sign.encode('utf-8'), hashlib.sha1)
signature = base64.encodebytes(h.digest()).strip()
@@ -106,10 +106,10 @@ def percent_encode(self, encode_str):
"""
对字符串进行百分比编码。
- 参数:
+ 参数:
- encode_str (str): 要编码的字符串。
- 返回值:
+ 返回值:
- str: 编码后的字符串。
"""
encode_str = str(encode_str)
@@ -123,7 +123,7 @@ def get_token(self):
"""
获取阿里云服务的令牌。
- 返回值:
+ 返回值:
- str: 获取到的令牌。
"""
# 设置请求参数
@@ -134,7 +134,7 @@ def get_token(self):
'SignatureMethod': 'HMAC-SHA1',
'Timestamp': datetime.datetime.utcnow().strftime("%Y-%m-%dT%H:%M:%SZ"),
'SignatureVersion': '1.0',
- 'SignatureNonce': str(uuid.uuid4()), # 使用uuid生成唯一的随机数
+ 'SignatureNonce': str(uuid.uuid4()), # 使用 uuid 生成唯一的随机数
'Action': 'CreateToken',
'RegionId': 'cn-shanghai'
}
@@ -143,7 +143,7 @@ def get_token(self):
signature = self.sign_request(params)
params['Signature'] = signature
- # 构造请求URL
+ # 构造请求 URL
url = 'http://nls-meta.cn-shanghai.aliyuncs.com/?' + urllib.parse.urlencode(params)
# 发送请求
diff --git a/voice/ali/ali_voice.py b/voice/ali/ali_voice.py
index 79a9aaa78..2c972c411 100644
--- a/voice/ali/ali_voice.py
+++ b/voice/ali/ali_voice.py
@@ -24,7 +24,7 @@
class AliVoice(Voice):
def __init__(self):
"""
- 初始化AliVoice类,从配置文件加载必要的配置。
+ 初始化 AliVoice 类,从配置文件加载必要的配置。
"""
try:
curdir = os.path.dirname(__file__)
@@ -46,12 +46,12 @@ def textToVoice(self, text):
将文本转换为语音文件。
:param text: 要转换的文本。
- :return: 返回一个Reply对象,其中包含转换得到的语音文件或错误信息。
+ :return: 返回一个 Reply 对象,其中包含转换得到的语音文件或错误信息。
"""
# 清除文本中的非中文、非英文和非基本字符
text = re.sub(r'[^\u4e00-\u9fa5\u3040-\u30FF\uAC00-\uD7AFa-zA-Z0-9'
r'äöüÄÖÜáéíóúÁÉÍÓÚàèìòùÀÈÌÒÙâêîôûÂÊÎÔÛçÇñÑ,。!?,.]', '', text)
- # 提取有效的token
+ # 提取有效的 token
token_id = self.get_valid_token()
fileName = text_to_speech_aliyun(self.api_url, text, self.app_key, token_id)
if fileName:
@@ -63,9 +63,9 @@ def textToVoice(self, text):
def get_valid_token(self):
"""
- 获取有效的阿里云token。
+ 获取有效的阿里云 token。
- :return: 返回有效的token字符串。
+ :return: 返回有效的 token 字符串。
"""
current_time = time.time()
if self.token is None or current_time >= self.token_expire_time:
@@ -73,9 +73,9 @@ def get_valid_token(self):
token_str = get_token.get_token()
token_data = json.loads(token_str)
self.token = token_data["Token"]["Id"]
- # 将过期时间减少一小段时间(例如5分钟),以避免在边界条件下的过期
+ # 将过期时间减少一小段时间(例如 5 分钟),以避免在边界条件下的过期
self.token_expire_time = token_data["Token"]["ExpireTime"] - 300
- logger.debug(f"新获取的阿里云token:{self.token}")
+ logger.debug(f"新获取的阿里云 token:{self.token}")
else:
- logger.debug("使用缓存的token")
+ logger.debug("使用缓存的 token")
return self.token
diff --git a/voice/audio_convert.py b/voice/audio_convert.py
index 426367883..5b5524a76 100644
--- a/voice/audio_convert.py
+++ b/voice/audio_convert.py
@@ -10,7 +10,7 @@
from pydub import AudioSegment
-sil_supports = [8000, 12000, 16000, 24000, 32000, 44100, 48000] # slk转wav时,支持的采样率
+sil_supports = [8000, 12000, 16000, 24000, 32000, 44100, 48000] # slk 转 wav 时,支持的采样率
def find_closest_sil_supports(sample_rate):
@@ -42,7 +42,7 @@ def get_pcm_from_wav(wav_path):
def any_to_mp3(any_path, mp3_path):
"""
- 把任意格式转成mp3文件
+ 把任意格式转成 mp3 文件
"""
if any_path.endswith(".mp3"):
shutil.copy2(any_path, mp3_path)
@@ -56,7 +56,7 @@ def any_to_mp3(any_path, mp3_path):
def any_to_wav(any_path, wav_path):
"""
- 把任意格式转成wav文件
+ 把任意格式转成 wav 文件
"""
if any_path.endswith(".wav"):
shutil.copy2(any_path, wav_path)
@@ -64,14 +64,14 @@ def any_to_wav(any_path, wav_path):
if any_path.endswith(".sil") or any_path.endswith(".silk") or any_path.endswith(".slk"):
return sil_to_wav(any_path, wav_path)
audio = AudioSegment.from_file(any_path)
- audio.set_frame_rate(8000) # 百度语音转写支持8000采样率, pcm_s16le, 单通道语音识别
+ audio.set_frame_rate(8000) # 百度语音转写支持 8000 采样率,pcm_s16le, 单通道语音识别
audio.set_channels(1)
audio.export(wav_path, format="wav", codec='pcm_s16le')
def any_to_sil(any_path, sil_path):
"""
- 把任意格式转成sil文件
+ 把任意格式转成 sil 文件
"""
if any_path.endswith(".sil") or any_path.endswith(".silk") or any_path.endswith(".slk"):
shutil.copy2(any_path, sil_path)
@@ -90,7 +90,7 @@ def any_to_sil(any_path, sil_path):
def any_to_amr(any_path, amr_path):
"""
- 把任意格式转成amr文件
+ 把任意格式转成 amr 文件
"""
if any_path.endswith(".amr"):
shutil.copy2(any_path, amr_path)
diff --git a/voice/azure/azure_voice.py b/voice/azure/azure_voice.py
index b5884ed4f..39360d821 100644
--- a/voice/azure/azure_voice.py
+++ b/voice/azure/azure_voice.py
@@ -16,9 +16,9 @@
"""
Azure voice
-主目录设置文件中需填写azure_voice_api_key和azure_voice_region
+主目录设置文件中需填写 azure_voice_api_key 和 azure_voice_region
-查看可用的 voice: https://speech.microsoft.com/portal/voicegallery
+查看可用的 voice:https://speech.microsoft.com/portal/voicegallery
"""
diff --git a/voice/baidu/README.md b/voice/baidu/README.md
index d4628a1f8..48c0d4ceb 100644
--- a/voice/baidu/README.md
+++ b/voice/baidu/README.md
@@ -4,11 +4,11 @@
pip install baidu-aip
pip install pydub
pip install pysilk
-还有ffmpeg,不同系统安装方式不同
+还有 ffmpeg,不同系统安装方式不同
-系统中收到的语音文件为mp3格式(wx)或者sil格式(wxy),如果要识别需要转换为pcm格式,转换后的文件为16k采样率,单声道,16bit的pcm文件
-发送时又需要(wx)转换为mp3格式,转换后的文件为16k采样率,单声道,16bit的pcm文件,(wxy)转换为sil格式,还要计算声音长度,发送时需要带上声音长度
-这些事情都在audio_convert.py中封装了,直接调用即可
+系统中收到的语音文件为 mp3 格式(wx)或者 sil 格式(wxy),如果要识别需要转换为 pcm 格式,转换后的文件为 16k 采样率,单声道,16bit 的 pcm 文件
+发送时又需要(wx)转换为 mp3 格式,转换后的文件为 16k 采样率,单声道,16bit 的 pcm 文件,(wxy)转换为 sil 格式,还要计算声音长度,发送时需要带上声音长度
+这些事情都在 audio_convert.py 中封装了,直接调用即可
参数说明
@@ -20,25 +20,25 @@ https://ai.baidu.com/ai-doc/SPEECH/Gk38y8lzk
## 使用说明
分两个地方配置
-1、对于def voiceToText(self, filename)函数中调用的百度语音识别API,中接口调用asr(参数)这个配置见CHATGPT-ON-WECHAT工程目录下的`config.json`文件和config.py文件。
+1、对于 def voiceToText(self, filename) 函数中调用的百度语音识别 API,中接口调用 asr(参数)这个配置见 CHATGPT-ON-WECHAT 工程目录下的`config.json`文件和 config.py 文件。
参数 可需 描述
-app_id 必填 应用的APPID
-api_key 必填 应用的APIKey
-secret_key 必填 应用的SecretKey
-dev_pid 必填 语言选择,填写语言对应的dev_pid值
+app_id 必填 应用的 APPID
+api_key 必填 应用的 APIKey
+secret_key 必填 应用的 SecretKey
+dev_pid 必填 语言选择,填写语言对应的 dev_pid 值
-2、对于def textToVoice(self, text)函数中调用的百度语音合成API,中接口调用synthesis(参数)在本目录下的`config.json`文件中进行配置。
+2、对于 def textToVoice(self, text) 函数中调用的百度语音合成 API,中接口调用 synthesis(参数)在本目录下的`config.json`文件中进行配置。
参数 可需 描述
-tex 必填 合成的文本,使用UTF-8编码,请注意文本长度必须小于1024字节
-lan 必填 固定值zh。语言选择,目前只有中英文混合模式,填写固定值zh
-spd 选填 语速,取值0-15,默认为5中语速
-pit 选填 音调,取值0-15,默认为5中语调
-vol 选填 音量,取值0-15,默认为5中音量(取值为0时为音量最小值,并非为无声)
+tex 必填 合成的文本,使用 UTF-8 编码,请注意文本长度必须小于 1024 字节
+lan 必填 固定值 zh。语言选择,目前只有中英文混合模式,填写固定值 zh
+spd 选填 语速,取值 0-15,默认为 5 中语速
+pit 选填 音调,取值 0-15,默认为 5 中语调
+vol 选填 音量,取值 0-15,默认为 5 中音量(取值为 0 时为音量最小值,并非为无声)
per(基础音库) 选填 度小宇=1,度小美=0,度逍遥(基础)=3,度丫丫=4
per(精品音库) 选填 度逍遥(精品)=5003,度小鹿=5118,度博文=106,度小童=110,度小萌=111,度米朵=103,度小娇=5
-aue 选填 3为mp3格式(默认); 4为pcm-16k;5为pcm-8k;6为wav(内容同pcm-16k); 注意aue=4或者6是语音识别要求的格式,但是音频内容不是语音识别要求的自然人发音,所以识别效果会受影响。
+aue 选填 3 为 mp3 格式 (默认);4 为 pcm-16k;5 为 pcm-8k;6 为 wav(内容同 pcm-16k); 注意 aue=4 或者 6 是语音识别要求的格式,但是音频内容不是语音识别要求的自然人发音,所以识别效果会受影响。
-关于per参数的说明,注意您购买的哪个音库,就填写哪个音库的参数,否则会报错。如果您购买的是基础音库,那么per参数只能填写0到4,如果您购买的是精品音库,那么per参数只能填写5003,5118,106,110,111,103,5其他的都会报错。
+关于 per 参数的说明,注意您购买的哪个音库,就填写哪个音库的参数,否则会报错。如果您购买的是基础音库,那么 per 参数只能填写 0 到 4,如果您购买的是精品音库,那么 per 参数只能填写 5003,5118,106,110,111,103,5 其他的都会报错。
### 配置文件
将文件夹中`config.json.template`复制为`config.json`。
diff --git a/voice/baidu/baidu_voice.py b/voice/baidu/baidu_voice.py
index 66ba4d892..02a26c94d 100644
--- a/voice/baidu/baidu_voice.py
+++ b/voice/baidu/baidu_voice.py
@@ -15,17 +15,17 @@
from voice.voice import Voice
"""
- 百度的语音识别API.
+ 百度的语音识别 API.
dev_pid:
- 1936: 普通话远场
- - 1536:普通话(支持简单的英文识别)
- - 1537:普通话(纯中文识别)
+ - 1536:普通话 (支持简单的英文识别)
+ - 1537:普通话 (纯中文识别)
- 1737:英语
- 1637:粤语
- 1837:四川话
- 要使用本模块, 首先到 yuyin.baidu.com 注册一个开发者账号,
- 之后创建一个新应用, 然后在应用管理的"查看key"中获得 API Key 和 Secret Key
- 然后在 config.json 中填入这两个值, 以及 app_id, dev_pid
+ 要使用本模块,首先到 yuyin.baidu.com 注册一个开发者账号,
+ 之后创建一个新应用,然后在应用管理的"查看 key"中获得 API Key 和 Secret Key
+ 然后在 config.json 中填入这两个值,以及 app_id, dev_pid
"""
@@ -68,7 +68,7 @@ def voiceToText(self, voice_file):
text = "".join(res["result"])
reply = Reply(ReplyType.TEXT, text)
else:
- logger.info("百度语音识别出错了: {}".format(res["err_msg"]))
+ logger.info("百度语音识别出错了:{}".format(res["err_msg"]))
if res["err_msg"] == "request pv too much":
logger.info(" 出现这个原因很可能是你的百度语音服务调用量超出限制,或未开通付费")
reply = Reply(ReplyType.ERROR, "百度语音识别出错了;{0}".format(res["err_msg"]))