satori-python-core 0.11.2__tar.gz → 0.11.4__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.4}/PKG-INFO +1 -1
- {satori_python_core-0.11.2 → satori_python_core-0.11.4}/pyproject.toml +1 -1
- {satori_python_core-0.11.2 → satori_python_core-0.11.4}/src/satori/__init__.py +1 -1
- {satori_python_core-0.11.2 → satori_python_core-0.11.4}/src/satori/element.py +89 -31
- {satori_python_core-0.11.2 → satori_python_core-0.11.4}/.mina/core.toml +0 -0
- {satori_python_core-0.11.2 → satori_python_core-0.11.4}/LICENSE +0 -0
- {satori_python_core-0.11.2 → satori_python_core-0.11.4}/README.md +0 -0
- {satori_python_core-0.11.2 → satori_python_core-0.11.4}/src/satori/config.py +0 -0
- {satori_python_core-0.11.2 → satori_python_core-0.11.4}/src/satori/const.py +0 -0
- {satori_python_core-0.11.2 → satori_python_core-0.11.4}/src/satori/event.py +0 -0
- {satori_python_core-0.11.2 → satori_python_core-0.11.4}/src/satori/exception.py +0 -0
- {satori_python_core-0.11.2 → satori_python_core-0.11.4}/src/satori/model.py +0 -0
- {satori_python_core-0.11.2 → satori_python_core-0.11.4}/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,21 @@ 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
|
|
163
|
+
title: Optional[str] = None
|
|
139
164
|
extra: InitVar[Optional[Dict[str, Any]]] = None
|
|
140
165
|
cache: Optional[bool] = None
|
|
141
166
|
timeout: Optional[str] = None
|
|
142
167
|
|
|
168
|
+
__names__ = ("src", "title")
|
|
169
|
+
|
|
143
170
|
@classmethod
|
|
144
171
|
def of(
|
|
145
172
|
cls,
|
|
@@ -147,9 +174,12 @@ class Resource(Element):
|
|
|
147
174
|
path: Optional[Union[str, Path]] = None,
|
|
148
175
|
raw: Optional[Union[bytes, BytesIO]] = None,
|
|
149
176
|
mime: Optional[str] = None,
|
|
177
|
+
name: Optional[str] = None,
|
|
178
|
+
poster: Optional[str] = None,
|
|
150
179
|
extra: Optional[Dict[str, Any]] = None,
|
|
151
180
|
cache: Optional[bool] = None,
|
|
152
181
|
timeout: Optional[str] = None,
|
|
182
|
+
**kwargs,
|
|
153
183
|
):
|
|
154
184
|
data: Dict[str, Any] = {"extra": extra}
|
|
155
185
|
if url is not None:
|
|
@@ -161,6 +191,10 @@ class Resource(Element):
|
|
|
161
191
|
data = {"src": f"data:{mime};base64,{b64encode(bd).decode()}"}
|
|
162
192
|
else:
|
|
163
193
|
raise ValueError(f"{cls} need at least one of url, path and raw")
|
|
194
|
+
if name is not None:
|
|
195
|
+
data["title"] = name
|
|
196
|
+
if poster is not None and cls in (Video, Audio, File):
|
|
197
|
+
data["poster"] = poster
|
|
164
198
|
if cache is not None:
|
|
165
199
|
data["cache"] = cache
|
|
166
200
|
if timeout is not None:
|
|
@@ -173,44 +207,58 @@ class Resource(Element):
|
|
|
173
207
|
self._attrs.update(extra)
|
|
174
208
|
|
|
175
209
|
|
|
176
|
-
@dataclass
|
|
210
|
+
@dataclass(repr=False)
|
|
177
211
|
class Image(Resource):
|
|
178
212
|
"""<img> 元素用于表示图片。"""
|
|
179
213
|
|
|
180
214
|
width: Optional[int] = None
|
|
181
215
|
height: Optional[int] = None
|
|
182
216
|
|
|
217
|
+
__names__ = ("src", "title", "width", "height")
|
|
218
|
+
|
|
183
219
|
@property
|
|
184
220
|
@override
|
|
185
221
|
def tag(self) -> str:
|
|
186
222
|
return "img"
|
|
187
223
|
|
|
188
224
|
|
|
189
|
-
@dataclass
|
|
225
|
+
@dataclass(repr=False)
|
|
190
226
|
class Audio(Resource):
|
|
191
227
|
"""<audio> 元素用于表示语音。"""
|
|
192
228
|
|
|
193
|
-
|
|
229
|
+
duration: Optional[int] = None
|
|
230
|
+
poster: Optional[str] = None
|
|
194
231
|
|
|
232
|
+
__names__ = ("src", "title", "duration", "poster")
|
|
195
233
|
|
|
196
|
-
|
|
234
|
+
|
|
235
|
+
@dataclass(repr=False)
|
|
197
236
|
class Video(Resource):
|
|
198
237
|
"""<video> 元素用于表示视频。"""
|
|
199
238
|
|
|
200
|
-
|
|
239
|
+
width: Optional[int] = None
|
|
240
|
+
height: Optional[int] = None
|
|
241
|
+
duration: Optional[int] = None
|
|
242
|
+
poster: Optional[str] = None
|
|
201
243
|
|
|
244
|
+
__names__ = ("src", "title", "width", "height", "duration", "poster")
|
|
202
245
|
|
|
203
|
-
|
|
246
|
+
|
|
247
|
+
@dataclass(repr=False)
|
|
204
248
|
class File(Resource):
|
|
205
249
|
"""<file> 元素用于表示文件。"""
|
|
206
250
|
|
|
207
|
-
|
|
251
|
+
poster: Optional[str] = None
|
|
252
|
+
|
|
253
|
+
__names__ = ("src", "title", "poster")
|
|
208
254
|
|
|
209
255
|
|
|
210
|
-
@dataclass(init=False)
|
|
256
|
+
@dataclass(init=False, repr=False)
|
|
211
257
|
class Style(Element):
|
|
212
258
|
"""样式元素的基类。"""
|
|
213
259
|
|
|
260
|
+
__names__ = ()
|
|
261
|
+
|
|
214
262
|
def __init__(self, *text: Union[str, Text, "Style"]):
|
|
215
263
|
super().__init__()
|
|
216
264
|
self.__call__(*text)
|
|
@@ -311,13 +359,15 @@ class Paragraph(Style):
|
|
|
311
359
|
return "p"
|
|
312
360
|
|
|
313
361
|
|
|
314
|
-
@dataclass(init=False)
|
|
362
|
+
@dataclass(init=False, repr=False)
|
|
315
363
|
class Message(Element):
|
|
316
364
|
"""<message> 元素的基本用法是表示一条消息。
|
|
317
365
|
|
|
318
366
|
子元素对应于消息的内容。如果其没有子元素,则消息不会被发送。
|
|
319
367
|
"""
|
|
320
368
|
|
|
369
|
+
__names__ = ("id", "forward")
|
|
370
|
+
|
|
321
371
|
id: Optional[str]
|
|
322
372
|
forward: Optional[bool]
|
|
323
373
|
|
|
@@ -342,16 +392,18 @@ class Quote(Message):
|
|
|
342
392
|
pass
|
|
343
393
|
|
|
344
394
|
|
|
345
|
-
@dataclass
|
|
395
|
+
@dataclass(repr=False)
|
|
346
396
|
class Author(Element):
|
|
347
397
|
"""<author> 元素用于表示消息的作者。它的子元素会被渲染为作者的名字。"""
|
|
348
398
|
|
|
349
399
|
id: str
|
|
350
|
-
|
|
400
|
+
name: Optional[str] = None
|
|
351
401
|
avatar: Optional[str] = None
|
|
352
402
|
|
|
403
|
+
__names__ = ("id", "name", "avatar")
|
|
353
404
|
|
|
354
|
-
|
|
405
|
+
|
|
406
|
+
@dataclass(repr=False)
|
|
355
407
|
class Button(Element):
|
|
356
408
|
"""<button> 元素用于表示一个按钮。它的子元素会被渲染为按钮的文本。"""
|
|
357
409
|
|
|
@@ -361,6 +413,8 @@ class Button(Element):
|
|
|
361
413
|
text: Optional[str] = None
|
|
362
414
|
theme: Optional[str] = None
|
|
363
415
|
|
|
416
|
+
__names__ = ("type", "id", "href", "text", "theme")
|
|
417
|
+
|
|
364
418
|
@classmethod
|
|
365
419
|
def action(cls, button_id: str, theme: Optional[str] = None):
|
|
366
420
|
return Button("action", id=button_id, theme=theme)
|
|
@@ -394,12 +448,14 @@ class Button(Element):
|
|
|
394
448
|
raise ValueError("Button can only have one Text child")
|
|
395
449
|
|
|
396
450
|
|
|
397
|
-
@dataclass(init=False)
|
|
451
|
+
@dataclass(init=False, repr=False)
|
|
398
452
|
class Custom(Element):
|
|
399
453
|
"""自定义元素用于构造标准元素以外的元素"""
|
|
400
454
|
|
|
401
455
|
type: str
|
|
402
456
|
|
|
457
|
+
__names__ = ()
|
|
458
|
+
|
|
403
459
|
def __init__(
|
|
404
460
|
self,
|
|
405
461
|
type: str,
|
|
@@ -423,12 +479,14 @@ class Custom(Element):
|
|
|
423
479
|
return self.type
|
|
424
480
|
|
|
425
481
|
|
|
426
|
-
@dataclass
|
|
482
|
+
@dataclass(repr=False)
|
|
427
483
|
class Raw(Element):
|
|
428
484
|
"""Raw 元素表示原始文本"""
|
|
429
485
|
|
|
430
486
|
content: str
|
|
431
487
|
|
|
488
|
+
__names__ = ()
|
|
489
|
+
|
|
432
490
|
@override
|
|
433
491
|
def dumps(self, strip: bool = False):
|
|
434
492
|
return self.content if strip else escape(self.content)
|
|
@@ -479,25 +537,25 @@ def transform(elements: List[RawElement]) -> List[Element]:
|
|
|
479
537
|
tag = elem.tag()
|
|
480
538
|
if tag in ELEMENT_TYPE_MAP:
|
|
481
539
|
seg_cls = ELEMENT_TYPE_MAP[tag]
|
|
482
|
-
msg.append(seg_cls(
|
|
540
|
+
msg.append(seg_cls.unpack(elem.attrs))
|
|
483
541
|
elif tag in ("a", "link"):
|
|
484
|
-
link = Link(elem.attrs
|
|
542
|
+
link = Link.unpack(elem.attrs)
|
|
485
543
|
if elem.children:
|
|
486
544
|
link(*transform(elem.children))
|
|
487
545
|
msg.append(link)
|
|
488
546
|
elif tag == "button":
|
|
489
|
-
button = Button(
|
|
547
|
+
button = Button.unpack(elem.attrs)
|
|
490
548
|
if elem.children:
|
|
491
549
|
button(*transform(elem.children))
|
|
492
550
|
elif tag in STYLE_TYPE_MAP:
|
|
493
551
|
seg_cls = STYLE_TYPE_MAP[tag]
|
|
494
|
-
msg.append(seg_cls()(*transform(elem.children)))
|
|
552
|
+
msg.append(seg_cls.unpack(elem.attrs)(*transform(elem.children)))
|
|
495
553
|
elif tag in ("br", "newline"):
|
|
496
554
|
msg.append(Br())
|
|
497
555
|
elif tag == "message":
|
|
498
|
-
msg.append(Message(
|
|
556
|
+
msg.append(Message.unpack(elem.attrs)(*transform(elem.children)))
|
|
499
557
|
elif tag == "quote":
|
|
500
|
-
msg.append(Quote(
|
|
558
|
+
msg.append(Quote.unpack(elem.attrs)(*transform(elem.children)))
|
|
501
559
|
else:
|
|
502
560
|
msg.append(Custom(elem.type, elem.attrs)(*transform(elem.children)))
|
|
503
561
|
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
|