Repository: MountainDash/nonebot-plugin-send-anything-anywhere (language: python)

Issue:
Drop Python 3.8
follow https://github.com/nonebot/nonebot2/pull/2641


Discussion / hints:
所以我们可以用哪些3.9的语法了。。。

Fix patch:
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index b94b61c..6c431de 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -7,26 +7,26 @@ ci:
   autoupdate_commit_msg: ":arrow_up: auto update by pre-commit hooks"
 repos:
   - repo: https://github.com/charliermarsh/ruff-pre-commit
-    rev: "v0.0.253"
+    rev: "v0.3.7"
     hooks:
       - id: ruff
         stages: [commit]
         args: [--fix, --exit-non-zero-on-fix]
 
   - repo: https://github.com/pycqa/isort
-    rev: 5.12.0
+    rev: 5.13.2
     hooks:
       - id: isort
         stages: [commit]
 
   - repo: https://github.com/psf/black
-    rev: 23.1.0
+    rev: 24.4.0
     hooks:
       - id: black
         stages: [commit]
 
   - repo: https://github.com/pre-commit/mirrors-prettier
-    rev: v3.0.0-alpha.4
+    rev: v4.0.0-alpha.8
     hooks:
       - id: prettier
         stages: [commit]
diff --git a/nonebot_plugin_saa/abstract_factories.py b/nonebot_plugin_saa/abstract_factories.py
index bce4701..2b0b97f 100644
--- a/nonebot_plugin_saa/abstract_factories.py
+++ b/nonebot_plugin_saa/abstract_factories.py
@@ -4,21 +4,16 @@
 from warnings import warn
 from inspect import signature
 from typing_extensions import Self
