From 46ace83106829348be7172e9e76f6e6b58f782a0 Mon Sep 17 00:00:00 2001 From: StarHeartHunt Date: Thu, 1 Jul 2021 07:59:50 +0800 Subject: [PATCH 01/64] =?UTF-8?q?=F0=9F=8E=89=20init=20feishu=20adapter?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/nonebot-adapter-feishu/README.md | 11 + .../nonebot/__init__.py | 0 .../nonebot/adapters/__init__.py | 0 .../nonebot/adapters/feishu/__init__.py | 3 + .../nonebot/adapters/feishu/bot.py | 45 ++ .../nonebot/adapters/feishu/event.py | 31 + .../nonebot/adapters/feishu/message.py | 26 + packages/nonebot-adapter-feishu/poetry.lock | 609 ++++++++++++++++++ .../nonebot-adapter-feishu/pyproject.toml | 40 ++ 9 files changed, 765 insertions(+) create mode 100644 packages/nonebot-adapter-feishu/README.md create mode 100644 packages/nonebot-adapter-feishu/nonebot/__init__.py create mode 100644 packages/nonebot-adapter-feishu/nonebot/adapters/__init__.py create mode 100644 packages/nonebot-adapter-feishu/nonebot/adapters/feishu/__init__.py create mode 100644 packages/nonebot-adapter-feishu/nonebot/adapters/feishu/bot.py create mode 100644 packages/nonebot-adapter-feishu/nonebot/adapters/feishu/event.py create mode 100644 packages/nonebot-adapter-feishu/nonebot/adapters/feishu/message.py create mode 100644 packages/nonebot-adapter-feishu/poetry.lock create mode 100644 packages/nonebot-adapter-feishu/pyproject.toml diff --git a/packages/nonebot-adapter-feishu/README.md b/packages/nonebot-adapter-feishu/README.md new file mode 100644 index 00000000..8d9c629c --- /dev/null +++ b/packages/nonebot-adapter-feishu/README.md @@ -0,0 +1,11 @@ +

+ nonebot +

