yourbot-sdk 0.6.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.
- yourbot_sdk-0.6.0/LICENSE +21 -0
- yourbot_sdk-0.6.0/PKG-INFO +80 -0
- yourbot_sdk-0.6.0/README.md +57 -0
- yourbot_sdk-0.6.0/mmo_maid_sdk/__init__.py +52 -0
- yourbot_sdk-0.6.0/pyproject.toml +42 -0
- yourbot_sdk-0.6.0/setup.cfg +4 -0
- yourbot_sdk-0.6.0/setup.py +3 -0
- yourbot_sdk-0.6.0/yourbot_sdk/__init__.py +85 -0
- yourbot_sdk-0.6.0/yourbot_sdk/_components.py +276 -0
- yourbot_sdk-0.6.0/yourbot_sdk/_context.py +1263 -0
- yourbot_sdk-0.6.0/yourbot_sdk/_exceptions.py +62 -0
- yourbot_sdk-0.6.0/yourbot_sdk/_plugin.py +729 -0
- yourbot_sdk-0.6.0/yourbot_sdk/_transport.py +327 -0
- yourbot_sdk-0.6.0/yourbot_sdk/_validation.py +575 -0
- yourbot_sdk-0.6.0/yourbot_sdk/cli.py +1460 -0
- yourbot_sdk-0.6.0/yourbot_sdk/dashboard.py +562 -0
- yourbot_sdk-0.6.0/yourbot_sdk/events.py +335 -0
- yourbot_sdk-0.6.0/yourbot_sdk/testing.py +615 -0
- yourbot_sdk-0.6.0/yourbot_sdk.egg-info/PKG-INFO +80 -0
- yourbot_sdk-0.6.0/yourbot_sdk.egg-info/SOURCES.txt +21 -0
- yourbot_sdk-0.6.0/yourbot_sdk.egg-info/dependency_links.txt +1 -0
- yourbot_sdk-0.6.0/yourbot_sdk.egg-info/entry_points.txt +2 -0
- yourbot_sdk-0.6.0/yourbot_sdk.egg-info/top_level.txt +2 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 YourBot
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: yourbot-sdk
|
|
3
|
+
Version: 0.6.0
|
|
4
|
+
Summary: SDK for building YourBot Marketplace plugins
|
|
5
|
+
License: MIT
|
|
6
|
+
Project-URL: Homepage, https://yourbot.gg/dev
|
|
7
|
+
Project-URL: Documentation, https://yourbot.gg/dev/docs
|
|
8
|
+
Project-URL: Repository, https://github.com/NotUSeee/yourbot-sdk
|
|
9
|
+
Project-URL: Issues, https://github.com/NotUSeee/yourbot-sdk/issues
|
|
10
|
+
Classifier: Development Status :: 4 - Beta
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
13
|
+
Classifier: Operating System :: OS Independent
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
19
|
+
Requires-Python: >=3.10
|
|
20
|
+
Description-Content-Type: text/markdown
|
|
21
|
+
License-File: LICENSE
|
|
22
|
+
Dynamic: license-file
|
|
23
|
+
|
|
24
|
+
# YourBot SDK
|
|
25
|
+
|
|
26
|
+
SDK for building plugins for the [YourBot](https://yourbot.gg) Discord bot platform.
|
|
27
|
+
|
|
28
|
+
YourBot runs marketplace plugins in sandboxed Docker containers. This SDK is the
|
|
29
|
+
official Python interface plugins use to receive Discord events, call the Discord
|
|
30
|
+
API, store data, render dashboards, and emit metrics — all routed through the
|
|
31
|
+
platform so plugins never need direct network access or credentials.
|
|
32
|
+
|
|
33
|
+
## Install
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
pip install yourbot-sdk
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Python 3.10 or newer.
|
|
40
|
+
|
|
41
|
+
## Hello, plugin
|
|
42
|
+
|
|
43
|
+
```python
|
|
44
|
+
from yourbot_sdk import Plugin, Context
|
|
45
|
+
|
|
46
|
+
plugin = Plugin()
|
|
47
|
+
|
|
48
|
+
@plugin.on_event("message_create")
|
|
49
|
+
def on_message(ctx: Context, event: dict):
|
|
50
|
+
if "!ping" in event.get("content", ""):
|
|
51
|
+
ctx.discord.send_message(
|
|
52
|
+
channel_id=event["channel_id"],
|
|
53
|
+
content="Pong!",
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
plugin.run() # must be the last line
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Drop this in a folder named `my_plugin/` as `__main__.py`, zip it, and upload it
|
|
60
|
+
via the [Developer Portal](https://yourbot.gg/dev).
|
|
61
|
+
|
|
62
|
+
## CLI
|
|
63
|
+
|
|
64
|
+
The package installs a `yourbot` command for scaffolding and a local dev loop:
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
yourbot new my_plugin # scaffold a new plugin from the template
|
|
68
|
+
yourbot dev # run your plugin locally against a mock host
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## Documentation
|
|
72
|
+
|
|
73
|
+
Full plugin contract, capability reference, and publishing guide:
|
|
74
|
+
|
|
75
|
+
- **Docs:** https://yourbot.gg/dev/docs
|
|
76
|
+
- **Developer Portal:** https://yourbot.gg/dev
|
|
77
|
+
|
|
78
|
+
## License
|
|
79
|
+
|
|
80
|
+
MIT — see [LICENSE](LICENSE).
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# YourBot SDK
|
|
2
|
+
|
|
3
|
+
SDK for building plugins for the [YourBot](https://yourbot.gg) Discord bot platform.
|
|
4
|
+
|
|
5
|
+
YourBot runs marketplace plugins in sandboxed Docker containers. This SDK is the
|
|
6
|
+
official Python interface plugins use to receive Discord events, call the Discord
|
|
7
|
+
API, store data, render dashboards, and emit metrics — all routed through the
|
|
8
|
+
platform so plugins never need direct network access or credentials.
|
|
9
|
+
|
|
10
|
+
## Install
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
pip install yourbot-sdk
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
Python 3.10 or newer.
|
|
17
|
+
|
|
18
|
+
## Hello, plugin
|
|
19
|
+
|
|
20
|
+
```python
|
|
21
|
+
from yourbot_sdk import Plugin, Context
|
|
22
|
+
|
|
23
|
+
plugin = Plugin()
|
|
24
|
+
|
|
25
|
+
@plugin.on_event("message_create")
|
|
26
|
+
def on_message(ctx: Context, event: dict):
|
|
27
|
+
if "!ping" in event.get("content", ""):
|
|
28
|
+
ctx.discord.send_message(
|
|
29
|
+
channel_id=event["channel_id"],
|
|
30
|
+
content="Pong!",
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
plugin.run() # must be the last line
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Drop this in a folder named `my_plugin/` as `__main__.py`, zip it, and upload it
|
|
37
|
+
via the [Developer Portal](https://yourbot.gg/dev).
|
|
38
|
+
|
|
39
|
+
## CLI
|
|
40
|
+
|
|
41
|
+
The package installs a `yourbot` command for scaffolding and a local dev loop:
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
yourbot new my_plugin # scaffold a new plugin from the template
|
|
45
|
+
yourbot dev # run your plugin locally against a mock host
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## Documentation
|
|
49
|
+
|
|
50
|
+
Full plugin contract, capability reference, and publishing guide:
|
|
51
|
+
|
|
52
|
+
- **Docs:** https://yourbot.gg/dev/docs
|
|
53
|
+
- **Developer Portal:** https://yourbot.gg/dev
|
|
54
|
+
|
|
55
|
+
## License
|
|
56
|
+
|
|
57
|
+
MIT — see [LICENSE](LICENSE).
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
"""Backward-compatibility shim for the renamed ``yourbot_sdk`` package.
|
|
2
|
+
|
|
3
|
+
``mmo_maid_sdk`` was renamed to ``yourbot_sdk`` in 0.6.0. This module keeps
|
|
4
|
+
``import mmo_maid_sdk`` (and every submodule / legacy nested import) working
|
|
5
|
+
by re-exporting the real package. It ships inside the ``yourbot-sdk`` wheel,
|
|
6
|
+
so installing either ``yourbot-sdk`` or the ``mmo-maid-sdk`` alias makes the
|
|
7
|
+
old import path resolve. Prefer ``import yourbot_sdk`` in new code.
|
|
8
|
+
|
|
9
|
+
The identical file is kept at the monorepo root (``mmo_maid_sdk/__init__.py``)
|
|
10
|
+
as a dev-time shim so in-process imports from the source tree behave the same
|
|
11
|
+
as the installed wheel.
|
|
12
|
+
"""
|
|
13
|
+
import importlib as _importlib
|
|
14
|
+
import sys as _sys
|
|
15
|
+
import warnings as _warnings
|
|
16
|
+
|
|
17
|
+
import yourbot_sdk as _real
|
|
18
|
+
from yourbot_sdk import * # noqa: F403
|
|
19
|
+
from yourbot_sdk import __all__, __version__ # noqa: F401
|
|
20
|
+
|
|
21
|
+
_warnings.warn(
|
|
22
|
+
"'mmo_maid_sdk' is deprecated and was renamed to 'yourbot_sdk'; import "
|
|
23
|
+
"'yourbot_sdk' instead. The compatibility shim will be removed in a "
|
|
24
|
+
"future major release.",
|
|
25
|
+
DeprecationWarning,
|
|
26
|
+
stacklevel=2,
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
# A star import does NOT register submodules in sys.modules, so
|
|
30
|
+
# ``from mmo_maid_sdk.testing import ...`` and the legacy deep
|
|
31
|
+
# ``from mmo_maid_sdk.mmo_maid_sdk._transport import ...`` would fail without
|
|
32
|
+
# explicit aliasing. Map every submodule under both the flat ``mmo_maid_sdk.*``
|
|
33
|
+
# and the legacy nested ``mmo_maid_sdk.mmo_maid_sdk.*`` names to the real
|
|
34
|
+
# module object. The try/except covers both layouts: the installed wheel
|
|
35
|
+
# (flat ``yourbot_sdk.<name>``) and the monorepo dev tree
|
|
36
|
+
# (nested ``yourbot_sdk.yourbot_sdk.<name>``).
|
|
37
|
+
_SUBMODULES = (
|
|
38
|
+
"_plugin", "_context", "_transport", "_components", "_exceptions",
|
|
39
|
+
"_validation", "events", "dashboard", "testing", "cli",
|
|
40
|
+
)
|
|
41
|
+
for _name in _SUBMODULES:
|
|
42
|
+
try:
|
|
43
|
+
_mod = _importlib.import_module(f"yourbot_sdk.{_name}")
|
|
44
|
+
except ModuleNotFoundError:
|
|
45
|
+
_mod = _importlib.import_module(f"yourbot_sdk.yourbot_sdk.{_name}")
|
|
46
|
+
_sys.modules[f"mmo_maid_sdk.{_name}"] = _mod
|
|
47
|
+
_sys.modules[f"mmo_maid_sdk.mmo_maid_sdk.{_name}"] = _mod
|
|
48
|
+
|
|
49
|
+
# ``from mmo_maid_sdk.mmo_maid_sdk import Plugin`` resolves to the real package.
|
|
50
|
+
_sys.modules["mmo_maid_sdk.mmo_maid_sdk"] = _real
|
|
51
|
+
|
|
52
|
+
del _importlib, _sys, _warnings, _name, _mod, _real
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=68", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "yourbot-sdk"
|
|
7
|
+
dynamic = ["version"]
|
|
8
|
+
description = "SDK for building YourBot Marketplace plugins"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.10"
|
|
11
|
+
license = {text = "MIT"}
|
|
12
|
+
dependencies = []
|
|
13
|
+
classifiers = [
|
|
14
|
+
"Development Status :: 4 - Beta",
|
|
15
|
+
"Intended Audience :: Developers",
|
|
16
|
+
"License :: OSI Approved :: MIT License",
|
|
17
|
+
"Operating System :: OS Independent",
|
|
18
|
+
"Programming Language :: Python :: 3",
|
|
19
|
+
"Programming Language :: Python :: 3.10",
|
|
20
|
+
"Programming Language :: Python :: 3.11",
|
|
21
|
+
"Programming Language :: Python :: 3.12",
|
|
22
|
+
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
23
|
+
]
|
|
24
|
+
|
|
25
|
+
[project.urls]
|
|
26
|
+
Homepage = "https://yourbot.gg/dev"
|
|
27
|
+
Documentation = "https://yourbot.gg/dev/docs"
|
|
28
|
+
Repository = "https://github.com/NotUSeee/yourbot-sdk"
|
|
29
|
+
Issues = "https://github.com/NotUSeee/yourbot-sdk/issues"
|
|
30
|
+
|
|
31
|
+
[project.scripts]
|
|
32
|
+
yourbot = "yourbot_sdk.cli:main"
|
|
33
|
+
|
|
34
|
+
# Ship BOTH the real package and the ``mmo_maid_sdk`` compatibility shim so
|
|
35
|
+
# ``pip install yourbot-sdk`` provides both ``import yourbot_sdk`` (preferred)
|
|
36
|
+
# and the legacy ``import mmo_maid_sdk`` (kept working for deployed plugins).
|
|
37
|
+
[tool.setuptools.packages.find]
|
|
38
|
+
where = ["."]
|
|
39
|
+
include = ["yourbot_sdk*", "mmo_maid_sdk*"]
|
|
40
|
+
|
|
41
|
+
[tool.setuptools.dynamic]
|
|
42
|
+
version = {attr = "yourbot_sdk.__version__"}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
"""
|
|
2
|
+
YourBot Plugin SDK
|
|
3
|
+
===================
|
|
4
|
+
|
|
5
|
+
Write a plugin in three steps::
|
|
6
|
+
|
|
7
|
+
from yourbot_sdk import Plugin, Context, Button, ActionRow
|
|
8
|
+
|
|
9
|
+
plugin = Plugin()
|
|
10
|
+
|
|
11
|
+
@plugin.on_ready
|
|
12
|
+
def ready(ctx: Context):
|
|
13
|
+
ctx.log("My plugin is alive!")
|
|
14
|
+
|
|
15
|
+
@plugin.on_event("message_create")
|
|
16
|
+
def on_message(ctx: Context, event: dict):
|
|
17
|
+
if "!hello" in event.get("content", ""):
|
|
18
|
+
ctx.discord.send_message(
|
|
19
|
+
channel_id=event["channel_id"],
|
|
20
|
+
content="Hello from my plugin!",
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
# For IDE autocomplete on event fields, import the typed shape:
|
|
24
|
+
# from yourbot_sdk.events import MessageCreate
|
|
25
|
+
# def on_message(ctx: Context, event: MessageCreate): ...
|
|
26
|
+
|
|
27
|
+
@plugin.on_slash_command("greet")
|
|
28
|
+
def greet(ctx: Context, event: dict):
|
|
29
|
+
ctx.interaction.respond(
|
|
30
|
+
content="Hey there!",
|
|
31
|
+
components=[ActionRow(Button("Click me", custom_id="btn_hi"))],
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
@plugin.on_component("btn_hi")
|
|
35
|
+
def btn_hi(ctx: Context, event: dict):
|
|
36
|
+
ctx.interaction.respond(content="You clicked!", ephemeral=True)
|
|
37
|
+
|
|
38
|
+
plugin.run()
|
|
39
|
+
|
|
40
|
+
The SDK handles JSON-RPC, event acking, boot handshake, and error
|
|
41
|
+
recovery so you don't have to.
|
|
42
|
+
"""
|
|
43
|
+
|
|
44
|
+
from ._plugin import Plugin
|
|
45
|
+
from ._context import Context
|
|
46
|
+
from ._exceptions import (
|
|
47
|
+
SdkError,
|
|
48
|
+
CapabilityError,
|
|
49
|
+
RateLimitError,
|
|
50
|
+
DiscordApiError,
|
|
51
|
+
SdkPermissionError,
|
|
52
|
+
PermissionError, # alias for SdkPermissionError (backwards compat)
|
|
53
|
+
ValidationError,
|
|
54
|
+
KvQuotaError,
|
|
55
|
+
RpcTimeoutError,
|
|
56
|
+
TimeoutError, # alias for RpcTimeoutError (backwards compat)
|
|
57
|
+
)
|
|
58
|
+
from ._components import (
|
|
59
|
+
ActionRow,
|
|
60
|
+
Button,
|
|
61
|
+
SelectMenu,
|
|
62
|
+
SelectOption,
|
|
63
|
+
TextInput,
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
__all__ = [
|
|
67
|
+
"Plugin",
|
|
68
|
+
"Context",
|
|
69
|
+
"SdkError",
|
|
70
|
+
"CapabilityError",
|
|
71
|
+
"RateLimitError",
|
|
72
|
+
"DiscordApiError",
|
|
73
|
+
"SdkPermissionError",
|
|
74
|
+
"PermissionError", # alias
|
|
75
|
+
"ValidationError",
|
|
76
|
+
"KvQuotaError",
|
|
77
|
+
"RpcTimeoutError",
|
|
78
|
+
"TimeoutError", # alias
|
|
79
|
+
"ActionRow",
|
|
80
|
+
"Button",
|
|
81
|
+
"SelectMenu",
|
|
82
|
+
"SelectOption",
|
|
83
|
+
"TextInput",
|
|
84
|
+
]
|
|
85
|
+
__version__ = "0.6.0"
|
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
"""Discord UI component builders for marketplace plugins.
|
|
2
|
+
|
|
3
|
+
These produce JSON dicts matching Discord's component schema.
|
|
4
|
+
Used with ``ctx.interaction.respond(components=[...])`` or
|
|
5
|
+
``ctx.discord.send_message(channel_id, components=[...])``.
|
|
6
|
+
|
|
7
|
+
Usage::
|
|
8
|
+
|
|
9
|
+
from yourbot_sdk import ActionRow, Button, SelectMenu, TextInput
|
|
10
|
+
|
|
11
|
+
# Buttons in a row
|
|
12
|
+
row = ActionRow(
|
|
13
|
+
Button("Click me", custom_id="btn_click", style="primary"),
|
|
14
|
+
Button("Cancel", custom_id="btn_cancel", style="secondary"),
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
# Select menu
|
|
18
|
+
row2 = ActionRow(
|
|
19
|
+
SelectMenu("pick_role", options=[
|
|
20
|
+
SelectOption("Tank", "tank", emoji="🛡"),
|
|
21
|
+
SelectOption("Healer", "healer", emoji="💚"),
|
|
22
|
+
SelectOption("DPS", "dps", emoji="⚔"),
|
|
23
|
+
], placeholder="Pick your role"),
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
# Modal text inputs (used with ctx.interaction.send_modal)
|
|
27
|
+
fields = [
|
|
28
|
+
TextInput("Character Name", "char_name", placeholder="e.g. Aerilyn"),
|
|
29
|
+
TextInput("Notes", "notes", style="paragraph", required=False),
|
|
30
|
+
]
|
|
31
|
+
"""
|
|
32
|
+
|
|
33
|
+
from __future__ import annotations
|
|
34
|
+
|
|
35
|
+
from typing import Any, Dict, List, Optional
|
|
36
|
+
|
|
37
|
+
__all__ = [
|
|
38
|
+
"ActionRow",
|
|
39
|
+
"Button",
|
|
40
|
+
"SelectMenu",
|
|
41
|
+
"SelectOption",
|
|
42
|
+
"TextInput",
|
|
43
|
+
]
|
|
44
|
+
|
|
45
|
+
# Discord component types
|
|
46
|
+
_COMPONENT_ACTION_ROW = 1
|
|
47
|
+
_COMPONENT_BUTTON = 2
|
|
48
|
+
_COMPONENT_SELECT_MENU = 3
|
|
49
|
+
_COMPONENT_TEXT_INPUT = 4
|
|
50
|
+
|
|
51
|
+
# Button styles
|
|
52
|
+
_BUTTON_STYLES = {
|
|
53
|
+
"primary": 1,
|
|
54
|
+
"secondary": 2,
|
|
55
|
+
"success": 3,
|
|
56
|
+
"danger": 4,
|
|
57
|
+
"link": 5,
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
# Text input styles
|
|
61
|
+
_TEXT_INPUT_STYLES = {
|
|
62
|
+
"short": 1,
|
|
63
|
+
"paragraph": 2,
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
class Button:
|
|
68
|
+
"""A clickable button component.
|
|
69
|
+
|
|
70
|
+
Args:
|
|
71
|
+
label: Button text (max 80 chars).
|
|
72
|
+
custom_id: Developer-defined ID for handling clicks (max 100 chars).
|
|
73
|
+
Not needed for link buttons.
|
|
74
|
+
style: One of "primary", "secondary", "success", "danger", "link".
|
|
75
|
+
emoji: Optional emoji string (e.g., "🎮" or a custom emoji dict).
|
|
76
|
+
url: URL for link-style buttons (required if style="link").
|
|
77
|
+
disabled: Whether the button is greyed out.
|
|
78
|
+
"""
|
|
79
|
+
|
|
80
|
+
def __init__(
|
|
81
|
+
self,
|
|
82
|
+
label: str,
|
|
83
|
+
custom_id: str = "",
|
|
84
|
+
*,
|
|
85
|
+
style: str = "primary",
|
|
86
|
+
emoji: Optional[str] = None,
|
|
87
|
+
url: Optional[str] = None,
|
|
88
|
+
disabled: bool = False,
|
|
89
|
+
):
|
|
90
|
+
self.label = str(label)[:80]
|
|
91
|
+
self.custom_id = str(custom_id)[:100]
|
|
92
|
+
self.style = style
|
|
93
|
+
self.emoji = emoji
|
|
94
|
+
self.url = url
|
|
95
|
+
self.disabled = disabled
|
|
96
|
+
|
|
97
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
98
|
+
d: Dict[str, Any] = {
|
|
99
|
+
"type": _COMPONENT_BUTTON,
|
|
100
|
+
"style": _BUTTON_STYLES.get(self.style, 1),
|
|
101
|
+
"label": self.label,
|
|
102
|
+
}
|
|
103
|
+
if self.style == "link" and self.url:
|
|
104
|
+
d["url"] = self.url
|
|
105
|
+
elif self.custom_id:
|
|
106
|
+
d["custom_id"] = self.custom_id
|
|
107
|
+
if self.emoji:
|
|
108
|
+
if isinstance(self.emoji, str):
|
|
109
|
+
d["emoji"] = {"name": self.emoji}
|
|
110
|
+
elif isinstance(self.emoji, dict):
|
|
111
|
+
d["emoji"] = self.emoji
|
|
112
|
+
if self.disabled:
|
|
113
|
+
d["disabled"] = True
|
|
114
|
+
return d
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
class SelectOption:
|
|
118
|
+
"""A single option within a SelectMenu.
|
|
119
|
+
|
|
120
|
+
Args:
|
|
121
|
+
label: Display text (max 100 chars).
|
|
122
|
+
value: Developer-defined value returned on select (max 100 chars).
|
|
123
|
+
description: Optional secondary text (max 100 chars).
|
|
124
|
+
emoji: Optional emoji string.
|
|
125
|
+
default: Whether this option is pre-selected.
|
|
126
|
+
"""
|
|
127
|
+
|
|
128
|
+
def __init__(
|
|
129
|
+
self,
|
|
130
|
+
label: str,
|
|
131
|
+
value: str,
|
|
132
|
+
*,
|
|
133
|
+
description: Optional[str] = None,
|
|
134
|
+
emoji: Optional[str] = None,
|
|
135
|
+
default: bool = False,
|
|
136
|
+
):
|
|
137
|
+
self.label = str(label)[:100]
|
|
138
|
+
self.value = str(value)[:100]
|
|
139
|
+
self.description = str(description)[:100] if description else None
|
|
140
|
+
self.emoji = emoji
|
|
141
|
+
self.default = default
|
|
142
|
+
|
|
143
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
144
|
+
d: Dict[str, Any] = {"label": self.label, "value": self.value}
|
|
145
|
+
if self.description:
|
|
146
|
+
d["description"] = self.description
|
|
147
|
+
if self.emoji:
|
|
148
|
+
if isinstance(self.emoji, str):
|
|
149
|
+
d["emoji"] = {"name": self.emoji}
|
|
150
|
+
elif isinstance(self.emoji, dict):
|
|
151
|
+
d["emoji"] = self.emoji
|
|
152
|
+
if self.default:
|
|
153
|
+
d["default"] = True
|
|
154
|
+
return d
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
class SelectMenu:
|
|
158
|
+
"""A dropdown select menu component.
|
|
159
|
+
|
|
160
|
+
Args:
|
|
161
|
+
custom_id: Developer-defined ID for handling selections (max 100 chars).
|
|
162
|
+
options: List of SelectOption objects (max 25).
|
|
163
|
+
placeholder: Greyed-out text when nothing is selected (max 150 chars).
|
|
164
|
+
min_values: Minimum selections required (default 1).
|
|
165
|
+
max_values: Maximum selections allowed (default 1).
|
|
166
|
+
disabled: Whether the menu is greyed out.
|
|
167
|
+
"""
|
|
168
|
+
|
|
169
|
+
def __init__(
|
|
170
|
+
self,
|
|
171
|
+
custom_id: str,
|
|
172
|
+
options: Optional[List[SelectOption]] = None,
|
|
173
|
+
*,
|
|
174
|
+
placeholder: str = "",
|
|
175
|
+
min_values: int = 1,
|
|
176
|
+
max_values: int = 1,
|
|
177
|
+
disabled: bool = False,
|
|
178
|
+
):
|
|
179
|
+
self.custom_id = str(custom_id)[:100]
|
|
180
|
+
self.options = (options or [])[:25]
|
|
181
|
+
self.placeholder = str(placeholder)[:150]
|
|
182
|
+
self.min_values = min_values
|
|
183
|
+
self.max_values = max_values
|
|
184
|
+
self.disabled = disabled
|
|
185
|
+
|
|
186
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
187
|
+
d: Dict[str, Any] = {
|
|
188
|
+
"type": _COMPONENT_SELECT_MENU,
|
|
189
|
+
"custom_id": self.custom_id,
|
|
190
|
+
"options": [o.to_dict() for o in self.options],
|
|
191
|
+
}
|
|
192
|
+
if self.placeholder:
|
|
193
|
+
d["placeholder"] = self.placeholder
|
|
194
|
+
if self.min_values != 1:
|
|
195
|
+
d["min_values"] = self.min_values
|
|
196
|
+
if self.max_values != 1:
|
|
197
|
+
d["max_values"] = self.max_values
|
|
198
|
+
if self.disabled:
|
|
199
|
+
d["disabled"] = True
|
|
200
|
+
return d
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
class TextInput:
|
|
204
|
+
"""A text input field for modal dialogs.
|
|
205
|
+
|
|
206
|
+
Args:
|
|
207
|
+
label: Input label shown to the user (max 45 chars).
|
|
208
|
+
custom_id: Developer-defined ID (max 100 chars).
|
|
209
|
+
style: "short" (single line) or "paragraph" (multi-line).
|
|
210
|
+
placeholder: Greyed-out placeholder text (max 100 chars).
|
|
211
|
+
value: Pre-filled value (max 4000 chars).
|
|
212
|
+
required: Whether the field must be filled.
|
|
213
|
+
min_length: Minimum input length (0-4000).
|
|
214
|
+
max_length: Maximum input length (1-4000).
|
|
215
|
+
"""
|
|
216
|
+
|
|
217
|
+
def __init__(
|
|
218
|
+
self,
|
|
219
|
+
label: str,
|
|
220
|
+
custom_id: str,
|
|
221
|
+
*,
|
|
222
|
+
style: str = "short",
|
|
223
|
+
placeholder: str = "",
|
|
224
|
+
value: str = "",
|
|
225
|
+
required: bool = True,
|
|
226
|
+
min_length: Optional[int] = None,
|
|
227
|
+
max_length: Optional[int] = None,
|
|
228
|
+
):
|
|
229
|
+
self.label = str(label)[:45]
|
|
230
|
+
self.custom_id = str(custom_id)[:100]
|
|
231
|
+
self.style = style
|
|
232
|
+
self.placeholder = str(placeholder)[:100]
|
|
233
|
+
self.value = str(value)[:4000] if value else ""
|
|
234
|
+
self.required = required
|
|
235
|
+
self.min_length = min_length
|
|
236
|
+
self.max_length = max_length
|
|
237
|
+
|
|
238
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
239
|
+
d: Dict[str, Any] = {
|
|
240
|
+
"type": _COMPONENT_TEXT_INPUT,
|
|
241
|
+
"custom_id": self.custom_id,
|
|
242
|
+
"style": _TEXT_INPUT_STYLES.get(self.style, 1),
|
|
243
|
+
"label": self.label,
|
|
244
|
+
}
|
|
245
|
+
if self.placeholder:
|
|
246
|
+
d["placeholder"] = self.placeholder
|
|
247
|
+
if self.value:
|
|
248
|
+
d["value"] = self.value
|
|
249
|
+
d["required"] = self.required
|
|
250
|
+
if self.min_length is not None:
|
|
251
|
+
d["min_length"] = self.min_length
|
|
252
|
+
if self.max_length is not None:
|
|
253
|
+
d["max_length"] = self.max_length
|
|
254
|
+
return d
|
|
255
|
+
|
|
256
|
+
|
|
257
|
+
class ActionRow:
|
|
258
|
+
"""A container row for up to 5 components.
|
|
259
|
+
|
|
260
|
+
An ActionRow can contain either:
|
|
261
|
+
- Up to 5 Button components, OR
|
|
262
|
+
- 1 SelectMenu component, OR
|
|
263
|
+
- 1 TextInput component (in modals only)
|
|
264
|
+
|
|
265
|
+
Args:
|
|
266
|
+
*children: Component objects (Button, SelectMenu, or TextInput).
|
|
267
|
+
"""
|
|
268
|
+
|
|
269
|
+
def __init__(self, *children):
|
|
270
|
+
self.children = list(children)[:5]
|
|
271
|
+
|
|
272
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
273
|
+
return {
|
|
274
|
+
"type": _COMPONENT_ACTION_ROW,
|
|
275
|
+
"components": [c.to_dict() for c in self.children],
|
|
276
|
+
}
|