Compare commits

...

47 Commits

Author SHA1 Message Date
Junyan Qin
bd0438df76 chore: release v3.4.10.4 2025-03-16 17:05:10 +08:00
Junyan Qin (Chin)
9ca1fc59ef Merge pull request #1198 from fdc310/master
当机器人群名称改名后群聊单独at机器人时候替换@信息为空
2025-03-16 16:24:52 +08:00
Dong_master
84a80a5ec8 当是单独群聊at机器人时候替换@信息为空 2025-03-15 22:21:21 +08:00
Dong_master
4b2e248646 当是单独群聊at机器人时候替换@信息为空 2025-03-15 22:13:15 +08:00
Dong_master
b90e45590a 当微信群有at消息时删除第一个at已达到能激活管理员模式 2025-03-15 01:15:56 +08:00
Junyan Qin (Chin)
ff93d563a8 Merge pull request #1194 from fdc310/master
'增加了主动发送at信息'
2025-03-14 22:41:41 +08:00
Junyan Qin (Chin)
53228498ed Merge pull request #1191 from wangcham/master
fix: eliminate critical message queue blockage in wxoa
2025-03-14 22:29:03 +08:00
Dong_master
8ece82e43a '增加了主动发送at信息' 2025-03-14 02:33:52 +08:00
wangcham
8b4684675e fix: eliminate critical message queue blockage in wxoa 2025-03-13 10:44:09 -04:00
Junyan Qin (Chin)
8cca12fff2 Merge pull request #1190 from wangcham/master
feat: add support for longer response in wxoa
2025-03-13 17:30:19 +08:00
Junyan Qin
a74111612e chore: config for wxoa mode 2025-03-13 17:29:05 +08:00
wangcham
c7799a65c4 fix: update config in wxoa 2025-03-13 05:15:03 -04:00
wangcham
aabb01c50f feat: add support for longer response in wxoa 2025-03-12 23:39:43 -04:00
Junyan Qin
95e2ada965 fix(schema): adapt str type session id 2025-03-12 21:32:19 +08:00
Junyan Qin (Chin)
3fe7d53c76 Merge pull request #1188 from RockChinQ/feat/query-variables
feat: add query variables
2025-03-12 21:21:50 +08:00
Junyan Qin
e8634bb1ab feat(variables): add api for plugin 2025-03-12 20:57:42 +08:00
Junyan Qin
dbe46b5770 feat: add query variables 2025-03-12 19:13:04 +08:00
Junyan Qin (Chin)
6d9fba30b1 Merge pull request #1187 from wangcham/master
feat: add support for sending active messages in wecom
2025-03-12 16:49:47 +08:00
wangcham
6a866bf871 feat: add support for sending active messages in wecom 2025-03-12 04:03:02 -04:00
Junyan Qin (Chin)
3c961e4652 Merge pull request #1184 from wangcham/master
feat: add support for sending active messages in dingtalk
2025-03-11 16:42:29 +08:00
wangcham
7abd999420 feat: add support for sending active messages in dingtalk 2025-03-11 04:27:17 -04:00
Junyan Qin
fca8fbb135 perf: no longer add slash as ignored prefix as default 2025-03-11 12:06:37 +08:00
Junyan Qin
c67caf18df chore: release v3.4.10.3 2025-03-10 22:58:23 +08:00
Junyan Qin (Chin)
fe956fe4a5 Merge pull request #1181 from wangcham/master
fix: wrong group id in qqofficial web hook
2025-03-10 19:38:00 +08:00
wangcham
0e52f679a2 fix: wrong group id in webhook qq 2025-03-10 06:56:15 -04:00
Junyan Qin
b9500283ec fix(gewechat): remove necessary await in send_message 2025-03-10 15:16:10 +08:00
Junyan Qin (Chin)
441b69b528 Merge pull request #1174 from Muffeter/master 2025-03-07 18:37:18 +08:00
sonmu
898bcdc96b typo: fix README 2025-03-07 18:33:27 +08:00
Junyan Qin
02bc1fc45e chore: release v3.4.10.2 2025-03-05 21:12:52 +08:00
Junyan Qin (Chin)
5585981dc3 Merge pull request #1165 from fdc310/master
修复因为gewe将字段修改回原版而导致的我的判断逻辑问题
2025-03-05 21:12:21 +08:00
Dong_master
a4777f194b 修复因为gewe将字段修改回原版而导致的我的判断逻辑问题 2025-03-05 19:56:28 +08:00
Junyan Qin
41aeda8dc0 chore: release v3.4.10.1 2025-03-05 17:16:05 +08:00
Junyan Qin (Chin)
2ed522667e Merge pull request #1162 from fdc310/master
'修复了gewe更新回调参数data和typename字段改变造成的不回复的问题'
2025-03-05 17:14:27 +08:00
Dong_master
1932444666 '修复了gewe更新回调参数data和typename字段改变造成的不回复的问题' 2025-03-05 16:48:46 +08:00
Dong_master
b49b7e963d '修复了gewe更新回调参数data和typename字段改变造成的不回复的问题' 2025-03-05 00:54:39 +08:00
Junyan Qin
435c11ff27 doc(README): add more model in README 2025-03-03 21:26:39 +08:00
Junyan Qin
2e93600437 feat: update llm-models.json template 2025-03-03 21:02:48 +08:00
Junyan Qin (Chin)
faecb70d0f Merge pull request #1154 from Yi-Lyu/master
将微信消息时间戳传递给 dify,便于 dify 通过消息时间戳来做业务逻辑。
2025-03-02 20:21:08 +08:00
Junyan Qin
92e1ac5c3a feat: add supports for passing time to dify workflow 2025-03-02 20:18:33 +08:00
Junyan Qin
8963a2117b feat: add field time in MessageEvent 2025-03-02 20:16:34 +08:00
Ethan
aa300258ab feat: enhance user message preprocessing to include message creation timestamp 2025-03-02 19:45:10 +08:00
Ethan
48841daff5 feat: enhance user message preprocessing to include message creation timestamp 2025-03-02 19:30:07 +08:00
Ethan
8878f1ed87 feat: enhance user message preprocessing to include message creation timestamp 2025-03-02 19:20:10 +08:00
Ethan
f6205d79c0 feat: enhance user message preprocessing to include message creation timestamp 2025-03-02 19:18:26 +08:00
Ethan
d6d5dac6b3 feat: enhance user message preprocessing to include message creation timestamp 2025-03-02 19:10:53 +08:00
Ethan
05b979e68a feat: enhance user message preprocessing to include message creation timestamp 2025-03-02 19:10:09 +08:00
Ethan
9f7d9e4c0d feat: enhance user message preprocessing to include message creation timestamp 2025-03-02 18:49:32 +08:00
26 changed files with 458 additions and 71 deletions

