🎨 improve cqhttp

This commit is contained in:
yanyongyu 2020-12-03 16:04:14 +08:00
parent 4b764ccba3
commit dc691889e3
13 changed files with 529 additions and 290 deletions

View File

@ -198,6 +198,10 @@ module.exports = context => ({
{ {
title: "nonebot.adapters.cqhttp 模块", title: "nonebot.adapters.cqhttp 模块",
path: "adapters/cqhttp" path: "adapters/cqhttp"
},
{
title: "nonebot.adapters.ding 模块",
path: "adapters/ding"
} }
] ]
} }

View File

@ -47,3 +47,6 @@
* [nonebot.adapters.cqhttp](adapters/cqhttp.html) * [nonebot.adapters.cqhttp](adapters/cqhttp.html)
* [nonebot.adapters.ding](adapters/ding.html)

View File

@ -4,3 +4,439 @@ sidebarDepth: 0
--- ---
# NoneBot.adapters.cqhttp 模块 # 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 适配。

View File

@ -17,3 +17,4 @@ NoneBot Api Reference
- `nonebot.drivers.fastapi <drivers/fastapi.html>`_ - `nonebot.drivers.fastapi <drivers/fastapi.html>`_
- `nonebot.adapters <adapters/>`_ - `nonebot.adapters <adapters/>`_
- `nonebot.adapters.cqhttp <adapters/cqhttp.html>`_ - `nonebot.adapters.cqhttp <adapters/cqhttp.html>`_
- `nonebot.adapters.ding <adapters/ding.html>`_

View File

@ -6,7 +6,28 @@ sidebarDepth: 0
NoneBot.adapters.cqhttp 模块 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: :members:
:private-members: :private-members:
:show-inheritance: :show-inheritance:

View File

@ -10,7 +10,7 @@ CQHTTP (OneBot) v11 协议适配
https://github.com/howmanybots/onebot/blob/master/README.md https://github.com/howmanybots/onebot/blob/master/README.md
""" """
from .utils import log
from .event import Event from .event import Event
from .message import Message, MessageSegment 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

View File

@ -10,14 +10,24 @@ from nonebot.log import logger
from nonebot.config import Config from nonebot.config import Config
from nonebot.adapters import BaseBot from nonebot.adapters import BaseBot
from nonebot.message import handle_event from nonebot.message import handle_event
from nonebot.exception import RequestDenied
from nonebot.typing import Any, Dict, Union, Optional from nonebot.typing import Any, Dict, Union, Optional
from nonebot.typing import overrides, Driver, WebSocket, NoReturn from nonebot.typing import overrides, Driver, WebSocket, NoReturn
from nonebot.exception import NetworkError, RequestDenied, ApiNotAvailable
from .event import Event from .event import Event
from .exception import ApiError
from .utils import log, get_auth_bearer
from .message import Message, MessageSegment 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"): 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 isinstance(result, dict):
if result.get("status") == "failed": if result.get("status") == "failed":
raise ApiError(retcode=result.get("retcode")) raise ActionFailed(retcode=result.get("retcode"))
return result.get("data") return result.get("data")
@ -318,7 +328,7 @@ class Bot(BaseBot):
:异常: :异常:
- ``NetworkError``: 网络错误 - ``NetworkError``: 网络错误
- ``ApiError``: API 调用失败 - ``ActionFailed``: API 调用失败
""" """
if "self_id" in data: if "self_id" in data:
self_id = data.pop("self_id") self_id = data.pop("self_id")
@ -393,7 +403,7 @@ class Bot(BaseBot):
- ``ValueError``: 缺少 ``user_id``, ``group_id`` - ``ValueError``: 缺少 ``user_id``, ``group_id``
- ``NetworkError``: 网络错误 - ``NetworkError``: 网络错误
- ``ApiError``: API 调用失败 - ``ActionFailed``: API 调用失败
""" """
msg = message if isinstance(message, Message) else Message(message) msg = message if isinstance(message, Message) else Message(message)

View File

@ -1,12 +1,11 @@
import asyncio import asyncio
from nonebot.config import Config from nonebot.config import Config
from nonebot.adapters import BaseBot, BaseEvent, BaseMessage, BaseMessageSegment from nonebot.adapters import BaseBot
from nonebot.typing import Any, Dict, List, Union, Driver, Optional, NoReturn, WebSocket, Iterable from nonebot.typing import Any, Dict, List, Union, Driver, Optional, NoReturn, WebSocket
from .event import Event
def log(level: str, message: str): from .message import Message, MessageSegment
...
def get_auth_bearer( 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]: def _check_nickname(bot: "Bot", event: Event):
...
async def _check_reply(bot: "Bot", event: "Event"):
...
def _check_at_me(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 call_api(self, api: str, **data) -> Union[Any, NoReturn]:
... ...
async def send(self, event: "Event", message: Union[str, "Message", async def send(self, event: Event, message: Union[str, Message,
"MessageSegment"], MessageSegment],
**kwargs) -> Union[Any, NoReturn]: **kwargs) -> Union[Any, NoReturn]:
... ...
@ -759,242 +746,3 @@ class Bot(BaseBot):
* ``self_id``: 机器人 QQ * ``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]:
...

View File

@ -1,6 +1,5 @@
from nonebot.typing import overrides
from nonebot.typing import Optional
from nonebot.adapters import BaseEvent from nonebot.adapters import BaseEvent
from nonebot.typing import Optional, overrides
from .message import Message from .message import Message

View File

@ -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): class CQHTTPAdapterException(AdapterException):
@ -7,7 +11,7 @@ class CQHTTPAdapterException(AdapterException):
super().__init__("cqhttp") super().__init__("cqhttp")
class ApiError(CQHTTPAdapterException, ActionFailed): class ActionFailed(BaseActionFailed, CQHTTPAdapterException):
""" """
:说明: :说明:
@ -27,3 +31,29 @@ class ApiError(CQHTTPAdapterException, ActionFailed):
def __str__(self): def __str__(self):
return self.__repr__() 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"<NetWorkError message={self.msg}>"
def __str__(self):
return self.__repr__()
class ApiNotAvailable(BaseApiNotAvailable, CQHTTPAdapterException):
pass

View File

@ -3,6 +3,7 @@ import re
from nonebot.typing import overrides from nonebot.typing import overrides
from nonebot.typing import Any, Dict, Union, Tuple, Iterable, Optional from nonebot.typing import Any, Dict, Union, Tuple, Iterable, Optional
from nonebot.adapters import BaseMessage, BaseMessageSegment from nonebot.adapters import BaseMessage, BaseMessageSegment
from .utils import log, escape, unescape, _b2s from .utils import log, escape, unescape, _b2s

View File

@ -1,21 +1,9 @@
from nonebot.typing import NoReturn from nonebot.typing import Optional
from nonebot.typing import Union, Optional
from nonebot.exception import RequestDenied
from nonebot.utils import logger_wrapper from nonebot.utils import logger_wrapper
log = logger_wrapper("CQHTTP") 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: def escape(s: str, *, escape_comma: bool = True) -> str:
""" """
:说明: :说明:

View File

@ -6,8 +6,6 @@
这些异常并非所有需要用户处理 NoneBot 内部运行时被捕获并进行对应操作 这些异常并非所有需要用户处理 NoneBot 内部运行时被捕获并进行对应操作
""" """
from nonebot.typing import Optional
class NoneBotException(Exception): class NoneBotException(Exception):
""" """