mirror of
				https://github.com/nonebot/nonebot2.git
				synced 2025-10-31 15:06:42 +00:00 
			
		
		
		
	| @@ -198,6 +198,10 @@ module.exports = context => ({ | ||||
|                 { | ||||
|                   title: "nonebot.adapters.cqhttp 模块", | ||||
|                   path: "adapters/cqhttp" | ||||
|                 }, | ||||
|                 { | ||||
|                   title: "nonebot.adapters.ding 模块", | ||||
|                   path: "adapters/ding" | ||||
|                 } | ||||
|               ] | ||||
|             } | ||||
|   | ||||
| @@ -47,3 +47,6 @@ | ||||
|  | ||||
|  | ||||
|     * [nonebot.adapters.cqhttp](adapters/cqhttp.html) | ||||
|  | ||||
|  | ||||
|     * [nonebot.adapters.ding](adapters/ding.html) | ||||
|   | ||||
| @@ -176,7 +176,7 @@ await bot.send_msg(message="hello world") | ||||
|  | ||||
| ## _class_ `BaseEvent` | ||||
|  | ||||
| 基类:`abc.ABC` | ||||
| 基类:`abc.ABC`, `typing.Generic` | ||||
|  | ||||
| Event 基类。提供上报信息的关键信息,其余信息可从原始上报消息获取。 | ||||
|  | ||||
| @@ -187,7 +187,7 @@ Event 基类。提供上报信息的关键信息,其余信息可从原始上 | ||||
| * **参数** | ||||
|  | ||||
|      | ||||
|     * `raw_event: dict`: 原始上报消息 | ||||
|     * `raw_event: Union[dict, T]`: 原始上报消息 | ||||
|  | ||||
|  | ||||
|  | ||||
| @@ -309,7 +309,7 @@ Event 基类。提供上报信息的关键信息,其余信息可从原始上 | ||||
| * **参数** | ||||
|  | ||||
|      | ||||
|     * `message: Union[str, dict, list, MessageSegment, Message]`: 消息内容 | ||||
|     * `message: Union[str, dict, list, BaseModel, MessageSegment, Message]`: 消息内容 | ||||
|  | ||||
|  | ||||
|  | ||||
| @@ -350,7 +350,7 @@ Event 基类。提供上报信息的关键信息,其余信息可从原始上 | ||||
|  | ||||
| * **说明** | ||||
|  | ||||
|     缩减消息数组,即拼接相邻纯文本消息段 | ||||
|     缩减消息数组,即按 MessageSegment 的实现拼接相邻消息段 | ||||
|  | ||||
|  | ||||
|  | ||||
|   | ||||
| @@ -5,29 +5,6 @@ sidebarDepth: 0 | ||||
|  | ||||
| # NoneBot.adapters.cqhttp 模块 | ||||
|  | ||||
| ## CQHTTP (OneBot) v11 协议适配 | ||||
|  | ||||
| 协议详情请看: [CQHTTP](https://github.com/howmanybots/onebot/blob/master/README.md) | [OneBot](https://github.com/howmanybots/onebot/blob/master/README.md) | ||||
|  | ||||
|  | ||||
| ## `log(level, message)` | ||||
|  | ||||
|  | ||||
| * **说明** | ||||
|  | ||||
|     用于打印 CQHTTP 日志。 | ||||
|  | ||||
|  | ||||
|  | ||||
| * **参数** | ||||
|  | ||||
|      | ||||
|     * `level: str`: 日志等级 | ||||
|  | ||||
|  | ||||
|     * `message: str`: 日志信息 | ||||
|  | ||||
|  | ||||
|  | ||||
| ## `escape(s, *, escape_comma=True)` | ||||
|  | ||||
| @@ -64,9 +41,50 @@ sidebarDepth: 0 | ||||
|  | ||||
|  | ||||
|  | ||||
| ## `_b2s(b)` | ||||
| ## _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)` | ||||
|   | ||||
							
								
								
									
										380
									
								
								docs/api/adapters/ding.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										380
									
								
								docs/api/adapters/ding.md
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,380 @@ | ||||
| --- | ||||
| contentSidebar: true | ||||
| sidebarDepth: 0 | ||||
| --- | ||||
|  | ||||
| # NoneBot.adapters.ding 模块 | ||||
|  | ||||
|  | ||||
| ## _exception_ `DingAdapterException` | ||||
|  | ||||
| 基类:[`nonebot.exception.AdapterException`](../exception.md#nonebot.exception.AdapterException) | ||||
|  | ||||
|  | ||||
| * **说明** | ||||
|  | ||||
|     钉钉 Adapter 错误基类 | ||||
|  | ||||
|  | ||||
|  | ||||
| ## _exception_ `ActionFailed` | ||||
|  | ||||
| 基类:[`nonebot.exception.ActionFailed`](../exception.md#nonebot.exception.ActionFailed), `nonebot.adapters.ding.exception.DingAdapterException` | ||||
|  | ||||
|  | ||||
| * **说明** | ||||
|  | ||||
|     API 请求返回错误信息。 | ||||
|  | ||||
|  | ||||
|  | ||||
| * **参数** | ||||
|  | ||||
|      | ||||
|     * `errcode: Optional[int]`: 错误码 | ||||
|  | ||||
|  | ||||
|     * `errmsg: Optional[str]`: 错误信息 | ||||
|  | ||||
|  | ||||
|  | ||||
| ## _exception_ `ApiNotAvailable` | ||||
|  | ||||
| 基类:[`nonebot.exception.ApiNotAvailable`](../exception.md#nonebot.exception.ApiNotAvailable), `nonebot.adapters.ding.exception.DingAdapterException` | ||||
|  | ||||
|  | ||||
| ## _exception_ `NetworkError` | ||||
|  | ||||
| 基类:[`nonebot.exception.NetworkError`](../exception.md#nonebot.exception.NetworkError), `nonebot.adapters.ding.exception.DingAdapterException` | ||||
|  | ||||
|  | ||||
| * **说明** | ||||
|  | ||||
|     网络错误。 | ||||
|  | ||||
|  | ||||
|  | ||||
| * **参数** | ||||
|  | ||||
|      | ||||
|     * `retcode: Optional[int]`: 错误码 | ||||
|  | ||||
|  | ||||
|  | ||||
| ## _exception_ `SessionExpired` | ||||
|  | ||||
| 基类:[`nonebot.exception.ApiNotAvailable`](../exception.md#nonebot.exception.ApiNotAvailable), `nonebot.adapters.ding.exception.DingAdapterException` | ||||
|  | ||||
|  | ||||
| * **说明** | ||||
|  | ||||
|     发消息的 session 已经过期。 | ||||
|  | ||||
|  | ||||
|  | ||||
| ## _class_ `Bot` | ||||
|  | ||||
| 基类:[`nonebot.adapters.BaseBot`](README.md#nonebot.adapters.BaseBot) | ||||
|  | ||||
| 钉钉 协议 Bot 适配。继承属性参考 [BaseBot](./#class-basebot) 。 | ||||
|  | ||||
|  | ||||
| ### _property_ `type` | ||||
|  | ||||
|  | ||||
| * 返回: `"ding"` | ||||
|  | ||||
|  | ||||
| ### _async classmethod_ `check_permission(driver, connection_type, headers, body)` | ||||
|  | ||||
|  | ||||
| * **说明** | ||||
|  | ||||
|     钉钉协议鉴权。参考 [鉴权](https://ding-doc.dingtalk.com/doc#/serverapi2/elzz1p) | ||||
|  | ||||
|  | ||||
|  | ||||
| ### _async_ `handle_message(body)` | ||||
|  | ||||
|  | ||||
| * **说明** | ||||
|  | ||||
|     处理上报消息的函数,转换为 `Event` 事件后调用 `nonebot.message.handle_event` 进一步处理事件。 | ||||
|  | ||||
|  | ||||
|  | ||||
| * **参数** | ||||
|  | ||||
|      | ||||
|     * `message: dict`: 收到的上报消息 | ||||
|  | ||||
|  | ||||
|  | ||||
| ### _async_ `call_api(api, event=None, **data)` | ||||
|  | ||||
|  | ||||
| * **说明** | ||||
|  | ||||
|     调用 钉钉 协议 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) | ||||
|  | ||||
| 钉钉 协议 Event 适配。继承属性参考 [BaseEvent](./#class-baseevent) 。 | ||||
|  | ||||
|  | ||||
| ### _property_ `raw_event` | ||||
|  | ||||
| 原始上报消息 | ||||
|  | ||||
|  | ||||
| ### _property_ `id` | ||||
|  | ||||
|  | ||||
| * 类型: `Optional[str]` | ||||
|  | ||||
|  | ||||
| * 说明: 消息 ID | ||||
|  | ||||
|  | ||||
| ### _property_ `name` | ||||
|  | ||||
|  | ||||
| * 类型: `str` | ||||
|  | ||||
|  | ||||
| * 说明: 事件名称,由 type.\`detail_type\` 组合而成 | ||||
|  | ||||
|  | ||||
| ### _property_ `self_id` | ||||
|  | ||||
|  | ||||
| * 类型: `str` | ||||
|  | ||||
|  | ||||
| * 说明: 机器人自身 ID | ||||
|  | ||||
|  | ||||
| ### _property_ `time` | ||||
|  | ||||
|  | ||||
| * 类型: `int` | ||||
|  | ||||
|  | ||||
| * 说明: 消息的时间戳,单位 s | ||||
|  | ||||
|  | ||||
| ### _property_ `type` | ||||
|  | ||||
|  | ||||
| * 类型: `str` | ||||
|  | ||||
|  | ||||
| * 说明: 事件类型 | ||||
|  | ||||
|  | ||||
| ### _property_ `detail_type` | ||||
|  | ||||
|  | ||||
| * 类型: `str` | ||||
|  | ||||
|  | ||||
| * 说明: 事件详细类型 | ||||
|  | ||||
|  | ||||
| ### _property_ `sub_type` | ||||
|  | ||||
|  | ||||
| * 类型: `None` | ||||
|  | ||||
|  | ||||
| * 说明: 钉钉适配器无事件子类型 | ||||
|  | ||||
|  | ||||
| ### _property_ `user_id` | ||||
|  | ||||
|  | ||||
| * 类型: `Optional[str]` | ||||
|  | ||||
|  | ||||
| * 说明: 发送者 ID | ||||
|  | ||||
|  | ||||
| ### _property_ `group_id` | ||||
|  | ||||
|  | ||||
| * 类型: `Optional[str]` | ||||
|  | ||||
|  | ||||
| * 说明: 事件主体群 ID | ||||
|  | ||||
|  | ||||
| ### _property_ `to_me` | ||||
|  | ||||
|  | ||||
| * 类型: `Optional[bool]` | ||||
|  | ||||
|  | ||||
| * 说明: 消息是否与机器人相关 | ||||
|  | ||||
|  | ||||
| ### _property_ `message` | ||||
|  | ||||
|  | ||||
| * 类型: `Optional[Message]` | ||||
|  | ||||
|  | ||||
| * 说明: 消息内容 | ||||
|  | ||||
|  | ||||
| ### _property_ `reply` | ||||
|  | ||||
|  | ||||
| * 类型: `None` | ||||
|  | ||||
|  | ||||
| * 说明: 回复消息详情 | ||||
|  | ||||
|  | ||||
| ### _property_ `raw_message` | ||||
|  | ||||
|  | ||||
| * 类型: `Optional[str]` | ||||
|  | ||||
|  | ||||
| * 说明: 原始消息 | ||||
|  | ||||
|  | ||||
| ### _property_ `plain_text` | ||||
|  | ||||
|  | ||||
| * 类型: `Optional[str]` | ||||
|  | ||||
|  | ||||
| * 说明: 纯文本消息内容 | ||||
|  | ||||
|  | ||||
| ### _property_ `sender` | ||||
|  | ||||
|  | ||||
| * 类型: `Optional[dict]` | ||||
|  | ||||
|  | ||||
| * 说明: 消息发送者信息 | ||||
|  | ||||
|  | ||||
| ## _class_ `MessageSegment` | ||||
|  | ||||
| 基类:[`nonebot.adapters.BaseMessageSegment`](README.md#nonebot.adapters.BaseMessageSegment) | ||||
|  | ||||
| 钉钉 协议 MessageSegment 适配。具体方法参考协议消息段类型或源码。 | ||||
|  | ||||
|  | ||||
| ### _static_ `actionCardSingleMultiBtns(title, text, btns=[], hideAvatar=False, btnOrientation='1')` | ||||
|  | ||||
|  | ||||
| * **参数** | ||||
|  | ||||
|      | ||||
|     * `btnOrientation`: 0:按钮竖直排列 1:按钮横向排列 | ||||
|  | ||||
|  | ||||
|     * `btns`: [{ "title": title, "actionURL": actionURL }, ...] | ||||
|  | ||||
|  | ||||
|  | ||||
| ### _static_ `feedCard(links=[])` | ||||
|  | ||||
|  | ||||
| * **参数** | ||||
|  | ||||
|      | ||||
|     * `links`: [{ "title": xxx, "messageURL": xxx, "picURL": xxx }, ...] | ||||
|  | ||||
|  | ||||
|  | ||||
| ### _static_ `empty()` | ||||
|  | ||||
| 不想回复消息到群里 | ||||
|  | ||||
|  | ||||
| ## _class_ `Message` | ||||
|  | ||||
| 基类:[`nonebot.adapters.BaseMessage`](README.md#nonebot.adapters.BaseMessage) | ||||
|  | ||||
| 钉钉 协议 Message 适配。 | ||||
| @@ -11,11 +11,22 @@ sidebarDepth: 0 | ||||
| 这些异常并非所有需要用户处理,在 NoneBot 内部运行时被捕获,并进行对应操作。 | ||||
|  | ||||
|  | ||||
| ## _exception_ `IgnoredException` | ||||
| ## _exception_ `NoneBotException` | ||||
|  | ||||
| 基类:`Exception` | ||||
|  | ||||
|  | ||||
| * **说明** | ||||
|  | ||||
|     所有 NoneBot 发生的异常基类。 | ||||
|  | ||||
|  | ||||
|  | ||||
| ## _exception_ `IgnoredException` | ||||
|  | ||||
| 基类:`nonebot.exception.NoneBotException` | ||||
|  | ||||
|  | ||||
| * **说明** | ||||
|  | ||||
|     指示 NoneBot 应该忽略该事件。可由 PreProcessor 抛出。 | ||||
| @@ -31,7 +42,7 @@ sidebarDepth: 0 | ||||
|  | ||||
| ## _exception_ `PausedException` | ||||
|  | ||||
| 基类:`Exception` | ||||
| 基类:`nonebot.exception.NoneBotException` | ||||
|  | ||||
|  | ||||
| * **说明** | ||||
| @@ -49,7 +60,7 @@ sidebarDepth: 0 | ||||
|  | ||||
| ## _exception_ `RejectedException` | ||||
|  | ||||
| 基类:`Exception` | ||||
| 基类:`nonebot.exception.NoneBotException` | ||||
|  | ||||
|  | ||||
| * **说明** | ||||
| @@ -67,7 +78,7 @@ sidebarDepth: 0 | ||||
|  | ||||
| ## _exception_ `FinishedException` | ||||
|  | ||||
| 基类:`Exception` | ||||
| 基类:`nonebot.exception.NoneBotException` | ||||
|  | ||||
|  | ||||
| * **说明** | ||||
| @@ -85,7 +96,7 @@ sidebarDepth: 0 | ||||
|  | ||||
| ## _exception_ `StopPropagation` | ||||
|  | ||||
| 基类:`Exception` | ||||
| 基类:`nonebot.exception.NoneBotException` | ||||
|  | ||||
|  | ||||
| * **说明** | ||||
| @@ -102,7 +113,7 @@ sidebarDepth: 0 | ||||
|  | ||||
| ## _exception_ `RequestDenied` | ||||
|  | ||||
| 基类:`Exception` | ||||
| 基类:`nonebot.exception.NoneBotException` | ||||
|  | ||||
|  | ||||
| * **说明** | ||||
| @@ -121,9 +132,27 @@ sidebarDepth: 0 | ||||
|  | ||||
|  | ||||
|  | ||||
| ## _exception_ `AdapterException` | ||||
|  | ||||
| 基类:`nonebot.exception.NoneBotException` | ||||
|  | ||||
|  | ||||
| * **说明** | ||||
|  | ||||
|     代表 `Adapter` 抛出的异常,所有的 `Adapter` 都要在内部继承自这个 `Exception` | ||||
|  | ||||
|  | ||||
|  | ||||
| * **参数** | ||||
|  | ||||
|      | ||||
|     * `adapter_name: str`: 标识 adapter | ||||
|  | ||||
|  | ||||
|  | ||||
| ## _exception_ `ApiNotAvailable` | ||||
|  | ||||
| 基类:`Exception` | ||||
| 基类:`nonebot.exception.AdapterException` | ||||
|  | ||||
|  | ||||
| * **说明** | ||||
| @@ -134,7 +163,7 @@ sidebarDepth: 0 | ||||
|  | ||||
| ## _exception_ `NetworkError` | ||||
|  | ||||
| 基类:`Exception` | ||||
| 基类:`nonebot.exception.AdapterException` | ||||
|  | ||||
|  | ||||
| * **说明** | ||||
| @@ -145,16 +174,9 @@ sidebarDepth: 0 | ||||
|  | ||||
| ## _exception_ `ActionFailed` | ||||
|  | ||||
| 基类:`Exception` | ||||
| 基类:`nonebot.exception.AdapterException` | ||||
|  | ||||
|  | ||||
| * **说明** | ||||
|  | ||||
|     API 请求成功返回数据,但 API 操作失败。 | ||||
|  | ||||
|  | ||||
|  | ||||
| * **参数** | ||||
|  | ||||
|      | ||||
|     * `retcode: Optional[int]`: 错误代码 | ||||
|   | ||||
| @@ -54,8 +54,6 @@ sidebarDepth: 0 | ||||
|  | ||||
| ## _class_ `DataclassEncoder` | ||||
|  | ||||
| 基类:`json.encoder.JSONEncoder` | ||||
|  | ||||
|  | ||||
| * **说明** | ||||
|  | ||||
|   | ||||
| @@ -17,3 +17,4 @@ NoneBot Api Reference | ||||
|   - `nonebot.drivers.fastapi <drivers/fastapi.html>`_ | ||||
|   - `nonebot.adapters <adapters/>`_ | ||||
|   - `nonebot.adapters.cqhttp <adapters/cqhttp.html>`_ | ||||
|   - `nonebot.adapters.ding <adapters/ding.html>`_ | ||||
|   | ||||
| @@ -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: | ||||
|   | ||||
							
								
								
									
										29
									
								
								docs_build/adapters/ding.rst
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								docs_build/adapters/ding.rst
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,29 @@ | ||||
| --- | ||||
| contentSidebar: true | ||||
| sidebarDepth: 0 | ||||
| --- | ||||
|  | ||||
| NoneBot.adapters.ding 模块 | ||||
| ============================ | ||||
|  | ||||
| .. automodule:: nonebot.adapters.ding.exception | ||||
|    :members: | ||||
|    :show-inheritance: | ||||
|  | ||||
|  | ||||
| .. automodule:: nonebot.adapters.ding.bot | ||||
|    :members: | ||||
|    :private-members: | ||||
|    :show-inheritance: | ||||
|  | ||||
|  | ||||
| .. automodule:: nonebot.adapters.ding.event | ||||
|    :members: | ||||
|    :private-members: | ||||
|    :show-inheritance: | ||||
|  | ||||
|  | ||||
| .. automodule:: nonebot.adapters.ding.message | ||||
|    :members: | ||||
|    :private-members: | ||||
|    :show-inheritance: | ||||
| @@ -10,4 +10,5 @@ NoneBot.utils 模块 | ||||
| .. autofunction:: nonebot.utils.escape_tag | ||||
| .. autodecorator:: nonebot.utils.run_sync | ||||
| .. autoclass:: nonebot.utils.DataclassEncoder | ||||
| .. autodecorator:: nonebot.utils.logger_wrapper | ||||
|   :show-inheritance: | ||||
|   | ||||
| @@ -9,9 +9,11 @@ import abc | ||||
| from functools import reduce, partial | ||||
| from dataclasses import dataclass, field | ||||
|  | ||||
| from pydantic import BaseModel | ||||
|  | ||||
| from nonebot.config import Config | ||||
| from nonebot.typing import Driver, Message, WebSocket | ||||
| from nonebot.typing import Any, Dict, Union, Optional, NoReturn, Callable, Iterable, Awaitable | ||||
| from nonebot.typing import Any, Dict, Union, Optional, NoReturn, Callable, Iterable, Awaitable, TypeVar, Generic | ||||
|  | ||||
|  | ||||
| class BaseBot(abc.ABC): | ||||
| @@ -135,16 +137,19 @@ class BaseBot(abc.ABC): | ||||
|         raise NotImplementedError | ||||
|  | ||||
|  | ||||
| class BaseEvent(abc.ABC): | ||||
| T = TypeVar("T", bound=BaseModel) | ||||
|  | ||||
|  | ||||
| class BaseEvent(abc.ABC, Generic[T]): | ||||
|     """ | ||||
|     Event 基类。提供上报信息的关键信息,其余信息可从原始上报消息获取。 | ||||
|     """ | ||||
|  | ||||
|     def __init__(self, raw_event: dict): | ||||
|     def __init__(self, raw_event: Union[dict, T]): | ||||
|         """ | ||||
|         :参数: | ||||
|  | ||||
|           * ``raw_event: dict``: 原始上报消息 | ||||
|           * ``raw_event: Union[dict, T]``: 原始上报消息 | ||||
|         """ | ||||
|         self._raw_event = raw_event | ||||
|  | ||||
| @@ -152,7 +157,7 @@ class BaseEvent(abc.ABC): | ||||
|         return f"<Event {self.self_id}: {self.name} {self.time}>" | ||||
|  | ||||
|     @property | ||||
|     def raw_event(self) -> dict: | ||||
|     def raw_event(self) -> Union[dict, T]: | ||||
|         """原始上报消息""" | ||||
|         return self._raw_event | ||||
|  | ||||
| @@ -347,17 +352,17 @@ class BaseMessage(list, abc.ABC): | ||||
|     """消息数组""" | ||||
|  | ||||
|     def __init__(self, | ||||
|                  message: Union[str, dict, list, BaseMessageSegment, | ||||
|                  message: Union[str, dict, list, BaseModel, BaseMessageSegment, | ||||
|                                 "BaseMessage"] = None, | ||||
|                  *args, | ||||
|                  **kwargs): | ||||
|         """ | ||||
|         :参数: | ||||
|  | ||||
|           * ``message: Union[str, dict, list, MessageSegment, Message]``: 消息内容 | ||||
|           * ``message: Union[str, dict, list, BaseModel, MessageSegment, Message]``: 消息内容 | ||||
|         """ | ||||
|         super().__init__(*args, **kwargs) | ||||
|         if isinstance(message, (str, dict, list)): | ||||
|         if isinstance(message, (str, dict, list, BaseModel)): | ||||
|             self.extend(self._construct(message)) | ||||
|         elif isinstance(message, BaseMessage): | ||||
|             self.extend(message) | ||||
|   | ||||
| @@ -10,6 +10,8 @@ CQHTTP (OneBot) v11 协议适配 | ||||
|     https://github.com/howmanybots/onebot/blob/master/README.md | ||||
| """ | ||||
|  | ||||
| from .message import Message, MessageSegment | ||||
| from .bot import Bot | ||||
| from .event import Event | ||||
| from .message import Message, MessageSegment | ||||
| from .utils import log, escape, unescape, _b2s | ||||
| from .bot import Bot, _check_at_me, _check_nickname, _check_reply, _handle_api_result | ||||
| from .exception import CQHTTPAdapterException, ApiNotAvailable, ActionFailed, NetworkError | ||||
|   | ||||
| @@ -8,15 +8,26 @@ import httpx | ||||
|  | ||||
| from nonebot.log import logger | ||||
| from nonebot.config import Config | ||||
| from nonebot.message import handle_event | ||||
| from nonebot.typing import overrides, Driver, WebSocket, NoReturn | ||||
| from nonebot.typing import Any, Dict, Union, Optional | ||||
| from nonebot.adapters import BaseBot | ||||
| from nonebot.exception import NetworkError, ActionFailed, RequestDenied, ApiNotAvailable | ||||
| 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 .message import Message, MessageSegment | ||||
| from .utils import log, get_auth_bearer | ||||
| from .event import Event | ||||
| 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"): | ||||
| @@ -236,7 +247,7 @@ class Bot(BaseBot): | ||||
|         """ | ||||
|         x_self_id = headers.get("x-self-id") | ||||
|         x_signature = headers.get("x-signature") | ||||
|         access_token = get_auth_bearer(headers.get("authorization")) | ||||
|         token = get_auth_bearer(headers.get("authorization")) | ||||
|  | ||||
|         # 检查连接方式 | ||||
|         if connection_type not in ["http", "websocket"]: | ||||
| @@ -261,13 +272,13 @@ class Bot(BaseBot): | ||||
|                 raise RequestDenied(403, "Signature is invalid") | ||||
|  | ||||
|         access_token = driver.config.access_token | ||||
|         if access_token and access_token != access_token: | ||||
|         if access_token and access_token != token: | ||||
|             log( | ||||
|                 "WARNING", "Authorization Header is invalid" | ||||
|                 if access_token else "Missing Authorization Header") | ||||
|                 if token else "Missing Authorization Header") | ||||
|             raise RequestDenied( | ||||
|                 403, "Authorization Header is invalid" | ||||
|                 if access_token else "Missing Authorization Header") | ||||
|                 if token else "Missing Authorization Header") | ||||
|         return str(x_self_id) | ||||
|  | ||||
|     @overrides(BaseBot) | ||||
| @@ -368,8 +379,8 @@ class Bot(BaseBot): | ||||
|  | ||||
|     @overrides(BaseBot) | ||||
|     async def send(self, | ||||
|                    event: "Event", | ||||
|                    message: Union[str, "Message", "MessageSegment"], | ||||
|                    event: Event, | ||||
|                    message: Union[str, Message, MessageSegment], | ||||
|                    at_sender: bool = False, | ||||
|                    **kwargs) -> Union[Any, NoReturn]: | ||||
|         """ | ||||
|   | ||||
| @@ -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]: | ||||
|         ... | ||||
| @@ -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 | ||||
|  | ||||
|   | ||||
							
								
								
									
										59
									
								
								nonebot/adapters/cqhttp/exception.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										59
									
								
								nonebot/adapters/cqhttp/exception.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,59 @@ | ||||
| from nonebot.typing import Optional | ||||
| from nonebot.exception import (AdapterException, ActionFailed as | ||||
|                                BaseActionFailed, NetworkError as | ||||
|                                BaseNetworkError, ApiNotAvailable as | ||||
|                                BaseApiNotAvailable) | ||||
|  | ||||
|  | ||||
| class CQHTTPAdapterException(AdapterException): | ||||
|  | ||||
|     def __init__(self): | ||||
|         super().__init__("cqhttp") | ||||
|  | ||||
|  | ||||
| class ActionFailed(BaseActionFailed, CQHTTPAdapterException): | ||||
|     """ | ||||
|     :说明: | ||||
|  | ||||
|       API 请求返回错误信息。 | ||||
|  | ||||
|     :参数: | ||||
|  | ||||
|       * ``retcode: Optional[int]``: 错误码 | ||||
|     """ | ||||
|  | ||||
|     def __init__(self, retcode: Optional[int] = None): | ||||
|         super().__init__() | ||||
|         self.retcode = retcode | ||||
|  | ||||
|     def __repr__(self): | ||||
|         return f"<ActionFailed retcode={self.retcode}>" | ||||
|  | ||||
|     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"<NetWorkError message={self.msg}>" | ||||
|  | ||||
|     def __str__(self): | ||||
|         return self.__repr__() | ||||
|  | ||||
|  | ||||
| class ApiNotAvailable(BaseApiNotAvailable, CQHTTPAdapterException): | ||||
|     pass | ||||
| @@ -1,8 +1,8 @@ | ||||
| import re | ||||
|  | ||||
| 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, overrides | ||||
| from nonebot.adapters import BaseMessage, BaseMessageSegment | ||||
|  | ||||
| from .utils import log, escape, unescape, _b2s | ||||
|  | ||||
|  | ||||
|   | ||||
| @@ -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: | ||||
|     """ | ||||
|     :说明: | ||||
|   | ||||
							
								
								
									
										17
									
								
								nonebot/adapters/ding/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								nonebot/adapters/ding/__init__.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,17 @@ | ||||
| """ | ||||
| 钉钉群机器人 协议适配 | ||||
| ============================ | ||||
|  | ||||
| 协议详情请看: `钉钉文档`_ | ||||
|  | ||||
| .. _钉钉文档: | ||||
|     https://ding-doc.dingtalk.com/doc#/serverapi2/krgddi | ||||
|  | ||||
| """ | ||||
|  | ||||
| from .utils import log | ||||
| from .bot import Bot | ||||
| from .event import Event | ||||
| from .message import Message, MessageSegment | ||||
| from .exception import (DingAdapterException, ApiNotAvailable, NetworkError, | ||||
|                         ActionFailed, SessionExpired) | ||||
							
								
								
									
										197
									
								
								nonebot/adapters/ding/bot.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										197
									
								
								nonebot/adapters/ding/bot.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,197 @@ | ||||
| import hmac | ||||
| import base64 | ||||
| from datetime import datetime | ||||
|  | ||||
| import httpx | ||||
| 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, Union, Driver, Optional, NoReturn | ||||
|  | ||||
| from .utils import log | ||||
| from .event import Event | ||||
| from .model import MessageModel | ||||
| from .message import Message, MessageSegment | ||||
| from .exception import NetworkError, ApiNotAvailable, ActionFailed, SessionExpired | ||||
|  | ||||
|  | ||||
| class Bot(BaseBot): | ||||
|     """ | ||||
|     钉钉 协议 Bot 适配。继承属性参考 `BaseBot <./#class-basebot>`_ 。 | ||||
|     """ | ||||
|  | ||||
|     def __init__(self, driver: Driver, connection_type: str, config: Config, | ||||
|                  self_id: str, **kwargs): | ||||
|  | ||||
|         super().__init__(driver, connection_type, config, self_id, **kwargs) | ||||
|  | ||||
|     @property | ||||
|     def type(self) -> str: | ||||
|         """ | ||||
|         - 返回: ``"ding"`` | ||||
|         """ | ||||
|         return "ding" | ||||
|  | ||||
|     @classmethod | ||||
|     async def check_permission(cls, driver: Driver, connection_type: str, | ||||
|                                headers: dict, body: Optional[dict]) -> str: | ||||
|         """ | ||||
|         :说明: | ||||
|  | ||||
|           钉钉协议鉴权。参考 `鉴权 <https://ding-doc.dingtalk.com/doc#/serverapi2/elzz1p>`_ | ||||
|         """ | ||||
|         timestamp = headers.get("timestamp") | ||||
|         sign = headers.get("sign") | ||||
|  | ||||
|         # 检查连接方式 | ||||
|         if connection_type not in ["http"]: | ||||
|             raise RequestDenied(405, "Unsupported connection type") | ||||
|  | ||||
|         # 检查 timestamp | ||||
|         if not timestamp: | ||||
|             raise RequestDenied(400, "Missing `timestamp` Header") | ||||
|  | ||||
|         # 检查 sign | ||||
|         secret = driver.config.secret | ||||
|         if secret: | ||||
|             if not sign: | ||||
|                 log("WARNING", "Missing Signature Header") | ||||
|                 raise RequestDenied(400, "Missing `sign` Header") | ||||
|             string_to_sign = f"{timestamp}\n{secret}" | ||||
|             sig = hmac.new(secret.encode("utf-8"), | ||||
|                            string_to_sign.encode("utf-8"), "sha256").digest() | ||||
|             if sign != base64.b64encode(sig).decode("utf-8"): | ||||
|                 log("WARNING", "Signature Header is invalid") | ||||
|                 raise RequestDenied(403, "Signature is invalid") | ||||
|         else: | ||||
|             log("WARNING", "Ding signature check ignored!") | ||||
|         return body["chatbotUserId"] | ||||
|  | ||||
|     async def handle_message(self, body: dict): | ||||
|         message = MessageModel.parse_obj(body) | ||||
|         if not message: | ||||
|             return | ||||
|  | ||||
|         try: | ||||
|             event = Event(message) | ||||
|             await handle_event(self, event) | ||||
|         except Exception as e: | ||||
|             logger.opt(colors=True, exception=e).error( | ||||
|                 f"<r><bg #f8bbd0>Failed to handle event. Raw: {message}</bg #f8bbd0></r>" | ||||
|             ) | ||||
|         return | ||||
|  | ||||
|     async def call_api(self, | ||||
|                        api: str, | ||||
|                        event: Optional[Event] = None, | ||||
|                        **data) -> Union[Any, NoReturn]: | ||||
|         """ | ||||
|         :说明: | ||||
|  | ||||
|           调用 钉钉 协议 API | ||||
|  | ||||
|         :参数: | ||||
|  | ||||
|           * ``api: str``: API 名称 | ||||
|           * ``**data: Any``: API 参数 | ||||
|  | ||||
|         :返回: | ||||
|  | ||||
|           - ``Any``: API 调用返回数据 | ||||
|  | ||||
|         :异常: | ||||
|  | ||||
|           - ``NetworkError``: 网络错误 | ||||
|           - ``ActionFailed``: API 调用失败 | ||||
|         """ | ||||
|         if self.connection_type != "http": | ||||
|             log("ERROR", "Only support http connection.") | ||||
|             return | ||||
|         if "self_id" in data: | ||||
|             self_id = data.pop("self_id") | ||||
|             if self_id: | ||||
|                 bot = self.driver.bots[str(self_id)] | ||||
|                 return await bot.call_api(api, **data) | ||||
|  | ||||
|         log("DEBUG", f"Calling API <y>{api}</y>") | ||||
|  | ||||
|         if api == "send_message": | ||||
|             if event: | ||||
|                 # 确保 sessionWebhook 没有过期 | ||||
|                 if int(datetime.now().timestamp()) > int( | ||||
|                         event.raw_event.sessionWebhookExpiredTime / 1000): | ||||
|                     raise SessionExpired | ||||
|  | ||||
|                 target = event.raw_event.sessionWebhook | ||||
|             else: | ||||
|                 target = None | ||||
|  | ||||
|             if not target: | ||||
|                 raise ApiNotAvailable | ||||
|  | ||||
|             headers = {} | ||||
|             segment: MessageSegment = data["message"][0] | ||||
|             try: | ||||
|                 async with httpx.AsyncClient(headers=headers) as client: | ||||
|                     response = await client.post( | ||||
|                         target, | ||||
|                         params={"access_token": self.config.access_token}, | ||||
|                         json=segment.data, | ||||
|                         timeout=self.config.api_timeout) | ||||
|  | ||||
|                 if 200 <= response.status_code < 300: | ||||
|                     result = response.json() | ||||
|                     if isinstance(result, dict): | ||||
|                         if result.get("errcode") != 0: | ||||
|                             raise ActionFailed(errcode=result.get("errcode"), | ||||
|                                                errmsg=result.get("errmsg")) | ||||
|                         return result | ||||
|                 raise NetworkError(f"HTTP request received unexpected " | ||||
|                                    f"status code: {response.status_code}") | ||||
|             except httpx.InvalidURL: | ||||
|                 raise NetworkError("API root url invalid") | ||||
|             except httpx.HTTPError: | ||||
|                 raise NetworkError("HTTP request failed") | ||||
|  | ||||
|     async def send(self, | ||||
|                    event: Event, | ||||
|                    message: Union[str, "Message", "MessageSegment"], | ||||
|                    at_sender: bool = False, | ||||
|                    **kwargs) -> Union[Any, NoReturn]: | ||||
|         """ | ||||
|         :说明: | ||||
|  | ||||
|           根据 ``event``  向触发事件的主体发送消息。 | ||||
|  | ||||
|         :参数: | ||||
|  | ||||
|           * ``event: Event``: Event 对象 | ||||
|           * ``message: Union[str, Message, MessageSegment]``: 要发送的消息 | ||||
|           * ``at_sender: bool``: 是否 @ 事件主体 | ||||
|           * ``**kwargs``: 覆盖默认参数 | ||||
|  | ||||
|         :返回: | ||||
|  | ||||
|           - ``Any``: API 调用返回数据 | ||||
|  | ||||
|         :异常: | ||||
|  | ||||
|           - ``ValueError``: 缺少 ``user_id``, ``group_id`` | ||||
|           - ``NetworkError``: 网络错误 | ||||
|           - ``ActionFailed``: API 调用失败 | ||||
|         """ | ||||
|         msg = message if isinstance(message, Message) else Message(message) | ||||
|  | ||||
|         at_sender = at_sender and bool(event.user_id) | ||||
|         params = {} | ||||
|         params["event"] = event | ||||
|         params.update(kwargs) | ||||
|  | ||||
|         if at_sender and event.detail_type != "private": | ||||
|             params["message"] = f"@{event.user_id} " + msg | ||||
|         else: | ||||
|             params["message"] = msg | ||||
|  | ||||
|         return await self.call_api("send_message", **params) | ||||
							
								
								
									
										196
									
								
								nonebot/adapters/ding/event.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										196
									
								
								nonebot/adapters/ding/event.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,196 @@ | ||||
| from nonebot.adapters import BaseEvent | ||||
| from nonebot.typing import Union, Optional | ||||
|  | ||||
| from .message import Message | ||||
| from .model import MessageModel, ConversationType, TextMessage | ||||
|  | ||||
|  | ||||
| class Event(BaseEvent): | ||||
|     """ | ||||
|     钉钉 协议 Event 适配。继承属性参考 `BaseEvent <./#class-baseevent>`_ 。 | ||||
|     """ | ||||
|  | ||||
|     def __init__(self, message: MessageModel): | ||||
|         super().__init__(message) | ||||
|         # 其实目前钉钉机器人只能接收到 text 类型的消息 | ||||
|         self._message = Message(getattr(message, message.msgtype or "text")) | ||||
|  | ||||
|     @property | ||||
|     def raw_event(self) -> MessageModel: | ||||
|         """原始上报消息""" | ||||
|         return self._raw_event | ||||
|  | ||||
|     @property | ||||
|     def id(self) -> Optional[str]: | ||||
|         """ | ||||
|         - 类型: ``Optional[str]`` | ||||
|         - 说明: 消息 ID | ||||
|         """ | ||||
|         return self.raw_event.msgId | ||||
|  | ||||
|     @property | ||||
|     def name(self) -> str: | ||||
|         """ | ||||
|         - 类型: ``str`` | ||||
|         - 说明: 事件名称,由 `type`.`detail_type` 组合而成 | ||||
|         """ | ||||
|         return self.type + "." + self.detail_type | ||||
|  | ||||
|     @property | ||||
|     def self_id(self) -> str: | ||||
|         """ | ||||
|         - 类型: ``str`` | ||||
|         - 说明: 机器人自身 ID | ||||
|         """ | ||||
|         return str(self.raw_event.chatbotUserId) | ||||
|  | ||||
|     @property | ||||
|     def time(self) -> int: | ||||
|         """ | ||||
|         - 类型: ``int`` | ||||
|         - 说明: 消息的时间戳,单位 s | ||||
|         """ | ||||
|         # 单位 ms -> s | ||||
|         return int(self.raw_event.createAt / 1000) | ||||
|  | ||||
|     @property | ||||
|     def type(self) -> str: | ||||
|         """ | ||||
|         - 类型: ``str`` | ||||
|         - 说明: 事件类型 | ||||
|         """ | ||||
|         return "message" | ||||
|  | ||||
|     @type.setter | ||||
|     def type(self, value) -> None: | ||||
|         pass | ||||
|  | ||||
|     @property | ||||
|     def detail_type(self) -> str: | ||||
|         """ | ||||
|         - 类型: ``str`` | ||||
|         - 说明: 事件详细类型 | ||||
|         """ | ||||
|         return self.raw_event.conversationType.name | ||||
|  | ||||
|     @detail_type.setter | ||||
|     def detail_type(self, value) -> None: | ||||
|         if value == "private": | ||||
|             self.raw_event.conversationType = ConversationType.private | ||||
|         if value == "group": | ||||
|             self.raw_event.conversationType = ConversationType.group | ||||
|  | ||||
|     @property | ||||
|     def sub_type(self) -> None: | ||||
|         """ | ||||
|         - 类型: ``None`` | ||||
|         - 说明: 钉钉适配器无事件子类型 | ||||
|         """ | ||||
|         return None | ||||
|  | ||||
|     @sub_type.setter | ||||
|     def sub_type(self, value) -> None: | ||||
|         pass | ||||
|  | ||||
|     @property | ||||
|     def user_id(self) -> Optional[str]: | ||||
|         """ | ||||
|         - 类型: ``Optional[str]`` | ||||
|         - 说明: 发送者 ID | ||||
|         """ | ||||
|         return self.raw_event.senderId | ||||
|  | ||||
|     @user_id.setter | ||||
|     def user_id(self, value) -> None: | ||||
|         self.raw_event.senderId = value | ||||
|  | ||||
|     @property | ||||
|     def group_id(self) -> Optional[str]: | ||||
|         """ | ||||
|         - 类型: ``Optional[str]`` | ||||
|         - 说明: 事件主体群 ID | ||||
|         """ | ||||
|         return self.raw_event.conversationId | ||||
|  | ||||
|     @group_id.setter | ||||
|     def group_id(self, value) -> None: | ||||
|         self.raw_event.conversationId = value | ||||
|  | ||||
|     @property | ||||
|     def to_me(self) -> Optional[bool]: | ||||
|         """ | ||||
|         - 类型: ``Optional[bool]`` | ||||
|         - 说明: 消息是否与机器人相关 | ||||
|         """ | ||||
|         return self.detail_type == "private" or self.raw_event.isInAtList | ||||
|  | ||||
|     @property | ||||
|     def message(self) -> Optional["Message"]: | ||||
|         """ | ||||
|         - 类型: ``Optional[Message]`` | ||||
|         - 说明: 消息内容 | ||||
|         """ | ||||
|         return self._message | ||||
|  | ||||
|     @message.setter | ||||
|     def message(self, value) -> None: | ||||
|         self._message = value | ||||
|  | ||||
|     @property | ||||
|     def reply(self) -> None: | ||||
|         """ | ||||
|         - 类型: ``None`` | ||||
|         - 说明: 回复消息详情 | ||||
|         """ | ||||
|         raise ValueError("暂不支持 reply") | ||||
|  | ||||
|     @property | ||||
|     def raw_message(self) -> Optional[Union[TextMessage]]: | ||||
|         """ | ||||
|         - 类型: ``Optional[str]`` | ||||
|         - 说明: 原始消息 | ||||
|         """ | ||||
|         return getattr(self.raw_event, self.raw_event.msgtype) | ||||
|  | ||||
|     @raw_message.setter | ||||
|     def raw_message(self, value) -> None: | ||||
|         setattr(self.raw_event, self.raw_event.msgtype, value) | ||||
|  | ||||
|     @property | ||||
|     def plain_text(self) -> Optional[str]: | ||||
|         """ | ||||
|         - 类型: ``Optional[str]`` | ||||
|         - 说明: 纯文本消息内容 | ||||
|         """ | ||||
|         return self.message and self.message.extract_plain_text().strip() | ||||
|  | ||||
|     @property | ||||
|     def sender(self) -> Optional[dict]: | ||||
|         """ | ||||
|         - 类型: ``Optional[dict]`` | ||||
|         - 说明: 消息发送者信息 | ||||
|         """ | ||||
|         result = { | ||||
|             # 加密的发送者ID。 | ||||
|             "senderId": self.raw_event.senderId, | ||||
|             # 发送者昵称。 | ||||
|             "senderNick": self.raw_event.senderNick, | ||||
|             # 企业内部群有的发送者当前群的企业 corpId。 | ||||
|             "senderCorpId": self.raw_event.senderCorpId, | ||||
|             # 企业内部群有的发送者在企业内的 userId。 | ||||
|             "senderStaffId": self.raw_event.senderStaffId, | ||||
|             "role": "admin" if self.raw_event.isAdmin else "member" | ||||
|         } | ||||
|         return result | ||||
|  | ||||
|     @sender.setter | ||||
|     def sender(self, value) -> None: | ||||
|  | ||||
|         def set_wrapper(name): | ||||
|             if value.get(name): | ||||
|                 setattr(self.raw_event, name, value.get(name)) | ||||
|  | ||||
|         set_wrapper("senderId") | ||||
|         set_wrapper("senderNick") | ||||
|         set_wrapper("senderCorpId") | ||||
|         set_wrapper("senderStaffId") | ||||
							
								
								
									
										76
									
								
								nonebot/adapters/ding/exception.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										76
									
								
								nonebot/adapters/ding/exception.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,76 @@ | ||||
| from nonebot.typing import Optional | ||||
| from nonebot.exception import (AdapterException, ActionFailed as | ||||
|                                BaseActionFailed, ApiNotAvailable as | ||||
|                                BaseApiNotAvailable, NetworkError as | ||||
|                                BaseNetworkError) | ||||
|  | ||||
|  | ||||
| class DingAdapterException(AdapterException): | ||||
|     """ | ||||
|     :说明: | ||||
|  | ||||
|       钉钉 Adapter 错误基类 | ||||
|     """ | ||||
|  | ||||
|     def __init__(self) -> None: | ||||
|         super().__init__("ding") | ||||
|  | ||||
|  | ||||
| class ActionFailed(BaseActionFailed, DingAdapterException): | ||||
|     """ | ||||
|     :说明: | ||||
|  | ||||
|       API 请求返回错误信息。 | ||||
|  | ||||
|     :参数: | ||||
|  | ||||
|       * ``errcode: Optional[int]``: 错误码 | ||||
|       * ``errmsg: Optional[str]``: 错误信息 | ||||
|     """ | ||||
|  | ||||
|     def __init__(self, | ||||
|                  errcode: Optional[int] = None, | ||||
|                  errmsg: Optional[str] = None): | ||||
|         super().__init__() | ||||
|         self.errcode = errcode | ||||
|         self.errmsg = errmsg | ||||
|  | ||||
|     def __repr__(self): | ||||
|         return f"<ApiError errcode={self.errcode} errmsg={self.errmsg}>" | ||||
|  | ||||
|  | ||||
| class ApiNotAvailable(BaseApiNotAvailable, DingAdapterException): | ||||
|     pass | ||||
|  | ||||
|  | ||||
| class NetworkError(BaseNetworkError, DingAdapterException): | ||||
|     """ | ||||
|     :说明: | ||||
|  | ||||
|       网络错误。 | ||||
|  | ||||
|     :参数: | ||||
|  | ||||
|       * ``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 SessionExpired(BaseApiNotAvailable, DingAdapterException): | ||||
|     """ | ||||
|     :说明: | ||||
|  | ||||
|       发消息的 session 已经过期。 | ||||
|     """ | ||||
|  | ||||
|     def __repr__(self) -> str: | ||||
|         return f"<Session Webhook is Expired>" | ||||
							
								
								
									
										133
									
								
								nonebot/adapters/ding/message.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										133
									
								
								nonebot/adapters/ding/message.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,133 @@ | ||||
| from nonebot.typing import Any, Dict, Union, Iterable | ||||
| from nonebot.adapters import BaseMessage, BaseMessageSegment | ||||
| from .utils import log | ||||
| from .model import TextMessage | ||||
|  | ||||
|  | ||||
| class MessageSegment(BaseMessageSegment): | ||||
|     """ | ||||
|     钉钉 协议 MessageSegment 适配。具体方法参考协议消息段类型或源码。 | ||||
|     """ | ||||
|  | ||||
|     def __init__(self, type_: str, msg: Dict[str, Any]) -> None: | ||||
|         data = { | ||||
|             "msgtype": type_, | ||||
|         } | ||||
|         if msg: | ||||
|             data.update(msg) | ||||
|         log("DEBUG", f"data {data}") | ||||
|         super().__init__(type=type_, data=data) | ||||
|  | ||||
|     @classmethod | ||||
|     def from_segment(cls, segment: "MessageSegment"): | ||||
|         return MessageSegment(segment.type, segment.data) | ||||
|  | ||||
|     def __str__(self): | ||||
|         log("DEBUG", f"__str__: self.type {self.type} data {self.data}") | ||||
|         if self.type == "text": | ||||
|             return str(self.data["text"]["content"].strip()) | ||||
|         return "" | ||||
|  | ||||
|     def __add__(self, other) -> "Message": | ||||
|         if isinstance(other, str): | ||||
|             if self.type == 'text': | ||||
|                 self.data['text']['content'] += other | ||||
|                 return MessageSegment.from_segment(self) | ||||
|         return Message(self) + other | ||||
|  | ||||
|     def atMobile(self, mobileNumber): | ||||
|         self.data.setdefault("at", {}) | ||||
|         self.data["at"].setdefault("atMobiles", []) | ||||
|         self.data["at"]["atMobiles"].append(mobileNumber) | ||||
|  | ||||
|     def atAll(self, value): | ||||
|         self.data.setdefault("at", {}) | ||||
|         self.data["at"]["isAtAll"] = value | ||||
|  | ||||
|     @staticmethod | ||||
|     def text(text_: str) -> "MessageSegment": | ||||
|         return MessageSegment("text", {"text": {"content": text_.strip()}}) | ||||
|  | ||||
|     @staticmethod | ||||
|     def markdown(title: str, text: str) -> "MessageSegment": | ||||
|         return MessageSegment("markdown", { | ||||
|             "markdown": { | ||||
|                 "title": title, | ||||
|                 "text": text, | ||||
|             }, | ||||
|         }) | ||||
|  | ||||
|     @staticmethod | ||||
|     def actionCardSingleBtn(title: str, text: str, btnTitle: str, | ||||
|                             btnUrl) -> "MessageSegment": | ||||
|         return MessageSegment( | ||||
|             "actionCard", { | ||||
|                 "actionCard": { | ||||
|                     "title": title, | ||||
|                     "text": text, | ||||
|                     "singleTitle": btnTitle, | ||||
|                     "singleURL": btnUrl | ||||
|                 } | ||||
|             }) | ||||
|  | ||||
|     @staticmethod | ||||
|     def actionCardSingleMultiBtns( | ||||
|         title: str, | ||||
|         text: str, | ||||
|         btns: list = [], | ||||
|         hideAvatar: bool = False, | ||||
|         btnOrientation: str = '1', | ||||
|     ) -> "MessageSegment": | ||||
|         """ | ||||
|         :参数: | ||||
|  | ||||
|             * ``btnOrientation``: 0:按钮竖直排列 1:按钮横向排列 | ||||
|  | ||||
|             * ``btns``: [{ "title": title, "actionURL": actionURL }, ...] | ||||
|         """ | ||||
|         return MessageSegment( | ||||
|             "actionCard", { | ||||
|                 "actionCard": { | ||||
|                     "title": title, | ||||
|                     "text": text, | ||||
|                     "hideAvatar": "1" if hideAvatar else "0", | ||||
|                     "btnOrientation": btnOrientation, | ||||
|                     "btns": btns | ||||
|                 } | ||||
|             }) | ||||
|  | ||||
|     @staticmethod | ||||
|     def feedCard(links: list = [],) -> "MessageSegment": | ||||
|         """ | ||||
|         :参数: | ||||
|  | ||||
|             * ``links``: [{ "title": xxx, "messageURL": xxx, "picURL": xxx }, ...] | ||||
|         """ | ||||
|         return MessageSegment("feedCard", {"feedCard": {"links": links}}) | ||||
|  | ||||
|     @staticmethod | ||||
|     def empty() -> "MessageSegment": | ||||
|         """不想回复消息到群里""" | ||||
|         return MessageSegment("empty") | ||||
|  | ||||
|  | ||||
| class Message(BaseMessage): | ||||
|     """ | ||||
|     钉钉 协议 Message 适配。 | ||||
|     """ | ||||
|  | ||||
|     @staticmethod | ||||
|     def _construct( | ||||
|             msg: Union[str, dict, list, | ||||
|                        TextMessage]) -> Iterable[MessageSegment]: | ||||
|         if isinstance(msg, dict): | ||||
|             yield MessageSegment(msg["type"], msg.get("data") or {}) | ||||
|             return | ||||
|         elif isinstance(msg, list): | ||||
|             for seg in msg: | ||||
|                 yield MessageSegment(seg["type"], seg.get("data") or {}) | ||||
|             return | ||||
|         elif isinstance(msg, TextMessage): | ||||
|             yield MessageSegment("text", {"text": msg.dict()}) | ||||
|         elif isinstance(msg, str): | ||||
|             yield MessageSegment.text(msg) | ||||
							
								
								
									
										47
									
								
								nonebot/adapters/ding/model.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								nonebot/adapters/ding/model.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,47 @@ | ||||
| from typing import List, Optional | ||||
| from enum import Enum | ||||
| from pydantic import BaseModel | ||||
|  | ||||
|  | ||||
| class Headers(BaseModel): | ||||
|     sign: str | ||||
|     token: str | ||||
|     # ms | ||||
|     timestamp: int | ||||
|  | ||||
|  | ||||
| class TextMessage(BaseModel): | ||||
|     content: str | ||||
|  | ||||
|  | ||||
| class AtUsersItem(BaseModel): | ||||
|     dingtalkId: str | ||||
|     staffId: Optional[str] | ||||
|  | ||||
|  | ||||
| class ConversationType(str, Enum): | ||||
|     private = '1' | ||||
|     group = '2' | ||||
|  | ||||
|  | ||||
| class MessageModel(BaseModel): | ||||
|     msgtype: str = None | ||||
|     text: Optional[TextMessage] = None | ||||
|     msgId: str | ||||
|     # ms | ||||
|     createAt: int = None | ||||
|     conversationType: ConversationType = None | ||||
|     conversationId: str = None | ||||
|     conversationTitle: str = None | ||||
|     senderId: str = None | ||||
|     senderNick: str = None | ||||
|     senderCorpId: str = None | ||||
|     senderStaffId: str = None | ||||
|     chatbotUserId: str = None | ||||
|     chatbotCorpId: str = None | ||||
|     atUsers: List[AtUsersItem] = None | ||||
|     sessionWebhook: str = None | ||||
|     # ms | ||||
|     sessionWebhookExpiredTime: int = None | ||||
|     isAdmin: bool = None | ||||
|     isInAtList: bool = None | ||||
							
								
								
									
										3
									
								
								nonebot/adapters/ding/utils.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								nonebot/adapters/ding/utils.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,3 @@ | ||||
| from nonebot.utils import logger_wrapper | ||||
|  | ||||
| log = logger_wrapper("DING") | ||||
| @@ -6,10 +6,17 @@ | ||||
| 这些异常并非所有需要用户处理,在 NoneBot 内部运行时被捕获,并进行对应操作。 | ||||
| """ | ||||
|  | ||||
| from nonebot.typing import List, Type, Optional | ||||
|  | ||||
| class NoneBotException(Exception): | ||||
|     """ | ||||
|     :说明: | ||||
|  | ||||
|       所有 NoneBot 发生的异常基类。 | ||||
|     """ | ||||
|     pass | ||||
|  | ||||
|  | ||||
| class IgnoredException(Exception): | ||||
| class IgnoredException(NoneBotException): | ||||
|     """ | ||||
|     :说明: | ||||
|  | ||||
| @@ -30,7 +37,7 @@ class IgnoredException(Exception): | ||||
|         return self.__repr__() | ||||
|  | ||||
|  | ||||
| class PausedException(Exception): | ||||
| class PausedException(NoneBotException): | ||||
|     """ | ||||
|     :说明: | ||||
|  | ||||
| @@ -44,7 +51,7 @@ class PausedException(Exception): | ||||
|     pass | ||||
|  | ||||
|  | ||||
| class RejectedException(Exception): | ||||
| class RejectedException(NoneBotException): | ||||
|     """ | ||||
|     :说明: | ||||
|  | ||||
| @@ -58,7 +65,7 @@ class RejectedException(Exception): | ||||
|     pass | ||||
|  | ||||
|  | ||||
| class FinishedException(Exception): | ||||
| class FinishedException(NoneBotException): | ||||
|     """ | ||||
|     :说明: | ||||
|  | ||||
| @@ -72,7 +79,7 @@ class FinishedException(Exception): | ||||
|     pass | ||||
|  | ||||
|  | ||||
| class StopPropagation(Exception): | ||||
| class StopPropagation(NoneBotException): | ||||
|     """ | ||||
|     :说明: | ||||
|  | ||||
| @@ -85,7 +92,7 @@ class StopPropagation(Exception): | ||||
|     pass | ||||
|  | ||||
|  | ||||
| class RequestDenied(Exception): | ||||
| class RequestDenied(NoneBotException): | ||||
|     """ | ||||
|     :说明: | ||||
|  | ||||
| @@ -108,7 +115,22 @@ class RequestDenied(Exception): | ||||
|         return self.__repr__() | ||||
|  | ||||
|  | ||||
| class ApiNotAvailable(Exception): | ||||
| class AdapterException(NoneBotException): | ||||
|     """ | ||||
|     :说明: | ||||
|  | ||||
|       代表 ``Adapter`` 抛出的异常,所有的 ``Adapter`` 都要在内部继承自这个 ``Exception`` | ||||
|  | ||||
|     :参数: | ||||
|  | ||||
|       * ``adapter_name: str``: 标识 adapter | ||||
|     """ | ||||
|  | ||||
|     def __init__(self, adapter_name: str) -> None: | ||||
|         self.adapter_name = adapter_name | ||||
|  | ||||
|  | ||||
| class ApiNotAvailable(AdapterException): | ||||
|     """ | ||||
|     :说明: | ||||
|  | ||||
| @@ -117,7 +139,7 @@ class ApiNotAvailable(Exception): | ||||
|     pass | ||||
|  | ||||
|  | ||||
| class NetworkError(Exception): | ||||
| class NetworkError(AdapterException): | ||||
|     """ | ||||
|     :说明: | ||||
|  | ||||
| @@ -126,22 +148,10 @@ class NetworkError(Exception): | ||||
|     pass | ||||
|  | ||||
|  | ||||
| class ActionFailed(Exception): | ||||
| class ActionFailed(AdapterException): | ||||
|     """ | ||||
|     :说明: | ||||
|  | ||||
|       API 请求成功返回数据,但 API 操作失败。 | ||||
|  | ||||
|     :参数: | ||||
|  | ||||
|       * ``retcode: Optional[int]``: 错误代码 | ||||
|     """ | ||||
|  | ||||
|     def __init__(self, retcode: Optional[int]): | ||||
|         self.retcode = retcode | ||||
|  | ||||
|     def __repr__(self): | ||||
|         return f"<ActionFailed, retcode={self.retcode}>" | ||||
|  | ||||
|     def __str__(self): | ||||
|         return self.__repr__() | ||||
|     pass | ||||
|   | ||||
| @@ -21,7 +21,7 @@ | ||||
| from types import ModuleType | ||||
| from typing import NoReturn, TYPE_CHECKING | ||||
| from typing import Any, Set, List, Dict, Type, Tuple, Mapping | ||||
| from typing import Union, TypeVar, Optional, Iterable, Callable, Awaitable | ||||
| from typing import Union, TypeVar, Optional, Iterable, Callable, Awaitable, Generic | ||||
|  | ||||
| # import some modules needed when checking types | ||||
| if TYPE_CHECKING: | ||||
|   | ||||
| @@ -5,7 +5,7 @@ import dataclasses | ||||
| from functools import wraps, partial | ||||
|  | ||||
| from nonebot.log import logger | ||||
| from nonebot.typing import Any, Callable, Awaitable, overrides | ||||
| from nonebot.typing import Any, Optional, Callable, Awaitable, overrides | ||||
|  | ||||
|  | ||||
| def escape_tag(s: str) -> str: | ||||
| @@ -65,19 +65,20 @@ class DataclassEncoder(json.JSONEncoder): | ||||
|  | ||||
|  | ||||
| def logger_wrapper(logger_name: str): | ||||
|  | ||||
|     def log(level: str, message: str): | ||||
|     """ | ||||
|     :说明: | ||||
|  | ||||
|     用于打印 adapter 的日志。 | ||||
|  | ||||
|         :参数: | ||||
|     :log 参数: | ||||
|  | ||||
|     * ``level: Literal['WARNING', 'DEBUG', 'INFO']``: 日志等级 | ||||
|     * ``message: str``: 日志信息 | ||||
|     * ``exception: Optional[Exception]``: 异常信息 | ||||
|     """ | ||||
|         return logger.opt(colors=True).log(level, | ||||
|                                            f"<m>{logger_name}</m> | " + message) | ||||
|  | ||||
|     def log(level: str, message: str, exception: Optional[Exception] = None): | ||||
|         return logger.opt(colors=True, exception=exception).log( | ||||
|             level, f"<m>{logger_name}</m> | " + message) | ||||
|  | ||||
|     return log | ||||
|   | ||||
| @@ -5,6 +5,7 @@ sys.path.insert(0, os.path.abspath("..")) | ||||
|  | ||||
| import nonebot | ||||
| from nonebot.adapters.cqhttp import Bot | ||||
| from nonebot.adapters.ding import Bot as DingBot | ||||
| from nonebot.log import logger, default_format | ||||
|  | ||||
| # test custom log | ||||
| @@ -18,6 +19,7 @@ nonebot.init(custom_config2="config on init") | ||||
| app = nonebot.get_asgi() | ||||
| driver = nonebot.get_driver() | ||||
| driver.register_adapter("cqhttp", Bot) | ||||
| driver.register_adapter("ding", DingBot) | ||||
|  | ||||
| # load builtin plugin | ||||
| nonebot.load_builtin_plugins() | ||||
|   | ||||
| @@ -2,6 +2,7 @@ from nonebot.rule import to_me | ||||
| from nonebot.typing import Event | ||||
| from nonebot.plugin import on_startswith | ||||
| from nonebot.adapters.cqhttp import Bot | ||||
| from nonebot.adapters.ding import Bot as DingBot, Event as DingEvent | ||||
| from nonebot.permission import GROUP_ADMIN | ||||
|  | ||||
| test_command = on_startswith("hello", to_me(), permission=GROUP_ADMIN) | ||||
| @@ -9,4 +10,9 @@ test_command = on_startswith("hello", to_me(), permission=GROUP_ADMIN) | ||||
|  | ||||
| @test_command.handle() | ||||
| async def test_handler(bot: Bot, event: Event, state: dict): | ||||
|     await test_command.finish("hello") | ||||
|     await test_command.finish("cqhttp hello") | ||||
|  | ||||
|  | ||||
| @test_command.handle() | ||||
| async def test_handler(bot: DingBot, event: DingEvent, state: dict): | ||||
|     await test_command.finish("ding hello") | ||||
|   | ||||
		Reference in New Issue
	
	Block a user