satori-python-adapter-onebot11 0.4.2__tar.gz → 0.5.1__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 (16) hide show
  1. {satori_python_adapter_onebot11-0.4.2 → satori_python_adapter_onebot11-0.5.1}/.mina/adapter_onebot11.toml +4 -4
  2. {satori_python_adapter_onebot11-0.4.2 → satori_python_adapter_onebot11-0.5.1}/PKG-INFO +7 -6
  3. {satori_python_adapter_onebot11-0.4.2 → satori_python_adapter_onebot11-0.5.1}/README.md +3 -2
  4. {satori_python_adapter_onebot11-0.4.2 → satori_python_adapter_onebot11-0.5.1}/pyproject.toml +15 -17
  5. {satori_python_adapter_onebot11-0.4.2 → satori_python_adapter_onebot11-0.5.1}/src/satori/adapters/onebot11/__init__.py +2 -0
  6. {satori_python_adapter_onebot11-0.4.2 → satori_python_adapter_onebot11-0.5.1}/src/satori/adapters/onebot11/forward.py +77 -68
  7. {satori_python_adapter_onebot11-0.4.2 → satori_python_adapter_onebot11-0.5.1}/src/satori/adapters/onebot11/message.py +8 -6
  8. {satori_python_adapter_onebot11-0.4.2 → satori_python_adapter_onebot11-0.5.1}/src/satori/adapters/onebot11/reverse.py +81 -70
  9. {satori_python_adapter_onebot11-0.4.2 → satori_python_adapter_onebot11-0.5.1}/LICENSE +0 -0
  10. {satori_python_adapter_onebot11-0.4.2 → satori_python_adapter_onebot11-0.5.1}/src/satori/adapters/onebot11/api.py +0 -0
  11. {satori_python_adapter_onebot11-0.4.2 → satori_python_adapter_onebot11-0.5.1}/src/satori/adapters/onebot11/events/__init__.py +0 -0
  12. {satori_python_adapter_onebot11-0.4.2 → satori_python_adapter_onebot11-0.5.1}/src/satori/adapters/onebot11/events/base.py +0 -0
  13. {satori_python_adapter_onebot11-0.4.2 → satori_python_adapter_onebot11-0.5.1}/src/satori/adapters/onebot11/events/message.py +0 -0
  14. {satori_python_adapter_onebot11-0.4.2 → satori_python_adapter_onebot11-0.5.1}/src/satori/adapters/onebot11/events/notice.py +0 -0
  15. {satori_python_adapter_onebot11-0.4.2 → satori_python_adapter_onebot11-0.5.1}/src/satori/adapters/onebot11/events/request.py +0 -0
  16. {satori_python_adapter_onebot11-0.4.2 → satori_python_adapter_onebot11-0.5.1}/src/satori/adapters/onebot11/utils.py +0 -0
@@ -1,9 +1,9 @@
1
1
  includes = ["src/satori/adapters/onebot11"]
2
- raw-dependencies = ["satori-python-server<1.4.0,>= 1.3.0"]
2
+ raw-dependencies = ["satori-python-server<1.4.0,>= 1.3.7"]
3
3
 
4
4
  [project]
5
5
  name = "satori-python-adapter-onebot11"
6
- version = "0.4.2"
6
+ version = "0.5.1"
7
7
  authors = [
8
8
  {name = "RF-Tar-Railt", email = "rf_tar_railt@qq.com"}
9
9
  ]
@@ -16,11 +16,11 @@ classifiers = [
16
16
  "Typing :: Typed",
17
17
  "Development Status :: 4 - Beta",
18
18
  "License :: OSI Approved :: MIT License",
19
- "Programming Language :: Python :: 3.8",
20
- "Programming Language :: Python :: 3.9",
21
19
  "Programming Language :: Python :: 3.10",
22
20
  "Programming Language :: Python :: 3.11",
23
21
  "Programming Language :: Python :: 3.12",
22
+ "Programming Language :: Python :: 3.13",
23
+ "Programming Language :: Python :: 3.14",
24
24
  "Operating System :: OS Independent",
25
25
  ]
