satori-python-core 0.11.2__tar.gz → 0.11.3__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.2
3
+ Version: 0.11.3
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>
@@ -23,7 +23,7 @@ classifiers = [
23
23
  "Programming Language :: Python :: 3.12",
24
24
  "Operating System :: OS Independent",
25
25
  ]
26
- version = "0.11.2"
26
+ version = "0.11.3"
27
27
 
28
28
  [project.license]
29
29
  text = "MIT"
@@ -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.2"
42
+ __version__ = "0.11.3"
@@ -2,7 +2,7 @@ from base64 import b64encode
2
2
  from dataclasses import InitVar, dataclass, field, fields
3
3
  from io import BytesIO
4
4
  from pathlib import Path
5
- from typing import Any, Dict, List, Optional, TypeVar, Union, get_args
5
+ from typing import Any, ClassVar, Dict, List, Optional, Tuple, TypeVar, Union, get_args
6
6
  from typing_extensions import override
7
7
 
8
8
  from .parser import Element as RawElement
@@ -11,15 +11,23 @@ from .parser import escape, param_case
11
11
  TE = TypeVar("TE", bound="Element")
12
12
 
13
13
 
14
- @dataclass
14
+ @dataclass(repr=False)
15
15
  class Element:
16
- _attrs: Dict[str, Any] = field(init=False, default_factory=dict, repr=False)
16
+ _attrs: Dict[str, Any] = field(init=False, default_factory=dict)
17
17
  _children: List["Element"] = field(init=False, default_factory=list)
18
18
 
19
+ __names__: ClassVar[Tuple[str, ...]]
20
+
19
21
  @property
20
22
  def tag(self) -> str:
21
23
  return self.__class__.__name__.lower()
22
24
 
25
+ @classmethod
26
+ def unpack(cls, attrs: Dict[str, Any]):
27
+ obj = cls(**{k: v for k, v in attrs.items() if k in cls.__names__})
28
+ obj._attrs.update({k: v for k, v in attrs.items() if k not in cls.__names__})
29
+ return obj
30
+
23
31
  def __post_init__(self):
24
32
  for f in fields(self):
25
33
  if f.name in ("_attrs", "_children"):
@@ -62,6 +70,13 @@ class Element:
62
70
  def __str__(self) -> str:
63
71
  return self.dumps()
64
72
 
73
+ def __repr__(self) -> str:
74
+ args = {**self._attrs}
75
+ elem = f"{self.__class__.__name__}(" + ", ".join(f"{k}={v!r}" for k, v in args.items())
76
+ if self._children:
77
+ elem += ", { " + ", ".join(repr(i) for i in self._children) + " }"
78
+ return elem + ")"
79
+
65
80
  def __call__(self, *content: Union[str, "Element"]):
66
81
  self._children.extend(Text(i) if isinstance(i, str) else i for i in content)
67
82
  self.__post_call__()
@@ -69,8 +84,11 @@ class Element:
69
84
 
70
85
  def __post_call__(self): ...
71
86
 
87
+ def __getitem__(self, key: str) -> Any:
88
+ return self._attrs[key]
72
89
 
73
- @dataclass
90
+
91
+ @dataclass(repr=False)
74
92
  class Text(Element):
75
93
  """一段纯文本。"""
76
94
 
@@ -80,8 +98,10 @@ class Text(Element):
80
98
  def dumps(self, strip: bool = False) -> str:
81
99
  return self.text if strip else escape(self.text)
82
100
 
101
+ __names__ = ("text",)
83
102
 
84
- @dataclass
103
+
104
+ @dataclass(repr=False)
85
105
  class At(Element):
86
106
  """<at> 元素用于提及某个或某些用户。"""
87
107
 
@@ -101,8 +121,10 @@ class At(Element):
101
121
  def all(here: bool = False) -> "At":
102
122
  return At(type="here" if here else "all")
103
123
 
124
+ __names__ = ("id", "name", "role", "type")
104
125
 
