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/actions/setup-python/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ description: Setup Python
inputs:
python-version:
description: Python version
default: '3.12'
default: '3.14'

runs:
using: composite
Expand Down
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.11', '3.12', '3.13']
python-version: ['3.13', '3.14']
os: [ubuntu-latest, windows-latest, macos-latest]
fail-fast: false
env:
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -128,9 +128,9 @@ AliceBot 是一个简单的 Python 异步多后端机器人框架,支持多种

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

# 支持的 Python 版本
## 支持的 Python 版本

AliceBot 的最新版本仅支持最近的三个 Python 主要版本。
AliceBot 的最新版本仅支持处于 `bugfix` 维护状态的 Python 版本,您可以在 [Python 官方文档](https://devguide.python.org/versions/) 中查看 Python 版本的维护状态,通常这意味着最新的两个 Python 主要版本。

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

Expand Down
10 changes: 10 additions & 0 deletions alicebot/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,16 @@
- `Depends` => [`alicebot.dependencies.Depends`](./dependencies#Depends)
"""

# pylint: disable=wrong-import-position

import os

if os.getenv("ALICEBOT_DEV") == "1": # pragma: no cover
from pkgutil import extend_path

__path__ = extend_path(__path__, __name__)


from alicebot.adapter import Adapter
from alicebot.bot import Bot
from alicebot.config import ConfigModel
Expand Down
41 changes: 18 additions & 23 deletions alicebot/adapter/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,23 @@
所有协议适配器都必须继承自 `Adapter` 基类。
"""

# pylint: disable=wrong-import-position

import os

if os.getenv("ALICEBOT_DEV") == "1": # pragma: no cover
from pkgutil import extend_path

__path__ = extend_path(__path__, __name__)

from abc import ABC, abstractmethod
from collections.abc import Awaitable, Callable
from typing import (
TYPE_CHECKING,
Any,
Generic,
final,
overload,
)
from typing_extensions import TypeVar
from typing import TYPE_CHECKING, Any, final, overload

import structlog

from alicebot.typing import AnyEvent, ConfigT, EventT
from alicebot.config import ConfigModel
from alicebot.typing import AnyEvent
from alicebot.utils import is_config_class

if TYPE_CHECKING:
Expand All @@ -27,15 +29,8 @@

logger = structlog.stdlib.get_logger()

if os.getenv("ALICEBOT_DEV") == "1": # pragma: no cover
# 当处于开发环境时,使用 pkg_resources 风格的命名空间包
__import__("pkg_resources").declare_namespace(__name__)


_EventT = TypeVar("_EventT", bound=AnyEvent, default=AnyEvent)


class Adapter(Generic[EventT, ConfigT], ABC):
class Adapter[EventT: AnyEvent = AnyEvent, ConfigT: ConfigModel | None = None](ABC):
"""协议适配器基类。

Attributes:
Expand Down Expand Up @@ -87,13 +82,13 @@ async def run(self) -> None:
"""
raise NotImplementedError

async def startup(self) -> None:
async def startup(self) -> None: # noqa: B027
"""在适配器开始运行前运行的方法,用于初始化适配器。

AliceBot 依次运行并等待所有适配器的 `startup()` 方法,待运行完毕后再创建 `run()` 任务。
"""

async def shutdown(self) -> None:
async def shutdown(self) -> None: # noqa: B027
"""在适配器结束运行时运行的方法,用于安全地关闭适配器。

AliceBot 在接收到系统的结束信号后先发送 cancel 请求给 run 任务。
Expand All @@ -113,15 +108,15 @@ async def get(
) -> EventT: ...

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

@final
async def get(
Expand Down
36 changes: 27 additions & 9 deletions alicebot/adapter/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,7 @@
"""

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

import aiohttp
import anyio
Expand All @@ -14,7 +13,8 @@
from anyio.lowlevel import checkpoint

from alicebot.adapter import Adapter
from alicebot.typing import ConfigT, EventT
from alicebot.config import ConfigModel
from alicebot.typing import AnyEvent

__all__ = [
"HttpClientAdapter",
Expand All @@ -28,7 +28,10 @@
logger = structlog.stdlib.get_logger()


class PollingAdapter(Adapter[EventT, ConfigT], metaclass=ABCMeta):
class PollingAdapter[
EventT: AnyEvent = AnyEvent,
ConfigT: ConfigModel | None = None,
](Adapter[EventT, ConfigT], metaclass=ABCMeta):
"""轮询式适配器示例。"""

@override
Expand All @@ -42,7 +45,10 @@ async def on_tick(self) -> None:
"""当轮询发生。"""


class HttpClientAdapter(PollingAdapter[EventT, ConfigT], metaclass=ABCMeta):
class HttpClientAdapter[
EventT: AnyEvent = AnyEvent,
ConfigT: ConfigModel | None = None,
](PollingAdapter[EventT, ConfigT], metaclass=ABCMeta):
"""HTTP 客户端适配器示例。"""

session: aiohttp.ClientSession
Expand All @@ -56,7 +62,10 @@ async def shutdown(self) -> None:
await self.session.close()


class WebSocketClientAdapter(Adapter[EventT, ConfigT], metaclass=ABCMeta):
class WebSocketClientAdapter[
EventT: AnyEvent = AnyEvent,
ConfigT: ConfigModel | None = None,
](Adapter[EventT, ConfigT], metaclass=ABCMeta):
"""WebSocket 客户端适配器示例。"""

url: str
Expand All @@ -79,7 +88,10 @@ async def handle_response(self, msg: aiohttp.WSMessage) -> None:
"""处理响应。"""


class HttpServerAdapter(Adapter[EventT, ConfigT], metaclass=ABCMeta):
class HttpServerAdapter[
EventT: AnyEvent = AnyEvent,
ConfigT: ConfigModel | None = None,
](Adapter[EventT, ConfigT], metaclass=ABCMeta):
"""HTTP 服务端适配器示例。"""

app: web.Application
Expand Down Expand Up @@ -116,7 +128,10 @@ async def handle_response(self, request: web.Request) -> web.StreamResponse:
"""处理响应。"""


class WebSocketServerAdapter(Adapter[EventT, ConfigT], metaclass=ABCMeta):
class WebSocketServerAdapter[
EventT: AnyEvent = AnyEvent,
ConfigT: ConfigModel | None = None,
](Adapter[EventT, ConfigT], metaclass=ABCMeta):
"""WebSocket 服务端适配器示例。"""

app: web.Application
Expand Down Expand Up @@ -165,7 +180,10 @@ async def handle_ws_response(self, msg: aiohttp.WSMessage) -> None:
"""处理 WebSocket 响应。"""


class WebSocketAdapter(Adapter[EventT, ConfigT], metaclass=ABCMeta):
class WebSocketAdapter[
EventT: AnyEvent = AnyEvent,
ConfigT: ConfigModel | None = None,
](Adapter[EventT, ConfigT], metaclass=ABCMeta):
"""WebSocket 适配器示例。

同时支持 WebSocket 客户端和服务端。
Expand Down
22 changes: 13 additions & 9 deletions alicebot/bot.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,11 @@
from alicebot.plugin import Plugin, PluginLoadType
from alicebot.typing import (
AdapterHook,
AdapterT,
AnyAdapter,
AnyEvent,
AnyPlugin,
BotHook,
EventHook,
EventT,
)
from alicebot.utils import (
ModulePathFinder,
Expand Down Expand Up @@ -257,9 +255,11 @@ def _remove_plugin_by_path(
for plugins in self.plugins_priority_dict.values():
_removed_plugins = list(
filter(
lambda x: x.__plugin_load_type__ != PluginLoadType.CLASS
and x.__plugin_file_path__ is not None
and samefile(x.__plugin_file_path__, file),
lambda x: (
x.__plugin_load_type__ != PluginLoadType.CLASS
and x.__plugin_file_path__ is not None
and samefile(x.__plugin_file_path__, file)
),
plugins,
)
)
Expand Down Expand Up @@ -591,7 +591,7 @@ async def get(
) -> AnyEvent: ...

@overload
async def get(
async def get[EventT: AnyEvent](
self,
func: Callable[[EventT], bool | Awaitable[bool]] | None = None,
*,
Expand All @@ -603,7 +603,7 @@ async def get(
) -> EventT: ...

@overload
async def get(
async def get[EventT: AnyEvent](
self,
func: Callable[[EventT], bool | Awaitable[bool]] | None = None,
*,
Expand Down Expand Up @@ -881,9 +881,13 @@ def load_adapters(self, *adapters: type[AnyAdapter] | str) -> None:
def get_adapter(self, adapter: str) -> AnyAdapter: ...

@overload
def get_adapter(self, adapter: type[AdapterT]) -> AdapterT: ...
def get_adapter[AdapterT: AnyAdapter](
self, adapter: type[AdapterT]
) -> AdapterT: ...

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

Args:
Expand Down
Loading
Loading