From d9ec43469993052dd416a2fbce00bccf254749a8 Mon Sep 17 00:00:00 2001 From: Moyuyanli <572490972@qq.com> Date: Tue, 11 Mar 2025 17:08:33 +0800 Subject: [PATCH 01/63] =?UTF-8?q?feat:gewe=E7=9A=84client=E6=B7=BB?= =?UTF-8?q?=E5=8A=A0=20=E6=B7=BB=E5=8A=A0=E5=A5=BD=E5=8F=8B=E6=8E=A5?= =?UTF-8?q?=E5=8F=A3=20feat:gewe=E7=9A=84client=E6=B7=BB=E5=8A=A0=20?= =?UTF-8?q?=E8=8E=B7=E5=8F=96=E7=BE=A4=E4=BF=A1=E6=81=AF/=E7=BE=A4?= =?UTF-8?q?=E6=88=90=E5=91=98=E6=8E=A5=E5=8F=A3=20feat:gewe=E7=9A=84client?= =?UTF-8?q?=E6=B7=BB=E5=8A=A0=20=E6=B7=BB=E5=8A=A0=E7=BE=A4=E6=88=90?= =?UTF-8?q?=E5=91=98=E4=B8=BA=E5=A5=BD=E5=8F=8B=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../core/platform/sources/gewechat/client.py | 72 +++++++++++++++++++ 1 file changed, 72 insertions(+) diff --git a/astrbot/core/platform/sources/gewechat/client.py b/astrbot/core/platform/sources/gewechat/client.py index 7268efded..de7762b2d 100644 --- a/astrbot/core/platform/sources/gewechat/client.py +++ b/astrbot/core/platform/sources/gewechat/client.py @@ -487,3 +487,75 @@ class SimpleGewechatClient: ) as resp: json_blob = await resp.json() logger.debug(f"发送文件结果: {json_blob}") + + async def add_friend(self, v3: str, v4: str, content: str): + """申请添加好友""" + payload = { + "appId": self.appid, + "scene": 3, + "content": content, + "v4": v4, + "v3": v3, + "option": 2, + } + + async with aiohttp.ClientSession() as session: + async with session.post( + f"{self.base_url}/contacts/addContacts", + headers=self.headers, + json=payload, + ) as resp: + json_blob = await resp.json() + logger.debug(f"申请添加好友结果: {json_blob}") + return json_blob + + async def get_group(self, group_id: str): + payload = { + "appId": self.appid, + "chatroomId": group_id, + } + + async with aiohttp.ClientSession() as session: + async with session.post( + f"{self.base_url}/group/getChatroomInfo", + headers=self.headers, + json=payload, + ) as resp: + json_blob = await resp.json() + logger.debug(f"获取群信息结果: {json_blob}") + return json_blob + + async def get_group_member(self, group_id: str): + payload = { + "appId": self.appid, + "chatroomId": group_id, + } + + async with aiohttp.ClientSession() as session: + async with session.post( + f"{self.base_url}/group/getChatroomMemberList", + headers=self.headers, + json=payload, + ) as resp: + json_blob = await resp.json() + logger.debug(f"获取群信息结果: {json_blob}") + return json_blob + + + async def add_group_member_to_friend(self, group_id: str,to_wxid: str,content: str): + payload = { + "appId": self.appid, + "chatroomId": group_id, + "content": content, + "memberWxid": to_wxid + } + + async with aiohttp.ClientSession() as session: + async with session.post( + f"{self.base_url}/group/addGroupMemberAsFriend", + headers=self.headers, + json=payload, + ) as resp: + json_blob = await resp.json() + logger.debug(f"获取群信息结果: {json_blob}") + return json_blob \ No newline at end of file From 76cfc31a1d992f6bf9dea23f2f1a71d9fbb7124d Mon Sep 17 00:00:00 2001 From: Moyuyanli <572490972@qq.com> Date: Tue, 11 Mar 2025 17:10:04 +0800 Subject: [PATCH 02/63] =?UTF-8?q?feat:=E6=B7=BB=E5=8A=A0=20Group=20?= =?UTF-8?q?=E7=B1=BB=E5=9E=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- astrbot/core/platform/astrbot_message.py | 48 +++++++++++++++++++++++- 1 file changed, 47 insertions(+), 1 deletion(-) diff --git a/astrbot/core/platform/astrbot_message.py b/astrbot/core/platform/astrbot_message.py index ea55eaf4b..cb399bacf 100644 --- a/astrbot/core/platform/astrbot_message.py +++ b/astrbot/core/platform/astrbot_message.py @@ -1,5 +1,5 @@ import time -from typing import List +from typing import List, Dict, Any from dataclasses import dataclass from astrbot.core.message.components import BaseMessageComponent from .message_type import MessageType @@ -10,6 +10,52 @@ class MessageMember: user_id: str # 发送者id nickname: str = None + def __str__(self): + # 使用 f-string 来构建返回的字符串表示形式 + return (f"User ID: {self.user_id}\n" + f"Nickname: {self.nickname if self.nickname else 'N/A'}") + +@dataclass +class Group: + group_id: str + group_name: str = None + + # 群头像 + group_avatar: str = None + + # 群主id + group_owner: str = None + + # 群管理员id + group_admin: str = None + + # 群成员 + members: List[MessageMember] = None + + def __str__(self): + # 使用 f-string 来构建返回的字符串表示形式 + return (f"Group ID: {self.group_id}\n" + f"Name: {self.group_name if self.group_name else 'N/A'}\n" + f"Avatar: {self.group_avatar if self.group_avatar else 'N/A'}\n" + f"Owner ID: {self.group_owner if self.group_owner else 'N/A'}\n" + f"Admin ID: {self.group_admin if self.group_admin else 'N/A'}") + + @classmethod + def from_dict(cls, data: Dict[str, Any]) -> "Group": + # 提取members信息并转换为MessageMember对象 + members = [ + MessageMember(user_id=member["wxid"], nickname=member["nickName"]) + for member in data.get("memberList", []) + ] + + return cls( + group_id=data["chatroomId"], + group_name=data.get("nickName"), + group_avatar=data.get("smallHeadImgUrl"), + group_owner=data.get("chatRoomOwner"), + members=members, + ) + class AstrBotMessage: """ From 4a8309ed1f77e6921be5bd81a63806ca0ea7ba60 Mon Sep 17 00:00:00 2001 From: Moyuyanli <572490972@qq.com> Date: Tue, 11 Mar 2025 17:10:55 +0800 Subject: [PATCH 03/63] =?UTF-8?q?style:idea=E9=BB=98=E8=AE=A4=E6=A0=BC?= =?UTF-8?q?=E5=BC=8F=E5=8C=96=E4=BA=86=E9=83=A8=E5=88=86=E4=BB=A3=E7=A0=81?= =?UTF-8?q?=20feat:=E6=B7=BB=E5=8A=A0=E6=A0=B9=E6=8D=AE=E6=B6=88=E6=81=AF?= =?UTF-8?q?=E4=BA=8B=E4=BB=B6=E8=8E=B7=E5=8F=96=E7=BE=A4=E4=BF=A1=E6=81=AF?= =?UTF-8?q?=E7=9A=84=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- astrbot/core/platform/astr_message_event.py | 75 +++++++++++++++------ 1 file changed, 55 insertions(+), 20 deletions(-) diff --git a/astrbot/core/platform/astr_message_event.py b/astrbot/core/platform/astr_message_event.py index 72e4414b6..a85ec0b9b 100644 --- a/astrbot/core/platform/astr_message_event.py +++ b/astrbot/core/platform/astr_message_event.py @@ -1,11 +1,9 @@ import abc import asyncio from dataclasses import dataclass -from .astrbot_message import AstrBotMessage -from .platform_metadata import PlatformMetadata -from astrbot.core.message.message_event_result import MessageEventResult, MessageChain -from astrbot.core.platform.message_type import MessageType -from typing import List, Union +from typing import List, Union, Optional + +from astrbot.core.db.po import Conversation from astrbot.core.message.components import ( Plain, Image, @@ -15,9 +13,12 @@ from astrbot.core.message.components import ( AtAll, Forward, ) -from astrbot.core.utils.metrics import Metric +from astrbot.core.message.message_event_result import MessageEventResult, MessageChain +from astrbot.core.platform.message_type import MessageType from astrbot.core.provider.entites import ProviderRequest -from astrbot.core.db.po import Conversation +from astrbot.core.utils.metrics import Metric +from .astrbot_message import AstrBotMessage, Group +from .platform_metadata import PlatformMetadata @dataclass @@ -37,11 +38,11 @@ class MessageSesion: class AstrMessageEvent(abc.ABC): def __init__( - self, - message_str: str, - message_obj: AstrBotMessage, - platform_meta: PlatformMetadata, - session_id: str, + self, + message_str: str, + message_obj: AstrBotMessage, + platform_meta: PlatformMetadata, + session_id: str, ): self.message_str = message_str """纯文本的消息""" @@ -320,14 +321,14 @@ class AstrMessageEvent(abc.ABC): """LLM 请求相关""" def request_llm( - self, - prompt: str, - func_tool_manager=None, - session_id: str = None, - image_urls: List[str] = [], - contexts: List = [], - system_prompt: str = "", - conversation: Conversation = None, + self, + prompt: str, + func_tool_manager=None, + session_id: str = None, + image_urls: List[str] = [], + contexts: List = [], + system_prompt: str = "", + conversation: Conversation = None, ) -> ProviderRequest: """ 创建一个 LLM 请求。 @@ -363,3 +364,37 @@ class AstrMessageEvent(abc.ABC): system_prompt=system_prompt, conversation=conversation, ) + + async def get_group(self, group_id: str = None) -> Optional[Group]: + """ + 获取群聊,如果不填写group_id,且消息是私聊消息,则返回 None + 目前只实现了 GeweChat 协议 + """ + # 确定有效的 group_id + if group_id is None: + group_id = self.message_obj.group_id + + if group_id is None: + return None + + # 检查平台是否为 gewechat + if self.platform_meta.name != "gewechat": + return None + + from astrbot.core.platform.sources.gewechat.gewechat_event import ( + GewechatPlatformEvent, + ) + + assert isinstance(self, GewechatPlatformEvent) + client = self.client + + # 从客户端获取群信息 + res = await client.get_group(group_id) + + data = res["data"] + + # 检查 chatroomId 是否为空 + if data["chatroomId"] == "": + return None + + return Group.from_dict(data) From 00f5189f58cab395512ec670f21d7d395d3fc1ad Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 11 Mar 2025 09:16:43 +0000 Subject: [PATCH 04/63] :balloon: auto fixes by pre-commit hooks --- astrbot/core/platform/astr_message_event.py | 26 +++++++++---------- astrbot/core/platform/astrbot_message.py | 19 +++++++++----- .../core/platform/sources/gewechat/client.py | 9 ++++--- 3 files changed, 30 insertions(+), 24 deletions(-) diff --git a/astrbot/core/platform/astr_message_event.py b/astrbot/core/platform/astr_message_event.py index a85ec0b9b..02d600768 100644 --- a/astrbot/core/platform/astr_message_event.py +++ b/astrbot/core/platform/astr_message_event.py @@ -38,11 +38,11 @@ class MessageSesion: class AstrMessageEvent(abc.ABC): def __init__( - self, - message_str: str, - message_obj: AstrBotMessage, - platform_meta: PlatformMetadata, - session_id: str, + self, + message_str: str, + message_obj: AstrBotMessage, + platform_meta: PlatformMetadata, + session_id: str, ): self.message_str = message_str """纯文本的消息""" @@ -321,14 +321,14 @@ class AstrMessageEvent(abc.ABC): """LLM 请求相关""" def request_llm( - self, - prompt: str, - func_tool_manager=None, - session_id: str = None, - image_urls: List[str] = [], - contexts: List = [], - system_prompt: str = "", - conversation: Conversation = None, + self, + prompt: str, + func_tool_manager=None, + session_id: str = None, + image_urls: List[str] = [], + contexts: List = [], + system_prompt: str = "", + conversation: Conversation = None, ) -> ProviderRequest: """ 创建一个 LLM 请求。 diff --git a/astrbot/core/platform/astrbot_message.py b/astrbot/core/platform/astrbot_message.py index cb399bacf..be49ba18e 100644 --- a/astrbot/core/platform/astrbot_message.py +++ b/astrbot/core/platform/astrbot_message.py @@ -12,8 +12,11 @@ class MessageMember: def __str__(self): # 使用 f-string 来构建返回的字符串表示形式 - return (f"User ID: {self.user_id}\n" - f"Nickname: {self.nickname if self.nickname else 'N/A'}") + return ( + f"User ID: {self.user_id}\n" + f"Nickname: {self.nickname if self.nickname else 'N/A'}" + ) + @dataclass class Group: @@ -34,11 +37,13 @@ class Group: def __str__(self): # 使用 f-string 来构建返回的字符串表示形式 - return (f"Group ID: {self.group_id}\n" - f"Name: {self.group_name if self.group_name else 'N/A'}\n" - f"Avatar: {self.group_avatar if self.group_avatar else 'N/A'}\n" - f"Owner ID: {self.group_owner if self.group_owner else 'N/A'}\n" - f"Admin ID: {self.group_admin if self.group_admin else 'N/A'}") + return ( + f"Group ID: {self.group_id}\n" + f"Name: {self.group_name if self.group_name else 'N/A'}\n" + f"Avatar: {self.group_avatar if self.group_avatar else 'N/A'}\n" + f"Owner ID: {self.group_owner if self.group_owner else 'N/A'}\n" + f"Admin ID: {self.group_admin if self.group_admin else 'N/A'}" + ) @classmethod def from_dict(cls, data: Dict[str, Any]) -> "Group": diff --git a/astrbot/core/platform/sources/gewechat/client.py b/astrbot/core/platform/sources/gewechat/client.py index de7762b2d..6c7bd0a23 100644 --- a/astrbot/core/platform/sources/gewechat/client.py +++ b/astrbot/core/platform/sources/gewechat/client.py @@ -541,13 +541,14 @@ class SimpleGewechatClient: logger.debug(f"获取群信息结果: {json_blob}") return json_blob - - async def add_group_member_to_friend(self, group_id: str,to_wxid: str,content: str): + async def add_group_member_to_friend( + self, group_id: str, to_wxid: str, content: str + ): payload = { "appId": self.appid, "chatroomId": group_id, "content": content, - "memberWxid": to_wxid + "memberWxid": to_wxid, } async with aiohttp.ClientSession() as session: @@ -558,4 +559,4 @@ class SimpleGewechatClient: ) as resp: json_blob = await resp.json() logger.debug(f"获取群信息结果: {json_blob}") - return json_blob \ No newline at end of file + return json_blob From d177b9f7fab89678d479888603034ec8f6591816 Mon Sep 17 00:00:00 2001 From: Moyuyanli <572490972@qq.com> Date: Fri, 14 Mar 2025 17:11:10 +0800 Subject: [PATCH 05/63] =?UTF-8?q?feat:=E6=B7=BB=E5=8A=A0=E4=B8=BB=E5=8A=A8?= =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E5=A5=BD=E5=8F=8B=E4=BA=8B=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 1 + .../core/platform/sources/gewechat/client.py | 155 +++++++++++------- requirements.txt | 44 ++--- 3 files changed, 124 insertions(+), 76 deletions(-) diff --git a/.gitignore b/.gitignore index a863e36ec..4b683ee8b 100644 --- a/.gitignore +++ b/.gitignore @@ -26,3 +26,4 @@ venv/* packages/python_interpreter/workplace .venv/* .conda/ +.idea \ No newline at end of file diff --git a/astrbot/core/platform/sources/gewechat/client.py b/astrbot/core/platform/sources/gewechat/client.py index 6c7bd0a23..985185b12 100644 --- a/astrbot/core/platform/sources/gewechat/client.py +++ b/astrbot/core/platform/sources/gewechat/client.py @@ -1,17 +1,19 @@ -import threading import asyncio -import aiohttp -import quart import base64 import datetime -import re import os +import re +import threading + +import aiohttp import anyio -from astrbot.api.platform import AstrBotMessage, MessageMember, MessageType -from astrbot.api.message_components import Plain, Image, At, Record +import quart + from astrbot.api import logger, sp -from .downloader import GeweDownloader +from astrbot.api.message_components import Plain, Image, At, Record +from astrbot.api.platform import AstrBotMessage, MessageMember, MessageType from astrbot.core.utils.io import download_image_by_url +from .downloader import GeweDownloader class SimpleGewechatClient: @@ -22,12 +24,12 @@ class SimpleGewechatClient: """ def __init__( - self, - base_url: str, - nickname: str, - host: str, - port: int, - event_queue: asyncio.Queue, + self, + base_url: str, + nickname: str, + host: str, + port: int, + event_queue: asyncio.Queue, ): self.base_url = base_url if self.base_url.endswith("/"): @@ -137,8 +139,8 @@ class SimpleGewechatClient: # at msg_source = d["MsgSource"] if ( - f"" in msg_source - or f"" in msg_source + f"" in msg_source + or f"" in msg_source ): at_me = True if "在群聊中@了你" in d.get("PushContent", ""): @@ -155,8 +157,8 @@ class SimpleGewechatClient: user_real_name = "unknown" if abm.group_id: if ( - abm.group_id not in self.userrealnames - or user_id not in self.userrealnames[abm.group_id] + abm.group_id not in self.userrealnames + or user_id not in self.userrealnames[abm.group_id] ): # 获取群成员列表,并且缓存 if abm.group_id not in self.userrealnames: @@ -250,9 +252,9 @@ class SimpleGewechatClient: await asyncio.sleep(3) async with aiohttp.ClientSession() as session: async with session.post( - f"{self.base_url}/tools/setCallback", - headers=self.headers, - json={"token": self.token, "callbackUrl": self.callback_url}, + f"{self.base_url}/tools/setCallback", + headers=self.headers, + json={"token": self.token, "callbackUrl": self.callback_url}, ) as resp: json_blob = await resp.json() logger.info(f"设置回调结果: {json_blob}") @@ -280,9 +282,9 @@ class SimpleGewechatClient: # /login/checkOnline async with aiohttp.ClientSession() as session: async with session.post( - f"{self.base_url}/login/checkOnline", - headers=self.headers, - json={"appId": appid}, + f"{self.base_url}/login/checkOnline", + headers=self.headers, + json={"appId": appid}, ) as resp: json_blob = await resp.json() return json_blob["data"] @@ -293,9 +295,9 @@ class SimpleGewechatClient: if online: async with aiohttp.ClientSession() as session: async with session.post( - f"{self.base_url}/login/logout", - headers=self.headers, - json={"appId": self.appid}, + f"{self.base_url}/login/logout", + headers=self.headers, + json={"appId": self.appid}, ) as resp: json_blob = await resp.json() logger.info(f"登出结果: {json_blob}") @@ -327,9 +329,9 @@ class SimpleGewechatClient: try: async with aiohttp.ClientSession() as session: async with session.post( - f"{self.base_url}/login/getLoginQrCode", - headers=self.headers, - json=payload, + f"{self.base_url}/login/getLoginQrCode", + headers=self.headers, + json=payload, ) as resp: json_blob = await resp.json() if json_blob["ret"] != 200: @@ -378,9 +380,9 @@ class SimpleGewechatClient: async with aiohttp.ClientSession() as session: async with session.post( - f"{self.base_url}/login/checkLogin", - headers=self.headers, - json=payload, + f"{self.base_url}/login/checkLogin", + headers=self.headers, + json=payload, ) as resp: json_blob = await resp.json() logger.info(f"检查登录状态: {json_blob}") @@ -419,9 +421,9 @@ class SimpleGewechatClient: async with aiohttp.ClientSession() as session: async with session.post( - f"{self.base_url}/group/getChatroomMemberList", - headers=self.headers, - json=payload, + f"{self.base_url}/group/getChatroomMemberList", + headers=self.headers, + json=payload, ) as resp: json_blob = await resp.json() return json_blob["data"] @@ -437,7 +439,7 @@ class SimpleGewechatClient: async with aiohttp.ClientSession() as session: async with session.post( - f"{self.base_url}/message/postText", headers=self.headers, json=payload + f"{self.base_url}/message/postText", headers=self.headers, json=payload ) as resp: json_blob = await resp.json() logger.debug(f"发送消息结果: {json_blob}") @@ -451,7 +453,7 @@ class SimpleGewechatClient: async with aiohttp.ClientSession() as session: async with session.post( - f"{self.base_url}/message/postImage", headers=self.headers, json=payload + f"{self.base_url}/message/postImage", headers=self.headers, json=payload ) as resp: json_blob = await resp.json() logger.debug(f"发送图片结果: {json_blob}") @@ -468,7 +470,7 @@ class SimpleGewechatClient: async with aiohttp.ClientSession() as session: async with session.post( - f"{self.base_url}/message/postVoice", headers=self.headers, json=payload + f"{self.base_url}/message/postVoice", headers=self.headers, json=payload ) as resp: json_blob = await resp.json() logger.debug(f"发送语音结果: {json_blob}") @@ -483,7 +485,7 @@ class SimpleGewechatClient: async with aiohttp.ClientSession() as session: async with session.post( - f"{self.base_url}/message/postFile", headers=self.headers, json=payload + f"{self.base_url}/message/postFile", headers=self.headers, json=payload ) as resp: json_blob = await resp.json() logger.debug(f"发送文件结果: {json_blob}") @@ -501,9 +503,9 @@ class SimpleGewechatClient: async with aiohttp.ClientSession() as session: async with session.post( - f"{self.base_url}/contacts/addContacts", - headers=self.headers, - json=payload, + f"{self.base_url}/contacts/addContacts", + headers=self.headers, + json=payload, ) as resp: json_blob = await resp.json() logger.debug(f"申请添加好友结果: {json_blob}") @@ -517,9 +519,9 @@ class SimpleGewechatClient: async with aiohttp.ClientSession() as session: async with session.post( - f"{self.base_url}/group/getChatroomInfo", - headers=self.headers, - json=payload, + f"{self.base_url}/group/getChatroomInfo", + headers=self.headers, + json=payload, ) as resp: json_blob = await resp.json() logger.debug(f"获取群信息结果: {json_blob}") @@ -533,29 +535,68 @@ class SimpleGewechatClient: async with aiohttp.ClientSession() as session: async with session.post( - f"{self.base_url}/group/getChatroomMemberList", - headers=self.headers, - json=payload, + f"{self.base_url}/group/getChatroomMemberList", + headers=self.headers, + json=payload, ) as resp: json_blob = await resp.json() logger.debug(f"获取群信息结果: {json_blob}") return json_blob - async def add_group_member_to_friend( - self, group_id: str, to_wxid: str, content: str - ): + async def accept_group_invite(self, url: str): + """同意进群""" payload = { "appId": self.appid, - "chatroomId": group_id, - "content": content, - "memberWxid": to_wxid, + "url": url } async with aiohttp.ClientSession() as session: async with session.post( - f"{self.base_url}/group/addGroupMemberAsFriend", - headers=self.headers, - json=payload, + f"{self.base_url}/group/agreeJoinRoom", + headers=self.headers, + json=payload, + ) as resp: + json_blob = await resp.json() + logger.debug(f"获取群信息结果: {json_blob}") + return json_blob + + async def add_group_member_to_friend(self, group_id: str, to_wxid: str, content: str): + payload = { + "appId": self.appid, + "chatroomId": group_id, + "content": content, + "memberWxid": to_wxid + } + + async with aiohttp.ClientSession() as session: + async with session.post( + f"{self.base_url}/group/addGroupMemberAsFriend", + headers=self.headers, + json=payload, + ) as resp: + json_blob = await resp.json() + logger.debug(f"获取群信息结果: {json_blob}") + return json_blob + + async def get_user_or_group_info(self, *ids): + """ + 获取用户或群组信息。 + + :param ids: 可变数量的 wxid 参数 + """ + + wxids_str = list(ids) + + payload = { + "appId": self.appid, + "wxids": wxids_str # 使用逗号分隔的字符串 + } + + async with aiohttp.ClientSession() as session: + async with session.post( + f"{self.base_url}/contacts/getDetailInfo", + headers=self.headers, + json=payload, ) as resp: json_blob = await resp.json() logger.debug(f"获取群信息结果: {json_blob}") diff --git a/requirements.txt b/requirements.txt index 07b168cc1..ac56e3db4 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,27 +1,33 @@ pydantic~=2.10.3 -aiohttp -openai -anthropic +aiohttp~=3.11.13 +openai~=1.65.5 +anthropic~=0.49.0 qq-botpy chardet~=5.1.0 -Pillow -beautifulsoup4 +Pillow~=11.1.0 +beautifulsoup4~=4.13.3 googlesearch-python -readability-lxml -quart +readability-lxml~=0.8.1 +quart~=0.20.0 lxml_html_clean -colorlog -aiocqhttp -pyjwt -apscheduler -docstring_parser -aiodocker +colorlog~=6.9.0 +aiocqhttp~=1.4.4 +pyjwt~=2.10.1 +apscheduler~=3.11.0 +docstring_parser~=0.16 +aiodocker~=0.24.0 silk-python psutil>=5.8.0 lark-oapi -ormsgpack -cryptography -dashscope -python-telegram-bot -wechatpy -dingtalk-stream \ No newline at end of file +ormsgpack~=1.8.0 +cryptography~=44.0.2 +dashscope~=1.22.2 +python-telegram-bot~=21.11.1 +wechatpy~=1.8.18 +dingtalk-stream +PyYAML~=6.0.2 +pip~=23.2.1 +typing_extensions~=4.12.2 +requests~=2.32.3 +anyio~=4.8.0 +httpx~=0.28.1 \ No newline at end of file From c95682a0c7f5a8e5d3802d0ebc56edb8b19739dc Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 14 Mar 2025 09:11:21 +0000 Subject: [PATCH 06/63] :balloon: auto fixes by pre-commit hooks --- .../core/platform/sources/gewechat/client.py | 119 +++++++++--------- 1 file changed, 59 insertions(+), 60 deletions(-) diff --git a/astrbot/core/platform/sources/gewechat/client.py b/astrbot/core/platform/sources/gewechat/client.py index 985185b12..abaf1a877 100644 --- a/astrbot/core/platform/sources/gewechat/client.py +++ b/astrbot/core/platform/sources/gewechat/client.py @@ -24,12 +24,12 @@ class SimpleGewechatClient: """ def __init__( - self, - base_url: str, - nickname: str, - host: str, - port: int, - event_queue: asyncio.Queue, + self, + base_url: str, + nickname: str, + host: str, + port: int, + event_queue: asyncio.Queue, ): self.base_url = base_url if self.base_url.endswith("/"): @@ -139,8 +139,8 @@ class SimpleGewechatClient: # at msg_source = d["MsgSource"] if ( - f"" in msg_source - or f"" in msg_source + f"" in msg_source + or f"" in msg_source ): at_me = True if "在群聊中@了你" in d.get("PushContent", ""): @@ -157,8 +157,8 @@ class SimpleGewechatClient: user_real_name = "unknown" if abm.group_id: if ( - abm.group_id not in self.userrealnames - or user_id not in self.userrealnames[abm.group_id] + abm.group_id not in self.userrealnames + or user_id not in self.userrealnames[abm.group_id] ): # 获取群成员列表,并且缓存 if abm.group_id not in self.userrealnames: @@ -252,9 +252,9 @@ class SimpleGewechatClient: await asyncio.sleep(3) async with aiohttp.ClientSession() as session: async with session.post( - f"{self.base_url}/tools/setCallback", - headers=self.headers, - json={"token": self.token, "callbackUrl": self.callback_url}, + f"{self.base_url}/tools/setCallback", + headers=self.headers, + json={"token": self.token, "callbackUrl": self.callback_url}, ) as resp: json_blob = await resp.json() logger.info(f"设置回调结果: {json_blob}") @@ -282,9 +282,9 @@ class SimpleGewechatClient: # /login/checkOnline async with aiohttp.ClientSession() as session: async with session.post( - f"{self.base_url}/login/checkOnline", - headers=self.headers, - json={"appId": appid}, + f"{self.base_url}/login/checkOnline", + headers=self.headers, + json={"appId": appid}, ) as resp: json_blob = await resp.json() return json_blob["data"] @@ -295,9 +295,9 @@ class SimpleGewechatClient: if online: async with aiohttp.ClientSession() as session: async with session.post( - f"{self.base_url}/login/logout", - headers=self.headers, - json={"appId": self.appid}, + f"{self.base_url}/login/logout", + headers=self.headers, + json={"appId": self.appid}, ) as resp: json_blob = await resp.json() logger.info(f"登出结果: {json_blob}") @@ -329,9 +329,9 @@ class SimpleGewechatClient: try: async with aiohttp.ClientSession() as session: async with session.post( - f"{self.base_url}/login/getLoginQrCode", - headers=self.headers, - json=payload, + f"{self.base_url}/login/getLoginQrCode", + headers=self.headers, + json=payload, ) as resp: json_blob = await resp.json() if json_blob["ret"] != 200: @@ -380,9 +380,9 @@ class SimpleGewechatClient: async with aiohttp.ClientSession() as session: async with session.post( - f"{self.base_url}/login/checkLogin", - headers=self.headers, - json=payload, + f"{self.base_url}/login/checkLogin", + headers=self.headers, + json=payload, ) as resp: json_blob = await resp.json() logger.info(f"检查登录状态: {json_blob}") @@ -421,9 +421,9 @@ class SimpleGewechatClient: async with aiohttp.ClientSession() as session: async with session.post( - f"{self.base_url}/group/getChatroomMemberList", - headers=self.headers, - json=payload, + f"{self.base_url}/group/getChatroomMemberList", + headers=self.headers, + json=payload, ) as resp: json_blob = await resp.json() return json_blob["data"] @@ -439,7 +439,7 @@ class SimpleGewechatClient: async with aiohttp.ClientSession() as session: async with session.post( - f"{self.base_url}/message/postText", headers=self.headers, json=payload + f"{self.base_url}/message/postText", headers=self.headers, json=payload ) as resp: json_blob = await resp.json() logger.debug(f"发送消息结果: {json_blob}") @@ -453,7 +453,7 @@ class SimpleGewechatClient: async with aiohttp.ClientSession() as session: async with session.post( - f"{self.base_url}/message/postImage", headers=self.headers, json=payload + f"{self.base_url}/message/postImage", headers=self.headers, json=payload ) as resp: json_blob = await resp.json() logger.debug(f"发送图片结果: {json_blob}") @@ -470,7 +470,7 @@ class SimpleGewechatClient: async with aiohttp.ClientSession() as session: async with session.post( - f"{self.base_url}/message/postVoice", headers=self.headers, json=payload + f"{self.base_url}/message/postVoice", headers=self.headers, json=payload ) as resp: json_blob = await resp.json() logger.debug(f"发送语音结果: {json_blob}") @@ -485,7 +485,7 @@ class SimpleGewechatClient: async with aiohttp.ClientSession() as session: async with session.post( - f"{self.base_url}/message/postFile", headers=self.headers, json=payload + f"{self.base_url}/message/postFile", headers=self.headers, json=payload ) as resp: json_blob = await resp.json() logger.debug(f"发送文件结果: {json_blob}") @@ -503,9 +503,9 @@ class SimpleGewechatClient: async with aiohttp.ClientSession() as session: async with session.post( - f"{self.base_url}/contacts/addContacts", - headers=self.headers, - json=payload, + f"{self.base_url}/contacts/addContacts", + headers=self.headers, + json=payload, ) as resp: json_blob = await resp.json() logger.debug(f"申请添加好友结果: {json_blob}") @@ -519,9 +519,9 @@ class SimpleGewechatClient: async with aiohttp.ClientSession() as session: async with session.post( - f"{self.base_url}/group/getChatroomInfo", - headers=self.headers, - json=payload, + f"{self.base_url}/group/getChatroomInfo", + headers=self.headers, + json=payload, ) as resp: json_blob = await resp.json() logger.debug(f"获取群信息结果: {json_blob}") @@ -535,9 +535,9 @@ class SimpleGewechatClient: async with aiohttp.ClientSession() as session: async with session.post( - f"{self.base_url}/group/getChatroomMemberList", - headers=self.headers, - json=payload, + f"{self.base_url}/group/getChatroomMemberList", + headers=self.headers, + json=payload, ) as resp: json_blob = await resp.json() logger.debug(f"获取群信息结果: {json_blob}") @@ -545,34 +545,33 @@ class SimpleGewechatClient: async def accept_group_invite(self, url: str): """同意进群""" - payload = { - "appId": self.appid, - "url": url - } + payload = {"appId": self.appid, "url": url} async with aiohttp.ClientSession() as session: async with session.post( - f"{self.base_url}/group/agreeJoinRoom", - headers=self.headers, - json=payload, + f"{self.base_url}/group/agreeJoinRoom", + headers=self.headers, + json=payload, ) as resp: json_blob = await resp.json() logger.debug(f"获取群信息结果: {json_blob}") return json_blob - async def add_group_member_to_friend(self, group_id: str, to_wxid: str, content: str): + async def add_group_member_to_friend( + self, group_id: str, to_wxid: str, content: str + ): payload = { "appId": self.appid, "chatroomId": group_id, "content": content, - "memberWxid": to_wxid + "memberWxid": to_wxid, } async with aiohttp.ClientSession() as session: async with session.post( - f"{self.base_url}/group/addGroupMemberAsFriend", - headers=self.headers, - json=payload, + f"{self.base_url}/group/addGroupMemberAsFriend", + headers=self.headers, + json=payload, ) as resp: json_blob = await resp.json() logger.debug(f"获取群信息结果: {json_blob}") @@ -580,23 +579,23 @@ class SimpleGewechatClient: async def get_user_or_group_info(self, *ids): """ - 获取用户或群组信息。 + 获取用户或群组信息。 - :param ids: 可变数量的 wxid 参数 - """ + :param ids: 可变数量的 wxid 参数 + """ wxids_str = list(ids) payload = { "appId": self.appid, - "wxids": wxids_str # 使用逗号分隔的字符串 + "wxids": wxids_str, # 使用逗号分隔的字符串 } async with aiohttp.ClientSession() as session: async with session.post( - f"{self.base_url}/contacts/getDetailInfo", - headers=self.headers, - json=payload, + f"{self.base_url}/contacts/getDetailInfo", + headers=self.headers, + json=payload, ) as resp: json_blob = await resp.json() logger.debug(f"获取群信息结果: {json_blob}") From 44601c8954c25b60f568f49652af3dd27337d04d Mon Sep 17 00:00:00 2001 From: Moyuyanli <572490972@qq.com> Date: Fri, 14 Mar 2025 18:30:27 +0800 Subject: [PATCH 07/63] =?UTF-8?q?fix:=E4=BF=AE=E5=A4=8Dgewe=E7=9A=84ModCon?= =?UTF-8?q?tacts=E6=B6=88=E6=81=AF=E7=B1=BB=E5=9E=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- astrbot/core/platform/sources/gewechat/client.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/astrbot/core/platform/sources/gewechat/client.py b/astrbot/core/platform/sources/gewechat/client.py index 985185b12..d7550490b 100644 --- a/astrbot/core/platform/sources/gewechat/client.py +++ b/astrbot/core/platform/sources/gewechat/client.py @@ -115,6 +115,12 @@ class SimpleGewechatClient: abm = AstrBotMessage() + if type_name == "ModContacts": + abm.type = MessageType.OTHER_MESSAGE + abm.raw_message = data + logger.debug(f"abm: {abm}") + return abm + from_user_name = d["FromUserName"]["string"] # 消息来源 d["to_wxid"] = from_user_name # 用于发信息 From 80b2b7dc00dfbbcb40368f0b2af858cc0165d55a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=86=B0=E8=8B=B7=E6=99=B6?= <2749332490@qq.com> Date: Sun, 16 Mar 2025 21:35:30 +0800 Subject: [PATCH 08/63] =?UTF-8?q?=F0=9F=8E=88=20perf:=20=E4=BC=98=E5=8C=96?= =?UTF-8?q?=E4=BA=86iframe=E7=AA=97=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../full/vertical-sidebar/VerticalSidebar.vue | 239 ++++++++++++------ 1 file changed, 163 insertions(+), 76 deletions(-) diff --git a/dashboard/src/layouts/full/vertical-sidebar/VerticalSidebar.vue b/dashboard/src/layouts/full/vertical-sidebar/VerticalSidebar.vue index 17cec39fa..9b2abed91 100644 --- a/dashboard/src/layouts/full/vertical-sidebar/VerticalSidebar.vue +++ b/dashboard/src/layouts/full/vertical-sidebar/VerticalSidebar.vue @@ -1,5 +1,6 @@ - - From 2e4fef6c6613309789de8e56fd6dee20d51fa138 Mon Sep 17 00:00:00 2001 From: Moyuyanli <572490972@qq.com> Date: Mon, 17 Mar 2025 16:02:55 +0800 Subject: [PATCH 09/63] =?UTF-8?q?feat:=E6=B7=BB=E5=8A=A0=E6=B6=88=E6=81=AF?= =?UTF-8?q?=E8=AE=B0=E5=BD=95=E5=99=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../core/platform/sources/gewechat/client.py | 23 +++++++++++++++---- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/astrbot/core/platform/sources/gewechat/client.py b/astrbot/core/platform/sources/gewechat/client.py index 026195fc8..7a07afe9c 100644 --- a/astrbot/core/platform/sources/gewechat/client.py +++ b/astrbot/core/platform/sources/gewechat/client.py @@ -115,11 +115,8 @@ class SimpleGewechatClient: abm = AstrBotMessage() - if type_name == "ModContacts": - abm.type = MessageType.OTHER_MESSAGE - abm.raw_message = data - logger.debug(f"abm: {abm}") - return abm + # if type_name == "ModContacts": + # self from_user_name = d["FromUserName"]["string"] # 消息来源 d["to_wxid"] = from_user_name # 用于发信息 @@ -606,3 +603,19 @@ class SimpleGewechatClient: json_blob = await resp.json() logger.debug(f"获取群信息结果: {json_blob}") return json_blob + async def send_message(self, to_wxid, content): + payload = { + "appId": self.appid, + "toWxid": to_wxid, + "content":content + } + + async with aiohttp.ClientSession() as session: + async with session.post( + f"{self.base_url}/message/postText", + headers=self.headers, + json=payload, + ) as resp: + json_blob = await resp.json() + logger.debug(f"获取群信息结果: {json_blob}") + # return json_blob From 25f8d5519bca7792f5673c2be4cf9797ae8fb1bc Mon Sep 17 00:00:00 2001 From: Soulter <905617992@qq.com> Date: Mon, 17 Mar 2025 22:42:48 +0800 Subject: [PATCH 10/63] =?UTF-8?q?=F0=9F=90=9B=20fix:=20LLOnebot=20?= =?UTF-8?q?=E5=90=88=E5=B9=B6=E6=B6=88=E6=81=AF=E8=BD=AC=E5=8F=91=E9=94=99?= =?UTF-8?q?=E8=AF=AF=20#842?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../sources/aiocqhttp/aiocqhttp_message_event.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/astrbot/core/platform/sources/aiocqhttp/aiocqhttp_message_event.py b/astrbot/core/platform/sources/aiocqhttp/aiocqhttp_message_event.py index dbe9a3ce0..57d0c4f5b 100644 --- a/astrbot/core/platform/sources/aiocqhttp/aiocqhttp_message_event.py +++ b/astrbot/core/platform/sources/aiocqhttp/aiocqhttp_message_event.py @@ -56,8 +56,13 @@ class AiocqhttpMessageEvent(AstrMessageEvent): if send_one_by_one: for seg in message.chain: - if isinstance(seg, Nodes): - # 带有多个节点的合并转发消息 + if isinstance(seg, (Node, Nodes)): + # 合并转发消息 + + if isinstance(seg, Node): + nodes = Nodes([seg]) + seg = nodes + payload = seg.toDict() if self.get_group_id(): payload["group_id"] = self.get_group_id() From 53435ba1840183fa7c3d8986e4262419a809914c Mon Sep 17 00:00:00 2001 From: Soulter <905617992@qq.com> Date: Mon, 17 Mar 2025 23:11:57 +0800 Subject: [PATCH 11/63] =?UTF-8?q?=F0=9F=90=9B=20fix:=20=E4=BF=AE=E5=A4=8D?= =?UTF-8?q?=20model=5Fconfig=20=E4=B8=AD=E8=87=AA=E5=AE=9A=E4=B9=89?= =?UTF-8?q?=E7=9A=84=E9=85=8D=E7=BD=AE=E9=A1=B9=EF=BC=88=E5=A6=82=E6=B8=A9?= =?UTF-8?q?=E5=BA=A6=EF=BC=89=E7=B1=BB=E5=9E=8B=E8=87=AA=E5=8A=A8=E5=8F=98?= =?UTF-8?q?=E5=9B=9E=20string=20#854?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- astrbot/dashboard/routes/config.py | 29 +++++++++++++++++++++++++---- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/astrbot/dashboard/routes/config.py b/astrbot/dashboard/routes/config.py index 54140d92a..443a544bb 100644 --- a/astrbot/dashboard/routes/config.py +++ b/astrbot/dashboard/routes/config.py @@ -29,11 +29,22 @@ def validate_config( ) -> typing.Tuple[typing.List[str], typing.Dict]: errors = [] - def validate(data, metadata=schema, path=""): - for key, meta in metadata.items(): - if key not in data: + def validate(data: dict, metadata: dict = schema, path=""): + for key, value in data.items(): + print(key, value) + if key not in metadata: + # 无 schema 的配置项,执行类型猜测 + if isinstance(value, str): + if value.isdigit(): + data[key] = int(value) + elif value.replace(".", "", 1).isdigit(): + data[key] = float(value) + elif value == "true": + data[key] = True + elif value == "false": + data[key] = False continue - value = data[key] + meta = metadata[key] # null 转换 if value is None: data[key] = DEFAULT_VALUE_MAP[meta["type"]] @@ -43,6 +54,16 @@ def validate_config( errors.append( f"错误的类型 {path}{key}: 期望是 list, 得到了 {type(value).__name__}" ) + elif ( + meta["type"] == "list" + and isinstance(value, list) + and value + and "items" in meta + and isinstance(value[0], dict) + ): + # 当前仅针对 list[dict] 的情况进行类型校验,以适配 AstrBot 中 platform、provider 的配置 + for item in value: + validate(item, meta["items"], path=f"{path}{key}.") elif meta["type"] == "object" and isinstance(value, dict): validate(value, meta["items"], path=f"{path}{key}.") From c364f7c64363cd3b3783091361fc0d33de9901e3 Mon Sep 17 00:00:00 2001 From: Soulter <905617992@qq.com> Date: Mon, 17 Mar 2025 23:17:07 +0800 Subject: [PATCH 12/63] =?UTF-8?q?=F0=9F=8E=88=20perf:=20Dify=20=E4=B8=8B?= =?UTF-8?q?=E5=BD=93=E5=8F=AA=E6=9C=89=E5=9B=BE=E7=89=87=E8=BE=93=E5=85=A5?= =?UTF-8?q?=E6=97=B6=E7=9A=84=E9=BB=98=E8=AE=A4=20prompt=20#837?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- astrbot/core/provider/sources/dify_source.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/astrbot/core/provider/sources/dify_source.py b/astrbot/core/provider/sources/dify_source.py index 9af198aa7..e4cc76263 100644 --- a/astrbot/core/provider/sources/dify_source.py +++ b/astrbot/core/provider/sources/dify_source.py @@ -99,6 +99,9 @@ class ProviderDify(Provider): try: match self.api_type: case "chat" | "agent": + if not prompt: + prompt = "请描述这张图片。" + async for chunk in self.api_client.chat_messages( inputs={ **payload_vars, From ad6487d042a78ce302da9a5db5b8cade857e2a8b Mon Sep 17 00:00:00 2001 From: Soulter <905617992@qq.com> Date: Mon, 17 Mar 2025 23:44:04 +0800 Subject: [PATCH 13/63] =?UTF-8?q?=F0=9F=90=9B=20fix:=20=E4=BF=AE=E5=A4=8D?= =?UTF-8?q?=E9=83=A8=E5=88=86=E6=8C=87=E4=BB=A4=E5=8F=AF=E8=83=BD=E9=80=A0?= =?UTF-8?q?=E6=88=90=E7=9A=84=E9=85=8D=E7=BD=AE=E7=B1=BB=E5=9E=8B=E9=97=AE?= =?UTF-8?q?=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/astrbot/main.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/astrbot/main.py b/packages/astrbot/main.py index 463f24950..c2c4dfc32 100644 --- a/packages/astrbot/main.py +++ b/packages/astrbot/main.py @@ -287,7 +287,7 @@ UID: {user_id} 此 ID 可用于设置管理员。 ) ) return - self.context.get_config()["admins_id"].append(admin_id) + self.context.get_config()["admins_id"].append(str(admin_id)) self.context.get_config().save_config() event.set_result(MessageEventResult().message("授权成功。")) @@ -296,7 +296,7 @@ UID: {user_id} 此 ID 可用于设置管理员。 async def deop(self, event: AstrMessageEvent, admin_id: str): """取消授权管理员。deop """ try: - self.context.get_config()["admins_id"].remove(admin_id) + self.context.get_config()["admins_id"].remove(str(admin_id)) self.context.get_config().save_config() event.set_result(MessageEventResult().message("取消授权成功。")) except ValueError: @@ -314,7 +314,7 @@ UID: {user_id} 此 ID 可用于设置管理员。 "使用方法: /wl 添加白名单;/dwl 删除白名单。可通过 /sid 获取 ID。" ) ) - self.context.get_config()["platform_settings"]["id_whitelist"].append(sid) + self.context.get_config()["platform_settings"]["id_whitelist"].append(str(sid)) self.context.get_config().save_config() event.set_result(MessageEventResult().message("添加白名单成功。")) @@ -323,7 +323,7 @@ UID: {user_id} 此 ID 可用于设置管理员。 async def dwl(self, event: AstrMessageEvent, sid: str): """删除白名单。dwl """ try: - self.context.get_config()["platform_settings"]["id_whitelist"].remove(sid) + self.context.get_config()["platform_settings"]["id_whitelist"].remove(str(sid)) self.context.get_config().save_config() event.set_result(MessageEventResult().message("删除白名单成功。")) except ValueError: From 75dace2dee4b8a49f8a804d8aaaa0e208560a06a Mon Sep 17 00:00:00 2001 From: Soulter <905617992@qq.com> Date: Tue, 18 Mar 2025 00:16:47 +0800 Subject: [PATCH 14/63] =?UTF-8?q?=F0=9F=8E=88=20perf:=20=E4=BC=98=E5=8C=96?= =?UTF-8?q?=E9=85=8D=E7=BD=AE=E9=A1=B5=E7=9A=84=E6=98=BE=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/components/shared/AstrBotConfig.vue | 64 +++++++++++-------- dashboard/src/views/ConfigPage.vue | 5 +- 2 files changed, 37 insertions(+), 32 deletions(-) diff --git a/dashboard/src/components/shared/AstrBotConfig.vue b/dashboard/src/components/shared/AstrBotConfig.vue index 39d5812dc..84d3fadcc 100644 --- a/dashboard/src/components/shared/AstrBotConfig.vue +++ b/dashboard/src/components/shared/AstrBotConfig.vue @@ -1,9 +1,6 @@ diff --git a/dashboard/src/views/ConfigPage.vue b/dashboard/src/views/ConfigPage.vue index 55d6e8e33..37d740a3c 100644 --- a/dashboard/src/views/ConfigPage.vue +++ b/dashboard/src/views/ConfigPage.vue @@ -44,11 +44,8 @@ import config from '@/config';
- + {{ metadata[key]['metadata'][key2]['description'] }} ({{ key2 }}) - - object - Date: Tue, 18 Mar 2025 00:54:51 +0800 Subject: [PATCH 15/63] v3.4.38 --- astrbot/core/config/default.py | 2 +- changelogs/v3.4.38.md | 57 ++++++++++++++++++++++++++++++++++ 2 files changed, 58 insertions(+), 1 deletion(-) create mode 100644 changelogs/v3.4.38.md diff --git a/astrbot/core/config/default.py b/astrbot/core/config/default.py index 280c841d0..20a943d19 100644 --- a/astrbot/core/config/default.py +++ b/astrbot/core/config/default.py @@ -2,7 +2,7 @@ 如需修改配置,请在 `data/cmd_config.json` 中修改或者在管理面板中可视化修改。 """ -VERSION = "3.4.37" +VERSION = "3.4.38" DB_PATH = "data/data_v3.db" # 默认配置 diff --git a/changelogs/v3.4.38.md b/changelogs/v3.4.38.md new file mode 100644 index 000000000..2a3504fcf --- /dev/null +++ b/changelogs/v3.4.38.md @@ -0,0 +1,57 @@ +# What's Changed + +> Special thanks for all contributors and plugin developers and users who love AstrBot. 💖 + +## ✨ 新增的功能 + +1. 支持解析回复消息,支持 LLM 对所引用消息具有感知 #783 +2. 支持 Dify 的文件、图片、视频、音频输出 #819 +3. QQ 下支持嵌套转发(napcat) @zouyonghe +4. 配置页样式重写,更紧凑的 WebUI 配置 + +## 🎈 功能性优化 + +1. 使用系统时间而不是 UTC+8 时间作为默认时间以适应海外用户需求 @roeseth +2. 在对话隔离情况下也可以将整个群聊加入白名单 #746 +3. 在调用插件异常时更完整的报错输出 +4. gewechat 下对已知且没有业务处理的事件类型不显示详细日志 @diudiu62 +5. 优化 WebUI 悬浮文档 @IGCrystal +6. 支持自定义 WebUI、Wecom Webhook Server, QQ Official Webhook Server 的 host #821 +7. Dify 下当只有图片输入时的默认 prompt 防止一些报错 #837 + +## 🐛 修复的 Bug + +1. fishaudio 默认 baseurl 不可用 +2. gewechat 下重复登录后提示设备不存在导致无法重新登陆 @beat4ocean +3. gewechat 下用户本人发消息会触发消息回复 @beat4ocean +4. 钉钉 WebUI 文档不显示 +5. 更新插件后插件热重载不完全、函数工具重复添加 +6. OpenAI TTS API TypeError 报错 #755 +7. EdgeTTS 部分情况下无法使用 @Soulter @需要哦 +8. QQ 官方机器人平台下发送 base64 图片消息段报错 @Soulter @shuiping233 +9. QQ 官方机器人平台下命令参数报错信息无法正常发送 @shuiping233 +10. WebUI 错误地显示未知更新 +11. 部分情况下文件无法上传到 Telegram 群组 #601 +12. 插件管理的插件简介太长导致 “帮助”“操作”图标不显示 #790 +13. LLOnebot 合并消息转发错误 #842 +14. model_config 中自定义的配置项(如温度)类型自动变回 string #854 + +## 🧩 新增的插件 + +1. astrbot_plugin_image_understanding_Janus-Pro - 使用deepseek-ai/Janus-Pro系列模型为本地模型提供的图片理解补充 @xiewoc +2. astrbot_plugin_moyurenpro - 摸鱼人日历,支持自定义时间时区,自定义api,支持立即发送,工作日定时发送。 @quirrel-zh @DuBwTf +3. astrbot_plugin_wechat_manager - 微信关键字好友自动审核、关键字邀请进群。@diudiu62 +4. astrbot_plugin_qwq_filter - qwq 思考过滤工具 @beat4ocean +5. astrbot_plugin_chatsummary - 一个通过拉取历史聊天记录,调用LLM大模型接口实现消息总结功能。@laopanmemz +6. astrBot_PGR_Dialogue - 检测到部分战双角色的名称(或别称)时,有概率发送一条语音文本 @KurisuRee7 +7. astrbot_plugin_bv - 解析群内https://www.bilibili.com/video/BV号/ 的链接并获取视频数据与视频文件,以合并转发方式发送 @haliludaxuanfeng +8. astrbot_plugin_gemini_exp - 让你在AstrBot调用Gemini2.0-flash-exp来生成图片或者p图。Gemini2.0-flash-exp为原生多模态模型,其既是语言模型,也是生图模型,因此能够对图像使用简单的自然语言命令进行处理。@Elen123bot +9. astrbot_plugin_sjzb - 随机生成绝地潜兵2游戏中一组4个战备配置 @tenno1174 +10. astrbot_plugin_picture_manager - 图片管理插件,允许用户通过自定义触发指令从API或直接URL获取图片。@bigshabei +11. astrbot_plugin_bilibiliParse - 解析哔哩哔哩视频,并以图片的形式发送给用户 @7Hello12 +12. astrbot_plugin_sensoji - 这是一个模拟日本浅草寺抽签功能的插件。用户可以通过发送 /抽签 命令随机抽取一个签文,获取运势提示。签文包含吉凶结果(如“大吉”、“凶”等)以及对应的运势描述。 @Shouugou +13. astrbot_plugin_videosummary - 使用 bibigpt 实现视频总结 @kterna +14. astrbot_plugin_InitiativeDialogue - 使 bot 在用户长时间未发送消息时主动与用户对话的插件 @advent259141 +15. astrbot_plugin_emoji - 基于达莉娅综合群娱插件的表情包制作插件,仅保留了@其他群员制作表情包的部分。由桑帛云API提供表情包制作。@KurisuRee7 +16. astrbot_plugin_videos_analysis - 聚合视频分享链接解析(仅测试过napcat) @miaoxutao123 +17. astrbot_plugin_daily_news - 每日 60 秒新闻推送插件 - 自动推送每日热点新闻 @anka-afk \ No newline at end of file From caffb837800460d4071d125b4f9b06572de0ab4e Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 17 Mar 2025 17:09:59 +0000 Subject: [PATCH 16/63] :balloon: pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/astral-sh/ruff-pre-commit: v0.9.10 → v0.11.0](https://github.com/astral-sh/ruff-pre-commit/compare/v0.9.10...v0.11.0) --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 2c38647e7..4dece7145 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -7,7 +7,7 @@ ci: autoupdate_commit_msg: ":balloon: pre-commit autoupdate" repos: - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.9.10 + rev: v0.11.0 hooks: - id: ruff - id: ruff-format From b2c6e12647e92ff740e4a79ab8794a1da3fdf4f6 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 17 Mar 2025 17:10:05 +0000 Subject: [PATCH 17/63] :balloon: auto fixes by pre-commit hooks --- packages/astrbot/main.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/astrbot/main.py b/packages/astrbot/main.py index c2c4dfc32..5eae101ae 100644 --- a/packages/astrbot/main.py +++ b/packages/astrbot/main.py @@ -323,7 +323,9 @@ UID: {user_id} 此 ID 可用于设置管理员。 async def dwl(self, event: AstrMessageEvent, sid: str): """删除白名单。dwl """ try: - self.context.get_config()["platform_settings"]["id_whitelist"].remove(str(sid)) + self.context.get_config()["platform_settings"]["id_whitelist"].remove( + str(sid) + ) self.context.get_config().save_config() event.set_result(MessageEventResult().message("删除白名单成功。")) except ValueError: From f9e0a95c5e196a748d964077a686146743250cb7 Mon Sep 17 00:00:00 2001 From: Soulter <905617992@qq.com> Date: Tue, 18 Mar 2025 22:15:22 +0800 Subject: [PATCH 18/63] =?UTF-8?q?chore:=20=E9=BB=98=E8=AE=A4=E5=9C=B0?= =?UTF-8?q?=E5=9D=80=E6=94=B9=E5=9B=9E=200.0.0.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- astrbot/core/config/default.py | 2 +- astrbot/dashboard/server.py | 7 +++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/astrbot/core/config/default.py b/astrbot/core/config/default.py index 20a943d19..3a2b7f07a 100644 --- a/astrbot/core/config/default.py +++ b/astrbot/core/config/default.py @@ -85,7 +85,7 @@ DEFAULT_CONFIG = { "enable": True, "username": "astrbot", "password": "77b90590a8945a7d36c963981a307dc9", - "host": "127.0.0.1", + "host": "0.0.0.0", "port": 6185, }, "platform": [], diff --git a/astrbot/dashboard/server.py b/astrbot/dashboard/server.py index 072ded4ae..932c2ed4e 100644 --- a/astrbot/dashboard/server.py +++ b/astrbot/dashboard/server.py @@ -122,7 +122,10 @@ class AstrBotDashboard: def run(self): ip_addr = [] port = self.core_lifecycle.astrbot_config["dashboard"].get("port", 6185) - host = self.core_lifecycle.astrbot_config["dashboard"].get("host", "127.0.0.1") + host = self.core_lifecycle.astrbot_config["dashboard"].get("host", "0.0.0.0") + + logger.info(f"正在启动 WebUI, 监听地址: http://{host}:{port}") + if host not in ["localhost", "127.0.0.1"]: try: ip_addr = get_local_ip_addresses() @@ -144,7 +147,7 @@ class AstrBotDashboard: raise Exception(f"端口 {port} 已被占用") - display = f"\n ✨✨✨\n AstrBot v{VERSION} 管理面板已启动,可访问\n\n" + display = f"\n ✨✨✨\n AstrBot v{VERSION} WebUI 已启动,可访问\n\n" display += f" ➜ 本地: http://localhost:{port}\n" for ip in ip_addr: display += f" ➜ 网络: http://{ip}:{port}\n" From b6fa7f62dc80d6a1281bf012d27d7bb9f675e2ac Mon Sep 17 00:00:00 2001 From: Soulter <905617992@qq.com> Date: Tue, 18 Mar 2025 22:18:01 +0800 Subject: [PATCH 19/63] =?UTF-8?q?chore:=20=E6=B7=BB=E5=8A=A0=E5=AE=89?= =?UTF-8?q?=E5=85=A8=E6=8F=90=E7=A4=BA=E4=BF=A1=E6=81=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- astrbot/dashboard/server.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/astrbot/dashboard/server.py b/astrbot/dashboard/server.py index 932c2ed4e..70809ab3a 100644 --- a/astrbot/dashboard/server.py +++ b/astrbot/dashboard/server.py @@ -126,6 +126,9 @@ class AstrBotDashboard: logger.info(f"正在启动 WebUI, 监听地址: http://{host}:{port}") + if host == "0.0.0.0": + logger.info("提示: WebUI 将监听所有网络接口,请注意安全。(可在 data/cmd_config.json 中配置 dashboard.host 以修改 host)") + if host not in ["localhost", "127.0.0.1"]: try: ip_addr = get_local_ip_addresses() From 82dc37de5091e2a4619000a1be5b7685e68bea01 Mon Sep 17 00:00:00 2001 From: Soulter <905617992@qq.com> Date: Tue, 18 Mar 2025 22:21:47 +0800 Subject: [PATCH 20/63] style: format codes --- astrbot/dashboard/server.py | 4 +++- packages/astrbot/main.py | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/astrbot/dashboard/server.py b/astrbot/dashboard/server.py index 70809ab3a..6fc0651fa 100644 --- a/astrbot/dashboard/server.py +++ b/astrbot/dashboard/server.py @@ -127,7 +127,9 @@ class AstrBotDashboard: logger.info(f"正在启动 WebUI, 监听地址: http://{host}:{port}") if host == "0.0.0.0": - logger.info("提示: WebUI 将监听所有网络接口,请注意安全。(可在 data/cmd_config.json 中配置 dashboard.host 以修改 host)") + logger.info( + "提示: WebUI 将监听所有网络接口,请注意安全。(可在 data/cmd_config.json 中配置 dashboard.host 以修改 host)" + ) if host not in ["localhost", "127.0.0.1"]: try: diff --git a/packages/astrbot/main.py b/packages/astrbot/main.py index c2c4dfc32..5eae101ae 100644 --- a/packages/astrbot/main.py +++ b/packages/astrbot/main.py @@ -323,7 +323,9 @@ UID: {user_id} 此 ID 可用于设置管理员。 async def dwl(self, event: AstrMessageEvent, sid: str): """删除白名单。dwl """ try: - self.context.get_config()["platform_settings"]["id_whitelist"].remove(str(sid)) + self.context.get_config()["platform_settings"]["id_whitelist"].remove( + str(sid) + ) self.context.get_config().save_config() event.set_result(MessageEventResult().message("删除白名单成功。")) except ValueError: From de75386944657be2d486ae79afdb69b01144667e Mon Sep 17 00:00:00 2001 From: Soulter <905617992@qq.com> Date: Tue, 18 Mar 2025 22:41:33 +0800 Subject: [PATCH 21/63] =?UTF-8?q?=F0=9F=8E=88=20perf:=20=E7=99=BB=E5=BD=95?= =?UTF-8?q?=E5=90=8E=E6=A3=80=E6=9F=A5=E9=BB=98=E8=AE=A4=E5=AF=86=E7=A0=81?= =?UTF-8?q?=E5=92=8C=E5=BC=B9=E5=87=BA=E4=BF=AE=E6=94=B9=E8=AD=A6=E5=91=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- astrbot/dashboard/routes/auth.py | 14 +++++++++++++- .../full/vertical-header/VerticalHeader.vue | 14 ++++++++++++++ dashboard/src/stores/auth.ts | 1 + 3 files changed, 28 insertions(+), 1 deletion(-) diff --git a/astrbot/dashboard/routes/auth.py b/astrbot/dashboard/routes/auth.py index 2e525a3ca..34ba8fd3f 100644 --- a/astrbot/dashboard/routes/auth.py +++ b/astrbot/dashboard/routes/auth.py @@ -3,6 +3,7 @@ import datetime from .route import Route, Response, RouteContext from quart import request from astrbot.core import WEBUI_SK +from astrbot import logger class AuthRoute(Route): @@ -19,9 +20,20 @@ class AuthRoute(Route): password = self.config["dashboard"]["password"] post_data = await request.json if post_data["username"] == username and post_data["password"] == password: + change_pwd_hint = False + if username == "astrbot" and password == "77b90590a8945a7d36c963981a307dc9": + change_pwd_hint = True + logger.warning("为了保证安全,请尽快修改默认密码。") + return ( Response() - .ok({"token": self.generate_jwt(username), "username": username}) + .ok( + { + "token": self.generate_jwt(username), + "username": username, + "change_pwd_hint": change_pwd_hint, + } + ) .__dict__ ) else: diff --git a/dashboard/src/layouts/full/vertical-header/VerticalHeader.vue b/dashboard/src/layouts/full/vertical-header/VerticalHeader.vue index 68e6d38ee..3d08c9cd6 100644 --- a/dashboard/src/layouts/full/vertical-header/VerticalHeader.vue +++ b/dashboard/src/layouts/full/vertical-header/VerticalHeader.vue @@ -8,6 +8,7 @@ import { useCommonStore } from '@/stores/common'; const customizer = useCustomizerStore(); let dialog = ref(false); +let accountWarning = ref(false) let updateStatusDialog = ref(false); let password = ref(''); let newPassword = ref(''); @@ -177,6 +178,14 @@ checkUpdate(); const commonStore = useCommonStore(); commonStore.createWebSocket(); commonStore.getStartTime(); + + +if (localStorage.getItem('change_pwd_hint') != null && localStorage.getItem('change_pwd_hint') == 'true') { + dialog.value = true; + accountWarning.value = true; + localStorage.removeItem('change_pwd_hint'); +} +