diff --git a/nonebot_plugin_chatrecorder/adapters/__init__.py b/nonebot_plugin_chatrecorder/adapters/__init__.py index f7f3406..7ed1009 100644 --- a/nonebot_plugin_chatrecorder/adapters/__init__.py +++ b/nonebot_plugin_chatrecorder/adapters/__init__.py @@ -8,3 +8,4 @@ from . import qqguild as qqguild from . import red as red from . import telegram as telegram +from . import villa as villa diff --git a/nonebot_plugin_chatrecorder/adapters/villa.py b/nonebot_plugin_chatrecorder/adapters/villa.py new file mode 100644 index 0000000..0a4f2a1 --- /dev/null +++ b/nonebot_plugin_chatrecorder/adapters/villa.py @@ -0,0 +1,103 @@ +from datetime import datetime +from typing import Any, Dict, Optional, Type, Union + +from nonebot.adapters import Bot as BaseBot +from nonebot.message import event_postprocessor +from nonebot_plugin_orm import get_session +from nonebot_plugin_session import Session, SessionLevel, extract_session +from nonebot_plugin_session_orm import get_session_persist_id +from typing_extensions import override + +from ..config import plugin_config +from ..consts import SupportedAdapter, SupportedPlatform +from ..message import ( + MessageDeserializer, + MessageSerializer, + register_deserializer, + register_serializer, + serialize_message, +) +from ..model import MessageRecord + +try: + from nonebot.adapters.villa import Bot, Message, SendMessageEvent + from nonebot.adapters.villa.models import MessageContentInfo + + adapter = SupportedAdapter.villa + + @event_postprocessor + async def record_recv_msg(bot: Bot, event: SendMessageEvent): + session = extract_session(bot, event) + session_persist_id = await get_session_persist_id(session) + + record = MessageRecord( + session_persist_id=session_persist_id, + time=datetime.utcfromtimestamp(event.send_at / 1000), + type=event.get_type(), + message_id=event.msg_uid, + message=serialize_message(adapter, event.message), + plain_text=event.message.extract_plain_text(), + ) + async with get_session() as db_session: + db_session.add(record) + await db_session.commit() + + if plugin_config.chatrecorder_record_send_msg: + + @Bot.on_called_api + async def record_send_msg( + bot: BaseBot, + e: Optional[Exception], + api: str, + data: Dict[str, Any], + result: Optional[Any], + ): + if not isinstance(bot, Bot): + return + if e or not result: + return + if api not in ["send_message"]: + return + + msg_content: Union[str, MessageContentInfo] = data["msg_content"] + if isinstance(msg_content, str): + msg_content = MessageContentInfo.parse_raw(msg_content) + message = Message.from_message_content_info(msg_content) + + session = Session( + bot_id=bot.self_id, + bot_type=bot.type, + platform=SupportedPlatform.villa, + level=SessionLevel.LEVEL3, + id1=None, + id2=str(data["room_id"]), + id3=str(data["villa_id"]), + ) + session_persist_id = await get_session_persist_id(session) + + record = MessageRecord( + session_persist_id=session_persist_id, + time=datetime.utcnow(), + type="message_sent", + message_id=str(result), + message=serialize_message(adapter, message), + plain_text=message.extract_plain_text(), + ) + async with get_session() as db_session: + db_session.add(record) + await db_session.commit() + + class Serializer(MessageSerializer[Message]): + pass + + class Deserializer(MessageDeserializer[Message]): + @classmethod + @override + def get_message_class(cls) -> Type[Message]: + return Message + + register_serializer(adapter, Serializer) + register_deserializer(adapter, Deserializer) + +except ImportError: + pass diff --git a/nonebot_plugin_chatrecorder/consts.py b/nonebot_plugin_chatrecorder/consts.py index 389e0f2..c8890fd 100644 --- a/nonebot_plugin_chatrecorder/consts.py +++ b/nonebot_plugin_chatrecorder/consts.py @@ -22,6 +22,7 @@ class SupportedAdapter(StrEnum): red = "RedProtocol" discord = "Discord" dodo = "DoDo" + villa = "Villa" class SupportedPlatform(StrEnum): @@ -33,4 +34,5 @@ class SupportedPlatform(StrEnum): feishu = "feishu" discord = "discord" dodo = "dodo" + villa = "villa" unknown = "unknown" diff --git a/poetry.lock b/poetry.lock index 260f9f6..3c61c62 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,10 +1,9 @@ -# This file is automatically @generated by Poetry and should not be changed by hand. +# This file is automatically @generated by Poetry 1.7.1 and should not be changed by hand. [[package]] name = "aiomysql" version = "0.1.1" description = "MySQL driver for asyncio." -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -23,7 +22,6 @@ sa = ["sqlalchemy (>=1.0,<1.4)"] name = "aiosqlite" version = "0.19.0" description = "asyncio bridge to the standard sqlite3 module" -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -39,7 +37,6 @@ docs = ["sphinx (==6.1.3)", "sphinx-mdinclude (==0.5.3)"] name = "alembic" version = "1.12.1" description = "A database migration tool for SQLAlchemy." -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -61,7 +58,6 @@ tz = ["python-dateutil"] name = "anyio" version = "3.7.1" description = "High level compatibility layer for multiple asynchronous event loop implementations" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -83,7 +79,6 @@ trio = ["trio (<0.22)"] name = "arrow" version = "1.3.0" description = "Better dates & times for Python" -category = "dev" optional = false python-versions = ">=3.8" files = [ @@ -97,13 +92,12 @@ types-python-dateutil = ">=2.8.10" [package.extras] doc = ["doc8", "sphinx (>=7.0.0)", "sphinx-autobuild", "sphinx-autodoc-typehints", "sphinx_rtd_theme (>=1.3.0)"] -test = ["dateparser (>=1.0.0,<2.0.0)", "pre-commit", "pytest", "pytest-cov", "pytest-mock", "pytz (==2021.1)", "simplejson (>=3.0.0,<4.0.0)"] +test = ["dateparser (==1.*)", "pre-commit", "pytest", "pytest-cov", "pytest-mock", "pytz (==2021.1)", "simplejson (==3.*)"] [[package]] name = "asgiref" version = "3.7.2" description = "ASGI specs, helper code, and adapters" -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -121,7 +115,6 @@ tests = ["mypy (>=0.800)", "pytest", "pytest-asyncio"] name = "async-asgi-testclient" version = "1.4.11" description = "Async client for testing ASGI web applications" -category = "dev" optional = false python-versions = "*" files = [ @@ -136,7 +129,6 @@ requests = ">=2.21,<3.0" name = "asyncpg" version = "0.27.0" description = "An asyncio PostgreSQL driver" -category = "dev" optional = false python-versions = ">=3.7.0" files = [ @@ -183,11 +175,27 @@ dev = ["Cython (>=0.29.24,<0.30.0)", "Sphinx (>=4.1.2,<4.2.0)", "flake8 (>=5.0.4 docs = ["Sphinx (>=4.1.2,<4.2.0)", "sphinx-rtd-theme (>=0.5.2,<0.6.0)", "sphinxcontrib-asyncio (>=0.3.0,<0.4.0)"] test = ["flake8 (>=5.0.4,<5.1.0)", "uvloop (>=0.15.3)"] +[[package]] +name = "betterproto" +version = "1.2.5" +description = "A better Protobuf / gRPC generator & library" +optional = false +python-versions = ">=3.6" +files = [ + {file = "betterproto-1.2.5.tar.gz", hash = "sha256:74a3ab34646054f674d236d1229ba8182dc2eae86feb249b8590ef496ce9803d"}, +] + +[package.dependencies] +grpclib = "*" +stringcase = "*" + +[package.extras] +compiler = ["black", "jinja2", "protobuf"] + [[package]] name = "binaryornot" version = "0.4.4" description = "Ultra-lightweight pure Python package to check if a file is binary or text." -category = "dev" optional = false python-versions = "*" files = [ @@ -202,7 +210,6 @@ chardet = ">=3.0.2" name = "cashews" version = "6.3.0" description = "cache tools with async power" -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -221,7 +228,6 @@ tests = ["hypothesis", "pytest", "pytest-asyncio"] name = "certifi" version = "2023.11.17" description = "Python package for providing Mozilla's CA Bundle." -category = "dev" optional = false python-versions = ">=3.6" files = [ @@ -233,7 +239,6 @@ files = [ name = "chardet" version = "5.2.0" description = "Universal encoding detector for Python 3" -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -245,7 +250,6 @@ files = [ name = "charset-normalizer" version = "3.3.2" description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." -category = "dev" optional = false python-versions = ">=3.7.0" files = [ @@ -345,7 +349,6 @@ files = [ name = "click" version = "8.1.7" description = "Composable command line interface toolkit" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -360,7 +363,6 @@ colorama = {version = "*", markers = "platform_system == \"Windows\""} name = "colorama" version = "0.4.6" description = "Cross-platform colored terminal text." -category = "main" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" files = [ @@ -372,7 +374,6 @@ files = [ name = "cookiecutter" version = "2.5.0" description = "A command-line utility that creates projects from project templates, e.g. creating a Python package project from a Python package project template." -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -394,7 +395,6 @@ rich = "*" name = "coverage" version = "7.3.2" description = "Code coverage measurement for Python" -category = "dev" optional = false python-versions = ">=3.8" files = [ @@ -462,7 +462,6 @@ toml = ["tomli"] name = "distlib" version = "0.3.7" description = "Distribution utilities" -category = "dev" optional = false python-versions = "*" files = [ @@ -474,7 +473,6 @@ files = [ name = "exceptiongroup" version = "1.2.0" description = "Backport of PEP 654 (exception groups)" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -489,7 +487,6 @@ test = ["pytest (>=6)"] name = "execnet" version = "2.0.2" description = "execnet: rapid multi-Python deployment" -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -504,7 +501,6 @@ testing = ["hatch", "pre-commit", "pytest", "tox"] name = "fastapi" version = "0.104.1" description = "FastAPI framework, high performance, easy to learn, fast to code, ready for production" -category = "main" optional = false python-versions = ">=3.8" files = [ @@ -525,7 +521,6 @@ all = ["email-validator (>=2.0.0)", "httpx (>=0.23.0)", "itsdangerous (>=1.1.0)" name = "filelock" version = "3.13.1" description = "A platform independent file lock." -category = "dev" optional = false python-versions = ">=3.8" files = [ @@ -542,7 +537,6 @@ typing = ["typing-extensions (>=4.8)"] name = "greenlet" version = "3.0.1" description = "Lightweight in-process concurrent programming" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -609,11 +603,27 @@ files = [ docs = ["Sphinx"] test = ["objgraph", "psutil"] +[[package]] +name = "grpclib" +version = "0.4.6" +description = "Pure-Python gRPC implementation for asyncio" +optional = false +python-versions = ">=3.7" +files = [ + {file = "grpclib-0.4.6.tar.gz", hash = "sha256:595d05236ca8b8f8e433f5bf6095e6354c1d8777d003ddaf5288efa9611e3fd6"}, +] + +[package.dependencies] +h2 = ">=3.1.0,<5" +multidict = "*" + +[package.extras] +protobuf = ["protobuf (>=3.20.0)"] + [[package]] name = "h11" version = "0.14.0" description = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -625,7 +635,6 @@ files = [ name = "h2" version = "4.1.0" description = "HTTP/2 State-Machine based protocol implementation" -category = "dev" optional = false python-versions = ">=3.6.1" files = [ @@ -641,7 +650,6 @@ hyperframe = ">=6.0,<7" name = "hpack" version = "4.0.0" description = "Pure-Python HPACK header compression" -category = "dev" optional = false python-versions = ">=3.6.1" files = [ @@ -653,7 +661,6 @@ files = [ name = "httpcore" version = "1.0.2" description = "A minimal low-level HTTP client." -category = "dev" optional = false python-versions = ">=3.8" files = [ @@ -668,14 +675,13 @@ h11 = ">=0.13,<0.15" [package.extras] asyncio = ["anyio (>=4.0,<5.0)"] http2 = ["h2 (>=3,<5)"] -socks = ["socksio (>=1.0.0,<2.0.0)"] +socks = ["socksio (==1.*)"] trio = ["trio (>=0.22.0,<0.23.0)"] [[package]] name = "httptools" version = "0.6.1" description = "A collection of framework independent HTTP protocol utils." -category = "main" optional = false python-versions = ">=3.8.0" files = [ @@ -724,7 +730,6 @@ test = ["Cython (>=0.29.24,<0.30.0)"] name = "httpx" version = "0.25.2" description = "The next generation HTTP client." -category = "dev" optional = false python-versions = ">=3.8" files = [ @@ -736,21 +741,20 @@ files = [ anyio = "*" certifi = "*" h2 = {version = ">=3,<5", optional = true, markers = "extra == \"http2\""} -httpcore = ">=1.0.0,<2.0.0" +httpcore = "==1.*" idna = "*" sniffio = "*" [package.extras] brotli = ["brotli", "brotlicffi"] -cli = ["click (>=8.0.0,<9.0.0)", "pygments (>=2.0.0,<3.0.0)", "rich (>=10,<14)"] +cli = ["click (==8.*)", "pygments (==2.*)", "rich (>=10,<14)"] http2 = ["h2 (>=3,<5)"] -socks = ["socksio (>=1.0.0,<2.0.0)"] +socks = ["socksio (==1.*)"] [[package]] name = "hyperframe" version = "6.0.1" description = "HTTP/2 framing layer for Python" -category = "dev" optional = false python-versions = ">=3.6.1" files = [ @@ -762,7 +766,6 @@ files = [ name = "idna" version = "3.6" description = "Internationalized Domain Names in Applications (IDNA)" -category = "main" optional = false python-versions = ">=3.5" files = [ @@ -774,7 +777,6 @@ files = [ name = "importlib-metadata" version = "6.8.0" description = "Read metadata from Python packages" -category = "main" optional = false python-versions = ">=3.8" files = [ @@ -794,7 +796,6 @@ testing = ["flufl.flake8", "importlib-resources (>=1.3)", "packaging", "pyfakefs name = "importlib-resources" version = "6.1.1" description = "Read resources from Python packages" -category = "main" optional = false python-versions = ">=3.8" files = [ @@ -813,7 +814,6 @@ testing = ["pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", name = "iniconfig" version = "2.0.0" description = "brain-dead simple config-ini parsing" -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -825,7 +825,6 @@ files = [ name = "jinja2" version = "3.1.2" description = "A very fast and expressive template engine." -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -843,7 +842,6 @@ i18n = ["Babel (>=2.7)"] name = "linkify-it-py" version = "2.0.2" description = "Links recognition library with FULL unicode support." -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -864,7 +862,6 @@ test = ["coverage", "pytest", "pytest-cov"] name = "loguru" version = "0.7.2" description = "Python logging made (stupidly) simple" -category = "main" optional = false python-versions = ">=3.5" files = [ @@ -883,7 +880,6 @@ dev = ["Sphinx (==7.2.5)", "colorama (==0.4.5)", "colorama (==0.4.6)", "exceptio name = "mako" version = "1.3.0" description = "A super-fast templating language that borrows the best ideas from the existing templating languages." -category = "main" optional = false python-versions = ">=3.8" files = [ @@ -903,7 +899,6 @@ testing = ["pytest"] name = "markdown-it-py" version = "3.0.0" description = "Python port of markdown-it. Markdown parsing, done right!" -category = "dev" optional = false python-versions = ">=3.8" files = [ @@ -930,7 +925,6 @@ testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions"] name = "markupsafe" version = "2.1.3" description = "Safely add untrusted strings to HTML/XML markup." -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -1000,7 +994,6 @@ files = [ name = "mdit-py-plugins" version = "0.4.0" description = "Collection of plugins for markdown-it-py" -category = "dev" optional = false python-versions = ">=3.8" files = [ @@ -1020,7 +1013,6 @@ testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions"] name = "mdurl" version = "0.1.2" description = "Markdown URL utilities" -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -1032,7 +1024,6 @@ files = [ name = "msgpack" version = "1.0.7" description = "MessagePack serializer" -category = "dev" optional = false python-versions = ">=3.8" files = [ @@ -1098,7 +1089,6 @@ files = [ name = "multidict" version = "6.0.4" description = "multidict implementation" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -1182,7 +1172,6 @@ files = [ name = "nb-cli" version = "1.2.6" description = "CLI for nonebot2" -category = "dev" optional = false python-versions = ">=3.8, <4.0" files = [ @@ -1211,7 +1200,6 @@ wcwidth = ">=0.2,<1.0" name = "nonebot-adapter-console" version = "0.4.0" description = "console adapter for nonebot2" -category = "dev" optional = false python-versions = "<4.0,>=3.8" files = [ @@ -1227,7 +1215,6 @@ nonechat = ">=0.2.0,<1.0.0" name = "nonebot-adapter-discord" version = "0.1.1" description = "Discord adapter for nonebot2" -category = "dev" optional = false python-versions = ">=3.8,<4.0" files = [ @@ -1242,7 +1229,6 @@ nonebot2 = ">=2.0.0,<3.0.0" name = "nonebot-adapter-dodo" version = "0.1.3" description = "Dodo adapter for nonebot2" -category = "dev" optional = false python-versions = ">=3.8,<4.0" files = [ @@ -1257,7 +1243,6 @@ nonebot2 = ">=2.0.0,<3.0.0" name = "nonebot-adapter-feishu" version = "2.3.1" description = "feishu(larksuite) adapter for nonebot2" -category = "dev" optional = false python-versions = ">=3.8,<4.0" files = [ @@ -1274,7 +1259,6 @@ pycryptodome = ">=3.18.0,<4.0.0" name = "nonebot-adapter-kaiheila" version = "0.2.12" description = "kaiheila adapter for nonebot2" -category = "dev" optional = false python-versions = ">=3.8,<4.0" files = [ @@ -1289,7 +1273,6 @@ nonebot2 = ">=2.0.0,<3.0.0" name = "nonebot-adapter-onebot" version = "2.3.1" description = "OneBot(CQHTTP) adapter for nonebot2" -category = "dev" optional = false python-versions = ">=3.8,<4.0" files = [ @@ -1306,7 +1289,6 @@ typing-extensions = ">=4.0.0,<5.0.0" name = "nonebot-adapter-qqguild" version = "0.2.5" description = "QQ Guild adapter for nonebot2" -category = "dev" optional = false python-versions = ">=3.8,<4.0" files = [ @@ -1322,7 +1304,6 @@ pydantic = ">=1.9.0,<2.0.0" name = "nonebot-adapter-red" version = "0.5.2" description = "Red Protocol Adapter for Nonebot2" -category = "dev" optional = false python-versions = ">=3.8" files = [ @@ -1341,7 +1322,6 @@ auto-detect = ["PyYAML"] name = "nonebot-adapter-telegram" version = "0.1.0b14" description = "Telegram Adapter for NoneBot2" -category = "dev" optional = false python-versions = ">=3.8,<4.0" files = [ @@ -1353,11 +1333,26 @@ files = [ anyio = ">=3.6.2,<4.0.0" nonebot2 = ">=2.0.0b1,<3.0.0" +[[package]] +name = "nonebot-adapter-villa" +version = "1.2.2" +description = "NoneBot2 米游社大别野 Bot 适配器。MiHoYo Villa Bot adapter for nonebot2." +optional = false +python-versions = ">=3.8,<4.0" +files = [ + {file = "nonebot_adapter_villa-1.2.2-py3-none-any.whl", hash = "sha256:993a89d978471dfa2a59030ac4ffd9fb043b0a8ee3576b4461dfbf1ae26a244f"}, + {file = "nonebot_adapter_villa-1.2.2.tar.gz", hash = "sha256:a10841c3ea02ea75152dfee4cd6cdc1b67c6ff0aa1f0b04a51f5b77ee68e206b"}, +] + +[package.dependencies] +betterproto = ">=1.2.5,<2.0.0" +nonebot2 = ">=2.1.2,<3.0.0" +rsa = ">=4.9,<5.0" + [[package]] name = "nonebot-plugin-datastore" version = "1.1.2" description = "适用于 Nonebot2 的数据存储插件" -category = "dev" optional = false python-versions = ">=3.8,<4.0" files = [ @@ -1381,7 +1376,6 @@ yaml = ["pyyaml (>=6.0)"] name = "nonebot-plugin-localstore" version = "0.5.1" description = "Local Storage Support for NoneBot2" -category = "main" optional = false python-versions = ">=3.8,<4.0" files = [ @@ -1397,7 +1391,6 @@ typing-extensions = ">=4.0.0" name = "nonebot-plugin-orm" version = "0.6.0" description = "SQLAlchemy ORM support for nonebot" -category = "main" optional = false python-versions = "<4.0,>=3.8" files = [ @@ -1431,7 +1424,6 @@ sqlite = ["aiosqlite (>=0.19,<1.0)"] name = "nonebot-plugin-session" version = "0.2.3" description = "Nonebot2 会话信息提取与会话id定义" -category = "main" optional = false python-versions = ">=3.8,<4.0" files = [ @@ -1447,7 +1439,6 @@ strenum = ">=0.4.8,<0.5.0" name = "nonebot-plugin-session-orm" version = "0.1.1" description = "session 插件 orm 扩展" -category = "main" optional = false python-versions = ">=3.8,<4.0" files = [ @@ -1464,7 +1455,6 @@ nonebot2 = {version = ">=2.0.1,<3.0.0", extras = ["fastapi"]} name = "nonebot2" version = "2.1.2" description = "An asynchronous python bot framework." -category = "main" optional = false python-versions = ">=3.8,<4.0" files = [ @@ -1495,7 +1485,6 @@ websockets = ["websockets (>=10.0)"] name = "nonebug" version = "0.3.5" description = "nonebot2 test framework" -category = "dev" optional = false python-versions = ">=3.8,<4.0" files = [ @@ -1514,7 +1503,6 @@ typing-extensions = ">=4.0.0,<5.0.0" name = "nonechat" version = "0.2.1" description = "Awesome chat console using Textual" -category = "dev" optional = false python-versions = "<4.0,>=3.8" files = [ @@ -1529,7 +1517,6 @@ textual = ">=0.29.0,<0.30.0" name = "noneprompt" version = "0.1.9" description = "Prompt toolkit for console interaction" -category = "dev" optional = false python-versions = ">=3.8,<4.0" files = [ @@ -1544,7 +1531,6 @@ prompt-toolkit = ">=3.0.19,<4.0.0" name = "packaging" version = "23.2" description = "Core utilities for Python packages" -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -1556,7 +1542,6 @@ files = [ name = "platformdirs" version = "4.0.0" description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -1572,7 +1557,6 @@ test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4)", "pytest-co name = "pluggy" version = "1.3.0" description = "plugin and hook calling mechanisms for python" -category = "dev" optional = false python-versions = ">=3.8" files = [ @@ -1588,7 +1572,6 @@ testing = ["pytest", "pytest-benchmark"] name = "prompt-toolkit" version = "3.0.41" description = "Library for building powerful interactive command lines in Python" -category = "dev" optional = false python-versions = ">=3.7.0" files = [ @@ -1599,11 +1582,21 @@ files = [ [package.dependencies] wcwidth = "*" +[[package]] +name = "pyasn1" +version = "0.5.1" +description = "Pure-Python implementation of ASN.1 types and DER/BER/CER codecs (X.208)" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7" +files = [ + {file = "pyasn1-0.5.1-py2.py3-none-any.whl", hash = "sha256:4439847c58d40b1d0a573d07e3856e95333f1976294494c325775aeca506eb58"}, + {file = "pyasn1-0.5.1.tar.gz", hash = "sha256:6d391a96e59b23130a5cfa74d6fd7f388dbbe26cc8f1edf39fdddf08d9d6676c"}, +] + [[package]] name = "pycryptodome" version = "3.19.0" description = "Cryptographic library for Python" -category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" files = [ @@ -1645,7 +1638,6 @@ files = [ name = "pydantic" version = "1.10.13" description = "Data validation and settings management using python type hints" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -1699,7 +1691,6 @@ email = ["email-validator (>=1.0.3)"] name = "pyfiglet" version = "0.8.post1" description = "Pure-python FIGlet implementation" -category = "dev" optional = false python-versions = "*" files = [ @@ -1711,7 +1702,6 @@ files = [ name = "pygments" version = "2.17.2" description = "Pygments is a syntax highlighting package written in Python." -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -1727,7 +1717,6 @@ windows-terminal = ["colorama (>=0.4.6)"] name = "pygtrie" version = "2.5.0" description = "A pure Python trie data structure implementation." -category = "main" optional = false python-versions = "*" files = [ @@ -1739,7 +1728,6 @@ files = [ name = "pymysql" version = "1.1.0" description = "Pure Python MySQL Driver" -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -1755,7 +1743,6 @@ rsa = ["cryptography"] name = "pytest" version = "7.4.3" description = "pytest: simple powerful testing with Python" -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -1778,7 +1765,6 @@ testing = ["argcomplete", "attrs (>=19.2.0)", "hypothesis (>=3.56)", "mock", "no name = "pytest-asyncio" version = "0.21.1" description = "Pytest support for asyncio" -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -1797,7 +1783,6 @@ testing = ["coverage (>=6.2)", "flaky (>=3.5.0)", "hypothesis (>=5.7.1)", "mypy name = "pytest-cov" version = "4.1.0" description = "Pytest plugin for measuring coverage." -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -1816,7 +1801,6 @@ testing = ["fields", "hunter", "process-tests", "pytest-xdist", "six", "virtuale name = "pytest-xdist" version = "3.5.0" description = "pytest xdist plugin for distributed testing, most importantly across multiple CPUs" -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -1837,7 +1821,6 @@ testing = ["filelock"] name = "python-dateutil" version = "2.8.2" description = "Extensions to the standard Python datetime module" -category = "dev" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" files = [ @@ -1852,7 +1835,6 @@ six = ">=1.5" name = "python-dotenv" version = "1.0.0" description = "Read key-value pairs from a .env file and set them as environment variables" -category = "main" optional = false python-versions = ">=3.8" files = [ @@ -1867,7 +1849,6 @@ cli = ["click (>=5.0)"] name = "python-slugify" version = "8.0.1" description = "A Python slugify application that also handles Unicode" -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -1885,7 +1866,6 @@ unidecode = ["Unidecode (>=1.1.1)"] name = "pyyaml" version = "6.0.1" description = "YAML parser and emitter for Python" -category = "main" optional = false python-versions = ">=3.6" files = [ @@ -1945,7 +1925,6 @@ files = [ name = "requests" version = "2.31.0" description = "Python HTTP for Humans." -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -1967,7 +1946,6 @@ use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] name = "rich" version = "13.7.0" description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal" -category = "dev" optional = false python-versions = ">=3.7.0" files = [ @@ -1983,11 +1961,24 @@ typing-extensions = {version = ">=4.0.0,<5.0", markers = "python_version < \"3.9 [package.extras] jupyter = ["ipywidgets (>=7.5.1,<9)"] +[[package]] +name = "rsa" +version = "4.9" +description = "Pure-Python RSA implementation" +optional = false +python-versions = ">=3.6,<4" +files = [ + {file = "rsa-4.9-py3-none-any.whl", hash = "sha256:90260d9058e514786967344d0ef75fa8727eed8a7d2e43ce9f4bcf1b536174f7"}, + {file = "rsa-4.9.tar.gz", hash = "sha256:e38464a49c6c85d7f1351b0126661487a7e0a14a50f1675ec50eb34d4f20ef21"}, +] + +[package.dependencies] +pyasn1 = ">=0.1.3" + [[package]] name = "six" version = "1.16.0" description = "Python 2 and 3 compatibility utilities" -category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" files = [ @@ -1999,7 +1990,6 @@ files = [ name = "sniffio" version = "1.3.0" description = "Sniff out which async library your code is running under" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -2011,7 +2001,6 @@ files = [ name = "sqlalchemy" version = "2.0.23" description = "Database Abstraction Library" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -2068,11 +2057,8 @@ files = [ [package.dependencies] aiosqlite = {version = "*", optional = true, markers = "extra == \"aiosqlite\""} -greenlet = {version = "!=0.4.17", markers = "platform_machine == \"aarch64\" or platform_machine == \"ppc64le\" or platform_machine == \"x86_64\" or platform_machine == \"amd64\" or platform_machine == \"AMD64\" or platform_machine == \"win32\" or platform_machine == \"WIN32\""} -typing-extensions = [ - {version = ">=4.2.0"}, - {version = "!=3.10.0.1", optional = true, markers = "extra == \"aiosqlite\""}, -] +greenlet = {version = "!=0.4.17", optional = true, markers = "platform_machine == \"aarch64\" or platform_machine == \"ppc64le\" or platform_machine == \"x86_64\" or platform_machine == \"amd64\" or platform_machine == \"AMD64\" or platform_machine == \"win32\" or platform_machine == \"WIN32\" or extra == \"aiosqlite\""} +typing-extensions = {version = ">=4.2.0", optional = true, markers = "extra == \"aiosqlite\""} [package.extras] aiomysql = ["aiomysql (>=0.2.0)", "greenlet (!=0.4.17)"] @@ -2103,7 +2089,6 @@ sqlcipher = ["sqlcipher3-binary"] name = "starlette" version = "0.27.0" description = "The little ASGI library that shines." -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -2122,7 +2107,6 @@ full = ["httpx (>=0.22.0)", "itsdangerous", "jinja2", "python-multipart", "pyyam name = "strenum" version = "0.4.15" description = "An Enum that inherits from str." -category = "main" optional = false python-versions = "*" files = [ @@ -2135,11 +2119,20 @@ docs = ["myst-parser[linkify]", "sphinx", "sphinx-rtd-theme"] release = ["twine"] test = ["pylint", "pytest", "pytest-black", "pytest-cov", "pytest-pylint"] +[[package]] +name = "stringcase" +version = "1.2.0" +description = "String case converter." +optional = false +python-versions = "*" +files = [ + {file = "stringcase-1.2.0.tar.gz", hash = "sha256:48a06980661908efe8d9d34eab2b6c13aefa2163b3ced26972902e3bdfd87008"}, +] + [[package]] name = "text-unidecode" version = "1.3" description = "The most basic Text::Unidecode port" -category = "dev" optional = false python-versions = "*" files = [ @@ -2151,7 +2144,6 @@ files = [ name = "textual" version = "0.29.0" description = "Modern Text User Interface framework" -category = "dev" optional = false python-versions = ">=3.7,<4.0" files = [ @@ -2169,7 +2161,6 @@ typing-extensions = ">=4.4.0,<5.0.0" name = "tomli" version = "2.0.1" description = "A lil' TOML parser" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -2181,7 +2172,6 @@ files = [ name = "tomlkit" version = "0.12.3" description = "Style preserving TOML library" -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -2193,7 +2183,6 @@ files = [ name = "types-python-dateutil" version = "2.8.19.14" description = "Typing stubs for python-dateutil" -category = "dev" optional = false python-versions = "*" files = [ @@ -2205,7 +2194,6 @@ files = [ name = "typing-extensions" version = "4.8.0" description = "Backported and Experimental Type Hints for Python 3.8+" -category = "main" optional = false python-versions = ">=3.8" files = [ @@ -2217,7 +2205,6 @@ files = [ name = "uc-micro-py" version = "1.0.2" description = "Micro subset of unicode data files for linkify-it-py projects." -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -2232,7 +2219,6 @@ test = ["coverage", "pytest", "pytest-cov"] name = "urllib3" version = "2.1.0" description = "HTTP library with thread-safe connection pooling, file post, and more." -category = "dev" optional = false python-versions = ">=3.8" files = [ @@ -2249,7 +2235,6 @@ zstd = ["zstandard (>=0.18.0)"] name = "uvicorn" version = "0.24.0.post1" description = "The lightning-fast ASGI server." -category = "main" optional = false python-versions = ">=3.8" files = [ @@ -2265,7 +2250,7 @@ httptools = {version = ">=0.5.0", optional = true, markers = "extra == \"standar python-dotenv = {version = ">=0.13", optional = true, markers = "extra == \"standard\""} pyyaml = {version = ">=5.1", optional = true, markers = "extra == \"standard\""} typing-extensions = {version = ">=4.0", markers = "python_version < \"3.11\""} -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\""} +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\""} watchfiles = {version = ">=0.13", optional = true, markers = "extra == \"standard\""} websockets = {version = ">=10.4", optional = true, markers = "extra == \"standard\""} @@ -2276,7 +2261,6 @@ standard = ["colorama (>=0.4)", "httptools (>=0.5.0)", "python-dotenv (>=0.13)", name = "uvloop" version = "0.19.0" description = "Fast implementation of asyncio event loop on top of libuv" -category = "main" optional = false python-versions = ">=3.8.0" files = [ @@ -2321,7 +2305,6 @@ test = ["Cython (>=0.29.36,<0.30.0)", "aiohttp (==3.9.0b0)", "aiohttp (>=3.8.1)" name = "virtualenv" version = "20.24.7" description = "Virtual Python Environment builder" -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -2342,7 +2325,6 @@ test = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "coverage-enable-subprocess name = "watchfiles" version = "0.21.0" description = "Simple, modern and high performance file watching and code reload in python." -category = "main" optional = false python-versions = ">=3.8" files = [ @@ -2430,7 +2412,6 @@ anyio = ">=3.0.0" name = "wcwidth" version = "0.2.12" description = "Measures the displayed width of unicode strings in a terminal" -category = "dev" optional = false python-versions = "*" files = [ @@ -2442,7 +2423,6 @@ files = [ name = "websockets" version = "12.0" description = "An implementation of the WebSocket Protocol (RFC 6455 & 7692)" -category = "main" optional = false python-versions = ">=3.8" files = [ @@ -2524,7 +2504,6 @@ files = [ name = "win32-setctime" version = "1.1.0" description = "A small Python utility to set file creation time on Windows" -category = "main" optional = false python-versions = ">=3.5" files = [ @@ -2539,7 +2518,6 @@ dev = ["black (>=19.3b0)", "pytest (>=4.6.2)"] name = "yarl" version = "1.9.3" description = "Yet another URL library" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -2643,7 +2621,6 @@ multidict = ">=4.0" name = "zipp" version = "3.17.0" description = "Backport of pathlib-compatible object wrapper for zip files" -category = "main" optional = false python-versions = ">=3.8" files = [ @@ -2658,4 +2635,4 @@ testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "p [metadata] lock-version = "2.0" python-versions = "^3.8" -content-hash = "a49e68b6bbd25fc70ba7ad32792d06ae70062e518ab912a094eb224789d8011c" +content-hash = "3e144aa85948935bdfb6e9b9af83b01ca71af5d8885d2b67bd2bc9f90e3efcb2" diff --git a/pyproject.toml b/pyproject.toml index 9fa6145..0ac5e4b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -42,6 +42,7 @@ nonebot-adapter-feishu = "^2.2.0" nonebot-adapter-red = "^0.5.2" nonebot-adapter-discord = "^0.1.0b4" nonebot-adapter-dodo = "^0.1.3" +nonebot-adapter-villa = "^1.2.2" [tool.pytest.ini_options] asyncio_mode = "auto" diff --git a/tests/test_villa.py b/tests/test_villa.py new file mode 100644 index 0000000..8f65707 --- /dev/null +++ b/tests/test_villa.py @@ -0,0 +1,139 @@ +import json +from datetime import datetime + +from nonebot import get_driver +from nonebot.adapters.villa import Adapter, Bot, Message, SendMessageEvent +from nonebot.adapters.villa.config import BotInfo +from nonebug.app import App + +from .utils import check_record + + +async def test_record_recv_msg(app: App): + """测试记录收到的消息""" + from nonebot_plugin_chatrecorder.adapters.villa import record_recv_msg + from nonebot_plugin_chatrecorder.message import serialize_message + + async with app.test_api() as ctx: + bot = ctx.create_bot( + base=Bot, + adapter=Adapter(get_driver()), + self_id="2233", + bot_info=BotInfo( + bot_id="2233", + bot_secret="xxxx", + pub_key=( + "-----BEGIN PUBLIC KEY-----\n" + "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC8KTG22btc3g0SjiH9z352/SkQ\n" + "QLXcpaQbzBCgV1A410yMKlgRkM3uyO/kJHGBNAiL6JNe0aH9Gjh18jQgq0toKIUe\n" + "1GLPXiC9rrwoFj7VS6istJteSfm2vPIwZw98duUlBnK39OjKDhKSh5TOPpgJh9gn\n" + "FVaGNJhR9k4pCvbtzQIDAQAB\n" + "-----END PUBLIC KEY-----\n" + ), + ), + ) + + event = SendMessageEvent.parse_obj( + { + "robot": { + "template": { + "id": "2233", + "name": "bot", + "desc": "", + "icon": "", + "commands": [{"name": "/echo", "desc": ""}], + }, + "villa_id": 7788, + }, + "type": 2, + "created_at": 1701226761, + "id": "8ee4c10d-8354-18d7-84df-7e02f034cfd1", + "send_at": 1701226761000, + "content": json.dumps( + { + "content": {"text": "test send message", "entities": []}, + "user": { + "portraitUri": "https://bbs-static.miyoushe.com/avatar/avatar40004.png", + "extra": {}, + "name": "user", + "alias": "", + "id": "114514", + "portrait": "https://bbs-static.miyoushe.com/avatar/avatar40004.png", + }, + } + ), + "villa_id": 7788, + "from_user_id": 3344, + "object_name": 1, + "room_id": 5566, + "nickname": "user", + "msg_uid": "123456", + } + ) + await record_recv_msg(bot, event) + await check_record( + "2233", + "Villa", + "villa", + 3, + "3344", + "5566", + "7788", + datetime.utcfromtimestamp(1701226761000 / 1000), + "message", + "123456", + serialize_message(bot, Message("test send message")), + "test send message", + ) + + +async def test_record_send_msg(app: App): + """测试记录发送的消息""" + from nonebot_plugin_chatrecorder.adapters.villa import record_send_msg + from nonebot_plugin_chatrecorder.message import serialize_message + + async with app.test_api() as ctx: + bot = ctx.create_bot( + base=Bot, + adapter=Adapter(get_driver()), + self_id="2233", + bot_info=BotInfo( + bot_id="2233", + bot_secret="xxxx", + pub_key=( + "-----BEGIN PUBLIC KEY-----\n" + "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC8KTG22btc3g0SjiH9z352/SkQ\n" + "QLXcpaQbzBCgV1A410yMKlgRkM3uyO/kJHGBNAiL6JNe0aH9Gjh18jQgq0toKIUe\n" + "1GLPXiC9rrwoFj7VS6istJteSfm2vPIwZw98duUlBnK39OjKDhKSh5TOPpgJh9gn\n" + "FVaGNJhR9k4pCvbtzQIDAQAB\n" + "-----END PUBLIC KEY-----\n" + ), + ), + ) + + await record_send_msg( + bot, + None, + "send_message", + { + "villa_id": 7788, + "room_id": 5566, + "object_name": "MHY:Text", + "msg_content": '{"content": {"text": "test", "entities": []}}', + }, + "123457", + ) + await check_record( + "2233", + "Villa", + "villa", + 3, + None, + "5566", + "7788", + None, + "message_sent", + "123457", + serialize_message(bot, Message("test")), + "test", + )