satori-python-adapter-qq 0.2.0__tar.gz → 0.3.0__tar.gz

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.
Files changed (19) hide show
  1. {satori_python_adapter_qq-0.2.0 → satori_python_adapter_qq-0.3.0}/.mina/adapter_qq.toml +3 -3
  2. {satori_python_adapter_qq-0.2.0 → satori_python_adapter_qq-0.3.0}/PKG-INFO +5 -5
  3. {satori_python_adapter_qq-0.2.0 → satori_python_adapter_qq-0.3.0}/README.md +2 -2
  4. {satori_python_adapter_qq-0.2.0 → satori_python_adapter_qq-0.3.0}/pyproject.toml +3 -3
  5. {satori_python_adapter_qq-0.2.0 → satori_python_adapter_qq-0.3.0}/src/satori/adapters/qq/api.py +8 -2
  6. {satori_python_adapter_qq-0.2.0 → satori_python_adapter_qq-0.3.0}/src/satori/adapters/qq/events/group.py +5 -1
  7. {satori_python_adapter_qq-0.2.0 → satori_python_adapter_qq-0.3.0}/src/satori/adapters/qq/events/guild.py +2 -2
  8. {satori_python_adapter_qq-0.2.0 → satori_python_adapter_qq-0.3.0}/src/satori/adapters/qq/events/message.py +15 -5
  9. {satori_python_adapter_qq-0.2.0 → satori_python_adapter_qq-0.3.0}/src/satori/adapters/qq/main.py +3 -0
  10. {satori_python_adapter_qq-0.2.0 → satori_python_adapter_qq-0.3.0}/src/satori/adapters/qq/message.py +38 -6
  11. {satori_python_adapter_qq-0.2.0 → satori_python_adapter_qq-0.3.0}/src/satori/adapters/qq/utils.py +7 -6
  12. {satori_python_adapter_qq-0.2.0 → satori_python_adapter_qq-0.3.0}/src/satori/adapters/qq/websocket.py +20 -6
  13. {satori_python_adapter_qq-0.2.0 → satori_python_adapter_qq-0.3.0}/LICENSE +0 -0
  14. {satori_python_adapter_qq-0.2.0 → satori_python_adapter_qq-0.3.0}/src/satori/adapters/qq/__init__.py +0 -0
  15. {satori_python_adapter_qq-0.2.0 → satori_python_adapter_qq-0.3.0}/src/satori/adapters/qq/audit_store.py +0 -0
  16. {satori_python_adapter_qq-0.2.0 → satori_python_adapter_qq-0.3.0}/src/satori/adapters/qq/events/__init__.py +0 -0
  17. {satori_python_adapter_qq-0.2.0 → satori_python_adapter_qq-0.3.0}/src/satori/adapters/qq/events/base.py +0 -0
  18. {satori_python_adapter_qq-0.2.0 → satori_python_adapter_qq-0.3.0}/src/satori/adapters/qq/events/interaction.py +0 -0
  19. {satori_python_adapter_qq-0.2.0 → satori_python_adapter_qq-0.3.0}/src/satori/adapters/qq/exception.py +0 -0
