feat: complete the QQ channel and supplement the docs

This commit is contained in:
zhayujie
2026-03-17 17:25:36 +08:00
parent 005a0e1bad
commit a4d54f58c8
12 changed files with 355 additions and 23 deletions

View File

@@ -7,7 +7,7 @@
[中文] | [<a href="docs/en/README.md">English</a>] [中文] | [<a href="docs/en/README.md">English</a>]
</p> </p>
**CowAgent** 是基于大模型的超级AI助理能够主动思考和任务规划、操作计算机和外部资源、创造和执行Skills、拥有长期记忆并不断成长。CowAgent 支持灵活切换多种模型,能处理文本、语音、图片、文件等多模态消息,可接入网页、飞书、钉钉、企微智能机器人、企业微信应用、微信公众号中使用7*24小时运行于你的个人电脑或服务器中。 **CowAgent** 是基于大模型的超级AI助理能够主动思考和任务规划、操作计算机和外部资源、创造和执行Skills、拥有长期记忆并不断成长。CowAgent 支持灵活切换多种模型,能处理文本、语音、图片、文件等多模态消息,可接入网页、飞书、钉钉、企微智能机器人、QQ、企微自建应用、微信公众号中使用7*24小时运行于你的个人电脑或服务器中。
<p align="center"> <p align="center">
<a href="https://cowagent.ai/">🌐 官网</a> &nbsp;·&nbsp; <a href="https://cowagent.ai/">🌐 官网</a> &nbsp;·&nbsp;
@@ -143,7 +143,7 @@ pip3 install -r requirements-optional.txt
```bash ```bash
# config.json 文件内容示例 # config.json 文件内容示例
{ {
"channel_type": "web", # 接入渠道类型默认为web支持修改为:feishu,dingtalk,wecom_bot,wechatcom_app,wechatmp_service,wechatmp,terminal "channel_type": "web", # 接入渠道类型默认为web支持修改为:feishu,dingtalk,wecom_bot,qq,wechatcom_app,wechatmp_service,wechatmp,terminal
"model": "MiniMax-M2.5", # 模型名称 "model": "MiniMax-M2.5", # 模型名称
"minimax_api_key": "", # MiniMax API Key "minimax_api_key": "", # MiniMax API Key
"zhipu_ai_api_key": "", # 智谱GLM API Key "zhipu_ai_api_key": "", # 智谱GLM API Key
@@ -702,7 +702,23 @@ API Key创建在 [控制台](https://aistudio.google.com/app/apikey?hl=zh-cn)
</details> </details>
<details> <details>
<summary>5. WeCom App - 企业微信应用</summary> <summary>5. QQ - QQ 机器人</summary>
QQ 机器人使用 WebSocket 长连接模式,无需公网 IP 和域名,支持 QQ 单聊、群聊和频道消息:
```json
{
"channel_type": "qq",
"qq_app_id": "YOUR_APP_ID",
"qq_app_secret": "YOUR_APP_SECRET"
}
```
详细步骤和参数说明参考 [QQ 机器人接入](https://docs.cowagent.ai/channels/qq)
</details>
<details>
<summary>6. WeCom App - 企业微信应用</summary>
企业微信自建应用接入需在后台创建应用并启用消息回调,配置示例: 企业微信自建应用接入需在后台创建应用并启用消息回调,配置示例:
@@ -722,7 +738,7 @@ API Key创建在 [控制台](https://aistudio.google.com/app/apikey?hl=zh-cn)
</details> </details>
<details> <details>
<summary>6. WeChat MP - 微信公众号</summary> <summary>7. WeChat MP - 微信公众号</summary>
本项目支持订阅号和服务号两种公众号,通过服务号(`wechatmp_service`)体验更佳。 本项目支持订阅号和服务号两种公众号,通过服务号(`wechatmp_service`)体验更佳。
@@ -757,7 +773,7 @@ API Key创建在 [控制台](https://aistudio.google.com/app/apikey?hl=zh-cn)
</details> </details>
<details> <details>
<summary>7. Terminal - 终端</summary> <summary>8. Terminal - 终端</summary>
修改 `config.json` 中的 `channel_type` 字段: 修改 `config.json` 中的 `channel_type` 字段:

View File

@@ -237,6 +237,8 @@ def _execute_send_message(task: dict, agent_bridge):
logger.warning(f"[Scheduler] Task {task['id']}: DingTalk single chat message missing sender_staff_id") logger.warning(f"[Scheduler] Task {task['id']}: DingTalk single chat message missing sender_staff_id")
elif channel_type == "wecom_bot": elif channel_type == "wecom_bot":
context["msg"] = None context["msg"] = None
elif channel_type == "qq":
context["msg"] = None
# Create reply # Create reply
reply = Reply(ReplyType.TEXT, content) reply = Reply(ReplyType.TEXT, content)

View File

@@ -130,7 +130,7 @@ class QQChannel(ChatChannel):
self._access_token = data.get("access_token", "") self._access_token = data.get("access_token", "")
expires_in = int(data.get("expires_in", 7200)) expires_in = int(data.get("expires_in", 7200))
self._token_expires_at = time.time() + expires_in - 60 self._token_expires_at = time.time() + expires_in - 60
logger.info(f"[QQ] Access token refreshed, expires_in={expires_in}s") logger.debug(f"[QQ] Access token refreshed, expires_in={expires_in}s")
except Exception as e: except Exception as e:
logger.error(f"[QQ] Failed to refresh access_token: {e}") logger.error(f"[QQ] Failed to refresh access_token: {e}")
@@ -159,7 +159,7 @@ class QQChannel(ChatChannel):
) )
resp.raise_for_status() resp.raise_for_status()
url = resp.json().get("url", "") url = resp.json().get("url", "")
logger.info(f"[QQ] Gateway URL: {url}") logger.debug(f"[QQ] Gateway URL: {url}")
return url return url
except Exception as e: except Exception as e:
logger.error(f"[QQ] Failed to get gateway URL: {e}") logger.error(f"[QQ] Failed to get gateway URL: {e}")
@@ -173,7 +173,7 @@ class QQChannel(ChatChannel):
return return
def _on_open(ws): def _on_open(ws):
logger.info("[QQ] WebSocket connected, waiting for Hello...") logger.debug("[QQ] WebSocket connected, waiting for Hello...")
def _on_message(ws, raw): def _on_message(ws, raw):
try: try:
@@ -242,7 +242,7 @@ class QQChannel(ChatChannel):
}, },
}, },
}) })
logger.info(f"[QQ] Identify sent with intents={DEFAULT_INTENTS}") logger.debug(f"[QQ] Identify sent with intents={DEFAULT_INTENTS}")
def _send_resume(self): def _send_resume(self):
self._ws_send({ self._ws_send({
@@ -253,7 +253,7 @@ class QQChannel(ChatChannel):
"seq": self._last_seq, "seq": self._last_seq,
}, },
}) })
logger.info(f"[QQ] Resume sent: session_id={self._session_id}, seq={self._last_seq}") logger.debug(f"[QQ] Resume sent: session_id={self._session_id}, seq={self._last_seq}")
def _start_heartbeat(self, interval_ms: int): def _start_heartbeat(self, interval_ms: int):
if self._heartbeat_thread and self._heartbeat_thread.is_alive(): if self._heartbeat_thread and self._heartbeat_thread.is_alive():
@@ -291,7 +291,7 @@ class QQChannel(ChatChannel):
if op == OP_HELLO: if op == OP_HELLO:
heartbeat_interval = d.get("heartbeat_interval", 45000) if d else 45000 heartbeat_interval = d.get("heartbeat_interval", 45000) if d else 45000
logger.info(f"[QQ] Received Hello, heartbeat_interval={heartbeat_interval}ms") logger.debug(f"[QQ] Received Hello, heartbeat_interval={heartbeat_interval}ms")
self._heartbeat_interval = heartbeat_interval self._heartbeat_interval = heartbeat_interval
if self._can_resume and self._session_id: if self._can_resume and self._session_id:
self._send_resume() self._send_resume()
@@ -321,8 +321,8 @@ class QQChannel(ChatChannel):
if t == "READY": if t == "READY":
self._session_id = d.get("session_id", "") self._session_id = d.get("session_id", "")
user = d.get("user", {}) user = d.get("user", {})
logger.info(f"[QQ] Ready: session_id={self._session_id}, " bot_name = user.get('username', '')
f"bot={user.get('username', '')}") logger.info(f"[QQ] ✅ Connected successfully (bot={bot_name})")
self._connected = True self._connected = True
self._can_resume = False self._can_resume = False
self._start_heartbeat(self._heartbeat_interval) self._start_heartbeat(self._heartbeat_interval)
@@ -445,8 +445,13 @@ class QQChannel(ChatChannel):
def send(self, reply: Reply, context: Context): def send(self, reply: Reply, context: Context):
msg = context.get("msg") msg = context.get("msg")
is_group = context.get("isgroup", False)
receiver = context.get("receiver", "")
if not msg: if not msg:
logger.warning("[QQ] No msg in context, cannot send reply") # Active send (e.g. scheduled tasks), no original message to reply to
self._active_send_text(reply.content if reply.type == ReplyType.TEXT else str(reply.content),
receiver, is_group)
return return
event_type = getattr(msg, "event_type", "") event_type = getattr(msg, "event_type", "")
@@ -521,6 +526,26 @@ class QQChannel(ChatChannel):
except Exception as e: except Exception as e:
logger.error(f"[QQ] Send message error: {e}") logger.error(f"[QQ] Send message error: {e}")
# ------------------------------------------------------------------
# Active send (no original message, e.g. scheduled tasks)
# ------------------------------------------------------------------
def _active_send_text(self, content: str, receiver: str, is_group: bool):
"""Send text without an original message (active push). QQ limits active messages to 4/month per user."""
if not receiver:
logger.warning("[QQ] No receiver for active send")
return
if is_group:
url = f"{QQ_API_BASE}/v2/groups/{receiver}/messages"
else:
url = f"{QQ_API_BASE}/v2/users/{receiver}/messages"
body = {
"content": content,
"msg_type": 0,
}
event_label = "GROUP_ACTIVE" if is_group else "C2C_ACTIVE"
self._post_message(url, body, event_label)
# ------------------------------------------------------------------ # ------------------------------------------------------------------
# Send text # Send text
# ------------------------------------------------------------------ # ------------------------------------------------------------------

