Compare commits
11 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 20d6ff4620 | |||
| a2b61e2ab8 | |||
| c6289d8f75 | |||
| 567390e27c | |||
| 0c0f8bf484 | |||
| ae0a9cb591 | |||
| 3f4d7255a0 | |||
| b8d2499475 | |||
| 8cb26d886f | |||
| 3ca8dd204f | |||
| 3476afce41 |
@@ -154,7 +154,8 @@ paru -S astrbot-git
|
||||
|
||||
**官方维护**
|
||||
|
||||
- QQ (官方平台 & OneBot)
|
||||
- QQ
|
||||
- OneBot v11 协议实现
|
||||
- Telegram
|
||||
- 企微应用 & 企微智能机器人
|
||||
- 微信客服 & 微信公众号
|
||||
@@ -162,10 +163,10 @@ paru -S astrbot-git
|
||||
- 钉钉
|
||||
- Slack
|
||||
- Discord
|
||||
- LINE
|
||||
- Satori
|
||||
- Misskey
|
||||
- Whatsapp (将支持)
|
||||
- LINE (将支持)
|
||||
|
||||
**社区维护**
|
||||
|
||||
@@ -185,6 +186,7 @@ paru -S astrbot-git
|
||||
- DeepSeek
|
||||
- Ollama (本地部署)
|
||||
- LM Studio (本地部署)
|
||||
- [AIHubMix](https://aihubmix.com/?aff=4bfH)
|
||||
- [优云智算](https://www.compshare.cn/?ytag=GPU_YY-gh_astrbot&referral_code=FV7DcGowN4hB5UuXKgpE74)
|
||||
- [302.AI](https://share.302.ai/rr1M3l)
|
||||
- [小马算力](https://www.tokenpony.cn/3YPyf)
|
||||
|
||||
+1
-1
@@ -172,8 +172,8 @@ For desktop build steps (Electron packaging, `pnpm` workflow), see [`desktop/REA
|
||||
- Discord
|
||||
- Satori
|
||||
- Misskey
|
||||
- LINE
|
||||
- WhatsApp (Coming Soon)
|
||||
- LINE (Coming Soon)
|
||||
|
||||
**Community Maintained**
|
||||
|
||||
|
||||
+1
-1
@@ -168,8 +168,8 @@ paru -S astrbot-git
|
||||
- Discord
|
||||
- Satori
|
||||
- Misskey
|
||||
- LINE
|
||||
- WhatsApp (Bientôt disponible)
|
||||
- LINE (Bientôt disponible)
|
||||
|
||||
**Maintenues par la communauté**
|
||||
|
||||
|
||||
+1
-1
@@ -168,8 +168,8 @@ paru -S astrbot-git
|
||||
- Discord
|
||||
- Satori
|
||||
- Misskey
|
||||
- LINE
|
||||
- WhatsApp (近日対応予定)
|
||||
- LINE (近日対応予定)
|
||||
|
||||
**コミュニティメンテナンス**
|
||||
|
||||
|
||||
+2
-1
@@ -158,8 +158,9 @@ paru -S astrbot-git
|
||||
- Discord
|
||||
- Satori
|
||||
- Misskey
|
||||
- LINE
|
||||
- WhatsApp (Скоро)
|
||||
- LINE (Скоро)
|
||||
|
||||
|
||||
**Поддерживаемые сообществом**
|
||||
|
||||
|
||||
+2
-1
@@ -158,8 +158,9 @@ paru -S astrbot-git
|
||||
- Discord
|
||||
- Satori
|
||||
- Misskey
|
||||
- LINE
|
||||
- Whatsapp(即將支援)
|
||||
- LINE(即將支援)
|
||||
|
||||
|
||||
**社群維護**
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@ from astrbot.core.star.register import (
|
||||
register_on_llm_tool_respond as on_llm_tool_respond,
|
||||
)
|
||||
from astrbot.core.star.register import register_on_platform_loaded as on_platform_loaded
|
||||
from astrbot.core.star.register import register_on_plugin_error as on_plugin_error
|
||||
from astrbot.core.star.register import register_on_using_llm_tool as on_using_llm_tool
|
||||
from astrbot.core.star.register import (
|
||||
register_on_waiting_llm_request as on_waiting_llm_request,
|
||||
@@ -52,6 +53,7 @@ __all__ = [
|
||||
"on_decorating_result",
|
||||
"on_llm_request",
|
||||
"on_llm_response",
|
||||
"on_plugin_error",
|
||||
"on_platform_loaded",
|
||||
"on_waiting_llm_request",
|
||||
"permission_type",
|
||||
|
||||
@@ -1 +1 @@
|
||||
__version__ = "4.17.4"
|
||||
__version__ = "4.17.5"
|
||||
|
||||
@@ -357,6 +357,7 @@ class ToolLoopAgentRunner(BaseAgentRunner[TContext]):
|
||||
),
|
||||
),
|
||||
)
|
||||
return
|
||||
|
||||
if not llm_resp.tools_call_name:
|
||||
# 如果没有工具调用,转换到完成状态
|
||||
|
||||
@@ -5,7 +5,7 @@ from typing import Any, TypedDict
|
||||
|
||||
from astrbot.core.utils.astrbot_path import get_astrbot_data_path
|
||||
|
||||
VERSION = "4.17.4"
|
||||
VERSION = "4.17.5"
|
||||
DB_PATH = os.path.join(get_astrbot_data_path(), "data_v4.db")
|
||||
|
||||
WEBHOOK_SUPPORTED_PLATFORMS = [
|
||||
@@ -1029,6 +1029,18 @@ CONFIG_METADATA_2 = {
|
||||
"proxy": "",
|
||||
"custom_headers": {},
|
||||
},
|
||||
"AIHubMix": {
|
||||
"id": "aihubmix",
|
||||
"provider": "aihubmix",
|
||||
"type": "aihubmix_chat_completion",
|
||||
"provider_type": "chat_completion",
|
||||
"enable": True,
|
||||
"key": [],
|
||||
"timeout": 120,
|
||||
"api_base": "https://aihubmix.com/v1",
|
||||
"proxy": "",
|
||||
"custom_headers": {},
|
||||
},
|
||||
"NVIDIA": {
|
||||
"id": "nvidia",
|
||||
"provider": "nvidia",
|
||||
|
||||
@@ -119,6 +119,8 @@ class Record(BaseMessageComponent):
|
||||
cache: bool | None = True
|
||||
proxy: bool | None = True
|
||||
timeout: int | None = 0
|
||||
# Original text content (e.g. TTS source text), used as caption in fallback scenarios
|
||||
text: str | None = None
|
||||
# 额外
|
||||
path: str | None
|
||||
|
||||
|
||||
@@ -8,9 +8,9 @@ from astrbot.core import logger
|
||||
from astrbot.core.message.message_event_result import MessageEventResult
|
||||
from astrbot.core.platform.astr_message_event import AstrMessageEvent
|
||||
from astrbot.core.star.star import star_map
|
||||
from astrbot.core.star.star_handler import StarHandlerMetadata
|
||||
from astrbot.core.star.star_handler import EventType, StarHandlerMetadata
|
||||
|
||||
from ...context import PipelineContext, call_handler
|
||||
from ...context import PipelineContext, call_event_hook, call_handler
|
||||
from ..stage import Stage
|
||||
|
||||
|
||||
@@ -48,10 +48,20 @@ class StarRequestSubStage(Stage):
|
||||
yield ret
|
||||
event.clear_result() # 清除上一个 handler 的结果
|
||||
except Exception as e:
|
||||
logger.error(traceback.format_exc())
|
||||
traceback_text = traceback.format_exc()
|
||||
logger.error(traceback_text)
|
||||
logger.error(f"Star {handler.handler_full_name} handle error: {e}")
|
||||
|
||||
if event.is_at_or_wake_command:
|
||||
await call_event_hook(
|
||||
event,
|
||||
EventType.OnPluginErrorEvent,
|
||||
md.name,
|
||||
handler.handler_name,
|
||||
e,
|
||||
traceback_text,
|
||||
)
|
||||
|
||||
if not event.is_stopped() and event.is_at_or_wake_command:
|
||||
ret = f":(\n\n在调用插件 {md.name} 的处理函数 {handler.handler_name} 时出现异常:{e}"
|
||||
event.set_result(MessageEventResult().message(ret))
|
||||
yield
|
||||
|
||||
@@ -315,6 +315,7 @@ class ResultDecorateStage(Stage):
|
||||
Record(
|
||||
file=url or audio_path,
|
||||
url=url or audio_path,
|
||||
text=comp.text,
|
||||
),
|
||||
)
|
||||
if dual_output:
|
||||
|
||||
@@ -7,13 +7,14 @@ from typing import cast
|
||||
|
||||
import aiofiles
|
||||
import botpy
|
||||
import botpy.errors
|
||||
import botpy.message
|
||||
import botpy.types
|
||||
import botpy.types.message
|
||||
from botpy import Client
|
||||
from botpy.http import Route
|
||||
from botpy.types import message
|
||||
from botpy.types.message import Media
|
||||
from botpy.types.message import MarkdownPayload, Media
|
||||
|
||||
from astrbot.api import logger
|
||||
from astrbot.api.event import AstrMessageEvent, MessageChain
|
||||
@@ -25,6 +26,8 @@ from astrbot.core.utils.tencent_record_helper import wav_to_tencent_silk
|
||||
|
||||
|
||||
class QQOfficialMessageEvent(AstrMessageEvent):
|
||||
MARKDOWN_NOT_ALLOWED_ERROR = "不允许发送原生 markdown"
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
message_str: str,
|
||||
@@ -114,7 +117,9 @@ class QQOfficialMessageEvent(AstrMessageEvent):
|
||||
return None
|
||||
|
||||
payload: dict = {
|
||||
"content": plain_text,
|
||||
# "content": plain_text,
|
||||
"markdown": MarkdownPayload(content=plain_text) if plain_text else None,
|
||||
"msg_type": 2,
|
||||
"msg_id": self.message_obj.message_id,
|
||||
}
|
||||
|
||||
@@ -145,9 +150,13 @@ class QQOfficialMessageEvent(AstrMessageEvent):
|
||||
)
|
||||
payload["media"] = media
|
||||
payload["msg_type"] = 7
|
||||
ret = await self.bot.api.post_group_message(
|
||||
group_openid=source.group_openid,
|
||||
**payload,
|
||||
ret = await self._send_with_markdown_fallback(
|
||||
send_func=lambda retry_payload: self.bot.api.post_group_message(
|
||||
group_openid=source.group_openid, # type: ignore
|
||||
**retry_payload,
|
||||
),
|
||||
payload=payload,
|
||||
plain_text=plain_text,
|
||||
)
|
||||
|
||||
case botpy.message.C2CMessage():
|
||||
@@ -168,30 +177,49 @@ class QQOfficialMessageEvent(AstrMessageEvent):
|
||||
payload["media"] = media
|
||||
payload["msg_type"] = 7
|
||||
if stream:
|
||||
ret = await self.post_c2c_message(
|
||||
openid=source.author.user_openid,
|
||||
**payload,
|
||||
stream=stream,
|
||||
ret = await self._send_with_markdown_fallback(
|
||||
send_func=lambda retry_payload: self.post_c2c_message(
|
||||
openid=source.author.user_openid,
|
||||
**retry_payload,
|
||||
stream=stream,
|
||||
),
|
||||
payload=payload,
|
||||
plain_text=plain_text,
|
||||
)
|
||||
else:
|
||||
ret = await self.post_c2c_message(
|
||||
openid=source.author.user_openid,
|
||||
**payload,
|
||||
ret = await self._send_with_markdown_fallback(
|
||||
send_func=lambda retry_payload: self.post_c2c_message(
|
||||
openid=source.author.user_openid,
|
||||
**retry_payload,
|
||||
),
|
||||
payload=payload,
|
||||
plain_text=plain_text,
|
||||
)
|
||||
logger.debug(f"Message sent to C2C: {ret}")
|
||||
|
||||
case botpy.message.Message():
|
||||
if image_path:
|
||||
payload["file_image"] = image_path
|
||||
ret = await self.bot.api.post_message(
|
||||
channel_id=source.channel_id,
|
||||
**payload,
|
||||
ret = await self._send_with_markdown_fallback(
|
||||
send_func=lambda retry_payload: self.bot.api.post_message(
|
||||
channel_id=source.channel_id,
|
||||
**retry_payload,
|
||||
),
|
||||
payload=payload,
|
||||
plain_text=plain_text,
|
||||
)
|
||||
|
||||
case botpy.message.DirectMessage():
|
||||
if image_path:
|
||||
payload["file_image"] = image_path
|
||||
ret = await self.bot.api.post_dms(guild_id=source.guild_id, **payload)
|
||||
ret = await self._send_with_markdown_fallback(
|
||||
send_func=lambda retry_payload: self.bot.api.post_dms(
|
||||
guild_id=source.guild_id,
|
||||
**retry_payload,
|
||||
),
|
||||
payload=payload,
|
||||
plain_text=plain_text,
|
||||
)
|
||||
|
||||
case _:
|
||||
pass
|
||||
@@ -202,6 +230,32 @@ class QQOfficialMessageEvent(AstrMessageEvent):
|
||||
|
||||
return ret
|
||||
|
||||
async def _send_with_markdown_fallback(
|
||||
self,
|
||||
send_func,
|
||||
payload: dict,
|
||||
plain_text: str,
|
||||
):
|
||||
try:
|
||||
return await send_func(payload)
|
||||
except botpy.errors.ServerError as err:
|
||||
if (
|
||||
self.MARKDOWN_NOT_ALLOWED_ERROR not in str(err)
|
||||
or not payload.get("markdown")
|
||||
or not plain_text
|
||||
):
|
||||
raise
|
||||
|
||||
logger.warning(
|
||||
"[QQOfficial] markdown 发送被拒绝,回退到 content 模式重试。"
|
||||
)
|
||||
fallback_payload = payload.copy()
|
||||
fallback_payload["markdown"] = None
|
||||
fallback_payload["content"] = plain_text
|
||||
if fallback_payload.get("msg_type") == 2:
|
||||
fallback_payload["msg_type"] = 0
|
||||
return await send_func(fallback_payload)
|
||||
|
||||
async def upload_group_and_c2c_image(
|
||||
self,
|
||||
image_base64: str,
|
||||
|
||||
@@ -6,6 +6,7 @@ from typing import Any, cast
|
||||
import telegramify_markdown
|
||||
from telegram import ReactionTypeCustomEmoji, ReactionTypeEmoji
|
||||
from telegram.constants import ChatAction
|
||||
from telegram.error import BadRequest
|
||||
from telegram.ext import ExtBot
|
||||
|
||||
from astrbot import logger
|
||||
@@ -119,6 +120,65 @@ class TelegramPlatformEvent(AstrMessageEvent):
|
||||
client, user_name, ChatAction.TYPING, message_thread_id
|
||||
)
|
||||
|
||||
@classmethod
|
||||
async def _send_voice_with_fallback(
|
||||
cls,
|
||||
client: ExtBot,
|
||||
path: str,
|
||||
payload: dict[str, Any],
|
||||
*,
|
||||
caption: str | None = None,
|
||||
user_name: str = "",
|
||||
message_thread_id: str | None = None,
|
||||
use_media_action: bool = False,
|
||||
) -> None:
|
||||
"""Send a voice message, falling back to a document if the user's
|
||||
privacy settings forbid voice messages (``BadRequest`` with
|
||||
``Voice_messages_forbidden``).
|
||||
|
||||
When *use_media_action* is ``True`` the helper wraps the send calls
|
||||
with ``_send_media_with_action`` (used by the streaming path).
|
||||
"""
|
||||
try:
|
||||
if use_media_action:
|
||||
await cls._send_media_with_action(
|
||||
client,
|
||||
ChatAction.UPLOAD_VOICE,
|
||||
client.send_voice,
|
||||
user_name=user_name,
|
||||
message_thread_id=message_thread_id,
|
||||
voice=path,
|
||||
**cast(Any, payload),
|
||||
)
|
||||
else:
|
||||
await client.send_voice(voice=path, **cast(Any, payload))
|
||||
except BadRequest as e:
|
||||
# python-telegram-bot raises BadRequest for Voice_messages_forbidden;
|
||||
# distinguish the voice-privacy case via the API error message.
|
||||
if "Voice_messages_forbidden" not in e.message:
|
||||
raise
|
||||
logger.warning(
|
||||
"User privacy settings prevent receiving voice messages, falling back to sending an audio file. "
|
||||
"To enable voice messages, go to Telegram Settings → Privacy and Security → Voice Messages → set to 'Everyone'."
|
||||
)
|
||||
if use_media_action:
|
||||
await cls._send_media_with_action(
|
||||
client,
|
||||
ChatAction.UPLOAD_DOCUMENT,
|
||||
client.send_document,
|
||||
user_name=user_name,
|
||||
message_thread_id=message_thread_id,
|
||||
document=path,
|
||||
caption=caption,
|
||||
**cast(Any, payload),
|
||||
)
|
||||
else:
|
||||
await client.send_document(
|
||||
document=path,
|
||||
caption=caption,
|
||||
**cast(Any, payload),
|
||||
)
|
||||
|
||||
async def _ensure_typing(
|
||||
self,
|
||||
user_name: str,
|
||||
@@ -211,7 +271,13 @@ class TelegramPlatformEvent(AstrMessageEvent):
|
||||
)
|
||||
elif isinstance(i, Record):
|
||||
path = await i.convert_to_file_path()
|
||||
await client.send_voice(voice=path, **cast(Any, payload))
|
||||
await cls._send_voice_with_fallback(
|
||||
client,
|
||||
path,
|
||||
payload,
|
||||
caption=i.text or None,
|
||||
use_media_action=False,
|
||||
)
|
||||
|
||||
async def send(self, message: MessageChain) -> None:
|
||||
if self.get_message_type() == MessageType.GROUP_MESSAGE:
|
||||
@@ -330,14 +396,14 @@ class TelegramPlatformEvent(AstrMessageEvent):
|
||||
continue
|
||||
elif isinstance(i, Record):
|
||||
path = await i.convert_to_file_path()
|
||||
await self._send_media_with_action(
|
||||
await self._send_voice_with_fallback(
|
||||
self.client,
|
||||
ChatAction.UPLOAD_VOICE,
|
||||
self.client.send_voice,
|
||||
path,
|
||||
payload,
|
||||
caption=i.text or delta or None,
|
||||
user_name=user_name,
|
||||
message_thread_id=message_thread_id,
|
||||
voice=path,
|
||||
**cast(Any, payload),
|
||||
use_media_action=True,
|
||||
)
|
||||
continue
|
||||
else:
|
||||
|
||||
@@ -295,6 +295,12 @@ class ProviderManager:
|
||||
from .sources.zhipu_source import ProviderZhipu as ProviderZhipu
|
||||
case "groq_chat_completion":
|
||||
from .sources.groq_source import ProviderGroq as ProviderGroq
|
||||
case "xai_chat_completion":
|
||||
from .sources.xai_source import ProviderXAI as ProviderXAI
|
||||
case "aihubmix_chat_completion":
|
||||
from .sources.oai_aihubmix_source import (
|
||||
ProviderAIHubMix as ProviderAIHubMix,
|
||||
)
|
||||
case "anthropic_chat_completion":
|
||||
from .sources.anthropic_source import (
|
||||
ProviderAnthropic as ProviderAnthropic,
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
from ..register import register_provider_adapter
|
||||
from .openai_source import ProviderOpenAIOfficial
|
||||
|
||||
|
||||
@register_provider_adapter(
|
||||
"aihubmix_chat_completion", "AIHubMix Chat Completion Provider Adapter"
|
||||
)
|
||||
class ProviderAIHubMix(ProviderOpenAIOfficial):
|
||||
def __init__(
|
||||
self,
|
||||
provider_config: dict,
|
||||
provider_settings: dict,
|
||||
) -> None:
|
||||
super().__init__(provider_config, provider_settings)
|
||||
# Reference to: https://aihubmix.com/appstore
|
||||
# Use this code can enjoy 10% off prices for AIHubMix API calls.
|
||||
self.client._custom_headers["APP-Code"] = "KRLC5702" # type: ignore
|
||||
@@ -13,6 +13,7 @@ from .star_handler import (
|
||||
register_on_llm_response,
|
||||
register_on_llm_tool_respond,
|
||||
register_on_platform_loaded,
|
||||
register_on_plugin_error,
|
||||
register_on_using_llm_tool,
|
||||
register_on_waiting_llm_request,
|
||||
register_permission_type,
|
||||
@@ -32,6 +33,7 @@ __all__ = [
|
||||
"register_on_decorating_result",
|
||||
"register_on_llm_request",
|
||||
"register_on_llm_response",
|
||||
"register_on_plugin_error",
|
||||
"register_on_platform_loaded",
|
||||
"register_on_waiting_llm_request",
|
||||
"register_permission_type",
|
||||
|
||||
@@ -339,6 +339,24 @@ def register_on_platform_loaded(**kwargs):
|
||||
return decorator
|
||||
|
||||
|
||||
def register_on_plugin_error(**kwargs):
|
||||
"""当插件处理消息异常时触发。
|
||||
|
||||
Hook 参数:
|
||||
event, plugin_name, handler_name, error, traceback_text
|
||||
|
||||
说明:
|
||||
在 hook 中调用 `event.stop_event()` 可屏蔽默认报错回显,
|
||||
并由插件自行决定是否转发到其他会话。
|
||||
"""
|
||||
|
||||
def decorator(awaitable):
|
||||
_ = get_handler_or_create(awaitable, EventType.OnPluginErrorEvent, **kwargs)
|
||||
return awaitable
|
||||
|
||||
return decorator
|
||||
|
||||
|
||||
def register_on_waiting_llm_request(**kwargs):
|
||||
"""当等待调用 LLM 时的通知事件(在获取锁之前)
|
||||
|
||||
|
||||
@@ -97,6 +97,14 @@ class StarHandlerRegistry(Generic[T]):
|
||||
plugins_name: list[str] | None = None,
|
||||
) -> list[StarHandlerMetadata[Callable[..., Awaitable[Any]]]]: ...
|
||||
|
||||
@overload
|
||||
def get_handlers_by_event_type(
|
||||
self,
|
||||
event_type: Literal[EventType.OnPluginErrorEvent],
|
||||
only_activated=True,
|
||||
plugins_name: list[str] | None = None,
|
||||
) -> list[StarHandlerMetadata[Callable[..., Awaitable[Any]]]]: ...
|
||||
|
||||
@overload
|
||||
def get_handlers_by_event_type(
|
||||
self,
|
||||
@@ -192,6 +200,7 @@ class EventType(enum.Enum):
|
||||
OnUsingLLMToolEvent = enum.auto() # 使用 LLM 工具
|
||||
OnLLMToolRespondEvent = enum.auto() # 调用函数工具后
|
||||
OnAfterMessageSentEvent = enum.auto() # 发送消息后
|
||||
OnPluginErrorEvent = enum.auto() # 插件处理消息异常时
|
||||
|
||||
|
||||
H = TypeVar("H", bound=Callable[..., Any])
|
||||
|
||||
@@ -73,6 +73,7 @@ class PluginRoute(Route):
|
||||
EventType.OnDecoratingResultEvent: "回复消息前",
|
||||
EventType.OnCallingFuncToolEvent: "函数工具",
|
||||
EventType.OnAfterMessageSentEvent: "发送消息后",
|
||||
EventType.OnPluginErrorEvent: "插件报错时",
|
||||
}
|
||||
|
||||
self._logo_cache = {}
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
## What's Changed
|
||||
|
||||
### 新增
|
||||
- 支持 QQ 官方机器人平台发送 Markdown 消息,提升富文本消息呈现能力 ([#5173](https://github.com/AstrBotDevs/AstrBot/issues/5173))。
|
||||
- 新增在插件市场中集成随机插件推荐能力 ([#5190](https://github.com/AstrBotDevs/AstrBot/issues/5190))。
|
||||
- 新增插件错误钩子(plugin error hook),支持自定义错误路由处理,便于插件统一异常控制 ([#5192](https://github.com/AstrBotDevs/AstrBot/issues/5192))。
|
||||
|
||||
### 修复
|
||||
- 修复全部 LLM Provider 失败时重复显示错误信息的问题,减少冗余报错干扰 ([#5183](https://github.com/AstrBotDevs/AstrBot/issues/5183))。
|
||||
- 修复从“选择配置文件”进入配置管理后直接关闭弹窗时,显示配置文件不正确的问题 ([#5174](https://github.com/AstrBotDevs/AstrBot/issues/5174))。
|
||||
|
||||
### 优化
|
||||
- 重构 telegram `Voice_messages_forbidden` 回退逻辑,提取为共享辅助方法并引入类型化 `BadRequest` 异常,提升异常处理一致性 ([#5204](https://github.com/AstrBotDevs/AstrBot/issues/5204))。
|
||||
|
||||
### 其他
|
||||
- 更新 README 相关文档内容。
|
||||
- 执行 `ruff format` 代码格式整理。
|
||||
|
||||
## What's Changed (EN)
|
||||
|
||||
### New Features
|
||||
- Added a plugin error hook for custom error routing, enabling unified exception handling in plugins ([#5192](https://github.com/AstrBotDevs/AstrBot/issues/5192)).
|
||||
- Added Markdown message sending support for `qqofficial` to improve rich-text delivery ([#5173](https://github.com/AstrBotDevs/AstrBot/issues/5173)).
|
||||
- Added the `MarketPluginCard` component and integrated random plugin recommendations in the extension marketplace ([#5190](https://github.com/AstrBotDevs/AstrBot/issues/5190)).
|
||||
- Added support for the `aihubmix` provider.
|
||||
- Added LINE support notes to multilingual README files.
|
||||
|
||||
### Fixes
|
||||
- Fixed duplicate error messages when all LLM providers fail, reducing noisy error output ([#5183](https://github.com/AstrBotDevs/AstrBot/issues/5183)).
|
||||
- Fixed incorrect displayed profile after opening configuration management from profile selection and closing the dialog directly ([#5174](https://github.com/AstrBotDevs/AstrBot/issues/5174)).
|
||||
|
||||
### Improvements
|
||||
- Refactored `Voice_messages_forbidden` fallback logic into a shared helper and introduced a typed `BadRequest` exception for more consistent error handling ([#5204](https://github.com/AstrBotDevs/AstrBot/issues/5204)).
|
||||
|
||||
### Others
|
||||
- Updated README documentation.
|
||||
- Applied `ruff format` code formatting.
|
||||
@@ -0,0 +1,277 @@
|
||||
<script setup>
|
||||
import { useModuleI18n } from "@/i18n/composables";
|
||||
|
||||
const { tm } = useModuleI18n("features/extension");
|
||||
|
||||
defineProps({
|
||||
plugin: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
defaultPluginIcon: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
showPluginFullName: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
});
|
||||
|
||||
const emit = defineEmits(["install"]);
|
||||
|
||||
const handleInstall = (plugin) => {
|
||||
emit("install", plugin);
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<v-card
|
||||
class="rounded-lg d-flex flex-column plugin-card"
|
||||
elevation="0"
|
||||
style="height: 12rem; position: relative"
|
||||
>
|
||||
<v-chip
|
||||
v-if="plugin?.pinned"
|
||||
color="warning"
|
||||
size="x-small"
|
||||
label
|
||||
style="
|
||||
position: absolute;
|
||||
right: 8px;
|
||||
top: 8px;
|
||||
z-index: 10;
|
||||
height: 20px;
|
||||
font-weight: bold;
|
||||
"
|
||||
>
|
||||
{{ tm("market.recommended") }}
|
||||
</v-chip>
|
||||
|
||||
<v-card-text
|
||||
style="
|
||||
padding: 12px;
|
||||
padding-bottom: 8px;
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
width: 100%;
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
"
|
||||
>
|
||||
<div style="flex-shrink: 0">
|
||||
<img
|
||||
:src="plugin?.logo || defaultPluginIcon"
|
||||
:alt="plugin.name"
|
||||
style="
|
||||
height: 75px;
|
||||
width: 75px;
|
||||
border-radius: 8px;
|
||||
object-fit: cover;
|
||||
"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div
|
||||
style="
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
"
|
||||
>
|
||||
<div
|
||||
class="font-weight-bold"
|
||||
style="
|
||||
margin-bottom: 4px;
|
||||
line-height: 1.3;
|
||||
font-size: 1.2rem;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
"
|
||||
>
|
||||
<span style="overflow: hidden; text-overflow: ellipsis">
|
||||
{{
|
||||
plugin.display_name?.length
|
||||
? plugin.display_name
|
||||
: showPluginFullName
|
||||
? plugin.name
|
||||
: plugin.trimmedName
|
||||
}}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="d-flex align-center" style="gap: 4px; margin-bottom: 6px">
|
||||
<v-icon
|
||||
icon="mdi-account"
|
||||
size="x-small"
|
||||
style="color: rgba(var(--v-theme-on-surface), 0.5)"
|
||||
></v-icon>
|
||||
<a
|
||||
v-if="plugin?.social_link"
|
||||
:href="plugin.social_link"
|
||||
target="_blank"
|
||||
class="text-subtitle-2 font-weight-medium"
|
||||
style="
|
||||
text-decoration: none;
|
||||
color: rgb(var(--v-theme-primary));
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
"
|
||||
>
|
||||
{{ plugin.author }}
|
||||
</a>
|
||||
<span
|
||||
v-else
|
||||
class="text-subtitle-2 font-weight-medium"
|
||||
style="
|
||||
color: rgb(var(--v-theme-primary));
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
"
|
||||
>
|
||||
{{ plugin.author }}
|
||||
</span>
|
||||
<div
|
||||
class="d-flex align-center text-subtitle-2 ml-2"
|
||||
style="color: rgba(var(--v-theme-on-surface), 0.7)"
|
||||
>
|
||||
<v-icon
|
||||
icon="mdi-source-branch"
|
||||
size="x-small"
|
||||
style="margin-right: 2px"
|
||||
></v-icon>
|
||||
<span>{{ plugin.version }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="text-caption plugin-description">
|
||||
{{ plugin.desc }}
|
||||
</div>
|
||||
|
||||
<div class="d-flex align-center" style="gap: 8px; margin-top: auto">
|
||||
<div
|
||||
v-if="plugin.stars !== undefined"
|
||||
class="d-flex align-center text-subtitle-2"
|
||||
style="color: rgba(var(--v-theme-on-surface), 0.7)"
|
||||
>
|
||||
<v-icon
|
||||
icon="mdi-star"
|
||||
size="x-small"
|
||||
style="margin-right: 2px"
|
||||
></v-icon>
|
||||
<span>{{ plugin.stars }}</span>
|
||||
</div>
|
||||
<div
|
||||
v-if="plugin.updated_at"
|
||||
class="d-flex align-center text-subtitle-2"
|
||||
style="color: rgba(var(--v-theme-on-surface), 0.7)"
|
||||
>
|
||||
<v-icon
|
||||
icon="mdi-clock-outline"
|
||||
size="x-small"
|
||||
style="margin-right: 2px"
|
||||
></v-icon>
|
||||
<span>{{ new Date(plugin.updated_at).toLocaleString() }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</v-card-text>
|
||||
|
||||
<v-card-actions style="gap: 6px; padding: 8px 12px; padding-top: 0">
|
||||
<v-chip
|
||||
v-for="tag in plugin.tags?.slice(0, 2)"
|
||||
:key="tag"
|
||||
:color="tag === 'danger' ? 'error' : 'primary'"
|
||||
label
|
||||
size="x-small"
|
||||
style="height: 20px"
|
||||
>
|
||||
{{ tag === "danger" ? tm("tags.danger") : tag }}
|
||||
</v-chip>
|
||||
<v-menu v-if="plugin.tags && plugin.tags.length > 2" open-on-hover offset-y>
|
||||
<template v-slot:activator="{ props: menuProps }">
|
||||
<v-chip
|
||||
v-bind="menuProps"
|
||||
color="grey"
|
||||
label
|
||||
size="x-small"
|
||||
style="height: 20px; cursor: pointer"
|
||||
>
|
||||
+{{ plugin.tags.length - 2 }}
|
||||
</v-chip>
|
||||
</template>
|
||||
<v-list density="compact">
|
||||
<v-list-item v-for="tag in plugin.tags.slice(2)" :key="tag">
|
||||
<v-chip :color="tag === 'danger' ? 'error' : 'primary'" label size="small">
|
||||
{{ tag === "danger" ? tm("tags.danger") : tag }}
|
||||
</v-chip>
|
||||
</v-list-item>
|
||||
</v-list>
|
||||
</v-menu>
|
||||
<v-spacer></v-spacer>
|
||||
<v-btn
|
||||
v-if="plugin?.repo"
|
||||
color="secondary"
|
||||
size="x-small"
|
||||
variant="tonal"
|
||||
:href="plugin.repo"
|
||||
target="_blank"
|
||||
style="height: 24px"
|
||||
>
|
||||
<v-icon icon="mdi-github" start size="x-small"></v-icon>
|
||||
{{ tm("buttons.viewRepo") }}
|
||||
</v-btn>
|
||||
<v-btn
|
||||
v-if="!plugin?.installed"
|
||||
color="primary"
|
||||
size="x-small"
|
||||
@click="handleInstall(plugin)"
|
||||
variant="flat"
|
||||
style="height: 24px"
|
||||
>
|
||||
{{ tm("buttons.install") }}
|
||||
</v-btn>
|
||||
<v-chip v-else color="success" size="x-small" label style="height: 20px">
|
||||
✓ {{ tm("status.installed") }}
|
||||
</v-chip>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.plugin-description {
|
||||
color: rgba(var(--v-theme-on-surface), 0.6);
|
||||
line-height: 1.3;
|
||||
margin-bottom: 6px;
|
||||
flex: 1;
|
||||
overflow-y: hidden;
|
||||
}
|
||||
|
||||
.plugin-card:hover .plugin-description {
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.plugin-description::-webkit-scrollbar {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
}
|
||||
|
||||
.plugin-description::-webkit-scrollbar-track {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.plugin-description::-webkit-scrollbar-thumb {
|
||||
background-color: rgba(var(--v-theme-primary-rgb), 0.4);
|
||||
border-radius: 4px;
|
||||
border: 2px solid transparent;
|
||||
background-clip: content-box;
|
||||
}
|
||||
|
||||
.plugin-description::-webkit-scrollbar-thumb:hover {
|
||||
background-color: rgba(var(--v-theme-primary-rgb), 0.6);
|
||||
}
|
||||
</style>
|
||||
@@ -38,7 +38,8 @@
|
||||
"selectFile": "Select File",
|
||||
"refresh": "Refresh",
|
||||
"updateAll": "Update All",
|
||||
"deleteSource": "Delete Source"
|
||||
"deleteSource": "Delete Source",
|
||||
"reshuffle": "Shuffle Again"
|
||||
},
|
||||
"status": {
|
||||
"enabled": "Enabled",
|
||||
@@ -103,7 +104,9 @@
|
||||
"sourceUpdated": "Source updated successfully",
|
||||
"defaultOfficialSource": "Default Official Source",
|
||||
"sourceExists": "This source already exists",
|
||||
"installPlugin": "Install Plugin"
|
||||
"installPlugin": "Install Plugin",
|
||||
"randomPlugins": "🎲 Random Plugins",
|
||||
"sourceSafetyWarning": "Even with the default source, plugin stability and security cannot be fully guaranteed. Please verify carefully before use."
|
||||
},
|
||||
"sort": {
|
||||
"default": "Default",
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
"subtitle": "管理和配置系统插件",
|
||||
"tabs": {
|
||||
"installedPlugins": "AstrBot 插件",
|
||||
"market": "AstrBot 插件市场",
|
||||
"market": "AstrBot 插件市场",
|
||||
"installedMcpServers": "MCP",
|
||||
"skills": "Skills",
|
||||
"handlersOperation": "管理行为"
|
||||
@@ -38,7 +38,8 @@
|
||||
"selectFile": "选择文件",
|
||||
"refresh": "刷新",
|
||||
"updateAll": "更新全部插件",
|
||||
"deleteSource": "删除源"
|
||||
"deleteSource": "删除源",
|
||||
"reshuffle": "随机一发"
|
||||
},
|
||||
"status": {
|
||||
"enabled": "启用",
|
||||
@@ -103,7 +104,9 @@
|
||||
"sourceUpdated": "插件源更新成功",
|
||||
"defaultOfficialSource": "默认官方源",
|
||||
"sourceExists": "该插件源已存在",
|
||||
"installPlugin": "安装插件"
|
||||
"installPlugin": "安装插件",
|
||||
"randomPlugins": "🎲 随机插件",
|
||||
"sourceSafetyWarning": "即使是默认插件源,我们也不能完全保证插件的稳定性和安全性,使用前请谨慎核查。"
|
||||
},
|
||||
"sort": {
|
||||
"default": "默认排序",
|
||||
|
||||
@@ -33,6 +33,7 @@ export function getProviderIcon(type) {
|
||||
'microsoft': 'https://registry.npmmirror.com/@lobehub/icons-static-svg/latest/files/icons/microsoft.svg',
|
||||
'vllm': 'https://registry.npmmirror.com/@lobehub/icons-static-svg/latest/files/icons/vllm.svg',
|
||||
'groq': 'https://registry.npmmirror.com/@lobehub/icons-static-svg/latest/files/icons/groq.svg',
|
||||
'aihubmix': 'https://registry.npmmirror.com/@lobehub/icons-static-svg/latest/files/icons/aihubmix-color.svg',
|
||||
"tokenpony": "https://tokenpony.cn/tokenpony-web/logo.png",
|
||||
"compshare": "https://compshare.cn/favicon.ico"
|
||||
};
|
||||
|
||||
@@ -503,6 +503,7 @@ export default {
|
||||
// 重置选择到之前的值
|
||||
this.$nextTick(() => {
|
||||
this.selectedConfigID = this.selectedConfigInfo.id || 'default';
|
||||
this.getConfig(this.selectedConfigID);
|
||||
});
|
||||
} else {
|
||||
this.getConfig(value);
|
||||
|
||||
@@ -7,6 +7,7 @@ import ProxySelector from "@/components/shared/ProxySelector.vue";
|
||||
import UninstallConfirmDialog from "@/components/shared/UninstallConfirmDialog.vue";
|
||||
import McpServersSection from "@/components/extension/McpServersSection.vue";
|
||||
import SkillsSection from "@/components/extension/SkillsSection.vue";
|
||||
import MarketPluginCard from "@/components/extension/MarketPluginCard.vue";
|
||||
import ComponentPanel from "@/components/extension/componentPanel/index.vue";
|
||||
import axios from "axios";
|
||||
import { pinyin } from "pinyin-pro";
|
||||
@@ -175,6 +176,7 @@ const debouncedMarketSearch = ref("");
|
||||
const refreshingMarket = ref(false);
|
||||
const sortBy = ref("default"); // default, stars, author, updated
|
||||
const sortOrder = ref("desc"); // desc (降序) or asc (升序)
|
||||
const randomPluginNames = ref([]);
|
||||
|
||||
// 插件市场拼音搜索
|
||||
const normalizeStr = (s) => (s ?? "").toString().toLowerCase().trim();
|
||||
@@ -310,8 +312,42 @@ const sortedPlugins = computed(() => {
|
||||
return plugins;
|
||||
});
|
||||
|
||||
const RANDOM_PLUGINS_COUNT = 6;
|
||||
|
||||
const randomPlugins = computed(() => {
|
||||
const allPlugins = pluginMarketData.value;
|
||||
if (allPlugins.length === 0) return [];
|
||||
|
||||
const pluginsByName = new Map(allPlugins.map((plugin) => [plugin.name, plugin]));
|
||||
const selected = randomPluginNames.value
|
||||
.map((name) => pluginsByName.get(name))
|
||||
.filter(Boolean);
|
||||
|
||||
if (selected.length > 0) {
|
||||
return selected;
|
||||
}
|
||||
|
||||
return allPlugins.slice(0, Math.min(RANDOM_PLUGINS_COUNT, allPlugins.length));
|
||||
});
|
||||
|
||||
const shufflePlugins = (plugins) => {
|
||||
const shuffled = [...plugins];
|
||||
for (let i = shuffled.length - 1; i > 0; i -= 1) {
|
||||
const j = Math.floor(Math.random() * (i + 1));
|
||||
[shuffled[i], shuffled[j]] = [shuffled[j], shuffled[i]];
|
||||
}
|
||||
return shuffled;
|
||||
};
|
||||
|
||||
const refreshRandomPlugins = () => {
|
||||
const shuffled = shufflePlugins(pluginMarketData.value);
|
||||
randomPluginNames.value = shuffled
|
||||
.slice(0, Math.min(RANDOM_PLUGINS_COUNT, shuffled.length))
|
||||
.map((plugin) => plugin.name);
|
||||
};
|
||||
|
||||
// 分页计算属性
|
||||
const displayItemsPerPage = 9; // 固定每页显示6个卡片(2行)
|
||||
const displayItemsPerPage = 9; // 固定每页显示9个卡片(3行)
|
||||
|
||||
const totalPages = computed(() => {
|
||||
return Math.ceil(sortedPlugins.value.length / displayItemsPerPage);
|
||||
@@ -1037,6 +1073,7 @@ const refreshPluginMarket = async () => {
|
||||
trimExtensionName();
|
||||
checkAlreadyInstalled();
|
||||
checkUpdate();
|
||||
refreshRandomPlugins();
|
||||
currentPage.value = 1; // 重置到第一页
|
||||
|
||||
toast(tm("messages.refreshSuccess"), "success");
|
||||
@@ -1085,6 +1122,7 @@ onMounted(async () => {
|
||||
trimExtensionName();
|
||||
checkAlreadyInstalled();
|
||||
checkUpdate();
|
||||
refreshRandomPlugins();
|
||||
} catch (err) {
|
||||
toast(tm("messages.getMarketDataFailed") + " " + err, "error");
|
||||
}
|
||||
@@ -1788,17 +1826,21 @@ watch(activeTab, (newTab) => {
|
||||
</v-list-item>
|
||||
</v-list>
|
||||
</v-menu>
|
||||
</div>
|
||||
|
||||
<!-- 垂直分隔线 -->
|
||||
<div
|
||||
style="
|
||||
height: 20px;
|
||||
width: 1px;
|
||||
background-color: rgba(var(--v-border-color), 0.15);
|
||||
margin: 0 8px;
|
||||
"
|
||||
></div>
|
||||
<div
|
||||
class="d-flex align-center ml-2"
|
||||
style="
|
||||
color: grey;
|
||||
font-size: 12px;
|
||||
line-height: 1.3;
|
||||
white-space: normal;
|
||||
text-align: left;
|
||||
"
|
||||
>
|
||||
<v-icon size="16" class="mr-1">mdi-alert-outline</v-icon>
|
||||
<span>{{ tm("market.sourceSafetyWarning") }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!--右侧:操作按钮组-->
|
||||
<div class="d-flex align-center">
|
||||
@@ -1883,6 +1925,42 @@ watch(activeTab, (newTab) => {
|
||||
</v-tooltip>
|
||||
|
||||
<div class="mt-4">
|
||||
<div
|
||||
class="d-flex align-center mb-2"
|
||||
style="justify-content: space-between; flex-wrap: wrap; gap: 8px"
|
||||
>
|
||||
<h2>
|
||||
{{ tm("market.randomPlugins") }}
|
||||
</h2>
|
||||
<v-btn
|
||||
color="primary"
|
||||
variant="tonal"
|
||||
prepend-icon="mdi-shuffle-variant"
|
||||
:disabled="pluginMarketData.length === 0"
|
||||
@click="refreshRandomPlugins"
|
||||
>
|
||||
{{ tm("buttons.reshuffle") }}
|
||||
</v-btn>
|
||||
</div>
|
||||
|
||||
<v-row class="mb-6" dense>
|
||||
<v-col
|
||||
v-for="plugin in randomPlugins"
|
||||
:key="`random-${plugin.name}`"
|
||||
cols="12"
|
||||
md="6"
|
||||
lg="4"
|
||||
class="pb-2"
|
||||
>
|
||||
<MarketPluginCard
|
||||
:plugin="plugin"
|
||||
:default-plugin-icon="defaultPluginIcon"
|
||||
:show-plugin-full-name="showPluginFullName"
|
||||
@install="handleInstallPlugin"
|
||||
/>
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
||||
<div
|
||||
class="d-flex align-center mb-2"
|
||||
style="
|
||||
@@ -1919,7 +1997,6 @@ watch(activeTab, (newTab) => {
|
||||
density="comfortable"
|
||||
></v-pagination>
|
||||
|
||||
<!-- 排序选择器 -->
|
||||
<v-select
|
||||
v-model="sortBy"
|
||||
:items="[
|
||||
@@ -1938,7 +2015,6 @@ watch(activeTab, (newTab) => {
|
||||
</template>
|
||||
</v-select>
|
||||
|
||||
<!-- 排序方向切换按钮 -->
|
||||
<v-btn
|
||||
icon
|
||||
v-if="sortBy !== 'default'"
|
||||
@@ -1959,272 +2035,27 @@ watch(activeTab, (newTab) => {
|
||||
}}
|
||||
</v-tooltip>
|
||||
</v-btn>
|
||||
<!-- <v-switch v-model="showPluginFullName" :label="tm('market.showFullName')" hide-details
|
||||
density="compact" style="margin-left: 12px" /> -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<v-row style="min-height: 26rem">
|
||||
<v-row style="min-height: 26rem" dense>
|
||||
<v-col
|
||||
v-for="plugin in paginatedPlugins"
|
||||
:key="plugin.name"
|
||||
cols="12"
|
||||
md="6"
|
||||
lg="4"
|
||||
class="pb-2"
|
||||
>
|
||||
<v-card
|
||||
class="rounded-lg d-flex flex-column plugin-card"
|
||||
elevation="0"
|
||||
style="height: 12rem; position: relative"
|
||||
>
|
||||
<!-- 推荐标记 -->
|
||||
<v-chip
|
||||
v-if="plugin?.pinned"
|
||||
color="warning"
|
||||
size="x-small"
|
||||
label
|
||||
style="
|
||||
position: absolute;
|
||||
right: 8px;
|
||||
top: 8px;
|
||||
z-index: 10;
|
||||
height: 20px;
|
||||
font-weight: bold;
|
||||
"
|
||||
>
|
||||
🥳 推荐
|
||||
</v-chip>
|
||||
|
||||
<v-card-text
|
||||
style="
|
||||
padding: 12px;
|
||||
padding-bottom: 8px;
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
width: 100%;
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
"
|
||||
>
|
||||
<div style="flex-shrink: 0">
|
||||
<img
|
||||
:src="plugin?.logo || defaultPluginIcon"
|
||||
:alt="plugin.name"
|
||||
style="
|
||||
height: 75px;
|
||||
width: 75px;
|
||||
border-radius: 8px;
|
||||
object-fit: cover;
|
||||
"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div
|
||||
style="
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
"
|
||||
>
|
||||
<!-- Display Name -->
|
||||
<div
|
||||
class="font-weight-bold"
|
||||
style="
|
||||
margin-bottom: 4px;
|
||||
line-height: 1.3;
|
||||
font-size: 1.2rem;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
"
|
||||
>
|
||||
<span
|
||||
style="overflow: hidden; text-overflow: ellipsis"
|
||||
>
|
||||
{{
|
||||
plugin.display_name?.length
|
||||
? plugin.display_name
|
||||
: showPluginFullName
|
||||
? plugin.name
|
||||
: plugin.trimmedName
|
||||
}}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<!-- Author with link -->
|
||||
<div
|
||||
class="d-flex align-center"
|
||||
style="gap: 4px; margin-bottom: 6px"
|
||||
>
|
||||
<v-icon
|
||||
icon="mdi-account"
|
||||
size="x-small"
|
||||
style="color: rgba(var(--v-theme-on-surface), 0.5)"
|
||||
></v-icon>
|
||||
<a
|
||||
v-if="plugin?.social_link"
|
||||
:href="plugin.social_link"
|
||||
target="_blank"
|
||||
class="text-subtitle-2 font-weight-medium"
|
||||
style="
|
||||
text-decoration: none;
|
||||
color: rgb(var(--v-theme-primary));
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
"
|
||||
>
|
||||
{{ plugin.author }}
|
||||
</a>
|
||||
<span
|
||||
v-else
|
||||
class="text-subtitle-2 font-weight-medium"
|
||||
style="
|
||||
color: rgb(var(--v-theme-primary));
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
"
|
||||
>
|
||||
{{ plugin.author }}
|
||||
</span>
|
||||
<div
|
||||
class="d-flex align-center text-subtitle-2 ml-2"
|
||||
style="color: rgba(var(--v-theme-on-surface), 0.7)"
|
||||
>
|
||||
<v-icon
|
||||
icon="mdi-source-branch"
|
||||
size="x-small"
|
||||
style="margin-right: 2px"
|
||||
></v-icon>
|
||||
<span>{{ plugin.version }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Description -->
|
||||
<div class="text-caption plugin-description">
|
||||
{{ plugin.desc }}
|
||||
</div>
|
||||
|
||||
<!-- Stats: Stars & Updated & Version -->
|
||||
<div
|
||||
class="d-flex align-center"
|
||||
style="gap: 8px; margin-top: auto"
|
||||
>
|
||||
<div
|
||||
v-if="plugin.stars !== undefined"
|
||||
class="d-flex align-center text-subtitle-2"
|
||||
style="color: rgba(var(--v-theme-on-surface), 0.7)"
|
||||
>
|
||||
<v-icon
|
||||
icon="mdi-star"
|
||||
size="x-small"
|
||||
style="margin-right: 2px"
|
||||
></v-icon>
|
||||
<span>{{ plugin.stars }}</span>
|
||||
</div>
|
||||
<div
|
||||
v-if="plugin.updated_at"
|
||||
class="d-flex align-center text-subtitle-2"
|
||||
style="color: rgba(var(--v-theme-on-surface), 0.7)"
|
||||
>
|
||||
<v-icon
|
||||
icon="mdi-clock-outline"
|
||||
size="x-small"
|
||||
style="margin-right: 2px"
|
||||
></v-icon>
|
||||
<span>{{
|
||||
new Date(plugin.updated_at).toLocaleString()
|
||||
}}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</v-card-text>
|
||||
|
||||
<!-- Actions -->
|
||||
<v-card-actions
|
||||
style="gap: 6px; padding: 8px 12px; padding-top: 0"
|
||||
>
|
||||
<v-chip
|
||||
v-for="tag in plugin.tags?.slice(0, 2)"
|
||||
:key="tag"
|
||||
:color="tag === 'danger' ? 'error' : 'primary'"
|
||||
label
|
||||
size="x-small"
|
||||
style="height: 20px"
|
||||
>
|
||||
{{ tag === "danger" ? tm("tags.danger") : tag }}
|
||||
</v-chip>
|
||||
<v-menu
|
||||
v-if="plugin.tags && plugin.tags.length > 2"
|
||||
open-on-hover
|
||||
offset-y
|
||||
>
|
||||
<template v-slot:activator="{ props: menuProps }">
|
||||
<v-chip
|
||||
v-bind="menuProps"
|
||||
color="grey"
|
||||
label
|
||||
size="x-small"
|
||||
style="height: 20px; cursor: pointer"
|
||||
>
|
||||
+{{ plugin.tags.length - 2 }}
|
||||
</v-chip>
|
||||
</template>
|
||||
<v-list density="compact">
|
||||
<v-list-item
|
||||
v-for="tag in plugin.tags.slice(2)"
|
||||
:key="tag"
|
||||
>
|
||||
<v-chip
|
||||
:color="tag === 'danger' ? 'error' : 'primary'"
|
||||
label
|
||||
size="small"
|
||||
>
|
||||
{{ tag === "danger" ? tm("tags.danger") : tag }}
|
||||
</v-chip>
|
||||
</v-list-item>
|
||||
</v-list>
|
||||
</v-menu>
|
||||
<v-spacer></v-spacer>
|
||||
<v-btn
|
||||
v-if="plugin?.repo"
|
||||
color="secondary"
|
||||
size="x-small"
|
||||
variant="tonal"
|
||||
:href="plugin.repo"
|
||||
target="_blank"
|
||||
style="height: 24px"
|
||||
>
|
||||
<v-icon icon="mdi-github" start size="x-small"></v-icon>
|
||||
{{ tm("buttons.viewRepo") }}
|
||||
</v-btn>
|
||||
<v-btn
|
||||
v-if="!plugin?.installed"
|
||||
color="primary"
|
||||
size="x-small"
|
||||
@click="handleInstallPlugin(plugin)"
|
||||
variant="flat"
|
||||
style="height: 24px"
|
||||
>
|
||||
{{ tm("buttons.install") }}
|
||||
</v-btn>
|
||||
<v-chip
|
||||
v-else
|
||||
color="success"
|
||||
size="x-small"
|
||||
label
|
||||
style="height: 20px"
|
||||
>
|
||||
✓ {{ tm("status.installed") }}
|
||||
</v-chip>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
<MarketPluginCard
|
||||
:plugin="plugin"
|
||||
:default-plugin-icon="defaultPluginIcon"
|
||||
:show-plugin-full-name="showPluginFullName"
|
||||
@install="handleInstallPlugin"
|
||||
/>
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
||||
<!-- 底部分页控件 -->
|
||||
<div class="d-flex justify-center mt-4" v-if="totalPages > 1">
|
||||
<v-pagination
|
||||
v-model="currentPage"
|
||||
@@ -2729,38 +2560,6 @@ watch(activeTab, (newTab) => {
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
|
||||
.plugin-description {
|
||||
color: rgba(var(--v-theme-on-surface), 0.6);
|
||||
line-height: 1.3;
|
||||
margin-bottom: 6px;
|
||||
flex: 1;
|
||||
overflow-y: hidden;
|
||||
}
|
||||
|
||||
.plugin-card:hover .plugin-description {
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.plugin-description::-webkit-scrollbar {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
}
|
||||
|
||||
.plugin-description::-webkit-scrollbar-track {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.plugin-description::-webkit-scrollbar-thumb {
|
||||
background-color: rgba(var(--v-theme-primary-rgb), 0.4);
|
||||
border-radius: 4px;
|
||||
border: 2px solid transparent;
|
||||
background-clip: content-box;
|
||||
}
|
||||
|
||||
.plugin-description::-webkit-scrollbar-thumb:hover {
|
||||
background-color: rgba(var(--v-theme-primary-rgb), 0.6);
|
||||
}
|
||||
|
||||
.fab-button {
|
||||
transition: all 0.3s cubic-bezier(0.25, 0.8, 0.25, 1);
|
||||
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "astrbot-desktop",
|
||||
"version": "4.17.4",
|
||||
"version": "4.17.5",
|
||||
"description": "AstrBot desktop wrapper",
|
||||
"private": true,
|
||||
"main": "main.js",
|
||||
|
||||
@@ -0,0 +1,93 @@
|
||||
# 黑盒语音机器人帮助文档
|
||||
codex resume 019c57d5-3b44-7a50-a514-1b1b3f0a4448
|
||||
## Docs
|
||||
- [教程](https://s.apifox.cn/43256fe4-9a8c-4f22-949a-74a3f8b431f5/5031038m0.md):
|
||||
- [开发者服务协议](https://s.apifox.cn/43256fe4-9a8c-4f22-949a-74a3f8b431f5/5083727m0.md):
|
||||
- [使用交流](https://s.apifox.cn/43256fe4-9a8c-4f22-949a-74a3f8b431f5/4778396m0.md):
|
||||
- [更新日志](https://s.apifox.cn/43256fe4-9a8c-4f22-949a-74a3f8b431f5/5029501m0.md):
|
||||
- [开发计划](https://s.apifox.cn/43256fe4-9a8c-4f22-949a-74a3f8b431f5/5029504m0.md):
|
||||
- [基础框架须知](https://s.apifox.cn/43256fe4-9a8c-4f22-949a-74a3f8b431f5/7279187m0.md):
|
||||
- 资源 [请求速率限制](https://s.apifox.cn/43256fe4-9a8c-4f22-949a-74a3f8b431f5/5192003m0.md):
|
||||
- 资源 [Websocket](https://s.apifox.cn/43256fe4-9a8c-4f22-949a-74a3f8b431f5/5029558m0.md):
|
||||
- 资源 [Bot命令](https://s.apifox.cn/43256fe4-9a8c-4f22-949a-74a3f8b431f5/5030757m0.md):
|
||||
- HTTP接口 > 消息接口 [发送消息接口的参数](https://s.apifox.cn/43256fe4-9a8c-4f22-949a-74a3f8b431f5/5112305m0.md):
|
||||
- HTTP接口 > 消息接口 [发送消息接口的返回值](https://s.apifox.cn/43256fe4-9a8c-4f22-949a-74a3f8b431f5/5156437m0.md):
|
||||
- HTTP接口 > 消息接口 [发送图片形式的频道消息](https://s.apifox.cn/43256fe4-9a8c-4f22-949a-74a3f8b431f5/5088949m0.md):
|
||||
- HTTP接口 > 消息接口 [发送Markdown文档](https://s.apifox.cn/43256fe4-9a8c-4f22-949a-74a3f8b431f5/5324071m0.md):
|
||||
- HTTP接口 > 消息接口 [更新指定频道消息](https://s.apifox.cn/43256fe4-9a8c-4f22-949a-74a3f8b431f5/5274453m0.md):
|
||||
- HTTP接口 > 消息接口 [删除指定的频道消息](https://s.apifox.cn/43256fe4-9a8c-4f22-949a-74a3f8b431f5/5274471m0.md):
|
||||
- HTTP接口 > 消息接口 [对某条频道消息增加/取消回应(小表情)](https://s.apifox.cn/43256fe4-9a8c-4f22-949a-74a3f8b431f5/5274495m0.md):
|
||||
- HTTP接口 > 消息接口 [发送卡片消息](https://s.apifox.cn/43256fe4-9a8c-4f22-949a-74a3f8b431f5/5430115m0.md):
|
||||
- HTTP接口 > 消息接口 [给用户发送私聊消息](https://s.apifox.cn/43256fe4-9a8c-4f22-949a-74a3f8b431f5/5722305m0.md):
|
||||
- HTTP接口 > 媒体文件上传 [上传媒体文件的参数解析](https://s.apifox.cn/43256fe4-9a8c-4f22-949a-74a3f8b431f5/5156807m0.md):
|
||||
- HTTP接口 > 房间角色接口 [权限相关说明](https://s.apifox.cn/43256fe4-9a8c-4f22-949a-74a3f8b431f5/4781009m0.md):
|
||||
- HTTP接口 > 房间角色接口 [接口说明](https://s.apifox.cn/43256fe4-9a8c-4f22-949a-74a3f8b431f5/5274618m0.md):
|
||||
- HTTP接口 > 房间表情 [房间表情包](https://s.apifox.cn/43256fe4-9a8c-4f22-949a-74a3f8b431f5/5252750m0.md):
|
||||
- HTTP接口 > 房间接口 [房间相关接口文档](https://s.apifox.cn/43256fe4-9a8c-4f22-949a-74a3f8b431f5/5650569m0.md):
|
||||
- HTTP接口 > 在线媒体流 [在线媒体流说明文档](https://s.apifox.cn/43256fe4-9a8c-4f22-949a-74a3f8b431f5/7020148m0.md):
|
||||
- HTTP接口 > OAuth [OAuth使用说明](https://s.apifox.cn/43256fe4-9a8c-4f22-949a-74a3f8b431f5/7145802m0.md):
|
||||
- 服务端推送事件 [事件说明](https://s.apifox.cn/43256fe4-9a8c-4f22-949a-74a3f8b431f5/5243813m0.md):
|
||||
- 服务端推送事件 [通用推送字段](https://s.apifox.cn/43256fe4-9a8c-4f22-949a-74a3f8b431f5/5254214m0.md):
|
||||
- 服务端推送事件 > 机器人命令 [用户使用Bot命令](https://s.apifox.cn/43256fe4-9a8c-4f22-949a-74a3f8b431f5/5116164m0.md):
|
||||
- 服务端推送事件 > 频道消息事件 [频道消息事件](https://s.apifox.cn/43256fe4-9a8c-4f22-949a-74a3f8b431f5/5243816m0.md):
|
||||
- 服务端推送事件 > 房间消息事件 [房间消息事件](https://s.apifox.cn/43256fe4-9a8c-4f22-949a-74a3f8b431f5/5254078m0.md):
|
||||
- 自定义卡片消息 [自定义卡片编辑器](https://s.apifox.cn/43256fe4-9a8c-4f22-949a-74a3f8b431f5/6997428m0.md):
|
||||
- 自定义卡片消息 > 物料组件 [卡片](https://s.apifox.cn/43256fe4-9a8c-4f22-949a-74a3f8b431f5/6997517m0.md):
|
||||
- 自定义卡片消息 > 物料组件 [文本](https://s.apifox.cn/43256fe4-9a8c-4f22-949a-74a3f8b431f5/6997518m0.md):
|
||||
- 自定义卡片消息 > 物料组件 [标题](https://s.apifox.cn/43256fe4-9a8c-4f22-949a-74a3f8b431f5/6997729m0.md):
|
||||
- 自定义卡片消息 > 物料组件 [图片](https://s.apifox.cn/43256fe4-9a8c-4f22-949a-74a3f8b431f5/6997730m0.md):
|
||||
- 自定义卡片消息 > 物料组件 [按钮组](https://s.apifox.cn/43256fe4-9a8c-4f22-949a-74a3f8b431f5/6997731m0.md):
|
||||
- 自定义卡片消息 > 物料组件 [分割线](https://s.apifox.cn/43256fe4-9a8c-4f22-949a-74a3f8b431f5/6997733m0.md):
|
||||
- 自定义卡片消息 > 物料组件 [倒计时](https://s.apifox.cn/43256fe4-9a8c-4f22-949a-74a3f8b431f5/6997735m0.md):
|
||||
|
||||
## API Docs
|
||||
- WEBSOCKET 连接请求 [连接到黑盒语音服务](https://s.apifox.cn/43256fe4-9a8c-4f22-949a-74a3f8b431f5/3545540w0.md):
|
||||
- HTTP接口 > 消息接口 [发送频道图片消息](https://s.apifox.cn/43256fe4-9a8c-4f22-949a-74a3f8b431f5/196181766e0.md):
|
||||
- HTTP接口 > 消息接口 [发送频道消息](https://s.apifox.cn/43256fe4-9a8c-4f22-949a-74a3f8b431f5/195916005e0.md):
|
||||
- HTTP接口 > 消息接口 [发送卡片消息](https://s.apifox.cn/43256fe4-9a8c-4f22-949a-74a3f8b431f5/231244234e0.md):
|
||||
- HTTP接口 > 消息接口 [发送频道消息@全体成员/@在线成员](https://s.apifox.cn/43256fe4-9a8c-4f22-949a-74a3f8b431f5/196225350e0.md):
|
||||
- HTTP接口 > 消息接口 [更新指定的频道消息](https://s.apifox.cn/43256fe4-9a8c-4f22-949a-74a3f8b431f5/221115476e0.md):
|
||||
- HTTP接口 > 消息接口 [删除指定的频道消息](https://s.apifox.cn/43256fe4-9a8c-4f22-949a-74a3f8b431f5/221117785e0.md):
|
||||
- HTTP接口 > 消息接口 [对某条频道消息增加/取消回应(小表情)](https://s.apifox.cn/43256fe4-9a8c-4f22-949a-74a3f8b431f5/220985915e0.md):
|
||||
- HTTP接口 > 消息接口 [给用户发送私聊消息](https://s.apifox.cn/43256fe4-9a8c-4f22-949a-74a3f8b431f5/247164510e0.md):
|
||||
- HTTP接口 > 媒体文件上传 [上传媒体文件](https://s.apifox.cn/43256fe4-9a8c-4f22-949a-74a3f8b431f5/196172729e0.md):
|
||||
- HTTP接口 > 房间角色接口 [获取房间角色列表](https://s.apifox.cn/43256fe4-9a8c-4f22-949a-74a3f8b431f5/220721816e0.md):
|
||||
- HTTP接口 > 房间角色接口 [创建角色](https://s.apifox.cn/43256fe4-9a8c-4f22-949a-74a3f8b431f5/220860098e0.md):
|
||||
- HTTP接口 > 房间角色接口 [更新角色](https://s.apifox.cn/43256fe4-9a8c-4f22-949a-74a3f8b431f5/220893910e0.md):
|
||||
- HTTP接口 > 房间角色接口 [删除角色](https://s.apifox.cn/43256fe4-9a8c-4f22-949a-74a3f8b431f5/220864876e0.md):
|
||||
- HTTP接口 > 房间角色接口 [对指定用户授予指定权限](https://s.apifox.cn/43256fe4-9a8c-4f22-949a-74a3f8b431f5/195925401e0.md):
|
||||
- HTTP接口 > 房间角色接口 [对指定用户剥夺指定权限](https://s.apifox.cn/43256fe4-9a8c-4f22-949a-74a3f8b431f5/195927164e0.md):
|
||||
- HTTP接口 > 房间表情 [获取房间上传的表情包](https://s.apifox.cn/43256fe4-9a8c-4f22-949a-74a3f8b431f5/221092473e0.md):
|
||||
- HTTP接口 > 房间表情 [房间删除表情包](https://s.apifox.cn/43256fe4-9a8c-4f22-949a-74a3f8b431f5/221112168e0.md):
|
||||
- HTTP接口 > 房间表情 [房间更新表情包名称](https://s.apifox.cn/43256fe4-9a8c-4f22-949a-74a3f8b431f5/221346019e0.md):
|
||||
- HTTP接口 > 房间接口 [修改房间内昵称](https://s.apifox.cn/43256fe4-9a8c-4f22-949a-74a3f8b431f5/226373089e0.md):
|
||||
- HTTP接口 > 房间接口 [分页获取加入的房间列表](https://s.apifox.cn/43256fe4-9a8c-4f22-949a-74a3f8b431f5/226373523e0.md):
|
||||
- HTTP接口 > 房间接口 [获取房间信息](https://s.apifox.cn/43256fe4-9a8c-4f22-949a-74a3f8b431f5/226373528e0.md):
|
||||
- HTTP接口 > 房间接口 [退出房间](https://s.apifox.cn/43256fe4-9a8c-4f22-949a-74a3f8b431f5/226373638e0.md):
|
||||
- HTTP接口 > 房间接口 [房间踢人](https://s.apifox.cn/43256fe4-9a8c-4f22-949a-74a3f8b431f5/226373709e0.md):
|
||||
- HTTP接口 > 房间接口 [语音频道之间移动用户](https://s.apifox.cn/43256fe4-9a8c-4f22-949a-74a3f8b431f5/318260744e0.md):
|
||||
- HTTP接口 > 房间接口 [踢出语音频道中的用户](https://s.apifox.cn/43256fe4-9a8c-4f22-949a-74a3f8b431f5/318266039e0.md):
|
||||
- HTTP接口 > 房间接口 [禁言/解禁用户](https://s.apifox.cn/43256fe4-9a8c-4f22-949a-74a3f8b431f5/325086722e0.md):
|
||||
- HTTP接口 > 房间接口 [频道内麦克风静音/解禁](https://s.apifox.cn/43256fe4-9a8c-4f22-949a-74a3f8b431f5/325092125e0.md): 对未静音对象调用时对其静音;对静音对象调用时解除静音
|
||||
- HTTP接口 > 房间接口 [房间内麦克风静音/解禁](https://s.apifox.cn/43256fe4-9a8c-4f22-949a-74a3f8b431f5/325104333e0.md):
|
||||
- HTTP接口 > 房间接口 [房间内扬声器静音/解禁](https://s.apifox.cn/43256fe4-9a8c-4f22-949a-74a3f8b431f5/325105640e0.md):
|
||||
- HTTP接口 > 房间接口 [获取用户所在频道](https://s.apifox.cn/43256fe4-9a8c-4f22-949a-74a3f8b431f5/325187362e0.md): bot需要在查询的房间中
|
||||
- HTTP接口 > 房间接口 [获取语音频道内在线成员列表](https://s.apifox.cn/43256fe4-9a8c-4f22-949a-74a3f8b431f5/325207647e0.md):
|
||||
- HTTP接口 > 房间接口 [创建频道邀请链接](https://s.apifox.cn/43256fe4-9a8c-4f22-949a-74a3f8b431f5/325223584e0.md): 需要 创建邀请 权限
|
||||
- HTTP接口 > 房间接口 [频道设置修改](https://s.apifox.cn/43256fe4-9a8c-4f22-949a-74a3f8b431f5/325259753e0.md): 需要 编辑频道 权限
|
||||
- HTTP接口 > 房间接口 [频道名编辑](https://s.apifox.cn/43256fe4-9a8c-4f22-949a-74a3f8b431f5/325264530e0.md): 需要 编辑频道 权限
|
||||
- HTTP接口 > 房间接口 [设置频道密码](https://s.apifox.cn/43256fe4-9a8c-4f22-949a-74a3f8b431f5/325512688e0.md):
|
||||
- HTTP接口 > 房间接口 [修改权限组或成员权限](https://s.apifox.cn/43256fe4-9a8c-4f22-949a-74a3f8b431f5/325672775e0.md): # 服务器权限管理文档
|
||||
- HTTP接口 > 房间接口 [获取房间用户列表](https://s.apifox.cn/43256fe4-9a8c-4f22-949a-74a3f8b431f5/326508787e0.md):
|
||||
- HTTP接口 > 房间接口 [获取用户频道权限](https://s.apifox.cn/43256fe4-9a8c-4f22-949a-74a3f8b431f5/339765173e0.md):
|
||||
- HTTP接口 > 房间接口 [创建频道](https://s.apifox.cn/43256fe4-9a8c-4f22-949a-74a3f8b431f5/340658298e0.md): 需要 管理频道(1<<2) 权限
|
||||
- HTTP接口 > 房间接口 [删除频道](https://s.apifox.cn/43256fe4-9a8c-4f22-949a-74a3f8b431f5/340660409e0.md): 需要 管理频道(1<<2) 权限
|
||||
- HTTP接口 > 在线媒体流 [推流至语音频道](https://s.apifox.cn/43256fe4-9a8c-4f22-949a-74a3f8b431f5/320947489e0.md):
|
||||
- HTTP接口 > 在线媒体流 [停止推流至语音频道](https://s.apifox.cn/43256fe4-9a8c-4f22-949a-74a3f8b431f5/320947513e0.md):
|
||||
- HTTP接口 > OAuth [获取授权码](https://s.apifox.cn/43256fe4-9a8c-4f22-949a-74a3f8b431f5/329051392e0.md): 获取授权码链接示例
|
||||
- HTTP接口 > OAuth [获取AccessToken](https://s.apifox.cn/43256fe4-9a8c-4f22-949a-74a3f8b431f5/329070402e0.md):
|
||||
- HTTP接口 > OAuth [刷新AccessToken](https://s.apifox.cn/43256fe4-9a8c-4f22-949a-74a3f8b431f5/329079907e0.md):
|
||||
- HTTP接口 > OAuth [获取用户信息](https://s.apifox.cn/43256fe4-9a8c-4f22-949a-74a3f8b431f5/329599863e0.md):
|
||||
- HTTP接口 > OAuth [获取用户房间内语音时长](https://s.apifox.cn/43256fe4-9a8c-4f22-949a-74a3f8b431f5/332236185e0.md): 时间跨度不能超过30天
|
||||
- HTTP接口 > OAuth [获取用户房间内语音游戏时长](https://s.apifox.cn/43256fe4-9a8c-4f22-949a-74a3f8b431f5/332238065e0.md): 时间跨度不能超过30天
|
||||
- HTTP接口 > OAuth [获取用户信息-自动触发授权](https://s.apifox.cn/43256fe4-9a8c-4f22-949a-74a3f8b431f5/331602654e0.md): 在发起api请求时可以携带以下query作为参数 如果没有token且用户在线则会为用户唤起授权弹窗
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
[project]
|
||||
name = "AstrBot"
|
||||
version = "4.17.4"
|
||||
version = "4.17.5"
|
||||
description = "Easy-to-use multi-platform LLM chatbot and development framework"
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.12"
|
||||
|
||||
Reference in New Issue
Block a user