View File

@@ -9,7 +9,7 @@
<a href="https://trendshift.io/repositories/12901" target="_blank"><img src="https://trendshift.io/api/badge/repositories/12901" alt="RockChinQ%2FLangBot | Trendshift" style="width: 250px; height: 55px;" width="250" height="55"/></a> <a href="https://trendshift.io/repositories/12901" target="_blank"><img src="https://trendshift.io/api/badge/repositories/12901" alt="RockChinQ%2FLangBot | Trendshift" style="width: 250px; height: 55px;" width="250" height="55"/></a>
<a href="https://docs.langbot.app">项目主页</a> <a href="https://docs.langbot.app">项目主页</a>
<a href="https://docs.langbot.app/insight/intro.htmll">功能介绍</a> <a href="https://docs.langbot.app/insight/intro.html">功能介绍</a>
<a href="https://docs.langbot.app/insight/guide.html">部署文档</a> <a href="https://docs.langbot.app/insight/guide.html">部署文档</a>
<a href="https://docs.langbot.app/usage/faq.html">常见问题</a> <a href="https://docs.langbot.app/usage/faq.html">常见问题</a>
<a href="https://docs.langbot.app/plugin/plugin-intro.html">插件介绍</a> <a href="https://docs.langbot.app/plugin/plugin-intro.html">插件介绍</a>
@@ -115,6 +115,20 @@
| [阿里云百炼](https://bailian.console.aliyun.com/) | ✅ | 大模型聚合平台, LLMOps 平台 | | [阿里云百炼](https://bailian.console.aliyun.com/) | ✅ | 大模型聚合平台, LLMOps 平台 |
| [火山方舟](https://console.volcengine.com/ark/region:ark+cn-beijing/model?vendor=Bytedance&view=LIST_VIEW) | ✅ | 大模型聚合平台, LLMOps 平台 | | [火山方舟](https://console.volcengine.com/ark/region:ark+cn-beijing/model?vendor=Bytedance&view=LIST_VIEW) | ✅ | 大模型聚合平台, LLMOps 平台 |
### TTS
| 平台/模型 | 备注 |
| --- | --- |
| [FishAudio](https://fish.audio/zh-CN/discovery/) | [插件](https://github.com/the-lazy-me/NewChatVoice) |
| [海豚 AI](https://www.ttson.cn/?source=thelazy) | [插件](https://github.com/the-lazy-me/NewChatVoice) |
| [AzureTTS](https://portal.azure.com/) | [插件](https://github.com/Ingnaryk/LangBot_AzureTTS) |
### 文生图
| 平台/模型 | 备注 |
| --- | --- |
| 阿里云百炼 | [插件](https://github.com/Thetail001/LangBot_BailianTextToImagePlugin)
## 😘 社区贡献 ## 😘 社区贡献
LangBot 离不开以下贡献者和社区内所有人的贡献,我们欢迎任何形式的贡献和反馈。 LangBot 离不开以下贡献者和社区内所有人的贡献,我们欢迎任何形式的贡献和反馈。

View File

@@ -8,7 +8,7 @@
<a href="https://trendshift.io/repositories/12901" target="_blank"><img src="https://trendshift.io/api/badge/repositories/12901" alt="RockChinQ%2FLangBot | Trendshift" style="width: 250px; height: 55px;" width="250" height="55"/></a> <a href="https://trendshift.io/repositories/12901" target="_blank"><img src="https://trendshift.io/api/badge/repositories/12901" alt="RockChinQ%2FLangBot | Trendshift" style="width: 250px; height: 55px;" width="250" height="55"/></a>
<a href="https://docs.langbot.app">Home</a> <a href="https://docs.langbot.app">Home</a>
<a href="https://docs.langbot.app/insight/intro.htmll">Features</a> <a href="https://docs.langbot.app/insight/intro.html">Features</a>
<a href="https://docs.langbot.app/insight/guide.html">Deployment</a> <a href="https://docs.langbot.app/insight/guide.html">Deployment</a>
<a href="https://docs.langbot.app/usage/faq.html">FAQ</a> <a href="https://docs.langbot.app/usage/faq.html">FAQ</a>
<a href="https://docs.langbot.app/plugin/plugin-intro.html">Plugin</a> <a href="https://docs.langbot.app/plugin/plugin-intro.html">Plugin</a>

View File

@@ -8,7 +8,7 @@
<a href="https://trendshift.io/repositories/12901" target="_blank"><img src="https://trendshift.io/api/badge/repositories/12901" alt="RockChinQ%2FLangBot | Trendshift" style="width: 250px; height: 55px;" width="250" height="55"/></a> <a href="https://trendshift.io/repositories/12901" target="_blank"><img src="https://trendshift.io/api/badge/repositories/12901" alt="RockChinQ%2FLangBot | Trendshift" style="width: 250px; height: 55px;" width="250" height="55"/></a>
<a href="https://docs.langbot.app">ホーム</a> <a href="https://docs.langbot.app">ホーム</a>
<a href="https://docs.langbot.app/insight/intro.htmll">機能</a> <a href="https://docs.langbot.app/insight/intro.html">機能</a>
<a href="https://docs.langbot.app/insight/guide.html">デプロイ</a> <a href="https://docs.langbot.app/insight/guide.html">デプロイ</a>
<a href="https://docs.langbot.app/usage/faq.html">FAQ</a> <a href="https://docs.langbot.app/usage/faq.html">FAQ</a>
<a href="https://docs.langbot.app/plugin/plugin-intro.html">プラグイン</a> <a href="https://docs.langbot.app/plugin/plugin-intro.html">プラグイン</a>

View File

@@ -23,6 +23,7 @@ class EchoTextHandler(dingtalk_stream.ChatbotHandler):
"""异步等待消息的到来""" """异步等待消息的到来"""
while self.incoming_message is None: while self.incoming_message is None:
await asyncio.sleep(0.1) # 异步等待,避免阻塞 await asyncio.sleep(0.1) # 异步等待,避免阻塞
return self.incoming_message return self.incoming_message
async def get_dingtalk_client(client_id, client_secret): async def get_dingtalk_client(client_id, client_secret):

View File

@@ -157,6 +157,7 @@ class DingTalkClient:
async def get_message(self,incoming_message:dingtalk_stream.chatbot.ChatbotMessage): async def get_message(self,incoming_message:dingtalk_stream.chatbot.ChatbotMessage):
try: try:
# print(json.dumps(incoming_message.to_dict(), indent=4, ensure_ascii=False)) # print(json.dumps(incoming_message.to_dict(), indent=4, ensure_ascii=False))
message_data = { message_data = {
"IncomingMessage":incoming_message, "IncomingMessage":incoming_message,
@@ -197,6 +198,53 @@ class DingTalkClient:
traceback.print_exc() traceback.print_exc()
return message_data return message_data
async def send_proactive_message_to_one(self,target_id:str,content:str):
if not await self.check_access_token():
await self.get_access_token()
url = 'https://api.dingtalk.com/v1.0/robot/oToMessages/batchSend'
headers ={
"x-acs-dingtalk-access-token":self.access_token,
"Content-Type":"application/json",
}
data ={
"robotCode":self.robot_code,
"userIds":[target_id],
"msgKey": "sampleText",
"msgParam": json.dumps({"content":content}),
}
try:
async with httpx.AsyncClient() as client:
response = await client.post(url,headers=headers,json=data)
except Exception:
traceback.print_exc()
async def send_proactive_message_to_group(self,target_id:str,content:str):
if not await self.check_access_token():
await self.get_access_token()
url = 'https://api.dingtalk.com/v1.0/robot/groupMessages/send'
headers ={
"x-acs-dingtalk-access-token":self.access_token,
"Content-Type":"application/json",
}
data ={
"robotCode":self.robot_code,
"openConversationId":target_id,
"msgKey": "sampleText",
"msgParam": json.dumps({"content":content}),
}
try:
async with httpx.AsyncClient() as client:
response = await client.post(url,headers=headers,json=data)
except Exception:
traceback.print_exc()
async def start(self): async def start(self):
"""启动 WebSocket 连接,监听消息""" """启动 WebSocket 连接,监听消息"""

View File

@@ -1,4 +1,5 @@
# 微信公众号的加解密算法与企业微信一样,所以直接使用企业微信的加解密算法文件 # 微信公众号的加解密算法与企业微信一样,所以直接使用企业微信的加解密算法文件
from collections import deque
import time import time
import traceback import traceback
from ..wecom_api.WXBizMsgCrypt3 import WXBizMsgCrypt from ..wecom_api.WXBizMsgCrypt3 import WXBizMsgCrypt
@@ -12,6 +13,7 @@ import httpx
import asyncio import asyncio
import time import time
import xml.etree.ElementTree as ET import xml.etree.ElementTree as ET
from pkg.platform.sources import officialaccount as oa
@@ -25,6 +27,8 @@ xml_template = """
</xml> </xml>
""" """
user_msg_queue = {}
class OAClient(): class OAClient():
def __init__(self,token:str,EncodingAESKey:str,AppID:str,Appsecret:str): def __init__(self,token:str,EncodingAESKey:str,AppID:str,Appsecret:str):
@@ -169,6 +173,152 @@ class OAClient():
await handler(event) await handler(event)
class OAClientForLongerResponse():
def __init__(self,token:str,EncodingAESKey:str,AppID:str,Appsecret:str):
self.token = token
self.aes = EncodingAESKey
self.appid = AppID
self.appsecret = Appsecret
self.base_url = 'https://api.weixin.qq.com'
self.access_token = ''
self.app = Quart(__name__)
self.app.add_url_rule('/callback/command', 'handle_callback', self.handle_callback_request, methods=['GET', 'POST'])
self._message_handlers = {
"example":[],
}
self.access_token_expiry_time = None
async def handle_callback_request(self):
try:
start_time = time.time()
signature = request.args.get("signature", "")
timestamp = request.args.get("timestamp", "")
nonce = request.args.get("nonce", "")
echostr = request.args.get("echostr", "")
msg_signature = request.args.get("msg_signature", "")
if msg_signature is None:
raise Exception("msg_signature不在请求体中")
if request.method == 'GET':
check_str = "".join(sorted([self.token, timestamp, nonce]))
check_signature = hashlib.sha1(check_str.encode("utf-8")).hexdigest()
return echostr if check_signature == signature else "拒绝请求"
elif request.method == "POST":
encryt_msg = await request.data
wxcpt = WXBizMsgCrypt(self.token, self.aes, self.appid)
ret, xml_msg = wxcpt.DecryptMsg(encryt_msg, msg_signature, timestamp, nonce)
xml_msg = xml_msg.decode('utf-8')
if ret != 0:
raise Exception("消息解密失败")
# 解析 XML
root = ET.fromstring(xml_msg)
from_user = root.find("FromUserName").text
to_user = root.find("ToUserName").text
from pkg.platform.sources import officialaccount as oa
if oa.msg_queue.get(from_user) and oa.msg_queue[from_user][0]["content"]:
queue_top = oa.msg_queue[from_user].pop(0)
queue_content = queue_top["content"]
# 弹出用户消息
if user_msg_queue.get(from_user) and user_msg_queue[from_user]:
user_msg_queue[from_user].pop(0)
response_xml = xml_template.format(
to_user=from_user,
from_user=to_user,
create_time=int(time.time()),
content=queue_content
)
return response_xml
else:
response_xml = xml_template.format(
to_user=from_user,
from_user=to_user,
create_time=int(time.time()),
content="AI正在思考中请发送任意内容获取回答。"
)
if user_msg_queue.get(from_user) and user_msg_queue[from_user][0]["content"]:
return response_xml
else:
message_data = await self.get_message(xml_msg)
if message_data:
event = OAEvent.from_payload(message_data)
if event:
user_msg_queue.setdefault(from_user,[]).append(
{
"content":event.message,
}
)
await self._handle_message(event)
return response_xml
except Exception as e:
traceback.print_exc()
async def get_message(self, xml_msg: str):
root = ET.fromstring(xml_msg)
message_data = {
"ToUserName": root.find("ToUserName").text,
"FromUserName": root.find("FromUserName").text,
"CreateTime": int(root.find("CreateTime").text),
"MsgType": root.find("MsgType").text,
"Content": root.find("Content").text if root.find("Content") is not None else None,
"MsgId": int(root.find("MsgId").text) if root.find("MsgId") is not None else None,
}
return message_data
async def run_task(self, host: str, port: int, *args, **kwargs):
"""
启动 Quart 应用。
"""
await self.app.run_task(host=host, port=port, *args, **kwargs)
def on_message(self, msg_type: str):
"""
注册消息类型处理器。
"""
def decorator(func: Callable[[OAEvent], None]):
if msg_type not in self._message_handlers:
self._message_handlers[msg_type] = []
self._message_handlers[msg_type].append(func)
return func
return decorator
async def _handle_message(self, event: OAEvent):
"""
处理消息事件。
"""
msg_type = event.type
if msg_type in self._message_handlers:
for handler in self._message_handlers[msg_type]:
await handler(event)

View File

@@ -66,7 +66,7 @@ class WecomClient():
else: else:
raise Exception("未获取用户") raise Exception("未获取用户")
async def send_to_all(self,content:str): async def send_to_all(self,content:str,agent_id:int):
if not self.check_access_token_for_contacts(): if not self.check_access_token_for_contacts():
self.access_token_for_contacts = await self.get_access_token(self.secret_for_contacts) self.access_token_for_contacts = await self.get_access_token(self.secret_for_contacts)
@@ -77,7 +77,7 @@ class WecomClient():
params = { params = {
"touser" : user_ids_string, "touser" : user_ids_string,
"msgtype" : "text", "msgtype" : "text",
"agentid" : 1000002, "agentid" : agent_id,
"text" : { "text" : {
"content" : content, "content" : content,
}, },

View File

@@ -72,6 +72,9 @@ class Query(pydantic.BaseModel):
user_message: typing.Optional[llm_entities.Message] = None user_message: typing.Optional[llm_entities.Message] = None
"""此次请求的用户消息对象,由前置处理器阶段设置""" """此次请求的用户消息对象,由前置处理器阶段设置"""
variables: typing.Optional[dict[str, typing.Any]] = None
"""变量由前置处理器阶段设置。在prompt中嵌入或由 Runner 传递到 LLMOps 平台。"""
use_model: typing.Optional[entities.LLMModelInfo] = None use_model: typing.Optional[entities.LLMModelInfo] = None
"""使用的模型,由前置处理器阶段设置""" """使用的模型,由前置处理器阶段设置"""
@@ -86,10 +89,31 @@ class Query(pydantic.BaseModel):
# ======= 内部保留 ======= # ======= 内部保留 =======
current_stage: "pkg.pipeline.stagemgr.StageInstContainer" = None current_stage: "pkg.pipeline.stagemgr.StageInstContainer" = None
"""当前所处阶段"""
class Config: class Config:
arbitrary_types_allowed = True arbitrary_types_allowed = True
# ========== 插件可调用的 API请求 API ==========
def set_variable(self, key: str, value: typing.Any):
"""设置变量"""
if self.variables is None:
self.variables = {}
self.variables[key] = value
def get_variable(self, key: str) -> typing.Any:
"""获取变量"""
if self.variables is None:
return None
return self.variables.get(key)
def get_variables(self) -> dict[str, typing.Any]:
"""获取所有变量"""
if self.variables is None:
return {}
return self.variables
class Conversation(pydantic.BaseModel): class Conversation(pydantic.BaseModel):
"""对话,包含于 Session 中,一个 Session 可以有多个历史 Conversation但只有一个当前使用的 Conversation""" """对话,包含于 Session 中,一个 Session 可以有多个历史 Conversation但只有一个当前使用的 Conversation"""

View File

@@ -0,0 +1,26 @@
from __future__ import annotations
from .. import migration
@migration.migration_class("wxoa-mode", 35)
class WxoaModeMigration(migration.Migration):
"""迁移"""
async def need_migrate(self) -> bool:
"""判断当前环境是否需要运行此迁移"""
for adapter in self.ap.platform_cfg.data['platform-adapters']:
if adapter['adapter'] == 'officialaccount':
if 'Mode' not in adapter:
return True
return False
async def run(self):
"""执行迁移"""
for adapter in self.ap.platform_cfg.data['platform-adapters']:
if adapter['adapter'] == 'officialaccount':
if 'Mode' not in adapter:
adapter['Mode'] = 'drop'
await self.ap.platform_cfg.dump_config()

View File

@@ -11,7 +11,7 @@ from ..migrations import m015_gitee_ai_config, m016_dify_service_api, m017_dify_
from ..migrations import m020_wecom_config, m021_lark_config, m022_lmstudio_config, m023_siliconflow_config, m024_discord_config, m025_gewechat_config from ..migrations import m020_wecom_config, m021_lark_config, m022_lmstudio_config, m023_siliconflow_config, m024_discord_config, m025_gewechat_config
from ..migrations import m026_qqofficial_config, m027_wx_official_account_config, m028_aliyun_requester_config from ..migrations import m026_qqofficial_config, m027_wx_official_account_config, m028_aliyun_requester_config
from ..migrations import m029_dashscope_app_api_config, m030_lark_config_cmpl, m031_dingtalk_config, m032_volcark_config from ..migrations import m029_dashscope_app_api_config, m030_lark_config_cmpl, m031_dingtalk_config, m032_volcark_config
from ..migrations import m033_dify_thinking_config, m034_gewechat_file_url_config from ..migrations import m033_dify_thinking_config, m034_gewechat_file_url_config, m035_wxoa_mode
@stage.stage_class("MigrationStage") @stage.stage_class("MigrationStage")
class MigrationStage(stage.BootingStage): class MigrationStage(stage.BootingStage):

View File

@@ -1,5 +1,6 @@
from __future__ import annotations from __future__ import annotations
import datetime
from .. import stage, entities, stagemgr from .. import stage, entities, stagemgr
from ...core import entities as core_entities from ...core import entities as core_entities
@@ -34,7 +35,7 @@ class PreProcessor(stage.PipelineStage):
conversation = await self.ap.sess_mgr.get_conversation(session) conversation = await self.ap.sess_mgr.get_conversation(session)
# 从会话取出消息和情景预设到query # 设置query
query.session = session query.session = session
query.prompt = conversation.prompt.copy() query.prompt = conversation.prompt.copy()
query.messages = conversation.messages.copy() query.messages = conversation.messages.copy()
@@ -43,6 +44,11 @@ class PreProcessor(stage.PipelineStage):
query.use_funcs = conversation.use_funcs if query.use_model.tool_call_supported else None query.use_funcs = conversation.use_funcs if query.use_model.tool_call_supported else None
query.variables = {
"session_id": f"{query.session.launcher_type.value}_{query.session.launcher_id}",
"conversation_id": conversation.uuid,
"msg_create_time": int(query.message_event.time) if query.message_event.time else int(datetime.datetime.now().timestamp()),
}
# 检查vision是否启用没启用就删除所有图片 # 检查vision是否启用没启用就删除所有图片
if not self.ap.provider_cfg.data['enable-vision'] or (self.ap.provider_cfg.data['runner'] == 'local-agent' and not query.use_model.vision_supported): if not self.ap.provider_cfg.data['enable-vision'] or (self.ap.provider_cfg.data['runner'] == 'local-agent' and not query.use_model.vision_supported):

View File

@@ -77,7 +77,7 @@ class DingTalkEventConverter(adapter.EventConverter):
remark="" remark=""
), ),
message_chain = message_chain, message_chain = message_chain,
time = datetime.datetime.now(), time = event.incoming_message.create_at,
source_platform_object=event, source_platform_object=event,
) )
elif event.conversation == 'GroupMessage': elif event.conversation == 'GroupMessage':
@@ -95,7 +95,7 @@ class DingTalkEventConverter(adapter.EventConverter):
last_speak_timestamp=0, last_speak_timestamp=0,
mute_time_remaining=0 mute_time_remaining=0
) )
time = datetime.datetime.now(), time = event.incoming_message.create_at
return platform_events.GroupMessage( return platform_events.GroupMessage(
sender =sender, sender =sender,
message_chain = message_chain, message_chain = message_chain,
@@ -152,7 +152,11 @@ class DingTalkAdapter(adapter.MessagePlatformAdapter):
async def send_message( async def send_message(
self, target_type: str, target_id: str, message: platform_message.MessageChain self, target_type: str, target_id: str, message: platform_message.MessageChain
): ):
pass content = await DingTalkMessageConverter.yiri2target(message)
if target_type == 'person':
await self.bot.send_proactive_message_to_one(target_id,content)
if target_type == 'group':
await self.bot.send_proactive_message_to_group(target_id,content)
def register_listener( def register_listener(
self, self,

View File

@@ -62,20 +62,29 @@ class GewechatMessageConverter(adapter.MessageConverter):
bot_account_id: str bot_account_id: str
) -> platform_message.MessageChain: ) -> platform_message.MessageChain:
if message["Data"]["MsgType"] == 1: if message["Data"]["MsgType"] == 1:
# 检查消息开头,如果有 wxid_sbitaz0mt65n22:\n 则删掉 # 检查消息开头,如果有 wxid_sbitaz0mt65n22:\n 则删掉
regex = re.compile(r"^wxid_.*:") regex = re.compile(r"^wxid_.*:")
# print(message)
line_split = message["Data"]["Content"]["string"].split("\n") line_split = message["Data"]["Content"]["string"].split("\n")
if len(line_split) > 0 and regex.match(line_split[0]): if len(line_split) > 0 and regex.match(line_split[0]):
message["Data"]["Content"]["string"] = "\n".join(line_split[1:]) message["Data"]["Content"]["string"] = "\n".join(line_split[1:])
at_string = f'@{bot_account_id}' # 正则表达式模式,匹配'@'后跟任意数量的非空白字符
pattern = r'@\S+'
at_string = f"@{bot_account_id}"
content_list = [] content_list = []
if at_string in message["Data"]["Content"]["string"]: if at_string in message["Data"]["Content"]["string"]:
content_list.append(platform_message.At(target=bot_account_id)) content_list.append(platform_message.At(target=bot_account_id))
content_list.append(platform_message.Plain(message["Data"]["Content"]["string"].replace(at_string, "", 1))) content_list.append(platform_message.Plain(message["Data"]["Content"]["string"].replace(at_string, '', 1)))
# 更优雅的替换改名后@机器人仅仅限于单独AT的情况
elif '在群聊中@了你' in message["Data"]["PushContent"]:
content_list.append(platform_message.At(target=bot_account_id))
content_list.append(platform_message.Plain(re.sub(pattern, '', message["Data"]["Content"]["string"])))
else: else:
content_list = [platform_message.Plain(message["Data"]["Content"]["string"])] content_list = [platform_message.Plain(message["Data"]["Content"]["string"])]
@@ -227,6 +236,13 @@ class GeWeChatAdapter(adapter.MessagePlatformAdapter):
async def gewechat_callback(): async def gewechat_callback():
data = await quart.request.json data = await quart.request.json
# print(json.dumps(data, indent=4, ensure_ascii=False)) # print(json.dumps(data, indent=4, ensure_ascii=False))
if 'data' in data:
data['Data'] = data['data']
if 'type_name' in data:
data['TypeName'] = data['type_name']
# print(json.dumps(data, indent=4, ensure_ascii=False))
if 'testMsg' in data: if 'testMsg' in data:
return 'ok' return 'ok'
@@ -248,17 +264,29 @@ class GeWeChatAdapter(adapter.MessagePlatformAdapter):
target_id: str, target_id: str,
message: platform_message.MessageChain message: platform_message.MessageChain
): ):
geweap_msg = await GewechatMessageConverter.yiri2target(message) geweap_msg = await self.message_converter.yiri2target(message)
# 此处加上群消息at处理 # 此处加上群消息at处理
# ats = [item["target"] for item in geweap_msg if item["type"] == "at"] ats = [item["target"] for item in geweap_msg if item["type"] == "at"]
for msg in geweap_msg: for msg in geweap_msg:
# at主动发送消息
if msg['type'] == 'text': if msg['type'] == 'text':
await self.bot.post_text(app_id=self.config['app_id'], to_wxid=target_id, content=msg['content']) if ats:
member_info = self.bot.get_chatroom_member_detail(
self.config["app_id"],
target_id,
ats[::-1]
)["data"]
for member in member_info:
msg['content'] = f'@{member["nickName"]} {msg["content"]}'
self.bot.post_text(app_id=self.config['app_id'], to_wxid=target_id, content=msg['content'],
ats=",".join(ats))
elif msg['type'] == 'image': elif msg['type'] == 'image':
await self.bot.post_image(app_id=self.config['app_id'], to_wxid=target_id, img_url=msg["image"]) self.bot.post_image(app_id=self.config['app_id'], to_wxid=target_id, img_url=msg["image"])
@@ -289,7 +317,7 @@ class GeWeChatAdapter(adapter.MessagePlatformAdapter):
app_id=self.config["app_id"], app_id=self.config["app_id"],
to_wxid=message_source.source_platform_object["Data"]["FromUserName"]["string"], to_wxid=message_source.source_platform_object["Data"]["FromUserName"]["string"],
content=msg["content"], content=msg["content"],
ats=','.join(ats) ats=",".join(ats)
) )
async def is_muted(self, group_id: int) -> bool: async def is_muted(self, group_id: int) -> bool:

View File

@@ -2,21 +2,19 @@ from __future__ import annotations
import typing import typing
import asyncio import asyncio
import traceback import traceback
import time
import datetime import datetime
from pkg.core import app from pkg.core import app
from pkg.platform.adapter import MessagePlatformAdapter from pkg.platform.adapter import MessagePlatformAdapter
from pkg.platform.types import events as platform_events, message as platform_message from pkg.platform.types import events as platform_events, message as platform_message
from collections import deque
import aiocqhttp
import aiohttp
from libs.official_account_api.oaevent import OAEvent from libs.official_account_api.oaevent import OAEvent
from pkg.platform.adapter import MessagePlatformAdapter from pkg.platform.adapter import MessagePlatformAdapter
from pkg.platform.types import events as platform_events, message as platform_message from pkg.platform.types import events as platform_events, message as platform_message
from libs.official_account_api.api import OAClient from libs.official_account_api.api import OAClient
from libs.official_account_api.api import OAClientForLongerResponse
from pkg.core import app from pkg.core import app
from .. import adapter from .. import adapter
from ...pipeline.longtext.strategies import forward
from ...core import app from ...core import app
from ..types import message as platform_message from ..types import message as platform_message
from ..types import events as platform_events from ..types import events as platform_events
@@ -26,6 +24,7 @@ from ...command.errors import ParamNotEnoughError
# 生成的ai回答 # 生成的ai回答
generated_content = {} generated_content = {}
msg_queue = {}
class OAMessageConverter(adapter.MessageConverter): class OAMessageConverter(adapter.MessageConverter):
@staticmethod @staticmethod
@@ -88,17 +87,30 @@ class OfficialAccountAdapter(adapter.MessagePlatformAdapter):
"EncodingAESKey", "EncodingAESKey",
"AppSecret", "AppSecret",
"AppID", "AppID",
"Mode",
] ]
missing_keys = [key for key in required_keys if key not in config] missing_keys = [key for key in required_keys if key not in config]
if missing_keys: if missing_keys:
raise ParamNotEnoughError("企业微信缺少相关配置项,请查看文档或联系管理员") raise ParamNotEnoughError("微信公众号缺少相关配置项,请查看文档或联系管理员")
self.bot = OAClient(
token=config['token'], if self.config['Mode'] == "drop":
EncodingAESKey=config['EncodingAESKey'], self.bot = OAClient(
Appsecret=config['AppSecret'], token=config['token'],
AppID=config['AppID'], EncodingAESKey=config['EncodingAESKey'],
) Appsecret=config['AppSecret'],
AppID=config['AppID'],
)
elif self.config['Mode'] == "passive":
self.bot = OAClientForLongerResponse(
token=config['token'],
EncodingAESKey=config['EncodingAESKey'],
Appsecret=config['AppSecret'],
AppID=config['AppID'],
)
else:
raise KeyError("请设置微信公众号通信模式")
async def reply_message(self, message_source: platform_events.FriendMessage, message: platform_message.MessageChain, quote_origin: bool = False): async def reply_message(self, message_source: platform_events.FriendMessage, message: platform_message.MessageChain, quote_origin: bool = False):
global generated_content global generated_content
@@ -109,6 +121,20 @@ class OfficialAccountAdapter(adapter.MessagePlatformAdapter):
generated_content[message_source.message_chain.message_id] = content generated_content[message_source.message_chain.message_id] = content
from_user = message_source.sender.id
if from_user not in msg_queue:
msg_queue[from_user] = []
msg_queue[from_user].append(
{
"msg_id":message_source.message_chain.message_id,
"content":content,
}
)
async def send_message( async def send_message(
self, target_type: str, target_id: str, message: platform_message.MessageChain self, target_type: str, target_id: str, message: platform_message.MessageChain
): ):

View File

@@ -2,15 +2,14 @@ from __future__ import annotations
import typing import typing
import asyncio import asyncio
import traceback import traceback
import time
import datetime import datetime
import aiocqhttp
import aiohttp
from pkg.platform.adapter import MessagePlatformAdapter from pkg.platform.adapter import MessagePlatformAdapter
from pkg.platform.types import events as platform_events, message as platform_message from pkg.platform.types import events as platform_events, message as platform_message
from pkg.core import app from pkg.core import app
from .. import adapter from .. import adapter
from ...pipeline.longtext.strategies import forward
from ...core import app from ...core import app
from ..types import message as platform_message from ..types import message as platform_message
from ..types import events as platform_events from ..types import events as platform_events
@@ -74,7 +73,11 @@ class QQOfficialEventConverter(adapter.EventConverter):
remark = "", remark = "",
) )
return platform_events.FriendMessage( return platform_events.FriendMessage(
sender = friend,message_chain = yiri_chain,time = event.timestamp, sender = friend,message_chain = yiri_chain,time = int(
datetime.datetime.strptime(
event.timestamp, "%Y-%m-%dT%H:%M:%S%z"
).timestamp()
),
source_platform_object=event source_platform_object=event
) )
@@ -96,7 +99,7 @@ class QQOfficialEventConverter(adapter.EventConverter):
member_name= event.t, member_name= event.t,
permission= 'MEMBER', permission= 'MEMBER',
group = platform_entities.Group( group = platform_entities.Group(
id = 0, id = event.group_openid,
name = 'MEMBER', name = 'MEMBER',
permission= platform_entities.Permission.Member permission= platform_entities.Permission.Member
), ),
@@ -105,7 +108,11 @@ class QQOfficialEventConverter(adapter.EventConverter):
last_speak_timestamp=0, last_speak_timestamp=0,
mute_time_remaining=0 mute_time_remaining=0
) )
time = event.timestamp time = int(
datetime.datetime.strptime(
event.timestamp, "%Y-%m-%dT%H:%M:%S%z"
).timestamp()
)
return platform_events.GroupMessage( return platform_events.GroupMessage(
sender = sender, sender = sender,
message_chain=yiri_chain, message_chain=yiri_chain,
@@ -119,7 +126,7 @@ class QQOfficialEventConverter(adapter.EventConverter):
member_name=event.t, member_name=event.t,
permission= 'MEMBER', permission= 'MEMBER',
group = platform_entities.Group( group = platform_entities.Group(
id = 0, id = event.channel_id,
name = 'MEMBER', name = 'MEMBER',
permission=platform_entities.Permission.Member permission=platform_entities.Permission.Member
), ),
@@ -128,7 +135,11 @@ class QQOfficialEventConverter(adapter.EventConverter):
last_speak_timestamp=0, last_speak_timestamp=0,
mute_time_remaining=0 mute_time_remaining=0
) )
time = event.timestamp, time = int(
datetime.datetime.strptime(
event.timestamp, "%Y-%m-%dT%H:%M:%S%z"
).timestamp()
)
return platform_events.GroupMessage( return platform_events.GroupMessage(
sender =sender, sender =sender,
message_chain = yiri_chain, message_chain = yiri_chain,

View File

@@ -2,18 +2,15 @@ from __future__ import annotations
import typing import typing
import asyncio import asyncio
import traceback import traceback
import time
import datetime import datetime
import aiocqhttp
import aiohttp
from libs.wecom_api.api import WecomClient from libs.wecom_api.api import WecomClient
from pkg.platform.adapter import MessagePlatformAdapter from pkg.platform.adapter import MessagePlatformAdapter
from pkg.platform.types import events as platform_events, message as platform_message from pkg.platform.types import events as platform_events, message as platform_message
from libs.wecom_api.wecomevent import WecomEvent from libs.wecom_api.wecomevent import WecomEvent
from pkg.core import app from pkg.core import app
from .. import adapter from .. import adapter
from ...pipeline.longtext.strategies import forward
from ...core import app from ...core import app
from ..types import message as platform_message from ..types import message as platform_message
from ..types import events as platform_events from ..types import events as platform_events
@@ -200,7 +197,19 @@ class WecomAdapter(adapter.MessagePlatformAdapter):
async def send_message( async def send_message(
self, target_type: str, target_id: str, message: platform_message.MessageChain self, target_type: str, target_id: str, message: platform_message.MessageChain
): ):
pass """企业微信目前只有发送给个人的方法,
构造target_id的方式为前半部分为账户id后半部分为agent_id,中间使用“|”符号隔开。
"""
content_list = await WecomMessageConverter.yiri2target(message, self.bot)
parts = target_id.split("|")
user_id = parts[0]
agent_id = int(parts[1])
if target_type == 'person':
for content in content_list:
if content["type"] == "text":
await self.bot.send_private_msg(user_id,agent_id,content["content"])
if content["type"] == "image":
await self.bot.send_image(user_id,agent_id,content["media"])
def register_listener( def register_listener(
self, self,

View File

@@ -57,6 +57,9 @@ class MessageEvent(Event):
message_chain: platform_message.MessageChain message_chain: platform_message.MessageChain
"""消息内容。""" """消息内容。"""
time: float | None = None
"""消息发送时间戳。"""
source_platform_object: typing.Optional[typing.Any] = None source_platform_object: typing.Optional[typing.Any] = None
"""原消息平台对象。 """原消息平台对象。
供消息平台适配器开发者使用,如果回复用户时需要使用原消息事件对象的信息, 供消息平台适配器开发者使用,如果回复用户时需要使用原消息事件对象的信息,

View File

@@ -167,6 +167,10 @@ class DashScopeAPIRunner(runner.RequestRunner):
image_ids = [] # 用户输入的图片ID列表 (暂不支持) image_ids = [] # 用户输入的图片ID列表 (暂不支持)
plain_text, image_ids = await self._preprocess_user_message(query) plain_text, image_ids = await self._preprocess_user_message(query)
biz_params = {}
biz_params.update(self.biz_params)
biz_params.update(query.variables)
#发送对话请求 #发送对话请求
response = dashscope.Application.call( response = dashscope.Application.call(
@@ -176,7 +180,7 @@ class DashScopeAPIRunner(runner.RequestRunner):
stream=True, # 流式输出 stream=True, # 流式输出
incremental_output=True, # 增量输出,使用流式输出需要开启增量输出 incremental_output=True, # 增量输出,使用流式输出需要开启增量输出
session_id=query.session.using_conversation.uuid, # 会话ID用于多轮对话 session_id=query.session.using_conversation.uuid, # 会话ID用于多轮对话
biz_params=self.biz_params # 工作流应用的自定义输入参数传递 biz_params=biz_params, # 工作流应用的自定义输入参数传递
# rag_options={ # 主要用于文件交互,暂不支持 # rag_options={ # 主要用于文件交互,暂不支持
# "session_file_ids": ["FILE_ID1"], # FILE_ID1 替换为实际的临时文件ID,逗号隔开多个 # "session_file_ids": ["FILE_ID1"], # FILE_ID1 替换为实际的临时文件ID,逗号隔开多个
# } # }

View File

@@ -5,6 +5,7 @@ import json
import uuid import uuid
import re import re
import base64 import base64
import datetime
import aiohttp import aiohttp
@@ -69,6 +70,7 @@ class DifyServiceAPIRunner(runner.RequestRunner):
""" """
plain_text = "" plain_text = ""
image_ids = [] image_ids = []
if isinstance(query.user_message.content, list): if isinstance(query.user_message.content, list):
for ce in query.user_message.content: for ce in query.user_message.content:
if ce.type == "text": if ce.type == "text":
@@ -109,8 +111,12 @@ class DifyServiceAPIRunner(runner.RequestRunner):
basic_mode_pending_chunk = '' basic_mode_pending_chunk = ''
inputs = {}
inputs.update(query.variables)
async for chunk in self.dify_client.chat_messages( async for chunk in self.dify_client.chat_messages(
inputs={}, inputs=inputs,
query=plain_text, query=plain_text,
user=f"{query.session.launcher_type.value}_{query.session.launcher_id}", user=f"{query.session.launcher_type.value}_{query.session.launcher_id}",
conversation_id=cov_id, conversation_id=cov_id,
@@ -160,8 +166,12 @@ class DifyServiceAPIRunner(runner.RequestRunner):
ignored_events = ["agent_message"] ignored_events = ["agent_message"]
inputs = {}
inputs.update(query.variables)
async for chunk in self.dify_client.chat_messages( async for chunk in self.dify_client.chat_messages(
inputs={}, inputs=inputs,
query=plain_text, query=plain_text,
user=f"{query.session.launcher_type.value}_{query.session.launcher_id}", user=f"{query.session.launcher_type.value}_{query.session.launcher_id}",
response_mode="streaming", response_mode="streaming",
@@ -225,8 +235,8 @@ class DifyServiceAPIRunner(runner.RequestRunner):
if not query.session.using_conversation.uuid: if not query.session.using_conversation.uuid:
query.session.using_conversation.uuid = str(uuid.uuid4()) query.session.using_conversation.uuid = str(uuid.uuid4())
cov_id = query.session.using_conversation.uuid query.variables["conversation_id"] = query.session.using_conversation.uuid
plain_text, image_ids = await self._preprocess_user_message(query) plain_text, image_ids = await self._preprocess_user_message(query)
@@ -241,12 +251,17 @@ class DifyServiceAPIRunner(runner.RequestRunner):
ignored_events = ["text_chunk", "workflow_started"] ignored_events = ["text_chunk", "workflow_started"]
inputs = { # these variables are legacy variables, we need to keep them for compatibility
"langbot_user_message_text": plain_text,
"langbot_session_id": query.variables["session_id"],
"langbot_conversation_id": query.variables["conversation_id"],
"langbot_msg_create_time": query.variables["msg_create_time"],
}
inputs.update(query.variables)
async for chunk in self.dify_client.workflow_run( async for chunk in self.dify_client.workflow_run(
inputs={ inputs=inputs,
"langbot_user_message_text": plain_text,
"langbot_session_id": f"{query.session.launcher_type.value}_{query.session.launcher_id}",
"langbot_conversation_id": cov_id,
},
user=f"{query.session.launcher_type.value}_{query.session.launcher_id}", user=f"{query.session.launcher_type.value}_{query.session.launcher_id}",
files=files, files=files,
timeout=self.ap.provider_cfg.data["dify-service-api"]["workflow"]["timeout"], timeout=self.ap.provider_cfg.data["dify-service-api"]["workflow"]["timeout"],

View File

@@ -1,4 +1,4 @@
semantic_version = "v3.4.10" semantic_version = "v3.4.10.4"
debug_mode = False debug_mode = False

View File

@@ -166,61 +166,72 @@
{ {
"name": "glm-4-plus", "name": "glm-4-plus",
"requester": "zhipuai-chat-completions", "requester": "zhipuai-chat-completions",
"token_mgr": "zhipuai" "token_mgr": "zhipuai",
"tool_call_supported": true
}, },
{ {
"name": "glm-4-0520", "name": "glm-4-0520",
"requester": "zhipuai-chat-completions", "requester": "zhipuai-chat-completions",
"token_mgr": "zhipuai" "token_mgr": "zhipuai",
"tool_call_supported": true
}, },
{ {
"name": "glm-4-air", "name": "glm-4-air",
"requester": "zhipuai-chat-completions", "requester": "zhipuai-chat-completions",
"token_mgr": "zhipuai" "token_mgr": "zhipuai",
"tool_call_supported": true
}, },
{ {
"name": "glm-4-airx", "name": "glm-4-airx",
"requester": "zhipuai-chat-completions", "requester": "zhipuai-chat-completions",
"token_mgr": "zhipuai" "token_mgr": "zhipuai",
"tool_call_supported": true
}, },
{ {
"name": "glm-4-long", "name": "glm-4-long",
"requester": "zhipuai-chat-completions", "requester": "zhipuai-chat-completions",
"token_mgr": "zhipuai" "token_mgr": "zhipuai",
"tool_call_supported": true
}, },
{ {
"name": "glm-4-flashx", "name": "glm-4-flashx",
"requester": "zhipuai-chat-completions", "requester": "zhipuai-chat-completions",
"token_mgr": "zhipuai" "token_mgr": "zhipuai",
"tool_call_supported": true
}, },
{ {
"name": "glm-4-flash", "name": "glm-4-flash",
"requester": "zhipuai-chat-completions", "requester": "zhipuai-chat-completions",
"token_mgr": "zhipuai" "token_mgr": "zhipuai",
"tool_call_supported": true
}, },
{ {
"name": "glm-4v-plus", "name": "glm-4v-plus",
"requester": "zhipuai-chat-completions", "requester": "zhipuai-chat-completions",
"token_mgr": "zhipuai", "token_mgr": "zhipuai",
"vision_supported": true "vision_supported": true,
"tool_call_supported": true
}, },
{ {
"name": "glm-4v", "name": "glm-4v",
"requester": "zhipuai-chat-completions", "requester": "zhipuai-chat-completions",
"token_mgr": "zhipuai", "token_mgr": "zhipuai",
"vision_supported": true "vision_supported": true,
"tool_call_supported": true
}, },
{ {
"name": "glm-4v-flash", "name": "glm-4v-flash",
"requester": "zhipuai-chat-completions", "requester": "zhipuai-chat-completions",
"token_mgr": "zhipuai", "token_mgr": "zhipuai",
"vision_supported": true "vision_supported": true,
"tool_call_supported": true
}, },
{ {
"name": "glm-zero-preview", "name": "glm-zero-preview",
"requester": "zhipuai-chat-completions", "requester": "zhipuai-chat-completions",
"token_mgr": "zhipuai", "token_mgr": "zhipuai",
"vision_supported": true "vision_supported": true,
"tool_call_supported": true
} }
] ]
} }

View File

@@ -16,7 +16,7 @@
}, },
"income-msg-check": true, "income-msg-check": true,
"ignore-rules": { "ignore-rules": {
"prefix": ["/"], "prefix": [],
"regexp": [] "regexp": []
}, },
"check-sensitive-words": true, "check-sensitive-words": true,

View File

@@ -77,6 +77,7 @@
"EncodingAESKey":"", "EncodingAESKey":"",
"AppID":"", "AppID":"",
"AppSecret":"", "AppSecret":"",
"Mode":"drop",
"host": "0.0.0.0", "host": "0.0.0.0",
"port": 2287 "port": 2287
}, },

View File

@@ -23,7 +23,7 @@
"items": { "items": {
"type": "string", "type": "string",
"format": "regex", "format": "regex",
"pattern": "^(person|group)_(\\d)*$" "pattern": "^(person|group)_.*$"
}, },
"default": [] "default": []
}, },
@@ -34,7 +34,7 @@
"items": { "items": {
"type": "string", "type": "string",
"format": "regex", "format": "regex",
"pattern": "^(person|group)_(\\d)*$" "pattern": "^(person|group)_.*$"
}, },
"default": [] "default": []
} }
@@ -99,7 +99,7 @@
} }
}, },
"patternProperties": { "patternProperties": {
"^\\d+$": { "^.*$": {
"type": "object", "type": "object",
"properties": { "properties": {
"at": { "at": {

View File

@@ -385,6 +385,12 @@
"default": "", "default": "",
"description": "微信公众号的AppSecret" "description": "微信公众号的AppSecret"
}, },
"Mode": {
"type": "string",
"default": "drop",
"description": "对于超过15s的响应的处理模式",
"enum": ["drop", "passive"]
},
"host": { "host": {
"type": "string", "type": "string",
"default": "0.0.0.0", "default": "0.0.0.0",

View File

@@ -9,7 +9,7 @@
"items": { "items": {
"type": "string", "type": "string",
"format": "regex", "format": "regex",
"pattern": "^(person|group)_(\\d+)$" "pattern": "^(person|group)_.*$"
}, },
"default": [] "default": []
}, },
@@ -52,7 +52,7 @@
} }
}, },
"patternProperties": { "patternProperties": {
"^(person|group)_(\\d+)$": { "^(person|group)_.*$": {
"type": "integer" "type": "integer"
} }
} }