+from collections.abc import Iterable, Awaitable
 from dataclasses import field, asdict, dataclass
 from typing import (
     Any,
-    Dict,
-    List,
-    Type,
-    Tuple,
     Union,
     TypeVar,
     Callable,
     ClassVar,
-    Iterable,
     NoReturn,
     Optional,
-    Awaitable,
     SupportsIndex,
     cast,
     overload,
@@ -102,7 +97,7 @@ async def do_build_custom(builder: CustomBuildFunc, bot: Bot) -> MessageSegment:
 @dataclass
 class MessageSegmentFactory(ABC):
     _builders: ClassVar[
-        Dict[
+        dict[
             SupportedAdapters,
             Union[
                 Callable[[Self], Union[MessageSegment, Awaitable[MessageSegment]]],
@@ -111,8 +106,8 @@ class MessageSegmentFactory(ABC):
         ]
     ]
 
-    data: Dict[str, Any] = field(default_factory=dict)
-    _custom_builders: Dict[SupportedAdapters, CustomBuildFunc] = field(
+    data: dict[str, Any] = field(default_factory=dict)
+    _custom_builders: dict[SupportedAdapters, CustomBuildFunc] = field(
         init=False, default_factory=dict
     )
 
@@ -261,9 +256,9 @@ def join(
         return MessageFactory(self).join(iterable)
 
 
-class MessageFactory(List[MessageSegmentFactory]):
+class MessageFactory(list[MessageSegmentFactory]):
     _text_factory: Callable[[str], MessageSegmentFactory]
-    _message_registry: Dict[SupportedAdapters, Type[Message]] = {}
+    _message_registry: ClassVar[dict[SupportedAdapters, type[Message]]] = {}
 
     @classmethod
     def register_text_ms(cls, factory: Callable[[str], MessageSegmentFactory]):
@@ -278,7 +273,7 @@ def get_text_factory(cls):
     def register_adapter_message(
         cls,
         adapter: SupportedAdapters,
-        message_class: Type[Message],
+        message_class: type[Message],
     ):
         cls._message_registry[adapter] = message_class
 
@@ -289,7 +284,7 @@ async def build(self, bot: Bot) -> Message:
     async def _build(self, bot: Bot) -> Message:
         adapter_name = extract_adapter_type(bot)
         if message_type := self._message_registry.get(adapter_name):
-            ms: List[MessageSegment] = await asyncio.gather(
+            ms: list[MessageSegment] = await asyncio.gather(
                 *[ms_factory.build(bot) for ms_factory in self],
             )
             return message_type(ms)
@@ -326,7 +321,7 @@ def __radd__(
         result = self.__class__(other)
         return result + self
 
-    def __iadd__(
+    def __iadd__(  # noqa: PYI034
         self: TMF,
         other: "str | MessageSegmentFactory | Iterable[str | MessageSegmentFactory]",
     ) -> TMF:
@@ -386,7 +381,9 @@ async def send(self, *, at_sender=False, reply=False) -> "Receipt":
             event = current_event.get()
             bot = current_bot.get()
         except LookupError as e:
-            raise RuntimeError("send() 仅能在事件响应器中使用，主动发送消息请使用 send_to") from e
+            raise RuntimeError(
+                "send() 仅能在事件响应器中使用，主动发送消息请使用 send_to"
+            ) from e
 
         target = extract_target(event, bot)
         return await self._do_send(bot, target, event, at_sender, reply)
@@ -456,7 +453,7 @@ async def _do_send(
         return await sender(bot, self, target, event, at_sender, reply)
 
     @overload
-    def __getitem__(self, args: Type[MessageSegmentFactory]) -> Self:
+    def __getitem__(self, args: type[MessageSegmentFactory]) -> Self:
         """获取仅包含指定消息段类型的消息
 
         参数:
@@ -467,7 +464,7 @@ def __getitem__(self, args: Type[MessageSegmentFactory]) -> Self:
         """
 
     @overload
-    def __getitem__(self, args: Tuple[Type[TMSF], int]) -> TMSF:
+    def __getitem__(self, args: tuple[type[TMSF], int]) -> TMSF:
         """索引指定类型的消息段
 
         参数:
@@ -478,7 +475,7 @@ def __getitem__(self, args: Tuple[Type[TMSF], int]) -> TMSF:
         """
 
     @overload
-    def __getitem__(self, args: Tuple[Type[TMSF], slice]) -> "MessageFactory":
+    def __getitem__(self, args: tuple[type[TMSF], slice]) -> "MessageFactory":
         """切片指定类型的消息段
 
         参数:
@@ -513,9 +510,9 @@ def __getitem__(self, args: slice) -> "MessageFactory":
     def __getitem__(
         self,
         args: Union[
-            Type[MessageSegmentFactory],
-            Tuple[Type[MessageSegmentFactory], int],
-            Tuple[Type[MessageSegmentFactory], slice],
+            type[MessageSegmentFactory],
+            tuple[type[MessageSegmentFactory], int],
+            tuple[type[MessageSegmentFactory], slice],
             int,
             slice,
         ],
@@ -535,7 +532,7 @@ def __getitem__(
             raise ValueError("Incorrect arguments to slice")  # pragma: no cover
 
     def __contains__(
-        self, value: Union[MessageSegmentFactory, Type[MessageSegmentFactory]]
+        self, value: Union[MessageSegmentFactory, type[MessageSegmentFactory]]
     ) -> bool:
         """检查消息段是否存在
 
@@ -549,14 +546,14 @@ def __contains__(
         return super().__contains__(value)
 
     def has(
-        self, value: Union[MessageSegmentFactory, Type[MessageSegmentFactory]]
+        self, value: Union[MessageSegmentFactory, type[MessageSegmentFactory]]
     ) -> bool:
         """与 `__contains__` 相同"""
         return value in self
 
     def index(
         self,
-        value: Union[MessageSegmentFactory, Type[MessageSegmentFactory]],
+        value: Union[MessageSegmentFactory, type[MessageSegmentFactory]],
         *args: SupportsIndex,
     ) -> int:
         """索引消息段
@@ -578,7 +575,7 @@ def index(
             return super().index(first_segment, *args)
         return super().index(value, *args)
 
-    def get(self, type_: Type[MessageSegmentFactory], count: Optional[int] = None):
+    def get(self, type_: type[MessageSegmentFactory], count: Optional[int] = None):
         """获取指定类型的消息段
 
         参数:
@@ -591,7 +588,7 @@ def get(self, type_: Type[MessageSegmentFactory], count: Optional[int] = None):
         return self[type_] if count is None else self[type_, :count]
 
     def count(
-        self, value: Union[MessageSegmentFactory, Type[MessageSegmentFactory]]
+        self, value: Union[MessageSegmentFactory, type[MessageSegmentFactory]]
     ) -> int:
         """计算指定消息段的个数
 
@@ -604,7 +601,7 @@ def count(
         return len(self[value]) if isinstance(value, type) else super().count(value)
 
     def only(
-        self, value: Union[MessageSegmentFactory, Type[MessageSegmentFactory]]
+        self, value: Union[MessageSegmentFactory, type[MessageSegmentFactory]]
     ) -> bool:
         """检查消息中是否仅包含指定消息段
 
@@ -618,7 +615,7 @@ def only(
             return all(isinstance(seg, value) for seg in self)
         return all(seg == value for seg in self)
 
-    def include(self, *types: Type[MessageSegmentFactory]) -> Self:
+    def include(self, *types: type[MessageSegmentFactory]) -> Self:
         """过滤消息
 
         参数:
@@ -629,7 +626,7 @@ def include(self, *types: Type[MessageSegmentFactory]) -> Self:
         """
         return self.__class__(seg for seg in self if isinstance(seg, types))
 
-    def exclude(self, *types: Type[MessageSegmentFactory]) -> Self:
+    def exclude(self, *types: type[MessageSegmentFactory]) -> Self:
         """过滤消息
 
         参数:
@@ -642,14 +639,14 @@ def exclude(self, *types: Type[MessageSegmentFactory]) -> Self:
 
 
 AggregatedSender = Callable[
-    [Bot, List[MessageFactory], PlatformTarget, Optional[Event]],
+    [Bot, list[MessageFactory], PlatformTarget, Optional[Event]],
     Awaitable[None],
 ]
 
 
 class AggregatedMessageFactory:
-    message_factories: List[MessageFactory]
-    sender: ClassVar[Dict[SupportedAdapters, AggregatedSender]] = {}
+    message_factories: list[MessageFactory]
+    sender: ClassVar[dict[SupportedAdapters, AggregatedSender]] = {}
 
     def __init__(
         self,
@@ -680,7 +677,7 @@ async def _send_aggregated_message_default(
         event: Optional[Event],
     ):
         for msg_fac in self.message_factories:
-            await msg_fac._do_send(bot, target, event, False, False)  # noqa: SLF001
+            await msg_fac._do_send(bot, target, event, False, False)
 
     async def _do_send(self, bot: Bot, target: PlatformTarget, event: Optional[Event]):
         adapter = extract_adapter_type(bot)
@@ -699,7 +696,9 @@ async def send(self):
             event = current_event.get()
             bot = current_bot.get()
         except LookupError as e:
-            raise RuntimeError("send() 仅能在事件响应器中使用，主动发送消息请使用 send_to") from e
+            raise RuntimeError(
+                "send() 仅能在事件响应器中使用，主动发送消息请使用 send_to"
+            ) from e
 
         target = extract_target(event, bot)
         await self._do_send(bot, target, event)
@@ -750,10 +749,10 @@ async def reject_receive(self, key: str, **kwargs) -> NoReturn:
 
 def register_ms_adapter(
     adapter: SupportedAdapters,
-    ms_factory: Type[TMSF],
+    ms_factory: type[TMSF],
 ) -> Callable[[BuildFunc], BuildFunc]:
     def decorator(builder: BuildFunc) -> BuildFunc:
-        ms_factory._builders[adapter] = builder  # noqa: SLF001
+        ms_factory._builders[adapter] = builder
         return builder
 
     return decorator
diff --git a/nonebot_plugin_saa/adapters/dodo.py b/nonebot_plugin_saa/adapters/dodo.py
index 94f717a..2ba1ef3 100644
--- a/nonebot_plugin_saa/adapters/dodo.py
+++ b/nonebot_plugin_saa/adapters/dodo.py
@@ -1,6 +1,6 @@
 from functools import partial
 from contextlib import suppress
-from typing import Any, Dict, List, Literal, Optional, cast
+from typing import Any, Literal, Optional, cast
 
 from nonebot import logger
 from nonebot.adapters import Event
@@ -9,8 +9,8 @@
 from nonebot.adapters import Bot as BaseBot
 
 from ..auto_select_bot import register_list_targets
-from ..utils import SupportedAdapters, SupportedPlatform
 from ..types import Text, Image, Reply, Mention, MentionAll
+from ..utils import SupportedAdapters, SupportedPlatform, type_message_id_check
 from ..abstract_factories import (
     MessageFactory,
     register_ms_adapter,
@@ -92,7 +92,7 @@ async def _image(image: Image, bot: BaseBot) -> MessageSegment:
 
     @register_dodo(Reply)
     def _reply(reply: Reply) -> MessageSegment:
-        assert isinstance(mid := reply.data["message_id"], DodoMessageId)
+        mid = type_message_id_check(DodoMessageId, reply.data["message_id"])
         return MessageSegment.reference(mid.message_id)
 
     @register_dodo(Mention)
@@ -102,7 +102,7 @@ def _mention(mention: Mention) -> MessageSegment:
     @register_dodo(MentionAll)
     def _mention_all(m: MentionAll) -> MessageSegment:
         logger.warning(
-            "DODO does not support to send @all yet, ignore.\nsee: https://open.imdodo.com/dev/api/message.html#%E6%B6%88%E6%81%AF%E8%AF%AD%E6%B3%95"  # noqa: E501
+            "DODO does not support to send @all yet, ignore.\nsee: https://open.imdodo.com/dev/api/message.html#%E6%B6%88%E6%81%AF%E8%AF%AD%E6%B3%95"
         )
         if text := m.data.get("special_fallback", {}).get(adapter):
             return MessageSegment.text(text)
@@ -158,7 +158,7 @@ def _extract_personal_msg_event(event: Event) -> TargetDoDoPrivate:
         )
 
     @register_convert_to_arg(adapter, SupportedPlatform.dodo_channel)
-    def _gen_channel(target: PlatformTarget) -> Dict[str, Any]:
+    def _gen_channel(target: PlatformTarget) -> dict[str, Any]:
         assert isinstance(target, TargetDoDoChannel)
         args = {
             "channel_id": target.channel_id,
@@ -168,7 +168,7 @@ def _gen_channel(target: PlatformTarget) -> Dict[str, Any]:
         return args
 
     @register_convert_to_arg(adapter, SupportedPlatform.dodo_private)
-    def _gen_private(target: PlatformTarget) -> Dict[str, Any]:
+    def _gen_private(target: PlatformTarget) -> dict[str, Any]:
         assert isinstance(target, TargetDoDoPrivate)
         return {
             "dodo_source_id": target.dodo_source_id,
@@ -248,7 +248,7 @@ async def send(
         return DodoReceipt(message_id=resp, bot_id=bot.self_id)
 
     @register_list_targets(adapter)
-    async def list_targets(bot: BaseBot) -> List[PlatformTarget]:
+    async def list_targets(bot: BaseBot) -> list[PlatformTarget]:
         assert isinstance(bot, BotDodo)
         targets = []
         for island in await bot.get_island_list():
diff --git a/nonebot_plugin_saa/adapters/feishu.py b/nonebot_plugin_saa/adapters/feishu.py
index 151f78c..d6aca03 100644
--- a/nonebot_plugin_saa/adapters/feishu.py
+++ b/nonebot_plugin_saa/adapters/feishu.py
@@ -1,14 +1,14 @@
 from io import BytesIO
 from pathlib import Path
 from functools import partial
-from typing import Any, Dict, Literal, cast
+from typing import Any, Literal, cast
 
 from nonebot.adapters import Event
 from nonebot.drivers import Request
 from nonebot.adapters import Bot as BaseBot
 
-from ..utils import SupportedAdapters
 from ..types import Text, Image, Reply, Mention, MentionAll
+from ..utils import SupportedAdapters, type_message_id_check
 from ..abstract_factories import (
     MessageFactory,
     register_ms_adapter,
@@ -84,7 +84,7 @@ def _mention_all(m: MentionAll) -> MessageSegment:
 
     @register_feishu(Reply)
     def _reply(r: Reply) -> MessageSegment:
-        assert isinstance(mid := r.data["message_id"], FeishuMessageId)
+        mid = type_message_id_check(FeishuMessageId, r.data["message_id"])
         return MessageSegment("reply", {"message_id": mid.message_id})
 
     @register_target_extractor(PrivateMessageEvent)
@@ -100,7 +100,7 @@ def _extract_channel_msg_event(event: Event) -> TargetFeishuGroup:
     class FeishuReceipt(Receipt):
         message_id: str
         adapter_name: Literal[adapter] = adapter
-        data: Dict[str, Any]
+        data: dict[str, Any]
 
         async def revoke(self):
             bot = cast(Bot, self._get_bot())
@@ -151,8 +151,8 @@ async def send(
         message_to_send = Message()
         for message_segment_factory in full_msg:
             if isinstance(message_segment_factory, Reply):
-                assert isinstance(
-                    mid := message_segment_factory.data["message_id"], FeishuMessageId
+                mid = type_message_id_check(
+                    FeishuMessageId, message_segment_factory.data["message_id"]
                 )
                 reply_to_message_id = mid.message_id
                 continue
diff --git a/nonebot_plugin_saa/adapters/kaiheila.py b/nonebot_plugin_saa/adapters/kaiheila.py
index 8bef652..0eee4dc 100644
--- a/nonebot_plugin_saa/adapters/kaiheila.py
+++ b/nonebot_plugin_saa/adapters/kaiheila.py
@@ -1,12 +1,12 @@
 from functools import partial
-from typing import Any, Dict, List, Literal, cast
+from typing import Any, Literal, cast
 
 from nonebot.adapters import Event
 from nonebot.adapters import Bot as BaseBot
 
 from ..auto_select_bot import register_list_targets
-from ..utils import SupportedAdapters, SupportedPlatform
 from ..types import Text, Image, Reply, Mention, MentionAll
+from ..utils import SupportedAdapters, SupportedPlatform, type_message_id_check
 from ..abstract_factories import (
     MessageFactory,
     register_ms_adapter,
@@ -87,7 +87,7 @@ def _mention_all(m: MentionAll) -> MessageSegment:
 
     @register_kaiheila(Reply)
     def _reply(r: Reply) -> MessageSegment:
-        assert isinstance(mid := r.data["message_id"], KaiheilaMessageId)
+        mid = type_message_id_check(KaiheilaMessageId, r.data["message_id"])
         return MessageSegment.quote(mid.message_id)
 
     @register_target_extractor(PrivateMessageEvent)
@@ -101,14 +101,14 @@ def _extract_channel_msg_event(event: Event) -> TargetKaiheilaChannel:
         return TargetKaiheilaChannel(channel_id=event.target_id)
 
     @register_convert_to_arg(adapter, SupportedPlatform.kaiheila_private)
-    def _gen_private(target: PlatformTarget) -> Dict[str, Any]:
+    def _gen_private(target: PlatformTarget) -> dict[str, Any]:
         assert isinstance(target, TargetKaiheilaPrivate)
         return {
             "user_id": target.user_id,
         }
 
     @register_convert_to_arg(adapter, SupportedPlatform.kaiheila_channel)
-    def _gen_channel(target: PlatformTarget) -> Dict[str, Any]:
+    def _gen_channel(target: PlatformTarget) -> dict[str, Any]:
         assert isinstance(target, TargetKaiheilaChannel)
         return {
             "channel_id": target.channel_id,
@@ -170,7 +170,7 @@ async def send(
         return KaiheilaReceipt(bot_id=bot.self_id, data=resp)
 
     @register_list_targets(SupportedAdapters.kaiheila)
-    async def list_targets(bot: BaseBot) -> List[PlatformTarget]:
+    async def list_targets(bot: BaseBot) -> list[PlatformTarget]:
         assert isinstance(bot, Bot)
 
         targets = []
@@ -187,7 +187,8 @@ async def list_targets(bot: BaseBot) -> List[PlatformTarget]:
 
         async for user_chat in _unwrap_paging_api("user_chats")(bot.userChat_list)():
             user_chat: UserChat
-            assert user_chat.target_info and user_chat.target_info.id_
+            assert user_chat.target_info
+            assert user_chat.target_info.id_
             target = TargetKaiheilaPrivate(user_id=user_chat.target_info.id_)
             targets.append(target)
 
diff --git a/nonebot_plugin_saa/adapters/onebot_v11.py b/nonebot_plugin_saa/adapters/onebot_v11.py
index 3c616a9..93dbffa 100644
--- a/nonebot_plugin_saa/adapters/onebot_v11.py
+++ b/nonebot_plugin_saa/adapters/onebot_v11.py
@@ -1,11 +1,11 @@
 from functools import partial
-from typing import Any, Dict, List, Union, Literal, Optional, cast
+from typing import Any, Union, Literal, Optional, cast
 
 from nonebot.adapters import Bot, Event
 
 from ..auto_select_bot import register_list_targets
-from ..utils import SupportedAdapters, SupportedPlatform
 from ..types import Text, Image, Reply, Mention, MentionAll
+from ..utils import SupportedAdapters, SupportedPlatform, type_message_id_check
 from ..abstract_factories import (
     MessageFactory,
     AggregatedMessageFactory,
@@ -74,7 +74,7 @@ async def _mention_all(m: MentionAll) -> MessageSegment:
 
     @register_onebot_v11(Reply)
     async def _reply(r: Reply) -> MessageSegment:
-        assert isinstance(mid := r.data["message_id"], OB11MessageId)
+        mid = type_message_id_check(OB11MessageId, r.data["message_id"])
         return MessageSegment.reply(mid.message_id)
 
     @register_target_extractor(PrivateMessageEvent)
@@ -145,7 +145,7 @@ def _(event: Event) -> OB11MessageId:
         return OB11MessageId(message_id=event.message_id)
 
     @register_convert_to_arg(adapter, SupportedPlatform.qq_private)
-    def _gen_private(target: PlatformTarget) -> Dict[str, Any]:
+    def _gen_private(target: PlatformTarget) -> dict[str, Any]:
         assert isinstance(target, TargetQQPrivate)
         return {
             "message_type": "private",
@@ -153,7 +153,7 @@ def _gen_private(target: PlatformTarget) -> Dict[str, Any]:
         }
 
     @register_convert_to_arg(adapter, SupportedPlatform.qq_group)
-    def _gen_group(target: PlatformTarget) -> Dict[str, Any]:
+    def _gen_group(target: PlatformTarget) -> dict[str, Any]:
         assert isinstance(target, TargetQQGroup)
         return {
             "message_type": "group",
@@ -210,14 +210,14 @@ async def send(
     @AggregatedMessageFactory.register_aggregated_sender(adapter)
     async def aggregate_send(
         bot: Bot,
-        message_factories: List[MessageFactory],
+        message_factories: list[MessageFactory],
         target: PlatformTarget,
         event: Optional[Event],
     ):
         assert isinstance(bot, BotOB11)
         login_info = await bot.get_login_info()
 
-        msg_list: List[Message] = []
+        msg_list: list[Message] = []
         for msg_fac in message_factories:
             msg = await msg_fac.build(bot)
             assert isinstance(msg, Message)
@@ -245,7 +245,7 @@ async def aggregate_send(
             raise RuntimeError(f"{target.__class__.__name__} not supported")
 
     @register_list_targets(SupportedAdapters.onebot_v11)
-    async def list_targets(bot: Bot) -> List[PlatformTarget]:
+    async def list_targets(bot: Bot) -> list[PlatformTarget]:
         assert isinstance(bot, BotOB11)
 
         targets = []
diff --git a/nonebot_plugin_saa/adapters/onebot_v12.py b/nonebot_plugin_saa/adapters/onebot_v12.py
index b2c2d90..2722629 100644
--- a/nonebot_plugin_saa/adapters/onebot_v12.py
+++ b/nonebot_plugin_saa/adapters/onebot_v12.py
@@ -1,15 +1,15 @@
 from io import BytesIO
 from pathlib import Path
+from typing import Literal
 from functools import partial
-from typing import List, Literal
 
 from nonebot.adapters import Event
 from nonebot.compat import model_dump
 from nonebot.adapters import Bot as BaseBot
 
 from ..auto_select_bot import register_list_targets
-from ..utils import SupportedAdapters, SupportedPlatform
 from ..types import Text, Image, Reply, Mention, MentionAll
+from ..utils import SupportedAdapters, SupportedPlatform, type_message_id_check
 from ..abstract_factories import (
     MessageFactory,
     register_ms_adapter,
@@ -101,7 +101,7 @@ async def _mention_all(m: MentionAll) -> MessageSegment:
 
     @register_onebot_v12(Reply)
     async def _reply(r: Reply) -> MessageSegment:
-        assert isinstance(mid := r.data["message_id"], OB12MessageId)
+        mid = type_message_id_check(OB12MessageId, r.data["message_id"])
         return MessageSegment.reply(mid.message_id)
 
     @register_target_extractor(PrivateMessageEvent)
@@ -314,7 +314,7 @@ async def send(
         return OB12Receipt(bot_id=bot.self_id, message_id=message_id)
 
     @register_list_targets(SupportedAdapters.onebot_v12)
-    async def list_targets(bot: BaseBot) -> List[PlatformTarget]:
+    async def list_targets(bot: BaseBot) -> list[PlatformTarget]:
         assert isinstance(bot, Bot)
 
         targets = []
diff --git a/nonebot_plugin_saa/adapters/qq.py b/nonebot_plugin_saa/adapters/qq.py
index 9d81aaa..4fdb705 100644
--- a/nonebot_plugin_saa/adapters/qq.py
+++ b/nonebot_plugin_saa/adapters/qq.py
@@ -1,13 +1,13 @@
 from functools import partial
-from typing import List, Union, Literal, Optional
+from typing import Union, Literal, Optional
 
 from nonebot.adapters import Event
 from nonebot.adapters import Bot as BaseBot
 
 from ..config import plugin_config
-from ..utils import SupportedAdapters
 from ..auto_select_bot import register_list_targets
 from ..types import Text, Image, Reply, Mention, MentionAll
+from ..utils import SupportedAdapters, type_message_id_check
 from ..abstract_factories import (
     MessageFactory,
     register_ms_adapter,
@@ -77,14 +77,15 @@ def _mention_all(m: MentionAll) -> MessageSegment:
 
     @register_qq(Reply)
     def _reply(r: Reply) -> MessageSegment:
-        assert isinstance(mid := r.data["message_id"], QQMessageId)
+        mid = type_message_id_check(QQMessageId, r.data["message_id"])
         return MessageSegment.reference(mid.message_id)
 
     @register_target_extractor(GuildMessageEvent)
     def extract_message_event(event: Event) -> PlatformTarget:
         if isinstance(event, DirectMessageCreateEvent):
             assert event.guild_id
-            assert event.author and event.author.id
+            assert event.author
+            assert event.author.id
             return TargetQQGuildDirect(
                 source_guild_id=int(event.guild_id),
                 recipient_id=int(event.author.id),
@@ -225,7 +226,7 @@ async def send(
         return QQReceipt(bot_id=bot.self_id, msg_return=msg_return)
 
     @register_list_targets(SupportedAdapters.qq)
-    async def list_targets(bot: BaseBot) -> List[PlatformTarget]:
+    async def list_targets(bot: BaseBot) -> list[PlatformTarget]:
         assert isinstance(bot, Bot)
 
         targets = []
diff --git a/nonebot_plugin_saa/adapters/red.py b/nonebot_plugin_saa/adapters/red.py
index 00014d8..10ae557 100644
--- a/nonebot_plugin_saa/adapters/red.py
+++ b/nonebot_plugin_saa/adapters/red.py
@@ -1,14 +1,14 @@
 from datetime import datetime
 from functools import partial
-from typing import Any, Dict, List, Literal, Optional, cast
+from typing import Any, Literal, Optional, cast
 
 from nonebot import get_driver
 from nonebot.adapters import Bot, Event
 from nonebot.drivers import Request, HTTPClientMixin
 
 from ..auto_select_bot import register_list_targets
-from ..utils import SupportedAdapters, SupportedPlatform
 from ..types import Text, Image, Reply, Mention, MentionAll
+from ..utils import SupportedAdapters, SupportedPlatform, type_message_id_check
 from ..abstract_factories import (
     MessageFactory,
     AggregatedMessageFactory,
@@ -75,7 +75,7 @@ async def _mention_all(m: MentionAll) -> MessageSegment:
 
     @register_red(Reply)
     async def _reply(r: Reply) -> MessageSegment:
-        assert isinstance(mid := r.data["message_id"], RedMessageId)
+        mid = type_message_id_check(RedMessageId, r.data["message_id"])
         return MessageSegment.reply(
             message_seq=mid.message_seq,
             message_id=mid.message_id,
@@ -95,7 +95,7 @@ def _extract_group_msg_event(event: Event) -> TargetQQGroup:
         return TargetQQGroup(group_id=int(event.peerUin))
 
     @register_convert_to_arg(adapter, SupportedPlatform.qq_private)
-    def _gen_private(target: PlatformTarget) -> Dict[str, Any]:
+    def _gen_private(target: PlatformTarget) -> dict[str, Any]:
         assert isinstance(target, TargetQQPrivate)
         return {
             "chat_type": ChatType.FRIEND,
@@ -103,7 +103,7 @@ def _gen_private(target: PlatformTarget) -> Dict[str, Any]:
         }
 
     @register_convert_to_arg(adapter, SupportedPlatform.qq_group)
-    def _gen_group(target: PlatformTarget) -> Dict[str, Any]:
+    def _gen_group(target: PlatformTarget) -> dict[str, Any]:
         assert isinstance(target, TargetQQGroup)
         return {
             "chat_type": ChatType.GROUP,
@@ -172,13 +172,13 @@ async def send(
     @AggregatedMessageFactory.register_aggregated_sender(adapter)
     async def aggregate_send(
         bot: Bot,
-        message_factories: List[MessageFactory],
+        message_factories: list[MessageFactory],
         target: PlatformTarget,
         event: Optional[Event],
     ):
         assert isinstance(bot, BotRed)
 
-        msg_list: List[Message] = []
+        msg_list: list[Message] = []
         for msg_fac in message_factories:
             msg = await msg_fac.build(bot)
             assert isinstance(msg, Message)
@@ -205,7 +205,7 @@ async def aggregate_send(
             raise RuntimeError(f"{target.__class__.__name__} not supported")
 
     @register_list_targets(SupportedAdapters.red)
-    async def list_targets(bot: Bot) -> List[PlatformTarget]:
+    async def list_targets(bot: Bot) -> list[PlatformTarget]:
         assert isinstance(bot, BotRed)
 
         targets = []
diff --git a/nonebot_plugin_saa/adapters/satori.py b/nonebot_plugin_saa/adapters/satori.py
index eec2852..95362d0 100644
--- a/nonebot_plugin_saa/adapters/satori.py
+++ b/nonebot_plugin_saa/adapters/satori.py
@@ -2,26 +2,16 @@
 from io import BytesIO
 from pathlib import Path
 from functools import partial
-from typing import (
-    Any,
-    Dict,
-    List,
-    Generic,
-    Literal,
-    TypeVar,
-    Optional,
-    Protocol,
-    Awaitable,
-    cast,
-)
+from collections.abc import Awaitable
+from typing import Any, Generic, Literal, TypeVar, Optional, Protocol, cast
 
 from nonebot import logger
 from filetype import guess_mime
 from nonebot.adapters import Bot, Event
 
 from ..auto_select_bot import register_list_targets
-from ..utils import SupportedAdapters, SupportedPlatform
 from ..types import Text, Image, Reply, Mention, MentionAll
+from ..utils import SupportedAdapters, SupportedPlatform, type_message_id_check
 from ..abstract_factories import (
     MessageFactory,
     AggregatedMessageFactory,
@@ -117,7 +107,7 @@ async def _mention_all(m: MentionAll) -> MessageSegment:
 
     @register_satori(Reply)
     async def _reply(r: Reply) -> MessageSegment:
-        assert isinstance(mid := r.data["message_id"], SatoriMessageId)
+        mid = type_message_id_check(SatoriMessageId, r.data["message_id"])
         return MessageSegment.quote(mid.message_id)
 
     @register_target_extractor(PrivateMessageEvent)
@@ -154,27 +144,27 @@ def _extract_group_msg_event(event: Event) -> PlatformTarget:
         return TargetSatoriUnknown(platform=event.platform, channel_id=event.channel.id)
 
     @register_convert_to_arg(adapter, SupportedPlatform.qq_private)
-    def _gen_qq_private(target: PlatformTarget) -> Dict[str, Any]:
+    def _gen_qq_private(target: PlatformTarget) -> dict[str, Any]:
         assert isinstance(target, TargetQQPrivate)
         return {"channel_id": f"private:{target.user_id}"}
 
     @register_convert_to_arg(adapter, SupportedPlatform.qq_group)
-    def _gen_qq_group(target: PlatformTarget) -> Dict[str, Any]:
+    def _gen_qq_group(target: PlatformTarget) -> dict[str, Any]:
         assert isinstance(target, TargetQQGroup)
         return {"channel_id": str(target.group_id)}
 
     @register_convert_to_arg(adapter, SupportedPlatform.feishu_private)
-    def _gen_feishu_private(target: PlatformTarget) -> Dict[str, Any]:
+    def _gen_feishu_private(target: PlatformTarget) -> dict[str, Any]:
         assert isinstance(target, TargetFeishuPrivate)
         return {"channel_id": target.open_id}
 
     @register_convert_to_arg(adapter, SupportedPlatform.feishu_group)
-    def _gen_feishu_group(target: PlatformTarget) -> Dict[str, Any]:
+    def _gen_feishu_group(target: PlatformTarget) -> dict[str, Any]:
         assert isinstance(target, TargetFeishuGroup)
         return {"channel_id": target.chat_id}
 
     @register_convert_to_arg(adapter, SupportedPlatform.telegram_common)
-    def _gen_telegram_common(target: PlatformTarget) -> Dict[str, Any]:
+    def _gen_telegram_common(target: PlatformTarget) -> dict[str, Any]:
         assert isinstance(target, TargetTelegramCommon)
         return {"channel_id": target.chat_id}
 
@@ -188,7 +178,7 @@ def _to_unknow(target: PlatformTarget):
 
     class SatoriReceipt(Receipt):
         adapter_name: Literal[adapter] = adapter
-        messages: List[SatoriMessage]
+        messages: list[SatoriMessage]
 
         async def revoke(self):
             for message in self.messages:
@@ -199,7 +189,7 @@ async def revoke(self):
                 )
 
         @property
-        def raw(self) -> List[SatoriMessage]:
+        def raw(self) -> list[SatoriMessage]:
             return self.messages
 
         def extract_message_id(self, index: int = 0) -> MessageId:
@@ -250,13 +240,13 @@ async def send(
     @AggregatedMessageFactory.register_aggregated_sender(adapter)
     async def aggregate_send(
         bot: Bot,
-        message_factories: List[MessageFactory],
+        message_factories: list[MessageFactory],
         target: PlatformTarget,
         event: Optional[Event],
     ):
         assert isinstance(bot, BotSatori)
 
-        msg_list: List[Message] = []
+        msg_list: list[Message] = []
         for msg_fac in message_factories:
             msg = await msg_fac.build(bot)
             assert isinstance(msg, Message)
@@ -275,12 +265,12 @@ async def aggregate_send(
     class PagedAPI(Generic[T], Protocol):
         def __call__(
             self, *, next_token: Optional[str] = None
-        ) -> Awaitable[PageResult[T]]:
-            ...
+        ) -> Awaitable[PageResult[T]]: ...
 
-    async def _fetch_all(paged_api: PagedAPI[T]) -> List[T]:
+    async def _fetch_all(paged_api: PagedAPI[T]) -> list[T]:
         results = []
-        # nonebor-adapter-satori < 0.10.2 的 `channel_list` API 会因为 next_token 为 None 而报错
+        # nonebor-adapter-satori < 0.10.2 的 `channel_list` API
+        # 会因为 next_token 为 None 而报错
         token = None
         while True:
             resp = await paged_api(next_token=token)
@@ -293,7 +283,7 @@ async def _fetch_all(paged_api: PagedAPI[T]) -> List[T]:
         return results
 
     @register_list_targets(SupportedAdapters.satori)
-    async def list_targets(bot: Bot) -> List[PlatformTarget]:
+    async def list_targets(bot: Bot) -> list[PlatformTarget]:
         assert isinstance(bot, BotSatori)
 
         targets = []
diff --git a/nonebot_plugin_saa/adapters/telegram.py b/nonebot_plugin_saa/adapters/telegram.py
index f219ad1..6ccccce 100644
--- a/nonebot_plugin_saa/adapters/telegram.py
+++ b/nonebot_plugin_saa/adapters/telegram.py
@@ -2,13 +2,13 @@
 from io import BytesIO
 from pathlib import Path
 from functools import partial
-from typing import TYPE_CHECKING, List, Union, Literal, Optional, cast
+from typing import TYPE_CHECKING, Union, Literal, Optional, cast
 
 import anyio
 from nonebot import logger
 
-from ..utils import SupportedAdapters
 from ..types import Text, Image, Reply, Mention, MentionAll
+from ..utils import SupportedAdapters, type_message_id_check
 from ..abstract_factories import (
     MessageFactory,
     register_ms_adapter,
@@ -93,7 +93,7 @@ def _mention_all(m: MentionAll) -> MessageSegment:
 
     @register_telegram(Reply)
     async def _reply(r: Reply) -> MessageSegment:
-        assert isinstance(mid := r.data["message_id"], TelegramMessageId)
+        mid = type_message_id_check(TelegramMessageId, r.data["message_id"])
         return TGReply.reply(mid.message_id, mid.chat_id)
 
     @register_target_extractor(PrivateMessageEvent)
@@ -135,7 +135,7 @@ def build_mention_from_event(event: MessageEvent) -> MessageSegment:
 
     class TelegramReceipt(Receipt):
         chat_id: Union[int, str]
-        messages: List[MessageModel]
+        messages: list[MessageModel]
         adapter_name: Literal[adapter] = adapter
 
         async def revoke(self):
@@ -222,7 +222,7 @@ async def send(
             else None
         )
         message_sent = cast(
-            Union[MessageModel, List[MessageModel]],
+            Union[MessageModel, list[MessageModel]],
             await bot.send_to(
                 chat_id,
                 message_to_send,
diff --git a/nonebot_plugin_saa/auto_select_bot.py b/nonebot_plugin_saa/auto_select_bot.py
index fd6c124..7a5a9f2 100644
--- a/nonebot_plugin_saa/auto_select_bot.py
+++ b/nonebot_plugin_saa/auto_select_bot.py
@@ -1,8 +1,10 @@
-""" 提供获取 Bot 的方法 """
+"""提供获取 Bot 的方法"""
+
 import json
 import random
 import asyncio
-from typing import Set, Dict, List, Callable, Awaitable
+from typing import Callable
+from collections.abc import Awaitable
 
 import nonebot
 from nonebot.adapters import Bot
@@ -17,12 +19,12 @@
     extract_adapter_type,
 )
 
-BOT_CACHE: Dict[Bot, Set[PlatformTarget]] = {}
+BOT_CACHE: dict[Bot, set[PlatformTarget]] = {}
 BOT_CACHE_LOCK = asyncio.Lock()
 
-ListTargetsFunc = Callable[[Bot], Awaitable[List[PlatformTarget]]]
+ListTargetsFunc = Callable[[Bot], Awaitable[list[PlatformTarget]]]
 
-list_targets_map: Dict[str, ListTargetsFunc] = {}
+list_targets_map: dict[str, ListTargetsFunc] = {}
 
 inited = False
 
diff --git a/nonebot_plugin_saa/registries/message_id.py b/nonebot_plugin_saa/registries/message_id.py
index 6f3a8fc..53a3bef 100644
--- a/nonebot_plugin_saa/registries/message_id.py
+++ b/nonebot_plugin_saa/registries/message_id.py
@@ -1,6 +1,5 @@
 from abc import ABC
-from typing_extensions import Annotated
-from typing import Dict, Type, Callable, Optional
+from typing import Callable, Optional, Annotated
 
 from nonebot.adapters import Event
 from nonebot.params import Depends
@@ -17,10 +16,10 @@ class MessageId(SerializationMeta, ABC):
 
 MessageIdGetter = Callable[[Event], MessageId]
 
-_get_message_id_dict: Dict[Type[Event], MessageIdGetter] = {}
+_get_message_id_dict: dict[type[Event], MessageIdGetter] = {}
 
 
-def register_message_id_getter(event_type: Type[Event]):
+def register_message_id_getter(event_type: type[Event]):
     def register(getter: MessageIdGetter):
         _get_message_id_dict[event_type] = getter
         return getter
diff --git a/nonebot_plugin_saa/registries/meta.py b/nonebot_plugin_saa/registries/meta.py
index e0691a2..dd2bb6b 100644
--- a/nonebot_plugin_saa/registries/meta.py
+++ b/nonebot_plugin_saa/registries/meta.py
@@ -1,8 +1,8 @@
 import json
 from abc import ABC
 from enum import Enum
+from typing import Any, ClassVar
 from typing_extensions import Self
-from typing import Any, Dict, ClassVar
 
 from pydantic import BaseModel
 from nonebot.compat import PYDANTIC_V2, ConfigDict, type_validate_python
@@ -16,7 +16,7 @@ class Level(Enum):
 
 class SerializationMeta(BaseModel, ABC):
     _index_key: ClassVar[str]
-    _deserializer_dict: ClassVar[Dict]
+    _deserializer_dict: ClassVar[dict]
     _level: ClassVar[Level] = Level.MetaBase
 
     if PYDANTIC_V2:
diff --git a/nonebot_plugin_saa/registries/platform_send_target.py b/nonebot_plugin_saa/registries/platform_send_target.py
index cde7ec1..89cffb7 100644
--- a/nonebot_plugin_saa/registries/platform_send_target.py
+++ b/nonebot_plugin_saa/registries/platform_send_target.py
@@ -1,17 +1,14 @@
 import inspect
-from typing_extensions import Annotated
+from collections.abc import Awaitable
 from typing import (
     TYPE_CHECKING,
     Any,
-    Dict,
-    Type,
-    Tuple,
     Union,
     Literal,
     Callable,
     ClassVar,
     Optional,
-    Awaitable,
+    Annotated,
     cast,
 )
 
@@ -92,9 +89,9 @@ class TargetQQGroupOpenId(PlatformTarget, BotSpecifier):
         group_openid: 群 open_id
     """
 
-    platform_type: Literal[
+    platform_type: Literal[SupportedPlatform.qq_group_openid] = (
         SupportedPlatform.qq_group_openid
-    ] = SupportedPlatform.qq_group_openid
+    )
     group_openid: str
 
 
@@ -105,9 +102,9 @@ class TargetQQPrivateOpenId(PlatformTarget, BotSpecifier):
         user_openid: 用户 open_id
     """
 
-    platform_type: Literal[
+    platform_type: Literal[SupportedPlatform.qq_private_openid] = (
         SupportedPlatform.qq_private_openid
-    ] = SupportedPlatform.qq_private_openid
+    )
     user_openid: str
 
 
@@ -118,9 +115,9 @@ class TargetQQGuildChannel(PlatformTarget):
         channel_id: 子频道号
     """
 
-    platform_type: Literal[
+    platform_type: Literal[SupportedPlatform.qq_guild_channel] = (
         SupportedPlatform.qq_guild_channel
-    ] = SupportedPlatform.qq_guild_channel
+    )
     channel_id: int
 
 
@@ -132,9 +129,9 @@ class TargetQQGuildDirect(PlatformTarget):
         source_guild_id: 来自的频道号
     """
 
-    platform_type: Literal[
+    platform_type: Literal[SupportedPlatform.qq_guild_direct] = (
         SupportedPlatform.qq_guild_direct
-    ] = SupportedPlatform.qq_guild_direct
+    )
     recipient_id: int
     source_guild_id: int
 
@@ -147,9 +144,9 @@ class TargetOB12Unknow(PlatformTarget):
         user_id, group_id, channel_id, guild_id: 同 ob12 定义
     """
 
-    platform_type: Literal[
+    platform_type: Literal[SupportedPlatform.unknown_ob12] = (
         SupportedPlatform.unknown_ob12
-    ] = SupportedPlatform.unknown_ob12
+    )
     platform: str
     detail_type: Literal["private", "group", "channel"]
     user_id: Optional[str] = None
@@ -168,9 +165,9 @@ class TargetSatoriUnknown(PlatformTarget):
         channel_id: 频道 ID
     """
 
-    platform_type: Literal[
+    platform_type: Literal[SupportedPlatform.unknown_satori] = (
         SupportedPlatform.unknown_satori
-    ] = SupportedPlatform.unknown_satori
+    )
     platform: str
     user_id: Optional[str] = None
     guild_id: Optional[str] = None
@@ -184,9 +181,9 @@ class TargetKaiheilaChannel(PlatformTarget):
         channel_id: 频道ID
     """
 
-    platform_type: Literal[
+    platform_type: Literal[SupportedPlatform.kaiheila_channel] = (
         SupportedPlatform.kaiheila_channel
-    ] = SupportedPlatform.kaiheila_channel
+    )
     channel_id: str
 
 
@@ -197,9 +194,9 @@ class TargetKaiheilaPrivate(PlatformTarget):
         user_id: 接收人ID
     """
 
-    platform_type: Literal[
+    platform_type: Literal[SupportedPlatform.kaiheila_private] = (
         SupportedPlatform.kaiheila_private
-    ] = SupportedPlatform.kaiheila_private
+    )
     user_id: str
 
 
@@ -210,9 +207,9 @@ class TargetTelegramCommon(PlatformTarget):
         user_id: 对话ID
     """
 
-    platform_type: Literal[
+    platform_type: Literal[SupportedPlatform.telegram_common] = (
         SupportedPlatform.telegram_common
-    ] = SupportedPlatform.telegram_common
+    )
     chat_id: Union[int, str]
 
 
@@ -224,9 +221,9 @@ class TargetTelegramForum(PlatformTarget):
         message_thread_id: 板块ID
     """
 
-    platform_type: Literal[
+    platform_type: Literal[SupportedPlatform.telegram_forum] = (
         SupportedPlatform.telegram_forum
-    ] = SupportedPlatform.telegram_forum
+    )
     chat_id: int
     message_thread_id: int
 
@@ -238,9 +235,9 @@ class TargetFeishuPrivate(PlatformTarget):
         open_id: 用户 Open ID
     """
 
-    platform_type: Literal[
+    platform_type: Literal[SupportedPlatform.feishu_private] = (
         SupportedPlatform.feishu_private
-    ] = SupportedPlatform.feishu_private
+    )
     open_id: str
 
 
@@ -251,9 +248,9 @@ class TargetFeishuGroup(PlatformTarget):
         chat_id: 群 ID
     """
 
-    platform_type: Literal[
+    platform_type: Literal[SupportedPlatform.feishu_group] = (
         SupportedPlatform.feishu_group
-    ] = SupportedPlatform.feishu_group
+    )
     chat_id: str
 
 
@@ -265,9 +262,9 @@ class TargetDoDoChannel(PlatformTarget):
         dodo_source_id: 用户 ID(可选)
     """
 
-    platform_type: Literal[
+    platform_type: Literal[SupportedPlatform.dodo_channel] = (
         SupportedPlatform.dodo_channel
-    ] = SupportedPlatform.dodo_channel
+    )
     channel_id: str
     dodo_source_id: Optional[str] = None
 
@@ -280,9 +277,9 @@ class TargetDoDoPrivate(PlatformTarget):
         island_source_id: 群 ID
     """
 
-    platform_type: Literal[
+    platform_type: Literal[SupportedPlatform.dodo_private] = (
         SupportedPlatform.dodo_private
-    ] = SupportedPlatform.dodo_private
+    )
     island_source_id: str
     dodo_source_id: str
 
@@ -308,8 +305,8 @@ class TargetDoDoPrivate(PlatformTarget):
 ]
 
 
-ConvertToArg = Callable[[PlatformTarget], Dict[str, Any]]
-convert_to_arg_map: Dict[Tuple[SupportedPlatform, SupportedAdapters], ConvertToArg] = {}
+ConvertToArg = Callable[[PlatformTarget], dict[str, Any]]
+convert_to_arg_map: dict[tuple[SupportedPlatform, SupportedAdapters], ConvertToArg] = {}
 
 
 def register_convert_to_arg(adapter: SupportedAdapters, platform: SupportedPlatform):
@@ -322,10 +319,10 @@ def wrapper(func: ConvertToArg):
 
 Extractor = Callable[[Event], PlatformTarget]
 ExtractorWithBotSpecifier = Callable[[Event, Bot], PlatformTarget]
-extractor_map: Dict[Type[Event], Union[Extractor, ExtractorWithBotSpecifier]] = {}
+extractor_map: dict[type[Event], Union[Extractor, ExtractorWithBotSpecifier]] = {}
 
 
-def register_target_extractor(event: Type[Event]):
+def register_target_extractor(event: type[Event]):
     def wrapper(func: Union[Extractor, ExtractorWithBotSpecifier]):
         extractor_map[event] = func
         return func
@@ -364,7 +361,7 @@ def get_target(event: Event, bot: Optional[Bot] = None) -> Optional[PlatformTarg
     Awaitable["Receipt"],
 ]
 
-sender_map: Dict[SupportedAdapters, Sender] = {}
+sender_map: dict[SupportedAdapters, Sender] = {}
 
 
 def register_sender(adapter: SupportedAdapters):
@@ -378,7 +375,7 @@ def wrapper(sender: Sender):
 SaaTarget = Annotated[PlatformTarget, Depends(get_target)]
 
 QQGuild_DMS = Callable[[TargetQQGuildDirect, Bot], Awaitable[int]]
-qqguild_dms_map: Dict[SupportedAdapters, QQGuild_DMS] = {}
+qqguild_dms_map: dict[SupportedAdapters, QQGuild_DMS] = {}
 
 
 def register_qqguild_dms(adapter: SupportedAdapters):
@@ -390,7 +387,7 @@ def wrapper(func: QQGuild_DMS):
 
 
 class QQGuildDMSManager:
-    _cache: ClassVar[Dict[TargetQQGuildDirect, int]] = {}
+    _cache: ClassVar[dict[TargetQQGuildDirect, int]] = {}
 
     @classmethod
     def get_guild_id(cls, target: TargetQQGuildDirect) -> int:
diff --git a/nonebot_plugin_saa/registries/receipt.py b/nonebot_plugin_saa/registries/receipt.py
index 5b094fe..db49663 100644
--- a/nonebot_plugin_saa/registries/receipt.py
+++ b/nonebot_plugin_saa/registries/receipt.py
@@ -18,12 +18,10 @@ class Receipt(SerializationMeta):
     def _get_bot(self) -> Bot:
         return get_bot(self.bot_id)
 
-    async def revoke(self):
-        ...
+    async def revoke(self): ...
 
     @property
-    def raw(self) -> Any:
-        ...
+    def raw(self) -> Any: ...
 
     @abstractmethod
     def extract_message_id(self) -> MessageId:
diff --git a/nonebot_plugin_saa/types/common_message_segment.py b/nonebot_plugin_saa/types/common_message_segment.py
index 3da96c9..8f262bf 100644
--- a/nonebot_plugin_saa/types/common_message_segment.py
+++ b/nonebot_plugin_saa/types/common_message_segment.py
@@ -1,7 +1,7 @@
 from io import BytesIO
 from pathlib import Path
 from typing_extensions import NotRequired
-from typing import Dict, Union, TypedDict, overload
+from typing import Union, TypedDict, overload
 
 from ..registries import MessageId
 from ..utils import SupportedAdapters
@@ -56,8 +56,10 @@ def __init__(
         支持多种格式的数据
 
         参数:
-            image: str 为图片 URL，bytes 为图片数据，Path 为图片路径，BytesIO 为图片文件流
-            name: 图片名称，默认为 image
+            image:
+                str 为图片 URL，bytes 为图片数据，Path 为图片路径，BytesIO 为图片文件流
+            name:
+                图片名称，默认为 image
         """
         super().__init__()
         self.data = {"image": image, "name": name}
@@ -74,7 +76,7 @@ def __str__(self) -> str:
         else:
             image_str = format_template.format("image", repr(image))
 
-        kv_list = list(f"{k}={v!r}" for k, v in self.data.items() if k != "image")
+        kv_list = [f"{k}={v!r}" for k, v in self.data.items() if k != "image"]
         kv_list.append(image_str)
         return f"[SAA:{self.__class__.__name__}|{','.join(kv_list)}]"
 
@@ -85,11 +87,11 @@ def __repr__(self) -> str:
             image_str = format_template.format("image", repr(image))
         elif isinstance(image, BytesIO):
             image_str = format_template.format(
-                "image", f"BytesIO({repr(image.getvalue())})"
+                "image", f"BytesIO({image.getvalue()!r})"
             )
         else:
             image_str = format_template.format("image", repr(image))
-        kv_list = list(f"{k}={v!r}" for k, v in self.data.items() if k != "image")
+        kv_list = [f"{k}={v!r}" for k, v in self.data.items() if k != "image"]
         kv_list.append(image_str)
         return f"{self.__class__.__name__}({', '.join(kv_list)})"
 
@@ -117,7 +119,7 @@ def __init__(self, user_id: str):
 class MentionAllData(TypedDict):
     online_only: bool
     fallback: Union[str, None]
-    special_fallback: NotRequired[Dict[SupportedAdapters, str]]
+    special_fallback: NotRequired[dict[SupportedAdapters, str]]
 
 
 class MentionAll(MessageSegmentFactory):
@@ -126,26 +128,22 @@ class MentionAll(MessageSegmentFactory):
     data: MentionAllData
 
     @overload
-    def __init__(self):
-        ...
+    def __init__(self): ...
 
     # * 之后的参数只能通过关键字传递，方便 IDE 提示
     @overload
-    def __init__(self, *, online_only: bool = False) -> None:
-        ...
+    def __init__(self, *, online_only: bool = False) -> None: ...
 
     # fallback 参数可以通过位置传递
     @overload
-    def __init__(self, fallback: Union[str, None] = None) -> None:
-        ...
+    def __init__(self, fallback: Union[str, None] = None) -> None: ...
 
     @overload
     def __init__(
         self,
         fallback: Union[str, None] = None,
         online_only: bool = False,
-    ) -> None:
-        ...
+    ) -> None: ...
 
     def __init__(
         self,
diff --git a/nonebot_plugin_saa/types/custom_message_segment.py b/nonebot_plugin_saa/types/custom_message_segment.py
index f919865..fd25cac 100644
--- a/nonebot_plugin_saa/types/custom_message_segment.py
+++ b/nonebot_plugin_saa/types/custom_message_segment.py
@@ -1,4 +1,4 @@
-from typing import Dict, Union
+from typing import Union
 
 from nonebot.adapters import Bot, MessageSegment
 
@@ -17,7 +17,7 @@ class Custom(MessageSegmentFactory):
 
     def __init__(
         self,
-        ms_dict: Dict[SupportedAdapters, Union[MessageSegment, CustomBuildFunc]],
+        ms_dict: dict[SupportedAdapters, Union[MessageSegment, CustomBuildFunc]],
     ):
         """
         自定义 MessageSegment
diff --git a/nonebot_plugin_saa/utils/__init__.py b/nonebot_plugin_saa/utils/__init__.py
index 875efb8..37e2660 100644
--- a/nonebot_plugin_saa/utils/__init__.py
+++ b/nonebot_plugin_saa/utils/__init__.py
@@ -5,4 +5,5 @@
 from .helpers import extract_adapter_type as extract_adapter_type
 from .exceptions import AdapterNotInstalled as AdapterNotInstalled
 from .exceptions import AdapterNotSupported as AdapterNotSupported
+from .helpers import type_message_id_check as type_message_id_check
 from .const import supported_adapter_names as supported_adapter_names
diff --git a/nonebot_plugin_saa/utils/const.py b/nonebot_plugin_saa/utils/const.py
index 6072bbe..8c5f16f 100644
--- a/nonebot_plugin_saa/utils/const.py
+++ b/nonebot_plugin_saa/utils/const.py
@@ -34,4 +34,4 @@ class SupportedPlatform(StrEnum):
     dodo_private = "DoDo Private"
 
 
-supported_adapter_names = set(SupportedAdapters._member_map_.values())  # noqa: SLF001
+supported_adapter_names = set(SupportedAdapters._member_map_.values())
diff --git a/nonebot_plugin_saa/utils/exceptions.py b/nonebot_plugin_saa/utils/exceptions.py
index 4855e8c..c64a413 100644
--- a/nonebot_plugin_saa/utils/exceptions.py
+++ b/nonebot_plugin_saa/utils/exceptions.py
@@ -1,3 +1,9 @@
+from typing import TYPE_CHECKING
+
+if TYPE_CHECKING:
+    from nonebot_plugin_saa.registries.message_id import MessageId
+
+
 class AdapterNotInstalled(Exception):
     def __init__(self, adapter_name: str) -> None:
         message = f'adapter "{adapter_name}" not installed, please install first'
@@ -16,3 +22,14 @@ class NoBotFound(RuntimeError):
 
 class FallbackToDefault(Exception):
     pass
+
+
+class UnexpectedMessageIdType(Exception):
+    def __init__(
+        self, expected_type: "type[MessageId]", message_id: "MessageId"
+    ) -> None:
+        super().__init__(
+            self,
+            f"expected message_id type {expected_type.__name__}, "
+            f"got {message_id.__class__.__name__}",
+        )
diff --git a/nonebot_plugin_saa/utils/helpers.py b/nonebot_plugin_saa/utils/helpers.py
index b15ce50..f51bd45 100644
--- a/nonebot_plugin_saa/utils/helpers.py
+++ b/nonebot_plugin_saa/utils/helpers.py
@@ -1,9 +1,14 @@
-from typing import cast
+from typing import TYPE_CHECKING, TypeVar, cast
 
 from nonebot.internal.adapter.bot import Bot
 
-from .exceptions import AdapterNotSupported
 from .const import SupportedAdapters, supported_adapter_names
+from .exceptions import AdapterNotSupported, UnexpectedMessageIdType
+
+if TYPE_CHECKING:
+    from nonebot_plugin_saa.registries.message_id import MessageId
+
+TMessageId = TypeVar("TMessageId", bound="type[MessageId]")
 
 
 def extract_adapter_type(bot: Bot) -> SupportedAdapters:
@@ -13,3 +18,11 @@ def extract_adapter_type(bot: Bot) -> SupportedAdapters:
 
     adapter_name = SupportedAdapters(adapter_name)
     return cast(SupportedAdapters, adapter_name)
+
+
+def type_message_id_check(
+    expected_type: TMessageId, message_id: "MessageId"
+) -> TMessageId:
+    if not isinstance(message_id, expected_type):
+        raise UnexpectedMessageIdType(expected_type, message_id)
+    return cast(TMessageId, message_id)
diff --git a/poetry.lock b/poetry.lock
index 9af3b6b..b8408c1 100644
--- a/poetry.lock
+++ b/poetry.lock
@@ -11,9 +11,6 @@ files = [
     {file = "annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89"},
 ]
 
-[package.dependencies]
-typing-extensions = {version = ">=4.0.0", markers = "python_version < \"3.9\""}
-
 [[package]]
 name = "anyio"
 version = "4.4.0"
@@ -36,17 +33,6 @@ doc = ["Sphinx (>=7)", "packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphin
 test = ["anyio[trio]", "coverage[toml] (>=7)", "exceptiongroup (>=1.2.0)", "hypothesis (>=4.0)", "psutil (>=5.9)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "uvloop (>=0.17)"]
 trio = ["trio (>=0.23)"]
 
-[[package]]
-name = "appnope"
-version = "0.1.4"
-description = "Disable App Nap on macOS >= 10.9"
-optional = false
-python-versions = ">=3.6"
-files = [
-    {file = "appnope-0.1.4-py2.py3-none-any.whl", hash = "sha256:502575ee11cd7a28c0205f379b525beefebab9d161b7c964670864014ed7213c"},
-    {file = "appnope-0.1.4.tar.gz", hash = "sha256:1de3860566df9caf38f01f86f65e0e13e379af54f9e4bee1e66b48f2efffd1ee"},
-]
-
 [[package]]
 name = "asgiref"
 version = "3.8.1"
@@ -96,17 +82,6 @@ files = [
 multidict = ">=4.0,<7.0"
 requests = ">=2.21,<3.0"
 
-[[package]]
-name = "backcall"
-version = "0.2.0"
-description = "Specifications for callback functions passed in to an API"
-optional = false
-python-versions = "*"
-files = [
-    {file = "backcall-0.2.0-py2.py3-none-any.whl", hash = "sha256:fbbce6a29f263178a1f7915c1940bde0ec2b2a967566fe1c65c1dfb7422bd255"},
-    {file = "backcall-0.2.0.tar.gz", hash = "sha256:5cbdbf27be5e7cfadb448baf0aa95508f91f2bbc6c6437cd9cd06e2a4c215e1e"},
-]
-
 [[package]]
 name = "cashews"
 version = "7.1.0"
@@ -128,13 +103,13 @@ tests = ["hypothesis (==6.100.2)", "pytest (==8.2.0)", "pytest-asyncio (==0.23.6
 
 [[package]]
 name = "certifi"
-version = "2024.2.2"
+version = "2024.6.2"
 description = "Python package for providing Mozilla's CA Bundle."
 optional = false
 python-versions = ">=3.6"
 files = [
-    {file = "certifi-2024.2.2-py3-none-any.whl", hash = "sha256:dc383c07b76109f368f6106eee2b593b04a011ea4d55f652c6ca24a754d1cdd1"},
-    {file = "certifi-2024.2.2.tar.gz", hash = "sha256:0569859f95fc761b18b45ef421b1290a0f65f147e92a1e5eb3e635f9a5e4e66f"},
+    {file = "certifi-2024.6.2-py3-none-any.whl", hash = "sha256:ddc6c8ce995e6987e7faf5e3f1b02b302836a0e5d98ece18392cb1a36c72ad56"},
+    {file = "certifi-2024.6.2.tar.gz", hash = "sha256:3cd43f1c6fa7dedc5899d69d3ad0398fd018ad1a17fba83ddaf78aa46c747516"},
 ]
 
 [[package]]
@@ -639,42 +614,40 @@ tomli = {version = "*", markers = "python_version > \"3.6\" and python_version <
 
 [[package]]
 name = "ipython"
-version = "8.12.3"
+version = "8.18.1"
 description = "IPython: Productive Interactive Computing"
 optional = false
-python-versions = ">=3.8"
+python-versions = ">=3.9"
 files = [
-    {file = "ipython-8.12.3-py3-none-any.whl", hash = "sha256:b0340d46a933d27c657b211a329d0be23793c36595acf9e6ef4164bc01a1804c"},
-    {file = "ipython-8.12.3.tar.gz", hash = "sha256:3910c4b54543c2ad73d06579aa771041b7d5707b033bd488669b4cf544e3b363"},
+    {file = "ipython-8.18.1-py3-none-any.whl", hash = "sha256:e8267419d72d81955ec1177f8a29aaa90ac80ad647499201119e2f05e99aa397"},
+    {file = "ipython-8.18.1.tar.gz", hash = "sha256:ca6f079bb33457c66e233e4580ebfc4128855b4cf6370dddd73842a9563e8a27"},
 ]
 
 [package.dependencies]
-appnope = {version = "*", markers = "sys_platform == \"darwin\""}
-backcall = "*"
 colorama = {version = "*", markers = "sys_platform == \"win32\""}
 decorator = "*"
+exceptiongroup = {version = "*", markers = "python_version < \"3.11\""}
 jedi = ">=0.16"
 matplotlib-inline = "*"
 pexpect = {version = ">4.3", markers = "sys_platform != \"win32\""}
-pickleshare = "*"
-prompt-toolkit = ">=3.0.30,<3.0.37 || >3.0.37,<3.1.0"
+prompt-toolkit = ">=3.0.41,<3.1.0"
 pygments = ">=2.4.0"
 stack-data = "*"
 traitlets = ">=5"
 typing-extensions = {version = "*", markers = "python_version < \"3.10\""}
 
 [package.extras]
-all = ["black", "curio", "docrepr", "ipykernel", "ipyparallel", "ipywidgets", "matplotlib", "matplotlib (!=3.2.0)", "nbconvert", "nbformat", "notebook", "numpy (>=1.21)", "pandas", "pytest (<7)", "pytest (<7.1)", "pytest-asyncio", "qtconsole", "setuptools (>=18.5)", "sphinx (>=1.3)", "sphinx-rtd-theme", "stack-data", "testpath", "trio", "typing-extensions"]
+all = ["black", "curio", "docrepr", "exceptiongroup", "ipykernel", "ipyparallel", "ipywidgets", "matplotlib", "matplotlib (!=3.2.0)", "nbconvert", "nbformat", "notebook", "numpy (>=1.22)", "pandas", "pickleshare", "pytest (<7)", "pytest (<7.1)", "pytest-asyncio (<0.22)", "qtconsole", "setuptools (>=18.5)", "sphinx (>=1.3)", "sphinx-rtd-theme", "stack-data", "testpath", "trio", "typing-extensions"]
 black = ["black"]
-doc = ["docrepr", "ipykernel", "matplotlib", "pytest (<7)", "pytest (<7.1)", "pytest-asyncio", "setuptools (>=18.5)", "sphinx (>=1.3)", "sphinx-rtd-theme", "stack-data", "testpath", "typing-extensions"]
+doc = ["docrepr", "exceptiongroup", "ipykernel", "matplotlib", "pickleshare", "pytest (<7)", "pytest (<7.1)", "pytest-asyncio (<0.22)", "setuptools (>=18.5)", "sphinx (>=1.3)", "sphinx-rtd-theme", "stack-data", "testpath", "typing-extensions"]
 kernel = ["ipykernel"]
 nbconvert = ["nbconvert"]
 nbformat = ["nbformat"]
 notebook = ["ipywidgets", "notebook"]
 parallel = ["ipyparallel"]
 qtconsole = ["qtconsole"]
-test = ["pytest (<7.1)", "pytest-asyncio", "testpath"]
-test-extra = ["curio", "matplotlib (!=3.2.0)", "nbformat", "numpy (>=1.21)", "pandas", "pytest (<7.1)", "pytest-asyncio", "testpath", "trio"]
+test = ["pickleshare", "pytest (<7.1)", "pytest-asyncio (<0.22)", "testpath"]
+test-extra = ["curio", "matplotlib (!=3.2.0)", "nbformat", "numpy (>=1.22)", "pandas", "pickleshare", "pytest (<7.1)", "pytest-asyncio (<0.22)", "testpath", "trio"]
 
 [[package]]
 name = "jedi"
@@ -1028,13 +1001,13 @@ nonebot2 = ">=2.2.1,<3.0.0"
 
 [[package]]
 name = "nonebot-adapter-feishu"
-version = "2.5.0"
+version = "2.6.1"
 description = "feishu(larksuite) adapter for nonebot2"
 optional = false
-python-versions = "<4.0,>=3.8"
+python-versions = "<4.0,>=3.9"
 files = [
-    {file = "nonebot_adapter_feishu-2.5.0-py3-none-any.whl", hash = "sha256:1394802cbfb5d3337252e77a6015ddec27790a8a76059656a798a0f6fb988170"},
-    {file = "nonebot_adapter_feishu-2.5.0.tar.gz", hash = "sha256:4fcccd47f41f84105c303e338931b55d3e0a40b81a5a2e4d0513b9e0efe1ab8a"},
+    {file = "nonebot_adapter_feishu-2.6.1-py3-none-any.whl", hash = "sha256:db023b6b5e8ae96ec3654b65095b02daae84e16082a02dd2b2d186b3a9c7bb09"},
+    {file = "nonebot_adapter_feishu-2.6.1.tar.gz", hash = "sha256:5b16c3d3a64fb4f6bc7279d7569c1225283b2a779ac99bc64c0fe56b33c4d1e4"},
 ]
 
 [package.dependencies]
@@ -1145,13 +1118,13 @@ resolved_reference = "befcfc676fed6b7b5474b5559f6f35a694c709a0"
 
 [[package]]
 name = "nonebot2"
-version = "2.2.1"
+version = "2.3.1"
 description = "An asynchronous python bot framework."
 optional = false
-python-versions = ">=3.8,<4.0"
+python-versions = "<4.0,>=3.9"
 files = [
-    {file = "nonebot2-2.2.1-py3-none-any.whl", hash = "sha256:88f2bb456bf90922925bbe489a9effe3b09300f3aa50bfa75ee50d8a83d7330f"},
-    {file = "nonebot2-2.2.1.tar.gz", hash = "sha256:fe57692300571b00724999238545d8d894523460e6835a11b326a2e1cdf98fc4"},
+    {file = "nonebot2-2.3.1-py3-none-any.whl", hash = "sha256:91ac0abebe6c403c2443b11a49e065b79e6199460bdd61a32148366b35f81c4d"},
+    {file = "nonebot2-2.3.1.tar.gz", hash = "sha256:ac5a1a1759f15310e9183b606ce6bdbe52a90287bf36a69201be548e23d41e6c"},
 ]
 
 [package.dependencies]
@@ -1195,68 +1168,68 @@ typing-extensions = ">=4.0.0,<5.0.0"
 
 [[package]]
 name = "orjson"
-version = "3.10.3"
+version = "3.10.4"
 description = "Fast, correct Python JSON library supporting dataclasses, datetimes, and numpy"
 optional = false
 python-versions = ">=3.8"
 files = [
-    {file = "orjson-3.10.3-cp310-cp310-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:9fb6c3f9f5490a3eb4ddd46fc1b6eadb0d6fc16fb3f07320149c3286a1409dd8"},
-    {file = "orjson-3.10.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:252124b198662eee80428f1af8c63f7ff077c88723fe206a25df8dc57a57b1fa"},
-    {file = "orjson-3.10.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9f3e87733823089a338ef9bbf363ef4de45e5c599a9bf50a7a9b82e86d0228da"},
-    {file = "orjson-3.10.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c8334c0d87103bb9fbbe59b78129f1f40d1d1e8355bbed2ca71853af15fa4ed3"},
-    {file = "orjson-3.10.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1952c03439e4dce23482ac846e7961f9d4ec62086eb98ae76d97bd41d72644d7"},
-    {file = "orjson-3.10.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:c0403ed9c706dcd2809f1600ed18f4aae50be263bd7112e54b50e2c2bc3ebd6d"},
-    {file = "orjson-3.10.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:382e52aa4270a037d41f325e7d1dfa395b7de0c367800b6f337d8157367bf3a7"},
-    {file = "orjson-3.10.3-cp310-none-win32.whl", hash = "sha256:be2aab54313752c04f2cbaab4515291ef5af8c2256ce22abc007f89f42f49109"},
-    {file = "orjson-3.10.3-cp310-none-win_amd64.whl", hash = "sha256:416b195f78ae461601893f482287cee1e3059ec49b4f99479aedf22a20b1098b"},
-    {file = "orjson-3.10.3-cp311-cp311-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:73100d9abbbe730331f2242c1fc0bcb46a3ea3b4ae3348847e5a141265479700"},
-    {file = "orjson-3.10.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:544a12eee96e3ab828dbfcb4d5a0023aa971b27143a1d35dc214c176fdfb29b3"},
-    {file = "orjson-3.10.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:520de5e2ef0b4ae546bea25129d6c7c74edb43fc6cf5213f511a927f2b28148b"},
-    {file = "orjson-3.10.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ccaa0a401fc02e8828a5bedfd80f8cd389d24f65e5ca3954d72c6582495b4bcf"},
-    {file = "orjson-3.10.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9a7bc9e8bc11bac40f905640acd41cbeaa87209e7e1f57ade386da658092dc16"},
-    {file = "orjson-3.10.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:3582b34b70543a1ed6944aca75e219e1192661a63da4d039d088a09c67543b08"},
-    {file = "orjson-3.10.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:1c23dfa91481de880890d17aa7b91d586a4746a4c2aa9a145bebdbaf233768d5"},
-    {file = "orjson-3.10.3-cp311-none-win32.whl", hash = "sha256:1770e2a0eae728b050705206d84eda8b074b65ee835e7f85c919f5705b006c9b"},
-    {file = "orjson-3.10.3-cp311-none-win_amd64.whl", hash = "sha256:93433b3c1f852660eb5abdc1f4dd0ced2be031ba30900433223b28ee0140cde5"},
-    {file = "orjson-3.10.3-cp312-cp312-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:a39aa73e53bec8d410875683bfa3a8edf61e5a1c7bb4014f65f81d36467ea098"},
-    {file = "orjson-3.10.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0943a96b3fa09bee1afdfccc2cb236c9c64715afa375b2af296c73d91c23eab2"},
-    {file = "orjson-3.10.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e852baafceff8da3c9defae29414cc8513a1586ad93e45f27b89a639c68e8176"},
-    {file = "orjson-3.10.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:18566beb5acd76f3769c1d1a7ec06cdb81edc4d55d2765fb677e3eaa10fa99e0"},
-    {file = "orjson-3.10.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1bd2218d5a3aa43060efe649ec564ebedec8ce6ae0a43654b81376216d5ebd42"},
-    {file = "orjson-3.10.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:cf20465e74c6e17a104ecf01bf8cd3b7b252565b4ccee4548f18b012ff2f8069"},
-    {file = "orjson-3.10.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ba7f67aa7f983c4345eeda16054a4677289011a478ca947cd69c0a86ea45e534"},
-    {file = "orjson-3.10.3-cp312-none-win32.whl", hash = "sha256:17e0713fc159abc261eea0f4feda611d32eabc35708b74bef6ad44f6c78d5ea0"},
-    {file = "orjson-3.10.3-cp312-none-win_amd64.whl", hash = "sha256:4c895383b1ec42b017dd2c75ae8a5b862fc489006afde06f14afbdd0309b2af0"},
-    {file = "orjson-3.10.3-cp38-cp38-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:be2719e5041e9fb76c8c2c06b9600fe8e8584e6980061ff88dcbc2691a16d20d"},
-    {file = "orjson-3.10.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cb0175a5798bdc878956099f5c54b9837cb62cfbf5d0b86ba6d77e43861bcec2"},
-    {file = "orjson-3.10.3-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:978be58a68ade24f1af7758626806e13cff7748a677faf95fbb298359aa1e20d"},
-    {file = "orjson-3.10.3-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:16bda83b5c61586f6f788333d3cf3ed19015e3b9019188c56983b5a299210eb5"},
-    {file = "orjson-3.10.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4ad1f26bea425041e0a1adad34630c4825a9e3adec49079b1fb6ac8d36f8b754"},
-    {file = "orjson-3.10.3-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:9e253498bee561fe85d6325ba55ff2ff08fb5e7184cd6a4d7754133bd19c9195"},
-    {file = "orjson-3.10.3-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:0a62f9968bab8a676a164263e485f30a0b748255ee2f4ae49a0224be95f4532b"},
-    {file = "orjson-3.10.3-cp38-none-win32.whl", hash = "sha256:8d0b84403d287d4bfa9bf7d1dc298d5c1c5d9f444f3737929a66f2fe4fb8f134"},
-    {file = "orjson-3.10.3-cp38-none-win_amd64.whl", hash = "sha256:8bc7a4df90da5d535e18157220d7915780d07198b54f4de0110eca6b6c11e290"},
-    {file = "orjson-3.10.3-cp39-cp39-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:9059d15c30e675a58fdcd6f95465c1522b8426e092de9fff20edebfdc15e1cb0"},
-    {file = "orjson-3.10.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8d40c7f7938c9c2b934b297412c067936d0b54e4b8ab916fd1a9eb8f54c02294"},
-    {file = "orjson-3.10.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d4a654ec1de8fdaae1d80d55cee65893cb06494e124681ab335218be6a0691e7"},
-    {file = "orjson-3.10.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:831c6ef73f9aa53c5f40ae8f949ff7681b38eaddb6904aab89dca4d85099cb78"},
-    {file = "orjson-3.10.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:99b880d7e34542db89f48d14ddecbd26f06838b12427d5a25d71baceb5ba119d"},
-    {file = "orjson-3.10.3-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:2e5e176c994ce4bd434d7aafb9ecc893c15f347d3d2bbd8e7ce0b63071c52e25"},
-    {file = "orjson-3.10.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:b69a58a37dab856491bf2d3bbf259775fdce262b727f96aafbda359cb1d114d8"},
-    {file = "orjson-3.10.3-cp39-none-win32.whl", hash = "sha256:b8d4d1a6868cde356f1402c8faeb50d62cee765a1f7ffcfd6de732ab0581e063"},
-    {file = "orjson-3.10.3-cp39-none-win_amd64.whl", hash = "sha256:5102f50c5fc46d94f2033fe00d392588564378260d64377aec702f21a7a22912"},
-    {file = "orjson-3.10.3.tar.gz", hash = "sha256:2b166507acae7ba2f7c315dcf185a9111ad5e992ac81f2d507aac39193c2c818"},
+    {file = "orjson-3.10.4-cp310-cp310-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:afca963f19ca60c7aedadea9979f769139127288dd58ccf3f7c5e8e6dc62cabf"},
+    {file = "orjson-3.10.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42b112eff36ba7ccc7a9d6b87e17b9d6bde4312d05e3ddf66bf5662481dee846"},
+    {file = "orjson-3.10.4-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:02b192eaba048b1039eca9a0cef67863bd5623042f5c441889a9957121d97e14"},
+    {file = "orjson-3.10.4-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:827c3d0e4fc44242c82bfdb1a773235b8c0575afee99a9fa9a8ce920c14e440f"},
+    {file = "orjson-3.10.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ca8ec09724f10ec209244caeb1f9f428b6bb03f2eda9ed5e2c4dd7f2b7fabd44"},
+    {file = "orjson-3.10.4-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:8eaa5d531a8fde11993cbcb27e9acf7d9c457ba301adccb7fa3a021bfecab46c"},
+    {file = "orjson-3.10.4-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:e112aa7fc4ea67367ec5e86c39a6bb6c5719eddc8f999087b1759e765ddaf2d4"},
+    {file = "orjson-3.10.4-cp310-none-win32.whl", hash = "sha256:1538844fb88446c42da3889f8c4ecce95a630b5a5ba18ecdfe5aea596f4dff21"},
+    {file = "orjson-3.10.4-cp310-none-win_amd64.whl", hash = "sha256:de02811903a2e434127fba5389c3cc90f689542339a6e52e691ab7f693407b5a"},
+    {file = "orjson-3.10.4-cp311-cp311-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:358afaec75de7237dfea08e6b1b25d226e33a1e3b6dc154fc99eb697f24a1ffa"},
+    {file = "orjson-3.10.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bb4e292c3198ab3d93e5f877301d2746be4ca0ba2d9c513da5e10eb90e19ff52"},
+    {file = "orjson-3.10.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5c39e57cf6323a39238490092985d5d198a7da4a3be013cc891a33fef13a536e"},
+    {file = "orjson-3.10.4-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f86df433fc01361ff9270ad27455ce1ad43cd05e46de7152ca6adb405a16b2f6"},
+    {file = "orjson-3.10.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0c9966276a2c97e93e6cbe8286537f88b2a071827514f0d9d47a0aefa77db458"},
+    {file = "orjson-3.10.4-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c499a14155a1f5a1e16e0cd31f6cf6f93965ac60a0822bc8340e7e2d3dac1108"},
+    {file = "orjson-3.10.4-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:3087023ce904a327c29487eb7e1f2c060070e8dbb9a3991b8e7952a9c6e62f38"},
+    {file = "orjson-3.10.4-cp311-none-win32.whl", hash = "sha256:f965893244fe348b59e5ce560693e6dd03368d577ce26849b5d261ce31c70101"},
+    {file = "orjson-3.10.4-cp311-none-win_amd64.whl", hash = "sha256:c212f06fad6aa6ce85d5665e91a83b866579f29441a47d3865c57329c0857357"},
+    {file = "orjson-3.10.4-cp312-cp312-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:d0965a8b0131959833ca8a65af60285995d57ced0de2fd8f16fc03235975d238"},
+    {file = "orjson-3.10.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:27b64695d9f2aef3ae15a0522e370ec95c946aaea7f2c97a1582a62b3bdd9169"},
+    {file = "orjson-3.10.4-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:867d882ddee6a20be4c8b03ae3d2b0333894d53ad632d32bd9b8123649577171"},
+    {file = "orjson-3.10.4-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a0667458f8a8ceb6dee5c08fec0b46195f92c474cbbec71dca2a6b7fd5b67b8d"},
+    {file = "orjson-3.10.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a3eac9befc4eaec1d1ff3bba6210576be4945332dde194525601c5ddb5c060d3"},
+    {file = "orjson-3.10.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:4343245443552eae240a33047a6d1bcac7a754ad4b1c57318173c54d7efb9aea"},
+    {file = "orjson-3.10.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:30153e269eea43e98918d4d462a36a7065031d9246407dfff2579a4e457515c1"},
+    {file = "orjson-3.10.4-cp312-none-win32.whl", hash = "sha256:1a7d092ee043abf3db19c2183115e80676495c9911843fdb3ebd48ca7b73079e"},
+    {file = "orjson-3.10.4-cp312-none-win_amd64.whl", hash = "sha256:07a2adbeb8b9efe6d68fc557685954a1f19d9e33f5cc018ae1a89e96647c1b65"},
+    {file = "orjson-3.10.4-cp38-cp38-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:f5a746f3d908bce1a1e347b9ca89864047533bdfab5a450066a0315f6566527b"},
+    {file = "orjson-3.10.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:465b4a8a3e459f8d304c19071b4badaa9b267c59207a005a7dd9dfe13d3a423f"},
+    {file = "orjson-3.10.4-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:35858d260728c434a3d91b60685ab32418318567e8902039837e1c2af2719e0b"},
+    {file = "orjson-3.10.4-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8a5ba090d40c4460312dd69c232b38c2ff67a823185cfe667e841c9dd5c06841"},
+    {file = "orjson-3.10.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5dde86755d064664e62e3612a166c28298aa8dfd35a991553faa58855ae739cc"},
+    {file = "orjson-3.10.4-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:020a9e9001cfec85c156ef3b185ff758b62ef986cefdb8384c4579facd5ce126"},
+    {file = "orjson-3.10.4-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:3bf8e6e3388a2e83a86466c912387e0f0a765494c65caa7e865f99969b76ba0d"},
+    {file = "orjson-3.10.4-cp38-none-win32.whl", hash = "sha256:c5a1cca6a4a3129db3da68a25dc0a459a62ae58e284e363b35ab304202d9ba9e"},
+    {file = "orjson-3.10.4-cp38-none-win_amd64.whl", hash = "sha256:ecd97d98d7bee3e3d51d0b51c92c457f05db4993329eea7c69764f9820e27eb3"},
+    {file = "orjson-3.10.4-cp39-cp39-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:71362daa330a2fc85553a1469185ac448547392a8f83d34e67779f8df3a52743"},
+    {file = "orjson-3.10.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d24b59d1fecb0fd080c177306118a143f7322335309640c55ed9580d2044e363"},
+    {file = "orjson-3.10.4-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e906670aea5a605b083ebb58d575c35e88cf880fa372f7cedaac3d51e98ff164"},
+    {file = "orjson-3.10.4-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7ce32ed4bc4d632268e4978e595fe5ea07e026b751482b4a0feec48f66a90abc"},
+    {file = "orjson-3.10.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1dcd34286246e0c5edd0e230d1da2daab2c1b465fcb6bac85b8d44057229d40a"},
+    {file = "orjson-3.10.4-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:c45d4b8c403e50beedb1d006a8916d9910ed56bceaf2035dc253618b44d0a161"},
+    {file = "orjson-3.10.4-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:aaed3253041b5002a4f5bfdf6f7b5cce657d974472b0699a469d439beba40381"},
+    {file = "orjson-3.10.4-cp39-none-win32.whl", hash = "sha256:9a4f41b7dbf7896f8dbf559b9b43dcd99e31e0d49ac1b59d74f52ce51ab10eb9"},
+    {file = "orjson-3.10.4-cp39-none-win_amd64.whl", hash = "sha256:6c4eb7d867ed91cb61e6514cb4f457aa01d7b0fd663089df60a69f3d38b69d4c"},
+    {file = "orjson-3.10.4.tar.gz", hash = "sha256:c912ed25b787c73fe994a5decd81c3f3b256599b8a87d410d799d5d52013af2a"},
 ]
 
 [[package]]
 name = "packaging"
-version = "24.0"
+version = "24.1"
 description = "Core utilities for Python packages"
 optional = false
-python-versions = ">=3.7"
+python-versions = ">=3.8"
 files = [
-    {file = "packaging-24.0-py3-none-any.whl", hash = "sha256:2ddfb553fdf02fb784c234c7ba6ccc288296ceabec964ad2eae3777778130bc5"},
-    {file = "packaging-24.0.tar.gz", hash = "sha256:eb82c5e3e56209074766e6885bb04b8c38a0c015d0a30036ebe7ece34c9989e9"},
+    {file = "packaging-24.1-py3-none-any.whl", hash = "sha256:5b8f2217dbdbd2f7f384c41c628544e6d52f2d0f53c6d0c3ea61aa5d1d7ff124"},
+    {file = "packaging-24.1.tar.gz", hash = "sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002"},
 ]
 
 [[package]]
@@ -1288,17 +1261,6 @@ files = [
 [package.dependencies]
 ptyprocess = ">=0.5"
 
-[[package]]
-name = "pickleshare"
-version = "0.7.5"
-description = "Tiny 'shelve'-like database with concurrency support"
-optional = false
-python-versions = "*"
-files = [
-    {file = "pickleshare-0.7.5-py2.py3-none-any.whl", hash = "sha256:9649af414d74d4df115d5d718f82acb59c9d418196b7b4290ed47a12ce62df56"},
-    {file = "pickleshare-0.7.5.tar.gz", hash = "sha256:87683d47965c1da65cdacaf31c8441d12b8044cdec9aca500cd78fc2c683afca"},
-]
-
 [[package]]
 name = "pip"
 version = "23.3.2"
@@ -1327,13 +1289,13 @@ testing = ["pytest", "pytest-benchmark"]
 
 [[package]]
 name = "prompt-toolkit"
-version = "3.0.45"
+version = "3.0.47"
 description = "Library for building powerful interactive command lines in Python"
 optional = false
 python-versions = ">=3.7.0"
 files = [
-    {file = "prompt_toolkit-3.0.45-py3-none-any.whl", hash = "sha256:a29b89160e494e3ea8622b09fa5897610b437884dcdcd054fdc1308883326c2a"},
-    {file = "prompt_toolkit-3.0.45.tar.gz", hash = "sha256:07c60ee4ab7b7e90824b61afa840c8f5aad2d46b3e2e10acc33d8ecc94a49089"},
+    {file = "prompt_toolkit-3.0.47-py3-none-any.whl", hash = "sha256:0d7bfa67001d5e39d02c224b663abc33687405033a8c422d0d675a5a13361d10"},
+    {file = "prompt_toolkit-3.0.47.tar.gz", hash = "sha256:1e1b29cb58080b1e69f207c893a1a7bf16d127a5c30c9d17a25a5d77792e5360"},
 ]
 
 [package.dependencies]
@@ -1407,18 +1369,18 @@ files = [
 
 [[package]]
 name = "pydantic"
-version = "2.7.2"
+version = "2.7.3"
 description = "Data validation using Python type hints"
 optional = false
 python-versions = ">=3.8"
 files = [
-    {file = "pydantic-2.7.2-py3-none-any.whl", hash = "sha256:834ab954175f94e6e68258537dc49402c4a5e9d0409b9f1b86b7e934a8372de7"},
-    {file = "pydantic-2.7.2.tar.gz", hash = "sha256:71b2945998f9c9b7919a45bde9a50397b289937d215ae141c1d0903ba7149fd7"},
+    {file = "pydantic-2.7.3-py3-none-any.whl", hash = "sha256:ea91b002777bf643bb20dd717c028ec43216b24a6001a280f83877fd2655d0b4"},
+    {file = "pydantic-2.7.3.tar.gz", hash = "sha256:c46c76a40bb1296728d7a8b99aa73dd70a48c3510111ff290034f860c99c419e"},
 ]
 
 [package.dependencies]
 annotated-types = ">=0.4.0"
-pydantic-core = "2.18.3"
+pydantic-core = "2.18.4"
 typing-extensions = ">=4.6.1"
 
 [package.extras]
@@ -1426,90 +1388,90 @@ email = ["email-validator (>=2.0.0)"]
 
 [[package]]
 name = "pydantic-core"
-version = "2.18.3"
+version = "2.18.4"
 description = "Core functionality for Pydantic validation and serialization"
 optional = false
 python-versions = ">=3.8"
 files = [
-    {file = "pydantic_core-2.18.3-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:744697428fcdec6be5670460b578161d1ffe34743a5c15656be7ea82b008197c"},
-    {file = "pydantic_core-2.18.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:37b40c05ced1ba4218b14986fe6f283d22e1ae2ff4c8e28881a70fb81fbfcda7"},
-    {file = "pydantic_core-2.18.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:544a9a75622357076efb6b311983ff190fbfb3c12fc3a853122b34d3d358126c"},
-    {file = "pydantic_core-2.18.3-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e2e253af04ceaebde8eb201eb3f3e3e7e390f2d275a88300d6a1959d710539e2"},
-    {file = "pydantic_core-2.18.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:855ec66589c68aa367d989da5c4755bb74ee92ccad4fdb6af942c3612c067e34"},
-    {file = "pydantic_core-2.18.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3d3e42bb54e7e9d72c13ce112e02eb1b3b55681ee948d748842171201a03a98a"},
-    {file = "pydantic_core-2.18.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c6ac9ffccc9d2e69d9fba841441d4259cb668ac180e51b30d3632cd7abca2b9b"},
-    {file = "pydantic_core-2.18.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c56eca1686539fa0c9bda992e7bd6a37583f20083c37590413381acfc5f192d6"},
-    {file = "pydantic_core-2.18.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:17954d784bf8abfc0ec2a633108207ebc4fa2df1a0e4c0c3ccbaa9bb01d2c426"},
-    {file = "pydantic_core-2.18.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:98ed737567d8f2ecd54f7c8d4f8572ca7c7921ede93a2e52939416170d357812"},
-    {file = "pydantic_core-2.18.3-cp310-none-win32.whl", hash = "sha256:9f9e04afebd3ed8c15d67a564ed0a34b54e52136c6d40d14c5547b238390e779"},
-    {file = "pydantic_core-2.18.3-cp310-none-win_amd64.whl", hash = "sha256:45e4ffbae34f7ae30d0047697e724e534a7ec0a82ef9994b7913a412c21462a0"},
-    {file = "pydantic_core-2.18.3-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:b9ebe8231726c49518b16b237b9fe0d7d361dd221302af511a83d4ada01183ab"},
-    {file = "pydantic_core-2.18.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b8e20e15d18bf7dbb453be78a2d858f946f5cdf06c5072453dace00ab652e2b2"},
-    {file = "pydantic_core-2.18.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c0d9ff283cd3459fa0bf9b0256a2b6f01ac1ff9ffb034e24457b9035f75587cb"},
-    {file = "pydantic_core-2.18.3-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2f7ef5f0ebb77ba24c9970da18b771711edc5feaf00c10b18461e0f5f5949231"},
-    {file = "pydantic_core-2.18.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:73038d66614d2e5cde30435b5afdced2b473b4c77d4ca3a8624dd3e41a9c19be"},
-    {file = "pydantic_core-2.18.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6afd5c867a74c4d314c557b5ea9520183fadfbd1df4c2d6e09fd0d990ce412cd"},
-    {file = "pydantic_core-2.18.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bd7df92f28d351bb9f12470f4c533cf03d1b52ec5a6e5c58c65b183055a60106"},
-    {file = "pydantic_core-2.18.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:80aea0ffeb1049336043d07799eace1c9602519fb3192916ff525b0287b2b1e4"},
-    {file = "pydantic_core-2.18.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:aaee40f25bba38132e655ffa3d1998a6d576ba7cf81deff8bfa189fb43fd2bbe"},
-    {file = "pydantic_core-2.18.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:9128089da8f4fe73f7a91973895ebf2502539d627891a14034e45fb9e707e26d"},
-    {file = "pydantic_core-2.18.3-cp311-none-win32.whl", hash = "sha256:fec02527e1e03257aa25b1a4dcbe697b40a22f1229f5d026503e8b7ff6d2eda7"},
-    {file = "pydantic_core-2.18.3-cp311-none-win_amd64.whl", hash = "sha256:58ff8631dbab6c7c982e6425da8347108449321f61fe427c52ddfadd66642af7"},
-    {file = "pydantic_core-2.18.3-cp311-none-win_arm64.whl", hash = "sha256:3fc1c7f67f34c6c2ef9c213e0f2a351797cda98249d9ca56a70ce4ebcaba45f4"},
-    {file = "pydantic_core-2.18.3-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:f0928cde2ae416a2d1ebe6dee324709c6f73e93494d8c7aea92df99aab1fc40f"},
-    {file = "pydantic_core-2.18.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0bee9bb305a562f8b9271855afb6ce00223f545de3d68560b3c1649c7c5295e9"},
-    {file = "pydantic_core-2.18.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e862823be114387257dacbfa7d78547165a85d7add33b446ca4f4fae92c7ff5c"},
-    {file = "pydantic_core-2.18.3-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6a36f78674cbddc165abab0df961b5f96b14461d05feec5e1f78da58808b97e7"},
-    {file = "pydantic_core-2.18.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ba905d184f62e7ddbb7a5a751d8a5c805463511c7b08d1aca4a3e8c11f2e5048"},
-    {file = "pydantic_core-2.18.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7fdd362f6a586e681ff86550b2379e532fee63c52def1c666887956748eaa326"},
-    {file = "pydantic_core-2.18.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:24b214b7ee3bd3b865e963dbed0f8bc5375f49449d70e8d407b567af3222aae4"},
-    {file = "pydantic_core-2.18.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:691018785779766127f531674fa82bb368df5b36b461622b12e176c18e119022"},
-    {file = "pydantic_core-2.18.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:60e4c625e6f7155d7d0dcac151edf5858102bc61bf959d04469ca6ee4e8381bd"},
-    {file = "pydantic_core-2.18.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a4e651e47d981c1b701dcc74ab8fec5a60a5b004650416b4abbef13db23bc7be"},
-    {file = "pydantic_core-2.18.3-cp312-none-win32.whl", hash = "sha256:ffecbb5edb7f5ffae13599aec33b735e9e4c7676ca1633c60f2c606beb17efc5"},
-    {file = "pydantic_core-2.18.3-cp312-none-win_amd64.whl", hash = "sha256:2c8333f6e934733483c7eddffdb094c143b9463d2af7e6bd85ebcb2d4a1b82c6"},
-    {file = "pydantic_core-2.18.3-cp312-none-win_arm64.whl", hash = "sha256:7a20dded653e516a4655f4c98e97ccafb13753987434fe7cf044aa25f5b7d417"},
-    {file = "pydantic_core-2.18.3-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:eecf63195be644b0396f972c82598cd15693550f0ff236dcf7ab92e2eb6d3522"},
-    {file = "pydantic_core-2.18.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:2c44efdd3b6125419c28821590d7ec891c9cb0dff33a7a78d9d5c8b6f66b9702"},
-    {file = "pydantic_core-2.18.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6e59fca51ffbdd1638b3856779342ed69bcecb8484c1d4b8bdb237d0eb5a45e2"},
-    {file = "pydantic_core-2.18.3-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:70cf099197d6b98953468461d753563b28e73cf1eade2ffe069675d2657ed1d5"},
-    {file = "pydantic_core-2.18.3-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:63081a49dddc6124754b32a3774331467bfc3d2bd5ff8f10df36a95602560361"},
-    {file = "pydantic_core-2.18.3-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:370059b7883485c9edb9655355ff46d912f4b03b009d929220d9294c7fd9fd60"},
-    {file = "pydantic_core-2.18.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5a64faeedfd8254f05f5cf6fc755023a7e1606af3959cfc1a9285744cc711044"},
-    {file = "pydantic_core-2.18.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:19d2e725de0f90d8671f89e420d36c3dd97639b98145e42fcc0e1f6d492a46dc"},
-    {file = "pydantic_core-2.18.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:67bc078025d70ec5aefe6200ef094576c9d86bd36982df1301c758a9fff7d7f4"},
-    {file = "pydantic_core-2.18.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:adf952c3f4100e203cbaf8e0c907c835d3e28f9041474e52b651761dc248a3c0"},
-    {file = "pydantic_core-2.18.3-cp38-none-win32.whl", hash = "sha256:9a46795b1f3beb167eaee91736d5d17ac3a994bf2215a996aed825a45f897558"},
-    {file = "pydantic_core-2.18.3-cp38-none-win_amd64.whl", hash = "sha256:200ad4e3133cb99ed82342a101a5abf3d924722e71cd581cc113fe828f727fbc"},
-    {file = "pydantic_core-2.18.3-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:304378b7bf92206036c8ddd83a2ba7b7d1a5b425acafff637172a3aa72ad7083"},
-    {file = "pydantic_core-2.18.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c826870b277143e701c9ccf34ebc33ddb4d072612683a044e7cce2d52f6c3fef"},
-    {file = "pydantic_core-2.18.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e201935d282707394f3668380e41ccf25b5794d1b131cdd96b07f615a33ca4b1"},
-    {file = "pydantic_core-2.18.3-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5560dda746c44b48bf82b3d191d74fe8efc5686a9ef18e69bdabccbbb9ad9442"},
-    {file = "pydantic_core-2.18.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6b32c2a1f8032570842257e4c19288eba9a2bba4712af542327de9a1204faff8"},
-    {file = "pydantic_core-2.18.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:929c24e9dea3990bc8bcd27c5f2d3916c0c86f5511d2caa69e0d5290115344a9"},
-    {file = "pydantic_core-2.18.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e1a8376fef60790152564b0eab376b3e23dd6e54f29d84aad46f7b264ecca943"},
-    {file = "pydantic_core-2.18.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:dccf3ef1400390ddd1fb55bf0632209d39140552d068ee5ac45553b556780e06"},
-    {file = "pydantic_core-2.18.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:41dbdcb0c7252b58fa931fec47937edb422c9cb22528f41cb8963665c372caf6"},
-    {file = "pydantic_core-2.18.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:666e45cf071669fde468886654742fa10b0e74cd0fa0430a46ba6056b24fb0af"},
-    {file = "pydantic_core-2.18.3-cp39-none-win32.whl", hash = "sha256:f9c08cabff68704a1b4667d33f534d544b8a07b8e5d039c37067fceb18789e78"},
-    {file = "pydantic_core-2.18.3-cp39-none-win_amd64.whl", hash = "sha256:4afa5f5973e8572b5c0dcb4e2d4fda7890e7cd63329bd5cc3263a25c92ef0026"},
-    {file = "pydantic_core-2.18.3-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:77319771a026f7c7d29c6ebc623de889e9563b7087911b46fd06c044a12aa5e9"},
-    {file = "pydantic_core-2.18.3-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:df11fa992e9f576473038510d66dd305bcd51d7dd508c163a8c8fe148454e059"},
-    {file = "pydantic_core-2.18.3-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d531076bdfb65af593326ffd567e6ab3da145020dafb9187a1d131064a55f97c"},
-    {file = "pydantic_core-2.18.3-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d33ce258e4e6e6038f2b9e8b8a631d17d017567db43483314993b3ca345dcbbb"},
-    {file = "pydantic_core-2.18.3-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1f9cd7f5635b719939019be9bda47ecb56e165e51dd26c9a217a433e3d0d59a9"},
-    {file = "pydantic_core-2.18.3-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:cd4a032bb65cc132cae1fe3e52877daecc2097965cd3914e44fbd12b00dae7c5"},
-    {file = "pydantic_core-2.18.3-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:82f2718430098bcdf60402136c845e4126a189959d103900ebabb6774a5d9fdb"},
-    {file = "pydantic_core-2.18.3-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:c0037a92cf0c580ed14e10953cdd26528e8796307bb8bb312dc65f71547df04d"},
-    {file = "pydantic_core-2.18.3-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:b95a0972fac2b1ff3c94629fc9081b16371dad870959f1408cc33b2f78ad347a"},
-    {file = "pydantic_core-2.18.3-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:a62e437d687cc148381bdd5f51e3e81f5b20a735c55f690c5be94e05da2b0d5c"},
-    {file = "pydantic_core-2.18.3-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b367a73a414bbb08507da102dc2cde0fa7afe57d09b3240ce82a16d608a7679c"},
-    {file = "pydantic_core-2.18.3-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0ecce4b2360aa3f008da3327d652e74a0e743908eac306198b47e1c58b03dd2b"},
-    {file = "pydantic_core-2.18.3-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:bd4435b8d83f0c9561a2a9585b1de78f1abb17cb0cef5f39bf6a4b47d19bafe3"},
-    {file = "pydantic_core-2.18.3-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:616221a6d473c5b9aa83fa8982745441f6a4a62a66436be9445c65f241b86c94"},
-    {file = "pydantic_core-2.18.3-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:7e6382ce89a92bc1d0c0c5edd51e931432202b9080dc921d8d003e616402efd1"},
-    {file = "pydantic_core-2.18.3-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:ff58f379345603d940e461eae474b6bbb6dab66ed9a851ecd3cb3709bf4dcf6a"},
-    {file = "pydantic_core-2.18.3.tar.gz", hash = "sha256:432e999088d85c8f36b9a3f769a8e2b57aabd817bbb729a90d1fe7f18f6f1f39"},
+    {file = "pydantic_core-2.18.4-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:f76d0ad001edd426b92233d45c746fd08f467d56100fd8f30e9ace4b005266e4"},
+    {file = "pydantic_core-2.18.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:59ff3e89f4eaf14050c8022011862df275b552caef8082e37b542b066ce1ff26"},
+    {file = "pydantic_core-2.18.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a55b5b16c839df1070bc113c1f7f94a0af4433fcfa1b41799ce7606e5c79ce0a"},
+    {file = "pydantic_core-2.18.4-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4d0dcc59664fcb8974b356fe0a18a672d6d7cf9f54746c05f43275fc48636851"},
+    {file = "pydantic_core-2.18.4-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8951eee36c57cd128f779e641e21eb40bc5073eb28b2d23f33eb0ef14ffb3f5d"},
+    {file = "pydantic_core-2.18.4-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4701b19f7e3a06ea655513f7938de6f108123bf7c86bbebb1196eb9bd35cf724"},
+    {file = "pydantic_core-2.18.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e00a3f196329e08e43d99b79b286d60ce46bed10f2280d25a1718399457e06be"},
+    {file = "pydantic_core-2.18.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:97736815b9cc893b2b7f663628e63f436018b75f44854c8027040e05230eeddb"},
+    {file = "pydantic_core-2.18.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:6891a2ae0e8692679c07728819b6e2b822fb30ca7445f67bbf6509b25a96332c"},
+    {file = "pydantic_core-2.18.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:bc4ff9805858bd54d1a20efff925ccd89c9d2e7cf4986144b30802bf78091c3e"},
+    {file = "pydantic_core-2.18.4-cp310-none-win32.whl", hash = "sha256:1b4de2e51bbcb61fdebd0ab86ef28062704f62c82bbf4addc4e37fa4b00b7cbc"},
+    {file = "pydantic_core-2.18.4-cp310-none-win_amd64.whl", hash = "sha256:6a750aec7bf431517a9fd78cb93c97b9b0c496090fee84a47a0d23668976b4b0"},
+    {file = "pydantic_core-2.18.4-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:942ba11e7dfb66dc70f9ae66b33452f51ac7bb90676da39a7345e99ffb55402d"},
+    {file = "pydantic_core-2.18.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b2ebef0e0b4454320274f5e83a41844c63438fdc874ea40a8b5b4ecb7693f1c4"},
+    {file = "pydantic_core-2.18.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a642295cd0c8df1b86fc3dced1d067874c353a188dc8e0f744626d49e9aa51c4"},
+    {file = "pydantic_core-2.18.4-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5f09baa656c904807e832cf9cce799c6460c450c4ad80803517032da0cd062e2"},
+    {file = "pydantic_core-2.18.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:98906207f29bc2c459ff64fa007afd10a8c8ac080f7e4d5beff4c97086a3dabd"},
+    {file = "pydantic_core-2.18.4-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:19894b95aacfa98e7cb093cd7881a0c76f55731efad31073db4521e2b6ff5b7d"},
+    {file = "pydantic_core-2.18.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0fbbdc827fe5e42e4d196c746b890b3d72876bdbf160b0eafe9f0334525119c8"},
+    {file = "pydantic_core-2.18.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f85d05aa0918283cf29a30b547b4df2fbb56b45b135f9e35b6807cb28bc47951"},
+    {file = "pydantic_core-2.18.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e85637bc8fe81ddb73fda9e56bab24560bdddfa98aa64f87aaa4e4b6730c23d2"},
+    {file = "pydantic_core-2.18.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:2f5966897e5461f818e136b8451d0551a2e77259eb0f73a837027b47dc95dab9"},
+    {file = "pydantic_core-2.18.4-cp311-none-win32.whl", hash = "sha256:44c7486a4228413c317952e9d89598bcdfb06399735e49e0f8df643e1ccd0558"},
+    {file = "pydantic_core-2.18.4-cp311-none-win_amd64.whl", hash = "sha256:8a7164fe2005d03c64fd3b85649891cd4953a8de53107940bf272500ba8a788b"},
+    {file = "pydantic_core-2.18.4-cp311-none-win_arm64.whl", hash = "sha256:4e99bc050fe65c450344421017f98298a97cefc18c53bb2f7b3531eb39bc7805"},
+    {file = "pydantic_core-2.18.4-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:6f5c4d41b2771c730ea1c34e458e781b18cc668d194958e0112455fff4e402b2"},
+    {file = "pydantic_core-2.18.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2fdf2156aa3d017fddf8aea5adfba9f777db1d6022d392b682d2a8329e087cef"},
+    {file = "pydantic_core-2.18.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4748321b5078216070b151d5271ef3e7cc905ab170bbfd27d5c83ee3ec436695"},
+    {file = "pydantic_core-2.18.4-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:847a35c4d58721c5dc3dba599878ebbdfd96784f3fb8bb2c356e123bdcd73f34"},
+    {file = "pydantic_core-2.18.4-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3c40d4eaad41f78e3bbda31b89edc46a3f3dc6e171bf0ecf097ff7a0ffff7cb1"},
+    {file = "pydantic_core-2.18.4-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:21a5e440dbe315ab9825fcd459b8814bb92b27c974cbc23c3e8baa2b76890077"},
+    {file = "pydantic_core-2.18.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:01dd777215e2aa86dfd664daed5957704b769e726626393438f9c87690ce78c3"},
+    {file = "pydantic_core-2.18.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4b06beb3b3f1479d32befd1f3079cc47b34fa2da62457cdf6c963393340b56e9"},
+    {file = "pydantic_core-2.18.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:564d7922e4b13a16b98772441879fcdcbe82ff50daa622d681dd682175ea918c"},
+    {file = "pydantic_core-2.18.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:0eb2a4f660fcd8e2b1c90ad566db2b98d7f3f4717c64fe0a83e0adb39766d5b8"},
+    {file = "pydantic_core-2.18.4-cp312-none-win32.whl", hash = "sha256:8b8bab4c97248095ae0c4455b5a1cd1cdd96e4e4769306ab19dda135ea4cdb07"},
+    {file = "pydantic_core-2.18.4-cp312-none-win_amd64.whl", hash = "sha256:14601cdb733d741b8958224030e2bfe21a4a881fb3dd6fbb21f071cabd48fa0a"},
+    {file = "pydantic_core-2.18.4-cp312-none-win_arm64.whl", hash = "sha256:c1322d7dd74713dcc157a2b7898a564ab091ca6c58302d5c7b4c07296e3fd00f"},
+    {file = "pydantic_core-2.18.4-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:823be1deb01793da05ecb0484d6c9e20baebb39bd42b5d72636ae9cf8350dbd2"},
+    {file = "pydantic_core-2.18.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:ebef0dd9bf9b812bf75bda96743f2a6c5734a02092ae7f721c048d156d5fabae"},
+    {file = "pydantic_core-2.18.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ae1d6df168efb88d7d522664693607b80b4080be6750c913eefb77e34c12c71a"},
+    {file = "pydantic_core-2.18.4-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f9899c94762343f2cc2fc64c13e7cae4c3cc65cdfc87dd810a31654c9b7358cc"},
+    {file = "pydantic_core-2.18.4-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:99457f184ad90235cfe8461c4d70ab7dd2680e28821c29eca00252ba90308c78"},
+    {file = "pydantic_core-2.18.4-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:18f469a3d2a2fdafe99296a87e8a4c37748b5080a26b806a707f25a902c040a8"},
+    {file = "pydantic_core-2.18.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b7cdf28938ac6b8b49ae5e92f2735056a7ba99c9b110a474473fd71185c1af5d"},
+    {file = "pydantic_core-2.18.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:938cb21650855054dc54dfd9120a851c974f95450f00683399006aa6e8abb057"},
+    {file = "pydantic_core-2.18.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:44cd83ab6a51da80fb5adbd9560e26018e2ac7826f9626bc06ca3dc074cd198b"},
+    {file = "pydantic_core-2.18.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:972658f4a72d02b8abfa2581d92d59f59897d2e9f7e708fdabe922f9087773af"},
+    {file = "pydantic_core-2.18.4-cp38-none-win32.whl", hash = "sha256:1d886dc848e60cb7666f771e406acae54ab279b9f1e4143babc9c2258213daa2"},
+    {file = "pydantic_core-2.18.4-cp38-none-win_amd64.whl", hash = "sha256:bb4462bd43c2460774914b8525f79b00f8f407c945d50881568f294c1d9b4443"},
+    {file = "pydantic_core-2.18.4-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:44a688331d4a4e2129140a8118479443bd6f1905231138971372fcde37e43528"},
+    {file = "pydantic_core-2.18.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a2fdd81edd64342c85ac7cf2753ccae0b79bf2dfa063785503cb85a7d3593223"},
+    {file = "pydantic_core-2.18.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:86110d7e1907ab36691f80b33eb2da87d780f4739ae773e5fc83fb272f88825f"},
+    {file = "pydantic_core-2.18.4-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:46387e38bd641b3ee5ce247563b60c5ca098da9c56c75c157a05eaa0933ed154"},
+    {file = "pydantic_core-2.18.4-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:123c3cec203e3f5ac7b000bd82235f1a3eced8665b63d18be751f115588fea30"},
+    {file = "pydantic_core-2.18.4-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dc1803ac5c32ec324c5261c7209e8f8ce88e83254c4e1aebdc8b0a39f9ddb443"},
+    {file = "pydantic_core-2.18.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:53db086f9f6ab2b4061958d9c276d1dbe3690e8dd727d6abf2321d6cce37fa94"},
+    {file = "pydantic_core-2.18.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:abc267fa9837245cc28ea6929f19fa335f3dc330a35d2e45509b6566dc18be23"},
+    {file = "pydantic_core-2.18.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:a0d829524aaefdebccb869eed855e2d04c21d2d7479b6cada7ace5448416597b"},
+    {file = "pydantic_core-2.18.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:509daade3b8649f80d4e5ff21aa5673e4ebe58590b25fe42fac5f0f52c6f034a"},
+    {file = "pydantic_core-2.18.4-cp39-none-win32.whl", hash = "sha256:ca26a1e73c48cfc54c4a76ff78df3727b9d9f4ccc8dbee4ae3f73306a591676d"},
+    {file = "pydantic_core-2.18.4-cp39-none-win_amd64.whl", hash = "sha256:c67598100338d5d985db1b3d21f3619ef392e185e71b8d52bceacc4a7771ea7e"},
+    {file = "pydantic_core-2.18.4-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:574d92eac874f7f4db0ca653514d823a0d22e2354359d0759e3f6a406db5d55d"},
+    {file = "pydantic_core-2.18.4-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:1f4d26ceb5eb9eed4af91bebeae4b06c3fb28966ca3a8fb765208cf6b51102ab"},
+    {file = "pydantic_core-2.18.4-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:77450e6d20016ec41f43ca4a6c63e9fdde03f0ae3fe90e7c27bdbeaece8b1ed4"},
+    {file = "pydantic_core-2.18.4-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d323a01da91851a4f17bf592faf46149c9169d68430b3146dcba2bb5e5719abc"},
+    {file = "pydantic_core-2.18.4-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:43d447dd2ae072a0065389092a231283f62d960030ecd27565672bd40746c507"},
+    {file = "pydantic_core-2.18.4-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:578e24f761f3b425834f297b9935e1ce2e30f51400964ce4801002435a1b41ef"},
+    {file = "pydantic_core-2.18.4-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:81b5efb2f126454586d0f40c4d834010979cb80785173d1586df845a632e4e6d"},
+    {file = "pydantic_core-2.18.4-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:ab86ce7c8f9bea87b9d12c7f0af71102acbf5ecbc66c17796cff45dae54ef9a5"},
+    {file = "pydantic_core-2.18.4-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:90afc12421df2b1b4dcc975f814e21bc1754640d502a2fbcc6d41e77af5ec312"},
+    {file = "pydantic_core-2.18.4-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:51991a89639a912c17bef4b45c87bd83593aee0437d8102556af4885811d59f5"},
+    {file = "pydantic_core-2.18.4-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:293afe532740370aba8c060882f7d26cfd00c94cae32fd2e212a3a6e3b7bc15e"},
+    {file = "pydantic_core-2.18.4-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b48ece5bde2e768197a2d0f6e925f9d7e3e826f0ad2271120f8144a9db18d5c8"},
+    {file = "pydantic_core-2.18.4-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:eae237477a873ab46e8dd748e515c72c0c804fb380fbe6c85533c7de51f23a8f"},
+    {file = "pydantic_core-2.18.4-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:834b5230b5dfc0c1ec37b2fda433b271cbbc0e507560b5d1588e2cc1148cf1ce"},
+    {file = "pydantic_core-2.18.4-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:e858ac0a25074ba4bce653f9b5d0a85b7456eaddadc0ce82d3878c22489fa4ee"},
+    {file = "pydantic_core-2.18.4-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:2fd41f6eff4c20778d717af1cc50eca52f5afe7805ee530a4fbd0bae284f16e9"},
+    {file = "pydantic_core-2.18.4.tar.gz", hash = "sha256:ec3beeada09ff865c344ff3bc2f427f5e6c26401cc6113d77e372c3fdac73864"},
 ]
 
 [package.dependencies]
@@ -1542,13 +1504,13 @@ files = [
 
 [[package]]
 name = "pytest"
-version = "8.2.1"
+version = "8.2.2"
 description = "pytest: simple powerful testing with Python"
 optional = false
 python-versions = ">=3.8"
 files = [
-    {file = "pytest-8.2.1-py3-none-any.whl", hash = "sha256:faccc5d332b8c3719f40283d0d44aa5cf101cec36f88cde9ed8f2bc0538612b1"},
-    {file = "pytest-8.2.1.tar.gz", hash = "sha256:5046e5b46d8e4cac199c373041f26be56fdb81eb4e67dc11d4e10811fc3408fd"},
+    {file = "pytest-8.2.2-py3-none-any.whl", hash = "sha256:c434598117762e2bd304e526244f67bf66bbd7b5d6cf22138be51ff661980343"},
+    {file = "pytest-8.2.2.tar.gz", hash = "sha256:de4bb8104e201939ccdc688b27a89a7be2079b22e2bd2b07f806b6ba71117977"},
 ]
 
 [package.dependencies]
@@ -1582,13 +1544,13 @@ testing = ["coverage (>=6.2)", "flaky (>=3.5.0)", "hypothesis (>=5.7.1)", "mypy
 
 [[package]]
 name = "pytest-cov"
-version = "4.1.0"
+version = "5.0.0"
 description = "Pytest plugin for measuring coverage."
 optional = false
-python-versions = ">=3.7"
+python-versions = ">=3.8"
 files = [
-    {file = "pytest-cov-4.1.0.tar.gz", hash = "sha256:3904b13dfbfec47f003b8e77fd5b589cd11904a21ddf1ab38a64f204d6a10ef6"},
-    {file = "pytest_cov-4.1.0-py3-none-any.whl", hash = "sha256:6ba70b9e97e69fcc3fb45bfeab2d0a138fb65c4d0d6a41ef33983ad114be8c3a"},
+    {file = "pytest-cov-5.0.0.tar.gz", hash = "sha256:5837b58e9f6ebd335b0f8060eecce69b662415b16dc503883a02f45dfeb14857"},
+    {file = "pytest_cov-5.0.0-py3-none-any.whl", hash = "sha256:4f0764a1219df53214206bf1feea4633c3b558a2925c8b59f144f682861ce652"},
 ]
 
 [package.dependencies]
@@ -1596,7 +1558,7 @@ coverage = {version = ">=5.2.1", extras = ["toml"]}
 pytest = ">=4.6"
 
 [package.extras]
-testing = ["fields", "hunter", "process-tests", "pytest-xdist", "six", "virtualenv"]
+testing = ["fields", "hunter", "process-tests", "pytest-xdist", "virtualenv"]
 
 [[package]]
 name = "pytest-mock"
@@ -1752,35 +1714,34 @@ files = [
 [package.dependencies]
 markdown-it-py = ">=2.2.0"
 pygments = ">=2.13.0,<3.0.0"
-typing-extensions = {version = ">=4.0.0,<5.0", markers = "python_version < \"3.9\""}
 
 [package.extras]
 jupyter = ["ipywidgets (>=7.5.1,<9)"]
 
 [[package]]
 name = "ruff"
-version = "0.4.7"
+version = "0.4.8"
 description = "An extremely fast Python linter and code formatter, written in Rust."
 optional = false
 python-versions = ">=3.7"
 files = [
-    {file = "ruff-0.4.7-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:e089371c67892a73b6bb1525608e89a2aca1b77b5440acf7a71dda5dac958f9e"},
-    {file = "ruff-0.4.7-py3-none-macosx_11_0_arm64.whl", hash = "sha256:10f973d521d910e5f9c72ab27e409e839089f955be8a4c8826601a6323a89753"},
-    {file = "ruff-0.4.7-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:59c3d110970001dfa494bcd95478e62286c751126dfb15c3c46e7915fc49694f"},
-    {file = "ruff-0.4.7-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fa9773c6c00f4958f73b317bc0fd125295110c3776089f6ef318f4b775f0abe4"},
-    {file = "ruff-0.4.7-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:07fc80bbb61e42b3b23b10fda6a2a0f5a067f810180a3760c5ef1b456c21b9db"},
-    {file = "ruff-0.4.7-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:fa4dafe3fe66d90e2e2b63fa1591dd6e3f090ca2128daa0be33db894e6c18648"},
-    {file = "ruff-0.4.7-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a7c0083febdec17571455903b184a10026603a1de078428ba155e7ce9358c5f6"},
-    {file = "ruff-0.4.7-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ad1b20e66a44057c326168437d680a2166c177c939346b19c0d6b08a62a37589"},
-    {file = "ruff-0.4.7-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cbf5d818553add7511c38b05532d94a407f499d1a76ebb0cad0374e32bc67202"},
-    {file = "ruff-0.4.7-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:50e9651578b629baec3d1513b2534de0ac7ed7753e1382272b8d609997e27e83"},
-    {file = "ruff-0.4.7-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:8874a9df7766cb956b218a0a239e0a5d23d9e843e4da1e113ae1d27ee420877a"},
-    {file = "ruff-0.4.7-py3-none-musllinux_1_2_i686.whl", hash = "sha256:b9de9a6e49f7d529decd09381c0860c3f82fa0b0ea00ea78409b785d2308a567"},
-    {file = "ruff-0.4.7-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:13a1768b0691619822ae6d446132dbdfd568b700ecd3652b20d4e8bc1e498f78"},
-    {file = "ruff-0.4.7-py3-none-win32.whl", hash = "sha256:769e5a51df61e07e887b81e6f039e7ed3573316ab7dd9f635c5afaa310e4030e"},
-    {file = "ruff-0.4.7-py3-none-win_amd64.whl", hash = "sha256:9e3ab684ad403a9ed1226894c32c3ab9c2e0718440f6f50c7c5829932bc9e054"},
-    {file = "ruff-0.4.7-py3-none-win_arm64.whl", hash = "sha256:10f2204b9a613988e3484194c2c9e96a22079206b22b787605c255f130db5ed7"},
-    {file = "ruff-0.4.7.tar.gz", hash = "sha256:2331d2b051dc77a289a653fcc6a42cce357087c5975738157cd966590b18b5e1"},
+    {file = "ruff-0.4.8-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:7663a6d78f6adb0eab270fa9cf1ff2d28618ca3a652b60f2a234d92b9ec89066"},
+    {file = "ruff-0.4.8-py3-none-macosx_11_0_arm64.whl", hash = "sha256:eeceb78da8afb6de0ddada93112869852d04f1cd0f6b80fe464fd4e35c330913"},
+    {file = "ruff-0.4.8-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aad360893e92486662ef3be0a339c5ca3c1b109e0134fcd37d534d4be9fb8de3"},
+    {file = "ruff-0.4.8-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:284c2e3f3396fb05f5f803c9fffb53ebbe09a3ebe7dda2929ed8d73ded736deb"},
+    {file = "ruff-0.4.8-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a7354f921e3fbe04d2a62d46707e569f9315e1a613307f7311a935743c51a764"},
+    {file = "ruff-0.4.8-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:72584676164e15a68a15778fd1b17c28a519e7a0622161eb2debdcdabdc71883"},
+    {file = "ruff-0.4.8-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9678d5c9b43315f323af2233a04d747409d1e3aa6789620083a82d1066a35199"},
+    {file = "ruff-0.4.8-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:704977a658131651a22b5ebeb28b717ef42ac6ee3b11e91dc87b633b5d83142b"},
+    {file = "ruff-0.4.8-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d05f8d6f0c3cce5026cecd83b7a143dcad503045857bc49662f736437380ad45"},
+    {file = "ruff-0.4.8-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:6ea874950daca5697309d976c9afba830d3bf0ed66887481d6bca1673fc5b66a"},
+    {file = "ruff-0.4.8-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:fc95aac2943ddf360376be9aa3107c8cf9640083940a8c5bd824be692d2216dc"},
+    {file = "ruff-0.4.8-py3-none-musllinux_1_2_i686.whl", hash = "sha256:384154a1c3f4bf537bac69f33720957ee49ac8d484bfc91720cc94172026ceed"},
+    {file = "ruff-0.4.8-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:e9d5ce97cacc99878aa0d084c626a15cd21e6b3d53fd6f9112b7fc485918e1fa"},
+    {file = "ruff-0.4.8-py3-none-win32.whl", hash = "sha256:6d795d7639212c2dfd01991259460101c22aabf420d9b943f153ab9d9706e6a9"},
+    {file = "ruff-0.4.8-py3-none-win_amd64.whl", hash = "sha256:e14a3a095d07560a9d6769a72f781d73259655919d9b396c650fc98a8157555d"},
+    {file = "ruff-0.4.8-py3-none-win_arm64.whl", hash = "sha256:14019a06dbe29b608f6b7cbcec300e3170a8d86efaddb7b23405cb7f7dcaf780"},
+    {file = "ruff-0.4.8.tar.gz", hash = "sha256:16d717b1d57b2e2fd68bd0bf80fb43931b79d05a7131aa477d66fc40fbd86268"},
 ]
 
 [[package]]
@@ -1914,13 +1875,13 @@ typing-extensions = ">=3.7.4.3"
 
 [[package]]
 name = "typing-extensions"
-version = "4.12.1"
+version = "4.12.2"
 description = "Backported and Experimental Type Hints for Python 3.8+"
 optional = false
 python-versions = ">=3.8"
 files = [
-    {file = "typing_extensions-4.12.1-py3-none-any.whl", hash = "sha256:6024b58b69089e5a89c347397254e35f1bf02a907728ec7fee9bf0fe837d203a"},
-    {file = "typing_extensions-4.12.1.tar.gz", hash = "sha256:915f5e35ff76f56588223f15fdd5938f9a1cf9195c0de25130c627e4d597f6d1"},
+    {file = "typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d"},
+    {file = "typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"},
 ]
 
 [[package]]
@@ -2029,13 +1990,13 @@ zstd = ["zstandard (>=0.18.0)"]
 
 [[package]]
 name = "uvicorn"
-version = "0.30.0"
+version = "0.30.1"
 description = "The lightning-fast ASGI server."
 optional = false
 python-versions = ">=3.8"
 files = [
-    {file = "uvicorn-0.30.0-py3-none-any.whl", hash = "sha256:78fa0b5f56abb8562024a59041caeb555c86e48d0efdd23c3fe7de7a4075bdab"},
-    {file = "uvicorn-0.30.0.tar.gz", hash = "sha256:f678dec4fa3a39706bbf49b9ec5fc40049d42418716cea52b53f07828a60aa37"},
+    {file = "uvicorn-0.30.1-py3-none-any.whl", hash = "sha256:cd17daa7f3b9d7a24de3617820e634d0933b69eed8e33a516071174427238c81"},
+    {file = "uvicorn-0.30.1.tar.gz", hash = "sha256:d46cd8e0fd80240baffbcd9ec1012a712938754afcf81bce56c024c1656aece8"},
 ]
 
 [package.dependencies]
@@ -2395,5 +2356,5 @@ multidict = ">=4.0"
 
 [metadata]
 lock-version = "2.0"
-python-versions = "^3.8"
-content-hash = "e70942584a5337d1cc0e05b42a6eead54f33bda3d802b7246c16538c33519d66"
+python-versions = ">=3.9, <4.0"
+content-hash = "3aac7d490897595692d1fb722cbde4fe8ca75d66f23cc3ad98c12227bb8bd078"
diff --git a/pyproject.toml b/pyproject.toml
index 691f500..53df875 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -8,10 +8,10 @@ readme = "README.md"
 packages = [{ include = "nonebot_plugin_saa" }]
 classifiers = [
   "Development Status :: 3 - Alpha",
-  "Programming Language :: Python :: 3.8",
   "Programming Language :: Python :: 3.9",
   "Programming Language :: Python :: 3.10",
   "Programming Language :: Python :: 3.11",
+  "Programming Language :: Python :: 3.12",
   "Operating System :: Microsoft :: Windows",
   "Operating System :: POSIX :: Linux",
   "Operating System :: MacOS",
@@ -19,8 +19,8 @@ classifiers = [
 ]
 
 [tool.poetry.dependencies]
-python = "^3.8"
-nonebot2 = "^2.0.0"
+python = ">=3.9, <4.0"
+nonebot2 = "^2.3.0"
 strenum = "^0.4.8"
 pydantic = ">=1.10.0,<3.0.0,!=2.5.0,!=2.5.1"
 anyio = ">=3.3.0,<5.0.0"
@@ -30,7 +30,7 @@ filetype = "^1.2.0"
 pytest-asyncio = "^0.20.2"
 ipdb = "^0.13.9"
 pip = "^23.0"
-pytest-cov = "^4.0.0"
+pytest-cov = "^5.0.0"
 nonebug = "^0.3.1"
 nonebot2 = { extras = [
   "fastapi",
@@ -56,7 +56,7 @@ nonebot-adapter-satori = "^0.10.2"
 
 [tool.black]
 line-length = 88
-target-version = ["py38", "py39", "py310"]
+target-version = ["py39", "py310", "py311", "py312"]
 include = '\.pyi?$'
 extend-exclude = '''
 '''
@@ -67,6 +67,7 @@ line_length = 88
 length_sort = true
 skip_gitignore = true
 force_sort_within_sections = true
+src_paths = ["nonebot_plugin_saa", "tests"]
 extra_standard_library = ["typing_extensions"]
 
 [build-system]
@@ -77,9 +78,43 @@ build-backend = "poetry.core.masonry.api"
 asyncio_mode = "auto"
 
 [tool.pyright]
-pythonVersion = "3.8"
+pythonVersion = "3.9"
 pythonPlatform = "All"
 defineConstant = { PYDANTIC_V2 = true }
+typeCheckingMode = "basic"
+reportShadowedImports = false
+disableBytesTypePromotions = true
+
+[tool.ruff]
+line-length = 88
+target-version = "py39"
+
+[tool.ruff.lint]
+select = [
+  "F",     # Pyflakes
+  "W",     # pycodestyle warnings
+  "E",     # pycodestyle errors
+  "UP",    # pyupgrade
+  "ASYNC", # flake8-async
+  "C4",    # flake8-comprehensions
+  "T10",   # flake8-debugger
+  "T20",   # flake8-print
+  "PYI",   # flake8-pyi
+  "PT",    # flake8-pytest-style
+  "Q",     # flake8-quotes
+  "RUF",   # Ruff-specific rules
+]
+ignore = [
+  "E402",   # module-import-not-at-top-of-file
+  "UP037",  # quoted-annotation
+  "RUF001", # ambiguous-unicode-character-string
+  "RUF002", # ambiguous-unicode-character-docstring
+  "RUF003", # ambiguous-unicode-character-comment
+]
+
+[tool.ruff.lint.flake8-pytest-style]
+fixture-parentheses = false
+mark-parentheses = false
 
 [tool.coverage.report]
 exclude_also = [


Test patch:
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index 155ba83..ef7a579 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -22,7 +22,7 @@ jobs:
     runs-on: ${{ matrix.os }}
     strategy:
       matrix:
-        python-version: ["3.8", "3.9", "3.10", "3.11"]
+        python-version: ["3.9", "3.10", "3.11", "3.12"]
         os: [ubuntu-latest, windows-latest, macos-latest]
       fail-fast: false
     env:
diff --git a/tests/conftest.py b/tests/conftest.py
index 9e47e91..f1bf42c 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -26,7 +26,7 @@ def app(app: App):
 
 
 @pytest.fixture(scope="session", autouse=True)
-def load_adapters(nonebug_init: None):
+def load_adapters(nonebug_init: None):  # noqa: PT004
     driver = nonebot.get_driver()
     driver.register_adapter(OnebotV11Adapter)
     driver.register_adapter(OnebotV12Adapter)
diff --git a/tests/test_dodo.py b/tests/test_dodo.py
index 35eb8ce..1dd1877 100644
--- a/tests/test_dodo.py
+++ b/tests/test_dodo.py
@@ -105,7 +105,9 @@ async def test_mention_all(app: App, assert_dodo):
     from nonebot_plugin_saa import MentionAll, SupportedAdapters
 
     await assert_dodo(app, MentionAll(), MessageSegment.text("@全体成员 "))
-    await assert_dodo(app, MentionAll(online_only=True), MessageSegment.text("@在线成员 "))
+    await assert_dodo(
+        app, MentionAll(online_only=True), MessageSegment.text("@在线成员 ")
+    )
     await assert_dodo(app, MentionAll("amiya"), MessageSegment.text("amiya"))
     await assert_dodo(
         app, MentionAll("@amiya", online_only=True), MessageSegment.text("@amiya")
@@ -495,7 +497,7 @@ async def test_extract_target(app: App):
         message_type=MessageType(1),
         message_body=TextMessage(content="aa"),
     )
-    with pytest.raises(ValueError):
+    with pytest.raises(ValueError):  # noqa: PT011
         extract_target(pme2)
 
     gse = GiftSendEvent(
diff --git a/tests/test_kaiheila.py b/tests/test_kaiheila.py
index 4a683ca..1d760a3 100644
--- a/tests/test_kaiheila.py
+++ b/tests/test_kaiheila.py
@@ -30,7 +30,7 @@
 
 
 @pytest.fixture(scope="session", autouse=True)
-def load_adapters(nonebug_init: None):
+def load_adapters(nonebug_init: None):  # noqa: PT004
     driver = nonebot.get_driver()
     driver.register_adapter(Adapter)
 
@@ -58,17 +58,17 @@ def mock_kaiheila_message_event(channel=False):
             event=EventMessage(
                 type=9,
                 author=User(id="3344", username="3344", identify_num="3344"),
-                content="/abc",
+                content="/abc",  # type: ignore
                 mention=[],
-                mention_role=[],
+                mention_roles=[],
                 mention_all=False,
                 mention_here=False,
                 kmarkdown={
                     "raw_content": "/abc",
                     "mention_part": [],
                     "mention_role_part": [],
-                },
-            ),  # type: ignore
+                },  # type: ignore
+            ),
             message_type="private",
         )
     else:
@@ -86,17 +86,17 @@ def mock_kaiheila_message_event(channel=False):
             event=EventMessage(
                 type=9,
                 author=User(id="3344", username="3344", identify_num="3344"),
-                content="/abc",
+                content="/abc",  # type: ignore
                 mention=[],
-                mention_role=[],
+                mention_roles=[],
                 mention_all=False,
                 mention_here=False,
                 kmarkdown={
                     "raw_content": "/abc",
                     "mention_part": [],
                     "mention_role_part": [],
-                },
-            ),  # type: ignore
+                },  # type: ignore
+            ),
             message_type="group",
             group_id="1111",
         )
diff --git a/tests/test_message.py b/tests/test_message.py
index ee3e4ea..ce2cd21 100644
--- a/tests/test_message.py
+++ b/tests/test_message.py
@@ -136,10 +136,10 @@ def test_message_add(app: App):
         [Text("123"), Image("http://example.com/abc.png"), Text("abc")]
     )
     assert sit == MessageFactory([s, i, t])
-    assert sit == [s, i] + t
+    assert sit == [s, i] + t  # noqa: RUF005
 
     tit = t + i + t
-    t_it = t + [i, t]
+    t_it = t + [i, t]  # noqa: RUF005
     assert tit == MessageFactory(
         [Text("abc"), Image("http://example.com/abc.png"), Text("abc")]
     )
@@ -147,7 +147,7 @@ def test_message_add(app: App):
     assert tit == t_it
 
     tir = t + i + r
-    assert tir == t + [i, r]
+    assert tir == t + [i, r]  # noqa: RUF005
     assert tir == MessageFactory([t, i, r])
     ir = i + r
     assert ti + r == t + ir
diff --git a/tests/test_message_id.py b/tests/test_message_id.py
new file mode 100644
index 0000000..75fd92e
--- /dev/null
+++ b/tests/test_message_id.py
@@ -0,0 +1,19 @@
+import pytest
+from nonebug import App
+
+
+def test_type_message_id_check(app: App):
+    from nonebot_plugin_saa.utils import type_message_id_check
+    from nonebot_plugin_saa.adapters.onebot_v11 import OB11MessageId
+    from nonebot_plugin_saa.adapters.onebot_v12 import OB12MessageId
+    from nonebot_plugin_saa.abstract_factories import SupportedAdapters
+    from nonebot_plugin_saa.utils.exceptions import UnexpectedMessageIdType
+
+    message_id = OB11MessageId(
+        adapter_name=SupportedAdapters.onebot_v11, message_id=1919810
+    )
+
+    assert type_message_id_check(OB11MessageId, message_id) == message_id
+
+    with pytest.raises(UnexpectedMessageIdType):
+        type_message_id_check(OB12MessageId, message_id)
diff --git a/tests/test_qq.py b/tests/test_qq.py
index d0e3694..25d0c84 100644
--- a/tests/test_qq.py
+++ b/tests/test_qq.py
@@ -4,6 +4,7 @@
 
 import pytest
 from nonebug import App
+from anyio import open_file
 from nonebot import get_adapter
 from nonebot.compat import model_dump
 from pytest_mock import MockerFixture
@@ -89,9 +90,10 @@ async def test_image(app: App, tmp_path: Path, assert_qq):
     await assert_qq(app, Image(data), MessageSegment.file_image(data))
 
     image_path = tmp_path / "image.png"
-    with open(image_path, "wb") as f:
-        f.write(data)
+    f = await open_file(image_path, "wb")
+    await f.write(data)
     await assert_qq(app, Image(image_path), MessageSegment.file_image(image_path))
+    await f.aclose()
 
 
 async def test_mention_user(app: App, assert_qq):
diff --git a/tests/test_satori.py b/tests/test_satori.py
index 7dddd98..0d4c73b 100644
--- a/tests/test_satori.py
+++ b/tests/test_satori.py
@@ -2,7 +2,7 @@
 from pathlib import Path
 from datetime import datetime
 from functools import partial
-from typing import Any, Dict, Literal
+from typing import Any, Literal
 
 import pytest
 from nonebug import App
@@ -14,7 +14,7 @@
 
 from .utils import assert_ms, mock_satori_message_event
 
-satori_kwargs: Dict[Literal["platform", "info"], Any] = {
+satori_kwargs: dict[Literal["platform", "info"], Any] = {
     "platform": "qq",
     "info": ClientInfo(port=12345),
 }
diff --git a/tests/test_telegram.py b/tests/test_telegram.py
index 6232362..8ef56d9 100644
--- a/tests/test_telegram.py
+++ b/tests/test_telegram.py
@@ -1,6 +1,7 @@
 import time
 from functools import partial
-from typing import TYPE_CHECKING, Callable, Awaitable
+from collections.abc import Awaitable
+from typing import TYPE_CHECKING, Callable
 
 import pytest
 from nonebug import App
@@ -9,7 +10,7 @@
 
 from .utils import assert_ms, mock_telegram_message_event
 
-BOT_CONFIG = BotConfig(token="123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11")  # noqa: S106
+BOT_CONFIG = BotConfig(token="123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11")
 
 if TYPE_CHECKING:
     from nonebot.adapters import MessageSegment
@@ -23,7 +24,7 @@
 ]
 
 
-@pytest.fixture()
+@pytest.fixture
 def assert_telegram(app: App):
     from nonebot_plugin_saa import SupportedAdapters
 
diff --git a/tests/utils.py b/tests/utils.py
index faf1238..d864802 100644
--- a/tests/utils.py
+++ b/tests/utils.py
@@ -1,6 +1,6 @@
 import random
 from datetime import datetime
-from typing import TYPE_CHECKING, Any, Dict, Type, Literal, cast
+from typing import TYPE_CHECKING, Any, Literal, cast
 
 from nonebot import get_driver
 
@@ -22,7 +22,7 @@
 
 
 async def assert_ms(
-    bot_base: "Type[Bot]",
+    bot_base: "type[Bot]",
     adapter: "SupportedAdapters",
     app: "App",
     ms_factory: "MessageSegmentFactory",
@@ -267,7 +267,7 @@ def mock_qq_message_event(message: "QQMessage", direct=False):
         )
 
 
-def ob12_kwargs(platform="qq", impl="walle") -> Dict[str, Any]:
+def ob12_kwargs(platform="qq", impl="walle") -> dict[str, Any]:
     return {"platform": platform, "impl": impl}