Merge branch 'AstrBotDevs:master' into master
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
name: Run tests and upload coverage
|
||||
|
||||
on:
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
@@ -8,6 +8,7 @@ on:
|
||||
- 'README.md'
|
||||
- 'changelogs/**'
|
||||
- 'dashboard/**'
|
||||
pull_request:
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
@@ -26,20 +27,19 @@ jobs:
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
pip install -r requirements.txt
|
||||
pip install pytest pytest-cov pytest-asyncio
|
||||
pip install pytest pytest-asyncio pytest-cov
|
||||
pip install --editable .
|
||||
|
||||
- name: Run tests
|
||||
run: |
|
||||
mkdir data
|
||||
mkdir data/plugins
|
||||
mkdir data/config
|
||||
mkdir data/temp
|
||||
mkdir -p data/plugins
|
||||
mkdir -p data/config
|
||||
mkdir -p data/temp
|
||||
export TESTING=true
|
||||
export ZHIPU_API_KEY=${{ secrets.OPENAI_API_KEY }}
|
||||
PYTHONPATH=./ pytest --cov=. tests/ -v -o log_cli=true -o log_level=DEBUG
|
||||
pytest --cov=. -v -o log_cli=true -o log_level=DEBUG
|
||||
|
||||
- name: Upload results to Codecov
|
||||
uses: codecov/codecov-action@v5
|
||||
with:
|
||||
token: ${{ secrets.CODECOV_TOKEN }}
|
||||
token: ${{ secrets.CODECOV_TOKEN }}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
from .star import StarMetadata
|
||||
from .star import StarMetadata, star_map
|
||||
from .star_manager import PluginManager
|
||||
from .context import Context
|
||||
from astrbot.core.provider import Provider
|
||||
@@ -14,12 +14,22 @@ class Star(CommandParserMixin):
|
||||
StarTools.initialize(context)
|
||||
self.context = context
|
||||
|
||||
async def text_to_image(self, text: str, return_url=True) -> str:
|
||||
def __init_subclass__(cls, **kwargs):
|
||||
super().__init_subclass__(**kwargs)
|
||||
metadata = StarMetadata(
|
||||
star_cls_type=cls,
|
||||
module_path=cls.__module__,
|
||||
)
|
||||
star_map[cls.__module__] = metadata
|
||||
|
||||
@staticmethod
|
||||
async def text_to_image(text: str, return_url=True) -> str:
|
||||
"""将文本转换为图片"""
|
||||
return await html_renderer.render_t2i(text, return_url=return_url)
|
||||
|
||||
@staticmethod
|
||||
async def html_render(
|
||||
self, tmpl: str, data: dict, return_url=True, options: dict = None
|
||||
tmpl: str, data: dict, return_url=True, options: dict = None
|
||||
) -> str:
|
||||
"""渲染 HTML"""
|
||||
return await html_renderer.render_custom_template(
|
||||
|
||||
@@ -8,22 +8,48 @@ from typing import Union
|
||||
class PlatformAdapterType(enum.Flag):
|
||||
AIOCQHTTP = enum.auto()
|
||||
QQOFFICIAL = enum.auto()
|
||||
VCHAT = enum.auto()
|
||||
GEWECHAT = enum.auto()
|
||||
TELEGRAM = enum.auto()
|
||||
WECOM = enum.auto()
|
||||
LARK = enum.auto()
|
||||
ALL = AIOCQHTTP | QQOFFICIAL | VCHAT | GEWECHAT | TELEGRAM | WECOM | LARK
|
||||
WECHATPADPRO = enum.auto()
|
||||
DINGTALK = enum.auto()
|
||||
DISCORD = enum.auto()
|
||||
SLACK = enum.auto()
|
||||
KOOK = enum.auto()
|
||||
VOCECHAT = enum.auto()
|
||||
WEIXIN_OFFICIAL_ACCOUNT = enum.auto()
|
||||
ALL = (
|
||||
AIOCQHTTP
|
||||
| QQOFFICIAL
|
||||
| GEWECHAT
|
||||
| TELEGRAM
|
||||
| WECOM
|
||||
| LARK
|
||||
| WECHATPADPRO
|
||||
| DINGTALK
|
||||
| DISCORD
|
||||
| SLACK
|
||||
| KOOK
|
||||
| VOCECHAT
|
||||
| WEIXIN_OFFICIAL_ACCOUNT
|
||||
)
|
||||
|
||||
|
||||
ADAPTER_NAME_2_TYPE = {
|
||||
"aiocqhttp": PlatformAdapterType.AIOCQHTTP,
|
||||
"qq_official": PlatformAdapterType.QQOFFICIAL,
|
||||
"vchat": PlatformAdapterType.VCHAT,
|
||||
"gewechat": PlatformAdapterType.GEWECHAT,
|
||||
"telegram": PlatformAdapterType.TELEGRAM,
|
||||
"wecom": PlatformAdapterType.WECOM,
|
||||
"lark": PlatformAdapterType.LARK,
|
||||
"dingtalk": PlatformAdapterType.DINGTALK,
|
||||
"discord": PlatformAdapterType.DISCORD,
|
||||
"slack": PlatformAdapterType.SLACK,
|
||||
"kook": PlatformAdapterType.KOOK,
|
||||
"wechatpadpro": PlatformAdapterType.WECHATPADPRO,
|
||||
"vocechat": PlatformAdapterType.VOCECHAT,
|
||||
"weixin_official_account": PlatformAdapterType.WEIXIN_OFFICIAL_ACCOUNT,
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,9 +1,15 @@
|
||||
from ..star import star_registry, StarMetadata, star_map
|
||||
import warnings
|
||||
|
||||
_warned_register_star = False
|
||||
|
||||
|
||||
def register_star(name: str, author: str, desc: str, version: str, repo: str = None):
|
||||
"""注册一个插件(Star)。
|
||||
|
||||
[DEPRECATED] 该装饰器已废弃,将在未来版本中移除。
|
||||
在 v3.5.19 版本之后(不含),您不需要使用该装饰器来装饰插件类,
|
||||
AstrBot 会自动识别继承自 Star 的类并将其作为插件类加载。
|
||||
|
||||
Args:
|
||||
name: 插件名称。
|
||||
author: 作者。
|
||||
@@ -21,18 +27,16 @@ def register_star(name: str, author: str, desc: str, version: str, repo: str = N
|
||||
帮助信息会被自动提取。使用 `/plugin <插件名> 可以查看帮助信息。`
|
||||
"""
|
||||
|
||||
def decorator(cls):
|
||||
star_metadata = StarMetadata(
|
||||
name=name,
|
||||
author=author,
|
||||
desc=desc,
|
||||
version=version,
|
||||
repo=repo,
|
||||
star_cls_type=cls,
|
||||
module_path=cls.__module__,
|
||||
global _warned_register_star
|
||||
if not _warned_register_star:
|
||||
_warned_register_star = True
|
||||
warnings.warn(
|
||||
"The 'register_star' decorator is deprecated and will be removed in a future version.",
|
||||
DeprecationWarning,
|
||||
stacklevel=2,
|
||||
)
|
||||
star_registry.append(star_metadata)
|
||||
star_map[cls.__module__] = star_metadata
|
||||
|
||||
def decorator(cls):
|
||||
return cls
|
||||
|
||||
return decorator
|
||||
|
||||
+22
-17
@@ -1,12 +1,12 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from types import ModuleType
|
||||
from typing import List, Dict
|
||||
from dataclasses import dataclass, field
|
||||
from types import ModuleType
|
||||
|
||||
from astrbot.core.config import AstrBotConfig
|
||||
|
||||
star_registry: List[StarMetadata] = []
|
||||
star_map: Dict[str, StarMetadata] = {}
|
||||
star_registry: list[StarMetadata] = []
|
||||
star_map: dict[str, StarMetadata] = {}
|
||||
"""key 是模块路径,__module__"""
|
||||
|
||||
|
||||
@@ -18,22 +18,27 @@ class StarMetadata:
|
||||
当 activated 为 False 时,star_cls 可能为 None,请不要在插件未激活时调用 star_cls 的方法。
|
||||
"""
|
||||
|
||||
name: str
|
||||
author: str # 插件作者
|
||||
desc: str # 插件简介
|
||||
version: str # 插件版本
|
||||
repo: str = None # 插件仓库地址
|
||||
name: str | None = None
|
||||
"""插件名"""
|
||||
author: str | None = None
|
||||
"""插件作者"""
|
||||
desc: str | None = None
|
||||
"""插件简介"""
|
||||
version: str | None = None
|
||||
"""插件版本"""
|
||||
repo: str | None = None
|
||||
"""插件仓库地址"""
|
||||
|
||||
star_cls_type: type = None
|
||||
star_cls_type: type | None = None
|
||||
"""插件的类对象的类型"""
|
||||
module_path: str = None
|
||||
module_path: str | None = None
|
||||
"""插件的模块路径"""
|
||||
|
||||
star_cls: object = None
|
||||
star_cls: object | None = None
|
||||
"""插件的类对象"""
|
||||
module: ModuleType = None
|
||||
module: ModuleType | None = None
|
||||
"""插件的模块对象"""
|
||||
root_dir_name: str = None
|
||||
root_dir_name: str | None = None
|
||||
"""插件的目录名称"""
|
||||
reserved: bool = False
|
||||
"""是否是 AstrBot 的保留插件"""
|
||||
@@ -41,13 +46,13 @@ class StarMetadata:
|
||||
activated: bool = True
|
||||
"""是否被激活"""
|
||||
|
||||
config: AstrBotConfig = None
|
||||
config: AstrBotConfig | None = None
|
||||
"""插件配置"""
|
||||
|
||||
star_handler_full_names: List[str] = field(default_factory=list)
|
||||
star_handler_full_names: list[str] = field(default_factory=list)
|
||||
"""注册的 Handler 的全名列表"""
|
||||
|
||||
supported_platforms: Dict[str, bool] = field(default_factory=dict)
|
||||
supported_platforms: dict[str, bool] = field(default_factory=dict)
|
||||
"""插件支持的平台ID字典,key为平台ID,value为是否支持"""
|
||||
|
||||
def __str__(self) -> str:
|
||||
|
||||
@@ -11,7 +11,6 @@ import os
|
||||
import sys
|
||||
import traceback
|
||||
from types import ModuleType
|
||||
from typing import List
|
||||
|
||||
import yaml
|
||||
|
||||
@@ -119,7 +118,8 @@ class PluginManager:
|
||||
reloaded_plugins.add(plugin_name)
|
||||
break
|
||||
|
||||
def _get_classes(self, arg: ModuleType):
|
||||
@staticmethod
|
||||
def _get_classes(arg: ModuleType):
|
||||
"""获取指定模块(可以理解为一个 python 文件)下所有的类"""
|
||||
classes = []
|
||||
clsmembers = inspect.getmembers(arg, inspect.isclass)
|
||||
@@ -129,7 +129,8 @@ class PluginManager:
|
||||
break
|
||||
return classes
|
||||
|
||||
def _get_modules(self, path):
|
||||
@staticmethod
|
||||
def _get_modules(path):
|
||||
modules = []
|
||||
|
||||
dirs = os.listdir(path)
|
||||
@@ -155,7 +156,7 @@ class PluginManager:
|
||||
)
|
||||
return modules
|
||||
|
||||
def _get_plugin_modules(self) -> List[dict]:
|
||||
def _get_plugin_modules(self) -> list[dict]:
|
||||
plugins = []
|
||||
if os.path.exists(self.plugin_store_path):
|
||||
plugins.extend(self._get_modules(self.plugin_store_path))
|
||||
@@ -189,7 +190,8 @@ class PluginManager:
|
||||
except Exception as e:
|
||||
logger.error(f"更新插件 {p} 的依赖失败。Code: {str(e)}")
|
||||
|
||||
def _load_plugin_metadata(self, plugin_path: str, plugin_obj=None) -> StarMetadata:
|
||||
@staticmethod
|
||||
def _load_plugin_metadata(plugin_path: str, plugin_obj=None) -> StarMetadata:
|
||||
"""v3.4.0 以前的方式载入插件元数据
|
||||
|
||||
先寻找 metadata.yaml 文件,如果不存在,则使用插件对象的 info() 函数获取元数据。
|
||||
@@ -228,8 +230,9 @@ class PluginManager:
|
||||
|
||||
return metadata
|
||||
|
||||
@staticmethod
|
||||
def _get_plugin_related_modules(
|
||||
self, plugin_root_dir: str, is_reserved: bool = False
|
||||
plugin_root_dir: str, is_reserved: bool = False
|
||||
) -> list[str]:
|
||||
"""获取与指定插件相关的所有已加载模块名
|
||||
|
||||
@@ -435,7 +438,7 @@ class PluginManager:
|
||||
)
|
||||
|
||||
if path in star_map:
|
||||
# 通过装饰器的方式注册插件
|
||||
# 通过__init__subclass__注册插件
|
||||
metadata = star_map[path]
|
||||
|
||||
try:
|
||||
@@ -504,6 +507,8 @@ class PluginManager:
|
||||
if func_tool.name in inactivated_llm_tools:
|
||||
func_tool.active = False
|
||||
|
||||
star_registry.append(metadata)
|
||||
|
||||
else:
|
||||
# v3.4.0 以前的方式注册插件
|
||||
logger.debug(
|
||||
@@ -775,7 +780,8 @@ class PluginManager:
|
||||
|
||||
plugin.activated = False
|
||||
|
||||
async def _terminate_plugin(self, star_metadata: StarMetadata):
|
||||
@staticmethod
|
||||
async def _terminate_plugin(star_metadata: StarMetadata):
|
||||
"""终止插件,调用插件的 terminate() 和 __del__() 方法"""
|
||||
logger.info(f"正在终止插件 {star_metadata.name} ...")
|
||||
|
||||
|
||||
@@ -117,7 +117,7 @@ async def audio_to_tencent_silk_base64(audio_path: str) -> tuple[str, float]:
|
||||
try:
|
||||
import pilk
|
||||
except ImportError as e:
|
||||
raise Exception("未安装 pysilk,请执行: pip install pysilk") from e
|
||||
raise Exception("未安装 pilk: pip install pilk") from e
|
||||
|
||||
temp_dir = os.path.join(get_astrbot_data_path(), "temp")
|
||||
os.makedirs(temp_dir, exist_ok=True)
|
||||
|
||||
@@ -56,12 +56,6 @@ class RstScene(Enum):
|
||||
return cls.PRIVATE
|
||||
|
||||
|
||||
@star.register(
|
||||
name="astrbot",
|
||||
desc="AstrBot 基础指令结合 + 拓展功能",
|
||||
author="Soulter",
|
||||
version="4.0.1",
|
||||
)
|
||||
class Main(star.Star):
|
||||
def __init__(self, context: star.Context) -> None:
|
||||
self.context = context
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
name: astrbot
|
||||
desc: AstrBot 基础指令结合 + 拓展功能
|
||||
author: Soulter
|
||||
version: 4.0.0
|
||||
@@ -94,12 +94,6 @@ DEFAULT_CONFIG = {
|
||||
PATH = os.path.join(get_astrbot_data_path(), "config", "python_interpreter.json")
|
||||
|
||||
|
||||
@star.register(
|
||||
name="astrbot-python-interpreter",
|
||||
desc="Python 代码执行器",
|
||||
author="Soulter",
|
||||
version="0.0.1",
|
||||
)
|
||||
class Main(star.Star):
|
||||
"""基于 Docker 沙箱的 Python 代码执行器"""
|
||||
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
name: astrbot-python-interpreter
|
||||
desc: Python 代码执行器
|
||||
author: Soulter
|
||||
version: 0.0.1
|
||||
@@ -11,9 +11,6 @@ from astrbot.api import llm_tool, logger
|
||||
from astrbot.core.utils.astrbot_path import get_astrbot_data_path
|
||||
|
||||
|
||||
@star.register(
|
||||
name="astrbot-reminder", desc="使用 LLM 待办提醒", author="Soulter", version="0.0.1"
|
||||
)
|
||||
class Main(star.Star):
|
||||
"""使用 LLM 待办提醒。只需对 LLM 说想要提醒的事情和时间即可。比如:`之后每天这个时候都提醒我做多邻国`"""
|
||||
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
name: astrbot-reminder
|
||||
desc: 使用 LLM 待办提醒
|
||||
author: Soulter
|
||||
version: 0.0.1
|
||||
@@ -2,7 +2,7 @@ import astrbot.api.message_components as Comp
|
||||
import copy
|
||||
from astrbot.api import logger
|
||||
from astrbot.api.event import AstrMessageEvent, filter
|
||||
from astrbot.api.star import Context, Star, register
|
||||
from astrbot.api.star import Context, Star
|
||||
from astrbot.core.utils.session_waiter import (
|
||||
SessionWaiter,
|
||||
USER_SESSIONS,
|
||||
@@ -13,13 +13,6 @@ from astrbot.core.utils.session_waiter import (
|
||||
from sys import maxsize
|
||||
|
||||
|
||||
@register(
|
||||
"session_controller",
|
||||
"Cvandia & Soulter",
|
||||
"为插件支持会话控制",
|
||||
"v1.0.1",
|
||||
"https://astrbot.app",
|
||||
)
|
||||
class Waiter(Star):
|
||||
"""会话控制"""
|
||||
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
name: session_controller
|
||||
desc: 为插件支持会话控制
|
||||
author: Cvandia & Soulter
|
||||
version: v1.0.1
|
||||
repo: https://astrbot.app
|
||||
@@ -1,17 +1,10 @@
|
||||
import re
|
||||
from astrbot.api.event import filter, AstrMessageEvent
|
||||
from astrbot.api.star import Context, Star, register
|
||||
from astrbot.api.star import Context, Star
|
||||
from astrbot.api.provider import LLMResponse
|
||||
from openai.types.chat.chat_completion import ChatCompletion
|
||||
|
||||
|
||||
@register(
|
||||
"thinking_filter",
|
||||
"Soulter",
|
||||
"可选择是否过滤推理模型的思考内容",
|
||||
"1.0.0",
|
||||
"https://astrbot.app",
|
||||
)
|
||||
class R1Filter(Star):
|
||||
def __init__(self, context: Context):
|
||||
super().__init__(context)
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
name: thinking_filter
|
||||
desc: 可选择是否过滤推理模型的思考内容
|
||||
author: Soulter
|
||||
version: 1.0.0
|
||||
repo: https://astrbot.app
|
||||
@@ -12,12 +12,6 @@ from bs4 import BeautifulSoup
|
||||
from .engines import HEADERS, USER_AGENTS
|
||||
|
||||
|
||||
@star.register(
|
||||
name="astrbot-web-searcher",
|
||||
desc="让 LLM 具有网页检索能力",
|
||||
author="Soulter",
|
||||
version="1.14.514",
|
||||
)
|
||||
class Main(star.Star):
|
||||
"""使用 /websearch on 或者 off 开启或者关闭网页搜索功能"""
|
||||
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
name: astrbot-web-searcher
|
||||
desc: 让 LLM 具有网页检索能力
|
||||
author: Soulter
|
||||
version: 1.14.514
|
||||
Reference in New Issue
Block a user