loopbot-discord-sdk 1.0.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.
@@ -0,0 +1,40 @@
1
+ """
2
+ Action Row Builder for grouping components
3
+ """
4
+
5
+ from typing import Any, Dict, List, Union
6
+
7
+ from .button import ButtonBuilder
8
+ from .select_menu import SelectMenuBuilder
9
+
10
+
11
+ class ActionRowBuilder:
12
+ """Builder for Discord action rows"""
13
+
14
+ def __init__(self):
15
+ self._components: List[Dict[str, Any]] = []
16
+
17
+ def add_button(self, button: ButtonBuilder) -> "ActionRowBuilder":
18
+ """Add a button to the row"""
19
+ self._components.append(button.to_dict())
20
+ return self
21
+
22
+ def add_select_menu(self, select: "SelectMenuBuilder") -> "ActionRowBuilder":
23
+ """Add a select menu to the row"""
24
+ self._components.append(select.to_dict())
25
+ return self
26
+
27
+ def add_component(self, component: Union[Dict[str, Any], Any]) -> "ActionRowBuilder":
28
+ """Add a component to the row"""
29
+ if hasattr(component, "to_dict"):
30
+ self._components.append(component.to_dict())
31
+ else:
32
+ self._components.append(component)
33
+ return self
34
+
35
+ def to_dict(self) -> Dict[str, Any]:
36
+ """Convert to Discord API format"""
37
+ return {
38
+ "type": 1,
39
+ "components": self._components,
40
+ }
@@ -0,0 +1,58 @@
1
+ """
2
+ Button Builder for Discord buttons
3
+ """
4
+
5
+ from enum import IntEnum
6
+ from typing import Any, Dict, Optional
7
+
8
+
9
+ class ButtonStyle(IntEnum):
10
+ PRIMARY = 1
11
+ SECONDARY = 2
12
+ SUCCESS = 3
13
+ DANGER = 4
14
+ LINK = 5
15
+
16
+
17
+ class ButtonBuilder:
18
+ """Builder for Discord buttons"""
19
+
20
+ def __init__(self):
21
+ self._button: Dict[str, Any] = {"type": 2}
22
+
23
+ def set_custom_id(self, custom_id: str) -> "ButtonBuilder":
24
+ """Set the button custom ID"""
25
+ self._button["custom_id"] = custom_id
26
+ return self
27
+
28
+ def set_label(self, label: str) -> "ButtonBuilder":
29
+ """Set the button label"""
30
+ self._button["label"] = label
31
+ return self
32
+
33
+ def set_style(self, style: ButtonStyle) -> "ButtonBuilder":
34
+ """Set the button style"""
35
+ self._button["style"] = int(style)
36
+ return self
37
+
38
+ def set_emoji(self, name: str, id: Optional[str] = None) -> "ButtonBuilder":
39
+ """Set the button emoji"""
40
+ self._button["emoji"] = {"name": name}
41
+ if id:
42
+ self._button["emoji"]["id"] = id
43
+ return self
44
+
45
+ def set_url(self, url: str) -> "ButtonBuilder":
46
+ """Set the button URL (for link buttons)"""
47
+ self._button["url"] = url
48
+ self._button["style"] = int(ButtonStyle.LINK)
49
+ return self
50
+
51
+ def set_disabled(self, disabled: bool = True) -> "ButtonBuilder":
52
+ """Set whether the button is disabled"""
53
+ self._button["disabled"] = disabled
54
+ return self
55
+
56
+ def to_dict(self) -> Dict[str, Any]:
57
+ """Convert to Discord API format"""
58
+ return self._button
@@ -0,0 +1,61 @@
1
+ """
2
+ Container Builder for Discord Components V2
3
+ """
4
+
5
+ from typing import Any, Dict, List, Union
6
+
7
+
8
+ class ContainerBuilder:
9
+ """Builder for Discord Container components (requires IS_COMPONENTS_V2 flag)"""
10
+
11
+ def __init__(self):
12
+ self._components: List[Dict[str, Any]] = []
13
+ self._accent_color: int | None = None
14
+ self._spoiler: bool = False
15
+
16
+ def add_component(self, component: Union[Dict[str, Any], Any]) -> "ContainerBuilder":
17
+ """Add a child component"""
18
+ if hasattr(component, "to_dict"):
19
+ self._components.append(component.to_dict())
20
+ else:
21
+ self._components.append(component)
22
+ return self
23
+
24
+ def add_text(self, content: str) -> "ContainerBuilder":
25
+ """Add a text display component"""
26
+ self._components.append({
27
+ "type": 10,
28
+ "content": content,
29
+ })
30
+ return self
31
+
32
+ def add_separator(self, divider: bool = True, spacing: int = 1) -> "ContainerBuilder":
33
+ """Add a separator component"""
34
+ self._components.append({
35
+ "type": 14,
36
+ "divider": divider,
37
+ "spacing": spacing,
38
+ })
39
+ return self
40
+
41
+ def set_accent_color(self, color: int) -> "ContainerBuilder":
42
+ """Set the accent color (RGB value)"""
43
+ self._accent_color = color
44
+ return self
45
+
46
+ def set_spoiler(self, spoiler: bool = True) -> "ContainerBuilder":
47
+ """Set whether container is a spoiler"""
48
+ self._spoiler = spoiler
49
+ return self
50
+
51
+ def to_dict(self) -> Dict[str, Any]:
52
+ """Convert to Discord API format"""
53
+ result: Dict[str, Any] = {
54
+ "type": 17,
55
+ "components": self._components,
56
+ }
57
+ if self._accent_color is not None:
58
+ result["accent_color"] = self._accent_color
59
+ if self._spoiler:
60
+ result["spoiler"] = self._spoiler
61
+ return result
@@ -0,0 +1,95 @@
1
+ """
2
+ Embed Builder for Discord rich embeds
3
+ """
4
+
5
+ from datetime import datetime
6
+ from typing import Any, Dict, List, Optional
7
+
8
+
9
+ class EmbedBuilder:
10
+ """Builder for Discord embeds"""
11
+
12
+ def __init__(self):
13
+ self._embed: Dict[str, Any] = {}
14
+
15
+ def set_title(self, title: str) -> "EmbedBuilder":
16
+ """Set the embed title"""
17
+ self._embed["title"] = title
18
+ return self
19
+
20
+ def set_description(self, description: str) -> "EmbedBuilder":
21
+ """Set the embed description"""
22
+ self._embed["description"] = description
23
+ return self
24
+
25
+ def set_url(self, url: str) -> "EmbedBuilder":
26
+ """Set the embed URL"""
27
+ self._embed["url"] = url
28
+ return self
29
+
30
+ def set_color(self, color: int) -> "EmbedBuilder":
31
+ """Set the embed color (RGB integer)"""
32
+ self._embed["color"] = color
33
+ return self
34
+
35
+ def set_timestamp(self, timestamp: Optional[datetime] = None) -> "EmbedBuilder":
36
+ """Set the embed timestamp"""
37
+ if timestamp is None:
38
+ timestamp = datetime.utcnow()
39
+ self._embed["timestamp"] = timestamp.isoformat()
40
+ return self
41
+
42
+ def set_footer(
43
+ self,
44
+ text: str,
45
+ icon_url: Optional[str] = None,
46
+ ) -> "EmbedBuilder":
47
+ """Set the embed footer"""
48
+ self._embed["footer"] = {"text": text}
49
+ if icon_url:
50
+ self._embed["footer"]["icon_url"] = icon_url
51
+ return self
52
+
53
+ def set_image(self, url: str) -> "EmbedBuilder":
54
+ """Set the embed image"""
55
+ self._embed["image"] = {"url": url}
56
+ return self
57
+
58
+ def set_thumbnail(self, url: str) -> "EmbedBuilder":
59
+ """Set the embed thumbnail"""
60
+ self._embed["thumbnail"] = {"url": url}
61
+ return self
62
+
63
+ def set_author(
64
+ self,
65
+ name: str,
66
+ url: Optional[str] = None,
67
+ icon_url: Optional[str] = None,
68
+ ) -> "EmbedBuilder":
69
+ """Set the embed author"""
70
+ self._embed["author"] = {"name": name}
71
+ if url:
72
+ self._embed["author"]["url"] = url
73
+ if icon_url:
74
+ self._embed["author"]["icon_url"] = icon_url
75
+ return self
76
+
77
+ def add_field(
78
+ self,
79
+ name: str,
80
+ value: str,
81
+ inline: bool = False,
82
+ ) -> "EmbedBuilder":
83
+ """Add a field to the embed"""
84
+ if "fields" not in self._embed:
85
+ self._embed["fields"] = []
86
+ self._embed["fields"].append({
87
+ "name": name,
88
+ "value": value,
89
+ "inline": inline,
90
+ })
91
+ return self
92
+
93
+ def to_dict(self) -> Dict[str, Any]:
94
+ """Convert to Discord API format"""
95
+ return self._embed
@@ -0,0 +1,33 @@
1
+ """
2
+ File Builder for Discord Components V2
3
+ """
4
+
5
+ from typing import Any, Dict
6
+
7
+
8
+ class FileBuilder:
9
+ """Builder for Discord File components (requires IS_COMPONENTS_V2 flag)"""
10
+
11
+ def __init__(self, url: str):
12
+ self._url: str = url
13
+ self._spoiler: bool = False
14
+
15
+ def set_url(self, url: str) -> "FileBuilder":
16
+ """Set the file URL"""
17
+ self._url = url
18
+ return self
19
+
20
+ def set_spoiler(self, spoiler: bool = True) -> "FileBuilder":
21
+ """Set whether file is a spoiler"""
22
+ self._spoiler = spoiler
23
+ return self
24
+
25
+ def to_dict(self) -> Dict[str, Any]:
26
+ """Convert to Discord API format"""
27
+ result: Dict[str, Any] = {
28
+ "type": 13,
29
+ "file": {"url": self._url},
30
+ }
31
+ if self._spoiler:
32
+ result["spoiler"] = self._spoiler
33
+ return result
@@ -0,0 +1,36 @@
1
+ """
2
+ Media Gallery Builder for Discord Components V2
3
+ """
4
+
5
+ from typing import Any, Dict, List, Optional
6
+
7
+
8
+ class MediaGalleryBuilder:
9
+ """Builder for Discord Media Gallery components (requires IS_COMPONENTS_V2 flag)"""
10
+
11
+ def __init__(self):
12
+ self._items: List[Dict[str, Any]] = []
13
+
14
+ def add_item(
15
+ self,
16
+ url: str,
17
+ description: Optional[str] = None,
18
+ spoiler: bool = False,
19
+ ) -> "MediaGalleryBuilder":
20
+ """Add a media item to the gallery"""
21
+ item: Dict[str, Any] = {
22
+ "media": {"url": url},
23
+ }
24
+ if description:
25
+ item["description"] = description
26
+ if spoiler:
27
+ item["spoiler"] = spoiler
28
+ self._items.append(item)
29
+ return self
30
+
31
+ def to_dict(self) -> Dict[str, Any]:
32
+ """Convert to Discord API format"""
33
+ return {
34
+ "type": 12,
35
+ "items": self._items,
36
+ }
@@ -0,0 +1,67 @@
1
+ """
2
+ Modal Builder for Discord modals
3
+ """
4
+
5
+ from typing import Any, Dict, List, Literal, Optional
6
+
7
+
8
+ class ModalBuilder:
9
+ """Builder for Discord modals"""
10
+
11
+ def __init__(self):
12
+ self._custom_id: str = ""
13
+ self._title: str = ""
14
+ self._components: List[Dict[str, Any]] = []
15
+
16
+ def set_custom_id(self, custom_id: str) -> "ModalBuilder":
17
+ """Set the modal custom ID"""
18
+ self._custom_id = custom_id
19
+ return self
20
+
21
+ def set_title(self, title: str) -> "ModalBuilder":
22
+ """Set the modal title"""
23
+ self._title = title
24
+ return self
25
+
26
+ def add_text_input(
27
+ self,
28
+ custom_id: str,
29
+ label: str,
30
+ style: Literal["short", "paragraph"] = "short",
31
+ required: bool = True,
32
+ min_length: Optional[int] = None,
33
+ max_length: Optional[int] = None,
34
+ placeholder: Optional[str] = None,
35
+ value: Optional[str] = None,
36
+ ) -> "ModalBuilder":
37
+ """Add a text input to the modal"""
38
+ input_data: Dict[str, Any] = {
39
+ "type": 4,
40
+ "custom_id": custom_id,
41
+ "style": 1 if style == "short" else 2,
42
+ "label": label,
43
+ "required": required,
44
+ }
45
+ if min_length is not None:
46
+ input_data["min_length"] = min_length
47
+ if max_length is not None:
48
+ input_data["max_length"] = max_length
49
+ if placeholder:
50
+ input_data["placeholder"] = placeholder
51
+ if value:
52
+ input_data["value"] = value
53
+
54
+ # Wrap in action row
55
+ self._components.append({
56
+ "type": 1,
57
+ "components": [input_data],
58
+ })
59
+ return self
60
+
61
+ def to_dict(self) -> Dict[str, Any]:
62
+ """Convert to Discord API format"""
63
+ return {
64
+ "title": self._title,
65
+ "custom_id": self._custom_id,
66
+ "components": self._components,
67
+ }
@@ -0,0 +1,52 @@
1
+ """
2
+ Section Builder for Discord Components V2
3
+ """
4
+
5
+ from typing import Any, Dict, List, Optional, Union
6
+
7
+ from .button import ButtonBuilder
8
+
9
+
10
+ class SectionBuilder:
11
+ """Builder for Discord Section components (requires IS_COMPONENTS_V2 flag)"""
12
+
13
+ def __init__(self):
14
+ self._components: List[Dict[str, Any]] = []
15
+ self._accessory: Dict[str, Any] | None = None
16
+
17
+ def add_text(self, content: str) -> "SectionBuilder":
18
+ """Add a text display component"""
19
+ self._components.append({
20
+ "type": 10,
21
+ "content": content,
22
+ })
23
+ return self
24
+
25
+ def set_button_accessory(self, button: ButtonBuilder) -> "SectionBuilder":
26
+ """Set a button as the accessory"""
27
+ self._accessory = button.to_dict()
28
+ return self
29
+
30
+ def set_thumbnail_accessory(
31
+ self,
32
+ url: str,
33
+ description: Optional[str] = None,
34
+ ) -> "SectionBuilder":
35
+ """Set a thumbnail as the accessory"""
36
+ self._accessory = {
37
+ "type": 11,
38
+ "media": {"url": url},
39
+ }
40
+ if description:
41
+ self._accessory["description"] = description
42
+ return self
43
+
44
+ def to_dict(self) -> Dict[str, Any]:
45
+ """Convert to Discord API format"""
46
+ result: Dict[str, Any] = {
47
+ "type": 9,
48
+ "components": self._components,
49
+ }
50
+ if self._accessory:
51
+ result["accessory"] = self._accessory
52
+ return result
@@ -0,0 +1,63 @@
1
+ """
2
+ Select Menu Builder for Discord dropdowns
3
+ """
4
+
5
+ from typing import Any, Dict, List, Optional
6
+
7
+
8
+ class SelectMenuBuilder:
9
+ """Builder for Discord select menus"""
10
+
11
+ def __init__(self):
12
+ self._select: Dict[str, Any] = {"type": 3, "options": []}
13
+
14
+ def set_custom_id(self, custom_id: str) -> "SelectMenuBuilder":
15
+ """Set the select menu custom ID"""
16
+ self._select["custom_id"] = custom_id
17
+ return self
18
+
19
+ def set_placeholder(self, placeholder: str) -> "SelectMenuBuilder":
20
+ """Set the placeholder text"""
21
+ self._select["placeholder"] = placeholder
22
+ return self
23
+
24
+ def set_min_values(self, min_values: int) -> "SelectMenuBuilder":
25
+ """Set minimum values to select"""
26
+ self._select["min_values"] = min_values
27
+ return self
28
+
29
+ def set_max_values(self, max_values: int) -> "SelectMenuBuilder":
30
+ """Set maximum values to select"""
31
+ self._select["max_values"] = max_values
32
+ return self
33
+
34
+ def set_disabled(self, disabled: bool = True) -> "SelectMenuBuilder":
35
+ """Set whether the select is disabled"""
36
+ self._select["disabled"] = disabled
37
+ return self
38
+
39
+ def add_option(
40
+ self,
41
+ label: str,
42
+ value: str,
43
+ description: Optional[str] = None,
44
+ emoji: Optional[Dict[str, str]] = None,
45
+ default: bool = False,
46
+ ) -> "SelectMenuBuilder":
47
+ """Add an option to the select menu"""
48
+ option: Dict[str, Any] = {
49
+ "label": label,
50
+ "value": value,
51
+ }
52
+ if description:
53
+ option["description"] = description
54
+ if emoji:
55
+ option["emoji"] = emoji
56
+ if default:
57
+ option["default"] = default
58
+ self._select["options"].append(option)
59
+ return self
60
+
61
+ def to_dict(self) -> Dict[str, Any]:
62
+ """Convert to Discord API format"""
63
+ return self._select
@@ -0,0 +1,31 @@
1
+ """
2
+ Separator Builder for Discord Components V2
3
+ """
4
+
5
+ from typing import Any, Dict, Literal
6
+
7
+
8
+ class SeparatorBuilder:
9
+ """Builder for Discord Separator components (requires IS_COMPONENTS_V2 flag)"""
10
+
11
+ def __init__(self):
12
+ self._divider: bool = True
13
+ self._spacing: int = 1
14
+
15
+ def set_divider(self, divider: bool) -> "SeparatorBuilder":
16
+ """Set whether to show visual divider"""
17
+ self._divider = divider
18
+ return self
19
+
20
+ def set_spacing(self, spacing: Literal[1, 2]) -> "SeparatorBuilder":
21
+ """Set spacing size (1 = small, 2 = large)"""
22
+ self._spacing = spacing
23
+ return self
24
+
25
+ def to_dict(self) -> Dict[str, Any]:
26
+ """Convert to Discord API format"""
27
+ return {
28
+ "type": 14,
29
+ "divider": self._divider,
30
+ "spacing": self._spacing,
31
+ }
@@ -0,0 +1,24 @@
1
+ """
2
+ Text Display Builder for Discord Components V2
3
+ """
4
+
5
+ from typing import Any, Dict
6
+
7
+
8
+ class TextDisplayBuilder:
9
+ """Builder for Discord Text Display components (requires IS_COMPONENTS_V2 flag)"""
10
+
11
+ def __init__(self, content: str = ""):
12
+ self._content: str = content
13
+
14
+ def set_content(self, content: str) -> "TextDisplayBuilder":
15
+ """Set the text content (supports markdown)"""
16
+ self._content = content
17
+ return self
18
+
19
+ def to_dict(self) -> Dict[str, Any]:
20
+ """Convert to Discord API format"""
21
+ return {
22
+ "type": 10,
23
+ "content": self._content,
24
+ }