mirror of
https://github.com/nonebot/nonebot2.git
synced 2024-11-27 18:45:05 +08:00
connect driver-adapter-matcher
This commit is contained in:
parent
ea21d6ffcc
commit
d616290626
@ -1,8 +1,47 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from typing import Any, Dict, Optional
|
||||
|
||||
from nonebot.config import Config
|
||||
|
||||
|
||||
class BaseBot(object):
|
||||
|
||||
def __init__(self):
|
||||
def __init__(self, type: str, config: Config, *, websocket=None):
|
||||
raise NotImplementedError
|
||||
|
||||
async def handle_message(self, message: dict):
|
||||
raise NotImplementedError
|
||||
|
||||
async def call_api(self, api: str, data: dict):
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
class BaseMessageSegment(dict):
|
||||
|
||||
def __init__(self,
|
||||
d: Optional[Dict[str, Any]] = None,
|
||||
*,
|
||||
type_: Optional[str] = None,
|
||||
data: Optional[Dict[str, str]] = None):
|
||||
super().__init__()
|
||||
if isinstance(d, dict) and d.get('type'):
|
||||
self.update(d)
|
||||
elif type_:
|
||||
self.type = type_
|
||||
self.data = data
|
||||
else:
|
||||
raise ValueError('the "type" field cannot be None or empty')
|
||||
|
||||
def __str__(self):
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
class BaseMessage(list):
|
||||
|
||||
def __init__(self, message: str = None):
|
||||
raise NotImplementedError
|
||||
|
||||
def __str__(self):
|
||||
return ''.join((str(seg) for seg in self))
|
||||
|
@ -1,10 +1,40 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from nonebot.adapters import BaseBot
|
||||
import httpx
|
||||
|
||||
from nonebot.event import Event
|
||||
from nonebot.config import Config
|
||||
from nonebot.adapters import BaseBot, BaseMessage, BaseMessageSegment
|
||||
from nonebot.message import handle_event
|
||||
|
||||
|
||||
class Bot(BaseBot):
|
||||
|
||||
def __init__(self):
|
||||
pass
|
||||
def __init__(self, type_: str, config: Config, *, websocket=None):
|
||||
if type_ not in ["http", "websocket"]:
|
||||
raise ValueError("Unsupported connection type")
|
||||
self.type = type_
|
||||
self.config = config
|
||||
self.websocket = websocket
|
||||
|
||||
async def handle_message(self, message: dict):
|
||||
# TODO: convert message into event
|
||||
event = Event.from_payload(message)
|
||||
|
||||
# TODO: Handle Meta Event
|
||||
await handle_event(self, event)
|
||||
|
||||
async def call_api(self, api: str, data: dict):
|
||||
if self.type == "websocket":
|
||||
pass
|
||||
elif self.type == "http":
|
||||
pass
|
||||
|
||||
|
||||
class MessageSegment(BaseMessageSegment):
|
||||
pass
|
||||
|
||||
|
||||
class Message(BaseMessage):
|
||||
pass
|
||||
|
@ -4,10 +4,12 @@
|
||||
from typing import Optional
|
||||
from ipaddress import IPv4Address
|
||||
|
||||
from nonebot.config import Config
|
||||
|
||||
|
||||
class BaseDriver(object):
|
||||
|
||||
def __init__(self):
|
||||
def __init__(self, config: Config):
|
||||
raise NotImplementedError
|
||||
|
||||
@property
|
||||
@ -34,3 +36,6 @@ class BaseDriver(object):
|
||||
|
||||
async def _handle_ws_reverse(self):
|
||||
raise NotImplementedError
|
||||
|
||||
async def _handle_http_api(self):
|
||||
raise NotImplementedError
|
||||
|
@ -7,15 +7,18 @@ from typing import Optional
|
||||
from ipaddress import IPv4Address
|
||||
|
||||
import uvicorn
|
||||
from starlette.websockets import WebSocketDisconnect
|
||||
from fastapi import Body, FastAPI, WebSocket
|
||||
|
||||
from nonebot.log import logger
|
||||
from nonebot.config import Config
|
||||
from nonebot.drivers import BaseDriver
|
||||
from nonebot.adapters.coolq import Bot as CoolQBot
|
||||
|
||||
|
||||
class Driver(BaseDriver):
|
||||
|
||||
def __init__(self, config):
|
||||
def __init__(self, config: Config):
|
||||
self._server_app = FastAPI(
|
||||
debug=config.debug,
|
||||
openapi_url=None,
|
||||
@ -25,8 +28,10 @@ class Driver(BaseDriver):
|
||||
|
||||
self.config = config
|
||||
|
||||
self._server_app.post("/coolq/")(self._handle_http)
|
||||
self._server_app.websocket("/coolq/ws")(self._handle_ws_reverse)
|
||||
self._server_app.post("/{adapter}/")(self._handle_http)
|
||||
self._server_app.post("/{adapter}/http")(self._handle_http)
|
||||
self._server_app.websocket("/{adapter}/ws")(self._handle_ws_reverse)
|
||||
self._server_app.websocket("/{adapter}/ws/")(self._handle_ws_reverse)
|
||||
|
||||
@property
|
||||
def server_app(self):
|
||||
@ -81,11 +86,16 @@ class Driver(BaseDriver):
|
||||
log_config=LOGGING_CONFIG,
|
||||
**kwargs)
|
||||
|
||||
async def _handle_http(self, data: dict = Body(...)):
|
||||
async def _handle_http(self, adapter: str, data: dict = Body(...)):
|
||||
# TODO: Check authorization
|
||||
logger.debug(f"Received message: {data}")
|
||||
if adapter == "coolq":
|
||||
bot = CoolQBot("http", self.config)
|
||||
await bot.handle_message(data)
|
||||
return {"status": 200, "message": "success"}
|
||||
|
||||
async def _handle_ws_reverse(self, websocket: WebSocket):
|
||||
async def _handle_ws_reverse(self, adapter: str, websocket: WebSocket):
|
||||
# TODO: Check authorization
|
||||
await websocket.accept()
|
||||
while True:
|
||||
try:
|
||||
@ -93,5 +103,11 @@ class Driver(BaseDriver):
|
||||
except json.decoder.JSONDecodeError as e:
|
||||
logger.exception(e)
|
||||
continue
|
||||
except WebSocketDisconnect:
|
||||
logger.error("WebSocket Disconnect")
|
||||
return
|
||||
|
||||
logger.debug(f"Received message: {data}")
|
||||
if adapter == "coolq":
|
||||
bot = CoolQBot("websocket", self.config, websocket=websocket)
|
||||
await bot.handle_message(data)
|
||||
|
@ -94,7 +94,6 @@ class Matcher:
|
||||
|
||||
def _decorator(func: Handler) -> Handler:
|
||||
|
||||
@wraps(func)
|
||||
async def _handler(bot, event: Event, state: dict):
|
||||
raise PausedException
|
||||
|
||||
|
@ -6,13 +6,19 @@ from nonebot.event import Event
|
||||
from nonebot.matcher import matchers
|
||||
|
||||
|
||||
async def handle_message(bot, event: Event):
|
||||
async def handle_event(bot, event: Event):
|
||||
# TODO: PreProcess
|
||||
|
||||
for priority in sorted(matchers.keys()):
|
||||
for index in range(len(matchers[priority])):
|
||||
Matcher = matchers[priority][index]
|
||||
if not Matcher.check_rule(event):
|
||||
try:
|
||||
if not Matcher.check_rule(event):
|
||||
continue
|
||||
except Exception as e:
|
||||
logger.error(
|
||||
f"Rule check failed for matcher {Matcher}. Ignored.")
|
||||
logger.exception(e)
|
||||
continue
|
||||
|
||||
matcher = Matcher()
|
||||
@ -22,5 +28,6 @@ async def handle_message(bot, event: Event):
|
||||
try:
|
||||
await matcher.run(bot, event)
|
||||
except Exception as e:
|
||||
logger.error(f"Running matcher {matcher} failed.")
|
||||
logger.exception(e)
|
||||
return
|
||||
|
165
poetry.lock
generated
165
poetry.lock
generated
@ -60,7 +60,7 @@ type = "legacy"
|
||||
url = "https://mirrors.aliyun.com/pypi/simple"
|
||||
|
||||
[[package]]
|
||||
category = "dev"
|
||||
category = "main"
|
||||
description = "Python package for providing Mozilla's CA Bundle."
|
||||
name = "certifi"
|
||||
optional = false
|
||||
@ -73,7 +73,7 @@ type = "legacy"
|
||||
url = "https://mirrors.aliyun.com/pypi/simple"
|
||||
|
||||
[[package]]
|
||||
category = "dev"
|
||||
category = "main"
|
||||
description = "Universal encoding detector for Python 2 and 3"
|
||||
name = "chardet"
|
||||
optional = false
|
||||
@ -161,6 +161,49 @@ reference = "aliyun"
|
||||
type = "legacy"
|
||||
url = "https://mirrors.aliyun.com/pypi/simple"
|
||||
|
||||
[[package]]
|
||||
category = "main"
|
||||
description = "HTTP/2 State-Machine based protocol implementation"
|
||||
name = "h2"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
version = "3.2.0"
|
||||
|
||||
[package.dependencies]
|
||||
hpack = ">=3.0,<4"
|
||||
hyperframe = ">=5.2.0,<6"
|
||||
|
||||
[package.source]
|
||||
reference = "aliyun"
|
||||
type = "legacy"
|
||||
url = "https://mirrors.aliyun.com/pypi/simple"
|
||||
|
||||
[[package]]
|
||||
category = "main"
|
||||
description = "Pure-Python HPACK header compression"
|
||||
name = "hpack"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
version = "3.0.0"
|
||||
|
||||
[package.source]
|
||||
reference = "aliyun"
|
||||
type = "legacy"
|
||||
url = "https://mirrors.aliyun.com/pypi/simple"
|
||||
|
||||
[[package]]
|
||||
category = "main"
|
||||
description = "Chromium HSTS Preload list as a Python package and updated daily"
|
||||
name = "hstspreload"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
version = "2020.7.7"
|
||||
|
||||
[package.source]
|
||||
reference = "aliyun"
|
||||
type = "legacy"
|
||||
url = "https://mirrors.aliyun.com/pypi/simple"
|
||||
|
||||
[[package]]
|
||||
category = "dev"
|
||||
description = "Turn HTML into equivalent Markdown-structured text."
|
||||
@ -174,6 +217,24 @@ reference = "aliyun"
|
||||
type = "legacy"
|
||||
url = "https://mirrors.aliyun.com/pypi/simple"
|
||||
|
||||
[[package]]
|
||||
category = "main"
|
||||
description = "A minimal low-level HTTP client."
|
||||
name = "httpcore"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
version = "0.9.1"
|
||||
|
||||
[package.dependencies]
|
||||
h11 = ">=0.8,<0.10"
|
||||
h2 = ">=3.0.0,<4.0.0"
|
||||
sniffio = ">=1.0.0,<2.0.0"
|
||||
|
||||
[package.source]
|
||||
reference = "aliyun"
|
||||
type = "legacy"
|
||||
url = "https://mirrors.aliyun.com/pypi/simple"
|
||||
|
||||
[[package]]
|
||||
category = "main"
|
||||
description = "A collection of framework independent HTTP protocol utils."
|
||||
@ -192,7 +253,42 @@ type = "legacy"
|
||||
url = "https://mirrors.aliyun.com/pypi/simple"
|
||||
|
||||
[[package]]
|
||||
category = "dev"
|
||||
category = "main"
|
||||
description = "The next generation HTTP client."
|
||||
name = "httpx"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
version = "0.13.3"
|
||||
|
||||
[package.dependencies]
|
||||
certifi = "*"
|
||||
chardet = ">=3.0.0,<4.0.0"
|
||||
hstspreload = "*"
|
||||
httpcore = ">=0.9.0,<0.10.0"
|
||||
idna = ">=2.0.0,<3.0.0"
|
||||
rfc3986 = ">=1.3,<2"
|
||||
sniffio = "*"
|
||||
|
||||
[package.source]
|
||||
reference = "aliyun"
|
||||
type = "legacy"
|
||||
url = "https://mirrors.aliyun.com/pypi/simple"
|
||||
|
||||
[[package]]
|
||||
category = "main"
|
||||
description = "HTTP/2 framing layer for Python"
|
||||
name = "hyperframe"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
version = "5.2.0"
|
||||
|
||||
[package.source]
|
||||
reference = "aliyun"
|
||||
type = "legacy"
|
||||
url = "https://mirrors.aliyun.com/pypi/simple"
|
||||
|
||||
[[package]]
|
||||
category = "main"
|
||||
description = "Internationalized Domain Names in Applications (IDNA)"
|
||||
name = "idna"
|
||||
optional = false
|
||||
@ -383,6 +479,22 @@ reference = "aliyun"
|
||||
type = "legacy"
|
||||
url = "https://mirrors.aliyun.com/pypi/simple"
|
||||
|
||||
[[package]]
|
||||
category = "main"
|
||||
description = "Validating URI References per RFC 3986"
|
||||
name = "rfc3986"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
version = "1.4.0"
|
||||
|
||||
[package.extras]
|
||||
idna2008 = ["idna"]
|
||||
|
||||
[package.source]
|
||||
reference = "aliyun"
|
||||
type = "legacy"
|
||||
url = "https://mirrors.aliyun.com/pypi/simple"
|
||||
|
||||
[[package]]
|
||||
category = "main"
|
||||
description = "Python 2 and 3 compatibility utilities"
|
||||
@ -396,6 +508,19 @@ reference = "aliyun"
|
||||
type = "legacy"
|
||||
url = "https://mirrors.aliyun.com/pypi/simple"
|
||||
|
||||
[[package]]
|
||||
category = "main"
|
||||
description = "Sniff out which async library your code is running under"
|
||||
name = "sniffio"
|
||||
optional = false
|
||||
python-versions = ">=3.5"
|
||||
version = "1.1.0"
|
||||
|
||||
[package.source]
|
||||
reference = "aliyun"
|
||||
type = "legacy"
|
||||
url = "https://mirrors.aliyun.com/pypi/simple"
|
||||
|
||||
[[package]]
|
||||
category = "dev"
|
||||
description = "This package provides 26 stemmers for 25 languages generated from Snowball algorithms."
|
||||
@ -713,7 +838,7 @@ url = "https://mirrors.aliyun.com/pypi/simple"
|
||||
scheduler = ["apscheduler"]
|
||||
|
||||
[metadata]
|
||||
content-hash = "5dc37a3a06ef422bb885c2f6b09179964e083f2005bf8873349431ebc4508152"
|
||||
content-hash = "e98c07c170a70d27c0cb15dd6b4cfd10e678a1df55bd3c3bebbbe26035b88a24"
|
||||
python-versions = "^3.7"
|
||||
|
||||
[metadata.files]
|
||||
@ -757,10 +882,26 @@ h11 = [
|
||||
{file = "h11-0.9.0-py2.py3-none-any.whl", hash = "sha256:4bc6d6a1238b7615b266ada57e0618568066f57dd6fa967d1290ec9309b2f2f1"},
|
||||
{file = "h11-0.9.0.tar.gz", hash = "sha256:33d4bca7be0fa039f4e84d50ab00531047e53d6ee8ffbc83501ea602c169cae1"},
|
||||
]
|
||||
h2 = [
|
||||
{file = "h2-3.2.0-py2.py3-none-any.whl", hash = "sha256:61e0f6601fa709f35cdb730863b4e5ec7ad449792add80d1410d4174ed139af5"},
|
||||
{file = "h2-3.2.0.tar.gz", hash = "sha256:875f41ebd6f2c44781259005b157faed1a5031df3ae5aa7bcb4628a6c0782f14"},
|
||||
]
|
||||
hpack = [
|
||||
{file = "hpack-3.0.0-py2.py3-none-any.whl", hash = "sha256:0edd79eda27a53ba5be2dfabf3b15780928a0dff6eb0c60a3d6767720e970c89"},
|
||||
{file = "hpack-3.0.0.tar.gz", hash = "sha256:8eec9c1f4bfae3408a3f30500261f7e6a65912dc138526ea054f9ad98892e9d2"},
|
||||
]
|
||||
hstspreload = [
|
||||
{file = "hstspreload-2020.7.7-py3-none-any.whl", hash = "sha256:051a752188c3422558a1302be99b742a2dd9d7568419614104ae7e87233f9d63"},
|
||||
{file = "hstspreload-2020.7.7.tar.gz", hash = "sha256:3e1b107d6c865fc28f0f023456f193f2e916d14bca5a16c93fe440bef90c7c58"},
|
||||
]
|
||||
html2text = [
|
||||
{file = "html2text-2020.1.16-py3-none-any.whl", hash = "sha256:c7c629882da0cf377d66f073329ccf34a12ed2adf0169b9285ae4e63ef54c82b"},
|
||||
{file = "html2text-2020.1.16.tar.gz", hash = "sha256:e296318e16b059ddb97f7a8a1d6a5c1d7af4544049a01e261731d2d5cc277bbb"},
|
||||
]
|
||||
httpcore = [
|
||||
{file = "httpcore-0.9.1-py3-none-any.whl", hash = "sha256:9850fe97a166a794d7e920590d5ec49a05488884c9fc8b5dba8561effab0c2a0"},
|
||||
{file = "httpcore-0.9.1.tar.gz", hash = "sha256:ecc5949310d9dae4de64648a4ce529f86df1f232ce23dcfefe737c24d21dfbe9"},
|
||||
]
|
||||
httptools = [
|
||||
{file = "httptools-0.1.1-cp35-cp35m-macosx_10_13_x86_64.whl", hash = "sha256:a2719e1d7a84bb131c4f1e0cb79705034b48de6ae486eb5297a139d6a3296dce"},
|
||||
{file = "httptools-0.1.1-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:fa3cd71e31436911a44620473e873a256851e1f53dee56669dae403ba41756a4"},
|
||||
@ -775,6 +916,14 @@ httptools = [
|
||||
{file = "httptools-0.1.1-cp38-cp38-win_amd64.whl", hash = "sha256:0a4b1b2012b28e68306575ad14ad5e9120b34fccd02a81eb08838d7e3bbb48be"},
|
||||
{file = "httptools-0.1.1.tar.gz", hash = "sha256:41b573cf33f64a8f8f3400d0a7faf48e1888582b6f6e02b82b9bd4f0bf7497ce"},
|
||||
]
|
||||
httpx = [
|
||||
{file = "httpx-0.13.3-py3-none-any.whl", hash = "sha256:32d930858eab677bc29a742aaa4f096de259f1c78c68a90ad11f5c3c04f08335"},
|
||||
{file = "httpx-0.13.3.tar.gz", hash = "sha256:3642bd13e90b80ba8a243a730275eb10a4c26ec96f5fc16b87e458d4ab21efae"},
|
||||
]
|
||||
hyperframe = [
|
||||
{file = "hyperframe-5.2.0-py2.py3-none-any.whl", hash = "sha256:5187962cb16dcc078f23cb5a4b110098d546c3f41ff2d4038a9896893bbd0b40"},
|
||||
{file = "hyperframe-5.2.0.tar.gz", hash = "sha256:a9f5c17f2cc3c719b917c4f33ed1c61bd1f8dfac4b1bd23b7c80b3400971b41f"},
|
||||
]
|
||||
idna = [
|
||||
{file = "idna-2.10-py2.py3-none-any.whl", hash = "sha256:b97d804b1e9b523befed77c48dacec60e6dcb0b5391d57af6a65a312a90648c0"},
|
||||
{file = "idna-2.10.tar.gz", hash = "sha256:b307872f855b18632ce0c21c5e45be78c0ea7ae4c15c828c20788b26921eb3f6"},
|
||||
@ -869,10 +1018,18 @@ requests = [
|
||||
{file = "requests-2.24.0-py2.py3-none-any.whl", hash = "sha256:fe75cc94a9443b9246fc7049224f75604b113c36acb93f87b80ed42c44cbb898"},
|
||||
{file = "requests-2.24.0.tar.gz", hash = "sha256:b3559a131db72c33ee969480840fff4bb6dd111de7dd27c8ee1f820f4f00231b"},
|
||||
]
|
||||
rfc3986 = [
|
||||
{file = "rfc3986-1.4.0-py2.py3-none-any.whl", hash = "sha256:af9147e9aceda37c91a05f4deb128d4b4b49d6b199775fd2d2927768abdc8f50"},
|
||||
{file = "rfc3986-1.4.0.tar.gz", hash = "sha256:112398da31a3344dc25dbf477d8df6cb34f9278a94fee2625d89e4514be8bb9d"},
|
||||
]
|
||||
six = [
|
||||
{file = "six-1.15.0-py2.py3-none-any.whl", hash = "sha256:8b74bedcbbbaca38ff6d7491d76f2b06b3592611af620f8426e82dddb04a5ced"},
|
||||
{file = "six-1.15.0.tar.gz", hash = "sha256:30639c035cdb23534cd4aa2dd52c3bf48f06e5f4a941509c8bafd8ce11080259"},
|
||||
]
|
||||
sniffio = [
|
||||
{file = "sniffio-1.1.0-py3-none-any.whl", hash = "sha256:20ed6d5b46f8ae136d00b9dcb807615d83ed82ceea6b2058cecb696765246da5"},
|
||||
{file = "sniffio-1.1.0.tar.gz", hash = "sha256:8e3810100f69fe0edd463d02ad407112542a11ffdc29f67db2bf3771afb87a21"},
|
||||
]
|
||||
snowballstemmer = [
|
||||
{file = "snowballstemmer-2.0.0-py2.py3-none-any.whl", hash = "sha256:209f257d7533fdb3cb73bdbd24f436239ca3b2fa67d56f6ff88e86be08cc5ef0"},
|
||||
{file = "snowballstemmer-2.0.0.tar.gz", hash = "sha256:df3bac3df4c2c01363f3dd2cfa78cce2840a79b9f1c2d2de9ce8d31683992f52"},
|
||||
|
@ -28,6 +28,7 @@ fastapi = "^0.58.1"
|
||||
uvicorn = "^0.11.5"
|
||||
pydantic = {extras = ["dotenv"], version = "^1.5.1"}
|
||||
apscheduler = { version = "^3.6.3", optional = true }
|
||||
httpx = "^0.13.3"
|
||||
|
||||
[tool.poetry.dev-dependencies]
|
||||
yapf = "^0.30.0"
|
||||
|
@ -13,8 +13,6 @@ nonebot.init()
|
||||
app = nonebot.get_asgi()
|
||||
|
||||
nonebot.load_plugins("test_plugins")
|
||||
print(nonebot.get_loaded_plugins())
|
||||
print(matchers)
|
||||
|
||||
if __name__ == "__main__":
|
||||
nonebot.run(app="bot:app")
|
||||
|
@ -10,4 +10,12 @@ test_matcher = on_message(Rule(), state={"default": 1})
|
||||
|
||||
@test_matcher.handle()
|
||||
async def test_handler(bot, event: Event, state: dict):
|
||||
print(state)
|
||||
print("Test Matcher Received:", event)
|
||||
print("Current State:", state)
|
||||
state["message1"] = event.get("raw_message")
|
||||
|
||||
|
||||
@test_matcher.receive()
|
||||
async def test_receive(bot, event: Event, state: dict):
|
||||
print("Test Matcher Received next time:", event)
|
||||
print("Current State:", state)
|
||||
|
Loading…
Reference in New Issue
Block a user