mirror of
https://github.com/langbot-app/LangBot.git
synced 2026-06-02 20:14:36 +00:00
Compare commits
22 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bd0438df76 | ||
|
|
9ca1fc59ef | ||
|
|
84a80a5ec8 | ||
|
|
4b2e248646 | ||
|
|
b90e45590a | ||
|
|
ff93d563a8 | ||
|
|
53228498ed | ||
|
|
8ece82e43a | ||
|
|
8b4684675e | ||
|
|
8cca12fff2 | ||
|
|
a74111612e | ||
|
|
c7799a65c4 | ||
|
|
aabb01c50f | ||
|
|
95e2ada965 | ||
|
|
3fe7d53c76 | ||
|
|
e8634bb1ab | ||
|
|
dbe46b5770 | ||
|
|
6d9fba30b1 | ||
|
|
6a866bf871 | ||
|
|
3c961e4652 | ||
|
|
7abd999420 | ||
|
|
fca8fbb135 |
@@ -23,6 +23,7 @@ class EchoTextHandler(dingtalk_stream.ChatbotHandler):
|
||||
"""异步等待消息的到来"""
|
||||
while self.incoming_message is None:
|
||||
await asyncio.sleep(0.1) # 异步等待,避免阻塞
|
||||
|
||||
return self.incoming_message
|
||||
|
||||
async def get_dingtalk_client(client_id, client_secret):
|
||||
|
||||
@@ -157,6 +157,7 @@ class DingTalkClient:
|
||||
|
||||
async def get_message(self,incoming_message:dingtalk_stream.chatbot.ChatbotMessage):
|
||||
try:
|
||||
|
||||
# print(json.dumps(incoming_message.to_dict(), indent=4, ensure_ascii=False))
|
||||
message_data = {
|
||||
"IncomingMessage":incoming_message,
|
||||
@@ -197,6 +198,53 @@ class DingTalkClient:
|
||||
traceback.print_exc()
|
||||
|
||||
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):
|
||||
"""启动 WebSocket 连接,监听消息"""
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
# 微信公众号的加解密算法与企业微信一样,所以直接使用企业微信的加解密算法文件
|
||||
from collections import deque
|
||||
import time
|
||||
import traceback
|
||||
from ..wecom_api.WXBizMsgCrypt3 import WXBizMsgCrypt
|
||||
@@ -12,6 +13,7 @@ import httpx
|
||||
import asyncio
|
||||
import time
|
||||
import xml.etree.ElementTree as ET
|
||||
from pkg.platform.sources import officialaccount as oa
|
||||
|
||||
|
||||
|
||||
@@ -25,6 +27,8 @@ xml_template = """
|
||||
</xml>
|
||||
"""
|
||||
|
||||
user_msg_queue = {}
|
||||
|
||||
class OAClient():
|
||||
|
||||
def __init__(self,token:str,EncodingAESKey:str,AppID:str,Appsecret:str):
|
||||
@@ -169,6 +173,152 @@ class OAClient():
|
||||
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)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -66,7 +66,7 @@ class WecomClient():
|
||||
else:
|
||||
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():
|
||||
self.access_token_for_contacts = await self.get_access_token(self.secret_for_contacts)
|
||||
|
||||
@@ -77,7 +77,7 @@ class WecomClient():
|
||||
params = {
|
||||
"touser" : user_ids_string,
|
||||
"msgtype" : "text",
|
||||
"agentid" : 1000002,
|
||||
"agentid" : agent_id,
|
||||
"text" : {
|
||||
"content" : content,
|
||||
},
|
||||
|
||||
@@ -72,6 +72,9 @@ class Query(pydantic.BaseModel):
|
||||
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
|
||||
"""使用的模型,由前置处理器阶段设置"""
|
||||
|
||||
@@ -86,10 +89,31 @@ class Query(pydantic.BaseModel):
|
||||
|
||||
# ======= 内部保留 =======
|
||||
current_stage: "pkg.pipeline.stagemgr.StageInstContainer" = None
|
||||
"""当前所处阶段"""
|
||||
|
||||
class Config:
|
||||
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):
|
||||
"""对话,包含于 Session 中,一个 Session 可以有多个历史 Conversation,但只有一个当前使用的 Conversation"""
|
||||
|
||||
26
pkg/core/migrations/m035_wxoa_mode.py
Normal file
26
pkg/core/migrations/m035_wxoa_mode.py
Normal 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()
|
||||
@@ -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 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 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")
|
||||
class MigrationStage(stage.BootingStage):
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import datetime
|
||||
|
||||
from .. import stage, entities, stagemgr
|
||||
from ...core import entities as core_entities
|
||||
@@ -34,7 +35,7 @@ class PreProcessor(stage.PipelineStage):
|
||||
|
||||
conversation = await self.ap.sess_mgr.get_conversation(session)
|
||||
|
||||
# 从会话取出消息和情景预设到query
|
||||
# 设置query
|
||||
query.session = session
|
||||
query.prompt = conversation.prompt.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.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是否启用,没启用就删除所有图片
|
||||
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):
|
||||
|
||||
@@ -152,7 +152,11 @@ class DingTalkAdapter(adapter.MessagePlatformAdapter):
|
||||
async def send_message(
|
||||
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(
|
||||
self,
|
||||
|
||||
@@ -62,20 +62,29 @@ class GewechatMessageConverter(adapter.MessageConverter):
|
||||
bot_account_id: str
|
||||
) -> platform_message.MessageChain:
|
||||
|
||||
|
||||
|
||||
if message["Data"]["MsgType"] == 1:
|
||||
# 检查消息开头,如果有 wxid_sbitaz0mt65n22:\n 则删掉
|
||||
regex = re.compile(r"^wxid_.*:")
|
||||
# print(message)
|
||||
|
||||
line_split = message["Data"]["Content"]["string"].split("\n")
|
||||
|
||||
if len(line_split) > 0 and regex.match(line_split[0]):
|
||||
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 = []
|
||||
if at_string in message["Data"]["Content"]["string"]:
|
||||
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:
|
||||
content_list = [platform_message.Plain(message["Data"]["Content"]["string"])]
|
||||
|
||||
@@ -257,11 +266,23 @@ class GeWeChatAdapter(adapter.MessagePlatformAdapter):
|
||||
):
|
||||
geweap_msg = await self.message_converter.yiri2target(message)
|
||||
# 此处加上群消息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:
|
||||
# at主动发送消息
|
||||
if msg['type'] == 'text':
|
||||
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':
|
||||
|
||||
@@ -296,7 +317,7 @@ class GeWeChatAdapter(adapter.MessagePlatformAdapter):
|
||||
app_id=self.config["app_id"],
|
||||
to_wxid=message_source.source_platform_object["Data"]["FromUserName"]["string"],
|
||||
content=msg["content"],
|
||||
ats=','.join(ats)
|
||||
ats=",".join(ats)
|
||||
)
|
||||
|
||||
async def is_muted(self, group_id: int) -> bool:
|
||||
|
||||
@@ -2,21 +2,19 @@ from __future__ import annotations
|
||||
import typing
|
||||
import asyncio
|
||||
import traceback
|
||||
import time
|
||||
|
||||
import datetime
|
||||
from pkg.core import app
|
||||
from pkg.platform.adapter import MessagePlatformAdapter
|
||||
from pkg.platform.types import events as platform_events, message as platform_message
|
||||
|
||||
import aiocqhttp
|
||||
import aiohttp
|
||||
from collections import deque
|
||||
from libs.official_account_api.oaevent import OAEvent
|
||||
from pkg.platform.adapter import MessagePlatformAdapter
|
||||
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 OAClientForLongerResponse
|
||||
from pkg.core import app
|
||||
from .. import adapter
|
||||
from ...pipeline.longtext.strategies import forward
|
||||
from ...core import app
|
||||
from ..types import message as platform_message
|
||||
from ..types import events as platform_events
|
||||
@@ -26,6 +24,7 @@ from ...command.errors import ParamNotEnoughError
|
||||
|
||||
# 生成的ai回答
|
||||
generated_content = {}
|
||||
msg_queue = {}
|
||||
|
||||
class OAMessageConverter(adapter.MessageConverter):
|
||||
@staticmethod
|
||||
@@ -88,17 +87,30 @@ class OfficialAccountAdapter(adapter.MessagePlatformAdapter):
|
||||
"EncodingAESKey",
|
||||
"AppSecret",
|
||||
"AppID",
|
||||
"Mode",
|
||||
]
|
||||
missing_keys = [key for key in required_keys if key not in config]
|
||||
if missing_keys:
|
||||
raise ParamNotEnoughError("企业微信缺少相关配置项,请查看文档或联系管理员")
|
||||
raise ParamNotEnoughError("微信公众号缺少相关配置项,请查看文档或联系管理员")
|
||||
|
||||
self.bot = OAClient(
|
||||
token=config['token'],
|
||||
EncodingAESKey=config['EncodingAESKey'],
|
||||
Appsecret=config['AppSecret'],
|
||||
AppID=config['AppID'],
|
||||
)
|
||||
|
||||
if self.config['Mode'] == "drop":
|
||||
self.bot = OAClient(
|
||||
token=config['token'],
|
||||
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):
|
||||
global generated_content
|
||||
@@ -109,6 +121,20 @@ class OfficialAccountAdapter(adapter.MessagePlatformAdapter):
|
||||
|
||||
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(
|
||||
self, target_type: str, target_id: str, message: platform_message.MessageChain
|
||||
):
|
||||
|
||||
@@ -2,15 +2,14 @@ from __future__ import annotations
|
||||
import typing
|
||||
import asyncio
|
||||
import traceback
|
||||
import time
|
||||
|
||||
import datetime
|
||||
import aiocqhttp
|
||||
import aiohttp
|
||||
|
||||
from pkg.platform.adapter import MessagePlatformAdapter
|
||||
from pkg.platform.types import events as platform_events, message as platform_message
|
||||
from pkg.core import app
|
||||
from .. import adapter
|
||||
from ...pipeline.longtext.strategies import forward
|
||||
|
||||
from ...core import app
|
||||
from ..types import message as platform_message
|
||||
from ..types import events as platform_events
|
||||
|
||||
@@ -197,7 +197,19 @@ class WecomAdapter(adapter.MessagePlatformAdapter):
|
||||
async def send_message(
|
||||
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(
|
||||
self,
|
||||
|
||||
@@ -167,6 +167,10 @@ class DashScopeAPIRunner(runner.RequestRunner):
|
||||
image_ids = [] # 用户输入的图片ID列表 (暂不支持)
|
||||
|
||||
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(
|
||||
@@ -176,7 +180,7 @@ class DashScopeAPIRunner(runner.RequestRunner):
|
||||
stream=True, # 流式输出
|
||||
incremental_output=True, # 增量输出,使用流式输出需要开启增量输出
|
||||
session_id=query.session.using_conversation.uuid, # 会话ID用于,多轮对话
|
||||
biz_params=self.biz_params # 工作流应用的自定义输入参数传递
|
||||
biz_params=biz_params, # 工作流应用的自定义输入参数传递
|
||||
# rag_options={ # 主要用于文件交互,暂不支持
|
||||
# "session_file_ids": ["FILE_ID1"], # FILE_ID1 替换为实际的临时文件ID,逗号隔开多个
|
||||
# }
|
||||
|
||||
@@ -111,8 +111,12 @@ class DifyServiceAPIRunner(runner.RequestRunner):
|
||||
|
||||
basic_mode_pending_chunk = ''
|
||||
|
||||
inputs = {}
|
||||
|
||||
inputs.update(query.variables)
|
||||
|
||||
async for chunk in self.dify_client.chat_messages(
|
||||
inputs={},
|
||||
inputs=inputs,
|
||||
query=plain_text,
|
||||
user=f"{query.session.launcher_type.value}_{query.session.launcher_id}",
|
||||
conversation_id=cov_id,
|
||||
@@ -162,8 +166,12 @@ class DifyServiceAPIRunner(runner.RequestRunner):
|
||||
|
||||
ignored_events = ["agent_message"]
|
||||
|
||||
inputs = {}
|
||||
|
||||
inputs.update(query.variables)
|
||||
|
||||
async for chunk in self.dify_client.chat_messages(
|
||||
inputs={},
|
||||
inputs=inputs,
|
||||
query=plain_text,
|
||||
user=f"{query.session.launcher_type.value}_{query.session.launcher_id}",
|
||||
response_mode="streaming",
|
||||
@@ -227,14 +235,11 @@ class DifyServiceAPIRunner(runner.RequestRunner):
|
||||
|
||||
if not query.session.using_conversation.uuid:
|
||||
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)
|
||||
|
||||
# 尝试获取 CreateTime
|
||||
create_time = int(query.message_event.time) if query.message_event.time else int(datetime.datetime.now().timestamp())
|
||||
|
||||
files = [
|
||||
{
|
||||
"type": "image",
|
||||
@@ -246,13 +251,17 @@ class DifyServiceAPIRunner(runner.RequestRunner):
|
||||
|
||||
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(
|
||||
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,
|
||||
"langbot_msg_create_time": create_time,
|
||||
},
|
||||
inputs=inputs,
|
||||
user=f"{query.session.launcher_type.value}_{query.session.launcher_id}",
|
||||
files=files,
|
||||
timeout=self.ap.provider_cfg.data["dify-service-api"]["workflow"]["timeout"],
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
semantic_version = "v3.4.10.3"
|
||||
semantic_version = "v3.4.10.4"
|
||||
|
||||
debug_mode = False
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
},
|
||||
"income-msg-check": true,
|
||||
"ignore-rules": {
|
||||
"prefix": ["/"],
|
||||
"prefix": [],
|
||||
"regexp": []
|
||||
},
|
||||
"check-sensitive-words": true,
|
||||
|
||||
@@ -77,6 +77,7 @@
|
||||
"EncodingAESKey":"",
|
||||
"AppID":"",
|
||||
"AppSecret":"",
|
||||
"Mode":"drop",
|
||||
"host": "0.0.0.0",
|
||||
"port": 2287
|
||||
},
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
"items": {
|
||||
"type": "string",
|
||||
"format": "regex",
|
||||
"pattern": "^(person|group)_(\\d)*$"
|
||||
"pattern": "^(person|group)_.*$"
|
||||
},
|
||||
"default": []
|
||||
},
|
||||
@@ -34,7 +34,7 @@
|
||||
"items": {
|
||||
"type": "string",
|
||||
"format": "regex",
|
||||
"pattern": "^(person|group)_(\\d)*$"
|
||||
"pattern": "^(person|group)_.*$"
|
||||
},
|
||||
"default": []
|
||||
}
|
||||
@@ -99,7 +99,7 @@
|
||||
}
|
||||
},
|
||||
"patternProperties": {
|
||||
"^\\d+$": {
|
||||
"^.*$": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"at": {
|
||||
|
||||
@@ -385,6 +385,12 @@
|
||||
"default": "",
|
||||
"description": "微信公众号的AppSecret"
|
||||
},
|
||||
"Mode": {
|
||||
"type": "string",
|
||||
"default": "drop",
|
||||
"description": "对于超过15s的响应的处理模式",
|
||||
"enum": ["drop", "passive"]
|
||||
},
|
||||
"host": {
|
||||
"type": "string",
|
||||
"default": "0.0.0.0",
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
"items": {
|
||||
"type": "string",
|
||||
"format": "regex",
|
||||
"pattern": "^(person|group)_(\\d+)$"
|
||||
"pattern": "^(person|group)_.*$"
|
||||
},
|
||||
"default": []
|
||||
},
|
||||
@@ -52,7 +52,7 @@
|
||||
}
|
||||
},
|
||||
"patternProperties": {
|
||||
"^(person|group)_(\\d+)$": {
|
||||
"^(person|group)_.*$": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user