26
26
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: satori-python-adapter-onebot11
3
- Version: 0.4.2
3
+ Version: 0.5.1
4
4
  Summary: Satori Protocol SDK for python, adapter for OneBot 11
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,16 +8,16 @@ 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
19
19
  Requires-Python: <4.0,>=3.10
20
- Requires-Dist: satori-python-server<1.4.0,>=1.3.0
20
+ Requires-Dist: satori-python-server<1.4.0,>=1.3.7
21
21
  Description-Content-Type: text/markdown
22
22
 
23
23
  # satori-python
@@ -27,11 +27,11 @@ Description-Content-Type: text/markdown
27
27
  [![PyPI](https://img.shields.io/pypi/v/satori-python)](https://pypi.org/project/satori-python)
28
28
  [![PyPI - Python Version](https://img.shields.io/pypi/pyversions/satori-python)](https://www.python.org/)
29
29
 
30
- 基于 [Satori](https://satori.chat/zh-CN/) 协议的 Python 开发工具包
30
+ 基于 [Satori](https://satori.chat/zh-CN/protocol) 协议的 Python 开发工具包
31
31
 
32
32
  ## 协议介绍
33
33
 
34
- [Satori Protocol](https://satori.chat/zh-CN/)
34
+ [Satori Protocol](https://satori.chat/zh-CN/protocol)
35
35
 
36
36
  ### 协议端
37
37
 
@@ -77,6 +77,7 @@ pip install satori-python-server
77
77
  | Console | `pip install satori-python-adapter-console` | satori.adapters.console |
78
78
  | Milky | `pip install satori-python-adapter-milky` | satori.adapters.milky.main, satori.adapters.milky.webhook, satori.adapters.milky.sse |
79
79
  | QQ | `pip install satori-python-adapter-qq` | satori.adapters.qq.main, satori.adapters.qq.websocket |
80
+ | WxClaw | `pip install satori-python-adapter-wxclaw` | satori.adapters.wxclaw |
80
81
 
81
82
  ### 社区适配器
82
83
 
@@ -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
 
@@ -55,6 +55,7 @@ pip install satori-python-server
55
55
  | Console | `pip install satori-python-adapter-console` | satori.adapters.console |
56
56
  | Milky | `pip install satori-python-adapter-milky` | satori.adapters.milky.main, satori.adapters.milky.webhook, satori.adapters.milky.sse |
57
57
  | QQ | `pip install satori-python-adapter-qq` | satori.adapters.qq.main, satori.adapters.qq.websocket |
58
+ | WxClaw | `pip install satori-python-adapter-wxclaw` | satori.adapters.wxclaw |
58
59
 
59
60
  ### 社区适配器
60
61
 
@@ -1,11 +1,11 @@
1
1
  [project]
2
2
  name = "satori-python-adapter-onebot11"
3
- version = "0.4.2"
3
+ version = "0.5.1"
4
4
  authors = [
5
5
  { name = "RF-Tar-Railt", email = "rf_tar_railt@qq.com" },
6
6
  ]
7
7
  dependencies = [
8
- "satori-python-server<1.4.0,>= 1.3.0",
8
+ "satori-python-server<1.4.0,>= 1.3.7",
9
9
  ]
10
10
  description = "Satori Protocol SDK for python, adapter for OneBot 11"
11
11
  readme = "README.md"
@@ -14,11 +14,11 @@ classifiers = [
14
14
  "Typing :: Typed",
15
15
  "Development Status :: 4 - Beta",
16
16
  "License :: OSI Approved :: MIT License",
17
- "Programming Language :: Python :: 3.8",
18
- "Programming Language :: Python :: 3.9",
19
17
  "Programming Language :: Python :: 3.10",
20
18
  "Programming Language :: Python :: 3.11",
21
19
  "Programming Language :: Python :: 3.12",
20
+ "Programming Language :: Python :: 3.13",
21
+ "Programming Language :: Python :: 3.14",
22
22
  "Operating System :: OS Independent",
23
23
  ]
24
24
 
@@ -38,15 +38,14 @@ build-backend = "mina.backend"
38
38
 
39
39
  [dependency-groups]
40
40
  dev = [
41
- "isort>=5.13.2",
42
- "black>=24.4.0",
43
- "ruff>=0.4.1",
41
+ "black>=26.3.0",
42
+ "ruff>=0.15.1",
44
43
  "pre-commit>=3.7.0",
45
- "fix-future-annotations>=0.5.0",
46
44
  "mina-build<0.6,>=0.5.1",
47
45
  "pdm-mina>=0.3.2",
48
46
  "nonechat<0.7.0,>=0.6.0",
49
47
  "uvicorn[standard]>=0.37.0",
48
+ "pydantic>=2.13.1",
50
49
  ]
51
50
 
52
51
  [tool.pdm.build]
@@ -60,7 +59,7 @@ excludes = [
60
59
 
61
60
  [tool.pdm.scripts.format]
62
61
  composite = [
63
- "isort ./src/ ./example/",
62
+ "ruff check --select I --fix ./src/ ./example/",
64
63
  "black ./src/ ./example/",
65
64
  "ruff check",
66
65
  ]
@@ -74,14 +73,6 @@ line-length = 120
74
73
  include = "\\.pyi?$"
75
74
  extend-exclude = ""
76
75
 
77
- [tool.isort]
78
- profile = "black"
79
- line_length = 120
80
- skip_gitignore = true
81
- extra_standard_library = [
82
- "typing_extensions",
83
- ]
84
-
85
76
  [tool.ruff]
86
77
  line-length = 120
87
78
  target-version = "py310"
@@ -91,6 +82,7 @@ exclude = [
91
82
  "exam2.py",
92
83
  "src/satori/_vendor/*",
93
84
  ]
85
+ respect-gitignore = true
94
86
 
95
87
  [tool.ruff.lint]
96
88
  select = [
@@ -110,6 +102,12 @@ ignore = [
110
102
  "T201",
111
103
  ]
112
104
 
105
+ [tool.ruff.lint.isort]
106
+ extra-standard-library = [
107
+ "typing_extensions",
108
+ ]
109
+ force-sort-within-sections = false
110
+
113
111
  [tool.pyright]
114
112
  pythonPlatform = "All"
115
113
  pythonVersion = "3.10"
@@ -1,2 +1,4 @@
1
1
  from .forward import OneBot11ForwardAdapter as OneBot11ForwardAdapter
2
+ from .forward import OneBot11ForwardConfig as OneBot11ForwardConfig
2
3
  from .reverse import OneBot11ReverseAdapter as OneBot11ReverseAdapter
4
+ from .reverse import OneBot11ReverseConfig as OneBot11ReverseConfig
@@ -2,6 +2,7 @@ from __future__ import annotations
2
2
 
3
3
  import asyncio
4
4
  from contextlib import suppress
5
+ from dataclasses import dataclass
5
6
  from datetime import datetime
6
7
  from typing import cast
7
8
 
@@ -25,21 +26,30 @@ from .events.base import events
25
26
  from .utils import USER_AVATAR_URL, onebot11_event_type
26
27
 
27
28
 
28
- class OneBot11ForwardAdapter(BaseAdapter):
29
+ @dataclass
30
+ class OneBot11ForwardConfig:
31
+ endpoint: str
32
+ """连接 OneBot V11协议端的路径"""
33
+ access_token: str | None = None
34
+ """OneBot V11协议的访问令牌, 默认为空"""
35
+ timeout: float = 60
36
+ """连接超时时间, 默认为 60 秒"""
37
+
38
+
39
+ class OneBot11ForwardAdapter(BaseAdapter[OneBot11ForwardConfig]):
29
40
 
30
41
  session: ClientSession
31
42
  connection: ClientWebSocketResponse | None
32
43
 
33
44
  def __init__(
34
45
  self,
35
- endpoint: str | URL,
36
- access_token: str | None = None,
37
- timeout: float = 60,
46
+ config: OneBot11ForwardConfig,
38
47
  ):
39
48
  super().__init__()
40
- self.endpoint = URL(endpoint)
41
- self.timeout = timeout
42
- self.access_token = access_token
49
+ self.config = config
50
+ self.endpoint = URL(config.endpoint)
51
+ self.timeout = config.timeout
52
+ self.access_token = config.access_token
43
53
  self.close_signal = asyncio.Event()
44
54
  self.response_waiters: dict[str, asyncio.Future] = {}
45
55
  self.logins: dict[str, Login] = {}
@@ -64,74 +74,73 @@ class OneBot11ForwardAdapter(BaseAdapter):
64
74
  return {"preparing", "blocking", "cleanup"}
65
75
 
66
76
  async def message_handle(self):
77
+ async def _login_add():
78
+ self_info = await self.call_api("get_login_info")
79
+ login = Login(
80
+ sn=0,
81
+ status=LoginStatus.ONLINE,
82
+ adapter="onebot",
83
+ platform="qq-client",
84
+ user=User(
85
+ self_id,
86
+ (self_info or {})["nickname"],
87
+ avatar=USER_AVATAR_URL.format(uin=self_id),
88
+ ),
89
+ features=["guild.plain"],
90
+ )
91
+ self.logins[self_id] = login
92
+ await self.server.post(Event(EventType.LOGIN_ADDED, datetime.now(), login))
93
+
67
94
  async for connection, data in self.message_receive():
68
95
  if echo := data.get("echo"):
69
96
  if future := self.response_waiters.get(echo):
70
97
  future.set_result(data)
71
98
  continue
72
99
 
73
- async def event_parse_task(data: dict):
74
- event_type = onebot11_event_type(data)
75
- if event_type == "meta_event.lifecycle.connect":
76
- self_id = str(data["self_id"])
77
- if self_id not in self.logins:
78
- self_info = await self.call_api("get_login_info")
79
- login = Login(
80
- 0,
81
- LoginStatus.ONLINE,
82
- "onebot",
83
- platform="onebot",
84
- user=User(
85
- self_id,
86
- (self_info or {})["nickname"],
87
- avatar=USER_AVATAR_URL.format(uin=self_id),
88
- ),
89
- features=["guild.plain"],
90
- )
91
- self.logins[self_id] = login
92
- await self.server.post(Event(EventType.LOGIN_ADDED, datetime.now(), login))
93
- elif event_type == "meta_event.lifecycle.enable":
94
- logger.warning(f"received lifecycle.enable event that is only supported in http adapter: {data}")
95
- return
96
- elif event_type == "meta_event.lifecycle.disable":
97
- logger.warning(f"received lifecycle.disable event that is only supported in http adapter: {data}")
100
+ event_type = onebot11_event_type(data)
101
+ if event_type == "meta_event.lifecycle.connect":
102
+ self_id = str(data["self_id"])
103
+ if self_id not in self.logins:
104
+ asyncio.create_task(_login_add())
105
+ elif event_type == "meta_event.lifecycle.enable":
106
+ logger.warning(f"received lifecycle.enable event that is only supported in http adapter: {data}")
107
+ return
108
+ elif event_type == "meta_event.lifecycle.disable":
109
+ logger.warning(f"received lifecycle.disable event that is only supported in http adapter: {data}")
110
+ return
111
+ elif event_type == "meta_event.heartbeat":
112
+ self_id = str(data["self_id"])
113
+ if self_id not in self.logins:
114
+ asyncio.create_task(_login_add())
115
+ logger.trace(f"received heartbeat from {self_id}")
116
+ else:
117
+ self_id = str(data["self_id"])
118
+ if self_id not in self.logins:
119
+ logger.warning(f"received event from unknown self_id: {data}")
98
120
  return
99
- elif event_type == "meta_event.heartbeat":
100
- self_id = str(data["self_id"])
101
- if self_id not in self.logins:
102
- self_info = await self.call_api("get_login_info")
103
- login = Login(
104
- 0,
105
- LoginStatus.ONLINE,
106
- "onebot",
107
- platform="onebot",
108
- user=User(
109
- self_id,
110
- (self_info or {})["nickname"],
111
- avatar=USER_AVATAR_URL.format(uin=self_id),
112
- ),
113
- features=["guild.plain"],
114
- )
115
- self.logins[self_id] = login
116
- await self.server.post(Event(EventType.LOGIN_ADDED, datetime.now(), login))
117
- logger.trace(f"received heartbeat from {self_id}")
118
- else:
119
- self_id = str(data["self_id"])
120
- if self_id not in self.logins:
121
- logger.warning(f"received event from unknown self_id: {data}")
122
- return
123
- login = self.logins[self_id]
124
- handler = events.get(event_type)
125
- if not handler:
126
- event = Event(EventType.INTERNAL, datetime.now(), login)
127
- else:
128
- event = await handler(login, self, data)
129
- if event:
130
- event._type = event_type
131
- event._data = data
132
- await self.server.post(event)
133
-
134
- asyncio.create_task(event_parse_task(data))
121
+ login = self.logins[self_id]
122
+ handler = events.get(event_type)
123
+
124
+ async def _event_parse(
125
+ _login=login,
126
+ _handler=handler,
127
+ _data=data,
128
+ _event_type=event_type,
129
+ ):
130
+ event = None
131
+ if _handler:
132
+ try:
133
+ event = await _handler(_login, self, _data)
134
+ except Exception as e:
135
+ logger.exception(f"failed to handle event {event_type}: {e!r}")
136
+ event = None
137
+ if not event:
138
+ event = Event(EventType.INTERNAL, datetime.now(), _login)
139
+ event._type = _event_type
140
+ event._data = _data
141
+ await self.server.post(event)
142
+
143
+ asyncio.create_task(_event_parse())
135
144
 
136
145
  async def message_receive(self):
137
146
  if self.connection is None:
@@ -85,7 +85,7 @@ class OneBot11MessageEncoder:
85
85
  },
86
86
  )
87
87
  if resp:
88
- self.results.append(MessageObject(resp["message_id"], ""))
88
+ self.results.append(MessageObject(str(resp["message_id"]), ""))
89
89
 
90
90
  async def flush(self):
91
91
  if not self.children:
@@ -111,15 +111,15 @@ class OneBot11MessageEncoder:
111
111
 
112
112
  slot = self.stack[0]
113
113
  type_, author = slot.type, slot.author
114
- if not self.children and "message_id" not in author:
114
+ if not self.children and "messageId" not in author:
115
115
  return
116
116
  if type_ == "forward":
117
- if "message_id" in author:
117
+ if "messageId" in author:
118
118
  self.stack[1].children.append(
119
119
  {
120
120
  "type": "node",
121
121
  "data": {
122
- "id": author["message_id"],
122
+ "id": author["messageId"],
123
123
  },
124
124
  }
125
125
  )
@@ -159,7 +159,7 @@ class OneBot11MessageEncoder:
159
159
  },
160
160
  )
161
161
  if resp:
162
- self.results.append(MessageObject(resp["message_id"], ""))
162
+ self.results.append(MessageObject(str(resp["message_id"]), ""))
163
163
  self.children = []
164
164
 
165
165
  async def _send_file(self, attrs: dict[str, Any]):
@@ -286,7 +286,7 @@ class OneBot11MessageEncoder:
286
286
  self.stack.pop(0)
287
287
  await self.send_forward()
288
288
  elif "id" in attrs:
289
- self.stack[0].author["message_id"] = str(attrs["id"])
289
+ self.stack[0].author["messageId"] = str(attrs["id"])
290
290
  else:
291
291
  payload = {}
292
292
  if "name" in attrs:
@@ -299,6 +299,8 @@ class OneBot11MessageEncoder:
299
299
  payload["id"] = int(attrs["id"])
300
300
  if "user_id" in attrs:
301
301
  payload["id"] = int(attrs["user_id"])
302
+ if "userId" in attrs:
303
+ payload["id"] = int(attrs["userId"])
302
304
  if "time" in attrs:
303
305
  payload["time"] = int(attrs["time"])
304
306
  self.stack[0].author.update(payload)
@@ -1,6 +1,7 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import asyncio
4
+ from dataclasses import dataclass
4
5
  from datetime import datetime
5
6
 
6
7
  from launart import Launart, any_completed
@@ -44,74 +45,73 @@ class _Connection:
44
45
  self.close_signal.set()
45
46
 
46
47
  async def message_handle(self):
48
+ async def _login_add():
49
+ self_info = await self.call_api("get_login_info")
50
+ login = Login(
51
+ sn=0,
52
+ status=LoginStatus.ONLINE,
53
+ adapter="onebot",
54
+ platform="qq-client",
55
+ user=User(
56
+ self_id,
57
+ (self_info or {})["nickname"],
58
+ avatar=USER_AVATAR_URL.format(uin=self_id),
59
+ ),
60
+ features=["guild.plain"],
61
+ )
62
+ self.adapter.logins[self_id] = login
63
+ await self.adapter.server.post(Event(EventType.LOGIN_ADDED, datetime.now(), login))
64
+
47
65
  async for connection, data in self.message_receive():
48
66
  if echo := data.get("echo"):
49
67
  if future := self.response_waiters.get(echo):
50
68
  future.set_result(data)
51
69
  continue
52
70
 
53
- async def event_parse_task(data: dict):
54
- event_type = onebot11_event_type(data)
55
- if event_type == "meta_event.lifecycle.connect":
56
- self_id = str(data["self_id"])
57
- if self_id not in self.adapter.logins:
58
- self_info = await self.call_api("get_login_info")
59
- login = Login(
60
- 0,
61
- LoginStatus.ONLINE,
62
- "onebot",
63
- platform="onebot",
64
- user=User(
65
- self_id,
66
- (self_info or {})["nickname"],
67
- avatar=USER_AVATAR_URL.format(uin=self_id),
68
- ),
69
- features=["guild.plain"],
70
- )
71
- self.adapter.logins[self_id] = login
72
- await self.adapter.server.post(Event(EventType.LOGIN_ADDED, datetime.now(), login))
73
- elif event_type == "meta_event.lifecycle.enable":
74
- logger.warning(f"received lifecycle.enable event that is only supported in http adapter: {data}")
75
- return
76
- elif event_type == "meta_event.lifecycle.disable":
77
- logger.warning(f"received lifecycle.disable event that is only supported in http adapter: {data}")
71
+ event_type = onebot11_event_type(data)
72
+ if event_type == "meta_event.lifecycle.connect":
73
+ self_id = str(data["self_id"])
74
+ if self_id not in self.adapter.logins:
75
+ asyncio.create_task(_login_add())
76
+ elif event_type == "meta_event.lifecycle.enable":
77
+ logger.warning(f"received lifecycle.enable event that is only supported in http adapter: {data}")
78
+ return
79
+ elif event_type == "meta_event.lifecycle.disable":
80
+ logger.warning(f"received lifecycle.disable event that is only supported in http adapter: {data}")
81
+ return
82
+ elif event_type == "meta_event.heartbeat":
83
+ self_id = str(data["self_id"])
84
+ if self_id not in self.adapter.logins:
85
+ asyncio.create_task(_login_add())
86
+ logger.trace(f"received heartbeat from {self_id}")
87
+ else:
88
+ self_id = str(data["self_id"])
89
+ if self_id not in self.adapter.logins:
90
+ logger.warning(f"received event from unknown self_id: {data}")
78
91
  return
79
- elif event_type == "meta_event.heartbeat":
80
- self_id = str(data["self_id"])
81
- if self_id not in self.adapter.logins:
82
- self_info = await self.call_api("get_login_info")
83
- login = Login(
84
- 0,
85
- LoginStatus.ONLINE,
86
- "onebot",
87
- platform="onebot",
88
- user=User(
89
- self_id,
90
- (self_info or {})["nickname"],
91
- avatar=USER_AVATAR_URL.format(uin=self_id),
92
- ),
93
- features=["guild.plain"],
94
- )
95
- self.adapter.logins[self_id] = login
96
- await self.adapter.server.post(Event(EventType.LOGIN_ADDED, datetime.now(), login))
97
- logger.trace(f"received heartbeat from {self_id}")
98
- else:
99
- self_id = str(data["self_id"])
100
- if self_id not in self.adapter.logins:
101
- logger.warning(f"received event from unknown self_id: {data}")
102
- return
103
- login = self.adapter.logins[self_id]
104
- handler = events.get(event_type)
105
- if not handler:
106
- event = Event(EventType.INTERNAL, datetime.now(), login)
107
- else:
108
- event = await handler(login, self, data)
109
- if event:
110
- event._type = event_type
111
- event._data = data
112
- await self.adapter.server.post(event)
113
-
114
- asyncio.create_task(event_parse_task(data))
92
+ login = self.adapter.logins[self_id]
93
+ handler = events.get(event_type)
94
+
95
+ async def _event_parse(
96
+ _login=login,
97
+ _handler=handler,
98
+ _data=data,
99
+ _event_type=event_type,
100
+ ):
101
+ event = None
102
+ if _handler:
103
+ try:
104
+ event = await _handler(_login, self, _data)
105
+ except Exception as e:
106
+ logger.exception(f"failed to handle event {_event_type}: {e!r}")
107
+ event = None
108
+ if not event:
109
+ event = Event(EventType.INTERNAL, datetime.now(), _login)
110
+ event._type = _event_type
111
+ event._data = _data
112
+ await self.adapter.server.post(event)
113
+
114
+ asyncio.create_task(_event_parse())
115
115
 
116
116
  async def call_api(self, action: str, params: dict | None = None) -> dict:
117
117
  if not self.ws:
@@ -133,22 +133,33 @@ class _Connection:
133
133
  return result.get("data", {})
134
134
 
135
135
 
136
- class OneBot11ReverseAdapter(BaseAdapter):
136
+ @dataclass
137
+ class OneBot11ReverseConfig:
138
+ prefix: str = "/"
139
+ """反向适配器于 Server 的路径前缀, 默认为 /"""
140
+ path: str = "onebot/v11"
141
+ """反向适配器于 Server 的路径, 默认为 onebot/v11"""
142
+ endpoint: str = "ws"
143
+ """反向适配器于 Server 的路径端点, 默认为 ws (完整路径即为 /onebot/v11/ws)"""
144
+ access_token: str | None = None
145
+ """反向适配器的访问令牌, 默认为空"""
146
+ timeout: int = 60
147
+ """发送请求的超时时间,默认为 60 秒"""
148
+
149
+
150
+ class OneBot11ReverseAdapter(BaseAdapter[OneBot11ReverseConfig]):
137
151
 
138
152
  def __init__(
139
153
  self,
140
- prefix: str = "/",
141
- path: str = "onebot/v11",
142
- endpoint: str = "ws",
143
- access_token: str | None = None,
144
- timeout: int = 60,
154
+ config: OneBot11ReverseConfig,
145
155
  ):
146
156
  super().__init__()
147
- self.endpoint = URL(prefix) / path / endpoint
148
- self.access_token = access_token
157
+ self.config = config
158
+ self.endpoint = URL(config.prefix) / config.path / config.endpoint
159
+ self.access_token = config.access_token
149
160
  self.logins: dict[str, Login] = {}
150
161
  self.connections: dict[str, _Connection] = {}
151
- self.timeout = timeout
162
+ self.timeout = config.timeout
152
163
 
153
164
  apply(self, lambda _: self.connections[_], lambda _: self.logins[_])
154
165