@@ -6,7 +6,7 @@ raw-dependencies = [
6
6
 
7
7
  [project]
8
8
  name = "satori-python-adapter-qq"
9
- version = "0.2.0"
9
+ version = "0.3.0"
10
10
  authors = [
11
11
  {name = "RF-Tar-Railt", email = "rf_tar_railt@qq.com"}
12
12
  ]
@@ -19,11 +19,11 @@ classifiers = [
19
19
  "Typing :: Typed",
20
20
  "Development Status :: 4 - Beta",
21
21
  "License :: OSI Approved :: MIT License",
22
- "Programming Language :: Python :: 3.8",
23
- "Programming Language :: Python :: 3.9",
24
22
  "Programming Language :: Python :: 3.10",
25
23
  "Programming Language :: Python :: 3.11",
26
24
  "Programming Language :: Python :: 3.12",
25
+ "Programming Language :: Python :: 3.13",
26
+ "Programming Language :: Python :: 3.14",
27
27
  "Operating System :: OS Independent",
28
28
  ]
29
29
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: satori-python-adapter-qq
3
- Version: 0.2.0
3
+ Version: 0.3.0
4
4
  Summary: Satori Protocol SDK for python, adapter for QQ
5
5
  Home-page: https://github.com/RF-Tar-Railt/satori-python
6
6
  Author-Email: RF-Tar-Railt <rf_tar_railt@qq.com>
@@ -8,11 +8,11 @@ License: MIT
8
8
  Classifier: Typing :: Typed
9
9
  Classifier: Development Status :: 4 - Beta
10
10
  Classifier: License :: OSI Approved :: MIT License
11
- Classifier: Programming Language :: Python :: 3.8
12
- Classifier: Programming Language :: Python :: 3.9
13
11
  Classifier: Programming Language :: Python :: 3.10
14
12
  Classifier: Programming Language :: Python :: 3.11
15
13
  Classifier: Programming Language :: Python :: 3.12
14
+ Classifier: Programming Language :: Python :: 3.13
15
+ Classifier: Programming Language :: Python :: 3.14
16
16
  Classifier: Operating System :: OS Independent
17
17
  Project-URL: Homepage, https://github.com/RF-Tar-Railt/satori-python
18
18
  Project-URL: Repository, https://github.com/RF-Tar-Railt/satori-python
@@ -28,11 +28,11 @@ Description-Content-Type: text/markdown
28
28
  [![PyPI](https://img.shields.io/pypi/v/satori-python)](https://pypi.org/project/satori-python)
29
29
  [![PyPI - Python Version](https://img.shields.io/pypi/pyversions/satori-python)](https://www.python.org/)
30
30
 
31
- 基于 [Satori](https://satori.chat/zh-CN/) 协议的 Python 开发工具包
31
+ 基于 [Satori](https://satori.chat/zh-CN/protocol) 协议的 Python 开发工具包
32
32
 
33
33
  ## 协议介绍
34
34
 
35
- [Satori Protocol](https://satori.chat/zh-CN/)
35
+ [Satori Protocol](https://satori.chat/zh-CN/protocol)
36
36
 
37
37
  ### 协议端
38
38
 
@@ -5,11 +5,11 @@
5
5
  [![PyPI](https://img.shields.io/pypi/v/satori-python)](https://pypi.org/project/satori-python)
6
6
  [![PyPI - Python Version](https://img.shields.io/pypi/pyversions/satori-python)](https://www.python.org/)
7
7
 
8
- 基于 [Satori](https://satori.chat/zh-CN/) 协议的 Python 开发工具包
8
+ 基于 [Satori](https://satori.chat/zh-CN/protocol) 协议的 Python 开发工具包
9
9
 
10
10
  ## 协议介绍
11
11
 
12
- [Satori Protocol](https://satori.chat/zh-CN/)
12
+ [Satori Protocol](https://satori.chat/zh-CN/protocol)
13
13
 
14
14
  ### 协议端
15
15
 
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "satori-python-adapter-qq"
3
- version = "0.2.0"
3
+ version = "0.3.0"
4
4
  authors = [
5
5
  { name = "RF-Tar-Railt", email = "rf_tar_railt@qq.com" },
6
6
  ]
@@ -15,11 +15,11 @@ classifiers = [
15
15
  "Typing :: Typed",
16
16
  "Development Status :: 4 - Beta",
17
17
  "License :: OSI Approved :: MIT License",
18
- "Programming Language :: Python :: 3.8",
19
- "Programming Language :: Python :: 3.9",
20
18
  "Programming Language :: Python :: 3.10",
21
19
  "Programming Language :: Python :: 3.11",
22
20
  "Programming Language :: Python :: 3.12",
21
+ "Programming Language :: Python :: 3.13",
22
+ "Programming Language :: Python :: 3.14",
23
23
  "Operating System :: OS Independent",
24
24
  ]
25
25
 
@@ -29,7 +29,7 @@ from satori.server.route import (
29
29
  )
30
30
 
31
31
  from .message import QQGroupMessageEncoder, QQGuildMessageEncoder, decode_segments
32
- from .utils import QQBotNetwork, decode_channel, decode_guild, decode_member, decode_user
32
+ from .utils import ROLE_MAPPING, QQBotNetwork, decode_channel, decode_guild, decode_member, decode_user
33
33
 
34
34
 
35
35
  def apply(
@@ -356,7 +356,13 @@ def apply(
356
356
  "get",
357
357
  f"guilds/{guild_id}/roles",
358
358
  )
359
- roles = [Role(role["id"], role.get("name", "")) for role in res]
359
+ # roles = [Role(role["id"], role.get("name", "")) for role in res]
360
+ roles = []
361
+ for role in res:
362
+ if role["id"] in ROLE_MAPPING:
363
+ roles.append(ROLE_MAPPING[role["id"]])
364
+ else:
365
+ roles.append(Role(role["id"], role.get("name", "")))
360
366
  return PageResult(roles, next=None)
361
367
  raise NotFoundException("qq platform does not support guild.role.list")
362
368
 
@@ -3,7 +3,7 @@ from __future__ import annotations
3
3
  from datetime import datetime
4
4
 
5
5
  from satori import EventType
6
- from satori.model import Event, Guild, User
6
+ from satori.model import Channel, ChannelType, Event, Guild, User
7
7
 
8
8
  from ..utils import Payload
9
9
  from .base import register_event
@@ -27,6 +27,8 @@ async def friend_event(login, guild_login, net, payload: Payload):
27
27
  ),
28
28
  login,
29
29
  user=user,
30
+ channel=Channel(f"private:{user.id}", type=ChannelType.DIRECT),
31
+ referrer={"event_id": payload.id},
30
32
  )
31
33
 
32
34
 
@@ -52,5 +54,7 @@ async def group_event(login, guild_login, net, payload: Payload):
52
54
  ),
53
55
  login,
54
56
  guild=guild,
57
+ channel=Channel(guild.id, type=ChannelType.TEXT),
55
58
  operator=operator,
59
+ referrer={"event_id": payload.id},
56
60
  )
@@ -5,7 +5,7 @@ from datetime import datetime
5
5
  from satori import EventType
6
6
  from satori.model import Event, Guild, Member, Role, User
7
7
 
8
- from ..utils import Payload, decode_channel, decode_guild, decode_user
8
+ from ..utils import ROLE_MAPPING, Payload, decode_channel, decode_guild, decode_user
9
9
  from .base import register_event
10
10
 
11
11
 
@@ -57,7 +57,7 @@ async def guild_member_event(login, guild_login, net, payload: Payload):
57
57
  nick=raw["nick"],
58
58
  avatar=user.avatar,
59
59
  joined_at=datetime.fromisoformat(raw["joined_at"]),
60
- roles=[Role(r) for r in raw["roles"]],
60
+ roles=[(ROLE_MAPPING.get(r) or Role(r)) for r in raw["roles"]],
61
61
  )
62
62
  operator = User(raw["op_user_id"])
63
63
  return Event(t, datetime.now(), guild_login, guild=guild, user=user, member=member, operator=operator)
@@ -6,7 +6,7 @@ from satori import At, EventType, Text
6
6
  from satori.model import Channel, ChannelType, EmojiObject, Event, Guild, Member, MessageObject, Role, User
7
7
 
8
8
  from ..message import decode_segments
9
- from ..utils import USER_AVATAR_URL, Payload, decode_user
9
+ from ..utils import ROLE_MAPPING, USER_AVATAR_URL, Payload, decode_user
10
10
  from .base import register_event
11
11
 
12
12
 
@@ -21,7 +21,7 @@ async def at_message(login, guild_login, net, payload: Payload):
21
21
  user,
22
22
  avatar=user.avatar,
23
23
  joined_at=datetime.fromisoformat(raw["member"]["joined_at"]),
24
- roles=[Role(r) for r in raw["member"]["roles"]],
24
+ roles=[(ROLE_MAPPING.get(r) or Role(r)) for r in raw["member"]["roles"]],
25
25
  )
26
26
  msg = decode_segments(raw)
27
27
  if len(msg) >= 2 and isinstance(msg[0], At) and isinstance(msg[1], Text):
@@ -90,13 +90,17 @@ async def group_at_message_create(login, guild_login, net, payload: Payload):
90
90
  else:
91
91
  channel = Channel(raw["group_id"], ChannelType.TEXT)
92
92
  app_id = net.bot_id_mapping[login.id]
93
+ name = raw["author"].get("username")
93
94
  if "member_openid" in raw["author"]:
94
95
  user = User(
95
96
  raw["author"]["member_openid"],
97
+ name=name,
96
98
  avatar=USER_AVATAR_URL.format(app_id=app_id, user_id=raw["author"]["member_openid"]),
97
99
  )
98
100
  else:
99
- user = User(raw["author"]["id"], avatar=USER_AVATAR_URL.format(app_id=app_id, user_id=raw["author"]["id"]))
101
+ user = User(
102
+ raw["author"]["id"], name=name, avatar=USER_AVATAR_URL.format(app_id=app_id, user_id=raw["author"]["id"])
103
+ )
100
104
  member = Member(user, avatar=user.avatar)
101
105
  msg = decode_segments(raw)
102
106
  msg.insert(0, At(login.id))
@@ -122,6 +126,7 @@ async def group_at_message_create(login, guild_login, net, payload: Payload):
122
126
  referrer={
123
127
  "msg_id": raw["id"],
124
128
  "msg_seq": -1,
129
+ "msg_scene": raw["message_scene"],
125
130
  },
126
131
  )
127
132
 
@@ -130,13 +135,17 @@ async def group_at_message_create(login, guild_login, net, payload: Payload):
130
135
  async def c2c_message_create(login, guild_login, net, payload: Payload):
131
136
  raw = payload.data
132
137
  app_id = net.bot_id_mapping[login.id]
138
+ name = raw["author"].get("username")
133
139
  if "user_openid" in raw["author"]:
134
140
  user = User(
135
141
  raw["author"]["user_openid"],
142
+ name,
136
143
  avatar=USER_AVATAR_URL.format(app_id=app_id, user_id=raw["author"]["user_openid"]),
137
144
  )
138
145
  else:
139
- user = User(raw["author"]["id"], avatar=USER_AVATAR_URL.format(app_id=app_id, user_id=raw["author"]["id"]))
146
+ user = User(
147
+ raw["author"]["id"], name, avatar=USER_AVATAR_URL.format(app_id=app_id, user_id=raw["author"]["id"])
148
+ )
140
149
  channel = Channel(f"private:{user.id}", ChannelType.DIRECT)
141
150
  return Event(
142
151
  EventType.MESSAGE_CREATED,
@@ -153,6 +162,7 @@ async def c2c_message_create(login, guild_login, net, payload: Payload):
153
162
  "direct": True,
154
163
  "msg_id": raw["id"],
155
164
  "msg_seq": -1,
165
+ "msg_scene": raw["message_scene"],
156
166
  },
157
167
  )
158
168
 
@@ -169,7 +179,7 @@ async def message_delete(login, guild_login, new, payload: Payload):
169
179
  user,
170
180
  avatar=user.avatar,
171
181
  joined_at=datetime.fromisoformat(raw["message"]["member"]["joined_at"]),
172
- roles=[Role(r) for r in raw["message"]["member"]["roles"]],
182
+ roles=[(ROLE_MAPPING.get(r) or Role(r)) for r in raw["message"]["member"]["roles"]],
173
183
  )
174
184
  return Event(
175
185
  EventType.MESSAGE_DELETED,
@@ -438,5 +438,8 @@ class QQBotWebhookAdapter(BaseAdapter):
438
438
  await self.handle_event(app_id, payload)
439
439
  return Response(status_code=200)
440
440
 
441
+ def __str__(self):
442
+ return self.id
443
+
441
444
 
442
445
  __all__ = ["QQBotWebhookAdapter"]
@@ -9,7 +9,7 @@ from pathlib import Path
9
9
 
10
10
  from loguru import logger
11
11
 
12
- from satori.element import Button, Custom, E, Element, select, transform
12
+ from satori.element import Button, Custom, E, Element, Raw, select, transform
13
13
  from satori.model import Login, MessageObject
14
14
  from satori.parser import Element as RawElement
15
15
  from satori.parser import parse
@@ -241,9 +241,12 @@ class QQGroupMessageEncoder(QQBotMessageEncoder):
241
241
  def __init__(self, login: Login, net: QQBotNetwork, channel_id: str, referrer: dict | None = None):
242
242
  super().__init__(login, net, channel_id, referrer)
243
243
  self.use_markdown = False
244
+ self.has_ark = False
244
245
  self.content = ""
246
+ self.reference = ""
245
247
  self.attachment: dict | None = None
246
248
  self.rows: list[list[dict]] = []
249
+ self.md_templates: dict | None = None
247
250
  # self.file_url = ""
248
251
  # self.file_data = {} # value, content_type, filename
249
252
 
@@ -251,24 +254,33 @@ class QQGroupMessageEncoder(QQBotMessageEncoder):
251
254
  if not self.content and not self.attachment and not self.rows:
252
255
  return
253
256
  self.strip_buttons()
257
+ event_id = self.referrer.get("event_id") if self.referrer else None
254
258
  msg_id = self.referrer.get("msg_id") if self.referrer else None
255
259
  msg_seq = self.referrer.get("msg_seq") if self.referrer else None
260
+ msg_scene = self.referrer.get("msg_scene", {}) if self.referrer else {}
261
+ refidx = msg_scene.get("ext", ["="])[0].partition("=")[-1]
256
262
  data = {
257
263
  "content": self.content,
264
+ "message_reference": {"message_id": refidx or self.reference} if refidx or self.reference else None,
258
265
  "msg_type": 0,
266
+ "event_id": event_id,
259
267
  "msg_id": msg_id,
260
268
  "msg_seq": (msg_seq + 1) if isinstance(msg_seq, int) else 0,
261
269
  }
262
- if self.attachment:
270
+ if self.has_ark:
271
+ data["msg_type"] = 3
272
+ data["ark"] = json.loads(self.content) if self.content else {}
273
+ elif self.attachment:
263
274
  if not self.content:
264
275
  self.content = " "
265
276
  data["media"] = self.attachment
266
277
  data["msg_type"] = 7
267
- if self.use_markdown:
278
+ elif self.use_markdown:
268
279
  data["msg_type"] = 2
269
280
  del data["content"]
270
281
  data["markdown"] = {
271
- "content": escape_markdown(self.content) or " ",
282
+ "content": self.content or " ", # escape_markdown(self.content) or " ",
283
+ **(self.md_templates or {}),
272
284
  }
273
285
  if self.rows:
274
286
  data["keyboard"] = {"content": {"rows": self.export_buttons()}}
@@ -286,6 +298,7 @@ class QQGroupMessageEncoder(QQBotMessageEncoder):
286
298
  resp = await self.net.call_api("post", endpoint, remove_empty(data))
287
299
  referrer = self.referrer.copy() if self.referrer else {}
288
300
  referrer |= {
301
+ "event_id": event_id,
289
302
  "msg_id": msg_id,
290
303
  "msg_seq": data["msg_seq"],
291
304
  }
@@ -293,8 +306,11 @@ class QQGroupMessageEncoder(QQBotMessageEncoder):
293
306
  except Exception as e:
294
307
  logger.error(f"Failed to send message to {self.channel_id}: {self._raw_content}\nError: {e}")
295
308
  self.content = ""
309
+ self.reference = ""
296
310
  self.attachment = None
311
+ self.has_ark = False
297
312
  self.use_markdown = False
313
+ self.md_templates = None
298
314
  self.rows = []
299
315
 
300
316
  async def send_file(self, type_: str, attrs: dict) -> dict | None:
@@ -340,7 +356,7 @@ class QQGroupMessageEncoder(QQBotMessageEncoder):
340
356
  self.content += attrs["text"]
341
357
  case "img" | "image":
342
358
  if attrs.get("src") or attrs.get("url"):
343
- await self.flush()
359
+ # await self.flush()
344
360
  if data := (await self.send_file(type_, attrs)):
345
361
  self.attachment = data
346
362
  case "video" | "audio" | "file":
@@ -357,6 +373,11 @@ class QQGroupMessageEncoder(QQBotMessageEncoder):
357
373
  await self.render(children)
358
374
  if not self.content.endswith("\n"):
359
375
  self.content += "\n"
376
+ case "qq:ark":
377
+ await self.flush()
378
+ self.has_ark = True
379
+ await self.render(children)
380
+ await self.flush()
360
381
  case "qq:button-group":
361
382
  self.use_markdown = True
362
383
  self.rows.append([])
@@ -369,10 +390,21 @@ class QQGroupMessageEncoder(QQBotMessageEncoder):
369
390
  case "markdown":
370
391
  self.use_markdown = True
371
392
  await self.render(children)
393
+ case "qq:markdown":
394
+ self.use_markdown = True
395
+ if attrs.get("template_id"):
396
+ self.md_templates = {
397
+ "custom_template_id": attrs["template_id"],
398
+ "params": [{"key": k, "value": v} for k, v in attrs.items() if k != "template_id"],
399
+ }
400
+ await self.render(children)
372
401
  case "message":
373
402
  await self.flush()
374
403
  await self.render(children)
375
404
  await self.flush()
405
+ case "quote":
406
+ self.reference = attrs["id"]
407
+ await self.flush()
376
408
  case _:
377
409
  await self.render(children)
378
410
 
@@ -466,5 +498,5 @@ def decode_segments(event: dict) -> list[Element]:
466
498
  for i in embeds:
467
499
  result.append(Custom("qq:embed", i))
468
500
  if ark := event.get("ark"):
469
- result.append(Custom("qq:ark", ark))
501
+ result.append(Custom("qq:ark", children=[Raw(json.dumps(ark, ensure_ascii=False))]))
470
502
  return result
@@ -122,15 +122,16 @@ def decode_member(profile: dict) -> Member:
122
122
  decode_user(profile["user"]) if "user" in profile else None,
123
123
  profile.get("nick"),
124
124
  joined_at=datetime.fromisoformat(profile["joined_at"]) if "joined_at" in profile else None,
125
- roles=[Role(r) for r in profile["roles"]],
125
+ roles=[(ROLE_MAPPING.get(r) or Role(r)) for r in profile["roles"]],
126
126
  )
127
127
 
128
128
 
129
129
  USER_AVATAR_URL = "https://q.qlogo.cn/qqapp/{app_id}/{user_id}/100"
130
130
 
131
131
 
132
- # ROLE_MAPPING = {
133
- # "member": Role("MEMBER", "群成员"),
134
- # "admin": Role("ADMINISTRATOR", "管理员"),
135
- # "owner": Role("OWNER", "群主"),
136
- # }
132
+ ROLE_MAPPING = {
133
+ "1": Role("member", "成员"),
134
+ "5": Role("channel_admin", "子频道管理员"),
135
+ "2": Role("admin", "管理员"),
136
+ "4": Role("owner", "创建者"),
137
+ }
@@ -55,25 +55,36 @@ QQ_GUILD_FEATURES = [
55
55
  @dataclass
56
56
  class Intents:
57
57
  guilds: bool = True
58
+ """频道和子频道的基础事件"""
58
59
  guild_members: bool = True
60
+ """频道成员事件"""
59
61
  guild_messages: bool = False
60
- """GUILD_MESSAGES"""
62
+ """频道消息事件,仅私域机器人能够设置此 intents。"""
61
63
  guild_message_reactions: bool = True
62
- direct_message: bool = False
63
- """DIRECT_MESSAGES"""
64
+ """频道消息表态事件"""
65
+ direct_messages: bool = False
66
+ """频道私信消息事件"""
64
67
  open_forum_event: bool = False
65
68
  audio_live_member: bool = False
66
69
  c2c_group_at_messages: bool = False
70
+ """群聊和单聊事件"""
67
71
  interaction: bool = False
72
+ """互动事件(由按钮等组件触发的事件)"""
68
73
  message_audit: bool = True
74
+ """频道消息审核事件(由消息审核结果触发的事件)"""
69
75
  forum_event: bool = False
76
+ """频道论坛事件,仅私域机器人能够设置此 intents。"""
70
77
  audio_action: bool = False
78
+ """语音频道事件"""
71
79
  at_messages: bool = True
72
- """PUBLIC_GUILD_MESSAGES"""
80
+ """频道消息事件(只包含被 @ 的消息),为公域的消息事件"""
73
81
 
74
82
  def __post_init__(self):
75
83
  if self.at_messages and self.guild_messages:
76
- logger.warning("at_messages and guild_messages are both enabled, which is not recommended.")
84
+ logger.warning(
85
+ "检测到 at_messages 和 guild_messages 同时开启。"
86
+ "请确认 bot 是否为公域机器人,若为公域机器人,则开启 guild_messages 将导致连接鉴权失败。"
87
+ )
77
88
 
78
89
  def to_int(self) -> int:
79
90
  return (
@@ -81,7 +92,7 @@ class Intents:
81
92
  | self.guild_members << 1
82
93
  | self.guild_messages << 9
83
94
  | self.guild_message_reactions << 10
84
- | self.direct_message << 12
95
+ | self.direct_messages << 12
85
96
  | self.open_forum_event << 18
86
97
  | self.audio_live_member << 19
87
98
  | self.c2c_group_at_messages << 25
@@ -554,5 +565,8 @@ class QQBotWebsocketAdapter(BaseAdapter):
554
565
  logger.info(f"{self} Reconnecting...")
555
566
  continue
556
567
 
568
+ def __str__(self):
569
+ return self.id
570
+
557
571
 
558
572
  __all__ = ["QQBotWebsocketAdapter", "Intents"]