105
- @dataclass
126
+
127
+ @dataclass(repr=False)
106
128
  class Sharp(Element):
107
129
  """<sharp> 元素用于提及某个频道。"""
108
130
 
@@ -114,7 +136,9 @@ class Sharp(Element):
114
136
  class Link(Element):
115
137
  """<a> 元素用于显示一个链接。"""
116
138
 
117
- url: str
139
+ href: str
140
+
141
+ __names__ = ("href",)
118
142
 
119
143
  def __post_call__(self):
120
144
  if not self._children:
@@ -128,18 +152,20 @@ class Link(Element):
128
152
  def tag(self) -> str:
129
153
  return "a"
130
154
 
131
- @override
132
- def attributes(self) -> str:
133
- return f' href="{escape(self.url)}"'
155
+ @property
156
+ def url(self) -> str:
157
+ return self.href
134
158
 
135
159
 
136
- @dataclass
160
+ @dataclass(repr=False)
137
161
  class Resource(Element):
138
162
  src: str
139
163
  extra: InitVar[Optional[Dict[str, Any]]] = None
140
164
  cache: Optional[bool] = None
141
165
  timeout: Optional[str] = None
142
166
 
167
+ __names__ = ("src",)
168
+
143
169
  @classmethod
144
170
  def of(
145
171
  cls,
@@ -173,44 +199,58 @@ class Resource(Element):
173
199
  self._attrs.update(extra)
174
200
 
175
201
 
176
- @dataclass
202
+ @dataclass(repr=False)
177
203
  class Image(Resource):
178
204
  """<img> 元素用于表示图片。"""
179
205
 
180
206
  width: Optional[int] = None
181
207
  height: Optional[int] = None
182
208
 
209
+ __names__ = ("src", "width", "height")
210
+
183
211
  @property
184
212
  @override
185
213
  def tag(self) -> str:
186
214
  return "img"
187
215
 
188
216
 
189
- @dataclass
217
+ @dataclass(repr=False)
190
218
  class Audio(Resource):
191
219
  """<audio> 元素用于表示语音。"""
192
220
 
193
- pass
221
+ duration: Optional[int] = None
222
+ poster: Optional[str] = None
194
223
 
224
+ __names__ = ("src", "duration", "poster")
195
225
 
196
- @dataclass
226
+
227
+ @dataclass(repr=False)
197
228
  class Video(Resource):
198
229
  """<video> 元素用于表示视频。"""
199
230
 
200
- pass
231
+ width: Optional[int] = None
232
+ height: Optional[int] = None
233
+ duration: Optional[int] = None
234
+ poster: Optional[str] = None
201
235
 
236
+ __names__ = ("src", "width", "height", "duration", "poster")
202
237
 
203
- @dataclass
238
+
239
+ @dataclass(repr=False)
204
240
  class File(Resource):
205
241
  """<file> 元素用于表示文件。"""
206
242
 
207
- pass
243
+ poster: Optional[str] = None
244
+
245
+ __names__ = ("src", "poster")
208
246
 
209
247
 
210
- @dataclass(init=False)
248
+ @dataclass(init=False, repr=False)
211
249
  class Style(Element):
212
250
  """样式元素的基类。"""
213
251
 
252
+ __names__ = ()
253
+
214
254
  def __init__(self, *text: Union[str, Text, "Style"]):
215
255
  super().__init__()
216
256
  self.__call__(*text)
@@ -311,13 +351,15 @@ class Paragraph(Style):
311
351
  return "p"
312
352
 
313
353
 
314
- @dataclass(init=False)
354
+ @dataclass(init=False, repr=False)
315
355
  class Message(Element):
316
356
  """<message> 元素的基本用法是表示一条消息。
317
357
 
318
358
  子元素对应于消息的内容。如果其没有子元素,则消息不会被发送。
319
359
  """
320
360
 
361
+ __names__ = ("id", "forward")
362
+
321
363
  id: Optional[str]
322
364
  forward: Optional[bool]
323
365
 
@@ -342,16 +384,18 @@ class Quote(Message):
342
384
  pass
343
385
 
344
386
 
345
- @dataclass
387
+ @dataclass(repr=False)
346
388
  class Author(Element):
347
389
  """<author> 元素用于表示消息的作者。它的子元素会被渲染为作者的名字。"""
348
390
 
349
391
  id: str
350
- nickname: Optional[str] = None
392
+ name: Optional[str] = None
351
393
  avatar: Optional[str] = None
352
394
 
395
+ __names__ = ("id", "name", "avatar")
353
396
 
354
- @dataclass
397
+
398
+ @dataclass(repr=False)
355
399
  class Button(Element):
356
400
  """<button> 元素用于表示一个按钮。它的子元素会被渲染为按钮的文本。"""
357
401
 
@@ -361,6 +405,8 @@ class Button(Element):
361
405
  text: Optional[str] = None
362
406
  theme: Optional[str] = None
363
407
 
408
+ __names__ = ("type", "id", "href", "text", "theme")
409
+
364
410
  @classmethod
365
411
  def action(cls, button_id: str, theme: Optional[str] = None):
366
412
  return Button("action", id=button_id, theme=theme)
@@ -394,12 +440,14 @@ class Button(Element):
394
440
  raise ValueError("Button can only have one Text child")
395
441
 
396
442
 
397
- @dataclass(init=False)
443
+ @dataclass(init=False, repr=False)
398
444
  class Custom(Element):
399
445
  """自定义元素用于构造标准元素以外的元素"""
400
446
 
401
447
  type: str
402
448
 
449
+ __names__ = ()
450
+
403
451
  def __init__(
404
452
  self,
405
453
  type: str,
@@ -423,12 +471,14 @@ class Custom(Element):
423
471
  return self.type
424
472
 
425
473
 
426
- @dataclass
474
+ @dataclass(repr=False)
427
475
  class Raw(Element):
428
476
  """Raw 元素表示原始文本"""
429
477
 
430
478
  content: str
431
479
 
480
+ __names__ = ()
481
+
432
482
  @override
433
483
  def dumps(self, strip: bool = False):
434
484
  return self.content if strip else escape(self.content)
@@ -479,25 +529,25 @@ def transform(elements: List[RawElement]) -> List[Element]:
479
529
  tag = elem.tag()
480
530
  if tag in ELEMENT_TYPE_MAP:
481
531
  seg_cls = ELEMENT_TYPE_MAP[tag]
482
- msg.append(seg_cls(**elem.attrs))
532
+ msg.append(seg_cls.unpack(elem.attrs))
483
533
  elif tag in ("a", "link"):
484
- link = Link(elem.attrs["href"])
534
+ link = Link.unpack(elem.attrs)
485
535
  if elem.children:
486
536
  link(*transform(elem.children))
487
537
  msg.append(link)
488
538
  elif tag == "button":
489
- button = Button(**elem.attrs)
539
+ button = Button.unpack(elem.attrs)
490
540
  if elem.children:
491
541
  button(*transform(elem.children))
492
542
  elif tag in STYLE_TYPE_MAP:
493
543
  seg_cls = STYLE_TYPE_MAP[tag]
494
- msg.append(seg_cls()(*transform(elem.children)))
544
+ msg.append(seg_cls.unpack(elem.attrs)(*transform(elem.children)))
495
545
  elif tag in ("br", "newline"):
496
546
  msg.append(Br())
497
547
  elif tag == "message":
498
- msg.append(Message(**elem.attrs)(*transform(elem.children)))
548
+ msg.append(Message.unpack(elem.attrs)(*transform(elem.children)))
499
549
  elif tag == "quote":
500
- msg.append(Quote(**elem.attrs)(*transform(elem.children)))
550
+ msg.append(Quote.unpack(elem.attrs)(*transform(elem.children)))
501
551
  else:
502
552
  msg.append(Custom(elem.type, elem.attrs)(*transform(elem.children)))
503
553
  return msg