diff --git a/docs/.vuepress/config.js b/docs/.vuepress/config.js index d9d942ab..5d7cf2fb 100644 --- a/docs/.vuepress/config.js +++ b/docs/.vuepress/config.js @@ -198,6 +198,10 @@ module.exports = context => ({ { title: "nonebot.adapters.cqhttp 模块", path: "adapters/cqhttp" + }, + { + title: "nonebot.adapters.ding 模块", + path: "adapters/ding" } ] } diff --git a/docs/api/README.md b/docs/api/README.md index 810b1964..243733f8 100644 --- a/docs/api/README.md +++ b/docs/api/README.md @@ -47,3 +47,6 @@ * [nonebot.adapters.cqhttp](adapters/cqhttp.html) + + + * [nonebot.adapters.ding](adapters/ding.html) diff --git a/docs/api/adapters/cqhttp.md b/docs/api/adapters/cqhttp.md index 6dd7f4a9..b80d6ad1 100644 --- a/docs/api/adapters/cqhttp.md +++ b/docs/api/adapters/cqhttp.md @@ -4,3 +4,439 @@ sidebarDepth: 0 --- # NoneBot.adapters.cqhttp 模块 + + +## `escape(s, *, escape_comma=True)` + + +* **说明** + + 对字符串进行 CQ 码转义。 + + + +* **参数** + + + * `s: str`: 需要转义的字符串 + + + * `escape_comma: bool`: 是否转义逗号(`,`)。 + + + +## `unescape(s)` + + +* **说明** + + 对字符串进行 CQ 码去转义。 + + + +* **参数** + + + * `s: str`: 需要转义的字符串 + + + +## _exception_ `CQHTTPAdapterException` + +基类:[`nonebot.exception.AdapterException`](../exception.md#nonebot.exception.AdapterException) + + +## _exception_ `ActionFailed` + +基类:[`nonebot.exception.ActionFailed`](../exception.md#nonebot.exception.ActionFailed), `nonebot.adapters.cqhttp.exception.CQHTTPAdapterException` + + +* **说明** + + API 请求返回错误信息。 + + + +* **参数** + + + * `retcode: Optional[int]`: 错误码 + + + +## _exception_ `NetworkError` + +基类:[`nonebot.exception.NetworkError`](../exception.md#nonebot.exception.NetworkError), `nonebot.adapters.cqhttp.exception.CQHTTPAdapterException` + + +* **说明** + + 网络错误。 + + + +* **参数** + + + * `retcode: Optional[int]`: 错误码 + + + +## _exception_ `ApiNotAvailable` + +基类:[`nonebot.exception.ApiNotAvailable`](../exception.md#nonebot.exception.ApiNotAvailable), `nonebot.adapters.cqhttp.exception.CQHTTPAdapterException` + + +## _async_ `_check_reply(bot, event)` + + +* **说明** + + 检查消息中存在的回复,去除并赋值 `event.reply`, `event.to_me` + + + +* **参数** + + + * `bot: Bot`: Bot 对象 + + + * `event: Event`: Event 对象 + + + +## `_check_at_me(bot, event)` + + +* **说明** + + 检查消息开头或结尾是否存在 @机器人,去除并赋值 `event.to_me` + + + +* **参数** + + + * `bot: Bot`: Bot 对象 + + + * `event: Event`: Event 对象 + + + +## `_check_nickname(bot, event)` + + +* **说明** + + 检查消息开头是否存在,去除并赋值 `event.to_me` + + + +* **参数** + + + * `bot: Bot`: Bot 对象 + + + * `event: Event`: Event 对象 + + + +## `_handle_api_result(result)` + + +* **说明** + + 处理 API 请求返回值。 + + + +* **参数** + + + * `result: Optional[Dict[str, Any]]`: API 返回数据 + + + +* **返回** + + + * `Any`: API 调用返回数据 + + + +* **异常** + + + * `ActionFailed`: API 调用失败 + + + +## _class_ `Bot` + +基类:[`nonebot.adapters.BaseBot`](README.md#nonebot.adapters.BaseBot) + +CQHTTP 协议 Bot 适配。继承属性参考 [BaseBot](./#class-basebot) 。 + + +### _property_ `type` + + +* 返回: `"cqhttp"` + + +### _async classmethod_ `check_permission(driver, connection_type, headers, body)` + + +* **说明** + + CQHTTP (OneBot) 协议鉴权。参考 [鉴权](https://github.com/howmanybots/onebot/blob/master/v11/specs/communication/authorization.md) + + + +### _async_ `handle_message(message)` + + +* **说明** + + 调用 [_check_reply](#async-check-reply-bot-event), [_check_at_me](#check-at-me-bot-event), [_check_nickname](#check-nickname-bot-event) 处理事件并转换为 [Event](#class-event) + + + +### _async_ `call_api(api, **data)` + + +* **说明** + + 调用 CQHTTP 协议 API + + + +* **参数** + + + * `api: str`: API 名称 + + + * `**data: Any`: API 参数 + + + +* **返回** + + + * `Any`: API 调用返回数据 + + + +* **异常** + + + * `NetworkError`: 网络错误 + + + * `ActionFailed`: API 调用失败 + + + +### _async_ `send(event, message, at_sender=False, **kwargs)` + + +* **说明** + + 根据 `event` 向触发事件的主体发送消息。 + + + +* **参数** + + + * `event: Event`: Event 对象 + + + * `message: Union[str, Message, MessageSegment]`: 要发送的消息 + + + * `at_sender: bool`: 是否 @ 事件主体 + + + * `**kwargs`: 覆盖默认参数 + + + +* **返回** + + + * `Any`: API 调用返回数据 + + + +* **异常** + + + * `ValueError`: 缺少 `user_id`, `group_id` + + + * `NetworkError`: 网络错误 + + + * `ActionFailed`: API 调用失败 + + + +## _class_ `Event` + +基类:[`nonebot.adapters.BaseEvent`](README.md#nonebot.adapters.BaseEvent) + +CQHTTP 协议 Event 适配。继承属性参考 [BaseEvent](./#class-baseevent) 。 + + +### _property_ `id` + + +* 类型: `Optional[int]` + + +* 说明: 事件/消息 ID + + +### _property_ `name` + + +* 类型: `str` + + +* 说明: 事件名称,由类型与 `.` 组合而成 + + +### _property_ `self_id` + + +* 类型: `str` + + +* 说明: 机器人自身 ID + + +### _property_ `time` + + +* 类型: `int` + + +* 说明: 事件发生时间 + + +### _property_ `type` + + +* 类型: `str` + + +* 说明: 事件类型 + + +### _property_ `detail_type` + + +* 类型: `str` + + +* 说明: 事件详细类型 + + +### _property_ `sub_type` + + +* 类型: `Optional[str]` + + +* 说明: 事件子类型 + + +### _property_ `user_id` + + +* 类型: `Optional[int]` + + +* 说明: 事件主体 ID + + +### _property_ `group_id` + + +* 类型: `Optional[int]` + + +* 说明: 事件主体群 ID + + +### _property_ `to_me` + + +* 类型: `Optional[bool]` + + +* 说明: 消息是否与机器人相关 + + +### _property_ `message` + + +* 类型: `Optional[Message]` + + +* 说明: 消息内容 + + +### _property_ `reply` + + +* 类型: `Optional[dict]` + + +* 说明: 回复消息详情 + + +### _property_ `raw_message` + + +* 类型: `Optional[str]` + + +* 说明: 原始消息 + + +### _property_ `plain_text` + + +* 类型: `Optional[str]` + + +* 说明: 纯文本消息内容 + + +### _property_ `sender` + + +* 类型: `Optional[dict]` + + +* 说明: 消息发送者信息 + + +## _class_ `MessageSegment` + +基类:[`nonebot.adapters.BaseMessageSegment`](README.md#nonebot.adapters.BaseMessageSegment) + +CQHTTP 协议 MessageSegment 适配。具体方法参考协议消息段类型或源码。 + + +## _class_ `Message` + +基类:[`nonebot.adapters.BaseMessage`](README.md#nonebot.adapters.BaseMessage) + +CQHTTP 协议 Message 适配。 diff --git a/docs_build/README.rst b/docs_build/README.rst index 9f40afa9..95ffcc2d 100644 --- a/docs_build/README.rst +++ b/docs_build/README.rst @@ -17,3 +17,4 @@ NoneBot Api Reference - `nonebot.drivers.fastapi `_ - `nonebot.adapters `_ - `nonebot.adapters.cqhttp `_ + - `nonebot.adapters.ding `_ diff --git a/docs_build/adapters/cqhttp.rst b/docs_build/adapters/cqhttp.rst index 3e63952b..4714296d 100644 --- a/docs_build/adapters/cqhttp.rst +++ b/docs_build/adapters/cqhttp.rst @@ -6,7 +6,28 @@ sidebarDepth: 0 NoneBot.adapters.cqhttp 模块 ============================ -.. automodule:: nonebot.adapters.cqhttp +.. automodule:: nonebot.adapters.cqhttp.utils + :members: + :show-inheritance: + +.. automodule:: nonebot.adapters.cqhttp.exception + :members: + :show-inheritance: + + +.. automodule:: nonebot.adapters.cqhttp.bot + :members: + :private-members: + :show-inheritance: + + +.. automodule:: nonebot.adapters.cqhttp.event + :members: + :private-members: + :show-inheritance: + + +.. automodule:: nonebot.adapters.cqhttp.message :members: :private-members: :show-inheritance: diff --git a/nonebot/adapters/cqhttp/__init__.py b/nonebot/adapters/cqhttp/__init__.py index 5de85a92..8271c1e6 100644 --- a/nonebot/adapters/cqhttp/__init__.py +++ b/nonebot/adapters/cqhttp/__init__.py @@ -10,7 +10,7 @@ CQHTTP (OneBot) v11 协议适配 https://github.com/howmanybots/onebot/blob/master/README.md """ -from .utils import log from .event import Event from .message import Message, MessageSegment -from .bot import Bot, _check_at_me, _check_nickname, _check_reply +from .utils import log, escape, unescape, _b2s +from .bot import Bot, _check_at_me, _check_nickname, _check_reply, _handle_api_result diff --git a/nonebot/adapters/cqhttp/bot.py b/nonebot/adapters/cqhttp/bot.py index 5c7ac7a4..af673ec9 100644 --- a/nonebot/adapters/cqhttp/bot.py +++ b/nonebot/adapters/cqhttp/bot.py @@ -10,14 +10,24 @@ from nonebot.log import logger from nonebot.config import Config from nonebot.adapters import BaseBot from nonebot.message import handle_event +from nonebot.exception import RequestDenied from nonebot.typing import Any, Dict, Union, Optional from nonebot.typing import overrides, Driver, WebSocket, NoReturn -from nonebot.exception import NetworkError, RequestDenied, ApiNotAvailable from .event import Event -from .exception import ApiError -from .utils import log, get_auth_bearer from .message import Message, MessageSegment +from .exception import NetworkError, ApiNotAvailable, ActionFailed +from .utils import log + + +def get_auth_bearer( + access_token: Optional[str] = None) -> Union[Optional[str], NoReturn]: + if not access_token: + return None + scheme, _, param = access_token.partition(" ") + if scheme.lower() not in ["bearer", "token"]: + raise RequestDenied(401, "Not authenticated") + return param async def _check_reply(bot: "Bot", event: "Event"): @@ -160,11 +170,11 @@ def _handle_api_result( :异常: - - ``ApiError``: API 调用失败 + - ``ActionFailed``: API 调用失败 """ if isinstance(result, dict): if result.get("status") == "failed": - raise ApiError(retcode=result.get("retcode")) + raise ActionFailed(retcode=result.get("retcode")) return result.get("data") @@ -318,7 +328,7 @@ class Bot(BaseBot): :异常: - ``NetworkError``: 网络错误 - - ``ApiError``: API 调用失败 + - ``ActionFailed``: API 调用失败 """ if "self_id" in data: self_id = data.pop("self_id") @@ -393,7 +403,7 @@ class Bot(BaseBot): - ``ValueError``: 缺少 ``user_id``, ``group_id`` - ``NetworkError``: 网络错误 - - ``ApiError``: API 调用失败 + - ``ActionFailed``: API 调用失败 """ msg = message if isinstance(message, Message) else Message(message) diff --git a/nonebot/adapters/cqhttp/__init__.pyi b/nonebot/adapters/cqhttp/bot.pyi similarity index 78% rename from nonebot/adapters/cqhttp/__init__.pyi rename to nonebot/adapters/cqhttp/bot.pyi index e5398588..745b4941 100644 --- a/nonebot/adapters/cqhttp/__init__.pyi +++ b/nonebot/adapters/cqhttp/bot.pyi @@ -1,12 +1,11 @@ import asyncio from nonebot.config import Config -from nonebot.adapters import BaseBot, BaseEvent, BaseMessage, BaseMessageSegment -from nonebot.typing import Any, Dict, List, Union, Driver, Optional, NoReturn, WebSocket, Iterable +from nonebot.adapters import BaseBot +from nonebot.typing import Any, Dict, List, Union, Driver, Optional, NoReturn, WebSocket - -def log(level: str, message: str): - ... +from .event import Event +from .message import Message, MessageSegment def get_auth_bearer( @@ -14,27 +13,15 @@ def get_auth_bearer( ... -def escape(s: str, *, escape_comma: bool = ...) -> str: +async def _check_reply(bot: "Bot", event: Event): ... -def unescape(s: str) -> str: +def _check_at_me(bot: "Bot", event: Event): ... -def _b2s(b: Optional[bool]) -> Optional[str]: - ... - - -async def _check_reply(bot: "Bot", event: "Event"): - ... - - -def _check_at_me(bot: "Bot", event: "Event"): - ... - - -def _check_nickname(bot: "Bot", event: "Event"): +def _check_nickname(bot: "Bot", event: Event): ... @@ -86,8 +73,8 @@ class Bot(BaseBot): async def call_api(self, api: str, **data) -> Union[Any, NoReturn]: ... - async def send(self, event: "Event", message: Union[str, "Message", - "MessageSegment"], + async def send(self, event: Event, message: Union[str, Message, + MessageSegment], **kwargs) -> Union[Any, NoReturn]: ... @@ -759,242 +746,3 @@ class Bot(BaseBot): * ``self_id``: 机器人 QQ 号 """ ... - - -class Event(BaseEvent): - - def __init__(self, raw_event: dict): - ... - - @property - def id(self) -> Optional[int]: - ... - - @property - def name(self) -> str: - ... - - @property - def self_id(self) -> str: - ... - - @property - def time(self) -> int: - ... - - @property - def type(self) -> str: - ... - - @type.setter - def type(self, value) -> None: - ... - - @property - def detail_type(self) -> str: - ... - - @detail_type.setter - def detail_type(self, value) -> None: - ... - - @property - def sub_type(self) -> Optional[str]: - ... - - @sub_type.setter - def sub_type(self, value) -> None: - ... - - @property - def user_id(self) -> Optional[int]: - ... - - @user_id.setter - def user_id(self, value) -> None: - ... - - @property - def group_id(self) -> Optional[int]: - ... - - @group_id.setter - def group_id(self, value) -> None: - ... - - @property - def to_me(self) -> Optional[bool]: - ... - - @to_me.setter - def to_me(self, value) -> None: - ... - - @property - def message(self) -> Optional["Message"]: - ... - - @message.setter - def message(self, value) -> None: - ... - - @property - def reply(self) -> Optional[dict]: - ... - - @reply.setter - def reply(self, value) -> None: - ... - - @property - def raw_message(self) -> Optional[str]: - ... - - @raw_message.setter - def raw_message(self, value) -> None: - ... - - @property - def plain_text(self) -> Optional[str]: - ... - - @property - def sender(self) -> Optional[dict]: - ... - - @sender.setter - def sender(self, value) -> None: - ... - - -class MessageSegment(BaseMessageSegment): - - def __init__(self, type: str, data: Dict[str, Any]) -> None: - ... - - def __str__(self): - ... - - def __add__(self, other) -> "Message": - ... - - @staticmethod - def anonymous(ignore_failure: Optional[bool] = ...) -> "MessageSegment": - ... - - @staticmethod - def at(user_id: Union[int, str]) -> "MessageSegment": - ... - - @staticmethod - def contact_group(group_id: int) -> "MessageSegment": - ... - - @staticmethod - def contact_user(user_id: int) -> "MessageSegment": - ... - - @staticmethod - def dice() -> "MessageSegment": - ... - - @staticmethod - def face(id_: int) -> "MessageSegment": - ... - - @staticmethod - def forward(id_: str) -> "MessageSegment": - ... - - @staticmethod - def image(file: str, - type_: Optional[str] = ..., - cache: bool = ..., - proxy: bool = ..., - timeout: Optional[int] = ...) -> "MessageSegment": - ... - - @staticmethod - def json(data: str) -> "MessageSegment": - ... - - @staticmethod - def location(latitude: float, - longitude: float, - title: Optional[str] = ..., - content: Optional[str] = ...) -> "MessageSegment": - ... - - @staticmethod - def music(type_: str, id_: int) -> "MessageSegment": - ... - - @staticmethod - def music_custom(url: str, - audio: str, - title: str, - content: Optional[str] = ..., - img_url: Optional[str] = ...) -> "MessageSegment": - ... - - @staticmethod - def node(id_: int) -> "MessageSegment": - ... - - @staticmethod - def node_custom(user_id: int, nickname: str, - content: Union[str, "Message"]) -> "MessageSegment": - ... - - @staticmethod - def poke(type_: str, id_: str) -> "MessageSegment": - ... - - @staticmethod - def record(file: str, - magic: Optional[bool] = ..., - cache: Optional[bool] = ..., - proxy: Optional[bool] = ..., - timeout: Optional[int] = ...) -> "MessageSegment": - ... - - @staticmethod - def reply(id_: int) -> "MessageSegment": - ... - - @staticmethod - def rps() -> "MessageSegment": - ... - - @staticmethod - def shake() -> "MessageSegment": - ... - - @staticmethod - def share(url: str = ..., - title: str = ..., - content: Optional[str] = ..., - img_url: Optional[str] = ...) -> "MessageSegment": - ... - - @staticmethod - def text(text: str) -> "MessageSegment": - ... - - @staticmethod - def video(file: str, - cache: Optional[bool] = ..., - proxy: Optional[bool] = ..., - timeout: Optional[int] = ...) -> "MessageSegment": - ... - - @staticmethod - def xml(data: str) -> "MessageSegment": - ... - - -class Message(BaseMessage): - - @staticmethod - def _construct(msg: Union[str, dict, list]) -> Iterable[MessageSegment]: - ... diff --git a/nonebot/adapters/cqhttp/event.py b/nonebot/adapters/cqhttp/event.py index f3071151..5bc959c9 100644 --- a/nonebot/adapters/cqhttp/event.py +++ b/nonebot/adapters/cqhttp/event.py @@ -1,6 +1,5 @@ -from nonebot.typing import overrides -from nonebot.typing import Optional from nonebot.adapters import BaseEvent +from nonebot.typing import Optional, overrides from .message import Message diff --git a/nonebot/adapters/cqhttp/exception.py b/nonebot/adapters/cqhttp/exception.py index 86fe963c..2bcc73f4 100644 --- a/nonebot/adapters/cqhttp/exception.py +++ b/nonebot/adapters/cqhttp/exception.py @@ -1,4 +1,8 @@ -from nonebot.exception import AdapterException, ActionFailed +from nonebot.typing import Optional +from nonebot.exception import (AdapterException, ActionFailed as + BaseActionFailed, NetworkError as + BaseNetworkError, ApiNotAvailable as + BaseApiNotAvailable) class CQHTTPAdapterException(AdapterException): @@ -7,7 +11,7 @@ class CQHTTPAdapterException(AdapterException): super().__init__("cqhttp") -class ApiError(CQHTTPAdapterException, ActionFailed): +class ActionFailed(BaseActionFailed, CQHTTPAdapterException): """ :说明: @@ -27,3 +31,29 @@ class ApiError(CQHTTPAdapterException, ActionFailed): def __str__(self): return self.__repr__() + + +class NetworkError(BaseNetworkError, CQHTTPAdapterException): + """ + :说明: + + 网络错误。 + + :参数: + + * ``retcode: Optional[int]``: 错误码 + """ + + def __init__(self, msg: Optional[str] = None): + super().__init__() + self.msg = msg + + def __repr__(self): + return f"" + + def __str__(self): + return self.__repr__() + + +class ApiNotAvailable(BaseApiNotAvailable, CQHTTPAdapterException): + pass diff --git a/nonebot/adapters/cqhttp/message.py b/nonebot/adapters/cqhttp/message.py index b9f317e0..f411aa2b 100644 --- a/nonebot/adapters/cqhttp/message.py +++ b/nonebot/adapters/cqhttp/message.py @@ -3,6 +3,7 @@ import re from nonebot.typing import overrides from nonebot.typing import Any, Dict, Union, Tuple, Iterable, Optional from nonebot.adapters import BaseMessage, BaseMessageSegment + from .utils import log, escape, unescape, _b2s diff --git a/nonebot/adapters/cqhttp/utils.py b/nonebot/adapters/cqhttp/utils.py index 40661891..ecfee872 100644 --- a/nonebot/adapters/cqhttp/utils.py +++ b/nonebot/adapters/cqhttp/utils.py @@ -1,21 +1,9 @@ -from nonebot.typing import NoReturn -from nonebot.typing import Union, Optional -from nonebot.exception import RequestDenied +from nonebot.typing import Optional from nonebot.utils import logger_wrapper log = logger_wrapper("CQHTTP") -def get_auth_bearer( - access_token: Optional[str] = None) -> Union[Optional[str], NoReturn]: - if not access_token: - return None - scheme, _, param = access_token.partition(" ") - if scheme.lower() not in ["bearer", "token"]: - raise RequestDenied(401, "Not authenticated") - return param - - def escape(s: str, *, escape_comma: bool = True) -> str: """ :说明: diff --git a/nonebot/exception.py b/nonebot/exception.py index b97c3f08..6a4b65dc 100644 --- a/nonebot/exception.py +++ b/nonebot/exception.py @@ -6,8 +6,6 @@ 这些异常并非所有需要用户处理,在 NoneBot 内部运行时被捕获,并进行对应操作。 """ -from nonebot.typing import Optional - class NoneBotException(Exception): """