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
33 changes: 22 additions & 11 deletions alicebot/dependencies.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,15 @@
asynccontextmanager,
contextmanager,
)
from inspect import get_annotations
from typing import Any, TypeVar, cast
from typing import Any, TypeVar, cast, get_type_hints
from typing_extensions import override

from typing_inspection.introspection import (
UNKNOWN,
AnnotationSource,
inspect_annotation,
)

from alicebot.utils import sync_ctx_manager_wrapper

_T = TypeVar("_T")
Expand All @@ -31,11 +36,9 @@
__all__ = ["Depends"]


class InnerDepends:
class _InnerDepends:
"""子依赖的内部实现。

用户无需关注此内部实现。

Attributes:
dependency: 依赖类。如果不指定则根据字段的类型注释自动判断。
use_cache: 是否使用缓存。默认为 `True`。
Expand Down Expand Up @@ -69,7 +72,7 @@ def Depends( # noqa: N802 # pylint: disable=invalid-name
Returns:
返回内部子依赖对象。
"""
return InnerDepends(dependency=dependency, use_cache=use_cache) # type: ignore
return _InnerDepends(dependency=dependency, use_cache=use_cache) # type: ignore


async def solve_dependencies(
Expand Down Expand Up @@ -99,22 +102,30 @@ async def solve_dependencies(
if isinstance(dependent, type):
# type of dependent is Type[T]
values: dict[str, Any] = {}
ann = get_annotations(dependent)
ann = get_type_hints(dependent)
for name, sub_dependent in inspect.getmembers(
dependent, lambda x: isinstance(x, InnerDepends)
dependent, lambda x: isinstance(x, _InnerDepends)
):
assert isinstance(sub_dependent, InnerDepends)
assert isinstance(sub_dependent, _InnerDepends)
if sub_dependent.dependency is None:
dependent_ann = ann.get(name)
if dependent_ann is None:
raise TypeError("can not solve dependent")
sub_dependent.dependency = dependent_ann
inspected_ann = inspect_annotation(
dependent_ann,
annotation_source=AnnotationSource.CLASS,
)
if inspected_ann.type == UNKNOWN:
raise TypeError("can not solve dependent")
sub_dependent.dependency = inspected_ann.type # type: ignore
assert sub_dependent.dependency is not None
values[name] = await solve_dependencies(
cast("Dependency[_T]", sub_dependent.dependency),
sub_dependent.dependency,
use_cache=sub_dependent.use_cache,
stack=stack,
dependency_cache=dependency_cache,
)

depend_obj = cast(
"_T | AbstractAsyncContextManager[_T] | AbstractContextManager[_T]",
dependent.__new__(dependent), # type: ignore
Expand Down
24 changes: 12 additions & 12 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,23 +24,23 @@
},
"dependencies": {
"@iconify-json/mdi": "^1.2.3",
"unocss": "^66.1.3",
"unocss": "^66.3.3",
"vitepress": "^1.6.3",
"vue": "^3.5.16"
"vue": "^3.5.18"
},
"devDependencies": {
"@eslint/js": "^9.28.0",
"@types/node": "^22.15.29",
"@unocss/eslint-config": "^66.1.3",
"@eslint/js": "^9.32.0",
"@types/node": "^22.16.5",
"@unocss/eslint-config": "^66.3.3",
"conventional-changelog-cli": "^5.0.0",
"eslint": "^9.28.0",
"eslint-config-prettier": "^10.1.5",
"eslint-plugin-vue": "^10.1.0",
"globals": "^16.2.0",
"eslint": "^9.32.0",
"eslint-config-prettier": "^10.1.8",
"eslint-plugin-vue": "^10.3.0",
"globals": "^16.3.0",
"markdownlint-cli2": "^0.18.1",
"prettier": "^3.5.3",
"typescript-eslint": "^8.33.0",
"vue-eslint-parser": "^10.1.3"
"prettier": "^3.6.2",
"typescript-eslint": "^8.38.0",
"vue-eslint-parser": "^10.2.0"
},
"pnpm": {
"peerDependencyRules": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,6 @@ async def _wrapper(self: PluginT) -> bool:
return _wrapper

cls.rule = _rule_decorator(cls.rule) # type: ignore[method-assign]
return cls # type: ignore[return-value]
return cls

return _decorator
31 changes: 5 additions & 26 deletions packages/alicebot-adapter-cqhttp/alicebot/adapter/cqhttp/event.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
"""CQHTTP 适配器事件。"""
# pyright: reportIncompatibleVariableOverride=false

from typing import TYPE_CHECKING, Any, Literal, get_args, get_origin
from typing import TYPE_CHECKING, Any, Literal, get_origin
from typing_extensions import override

from pydantic import BaseModel, ConfigDict, Field
from pydantic.fields import FieldInfo
from typing_inspection import typing_objects
from typing_inspection.introspection import get_literal_values

from alicebot.event import Event
from alicebot.event import MessageEvent as BaseMessageEvent
Expand Down Expand Up @@ -61,9 +63,9 @@ def _get_literal_field(field: FieldInfo | None) -> str | None:
if field is None:
return None
annotation = field.annotation
if annotation is None or get_origin(annotation) is not Literal:
if annotation is None or not typing_objects.is_literal(get_origin(annotation)):
return None
literal_values = get_args(annotation)
literal_values = list(get_literal_values(annotation))
if len(literal_values) != 1:
return None
return literal_values[0]
Expand All @@ -72,7 +74,6 @@ def _get_literal_field(field: FieldInfo | None) -> str | None:
class CQHTTPEvent(Event["CQHTTPAdapter"]):
"""CQHTTP 事件基类"""

__event__ = ""
type: str | None = Field(alias="post_type")
time: int
self_id: int
Expand Down Expand Up @@ -103,7 +104,6 @@ def get_event_type(cls) -> tuple[str | None, str | None, str | None]:
class MessageEvent(CQHTTPEvent, BaseMessageEvent["CQHTTPAdapter"]):
"""消息事件"""

__event__ = "message"
post_type: Literal["message"]
message_type: Literal["private", "group"]
sub_type: str
Expand Down Expand Up @@ -144,7 +144,6 @@ async def reply(
class PrivateMessageEvent(MessageEvent):
"""私聊消息"""

__event__ = "message.private"
message_type: Literal["private"]
sub_type: Literal["friend", "group", "other"]

Expand All @@ -160,7 +159,6 @@ async def reply(
class GroupMessageEvent(MessageEvent):
"""群消息"""

__event__ = "message.group"
message_type: Literal["group"]
sub_type: Literal["normal", "anonymous", "notice"]
group_id: int
Expand All @@ -178,15 +176,13 @@ async def reply(
class NoticeEvent(CQHTTPEvent):
"""通知事件"""

__event__ = "notice"
post_type: Literal["notice"]
notice_type: str


class GroupUploadNoticeEvent(NoticeEvent):
"""群文件上传"""

__event__ = "notice.group_upload"
notice_type: Literal["group_upload"]
user_id: int
group_id: int
Expand All @@ -196,7 +192,6 @@ class GroupUploadNoticeEvent(NoticeEvent):
class GroupAdminNoticeEvent(NoticeEvent):
"""群管理员变动"""

__event__ = "notice.group_admin"
notice_type: Literal["group_admin"]
sub_type: Literal["set", "unset"]
user_id: int
Expand All @@ -206,7 +201,6 @@ class GroupAdminNoticeEvent(NoticeEvent):
class GroupDecreaseNoticeEvent(NoticeEvent):
"""群成员减少"""

__event__ = "notice.group_decrease"
notice_type: Literal["group_decrease"]
sub_type: Literal["leave", "kick", "kick_me"]
group_id: int
Expand All @@ -217,7 +211,6 @@ class GroupDecreaseNoticeEvent(NoticeEvent):
class GroupIncreaseNoticeEvent(NoticeEvent):
"""群成员增加"""

__event__ = "notice.group_increase"
notice_type: Literal["group_increase"]
sub_type: Literal["approve", "invite"]
group_id: int
Expand All @@ -228,7 +221,6 @@ class GroupIncreaseNoticeEvent(NoticeEvent):
class GroupBanNoticeEvent(NoticeEvent):
"""群禁言"""

__event__ = "notice.group_ban"
notice_type: Literal["group_ban"]
sub_type: Literal["ban", "lift_ban"]
group_id: int
Expand All @@ -240,15 +232,13 @@ class GroupBanNoticeEvent(NoticeEvent):
class FriendAddNoticeEvent(NoticeEvent):
"""好友添加"""

__event__ = "notice.friend_add"
notice_type: Literal["friend_add"]
user_id: int


class GroupRecallNoticeEvent(NoticeEvent):
"""群消息撤回"""

__event__ = "notice.group_recall"
notice_type: Literal["group_recall"]
group_id: int
operator_id: int
Expand All @@ -259,7 +249,6 @@ class GroupRecallNoticeEvent(NoticeEvent):
class FriendRecallNoticeEvent(NoticeEvent):
"""好友消息撤回"""

__event__ = "notice.friend_recall"
notice_type: Literal["friend_recall"]
user_id: int
message_id: int
Expand All @@ -268,7 +257,6 @@ class FriendRecallNoticeEvent(NoticeEvent):
class NotifyEvent(NoticeEvent):
"""提醒事件"""

__event__ = "notice.notify"
notice_type: Literal["notify"]
sub_type: str
user_id: int
Expand All @@ -277,7 +265,6 @@ class NotifyEvent(NoticeEvent):
class PokeNotifyEvent(NotifyEvent):
"""戳一戳"""

__event__ = "notice.notify.poke"
sub_type: Literal["poke"]
target_id: int
group_id: int | None = None
Expand All @@ -286,7 +273,6 @@ class PokeNotifyEvent(NotifyEvent):
class GroupLuckyKingNotifyEvent(NotifyEvent):
"""群红包运气王"""

__event__ = "notice.notify.lucky_king"
sub_type: Literal["lucky_king"]
group_id: int
target_id: int
Expand All @@ -295,7 +281,6 @@ class GroupLuckyKingNotifyEvent(NotifyEvent):
class GroupHonorNotifyEvent(NotifyEvent):
"""群成员荣誉变更"""

__event__ = "notice.notify.honor"
sub_type: Literal["honor"]
group_id: int
honor_type: Literal["talkative", "performer", "emotion"]
Expand All @@ -304,7 +289,6 @@ class GroupHonorNotifyEvent(NotifyEvent):
class RequestEvent(CQHTTPEvent):
"""请求事件"""

__event__ = "request"
post_type: Literal["request"]
request_type: str

Expand All @@ -328,7 +312,6 @@ async def refuse(self) -> dict[str, Any]:
class FriendRequestEvent(RequestEvent):
"""加好友请求"""

__event__ = "request.friend"
request_type: Literal["friend"]
user_id: int
comment: str
Expand Down Expand Up @@ -356,7 +339,6 @@ async def refuse(self) -> dict[str, Any]:
class GroupRequestEvent(RequestEvent):
"""加群请求 / 邀请"""

__event__ = "request.group"
request_type: Literal["group"]
sub_type: Literal["add", "invite"]
group_id: int
Expand Down Expand Up @@ -388,23 +370,20 @@ async def refuse(self, reason: str = "") -> dict[str, Any]:
class MetaEvent(CQHTTPEvent):
"""元事件"""

__event__ = "meta_event"
post_type: Literal["meta_event"]
meta_event_type: str


class LifecycleMetaEvent(MetaEvent):
"""生命周期"""

__event__ = "meta_event.lifecycle"
meta_event_type: Literal["lifecycle"]
sub_type: Literal["enable", "disable", "connect"]


class HeartbeatMetaEvent(MetaEvent):
"""心跳"""

__event__ = "meta_event.heartbeat"
meta_event_type: Literal["heartbeat"]
status: Status
interval: int
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
"""OneBot 适配器事件。"""
# pyright: reportIncompatibleVariableOverride=false

from typing import TYPE_CHECKING, Any, Literal, get_args, get_origin
from typing import TYPE_CHECKING, Any, Literal, get_origin
from typing_extensions import override

from pydantic import BaseModel, ConfigDict
from pydantic.fields import FieldInfo
from typing_inspection import typing_objects
from typing_inspection.introspection import get_literal_values

from alicebot.event import Event
from alicebot.event import MessageEvent as BaseMessageEvent
Expand Down Expand Up @@ -52,9 +54,9 @@ def _get_literal_field(field: FieldInfo | None) -> str | None:
if field is None:
return None
annotation = field.annotation
if annotation is None or get_origin(annotation) is not Literal:
if annotation is None or not typing_objects.is_literal(get_origin(annotation)):
return None
literal_values = get_args(annotation)
literal_values = list(get_literal_values(annotation))
if len(literal_values) != 1:
return None
return literal_values[0]
Expand Down
Loading
Loading