mirror of
https://github.com/FuQuan233/nonebot-plugin-llmchat.git
synced 2026-08-13 10:09:27 +00:00
♻️ 大幅重构,拆分模块,增加一些MCP相关限制
This commit is contained in:
parent
41e6aeacb9
commit
0d6771eca6
17 changed files with 1142 additions and 912 deletions
93
nonebot_plugin_llmchat/message_utils.py
Normal file
93
nonebot_plugin_llmchat/message_utils.py
Normal file
|
|
@ -0,0 +1,93 @@
|
|||
import asyncio
|
||||
import base64
|
||||
from collections.abc import Awaitable, Callable
|
||||
from datetime import datetime
|
||||
import json
|
||||
import re
|
||||
import ssl
|
||||
|
||||
import httpx
|
||||
from nonebot import logger
|
||||
from nonebot.adapters.onebot.v11 import GroupMessageEvent, Message, PrivateMessageEvent
|
||||
|
||||
ChatEvent = GroupMessageEvent | PrivateMessageEvent
|
||||
MessageSender = Callable[[Message], Awaitable[object]]
|
||||
|
||||
|
||||
def pop_reasoning_content(content: str | None) -> tuple[str | None, str | None]:
|
||||
if content is None:
|
||||
return None, None
|
||||
if matched := re.match(r"<think>(.*?)</think>", content, flags=re.DOTALL):
|
||||
return content.replace(matched.group(0), "").strip(), matched.group(1).strip()
|
||||
return content, None
|
||||
|
||||
|
||||
def format_message(event: ChatEvent, bot_name: str) -> str:
|
||||
text = ""
|
||||
if isinstance(event, GroupMessageEvent) and event.reply is not None:
|
||||
text += f"[回复 {event.reply.sender.nickname} 的消息 {event.reply.message.extract_plain_text()}]\n"
|
||||
if isinstance(event, GroupMessageEvent) and event.is_tome():
|
||||
text += f"@{bot_name} "
|
||||
for segment in event.get_message():
|
||||
if segment.type == "at":
|
||||
text += segment.data.get("name", "")
|
||||
elif segment.type == "image":
|
||||
text += "[图片]"
|
||||
elif segment.type == "voice":
|
||||
text += "[语音]"
|
||||
elif segment.type == "text":
|
||||
text += segment.data.get("text", "")
|
||||
nickname = event.sender.card or event.sender.nickname if isinstance(event, GroupMessageEvent) else event.sender.nickname
|
||||
return json.dumps(
|
||||
{
|
||||
"SenderNickname": str(nickname),
|
||||
"SenderUserId": str(event.user_id),
|
||||
"Message": text,
|
||||
"MessageID": event.message_id,
|
||||
"SendTime": datetime.fromtimestamp(event.time).isoformat(),
|
||||
},
|
||||
ensure_ascii=False,
|
||||
)
|
||||
|
||||
|
||||
async def download_images(event: ChatEvent) -> list[str]:
|
||||
urls = [segment.data.get("url") or segment.data.get("file") for segment in event.get_message() if segment.type == "image"]
|
||||
urls = [url for url in urls if url]
|
||||
if not urls:
|
||||
return []
|
||||
ssl_context = ssl.create_default_context()
|
||||
ssl_context.check_hostname = False
|
||||
ssl_context.verify_mode = ssl.CERT_NONE
|
||||
ssl_context.set_ciphers("DEFAULT@SECLEVEL=2")
|
||||
images: list[str] = []
|
||||
async with httpx.AsyncClient(verify=ssl_context, timeout=10.0) as client:
|
||||
for url in urls:
|
||||
try:
|
||||
response = await client.get(url)
|
||||
response.raise_for_status()
|
||||
images.append(base64.b64encode(response.content).decode())
|
||||
except Exception:
|
||||
logger.exception(f"下载图片失败: {url}")
|
||||
return images
|
||||
|
||||
|
||||
async def send_split_messages(sender: MessageSender, content: str) -> None:
|
||||
segments = [segment.strip() for segment in content.split("<botbr>") if segment.strip()]
|
||||
logger.info(f"准备发送分段消息,分段数:{len(segments)}")
|
||||
for index, segment in enumerate(segments):
|
||||
if index:
|
||||
await asyncio.sleep(2)
|
||||
await sender(Message(segment))
|
||||
|
||||
|
||||
def build_reasoning_forward_nodes(self_id: str, nickname: str, content: str) -> list[dict]:
|
||||
return [
|
||||
{
|
||||
"type": "node",
|
||||
"data": {"nickname": nickname, "user_id": self_id, "content": f"{nickname}的内心OS:"},
|
||||
},
|
||||
{
|
||||
"type": "node",
|
||||
"data": {"nickname": nickname, "user_id": self_id, "content": content},
|
||||
},
|
||||
]
|
||||
Loading…
Add table
Add a link
Reference in a new issue