satori-python-adapter-milky 0.1.2__tar.gz → 0.2.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 (16) hide show
  1. {satori_python_adapter_milky-0.1.2 → satori_python_adapter_milky-0.2.0}/.mina/adapter_milky.toml +2 -2
  2. {satori_python_adapter_milky-0.1.2 → satori_python_adapter_milky-0.2.0}/PKG-INFO +6 -4
  3. {satori_python_adapter_milky-0.1.2 → satori_python_adapter_milky-0.2.0}/README.md +4 -2
  4. {satori_python_adapter_milky-0.1.2 → satori_python_adapter_milky-0.2.0}/pyproject.toml +2 -2
  5. {satori_python_adapter_milky-0.1.2 → satori_python_adapter_milky-0.2.0}/src/satori/adapters/milky/main.py +2 -3
  6. {satori_python_adapter_milky-0.1.2 → satori_python_adapter_milky-0.2.0}/src/satori/adapters/milky/message.py +123 -119
  7. {satori_python_adapter_milky-0.1.2 → satori_python_adapter_milky-0.2.0}/src/satori/adapters/milky/webhook.py +2 -3
  8. {satori_python_adapter_milky-0.1.2 → satori_python_adapter_milky-0.2.0}/LICENSE +0 -0
  9. {satori_python_adapter_milky-0.1.2 → satori_python_adapter_milky-0.2.0}/src/satori/adapters/milky/__init__.py +0 -0
  10. {satori_python_adapter_milky-0.1.2 → satori_python_adapter_milky-0.2.0}/src/satori/adapters/milky/api.py +0 -0
  11. {satori_python_adapter_milky-0.1.2 → satori_python_adapter_milky-0.2.0}/src/satori/adapters/milky/events/__init__.py +0 -0
  12. {satori_python_adapter_milky-0.1.2 → satori_python_adapter_milky-0.2.0}/src/satori/adapters/milky/events/base.py +0 -0
  13. {satori_python_adapter_milky-0.1.2 → satori_python_adapter_milky-0.2.0}/src/satori/adapters/milky/events/group.py +0 -0
  14. {satori_python_adapter_milky-0.1.2 → satori_python_adapter_milky-0.2.0}/src/satori/adapters/milky/events/message.py +0 -0
  15. {satori_python_adapter_milky-0.1.2 → satori_python_adapter_milky-0.2.0}/src/satori/adapters/milky/events/request.py +0 -0
  16. {satori_python_adapter_milky-0.1.2 → satori_python_adapter_milky-0.2.0}/src/satori/adapters/milky/utils.py +0 -0
@@ -1,9 +1,9 @@
1
1
  includes = ["src/satori/adapters/milky"]
2
- raw-dependencies = ["satori-python-server >= 0.17.6"]
2
+ raw-dependencies = ["satori-python-server >= 0.18.0"]
3
3
 
4
4
  [project]
5
5
  name = "satori-python-adapter-milky"
6
- version = "0.1.2"
6
+ version = "0.2.0"
7
7
  authors = [
8
8
  {name = "RF-Tar-Railt", email = "rf_tar_railt@qq.com"}
9
9
  ]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: satori-python-adapter-milky
3
- Version: 0.1.2
3
+ Version: 0.2.0
4
4
  Summary: Satori Protocol SDK for python, adapter for Milky
5
5
  Home-page: https://github.com/RF-Tar-Railt/satori-python
6
6
  Author-Email: RF-Tar-Railt <rf_tar_railt@qq.com>
@@ -17,7 +17,7 @@ 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>=0.17.6
20
+ Requires-Dist: satori-python-server>=0.18.0
21
21
  Description-Content-Type: text/markdown
22
22
 
23
23
  # satori-python
