zynko 0.1.0__py3-none-any.whl
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.
- zynko/__init__.py +74 -0
- zynko/_events.py +123 -0
- zynko/bits.py +62 -0
- zynko/client.py +471 -0
- zynko/gateway.py +171 -0
- zynko/interaction.py +106 -0
- zynko/message.py +121 -0
- zynko/models.py +387 -0
- zynko/permissions.py +52 -0
- zynko/player.py +413 -0
- zynko/py.typed +0 -0
- zynko/rest.py +309 -0
- zynko/voice.py +298 -0
- zynko-0.1.0.dist-info/METADATA +67 -0
- zynko-0.1.0.dist-info/RECORD +16 -0
- zynko-0.1.0.dist-info/WHEEL +4 -0
zynko/__init__.py
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
"""zynko: the official library for Zynko bots (docs.zynko.org/bots).
|
|
2
|
+
|
|
3
|
+
API and gateway on asyncio and aiohttp; voice through the ``livekit`` package, installed with
|
|
4
|
+
``pip install "zynko[voice]"`` and loaded only when a bot joins a voice room.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from .bits import ALL_PERMISSIONS, Intents, Permissions, id_time
|
|
8
|
+
from .client import Client
|
|
9
|
+
from .gateway import GatewayClosed
|
|
10
|
+
from .interaction import Interaction
|
|
11
|
+
from .message import Message
|
|
12
|
+
from .models import (
|
|
13
|
+
Attachment,
|
|
14
|
+
Button,
|
|
15
|
+
ButtonStyle,
|
|
16
|
+
Card,
|
|
17
|
+
CardField,
|
|
18
|
+
Channel,
|
|
19
|
+
ChannelType,
|
|
20
|
+
Command,
|
|
21
|
+
CommandOption,
|
|
22
|
+
Components,
|
|
23
|
+
Emoji,
|
|
24
|
+
Guild,
|
|
25
|
+
Member,
|
|
26
|
+
Overwrite,
|
|
27
|
+
Role,
|
|
28
|
+
User,
|
|
29
|
+
VoiceState,
|
|
30
|
+
)
|
|
31
|
+
from .permissions import compute_permissions
|
|
32
|
+
from .player import PCM, AudioInput, AudioPlayer, Stream, Track, tone
|
|
33
|
+
from .rest import File, Rest, ZynkoError
|
|
34
|
+
|
|
35
|
+
__version__ = "0.1.0"
|
|
36
|
+
|
|
37
|
+
__all__ = [
|
|
38
|
+
"ALL_PERMISSIONS",
|
|
39
|
+
"Attachment",
|
|
40
|
+
"AudioInput",
|
|
41
|
+
"AudioPlayer",
|
|
42
|
+
"Button",
|
|
43
|
+
"ButtonStyle",
|
|
44
|
+
"Card",
|
|
45
|
+
"CardField",
|
|
46
|
+
"Channel",
|
|
47
|
+
"ChannelType",
|
|
48
|
+
"Client",
|
|
49
|
+
"Command",
|
|
50
|
+
"CommandOption",
|
|
51
|
+
"Components",
|
|
52
|
+
"Emoji",
|
|
53
|
+
"File",
|
|
54
|
+
"GatewayClosed",
|
|
55
|
+
"Guild",
|
|
56
|
+
"Intents",
|
|
57
|
+
"Interaction",
|
|
58
|
+
"Member",
|
|
59
|
+
"Message",
|
|
60
|
+
"Overwrite",
|
|
61
|
+
"PCM",
|
|
62
|
+
"Permissions",
|
|
63
|
+
"Rest",
|
|
64
|
+
"Role",
|
|
65
|
+
"Stream",
|
|
66
|
+
"Track",
|
|
67
|
+
"User",
|
|
68
|
+
"VoiceState",
|
|
69
|
+
"ZynkoError",
|
|
70
|
+
"compute_permissions",
|
|
71
|
+
"id_time",
|
|
72
|
+
"tone",
|
|
73
|
+
"__version__",
|
|
74
|
+
]
|
zynko/_events.py
ADDED
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
"""A small event emitter: sync or async handlers, errors routed instead of raised."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import asyncio
|
|
6
|
+
import inspect
|
|
7
|
+
import logging
|
|
8
|
+
from collections.abc import Awaitable, Callable
|
|
9
|
+
from typing import Any, TypeVar, overload
|
|
10
|
+
|
|
11
|
+
log = logging.getLogger("zynko")
|
|
12
|
+
|
|
13
|
+
F = TypeVar("F", bound=Callable[..., Any])
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class Emitter:
|
|
17
|
+
"""Calls handlers by event name.
|
|
18
|
+
|
|
19
|
+
A handler may be a plain function or a coroutine function; coroutines run as their own
|
|
20
|
+
tasks, so a slow handler never holds up the next event. A handler that raises becomes an
|
|
21
|
+
``error`` event (or a log line, when nobody listens to ``error``).
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
def __init__(self, name: str = "zynko") -> None:
|
|
25
|
+
self._name = name
|
|
26
|
+
self._handlers: dict[str, list[Callable[..., Any]]] = {}
|
|
27
|
+
self._once: set[tuple[str, Callable[..., Any]]] = set()
|
|
28
|
+
self._tasks: set[asyncio.Task[Any]] = set()
|
|
29
|
+
|
|
30
|
+
@overload
|
|
31
|
+
def on(self, event: str) -> Callable[[F], F]: ...
|
|
32
|
+
@overload
|
|
33
|
+
def on(self, event: str, handler: F) -> F: ...
|
|
34
|
+
def on(self, event: str, handler: F | None = None) -> Any:
|
|
35
|
+
"""Registers a handler. Works as a call or as a decorator: ``@x.on("event")``."""
|
|
36
|
+
if handler is None:
|
|
37
|
+
return lambda f: self.on(event, f)
|
|
38
|
+
self._handlers.setdefault(event, []).append(handler)
|
|
39
|
+
return handler
|
|
40
|
+
|
|
41
|
+
def once(self, event: str, handler: F) -> F:
|
|
42
|
+
"""Registers a handler that runs once."""
|
|
43
|
+
self.on(event, handler)
|
|
44
|
+
self._once.add((event, handler))
|
|
45
|
+
return handler
|
|
46
|
+
|
|
47
|
+
def off(self, event: str, handler: Callable[..., Any]) -> None:
|
|
48
|
+
handlers = self._handlers.get(event, [])
|
|
49
|
+
if handler in handlers:
|
|
50
|
+
handlers.remove(handler)
|
|
51
|
+
self._once.discard((event, handler))
|
|
52
|
+
|
|
53
|
+
def listeners(self, event: str) -> int:
|
|
54
|
+
return len(self._handlers.get(event, []))
|
|
55
|
+
|
|
56
|
+
def emit(self, event: str, *args: Any) -> None:
|
|
57
|
+
for handler in list(self._handlers.get(event, [])):
|
|
58
|
+
if (event, handler) in self._once:
|
|
59
|
+
self.off(event, handler)
|
|
60
|
+
self._call(event, handler, args)
|
|
61
|
+
|
|
62
|
+
def _call(self, event: str, handler: Callable[..., Any], args: tuple[Any, ...]) -> None:
|
|
63
|
+
try:
|
|
64
|
+
result = handler(*args)
|
|
65
|
+
except Exception as e: # noqa: BLE001 - every handler error is reported, none raised
|
|
66
|
+
self._failed(event, e)
|
|
67
|
+
return
|
|
68
|
+
if inspect.isawaitable(result):
|
|
69
|
+
task = asyncio.ensure_future(_await(result))
|
|
70
|
+
self._tasks.add(task)
|
|
71
|
+
task.add_done_callback(lambda t: self._done(event, t))
|
|
72
|
+
|
|
73
|
+
def _done(self, event: str, task: asyncio.Task[Any]) -> None:
|
|
74
|
+
self._tasks.discard(task)
|
|
75
|
+
if task.cancelled():
|
|
76
|
+
return
|
|
77
|
+
e = task.exception()
|
|
78
|
+
if e is not None:
|
|
79
|
+
self._failed(event, e)
|
|
80
|
+
|
|
81
|
+
def _failed(self, event: str, error: BaseException) -> None:
|
|
82
|
+
if event != "error" and self.listeners("error"):
|
|
83
|
+
self.emit("error", error)
|
|
84
|
+
else:
|
|
85
|
+
log.error("%s: erro num handler de %r", self._name, event, exc_info=error)
|
|
86
|
+
|
|
87
|
+
async def wait_for(
|
|
88
|
+
self,
|
|
89
|
+
event: str,
|
|
90
|
+
check: Callable[..., bool] | None = None,
|
|
91
|
+
timeout: float | None = 30.0,
|
|
92
|
+
) -> Any:
|
|
93
|
+
"""Waits for the next ``event`` whose arguments pass ``check``.
|
|
94
|
+
|
|
95
|
+
Returns the single argument, or a tuple when the event has several. Raises
|
|
96
|
+
``asyncio.TimeoutError`` after ``timeout`` seconds (None: wait forever).
|
|
97
|
+
"""
|
|
98
|
+
loop = asyncio.get_running_loop()
|
|
99
|
+
future: asyncio.Future[Any] = loop.create_future()
|
|
100
|
+
|
|
101
|
+
def handler(*args: Any) -> None:
|
|
102
|
+
if future.done():
|
|
103
|
+
return
|
|
104
|
+
if check is not None:
|
|
105
|
+
try:
|
|
106
|
+
if not check(*args):
|
|
107
|
+
return
|
|
108
|
+
except Exception as e: # noqa: BLE001
|
|
109
|
+
future.set_exception(e)
|
|
110
|
+
return
|
|
111
|
+
future.set_result(args[0] if len(args) == 1 else args)
|
|
112
|
+
|
|
113
|
+
self.on(event, handler)
|
|
114
|
+
try:
|
|
115
|
+
return await asyncio.wait_for(future, timeout)
|
|
116
|
+
except asyncio.TimeoutError:
|
|
117
|
+
raise asyncio.TimeoutError(f"Esperando {event}: tempo esgotado") from None
|
|
118
|
+
finally:
|
|
119
|
+
self.off(event, handler)
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
async def _await(a: Awaitable[Any]) -> Any:
|
|
123
|
+
return await a
|
zynko/bits.py
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
"""Intents, permission bits and snowflake times (docs.zynko.org/api/gateway#intents)."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from enum import IntFlag
|
|
6
|
+
|
|
7
|
+
__all__ = ["Intents", "Permissions", "ALL_PERMISSIONS", "id_time"]
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class Intents(IntFlag):
|
|
11
|
+
"""The kinds of event a bot asks the gateway for.
|
|
12
|
+
|
|
13
|
+
READY, GUILD_CREATE, GUILD_DELETE, CHANNEL_CREATE and USER_UPDATE always arrive.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
#: Guilds changing, and their channels, roles and emojis.
|
|
17
|
+
GUILDS = 1 << 0
|
|
18
|
+
#: Members joining, leaving or changing.
|
|
19
|
+
GUILD_MEMBERS = 1 << 1
|
|
20
|
+
#: Who is online.
|
|
21
|
+
PRESENCES = 1 << 2
|
|
22
|
+
#: Messages created, edited and deleted, in guilds and DMs.
|
|
23
|
+
MESSAGES = 1 << 3
|
|
24
|
+
REACTIONS = 1 << 4
|
|
25
|
+
TYPING = 1 << 5
|
|
26
|
+
#: Who is in which voice room. A bot that joins voice needs it.
|
|
27
|
+
VOICE_STATES = 1 << 6
|
|
28
|
+
ALL = (1 << 7) - 1
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class Permissions(IntFlag):
|
|
32
|
+
"""Role permissions, as bits (docs.zynko.org/api/permissoes)."""
|
|
33
|
+
|
|
34
|
+
ADMINISTRATOR = 1 << 0
|
|
35
|
+
MANAGE_GUILD = 1 << 1
|
|
36
|
+
MANAGE_CHANNELS = 1 << 2
|
|
37
|
+
MANAGE_ROLES = 1 << 3
|
|
38
|
+
KICK_MEMBERS = 1 << 4
|
|
39
|
+
BAN_MEMBERS = 1 << 5
|
|
40
|
+
MANAGE_MESSAGES = 1 << 6
|
|
41
|
+
MANAGE_NICKNAMES = 1 << 7
|
|
42
|
+
CREATE_INVITE = 1 << 8
|
|
43
|
+
SEND_MESSAGES = 1 << 9
|
|
44
|
+
ATTACH_FILES = 1 << 10
|
|
45
|
+
CONNECT = 1 << 11
|
|
46
|
+
SPEAK = 1 << 12
|
|
47
|
+
MANAGE_EMOJIS = 1 << 13
|
|
48
|
+
VIEW_CHANNEL = 1 << 14
|
|
49
|
+
ADD_REACTIONS = 1 << 15
|
|
50
|
+
READ_MESSAGE_HISTORY = 1 << 16
|
|
51
|
+
STREAM = 1 << 17
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
#: Every permission bit set: what owners and admins get.
|
|
55
|
+
ALL_PERMISSIONS = (1 << 18) - 1
|
|
56
|
+
|
|
57
|
+
_EPOCH_MS = 1_767_225_600_000
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def id_time(id: str | int) -> int:
|
|
61
|
+
"""When an id was made, in milliseconds since 1970 (ids are snowflakes)."""
|
|
62
|
+
return (int(id) >> 22) + _EPOCH_MS
|