diff --git a/README.md b/README.md
index 07237bfa4..4e8c1a829 100644
--- a/README.md
+++ b/README.md
@@ -1,7 +1,6 @@
-
-
+
diff --git a/astrbot/core/pipeline/waking_check/stage.py b/astrbot/core/pipeline/waking_check/stage.py
index 7c91a2fcf..9b8a61ccf 100644
--- a/astrbot/core/pipeline/waking_check/stage.py
+++ b/astrbot/core/pipeline/waking_check/stage.py
@@ -4,7 +4,7 @@ from astrbot import logger
from typing import Union, AsyncGenerator
from astrbot.core.platform.astr_message_event import AstrMessageEvent
from astrbot.core.message.message_event_result import MessageEventResult, MessageChain
-from astrbot.core.message.components import At, AtAll
+from astrbot.core.message.components import At, AtAll, Reply
from astrbot.core.star.star_handler import star_handlers_registry, EventType
from astrbot.core.star.star import star_map
from astrbot.core.star.filter.permission import PermissionTypeFilter
@@ -81,11 +81,19 @@ class WakingCheckStage(Stage):
event.message_str = event.message_str[len(wake_prefix) :].strip()
break
if not is_wake:
- # 检查是否有 at 消息
+ # 检查是否有at消息 / at全体成员消息 / 引用了bot的消息
for message in messages:
- if (isinstance(message, At) and (
- str(message.qq) == str(event.get_self_id())
- )) or (isinstance(message, AtAll) and not self.ignore_at_all):
+ if (
+ (
+ isinstance(message, At)
+ and (str(message.qq) == str(event.get_self_id()))
+ )
+ or (isinstance(message, AtAll) and not self.ignore_at_all)
+ or (
+ isinstance(message, Reply)
+ and str(message.sender_id) == str(event.get_self_id())
+ )
+ ):
is_wake = True
event.is_wake = True
wake_prefix = ""
diff --git a/astrbot/core/platform/sources/dingtalk/dingtalk_event.py b/astrbot/core/platform/sources/dingtalk/dingtalk_event.py
index 4834032f5..c2188dc36 100644
--- a/astrbot/core/platform/sources/dingtalk/dingtalk_event.py
+++ b/astrbot/core/platform/sources/dingtalk/dingtalk_event.py
@@ -32,31 +32,31 @@ class DingtalkMessageEvent(AstrMessageEvent):
)
elif isinstance(segment, Comp.Image):
markdown_str = ""
- if segment.file and segment.file.startswith("file:///"):
- logger.warning(
- "dingtalk only support url image, not: " + segment.file
- )
- continue
- elif segment.file and segment.file.startswith("http"):
- markdown_str += f"\n\n"
- elif segment.file and segment.file.startswith("base64://"):
- logger.warning("dingtalk only support url image, not base64")
- continue
- else:
- logger.warning(
- "dingtalk only support url image, not: " + segment.file
- )
- continue
- ret = await asyncio.get_event_loop().run_in_executor(
- None,
- client.reply_markdown,
- "😄",
- markdown_str,
- self.message_obj.raw_message,
- )
- logger.debug(f"send image: {ret}")
+ try:
+ if not segment.file:
+ logger.warning("钉钉图片 segment 缺少 file 字段,跳过")
+ continue
+ if segment.file.startswith(("http://", "https://")):
+ image_url = segment.file
+ else:
+ image_url = await segment.register_to_file_service()
+ markdown_str = f"\n\n"
+
+ ret = await asyncio.get_event_loop().run_in_executor(
+ None,
+ client.reply_markdown,
+ "😄",
+ markdown_str,
+ self.message_obj.raw_message,
+ )
+ logger.debug(f"send image: {ret}")
+
+ except Exception as e:
+ logger.error(f"钉钉图片处理失败: {e}")
+ logger.warning(f"跳过图片发送: {image_path}")
+ continue
async def send(self, message: MessageChain):
await self.send_with_client(self.client, message)
await super().send(message)
diff --git a/astrbot/core/platform/sources/wechatpadpro/wechatpadpro_adapter.py b/astrbot/core/platform/sources/wechatpadpro/wechatpadpro_adapter.py
index 3cddeccce..58e3c9b19 100644
--- a/astrbot/core/platform/sources/wechatpadpro/wechatpadpro_adapter.py
+++ b/astrbot/core/platform/sources/wechatpadpro/wechatpadpro_adapter.py
@@ -2,6 +2,7 @@ import asyncio
import base64
import json
import os
+import traceback
import time
from typing import Optional
@@ -158,7 +159,6 @@ class WeChatPadProAdapter(Platform):
os.makedirs(data_dir, exist_ok=True)
with open(self.credentials_file, "w") as f:
json.dump(credentials, f)
- logger.info("成功保存 WeChatPadPro 凭据。")
except Exception as e:
logger.error(f"保存 WeChatPadPro 凭据失败: {e}")
@@ -166,6 +166,8 @@ class WeChatPadProAdapter(Platform):
"""
检查 WeChatPadPro 设备是否在线。
"""
+ if not self.auth_key:
+ return False
url = f"{self.base_url}/login/GetLoginStatus"
params = {"key": self.auth_key}
@@ -184,12 +186,16 @@ class WeChatPadProAdapter(Platform):
logger.info("WeChatPadPro 设备不在线。")
return False
else:
- logger.error(f"未知的在线状态: {login_state:}")
+ logger.error(f"未知的在线状态: {response_data}")
return False
# Code == 300 为微信退出状态。
elif response.status == 200 and response_data.get("Code") == 300:
logger.info("WeChatPadPro 设备已退出。")
return False
+ elif response.status == 200 and response_data.get("Code") == -2:
+ # 该链接不存在
+ self.auth_key = None
+ return False
else:
logger.error(
f"检查在线状态失败: {response.status}, {response_data}"
@@ -201,6 +207,7 @@ class WeChatPadProAdapter(Platform):
return False
except Exception as e:
logger.error(f"检查在线状态时发生错误: {e}")
+ logger.error(traceback.format_exc())
return False
async def generate_auth_key(self):
@@ -224,7 +231,7 @@ class WeChatPadProAdapter(Platform):
and len(response_data["Data"]) > 0
):
self.auth_key = response_data["Data"][0]
- logger.info("成功获取授权码")
+ logger.info(f"成功获取授权码 {self.auth_key[:8]}...")
else:
logger.error(
f"生成授权码成功但未找到授权码: {response_data}"
@@ -250,7 +257,6 @@ class WeChatPadProAdapter(Platform):
try:
async with session.post(url, params=params, json=payload) as response:
response_data = await response.json()
- # 修正成功判断条件和数据提取路径
if response.status == 200 and response_data.get("Code") == 200:
# 二维码地址在 Data.QrCodeUrl 字段中
if response_data.get("Data") and response_data["Data"].get(
@@ -262,6 +268,13 @@ class WeChatPadProAdapter(Platform):
f"获取登录二维码成功但未找到二维码地址: {response_data}"
)
return None
+ elif "该 key 无效" in response_data.get("Text"):
+ logger.error(
+ "授权码无效,已经清除。请重新启动 AstrBot 或者本消息适配器。原因也可能是 WeChatPadPro 的 MySQL 服务没有启动成功,请检查 WeChatPadPro 服务的日志。"
+ )
+ self.auth_key = None
+ self.save_credentials()
+ return None
else:
logger.error(
f"获取登录二维码失败: {response.status}, {response_data}"
@@ -354,7 +367,7 @@ class WeChatPadProAdapter(Platform):
while True:
try:
async with websockets.connect(ws_url) as websocket:
- logger.info("WebSocket 连接成功。")
+ logger.debug("WebSocket 连接成功。")
# 设置空闲超时重连
wait_time = (
self.active_message_poll_interval
@@ -369,7 +382,7 @@ class WeChatPadProAdapter(Platform):
# logger.debug(message) # 不显示原始消息内容
asyncio.create_task(self.handle_websocket_message(message))
except asyncio.TimeoutError:
- logger.warning(f"WebSocket 连接空闲超过 {wait_time} s")
+ logger.debug(f"WebSocket 连接空闲超过 {wait_time} s")
break
except websockets.exceptions.ConnectionClosedOK:
logger.info("WebSocket 连接正常关闭。")
@@ -492,7 +505,7 @@ class WeChatPadProAdapter(Platform):
# 对于群聊,session_id 可以是群聊 ID 或发送者 ID + 群聊 ID (如果 unique_session 为 True)
if self.unique_session:
- abm.session_id = f"{from_user_name}_{to_user_name}"
+ abm.session_id = f"{from_user_name}#{abm.sender.user_id}"
else:
abm.session_id = from_user_name
@@ -631,7 +644,11 @@ class WeChatPadProAdapter(Platform):
# wechatpadpro 的格式: wxid
# gewechat 的格式:
msg_source = raw_message.get("msg_source", "")
- if f"{abm.self_id}" in msg_source or f"{abm.self_id}," in msg_source or f",{abm.self_id}" in msg_source:
+ if (
+ f"{abm.self_id}" in msg_source
+ or f"{abm.self_id}," in msg_source
+ or f",{abm.self_id}" in msg_source
+ ):
at_me = True
# 也检查 push_content 中是否有@提示
@@ -641,19 +658,28 @@ class WeChatPadProAdapter(Platform):
if at_me:
# 被@了,在消息开头插入At组件(参考gewechat的做法)
- bot_nickname = await self._get_group_member_nickname(abm.group_id, abm.self_id)
- abm.message.insert(0, At(qq=abm.self_id, name=bot_nickname or abm.self_id))
+ bot_nickname = await self._get_group_member_nickname(
+ abm.group_id, abm.self_id
+ )
+ abm.message.insert(
+ 0, At(qq=abm.self_id, name=bot_nickname or abm.self_id)
+ )
# 只有当消息内容不仅仅是@时才添加Plain组件
if "\u2005" in message_content:
# 检查@之后是否还有其他内容
parts = message_content.split("\u2005")
- if len(parts) > 1 and any(part.strip() for part in parts[1:]):
+ if len(parts) > 1 and any(
+ part.strip() for part in parts[1:]
+ ):
abm.message.append(Plain(message_content))
else:
# 检查是否只包含@机器人
is_pure_at = False
- if bot_nickname and message_content.strip() == f"@{bot_nickname}":
+ if (
+ bot_nickname
+ and message_content.strip() == f"@{bot_nickname}"
+ ):
is_pure_at = True
if not is_pure_at:
abm.message.append(Plain(message_content))
@@ -806,7 +832,10 @@ class WeChatPadProAdapter(Platform):
# 根据 session_id 判断消息类型
if "@chatroom" in session.session_id:
dummy_message_obj.type = MessageType.GROUP_MESSAGE
- dummy_message_obj.group_id = session.session_id
+ if "#" in session.session_id:
+ dummy_message_obj.group_id = session.session_id.split("#")[0]
+ else:
+ dummy_message_obj.group_id = session.session_id
dummy_message_obj.sender = MessageMember(user_id="", nickname="")
else:
dummy_message_obj.type = MessageType.FRIEND_MESSAGE
diff --git a/astrbot/core/platform/sources/wechatpadpro/wechatpadpro_message_event.py b/astrbot/core/platform/sources/wechatpadpro/wechatpadpro_message_event.py
index ab836ad28..3bb753dd4 100644
--- a/astrbot/core/platform/sources/wechatpadpro/wechatpadpro_message_event.py
+++ b/astrbot/core/platform/sources/wechatpadpro/wechatpadpro_message_event.py
@@ -81,12 +81,16 @@ class WeChatPadProMessageEvent(AstrMessageEvent):
# logger.info(f"已添加 @ 信息: {message_text}")
else:
message_text = text
+ if self.get_group_id() and "#" in self.session_id:
+ session_id = self.session_id.split("#")[0]
+ else:
+ session_id = self.session_id
payload = {
"MsgItem": [
{
"MsgType": 1,
"TextContent": message_text,
- "ToUserName": self.session_id,
+ "ToUserName": session_id,
}
]
}
diff --git a/astrbot/core/star/star_manager.py b/astrbot/core/star/star_manager.py
index 13c93f226..3dd4cd1cf 100644
--- a/astrbot/core/star/star_manager.py
+++ b/astrbot/core/star/star_manager.py
@@ -652,7 +652,11 @@ class PluginManager:
plugin_info = None
if plugin:
- plugin_info = {"repo": plugin.repo, "readme": cleaned_content}
+ plugin_info = {
+ "repo": plugin.repo,
+ "readme": cleaned_content,
+ "name": plugin.name,
+ }
return plugin_info
@@ -847,6 +851,10 @@ class PluginManager:
plugin_info = None
if plugin:
- plugin_info = {"repo": plugin.repo, "readme": readme_content}
+ plugin_info = {
+ "repo": plugin.repo,
+ "readme": readme_content,
+ "name": plugin.name,
+ }
return plugin_info
diff --git a/dashboard/src/layouts/full/vertical-sidebar/sidebarItem.ts b/dashboard/src/layouts/full/vertical-sidebar/sidebarItem.ts
index 0a173844d..0c945fe22 100644
--- a/dashboard/src/layouts/full/vertical-sidebar/sidebarItem.ts
+++ b/dashboard/src/layouts/full/vertical-sidebar/sidebarItem.ts
@@ -41,7 +41,7 @@ const sidebarItem: menu[] = [
to: '/config',
},
{
- title: '插件管理',
+ title: '插件',
icon: 'mdi-puzzle',
to: '/extension'
},
@@ -51,7 +51,7 @@ const sidebarItem: menu[] = [
to: '/chat'
},
{
- title: '对话数据库',
+ title: '对话数据',
icon: 'mdi-database',
to: '/conversation'
},
diff --git a/dashboard/src/router/MainRoutes.ts b/dashboard/src/router/MainRoutes.ts
index cadb90860..e4a0fcb9c 100644
--- a/dashboard/src/router/MainRoutes.ts
+++ b/dashboard/src/router/MainRoutes.ts
@@ -19,7 +19,7 @@ const MainRoutes = {
{
name: 'ExtensionMarketplace',
path: '/extension-marketplace',
- component: () => import('@/views/ExtensionMarketplace.vue')
+ component: () => import('@/views/ExtensionPage.vue')
},
{
name: 'Platforms',
diff --git a/dashboard/src/views/ExtensionMarketplace.vue b/dashboard/src/views/ExtensionMarketplace.vue
deleted file mode 100644
index 764b8f830..000000000
--- a/dashboard/src/views/ExtensionMarketplace.vue
+++ /dev/null
@@ -1,704 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
✨ 插件市场
-
- mdi-help
-
-
- 如无法显示,请单击此按钮跳转至插件市场,复制想安装插件对应的
- repo链接然后点击右下角 + 号安装,或打开链接下载压缩包安装。
- 如果因为网络问题安装失败,点击设置页选择 GitHub 加速地址。或前往仓库下载压缩包然后本地上传。
-
-
-
-
-
- {{ isListView ? 'mdi-view-grid' : 'mdi-view-list' }}
-
-
-
-
-
-
-
-
-
-
- 每个插件都是作者无偿提供的的劳动成果。如果您喜欢某个插件,请 Star!
-
-
🥳 推荐
-
-
-
-
-
-
-
-
-
-
-
-
-
📦 全部插件
-
-
-
-
-
-
-
-
-
-
- {{ item.desc }}
-
-
-
-
-
-
-
- {{ item.stars }}
-
-
-
- {{ new Date(item.updated_at).toLocaleString() }}
-
-
-
- -
-
- {{ tag }}
-
-
-
- mdi-download
- mdi-check
- mdi-help
-
-
-
-
-
-
📦 全部插件
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- 插件开发文档 |
- 提交插件仓库
-
-
-
-
-
-
-
-
-
-
-
- 安装插件
-
-
-
-
- 从 GitHub 上在线下载
-
- 请输入合法的 GitHub 仓库链接,当前仅支持
- GitHub。如:https://github.com/Soulter/astrbot_plugin_aiocqhttp
-
-
-
-
- 从本机上传 .zip 压缩包
-
- 请保证插件文件存在压缩包根目录中的第一个文件夹中(即类似于从 GitHub 仓库页上下载的 Zip 压缩包的格式)。
-
-
-
-
-
-
- {{ status }}
-
-
-
-
- 关闭
-
-
- 安装
-
-
-
-
-
-
- {{ snack_message }}
-
-
-
-
-
-
-
-
- 插件说明文档
-
- mdi-close
-
-
-
-
-
- 在GitHub中查看文档
-
-
-
-
-
mdi-alert-circle-outline
-
{{ readmeDialog.error }}
-
-
-
mdi-file-question-outline
-
该插件未提供文档链接或GitHub仓库地址。
请查看插件市场或联系插件作者获取更多信息。
-
-
-
-
-
-
- 关闭
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/dashboard/src/views/ExtensionPage.vue b/dashboard/src/views/ExtensionPage.vue
index 5c9649246..681f66a59 100644
--- a/dashboard/src/views/ExtensionPage.vue
+++ b/dashboard/src/views/ExtensionPage.vue
@@ -1,16 +1,16 @@
@@ -417,205 +555,300 @@ onMounted(async () => {
- 已安装的插件
+ AstrBot 插件
- 管理已经安装的所有插件
+ 管理、安装 AstrBot 插件
-
-
-
-
-
- mdi-view-grid
-
-
- mdi-view-list
-
-
+
+
-
- {{ showReserved ? '隐藏系统插件' : '显示系统插件' }}
-
-
-
- 平台命令配置
-
-
-
-
-
-
-
-
+
+
+
+ mdi-puzzle
+ 已安装插件
+
+
+ mdi-store
+ 插件市场
+
+
+
+
-
-
-
- mdi-alert-circle
-
+
+
+
+
+
+
+
+
+
+ mdi-view-grid
-
-
-
-
- mdi-alert-circle
- 错误信息
-
-
- {{ extension_data.message }}
- 详情请检查控制台
-
-
-
- 关闭
-
-
-
-
-
-
+
+ mdi-view-list
+
+
-
-
-
-
-
-
-
-
- 加载中...
-
+
+ {{ showReserved ? '隐藏系统插件' : '显示系统插件' }}
+
+
+
+ 平台命令配置
+
+
+
+
+
+
+
+ mdi-alert-circle
+
+
+
+
+
+ mdi-alert-circle
+ 错误信息
+
+
+ {{ extension_data.message }}
+ 详情请检查控制台
+
+
+
+ 关闭
+
+
+
+
+
+
-
-
-
-
{{ item.name }}
-
-
系统
+
+
+
+
+
+
+
+
+ 加载中...
+
+
+
+
+
+
+
{{ item.name }}
+
+ 系统
+
-
-
+
-
- {{ item.desc }}
-
+
+ {{ item.desc }}
+
-
-
- {{ item.version }}
- mdi-alert
-
- 有新版本: {{ item.online_version }}
-
-
-
+
+
+ {{ item.version }}
+ mdi-alert
+
+ 有新版本: {{ item.online_version }}
+
+
+
-
- {{ item.author }}
-
+
+ {{ item.author }}
+
-
-
- {{ item.activated ? '启用' : '禁用' }}
-
-
+
+
+ {{ item.activated ? '启用' : '禁用' }}
+
+
-
-
-
-
- mdi-play
- 点击启用
-
-
- mdi-pause
- 点击禁用
-
+
+
+
+
+ mdi-play
+ 点击启用
+
+
+ mdi-pause
+ 点击禁用
+
-
- mdi-refresh
- 重载
-
+
+ mdi-refresh
+ 重载
+
-
- mdi-cog
- 配置
-
+
+ mdi-cog
+ 配置
+
-
- mdi-information
- 行为
-
+
+ mdi-information
+ 行为
+
-
- mdi-book-open-page-variant
- 文档
-
+
+ mdi-book-open-page-variant
+ 文档
+
-
- mdi-update
- 更新
-
+
+ mdi-update
+ 更新
+
-
- mdi-delete
- 卸载
-
-
+
+ mdi-delete
+ 卸载
+
+
-
-
+
+
-
-
-
mdi-puzzle-outline
-
暂无插件
-
尝试安装插件或者显示系统插件
-
-
-
-
-
+
+
+
mdi-puzzle-outline
+
暂无插件
+
尝试安装插件或者显示系统插件
+
+
+
+
+
-
-
-
-
- mdi-puzzle-outline
- 暂无插件
- 尝试安装插件或者显示系统插件
-
-
+
+
+
+
+ mdi-puzzle-outline
+ 暂无插件
+ 尝试安装插件或者显示系统插件
+
+
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
🥳 推荐
+
+
+
-
+
+
+
+
📦 全部插件
+
+
+
+
+
+
+
+
+
+
+
+ {{ item.desc }}
+
+
+
+
+
+
+ {{ item.stars }}
+
+
+ {{ new Date(item.updated_at).toLocaleString() }}
+
+
+ -
+
+ {{ tag }}
+
+
+
+ mdi-download
+ mdi-check
+ mdi-help
+
+
+
+
+
@@ -625,6 +858,11 @@ onMounted(async () => {
+
+
+ 插件开发文档 |
+ 提交插件仓库
+
@@ -796,8 +1034,6 @@ onMounted(async () => {
{{ snack_message }}
-
-