satori-python 0.18.0__tar.gz → 1.3.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.
- {satori_python-0.18.0 → satori_python-1.3.0}/PKG-INFO +8 -8
- {satori_python-0.18.0 → satori_python-1.3.0}/README.md +7 -7
- {satori_python-0.18.0 → satori_python-1.3.0}/pyproject.toml +1 -1
- {satori_python-0.18.0 → satori_python-1.3.0}/src/satori/__init__.py +4 -1
- {satori_python-0.18.0 → satori_python-1.3.0}/src/satori/client/__init__.py +9 -0
- {satori_python-0.18.0 → satori_python-1.3.0}/src/satori/client/account.py +2 -1
- {satori_python-0.18.0 → satori_python-1.3.0}/src/satori/client/account.pyi +30 -16
- {satori_python-0.18.0 → satori_python-1.3.0}/src/satori/client/config.py +7 -3
- {satori_python-0.18.0 → satori_python-1.3.0}/src/satori/client/protocol.py +42 -28
- {satori_python-0.18.0 → satori_python-1.3.0}/src/satori/const.py +5 -1
- {satori_python-0.18.0 → satori_python-1.3.0}/src/satori/element.py +18 -3
- {satori_python-0.18.0 → satori_python-1.3.0}/src/satori/event.py +6 -0
- {satori_python-0.18.0 → satori_python-1.3.0}/src/satori/model.py +55 -9
- {satori_python-0.18.0 → satori_python-1.3.0}/src/satori/server/__init__.py +4 -0
- {satori_python-0.18.0 → satori_python-1.3.0}/src/satori/server/route.py +9 -7
- {satori_python-0.18.0 → satori_python-1.3.0}/LICENSE +0 -0
- {satori_python-0.18.0 → satori_python-1.3.0}/src/satori/_vendor/fleep.py +0 -0
- {satori_python-0.18.0 → satori_python-1.3.0}/src/satori/client/network/__init__.py +0 -0
- {satori_python-0.18.0 → satori_python-1.3.0}/src/satori/client/network/base.py +0 -0
- {satori_python-0.18.0 → satori_python-1.3.0}/src/satori/client/network/util.py +0 -0
- {satori_python-0.18.0 → satori_python-1.3.0}/src/satori/client/network/webhook.py +0 -0
- {satori_python-0.18.0 → satori_python-1.3.0}/src/satori/client/network/websocket.py +0 -0
- {satori_python-0.18.0 → satori_python-1.3.0}/src/satori/exception.py +0 -0
- {satori_python-0.18.0 → satori_python-1.3.0}/src/satori/parser.py +0 -0
- {satori_python-0.18.0 → satori_python-1.3.0}/src/satori/server/adapter.py +0 -0
- {satori_python-0.18.0 → satori_python-1.3.0}/src/satori/server/connection.py +0 -0
- {satori_python-0.18.0 → satori_python-1.3.0}/src/satori/server/formdata.py +0 -0
- {satori_python-0.18.0 → satori_python-1.3.0}/src/satori/server/model.py +0 -0
- {satori_python-0.18.0 → satori_python-1.3.0}/src/satori/server/utils.py +0 -0
- {satori_python-0.18.0 → satori_python-1.3.0}/src/satori/utils.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: satori-python
|
|
3
|
-
Version:
|
|
3
|
+
Version: 1.3.0
|
|
4
4
|
Summary: Satori Protocol SDK for python
|
|
5
5
|
Home-page: https://github.com/RF-Tar-Railt/satori-python
|
|
6
6
|
Author-Email: RF-Tar-Railt <rf_tar_railt@qq.com>
|
|
@@ -81,13 +81,13 @@ pip install satori-python-server
|
|
|
81
81
|
|
|
82
82
|
### 官方适配器
|
|
83
83
|
|
|
84
|
-
| 适配器 | 安装 | 路径
|
|
85
|
-
|
|
86
|
-
| Satori | `pip install satori-python-adapter-satori` | satori.adapters.satori
|
|
87
|
-
| OneBot V11 | `pip install satori-python-adapter-onebot11` | satori.adapters.onebot11.forward, satori.adapters.onebot11.reverse
|
|
88
|
-
| Console | `pip install satori-python-adapter-console` | satori.adapters.console
|
|
89
|
-
| Milky | `pip install satori-python-adapter-milky` | satori.adapters.milky.main, satori.adapters.milky.webhook
|
|
90
|
-
| QQ | `pip install satori-python-adapter-qq` | satori.adapters.
|
|
84
|
+
| 适配器 | 安装 | 路径 |
|
|
85
|
+
|------------|----------------------------------------------|--------------------------------------------------------------------------------------|
|
|
86
|
+
| Satori | `pip install satori-python-adapter-satori` | satori.adapters.satori |
|
|
87
|
+
| OneBot V11 | `pip install satori-python-adapter-onebot11` | satori.adapters.onebot11.forward, satori.adapters.onebot11.reverse |
|
|
88
|
+
| Console | `pip install satori-python-adapter-console` | satori.adapters.console |
|
|
89
|
+
| Milky | `pip install satori-python-adapter-milky` | satori.adapters.milky.main, satori.adapters.milky.webhook, satori.adapters.milky.sse |
|
|
90
|
+
| QQ | `pip install satori-python-adapter-qq` | satori.adapters.qq.main, satori.adapters.qq.websocket |
|
|
91
91
|
|
|
92
92
|
### 社区适配器
|
|
93
93
|
|
|
@@ -48,13 +48,13 @@ pip install satori-python-server
|
|
|
48
48
|
|
|
49
49
|
### 官方适配器
|
|
50
50
|
|
|
51
|
-
| 适配器 | 安装 | 路径
|
|
52
|
-
|
|
53
|
-
| Satori | `pip install satori-python-adapter-satori` | satori.adapters.satori
|
|
54
|
-
| OneBot V11 | `pip install satori-python-adapter-onebot11` | satori.adapters.onebot11.forward, satori.adapters.onebot11.reverse
|
|
55
|
-
| Console | `pip install satori-python-adapter-console` | satori.adapters.console
|
|
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.
|
|
51
|
+
| 适配器 | 安装 | 路径 |
|
|
52
|
+
|------------|----------------------------------------------|--------------------------------------------------------------------------------------|
|
|
53
|
+
| Satori | `pip install satori-python-adapter-satori` | satori.adapters.satori |
|
|
54
|
+
| OneBot V11 | `pip install satori-python-adapter-onebot11` | satori.adapters.onebot11.forward, satori.adapters.onebot11.reverse |
|
|
55
|
+
| Console | `pip install satori-python-adapter-console` | satori.adapters.console |
|
|
56
|
+
| Milky | `pip install satori-python-adapter-milky` | satori.adapters.milky.main, satori.adapters.milky.webhook, satori.adapters.milky.sse |
|
|
57
|
+
| QQ | `pip install satori-python-adapter-qq` | satori.adapters.qq.main, satori.adapters.qq.websocket |
|
|
58
58
|
|
|
59
59
|
### 社区适配器
|
|
60
60
|
|
|
@@ -8,6 +8,7 @@ from .element import Button as Button
|
|
|
8
8
|
from .element import Code as Code
|
|
9
9
|
from .element import E as E
|
|
10
10
|
from .element import Element as Element
|
|
11
|
+
from .element import Emoji as Emoji
|
|
11
12
|
from .element import File as File
|
|
12
13
|
from .element import Image as Image
|
|
13
14
|
from .element import Italic as Italic
|
|
@@ -30,7 +31,9 @@ from .model import ArgvInteraction as ArgvInteraction
|
|
|
30
31
|
from .model import ButtonInteraction as ButtonInteraction
|
|
31
32
|
from .model import Channel as Channel
|
|
32
33
|
from .model import ChannelType as ChannelType
|
|
34
|
+
from .model import EmojiObject as EmojiObject
|
|
33
35
|
from .model import Event as Event
|
|
36
|
+
from .model import Friend as Friend
|
|
34
37
|
from .model import Guild as Guild
|
|
35
38
|
from .model import Login as Login
|
|
36
39
|
from .model import LoginStatus as LoginStatus
|
|
@@ -42,7 +45,7 @@ from .model import Role as Role
|
|
|
42
45
|
from .model import Upload as Upload
|
|
43
46
|
from .model import User as User
|
|
44
47
|
|
|
45
|
-
__version__ = "
|
|
48
|
+
__version__ = "1.3.0"
|
|
46
49
|
|
|
47
50
|
|
|
48
51
|
MessageReceipt = MessageObject
|
|
@@ -152,6 +152,15 @@ class App(Service):
|
|
|
152
152
|
Callable[[Account, events.GuildRoleEvent], Awaitable[Any]],
|
|
153
153
|
]: ...
|
|
154
154
|
|
|
155
|
+
@overload
|
|
156
|
+
def register_on(
|
|
157
|
+
self,
|
|
158
|
+
event_type: Literal[EventType.GUILD_EMOJI_ADDED, EventType.GUILD_EMOJI_REMOVED, EventType.GUILD_EMOJI_UPDATED],
|
|
159
|
+
) -> Callable[
|
|
160
|
+
[Callable[[Account, events.GuildEmojiEvent], Awaitable[Any]]],
|
|
161
|
+
Callable[[Account, events.GuildEmojiEvent], Awaitable[Any]],
|
|
162
|
+
]: ...
|
|
163
|
+
|
|
155
164
|
@overload
|
|
156
165
|
def register_on(
|
|
157
166
|
self, event_type: Literal[EventType.LOGIN_ADDED, EventType.LOGIN_REMOVED, EventType.LOGIN_UPDATED]
|
|
@@ -20,13 +20,14 @@ class ApiInfo:
|
|
|
20
20
|
port: int = 5140
|
|
21
21
|
path: str = ""
|
|
22
22
|
token: str | None = None
|
|
23
|
+
secure: bool = False
|
|
23
24
|
timeout: float | None = None
|
|
24
25
|
api_base: URL = field(init=False)
|
|
25
26
|
|
|
26
27
|
def __post_init__(self):
|
|
27
28
|
if self.path and not self.path.startswith("/"):
|
|
28
29
|
self.path = f"/{self.path}"
|
|
29
|
-
self.api_base = URL(f"http://{self.host}:{self.port}{self.path}") / "v1"
|
|
30
|
+
self.api_base = URL(f"http{'s' if self.secure else ''}://{self.host}:{self.port}{self.path}") / "v1"
|
|
30
31
|
|
|
31
32
|
|
|
32
33
|
class Account(Generic[TP]):
|
|
@@ -10,6 +10,7 @@ from satori.model import (
|
|
|
10
10
|
Channel,
|
|
11
11
|
Direction,
|
|
12
12
|
Event,
|
|
13
|
+
Friend,
|
|
13
14
|
Guild,
|
|
14
15
|
IterablePageResult,
|
|
15
16
|
Login,
|
|
@@ -40,6 +41,7 @@ class ApiInfo(Api):
|
|
|
40
41
|
port: int = 5140,
|
|
41
42
|
path: str = "",
|
|
42
43
|
token: str | None = None,
|
|
44
|
+
secure: bool = False,
|
|
43
45
|
timeout: float | None = None,
|
|
44
46
|
):
|
|
45
47
|
self.api_base: URL = ...
|
|
@@ -450,19 +452,21 @@ class Account(Generic[TP]):
|
|
|
450
452
|
None: 该方法无返回值
|
|
451
453
|
"""
|
|
452
454
|
|
|
453
|
-
async def reaction_create(self, channel_id: str, message_id: str,
|
|
455
|
+
async def reaction_create(self, channel_id: str, message_id: str, emoji_id: str) -> None:
|
|
454
456
|
"""向特定消息添加表态。
|
|
455
457
|
|
|
456
458
|
Args:
|
|
457
459
|
channel_id (str): 频道 ID
|
|
458
460
|
message_id (str): 消息 ID
|
|
459
|
-
|
|
461
|
+
emoji_id (str): 表情 ID
|
|
460
462
|
|
|
461
463
|
Returns:
|
|
462
464
|
None: 该方法无返回值
|
|
463
465
|
"""
|
|
464
466
|
|
|
465
|
-
async def reaction_delete(
|
|
467
|
+
async def reaction_delete(
|
|
468
|
+
self, channel_id: str, message_id: str, emoji_id: str, user_id: str | None = None
|
|
469
|
+
) -> None:
|
|
466
470
|
"""从特定消息删除某个用户添加的特定表态。
|
|
467
471
|
|
|
468
472
|
如果没有传入用户 ID 则表示删除自己的表态。
|
|
@@ -470,14 +474,14 @@ class Account(Generic[TP]):
|
|
|
470
474
|
Args:
|
|
471
475
|
channel_id (str): 频道 ID
|
|
472
476
|
message_id (str): 消息 ID
|
|
473
|
-
|
|
477
|
+
emoji_id (str): 表情 ID
|
|
474
478
|
user_id (str | None, optional): 用户 ID,默认为 None
|
|
475
479
|
|
|
476
480
|
Returns:
|
|
477
481
|
None: 该方法无返回值
|
|
478
482
|
"""
|
|
479
483
|
|
|
480
|
-
async def reaction_clear(self, channel_id: str, message_id: str,
|
|
484
|
+
async def reaction_clear(self, channel_id: str, message_id: str, emoji_id: str | None = None) -> None:
|
|
481
485
|
"""从特定消息清除某个特定表态。
|
|
482
486
|
|
|
483
487
|
如果没有传入表态名称则表示清除所有表态。
|
|
@@ -485,21 +489,21 @@ class Account(Generic[TP]):
|
|
|
485
489
|
Args:
|
|
486
490
|
channel_id (str): 频道 ID
|
|
487
491
|
message_id (str): 消息 ID
|
|
488
|
-
|
|
492
|
+
emoji_id (str | None, optional): 表情 ID,默认为 None
|
|
489
493
|
|
|
490
494
|
Returns:
|
|
491
495
|
None: 该方法无返回值
|
|
492
496
|
"""
|
|
493
497
|
|
|
494
498
|
def reaction_list(
|
|
495
|
-
self, channel_id: str, message_id: str,
|
|
499
|
+
self, channel_id: str, message_id: str, emoji_id: str, next_token: str | None = None
|
|
496
500
|
) -> IterablePageResult[User]:
|
|
497
501
|
"""获取添加特定消息的特定表态的用户列表。返回一个 User 的分页列表。
|
|
498
502
|
|
|
499
503
|
Args:
|
|
500
504
|
channel_id (str): 频道 ID
|
|
501
505
|
message_id (str): 消息 ID
|
|
502
|
-
|
|
506
|
+
emoji_id (str): 表情 ID
|
|
503
507
|
next_token (str | None, optional): 分页令牌,默认为空
|
|
504
508
|
|
|
505
509
|
Returns:
|
|
@@ -513,24 +517,24 @@ class Account(Generic[TP]):
|
|
|
513
517
|
Login: `Login` 对象
|
|
514
518
|
"""
|
|
515
519
|
|
|
516
|
-
|
|
517
|
-
"""
|
|
520
|
+
def friend_list(self, next_token: str | None = None) -> IterablePageResult[Friend]:
|
|
521
|
+
"""获取好友列表。返回一个 Friend 的分页列表。
|
|
518
522
|
|
|
519
523
|
Args:
|
|
520
|
-
|
|
524
|
+
next_token (str | None, optional): 分页令牌,默认为空
|
|
521
525
|
|
|
522
526
|
Returns:
|
|
523
|
-
|
|
527
|
+
IterablePageResult[Friend]: `Friend` 的分页列表
|
|
524
528
|
"""
|
|
525
529
|
|
|
526
|
-
def
|
|
527
|
-
"""
|
|
530
|
+
async def friend_delete(self, user_id: str) -> None:
|
|
531
|
+
"""删除好友。
|
|
528
532
|
|
|
529
533
|
Args:
|
|
530
|
-
|
|
534
|
+
user_id (str): 用户 ID
|
|
531
535
|
|
|
532
536
|
Returns:
|
|
533
|
-
|
|
537
|
+
None: 该方法无返回值
|
|
534
538
|
"""
|
|
535
539
|
|
|
536
540
|
async def friend_approve(self, request_id: str, approve: bool, comment: str) -> None:
|
|
@@ -545,6 +549,16 @@ class Account(Generic[TP]):
|
|
|
545
549
|
None: 该方法无返回值
|
|
546
550
|
"""
|
|
547
551
|
|
|
552
|
+
async def user_get(self, user_id: str) -> User:
|
|
553
|
+
"""获取用户信息。返回一个 `User` 对象。
|
|
554
|
+
|
|
555
|
+
Args:
|
|
556
|
+
user_id (str): 用户 ID
|
|
557
|
+
|
|
558
|
+
Returns:
|
|
559
|
+
User: `User` 对象
|
|
560
|
+
"""
|
|
561
|
+
|
|
548
562
|
async def internal(self, action: str, method: str = "POST", **kwargs) -> Any:
|
|
549
563
|
"""内部接口调用。
|
|
550
564
|
|
|
@@ -23,6 +23,7 @@ class WebsocketsInfo(Config):
|
|
|
23
23
|
port: int = 5140
|
|
24
24
|
path: str = ""
|
|
25
25
|
token: str | None = None
|
|
26
|
+
secure: bool = False
|
|
26
27
|
timeout: float | None = None
|
|
27
28
|
identity: str = None # type: ignore
|
|
28
29
|
api_base: URL = None # type: ignore
|
|
@@ -31,11 +32,11 @@ class WebsocketsInfo(Config):
|
|
|
31
32
|
if self.path and not self.path.startswith("/"):
|
|
32
33
|
self.path = f"/{self.path}"
|
|
33
34
|
self.identity = f"{self.host}:{self.port}"
|
|
34
|
-
self.api_base = URL(f"http://{self.host}:{self.port}{self.path}") / "v1"
|
|
35
|
+
self.api_base = URL(f"http{'s' if self.secure else ''}://{self.host}:{self.port}{self.path}") / "v1"
|
|
35
36
|
|
|
36
37
|
@property
|
|
37
38
|
def ws_base(self):
|
|
38
|
-
return URL(f"ws://{self.host}:{self.port}{self.path}") / "v1"
|
|
39
|
+
return URL(f"ws{'s' if self.secure else ''}://{self.host}:{self.port}{self.path}") / "v1"
|
|
39
40
|
|
|
40
41
|
|
|
41
42
|
@dataclass
|
|
@@ -44,6 +45,7 @@ class WebhookInfo(Config):
|
|
|
44
45
|
port: int = 8080
|
|
45
46
|
path: str = "v1/events"
|
|
46
47
|
token: str | None = None
|
|
48
|
+
secure: bool = False
|
|
47
49
|
server_host: str = "localhost"
|
|
48
50
|
server_port: int = 5140
|
|
49
51
|
server_path: str = ""
|
|
@@ -57,4 +59,6 @@ class WebhookInfo(Config):
|
|
|
57
59
|
if self.server_path and not self.server_path.startswith("/"):
|
|
58
60
|
self.server_path = f"/{self.server_path}"
|
|
59
61
|
self.identity = f"{self.host}:{self.port}{self.path}"
|
|
60
|
-
self.api_base =
|
|
62
|
+
self.api_base = (
|
|
63
|
+
URL(f"http{'s' if self.secure else ''}://{self.server_host}:{self.server_port}{self.server_path}") / "v1"
|
|
64
|
+
)
|
|
@@ -29,6 +29,7 @@ from satori.model import (
|
|
|
29
29
|
User,
|
|
30
30
|
)
|
|
31
31
|
|
|
32
|
+
from .. import Friend
|
|
32
33
|
from .network.util import validate_response
|
|
33
34
|
|
|
34
35
|
if TYPE_CHECKING:
|
|
@@ -632,23 +633,25 @@ class ApiProtocol:
|
|
|
632
633
|
{"guild_id": guild_id, "role_id": role_id},
|
|
633
634
|
)
|
|
634
635
|
|
|
635
|
-
async def reaction_create(self, channel_id: str, message_id: str,
|
|
636
|
+
async def reaction_create(self, channel_id: str, message_id: str, emoji_id: str) -> None:
|
|
636
637
|
"""向特定消息添加表态。
|
|
637
638
|
|
|
638
639
|
Args:
|
|
639
640
|
channel_id (str): 频道 ID
|
|
640
641
|
message_id (str): 消息 ID
|
|
641
|
-
|
|
642
|
+
emoji_id (str): 表情 ID
|
|
642
643
|
|
|
643
644
|
Returns:
|
|
644
645
|
None: 该方法无返回值
|
|
645
646
|
"""
|
|
646
647
|
await self.call_api(
|
|
647
648
|
Api.REACTION_CREATE,
|
|
648
|
-
{"channel_id": channel_id, "message_id": message_id, "
|
|
649
|
+
{"channel_id": channel_id, "message_id": message_id, "emoji_id": emoji_id},
|
|
649
650
|
)
|
|
650
651
|
|
|
651
|
-
async def reaction_delete(
|
|
652
|
+
async def reaction_delete(
|
|
653
|
+
self, channel_id: str, message_id: str, emoji_id: str, user_id: str | None = None
|
|
654
|
+
) -> None:
|
|
652
655
|
"""从特定消息删除某个用户添加的特定表态。
|
|
653
656
|
|
|
654
657
|
如果没有传入用户 ID 则表示删除自己的表态。
|
|
@@ -656,13 +659,13 @@ class ApiProtocol:
|
|
|
656
659
|
Args:
|
|
657
660
|
channel_id (str): 频道 ID
|
|
658
661
|
message_id (str): 消息 ID
|
|
659
|
-
|
|
662
|
+
emoji_id (str): 表情 ID
|
|
660
663
|
user_id (str | None, optional): 用户 ID,默认为 None
|
|
661
664
|
|
|
662
665
|
Returns:
|
|
663
666
|
None: 该方法无返回值
|
|
664
667
|
"""
|
|
665
|
-
data = {"channel_id": channel_id, "message_id": message_id, "
|
|
668
|
+
data = {"channel_id": channel_id, "message_id": message_id, "emoji_id": emoji_id}
|
|
666
669
|
if user_id is not None:
|
|
667
670
|
data["user_id"] = user_id
|
|
668
671
|
await self.call_api(
|
|
@@ -670,7 +673,7 @@ class ApiProtocol:
|
|
|
670
673
|
data,
|
|
671
674
|
)
|
|
672
675
|
|
|
673
|
-
async def reaction_clear(self, channel_id: str, message_id: str,
|
|
676
|
+
async def reaction_clear(self, channel_id: str, message_id: str, emoji_id: str | None = None) -> None:
|
|
674
677
|
"""从特定消息清除某个特定表态。
|
|
675
678
|
|
|
676
679
|
如果没有传入表态名称则表示清除所有表态。
|
|
@@ -678,28 +681,28 @@ class ApiProtocol:
|
|
|
678
681
|
Args:
|
|
679
682
|
channel_id (str): 频道 ID
|
|
680
683
|
message_id (str): 消息 ID
|
|
681
|
-
|
|
684
|
+
emoji_id (str | None, optional): 表情 ID,默认为 None
|
|
682
685
|
|
|
683
686
|
Returns:
|
|
684
687
|
None: 该方法无返回值
|
|
685
688
|
"""
|
|
686
689
|
data = {"channel_id": channel_id, "message_id": message_id}
|
|
687
|
-
if
|
|
688
|
-
data["
|
|
690
|
+
if emoji_id is not None:
|
|
691
|
+
data["emoji_id"] = emoji_id
|
|
689
692
|
await self.call_api(
|
|
690
693
|
Api.REACTION_CLEAR,
|
|
691
694
|
data,
|
|
692
695
|
)
|
|
693
696
|
|
|
694
697
|
def reaction_list(
|
|
695
|
-
self, channel_id: str, message_id: str,
|
|
698
|
+
self, channel_id: str, message_id: str, emoji_id: str, next_token: str | None = None
|
|
696
699
|
) -> IterablePageResult[User]:
|
|
697
700
|
"""获取添加特定消息的特定表态的用户列表。返回一个 User 的分页列表。
|
|
698
701
|
|
|
699
702
|
Args:
|
|
700
703
|
channel_id (str): 频道 ID
|
|
701
704
|
message_id (str): 消息 ID
|
|
702
|
-
|
|
705
|
+
emoji_id (str): 表情 ID
|
|
703
706
|
next_token (str | None, optional): 分页令牌,默认为空
|
|
704
707
|
|
|
705
708
|
Returns:
|
|
@@ -712,7 +715,7 @@ class ApiProtocol:
|
|
|
712
715
|
{
|
|
713
716
|
"channel_id": channel_id,
|
|
714
717
|
"message_id": message_id,
|
|
715
|
-
"
|
|
718
|
+
"emoji_id": emoji_id,
|
|
716
719
|
"next": token,
|
|
717
720
|
},
|
|
718
721
|
)
|
|
@@ -729,34 +732,33 @@ class ApiProtocol:
|
|
|
729
732
|
res = await self.call_api(Api.LOGIN_GET, {})
|
|
730
733
|
return Login.parse(res)
|
|
731
734
|
|
|
732
|
-
|
|
733
|
-
"""获取用户信息。返回一个 `User` 对象。
|
|
734
|
-
|
|
735
|
-
Args:
|
|
736
|
-
user_id (str): 用户 ID
|
|
737
|
-
|
|
738
|
-
Returns:
|
|
739
|
-
User: `User` 对象
|
|
740
|
-
"""
|
|
741
|
-
res = await self.call_api(Api.USER_GET, {"user_id": user_id})
|
|
742
|
-
return User.parse(res)
|
|
743
|
-
|
|
744
|
-
def friend_list(self, next_token: str | None = None) -> IterablePageResult[User]:
|
|
735
|
+
def friend_list(self, next_token: str | None = None) -> IterablePageResult[Friend]:
|
|
745
736
|
"""获取好友列表。返回一个 User 的分页列表。
|
|
746
737
|
|
|
747
738
|
Args:
|
|
748
739
|
next_token (str | None, optional): 分页令牌,默认为空
|
|
749
740
|
|
|
750
741
|
Returns:
|
|
751
|
-
IterablePageResult[
|
|
742
|
+
IterablePageResult[Friend]: `Friend` 的分页列表
|
|
752
743
|
"""
|
|
753
744
|
|
|
754
745
|
async def _(token: str | None):
|
|
755
746
|
res = await self.call_api(Api.FRIEND_LIST, {"next": token})
|
|
756
|
-
return PageResult.parse(res,
|
|
747
|
+
return PageResult.parse(res, Friend.parse)
|
|
757
748
|
|
|
758
749
|
return IterablePageResult(_, next_token)
|
|
759
750
|
|
|
751
|
+
async def friend_delete(self, user_id: str) -> None:
|
|
752
|
+
"""删除好友。
|
|
753
|
+
|
|
754
|
+
Args:
|
|
755
|
+
user_id (str): 用户 ID
|
|
756
|
+
|
|
757
|
+
Returns:
|
|
758
|
+
None: 该方法无返回值
|
|
759
|
+
"""
|
|
760
|
+
await self.call_api(Api.FRIEND_DELETE, {"user_id": user_id})
|
|
761
|
+
|
|
760
762
|
async def friend_approve(self, request_id: str, approve: bool, comment: str) -> None:
|
|
761
763
|
"""处理好友申请。
|
|
762
764
|
|
|
@@ -773,6 +775,18 @@ class ApiProtocol:
|
|
|
773
775
|
{"message_id": request_id, "approve": approve, "comment": comment},
|
|
774
776
|
)
|
|
775
777
|
|
|
778
|
+
async def user_get(self, user_id: str) -> User:
|
|
779
|
+
"""获取用户信息。返回一个 `User` 对象。
|
|
780
|
+
|
|
781
|
+
Args:
|
|
782
|
+
user_id (str): 用户 ID
|
|
783
|
+
|
|
784
|
+
Returns:
|
|
785
|
+
User: `User` 对象
|
|
786
|
+
"""
|
|
787
|
+
res = await self.call_api(Api.USER_GET, {"user_id": user_id})
|
|
788
|
+
return User.parse(res)
|
|
789
|
+
|
|
776
790
|
async def internal(self, action: str, method: str = "POST", **kwargs) -> Any:
|
|
777
791
|
"""内部接口调用。
|
|
778
792
|
|
|
@@ -40,10 +40,11 @@ class Api(str, Enum):
|
|
|
40
40
|
|
|
41
41
|
LOGIN_GET = "login.get"
|
|
42
42
|
|
|
43
|
-
USER_GET = "user.get"
|
|
44
43
|
FRIEND_LIST = "friend.list"
|
|
44
|
+
FRIEND_DELETE = "friend.delete"
|
|
45
45
|
FRIEND_APPROVE = "friend.approve"
|
|
46
46
|
|
|
47
|
+
USER_GET = "user.get"
|
|
47
48
|
UPLOAD_CREATE = "upload.create"
|
|
48
49
|
|
|
49
50
|
|
|
@@ -65,6 +66,9 @@ class EventType(str, Enum):
|
|
|
65
66
|
GUILD_ROLE_CREATED = "guild-role-created"
|
|
66
67
|
GUILD_ROLE_DELETED = "guild-role-deleted"
|
|
67
68
|
GUILD_ROLE_UPDATED = "guild-role-updated"
|
|
69
|
+
GUILD_EMOJI_ADDED = "guild-emoji-added"
|
|
70
|
+
GUILD_EMOJI_REMOVED = "guild-emoji-removed"
|
|
71
|
+
GUILD_EMOJI_UPDATED = "guild-emoji-updated"
|
|
68
72
|
LOGIN_ADDED = "login-added"
|
|
69
73
|
LOGIN_REMOVED = "login-removed"
|
|
70
74
|
LOGIN_UPDATED = "login-updated"
|
|
@@ -5,7 +5,7 @@ from io import BytesIO
|
|
|
5
5
|
from pathlib import Path
|
|
6
6
|
from types import UnionType
|
|
7
7
|
from typing import Any, ClassVar, Final, TypeVar, Union, final, get_args, get_origin, overload
|
|
8
|
-
from typing_extensions import override
|
|
8
|
+
from typing_extensions import Self, override
|
|
9
9
|
|
|
10
10
|
from ._vendor.fleep import get
|
|
11
11
|
from .parser import Element as RawElement
|
|
@@ -157,6 +157,19 @@ class At(Element):
|
|
|
157
157
|
return At(type="here" if here else "all")
|
|
158
158
|
|
|
159
159
|
|
|
160
|
+
@dataclass(repr=False)
|
|
161
|
+
class Emoji(Element):
|
|
162
|
+
"""<emoji> 元素用于表示一个表情。"""
|
|
163
|
+
|
|
164
|
+
id: str
|
|
165
|
+
name: str | None = None
|
|
166
|
+
|
|
167
|
+
def to_model(self):
|
|
168
|
+
from .model import EmojiObject
|
|
169
|
+
|
|
170
|
+
return EmojiObject(self.id, self.name)
|
|
171
|
+
|
|
172
|
+
|
|
160
173
|
@dataclass(repr=False)
|
|
161
174
|
class Sharp(Element):
|
|
162
175
|
"""<sharp> 元素用于提及某个频道。"""
|
|
@@ -200,7 +213,7 @@ class Resource(Element):
|
|
|
200
213
|
|
|
201
214
|
@classmethod
|
|
202
215
|
def of(
|
|
203
|
-
cls,
|
|
216
|
+
cls: type[Self],
|
|
204
217
|
url: str | None = None,
|
|
205
218
|
path: str | Path | None = None,
|
|
206
219
|
raw: bytes | BytesIO | None = None,
|
|
@@ -212,7 +225,7 @@ class Resource(Element):
|
|
|
212
225
|
cache: bool | None = None,
|
|
213
226
|
timeout: int | None = None,
|
|
214
227
|
**kwargs,
|
|
215
|
-
):
|
|
228
|
+
) -> Self:
|
|
216
229
|
data: dict[str, Any] = {"extra": extra, **kwargs}
|
|
217
230
|
if url is not None:
|
|
218
231
|
data |= {"src": url}
|
|
@@ -537,6 +550,7 @@ def register_element(cls: type[TE], tag: str | None = None) -> type[TE]:
|
|
|
537
550
|
ELEMENT_TYPE_MAP = {
|
|
538
551
|
"text": Text,
|
|
539
552
|
"at": At,
|
|
553
|
+
"emoji": Emoji,
|
|
540
554
|
"sharp": Sharp,
|
|
541
555
|
"img": Image,
|
|
542
556
|
"image": Image,
|
|
@@ -637,6 +651,7 @@ class _E:
|
|
|
637
651
|
self.at = At
|
|
638
652
|
self.at_role = At.role_
|
|
639
653
|
self.at_all = At.all
|
|
654
|
+
self.emoji = Emoji
|
|
640
655
|
self.sharp = Sharp
|
|
641
656
|
self.link = Link
|
|
642
657
|
self.image = Image.of
|
|
@@ -2,6 +2,7 @@ from satori.model import (
|
|
|
2
2
|
ArgvInteraction,
|
|
3
3
|
ButtonInteraction,
|
|
4
4
|
Channel,
|
|
5
|
+
EmojiObject,
|
|
5
6
|
Event,
|
|
6
7
|
Guild,
|
|
7
8
|
LoginPartial,
|
|
@@ -40,6 +41,10 @@ class GuildRoleEvent(GuildEvent):
|
|
|
40
41
|
role: Role
|
|
41
42
|
|
|
42
43
|
|
|
44
|
+
class GuildEmojiEvent(GuildEvent):
|
|
45
|
+
emoji: EmojiObject
|
|
46
|
+
|
|
47
|
+
|
|
43
48
|
class LoginEvent(Event):
|
|
44
49
|
login: LoginPartial
|
|
45
50
|
|
|
@@ -48,6 +53,7 @@ class ReactionEvent(Event):
|
|
|
48
53
|
channel: Channel
|
|
49
54
|
user: User
|
|
50
55
|
message: MessageObject
|
|
56
|
+
emoji: EmojiObject
|
|
51
57
|
|
|
52
58
|
|
|
53
59
|
class ButtonInteractionEvent(Event):
|
|
@@ -8,7 +8,7 @@ from pathlib import Path
|
|
|
8
8
|
from typing import IO, Any, ClassVar, Generic, Literal, TypeAlias, TypeVar
|
|
9
9
|
from typing_extensions import Self
|
|
10
10
|
|
|
11
|
-
from .element import Element, transform
|
|
11
|
+
from .element import Element, Emoji, transform
|
|
12
12
|
from .parser import Element as RawElement
|
|
13
13
|
from .parser import parse
|
|
14
14
|
|
|
@@ -98,13 +98,15 @@ class User(ModelBase):
|
|
|
98
98
|
|
|
99
99
|
|
|
100
100
|
@dataclass
|
|
101
|
-
class
|
|
101
|
+
class Friend(ModelBase):
|
|
102
102
|
user: User | None = None
|
|
103
103
|
nick: str | None = None
|
|
104
|
-
avatar: str | None = None
|
|
105
|
-
joined_at: datetime | None = None
|
|
106
104
|
|
|
107
|
-
|
|
105
|
+
@property
|
|
106
|
+
def remark(self) -> str | None:
|
|
107
|
+
return self.nick
|
|
108
|
+
|
|
109
|
+
__converter__ = {"user": User.parse}
|
|
108
110
|
|
|
109
111
|
def dump(self):
|
|
110
112
|
res = {}
|
|
@@ -112,10 +114,6 @@ class Member(ModelBase):
|
|
|
112
114
|
res["user"] = self.user.dump()
|
|
113
115
|
if self.nick:
|
|
114
116
|
res["nick"] = self.nick
|
|
115
|
-
if self.avatar:
|
|
116
|
-
res["avatar"] = self.avatar
|
|
117
|
-
if self.joined_at:
|
|
118
|
-
res["joined_at"] = int(self.joined_at.timestamp() * 1000)
|
|
119
117
|
return res
|
|
120
118
|
|
|
121
119
|
|
|
@@ -131,6 +129,35 @@ class Role(ModelBase):
|
|
|
131
129
|
return res
|
|
132
130
|
|
|
133
131
|
|
|
132
|
+
@dataclass
|
|
133
|
+
class Member(ModelBase):
|
|
134
|
+
user: User | None = None
|
|
135
|
+
nick: str | None = None
|
|
136
|
+
avatar: str | None = None
|
|
137
|
+
joined_at: datetime | None = None
|
|
138
|
+
roles: list[Role] = field(default_factory=list)
|
|
139
|
+
|
|
140
|
+
__converter__ = {
|
|
141
|
+
"user": User.parse,
|
|
142
|
+
"joined_at": lambda ts: datetime.fromtimestamp(int(ts) / 1000),
|
|
143
|
+
"roles": lambda raw: [Role.parse(role) for role in raw],
|
|
144
|
+
} # noqa: E501
|
|
145
|
+
|
|
146
|
+
def dump(self):
|
|
147
|
+
res = {}
|
|
148
|
+
if self.user:
|
|
149
|
+
res["user"] = self.user.dump()
|
|
150
|
+
if self.nick:
|
|
151
|
+
res["nick"] = self.nick
|
|
152
|
+
if self.avatar:
|
|
153
|
+
res["avatar"] = self.avatar
|
|
154
|
+
if self.joined_at:
|
|
155
|
+
res["joined_at"] = int(self.joined_at.timestamp() * 1000)
|
|
156
|
+
if self.roles:
|
|
157
|
+
res["roles"] = [role.dump() for role in self.roles]
|
|
158
|
+
return res
|
|
159
|
+
|
|
160
|
+
|
|
134
161
|
class LoginStatus(IntEnum):
|
|
135
162
|
OFFLINE = 0
|
|
136
163
|
"""离线"""
|
|
@@ -282,6 +309,21 @@ class Meta(ModelBase):
|
|
|
282
309
|
return {"logins": [login.dump() for login in self.logins], "proxy_urls": self.proxy_urls}
|
|
283
310
|
|
|
284
311
|
|
|
312
|
+
@dataclass
|
|
313
|
+
class EmojiObject(ModelBase):
|
|
314
|
+
id: str
|
|
315
|
+
name: str | None = None
|
|
316
|
+
|
|
317
|
+
def dump(self):
|
|
318
|
+
res = {"id": self.id}
|
|
319
|
+
if self.name:
|
|
320
|
+
res["name"] = self.name
|
|
321
|
+
return res
|
|
322
|
+
|
|
323
|
+
def to_element(self) -> Emoji:
|
|
324
|
+
return Emoji(self.id, self.name)
|
|
325
|
+
|
|
326
|
+
|
|
285
327
|
@dataclass
|
|
286
328
|
class MessageObject(ModelBase):
|
|
287
329
|
id: str
|
|
@@ -373,6 +415,7 @@ class Event(ModelBase):
|
|
|
373
415
|
role: Role | None = None
|
|
374
416
|
user: User | None = None
|
|
375
417
|
referrer: dict | None = None
|
|
418
|
+
emoji: EmojiObject | None = None
|
|
376
419
|
|
|
377
420
|
_type: str | None = None
|
|
378
421
|
_data: dict | None = None
|
|
@@ -391,6 +434,7 @@ class Event(ModelBase):
|
|
|
391
434
|
"operator": User.parse,
|
|
392
435
|
"role": Role.parse,
|
|
393
436
|
"user": User.parse,
|
|
437
|
+
"emoji": EmojiObject.parse,
|
|
394
438
|
}
|
|
395
439
|
|
|
396
440
|
@classmethod
|
|
@@ -447,6 +491,8 @@ class Event(ModelBase):
|
|
|
447
491
|
res["user"] = self.user.dump()
|
|
448
492
|
if self.referrer:
|
|
449
493
|
res["referrer"] = self.referrer
|
|
494
|
+
if self.emoji:
|
|
495
|
+
res["emoji"] = self.emoji.dump()
|
|
450
496
|
if self._type:
|
|
451
497
|
res["_type"] = self._type
|
|
452
498
|
if self._data:
|
|
@@ -97,6 +97,10 @@ async def _request_handler(action: str, request: StarletteRequest, func: RouteCa
|
|
|
97
97
|
self_id=self_id,
|
|
98
98
|
)
|
|
99
99
|
)
|
|
100
|
+
except asyncio.CancelledError:
|
|
101
|
+
return Response(status_code=503, content="Request cancelled")
|
|
102
|
+
except asyncio.TimeoutError:
|
|
103
|
+
return Response(status_code=504, content="Request timeout")
|
|
100
104
|
except ActionFailed as ae:
|
|
101
105
|
logger.warning(ae)
|
|
102
106
|
return Response(status_code=ae.CODE, content=str(ae))
|
|
@@ -7,6 +7,7 @@ from starlette.datastructures import FormData
|
|
|
7
7
|
from satori.model import (
|
|
8
8
|
Channel,
|
|
9
9
|
Direction,
|
|
10
|
+
Friend,
|
|
10
11
|
Guild,
|
|
11
12
|
Login,
|
|
12
13
|
Member,
|
|
@@ -207,7 +208,7 @@ GUILD_ROLE_DELETE: TypeAlias = RouteCall[GuildRoleDeleteParam, None]
|
|
|
207
208
|
class ReactionCreateParam(TypedDict):
|
|
208
209
|
channel_id: str
|
|
209
210
|
message_id: str
|
|
210
|
-
|
|
211
|
+
emoji_id: str
|
|
211
212
|
|
|
212
213
|
|
|
213
214
|
REACTION_CREATE: TypeAlias = RouteCall[ReactionCreateParam, None]
|
|
@@ -216,7 +217,7 @@ REACTION_CREATE: TypeAlias = RouteCall[ReactionCreateParam, None]
|
|
|
216
217
|
class ReactionDeleteParam(TypedDict):
|
|
217
218
|
channel_id: str
|
|
218
219
|
message_id: str
|
|
219
|
-
|
|
220
|
+
emoji_id: str
|
|
220
221
|
user_id: NotRequired[str]
|
|
221
222
|
|
|
222
223
|
|
|
@@ -226,7 +227,7 @@ REACTION_DELETE: TypeAlias = RouteCall[ReactionDeleteParam, None]
|
|
|
226
227
|
class ReactionClearParam(TypedDict):
|
|
227
228
|
channel_id: str
|
|
228
229
|
message_id: str
|
|
229
|
-
|
|
230
|
+
emoji_id: NotRequired[str]
|
|
230
231
|
|
|
231
232
|
|
|
232
233
|
REACTION_CLEAR: TypeAlias = RouteCall[ReactionClearParam, None]
|
|
@@ -235,7 +236,7 @@ REACTION_CLEAR: TypeAlias = RouteCall[ReactionClearParam, None]
|
|
|
235
236
|
class ReactionListParam(TypedDict):
|
|
236
237
|
channel_id: str
|
|
237
238
|
message_id: str
|
|
238
|
-
|
|
239
|
+
emoji_id: str
|
|
239
240
|
next: NotRequired[str]
|
|
240
241
|
|
|
241
242
|
|
|
@@ -243,18 +244,19 @@ REACTION_LIST: TypeAlias = RouteCall[ReactionListParam, PageResult[User] | dict[
|
|
|
243
244
|
LOGIN_GET: TypeAlias = RouteCall[Any, Login | dict[str, Any]]
|
|
244
245
|
|
|
245
246
|
|
|
246
|
-
class
|
|
247
|
+
class UserOpParam(TypedDict):
|
|
247
248
|
user_id: str
|
|
248
249
|
|
|
249
250
|
|
|
250
|
-
USER_GET: TypeAlias = RouteCall[
|
|
251
|
+
USER_GET: TypeAlias = RouteCall[UserOpParam, User | dict[str, Any]]
|
|
252
|
+
FRIEND_DELETE: TypeAlias = RouteCall[UserOpParam, None]
|
|
251
253
|
|
|
252
254
|
|
|
253
255
|
class FriendListParam(TypedDict):
|
|
254
256
|
next: NotRequired[str]
|
|
255
257
|
|
|
256
258
|
|
|
257
|
-
FRIEND_LIST: TypeAlias = RouteCall[FriendListParam, PageResult[
|
|
259
|
+
FRIEND_LIST: TypeAlias = RouteCall[FriendListParam, PageResult[Friend] | dict[str, Any]]
|
|
258
260
|
|
|
259
261
|
|
|
260
262
|
class ApproveParam(TypedDict):
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|