+ +
+ +# NoneBot-Adapter-Feishu + +_✨ 飞书协议适配 ✨_ + +
diff --git a/packages/nonebot-adapter-feishu/nonebot/__init__.py b/packages/nonebot-adapter-feishu/nonebot/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/packages/nonebot-adapter-feishu/nonebot/adapters/__init__.py b/packages/nonebot-adapter-feishu/nonebot/adapters/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/__init__.py b/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/__init__.py new file mode 100644 index 00000000..e0cfb5cb --- /dev/null +++ b/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/__init__.py @@ -0,0 +1,3 @@ +from .bot import Bot +from .message import Message, MessageSegment +from .event import Event diff --git a/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/bot.py b/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/bot.py new file mode 100644 index 00000000..60bebdc7 --- /dev/null +++ b/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/bot.py @@ -0,0 +1,45 @@ +from typing import Any, Union, Optional, TYPE_CHECKING + +from nonebot.log import logger +from nonebot.message import handle_event +from nonebot.adapters import Bot as BaseBot +from nonebot.exception import RequestDenied + +from .event import Event +from .message import Message, MessageSegment + +if TYPE_CHECKING: + from nonebot.drivers import Driver + + +class Bot(BaseBot): + """ + 飞书 协议 Bot 适配。继承属性参考 `BaseBot <./#class-basebot>`_ 。 + """ + + @property + def type(self) -> str: + return "feishu" + + @classmethod + async def check_permission(cls, driver: "Driver", connection_type: str, + headers: dict, body: Optional[dict]) -> str: + # raise RequestDenied(401, "reason") + return "bot id" + + async def handle_message(self, message: dict): + try: + event = Event.parse_obj(message) + await handle_event(self, event) + except Exception as e: + logger.opt(colors=True, exception=e).error( + f"Failed to handle event. Raw: {message}" + ) + + async def _call_api(self, api: str, **data) -> Any: + raise NotImplementedError + + async def send(self, event: Event, message: Union[str, Message, + MessageSegment], + **kwargs) -> Any: + raise NotImplementedError diff --git a/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/event.py b/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/event.py new file mode 100644 index 00000000..ae55076e --- /dev/null +++ b/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/event.py @@ -0,0 +1,31 @@ +from pydantic import BaseModel +from nonebot.adapters import Event as BaseEvent + +from .message import Message + + +class Event(BaseEvent): + + def get_type(self) -> str: + raise NotImplementedError + + def get_event_name(self) -> str: + raise NotImplementedError + + def get_event_description(self) -> str: + return str(self.dict()) + + def get_message(self) -> Message: + raise NotImplementedError + + def get_plaintext(self) -> str: + raise NotImplementedError + + def get_user_id(self) -> str: + raise NotImplementedError + + def get_session_id(self) -> str: + raise NotImplementedError + + def is_tome(self) -> bool: + return False diff --git a/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/message.py b/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/message.py new file mode 100644 index 00000000..d589e554 --- /dev/null +++ b/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/message.py @@ -0,0 +1,26 @@ +from typing import Union, Mapping, Iterable +from nonebot.adapters import Message as BaseMessage, MessageSegment as BaseMessageSegment + + +class MessageSegment(BaseMessageSegment): + + def __str__(self) -> str: + raise NotImplementedError + + def __add__(self, other) -> "Message": + return Message(self) + other + + def __radd__(self, other) -> "Message": + return Message(other) + self + + def is_text(self) -> bool: + raise NotImplementedError + + +class Message(BaseMessage): + + @staticmethod + def _construct( + msg: Union[str, Mapping, + Iterable[Mapping]]) -> Iterable[MessageSegment]: + raise NotImplementedError diff --git a/packages/nonebot-adapter-feishu/poetry.lock b/packages/nonebot-adapter-feishu/poetry.lock new file mode 100644 index 00000000..c1ca4a64 --- /dev/null +++ b/packages/nonebot-adapter-feishu/poetry.lock @@ -0,0 +1,609 @@ +[[package]] +name = "certifi" +version = "2021.5.30" +description = "Python package for providing Mozilla's CA Bundle." +category = "main" +optional = false +python-versions = "*" + +[package.source] +type = "legacy" +url = "https://mirrors.aliyun.com/pypi/simple" +reference = "aliyun" + +[[package]] +name = "click" +version = "7.1.2" +description = "Composable command line interface toolkit" +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" + +[package.source] +type = "legacy" +url = "https://mirrors.aliyun.com/pypi/simple" +reference = "aliyun" + +[[package]] +name = "colorama" +version = "0.4.4" +description = "Cross-platform colored terminal text." +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" + +[package.source] +type = "legacy" +url = "https://mirrors.aliyun.com/pypi/simple" +reference = "aliyun" + +[[package]] +name = "fastapi" +version = "0.65.2" +description = "FastAPI framework, high performance, easy to learn, fast to code, ready for production" +category = "main" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +pydantic = ">=1.6.2,<1.7 || >1.7,<1.7.1 || >1.7.1,<1.7.2 || >1.7.2,<1.7.3 || >1.7.3,<1.8 || >1.8,<1.8.1 || >1.8.1,<2.0.0" +starlette = "0.14.2" + +[package.extras] +all = ["requests (>=2.24.0,<3.0.0)", "aiofiles (>=0.5.0,<0.6.0)", "jinja2 (>=2.11.2,<3.0.0)", "python-multipart (>=0.0.5,<0.0.6)", "itsdangerous (>=1.1.0,<2.0.0)", "pyyaml (>=5.3.1,<6.0.0)", "graphene (>=2.1.8,<3.0.0)", "ujson (>=4.0.1,<5.0.0)", "orjson (>=3.2.1,<4.0.0)", "email_validator (>=1.1.1,<2.0.0)", "uvicorn[standard] (>=0.12.0,<0.14.0)", "async_exit_stack (>=1.0.1,<2.0.0)", "async_generator (>=1.10,<2.0.0)"] +dev = ["python-jose[cryptography] (>=3.1.0,<4.0.0)", "passlib[bcrypt] (>=1.7.2,<2.0.0)", "autoflake (>=1.3.1,<2.0.0)", "flake8 (>=3.8.3,<4.0.0)", "uvicorn[standard] (>=0.12.0,<0.14.0)", "graphene (>=2.1.8,<3.0.0)"] +doc = ["mkdocs (>=1.1.2,<2.0.0)", "mkdocs-material (>=6.1.4,<7.0.0)", "markdown-include (>=0.5.1,<0.6.0)", "mkdocs-markdownextradata-plugin (>=0.1.7,<0.2.0)", "typer-cli (>=0.0.9,<0.0.10)", "pyyaml (>=5.3.1,<6.0.0)"] +test = ["pytest (==5.4.3)", "pytest-cov (==2.10.0)", "pytest-asyncio (>=0.14.0,<0.15.0)", "mypy (==0.812)", "flake8 (>=3.8.3,<4.0.0)", "black (==20.8b1)", "isort (>=5.0.6,<6.0.0)", "requests (>=2.24.0,<3.0.0)", "httpx (>=0.14.0,<0.15.0)", "email_validator (>=1.1.1,<2.0.0)", "sqlalchemy (>=1.3.18,<1.4.0)", "peewee (>=3.13.3,<4.0.0)", "databases[sqlite] (>=0.3.2,<0.4.0)", "orjson (>=3.2.1,<4.0.0)", "ujson (>=4.0.1,<5.0.0)", "async_exit_stack (>=1.0.1,<2.0.0)", "async_generator (>=1.10,<2.0.0)", "python-multipart (>=0.0.5,<0.0.6)", "aiofiles (>=0.5.0,<0.6.0)", "flask (>=1.1.2,<2.0.0)"] + +[package.source] +type = "legacy" +url = "https://mirrors.aliyun.com/pypi/simple" +reference = "aliyun" + +[[package]] +name = "h11" +version = "0.12.0" +description = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1" +category = "main" +optional = false +python-versions = ">=3.6" + +[package.source] +type = "legacy" +url = "https://mirrors.aliyun.com/pypi/simple" +reference = "aliyun" + +[[package]] +name = "httpcore" +version = "0.12.3" +description = "A minimal low-level HTTP client." +category = "main" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +h11 = "<1.0.0" +sniffio = ">=1.0.0,<2.0.0" + +[package.extras] +http2 = ["h2 (>=3,<5)"] + +[package.source] +type = "legacy" +url = "https://mirrors.aliyun.com/pypi/simple" +reference = "aliyun" + +[[package]] +name = "httptools" +version = "0.1.2" +description = "A collection of framework independent HTTP protocol utils." +category = "main" +optional = false +python-versions = "*" + +[package.extras] +test = ["Cython (==0.29.22)"] + +[package.source] +type = "legacy" +url = "https://mirrors.aliyun.com/pypi/simple" +reference = "aliyun" + +[[package]] +name = "httpx" +version = "0.17.1" +description = "The next generation HTTP client." +category = "main" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +certifi = "*" +httpcore = ">=0.12.1,<0.13" +rfc3986 = {version = ">=1.3,<2", extras = ["idna2008"]} +sniffio = "*" + +[package.extras] +brotli = ["brotlipy (>=0.7.0,<0.8.0)"] +http2 = ["h2 (>=3.0.0,<4.0.0)"] + +[package.source] +type = "legacy" +url = "https://mirrors.aliyun.com/pypi/simple" +reference = "aliyun" + +[[package]] +name = "idna" +version = "3.2" +description = "Internationalized Domain Names in Applications (IDNA)" +category = "main" +optional = false +python-versions = ">=3.5" + +[package.source] +type = "legacy" +url = "https://mirrors.aliyun.com/pypi/simple" +reference = "aliyun" + +[[package]] +name = "loguru" +version = "0.5.3" +description = "Python logging made (stupidly) simple" +category = "main" +optional = false +python-versions = ">=3.5" + +[package.dependencies] +colorama = {version = ">=0.3.4", markers = "sys_platform == \"win32\""} +win32-setctime = {version = ">=1.0.0", markers = "sys_platform == \"win32\""} + +[package.extras] +dev = ["codecov (>=2.0.15)", "colorama (>=0.3.4)", "flake8 (>=3.7.7)", "tox (>=3.9.0)", "tox-travis (>=0.12)", "pytest (>=4.6.2)", "pytest-cov (>=2.7.1)", "Sphinx (>=2.2.1)", "sphinx-autobuild (>=0.7.1)", "sphinx-rtd-theme (>=0.4.3)", "black (>=19.10b0)", "isort (>=5.1.1)"] + +[package.source] +type = "legacy" +url = "https://mirrors.aliyun.com/pypi/simple" +reference = "aliyun" + +[[package]] +name = "nonebot2" +version = "2.0.0a13.post1" +description = "An asynchronous python bot framework." +category = "main" +optional = false +python-versions = "^3.7.3" +develop = true + +[package.dependencies] +fastapi = "^0.65.2" +loguru = "^0.5.1" +pydantic = {version = "~1.8.0", extras = ["dotenv"]} +pygtrie = "^2.4.1" +tomlkit = "^0.7.0" +uvicorn = {version = "^0.13.0", extras = ["standard"]} + +[package.extras] +quart = ["Quart (>=0.14.1,<0.15.0)"] +aiohttp = ["aiohttp (>=3.7.4,<4.0.0)"] +all = ["Quart (>=0.14.1,<0.15.0)", "aiohttp (>=3.7.4,<4.0.0)"] + +[package.source] +type = "directory" +url = "../.." + +[[package]] +name = "pydantic" +version = "1.8.2" +description = "Data validation and settings management using python 3.6 type hinting" +category = "main" +optional = false +python-versions = ">=3.6.1" + +[package.dependencies] +python-dotenv = {version = ">=0.10.4", optional = true, markers = "extra == \"dotenv\""} +typing-extensions = ">=3.7.4.3" + +[package.extras] +dotenv = ["python-dotenv (>=0.10.4)"] +email = ["email-validator (>=1.0.3)"] + +[package.source] +type = "legacy" +url = "https://mirrors.aliyun.com/pypi/simple" +reference = "aliyun" + +[[package]] +name = "pygtrie" +version = "2.4.2" +description = "A pure Python trie data structure implementation." +category = "main" +optional = false +python-versions = "*" + +[package.source] +type = "legacy" +url = "https://mirrors.aliyun.com/pypi/simple" +reference = "aliyun" + +[[package]] +name = "python-dotenv" +version = "0.18.0" +description = "Read key-value pairs from a .env file and set them as environment variables" +category = "main" +optional = false +python-versions = "*" + +[package.extras] +cli = ["click (>=5.0)"] + +[package.source] +type = "legacy" +url = "https://mirrors.aliyun.com/pypi/simple" +reference = "aliyun" + +[[package]] +name = "pyyaml" +version = "5.4.1" +description = "YAML parser and emitter for Python" +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" + +[package.source] +type = "legacy" +url = "https://mirrors.aliyun.com/pypi/simple" +reference = "aliyun" + +[[package]] +name = "rfc3986" +version = "1.5.0" +description = "Validating URI References per RFC 3986" +category = "main" +optional = false +python-versions = "*" + +[package.dependencies] +idna = {version = "*", optional = true, markers = "extra == \"idna2008\""} + +[package.extras] +idna2008 = ["idna"] + +[package.source] +type = "legacy" +url = "https://mirrors.aliyun.com/pypi/simple" +reference = "aliyun" + +[[package]] +name = "sniffio" +version = "1.2.0" +description = "Sniff out which async library your code is running under" +category = "main" +optional = false +python-versions = ">=3.5" + +[package.source] +type = "legacy" +url = "https://mirrors.aliyun.com/pypi/simple" +reference = "aliyun" + +[[package]] +name = "starlette" +version = "0.14.2" +description = "The little ASGI library that shines." +category = "main" +optional = false +python-versions = ">=3.6" + +[package.extras] +full = ["aiofiles", "graphene", "itsdangerous", "jinja2", "python-multipart", "pyyaml", "requests"] + +[package.source] +type = "legacy" +url = "https://mirrors.aliyun.com/pypi/simple" +reference = "aliyun" + +[[package]] +name = "tomlkit" +version = "0.7.2" +description = "Style preserving TOML library" +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" + +[package.source] +type = "legacy" +url = "https://mirrors.aliyun.com/pypi/simple" +reference = "aliyun" + +[[package]] +name = "typing-extensions" +version = "3.10.0.0" +description = "Backported and Experimental Type Hints for Python 3.5+" +category = "main" +optional = false +python-versions = "*" + +[package.source] +type = "legacy" +url = "https://mirrors.aliyun.com/pypi/simple" +reference = "aliyun" + +[[package]] +name = "uvicorn" +version = "0.13.4" +description = "The lightning-fast ASGI server." +category = "main" +optional = false +python-versions = "*" + +[package.dependencies] +click = ">=7.0.0,<8.0.0" +colorama = {version = ">=0.4", optional = true, markers = "sys_platform == \"win32\" and extra == \"standard\""} +h11 = ">=0.8" +httptools = {version = ">=0.1.0,<0.2.0", optional = true, markers = "sys_platform != \"win32\" and sys_platform != \"cygwin\" and platform_python_implementation != \"PyPy\" and extra == \"standard\""} +python-dotenv = {version = ">=0.13", optional = true, markers = "extra == \"standard\""} +PyYAML = {version = ">=5.1", optional = true, markers = "extra == \"standard\""} +typing-extensions = {version = "*", markers = "python_version < \"3.8\""} +uvloop = {version = ">=0.14.0,<0.15.0 || >0.15.0,<0.15.1 || >0.15.1", optional = true, markers = "sys_platform != \"win32\" and sys_platform != \"cygwin\" and platform_python_implementation != \"PyPy\" and extra == \"standard\""} +watchgod = {version = ">=0.6", optional = true, markers = "extra == \"standard\""} +websockets = {version = ">=8.0.0,<9.0.0", optional = true, markers = "extra == \"standard\""} + +[package.extras] +standard = ["websockets (>=8.0.0,<9.0.0)", "watchgod (>=0.6)", "python-dotenv (>=0.13)", "PyYAML (>=5.1)", "httptools (>=0.1.0,<0.2.0)", "uvloop (>=0.14.0,!=0.15.0,!=0.15.1)", "colorama (>=0.4)"] + +[package.source] +type = "legacy" +url = "https://mirrors.aliyun.com/pypi/simple" +reference = "aliyun" + +[[package]] +name = "uvloop" +version = "0.15.2" +description = "Fast implementation of asyncio event loop on top of libuv" +category = "main" +optional = false +python-versions = ">=3.7" + +[package.extras] +dev = ["Cython (>=0.29.20,<0.30.0)", "pytest (>=3.6.0)", "Sphinx (>=1.7.3,<1.8.0)", "sphinxcontrib-asyncio (>=0.2.0,<0.3.0)", "sphinx_rtd_theme (>=0.2.4,<0.3.0)", "aiohttp", "flake8 (>=3.8.4,<3.9.0)", "psutil", "pycodestyle (>=2.6.0,<2.7.0)", "pyOpenSSL (>=19.0.0,<19.1.0)", "mypy (>=0.800)"] +docs = ["Sphinx (>=1.7.3,<1.8.0)", "sphinxcontrib-asyncio (>=0.2.0,<0.3.0)", "sphinx_rtd_theme (>=0.2.4,<0.3.0)"] +test = ["aiohttp", "flake8 (>=3.8.4,<3.9.0)", "psutil", "pycodestyle (>=2.6.0,<2.7.0)", "pyOpenSSL (>=19.0.0,<19.1.0)", "mypy (>=0.800)"] + +[package.source] +type = "legacy" +url = "https://mirrors.aliyun.com/pypi/simple" +reference = "aliyun" + +[[package]] +name = "watchgod" +version = "0.7" +description = "Simple, modern file watching and code reload in python." +category = "main" +optional = false +python-versions = ">=3.5" + +[package.source] +type = "legacy" +url = "https://mirrors.aliyun.com/pypi/simple" +reference = "aliyun" + +[[package]] +name = "websockets" +version = "8.1" +description = "An implementation of the WebSocket Protocol (RFC 6455 & 7692)" +category = "main" +optional = false +python-versions = ">=3.6.1" + +[package.source] +type = "legacy" +url = "https://mirrors.aliyun.com/pypi/simple" +reference = "aliyun" + +[[package]] +name = "win32-setctime" +version = "1.0.3" +description = "A small Python utility to set file creation time on Windows" +category = "main" +optional = false +python-versions = ">=3.5" + +[package.extras] +dev = ["pytest (>=4.6.2)", "black (>=19.3b0)"] + +[package.source] +type = "legacy" +url = "https://mirrors.aliyun.com/pypi/simple" +reference = "aliyun" + +[metadata] +lock-version = "1.1" +python-versions = "^3.7.3" +content-hash = "f1908ea0987f3c4a50d18c8d0350b73602fa57e9349e1aa95b3190064ebcc881" + +[metadata.files] +certifi = [ + {file = "certifi-2021.5.30-py2.py3-none-any.whl", hash = "sha256:50b1e4f8446b06f41be7dd6338db18e0990601dce795c2b1686458aa7e8fa7d8"}, + {file = "certifi-2021.5.30.tar.gz", hash = "sha256:2bbf76fd432960138b3ef6dda3dde0544f27cbf8546c458e60baf371917ba9ee"}, +] +click = [ + {file = "click-7.1.2-py2.py3-none-any.whl", hash = "sha256:dacca89f4bfadd5de3d7489b7c8a566eee0d3676333fbb50030263894c38c0dc"}, + {file = "click-7.1.2.tar.gz", hash = "sha256:d2b5255c7c6349bc1bd1e59e08cd12acbbd63ce649f2588755783aa94dfb6b1a"}, +] +colorama = [ + {file = "colorama-0.4.4-py2.py3-none-any.whl", hash = "sha256:9f47eda37229f68eee03b24b9748937c7dc3868f906e8ba69fbcbdd3bc5dc3e2"}, + {file = "colorama-0.4.4.tar.gz", hash = "sha256:5941b2b48a20143d2267e95b1c2a7603ce057ee39fd88e7329b0c292aa16869b"}, +] +fastapi = [ + {file = "fastapi-0.65.2-py3-none-any.whl", hash = "sha256:39569a18914075b2f1aaa03bcb9dc96a38e0e5dabaf3972e088c9077dfffa379"}, + {file = "fastapi-0.65.2.tar.gz", hash = "sha256:8359e55d8412a5571c0736013d90af235d6949ec4ce978e9b63500c8f4b6f714"}, +] +h11 = [ + {file = "h11-0.12.0-py3-none-any.whl", hash = "sha256:36a3cb8c0a032f56e2da7084577878a035d3b61d104230d4bd49c0c6b555a9c6"}, + {file = "h11-0.12.0.tar.gz", hash = "sha256:47222cb6067e4a307d535814917cd98fd0a57b6788ce715755fa2b6c28b56042"}, +] +httpcore = [ + {file = "httpcore-0.12.3-py3-none-any.whl", hash = "sha256:93e822cd16c32016b414b789aeff4e855d0ccbfc51df563ee34d4dbadbb3bcdc"}, + {file = "httpcore-0.12.3.tar.gz", hash = "sha256:37ae835fb370049b2030c3290e12ed298bf1473c41bb72ca4aa78681eba9b7c9"}, +] +httptools = [ + {file = "httptools-0.1.2-cp35-cp35m-macosx_10_14_x86_64.whl", hash = "sha256:1e35aa179b67086cc600a984924a88589b90793c9c1b260152ca4908786e09df"}, + {file = "httptools-0.1.2-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:c4111a0a8a00eff1e495d43ea5230aaf64968a48ddba8ea2d5f982efae827404"}, + {file = "httptools-0.1.2-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:dce59ee45dd6ee6c434346a5ac527c44014326f560866b4b2f414a692ee1aca8"}, + {file = "httptools-0.1.2-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:f759717ca1b2ef498c67ba4169c2b33eecf943a89f5329abcff8b89d153eb500"}, + {file = "httptools-0.1.2-cp36-cp36m-win_amd64.whl", hash = "sha256:08b79e09114e6ab5c3dbf560bba2cb2257ea38cdaeaf99b7cb80d8f92622fcd9"}, + {file = "httptools-0.1.2-cp37-cp37m-macosx_10_14_x86_64.whl", hash = "sha256:8fcca4b7efe353b13a24017211334c57d055a6e132c7adffed13a10d28efca57"}, + {file = "httptools-0.1.2-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:aebdf0bd7bf7c90ae6b3be458692bf6e9e5b610b501f9f74c7979015a51db4c4"}, + {file = "httptools-0.1.2-cp37-cp37m-win_amd64.whl", hash = "sha256:fbf7ecd31c39728f251b1c095fd27c84e4d21f60a1d079a0333472ff3ae59d34"}, + {file = "httptools-0.1.2-cp38-cp38-macosx_10_14_x86_64.whl", hash = "sha256:c1c63d860749841024951b0a78e4dec6f543d23751ef061d6ab60064c7b8b524"}, + {file = "httptools-0.1.2-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:fb7199b8fb0c50a22e77260bb59017e0c075fa80cb03bb2c8692de76e7bb7fe7"}, + {file = "httptools-0.1.2-cp38-cp38-win_amd64.whl", hash = "sha256:bda99a5723e7eab355ce57435c70853fc137a65aebf2f1cd4d15d96e2956da7b"}, + {file = "httptools-0.1.2-cp39-cp39-macosx_10_14_x86_64.whl", hash = "sha256:851026bd63ec0af7e7592890d97d15c92b62d9e17094353f19a52c8e2b33710a"}, + {file = "httptools-0.1.2-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:31629e1f1b89959f8c0927bad12184dc07977dcf71e24f4772934aa490aa199b"}, + {file = "httptools-0.1.2-cp39-cp39-win_amd64.whl", hash = "sha256:9abd788465aa46a0f288bd3a99e53edd184177d6379e2098fd6097bb359ad9d6"}, + {file = "httptools-0.1.2.tar.gz", hash = "sha256:07659649fe6b3948b6490825f89abe5eb1cec79ebfaaa0b4bf30f3f33f3c2ba8"}, +] +httpx = [ + {file = "httpx-0.17.1-py3-none-any.whl", hash = "sha256:d379653bd457e8257eb0df99cb94557e4aac441b7ba948e333be969298cac272"}, + {file = "httpx-0.17.1.tar.gz", hash = "sha256:cc2a55188e4b25272d2bcd46379d300f632045de4377682aa98a8a6069d55967"}, +] +idna = [ + {file = "idna-3.2-py3-none-any.whl", hash = "sha256:14475042e284991034cb48e06f6851428fb14c4dc953acd9be9a5e95c7b6dd7a"}, + {file = "idna-3.2.tar.gz", hash = "sha256:467fbad99067910785144ce333826c71fb0e63a425657295239737f7ecd125f3"}, +] +loguru = [ + {file = "loguru-0.5.3-py3-none-any.whl", hash = "sha256:f8087ac396b5ee5f67c963b495d615ebbceac2796379599820e324419d53667c"}, + {file = "loguru-0.5.3.tar.gz", hash = "sha256:b28e72ac7a98be3d28ad28570299a393dfcd32e5e3f6a353dec94675767b6319"}, +] +nonebot2 = [] +pydantic = [ + {file = "pydantic-1.8.2-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:05ddfd37c1720c392f4e0d43c484217b7521558302e7069ce8d318438d297739"}, + {file = "pydantic-1.8.2-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:a7c6002203fe2c5a1b5cbb141bb85060cbff88c2d78eccbc72d97eb7022c43e4"}, + {file = "pydantic-1.8.2-cp36-cp36m-manylinux2014_i686.whl", hash = "sha256:589eb6cd6361e8ac341db97602eb7f354551482368a37f4fd086c0733548308e"}, + {file = "pydantic-1.8.2-cp36-cp36m-manylinux2014_x86_64.whl", hash = "sha256:10e5622224245941efc193ad1d159887872776df7a8fd592ed746aa25d071840"}, + {file = "pydantic-1.8.2-cp36-cp36m-win_amd64.whl", hash = "sha256:99a9fc39470010c45c161a1dc584997f1feb13f689ecf645f59bb4ba623e586b"}, + {file = "pydantic-1.8.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:a83db7205f60c6a86f2c44a61791d993dff4b73135df1973ecd9eed5ea0bda20"}, + {file = "pydantic-1.8.2-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:41b542c0b3c42dc17da70554bc6f38cbc30d7066d2c2815a94499b5684582ecb"}, + {file = "pydantic-1.8.2-cp37-cp37m-manylinux2014_i686.whl", hash = "sha256:ea5cb40a3b23b3265f6325727ddfc45141b08ed665458be8c6285e7b85bd73a1"}, + {file = "pydantic-1.8.2-cp37-cp37m-manylinux2014_x86_64.whl", hash = "sha256:18b5ea242dd3e62dbf89b2b0ec9ba6c7b5abaf6af85b95a97b00279f65845a23"}, + {file = "pydantic-1.8.2-cp37-cp37m-win_amd64.whl", hash = "sha256:234a6c19f1c14e25e362cb05c68afb7f183eb931dd3cd4605eafff055ebbf287"}, + {file = "pydantic-1.8.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:021ea0e4133e8c824775a0cfe098677acf6fa5a3cbf9206a376eed3fc09302cd"}, + {file = "pydantic-1.8.2-cp38-cp38-manylinux1_i686.whl", hash = "sha256:e710876437bc07bd414ff453ac8ec63d219e7690128d925c6e82889d674bb505"}, + {file = "pydantic-1.8.2-cp38-cp38-manylinux2014_i686.whl", hash = "sha256:ac8eed4ca3bd3aadc58a13c2aa93cd8a884bcf21cb019f8cfecaae3b6ce3746e"}, + {file = "pydantic-1.8.2-cp38-cp38-manylinux2014_x86_64.whl", hash = "sha256:4a03cbbe743e9c7247ceae6f0d8898f7a64bb65800a45cbdc52d65e370570820"}, + {file = "pydantic-1.8.2-cp38-cp38-win_amd64.whl", hash = "sha256:8621559dcf5afacf0069ed194278f35c255dc1a1385c28b32dd6c110fd6531b3"}, + {file = "pydantic-1.8.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:8b223557f9510cf0bfd8b01316bf6dd281cf41826607eada99662f5e4963f316"}, + {file = "pydantic-1.8.2-cp39-cp39-manylinux1_i686.whl", hash = "sha256:244ad78eeb388a43b0c927e74d3af78008e944074b7d0f4f696ddd5b2af43c62"}, + {file = "pydantic-1.8.2-cp39-cp39-manylinux2014_i686.whl", hash = "sha256:05ef5246a7ffd2ce12a619cbb29f3307b7c4509307b1b49f456657b43529dc6f"}, + {file = "pydantic-1.8.2-cp39-cp39-manylinux2014_x86_64.whl", hash = "sha256:54cd5121383f4a461ff7644c7ca20c0419d58052db70d8791eacbbe31528916b"}, + {file = "pydantic-1.8.2-cp39-cp39-win_amd64.whl", hash = "sha256:4be75bebf676a5f0f87937c6ddb061fa39cbea067240d98e298508c1bda6f3f3"}, + {file = "pydantic-1.8.2-py3-none-any.whl", hash = "sha256:fec866a0b59f372b7e776f2d7308511784dace622e0992a0b59ea3ccee0ae833"}, + {file = "pydantic-1.8.2.tar.gz", hash = "sha256:26464e57ccaafe72b7ad156fdaa4e9b9ef051f69e175dbbb463283000c05ab7b"}, +] +pygtrie = [ + {file = "pygtrie-2.4.2.tar.gz", hash = "sha256:43205559d28863358dbbf25045029f58e2ab357317a59b11f11ade278ac64692"}, +] +python-dotenv = [ + {file = "python-dotenv-0.18.0.tar.gz", hash = "sha256:effaac3c1e58d89b3ccb4d04a40dc7ad6e0275fda25fd75ae9d323e2465e202d"}, + {file = "python_dotenv-0.18.0-py2.py3-none-any.whl", hash = "sha256:dd8fe852847f4fbfadabf6183ddd4c824a9651f02d51714fa075c95561959c7d"}, +] +pyyaml = [ + {file = "PyYAML-5.4.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:3b2b1824fe7112845700f815ff6a489360226a5609b96ec2190a45e62a9fc922"}, + {file = "PyYAML-5.4.1-cp27-cp27m-win32.whl", hash = "sha256:129def1b7c1bf22faffd67b8f3724645203b79d8f4cc81f674654d9902cb4393"}, + {file = "PyYAML-5.4.1-cp27-cp27m-win_amd64.whl", hash = "sha256:4465124ef1b18d9ace298060f4eccc64b0850899ac4ac53294547536533800c8"}, + {file = "PyYAML-5.4.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:bb4191dfc9306777bc594117aee052446b3fa88737cd13b7188d0e7aa8162185"}, + {file = "PyYAML-5.4.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:6c78645d400265a062508ae399b60b8c167bf003db364ecb26dcab2bda048253"}, + {file = "PyYAML-5.4.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:4e0583d24c881e14342eaf4ec5fbc97f934b999a6828693a99157fde912540cc"}, + {file = "PyYAML-5.4.1-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:72a01f726a9c7851ca9bfad6fd09ca4e090a023c00945ea05ba1638c09dc3347"}, + {file = "PyYAML-5.4.1-cp36-cp36m-manylinux2014_s390x.whl", hash = "sha256:895f61ef02e8fed38159bb70f7e100e00f471eae2bc838cd0f4ebb21e28f8541"}, + {file = "PyYAML-5.4.1-cp36-cp36m-win32.whl", hash = "sha256:3bd0e463264cf257d1ffd2e40223b197271046d09dadf73a0fe82b9c1fc385a5"}, + {file = "PyYAML-5.4.1-cp36-cp36m-win_amd64.whl", hash = "sha256:e4fac90784481d221a8e4b1162afa7c47ed953be40d31ab4629ae917510051df"}, + {file = "PyYAML-5.4.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:5accb17103e43963b80e6f837831f38d314a0495500067cb25afab2e8d7a4018"}, + {file = "PyYAML-5.4.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:e1d4970ea66be07ae37a3c2e48b5ec63f7ba6804bdddfdbd3cfd954d25a82e63"}, + {file = "PyYAML-5.4.1-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:cb333c16912324fd5f769fff6bc5de372e9e7a202247b48870bc251ed40239aa"}, + {file = "PyYAML-5.4.1-cp37-cp37m-manylinux2014_s390x.whl", hash = "sha256:fe69978f3f768926cfa37b867e3843918e012cf83f680806599ddce33c2c68b0"}, + {file = "PyYAML-5.4.1-cp37-cp37m-win32.whl", hash = "sha256:dd5de0646207f053eb0d6c74ae45ba98c3395a571a2891858e87df7c9b9bd51b"}, + {file = "PyYAML-5.4.1-cp37-cp37m-win_amd64.whl", hash = "sha256:08682f6b72c722394747bddaf0aa62277e02557c0fd1c42cb853016a38f8dedf"}, + {file = "PyYAML-5.4.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d2d9808ea7b4af864f35ea216be506ecec180628aced0704e34aca0b040ffe46"}, + {file = "PyYAML-5.4.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:8c1be557ee92a20f184922c7b6424e8ab6691788e6d86137c5d93c1a6ec1b8fb"}, + {file = "PyYAML-5.4.1-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:fd7f6999a8070df521b6384004ef42833b9bd62cfee11a09bda1079b4b704247"}, + {file = "PyYAML-5.4.1-cp38-cp38-manylinux2014_s390x.whl", hash = "sha256:bfb51918d4ff3d77c1c856a9699f8492c612cde32fd3bcd344af9be34999bfdc"}, + {file = "PyYAML-5.4.1-cp38-cp38-win32.whl", hash = "sha256:fa5ae20527d8e831e8230cbffd9f8fe952815b2b7dae6ffec25318803a7528fc"}, + {file = "PyYAML-5.4.1-cp38-cp38-win_amd64.whl", hash = "sha256:0f5f5786c0e09baddcd8b4b45f20a7b5d61a7e7e99846e3c799b05c7c53fa696"}, + {file = "PyYAML-5.4.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:294db365efa064d00b8d1ef65d8ea2c3426ac366c0c4368d930bf1c5fb497f77"}, + {file = "PyYAML-5.4.1-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:74c1485f7707cf707a7aef42ef6322b8f97921bd89be2ab6317fd782c2d53183"}, + {file = "PyYAML-5.4.1-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:d483ad4e639292c90170eb6f7783ad19490e7a8defb3e46f97dfe4bacae89122"}, + {file = "PyYAML-5.4.1-cp39-cp39-manylinux2014_s390x.whl", hash = "sha256:fdc842473cd33f45ff6bce46aea678a54e3d21f1b61a7750ce3c498eedfe25d6"}, + {file = "PyYAML-5.4.1-cp39-cp39-win32.whl", hash = "sha256:49d4cdd9065b9b6e206d0595fee27a96b5dd22618e7520c33204a4a3239d5b10"}, + {file = "PyYAML-5.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:c20cfa2d49991c8b4147af39859b167664f2ad4561704ee74c1de03318e898db"}, + {file = "PyYAML-5.4.1.tar.gz", hash = "sha256:607774cbba28732bfa802b54baa7484215f530991055bb562efbed5b2f20a45e"}, +] +rfc3986 = [ + {file = "rfc3986-1.5.0-py2.py3-none-any.whl", hash = "sha256:a86d6e1f5b1dc238b218b012df0aa79409667bb209e58da56d0b94704e712a97"}, + {file = "rfc3986-1.5.0.tar.gz", hash = "sha256:270aaf10d87d0d4e095063c65bf3ddbc6ee3d0b226328ce21e036f946e421835"}, +] +sniffio = [ + {file = "sniffio-1.2.0-py3-none-any.whl", hash = "sha256:471b71698eac1c2112a40ce2752bb2f4a4814c22a54a3eed3676bc0f5ca9f663"}, + {file = "sniffio-1.2.0.tar.gz", hash = "sha256:c4666eecec1d3f50960c6bdf61ab7bc350648da6c126e3cf6898d8cd4ddcd3de"}, +] +starlette = [ + {file = "starlette-0.14.2-py3-none-any.whl", hash = "sha256:3c8e48e52736b3161e34c9f0e8153b4f32ec5d8995a3ee1d59410d92f75162ed"}, + {file = "starlette-0.14.2.tar.gz", hash = "sha256:7d49f4a27f8742262ef1470608c59ddbc66baf37c148e938c7038e6bc7a998aa"}, +] +tomlkit = [ + {file = "tomlkit-0.7.2-py2.py3-none-any.whl", hash = "sha256:173ad840fa5d2aac140528ca1933c29791b79a374a0861a80347f42ec9328117"}, + {file = "tomlkit-0.7.2.tar.gz", hash = "sha256:d7a454f319a7e9bd2e249f239168729327e4dd2d27b17dc68be264ad1ce36754"}, +] +typing-extensions = [ + {file = "typing_extensions-3.10.0.0-py2-none-any.whl", hash = "sha256:0ac0f89795dd19de6b97debb0c6af1c70987fd80a2d62d1958f7e56fcc31b497"}, + {file = "typing_extensions-3.10.0.0-py3-none-any.whl", hash = "sha256:779383f6086d90c99ae41cf0ff39aac8a7937a9283ce0a414e5dd782f4c94a84"}, + {file = "typing_extensions-3.10.0.0.tar.gz", hash = "sha256:50b6f157849174217d0656f99dc82fe932884fb250826c18350e159ec6cdf342"}, +] +uvicorn = [ + {file = "uvicorn-0.13.4-py3-none-any.whl", hash = "sha256:7587f7b08bd1efd2b9bad809a3d333e972f1d11af8a5e52a9371ee3a5de71524"}, + {file = "uvicorn-0.13.4.tar.gz", hash = "sha256:3292251b3c7978e8e4a7868f4baf7f7f7bb7e40c759ecc125c37e99cdea34202"}, +] +uvloop = [ + {file = "uvloop-0.15.2-cp37-cp37m-macosx_10_14_x86_64.whl", hash = "sha256:19fa1d56c91341318ac5d417e7b61c56e9a41183946cc70c411341173de02c69"}, + {file = "uvloop-0.15.2-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:e5e5f855c9bf483ee6cd1eb9a179b740de80cb0ae2988e3fa22309b78e2ea0e7"}, + {file = "uvloop-0.15.2-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:42eda9f525a208fbc4f7cecd00fa15c57cc57646c76632b3ba2fe005004f051d"}, + {file = "uvloop-0.15.2-cp38-cp38-macosx_10_14_x86_64.whl", hash = "sha256:90e56f17755e41b425ad19a08c41dc358fa7bf1226c0f8e54d4d02d556f7af7c"}, + {file = "uvloop-0.15.2-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:7ae39b11a5f4cec1432d706c21ecc62f9e04d116883178b09671aa29c46f7a47"}, + {file = "uvloop-0.15.2-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:b45218c99795803fb8bdbc9435ff7f54e3a591b44cd4c121b02fa83affb61c7c"}, + {file = "uvloop-0.15.2-cp39-cp39-macosx_10_14_x86_64.whl", hash = "sha256:114543c84e95df1b4ff546e6e3a27521580466a30127f12172a3278172ad68bc"}, + {file = "uvloop-0.15.2-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:44cac8575bf168601424302045234d74e3561fbdbac39b2b54cc1d1d00b70760"}, + {file = "uvloop-0.15.2-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:6de130d0cb78985a5d080e323b86c5ecaf3af82f4890492c05981707852f983c"}, + {file = "uvloop-0.15.2.tar.gz", hash = "sha256:2bb0624a8a70834e54dde8feed62ed63b50bad7a1265c40d6403a2ac447bce01"}, +] +watchgod = [ + {file = "watchgod-0.7-py3-none-any.whl", hash = "sha256:d6c1ea21df37847ac0537ca0d6c2f4cdf513562e95f77bb93abbcf05573407b7"}, + {file = "watchgod-0.7.tar.gz", hash = "sha256:48140d62b0ebe9dd9cf8381337f06351e1f2e70b2203fa9c6eff4e572ca84f29"}, +] +websockets = [ + {file = "websockets-8.1-cp36-cp36m-macosx_10_6_intel.whl", hash = "sha256:3762791ab8b38948f0c4d281c8b2ddfa99b7e510e46bd8dfa942a5fff621068c"}, + {file = "websockets-8.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:3db87421956f1b0779a7564915875ba774295cc86e81bc671631379371af1170"}, + {file = "websockets-8.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:4f9f7d28ce1d8f1295717c2c25b732c2bc0645db3215cf757551c392177d7cb8"}, + {file = "websockets-8.1-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:295359a2cc78736737dd88c343cd0747546b2174b5e1adc223824bcaf3e164cb"}, + {file = "websockets-8.1-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:1d3f1bf059d04a4e0eb4985a887d49195e15ebabc42364f4eb564b1d065793f5"}, + {file = "websockets-8.1-cp36-cp36m-win32.whl", hash = "sha256:2db62a9142e88535038a6bcfea70ef9447696ea77891aebb730a333a51ed559a"}, + {file = "websockets-8.1-cp36-cp36m-win_amd64.whl", hash = "sha256:0e4fb4de42701340bd2353bb2eee45314651caa6ccee80dbd5f5d5978888fed5"}, + {file = "websockets-8.1-cp37-cp37m-macosx_10_6_intel.whl", hash = "sha256:9b248ba3dd8a03b1a10b19efe7d4f7fa41d158fdaa95e2cf65af5a7b95a4f989"}, + {file = "websockets-8.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:ce85b06a10fc65e6143518b96d3dca27b081a740bae261c2fb20375801a9d56d"}, + {file = "websockets-8.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:965889d9f0e2a75edd81a07592d0ced54daa5b0785f57dc429c378edbcffe779"}, + {file = "websockets-8.1-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:751a556205d8245ff94aeef23546a1113b1dd4f6e4d102ded66c39b99c2ce6c8"}, + {file = "websockets-8.1-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:3ef56fcc7b1ff90de46ccd5a687bbd13a3180132268c4254fc0fa44ecf4fc422"}, + {file = "websockets-8.1-cp37-cp37m-win32.whl", hash = "sha256:7ff46d441db78241f4c6c27b3868c9ae71473fe03341340d2dfdbe8d79310acc"}, + {file = "websockets-8.1-cp37-cp37m-win_amd64.whl", hash = "sha256:20891f0dddade307ffddf593c733a3fdb6b83e6f9eef85908113e628fa5a8308"}, + {file = "websockets-8.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:c1ec8db4fac31850286b7cd3b9c0e1b944204668b8eb721674916d4e28744092"}, + {file = "websockets-8.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:5c01fd846263a75bc8a2b9542606927cfad57e7282965d96b93c387622487485"}, + {file = "websockets-8.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:9bef37ee224e104a413f0780e29adb3e514a5b698aabe0d969a6ba426b8435d1"}, + {file = "websockets-8.1-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:d705f8aeecdf3262379644e4b55107a3b55860eb812b673b28d0fbc347a60c55"}, + {file = "websockets-8.1-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:c8a116feafdb1f84607cb3b14aa1418424ae71fee131642fc568d21423b51824"}, + {file = "websockets-8.1-cp38-cp38-win32.whl", hash = "sha256:e898a0863421650f0bebac8ba40840fc02258ef4714cb7e1fd76b6a6354bda36"}, + {file = "websockets-8.1-cp38-cp38-win_amd64.whl", hash = "sha256:f8a7bff6e8664afc4e6c28b983845c5bc14965030e3fb98789734d416af77c4b"}, + {file = "websockets-8.1.tar.gz", hash = "sha256:5c65d2da8c6bce0fca2528f69f44b2f977e06954c8512a952222cea50dad430f"}, +] +win32-setctime = [ + {file = "win32_setctime-1.0.3-py3-none-any.whl", hash = "sha256:dc925662de0a6eb987f0b01f599c01a8236cb8c62831c22d9cada09ad958243e"}, + {file = "win32_setctime-1.0.3.tar.gz", hash = "sha256:4e88556c32fdf47f64165a2180ba4552f8bb32c1103a2fafd05723a0bd42bd4b"}, +] diff --git a/packages/nonebot-adapter-feishu/pyproject.toml b/packages/nonebot-adapter-feishu/pyproject.toml new file mode 100644 index 00000000..1818527c --- /dev/null +++ b/packages/nonebot-adapter-feishu/pyproject.toml @@ -0,0 +1,40 @@ +[tool.poetry] +name = "nonebot-adapter-feishu" +version = "2.0.0-alpha.13" +description = "feishu(larksuite) adapter for nonebot2" +authors = ["StarHeartHunt "] +license = "MIT" +readme = "README.md" +homepage = "https://v2.nonebot.dev/" +repository = "https://github.com/nonebot/nonebot2" +documentation = "https://v2.nonebot.dev/" +keywords = ["bot", "qq", "qqbot", "feishu", "larksuite"] +classifiers = [ + "Development Status :: 5 - Production/Stable", + "Framework :: Robot Framework", + "Framework :: Robot Framework :: Library", + "License :: OSI Approved :: MIT License", + "Operating System :: OS Independent", + "Programming Language :: Python :: 3" +] +packages = [ + { include = "nonebot" } +] +exclude = ["nonebot/__init__.py", "nonebot/adapters/__init__.py"] + +[tool.poetry.dependencies] +python = "^3.7.3" +httpx = "^0.17.0" +nonebot2 = "^2.0.0-alpha.13" + +[tool.poetry.dev-dependencies] +nonebot2 = { path = "../../", develop = true } + +[[tool.poetry.source]] +name = "aliyun" +url = "https://mirrors.aliyun.com/pypi/simple/" +default = true + +[build-system] +requires = ["poetry-core>=1.0.0"] +build-backend = "poetry.core.masonry.api" From d669fed5e97b0a7b861c9fddb140419504daf14b Mon Sep 17 00:00:00 2001 From: StarHeartHunt Date: Sat, 3 Jul 2021 13:53:01 +0800 Subject: [PATCH 02/64] =?UTF-8?q?=E2=9C=85=20add=20testcase=20for=20feishu?= =?UTF-8?q?=20adapter?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- poetry.lock | 92 ++++++++++++++++++++++++++----- pyproject.toml | 1 + tests/bot.py | 2 + tests/test_plugins/test_feishu.py | 12 ++++ 4 files changed, 93 insertions(+), 14 deletions(-) create mode 100644 tests/test_plugins/test_feishu.py diff --git a/poetry.lock b/poetry.lock index fa4c7843..0f94350d 100644 --- a/poetry.lock +++ b/poetry.lock @@ -490,6 +490,24 @@ nonebot2 = "^2.0.0-alpha.12" type = "directory" url = "packages/nonebot-adapter-ding" +[[package]] +name = "nonebot-adapter-feishu" +version = "2.0.0-alpha.13" +description = "feishu(larksuite) adapter for nonebot2" +category = "dev" +optional = false +python-versions = "^3.7.3" +develop = true + +[package.dependencies] +httpx = "^0.17.0" +nonebot2 = "^2.0.0-alpha.13" +pycryptodome = "^3.10.1" + +[package.source] +type = "directory" +url = "packages/nonebot-adapter-feishu" + [[package]] name = "nonebot-adapter-mirai" version = "2.0.0-alpha.12" @@ -544,11 +562,24 @@ reference = "aliyun" [[package]] name = "priority" -version = "1.3.0" +version = "2.0.0" description = "A pure-Python implementation of the HTTP/2 priority tree" category = "main" optional = true -python-versions = "*" +python-versions = ">=3.6.1" + +[package.source] +type = "legacy" +url = "https://mirrors.aliyun.com/pypi/simple" +reference = "aliyun" + +[[package]] +name = "pycryptodome" +version = "3.10.1" +description = "Cryptographic library for Python" +category = "dev" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" [package.source] type = "legacy" @@ -578,7 +609,7 @@ reference = "aliyun" [[package]] name = "pydash" -version = "5.0.0" +version = "5.0.1" description = "The kitchen sink of Python utility libraries for doing \"stuff\" in a functional way. Based on the Lo-Dash Javascript library." category = "dev" optional = false @@ -633,7 +664,7 @@ reference = "aliyun" [[package]] name = "python-dotenv" -version = "0.17.1" +version = "0.18.0" description = "Read key-value pairs from a .env file and set them as environment variables" category = "main" optional = false @@ -1067,7 +1098,7 @@ reference = "aliyun" [[package]] name = "urllib3" -version = "1.26.5" +version = "1.26.6" description = "HTTP library with thread-safe connection pooling, file post, and more." category = "dev" optional = false @@ -1242,7 +1273,7 @@ quart = ["Quart"] [metadata] lock-version = "1.1" python-versions = "^3.7.3" -content-hash = "c8d97292fdd918db17f6c53f38ff2d1fb3309da7153f58c4c8e818bd425e302e" +content-hash = "da87abf42c19eca5a1c943ae53d12b57121dda28dcb80533373568aed6c76470" [metadata.files] aiofiles = [ @@ -1477,6 +1508,7 @@ multidict = [ ] nonebot-adapter-cqhttp = [] nonebot-adapter-ding = [] +nonebot-adapter-feishu = [] nonebot-adapter-mirai = [] nonebot-plugin-test = [ {file = "nonebot-plugin-test-0.2.0.tar.gz", hash = "sha256:c9ee997c5c96160de4af02d10a7c6301b3fc4e942df7e70906df0534606ea23b"}, @@ -1487,8 +1519,40 @@ packaging = [ {file = "packaging-20.9.tar.gz", hash = "sha256:5b327ac1320dc863dca72f4514ecc086f31186744b84a230374cc1fd776feae5"}, ] priority = [ - {file = "priority-1.3.0-py2.py3-none-any.whl", hash = "sha256:be4fcb94b5e37cdeb40af5533afe6dd603bd665fe9c8b3052610fc1001d5d1eb"}, - {file = "priority-1.3.0.tar.gz", hash = "sha256:6bc1961a6d7fcacbfc337769f1a382c8e746566aaa365e78047abe9f66b2ffbe"}, + {file = "priority-2.0.0-py3-none-any.whl", hash = "sha256:6f8eefce5f3ad59baf2c080a664037bb4725cd0a790d53d59ab4059288faf6aa"}, + {file = "priority-2.0.0.tar.gz", hash = "sha256:c965d54f1b8d0d0b19479db3924c7c36cf672dbf2aec92d43fbdaf4492ba18c0"}, +] +pycryptodome = [ + {file = "pycryptodome-3.10.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:1c5e1ca507de2ad93474be5cfe2bfa76b7cf039a1a32fc196f40935944871a06"}, + {file = "pycryptodome-3.10.1-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:6260e24d41149268122dd39d4ebd5941e9d107f49463f7e071fd397e29923b0c"}, + {file = "pycryptodome-3.10.1-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:3f840c49d38986f6e17dbc0673d37947c88bc9d2d9dba1c01b979b36f8447db1"}, + {file = "pycryptodome-3.10.1-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:2dea65df54349cdfa43d6b2e8edb83f5f8d6861e5cf7b1fbc3e34c5694c85e27"}, + {file = "pycryptodome-3.10.1-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:e61e363d9a5d7916f3a4ce984a929514c0df3daf3b1b2eb5e6edbb131ee771cf"}, + {file = "pycryptodome-3.10.1-cp27-cp27m-manylinux2014_aarch64.whl", hash = "sha256:2603c98ae04aac675fefcf71a6c87dc4bb74a75e9071ae3923bbc91a59f08d35"}, + {file = "pycryptodome-3.10.1-cp27-cp27m-win32.whl", hash = "sha256:38661348ecb71476037f1e1f553159b80d256c00f6c0b00502acac891f7116d9"}, + {file = "pycryptodome-3.10.1-cp27-cp27m-win_amd64.whl", hash = "sha256:1723ebee5561628ce96748501cdaa7afaa67329d753933296321f0be55358dce"}, + {file = "pycryptodome-3.10.1-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:77997519d8eb8a4adcd9a47b9cec18f9b323e296986528186c0e9a7a15d6a07e"}, + {file = "pycryptodome-3.10.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:99b2f3fc51d308286071d0953f92055504a6ffe829a832a9fc7a04318a7683dd"}, + {file = "pycryptodome-3.10.1-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:e0a4d5933a88a2c98bbe19c0c722f5483dc628d7a38338ac2cb64a7dbd34064b"}, + {file = "pycryptodome-3.10.1-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:d3d6958d53ad307df5e8469cc44474a75393a434addf20ecd451f38a72fe29b8"}, + {file = "pycryptodome-3.10.1-cp27-cp27mu-manylinux2014_aarch64.whl", hash = "sha256:a8eb8b6ea09ec1c2535bf39914377bc8abcab2c7d30fa9225eb4fe412024e427"}, + {file = "pycryptodome-3.10.1-cp35-abi3-macosx_10_9_x86_64.whl", hash = "sha256:31c1df17b3dc5f39600a4057d7db53ac372f492c955b9b75dd439f5d8b460129"}, + {file = "pycryptodome-3.10.1-cp35-abi3-manylinux1_i686.whl", hash = "sha256:a3105a0eb63eacf98c2ecb0eb4aa03f77f40fbac2bdde22020bb8a536b226bb8"}, + {file = "pycryptodome-3.10.1-cp35-abi3-manylinux1_x86_64.whl", hash = "sha256:a92d5c414e8ee1249e850789052608f582416e82422502dc0ac8c577808a9067"}, + {file = "pycryptodome-3.10.1-cp35-abi3-manylinux2010_i686.whl", hash = "sha256:60386d1d4cfaad299803b45a5bc2089696eaf6cdd56f9fc17479a6f89595cfc8"}, + {file = "pycryptodome-3.10.1-cp35-abi3-manylinux2010_x86_64.whl", hash = "sha256:501ab36aae360e31d0ec370cf5ce8ace6cb4112060d099b993bc02b36ac83fb6"}, + {file = "pycryptodome-3.10.1-cp35-abi3-manylinux2014_aarch64.whl", hash = "sha256:fc7489a50323a0df02378bc2fff86eb69d94cc5639914346c736be981c6a02e7"}, + {file = "pycryptodome-3.10.1-cp35-abi3-win32.whl", hash = "sha256:9b6f711b25e01931f1c61ce0115245a23cdc8b80bf8539ac0363bdcf27d649b6"}, + {file = "pycryptodome-3.10.1-cp35-abi3-win_amd64.whl", hash = "sha256:7fd519b89585abf57bf47d90166903ec7b43af4fe23c92273ea09e6336af5c07"}, + {file = "pycryptodome-3.10.1-pp27-pypy_73-macosx_10_9_x86_64.whl", hash = "sha256:09c1555a3fa450e7eaca41ea11cd00afe7c91fef52353488e65663777d8524e0"}, + {file = "pycryptodome-3.10.1-pp27-pypy_73-manylinux1_x86_64.whl", hash = "sha256:758949ca62690b1540dfb24ad773c6da9cd0e425189e83e39c038bbd52b8e438"}, + {file = "pycryptodome-3.10.1-pp27-pypy_73-manylinux2010_x86_64.whl", hash = "sha256:e3bf558c6aeb49afa9f0c06cee7fb5947ee5a1ff3bd794b653d39926b49077fa"}, + {file = "pycryptodome-3.10.1-pp27-pypy_73-win32.whl", hash = "sha256:f977cdf725b20f6b8229b0c87acb98c7717e742ef9f46b113985303ae12a99da"}, + {file = "pycryptodome-3.10.1-pp36-pypy36_pp73-macosx_10_9_x86_64.whl", hash = "sha256:6d2df5223b12437e644ce0a3be7809471ffa71de44ccd28b02180401982594a6"}, + {file = "pycryptodome-3.10.1-pp36-pypy36_pp73-manylinux1_x86_64.whl", hash = "sha256:98213ac2b18dc1969a47bc65a79a8fca02a414249d0c8635abb081c7f38c91b6"}, + {file = "pycryptodome-3.10.1-pp36-pypy36_pp73-manylinux2010_x86_64.whl", hash = "sha256:12222a5edc9ca4a29de15fbd5339099c4c26c56e13c2ceddf0b920794f26165d"}, + {file = "pycryptodome-3.10.1-pp36-pypy36_pp73-win32.whl", hash = "sha256:6bbf7fee7b7948b29d7e71fcacf48bac0c57fb41332007061a933f2d996f9713"}, + {file = "pycryptodome-3.10.1.tar.gz", hash = "sha256:3e2e3a06580c5f190df843cdb90ea28d61099cf4924334d5297a995de68e4673"}, ] pydantic = [ {file = "pydantic-1.8.2-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:05ddfd37c1720c392f4e0d43c484217b7521558302e7069ce8d318438d297739"}, @@ -1515,8 +1579,8 @@ pydantic = [ {file = "pydantic-1.8.2.tar.gz", hash = "sha256:26464e57ccaafe72b7ad156fdaa4e9b9ef051f69e175dbbb463283000c05ab7b"}, ] pydash = [ - {file = "pydash-5.0.0-py3-none-any.whl", hash = "sha256:0d87f879a3df4ad9389ab6d63c69eea078517d41541ddd5744cfcff3396e8543"}, - {file = "pydash-5.0.0.tar.gz", hash = "sha256:845262df83b5411742e5f7f7dbfa5ed4d0ddac6d7d0a13c4375c6a3c40d4e8f4"}, + {file = "pydash-5.0.1-py3-none-any.whl", hash = "sha256:70ac35e71722c50e8444675e2446d6b81f412efeee52765aae86c84a9f3a7a94"}, + {file = "pydash-5.0.1.tar.gz", hash = "sha256:95234efc2f4bc11d32a9f7f9b7199a54ea46a596e842d0df645d3401f5393d50"}, ] pygments = [ {file = "Pygments-2.9.0-py3-none-any.whl", hash = "sha256:d66e804411278594d764fc69ec36ec13d9ae9147193a1740cd34d272ca383b8e"}, @@ -1530,8 +1594,8 @@ pyparsing = [ {file = "pyparsing-2.4.7.tar.gz", hash = "sha256:c203ec8783bf771a155b207279b9bccb8dea02d8f0c9e5f8ead507bc3246ecc1"}, ] python-dotenv = [ - {file = "python-dotenv-0.17.1.tar.gz", hash = "sha256:b1ae5e9643d5ed987fc57cc2583021e38db531946518130777734f9589b3141f"}, - {file = "python_dotenv-0.17.1-py2.py3-none-any.whl", hash = "sha256:00aa34e92d992e9f8383730816359647f358f4a3be1ba45e5a5cefd27ee91544"}, + {file = "python-dotenv-0.18.0.tar.gz", hash = "sha256:effaac3c1e58d89b3ccb4d04a40dc7ad6e0275fda25fd75ae9d323e2465e202d"}, + {file = "python_dotenv-0.18.0-py2.py3-none-any.whl", hash = "sha256:dd8fe852847f4fbfadabf6183ddd4c824a9651f02d51714fa075c95561959c7d"}, ] python-engineio = [ {file = "python-engineio-3.14.2.tar.gz", hash = "sha256:eab4553f2804c1ce97054c8b22cf0d5a9ab23128075248b97e1a5b2f29553085"}, @@ -1653,8 +1717,8 @@ untokenize = [ {file = "untokenize-0.1.1.tar.gz", hash = "sha256:3865dbbbb8efb4bb5eaa72f1be7f3e0be00ea8b7f125c69cbd1f5fda926f37a2"}, ] urllib3 = [ - {file = "urllib3-1.26.5-py2.py3-none-any.whl", hash = "sha256:753a0374df26658f99d826cfe40394a686d05985786d946fbe4165b5148f5a7c"}, - {file = "urllib3-1.26.5.tar.gz", hash = "sha256:a7acd0977125325f516bda9735fa7142b909a8d01e8b2e4c8108d0984e6e0098"}, + {file = "urllib3-1.26.6-py2.py3-none-any.whl", hash = "sha256:39fb8672126159acb139a7718dd10806104dec1e2f0f6c88aab05d17df10c8d4"}, + {file = "urllib3-1.26.6.tar.gz", hash = "sha256:f57b4c16c62fa2760b7e3d97c35b255512fb6b59a259730f36ba32ce9f8e342f"}, ] uvicorn = [ {file = "uvicorn-0.13.4-py3-none-any.whl", hash = "sha256:7587f7b08bd1efd2b9bad809a3d333e972f1d11af8a5e52a9371ee3a5de71524"}, diff --git a/pyproject.toml b/pyproject.toml index 95ed14f7..d86ecf72 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -40,6 +40,7 @@ nonebot-plugin-test = "^0.2.0" nonebot-adapter-cqhttp = { path = "./packages/nonebot-adapter-cqhttp", develop = true } nonebot-adapter-ding = { path = "./packages/nonebot-adapter-ding", develop = true } nonebot-adapter-mirai = { path = "./packages/nonebot-adapter-mirai", develop = true } +nonebot-adapter-feishu = { path = "./packages/nonebot-adapter-feishu", develop = true } sphinx-markdown-builder = { git = "https://github.com/nonebot/sphinx-markdown-builder.git" } [tool.poetry.extras] diff --git a/tests/bot.py b/tests/bot.py index f60c1878..ccccd009 100644 --- a/tests/bot.py +++ b/tests/bot.py @@ -7,6 +7,7 @@ import nonebot from nonebot.adapters.cqhttp import Bot from nonebot.adapters.ding import Bot as DingBot from nonebot.adapters.mirai import Bot as MiraiBot +from nonebot.adapters.feishu import Bot as FeishuBot from nonebot.log import logger, default_format # test custom log @@ -22,6 +23,7 @@ driver = nonebot.get_driver() driver.register_adapter("cqhttp", Bot) driver.register_adapter("ding", DingBot) driver.register_adapter("mirai", MiraiBot) +driver.register_adapter("feishu", FeishuBot) # load builtin plugin nonebot.load_builtin_plugins() diff --git a/tests/test_plugins/test_feishu.py b/tests/test_plugins/test_feishu.py new file mode 100644 index 00000000..fdd2e2fa --- /dev/null +++ b/tests/test_plugins/test_feishu.py @@ -0,0 +1,12 @@ +from nonebot.adapters.feishu.event import GroupMessageEvent, PrivateMessageEvent +from nonebot.rule import to_me +from nonebot.plugin import on_command +from nonebot.adapters.feishu import Bot as FeishuBot, MessageSegment, MessageEvent + +helper = on_command("ding_helper", to_me()) + + +@helper.handle() +async def ding_helper(bot: FeishuBot, event: MessageEvent): + message = MessageSegment.text("114514") + await helper.finish(message) From 3e66eb2fdbc9c6cd0a1fe0c532e7334df1f8eaec Mon Sep 17 00:00:00 2001 From: StarHeartHunt Date: Sat, 3 Jul 2021 13:53:43 +0800 Subject: [PATCH 03/64] =?UTF-8?q?=E2=9E=95=20add=20encryption=20dependency?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/nonebot-adapter-feishu/poetry.lock | 47 ++++++++++++++++++- .../nonebot-adapter-feishu/pyproject.toml | 1 + 2 files changed, 47 insertions(+), 1 deletion(-) diff --git a/packages/nonebot-adapter-feishu/poetry.lock b/packages/nonebot-adapter-feishu/poetry.lock index c1ca4a64..77323e08 100644 --- a/packages/nonebot-adapter-feishu/poetry.lock +++ b/packages/nonebot-adapter-feishu/poetry.lock @@ -191,6 +191,19 @@ all = ["Quart (>=0.14.1,<0.15.0)", "aiohttp (>=3.7.4,<4.0.0)"] type = "directory" url = "../.." +[[package]] +name = "pycryptodome" +version = "3.10.1" +description = "Cryptographic library for Python" +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" + +[package.source] +type = "legacy" +url = "https://mirrors.aliyun.com/pypi/simple" +reference = "aliyun" + [[package]] name = "pydantic" version = "1.8.2" @@ -419,7 +432,7 @@ reference = "aliyun" [metadata] lock-version = "1.1" python-versions = "^3.7.3" -content-hash = "f1908ea0987f3c4a50d18c8d0350b73602fa57e9349e1aa95b3190064ebcc881" +content-hash = "fbc5c896e785a9b9c948ec9f20d5d795684bc0f839bd31b10812c9dc52adb75b" [metadata.files] certifi = [ @@ -476,6 +489,38 @@ loguru = [ {file = "loguru-0.5.3.tar.gz", hash = "sha256:b28e72ac7a98be3d28ad28570299a393dfcd32e5e3f6a353dec94675767b6319"}, ] nonebot2 = [] +pycryptodome = [ + {file = "pycryptodome-3.10.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:1c5e1ca507de2ad93474be5cfe2bfa76b7cf039a1a32fc196f40935944871a06"}, + {file = "pycryptodome-3.10.1-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:6260e24d41149268122dd39d4ebd5941e9d107f49463f7e071fd397e29923b0c"}, + {file = "pycryptodome-3.10.1-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:3f840c49d38986f6e17dbc0673d37947c88bc9d2d9dba1c01b979b36f8447db1"}, + {file = "pycryptodome-3.10.1-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:2dea65df54349cdfa43d6b2e8edb83f5f8d6861e5cf7b1fbc3e34c5694c85e27"}, + {file = "pycryptodome-3.10.1-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:e61e363d9a5d7916f3a4ce984a929514c0df3daf3b1b2eb5e6edbb131ee771cf"}, + {file = "pycryptodome-3.10.1-cp27-cp27m-manylinux2014_aarch64.whl", hash = "sha256:2603c98ae04aac675fefcf71a6c87dc4bb74a75e9071ae3923bbc91a59f08d35"}, + {file = "pycryptodome-3.10.1-cp27-cp27m-win32.whl", hash = "sha256:38661348ecb71476037f1e1f553159b80d256c00f6c0b00502acac891f7116d9"}, + {file = "pycryptodome-3.10.1-cp27-cp27m-win_amd64.whl", hash = "sha256:1723ebee5561628ce96748501cdaa7afaa67329d753933296321f0be55358dce"}, + {file = "pycryptodome-3.10.1-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:77997519d8eb8a4adcd9a47b9cec18f9b323e296986528186c0e9a7a15d6a07e"}, + {file = "pycryptodome-3.10.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:99b2f3fc51d308286071d0953f92055504a6ffe829a832a9fc7a04318a7683dd"}, + {file = "pycryptodome-3.10.1-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:e0a4d5933a88a2c98bbe19c0c722f5483dc628d7a38338ac2cb64a7dbd34064b"}, + {file = "pycryptodome-3.10.1-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:d3d6958d53ad307df5e8469cc44474a75393a434addf20ecd451f38a72fe29b8"}, + {file = "pycryptodome-3.10.1-cp27-cp27mu-manylinux2014_aarch64.whl", hash = "sha256:a8eb8b6ea09ec1c2535bf39914377bc8abcab2c7d30fa9225eb4fe412024e427"}, + {file = "pycryptodome-3.10.1-cp35-abi3-macosx_10_9_x86_64.whl", hash = "sha256:31c1df17b3dc5f39600a4057d7db53ac372f492c955b9b75dd439f5d8b460129"}, + {file = "pycryptodome-3.10.1-cp35-abi3-manylinux1_i686.whl", hash = "sha256:a3105a0eb63eacf98c2ecb0eb4aa03f77f40fbac2bdde22020bb8a536b226bb8"}, + {file = "pycryptodome-3.10.1-cp35-abi3-manylinux1_x86_64.whl", hash = "sha256:a92d5c414e8ee1249e850789052608f582416e82422502dc0ac8c577808a9067"}, + {file = "pycryptodome-3.10.1-cp35-abi3-manylinux2010_i686.whl", hash = "sha256:60386d1d4cfaad299803b45a5bc2089696eaf6cdd56f9fc17479a6f89595cfc8"}, + {file = "pycryptodome-3.10.1-cp35-abi3-manylinux2010_x86_64.whl", hash = "sha256:501ab36aae360e31d0ec370cf5ce8ace6cb4112060d099b993bc02b36ac83fb6"}, + {file = "pycryptodome-3.10.1-cp35-abi3-manylinux2014_aarch64.whl", hash = "sha256:fc7489a50323a0df02378bc2fff86eb69d94cc5639914346c736be981c6a02e7"}, + {file = "pycryptodome-3.10.1-cp35-abi3-win32.whl", hash = "sha256:9b6f711b25e01931f1c61ce0115245a23cdc8b80bf8539ac0363bdcf27d649b6"}, + {file = "pycryptodome-3.10.1-cp35-abi3-win_amd64.whl", hash = "sha256:7fd519b89585abf57bf47d90166903ec7b43af4fe23c92273ea09e6336af5c07"}, + {file = "pycryptodome-3.10.1-pp27-pypy_73-macosx_10_9_x86_64.whl", hash = "sha256:09c1555a3fa450e7eaca41ea11cd00afe7c91fef52353488e65663777d8524e0"}, + {file = "pycryptodome-3.10.1-pp27-pypy_73-manylinux1_x86_64.whl", hash = "sha256:758949ca62690b1540dfb24ad773c6da9cd0e425189e83e39c038bbd52b8e438"}, + {file = "pycryptodome-3.10.1-pp27-pypy_73-manylinux2010_x86_64.whl", hash = "sha256:e3bf558c6aeb49afa9f0c06cee7fb5947ee5a1ff3bd794b653d39926b49077fa"}, + {file = "pycryptodome-3.10.1-pp27-pypy_73-win32.whl", hash = "sha256:f977cdf725b20f6b8229b0c87acb98c7717e742ef9f46b113985303ae12a99da"}, + {file = "pycryptodome-3.10.1-pp36-pypy36_pp73-macosx_10_9_x86_64.whl", hash = "sha256:6d2df5223b12437e644ce0a3be7809471ffa71de44ccd28b02180401982594a6"}, + {file = "pycryptodome-3.10.1-pp36-pypy36_pp73-manylinux1_x86_64.whl", hash = "sha256:98213ac2b18dc1969a47bc65a79a8fca02a414249d0c8635abb081c7f38c91b6"}, + {file = "pycryptodome-3.10.1-pp36-pypy36_pp73-manylinux2010_x86_64.whl", hash = "sha256:12222a5edc9ca4a29de15fbd5339099c4c26c56e13c2ceddf0b920794f26165d"}, + {file = "pycryptodome-3.10.1-pp36-pypy36_pp73-win32.whl", hash = "sha256:6bbf7fee7b7948b29d7e71fcacf48bac0c57fb41332007061a933f2d996f9713"}, + {file = "pycryptodome-3.10.1.tar.gz", hash = "sha256:3e2e3a06580c5f190df843cdb90ea28d61099cf4924334d5297a995de68e4673"}, +] pydantic = [ {file = "pydantic-1.8.2-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:05ddfd37c1720c392f4e0d43c484217b7521558302e7069ce8d318438d297739"}, {file = "pydantic-1.8.2-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:a7c6002203fe2c5a1b5cbb141bb85060cbff88c2d78eccbc72d97eb7022c43e4"}, diff --git a/packages/nonebot-adapter-feishu/pyproject.toml b/packages/nonebot-adapter-feishu/pyproject.toml index 1818527c..73e731bb 100644 --- a/packages/nonebot-adapter-feishu/pyproject.toml +++ b/packages/nonebot-adapter-feishu/pyproject.toml @@ -26,6 +26,7 @@ exclude = ["nonebot/__init__.py", "nonebot/adapters/__init__.py"] python = "^3.7.3" httpx = "^0.17.0" nonebot2 = "^2.0.0-alpha.13" +pycryptodome = "^3.10.1" [tool.poetry.dev-dependencies] nonebot2 = { path = "../../", develop = true } From 48816308f8c4417a2306ed1fa006473a1332714c Mon Sep 17 00:00:00 2001 From: StarHeartHunt Date: Sat, 3 Jul 2021 13:55:20 +0800 Subject: [PATCH 04/64] =?UTF-8?q?=E2=9C=A8=20add=20encryption=20support?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../nonebot/adapters/feishu/utils.py | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 packages/nonebot-adapter-feishu/nonebot/adapters/feishu/utils.py diff --git a/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/utils.py b/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/utils.py new file mode 100644 index 00000000..34750255 --- /dev/null +++ b/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/utils.py @@ -0,0 +1,33 @@ +import base64 +import hashlib + +from Crypto.Cipher import AES +from nonebot.utils import logger_wrapper + +log = logger_wrapper("FEISHU") + +class AESCipher(object): + + def __init__(self, key): + self.block_size = AES.block_size + self.key = hashlib.sha256(AESCipher.str_to_bytes(key)).digest() + + @staticmethod + def str_to_bytes(data): + u_type = type(b"".decode('utf8')) + if isinstance(data, u_type): + return data.encode('utf8') + return data + + @staticmethod + def _unpad(s): + return s[:-ord(s[len(s) - 1:])] + + def decrypt(self, enc): + iv = enc[:AES.block_size] + cipher = AES.new(self.key, AES.MODE_CBC, iv) + return self._unpad(cipher.decrypt(enc[AES.block_size:])) + + def decrypt_string(self, enc): + enc = base64.b64decode(enc) + return self.decrypt(enc).decode('utf8') From 4afd681ca9e7a9966a5c2cf456458e7a807482e0 Mon Sep 17 00:00:00 2001 From: StarHeartHunt Date: Sat, 3 Jul 2021 13:56:47 +0800 Subject: [PATCH 05/64] =?UTF-8?q?=F0=9F=8F=B7=EF=B8=8F=20add=20segment=20s?= =?UTF-8?q?chema?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../nonebot/adapters/feishu/message.py | 129 +++++++++++++++++- 1 file changed, 126 insertions(+), 3 deletions(-) diff --git a/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/message.py b/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/message.py index d589e554..a0722f30 100644 --- a/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/message.py +++ b/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/message.py @@ -1,11 +1,23 @@ -from typing import Union, Mapping, Iterable +from typing import Type, Union, Mapping, Iterable + from nonebot.adapters import Message as BaseMessage, MessageSegment as BaseMessageSegment +from nonebot.typing import overrides class MessageSegment(BaseMessageSegment): + """ + 飞书 协议 MessageSegment 适配。具体方法参考协议消息段类型或源码。 + """ + + @classmethod + @overrides(BaseMessageSegment) + def get_message_class(cls) -> Type["Message"]: + return Message def __str__(self) -> str: - raise NotImplementedError + if self.type == "text" or self.type == "hongbao": + return str(self.data["text"]) + return "" def __add__(self, other) -> "Message": return Message(self) + other @@ -14,13 +26,124 @@ class MessageSegment(BaseMessageSegment): return Message(other) + self def is_text(self) -> bool: - raise NotImplementedError + return self.type == "text" + + @staticmethod + def text(text: str) -> "MessageSegment": + return MessageSegment("text", {"text": text}) + + @staticmethod + def post(title: str, content: list) -> "MessageSegment": + return MessageSegment("post", {"title": title, "content": content}) + + @staticmethod + def image(image_key: str) -> "MessageSegment": + return MessageSegment("image", {"image_key": image_key}) + + @staticmethod + def file(file_key: str, file_name: str) -> "MessageSegment": + return MessageSegment("file", { + "file_key": file_key, + "file_name": file_name + }) + + @staticmethod + def audio(file_key: str, duration: int) -> "MessageSegment": + return MessageSegment("audio", { + "file_key": file_key, + "duration": duration + }) + + @staticmethod + def media(file_key: str, image_key: str, file_name: str, + duration: int) -> "MessageSegment": + return MessageSegment( + "media", { + "file_key": file_key, + "image_key": image_key, + "file_name": file_name, + "duration": duration + }) + + @staticmethod + def sticker(file_key) -> "MessageSegment": + return MessageSegment("sticker", {"file_key": file_key}) + + @staticmethod + def interactive(title: str, elements: list) -> "MessageSegment": + return MessageSegment("interactive", { + "title": title, + "elements": elements + }) + + @staticmethod + def hongbao(text: str) -> "MessageSegment": + return MessageSegment("hongbao", {"text": text}) + + @staticmethod + def share_calendar_event(summary: str, start_time: str, + end_time: str) -> "MessageSegment": + return MessageSegment("share_calendar_event", { + "summary": summary, + "start_time": start_time, + "end_time": end_time + }) + + @staticmethod + def share_chat(chat_id: str) -> "MessageSegment": + return MessageSegment("share_chat", {"chat_id": chat_id}) + + @staticmethod + def share_user(user_id: str) -> "MessageSegment": + return MessageSegment("share_user", {"user_id": user_id}) + + @staticmethod + def system(template: str, from_user: list, + to_chatters: list) -> "MessageSegment": + return MessageSegment( + "system", { + "template": template, + "from_user": from_user, + "to_chatters": to_chatters + }) + + @staticmethod + def location(name: str, longitude: str, latitude: str) -> "MessageSegment": + return MessageSegment("location", { + "name": name, + "longitude": longitude, + "latitude": latitude + }) + + @staticmethod + def video_chat(topic: str, start_time: str) -> "MessageSegment": + return MessageSegment("video_chat", { + "topic": topic, + "start_time": start_time, + }) class Message(BaseMessage): + """ + 飞书 协议 Message 适配。 + """ + + @classmethod + @overrides(BaseMessage) + def get_segment_class(cls) -> Type[MessageSegment]: + return MessageSegment @staticmethod def _construct( msg: Union[str, Mapping, Iterable[Mapping]]) -> Iterable[MessageSegment]: + if isinstance(msg, Mapping): + yield MessageSegment(msg["type"], msg.get("data") or {}) + elif isinstance(msg, str): + yield MessageSegment.text(msg) + elif isinstance(msg, Iterable): + for seg in msg: + yield MessageSegment(seg["type"], seg.get("data") or {}) + + def _produce(self) -> dict: raise NotImplementedError From 24144a9474c9019c73372cf0e0dffefcfaecf2fc Mon Sep 17 00:00:00 2001 From: StarHeartHunt Date: Sat, 3 Jul 2021 13:57:45 +0800 Subject: [PATCH 06/64] =?UTF-8?q?=E2=9C=A8=20implement=20check=5Fpermissio?= =?UTF-8?q?n=20procedure?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../nonebot/adapters/feishu/bot.py | 73 +++++++++++++++++-- 1 file changed, 65 insertions(+), 8 deletions(-) diff --git a/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/bot.py b/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/bot.py index 60bebdc7..06987111 100644 --- a/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/bot.py +++ b/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/bot.py @@ -1,15 +1,20 @@ -from typing import Any, Union, Optional, TYPE_CHECKING +import json + +from typing import Any, Tuple, Union, Optional, TYPE_CHECKING from nonebot.log import logger +from nonebot.typing import overrides from nonebot.message import handle_event from nonebot.adapters import Bot as BaseBot -from nonebot.exception import RequestDenied +from nonebot.drivers import Driver, HTTPRequest, HTTPResponse +from .config import Config as FeishuConfig from .event import Event from .message import Message, MessageSegment +from .utils import log, AESCipher if TYPE_CHECKING: - from nonebot.drivers import Driver + from nonebot.config import Config class Bot(BaseBot): @@ -22,12 +27,64 @@ class Bot(BaseBot): return "feishu" @classmethod - async def check_permission(cls, driver: "Driver", connection_type: str, - headers: dict, body: Optional[dict]) -> str: - # raise RequestDenied(401, "reason") - return "bot id" + def register(cls, driver: Driver, config: "Config"): + super().register(driver, config) + cls.feishu_config = FeishuConfig(**config.dict()) - async def handle_message(self, message: dict): + @classmethod + @overrides(BaseBot) + async def check_permission( + cls, driver: Driver, request: HTTPRequest + ) -> Tuple[Optional[str], Optional[HTTPResponse]]: + if not isinstance(request, HTTPRequest): + log("WARNING", + "Unsupported connection type, available type: `http`") + return None, HTTPResponse( + 405, b"Unsupported connection type, available type: `http`") + + encrypt_key = cls.feishu_config.encrypt_key + if encrypt_key: + encrypted = json.loads(request.body)["encrypt"] + decrypted = AESCipher(encrypt_key).decrypt_string(encrypted) + data = json.loads(decrypted) + else: + data = json.loads(request.body) + + challenge = data.get("challenge") + if challenge: + return None, HTTPResponse( + 200, + json.dumps({ + "challenge": challenge + }).encode()) + + headers = data.get("header") + if headers: + token = headers.get("token") + app_id = headers.get("app_id") + else: + log("WARNING", "Missing `header` in POST body") + return None, HTTPResponse(400, b"Missing `header` in POST body") + + if not token: + log("WARNING", "Missing `verification token` in POST body") + return None, HTTPResponse( + 400, b"Missing `verification token` in POST body") + else: + if token != cls.feishu_config.verification_token: + log("WARNING", "Verification token check failed") + return None, HTTPResponse(403, + b"Verification token check failed") + + return app_id, HTTPResponse(200, b'') + + async def handle_message(self, message: bytes): + """ + :说明: + + 处理事件并转换为 `Event <#class-event>`_ + """ + data = json.loads(message) try: event = Event.parse_obj(message) await handle_event(self, event) From 406511496d184bcad812dcbddd7ee15b3328a855 Mon Sep 17 00:00:00 2001 From: StarHeartHunt Date: Sat, 3 Jul 2021 13:58:26 +0800 Subject: [PATCH 07/64] =?UTF-8?q?=F0=9F=8F=B7=EF=B8=8F=20add=20events?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../nonebot/adapters/feishu/__init__.py | 1 + .../nonebot/adapters/feishu/event.py | 138 ++++++++++++++++-- 2 files changed, 130 insertions(+), 9 deletions(-) diff --git a/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/__init__.py b/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/__init__.py index e0cfb5cb..1569ff02 100644 --- a/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/__init__.py +++ b/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/__init__.py @@ -1,3 +1,4 @@ from .bot import Bot +from .event import * from .message import Message, MessageSegment from .event import Event diff --git a/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/event.py b/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/event.py index ae55076e..5d7f6dfe 100644 --- a/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/event.py +++ b/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/event.py @@ -1,31 +1,151 @@ -from pydantic import BaseModel -from nonebot.adapters import Event as BaseEvent +from typing import List, Literal +from pydantic import BaseModel, root_validator -from .message import Message +from nonebot.adapters import Event as BaseEvent +from nonebot.typing import overrides + +from .message import Message, MessageSegment class Event(BaseEvent): + """ + 飞书协议事件。各事件字段参考 `飞书文档`_ + .. _飞书事件列表文档: + https://open.feishu.cn/document/ukTMukTMukTM/uYDNxYjL2QTM24iN0EjN/event-list + """ + app_id: int + event_type: str + + @overrides(BaseEvent) def get_type(self) -> str: - raise NotImplementedError + return self.event_type + @overrides(BaseEvent) def get_event_name(self) -> str: - raise NotImplementedError + return self.event_type + @overrides(BaseEvent) def get_event_description(self) -> str: return str(self.dict()) + @overrides(BaseEvent) def get_message(self) -> Message: - raise NotImplementedError + raise ValueError("Event has no message!") + @overrides(BaseEvent) def get_plaintext(self) -> str: - raise NotImplementedError + raise ValueError("Event has no plaintext!") + @overrides(BaseEvent) def get_user_id(self) -> str: - raise NotImplementedError + raise ValueError("Event has no user_id!") + @overrides(BaseEvent) def get_session_id(self) -> str: - raise NotImplementedError + raise ValueError("Event has no session_id!") + @overrides(BaseEvent) def is_tome(self) -> bool: return False + + +class UserId(BaseModel): + union_id: str + user_id: str + open_id: str + + +class Sender(BaseModel): + sender_id: UserId + sender_type: str + tenant_key: str + + +class Mention(BaseModel): + key: str + id: UserId + name: str + tenant_key: str + + +class MessageBody(BaseModel): + message_id: str + root_id: str + parent_id: str + create_time: str + chat_id: str + chat_type: str + message_type: str + content: Message + mentions: List[Mention] + + plaintext: str + + @root_validator(pre=True) + def gen_message(cls, values: dict): + content = [] + for piece in values["content"]: + for segment in piece: + content.append( + MessageSegment(segment["tag"], segment.pop('name', segment))) + + values["content"] = Message(content) + return values + + @root_validator + def gen_plaintext(cls, values: dict): + values["plaintext"] = str(values["content"]) + return values + + +class MessageEvent(Event): + sender: Sender + message: MessageBody + + @overrides(Event) + def get_type(self) -> Literal["message", "notice", "meta_event"]: + return "message" + + @overrides(Event) + def get_event_name(self) -> str: + return f"{self.get_type()}.{super().get_type()}" + + @overrides(Event) + def get_event_description(self) -> str: + return ( + f"Message[{super().get_type()}]" + f" {self.message.message_id} from {self.sender.sender_id.user_id}" + f" {self.message.content}") + + @overrides(Event) + def get_message(self) -> Message: + return self.message.content + + @overrides(Event) + def get_plaintext(self) -> str: + return self.message.plaintext + + @overrides(Event) + def get_user_id(self) -> str: + return self.sender.sender_id.user_id + + @overrides(Event) + def get_session_id(self) -> str: + return self.sender.sender_id.user_id + + +class PrivateMessageEvent(MessageEvent): + ... + + +class GroupMessageEvent(MessageEvent): + ... + + +class NoticeEvent(Event): + ... + + +class MetaEvent(Event): + ... From b1fa2bbb8061d4f784b4a685638bcccc095940e4 Mon Sep 17 00:00:00 2001 From: StarHeartHunt Date: Sat, 3 Jul 2021 13:58:46 +0800 Subject: [PATCH 08/64] =?UTF-8?q?=F0=9F=94=A7=20add=20configs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../nonebot/adapters/feishu/config.py | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 packages/nonebot-adapter-feishu/nonebot/adapters/feishu/config.py diff --git a/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/config.py b/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/config.py new file mode 100644 index 00000000..6de9ff97 --- /dev/null +++ b/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/config.py @@ -0,0 +1,28 @@ +from typing import Optional + +from pydantic import Field, BaseModel + + +class Config(BaseModel): + """ + 钉钉配置类 + + :配置项: + + - ``app_id`` / ``feishu_app_id``: 飞书开放平台后台“凭证与基础信息”处给出的 App ID + - ``app_secret`` / ``feishu_app_secret``: 飞书开放平台后台“凭证与基础信息”处给出的 App Secret + - ``encrypt_key`` / ``feishu_encrypt_key``: 飞书开放平台后台“事件订阅”处设置的 Encrypt Key + - ``verification_token`` / ``feishu_verification_token``: 飞书开放平台后台“事件订阅”处设置的 Verification Token + - ``tenant_access_token`` / ``feishu_tenant_access_token``: 请求飞书 API 后返回的租户密钥 + """ + app_id: Optional[str] = Field(default=None, alias="feishu_app_id") + app_secret: Optional[str] = Field(default=None, alias="feishu_app_secret") + encrypt_key: Optional[str] = Field(default=None, alias="feishu_encrypt_key") + verification_token: Optional[str] = Field(default=None, + alias="feishu_verification_token") + tenant_access_token: Optional[str] = Field( + default=None, alias="feishu_tenant_access_token") + + class Config: + extra = "ignore" + allow_population_by_field_name = True From f80ba8451f5d69071f9af1bfa33ce1b6466d078a Mon Sep 17 00:00:00 2001 From: yanyongyu Date: Sat, 3 Jul 2021 11:50:56 +0800 Subject: [PATCH 09/64] :art: fix linter error --- nonebot/drivers/fastapi.py | 5 +++-- nonebot/log.py | 10 +++++----- nonebot/permission.py | 4 ++-- 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/nonebot/drivers/fastapi.py b/nonebot/drivers/fastapi.py index c9de4107..9e013a5f 100644 --- a/nonebot/drivers/fastapi.py +++ b/nonebot/drivers/fastapi.py @@ -195,8 +195,9 @@ class Driver(ReverseDriver): self, http_request) if not x_self_id: - raise HTTPException(response and response.status or 401, - response.body) + raise HTTPException( + response and response.status or 401, response and + response.body and response.body.decode("utf-8")) if x_self_id in self._clients: logger.warning("There's already a reverse websocket connection," diff --git a/nonebot/log.py b/nonebot/log.py index 0acb8a20..61fbd8af 100644 --- a/nonebot/log.py +++ b/nonebot/log.py @@ -48,11 +48,11 @@ class Filter: self.level: Union[int, str] = "DEBUG" def __call__(self, record): - module = sys.modules.get(record["name"]) + module_name: str = record["name"] + module = sys.modules.get(module_name) if module: - module_name = getattr(module, "__module_name__", record["name"]) - record["name"] = module_name - record["name"] = record["name"].split(".")[0] + module_name = getattr(module, "__module_name__", module_name) + record["name"] = module_name.split(".")[0] levelno = logger.level(self.level).no if isinstance(self.level, str) else self.level return record["level"].no >= levelno @@ -67,7 +67,7 @@ class LoguruHandler(logging.Handler): level = record.levelno frame, depth = logging.currentframe(), 2 - while frame.f_code.co_filename == logging.__file__: + while frame and frame.f_code.co_filename == logging.__file__: frame = frame.f_back depth += 1 diff --git a/nonebot/permission.py b/nonebot/permission.py index 69134fac..68c34f82 100644 --- a/nonebot/permission.py +++ b/nonebot/permission.py @@ -141,8 +141,8 @@ def USER(*user: str, perm: Optional[Permission] = None): """ async def _user(bot: "Bot", event: "Event") -> bool: - return event.get_session_id() in user and bool(perm) and await perm( - bot, event) + return bool(event.get_session_id() in user and perm and + await perm(bot, event)) return Permission(_user) From 20c3c24a6d7272f72fe561feea50759e707274c7 Mon Sep 17 00:00:00 2001 From: StarHeartHunt Date: Sun, 4 Jul 2021 14:17:59 +0800 Subject: [PATCH 10/64] =?UTF-8?q?=F0=9F=91=B7=20implement=20testcase?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/test_plugins/test_feishu.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/test_plugins/test_feishu.py b/tests/test_plugins/test_feishu.py index fdd2e2fa..3dee1ce1 100644 --- a/tests/test_plugins/test_feishu.py +++ b/tests/test_plugins/test_feishu.py @@ -1,12 +1,12 @@ -from nonebot.adapters.feishu.event import GroupMessageEvent, PrivateMessageEvent +from nonebot.adapters.feishu.event import MessageEvent from nonebot.rule import to_me from nonebot.plugin import on_command from nonebot.adapters.feishu import Bot as FeishuBot, MessageSegment, MessageEvent -helper = on_command("ding_helper", to_me()) +helper = on_command("114514") @helper.handle() async def ding_helper(bot: FeishuBot, event: MessageEvent): - message = MessageSegment.text("114514") + message = MessageSegment.text("1919810") await helper.finish(message) From 7a8f881b0477a1eed2646b6b8642065a44061c8d Mon Sep 17 00:00:00 2001 From: StarHeartHunt Date: Sun, 4 Jul 2021 14:19:10 +0800 Subject: [PATCH 11/64] =?UTF-8?q?=E2=9C=A8=20implement=20event=20parser?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../nonebot/adapters/feishu/bot.py | 62 +++++++++- .../nonebot/adapters/feishu/event.py | 111 ++++++++++++------ 2 files changed, 134 insertions(+), 39 deletions(-) diff --git a/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/bot.py b/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/bot.py index 06987111..8dfeb364 100644 --- a/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/bot.py +++ b/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/bot.py @@ -9,7 +9,7 @@ from nonebot.adapters import Bot as BaseBot from nonebot.drivers import Driver, HTTPRequest, HTTPResponse from .config import Config as FeishuConfig -from .event import Event +from .event import Event, get_event_model from .message import Message, MessageSegment from .utils import log, AESCipher @@ -17,6 +17,48 @@ if TYPE_CHECKING: from nonebot.config import Config +async def _check_reply(bot: "Bot", event: "Event"): + """ + :说明: + + 检查消息中存在的回复,去除并赋值 ``event.reply``, ``event.to_me`` + + :参数: + + * ``bot: Bot``: Bot 对象 + * ``event: Event``: Event 对象 + """ + ... + + +def _check_at_me(bot: "Bot", event: "Event"): + """ + :说明: + + 检查消息开头或结尾是否存在 @机器人,去除并赋值 ``event.to_me`` + + :参数: + + * ``bot: Bot``: Bot 对象 + * ``event: Event``: Event 对象 + """ + ... + + +def _check_nickname(bot: "Bot", event: "Event"): + """ + :说明: + + 检查消息开头是否存在,去除并赋值 ``event.to_me`` + + :参数: + + * ``bot: Bot``: Bot 对象 + * ``event: Event``: Event 对象 + """ + ... + + class Bot(BaseBot): """ 飞书 协议 Bot 适配。继承属性参考 `BaseBot <./#class-basebot>`_ 。 @@ -52,7 +94,7 @@ class Bot(BaseBot): challenge = data.get("challenge") if challenge: - return None, HTTPResponse( + return data.get("token"), HTTPResponse( 200, json.dumps({ "challenge": challenge @@ -85,8 +127,22 @@ class Bot(BaseBot): 处理事件并转换为 `Event <#class-event>`_ """ data = json.loads(message) + print(data) + if data.get("type") == "url_verification": + return + try: - event = Event.parse_obj(message) + header = data["header"] + event_type = header["event_type"] + models = get_event_model(event_type) + for model in models: + try: + event = model.parse_obj(data) + break + except Exception as e: + log("DEBUG", "Event Parser Error", e) + else: + event = Event.parse_obj(data) await handle_event(self, event) except Exception as e: logger.opt(colors=True, exception=e).error( diff --git a/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/event.py b/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/event.py index 5d7f6dfe..a1743a58 100644 --- a/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/event.py +++ b/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/event.py @@ -1,5 +1,10 @@ -from typing import List, Literal -from pydantic import BaseModel, root_validator +import inspect +import json +from nonebot import message + +from typing import Any, List, Literal, Optional, Type, Union +from pygtrie import StringTrie +from pydantic import BaseModel, root_validator, Field from nonebot.adapters import Event as BaseEvent from nonebot.typing import overrides @@ -7,6 +12,15 @@ from nonebot.typing import overrides from .message import Message, MessageSegment +class EventHeader(BaseModel): + event_id: str + event_type: str + create_time: str + token: str + app_id: str + tenant_key: str + + class Event(BaseEvent): """ 飞书协议事件。各事件字段参考 `飞书文档`_ @@ -14,16 +28,18 @@ class Event(BaseEvent): .. _飞书事件列表文档: https://open.feishu.cn/document/ukTMukTMukTM/uYDNxYjL2QTM24iN0EjN/event-list """ - app_id: int - event_type: str + __event__ = "" + schema_: str = Field("", alias='schema') + header: EventHeader + event: Any @overrides(BaseEvent) def get_type(self) -> str: - return self.event_type + return self.header.event_type @overrides(BaseEvent) def get_event_name(self) -> str: - return self.event_type + return self.header.event_type @overrides(BaseEvent) def get_event_description(self) -> str: @@ -69,39 +85,31 @@ class Mention(BaseModel): tenant_key: str -class MessageBody(BaseModel): +class EventMessage(BaseModel): message_id: str - root_id: str - parent_id: str + root_id: Optional[str] + parent_id: Optional[str] create_time: str chat_id: str chat_type: str message_type: str content: Message - mentions: List[Mention] - - plaintext: str + mentions: Optional[List[Mention]] @root_validator(pre=True) - def gen_message(cls, values: dict): - content = [] - for piece in values["content"]: - for segment in piece: - content.append( - MessageSegment(segment["tag"], segment.pop('name', segment))) - - values["content"] = Message(content) + def parse_message(cls, values: dict): + values["content"] = json.loads(values["content"]) return values - @root_validator - def gen_plaintext(cls, values: dict): - values["plaintext"] = str(values["content"]) - return values + +class MessageEventDetail(BaseModel): + sender: Sender + message: EventMessage class MessageEvent(Event): - sender: Sender - message: MessageBody + __event__ = "im.message.receive_v1" + event: MessageEventDetail @overrides(Event) def get_type(self) -> Literal["message", "notice", "meta_event"]: @@ -115,32 +123,40 @@ class MessageEvent(Event): def get_event_description(self) -> str: return ( f"Message[{super().get_type()}]" - f" {self.message.message_id} from {self.sender.sender_id.user_id}" - f" {self.message.content}") + f" {self.event.message.message_id} from {self.event.sender.sender_id.user_id}" + f" {self.event.message.content}") @overrides(Event) def get_message(self) -> Message: - return self.message.content + return self.event.message.content @overrides(Event) def get_plaintext(self) -> str: - return self.message.plaintext + return str(self.event.message.content) @overrides(Event) def get_user_id(self) -> str: - return self.sender.sender_id.user_id + return self.event.sender.sender_id.user_id @overrides(Event) def get_session_id(self) -> str: - return self.sender.sender_id.user_id + return self.event.sender.sender_id.user_id -class PrivateMessageEvent(MessageEvent): - ... +class MessageReader(BaseModel): + reader_id: UserId + read_time: str + tenant_key: str -class GroupMessageEvent(MessageEvent): - ... +class MessageReadEventDetail(BaseModel): + reader: MessageReader + message_id_list: List[str] + + +class MessageReadEvent(Event): + __event__ = "im.message.message_read_v1" + event: MessageReadEventDetail class NoticeEvent(Event): @@ -149,3 +165,26 @@ class NoticeEvent(Event): class MetaEvent(Event): ... + + +_t = StringTrie(separator=".") + +# define `model` first to avoid globals changing while `for` +model = None +for model in globals().values(): + if not inspect.isclass(model) or not issubclass(model, Event): + continue + _t["." + model.__event__] = model + + +def get_event_model(event_name) -> List[Type[Event]]: + """ + :说明: + + 根据事件名获取对应 ``Event Model`` 及 ``FallBack Event Model`` 列表 + + :返回: + + - ``List[Type[Event]]`` + """ + return [model.value for model in _t.prefixes("." + event_name)][::-1] From f9f1e332624c6b60c67b3e04592820b393a21974 Mon Sep 17 00:00:00 2001 From: StarHeartHunt Date: Sun, 4 Jul 2021 14:19:36 +0800 Subject: [PATCH 12/64] =?UTF-8?q?=E2=9C=A8=20implement=20message=20constru?= =?UTF-8?q?ctor?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../nonebot/adapters/feishu/message.py | 50 ++++++++++++++++--- 1 file changed, 44 insertions(+), 6 deletions(-) diff --git a/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/message.py b/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/message.py index a0722f30..8a40ad1e 100644 --- a/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/message.py +++ b/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/message.py @@ -1,10 +1,13 @@ -from typing import Type, Union, Mapping, Iterable +import itertools +import json + +from typing import Any, Tuple, Type, Union, Mapping, Iterable from nonebot.adapters import Message as BaseMessage, MessageSegment as BaseMessageSegment from nonebot.typing import overrides -class MessageSegment(BaseMessageSegment): +class MessageSegment(BaseMessageSegment["Message"]): """ 飞书 协议 MessageSegment 适配。具体方法参考协议消息段类型或源码。 """ @@ -19,12 +22,17 @@ class MessageSegment(BaseMessageSegment): return str(self.data["text"]) return "" + @overrides(BaseMessageSegment) def __add__(self, other) -> "Message": - return Message(self) + other + return Message(self) + (MessageSegment.text(other) if isinstance( + other, str) else other) + @overrides(BaseMessageSegment) def __radd__(self, other) -> "Message": - return Message(other) + self + return (MessageSegment.text(other) + if isinstance(other, str) else Message(other)) + self + @overrides(BaseMessageSegment) def is_text(self) -> bool: return self.type == "text" @@ -123,7 +131,7 @@ class MessageSegment(BaseMessageSegment): }) -class Message(BaseMessage): +class Message(BaseMessage[MessageSegment]): """ 飞书 协议 Message 适配。 """ @@ -133,12 +141,38 @@ class Message(BaseMessage): def get_segment_class(cls) -> Type[MessageSegment]: return MessageSegment + @overrides(BaseMessage) + def __add__(self, other: Union[str, Mapping, + Iterable[Mapping]]) -> "Message": + return super(Message, self).__add__( + MessageSegment.text(other) if isinstance(other, str) else other) + + @overrides(BaseMessage) + def __radd__(self, other: Union[str, Mapping, + Iterable[Mapping]]) -> "Message": + return super(Message, self).__radd__( + MessageSegment.text(other) if isinstance(other, str) else other) + @staticmethod + @overrides(BaseMessage) def _construct( msg: Union[str, Mapping, Iterable[Mapping]]) -> Iterable[MessageSegment]: if isinstance(msg, Mapping): - yield MessageSegment(msg["type"], msg.get("data") or {}) + + def _iter_message(msg: Mapping) -> Iterable[Tuple[str, dict]]: + pure_text: str = msg.get("text", "") + content: dict = msg.get("content", {}) + if pure_text and not content: + yield "text", {"text": pure_text} + elif content and not pure_text: + for element in list(itertools.chain(*content)): + tag = element.pop("tag") + yield tag, element + + for type_, data in _iter_message(msg): + yield MessageSegment(type_, data) + elif isinstance(msg, str): yield MessageSegment.text(msg) elif isinstance(msg, Iterable): @@ -147,3 +181,7 @@ class Message(BaseMessage): def _produce(self) -> dict: raise NotImplementedError + + @overrides(BaseMessage) + def extract_plain_text(self) -> str: + return "".join(seg.data["text"] for seg in self if seg.is_text()) From cc11e2e885a1c7de1db590c9e0598a8c8b6b870d Mon Sep 17 00:00:00 2001 From: StarHeartHunt Date: Sun, 4 Jul 2021 14:49:24 +0800 Subject: [PATCH 13/64] =?UTF-8?q?=F0=9F=94=A7=20add=20config=20`api=5Fendp?= =?UTF-8?q?oint`?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../nonebot-adapter-feishu/nonebot/adapters/feishu/config.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/config.py b/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/config.py index 6de9ff97..edb6e185 100644 --- a/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/config.py +++ b/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/config.py @@ -9,12 +9,16 @@ class Config(BaseModel): :配置项: + - ``api_endpoint`` / ``feishu_api_endpoint``: 飞书开放平台 API Endpoint - ``app_id`` / ``feishu_app_id``: 飞书开放平台后台“凭证与基础信息”处给出的 App ID - ``app_secret`` / ``feishu_app_secret``: 飞书开放平台后台“凭证与基础信息”处给出的 App Secret - ``encrypt_key`` / ``feishu_encrypt_key``: 飞书开放平台后台“事件订阅”处设置的 Encrypt Key - ``verification_token`` / ``feishu_verification_token``: 飞书开放平台后台“事件订阅”处设置的 Verification Token - ``tenant_access_token`` / ``feishu_tenant_access_token``: 请求飞书 API 后返回的租户密钥 """ + api_endpoint: Optional[str] = Field( + default="https://open.feishu.cn/open-apis/", + alias="feishu_api_endpoint") app_id: Optional[str] = Field(default=None, alias="feishu_app_id") app_secret: Optional[str] = Field(default=None, alias="feishu_app_secret") encrypt_key: Optional[str] = Field(default=None, alias="feishu_encrypt_key") From 136b13b4cc4863aed57589da8d024c71b0f11cd7 Mon Sep 17 00:00:00 2001 From: StarHeartHunt Date: Sun, 4 Jul 2021 14:50:01 +0800 Subject: [PATCH 14/64] =?UTF-8?q?=E2=9A=A1=EF=B8=8F=20remove=20unused=20im?= =?UTF-8?q?port?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../nonebot-adapter-feishu/nonebot/adapters/feishu/event.py | 5 ++--- .../nonebot/adapters/feishu/message.py | 3 +-- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/event.py b/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/event.py index a1743a58..eaf6524e 100644 --- a/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/event.py +++ b/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/event.py @@ -1,15 +1,14 @@ import inspect import json -from nonebot import message -from typing import Any, List, Literal, Optional, Type, Union +from typing import Any, List, Literal, Optional, Type from pygtrie import StringTrie from pydantic import BaseModel, root_validator, Field from nonebot.adapters import Event as BaseEvent from nonebot.typing import overrides -from .message import Message, MessageSegment +from .message import Message class EventHeader(BaseModel): diff --git a/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/message.py b/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/message.py index 8a40ad1e..a5b466c1 100644 --- a/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/message.py +++ b/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/message.py @@ -1,7 +1,6 @@ import itertools -import json -from typing import Any, Tuple, Type, Union, Mapping, Iterable +from typing import Tuple, Type, Union, Mapping, Iterable from nonebot.adapters import Message as BaseMessage, MessageSegment as BaseMessageSegment from nonebot.typing import overrides From 866149902cc9b512b651e7e536fd9cb2bba07053 Mon Sep 17 00:00:00 2001 From: StarHeartHunt Date: Sun, 4 Jul 2021 14:50:37 +0800 Subject: [PATCH 15/64] =?UTF-8?q?=E2=9C=85=20update=20testcase?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/test_plugins/test_feishu.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_plugins/test_feishu.py b/tests/test_plugins/test_feishu.py index 3dee1ce1..fc928da5 100644 --- a/tests/test_plugins/test_feishu.py +++ b/tests/test_plugins/test_feishu.py @@ -7,6 +7,6 @@ helper = on_command("114514") @helper.handle() -async def ding_helper(bot: FeishuBot, event: MessageEvent): +async def feishu_helper(bot: FeishuBot, event: MessageEvent): message = MessageSegment.text("1919810") await helper.finish(message) From 1042759d6032fa00e5301354f6f5110e94e54422 Mon Sep 17 00:00:00 2001 From: StarHeartHunt Date: Sun, 4 Jul 2021 17:19:20 +0800 Subject: [PATCH 16/64] =?UTF-8?q?=E2=9A=A1=EF=B8=8F=20make=20`api=5Fendpoi?= =?UTF-8?q?nt`=20hard=5Fencoded?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../nonebot-adapter-feishu/nonebot/adapters/feishu/config.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/config.py b/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/config.py index edb6e185..6de9ff97 100644 --- a/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/config.py +++ b/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/config.py @@ -9,16 +9,12 @@ class Config(BaseModel): :配置项: - - ``api_endpoint`` / ``feishu_api_endpoint``: 飞书开放平台 API Endpoint - ``app_id`` / ``feishu_app_id``: 飞书开放平台后台“凭证与基础信息”处给出的 App ID - ``app_secret`` / ``feishu_app_secret``: 飞书开放平台后台“凭证与基础信息”处给出的 App Secret - ``encrypt_key`` / ``feishu_encrypt_key``: 飞书开放平台后台“事件订阅”处设置的 Encrypt Key - ``verification_token`` / ``feishu_verification_token``: 飞书开放平台后台“事件订阅”处设置的 Verification Token - ``tenant_access_token`` / ``feishu_tenant_access_token``: 请求飞书 API 后返回的租户密钥 """ - api_endpoint: Optional[str] = Field( - default="https://open.feishu.cn/open-apis/", - alias="feishu_api_endpoint") app_id: Optional[str] = Field(default=None, alias="feishu_app_id") app_secret: Optional[str] = Field(default=None, alias="feishu_app_secret") encrypt_key: Optional[str] = Field(default=None, alias="feishu_encrypt_key") From 150ef30b739483253ebd271aa8cfea1f50285eee Mon Sep 17 00:00:00 2001 From: StarHeartHunt Date: Sun, 4 Jul 2021 17:19:47 +0800 Subject: [PATCH 17/64] =?UTF-8?q?=E2=9C=A8=20add=20custom=20exception?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../nonebot/adapters/feishu/exception.py | 61 +++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 packages/nonebot-adapter-feishu/nonebot/adapters/feishu/exception.py diff --git a/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/exception.py b/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/exception.py new file mode 100644 index 00000000..791000af --- /dev/null +++ b/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/exception.py @@ -0,0 +1,61 @@ +from typing import Optional + +from nonebot.exception import (AdapterException, ActionFailed as + BaseActionFailed, NetworkError as + BaseNetworkError, ApiNotAvailable as + BaseApiNotAvailable) + + +class FeishuAdapterException(AdapterException): + + def __init__(self): + super().__init__("feishu") + + +class ActionFailed(BaseActionFailed, FeishuAdapterException): + """ + :说明: + + API 请求返回错误信息。 + + :参数: + + * ``retcode: Optional[int]``: 错误码 + """ + + def __init__(self, **kwargs): + super().__init__() + self.info = kwargs + + def __repr__(self): + return f"" + + def __str__(self): + return self.__repr__() + + +class NetworkError(BaseNetworkError, FeishuAdapterException): + """ + :说明: + + 网络错误。 + + :参数: + + * ``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, FeishuAdapterException): + pass From b6979594a7912f492cdbbfa39dc30cb20a2ccd52 Mon Sep 17 00:00:00 2001 From: StarHeartHunt Date: Mon, 5 Jul 2021 11:07:33 +0800 Subject: [PATCH 18/64] =?UTF-8?q?=E2=9C=A8=20implement=20group=20and=20pri?= =?UTF-8?q?vate=20message?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../nonebot/adapters/feishu/event.py | 38 ++++++++++++++++--- 1 file changed, 33 insertions(+), 5 deletions(-) diff --git a/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/event.py b/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/event.py index eaf6524e..a22a404a 100644 --- a/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/event.py +++ b/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/event.py @@ -1,7 +1,7 @@ import inspect import json -from typing import Any, List, Literal, Optional, Type +from typing import Any, List, Literal, Optional, Type, Union from pygtrie import StringTrie from pydantic import BaseModel, root_validator, Field @@ -101,11 +101,27 @@ class EventMessage(BaseModel): return values +class GroupEventMessage(EventMessage): + chat_type: Literal["group"] + + +class PrivateEventMessage(EventMessage): + chat_type: Literal["p2p"] + + class MessageEventDetail(BaseModel): sender: Sender message: EventMessage +class GroupMessageEventDetail(MessageEventDetail): + message: GroupEventMessage + + +class PrivateMessageEventDetail(MessageEventDetail): + message: PrivateEventMessage + + class MessageEvent(Event): __event__ = "im.message.receive_v1" event: MessageEventDetail @@ -116,13 +132,15 @@ class MessageEvent(Event): @overrides(Event) def get_event_name(self) -> str: - return f"{self.get_type()}.{super().get_type()}" + return f"{self.get_type()}.{self.event.message.chat_type}" @overrides(Event) def get_event_description(self) -> str: + #TODO:换成GroupId return ( f"Message[{super().get_type()}]" - f" {self.event.message.message_id} from {self.event.sender.sender_id.user_id}" + f" {self.event.message.message_id} from {self.get_user_id()}" + f"@[{self.event.message.chat_type}:{self.event.message.chat_id}]" f" {self.event.message.content}") @overrides(Event) @@ -135,11 +153,21 @@ class MessageEvent(Event): @overrides(Event) def get_user_id(self) -> str: - return self.event.sender.sender_id.user_id + return self.event.sender.sender_id.union_id @overrides(Event) def get_session_id(self) -> str: - return self.event.sender.sender_id.user_id + return f"{self.event.message.chat_type}_{self.event.message.chat_id}_{self.get_user_id()}" + + +class GroupMessageEvent(MessageEvent): + __event__ = "im.message.receive_v1.group" + event: GroupMessageEventDetail + + +class PrivateMessageEvent(MessageEvent): + __event__ = "im.message.receive_v1.private" + event: PrivateMessageEventDetail class MessageReader(BaseModel): From 33bd7582becf9f526eceb11a629c84b6fe78578a Mon Sep 17 00:00:00 2001 From: StarHeartHunt Date: Mon, 5 Jul 2021 11:07:51 +0800 Subject: [PATCH 19/64] =?UTF-8?q?=F0=9F=8E=A8=20sort=20import?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../nonebot-adapter-feishu/nonebot/adapters/feishu/config.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/config.py b/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/config.py index 6de9ff97..853f553e 100644 --- a/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/config.py +++ b/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/config.py @@ -1,6 +1,6 @@ from typing import Optional -from pydantic import Field, BaseModel +from pydantic import BaseModel, Field class Config(BaseModel): From 0c39300f4b46051b66b71bcba9481f2821b1da85 Mon Sep 17 00:00:00 2001 From: StarHeartHunt Date: Mon, 5 Jul 2021 11:08:24 +0800 Subject: [PATCH 20/64] =?UTF-8?q?=E2=9C=A8=20add=20`call=5Fapi`=20and=20im?= =?UTF-8?q?plement=20`send`=20func?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../nonebot/adapters/feishu/bot.py | 139 +++++++++++++++++- 1 file changed, 134 insertions(+), 5 deletions(-) diff --git a/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/bot.py b/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/bot.py index 8dfeb364..407a202c 100644 --- a/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/bot.py +++ b/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/bot.py @@ -1,6 +1,7 @@ import json +import httpx -from typing import Any, Tuple, Union, Optional, TYPE_CHECKING +from typing import Any, Dict, Tuple, Union, Optional, TYPE_CHECKING from nonebot.log import logger from nonebot.typing import overrides @@ -9,7 +10,8 @@ from nonebot.adapters import Bot as BaseBot from nonebot.drivers import Driver, HTTPRequest, HTTPResponse from .config import Config as FeishuConfig -from .event import Event, get_event_model +from .event import Event, GroupMessageEvent, get_event_model +from .exception import ActionFailed, ApiNotAvailable, NetworkError from .message import Message, MessageSegment from .utils import log, AESCipher @@ -59,6 +61,30 @@ def _check_nickname(bot: "Bot", event: "Event"): ... +def _handle_api_result(result: Optional[Dict[str, Any]]) -> Any: + """ + :说明: + + 处理 API 请求返回值。 + + :参数: + + * ``result: Optional[Dict[str, Any]]``: API 返回数据 + + :返回: + + - ``Any``: API 调用返回数据 + + :异常: + + - ``ActionFailed``: API 调用失败 + """ + if isinstance(result, dict): + if result.get("code") != 0: + raise ActionFailed(**result) + return result.get("data") + + class Bot(BaseBot): """ 飞书 协议 Bot 适配。继承属性参考 `BaseBot <./#class-basebot>`_ 。 @@ -68,6 +94,10 @@ class Bot(BaseBot): def type(self) -> str: return "feishu" + @property + def api_root(self) -> str: + return "https://open.feishu.cn/open-apis/" + @classmethod def register(cls, driver: Driver, config: "Config"): super().register(driver, config) @@ -134,6 +164,10 @@ class Bot(BaseBot): try: header = data["header"] event_type = header["event_type"] + if data.get("event"): + if data["event"].get("message"): + event_type += f".{data['event']['message']['chat_type']}" + models = get_event_model(event_type) for model in models: try: @@ -149,10 +183,105 @@ class Bot(BaseBot): f"Failed to handle event. Raw: {message}" ) - async def _call_api(self, api: str, **data) -> Any: - raise NotImplementedError + def _construct_url(self, path: str) -> str: + return self.api_root + path + async def _fetch_tenant_access_token(self) -> str: + try: + async with httpx.AsyncClient() as client: + response = await client.post( + self._construct_url( + "auth/v3/tenant_access_token/internal/"), + json={ + "app_id": self.feishu_config.app_id, + "app_secret": self.feishu_config.app_secret + }, + timeout=self.config.api_timeout) + + if 200 <= response.status_code < 300: + result = response.json() + return result["tenant_access_token"] + else: + 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") + + @overrides(BaseBot) + async def _call_api(self, api: str, **data) -> Any: + log("DEBUG", f"Calling API {api}") + if isinstance(self.request, HTTPRequest): + if not self.api_root: + raise ApiNotAvailable + + headers = {} + if self.feishu_config.tenant_access_token is None: + self.feishu_config.tenant_access_token = await self._fetch_tenant_access_token( + ) + headers[ + "Authorization"] = "Bearer " + self.feishu_config.tenant_access_token + + try: + print(data) + async with httpx.AsyncClient(headers=headers) as client: + response = await client.post( + self.api_root + api, + json=data, + timeout=self.config.api_timeout) + + print(response.json()) + if 200 <= response.status_code < 300: + result = response.json() + return _handle_api_result(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") + + @overrides(BaseBot) + async def call_api(self, api: str, **data) -> Any: + """ + :说明: + + 调用 飞书 协议 API + + :参数: + + * ``api: str``: API 名称 + * ``**data: Any``: API 参数 + + :返回: + + - ``Any``: API 调用返回数据 + + :异常: + + - ``NetworkError``: 网络错误 + - ``ActionFailed``: API 调用失败 + """ + return await super().call_api(api, **data) + + @overrides(BaseBot) async def send(self, event: Event, message: Union[str, Message, MessageSegment], **kwargs) -> Any: - raise NotImplementedError + msg = message if isinstance(message, Message) else Message(message) + params = { + "receive_id": + event.event.message.chat_id if isinstance( + event, GroupMessageEvent) else event.get_user_id(), + "content": + str(message), + "msg_type": + "text" if len(message) == 1 else "content" + } + + receive_id_type = 'chat_id' if isinstance( + event, GroupMessageEvent) else 'union_id' + + return await self.call_api( + f"im/v1/messages?receive_id_type={receive_id_type}", **params) From 811e94e186d811f273b296f641f99f6b6f20d36d Mon Sep 17 00:00:00 2001 From: StarHeartHunt Date: Tue, 6 Jul 2021 10:45:00 +0800 Subject: [PATCH 21/64] =?UTF-8?q?=E2=9C=A8=20implement=20message=20target?= =?UTF-8?q?=20guessing?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../nonebot/adapters/feishu/bot.py | 33 +++++++++++-------- 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/bot.py b/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/bot.py index 407a202c..88932aae 100644 --- a/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/bot.py +++ b/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/bot.py @@ -10,7 +10,7 @@ from nonebot.adapters import Bot as BaseBot from nonebot.drivers import Driver, HTTPRequest, HTTPResponse from .config import Config as FeishuConfig -from .event import Event, GroupMessageEvent, get_event_model +from .event import Event, GroupMessageEvent, PrivateMessageEvent, get_event_model from .exception import ActionFailed, ApiNotAvailable, NetworkError from .message import Message, MessageSegment from .utils import log, AESCipher @@ -103,6 +103,7 @@ class Bot(BaseBot): super().register(driver, config) cls.feishu_config = FeishuConfig(**config.dict()) + #TODO:校验schema 要求为2.0 @classmethod @overrides(BaseBot) async def check_permission( @@ -186,6 +187,7 @@ class Bot(BaseBot): def _construct_url(self, path: str) -> str: return self.api_root + path + #TODO:实现token缓存与ttl async def _fetch_tenant_access_token(self) -> str: try: async with httpx.AsyncClient() as client: @@ -270,18 +272,23 @@ class Bot(BaseBot): MessageSegment], **kwargs) -> Any: msg = message if isinstance(message, Message) else Message(message) + + if isinstance(event, GroupMessageEvent): + receive_id, receive_id_type = event.event.message.chat_id, 'chat_id' + elif isinstance(event, PrivateMessageEvent): + receive_id, receive_id_type = event.get_user_id(), 'union_id' + else: + raise ValueError( + "Cannot guess `receive_id` and `receive_id_type` to reply!") params = { - "receive_id": - event.event.message.chat_id if isinstance( - event, GroupMessageEvent) else event.get_user_id(), - "content": - str(message), - "msg_type": - "text" if len(message) == 1 else "content" + "query": { + "receive_id_type": receive_id_type + }, + "body": { + "receive_id": receive_id, + "content": str(message), + "msg_type": "text" if len(message) == 1 else "content" + } } - receive_id_type = 'chat_id' if isinstance( - event, GroupMessageEvent) else 'union_id' - - return await self.call_api( - f"im/v1/messages?receive_id_type={receive_id_type}", **params) + return await self.call_api(f"im/v1/messages", **params) From 322ac98c5083abeb9d166faca1bbc2c0590fd8bb Mon Sep 17 00:00:00 2001 From: StarHeartHunt Date: Tue, 6 Jul 2021 10:57:51 +0800 Subject: [PATCH 22/64] =?UTF-8?q?=F0=9F=90=9B=20validate=20`schema`=20fiel?= =?UTF-8?q?d=20&=20fix=20request=20construct?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../nonebot-adapter-feishu/nonebot/adapters/feishu/bot.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/bot.py b/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/bot.py index 88932aae..a4582477 100644 --- a/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/bot.py +++ b/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/bot.py @@ -131,6 +131,10 @@ class Bot(BaseBot): "challenge": challenge }).encode()) + schema = data.get("schema") + if not schema: + return None, HTTPResponse(400, b"Missing `schema` in POST body, only accept event of version 2.0") + headers = data.get("header") if headers: token = headers.get("token") @@ -230,7 +234,8 @@ class Bot(BaseBot): async with httpx.AsyncClient(headers=headers) as client: response = await client.post( self.api_root + api, - json=data, + json=data["body"], + params=data["query"], timeout=self.config.api_timeout) print(response.json()) From 0ec7cdc3aa95daea5c04359d4dfb0cf4ae9688d1 Mon Sep 17 00:00:00 2001 From: StarHeartHunt Date: Tue, 6 Jul 2021 14:58:00 +0800 Subject: [PATCH 23/64] =?UTF-8?q?=E2=9C=85=20update=20testcase?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/test_plugins/test_feishu.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_plugins/test_feishu.py b/tests/test_plugins/test_feishu.py index fc928da5..eab47900 100644 --- a/tests/test_plugins/test_feishu.py +++ b/tests/test_plugins/test_feishu.py @@ -3,10 +3,10 @@ from nonebot.rule import to_me from nonebot.plugin import on_command from nonebot.adapters.feishu import Bot as FeishuBot, MessageSegment, MessageEvent -helper = on_command("114514") +helper = on_command("say") @helper.handle() async def feishu_helper(bot: FeishuBot, event: MessageEvent): - message = MessageSegment.text("1919810") + message = event.get_plaintext() await helper.finish(message) From 5635c83bfb5ca75c9252d8bea1d6c3eb8430ca99 Mon Sep 17 00:00:00 2001 From: StarHeartHunt Date: Tue, 6 Jul 2021 14:58:38 +0800 Subject: [PATCH 24/64] =?UTF-8?q?=F0=9F=90=9B=20fix=20p2p=20model=20parse?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../nonebot-adapter-feishu/nonebot/adapters/feishu/event.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/event.py b/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/event.py index a22a404a..dd60a8ac 100644 --- a/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/event.py +++ b/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/event.py @@ -166,7 +166,7 @@ class GroupMessageEvent(MessageEvent): class PrivateMessageEvent(MessageEvent): - __event__ = "im.message.receive_v1.private" + __event__ = "im.message.receive_v1.p2p" event: PrivateMessageEventDetail From 603a63a629db636961fd25714a764726dd221cf2 Mon Sep 17 00:00:00 2001 From: StarHeartHunt Date: Tue, 6 Jul 2021 18:01:41 +0800 Subject: [PATCH 25/64] =?UTF-8?q?=E2=9C=A8=20implement=20message=20seriali?= =?UTF-8?q?zer=20&=20deserializer?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../nonebot/adapters/feishu/bot.py | 24 ++++-- .../nonebot/adapters/feishu/event.py | 6 +- .../nonebot/adapters/feishu/message.py | 74 +++++++++++++++---- 3 files changed, 80 insertions(+), 24 deletions(-) diff --git a/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/bot.py b/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/bot.py index a4582477..2535a4d9 100644 --- a/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/bot.py +++ b/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/bot.py @@ -12,7 +12,7 @@ from nonebot.drivers import Driver, HTTPRequest, HTTPResponse from .config import Config as FeishuConfig from .event import Event, GroupMessageEvent, PrivateMessageEvent, get_event_model from .exception import ActionFailed, ApiNotAvailable, NetworkError -from .message import Message, MessageSegment +from .message import Message, MessageSegment, MessageSerializer from .utils import log, AESCipher if TYPE_CHECKING: @@ -103,7 +103,6 @@ class Bot(BaseBot): super().register(driver, config) cls.feishu_config = FeishuConfig(**config.dict()) - #TODO:校验schema 要求为2.0 @classmethod @overrides(BaseBot) async def check_permission( @@ -133,7 +132,10 @@ class Bot(BaseBot): schema = data.get("schema") if not schema: - return None, HTTPResponse(400, b"Missing `schema` in POST body, only accept event of version 2.0") + return None, HTTPResponse( + 400, + b"Missing `schema` in POST body, only accept event of version 2.0" + ) headers = data.get("header") if headers: @@ -162,6 +164,7 @@ class Bot(BaseBot): 处理事件并转换为 `Event <#class-event>`_ """ data = json.loads(message) + print("handle_event start") print(data) if data.get("type") == "url_verification": return @@ -230,6 +233,7 @@ class Bot(BaseBot): "Authorization"] = "Bearer " + self.feishu_config.tenant_access_token try: + print("call_api request start") print(data) async with httpx.AsyncClient(headers=headers) as client: response = await client.post( @@ -237,7 +241,7 @@ class Bot(BaseBot): json=data["body"], params=data["query"], timeout=self.config.api_timeout) - + print("remote server returned.") print(response.json()) if 200 <= response.status_code < 300: result = response.json() @@ -285,14 +289,22 @@ class Bot(BaseBot): else: raise ValueError( "Cannot guess `receive_id` and `receive_id_type` to reply!") + + if isinstance(message, MessageSegment): + msg_type = message.type + elif isinstance(message, Message): + msg_type = message[0].type + else: + msg_type = "text" + params = { "query": { "receive_id_type": receive_id_type }, "body": { "receive_id": receive_id, - "content": str(message), - "msg_type": "text" if len(message) == 1 else "content" + "content": MessageSerializer(Message(message)).serialize(), + "msg_type": msg_type } } diff --git a/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/event.py b/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/event.py index dd60a8ac..47cecd9e 100644 --- a/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/event.py +++ b/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/event.py @@ -8,7 +8,7 @@ from pydantic import BaseModel, root_validator, Field from nonebot.adapters import Event as BaseEvent from nonebot.typing import overrides -from .message import Message +from .message import Message, MessageDeserializer class EventHeader(BaseModel): @@ -97,7 +97,9 @@ class EventMessage(BaseModel): @root_validator(pre=True) def parse_message(cls, values: dict): - values["content"] = json.loads(values["content"]) + values["content"] = MessageDeserializer( + data=json.loads(values["content"]), + type=values["message_type"]).deserialize() return values diff --git a/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/message.py b/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/message.py index a5b466c1..f3bfa292 100644 --- a/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/message.py +++ b/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/message.py @@ -1,6 +1,8 @@ import itertools -from typing import Tuple, Type, Union, Mapping, Iterable +from dataclasses import dataclass +import json +from typing import Any, Dict, List, Tuple, Type, Union, Mapping, Iterable from nonebot.adapters import Message as BaseMessage, MessageSegment as BaseMessageSegment from nonebot.typing import overrides @@ -158,25 +160,16 @@ class Message(BaseMessage[MessageSegment]): msg: Union[str, Mapping, Iterable[Mapping]]) -> Iterable[MessageSegment]: if isinstance(msg, Mapping): - - def _iter_message(msg: Mapping) -> Iterable[Tuple[str, dict]]: - pure_text: str = msg.get("text", "") - content: dict = msg.get("content", {}) - if pure_text and not content: - yield "text", {"text": pure_text} - elif content and not pure_text: - for element in list(itertools.chain(*content)): - tag = element.pop("tag") - yield tag, element - - for type_, data in _iter_message(msg): - yield MessageSegment(type_, data) - + yield MessageSegment(msg["type"], msg.get("data") or {}) + return elif isinstance(msg, str): yield MessageSegment.text(msg) elif isinstance(msg, Iterable): for seg in msg: - yield MessageSegment(seg["type"], seg.get("data") or {}) + if isinstance(seg, MessageSegment): + yield seg + else: + yield MessageSegment(seg["type"], seg.get("data") or {}) def _produce(self) -> dict: raise NotImplementedError @@ -184,3 +177,52 @@ class Message(BaseMessage[MessageSegment]): @overrides(BaseMessage) def extract_plain_text(self) -> str: return "".join(seg.data["text"] for seg in self if seg.is_text()) + + +@dataclass +class MessageSerializer: + """ + 飞书 协议 Message 序列化器。 + """ + message: Message + + def serialize(self): + for segment in self.message: + if segment.type == "post": + raise NotImplementedError + else: + return json.dumps(segment.data) + + +@dataclass +class MessageDeserializer: + """ + 飞书 协议 Message 反序列化器。 + """ + data: Dict[str, Any] + type: str + + def deserialize(self): + print(self.type, self.data) + if self.type == "post": + return self._parse_rich_text(self.data) + else: + return Message(MessageSegment(self.type, self.data)) + + def _parse_rich_text(self, message_data: Dict[str, + Any]) -> List[MessageSegment]: + + def _iter_message( + message_data: Dict[str, + Any]) -> Iterable[Tuple[str, Dict[str, Any]]]: + content: dict = message_data.get("content", {}) + if content: + for element in list(itertools.chain(*content)): + tag = element.get("tag") + yield tag, element + + temp = Message() + for type_, data in _iter_message(message_data): + temp += MessageSegment(type_, data) + + return temp From e3c3e370cbd80560a8a3ecaa666c6f4cebdca339 Mon Sep 17 00:00:00 2001 From: StarHeartHunt Date: Tue, 6 Jul 2021 21:12:49 +0800 Subject: [PATCH 26/64] =?UTF-8?q?=E2=9A=A1=EF=B8=8F=20remove=20unnecessary?= =?UTF-8?q?=20code?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../nonebot/adapters/feishu/bot.py | 9 ++------ .../nonebot/adapters/feishu/event.py | 8 +++---- .../nonebot/adapters/feishu/message.py | 23 ++++++++----------- 3 files changed, 15 insertions(+), 25 deletions(-) diff --git a/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/bot.py b/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/bot.py index 2535a4d9..255f4747 100644 --- a/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/bot.py +++ b/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/bot.py @@ -290,12 +290,7 @@ class Bot(BaseBot): raise ValueError( "Cannot guess `receive_id` and `receive_id_type` to reply!") - if isinstance(message, MessageSegment): - msg_type = message.type - elif isinstance(message, Message): - msg_type = message[0].type - else: - msg_type = "text" + msg_type, content = MessageSerializer(msg).serialize() params = { "query": { @@ -303,7 +298,7 @@ class Bot(BaseBot): }, "body": { "receive_id": receive_id, - "content": MessageSerializer(Message(message)).serialize(), + "content": content, "msg_type": msg_type } } diff --git a/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/event.py b/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/event.py index 47cecd9e..9f1edd9a 100644 --- a/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/event.py +++ b/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/event.py @@ -1,14 +1,14 @@ import inspect import json -from typing import Any, List, Literal, Optional, Type, Union +from typing import Any, List, Literal, Optional, Type from pygtrie import StringTrie from pydantic import BaseModel, root_validator, Field from nonebot.adapters import Event as BaseEvent from nonebot.typing import overrides -from .message import Message, MessageDeserializer +from .message import Message, MessageDeserializer, MessageSerializer class EventHeader(BaseModel): @@ -143,7 +143,7 @@ class MessageEvent(Event): f"Message[{super().get_type()}]" f" {self.event.message.message_id} from {self.get_user_id()}" f"@[{self.event.message.chat_type}:{self.event.message.chat_id}]" - f" {self.event.message.content}") + f" {str(self.get_message()) and MessageSerializer(self.get_message()).serialize()}") @overrides(Event) def get_message(self) -> Message: @@ -155,7 +155,7 @@ class MessageEvent(Event): @overrides(Event) def get_user_id(self) -> str: - return self.event.sender.sender_id.union_id + return self.event.sender.sender_id.user_id @overrides(Event) def get_session_id(self) -> str: diff --git a/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/message.py b/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/message.py index f3bfa292..111b9ce0 100644 --- a/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/message.py +++ b/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/message.py @@ -186,12 +186,8 @@ class MessageSerializer: """ message: Message - def serialize(self): - for segment in self.message: - if segment.type == "post": - raise NotImplementedError - else: - return json.dumps(segment.data) + def serialize(self) -> Tuple[str, str]: + return self.message[0].type, json.dumps(self.message[0].data) @dataclass @@ -199,13 +195,13 @@ class MessageDeserializer: """ 飞书 协议 Message 反序列化器。 """ - data: Dict[str, Any] type: str + data: Dict[str, Any] - def deserialize(self): + def deserialize(self) -> Message: print(self.type, self.data) if self.type == "post": - return self._parse_rich_text(self.data) + return Message(self._parse_rich_text(self.data)) else: return Message(MessageSegment(self.type, self.data)) @@ -221,8 +217,7 @@ class MessageDeserializer: tag = element.get("tag") yield tag, element - temp = Message() - for type_, data in _iter_message(message_data): - temp += MessageSegment(type_, data) - - return temp + return [ + MessageSegment(type_, data) + for type_, data in _iter_message(message_data) + ] From b9befc835030849e8c5aa294c244ff85bf94f7a9 Mon Sep 17 00:00:00 2001 From: StarHeartHunt Date: Tue, 6 Jul 2021 21:15:02 +0800 Subject: [PATCH 27/64] =?UTF-8?q?=F0=9F=94=87=20remove=20print?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../nonebot-adapter-feishu/nonebot/adapters/feishu/bot.py | 6 ------ .../nonebot/adapters/feishu/message.py | 1 - 2 files changed, 7 deletions(-) diff --git a/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/bot.py b/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/bot.py index 255f4747..a8dbc6cf 100644 --- a/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/bot.py +++ b/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/bot.py @@ -164,8 +164,6 @@ class Bot(BaseBot): 处理事件并转换为 `Event <#class-event>`_ """ data = json.loads(message) - print("handle_event start") - print(data) if data.get("type") == "url_verification": return @@ -233,16 +231,12 @@ class Bot(BaseBot): "Authorization"] = "Bearer " + self.feishu_config.tenant_access_token try: - print("call_api request start") - print(data) async with httpx.AsyncClient(headers=headers) as client: response = await client.post( self.api_root + api, json=data["body"], params=data["query"], timeout=self.config.api_timeout) - print("remote server returned.") - print(response.json()) if 200 <= response.status_code < 300: result = response.json() return _handle_api_result(result) diff --git a/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/message.py b/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/message.py index 111b9ce0..624baefd 100644 --- a/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/message.py +++ b/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/message.py @@ -199,7 +199,6 @@ class MessageDeserializer: data: Dict[str, Any] def deserialize(self) -> Message: - print(self.type, self.data) if self.type == "post": return Message(self._parse_rich_text(self.data)) else: From dc5aa3d4bf17bbd8b12a6eeb20bbae2d889db2db Mon Sep 17 00:00:00 2001 From: StarHeartHunt Date: Tue, 6 Jul 2021 22:01:02 +0800 Subject: [PATCH 28/64] =?UTF-8?q?=F0=9F=94=A5=20remove=20rich=5Ftext=20par?= =?UTF-8?q?ser?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../nonebot/adapters/feishu/event.py | 6 ++--- .../nonebot/adapters/feishu/message.py | 22 +------------------ 2 files changed, 3 insertions(+), 25 deletions(-) diff --git a/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/event.py b/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/event.py index 9f1edd9a..8cf0422a 100644 --- a/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/event.py +++ b/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/event.py @@ -138,12 +138,10 @@ class MessageEvent(Event): @overrides(Event) def get_event_description(self) -> str: - #TODO:换成GroupId return ( - f"Message[{super().get_type()}]" - f" {self.event.message.message_id} from {self.get_user_id()}" + f"{self.event.message.message_id} from {self.get_user_id()}" f"@[{self.event.message.chat_type}:{self.event.message.chat_id}]" - f" {str(self.get_message()) and MessageSerializer(self.get_message()).serialize()}") + f" {MessageSerializer(self.get_message()).serialize()[1]}") @overrides(Event) def get_message(self) -> Message: diff --git a/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/message.py b/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/message.py index 624baefd..236d01ea 100644 --- a/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/message.py +++ b/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/message.py @@ -199,24 +199,4 @@ class MessageDeserializer: data: Dict[str, Any] def deserialize(self) -> Message: - if self.type == "post": - return Message(self._parse_rich_text(self.data)) - else: - return Message(MessageSegment(self.type, self.data)) - - def _parse_rich_text(self, message_data: Dict[str, - Any]) -> List[MessageSegment]: - - def _iter_message( - message_data: Dict[str, - Any]) -> Iterable[Tuple[str, Dict[str, Any]]]: - content: dict = message_data.get("content", {}) - if content: - for element in list(itertools.chain(*content)): - tag = element.get("tag") - yield tag, element - - return [ - MessageSegment(type_, data) - for type_, data in _iter_message(message_data) - ] + return Message(MessageSegment(self.type, self.data)) From 5a8b5b5696f30e36ebe89a94dc04d4a8d702de18 Mon Sep 17 00:00:00 2001 From: StarHeartHunt Date: Tue, 6 Jul 2021 22:08:22 +0800 Subject: [PATCH 29/64] =?UTF-8?q?=E2=9A=A1=EF=B8=8F=20optimize=20import?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../nonebot/adapters/feishu/message.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/message.py b/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/message.py index 236d01ea..0d059f42 100644 --- a/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/message.py +++ b/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/message.py @@ -1,8 +1,7 @@ -import itertools +import json from dataclasses import dataclass -import json -from typing import Any, Dict, List, Tuple, Type, Union, Mapping, Iterable +from typing import Any, Dict, Tuple, Type, Union, Mapping, Iterable from nonebot.adapters import Message as BaseMessage, MessageSegment as BaseMessageSegment from nonebot.typing import overrides From 840580334fac8320d30fe7bb0883cfe5df101d57 Mon Sep 17 00:00:00 2001 From: StarHeartHunt Date: Tue, 6 Jul 2021 22:08:38 +0800 Subject: [PATCH 30/64] =?UTF-8?q?=F0=9F=94=8A=20optimize=20log?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/nonebot-adapter-feishu/nonebot/adapters/feishu/bot.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/bot.py b/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/bot.py index a8dbc6cf..466d4978 100644 --- a/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/bot.py +++ b/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/bot.py @@ -241,7 +241,8 @@ class Bot(BaseBot): result = response.json() return _handle_api_result(result) raise NetworkError(f"HTTP request received unexpected " - f"status code: {response.status_code}") + f"status code: {response.status_code}" + f"response body: {response.text}") except httpx.InvalidURL: raise NetworkError("API root url invalid") except httpx.HTTPError: From f5843dd3ad883c0a4e1eeaa720afd2288a466cd0 Mon Sep 17 00:00:00 2001 From: StarHeartHunt Date: Tue, 6 Jul 2021 22:11:34 +0800 Subject: [PATCH 31/64] =?UTF-8?q?=F0=9F=94=8A=20forgot=20to=20add=20backsp?= =?UTF-8?q?ace?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/nonebot-adapter-feishu/nonebot/adapters/feishu/bot.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/bot.py b/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/bot.py index 466d4978..33b3e79a 100644 --- a/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/bot.py +++ b/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/bot.py @@ -241,7 +241,7 @@ class Bot(BaseBot): result = response.json() return _handle_api_result(result) raise NetworkError(f"HTTP request received unexpected " - f"status code: {response.status_code}" + f"status code: {response.status_code} " f"response body: {response.text}") except httpx.InvalidURL: raise NetworkError("API root url invalid") From e490be9eb0a769130103a023eb681b0cb2537845 Mon Sep 17 00:00:00 2001 From: StarHeartHunt Date: Wed, 7 Jul 2021 22:29:13 +0800 Subject: [PATCH 32/64] =?UTF-8?q?=F0=9F=8E=A8=20tweak?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../nonebot-adapter-feishu/nonebot/adapters/feishu/event.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/event.py b/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/event.py index 8cf0422a..b0e7a4dd 100644 --- a/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/event.py +++ b/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/event.py @@ -149,7 +149,7 @@ class MessageEvent(Event): @overrides(Event) def get_plaintext(self) -> str: - return str(self.event.message.content) + return str(self.get_message()) @overrides(Event) def get_user_id(self) -> str: From fe43c8d69a2829666a08dd540c78ebc8450e3e15 Mon Sep 17 00:00:00 2001 From: StarHeartHunt Date: Wed, 7 Jul 2021 22:36:08 +0800 Subject: [PATCH 33/64] =?UTF-8?q?=F0=9F=92=A9=20should=20implement=20rich?= =?UTF-8?q?=20text=20parser?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../nonebot/adapters/feishu/message.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/message.py b/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/message.py index 0d059f42..3a1201d6 100644 --- a/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/message.py +++ b/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/message.py @@ -1,4 +1,5 @@ import json +import itertools from dataclasses import dataclass from typing import Any, Dict, Tuple, Type, Union, Mapping, Iterable @@ -18,8 +19,17 @@ class MessageSegment(BaseMessageSegment["Message"]): return Message def __str__(self) -> str: - if self.type == "text" or self.type == "hongbao": + if self.type == "post": + return "".join( + str(MessageSegment(seg["tag"], seg)) + for seg in itertools.chain(*self.data["content"])) + + elif self.type == "text" or self.type == "hongbao": return str(self.data["text"]) + + elif self.type == "img" or self.type == "image": + return "[图片]" + return "" @overrides(BaseMessageSegment) From 79b6601d12bfebda2cd26db9706f2461f4acff35 Mon Sep 17 00:00:00 2001 From: StarHeartHunt Date: Thu, 8 Jul 2021 10:07:55 +0800 Subject: [PATCH 34/64] =?UTF-8?q?=F0=9F=8D=BB=20tweak?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../nonebot/adapters/feishu/event.py | 6 +- .../nonebot/adapters/feishu/message.py | 100 ++++++------------ 2 files changed, 35 insertions(+), 71 deletions(-) diff --git a/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/event.py b/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/event.py index b0e7a4dd..12452b97 100644 --- a/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/event.py +++ b/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/event.py @@ -98,8 +98,8 @@ class EventMessage(BaseModel): @root_validator(pre=True) def parse_message(cls, values: dict): values["content"] = MessageDeserializer( - data=json.loads(values["content"]), - type=values["message_type"]).deserialize() + values["message_type"], + json.loads(values["content"])).deserialize() return values @@ -141,7 +141,7 @@ class MessageEvent(Event): return ( f"{self.event.message.message_id} from {self.get_user_id()}" f"@[{self.event.message.chat_type}:{self.event.message.chat_id}]" - f" {MessageSerializer(self.get_message()).serialize()[1]}") + f" {self.get_message()}") @overrides(Event) def get_message(self) -> Message: diff --git a/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/message.py b/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/message.py index 3a1201d6..54a56395 100644 --- a/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/message.py +++ b/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/message.py @@ -19,15 +19,10 @@ class MessageSegment(BaseMessageSegment["Message"]): return Message def __str__(self) -> str: - if self.type == "post": - return "".join( - str(MessageSegment(seg["tag"], seg)) - for seg in itertools.chain(*self.data["content"])) - - elif self.type == "text" or self.type == "hongbao": + if self.type == "text" or self.type == "hongbao": return str(self.data["text"]) - elif self.type == "img" or self.type == "image": + elif self.type == "image": return "[图片]" return "" @@ -59,12 +54,20 @@ class MessageSegment(BaseMessageSegment["Message"]): return MessageSegment("image", {"image_key": image_key}) @staticmethod - def file(file_key: str, file_name: str) -> "MessageSegment": - return MessageSegment("file", { - "file_key": file_key, - "file_name": file_name + def interactive(title: str, elements: list) -> "MessageSegment": + return MessageSegment("interactive", { + "title": title, + "elements": elements }) + @staticmethod + def share_chat(chat_id: str) -> "MessageSegment": + return MessageSegment("share_chat", {"chat_id": chat_id}) + + @staticmethod + def share_user(user_id: str) -> "MessageSegment": + return MessageSegment("share_user", {"user_id": user_id}) + @staticmethod def audio(file_key: str, duration: int) -> "MessageSegment": return MessageSegment("audio", { @@ -83,63 +86,17 @@ class MessageSegment(BaseMessageSegment["Message"]): "duration": duration }) + @staticmethod + def file(file_key: str, file_name: str) -> "MessageSegment": + return MessageSegment("file", { + "file_key": file_key, + "file_name": file_name + }) + @staticmethod def sticker(file_key) -> "MessageSegment": return MessageSegment("sticker", {"file_key": file_key}) - @staticmethod - def interactive(title: str, elements: list) -> "MessageSegment": - return MessageSegment("interactive", { - "title": title, - "elements": elements - }) - - @staticmethod - def hongbao(text: str) -> "MessageSegment": - return MessageSegment("hongbao", {"text": text}) - - @staticmethod - def share_calendar_event(summary: str, start_time: str, - end_time: str) -> "MessageSegment": - return MessageSegment("share_calendar_event", { - "summary": summary, - "start_time": start_time, - "end_time": end_time - }) - - @staticmethod - def share_chat(chat_id: str) -> "MessageSegment": - return MessageSegment("share_chat", {"chat_id": chat_id}) - - @staticmethod - def share_user(user_id: str) -> "MessageSegment": - return MessageSegment("share_user", {"user_id": user_id}) - - @staticmethod - def system(template: str, from_user: list, - to_chatters: list) -> "MessageSegment": - return MessageSegment( - "system", { - "template": template, - "from_user": from_user, - "to_chatters": to_chatters - }) - - @staticmethod - def location(name: str, longitude: str, latitude: str) -> "MessageSegment": - return MessageSegment("location", { - "name": name, - "longitude": longitude, - "latitude": latitude - }) - - @staticmethod - def video_chat(topic: str, start_time: str) -> "MessageSegment": - return MessageSegment("video_chat", { - "topic": topic, - "start_time": start_time, - }) - class Message(BaseMessage[MessageSegment]): """ @@ -180,9 +137,6 @@ class Message(BaseMessage[MessageSegment]): else: yield MessageSegment(seg["type"], seg.get("data") or {}) - def _produce(self) -> dict: - raise NotImplementedError - @overrides(BaseMessage) def extract_plain_text(self) -> str: return "".join(seg.data["text"] for seg in self if seg.is_text()) @@ -208,4 +162,14 @@ class MessageDeserializer: data: Dict[str, Any] def deserialize(self) -> Message: - return Message(MessageSegment(self.type, self.data)) + if self.type == "post": + msg = Message() + if self.data["title"] != "": + msg += MessageSegment("text", {'text': self.data["title"]}) + for seg in itertools.chain(*self.data["content"]): + tag = seg.pop("tag") + msg += MessageSegment(tag if tag != "img" else "image", seg) + return msg + + else: + return Message(MessageSegment(self.type, self.data)) From 7df407056bece27527f7a24e6423ea0d73dcad2c Mon Sep 17 00:00:00 2001 From: StarHeartHunt Date: Thu, 8 Jul 2021 10:49:02 +0800 Subject: [PATCH 35/64] =?UTF-8?q?=E2=9C=A8=20implement=20text=20merger?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../nonebot/adapters/feishu/message.py | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/message.py b/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/message.py index 54a56395..aba04843 100644 --- a/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/message.py +++ b/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/message.py @@ -2,7 +2,7 @@ import json import itertools from dataclasses import dataclass -from typing import Any, Dict, Tuple, Type, Union, Mapping, Iterable +from typing import Any, Dict, List, Tuple, Type, Union, Mapping, Iterable from nonebot.adapters import Message as BaseMessage, MessageSegment as BaseMessageSegment from nonebot.typing import overrides @@ -137,6 +137,18 @@ class Message(BaseMessage[MessageSegment]): else: yield MessageSegment(seg["type"], seg.get("data") or {}) + def _merge(self) -> "Message": + i: int + seg: MessageSegment + msg: List[MessageSegment] = [] + for i, seg in enumerate(self): + if seg.type == "text" and i != 0 and msg[-1].type == "text": + msg[-1] = MessageSegment( + "text", {"text": msg[-1].data["text"] + seg.data["text"]}) + else: + msg.append(seg) + return Message(msg) + @overrides(BaseMessage) def extract_plain_text(self) -> str: return "".join(seg.data["text"] for seg in self if seg.is_text()) @@ -169,7 +181,7 @@ class MessageDeserializer: for seg in itertools.chain(*self.data["content"]): tag = seg.pop("tag") msg += MessageSegment(tag if tag != "img" else "image", seg) - return msg + return msg._merge() else: return Message(MessageSegment(self.type, self.data)) From 1222972b73eebf7e841f4170b5523c61d958dd4b Mon Sep 17 00:00:00 2001 From: StarHeartHunt Date: Thu, 8 Jul 2021 14:39:38 +0800 Subject: [PATCH 36/64] =?UTF-8?q?=E2=9A=A1=EF=B8=8F=20optimize=20import?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../nonebot-adapter-feishu/nonebot/adapters/feishu/event.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/event.py b/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/event.py index 12452b97..f00ba18d 100644 --- a/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/event.py +++ b/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/event.py @@ -8,7 +8,7 @@ from pydantic import BaseModel, root_validator, Field from nonebot.adapters import Event as BaseEvent from nonebot.typing import overrides -from .message import Message, MessageDeserializer, MessageSerializer +from .message import Message, MessageDeserializer class EventHeader(BaseModel): From f59730369813c6f6aced47f580d49cbe56a50413 Mon Sep 17 00:00:00 2001 From: StarHeartHunt Date: Thu, 8 Jul 2021 14:52:34 +0800 Subject: [PATCH 37/64] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20improve=20segment-st?= =?UTF-8?q?ring=20parser?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../nonebot/adapters/feishu/message.py | 29 +++++++++++++++---- 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/message.py b/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/message.py index aba04843..ecef3800 100644 --- a/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/message.py +++ b/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/message.py @@ -18,14 +18,31 @@ class MessageSegment(BaseMessageSegment["Message"]): def get_message_class(cls) -> Type["Message"]: return Message + @property + def segment_text(self) -> dict: + return { + "image": "[图片]", + "file": "[文件]", + "audio": "[音频]", + "media": "[视频]", + "sticker": "[表情包]", + "interactive": "[消息卡片]", + "hongbao": "[红包]", + "share_calendar_event": "[日程卡片]", + "share_chat": "[群名片]", + "share_user": "[个人名片]", + "system": "[系统消息]", + "location": "[位置]", + "video_chat": "[视频通话]" + } + def __str__(self) -> str: - if self.type == "text" or self.type == "hongbao": + if self.type in ["text", "hongbao", "a"]: return str(self.data["text"]) - - elif self.type == "image": - return "[图片]" - - return "" + elif self.type == "at": + return str(f"@{self.data['user_name']}") + else: + return self.segment_text.get(self.type, "") @overrides(BaseMessageSegment) def __add__(self, other) -> "Message": From 7c7994e2e2a40168219b0b130aeacb3e2d98acaa Mon Sep 17 00:00:00 2001 From: StarHeartHunt Date: Thu, 8 Jul 2021 16:12:43 +0800 Subject: [PATCH 38/64] =?UTF-8?q?=E2=9C=A8=20implement=20rich=20text=20ser?= =?UTF-8?q?ializer?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../nonebot/adapters/feishu/message.py | 27 ++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/message.py b/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/message.py index ecef3800..9bcc45f8 100644 --- a/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/message.py +++ b/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/message.py @@ -58,6 +58,15 @@ class MessageSegment(BaseMessageSegment["Message"]): def is_text(self) -> bool: return self.type == "text" + #接收消息 + @staticmethod + def at(user_id: str, user_name: str) -> "MessageSegment": + return MessageSegment("at", { + "user_id": user_id, + "user_name": user_name + }) + + #发送消息 @staticmethod def text(text: str) -> "MessageSegment": return MessageSegment("text", {"text": text}) @@ -179,7 +188,23 @@ class MessageSerializer: message: Message def serialize(self) -> Tuple[str, str]: - return self.message[0].type, json.dumps(self.message[0].data) + segments = list(self.message) + last_segment_type: str = "" + if len(segments) > 1: + msg = {"title": "", "content": [[]]} + for segment in segments: + if segment == "image": + if last_segment_type != "image": + msg["content"].append([]) + else: + if last_segment_type == "image": + msg["content"].append([]) + msg["content"][-1].append({"tag": segment.type, **segment.data}) + last_segment_type = segment.type + return "post", json.dumps(msg) + + else: + return self.message[0].type, json.dumps(self.message[0].data) @dataclass From af6f63dc4428a7b549e8132431245f395c2e05a1 Mon Sep 17 00:00:00 2001 From: StarHeartHunt Date: Thu, 8 Jul 2021 16:13:07 +0800 Subject: [PATCH 39/64] =?UTF-8?q?=F0=9F=9A=A7=20add=20todo?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../nonebot/adapters/feishu/bot.py | 14 ++++++++++++-- .../nonebot/adapters/feishu/event.py | 3 +++ 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/bot.py b/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/bot.py index 33b3e79a..31dc0573 100644 --- a/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/bot.py +++ b/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/bot.py @@ -30,6 +30,7 @@ async def _check_reply(bot: "Bot", event: "Event"): * ``bot: Bot``: Bot 对象 * ``event: Event``: Event 对象 """ + #TODO:实现该函数 ... @@ -44,6 +45,7 @@ def _check_at_me(bot: "Bot", event: "Event"): * ``bot: Bot``: Bot 对象 * ``event: Event``: Event 对象 """ + #TODO:实现该函数 ... @@ -58,6 +60,7 @@ def _check_nickname(bot: "Bot", event: "Event"): * ``bot: Bot``: Bot 对象 * ``event: Event``: Event 对象 """ + #TODO:实现该函数 ... @@ -272,8 +275,10 @@ class Bot(BaseBot): return await super().call_api(api, **data) @overrides(BaseBot) - async def send(self, event: Event, message: Union[str, Message, - MessageSegment], + async def send(self, + event: Event, + message: Union[str, Message, MessageSegment], + at_sender: bool = False, **kwargs) -> Any: msg = message if isinstance(message, Message) else Message(message) @@ -285,6 +290,11 @@ class Bot(BaseBot): raise ValueError( "Cannot guess `receive_id` and `receive_id_type` to reply!") + at_sender = at_sender and bool(event.get_user_id()) + + if at_sender and receive_id_type != "union_id": + msg += MessageSegment.at(event.get_user_id(), "StarHeart") + " " + msg_type, content = MessageSerializer(msg).serialize() params = { diff --git a/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/event.py b/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/event.py index f00ba18d..1b88e6d0 100644 --- a/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/event.py +++ b/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/event.py @@ -97,6 +97,7 @@ class EventMessage(BaseModel): @root_validator(pre=True) def parse_message(cls, values: dict): + #TODO:解析mentions替换message的user_id,传入deserializer values["content"] = MessageDeserializer( values["message_type"], json.loads(values["content"])).deserialize() @@ -187,10 +188,12 @@ class MessageReadEvent(Event): class NoticeEvent(Event): + #TODO:实现该事件 ... class MetaEvent(Event): + #TODO:实现该事件 ... From 15ab958a70d6353f28de42a0b8a2e1bec7b8d54d Mon Sep 17 00:00:00 2001 From: StarHeartHunt Date: Thu, 8 Jul 2021 22:30:21 +0800 Subject: [PATCH 40/64] =?UTF-8?q?=F0=9F=8E=A8=20format=20code?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../nonebot-adapter-feishu/nonebot/adapters/feishu/__init__.py | 2 +- .../nonebot-adapter-feishu/nonebot/adapters/feishu/utils.py | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/__init__.py b/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/__init__.py index 1569ff02..532b2d9c 100644 --- a/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/__init__.py +++ b/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/__init__.py @@ -1,4 +1,4 @@ from .bot import Bot from .event import * -from .message import Message, MessageSegment from .event import Event +from .message import Message, MessageSegment diff --git a/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/utils.py b/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/utils.py index 34750255..a04456a6 100644 --- a/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/utils.py +++ b/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/utils.py @@ -6,6 +6,7 @@ from nonebot.utils import logger_wrapper log = logger_wrapper("FEISHU") + class AESCipher(object): def __init__(self, key): From 67770ffa6f579b764edda15a062be76d559ce157 Mon Sep 17 00:00:00 2001 From: StarHeartHunt Date: Thu, 8 Jul 2021 22:30:39 +0800 Subject: [PATCH 41/64] =?UTF-8?q?=E2=9C=A8=20implement=20at=20parser?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../nonebot/adapters/feishu/bot.py | 2 +- .../nonebot/adapters/feishu/event.py | 7 +++---- .../nonebot/adapters/feishu/message.py | 15 ++++++++++++++- 3 files changed, 18 insertions(+), 6 deletions(-) diff --git a/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/bot.py b/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/bot.py index 31dc0573..f020318c 100644 --- a/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/bot.py +++ b/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/bot.py @@ -293,7 +293,7 @@ class Bot(BaseBot): at_sender = at_sender and bool(event.get_user_id()) if at_sender and receive_id_type != "union_id": - msg += MessageSegment.at(event.get_user_id(), "StarHeart") + " " + msg = MessageSegment.at(event.get_user_id(), "StarHeart") + " " + msg msg_type, content = MessageSerializer(msg).serialize() diff --git a/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/event.py b/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/event.py index 1b88e6d0..83499859 100644 --- a/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/event.py +++ b/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/event.py @@ -97,10 +97,9 @@ class EventMessage(BaseModel): @root_validator(pre=True) def parse_message(cls, values: dict): - #TODO:解析mentions替换message的user_id,传入deserializer values["content"] = MessageDeserializer( - values["message_type"], - json.loads(values["content"])).deserialize() + values["message_type"], json.loads(values["content"]), + values.get("mentions")).deserialize() return values @@ -154,7 +153,7 @@ class MessageEvent(Event): @overrides(Event) def get_user_id(self) -> str: - return self.event.sender.sender_id.user_id + return self.event.sender.sender_id.open_id @overrides(Event) def get_session_id(self) -> str: diff --git a/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/message.py b/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/message.py index 9bcc45f8..debf26e7 100644 --- a/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/message.py +++ b/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/message.py @@ -2,7 +2,7 @@ import json import itertools from dataclasses import dataclass -from typing import Any, Dict, List, Tuple, Type, Union, Mapping, Iterable +from typing import Any, Dict, List, Optional, Tuple, Type, Union, Mapping, Iterable from nonebot.adapters import Message as BaseMessage, MessageSegment as BaseMessageSegment from nonebot.typing import overrides @@ -214,15 +214,28 @@ class MessageDeserializer: """ type: str data: Dict[str, Any] + mentions: Optional[List[dict]] def deserialize(self) -> Message: + dict_mention = {} if self.type == "post": + if self.mentions: + for mention in self.mentions: + dict_mention[mention["key"]] = mention + msg = Message() if self.data["title"] != "": msg += MessageSegment("text", {'text': self.data["title"]}) + for seg in itertools.chain(*self.data["content"]): tag = seg.pop("tag") + if tag == "at": + seg["user_name"] = dict_mention[seg["user_id"]]["name"] + seg["user_id"] = dict_mention[ + seg["user_id"]]["id"]["open_id"] + msg += MessageSegment(tag if tag != "img" else "image", seg) + return msg._merge() else: From d2ae2e2982604dca63adaa4e230c9fabcbee7e13 Mon Sep 17 00:00:00 2001 From: StarHeartHunt Date: Thu, 8 Jul 2021 23:05:12 +0800 Subject: [PATCH 42/64] =?UTF-8?q?=F0=9F=9A=91=20safely=20get=20dict=20valu?= =?UTF-8?q?e?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../nonebot-adapter-feishu/nonebot/adapters/feishu/bot.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/bot.py b/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/bot.py index f020318c..8f34dbc3 100644 --- a/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/bot.py +++ b/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/bot.py @@ -237,8 +237,8 @@ class Bot(BaseBot): async with httpx.AsyncClient(headers=headers) as client: response = await client.post( self.api_root + api, - json=data["body"], - params=data["query"], + json=data.get("body", {}), + params=data.get("query", {}), timeout=self.config.api_timeout) if 200 <= response.status_code < 300: result = response.json() @@ -293,7 +293,7 @@ class Bot(BaseBot): at_sender = at_sender and bool(event.get_user_id()) if at_sender and receive_id_type != "union_id": - msg = MessageSegment.at(event.get_user_id(), "StarHeart") + " " + msg + msg = MessageSegment.at(event.get_user_id()) + " " + msg msg_type, content = MessageSerializer(msg).serialize() From 730186cfd402fff52e3d9abdec1af213639037f9 Mon Sep 17 00:00:00 2001 From: StarHeartHunt Date: Thu, 8 Jul 2021 23:05:24 +0800 Subject: [PATCH 43/64] :zap: remove optional param --- .../nonebot/adapters/feishu/message.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/message.py b/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/message.py index debf26e7..e1c748db 100644 --- a/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/message.py +++ b/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/message.py @@ -60,11 +60,8 @@ class MessageSegment(BaseMessageSegment["Message"]): #接收消息 @staticmethod - def at(user_id: str, user_name: str) -> "MessageSegment": - return MessageSegment("at", { - "user_id": user_id, - "user_name": user_name - }) + def at(user_id: str) -> "MessageSegment": + return MessageSegment("at", {"user_id": user_id}) #发送消息 @staticmethod @@ -199,9 +196,12 @@ class MessageSerializer: else: if last_segment_type == "image": msg["content"].append([]) - msg["content"][-1].append({"tag": segment.type, **segment.data}) + msg["content"][-1].append({ + "tag": segment.type if segment.type != "image" else "img", + **segment.data + }) last_segment_type = segment.type - return "post", json.dumps(msg) + return "post", json.dumps({"zh_cn": {**msg}}) else: return self.message[0].type, json.dumps(self.message[0].data) From 70e424b58f882a2c3b8c3fa26f476a9ad152303a Mon Sep 17 00:00:00 2001 From: StarHeartHunt Date: Fri, 9 Jul 2021 09:22:56 +0800 Subject: [PATCH 44/64] =?UTF-8?q?=E2=9A=A1=EF=B8=8F=20support=20cache=20te?= =?UTF-8?q?nant=20token?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../nonebot/adapters/feishu/bot.py | 7 +++++- packages/nonebot-adapter-feishu/poetry.lock | 25 ++++++++++++++++++- .../nonebot-adapter-feishu/pyproject.toml | 1 + 3 files changed, 31 insertions(+), 2 deletions(-) diff --git a/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/bot.py b/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/bot.py index 8f34dbc3..22756ab9 100644 --- a/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/bot.py +++ b/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/bot.py @@ -1,6 +1,8 @@ import json import httpx +from aiocache import cached, Cache +from aiocache.serializers import PickleSerializer from typing import Any, Dict, Tuple, Union, Optional, TYPE_CHECKING from nonebot.log import logger @@ -195,7 +197,10 @@ class Bot(BaseBot): def _construct_url(self, path: str) -> str: return self.api_root + path - #TODO:实现token缓存与ttl + @cached(ttl=60 * 60, + cache=Cache.MEMORY, + key="_feishu_tenant_access_token", + serializer=PickleSerializer()) async def _fetch_tenant_access_token(self) -> str: try: async with httpx.AsyncClient() as client: diff --git a/packages/nonebot-adapter-feishu/poetry.lock b/packages/nonebot-adapter-feishu/poetry.lock index 77323e08..15b8f951 100644 --- a/packages/nonebot-adapter-feishu/poetry.lock +++ b/packages/nonebot-adapter-feishu/poetry.lock @@ -1,3 +1,22 @@ +[[package]] +name = "aiocache" +version = "0.11.1" +description = "multi backend asyncio cache" +category = "main" +optional = false +python-versions = "*" + +[package.extras] +dev = ["asynctest (>=0.11.0)", "codecov", "coverage", "flake8", "ipdb", "marshmallow", "pystache", "pytest", "pytest-asyncio", "pytest-mock", "sphinx", "sphinx-autobuild", "sphinx-rtd-theme", "black"] +memcached = ["aiomcache (>=0.5.2)"] +msgpack = ["msgpack (>=0.5.5)"] +redis = ["aioredis (>=0.3.3)", "aioredis (>=1.0.0)"] + +[package.source] +type = "legacy" +url = "https://mirrors.aliyun.com/pypi/simple" +reference = "aliyun" + [[package]] name = "certifi" version = "2021.5.30" @@ -432,9 +451,13 @@ reference = "aliyun" [metadata] lock-version = "1.1" python-versions = "^3.7.3" -content-hash = "fbc5c896e785a9b9c948ec9f20d5d795684bc0f839bd31b10812c9dc52adb75b" +content-hash = "8cb6803a739721f108b5d43f0633275f26a97c6486eb67cdb9346f369a5182ee" [metadata.files] +aiocache = [ + {file = "aiocache-0.11.1-py2.py3-none-any.whl", hash = "sha256:e55c7caaa5753794fd301c3a2e592737fa1d036db9f8d04ae154facdfb48a157"}, + {file = "aiocache-0.11.1.tar.gz", hash = "sha256:f2ebe0b05cec45782e7b5ea0bb74640f157dd4bb1028b4565364dda9fe33be7f"}, +] certifi = [ {file = "certifi-2021.5.30-py2.py3-none-any.whl", hash = "sha256:50b1e4f8446b06f41be7dd6338db18e0990601dce795c2b1686458aa7e8fa7d8"}, {file = "certifi-2021.5.30.tar.gz", hash = "sha256:2bbf76fd432960138b3ef6dda3dde0544f27cbf8546c458e60baf371917ba9ee"}, diff --git a/packages/nonebot-adapter-feishu/pyproject.toml b/packages/nonebot-adapter-feishu/pyproject.toml index 73e731bb..86536fa1 100644 --- a/packages/nonebot-adapter-feishu/pyproject.toml +++ b/packages/nonebot-adapter-feishu/pyproject.toml @@ -27,6 +27,7 @@ python = "^3.7.3" httpx = "^0.17.0" nonebot2 = "^2.0.0-alpha.13" pycryptodome = "^3.10.1" +aiocache = "^0.11.1" [tool.poetry.dev-dependencies] nonebot2 = { path = "../../", develop = true } From b1c5013088620835488f85d3cca750b33d6de1a0 Mon Sep 17 00:00:00 2001 From: StarHeartHunt Date: Fri, 9 Jul 2021 16:01:32 +0800 Subject: [PATCH 45/64] =?UTF-8?q?=E2=9C=A8=20implement=20`check=5Freply`?= =?UTF-8?q?=20`check=5Fatme`=20`check=5Fnickname`?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../nonebot/adapters/feishu/bot.py | 102 ++++++++++++++---- .../nonebot/adapters/feishu/event.py | 46 ++++++++ 2 files changed, 128 insertions(+), 20 deletions(-) diff --git a/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/bot.py b/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/bot.py index 22756ab9..b08ce65a 100644 --- a/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/bot.py +++ b/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/bot.py @@ -1,5 +1,6 @@ -import json import httpx +import json +import re from aiocache import cached, Cache from aiocache.serializers import PickleSerializer @@ -12,7 +13,7 @@ from nonebot.adapters import Bot as BaseBot from nonebot.drivers import Driver, HTTPRequest, HTTPResponse from .config import Config as FeishuConfig -from .event import Event, GroupMessageEvent, PrivateMessageEvent, get_event_model +from .event import Event, GroupMessageEvent, MessageEvent, PrivateMessageEvent, Reply, get_event_model from .exception import ActionFailed, ApiNotAvailable, NetworkError from .message import Message, MessageSegment, MessageSerializer from .utils import log, AESCipher @@ -25,45 +26,99 @@ async def _check_reply(bot: "Bot", event: "Event"): """ :说明: - 检查消息中存在的回复,去除并赋值 ``event.reply``, ``event.to_me`` + 检查是否回复bot消息,赋值 ``event.reply``, ``event.to_me`` :参数: * ``bot: Bot``: Bot 对象 * ``event: Event``: Event 对象 """ - #TODO:实现该函数 - ... + if not isinstance(event, MessageEvent): + return + if event.event.message.parent_id: + ret = await bot.call_api( + f"im/v1/messages/{event.event.message.parent_id}", method="GET") + event.reply = Reply.parse_obj(ret["items"][0]) + if event.reply.sender.sender_type == "app": + event.to_me = True + return def _check_at_me(bot: "Bot", event: "Event"): """ :说明: - 检查消息开头或结尾是否存在 @机器人,去除并赋值 ``event.to_me`` + 检查消息开头或结尾是否存在 @机器人,去除并赋值 ``event.reply``, ``event.to_me`` :参数: * ``bot: Bot``: Bot 对象 * ``event: Event``: Event 对象 """ - #TODO:实现该函数 - ... + if not isinstance(event, MessageEvent): + return + + message = event.get_message() + # ensure message not empty + if not message: + message.append(MessageSegment.text("")) + + if event.event.message.chat_type == "p2p": + event.to_me = True + + for index, segment in enumerate(message): + if segment.type == "at" and segment.data.get( + "user_name") in bot.config.nickname: + event.to_me = True + del event.event.message.content[index] + return + elif segment.type == "text" and segment.data.get("mentions"): + for mention in segment.data["mentions"].values(): + if mention["name"] in bot.config.nickname: + event.to_me = True + segment.data["text"] = segment.data["text"].replace( + f"@{mention['name']}", "") + segment.data["text"] = segment.data["text"].lstrip() + break + else: + continue + break + + if not message: + message.append(MessageSegment.text("")) def _check_nickname(bot: "Bot", event: "Event"): """ :说明: - 检查消息开头是否存在,去除并赋值 ``event.to_me`` + 检查消息开头是否存在昵称,去除并赋值 ``event.to_me`` :参数: * ``bot: Bot``: Bot 对象 * ``event: Event``: Event 对象 """ - #TODO:实现该函数 - ... + if not isinstance(event, MessageEvent): + return + + first_msg_seg = event.get_message()[0] + if first_msg_seg.type != "text": + return + + first_text = first_msg_seg.data["text"] + + nicknames = set(filter(lambda n: n, bot.config.nickname)) + if nicknames: + # check if the user is calling me with my nickname + nickname_regex = "|".join(nicknames) + m = re.search(rf"^({nickname_regex})([\s,,]*|$)", first_text, + re.IGNORECASE) + if m: + nickname = m.group(1) + log("DEBUG", f"User is calling me {nickname}") + event.to_me = True + first_msg_seg.data["text"] = first_text[m.end():] def _handle_api_result(result: Optional[Dict[str, Any]]) -> Any: @@ -188,6 +243,11 @@ class Bot(BaseBot): log("DEBUG", "Event Parser Error", e) else: event = Event.parse_obj(data) + + _check_at_me(self, event) + await _check_reply(self, event) + _check_nickname(self, event) + await handle_event(self, event) except Exception as e: logger.opt(colors=True, exception=e).error( @@ -232,19 +292,20 @@ class Bot(BaseBot): raise ApiNotAvailable headers = {} - if self.feishu_config.tenant_access_token is None: - self.feishu_config.tenant_access_token = await self._fetch_tenant_access_token( - ) + self.feishu_config.tenant_access_token = await self._fetch_tenant_access_token( + ) headers[ "Authorization"] = "Bearer " + self.feishu_config.tenant_access_token try: - async with httpx.AsyncClient(headers=headers) as client: - response = await client.post( - self.api_root + api, - json=data.get("body", {}), - params=data.get("query", {}), - timeout=self.config.api_timeout) + async with httpx.AsyncClient( + timeout=self.config.api_timeout) as client: + response = await client.send( + httpx.Request(data["method"], + self.api_root + api, + json=data.get("body", {}), + params=data.get("query", {}), + headers=headers)) if 200 <= response.status_code < 300: result = response.json() return _handle_api_result(result) @@ -303,6 +364,7 @@ class Bot(BaseBot): msg_type, content = MessageSerializer(msg).serialize() params = { + "method": "POST", "query": { "receive_id_type": receive_id_type }, diff --git a/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/event.py b/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/event.py index 83499859..38406094 100644 --- a/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/event.py +++ b/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/event.py @@ -77,6 +77,13 @@ class Sender(BaseModel): tenant_key: str +class ReplySender(BaseModel): + id: str + id_type: str + sender_type: str + tenant_key: str + + class Mention(BaseModel): key: str id: UserId @@ -84,6 +91,37 @@ class Mention(BaseModel): tenant_key: str +class ReplyMention(BaseModel): + id: str + id_type: str + key: str + name: str + tenant_key: str + + +class MessageBody(BaseModel): + content: str + + +class Reply(BaseModel): + message_id: str + root_id: Optional[str] + parent_id: Optional[str] + msg_type: str + create_time: str + update_time: str + deleted: bool + updated: bool + chat_id: str + sender: ReplySender + body: MessageBody + mentions: List[ReplyMention] + upper_message_id: Optional[str] + + class Config: + extra = "allow" + + class EventMessage(BaseModel): message_id: str root_id: Optional[str] @@ -128,6 +166,14 @@ class MessageEvent(Event): __event__ = "im.message.receive_v1" event: MessageEventDetail + to_me: bool = False + reply: Optional[Reply] + """ + :说明: 消息是否与机器人有关 + + :类型: ``bool`` + """ + @overrides(Event) def get_type(self) -> Literal["message", "notice", "meta_event"]: return "message" From 2908cb3ce26929fb3d01367665bb8e71077ff47b Mon Sep 17 00:00:00 2001 From: StarHeartHunt Date: Fri, 9 Jul 2021 16:02:54 +0800 Subject: [PATCH 46/64] =?UTF-8?q?=F0=9F=90=9B=20tweak?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../nonebot/adapters/feishu/message.py | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/message.py b/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/message.py index e1c748db..f79c60bf 100644 --- a/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/message.py +++ b/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/message.py @@ -218,11 +218,11 @@ class MessageDeserializer: def deserialize(self) -> Message: dict_mention = {} - if self.type == "post": - if self.mentions: - for mention in self.mentions: - dict_mention[mention["key"]] = mention + if self.mentions: + for mention in self.mentions: + dict_mention[mention["key"]] = mention + if self.type == "post": msg = Message() if self.data["title"] != "": msg += MessageSegment("text", {'text': self.data["title"]}) @@ -237,6 +237,12 @@ class MessageDeserializer: msg += MessageSegment(tag if tag != "img" else "image", seg) return msg._merge() + elif self.type == "text": + for key, mention in dict_mention.items(): + self.data["text"] = self.data["text"].replace(key, f"@{mention['name']}") + self.data["mentions"] = dict_mention + + return Message(MessageSegment(self.type, self.data)) else: return Message(MessageSegment(self.type, self.data)) From fbd60e30fc6c078c3669feaa4a234126af486790 Mon Sep 17 00:00:00 2001 From: StarHeartHunt Date: Fri, 9 Jul 2021 16:09:01 +0800 Subject: [PATCH 47/64] =?UTF-8?q?=F0=9F=94=A5=20remove=20check=5Freply?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../nonebot/adapters/feishu/bot.py | 23 ------------------- 1 file changed, 23 deletions(-) diff --git a/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/bot.py b/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/bot.py index b08ce65a..9045dbc2 100644 --- a/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/bot.py +++ b/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/bot.py @@ -22,28 +22,6 @@ if TYPE_CHECKING: from nonebot.config import Config -async def _check_reply(bot: "Bot", event: "Event"): - """ - :说明: - - 检查是否回复bot消息,赋值 ``event.reply``, ``event.to_me`` - - :参数: - - * ``bot: Bot``: Bot 对象 - * ``event: Event``: Event 对象 - """ - if not isinstance(event, MessageEvent): - return - if event.event.message.parent_id: - ret = await bot.call_api( - f"im/v1/messages/{event.event.message.parent_id}", method="GET") - event.reply = Reply.parse_obj(ret["items"][0]) - if event.reply.sender.sender_type == "app": - event.to_me = True - return - - def _check_at_me(bot: "Bot", event: "Event"): """ :说明: @@ -245,7 +223,6 @@ class Bot(BaseBot): event = Event.parse_obj(data) _check_at_me(self, event) - await _check_reply(self, event) _check_nickname(self, event) await handle_event(self, event) From 30cb6be104ab64b3ad7e8c9d18b2b5446ce1b727 Mon Sep 17 00:00:00 2001 From: StarHeartHunt Date: Fri, 9 Jul 2021 16:09:25 +0800 Subject: [PATCH 48/64] =?UTF-8?q?=F0=9F=93=9D=20tweak?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../nonebot-adapter-feishu/nonebot/adapters/feishu/event.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/event.py b/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/event.py index 38406094..36a86eed 100644 --- a/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/event.py +++ b/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/event.py @@ -167,12 +167,12 @@ class MessageEvent(Event): event: MessageEventDetail to_me: bool = False - reply: Optional[Reply] """ :说明: 消息是否与机器人有关 :类型: ``bool`` """ + reply: Optional[Reply] @overrides(Event) def get_type(self) -> Literal["message", "notice", "meta_event"]: From 8c7498eeb59c155fa0455cce2fc2081d4ce4c7ba Mon Sep 17 00:00:00 2001 From: StarHeartHunt Date: Fri, 9 Jul 2021 16:14:25 +0800 Subject: [PATCH 49/64] =?UTF-8?q?=F0=9F=94=A5=20self-built=20app=20hasn't?= =?UTF-8?q?=20`MetaEvent`?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../nonebot-adapter-feishu/nonebot/adapters/feishu/event.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/event.py b/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/event.py index 36a86eed..55d5247f 100644 --- a/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/event.py +++ b/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/event.py @@ -237,11 +237,6 @@ class NoticeEvent(Event): ... -class MetaEvent(Event): - #TODO:实现该事件 - ... - - _t = StringTrie(separator=".") # define `model` first to avoid globals changing while `for` From c6921c731c47dcd663a857878b248004f94a0765 Mon Sep 17 00:00:00 2001 From: StarHeartHunt Date: Fri, 9 Jul 2021 16:15:24 +0800 Subject: [PATCH 50/64] =?UTF-8?q?=E2=9C=85=20update=20test=20file?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/test_plugins/test_feishu.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_plugins/test_feishu.py b/tests/test_plugins/test_feishu.py index eab47900..54a0d7ae 100644 --- a/tests/test_plugins/test_feishu.py +++ b/tests/test_plugins/test_feishu.py @@ -8,5 +8,5 @@ helper = on_command("say") @helper.handle() async def feishu_helper(bot: FeishuBot, event: MessageEvent): - message = event.get_plaintext() - await helper.finish(message) + message = event.get_message() + await helper.finish(message, at_sender=True) From 2c056364c1982868f3e3595ac2979f94369396d6 Mon Sep 17 00:00:00 2001 From: StarHeartHunt Date: Fri, 9 Jul 2021 16:18:19 +0800 Subject: [PATCH 51/64] =?UTF-8?q?=F0=9F=9A=A7=20message=20read=20event=20s?= =?UTF-8?q?hould=20implement=20`NoticeEvent`?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../nonebot/adapters/feishu/event.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/event.py b/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/event.py index 55d5247f..a31badab 100644 --- a/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/event.py +++ b/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/event.py @@ -216,6 +216,11 @@ class PrivateMessageEvent(MessageEvent): event: PrivateMessageEventDetail +class NoticeEvent(Event): + #TODO:实现该事件 + ... + + class MessageReader(BaseModel): reader_id: UserId read_time: str @@ -227,16 +232,11 @@ class MessageReadEventDetail(BaseModel): message_id_list: List[str] -class MessageReadEvent(Event): +class MessageReadEvent(NoticeEvent): __event__ = "im.message.message_read_v1" event: MessageReadEventDetail -class NoticeEvent(Event): - #TODO:实现该事件 - ... - - _t = StringTrie(separator=".") # define `model` first to avoid globals changing while `for` From 4fc655323d911ab49255af99e000b06b5aedd3d6 Mon Sep 17 00:00:00 2001 From: StarHeartHunt Date: Sun, 11 Jul 2021 12:25:27 +0800 Subject: [PATCH 52/64] =?UTF-8?q?=F0=9F=8E=A8=20format=20code?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../nonebot-adapter-feishu/nonebot/adapters/feishu/message.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/message.py b/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/message.py index f79c60bf..efcea8b1 100644 --- a/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/message.py +++ b/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/message.py @@ -239,7 +239,8 @@ class MessageDeserializer: return msg._merge() elif self.type == "text": for key, mention in dict_mention.items(): - self.data["text"] = self.data["text"].replace(key, f"@{mention['name']}") + self.data["text"] = self.data["text"].replace( + key, f"@{mention['name']}") self.data["mentions"] = dict_mention return Message(MessageSegment(self.type, self.data)) From 625c12fc12de56ccd4d0173a7f2bc973f5f55167 Mon Sep 17 00:00:00 2001 From: StarHeartHunt Date: Sun, 11 Jul 2021 12:26:11 +0800 Subject: [PATCH 53/64] =?UTF-8?q?=E2=9C=A8=20implement=20all=20self-built?= =?UTF-8?q?=20app=20event?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../nonebot/adapters/feishu/event.py | 163 +++++++++++++++++- 1 file changed, 159 insertions(+), 4 deletions(-) diff --git a/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/event.py b/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/event.py index a31badab..5abeda95 100644 --- a/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/event.py +++ b/packages/nonebot-adapter-feishu/nonebot/adapters/feishu/event.py @@ -1,7 +1,7 @@ import inspect import json -from typing import Any, List, Literal, Optional, Type +from typing import Any, Dict, List, Literal, Optional, Type from pygtrie import StringTrie from pydantic import BaseModel, root_validator, Field @@ -175,7 +175,7 @@ class MessageEvent(Event): reply: Optional[Reply] @overrides(Event) - def get_type(self) -> Literal["message", "notice", "meta_event"]: + def get_type(self) -> Literal["message", "notice"]: return "message" @overrides(Event) @@ -217,8 +217,35 @@ class PrivateMessageEvent(MessageEvent): class NoticeEvent(Event): - #TODO:实现该事件 - ... + event: Dict[str, Any] + + @overrides(Event) + def get_type(self) -> Literal["message", "notice"]: + return "notice" + + @overrides(Event) + def get_event_name(self) -> str: + raise ValueError("Event has no name!") + + @overrides(Event) + def get_event_description(self) -> str: + raise ValueError("Event has no description!") + + @overrides(Event) + def get_message(self) -> Message: + raise ValueError("Event has no message!") + + @overrides(Event) + def get_plaintext(self) -> str: + raise ValueError("Event has no plaintext!") + + @overrides(Event) + def get_user_id(self) -> str: + raise ValueError("Event has no user_id!") + + @overrides(Event) + def get_session_id(self) -> str: + raise ValueError("Event has no session_id!") class MessageReader(BaseModel): @@ -237,6 +264,134 @@ class MessageReadEvent(NoticeEvent): event: MessageReadEventDetail +class GroupDisbandedEventDetail(BaseModel): + chat_id: str + operator_id: UserId + external: bool + operator_tenant_key: str + + +class GroupDisbandedEvent(NoticeEvent): + __event__ = "im.chat.disbanded_v1" + event: GroupDisbandedEventDetail + + +class I18nNames(BaseModel): + zh_cn: str + en_us: str + ja_jp: str + + +class ChatChange(BaseModel): + avatar: str + name: str + description: str + i18n_names: I18nNames + add_member_permission: str + share_card_permission: str + at_all_permission: str + edit_permission: str + membership_approval: str + join_message_visibility: str + leave_message_visibility: str + moderation_permission: str + owner_id: UserId + + +class EventModerator(BaseModel): + tenant_key: str + user_id: UserId + + +class ModeratorList(BaseModel): + added_member_list: EventModerator + removed_member_list: EventModerator + + +class GroupConfigUpdatedEventDetail(BaseModel): + chat_id: str + operator_id: UserId + external: bool + operator_tenant_key: str + after_change: ChatChange + before_change: ChatChange + moderator_list: ModeratorList + + +class GroupConfigUpdatedEvent(NoticeEvent): + __event__ = "im.chat.updated_v1" + event: GroupConfigUpdatedEventDetail + + +class GroupMemberBotAddedEventDetail(BaseModel): + chat_id: str + operator_id: UserId + external: bool + operator_tenant_key: str + + +class GroupMemberBotAddedEvent(NoticeEvent): + __event__ = "im.chat.member.bot.added_v1" + event: GroupMemberBotAddedEventDetail + + +class GroupMemberBotDeletedEventDetail(BaseModel): + chat_id: str + operator_id: UserId + external: bool + operator_tenant_key: str + + +class GroupMemberBotDeletedEvent(NoticeEvent): + __event__ = "im.chat.member.bot.deleted_v1" + event: GroupMemberBotDeletedEventDetail + + +class ChatMemberUser(BaseModel): + name: str + tenant_key: str + user_id: UserId + + +class GroupMemberUserAddedEventDetail(BaseModel): + chat_id: str + operator_id: UserId + external: bool + operator_tenant_key: str + users: List[ChatMemberUser] + + +class GroupMemberUserAddedEvent(NoticeEvent): + __event__ = "im.chat.member.user.added_v1" + event: GroupMemberUserAddedEventDetail + + +class GroupMemberUserWithdrawnEventDetail(BaseModel): + chat_id: str + operator_id: UserId + external: bool + operator_tenant_key: str + users: List[ChatMemberUser] + + +class GroupMemberUserWithdrawnEvent(NoticeEvent): + __event__ = "im.chat.member.user.withdrawn_v1" + event: GroupMemberUserWithdrawnEventDetail + + +class GroupMemberUserDeletedEventDetail(BaseModel): + chat_id: str + operator_id: UserId + external: bool + operator_tenant_key: str + users: List[ChatMemberUser] + + +class GroupMemberUserDeletedEvent(NoticeEvent): + __event__ = "im.chat.member.user.deleted_v1" + event: GroupMemberUserDeletedEventDetail + + _t = StringTrie(separator=".") # define `model` first to avoid globals changing while `for` From 0436c1b7b8d4dc0845a55f525b1a4c8b9b54492c Mon Sep 17 00:00:00 2001 From: StarHeartHunt Date: Wed, 14 Jul 2021 09:26:53 +0800 Subject: [PATCH 54/64] =?UTF-8?q?=E2=9C=85=20update=20test=20env?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/.env.dev | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/.env.dev b/tests/.env.dev index 555cbe59..8e167d1d 100644 --- a/tests/.env.dev +++ b/tests/.env.dev @@ -16,3 +16,7 @@ CUSTOM_CONFIG3= MIRAI_AUTH_KEY=12345678 MIRAI_HOST=127.0.0.1 MIRAI_PORT=8080 + +APP_ID=111111111111 +APP_SECRET=222222222222222222 +VERIFICATION_TOKEN=3333333333333333333333333333 From aac4571d439fdf1638fe78f386a726ab676f10e4 Mon Sep 17 00:00:00 2001 From: StarHeartHunt Date: Wed, 14 Jul 2021 10:19:37 +0800 Subject: [PATCH 55/64] =?UTF-8?q?=F0=9F=93=9D=20add=20feishu=20adapter=20d?= =?UTF-8?q?oc?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/guide/feishu-guide.md | 57 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 docs/guide/feishu-guide.md diff --git a/docs/guide/feishu-guide.md b/docs/guide/feishu-guide.md new file mode 100644 index 00000000..342ea3af --- /dev/null +++ b/docs/guide/feishu-guide.md @@ -0,0 +1,57 @@ +# 飞书机器人使用指南 + +基于飞书开放平台事件回调与 API 进行机器人适配,目前仅适配企业自建应用。 + +## 安装 NoneBot 飞书 适配器 + +```bash +pip install nonebot-adapter-feishu +``` + +## 创建应用与启用应用“机器人”能力 + +::: tip +此部分可参考[飞书开放平台-快速开发机器人-创建应用](https://open.feishu.cn/document/home/develop-a-bot-in-5-minutes/create-an-app)部分文档。 + +::: + +## 开启应用权限 + +应用拥有所需权限后,才能调用飞书接口获取相关信息。如果需要用到所有飞书平台的 API,请开启所有应用权限。 + +在仅群聊功能的情况下,需要为应用开启用户、消息、通讯录和群聊权限组所有权限。 + +## 配置飞书事件订阅 + +::: tip + +在添加事件订阅时请注意,带有**(历史版本)**字样的事件的格式为**不受支持的旧版事件格式**,请使用对应的**新版事件(不带历史版本字样)作为替代**。 + +::: + +目前,飞书适配器支持以下事件: +| 事件名称 | 事件描述| +| ---- | ---- | +|接收消息|机器人接收到用户发送的消息。| +|消息已读|用户阅读机器人发送的单聊消息。| +|群解散|群组被解散。| +|群配置更改|群组配置被修改后触发此事件,包含:群主转移、群基本信息修改、群权限修改。| +|机器人进群|机器人被添加至群聊。| +|机器人被移出群|机器人被移出群聊。| +|用户进群|新用户进群。| +|撤销拉用户进群|撤销拉用户进群。| +|用户被移出群|用户主动退群或被移出群聊。| + +## 在 NoneBot 配置中添加相应配置 + +在 `.env` 文件中添加以下部分 + +``` +APP_ID= +APP_SECRET= +VERIFICATION_TOKEN= +``` + +复制所创建应用**“凭证和基础信息”**中的**App ID**与**App Secret**及**“事件订阅”**中的**Verification Token**,替换上面相应的配置的值。 + +大功告成!现在可以试试向机器人发送消息进行测试了。 From 758108229a25320ee81ab2d0103cc933b34be882 Mon Sep 17 00:00:00 2001 From: StarHeartHunt Date: Wed, 14 Jul 2021 10:22:31 +0800 Subject: [PATCH 56/64] =?UTF-8?q?=F0=9F=93=9D=20add=20feishu=20api=20doc?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs_build/adapters/feishu.rst | 48 ++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 docs_build/adapters/feishu.rst diff --git a/docs_build/adapters/feishu.rst b/docs_build/adapters/feishu.rst new file mode 100644 index 00000000..821b6908 --- /dev/null +++ b/docs_build/adapters/feishu.rst @@ -0,0 +1,48 @@ +--- +contentSidebar: true +sidebarDepth: 0 +--- + +NoneBot.adapters.feishu 模块 +=========================== + +.. automodule:: nonebot.adapters.feishu + +NoneBot.adapters.feishu.config 模块 +=================================== + +.. automodule:: nonebot.adapters.feishu.config + :members: + +NoneBot.adapters.feishu.exception 模块 +===================================== + +.. automodule:: nonebot.adapters.feishu.exception + :members: + :show-inheritance: + + +NoneBot.adapters.feishu.bot 模块 +=============================== + +.. automodule:: nonebot.adapters.feishu.bot + :members: + :private-members: + :show-inheritance: + + +NoneBot.adapters.feishu.message 模块 +=================================== + +.. automodule:: nonebot.adapters.feishu.message + :members: + :private-members: + :show-inheritance: + + +NoneBot.adapters.feishu.event 模块 +================================= + +.. automodule:: nonebot.adapters.feishu.event + :members: + :show-inheritance: From 607f33893418c0a6fe03e834709688a92cdc105d Mon Sep 17 00:00:00 2001 From: StarHeartHunt Date: Wed, 14 Jul 2021 02:24:35 +0000 Subject: [PATCH 57/64] :memo: update api docs --- docs/api/adapters/feishu.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 docs/api/adapters/feishu.md diff --git a/docs/api/adapters/feishu.md b/docs/api/adapters/feishu.md new file mode 100644 index 00000000..c6c9aae6 --- /dev/null +++ b/docs/api/adapters/feishu.md @@ -0,0 +1,16 @@ +--- +contentSidebar: true +sidebarDepth: 0 +--- + +# NoneBot.adapters.feishu 模块 + +# NoneBot.adapters.feishu.config 模块 + +# NoneBot.adapters.feishu.exception 模块 + +# NoneBot.adapters.feishu.bot 模块 + +# NoneBot.adapters.feishu.message 模块 + +# NoneBot.adapters.feishu.event 模块 From 3230dfc14a54655f0ca239b2d0856a69655020ec Mon Sep 17 00:00:00 2001 From: StarHeartHunt Date: Wed, 14 Jul 2021 10:28:48 +0800 Subject: [PATCH 58/64] =?UTF-8?q?=F0=9F=93=9D=20update=20main=20api=20doc?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs_build/README.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/docs_build/README.rst b/docs_build/README.rst index 745fc781..35d9368c 100644 --- a/docs_build/README.rst +++ b/docs_build/README.rst @@ -21,3 +21,4 @@ NoneBot Api Reference - `nonebot.adapters.cqhttp `_ - `nonebot.adapters.ding `_ - `nonebot.adapters.mirai `_ + - `nonebot.adapters.feishu `_ From 8acd561de6e01178c930f5a62cb2b8e8d1d5130e Mon Sep 17 00:00:00 2001 From: StarHeartHunt Date: Wed, 14 Jul 2021 02:31:27 +0000 Subject: [PATCH 59/64] :memo: update api docs --- docs/api/README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/api/README.md b/docs/api/README.md index 3d5a6497..26d4a0f5 100644 --- a/docs/api/README.md +++ b/docs/api/README.md @@ -59,3 +59,6 @@ * [nonebot.adapters.mirai](adapters/mirai.html) + + + * [nonebot.adapters.feishu](adapters/feishu.html) From d802fad111594e376a5f2a3a15ec3959bf4b56c9 Mon Sep 17 00:00:00 2001 From: StarHeartHunt Date: Wed, 14 Jul 2021 10:39:00 +0800 Subject: [PATCH 60/64] =?UTF-8?q?=F0=9F=93=9D=20update=20adapter=20store?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/.vuepress/public/adapters.json | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/docs/.vuepress/public/adapters.json b/docs/.vuepress/public/adapters.json index 4788e1b2..aa4c2b70 100644 --- a/docs/.vuepress/public/adapters.json +++ b/docs/.vuepress/public/adapters.json @@ -30,5 +30,13 @@ "desc": "在原 CQHTTP Adapter 的基础上进行修改以便更好地适配 go-cqhttp", "author": "Jigsaw111", "repo": "Jigsaw111/nonebot-adapter-gocq" + }, + { + "id": "nonebot_adapter_feishu", + "link": "nonebot-adapter-feishu", + "name": "feishu", + "desc": "飞书协议", + "author": "StarHeartHunt", + "repo": "nonebot/nonebot2/tree/master/packages/nonebot-adapter-feishu" } -] \ No newline at end of file +] From 66b54de86927804b75b5f655eb406b742df2f406 Mon Sep 17 00:00:00 2001 From: StarHeartHunt Date: Wed, 14 Jul 2021 10:39:39 +0800 Subject: [PATCH 61/64] =?UTF-8?q?=F0=9F=93=9D=20update=20copyright?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/README.md b/docs/README.md index c96de429..1ee526fb 100644 --- a/docs/README.md +++ b/docs/README.md @@ -11,5 +11,5 @@ features: details: 精心设计的消息处理流程使得你可以很方便地将原型扩充为具有大量实用功能的完整聊天机器人,并持续保证扩展性。 - title: 高性能 details: 采用异步 I/O,利用 WebSocket 进行通信,以获得极高的性能;同时,支持使用多账号同时接入,减少业务宕机的可能。 -footer: MIT Licensed | Copyright © 2018 - 2020 NoneBot Team +footer: MIT Licensed | Copyright © 2018 - 2021 NoneBot Team --- From f2295318a9dd247d3cf10368bc34fa12360a38fd Mon Sep 17 00:00:00 2001 From: StarHeartHunt Date: Wed, 14 Jul 2021 10:42:47 +0800 Subject: [PATCH 62/64] =?UTF-8?q?=F0=9F=93=9D=20update=20index?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/.vuepress/config.js | 128 +++++++++++++++++++++------------------ 1 file changed, 68 insertions(+), 60 deletions(-) diff --git a/docs/.vuepress/config.js b/docs/.vuepress/config.js index fdfac5b0..b8c82c43 100644 --- a/docs/.vuepress/config.js +++ b/docs/.vuepress/config.js @@ -1,11 +1,11 @@ const path = require("path"); -module.exports = context => ({ +module.exports = (context) => ({ base: process.env.VUEPRESS_BASE || "/", title: "NoneBot", description: "跨平台 Python 异步 QQ 机器人框架", markdown: { - lineNumbers: true + lineNumbers: true, }, /** * Extra tags to be injected to the page HTML `` @@ -21,43 +21,42 @@ module.exports = context => ({ ["meta", { name: "apple-mobile-web-app-capable", content: "yes" }], [ "meta", - { name: "apple-mobile-web-app-status-bar-style", content: "black" } + { name: "apple-mobile-web-app-status-bar-style", content: "black" }, ], [ "link", - { rel: "apple-touch-icon", href: "/icons/apple-touch-icon-180x180.png" } + { rel: "apple-touch-icon", href: "/icons/apple-touch-icon-180x180.png" }, ], [ "link", { rel: "mask-icon", href: "/icons/safari-pinned-tab.svg", - color: "#ea5252" - } + color: "#ea5252", + }, ], [ "meta", { name: "msapplication-TileImage", - content: "/icons/mstile-150x150.png" - } + content: "/icons/mstile-150x150.png", + }, ], ["meta", { name: "msapplication-TileColor", content: "#ea5252" }], [ "link", { rel: "stylesheet", - href: - "https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free@5/css/all.min.css" - } - ] + href: "https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free@5/css/all.min.css", + }, + ], ], locales: { "/": { lang: "zh-CN", title: "NoneBot", - description: "跨平台 Python 异步 QQ 机器人框架" - } + description: "跨平台 Python 异步 QQ 机器人框架", + }, }, theme: "nonebot", @@ -83,7 +82,7 @@ module.exports = context => ({ { text: "进阶", link: "/advanced/" }, { text: "API", link: "/api/" }, { text: "商店", link: "/store" }, - { text: "更新日志", link: "/changelog" } + { text: "更新日志", link: "/changelog" }, ], sidebarDepth: 2, sidebar: { @@ -97,8 +96,8 @@ module.exports = context => ({ "installation", "getting-started", "creating-a-project", - "basic-configuration" - ] + "basic-configuration", + ], }, { title: "编写插件", @@ -109,15 +108,20 @@ module.exports = context => ({ "creating-a-plugin", "creating-a-matcher", "creating-a-handler", - "end-or-start" - ] + "end-or-start", + ], }, { title: "协议适配", collapsable: false, sidebar: "auto", - children: ["cqhttp-guide", "ding-guide", "mirai-guide"] - } + children: [ + "cqhttp-guide", + "ding-guide", + "mirai-guide", + "feishu-guide", + ], + }, ], "/advanced/": [ { @@ -130,15 +134,15 @@ module.exports = context => ({ "permission", "runtime-hook", "export-and-require", - "overloaded-handlers" - ] + "overloaded-handlers", + ], }, { title: "发布", collapsable: false, sidebar: "auto", - children: ["publish-plugin"] - } + children: ["publish-plugin"], + }, ], "/api/": [ { @@ -148,86 +152,90 @@ module.exports = context => ({ children: [ { title: "nonebot 模块", - path: "nonebot" + path: "nonebot", }, { title: "nonebot.config 模块", - path: "config" + path: "config", }, { title: "nonebot.plugin 模块", - path: "plugin" + path: "plugin", }, { title: "nonebot.message 模块", - path: "message" + path: "message", }, { title: "nonebot.matcher 模块", - path: "matcher" + path: "matcher", }, { title: "nonebot.handler 模块", - path: "handler" + path: "handler", }, { title: "nonebot.rule 模块", - path: "rule" + path: "rule", }, { title: "nonebot.permission 模块", - path: "permission" + path: "permission", }, { title: "nonebot.log 模块", - path: "log" + path: "log", }, { title: "nonebot.utils 模块", - path: "utils" + path: "utils", }, { title: "nonebot.typing 模块", - path: "typing" + path: "typing", }, { title: "nonebot.exception 模块", - path: "exception" + path: "exception", }, { title: "nonebot.drivers 模块", - path: "drivers/" + path: "drivers/", }, { title: "nonebot.drivers.fastapi 模块", - path: "drivers/fastapi" + path: "drivers/fastapi", }, { title: "nonebot.drivers.quart 模块", - path: "drivers/quart" + path: "drivers/quart", }, { title: "nonebot.adapters 模块", - path: "adapters/" + path: "adapters/", }, { title: "nonebot.adapters.cqhttp 模块", - path: "adapters/cqhttp" + path: "adapters/cqhttp", }, { title: "nonebot.adapters.ding 模块", - path: "adapters/ding" + path: "adapters/ding", }, { title: "nonebot.adapters.mirai 模块", - path: "adapters/mirai" - } - ] - } - ] - } - } - } + path: "adapters/mirai", + }, + { + title: "nonebot.adapters.feishu 模块", + path: "adapters/feishu", + }, + ], + }, + ], + }, + }, + }, }, plugins: [ @@ -239,9 +247,9 @@ module.exports = context => ({ serviceWorker: true, updatePopup: { message: "发现新内容", - buttonText: "刷新" - } - } + buttonText: "刷新", + }, + }, ], [ "versioning", @@ -250,16 +258,16 @@ module.exports = context => ({ pagesSourceDir: path.resolve(context.sourceDir, "..", "pages"), onNewVersion(version, versionDestPath) { console.log(`Created version ${version} in ${versionDestPath}`); - } - } + }, + }, ], [ "container", { type: "vue", before: '
',
-        after: "
" - } - ] - ] + after: "", + }, + ], + ], }); From a14d475de24a8c799b66c5448c86419fa6a9829c Mon Sep 17 00:00:00 2001 From: yanyongyu Date: Sun, 18 Jul 2021 15:07:30 +0800 Subject: [PATCH 63/64] :art: format and update lock file --- docs/.vuepress/config.js | 3 +- packages/nonebot-adapter-feishu/poetry.lock | 30 ++--- .../nonebot-adapter-feishu/pyproject.toml | 4 +- poetry.lock | 112 ++++++++++++------ 4 files changed, 97 insertions(+), 52 deletions(-) diff --git a/docs/.vuepress/config.js b/docs/.vuepress/config.js index b8c82c43..f3fd32b6 100644 --- a/docs/.vuepress/config.js +++ b/docs/.vuepress/config.js @@ -47,7 +47,8 @@ module.exports = (context) => ({ "link", { rel: "stylesheet", - href: "https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free@5/css/all.min.css", + href: + "https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free@5/css/all.min.css", }, ], ], diff --git a/packages/nonebot-adapter-feishu/poetry.lock b/packages/nonebot-adapter-feishu/poetry.lock index 15b8f951..7bfe5f9a 100644 --- a/packages/nonebot-adapter-feishu/poetry.lock +++ b/packages/nonebot-adapter-feishu/poetry.lock @@ -58,7 +58,7 @@ reference = "aliyun" [[package]] name = "fastapi" -version = "0.65.2" +version = "0.65.3" description = "FastAPI framework, high performance, easy to learn, fast to code, ready for production" category = "main" optional = false @@ -71,7 +71,7 @@ starlette = "0.14.2" [package.extras] all = ["requests (>=2.24.0,<3.0.0)", "aiofiles (>=0.5.0,<0.6.0)", "jinja2 (>=2.11.2,<3.0.0)", "python-multipart (>=0.0.5,<0.0.6)", "itsdangerous (>=1.1.0,<2.0.0)", "pyyaml (>=5.3.1,<6.0.0)", "graphene (>=2.1.8,<3.0.0)", "ujson (>=4.0.1,<5.0.0)", "orjson (>=3.2.1,<4.0.0)", "email_validator (>=1.1.1,<2.0.0)", "uvicorn[standard] (>=0.12.0,<0.14.0)", "async_exit_stack (>=1.0.1,<2.0.0)", "async_generator (>=1.10,<2.0.0)"] dev = ["python-jose[cryptography] (>=3.1.0,<4.0.0)", "passlib[bcrypt] (>=1.7.2,<2.0.0)", "autoflake (>=1.3.1,<2.0.0)", "flake8 (>=3.8.3,<4.0.0)", "uvicorn[standard] (>=0.12.0,<0.14.0)", "graphene (>=2.1.8,<3.0.0)"] -doc = ["mkdocs (>=1.1.2,<2.0.0)", "mkdocs-material (>=6.1.4,<7.0.0)", "markdown-include (>=0.5.1,<0.6.0)", "mkdocs-markdownextradata-plugin (>=0.1.7,<0.2.0)", "typer-cli (>=0.0.9,<0.0.10)", "pyyaml (>=5.3.1,<6.0.0)"] +doc = ["mkdocs (>=1.1.2,<2.0.0)", "mkdocs-material (>=7.1.9,<8.0.0)", "markdown-include (>=0.6.0,<0.7.0)", "mkdocs-markdownextradata-plugin (>=0.1.7,<0.2.0)", "typer-cli (>=0.0.12,<0.0.13)", "pyyaml (>=5.3.1,<6.0.0)"] test = ["pytest (==5.4.3)", "pytest-cov (==2.10.0)", "pytest-asyncio (>=0.14.0,<0.15.0)", "mypy (==0.812)", "flake8 (>=3.8.3,<4.0.0)", "black (==20.8b1)", "isort (>=5.0.6,<6.0.0)", "requests (>=2.24.0,<3.0.0)", "httpx (>=0.14.0,<0.15.0)", "email_validator (>=1.1.1,<2.0.0)", "sqlalchemy (>=1.3.18,<1.4.0)", "peewee (>=3.13.3,<4.0.0)", "databases[sqlite] (>=0.3.2,<0.4.0)", "orjson (>=3.2.1,<4.0.0)", "ujson (>=4.0.1,<5.0.0)", "async_exit_stack (>=1.0.1,<2.0.0)", "async_generator (>=1.10,<2.0.0)", "python-multipart (>=0.0.5,<0.0.6)", "aiofiles (>=0.5.0,<0.6.0)", "flask (>=1.1.2,<2.0.0)"] [package.source] @@ -390,7 +390,7 @@ reference = "aliyun" [[package]] name = "uvloop" -version = "0.15.2" +version = "0.15.3" description = "Fast implementation of asyncio event loop on top of libuv" category = "main" optional = false @@ -471,8 +471,8 @@ colorama = [ {file = "colorama-0.4.4.tar.gz", hash = "sha256:5941b2b48a20143d2267e95b1c2a7603ce057ee39fd88e7329b0c292aa16869b"}, ] fastapi = [ - {file = "fastapi-0.65.2-py3-none-any.whl", hash = "sha256:39569a18914075b2f1aaa03bcb9dc96a38e0e5dabaf3972e088c9077dfffa379"}, - {file = "fastapi-0.65.2.tar.gz", hash = "sha256:8359e55d8412a5571c0736013d90af235d6949ec4ce978e9b63500c8f4b6f714"}, + {file = "fastapi-0.65.3-py3-none-any.whl", hash = "sha256:d3e3c0ac35110efb22ee3ed28201cf32f9d11a9a0e52d7dd676cad25f5219523"}, + {file = "fastapi-0.65.3.tar.gz", hash = "sha256:6ea2286e439c4ced7cce2b2862c25859601bf327a515c12dd6e431ef5d49d12f"}, ] h11 = [ {file = "h11-0.12.0-py3-none-any.whl", hash = "sha256:36a3cb8c0a032f56e2da7084577878a035d3b61d104230d4bd49c0c6b555a9c6"}, @@ -632,16 +632,16 @@ uvicorn = [ {file = "uvicorn-0.13.4.tar.gz", hash = "sha256:3292251b3c7978e8e4a7868f4baf7f7f7bb7e40c759ecc125c37e99cdea34202"}, ] uvloop = [ - {file = "uvloop-0.15.2-cp37-cp37m-macosx_10_14_x86_64.whl", hash = "sha256:19fa1d56c91341318ac5d417e7b61c56e9a41183946cc70c411341173de02c69"}, - {file = "uvloop-0.15.2-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:e5e5f855c9bf483ee6cd1eb9a179b740de80cb0ae2988e3fa22309b78e2ea0e7"}, - {file = "uvloop-0.15.2-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:42eda9f525a208fbc4f7cecd00fa15c57cc57646c76632b3ba2fe005004f051d"}, - {file = "uvloop-0.15.2-cp38-cp38-macosx_10_14_x86_64.whl", hash = "sha256:90e56f17755e41b425ad19a08c41dc358fa7bf1226c0f8e54d4d02d556f7af7c"}, - {file = "uvloop-0.15.2-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:7ae39b11a5f4cec1432d706c21ecc62f9e04d116883178b09671aa29c46f7a47"}, - {file = "uvloop-0.15.2-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:b45218c99795803fb8bdbc9435ff7f54e3a591b44cd4c121b02fa83affb61c7c"}, - {file = "uvloop-0.15.2-cp39-cp39-macosx_10_14_x86_64.whl", hash = "sha256:114543c84e95df1b4ff546e6e3a27521580466a30127f12172a3278172ad68bc"}, - {file = "uvloop-0.15.2-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:44cac8575bf168601424302045234d74e3561fbdbac39b2b54cc1d1d00b70760"}, - {file = "uvloop-0.15.2-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:6de130d0cb78985a5d080e323b86c5ecaf3af82f4890492c05981707852f983c"}, - {file = "uvloop-0.15.2.tar.gz", hash = "sha256:2bb0624a8a70834e54dde8feed62ed63b50bad7a1265c40d6403a2ac447bce01"}, + {file = "uvloop-0.15.3-cp37-cp37m-macosx_10_14_x86_64.whl", hash = "sha256:e71fb9038bfcd7646ca126c5ef19b17e48d4af9e838b2bcfda7a9f55a6552a32"}, + {file = "uvloop-0.15.3-cp37-cp37m-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:7522df4e45e4f25b50adbbbeb5bb9847495c438a628177099d2721f2751ff825"}, + {file = "uvloop-0.15.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ae2b325c0f6d748027f7463077e457006b4fdb35a8788f01754aadba825285ee"}, + {file = "uvloop-0.15.3-cp38-cp38-macosx_10_14_x86_64.whl", hash = "sha256:0de811931e90ae2da9e19ce70ffad73047ab0c1dba7c6e74f9ae1a3aabeb89bd"}, + {file = "uvloop-0.15.3-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:7f4b8a905df909a407c5791fb582f6c03b0d3b491ecdc1cdceaefbc9bf9e08f6"}, + {file = "uvloop-0.15.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2d8ffe44ae709f839c54bacf14ed283f41bee90430c3b398e521e10f8d117b3a"}, + {file = "uvloop-0.15.3-cp39-cp39-macosx_10_14_x86_64.whl", hash = "sha256:63a3288abbc9c8ee979d7e34c34e780b2fbab3e7e53d00b6c80271119f277399"}, + {file = "uvloop-0.15.3-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:5cda65fc60a645470b8525ce014516b120b7057b576fa876cdfdd5e60ab1efbb"}, + {file = "uvloop-0.15.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1ff05116ede1ebdd81802df339e5b1d4cab1dfbd99295bf27e90b4cec64d70e9"}, + {file = "uvloop-0.15.3.tar.gz", hash = "sha256:905f0adb0c09c9f44222ee02f6b96fd88b493478fffb7a345287f9444e926030"}, ] watchgod = [ {file = "watchgod-0.7-py3-none-any.whl", hash = "sha256:d6c1ea21df37847ac0537ca0d6c2f4cdf513562e95f77bb93abbcf05573407b7"}, diff --git a/packages/nonebot-adapter-feishu/pyproject.toml b/packages/nonebot-adapter-feishu/pyproject.toml index 86536fa1..ba3041b0 100644 --- a/packages/nonebot-adapter-feishu/pyproject.toml +++ b/packages/nonebot-adapter-feishu/pyproject.toml @@ -25,9 +25,9 @@ exclude = ["nonebot/__init__.py", "nonebot/adapters/__init__.py"] [tool.poetry.dependencies] python = "^3.7.3" httpx = "^0.17.0" -nonebot2 = "^2.0.0-alpha.13" -pycryptodome = "^3.10.1" aiocache = "^0.11.1" +pycryptodome = "^3.10.1" +nonebot2 = "^2.0.0-alpha.13" [tool.poetry.dev-dependencies] nonebot2 = { path = "../../", develop = true } diff --git a/poetry.lock b/poetry.lock index 0f94350d..8b7d2e3b 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,3 +1,22 @@ +[[package]] +name = "aiocache" +version = "0.11.1" +description = "multi backend asyncio cache" +category = "dev" +optional = false +python-versions = "*" + +[package.extras] +dev = ["asynctest (>=0.11.0)", "codecov", "coverage", "flake8", "ipdb", "marshmallow", "pystache", "pytest", "pytest-asyncio", "pytest-mock", "sphinx", "sphinx-autobuild", "sphinx-rtd-theme", "black"] +memcached = ["aiomcache (>=0.5.2)"] +msgpack = ["msgpack (>=0.5.5)"] +redis = ["aioredis (>=0.3.3)", "aioredis (>=1.0.0)"] + +[package.source] +type = "legacy" +url = "https://mirrors.aliyun.com/pypi/simple" +reference = "aliyun" + [[package]] name = "aiofiles" version = "0.6.0" @@ -127,7 +146,7 @@ name = "chardet" version = "4.0.0" description = "Universal encoding detector for Python 2 and 3" category = "main" -optional = false +optional = true python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" [package.source] @@ -135,6 +154,22 @@ type = "legacy" url = "https://mirrors.aliyun.com/pypi/simple" reference = "aliyun" +[[package]] +name = "charset-normalizer" +version = "2.0.3" +description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." +category = "dev" +optional = false +python-versions = ">=3.5.0" + +[package.extras] +unicode_backport = ["unicodedata2"] + +[package.source] +type = "legacy" +url = "https://mirrors.aliyun.com/pypi/simple" +reference = "aliyun" + [[package]] name = "click" version = "7.1.2" @@ -176,7 +211,7 @@ reference = "aliyun" [[package]] name = "fastapi" -version = "0.65.2" +version = "0.65.3" description = "FastAPI framework, high performance, easy to learn, fast to code, ready for production" category = "main" optional = false @@ -189,7 +224,7 @@ starlette = "0.14.2" [package.extras] all = ["requests (>=2.24.0,<3.0.0)", "aiofiles (>=0.5.0,<0.6.0)", "jinja2 (>=2.11.2,<3.0.0)", "python-multipart (>=0.0.5,<0.0.6)", "itsdangerous (>=1.1.0,<2.0.0)", "pyyaml (>=5.3.1,<6.0.0)", "graphene (>=2.1.8,<3.0.0)", "ujson (>=4.0.1,<5.0.0)", "orjson (>=3.2.1,<4.0.0)", "email_validator (>=1.1.1,<2.0.0)", "uvicorn[standard] (>=0.12.0,<0.14.0)", "async_exit_stack (>=1.0.1,<2.0.0)", "async_generator (>=1.10,<2.0.0)"] dev = ["python-jose[cryptography] (>=3.1.0,<4.0.0)", "passlib[bcrypt] (>=1.7.2,<2.0.0)", "autoflake (>=1.3.1,<2.0.0)", "flake8 (>=3.8.3,<4.0.0)", "uvicorn[standard] (>=0.12.0,<0.14.0)", "graphene (>=2.1.8,<3.0.0)"] -doc = ["mkdocs (>=1.1.2,<2.0.0)", "mkdocs-material (>=6.1.4,<7.0.0)", "markdown-include (>=0.5.1,<0.6.0)", "mkdocs-markdownextradata-plugin (>=0.1.7,<0.2.0)", "typer-cli (>=0.0.9,<0.0.10)", "pyyaml (>=5.3.1,<6.0.0)"] +doc = ["mkdocs (>=1.1.2,<2.0.0)", "mkdocs-material (>=7.1.9,<8.0.0)", "markdown-include (>=0.6.0,<0.7.0)", "mkdocs-markdownextradata-plugin (>=0.1.7,<0.2.0)", "typer-cli (>=0.0.12,<0.0.13)", "pyyaml (>=5.3.1,<6.0.0)"] test = ["pytest (==5.4.3)", "pytest-cov (==2.10.0)", "pytest-asyncio (>=0.14.0,<0.15.0)", "mypy (==0.812)", "flake8 (>=3.8.3,<4.0.0)", "black (==20.8b1)", "isort (>=5.0.6,<6.0.0)", "requests (>=2.24.0,<3.0.0)", "httpx (>=0.14.0,<0.15.0)", "email_validator (>=1.1.1,<2.0.0)", "sqlalchemy (>=1.3.18,<1.4.0)", "peewee (>=3.13.3,<4.0.0)", "databases[sqlite] (>=0.3.2,<0.4.0)", "orjson (>=3.2.1,<4.0.0)", "ujson (>=4.0.1,<5.0.0)", "async_exit_stack (>=1.0.1,<2.0.0)", "async_generator (>=1.10,<2.0.0)", "python-multipart (>=0.0.5,<0.0.6)", "aiofiles (>=0.5.0,<0.6.0)", "flask (>=1.1.2,<2.0.0)"] [package.source] @@ -354,11 +389,11 @@ reference = "aliyun" [[package]] name = "idna" -version = "2.10" +version = "3.2" description = "Internationalized Domain Names in Applications (IDNA)" category = "main" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +python-versions = ">=3.5" [package.source] type = "legacy" @@ -500,6 +535,7 @@ python-versions = "^3.7.3" develop = true [package.dependencies] +aiocache = "^0.11.1" httpx = "^0.17.0" nonebot2 = "^2.0.0-alpha.13" pycryptodome = "^3.10.1" @@ -546,11 +582,11 @@ reference = "aliyun" [[package]] name = "packaging" -version = "20.9" +version = "21.0" description = "Core utilities for Python packages" category = "dev" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +python-versions = ">=3.6" [package.dependencies] pyparsing = ">=2.0.2" @@ -609,7 +645,7 @@ reference = "aliyun" [[package]] name = "pydash" -version = "5.0.1" +version = "5.0.2" description = "The kitchen sink of Python utility libraries for doing \"stuff\" in a functional way. Based on the Lo-Dash Javascript library." category = "dev" optional = false @@ -774,21 +810,21 @@ reference = "aliyun" [[package]] name = "requests" -version = "2.25.1" +version = "2.26.0" description = "Python HTTP for Humans." category = "dev" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" [package.dependencies] certifi = ">=2017.4.17" -chardet = ">=3.0.2,<5" -idna = ">=2.5,<3" +charset-normalizer = {version = ">=2.0.0,<2.1.0", markers = "python_version >= \"3\""} +idna = {version = ">=2.5,<4", markers = "python_version >= \"3\""} urllib3 = ">=1.21.1,<1.27" [package.extras] -security = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)"] socks = ["PySocks (>=1.5.6,!=1.5.7)", "win-inet-pton"] +use_chardet_on_py3 = ["chardet (>=3.0.2,<5)"] [package.source] type = "legacy" @@ -1144,7 +1180,7 @@ reference = "aliyun" [[package]] name = "uvloop" -version = "0.15.2" +version = "0.15.3" description = "Fast implementation of asyncio event loop on top of libuv" category = "main" optional = false @@ -1276,6 +1312,10 @@ python-versions = "^3.7.3" content-hash = "da87abf42c19eca5a1c943ae53d12b57121dda28dcb80533373568aed6c76470" [metadata.files] +aiocache = [ + {file = "aiocache-0.11.1-py2.py3-none-any.whl", hash = "sha256:e55c7caaa5753794fd301c3a2e592737fa1d036db9f8d04ae154facdfb48a157"}, + {file = "aiocache-0.11.1.tar.gz", hash = "sha256:f2ebe0b05cec45782e7b5ea0bb74640f157dd4bb1028b4565364dda9fe33be7f"}, +] aiofiles = [ {file = "aiofiles-0.6.0-py3-none-any.whl", hash = "sha256:bd3019af67f83b739f8e4053c6c0512a7f545b9a8d91aaeab55e6e0f9d123c27"}, {file = "aiofiles-0.6.0.tar.gz", hash = "sha256:e0281b157d3d5d59d803e3f4557dcc9a3dff28a4dd4829a9ff478adae50ca092"}, @@ -1346,6 +1386,10 @@ chardet = [ {file = "chardet-4.0.0-py2.py3-none-any.whl", hash = "sha256:f864054d66fd9118f2e67044ac8981a54775ec5b67aed0441892edb553d21da5"}, {file = "chardet-4.0.0.tar.gz", hash = "sha256:0d6f53a15db4120f2b08c94f11e7d93d2c911ee118b6b30a04ec3ee8310179fa"}, ] +charset-normalizer = [ + {file = "charset-normalizer-2.0.3.tar.gz", hash = "sha256:c46c3ace2d744cfbdebceaa3c19ae691f53ae621b39fd7570f59d14fb7f2fd12"}, + {file = "charset_normalizer-2.0.3-py3-none-any.whl", hash = "sha256:88fce3fa5b1a84fdcb3f603d889f723d1dd89b26059d0123ca435570e848d5e1"}, +] click = [ {file = "click-7.1.2-py2.py3-none-any.whl", hash = "sha256:dacca89f4bfadd5de3d7489b7c8a566eee0d3676333fbb50030263894c38c0dc"}, {file = "click-7.1.2.tar.gz", hash = "sha256:d2b5255c7c6349bc1bd1e59e08cd12acbbd63ce649f2588755783aa94dfb6b1a"}, @@ -1359,8 +1403,8 @@ docutils = [ {file = "docutils-0.16.tar.gz", hash = "sha256:c2de3a60e9e7d07be26b7f2b00ca0309c207e06c100f9cc2a94931fc75a478fc"}, ] fastapi = [ - {file = "fastapi-0.65.2-py3-none-any.whl", hash = "sha256:39569a18914075b2f1aaa03bcb9dc96a38e0e5dabaf3972e088c9077dfffa379"}, - {file = "fastapi-0.65.2.tar.gz", hash = "sha256:8359e55d8412a5571c0736013d90af235d6949ec4ce978e9b63500c8f4b6f714"}, + {file = "fastapi-0.65.3-py3-none-any.whl", hash = "sha256:d3e3c0ac35110efb22ee3ed28201cf32f9d11a9a0e52d7dd676cad25f5219523"}, + {file = "fastapi-0.65.3.tar.gz", hash = "sha256:6ea2286e439c4ced7cce2b2862c25859601bf327a515c12dd6e431ef5d49d12f"}, ] h11 = [ {file = "h11-0.12.0-py3-none-any.whl", hash = "sha256:36a3cb8c0a032f56e2da7084577878a035d3b61d104230d4bd49c0c6b555a9c6"}, @@ -1412,8 +1456,8 @@ hyperframe = [ {file = "hyperframe-6.0.1.tar.gz", hash = "sha256:ae510046231dc8e9ecb1a6586f63d2347bf4c8905914aa84ba585ae85f28a914"}, ] idna = [ - {file = "idna-2.10-py2.py3-none-any.whl", hash = "sha256:b97d804b1e9b523befed77c48dacec60e6dcb0b5391d57af6a65a312a90648c0"}, - {file = "idna-2.10.tar.gz", hash = "sha256:b307872f855b18632ce0c21c5e45be78c0ea7ae4c15c828c20788b26921eb3f6"}, + {file = "idna-3.2-py3-none-any.whl", hash = "sha256:14475042e284991034cb48e06f6851428fb14c4dc953acd9be9a5e95c7b6dd7a"}, + {file = "idna-3.2.tar.gz", hash = "sha256:467fbad99067910785144ce333826c71fb0e63a425657295239737f7ecd125f3"}, ] imagesize = [ {file = "imagesize-1.2.0-py2.py3-none-any.whl", hash = "sha256:6965f19a6a2039c7d48bca7dba2473069ff854c36ae6f19d2cde309d998228a1"}, @@ -1515,8 +1559,8 @@ nonebot-plugin-test = [ {file = "nonebot_plugin_test-0.2.0-py3-none-any.whl", hash = "sha256:75cd18cc282815a03250bb86c7d2a8d6a66a5064ac335bedc9a3e268a1e7dd13"}, ] packaging = [ - {file = "packaging-20.9-py2.py3-none-any.whl", hash = "sha256:67714da7f7bc052e064859c05c595155bd1ee9f69f76557e21f051443c20947a"}, - {file = "packaging-20.9.tar.gz", hash = "sha256:5b327ac1320dc863dca72f4514ecc086f31186744b84a230374cc1fd776feae5"}, + {file = "packaging-21.0-py3-none-any.whl", hash = "sha256:c86254f9220d55e31cc94d69bade760f0847da8000def4dfe1c6b872fd14ff14"}, + {file = "packaging-21.0.tar.gz", hash = "sha256:7dc96269f53a4ccec5c0670940a4281106dd0bb343f47b7471f779df49c2fbe7"}, ] priority = [ {file = "priority-2.0.0-py3-none-any.whl", hash = "sha256:6f8eefce5f3ad59baf2c080a664037bb4725cd0a790d53d59ab4059288faf6aa"}, @@ -1579,8 +1623,8 @@ pydantic = [ {file = "pydantic-1.8.2.tar.gz", hash = "sha256:26464e57ccaafe72b7ad156fdaa4e9b9ef051f69e175dbbb463283000c05ab7b"}, ] pydash = [ - {file = "pydash-5.0.1-py3-none-any.whl", hash = "sha256:70ac35e71722c50e8444675e2446d6b81f412efeee52765aae86c84a9f3a7a94"}, - {file = "pydash-5.0.1.tar.gz", hash = "sha256:95234efc2f4bc11d32a9f7f9b7199a54ea46a596e842d0df645d3401f5393d50"}, + {file = "pydash-5.0.2-py3-none-any.whl", hash = "sha256:a0dfc36087b491653c7fbff4a04a52e1b58b67d3aa751d15e0dbb96fb7e09833"}, + {file = "pydash-5.0.2.tar.gz", hash = "sha256:7c02f5c27524abbd90743c4b60fd8c8c8e846ee0439642704f77a3cf21f7c371"}, ] pygments = [ {file = "Pygments-2.9.0-py3-none-any.whl", hash = "sha256:d66e804411278594d764fc69ec36ec13d9ae9147193a1740cd34d272ca383b8e"}, @@ -1645,8 +1689,8 @@ quart = [ {file = "Quart-0.14.1.tar.gz", hash = "sha256:429c5b4ff27e1d2f9ca0aacc38f6aba0ff49b38b815448bf24b613d3de12ea02"}, ] requests = [ - {file = "requests-2.25.1-py2.py3-none-any.whl", hash = "sha256:c210084e36a42ae6b9219e00e48287def368a26d03a048ddad7bfee44f75871e"}, - {file = "requests-2.25.1.tar.gz", hash = "sha256:27973dd4a904a4f13b263a19c866c13b92a39ed1c964655f025f3f8d3d75b804"}, + {file = "requests-2.26.0-py2.py3-none-any.whl", hash = "sha256:6c1246513ecd5ecd4528a0906f910e8f0f9c6b8ec72030dc9fd154dc1a6efd24"}, + {file = "requests-2.26.0.tar.gz", hash = "sha256:b8aa58f8cf793ffd8782d3d8cb19e66ef36f7aba4353eec859e74678b01b07a7"}, ] rfc3986 = [ {file = "rfc3986-1.5.0-py2.py3-none-any.whl", hash = "sha256:a86d6e1f5b1dc238b218b012df0aa79409667bb209e58da56d0b94704e712a97"}, @@ -1725,16 +1769,16 @@ uvicorn = [ {file = "uvicorn-0.13.4.tar.gz", hash = "sha256:3292251b3c7978e8e4a7868f4baf7f7f7bb7e40c759ecc125c37e99cdea34202"}, ] uvloop = [ - {file = "uvloop-0.15.2-cp37-cp37m-macosx_10_14_x86_64.whl", hash = "sha256:19fa1d56c91341318ac5d417e7b61c56e9a41183946cc70c411341173de02c69"}, - {file = "uvloop-0.15.2-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:e5e5f855c9bf483ee6cd1eb9a179b740de80cb0ae2988e3fa22309b78e2ea0e7"}, - {file = "uvloop-0.15.2-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:42eda9f525a208fbc4f7cecd00fa15c57cc57646c76632b3ba2fe005004f051d"}, - {file = "uvloop-0.15.2-cp38-cp38-macosx_10_14_x86_64.whl", hash = "sha256:90e56f17755e41b425ad19a08c41dc358fa7bf1226c0f8e54d4d02d556f7af7c"}, - {file = "uvloop-0.15.2-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:7ae39b11a5f4cec1432d706c21ecc62f9e04d116883178b09671aa29c46f7a47"}, - {file = "uvloop-0.15.2-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:b45218c99795803fb8bdbc9435ff7f54e3a591b44cd4c121b02fa83affb61c7c"}, - {file = "uvloop-0.15.2-cp39-cp39-macosx_10_14_x86_64.whl", hash = "sha256:114543c84e95df1b4ff546e6e3a27521580466a30127f12172a3278172ad68bc"}, - {file = "uvloop-0.15.2-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:44cac8575bf168601424302045234d74e3561fbdbac39b2b54cc1d1d00b70760"}, - {file = "uvloop-0.15.2-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:6de130d0cb78985a5d080e323b86c5ecaf3af82f4890492c05981707852f983c"}, - {file = "uvloop-0.15.2.tar.gz", hash = "sha256:2bb0624a8a70834e54dde8feed62ed63b50bad7a1265c40d6403a2ac447bce01"}, + {file = "uvloop-0.15.3-cp37-cp37m-macosx_10_14_x86_64.whl", hash = "sha256:e71fb9038bfcd7646ca126c5ef19b17e48d4af9e838b2bcfda7a9f55a6552a32"}, + {file = "uvloop-0.15.3-cp37-cp37m-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:7522df4e45e4f25b50adbbbeb5bb9847495c438a628177099d2721f2751ff825"}, + {file = "uvloop-0.15.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ae2b325c0f6d748027f7463077e457006b4fdb35a8788f01754aadba825285ee"}, + {file = "uvloop-0.15.3-cp38-cp38-macosx_10_14_x86_64.whl", hash = "sha256:0de811931e90ae2da9e19ce70ffad73047ab0c1dba7c6e74f9ae1a3aabeb89bd"}, + {file = "uvloop-0.15.3-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:7f4b8a905df909a407c5791fb582f6c03b0d3b491ecdc1cdceaefbc9bf9e08f6"}, + {file = "uvloop-0.15.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2d8ffe44ae709f839c54bacf14ed283f41bee90430c3b398e521e10f8d117b3a"}, + {file = "uvloop-0.15.3-cp39-cp39-macosx_10_14_x86_64.whl", hash = "sha256:63a3288abbc9c8ee979d7e34c34e780b2fbab3e7e53d00b6c80271119f277399"}, + {file = "uvloop-0.15.3-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:5cda65fc60a645470b8525ce014516b120b7057b576fa876cdfdd5e60ab1efbb"}, + {file = "uvloop-0.15.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1ff05116ede1ebdd81802df339e5b1d4cab1dfbd99295bf27e90b4cec64d70e9"}, + {file = "uvloop-0.15.3.tar.gz", hash = "sha256:905f0adb0c09c9f44222ee02f6b96fd88b493478fffb7a345287f9444e926030"}, ] watchgod = [ {file = "watchgod-0.7-py3-none-any.whl", hash = "sha256:d6c1ea21df37847ac0537ca0d6c2f4cdf513562e95f77bb93abbcf05573407b7"}, From 59ed869ed004ddfdf8424597694d179fa97a9afc Mon Sep 17 00:00:00 2001 From: StarHeartHunt Date: Sun, 18 Jul 2021 07:09:27 +0000 Subject: [PATCH 64/64] :memo: update api docs --- docs/api/adapters/feishu.md | 235 ++++++++++++++++++++++++++++++++++++ 1 file changed, 235 insertions(+) diff --git a/docs/api/adapters/feishu.md b/docs/api/adapters/feishu.md index c6c9aae6..7cbb311d 100644 --- a/docs/api/adapters/feishu.md +++ b/docs/api/adapters/feishu.md @@ -7,10 +7,245 @@ sidebarDepth: 0 # NoneBot.adapters.feishu.config 模块 + +## _class_ `Config` + +钉钉配置类 + + +* **配置项** + + + * `app_id` / `feishu_app_id`: 飞书开放平台后台“凭证与基础信息”处给出的 App ID + + + * `app_secret` / `feishu_app_secret`: 飞书开放平台后台“凭证与基础信息”处给出的 App Secret + + + * `encrypt_key` / `feishu_encrypt_key`: 飞书开放平台后台“事件订阅”处设置的 Encrypt Key + + + * `verification_token` / `feishu_verification_token`: 飞书开放平台后台“事件订阅”处设置的 Verification Token + + + * `tenant_access_token` / `feishu_tenant_access_token`: 请求飞书 API 后返回的租户密钥 + + # NoneBot.adapters.feishu.exception 模块 + +## _exception_ `ActionFailed` + +基类:[`nonebot.exception.ActionFailed`](../exception.md#nonebot.exception.ActionFailed), `nonebot.adapters.feishu.exception.FeishuAdapterException` + + +* **说明** + + API 请求返回错误信息。 + + + +* **参数** + + + * `retcode: Optional[int]`: 错误码 + + + +## _exception_ `NetworkError` + +基类:[`nonebot.exception.NetworkError`](../exception.md#nonebot.exception.NetworkError), `nonebot.adapters.feishu.exception.FeishuAdapterException` + + +* **说明** + + 网络错误。 + + + +* **参数** + + + * `retcode: Optional[int]`: 错误码 + + # NoneBot.adapters.feishu.bot 模块 + +## `_check_at_me(bot, event)` + + +* **说明** + + 检查消息开头或结尾是否存在 @机器人,去除并赋值 `event.reply`, `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._base.Bot`](README.md#nonebot.adapters._base.Bot) + +飞书 协议 Bot 适配。继承属性参考 [BaseBot](./#class-basebot) 。 + + +### _async_ `handle_message(message)` + + +* **说明** + + 处理事件并转换为 [Event](#class-event) + + + +### _async_ `call_api(api, **data)` + + +* **说明** + + 调用 飞书 协议 API + + + +* **参数** + + + * `api: str`: API 名称 + + + * `**data: Any`: API 参数 + + + +* **返回** + + + * `Any`: API 调用返回数据 + + + +* **异常** + + + * `NetworkError`: 网络错误 + + + * `ActionFailed`: API 调用失败 + + # NoneBot.adapters.feishu.message 模块 + +## _class_ `MessageSegment` + +基类:[`nonebot.adapters._base.MessageSegment`](README.md#nonebot.adapters._base.MessageSegment)[`Message`] + +飞书 协议 MessageSegment 适配。具体方法参考协议消息段类型或源码。 + + +## _class_ `Message` + +基类:[`nonebot.adapters._base.Message`](README.md#nonebot.adapters._base.Message)[`nonebot.adapters.feishu.message.MessageSegment`] + +飞书 协议 Message 适配。 + + +## _class_ `MessageSerializer` + +基类:`object` + +飞书 协议 Message 序列化器。 + + +## _class_ `MessageDeserializer` + +基类:`object` + +飞书 协议 Message 反序列化器。 + # NoneBot.adapters.feishu.event 模块 + + +## _class_ `Event` + +基类:[`nonebot.adapters._base.Event`](README.md#nonebot.adapters._base.Event) + +飞书协议事件。各事件字段参考 + +``` +`飞书文档`_ +``` + + + +## `get_event_model(event_name)` + + +* **说明** + + 根据事件名获取对应 `Event Model` 及 `FallBack Event Model` 列表 + + + +* **返回** + + + * `List[Type[Event]]`