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.
- {satori_python_core-0.11.2 → satori_python_core-0.11.3}/PKG-INFO +1 -1
- {satori_python_core-0.11.2 → satori_python_core-0.11.3}/pyproject.toml +1 -1
- {satori_python_core-0.11.2 → satori_python_core-0.11.3}/src/satori/__init__.py +1 -1
- {satori_python_core-0.11.2 → satori_python_core-0.11.3}/src/satori/element.py +81 -31
- {satori_python_core-0.11.2 → satori_python_core-0.11.3}/.mina/core.toml +0 -0
- {satori_python_core-0.11.2 → satori_python_core-0.11.3}/LICENSE +0 -0
- {satori_python_core-0.11.2 → satori_python_core-0.11.3}/README.md +0 -0
- {satori_python_core-0.11.2 → satori_python_core-0.11.3}/src/satori/config.py +0 -0
- {satori_python_core-0.11.2 → satori_python_core-0.11.3}/src/satori/const.py +0 -0
- {satori_python_core-0.11.2 → satori_python_core-0.11.3}/src/satori/event.py +0 -0
- {satori_python_core-0.11.2 → satori_python_core-0.11.3}/src/satori/exception.py +0 -0
- {satori_python_core-0.11.2 → satori_python_core-0.11.3}/src/satori/model.py +0 -0
- {satori_python_core-0.11.2 → satori_python_core-0.11.3}/src/satori/parser.py +0 -0
|
@@ -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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
@
|
|
132
|
-
def
|
|
133
|
-
return
|
|
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
|
-
|
|
221
|
+
duration: Optional[int] = None
|
|
222
|
+
poster: Optional[str] = None
|
|
194
223
|
|
|
224
|
+
__names__ = ("src", "duration", "poster")
|
|
195
225
|
|
|
196
|
-
|
|
226
|
+
|
|
227
|
+
@dataclass(repr=False)
|
|
197
228
|
class Video(Resource):
|
|
198
229
|
"""<video> 元素用于表示视频。"""
|
|
199
230
|
|
|
200
|
-
|
|
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
|
-
|
|
238
|
+
|
|
239
|
+
@dataclass(repr=False)
|
|
204
240
|
class File(Resource):
|
|
205
241
|
"""<file> 元素用于表示文件。"""
|
|
206
242
|
|
|
207
|
-
|
|
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
|
-
|
|
392
|
+
name: Optional[str] = None
|
|
351
393
|
avatar: Optional[str] = None
|
|
352
394
|
|
|
395
|
+
__names__ = ("id", "name", "avatar")
|
|
353
396
|
|
|
354
|
-
|
|
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(
|
|
532
|
+
msg.append(seg_cls.unpack(elem.attrs))
|
|
483
533
|
elif tag in ("a", "link"):
|
|
484
|
-
link = Link(elem.attrs
|
|
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(
|
|
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(
|
|
548
|
+
msg.append(Message.unpack(elem.attrs)(*transform(elem.children)))
|
|
499
549
|
elif tag == "quote":
|
|
500
|
-
msg.append(Quote(
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|