View File

@@ -615,6 +615,15 @@ class ChannelsHandler:
{"key": "wecom_bot_secret", "label": "Secret", "type": "secret"}, {"key": "wecom_bot_secret", "label": "Secret", "type": "secret"},
], ],
}), }),
("qq", {
"label": {"zh": "QQ 机器人", "en": "QQ Bot"},
"icon": "fa-comment",
"color": "blue",
"fields": [
{"key": "qq_app_id", "label": "App ID", "type": "text"},
{"key": "qq_app_secret", "label": "App Secret", "type": "secret"},
],
}),
("wechatcom_app", { ("wechatcom_app", {
"label": {"zh": "企微自建应用", "en": "WeCom App"}, "label": {"zh": "企微自建应用", "en": "WeCom App"},
"icon": "fa-building", "icon": "fa-building",

View File

@@ -26,6 +26,8 @@ CHANNEL_ACTIONS = {"channel_create", "channel_update", "channel_delete"}
CREDENTIAL_MAP = { CREDENTIAL_MAP = {
"feishu": ("feishu_app_id", "feishu_app_secret"), "feishu": ("feishu_app_id", "feishu_app_secret"),
"dingtalk": ("dingtalk_client_id", "dingtalk_client_secret"), "dingtalk": ("dingtalk_client_id", "dingtalk_client_secret"),
"wecom_bot": ("wecom_bot_id", "wecom_bot_secret"),
"qq": ("qq_app_id", "qq_app_secret"),
"wechatmp": ("wechatmp_app_id", "wechatmp_app_secret"), "wechatmp": ("wechatmp_app_id", "wechatmp_app_secret"),
"wechatmp_service": ("wechatmp_app_id", "wechatmp_app_secret"), "wechatmp_service": ("wechatmp_app_id", "wechatmp_app_secret"),
"wechatcom_app": ("wechatcomapp_agent_id", "wechatcomapp_secret"), "wechatcom_app": ("wechatcomapp_agent_id", "wechatcomapp_secret"),
@@ -669,6 +671,12 @@ def _build_config():
elif current_channel_type in ("wechatmp", "wechatmp_service"): elif current_channel_type in ("wechatmp", "wechatmp_service"):
config["app_id"] = local_conf.get("wechatmp_app_id") config["app_id"] = local_conf.get("wechatmp_app_id")
config["app_secret"] = local_conf.get("wechatmp_app_secret") config["app_secret"] = local_conf.get("wechatmp_app_secret")
elif current_channel_type == "wecom_bot":
config["app_id"] = local_conf.get("wecom_bot_id")
config["app_secret"] = local_conf.get("wecom_bot_secret")
elif current_channel_type == "qq":
config["app_id"] = local_conf.get("qq_app_id")
config["app_secret"] = local_conf.get("qq_app_secret")
elif current_channel_type == "wechatcom_app": elif current_channel_type == "wechatcom_app":
config["app_id"] = local_conf.get("wechatcomapp_agent_id") config["app_id"] = local_conf.get("wechatcomapp_agent_id")
config["app_secret"] = local_conf.get("wechatcomapp_secret") config["app_secret"] = local_conf.get("wechatcomapp_secret")

View File

@@ -381,6 +381,8 @@ def load_config():
"wechatmp_app_secret": "WECHATMP_APP_SECRET", "wechatmp_app_secret": "WECHATMP_APP_SECRET",
"wechatcomapp_agent_id": "WECHATCOMAPP_AGENT_ID", "wechatcomapp_agent_id": "WECHATCOMAPP_AGENT_ID",
"wechatcomapp_secret": "WECHATCOMAPP_SECRET", "wechatcomapp_secret": "WECHATCOMAPP_SECRET",
"qq_app_id": "QQ_APP_ID",
"qq_app_secret": "QQ_APP_SECRET"
} }
injected = 0 injected = 0
for conf_key, env_key in _CONFIG_TO_ENV.items(): for conf_key, env_key in _CONFIG_TO_ENV.items():

View File

@@ -179,5 +179,7 @@ Agent支持在多种渠道中使用只需修改 `config.json` 中的 `channel
- **飞书接入**[飞书接入文档](https://docs.link-ai.tech/cow/multi-platform/feishu) - **飞书接入**[飞书接入文档](https://docs.link-ai.tech/cow/multi-platform/feishu)
- **钉钉接入**[钉钉接入文档](https://docs.link-ai.tech/cow/multi-platform/dingtalk) - **钉钉接入**[钉钉接入文档](https://docs.link-ai.tech/cow/multi-platform/dingtalk)
- **企业微信应用接入**[企微应用文档](https://docs.link-ai.tech/cow/multi-platform/wechat-com) - **企业微信应用接入**[企微应用文档](https://docs.link-ai.tech/cow/multi-platform/wechat-com)
- **企微智能机器人**[企微智能机器人文档](https://docs.link-ai.tech/cow/multi-platform/wecom-bot)
- **QQ机器人**[QQ机器人文档](https://docs.link-ai.tech/cow/multi-platform/qq)
更多渠道配置参考:[通道说明](../README.md#通道说明) 更多渠道配置参考:[通道说明](../README.md#通道说明)

88
docs/channels/qq.mdx Normal file
View File

@@ -0,0 +1,88 @@
---
title: QQ 机器人
description: 将 CowAgent 接入 QQ 机器人WebSocket 长连接模式)
---
> 通过 QQ 开放平台的机器人接口接入 CowAgent支持 QQ 单聊、QQ 群聊(@机器人)、频道消息和频道私信,无需公网 IP使用 WebSocket 长连接模式。
<Note>
QQ 机器人通过 QQ 开放平台创建,使用 WebSocket 长连接接收消息,通过 OpenAPI 发送消息,无需公网 IP 和域名。
</Note>
## 一、创建 QQ 机器人
> 进入[QQ 开放平台](https://q.qq.com)QQ扫码登录如果未注册开放平台账号请先完成[账号注册](https://q.qq.com/#/register)。
1.在 [QQ开放平台-机器人列表页](https://q.qq.com/#/apps),点击创建机器人:
<img src="https://cdn.link-ai.tech/doc/20260317162900.png" width="800"/>
2.填写机器人名称、头像等基本信息,完成创建:
<img src="https://cdn.link-ai.tech/doc/20260317163005.png" width="800"/>
3.点击进入机器人配置页面,选择**开发管理**菜单,完成以下步骤:
- 复制并记录 **AppID**机器人ID
- 生成并记录 **AppSecret**(机器人秘钥)
<img src="https://cdn.link-ai.tech/doc/20260317164955.png" width="800"/>
## 二、配置和运行
### 方式一Web 控制台接入
启动 Cow项目后打开 Web 控制台 (本地链接为: http://127.0.0.1:9899/ ),选择 **通道** 菜单,点击 **接入通道**,选择 **QQ 机器人**,填写上一步保存的 AppID 和 AppSecret点击接入即可。
<img src="https://cdn.link-ai.tech/doc/20260317165425.png" width="800"/>
### 方式二:配置文件接入
在 `config.json` 中添加以下配置:
```json
{
"channel_type": "qq",
"qq_app_id": "YOUR_APP_ID",
"qq_app_secret": "YOUR_APP_SECRET"
}
```
| 参数 | 说明 |
| --- | --- |
| `qq_app_id` | QQ 机器人的 AppID在开放平台开发管理中获取 |
| `qq_app_secret` | QQ 机器人的 AppSecret在开放平台开发管理中获取 |
配置完成后启动程序,日志显示 `[QQ] ✅ Connected successfully` 即表示连接成功。
## 三、使用
在 QQ开放平台 - 管理 - **使用范围和人员** 菜单中使用QQ客户端扫描 "添加到群和消息列表" 的二维码即可开始与QQ机器人的聊天
<img src="https://cdn.link-ai.tech/doc/20260317165947.png" width="800"/>
对话效果:
<img src="https://cdn.link-ai.tech/doc/20260317171508.png" width="800"/>
## 四、功能说明
> 注意若需在群聊及频道中使用QQ机器人需完成发布上架审核并在使用范围配置权限使用范围。
| 功能 | 支持情况 |
| --- | --- |
| QQ 单聊 | ✅ |
| QQ 群聊(@机器人) | ✅ |
| 频道消息(@机器人) | ✅ |
| 频道私信 | ✅ |
| 文本消息 | ✅ 收发 |
| 图片消息 | ✅ 收发(群聊和单聊) |
| 文件消息 | ✅ 发送(群聊和单聊) |
| 定时任务 | ✅ 主动推送(每月每用户限 4 条) |
## 五、注意事项
- **被动消息限制**QQ 单聊被动消息有效期为 60 分钟,每条消息最多回复 5 次QQ 群聊被动消息有效期为 5 分钟。
- **主动消息限制**:单聊和群聊每月主动消息上限为 4 条,在使用定时任务功能时需要注意这个限制
- **事件权限**:默认订阅 `GROUP_AND_C2C_EVENT`QQ群/单聊)和 `PUBLIC_GUILD_MESSAGES`(频道公域消息),如需其他事件类型请在开放平台申请权限。

View File

@@ -29,7 +29,7 @@ description: 将 CowAgent 接入企业微信智能机器人(长连接模式)
### 方式一Web 控制台接入 ### 方式一Web 控制台接入
启动程序后打开 Web 控制台 (本地接为: http://127.0.0.1:9899/ ),选择 **通道** 菜单,点击 **接入通道**,选择 **企微智能机器人**,填写上一步保存的 Bot ID 和 Secret点击接入即可。 启动Cow项目后打开 Web 控制台 (本地接为: http://127.0.0.1:9899/ ),选择 **通道** 菜单,点击 **接入通道**,选择 **企微智能机器人**,填写上一步保存的 Bot ID 和 Secret点击接入即可。
<img src="https://cdn.link-ai.tech/doc/20260316181711.png" width="800"/> <img src="https://cdn.link-ai.tech/doc/20260316181711.png" width="800"/>

View File

@@ -157,6 +157,7 @@
"channels/feishu", "channels/feishu",
"channels/dingtalk", "channels/dingtalk",
"channels/wecom-bot", "channels/wecom-bot",
"channels/qq",
"channels/wecom", "channels/wecom",
"channels/wechatmp" "channels/wechatmp"
] ]
@@ -300,6 +301,7 @@
"en/channels/feishu", "en/channels/feishu",
"en/channels/dingtalk", "en/channels/dingtalk",
"en/channels/wecom-bot", "en/channels/wecom-bot",
"en/channels/qq",
"en/channels/wecom", "en/channels/wecom",
"en/channels/wechatmp" "en/channels/wechatmp"
] ]

88
docs/en/channels/qq.mdx Normal file
View File

@@ -0,0 +1,88 @@
---
title: QQ Bot
description: Connect CowAgent to QQ Bot (WebSocket long connection)
---
> Connect CowAgent via QQ Open Platform's bot API, supporting QQ direct messages, group chats (@bot), guild channel messages, and guild DMs. No public IP required — uses WebSocket long connection.
<Note>
QQ Bot is created through the QQ Open Platform. It uses WebSocket long connection to receive messages and OpenAPI to send messages. No public IP or domain is required.
</Note>
## 1. Create a QQ Bot
> Visit the [QQ Open Platform](https://q.qq.com), sign in with QQ. If you haven't registered, please complete [account registration](https://q.qq.com/#/register) first.
1.Go to the [QQ Open Platform - Bot List](https://q.qq.com/#/apps), and click **Create Bot**:
<img src="https://cdn.link-ai.tech/doc/20260317162900.png" width="800"/>
2.Fill in the bot name, avatar, and other basic information to complete the creation:
<img src="https://cdn.link-ai.tech/doc/20260317163005.png" width="800"/>
3.Enter the bot configuration page, go to **Development Management**, and complete the following steps:
- Copy and save the **AppID** (Bot ID)
- Generate and save the **AppSecret** (Bot Secret)
<img src="https://cdn.link-ai.tech/doc/20260317164955.png" width="800"/>
## 2. Configuration and Running
### Option A: Web Console
Start the program and open the Web console (local access: http://127.0.0.1:9899/). Go to the **Channels** tab, click **Connect Channel**, select **QQ Bot**, fill in the AppID and AppSecret from the previous step, and click Connect.
<img src="https://cdn.link-ai.tech/doc/20260317165425.png" width="800"/>
### Option B: Config File
Add the following to your `config.json`:
```json
{
"channel_type": "qq",
"qq_app_id": "YOUR_APP_ID",
"qq_app_secret": "YOUR_APP_SECRET"
}
```
| Parameter | Description |
| --- | --- |
| `qq_app_id` | AppID of the QQ Bot, found in Development Management on the open platform |
| `qq_app_secret` | AppSecret of the QQ Bot, found in Development Management on the open platform |
After configuration, start the program. The log message `[QQ] ✅ Connected successfully` indicates a successful connection.
## 3. Usage
In the QQ Open Platform, go to **Management → Usage Scope & Members**, scan the "Add to group and message list" QR code with your QQ client to start chatting with the bot:
<img src="https://cdn.link-ai.tech/doc/20260317165947.png" width="800"/>
Chat example:
<img src="https://cdn.link-ai.tech/doc/20260317171508.png" width="800"/>
## 4. Supported Features
> Note: To use the QQ bot in group chats and guild channels, you need to complete the publishing review and configure usage scope permissions.
| Feature | Status |
| --- | --- |
| QQ Direct Messages | ✅ |
| QQ Group Chat (@bot) | ✅ |
| Guild Channel (@bot) | ✅ |
| Guild DM | ✅ |
| Text Messages | ✅ Send & Receive |
| Image Messages | ✅ Send & Receive (group & direct) |
| File Messages | ✅ Send (group & direct) |
| Scheduled Tasks | ✅ Active push (4 per user per month) |
## 5. Notes
- **Passive message limits**: QQ direct message replies are valid for 60 minutes (max 5 replies per message); group chat replies are valid for 5 minutes.
- **Active message limits**: Both direct and group chats have a monthly limit of 4 active messages. Keep this in mind when using the scheduled tasks feature.
- **Event permissions**: By default, `GROUP_AND_C2C_EVENT` (QQ group/direct) and `PUBLIC_GUILD_MESSAGES` (guild public messages) are subscribed. Apply for additional permissions on the open platform if needed.

106
run.sh
View File

@@ -409,19 +409,21 @@ select_channel() {
echo -e "${CYAN}${BOLD}=========================================${NC}" echo -e "${CYAN}${BOLD}=========================================${NC}"
echo -e "${YELLOW}1) Feishu (飞书)${NC}" echo -e "${YELLOW}1) Feishu (飞书)${NC}"
echo -e "${YELLOW}2) DingTalk (钉钉)${NC}" echo -e "${YELLOW}2) DingTalk (钉钉)${NC}"
echo -e "${YELLOW}3) WeCom (企微应用)${NC}" echo -e "${YELLOW}3) WeCom Bot (企微智能机器人)${NC}"
echo -e "${YELLOW}4) Web (网页)${NC}" echo -e "${YELLOW}4) QQ (QQ 机器人)${NC}"
echo -e "${YELLOW}5) WeCom App (企微自建应用)${NC}"
echo -e "${YELLOW}6) Web (网页)${NC}"
echo "" echo ""
while true; do while true; do
read -p "Enter your choice [press Enter for default: 1 - Feishu]: " channel_choice read -p "Enter your choice [press Enter for default: 1 - Feishu]: " channel_choice
channel_choice=${channel_choice:-1} channel_choice=${channel_choice:-1}
case "$channel_choice" in case "$channel_choice" in
1|2|3|4) 1|2|3|4|5|6)
break break
;; ;;
*) *)
echo -e "${RED}Invalid choice. Please enter 1-4.${NC}" echo -e "${RED}Invalid choice. Please enter 1-6.${NC}"
;; ;;
esac esac
done done
@@ -456,9 +458,31 @@ configure_channel() {
ACCESS_INFO="DingTalk channel configured" ACCESS_INFO="DingTalk channel configured"
;; ;;
3) 3)
# WeCom # WeCom Bot
CHANNEL_TYPE="wecom_bot"
echo -e "${GREEN}Configure WeCom Bot...${NC}"
read -p "Enter WeCom Bot ID: " wecom_bot_id
read -p "Enter WeCom Bot Secret: " wecom_bot_secret
WECOM_BOT_ID="$wecom_bot_id"
WECOM_BOT_SECRET="$wecom_bot_secret"
ACCESS_INFO="WeCom Bot channel configured"
;;
4)
# QQ
CHANNEL_TYPE="qq"
echo -e "${GREEN}Configure QQ Bot...${NC}"
read -p "Enter QQ App ID: " qq_app_id
read -p "Enter QQ App Secret: " qq_app_secret
QQ_APP_ID="$qq_app_id"
QQ_APP_SECRET="$qq_app_secret"
ACCESS_INFO="QQ Bot channel configured"
;;
5)
# WeCom App
CHANNEL_TYPE="wechatcom_app" CHANNEL_TYPE="wechatcom_app"
echo -e "${GREEN}Configure WeCom...${NC}" echo -e "${GREEN}Configure WeCom App...${NC}"
read -p "Enter WeChat Corp ID: " corp_id read -p "Enter WeChat Corp ID: " corp_id
read -p "Enter WeChat Com App Token: " com_token read -p "Enter WeChat Com App Token: " com_token
read -p "Enter WeChat Com App Secret: " com_secret read -p "Enter WeChat Com App Secret: " com_secret
@@ -473,9 +497,9 @@ configure_channel() {
WECHATCOM_AGENT_ID="$com_agent_id" WECHATCOM_AGENT_ID="$com_agent_id"
WECHATCOM_AES_KEY="$com_aes_key" WECHATCOM_AES_KEY="$com_aes_key"
WECHATCOM_PORT="$com_port" WECHATCOM_PORT="$com_port"
ACCESS_INFO="WeCom channel configured on port ${com_port}" ACCESS_INFO="WeCom App channel configured on port ${com_port}"
;; ;;
4) 6)
# Web # Web
CHANNEL_TYPE="web" CHANNEL_TYPE="web"
read -p "Enter web port [press Enter for default: 9899]: " web_port read -p "Enter web port [press Enter for default: 9899]: " web_port
@@ -600,6 +624,72 @@ EOF
"agent_max_context_turns": 30, "agent_max_context_turns": 30,
"agent_max_steps": 15 "agent_max_steps": 15
} }
EOF
;;
wecom_bot)
cat > config.json <<EOF
{
"channel_type": "wecom_bot",
"wecom_bot_id": "${WECOM_BOT_ID}",
"wecom_bot_secret": "${WECOM_BOT_SECRET}",
"model": "${MODEL_NAME}",
"open_ai_api_key": "${OPENAI_KEY:-}",
"open_ai_api_base": "${OPENAI_BASE:-https://api.openai.com/v1}",
"claude_api_key": "${CLAUDE_KEY:-}",
"claude_api_base": "${CLAUDE_BASE:-https://api.anthropic.com/v1}",
"gemini_api_key": "${GEMINI_KEY:-}",
"gemini_api_base": "${GEMINI_BASE:-https://generativelanguage.googleapis.com}",
"zhipu_ai_api_key": "${ZHIPU_KEY:-}",
"moonshot_api_key": "${MOONSHOT_KEY:-}",
"ark_api_key": "${ARK_KEY:-}",
"dashscope_api_key": "${DASHSCOPE_KEY:-}",
"minimax_api_key": "${MINIMAX_KEY:-}",
"voice_to_text": "openai",
"text_to_voice": "openai",
"voice_reply_voice": false,
"speech_recognition": true,
"group_speech_recognition": false,
"use_linkai": ${USE_LINKAI:-false},
"linkai_api_key": "${LINKAI_KEY:-}",
"linkai_app_code": "",
"agent": true,
"agent_max_context_tokens": 40000,
"agent_max_context_turns": 30,
"agent_max_steps": 15
}
EOF
;;
qq)
cat > config.json <<EOF
{
"channel_type": "qq",
"qq_app_id": "${QQ_APP_ID}",
"qq_app_secret": "${QQ_APP_SECRET}",
"model": "${MODEL_NAME}",
"open_ai_api_key": "${OPENAI_KEY:-}",
"open_ai_api_base": "${OPENAI_BASE:-https://api.openai.com/v1}",
"claude_api_key": "${CLAUDE_KEY:-}",
"claude_api_base": "${CLAUDE_BASE:-https://api.anthropic.com/v1}",
"gemini_api_key": "${GEMINI_KEY:-}",
"gemini_api_base": "${GEMINI_BASE:-https://generativelanguage.googleapis.com}",
"zhipu_ai_api_key": "${ZHIPU_KEY:-}",
"moonshot_api_key": "${MOONSHOT_KEY:-}",
"ark_api_key": "${ARK_KEY:-}",
"dashscope_api_key": "${DASHSCOPE_KEY:-}",
"minimax_api_key": "${MINIMAX_KEY:-}",
"voice_to_text": "openai",
"text_to_voice": "openai",
"voice_reply_voice": false,
"speech_recognition": true,
"group_speech_recognition": false,
"use_linkai": ${USE_LINKAI:-false},
"linkai_api_key": "${LINKAI_KEY:-}",
"linkai_app_code": "",
"agent": true,
"agent_max_context_tokens": 40000,
"agent_max_context_turns": 30,
"agent_max_steps": 15
}
EOF EOF
;; ;;
wechatcom_app) wechatcom_app)