Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ jobs:
cancel-in-progress: true
strategy:
matrix:
python-version: ['3.9', '3.10', '3.11', '3.12', '3.13']
python-version: ['3.11', '3.12', '3.13']
os: [ubuntu-latest, windows-latest, macos-latest]
fail-fast: false
env:
Expand Down
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,14 @@ AliceBot 是一个简单的 Python 异步多后端机器人框架,支持多种

更多信息请参阅 AliceBot [文档](https://docs.alicebot.dev/)。

# 支持的 Python 版本

AliceBot 的最新版本仅支持最近的三个 Python 主要版本。

当前支持的最低 Python 版本为:

<img src="https://img.shields.io/python/required-version-toml?tomlFilePath=https%3A%2F%2Fgithub.com%2FAliceBotProject%2Falicebot%2Fraw%2Fmain%2Fpyproject.toml" alt="pypi">

## 对比

本项目受到了 [NoneBot](https://github.com/nonebot/nonebot2) 项目的启发,以下简单介绍两者的异同。
Expand Down
23 changes: 10 additions & 13 deletions alicebot/adapter/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,11 @@

import os
from abc import ABC, abstractmethod
from collections.abc import Awaitable
from collections.abc import Awaitable, Callable
from typing import (
TYPE_CHECKING,
Any,
Callable,
Generic,
Optional,
Union,
final,
overload,
)
Expand Down Expand Up @@ -108,33 +105,33 @@ async def shutdown(self) -> None:
@overload
async def get(
self,
func: Optional[Callable[[EventT], Union[bool, Awaitable[bool]]]] = None,
func: Callable[[EventT], bool | Awaitable[bool]] | None = None,
*,
event_type: None = None,
max_try_times: Optional[int] = None,
timeout: Optional[Union[int, float]] = None,
max_try_times: int | None = None,
timeout: float | None = None,
to_thread: bool = False,
) -> EventT: ...

@overload
async def get(
self,
func: Optional[Callable[[_EventT], Union[bool, Awaitable[bool]]]] = None,
func: Callable[[_EventT], bool | Awaitable[bool]] | None = None,
*,
event_type: type[_EventT],
max_try_times: Optional[int] = None,
timeout: Optional[Union[int, float]] = None,
max_try_times: int | None = None,
timeout: float | None = None,
to_thread: bool = False,
) -> _EventT: ...

@final
async def get(
self,
func: Optional[Callable[[Any], Union[bool, Awaitable[bool]]]] = None,
func: Callable[[Any], bool | Awaitable[bool]] | None = None,
*,
event_type: Any = None,
max_try_times: Optional[int] = None,
timeout: Optional[Union[int, float]] = None,
max_try_times: int | None = None,
timeout: float | None = None,
to_thread: bool = False,
) -> Event[Any]:
"""获取满足指定条件的的事件,协程会等待直到适配器接收到满足条件的事件、超过最大事件数或超时。
Expand Down
14 changes: 6 additions & 8 deletions alicebot/adapter/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"""

from abc import ABCMeta, abstractmethod
from typing import Literal, Optional, Union
from typing import Literal
from typing_extensions import override

import aiohttp
Expand Down Expand Up @@ -171,17 +171,15 @@ class WebSocketAdapter(Adapter[EventT, ConfigT], metaclass=ABCMeta):
同时支持 WebSocket 客户端和服务端。
"""

websocket: Union[web.WebSocketResponse, aiohttp.ClientWebSocketResponse, None] = (
None
)
websocket: web.WebSocketResponse | aiohttp.ClientWebSocketResponse | None = None

# ws
session: Optional[aiohttp.ClientSession]
session: aiohttp.ClientSession | None

# reverse-ws
app: Optional[web.Application]
runner: Optional[web.AppRunner]
site: Optional[web.TCPSite]
app: web.Application | None
runner: web.AppRunner | None
site: web.TCPSite | None

# config
adapter_type: Literal["ws", "reverse-ws"]
Expand Down
77 changes: 35 additions & 42 deletions alicebot/bot.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,13 @@
import signal
import sys
import threading
import tomllib
from collections import defaultdict
from collections.abc import Awaitable
from collections.abc import Awaitable, Callable
from contextlib import AsyncExitStack
from itertools import chain
from pathlib import Path
from typing import Any, Callable, Optional, Union, cast, overload
from typing import Any, cast, overload

import anyio
import structlog
Expand All @@ -36,12 +37,6 @@
samefile,
)

if sys.version_info >= (3, 11): # pragma: no cover
import tomllib
else: # pragma: no cover
import tomli as tomllib


__all__ = ["Bot"]

HANDLED_SIGNALS = (
Expand Down Expand Up @@ -81,19 +76,19 @@ class Bot:
_raw_config_dict: dict[str, Any] # 原始配置字典

# 以下属性不会在重启时清除
_config_file: Optional[str] # 配置文件
_config_dict: Optional[dict[str, Any]] # 配置字典
_config_file: str | None # 配置文件
_config_dict: dict[str, Any] | None # 配置字典
_hot_reload: bool # 热重载
_handle_signals: bool # 处理信号

_extend_plugins: list[
Union[type[Plugin[Any, Any, Any]], str, Path]
type[Plugin[Any, Any, Any]] | str | Path
] # 使用 load_plugins() 方法程序化加载的插件列表
_extend_plugin_dirs: list[
Path
] # 使用 load_plugins_from_dirs() 方法程序化加载的插件路径列表
_extend_adapters: list[
Union[type[Adapter[Any, Any]], str]
type[Adapter[Any, Any]] | str
] # 使用 load_adapter() 方法程序化加载的适配器列表
_bot_run_hooks: list[BotHook]
_bot_exit_hooks: list[BotHook]
Expand All @@ -106,8 +101,8 @@ class Bot:
def __init__(
self,
*,
config_file: Optional[str] = "config.toml",
config_dict: Optional[dict[str, Any]] = None,
config_file: str | None = "config.toml",
config_dict: dict[str, Any] | None = None,
hot_reload: bool = False,
handle_signals: bool = True,
) -> None:
Expand Down Expand Up @@ -269,7 +264,7 @@ def _remove_plugin_by_path(
async def _run_hot_reload(self) -> None: # pragma: no cover
"""热重载。"""
try:
from watchfiles import Change, awatch
from watchfiles import Change, awatch # noqa: PLC0415
except ImportError:
logger.warning(
'Hot reload needs to install "watchfiles", try "pip install watchfiles"'
Expand Down Expand Up @@ -346,7 +341,7 @@ def _update_config(self) -> None:
"""更新 config,合并入来自 Plugin 和 Adapter 的 Config。"""

def update_config(
source: Union[list[type[Plugin[Any, Any, Any]]], list[Adapter[Any, Any]]],
source: list[type[Plugin[Any, Any, Any]]] | list[Adapter[Any, Any]],
name: str,
base: type[ConfigModel],
) -> tuple[type[ConfigModel], ConfigModel]:
Expand Down Expand Up @@ -578,47 +573,47 @@ async def _run_plugin(
@overload
async def get(
self,
func: Optional[Callable[[Event[Any]], Union[bool, Awaitable[bool]]]] = None,
func: Callable[[Event[Any]], bool | Awaitable[bool]] | None = None,
*,
event_type: None = None,
adapter_type: None = None,
max_try_times: Optional[int] = None,
timeout: Optional[Union[int, float]] = None,
max_try_times: int | None = None,
timeout: float | None = None,
to_thread: bool = False,
) -> Event[Any]: ...

@overload
async def get(
self,
func: Optional[Callable[[EventT], Union[bool, Awaitable[bool]]]] = None,
func: Callable[[EventT], bool | Awaitable[bool]] | None = None,
*,
event_type: None = None,
adapter_type: type[Adapter[EventT, Any]],
max_try_times: Optional[int] = None,
timeout: Optional[Union[int, float]] = None,
max_try_times: int | None = None,
timeout: float | None = None,
to_thread: bool = False,
) -> EventT: ...

@overload
async def get(
self,
func: Optional[Callable[[EventT], Union[bool, Awaitable[bool]]]] = None,
func: Callable[[EventT], bool | Awaitable[bool]] | None = None,
*,
event_type: type[EventT],
adapter_type: Optional[type[Adapter[Any, Any]]] = None,
max_try_times: Optional[int] = None,
timeout: Optional[Union[int, float]] = None,
adapter_type: type[Adapter[Any, Any]] | None = None,
max_try_times: int | None = None,
timeout: float | None = None,
to_thread: bool = False,
) -> EventT: ...

async def get(
self,
func: Optional[Callable[[Any], Union[bool, Awaitable[bool]]]] = None,
func: Callable[[Any], bool | Awaitable[bool]] | None = None,
*,
event_type: Optional[type[Event[Any]]] = None,
adapter_type: Optional[type[Adapter[Any, Any]]] = None,
max_try_times: Optional[int] = None,
timeout: Optional[Union[int, float]] = None,
event_type: type[Event[Any]] | None = None,
adapter_type: type[Adapter[Any, Any]] | None = None,
max_try_times: int | None = None,
timeout: float | None = None,
to_thread: bool = False,
) -> Event[Any]:
"""获取满足指定条件的的事件,协程会等待直到适配器接收到满足条件的事件、超过最大事件数或超时。
Expand Down Expand Up @@ -655,7 +650,7 @@ def _load_plugin_class(
self,
plugin_class: type[Plugin[Any, Any, Any]],
plugin_load_type: PluginLoadType,
plugin_file_path: Optional[str],
plugin_file_path: str | None,
) -> None:
"""加载插件类。"""
priority = getattr(plugin_class, "priority", None)
Expand Down Expand Up @@ -703,8 +698,8 @@ def _load_plugins_from_module_name(

def _load_plugins(
self,
*plugins: Union[type[Plugin[Any, Any, Any]], str, Path],
plugin_load_type: Optional[PluginLoadType] = None,
*plugins: type[Plugin[Any, Any, Any]] | str | Path,
plugin_load_type: PluginLoadType | None = None,
reload: bool = False,
) -> None:
"""加载插件。
Expand Down Expand Up @@ -762,7 +757,7 @@ def _load_plugins(
plugin_module_name = ".".join(rel_path.parts[:-1])
else:
plugin_module_name = ".".join(
rel_path.parts[:-1] + (rel_path.stem,)
(*rel_path.parts[:-1], rel_path.stem)
)

self._load_plugins_from_module_name(
Expand All @@ -777,9 +772,7 @@ def _load_plugins(
except Exception:
logger.exception("Load plugin failed:", plugin=plugin_)

def load_plugins(
self, *plugins: Union[type[Plugin[Any, Any, Any]], str, Path]
) -> None:
def load_plugins(self, *plugins: type[Plugin[Any, Any, Any]] | str | Path) -> None:
"""加载插件。

Args:
Expand Down Expand Up @@ -820,7 +813,7 @@ def load_plugins_from_dirs(self, *dirs: Path) -> None:
self._extend_plugin_dirs.extend(dirs)
self._load_plugins_from_dirs(*dirs)

def _load_adapters(self, *adapters: Union[type[Adapter[Any, Any]], str]) -> None:
def _load_adapters(self, *adapters: type[Adapter[Any, Any]] | str) -> None:
"""加载适配器。

Args:
Expand Down Expand Up @@ -864,7 +857,7 @@ def _load_adapters(self, *adapters: Union[type[Adapter[Any, Any]], str]) -> None
else:
self.adapters.append(adapter_object)

def load_adapters(self, *adapters: Union[type[Adapter[Any, Any]], str]) -> None:
def load_adapters(self, *adapters: type[Adapter[Any, Any]] | str) -> None:
"""加载适配器。

Args:
Expand All @@ -883,8 +876,8 @@ def get_adapter(self, adapter: str) -> Adapter[Any, Any]: ...
def get_adapter(self, adapter: type[AdapterT]) -> AdapterT: ...

def get_adapter(
self, adapter: Union[str, type[AdapterT]]
) -> Union[Adapter[Any, Any], AdapterT]:
self, adapter: str | type[AdapterT]
) -> Adapter[Any, Any] | AdapterT:
"""按照名称或适配器类获取已经加载的适配器。

Args:
Expand Down
6 changes: 2 additions & 4 deletions alicebot/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@
AliceBot 使用 [pydantic](https://pydantic-docs.helpmanual.io/) 来读取配置。
"""

from typing import Optional, Union

from pydantic import BaseModel, ConfigDict, DirectoryPath, Field

__all__ = [
Expand Down Expand Up @@ -37,7 +35,7 @@ class LogConfig(ConfigModel):
verbose_exception: 详细的异常记录,设置为 `True` 时会在日志中添加异常的 Traceback。
"""

level: Union[str, int] = "DEBUG"
level: str | int = "DEBUG"
verbose_exception: bool = False


Expand All @@ -56,7 +54,7 @@ class BotConfig(ConfigModel):
plugins: set[str] = Field(default_factory=set[str])
plugin_dirs: set[DirectoryPath] = Field(default_factory=set[DirectoryPath])
adapters: set[str] = Field(default_factory=set[str])
log: Optional[LogConfig] = None
log: LogConfig | None = None


class PluginConfig(ConfigModel):
Expand Down
Loading
Loading