nonebot-adapter-qq 1.5.3__py3-none-any.whl → 1.6.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- nonebot/adapters/qq/adapter.py +201 -25
- nonebot/adapters/qq/bot.py +17 -16
- nonebot/adapters/qq/compat.py +3 -3
- nonebot/adapters/qq/config.py +5 -13
- nonebot/adapters/qq/event.py +54 -54
- nonebot/adapters/qq/message.py +2 -2
- nonebot/adapters/qq/models/__init__.py +1 -0
- nonebot/adapters/qq/models/common.py +17 -17
- nonebot/adapters/qq/models/guild.py +52 -52
- nonebot/adapters/qq/models/payload.py +20 -2
- nonebot/adapters/qq/models/qq.py +8 -6
- nonebot/adapters/qq/permission.py +2 -2
- {nonebot_adapter_qq-1.5.3.dist-info → nonebot_adapter_qq-1.6.1.dist-info}/METADATA +19 -8
- nonebot_adapter_qq-1.6.1.dist-info/RECORD +20 -0
- {nonebot_adapter_qq-1.5.3.dist-info → nonebot_adapter_qq-1.6.1.dist-info}/WHEEL +1 -1
- nonebot_adapter_qq-1.5.3.dist-info/RECORD +0 -20
- {nonebot_adapter_qq-1.5.3.dist-info → nonebot_adapter_qq-1.6.1.dist-info}/LICENSE +0 -0
nonebot/adapters/qq/adapter.py
CHANGED
@@ -1,20 +1,28 @@
|
|
1
1
|
import sys
|
2
|
+
import json
|
2
3
|
import asyncio
|
4
|
+
import binascii
|
3
5
|
from typing_extensions import override
|
4
|
-
from typing import Any, Literal, Optional
|
6
|
+
from typing import Any, Union, Literal, Optional, cast
|
5
7
|
|
6
8
|
from nonebot.utils import escape_tag
|
7
9
|
from nonebot.exception import WebSocketClosed
|
10
|
+
from cryptography.exceptions import InvalidSignature
|
11
|
+
from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey
|
8
12
|
from nonebot.compat import PYDANTIC_V2, type_validate_json, type_validate_python
|
9
13
|
from nonebot.drivers import (
|
10
14
|
URL,
|
11
15
|
Driver,
|
12
16
|
Request,
|
17
|
+
Response,
|
18
|
+
ASGIMixin,
|
13
19
|
WebSocket,
|
14
20
|
HTTPClientMixin,
|
21
|
+
HTTPServerSetup,
|
15
22
|
WebSocketClientMixin,
|
16
23
|
)
|
17
24
|
|
25
|
+
from nonebot import get_plugin_config
|
18
26
|
from nonebot.adapters import Adapter as BaseAdapter
|
19
27
|
|
20
28
|
from .bot import Bot
|
@@ -33,6 +41,7 @@ from .models import (
|
|
33
41
|
Reconnect,
|
34
42
|
PayloadType,
|
35
43
|
HeartbeatAck,
|
44
|
+
WebhookVerify,
|
36
45
|
InvalidSession,
|
37
46
|
)
|
38
47
|
|
@@ -44,7 +53,7 @@ class Adapter(BaseAdapter):
|
|
44
53
|
def __init__(self, driver: Driver, **kwargs: Any):
|
45
54
|
super().__init__(driver, **kwargs)
|
46
55
|
|
47
|
-
self.qq_config: Config = Config
|
56
|
+
self.qq_config: Config = get_plugin_config(Config)
|
48
57
|
|
49
58
|
self.tasks: set["asyncio.Task"] = set()
|
50
59
|
self.setup()
|
@@ -61,20 +70,29 @@ class Adapter(BaseAdapter):
|
|
61
70
|
"http client requests! "
|
62
71
|
"QQ Adapter need a HTTPClient Driver to work."
|
63
72
|
)
|
64
|
-
|
73
|
+
|
74
|
+
if any(bot.use_websocket for bot in self.qq_config.qq_bots) and not isinstance(
|
75
|
+
self.driver, WebSocketClientMixin
|
76
|
+
):
|
65
77
|
raise RuntimeError(
|
66
78
|
f"Current driver {self.config.driver} does not support "
|
67
79
|
"websocket client! "
|
68
80
|
"QQ Adapter need a WebSocketClient Driver to work."
|
69
81
|
)
|
82
|
+
|
83
|
+
if not all(
|
84
|
+
bot.use_websocket for bot in self.qq_config.qq_bots
|
85
|
+
) and not isinstance(self.driver, ASGIMixin):
|
86
|
+
raise RuntimeError(
|
87
|
+
f"Current driver {self.config.driver} does not support "
|
88
|
+
"ASGI server! "
|
89
|
+
"QQ Adapter need a ASGI Driver to receive webhook."
|
90
|
+
)
|
70
91
|
self.on_ready(self.startup)
|
71
92
|
self.driver.on_shutdown(self.shutdown)
|
72
93
|
|
73
94
|
async def startup(self) -> None:
|
74
|
-
log(
|
75
|
-
"DEBUG",
|
76
|
-
("QQ run in sandbox mode: " f"<y>{self.qq_config.qq_is_sandbox}</y>"),
|
77
|
-
)
|
95
|
+
log("DEBUG", f"QQ run in sandbox mode: <y>{self.qq_config.qq_is_sandbox}</y>")
|
78
96
|
|
79
97
|
try:
|
80
98
|
api_base = self.get_api_base()
|
@@ -84,8 +102,36 @@ class Adapter(BaseAdapter):
|
|
84
102
|
|
85
103
|
log("DEBUG", f"QQ api base url: <y>{escape_tag(str(api_base))}</y>")
|
86
104
|
|
105
|
+
if isinstance(self.driver, ASGIMixin):
|
106
|
+
self.setup_http_server(
|
107
|
+
HTTPServerSetup(
|
108
|
+
URL("/qq/"),
|
109
|
+
"POST",
|
110
|
+
f"{self.get_name()} Root Webhook",
|
111
|
+
self._handle_http,
|
112
|
+
),
|
113
|
+
)
|
114
|
+
self.setup_http_server(
|
115
|
+
HTTPServerSetup(
|
116
|
+
URL("/qq/webhook"),
|
117
|
+
"POST",
|
118
|
+
f"{self.get_name()} Webhook",
|
119
|
+
self._handle_http,
|
120
|
+
),
|
121
|
+
)
|
122
|
+
self.setup_http_server(
|
123
|
+
HTTPServerSetup(
|
124
|
+
URL("/qq/webhook/"),
|
125
|
+
"POST",
|
126
|
+
f"{self.get_name()} Webhook Slash",
|
127
|
+
self._handle_http,
|
128
|
+
),
|
129
|
+
)
|
130
|
+
|
87
131
|
for bot in self.qq_config.qq_bots:
|
88
|
-
|
132
|
+
if not bot.use_websocket:
|
133
|
+
continue
|
134
|
+
task = asyncio.create_task(self.run_bot_websocket(bot))
|
89
135
|
task.add_done_callback(self.tasks.discard)
|
90
136
|
self.tasks.add(task)
|
91
137
|
|
@@ -99,7 +145,7 @@ class Adapter(BaseAdapter):
|
|
99
145
|
return_exceptions=True,
|
100
146
|
)
|
101
147
|
|
102
|
-
async def
|
148
|
+
async def run_bot_websocket(self, bot_info: BotInfo) -> None:
|
103
149
|
bot = Bot(self, bot_info.id, bot_info)
|
104
150
|
|
105
151
|
# get sharded gateway url
|
@@ -347,20 +393,7 @@ class Adapter(BaseAdapter):
|
|
347
393
|
f"Received payload: {escape_tag(repr(payload))}",
|
348
394
|
)
|
349
395
|
if isinstance(payload, Dispatch):
|
350
|
-
|
351
|
-
event = self.payload_to_event(payload)
|
352
|
-
except Exception as e:
|
353
|
-
log(
|
354
|
-
"WARNING",
|
355
|
-
f"Failed to parse event {escape_tag(repr(payload))}",
|
356
|
-
e,
|
357
|
-
)
|
358
|
-
else:
|
359
|
-
if isinstance(event, MessageAuditEvent):
|
360
|
-
audit_result.add_result(event)
|
361
|
-
task = asyncio.create_task(bot.handle_event(event))
|
362
|
-
task.add_done_callback(self.tasks.discard)
|
363
|
-
self.tasks.add(task)
|
396
|
+
self.dispatch_event(bot, payload)
|
364
397
|
elif isinstance(payload, HeartbeatAck):
|
365
398
|
log("TRACE", "Heartbeat ACK")
|
366
399
|
continue
|
@@ -383,6 +416,133 @@ class Adapter(BaseAdapter):
|
|
383
416
|
f"Unknown payload from server: {escape_tag(repr(payload))}",
|
384
417
|
)
|
385
418
|
|
419
|
+
async def receive_payload(self, bot: Bot, ws: WebSocket) -> Payload:
|
420
|
+
return self.data_to_payload(bot, await ws.receive())
|
421
|
+
|
422
|
+
async def _handle_http(self, request: Request) -> Response:
|
423
|
+
bot_id = request.headers.get("X-Bot-Appid")
|
424
|
+
if not bot_id:
|
425
|
+
log("WARNING", "Missing X-Bot-Appid header in request")
|
426
|
+
return Response(403, content="Missing X-Bot-Appid header")
|
427
|
+
elif bot_id in self.bots:
|
428
|
+
bot = cast(Bot, self.bots[bot_id])
|
429
|
+
elif bot_info := next(
|
430
|
+
(bot_info for bot_info in self.qq_config.qq_bots if bot_info.id == bot_id),
|
431
|
+
None,
|
432
|
+
):
|
433
|
+
bot = Bot(self, bot_id, bot_info)
|
434
|
+
else:
|
435
|
+
log("ERROR", f"Bot {bot_id} not found")
|
436
|
+
return Response(403, content="Bot not found")
|
437
|
+
|
438
|
+
if request.content is None:
|
439
|
+
return Response(400, content="Missing request content")
|
440
|
+
|
441
|
+
try:
|
442
|
+
payload = self.data_to_payload(bot, request.content)
|
443
|
+
except Exception as e:
|
444
|
+
log(
|
445
|
+
"ERROR",
|
446
|
+
"<r><bg #f8bbd0>Error while parsing data from webhook</bg #f8bbd0></r>",
|
447
|
+
e,
|
448
|
+
)
|
449
|
+
return Response(400, content="Invalid request content")
|
450
|
+
|
451
|
+
log(
|
452
|
+
"TRACE",
|
453
|
+
f"Received payload: {escape_tag(repr(payload))}",
|
454
|
+
)
|
455
|
+
if isinstance(payload, WebhookVerify):
|
456
|
+
log("INFO", "Received qq webhook verify request")
|
457
|
+
return self._webhook_verify(bot, payload)
|
458
|
+
|
459
|
+
if self.qq_config.qq_verify_webhook and (
|
460
|
+
response := self._check_signature(bot, request)
|
461
|
+
):
|
462
|
+
return response
|
463
|
+
|
464
|
+
# ensure bot self info
|
465
|
+
if not bot._self_info:
|
466
|
+
bot.self_info = await bot.me()
|
467
|
+
|
468
|
+
if bot.self_id not in self.bots:
|
469
|
+
self.bot_connect(bot)
|
470
|
+
|
471
|
+
if isinstance(payload, Dispatch):
|
472
|
+
self.dispatch_event(bot, payload)
|
473
|
+
|
474
|
+
return Response(200)
|
475
|
+
|
476
|
+
def _get_ed25519_key(self, bot: Bot) -> Ed25519PrivateKey:
|
477
|
+
secret = bot.bot_info.secret.encode()
|
478
|
+
seed = secret
|
479
|
+
while len(seed) < 32:
|
480
|
+
seed += secret
|
481
|
+
seed = seed[:32]
|
482
|
+
return Ed25519PrivateKey.from_private_bytes(seed)
|
483
|
+
|
484
|
+
def _webhook_verify(self, bot: Bot, payload: WebhookVerify) -> Response:
|
485
|
+
plain_token = payload.data.plain_token
|
486
|
+
event_ts = payload.data.event_ts
|
487
|
+
|
488
|
+
try:
|
489
|
+
private_key = self._get_ed25519_key(bot)
|
490
|
+
except Exception as e:
|
491
|
+
log("ERROR", "Failed to create private key", e)
|
492
|
+
return Response(500, content="Failed to create private key")
|
493
|
+
|
494
|
+
msg = f"{event_ts}{plain_token}".encode()
|
495
|
+
try:
|
496
|
+
signature = private_key.sign(msg)
|
497
|
+
signature_hex = binascii.hexlify(signature).decode()
|
498
|
+
except Exception as e:
|
499
|
+
log("ERROR", "Failed to sign message", e)
|
500
|
+
return Response(500, content="Failed to sign message")
|
501
|
+
|
502
|
+
return Response(
|
503
|
+
200,
|
504
|
+
content=json.dumps(
|
505
|
+
{"plain_token": plain_token, "signature": signature_hex}
|
506
|
+
),
|
507
|
+
)
|
508
|
+
|
509
|
+
def _check_signature(self, bot: Bot, request: Request) -> Optional[Response]:
|
510
|
+
signature = request.headers.get("X-Signature-Ed25519")
|
511
|
+
timestamp = request.headers.get("X-Signature-Timestamp")
|
512
|
+
if not signature or not timestamp:
|
513
|
+
log("WARNING", "Missing signature or timestamp in request")
|
514
|
+
return Response(403, content="Missing signature or timestamp")
|
515
|
+
|
516
|
+
if request.content is None:
|
517
|
+
return Response(400, content="Missing request content")
|
518
|
+
|
519
|
+
try:
|
520
|
+
private_key = self._get_ed25519_key(bot)
|
521
|
+
public_key = private_key.public_key()
|
522
|
+
except Exception as e:
|
523
|
+
log("ERROR", "Failed to create public key", e)
|
524
|
+
return Response(500, content="Failed to create public key")
|
525
|
+
|
526
|
+
signature = binascii.unhexlify(signature)
|
527
|
+
if len(signature) != 64 or signature[63] & 224 != 0:
|
528
|
+
log("WARNING", "Invalid signature in request")
|
529
|
+
return Response(403, content="Invalid signature")
|
530
|
+
|
531
|
+
body = (
|
532
|
+
request.content.encode()
|
533
|
+
if isinstance(request.content, str)
|
534
|
+
else request.content
|
535
|
+
)
|
536
|
+
msg = timestamp.encode() + body
|
537
|
+
try:
|
538
|
+
public_key.verify(signature, msg)
|
539
|
+
except InvalidSignature:
|
540
|
+
log("WARNING", "Invalid signature in request")
|
541
|
+
return Response(403, content="Invalid signature")
|
542
|
+
except Exception as e:
|
543
|
+
log("ERROR", "Failed to verify signature", e)
|
544
|
+
return Response(403, content="Failed to verify signature")
|
545
|
+
|
386
546
|
def get_auth_base(self) -> URL:
|
387
547
|
return URL(str(self.qq_config.qq_auth_base))
|
388
548
|
|
@@ -393,8 +553,8 @@ class Adapter(BaseAdapter):
|
|
393
553
|
return URL(str(self.qq_config.qq_api_base))
|
394
554
|
|
395
555
|
@staticmethod
|
396
|
-
|
397
|
-
payload = type_validate_json(PayloadType,
|
556
|
+
def data_to_payload(bot: Bot, data: Union[str, bytes]) -> Payload:
|
557
|
+
payload = type_validate_json(PayloadType, data)
|
398
558
|
if isinstance(payload, Dispatch):
|
399
559
|
bot.on_dispatch(payload)
|
400
560
|
return payload
|
@@ -406,6 +566,22 @@ class Adapter(BaseAdapter):
|
|
406
566
|
|
407
567
|
return payload.json(by_alias=True)
|
408
568
|
|
569
|
+
def dispatch_event(self, bot: Bot, payload: Dispatch):
|
570
|
+
try:
|
571
|
+
event = self.payload_to_event(payload)
|
572
|
+
except Exception as e:
|
573
|
+
log(
|
574
|
+
"WARNING",
|
575
|
+
f"Failed to parse event {escape_tag(repr(payload))}",
|
576
|
+
e,
|
577
|
+
)
|
578
|
+
else:
|
579
|
+
if isinstance(event, MessageAuditEvent):
|
580
|
+
audit_result.add_result(event)
|
581
|
+
task = asyncio.create_task(bot.handle_event(event))
|
582
|
+
task.add_done_callback(self.tasks.discard)
|
583
|
+
self.tasks.add(task)
|
584
|
+
|
409
585
|
@staticmethod
|
410
586
|
def payload_to_event(payload: Dispatch) -> Event:
|
411
587
|
EventClass = EVENT_CLASSES.get(payload.type, None)
|
nonebot/adapters/qq/bot.py
CHANGED
@@ -200,6 +200,10 @@ class Bot(BaseBot):
|
|
200
200
|
raise RuntimeError(f"Bot {self.bot_info} is not connected!")
|
201
201
|
return self._self_info
|
202
202
|
|
203
|
+
@self_info.setter
|
204
|
+
def self_info(self, info: User):
|
205
|
+
self._self_info = info
|
206
|
+
|
203
207
|
@property
|
204
208
|
def ready(self) -> bool:
|
205
209
|
"""Bot 是否已经准备就绪"""
|
@@ -259,16 +263,14 @@ class Bot(BaseBot):
|
|
259
263
|
|
260
264
|
async def _get_authorization_header(self) -> str:
|
261
265
|
"""获取当前 Bot 的鉴权信息"""
|
262
|
-
|
263
|
-
return f"QQBot {await self.get_access_token()}"
|
264
|
-
return f"Bot {self.bot_info.id}.{self.bot_info.token}"
|
266
|
+
return f"QQBot {await self.get_access_token()}"
|
265
267
|
|
266
268
|
async def get_authorization_header(self) -> dict[str, str]:
|
267
269
|
"""获取当前 Bot 的鉴权信息"""
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
270
|
+
return {
|
271
|
+
"Authorization": await self._get_authorization_header(),
|
272
|
+
"X-Union-Appid": self.bot_info.id,
|
273
|
+
}
|
272
274
|
|
273
275
|
async def handle_event(self, event: Event) -> None:
|
274
276
|
if isinstance(event, (GuildMessageEvent, QQMessageEvent)):
|
@@ -283,9 +285,11 @@ class Bot(BaseBot):
|
|
283
285
|
return _message
|
284
286
|
|
285
287
|
@staticmethod
|
286
|
-
def _extract_send_message(
|
288
|
+
def _extract_send_message(
|
289
|
+
message: Message, escape_text: bool = True
|
290
|
+
) -> dict[str, Any]:
|
287
291
|
kwargs = {}
|
288
|
-
content = message.extract_content() or None
|
292
|
+
content = message.extract_content(escape_text) or None
|
289
293
|
kwargs["content"] = content
|
290
294
|
if embed := (message["embed"] or None):
|
291
295
|
kwargs["embed"] = embed[-1].data["embed"]
|
@@ -349,7 +353,7 @@ class Bot(BaseBot):
|
|
349
353
|
guild_id=guild_id,
|
350
354
|
msg_id=msg_id,
|
351
355
|
event_id=event_id,
|
352
|
-
**self._extract_send_message(message=message),
|
356
|
+
**self._extract_send_message(message=message, escape_text=True),
|
353
357
|
**self._extract_guild_image(message=message),
|
354
358
|
)
|
355
359
|
|
@@ -365,7 +369,7 @@ class Bot(BaseBot):
|
|
365
369
|
channel_id=channel_id,
|
366
370
|
msg_id=msg_id,
|
367
371
|
event_id=event_id,
|
368
|
-
**self._extract_send_message(message=message),
|
372
|
+
**self._extract_send_message(message=message, escape_text=True),
|
369
373
|
**self._extract_guild_image(message=message),
|
370
374
|
)
|
371
375
|
|
@@ -378,7 +382,7 @@ class Bot(BaseBot):
|
|
378
382
|
event_id: Optional[str] = None,
|
379
383
|
) -> Union[PostC2CMessagesReturn, PostC2CFilesReturn]:
|
380
384
|
message = self._prepare_message(message)
|
381
|
-
kwargs = self._extract_send_message(message=message)
|
385
|
+
kwargs = self._extract_send_message(message=message, escape_text=False)
|
382
386
|
if kwargs.get("embed"):
|
383
387
|
msg_type = 4
|
384
388
|
elif kwargs.get("ark"):
|
@@ -427,7 +431,7 @@ class Bot(BaseBot):
|
|
427
431
|
event_id: Optional[str] = None,
|
428
432
|
) -> Union[PostGroupMessagesReturn, PostGroupFilesReturn]:
|
429
433
|
message = self._prepare_message(message)
|
430
|
-
kwargs = self._extract_send_message(message=message)
|
434
|
+
kwargs = self._extract_send_message(message=message, escape_text=False)
|
431
435
|
if kwargs.get("embed"):
|
432
436
|
msg_type = 4
|
433
437
|
elif kwargs.get("ark"):
|
@@ -566,9 +570,6 @@ class Bot(BaseBot):
|
|
566
570
|
try:
|
567
571
|
return self._handle_response(response)
|
568
572
|
except UnauthorizedException as e:
|
569
|
-
if not self.bot_info.is_group_bot:
|
570
|
-
raise
|
571
|
-
|
572
573
|
log("DEBUG", "Access token expired, try to refresh it.")
|
573
574
|
|
574
575
|
# try to refresh access token
|
nonebot/adapters/qq/compat.py
CHANGED
@@ -2,7 +2,7 @@ from typing import Literal, overload
|
|
2
2
|
|
3
3
|
from nonebot.compat import PYDANTIC_V2
|
4
4
|
|
5
|
-
__all__ = ("
|
5
|
+
__all__ = ("field_validator", "model_validator")
|
6
6
|
|
7
7
|
if PYDANTIC_V2:
|
8
8
|
from pydantic import field_validator as field_validator
|
@@ -19,5 +19,5 @@ else:
|
|
19
19
|
def model_validator(*, mode: Literal["before", "after"]):
|
20
20
|
return root_validator(pre=mode == "before", allow_reuse=True)
|
21
21
|
|
22
|
-
def field_validator(
|
23
|
-
return validator(
|
22
|
+
def field_validator(field, /, *fields, mode: Literal["before", "after"] = "after"):
|
23
|
+
return validator(field, *fields, pre=mode == "before", allow_reuse=True)
|
nonebot/adapters/qq/config.py
CHANGED
@@ -43,11 +43,6 @@ class Intents(BaseModel):
|
|
43
43
|
| self.at_messages << 30
|
44
44
|
)
|
45
45
|
|
46
|
-
@property
|
47
|
-
def is_group_enabled(self) -> bool:
|
48
|
-
"""是否开启群聊功能"""
|
49
|
-
return self.c2c_group_at_messages is True
|
50
|
-
|
51
46
|
|
52
47
|
class BotInfo(BaseModel):
|
53
48
|
id: str = Field(alias="id")
|
@@ -55,16 +50,13 @@ class BotInfo(BaseModel):
|
|
55
50
|
secret: str = Field(alias="secret")
|
56
51
|
shard: Optional[tuple[int, int]] = None
|
57
52
|
intent: Intents = Field(default_factory=Intents)
|
58
|
-
|
59
|
-
@property
|
60
|
-
def is_group_bot(self) -> bool:
|
61
|
-
"""是否为群机器人"""
|
62
|
-
return self.intent.is_group_enabled
|
53
|
+
use_websocket: bool = True
|
63
54
|
|
64
55
|
|
65
56
|
class Config(BaseModel):
|
66
57
|
qq_is_sandbox: bool = False
|
67
|
-
qq_api_base: HttpUrl = Field("https://api.sgroup.qq.com/")
|
68
|
-
qq_sandbox_api_base: HttpUrl = Field("https://sandbox.api.sgroup.qq.com")
|
69
|
-
qq_auth_base: HttpUrl = Field("https://bots.qq.com/app/getAppAccessToken")
|
58
|
+
qq_api_base: HttpUrl = Field("https://api.sgroup.qq.com/") # type: ignore
|
59
|
+
qq_sandbox_api_base: HttpUrl = Field("https://sandbox.api.sgroup.qq.com") # type: ignore
|
60
|
+
qq_auth_base: HttpUrl = Field("https://bots.qq.com/app/getAppAccessToken") # type: ignore
|
61
|
+
qq_verify_webhook: bool = True
|
70
62
|
qq_bots: list[BotInfo] = Field(default_factory=list)
|
nonebot/adapters/qq/event.py
CHANGED
@@ -680,75 +680,75 @@ class GroupMsgReceiveEvent(GroupRobotEvent):
|
|
680
680
|
|
681
681
|
__all__ = [
|
682
682
|
"EVENT_CLASSES",
|
683
|
-
"
|
683
|
+
"AtMessageCreateEvent",
|
684
|
+
"AudioEvent",
|
685
|
+
"AudioFinishEvent",
|
686
|
+
"AudioOffMicEvent",
|
687
|
+
"AudioOnMicEvent",
|
688
|
+
"AudioStartEvent",
|
689
|
+
"C2CMessageCreateEvent",
|
690
|
+
"C2CMsgReceiveEvent",
|
691
|
+
"C2CMsgRejectEvent",
|
692
|
+
"ChannelCreateEvent",
|
693
|
+
"ChannelDeleteEvent",
|
694
|
+
"ChannelEvent",
|
695
|
+
"ChannelUpdateEvent",
|
696
|
+
"DirectMessageCreateEvent",
|
697
|
+
"DirectMessageDeleteEvent",
|
684
698
|
"Event",
|
685
|
-
"
|
686
|
-
"
|
687
|
-
"
|
688
|
-
"
|
689
|
-
"
|
699
|
+
"EventType",
|
700
|
+
"ForumEvent",
|
701
|
+
"ForumPostCreateEvent",
|
702
|
+
"ForumPostDeleteEvent",
|
703
|
+
"ForumPostEvent",
|
704
|
+
"ForumPublishAuditResult",
|
705
|
+
"ForumReplyCreateEvent",
|
706
|
+
"ForumReplyDeleteEvent",
|
707
|
+
"ForumReplyEvent",
|
708
|
+
"ForumThreadCreateEvent",
|
709
|
+
"ForumThreadDeleteEvent",
|
710
|
+
"ForumThreadEvent",
|
711
|
+
"ForumThreadUpdateEvent",
|
712
|
+
"FriendAddEvent",
|
713
|
+
"FriendDelEvent",
|
714
|
+
"FriendRobotEvent",
|
715
|
+
"GroupAddRobotEvent",
|
716
|
+
"GroupAtMessageCreateEvent",
|
717
|
+
"GroupDelRobotEvent",
|
718
|
+
"GroupMsgReceiveEvent",
|
719
|
+
"GroupMsgRejectEvent",
|
720
|
+
"GroupRobotEvent",
|
690
721
|
"GuildCreateEvent",
|
691
|
-
"GuildUpdateEvent",
|
692
722
|
"GuildDeleteEvent",
|
693
|
-
"
|
694
|
-
"ChannelCreateEvent",
|
695
|
-
"ChannelUpdateEvent",
|
696
|
-
"ChannelDeleteEvent",
|
697
|
-
"GuildMemberEvent",
|
723
|
+
"GuildEvent",
|
698
724
|
"GuildMemberAddEvent",
|
699
|
-
"
|
725
|
+
"GuildMemberEvent",
|
700
726
|
"GuildMemberRemoveEvent",
|
701
|
-
"
|
727
|
+
"GuildMemberUpdateEvent",
|
702
728
|
"GuildMessageEvent",
|
703
|
-
"
|
704
|
-
"MessageDeleteEvent",
|
705
|
-
"AtMessageCreateEvent",
|
706
|
-
"PublicMessageDeleteEvent",
|
707
|
-
"DirectMessageCreateEvent",
|
708
|
-
"DirectMessageDeleteEvent",
|
709
|
-
"QQMessageEvent",
|
710
|
-
"C2CMessageCreateEvent",
|
711
|
-
"GroupAtMessageCreateEvent",
|
729
|
+
"GuildUpdateEvent",
|
712
730
|
"InteractionCreateEvent",
|
713
731
|
"MessageAuditEvent",
|
714
732
|
"MessageAuditPassEvent",
|
715
733
|
"MessageAuditRejectEvent",
|
716
|
-
"
|
734
|
+
"MessageCreateEvent",
|
735
|
+
"MessageDeleteEvent",
|
736
|
+
"MessageEvent",
|
717
737
|
"MessageReactionAddEvent",
|
738
|
+
"MessageReactionEvent",
|
718
739
|
"MessageReactionRemoveEvent",
|
719
|
-
"
|
720
|
-
"
|
721
|
-
"AudioFinishEvent",
|
722
|
-
"AudioOnMicEvent",
|
723
|
-
"AudioOffMicEvent",
|
724
|
-
"ForumEvent",
|
725
|
-
"ForumThreadEvent",
|
726
|
-
"ForumThreadCreateEvent",
|
727
|
-
"ForumThreadUpdateEvent",
|
728
|
-
"ForumThreadDeleteEvent",
|
729
|
-
"ForumPostEvent",
|
730
|
-
"ForumPostCreateEvent",
|
731
|
-
"ForumPostDeleteEvent",
|
732
|
-
"ForumReplyEvent",
|
733
|
-
"ForumReplyCreateEvent",
|
734
|
-
"ForumReplyDeleteEvent",
|
735
|
-
"ForumPublishAuditResult",
|
740
|
+
"MetaEvent",
|
741
|
+
"NoticeEvent",
|
736
742
|
"OpenForumEvent",
|
737
|
-
"OpenForumThreadCreateEvent",
|
738
|
-
"OpenForumThreadUpdateEvent",
|
739
|
-
"OpenForumThreadDeleteEvent",
|
740
743
|
"OpenForumPostCreateEvent",
|
741
744
|
"OpenForumPostDeleteEvent",
|
742
745
|
"OpenForumReplyCreateEvent",
|
743
746
|
"OpenForumReplyDeleteEvent",
|
744
|
-
"
|
745
|
-
"
|
746
|
-
"
|
747
|
-
"
|
748
|
-
"
|
749
|
-
"
|
750
|
-
"
|
751
|
-
"GroupDelRobotEvent",
|
752
|
-
"GroupMsgRejectEvent",
|
753
|
-
"GroupMsgReceiveEvent",
|
747
|
+
"OpenForumThreadCreateEvent",
|
748
|
+
"OpenForumThreadDeleteEvent",
|
749
|
+
"OpenForumThreadUpdateEvent",
|
750
|
+
"PublicMessageDeleteEvent",
|
751
|
+
"QQMessageEvent",
|
752
|
+
"ReadyEvent",
|
753
|
+
"ResumedEvent",
|
754
754
|
]
|
nonebot/adapters/qq/message.py
CHANGED
@@ -532,9 +532,9 @@ class Message(BaseMessage[MessageSegment]):
|
|
532
532
|
)
|
533
533
|
return msg
|
534
534
|
|
535
|
-
def extract_content(self) -> str:
|
535
|
+
def extract_content(self, escape_text: bool = True) -> str:
|
536
536
|
return "".join(
|
537
|
-
str(seg)
|
537
|
+
seg.data["text"] if not escape_text and seg.type == "text" else str(seg)
|
538
538
|
for seg in self
|
539
539
|
if seg.type
|
540
540
|
in ("text", "emoji", "mention_user", "mention_everyone", "mention_channel")
|
@@ -11,4 +11,5 @@ from .payload import Heartbeat as Heartbeat
|
|
11
11
|
from .payload import Reconnect as Reconnect
|
12
12
|
from .payload import PayloadType as PayloadType
|
13
13
|
from .payload import HeartbeatAck as HeartbeatAck
|
14
|
+
from .payload import WebhookVerify as WebhookVerify
|
14
15
|
from .payload import InvalidSession as InvalidSession
|
@@ -165,26 +165,26 @@ class ButtonInteraction(BaseModel):
|
|
165
165
|
|
166
166
|
|
167
167
|
__all__ = [
|
168
|
+
"Action",
|
169
|
+
"Button",
|
170
|
+
"ButtonInteraction",
|
171
|
+
"ButtonInteractionContent",
|
172
|
+
"ButtonInteractionData",
|
173
|
+
"InlineKeyboard",
|
174
|
+
"InlineKeyboardRow",
|
175
|
+
"MessageArk",
|
176
|
+
"MessageArkKv",
|
177
|
+
"MessageArkObj",
|
178
|
+
"MessageArkObjKv",
|
168
179
|
"MessageAttachment",
|
169
|
-
"
|
170
|
-
"MessageEmbedField",
|
180
|
+
"MessageAudited",
|
171
181
|
"MessageEmbed",
|
172
|
-
"
|
173
|
-
"
|
174
|
-
"
|
175
|
-
"MessageArk",
|
176
|
-
"MessageReference",
|
177
|
-
"MessageMarkdownParams",
|
182
|
+
"MessageEmbedField",
|
183
|
+
"MessageEmbedThumbnail",
|
184
|
+
"MessageKeyboard",
|
178
185
|
"MessageMarkdown",
|
186
|
+
"MessageMarkdownParams",
|
187
|
+
"MessageReference",
|
179
188
|
"Permission",
|
180
|
-
"Action",
|
181
189
|
"RenderData",
|
182
|
-
"Button",
|
183
|
-
"InlineKeyboardRow",
|
184
|
-
"InlineKeyboard",
|
185
|
-
"MessageKeyboard",
|
186
|
-
"MessageAudited",
|
187
|
-
"ButtonInteractionContent",
|
188
|
-
"ButtonInteractionData",
|
189
|
-
"ButtonInteraction",
|
190
190
|
]
|
@@ -498,67 +498,67 @@ class ShardUrlGetReturn(BaseModel):
|
|
498
498
|
|
499
499
|
|
500
500
|
__all__ = [
|
501
|
-
"
|
502
|
-
"
|
503
|
-
"
|
504
|
-
"
|
505
|
-
"
|
506
|
-
"
|
501
|
+
"DMS",
|
502
|
+
"APIPermission",
|
503
|
+
"APIPermissionDemand",
|
504
|
+
"APIPermissionDemandIdentify",
|
505
|
+
"Alignment",
|
506
|
+
"Announces",
|
507
|
+
"AudioAction",
|
508
|
+
"AudioControl",
|
509
|
+
"AudioStatus",
|
507
510
|
"Channel",
|
508
|
-
"Member",
|
509
|
-
"GetRoleMembersReturn",
|
510
|
-
"Role",
|
511
|
-
"GetGuildRolesReturn",
|
512
|
-
"PostGuildRoleReturn",
|
513
|
-
"PatchGuildRoleReturn",
|
514
511
|
"ChannelPermissions",
|
512
|
+
"ChannelSubType",
|
513
|
+
"ChannelType",
|
514
|
+
"Elem",
|
515
|
+
"ElemType",
|
516
|
+
"Emoji",
|
517
|
+
"EmojiType",
|
518
|
+
"ForumAuditResult",
|
519
|
+
"ForumAuditType",
|
520
|
+
"ForumSourceInfo",
|
521
|
+
"GetGuildAPIPermissionReturn",
|
522
|
+
"GetGuildRolesReturn",
|
523
|
+
"GetReactionUsersReturn",
|
524
|
+
"GetRoleMembersReturn",
|
525
|
+
"GetThreadReturn",
|
526
|
+
"GetThreadsListReturn",
|
527
|
+
"Guild",
|
528
|
+
"ImageElem",
|
529
|
+
"Member",
|
515
530
|
"Message",
|
516
531
|
"MessageDelete",
|
532
|
+
"MessageReaction",
|
517
533
|
"MessageSetting",
|
518
|
-
"
|
519
|
-
"
|
520
|
-
"
|
534
|
+
"Paragraph",
|
535
|
+
"ParagraphProps",
|
536
|
+
"PatchGuildRoleReturn",
|
521
537
|
"PinsMessage",
|
538
|
+
"Post",
|
539
|
+
"PostGuildRoleReturn",
|
540
|
+
"PostInfo",
|
541
|
+
"PrivateType",
|
542
|
+
"PutThreadReturn",
|
543
|
+
"ReactionTarget",
|
544
|
+
"ReactionTargetType",
|
545
|
+
"RecommendChannel",
|
522
546
|
"RemindType",
|
547
|
+
"Reply",
|
548
|
+
"ReplyInfo",
|
549
|
+
"RichText",
|
550
|
+
"Role",
|
523
551
|
"Schedule",
|
524
|
-
"
|
525
|
-
"
|
526
|
-
"
|
527
|
-
"ReactionTarget",
|
528
|
-
"MessageReaction",
|
529
|
-
"GetReactionUsersReturn",
|
530
|
-
"AudioStatus",
|
531
|
-
"AudioControl",
|
532
|
-
"AudioAction",
|
533
|
-
"ElemType",
|
534
|
-
"TextProps",
|
552
|
+
"SessionStartLimit",
|
553
|
+
"ShardUrlGetReturn",
|
554
|
+
"SpeakPermission",
|
535
555
|
"TextElem",
|
536
|
-
"
|
537
|
-
"VideoElem",
|
538
|
-
"URLElem",
|
539
|
-
"Elem",
|
540
|
-
"Alignment",
|
541
|
-
"ParagraphProps",
|
542
|
-
"Paragraph",
|
543
|
-
"RichText",
|
544
|
-
"ThreadObjectInfo",
|
545
|
-
"ThreadInfo",
|
546
|
-
"ForumSourceInfo",
|
556
|
+
"TextProps",
|
547
557
|
"Thread",
|
548
|
-
"
|
549
|
-
"
|
550
|
-
"
|
551
|
-
"Reply",
|
552
|
-
"ForumAuditType",
|
553
|
-
"ForumAuditResult",
|
554
|
-
"GetThreadsListReturn",
|
555
|
-
"GetThreadReturn",
|
556
|
-
"PutThreadReturn",
|
557
|
-
"APIPermission",
|
558
|
-
"APIPermissionDemandIdentify",
|
559
|
-
"APIPermissionDemand",
|
560
|
-
"GetGuildAPIPermissionReturn",
|
558
|
+
"ThreadInfo",
|
559
|
+
"ThreadObjectInfo",
|
560
|
+
"URLElem",
|
561
561
|
"UrlGetReturn",
|
562
|
-
"
|
563
|
-
"
|
562
|
+
"User",
|
563
|
+
"VideoElem",
|
564
564
|
]
|
@@ -18,6 +18,7 @@ class Opcode(IntEnum):
|
|
18
18
|
HELLO = 10
|
19
19
|
HEARTBEAT_ACK = 11
|
20
20
|
HTTP_CALLBACK_ACK = 12
|
21
|
+
WEBHOOK_VERIFY = 13
|
21
22
|
|
22
23
|
|
23
24
|
class Payload(BaseModel):
|
@@ -41,7 +42,7 @@ class Payload(BaseModel):
|
|
41
42
|
class Dispatch(Payload):
|
42
43
|
opcode: Literal[Opcode.DISPATCH] = Field(Opcode.DISPATCH)
|
43
44
|
data: dict
|
44
|
-
sequence: int
|
45
|
+
sequence: Optional[int] = None
|
45
46
|
type: str
|
46
47
|
id: Optional[str] = None
|
47
48
|
|
@@ -121,9 +122,26 @@ class HTTPCallbackAck(Payload):
|
|
121
122
|
data: int
|
122
123
|
|
123
124
|
|
125
|
+
class WebhookVerifyData(BaseModel):
|
126
|
+
plain_token: str
|
127
|
+
event_ts: str
|
128
|
+
|
129
|
+
if PYDANTIC_V2:
|
130
|
+
model_config: ConfigDict = ConfigDict(extra="allow")
|
131
|
+
else:
|
132
|
+
|
133
|
+
class Config:
|
134
|
+
extra = "allow"
|
135
|
+
|
136
|
+
|
137
|
+
class WebhookVerify(Payload):
|
138
|
+
opcode: Literal[Opcode.WEBHOOK_VERIFY] = Field(Opcode.WEBHOOK_VERIFY)
|
139
|
+
data: WebhookVerifyData
|
140
|
+
|
141
|
+
|
124
142
|
PayloadType = Union[
|
125
143
|
Annotated[
|
126
|
-
Union[Dispatch, Reconnect, InvalidSession, Hello, HeartbeatAck],
|
144
|
+
Union[Dispatch, Reconnect, InvalidSession, Hello, HeartbeatAck, WebhookVerify],
|
127
145
|
Field(discriminator="opcode"),
|
128
146
|
],
|
129
147
|
Payload,
|
nonebot/adapters/qq/models/qq.py
CHANGED
@@ -10,11 +10,13 @@ from nonebot.adapters.qq.compat import field_validator
|
|
10
10
|
class FriendAuthor(BaseModel):
|
11
11
|
id: str
|
12
12
|
user_openid: str
|
13
|
+
union_openid: Optional[str] = None
|
13
14
|
|
14
15
|
|
15
16
|
class GroupMemberAuthor(BaseModel):
|
16
17
|
id: str
|
17
18
|
member_openid: str
|
19
|
+
union_openid: Optional[str] = None
|
18
20
|
|
19
21
|
|
20
22
|
class Attachment(BaseModel):
|
@@ -76,15 +78,15 @@ class PostGroupMembersReturn(BaseModel):
|
|
76
78
|
|
77
79
|
|
78
80
|
__all__ = [
|
81
|
+
"Attachment",
|
79
82
|
"FriendAuthor",
|
83
|
+
"GroupMember",
|
80
84
|
"GroupMemberAuthor",
|
81
|
-
"Attachment",
|
82
85
|
"Media",
|
83
|
-
"QQMessage",
|
84
|
-
"PostC2CMessagesReturn",
|
85
|
-
"PostGroupMessagesReturn",
|
86
86
|
"PostC2CFilesReturn",
|
87
|
-
"
|
88
|
-
"PostGroupMembersReturn",
|
87
|
+
"PostC2CMessagesReturn",
|
89
88
|
"PostGroupFilesReturn",
|
89
|
+
"PostGroupMembersReturn",
|
90
|
+
"PostGroupMessagesReturn",
|
91
|
+
"QQMessage",
|
90
92
|
]
|
@@ -6,7 +6,7 @@ from .event import MessageCreateEvent, AtMessageCreateEvent
|
|
6
6
|
|
7
7
|
|
8
8
|
async def _guild_channel_admin(
|
9
|
-
event: Union[AtMessageCreateEvent, MessageCreateEvent]
|
9
|
+
event: Union[AtMessageCreateEvent, MessageCreateEvent],
|
10
10
|
) -> bool:
|
11
11
|
return "5" in getattr(event.member, "roles", ())
|
12
12
|
|
@@ -27,7 +27,7 @@ GUILD_OWNER: Permission = Permission(_guild_owner)
|
|
27
27
|
"""匹配任意频道群主群消息类型事件"""
|
28
28
|
|
29
29
|
__all__ = [
|
30
|
-
"GUILD_CHANNEL_ADMIN",
|
31
30
|
"GUILD_ADMIN",
|
31
|
+
"GUILD_CHANNEL_ADMIN",
|
32
32
|
"GUILD_OWNER",
|
33
33
|
]
|
@@ -1,6 +1,6 @@
|
|
1
|
-
Metadata-Version: 2.
|
1
|
+
Metadata-Version: 2.3
|
2
2
|
Name: nonebot-adapter-qq
|
3
|
-
Version: 1.
|
3
|
+
Version: 1.6.1
|
4
4
|
Summary: QQ adapter for nonebot2
|
5
5
|
Home-page: https://github.com/nonebot/adapter-qq
|
6
6
|
License: MIT
|
@@ -19,6 +19,7 @@ Classifier: Programming Language :: Python :: 3.10
|
|
19
19
|
Classifier: Programming Language :: Python :: 3.11
|
20
20
|
Classifier: Programming Language :: Python :: 3.12
|
21
21
|
Classifier: Programming Language :: Python :: 3.13
|
22
|
+
Requires-Dist: cryptography (>=43.0.3,<45.0.0)
|
22
23
|
Requires-Dist: nonebot2 (>=2.2.1,<3.0.0)
|
23
24
|
Requires-Dist: pydantic (>=1.10.0,<3.0.0,!=2.5.0,!=2.5.1)
|
24
25
|
Requires-Dist: typing-extensions (>=4.4.0,<5.0.0)
|
@@ -45,15 +46,19 @@ _✨ QQ 协议适配 ✨_
|
|
45
46
|
|
46
47
|
### Driver
|
47
48
|
|
48
|
-
|
49
|
-
|
50
|
-
如:
|
49
|
+
如果使用 WebSocket 连接方式,请参考 [driver](https://nonebot.dev/docs/appendices/config#driver) 配置项,添加 `HTTPClient` 和 `WebSocketClient` 支持。如:
|
51
50
|
|
52
51
|
```dotenv
|
53
52
|
DRIVER=~httpx+~websockets
|
54
53
|
DRIVER=~aiohttp
|
55
54
|
```
|
56
55
|
|
56
|
+
如果使用 WebHook 连接方式,则添加 `ASGIServer` 支持。如:
|
57
|
+
|
58
|
+
```dotenv
|
59
|
+
DRIVER=~fastapi
|
60
|
+
```
|
61
|
+
|
57
62
|
### QQ_IS_SANDBOX
|
58
63
|
|
59
64
|
是否为沙盒模式,默认为 `False`。
|
@@ -66,9 +71,13 @@ QQ_IS_SANDBOX=true
|
|
66
71
|
|
67
72
|
配置机器人帐号 `id` `token` `secret`,intent 需要根据机器人类型以及需要的事件进行配置。
|
68
73
|
|
74
|
+
#### Webhook / WebSocket
|
75
|
+
|
76
|
+
通过配置项 `use_websocket` 来选择是否启用 WebSocket 连接,当前默认为 `True`。如果关闭 WebSocket 连接方式,则可以通过 WebHook 方式来连接机器人,请在 QQ 开放平台中配置机器人回调地址:`https://host:port/qq/webhook`。
|
77
|
+
|
69
78
|
#### Intent
|
70
79
|
|
71
|
-
|
80
|
+
Intent 仅对 WebSocket 连接方式生效。以下为所有 Intent 配置项以及默认值:
|
72
81
|
|
73
82
|
```json
|
74
83
|
{
|
@@ -102,7 +111,8 @@ QQ_BOTS='
|
|
102
111
|
"intent": {
|
103
112
|
"guild_messages": true,
|
104
113
|
"at_messages": false
|
105
|
-
}
|
114
|
+
},
|
115
|
+
"use_websocket": false
|
106
116
|
}
|
107
117
|
]
|
108
118
|
'
|
@@ -119,7 +129,8 @@ QQ_BOTS='
|
|
119
129
|
"secret": "xxx",
|
120
130
|
"intent": {
|
121
131
|
"c2c_group_at_messages": true
|
122
|
-
}
|
132
|
+
},
|
133
|
+
"use_websocket": false
|
123
134
|
}
|
124
135
|
]
|
125
136
|
'
|
@@ -0,0 +1,20 @@
|
|
1
|
+
nonebot/adapters/qq/__init__.py,sha256=jm1QxHfhcYIrWhkXxF9gE4G3lkagrLCWsmVqKBTQSOw,815
|
2
|
+
nonebot/adapters/qq/adapter.py,sha256=UwAjO168oWmvb3frjay4CTC54xq3hsCiVbeFxRwaGxI,21243
|
3
|
+
nonebot/adapters/qq/bot.py,sha256=eYW4j4Vc0IBGmpZATal-zpq1BVjWrwL-Ff9Fy2tTWIM,61383
|
4
|
+
nonebot/adapters/qq/compat.py,sha256=Fr96IgBHHiEqGm8DVmlTekuaNdI5EPxti7J7HDwOHwg,767
|
5
|
+
nonebot/adapters/qq/config.py,sha256=YRVpGNvslGcNHNbbFN9LOjZ98-yon5x9GMF98FVJ2xo,1974
|
6
|
+
nonebot/adapters/qq/event.py,sha256=3A78iz-G6wC-KsW5gYQM16veqgCtjWWsb8AysXhDOK4,18802
|
7
|
+
nonebot/adapters/qq/exception.py,sha256=8Xa9NwYyO7Ih0owBAC_zmEiIRAA8GjgQcoJcF5ogo0g,2701
|
8
|
+
nonebot/adapters/qq/message.py,sha256=74H7L3FTpZu5GAm4j3wnVYf7wJQ_kP9HHjqaXn3uRTQ,15616
|
9
|
+
nonebot/adapters/qq/models/__init__.py,sha256=q2OhopcTn0UD2wuep_86dXMVhF_wQBzBDhyGLN1q0OE,631
|
10
|
+
nonebot/adapters/qq/models/common.py,sha256=Hoot6Qr3PGj13G4Cz2Eyr-liaLiJXBpxpeM59IXBri0,4539
|
11
|
+
nonebot/adapters/qq/models/guild.py,sha256=uKvSijWFMJ0x26Gd04b8QbUr8IOIn3U3m1d-I6xTrcI,11686
|
12
|
+
nonebot/adapters/qq/models/payload.py,sha256=IhAx2W5rN8ihg2NchQ9rJMfcxDecwnnJ-5_N-ky8deM,3261
|
13
|
+
nonebot/adapters/qq/models/qq.py,sha256=JH-1tC7ujwHpuJtPefGOJh2NQjrxAL7L-aRyu7xqplU,1931
|
14
|
+
nonebot/adapters/qq/permission.py,sha256=Jw00btklCMviP001MBlLQmqThb9OsjJwHO1nTEDFRWM,989
|
15
|
+
nonebot/adapters/qq/store.py,sha256=GKVbto6K7jI-oK9tSbw_xLxDiSA6Y9QnNQStKJU7mEY,868
|
16
|
+
nonebot/adapters/qq/utils.py,sha256=bzJwm1zWyHjFILr-bHrBKpKlkiveph9S4pLAXMcW9AY,1538
|
17
|
+
nonebot_adapter_qq-1.6.1.dist-info/LICENSE,sha256=4EZBnkZPQYBvtO2KFGaUQHTO8nZ26pGeaCFchdBq_AE,1064
|
18
|
+
nonebot_adapter_qq-1.6.1.dist-info/METADATA,sha256=nAY5TQGs53DbKIJTI9dO-A0k4bh6hfyQ8UJjfaEi7ss,3383
|
19
|
+
nonebot_adapter_qq-1.6.1.dist-info/WHEEL,sha256=RaoafKOydTQ7I_I3JTrPCg6kUmTgtm4BornzOqyEfJ8,88
|
20
|
+
nonebot_adapter_qq-1.6.1.dist-info/RECORD,,
|
@@ -1,20 +0,0 @@
|
|
1
|
-
nonebot/adapters/qq/__init__.py,sha256=jm1QxHfhcYIrWhkXxF9gE4G3lkagrLCWsmVqKBTQSOw,815
|
2
|
-
nonebot/adapters/qq/adapter.py,sha256=mK_lTLADDyV-rXDlsgb4noYeJNnxlHxf-iWZBvxU9uY,14935
|
3
|
-
nonebot/adapters/qq/bot.py,sha256=AA2eGpTZxHRTj3Z7mzCa3K77QdKSL6O-wTcEJ4F7bmE,61394
|
4
|
-
nonebot/adapters/qq/compat.py,sha256=7g5QvRzWREesZ7MSEhNRh53UZwEyhQlxE3e_KpqQ0pw,768
|
5
|
-
nonebot/adapters/qq/config.py,sha256=vvTZ4JRJEQ60PyLh92IEnQkOltdUs41lGKZuYXxq6yA,2135
|
6
|
-
nonebot/adapters/qq/event.py,sha256=9URqXn-U0K2JwYR1TJkNjejWB1C_-LnflLZPsTNn32I,18802
|
7
|
-
nonebot/adapters/qq/exception.py,sha256=8Xa9NwYyO7Ih0owBAC_zmEiIRAA8GjgQcoJcF5ogo0g,2701
|
8
|
-
nonebot/adapters/qq/message.py,sha256=q4dhlpuQ-Fyiqj2_cL1TCaZutdF41fcyGXpiI6RucVg,15526
|
9
|
-
nonebot/adapters/qq/models/__init__.py,sha256=VsLh2rceTl9fK4hReYXBosOPZj6E1x7lDUdk7py_R8A,579
|
10
|
-
nonebot/adapters/qq/models/common.py,sha256=oQDNaPUNUO64-_bgQdHobQyZLdtdDNnkykzGjbRdAUM,4539
|
11
|
-
nonebot/adapters/qq/models/guild.py,sha256=8xyFLwXXzIk-LxqzSlkfkuJfPgxxIkLYWsrPRcX4lsM,11686
|
12
|
-
nonebot/adapters/qq/models/payload.py,sha256=_Q-4yHlSlzb5Lm2MLv8QxQ_ADVbjWJt3SCWN3_niH-Q,2851
|
13
|
-
nonebot/adapters/qq/models/qq.py,sha256=9gPQZBuuZEOvAsk7a6-w0Gh0Scf4qQL9y08pubdfk1o,1853
|
14
|
-
nonebot/adapters/qq/permission.py,sha256=0aedypl6xymhAV3UkW0vlvHTBkJeDmOef3SxG2tPcHQ,988
|
15
|
-
nonebot/adapters/qq/store.py,sha256=GKVbto6K7jI-oK9tSbw_xLxDiSA6Y9QnNQStKJU7mEY,868
|
16
|
-
nonebot/adapters/qq/utils.py,sha256=bzJwm1zWyHjFILr-bHrBKpKlkiveph9S4pLAXMcW9AY,1538
|
17
|
-
nonebot_adapter_qq-1.5.3.dist-info/LICENSE,sha256=4EZBnkZPQYBvtO2KFGaUQHTO8nZ26pGeaCFchdBq_AE,1064
|
18
|
-
nonebot_adapter_qq-1.5.3.dist-info/METADATA,sha256=VriHr0yg5zp4oD6wVkuN0oRgs5XhY3OJry1uNxRlIts,2786
|
19
|
-
nonebot_adapter_qq-1.5.3.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
|
20
|
-
nonebot_adapter_qq-1.5.3.dist-info/RECORD,,
|
File without changes
|