@@ -27,17 +27,18 @@ 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.js.org/zh-CN/) 协议的 Python 开发工具包
30
+ 基于 [Satori](https://satori.chat/zh-CN/) 协议的 Python 开发工具包
31
31
 
32
32
  ## 协议介绍
33
33
 
34
- [Satori Protocol](https://satori.js.org/zh-CN/)
34
+ [Satori Protocol](https://satori.chat/zh-CN/)
35
35
 
36
36
  ### 协议端
37
37
 
38
38
  目前提供了 `satori` 协议实现的有:
39
39
 
40
40
  - [Chronocat](https://chronocat.vercel.app)
41
+ - [LLBot](https://www.llonebot.com/guide/introduction)
41
42
  - [nekobox](https://github.com/wyapx/nekobox)
42
43
  - Koishi (搭配 `@koishijs/plugin-server`)
43
44
 
@@ -75,6 +76,7 @@ pip install satori-python-server
75
76
  | OneBot V11 | `pip install satori-python-adapter-onebot11` | satori.adapters.onebot11.forward, satori.adapters.onebot11.reverse |
76
77
  | Console | `pip install satori-python-adapter-console` | satori.adapters.console |
77
78
  | Milky | `pip install satori-python-adapter-milky` | satori.adapters.milky.main, satori.adapters.milky.webhook |
79
+ | QQ | `pip install satori-python-adapter-qq` | satori.adapters.milky.main, satori.adapters.milky.websocket |
78
80
 
79
81
  ### 社区适配器
80
82
 
@@ -5,17 +5,18 @@
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.js.org/zh-CN/) 协议的 Python 开发工具包
8
+ 基于 [Satori](https://satori.chat/zh-CN/) 协议的 Python 开发工具包
9
9
 
10
10
  ## 协议介绍
11
11
 
12
- [Satori Protocol](https://satori.js.org/zh-CN/)
12
+ [Satori Protocol](https://satori.chat/zh-CN/)
13
13
 
14
14
  ### 协议端
15
15
 
16
16
  目前提供了 `satori` 协议实现的有:
17
17
 
18
18
  - [Chronocat](https://chronocat.vercel.app)
19
+ - [LLBot](https://www.llonebot.com/guide/introduction)
19
20
  - [nekobox](https://github.com/wyapx/nekobox)
20
21
  - Koishi (搭配 `@koishijs/plugin-server`)
21
22
 
@@ -53,6 +54,7 @@ pip install satori-python-server
53
54
  | OneBot V11 | `pip install satori-python-adapter-onebot11` | satori.adapters.onebot11.forward, satori.adapters.onebot11.reverse |
54
55
  | Console | `pip install satori-python-adapter-console` | satori.adapters.console |
55
56
  | Milky | `pip install satori-python-adapter-milky` | satori.adapters.milky.main, satori.adapters.milky.webhook |
57
+ | QQ | `pip install satori-python-adapter-qq` | satori.adapters.milky.main, satori.adapters.milky.websocket |
56
58
 
57
59
  ### 社区适配器
58
60
 
@@ -1,11 +1,11 @@
1
1
  [project]
2
2
  name = "satori-python-adapter-milky"
3
- version = "0.1.2"
3
+ version = "0.2.0"
4
4
  authors = [
5
5
  { name = "RF-Tar-Railt", email = "rf_tar_railt@qq.com" },
6
6
  ]
7
7
  dependencies = [
8
- "satori-python-server >= 0.17.6",
8
+ "satori-python-server >= 0.18.0",
9
9
  ]
10
10
  description = "Satori Protocol SDK for python, adapter for Milky"
11
11
  readme = "README.md"
@@ -196,15 +196,14 @@ class MilkyAdapter(BaseAdapter):
196
196
  if handler:
197
197
  event = await handler(login, network, payload)
198
198
  else:
199
- body = payload.get("data", {})
200
199
  event = Event(
201
200
  EventType.INTERNAL,
202
201
  datetime.fromtimestamp(payload.get("time", datetime.now().timestamp())),
203
202
  login,
204
- _type=event_type,
205
- _data=body,
206
203
  )
207
204
  if event:
205
+ event._type = event_type
206
+ event._data = payload.get("data", {})
208
207
  await self.server.post(event)
209
208
 
210
209
  async def refresh_login(self):
@@ -91,108 +91,111 @@ class MilkyMessageEncoder:
91
91
  type_ = element.type
92
92
  attrs = element.attrs
93
93
  children = element.children
94
- if type_ == "text":
95
- text = attrs.get("text", "")
96
- if not self.segments or self.segments[-1]["type"] != "text":
97
- self.segments.append({"type": "text", "data": {"text": text}})
98
- else:
99
- self.segments[-1]["data"]["text"] += text
100
- elif type_ == "br":
101
- if not self.segments or self.segments[-1]["type"] != "text":
102
- self.segments.append({"type": "text", "data": {"text": "\n"}})
103
- else:
104
- self.segments[-1]["data"]["text"] += "\n"
105
- elif type_ == "p":
106
- prev = self.segments[-1] if self.segments else None
107
- if prev and prev["type"] == "text":
108
- if not prev["data"]["text"].endswith("\n"):
109
- prev["data"]["text"] += "\n"
110
- else:
111
- self.segments.append({"type": "text", "data": {"text": "\n"}})
112
- await self.render(children)
113
- if self.segments and self.segments[-1]["type"] == "text":
114
- if not self.segments[-1]["data"]["text"].endswith("\n"):
115
- self.segments[-1]["data"]["text"] += "\n"
116
- else:
117
- self.segments.append({"type": "text", "data": {"text": "\n"}})
118
- elif type_ == "at":
119
- if attrs.get("type") == "all":
120
- self.segments.append({"type": "mention_all", "data": {}})
121
- elif "id" in attrs:
122
- target = attrs["id"]
123
- self.segments.append({"type": "mention", "data": {"user_id": int(target)}})
124
- elif type_ == "sharp":
125
- self.segments.append({"type": "text", "data": {"text": attrs["id"]}})
126
- elif type_ == "a":
127
- await self.render(children)
128
- if "href" in attrs:
94
+ match type_:
95
+ case "text":
96
+ text = attrs.get("text", "")
129
97
  if not self.segments or self.segments[-1]["type"] != "text":
130
- self.segments.append({"type": "text", "data": {"text": f" ({attrs['href']})"}})
98
+ self.segments.append({"type": "text", "data": {"text": text}})
131
99
  else:
132
- self.segments[-1]["data"]["text"] += f" ({attrs['href']})"
133
- elif type_ in {"img", "image"}:
134
- uri = attrs.get("src") or attrs.get("url")
135
- if not uri:
136
- return
137
- if match := _BASE64_RE.match(uri):
138
- uri = f"base64://{uri[len(match.group(0)) :]}"
139
- self.segments.append({"type": "image", "data": {"uri": uri, "sub_type": attrs.get("sub_type", "normal")}})
140
- elif type_ == "audio":
141
- uri = attrs.get("src") or attrs.get("url")
142
- if not uri:
143
- return
144
- if match := _BASE64_RE.match(uri):
145
- uri = f"base64://{uri[len(match.group(0)) :]}"
146
- self.segments.append({"type": "record", "data": {"uri": uri}})
147
- elif type_ == "video":
148
- uri = attrs.get("src") or attrs.get("url")
149
- if not uri:
150
- return
151
- if match := _BASE64_RE.match(uri):
152
- uri = f"base64://{uri[len(match.group(0)) :]}"
153
- payload = {"uri": uri}
154
- if poster := attrs.get("poster"):
155
- payload["thumb_uri"] = poster
156
- self.segments.append({"type": "video", "data": payload})
157
- elif type_ == "milky:face":
158
- self.segments.append({"type": "face", "data": {"face_id": attrs["id"]}})
159
- elif type_ == "file":
160
- await self.flush()
161
- await self._send_file(attrs)
162
- elif type_ == "author":
163
- self.stack[0].author.update(attrs)
164
- elif type_ == "quote":
165
- await self.flush()
166
- self.segments.append({"type": "reply", "data": {"message_seq": int(attrs["id"])}})
167
- elif type_ == "message":
168
- await self.flush()
169
- if "forward" in attrs:
170
- self.stack.insert(0, State("forward"))
100
+ self.segments[-1]["data"]["text"] += text
101
+ case "br":
102
+ if not self.segments or self.segments[-1]["type"] != "text":
103
+ self.segments.append({"type": "text", "data": {"text": "\n"}})
104
+ else:
105
+ self.segments[-1]["data"]["text"] += "\n"
106
+ case "p":
107
+ prev = self.segments[-1] if self.segments else None
108
+ if prev and prev["type"] == "text":
109
+ if not prev["data"]["text"].endswith("\n"):
110
+ prev["data"]["text"] += "\n"
111
+ else:
112
+ self.segments.append({"type": "text", "data": {"text": "\n"}})
171
113
  await self.render(children)
172
- await self.flush()
173
- self.stack.pop(0)
174
- await self.send_forward()
175
- elif "id" in attrs:
176
- self.stack[0].author["seq"] = int(attrs["id"])
177
- else:
178
- payload = {}
179
- if "name" in attrs:
180
- payload["name"] = attrs["name"]
181
- if "nickname" in attrs:
182
- payload["name"] = attrs["nickname"]
183
- if "username" in attrs:
184
- payload["name"] = attrs["username"]
185
- if "id" in attrs:
186
- payload["id"] = int(attrs["id"])
187
- if "user_id" in attrs:
188
- payload["id"] = int(attrs["user_id"])
189
- if "time" in attrs:
190
- payload["time"] = int(attrs["time"])
191
- self.stack[0].author.update(payload)
114
+ if self.segments and self.segments[-1]["type"] == "text":
115
+ if not self.segments[-1]["data"]["text"].endswith("\n"):
116
+ self.segments[-1]["data"]["text"] += "\n"
117
+ else:
118
+ self.segments.append({"type": "text", "data": {"text": "\n"}})
119
+ case "at":
120
+ if attrs.get("type") == "all":
121
+ self.segments.append({"type": "mention_all", "data": {}})
122
+ elif "id" in attrs:
123
+ target = attrs["id"]
124
+ self.segments.append({"type": "mention", "data": {"user_id": int(target)}})
125
+ case "sharp":
126
+ self.segments.append({"type": "text", "data": {"text": attrs["id"]}})
127
+ case "a":
192
128
  await self.render(children)
129
+ if "href" in attrs:
130
+ if not self.segments or self.segments[-1]["type"] != "text":
131
+ self.segments.append({"type": "text", "data": {"text": f" ({attrs['href']})"}})
132
+ else:
133
+ self.segments[-1]["data"]["text"] += f" ({attrs['href']})"
134
+ case "img" | "image":
135
+ uri = attrs.get("src") or attrs.get("url")
136
+ if not uri:
137
+ return
138
+ if match := _BASE64_RE.match(uri):
139
+ uri = f"base64://{uri[len(match.group(0)) :]}"
140
+ self.segments.append(
141
+ {"type": "image", "data": {"uri": uri, "sub_type": attrs.get("sub_type", "normal")}}
142
+ )
143
+ case "audio":
144
+ uri = attrs.get("src") or attrs.get("url")
145
+ if not uri:
146
+ return
147
+ if match := _BASE64_RE.match(uri):
148
+ uri = f"base64://{uri[len(match.group(0)) :]}"
149
+ self.segments.append({"type": "record", "data": {"uri": uri}})
150
+ case "video":
151
+ uri = attrs.get("src") or attrs.get("url")
152
+ if not uri:
153
+ return
154
+ if match := _BASE64_RE.match(uri):
155
+ uri = f"base64://{uri[len(match.group(0)) :]}"
156
+ payload = {"uri": uri}
157
+ if poster := attrs.get("poster"):
158
+ payload["thumb_uri"] = poster
159
+ self.segments.append({"type": "video", "data": payload})
160
+ case "milky:face":
161
+ self.segments.append({"type": "face", "data": {"face_id": attrs["id"]}})
162
+ case "file":
193
163
  await self.flush()
194
- else:
195
- await self.render(children)
164
+ await self._send_file(attrs)
165
+ case "author":
166
+ self.stack[0].author.update(attrs)
167
+ case "quote":
168
+ await self.flush()
169
+ self.segments.append({"type": "reply", "data": {"message_seq": int(attrs["id"])}})
170
+ case "message":
171
+ await self.flush()
172
+ if "forward" in attrs:
173
+ self.stack.insert(0, State("forward"))
174
+ await self.render(children)
175
+ await self.flush()
176
+ self.stack.pop(0)
177
+ await self.send_forward()
178
+ elif "id" in attrs:
179
+ self.stack[0].author["seq"] = int(attrs["id"])
180
+ else:
181
+ payload = {}
182
+ if "name" in attrs:
183
+ payload["name"] = attrs["name"]
184
+ if "nickname" in attrs:
185
+ payload["name"] = attrs["nickname"]
186
+ if "username" in attrs:
187
+ payload["name"] = attrs["username"]
188
+ if "id" in attrs:
189
+ payload["id"] = int(attrs["id"])
190
+ if "user_id" in attrs:
191
+ payload["id"] = int(attrs["user_id"])
192
+ if "time" in attrs:
193
+ payload["time"] = int(attrs["time"])
194
+ self.stack[0].author.update(payload)
195
+ await self.render(children)
196
+ await self.flush()
197
+ case _:
198
+ await self.render(children)
196
199
 
197
200
  async def flush(self):
198
201
  if not self.segments:
@@ -352,28 +355,29 @@ async def _decode_segments(net: MilkyNetwork, payload: dict, segments: Sequence[
352
355
  for segment in segments:
353
356
  seg_type = segment.get("type")
354
357
  data = segment.get("data", {})
355
- if seg_type == "text":
356
- result.append(E.text(data.get("text", "")))
357
- elif seg_type == "mention":
358
- result.append(E.at(str(data.get("user_id"))))
359
- elif seg_type == "mention_all":
360
- result.append(E.at_all())
361
- elif seg_type == "image":
362
- result.append(E.image(_resource_url(data)))
363
- elif seg_type == "record":
364
- result.append(E.audio(_resource_url(data)))
365
- elif seg_type == "video":
366
- result.append(E.video(_resource_url(data)))
367
- elif seg_type == "file":
368
- result.append(E.file(_resource_url(data)))
369
- elif seg_type == "reply":
370
- seq = data.get("message_seq")
371
- if seq is not None:
372
- quote = await _decode_reply(net, payload, int(seq))
373
- if quote:
374
- result.append(quote)
375
- else:
376
- result.append(Custom(f"milky:{seg_type}", data))
358
+ match seg_type:
359
+ case "text":
360
+ result.append(E.text(data.get("text", "")))
361
+ case "mention":
362
+ result.append(E.at(str(data.get("user_id"))))
363
+ case "mention_all":
364
+ result.append(E.at_all())
365
+ case "image":
366
+ result.append(E.image(_resource_url(data)))
367
+ case "record":
368
+ result.append(E.audio(_resource_url(data)))
369
+ case "video":
370
+ result.append(E.video(_resource_url(data)))
371
+ case "file":
372
+ result.append(E.file(_resource_url(data)))
373
+ case "reply":
374
+ seq = data.get("message_seq")
375
+ if seq is not None:
376
+ quote = await _decode_reply(net, payload, int(seq))
377
+ if quote:
378
+ result.append(quote)
379
+ case _:
380
+ result.append(Custom(f"milky:{seg_type}", data))
377
381
  return result
378
382
 
379
383
 
@@ -147,15 +147,14 @@ class MilkyWebhookAdapter(BaseAdapter):
147
147
  if handler:
148
148
  event = await handler(login, network, payload)
149
149
  else:
150
- body = payload.get("data", {})
151
150
  event = Event(
152
151
  EventType.INTERNAL,
153
152
  datetime.fromtimestamp(payload.get("time", datetime.now().timestamp())),
154
153
  login,
155
- _type=event_type,
156
- _data=body,
157
154
  )
158
155
  if event:
156
+ event._type = event_type
157
+ event._data = payload.get("data", {})
159
158
  await self.server.post(event)
160
159
 
161
160
  async def refresh_login(self):