satori-python-core 0.11.4__tar.gz → 0.12.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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: satori-python-core
3
- Version: 0.11.4
3
+ Version: 0.12.0
4
4
  Summary: Satori Protocol SDK for python, specify common part
5
5
  Home-page: https://github.com/RF-Tar-Railt/satori-python
6
6
  Author-Email: RF-Tar-Railt <rf_tar_railt@qq.com>
@@ -19,7 +19,7 @@ Project-URL: Repository, https://github.com/RF-Tar-Railt/satori-python
19
19
  Requires-Python: >=3.8
20
20
  Requires-Dist: loguru>=0.7.2
21
21
  Requires-Dist: yarl>=1.9.4
22
- Requires-Dist: typing-extensions>=4.10.0
22
+ Requires-Dist: typing-extensions>=4.7.0
23
23
  Description-Content-Type: text/markdown
24
24
 
25
25
  # satori-python
@@ -7,7 +7,7 @@ authors = [
7
7
  dependencies = [
8
8
  "loguru>=0.7.2",
9
9
  "yarl>=1.9.4",
10
- "typing-extensions>=4.10.0",
10
+ "typing-extensions>=4.7.0",
11
11
  ]
12
12
  description = "Satori Protocol SDK for python, specify common part"
13
13
  readme = "README.md"
@@ -23,7 +23,7 @@ classifiers = [
23
23
  "Programming Language :: Python :: 3.12",
24
24
  "Operating System :: OS Independent",
25
25
  ]
26
- version = "0.11.4"
26
+ version = "0.12.0"
27
27
 
28
28
  [project.license]
29
29
  text = "MIT"
@@ -41,9 +41,9 @@ build-backend = "mina.backend"
41
41
  [tool.pdm.dev-dependencies]
42
42
  dev = [
43
43
  "isort>=5.13.2",
44
- "black>=24.2.0",
45
- "ruff>=0.3.2",
46
- "pre-commit>=3.6.2",
44
+ "black>=24.4.0",
45
+ "ruff>=0.4.1",
46
+ "pre-commit>=3.7.0",
47
47
  "fix-future-annotations>=0.5.0",
48
48
  "mina-build<0.6,>=0.5.1",
49
49
  "pdm-mina>=0.3.2",
@@ -39,4 +39,4 @@ from .model import MessageObject as MessageObject
39
39
  from .model import Role as Role
40
40
  from .model import User as User
41
41
 
42
- __version__ = "0.11.4"
42
+ __version__ = "0.12.0"
@@ -18,13 +18,17 @@ class Element:
18
18
 
19
19
  __names__: ClassVar[Tuple[str, ...]]
20
20
 
21
+ @property
22
+ def children(self) -> List["Element"]:
23
+ return self._children
24
+
21
25
  @property
22
26
  def tag(self) -> str:
23
27
  return self.__class__.__name__.lower()
24
28
 
25
29
  @classmethod
26
30
  def unpack(cls, attrs: Dict[str, Any]):
27
- obj = cls(**{k: v for k, v in attrs.items() if k in cls.__names__})
31
+ obj = cls(**{k: v for k, v in attrs.items() if k in cls.__names__}) # type: ignore
28
32
  obj._attrs.update({k: v for k, v in attrs.items() if k not in cls.__names__})
29
33
  return obj
30
34
 
@@ -132,7 +136,7 @@ class Sharp(Element):
132
136
  name: Optional[str] = None
133
137
 
134
138
 
135
- @dataclass
139
+ @dataclass(repr=False)
136
140
  class Link(Element):
137
141
  """<a> 元素用于显示一个链接。"""
138
142
 
@@ -163,7 +167,7 @@ class Resource(Element):
163
167
  title: Optional[str] = None
164
168
  extra: InitVar[Optional[Dict[str, Any]]] = None
165
169
  cache: Optional[bool] = None
166
- timeout: Optional[str] = None
170
+ timeout: Optional[int] = None
167
171
 
168
172
  __names__ = ("src", "title")
169
173
 
@@ -178,7 +182,7 @@ class Resource(Element):
178
182
  poster: Optional[str] = None,
179
183
  extra: Optional[Dict[str, Any]] = None,
180
184
  cache: Optional[bool] = None,
181
- timeout: Optional[str] = None,
185
+ timeout: Optional[int] = None,
182
186
  **kwargs,
183
187
  ):
184
188
  data: Dict[str, Any] = {"extra": extra}
@@ -537,7 +541,7 @@ def transform(elements: List[RawElement]) -> List[Element]:
537
541
  tag = elem.tag()
538
542
  if tag in ELEMENT_TYPE_MAP:
539
543
  seg_cls = ELEMENT_TYPE_MAP[tag]
540
- msg.append(seg_cls.unpack(elem.attrs))
544
+ msg.append(seg_cls.unpack(elem.attrs)(*transform(elem.children)))
541
545
  elif tag in ("a", "link"):
542
546
  link = Link.unpack(elem.attrs)
543
547
  if elem.children:
@@ -547,6 +551,7 @@ def transform(elements: List[RawElement]) -> List[Element]:
547
551
  button = Button.unpack(elem.attrs)
548
552
  if elem.children:
549
553
  button(*transform(elem.children))
554
+ msg.append(button)
550
555
  elif tag in STYLE_TYPE_MAP:
551
556
  seg_cls = STYLE_TYPE_MAP[tag]
552
557
  msg.append(seg_cls.unpack(elem.attrs)(*transform(elem.children)))
@@ -1,16 +1,29 @@
1
- from dataclasses import asdict, dataclass
1
+ from dataclasses import asdict, dataclass, field, fields
2
2
  from datetime import datetime
3
3
  from enum import IntEnum
4
- from typing import Any, Callable, Dict, Generic, List, Optional, TypeVar
4
+ from typing import Any, Callable, ClassVar, Dict, Generic, List, Literal, Optional, TypeVar
5
+ from typing_extensions import TypeAlias
5
6
 
6
7
  from .element import Element, transform
8
+ from .parser import Element as RawElement
7
9
  from .parser import parse
8
10
 
9
11
 
12
+ @dataclass
10
13
  class ModelBase:
14
+ __converter__: ClassVar[Dict[str, Callable[[Any], Any]]] = {}
15
+
11
16
  @classmethod
12
17
  def parse(cls, raw: dict):
13
- raise NotImplementedError
18
+ fs = fields(cls)
19
+ data = {}
20
+ for fd in fs:
21
+ if fd.name in raw:
22
+ if fd.name in cls.__converter__:
23
+ data[fd.name] = cls.__converter__[fd.name](raw[fd.name])
24
+ else:
25
+ data[fd.name] = raw[fd.name]
26
+ return cls(**data) # type: ignore
14
27
 
15
28
  def dump(self) -> dict:
16
29
  raise NotImplementedError
@@ -30,11 +43,7 @@ class Channel(ModelBase):
30
43
  name: Optional[str] = None
31
44
  parent_id: Optional[str] = None
32
45
 
33
- @classmethod
34
- def parse(cls, raw: dict):
35
- data = raw.copy()
36
- data["type"] = ChannelType(raw["type"])
37
- return cls(**data)
46
+ __converter__ = {"type": ChannelType}
38
47
 
39
48
  def dump(self):
40
49
  res = {"id": self.id, "type": self.type.value}
@@ -51,10 +60,6 @@ class Guild(ModelBase):
51
60
  name: Optional[str] = None
52
61
  avatar: Optional[str] = None
53
62
 
54
- @classmethod
55
- def parse(cls, raw: dict):
56
- return cls(**raw)
57
-
58
63
  def dump(self):
59
64
  res = {"id": self.id}
60
65
  if self.name:
@@ -72,10 +77,6 @@ class User(ModelBase):
72
77
  avatar: Optional[str] = None
73
78
  is_bot: Optional[bool] = None
74
79
 
75
- @classmethod
76
- def parse(cls, raw: dict):
77
- return cls(**raw)
78
-
79
80
  def dump(self):
80
81
  res: Dict[str, Any] = {"id": self.id}
81
82
  if self.name:
@@ -93,25 +94,17 @@ class User(ModelBase):
93
94
  class Member(ModelBase):
94
95
  user: Optional[User] = None
95
96
  nick: Optional[str] = None
96
- name: Optional[str] = None
97
97
  avatar: Optional[str] = None
98
98
  joined_at: Optional[datetime] = None
99
99
 
100
- @classmethod
101
- def parse(cls, raw: dict):
102
- data = raw.copy()
103
- if "user" in raw:
104
- data["user"] = User.parse(raw["user"])
105
- if "joined_at" in raw:
106
- data["joined_at"] = datetime.fromtimestamp(int(raw["joined_at"]) / 1000)
107
- return cls(**data)
100
+ __converter__ = {"user": User.parse, "joined_at": lambda ts: datetime.fromtimestamp(int(ts) / 1000)}
108
101
 
109
102
  def dump(self):
110
103
  res = {}
111
104
  if self.user:
112
105
  res["user"] = self.user.dump()
113
- if self.nick or self.name:
114
- res["nick"] = self.nick or self.name
106
+ if self.nick:
107
+ res["nick"] = self.nick
115
108
  if self.avatar:
116
109
  res["avatar"] = self.avatar
117
110
  if self.joined_at:
@@ -124,10 +117,6 @@ class Role(ModelBase):
124
117
  id: str
125
118
  name: Optional[str] = None
126
119
 
127
- @classmethod
128
- def parse(cls, raw: dict):
129
- return cls(**raw)
130
-
131
120
  def dump(self):
132
121
  res = {"id": self.id}
133
122
  if self.name:
@@ -149,17 +138,17 @@ class Login(ModelBase):
149
138
  user: Optional[User] = None
150
139
  self_id: Optional[str] = None
151
140
  platform: Optional[str] = None
141
+ features: List[str] = field(default_factory=list)
142
+ proxy_urls: List[str] = field(default_factory=list)
152
143
 
153
- @classmethod
154
- def parse(cls, raw: dict):
155
- data = raw.copy()
156
- if "user" in raw:
157
- data["user"] = User(**raw["user"])
158
- data["status"] = LoginStatus(data["status"])
159
- return cls(**data)
144
+ __converter__ = {"user": User.parse, "status": LoginStatus}
160
145
 
161
146
  def dump(self):
162
- res: Dict[str, Any] = {"status": self.status.value}
147
+ res: Dict[str, Any] = {
148
+ "status": self.status.value,
149
+ "features": self.features,
150
+ "proxy_urls": self.proxy_urls,
151
+ }
163
152
  if self.user:
164
153
  res["user"] = self.user.dump()
165
154
  if self.self_id:
@@ -175,10 +164,6 @@ class ArgvInteraction(ModelBase):
175
164
  arguments: list
176
165
  options: Any
177
166
 
178
- @classmethod
179
- def parse(cls, raw: dict):
180
- return cls(**raw)
181
-
182
167
  def dump(self):
183
168
  return asdict(self)
184
169
 
@@ -187,10 +172,6 @@ class ArgvInteraction(ModelBase):
187
172
  class ButtonInteraction(ModelBase):
188
173
  id: str
189
174
 
190
- @classmethod
191
- def parse(cls, raw: dict):
192
- return cls(**raw)
193
-
194
175
  def dump(self):
195
176
  return asdict(self)
196
177
 
@@ -245,23 +226,19 @@ class MessageObject(ModelBase):
245
226
 
246
227
  @classmethod
247
228
  def parse(cls, raw: dict):
248
- data = {
249
- "id": raw["id"],
250
- "content": raw["content"],
251
- }
252
- if "channel" in raw:
253
- data["channel"] = Channel.parse(raw["channel"])
254
- if "guild" in raw:
255
- data["guild"] = Guild.parse(raw["guild"])
256
- if "member" in raw:
257
- data["member"] = Member.parse(raw["member"])
258
- if "user" in raw:
259
- data["user"] = User.parse(raw["user"])
260
- if "created_at" in raw:
261
- data["created_at"] = datetime.fromtimestamp(int(raw["created_at"]) / 1000)
262
- if "updated_at" in raw:
263
- data["updated_at"] = datetime.fromtimestamp(int(raw["updated_at"]) / 1000)
264
- return cls(**data)
229
+ if "elements" in raw and "content" not in raw:
230
+ content = [RawElement(*item.values()) for item in raw["elements"]]
231
+ raw["content"] = "".join(str(i) for i in content)
232
+ return super().parse(raw)
233
+
234
+ __converter__ = {
235
+ "channel": Channel.parse,
236
+ "guild": Guild.parse,
237
+ "member": Member.parse,
238
+ "user": User.parse,
239
+ "created_at": lambda ts: datetime.fromtimestamp(int(ts) / 1000),
240
+ "updated_at": lambda ts: datetime.fromtimestamp(int(ts) / 1000),
241
+ }
265
242
 
266
243
  def dump(self):
267
244
  res: Dict[str, Any] = {"id": self.id, "content": self.content}
@@ -281,7 +258,7 @@ class MessageObject(ModelBase):
281
258
 
282
259
 
283
260
  @dataclass
284
- class Event:
261
+ class Event(ModelBase):
285
262
  id: int
286
263
  type: str
287
264
  platform: str
@@ -301,40 +278,19 @@ class Event:
301
278
  _type: Optional[str] = None
302
279
  _data: Optional[dict] = None
303
280
 
304
- @classmethod
305
- def parse(cls, raw: dict):
306
- data = {
307
- "id": raw["id"],
308
- "type": raw["type"],
309
- "platform": raw["platform"],
310
- "self_id": raw["self_id"],
311
- "timestamp": datetime.fromtimestamp(int(raw["timestamp"]) / 1000),
312
- }
313
- if "argv" in raw:
314
- data["argv"] = ArgvInteraction.parse(raw["argv"])
315
- if "button" in raw:
316
- data["button"] = ButtonInteraction.parse(raw["button"])
317
- if "channel" in raw:
318
- data["channel"] = Channel.parse(raw["channel"])
319
- if "guild" in raw:
320
- data["guild"] = Guild.parse(raw["guild"])
321
- if "login" in raw:
322
- data["login"] = Login.parse(raw["login"])
323
- if "member" in raw:
324
- data["member"] = Member.parse(raw["member"])
325
- if "message" in raw:
326
- data["message"] = MessageObject.parse(raw["message"])
327
- if "operator" in raw:
328
- data["operator"] = User.parse(raw["operator"])
329
- if "role" in raw:
330
- data["role"] = Role.parse(raw["role"])
331
- if "user" in raw:
332
- data["user"] = User.parse(raw["user"])
333
- if "_type" in raw:
334
- data["_type"] = raw["_type"]
335
- if "_data" in raw:
336
- data["_data"] = raw["_data"]
337
- return cls(**data)
281
+ __converter__ = {
282
+ "timestamp": lambda ts: datetime.fromtimestamp(int(ts) / 1000),
283
+ "argv": ArgvInteraction.parse,
284
+ "button": ButtonInteraction.parse,
285
+ "channel": Channel.parse,
286
+ "guild": Guild.parse,
287
+ "login": Login.parse,
288
+ "member": Member.parse,
289
+ "message": MessageObject.parse,
290
+ "operator": User.parse,
291
+ "role": Role.parse,
292
+ "user": User.parse,
293
+ }
338
294
 
339
295
  def dump(self):
340
296
  res = {
@@ -380,8 +336,8 @@ class PageResult(ModelBase, Generic[T]):
380
336
  next: Optional[str] = None
381
337
 
382
338
  @classmethod
383
- def parse(cls, raw: dict, parser: Callable[[dict], T]) -> "PageResult[T]":
384
- data = [parser(item) for item in raw["data"]]
339
+ def parse(cls, raw: dict, parser: Optional[Callable[[dict], T]] = None) -> "PageResult[T]":
340
+ data = [(parser or ModelBase.parse)(item) for item in raw["data"]]
385
341
  return cls(data, raw.get("next"))
386
342
 
387
343
  def dump(self):
@@ -389,3 +345,25 @@ class PageResult(ModelBase, Generic[T]):
389
345
  if self.next:
390
346
  res["next"] = self.next
391
347
  return res
348
+
349
+
350
+ @dataclass
351
+ class PageDequeResult(PageResult[T]):
352
+ prev: Optional[str] = None
353
+
354
+ @classmethod
355
+ def parse(cls, raw: dict, parser: Optional[Callable[[dict], T]] = None) -> "PageDequeResult[T]":
356
+ data = [(parser or ModelBase.parse)(item) for item in raw["data"]]
357
+ return cls(data, raw.get("next"), raw.get("prev"))
358
+
359
+ def dump(self):
360
+ res: dict = {"data": [item.dump() for item in self.data]}
361
+ if self.next:
362
+ res["next"] = self.next
363
+ if self.prev:
364
+ res["prev"] = self.prev
365
+ return res
366
+
367
+
368
+ Direction: TypeAlias = Literal["before", "after", "around"]
369
+ Order: TypeAlias = Literal["asc", "desc"]
@@ -97,6 +97,8 @@ class Element:
97
97
  self.attrs["is"] = type
98
98
  else:
99
99
  self.type = type
100
+ if self.tag() == "text" and "content" in self.attrs:
101
+ self.attrs["text"] = self.attrs.pop("content")
100
102
 
101
103
  def tag(self):
102
104
  if self.type == "component":