webhookloggerx 2.0.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.
@@ -0,0 +1,284 @@
1
+ Metadata-Version: 2.4
2
+ Name: webhookloggerx
3
+ Version: 2.0.0
4
+ Summary: A clean, user-friendly Python package for sending Discord webhook messages and rich embeds.
5
+ Author: Nur Mohammad Rafi
6
+ Author-email:
7
+ Keywords: discord webhook embed notification bot
8
+ Classifier: Programming Language :: Python :: 3
9
+ Classifier: License :: OSI Approved :: MIT License
10
+ Classifier: Operating System :: OS Independent
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: Topic :: Internet :: WWW/HTTP
13
+ Classifier: Topic :: Communications
14
+ Requires-Python: >=3.7
15
+ Description-Content-Type: text/markdown
16
+ Requires-Dist: requests>=2.20.0
17
+ Dynamic: author
18
+ Dynamic: classifier
19
+ Dynamic: description
20
+ Dynamic: description-content-type
21
+ Dynamic: keywords
22
+ Dynamic: requires-dist
23
+ Dynamic: requires-python
24
+ Dynamic: summary
25
+
26
+ # webhooklogger-python-rafi
27
+
28
+ A clean, user-friendly Python package for sending Discord webhook messages and rich embeds.
29
+
30
+ ## Installation
31
+
32
+ ```bash
33
+ pip install webhookloggerx
34
+ ```
35
+
36
+ ## Quick Start
37
+
38
+ ```python
39
+ from webhooklogger import WebhookClient, Embed
40
+
41
+ client = WebhookClient("https://discord.com/api/webhooks/YOUR_ID/YOUR_TOKEN")
42
+ client.send("Hello from webhooklogger!")
43
+ ```
44
+
45
+ ---
46
+
47
+ ## Features
48
+
49
+ - ✅ Send plain text messages
50
+ - ✅ Send rich Discord embeds
51
+ - ✅ Send multiple embeds in one request
52
+ - ✅ Customise webhook display name & avatar
53
+ - ✅ Embed builder with fluent (chainable) API
54
+ - ✅ Built-in colour themes (`success`, `error`, `warning`, etc.)
55
+ - ✅ Set embed title, description, author, footer, images, timestamp, fields
56
+
57
+ ---
58
+
59
+ ## WebhookClient
60
+
61
+ ### Create a Client
62
+
63
+ ```python
64
+ from webhooklogger import WebhookClient
65
+
66
+ client = WebhookClient("https://discord.com/api/webhooks/...")
67
+ ```
68
+
69
+ ### Set Webhook Profile (name & avatar)
70
+
71
+ ```python
72
+ client.set_profile(
73
+ username="MyBot",
74
+ avatar_url="https://example.com/avatar.png"
75
+ )
76
+ ```
77
+
78
+ ### Send a Plain Text Message
79
+
80
+ ```python
81
+ client.send("This is a plain message.")
82
+ ```
83
+
84
+ ### Send an Embed
85
+
86
+ ```python
87
+ from webhooklogger import Embed
88
+
89
+ embed = Embed().set_title("Hello!").set_description("Embed body here.")
90
+ client.send_embed(embed)
91
+ ```
92
+
93
+ ### Send Multiple Embeds
94
+
95
+ ```python
96
+ embed1 = Embed().set_title("First").set_theme("success")
97
+ embed2 = Embed().set_title("Second").set_theme("error")
98
+ client.send_embeds([embed1, embed2])
99
+ ```
100
+
101
+ ### Send Text + Embed Together
102
+
103
+ ```python
104
+ embed = Embed().set_title("Alert").set_description("Check this out!")
105
+ client.send_message(content="Hey team!", embed=embed)
106
+ ```
107
+
108
+ ---
109
+
110
+ ## Embed Builder
111
+
112
+ All methods return `self`, so you can **chain** them fluently.
113
+
114
+ ### Title & Description
115
+
116
+ ```python
117
+ embed = (
118
+ Embed()
119
+ .set_title("My Title")
120
+ .set_description("This is the body of the embed.")
121
+ .set_url("https://example.com") # makes title clickable
122
+ )
123
+ ```
124
+
125
+ ### Colour
126
+
127
+ ```python
128
+ # Hex string
129
+ embed.set_color("#FF5733")
130
+
131
+ # Integer
132
+ embed.set_color(16711680)
133
+
134
+ # Named theme
135
+ embed.set_theme("success")
136
+ ```
137
+
138
+ **Available themes:**
139
+
140
+ | Theme | Colour |
141
+ |---|---|
142
+ | `default` | Discord Blurple |
143
+ | `success` | Green |
144
+ | `warning` | Yellow |
145
+ | `error` | Red |
146
+ | `info` | Blue |
147
+ | `dark` | Dark Grey |
148
+ | `light` | White |
149
+ | `purple` | Purple |
150
+ | `orange` | Orange |
151
+ | `pink` | Pink |
152
+
153
+ ### Author
154
+
155
+ ```python
156
+ embed.set_author(
157
+ name="Nur Mohammad Rafi",
158
+ url="https://github.com/yourusername",
159
+ icon_url="https://example.com/icon.png"
160
+ )
161
+ ```
162
+
163
+ ### Thumbnail & Image
164
+
165
+ ```python
166
+ embed.set_thumbnail("https://example.com/thumb.png") # top-right corner
167
+ embed.set_image("https://example.com/big-image.png") # large bottom image
168
+ ```
169
+
170
+ ### Fields
171
+
172
+ ```python
173
+ embed.add_field("Status", "Online", inline=True)
174
+ embed.add_field("Server", "Production", inline=True)
175
+ embed.add_field("Message", "All systems go.", inline=False)
176
+ ```
177
+
178
+ - `inline=True` — fields sit side by side (up to 3 per row)
179
+ - `inline=False` — field takes the full width
180
+ - Max **25 fields** per embed
181
+
182
+ ### Footer
183
+
184
+ ```python
185
+ embed.set_footer("webhooklogger v1.0", icon_url="https://example.com/icon.png")
186
+ ```
187
+
188
+ ### Timestamp
189
+
190
+ ```python
191
+ embed.set_timestamp() # use current UTC time
192
+ ```
193
+
194
+ ```python
195
+ from datetime import datetime, timezone
196
+ embed.set_timestamp(datetime(2025, 1, 1, tzinfo=timezone.utc)) # custom time
197
+ ```
198
+
199
+ ---
200
+
201
+ ## Full Example
202
+
203
+ ```python
204
+ from webhooklogger import WebhookClient, Embed
205
+
206
+ # Create and configure client
207
+ client = WebhookClient("https://discord.com/api/webhooks/...")
208
+ client.set_profile(username="AlertBot", avatar_url="https://example.com/bot.png")
209
+
210
+ # Build embed
211
+ embed = (
212
+ Embed()
213
+ .set_title("🚨 System Alert")
214
+ .set_description("A critical event has been detected on the server.")
215
+ .set_theme("error")
216
+ .set_author("Monitor System", icon_url="https://example.com/icon.png")
217
+ .set_thumbnail("https://example.com/thumb.png")
218
+ .add_field("Host", "prod-server-01", inline=True)
219
+ .add_field("Status", "❌ Down", inline=True)
220
+ .add_field("Region", "US-East", inline=True)
221
+ .add_field("Details", "Connection timeout after 30s", inline=False)
222
+ .set_image("https://example.com/graph.png")
223
+ .set_footer("webhooklogger • Auto-alert system")
224
+ .set_timestamp()
225
+ )
226
+
227
+ # Send it
228
+ client.send_embed(embed)
229
+ ```
230
+
231
+ ---
232
+
233
+ ## Error Handling
234
+
235
+ ```python
236
+ from webhooklogger import WebhookClient, WebhookError
237
+
238
+ client = WebhookClient("https://discord.com/api/webhooks/...")
239
+
240
+ try:
241
+ client.send("Test message")
242
+ except WebhookError as e:
243
+ print(f"Failed: HTTP {e.status_code} — {e.message}")
244
+ ```
245
+
246
+ ---
247
+
248
+ ## API Reference
249
+
250
+ ### `WebhookClient`
251
+
252
+ | Method | Description |
253
+ |---|---|
254
+ | `__init__(url)` | Create client with webhook URL |
255
+ | `set_profile(username, avatar_url)` | Override display name & avatar |
256
+ | `send(content)` | Send plain text message |
257
+ | `send_embed(embed)` | Send a single Embed |
258
+ | `send_embeds(embeds)` | Send a list of Embeds (max 10) |
259
+ | `send_message(content, embed)` | Send text + embed together |
260
+
261
+ ### `Embed`
262
+
263
+ | Method | Description |
264
+ |---|---|
265
+ | `set_title(title)` | Embed title |
266
+ | `set_description(text)` | Embed body text |
267
+ | `set_url(url)` | Make title a hyperlink |
268
+ | `set_color(color)` | Hex string or int |
269
+ | `set_colour(color)` | Alias for `set_color` |
270
+ | `set_theme(theme)` | Named colour theme |
271
+ | `set_author(name, url, icon_url)` | Author block |
272
+ | `set_thumbnail(url)` | Small top-right image |
273
+ | `set_image(url)` | Large bottom image |
274
+ | `set_footer(text, icon_url)` | Footer text & icon |
275
+ | `set_timestamp(dt)` | Timestamp (default: now) |
276
+ | `add_field(name, value, inline)` | Add a field |
277
+ | `clear_fields()` | Remove all fields |
278
+ | `build()` | Return raw dict for API |
279
+
280
+ ---
281
+
282
+ ## License
283
+
284
+ MIT © Nur Mohammad Rafi
@@ -0,0 +1,259 @@
1
+ # webhooklogger-python-rafi
2
+
3
+ A clean, user-friendly Python package for sending Discord webhook messages and rich embeds.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ pip install webhookloggerx
9
+ ```
10
+
11
+ ## Quick Start
12
+
13
+ ```python
14
+ from webhooklogger import WebhookClient, Embed
15
+
16
+ client = WebhookClient("https://discord.com/api/webhooks/YOUR_ID/YOUR_TOKEN")
17
+ client.send("Hello from webhooklogger!")
18
+ ```
19
+
20
+ ---
21
+
22
+ ## Features
23
+
24
+ - ✅ Send plain text messages
25
+ - ✅ Send rich Discord embeds
26
+ - ✅ Send multiple embeds in one request
27
+ - ✅ Customise webhook display name & avatar
28
+ - ✅ Embed builder with fluent (chainable) API
29
+ - ✅ Built-in colour themes (`success`, `error`, `warning`, etc.)
30
+ - ✅ Set embed title, description, author, footer, images, timestamp, fields
31
+
32
+ ---
33
+
34
+ ## WebhookClient
35
+
36
+ ### Create a Client
37
+
38
+ ```python
39
+ from webhooklogger import WebhookClient
40
+
41
+ client = WebhookClient("https://discord.com/api/webhooks/...")
42
+ ```
43
+
44
+ ### Set Webhook Profile (name & avatar)
45
+
46
+ ```python
47
+ client.set_profile(
48
+ username="MyBot",
49
+ avatar_url="https://example.com/avatar.png"
50
+ )
51
+ ```
52
+
53
+ ### Send a Plain Text Message
54
+
55
+ ```python
56
+ client.send("This is a plain message.")
57
+ ```
58
+
59
+ ### Send an Embed
60
+
61
+ ```python
62
+ from webhooklogger import Embed
63
+
64
+ embed = Embed().set_title("Hello!").set_description("Embed body here.")
65
+ client.send_embed(embed)
66
+ ```
67
+
68
+ ### Send Multiple Embeds
69
+
70
+ ```python
71
+ embed1 = Embed().set_title("First").set_theme("success")
72
+ embed2 = Embed().set_title("Second").set_theme("error")
73
+ client.send_embeds([embed1, embed2])
74
+ ```
75
+
76
+ ### Send Text + Embed Together
77
+
78
+ ```python
79
+ embed = Embed().set_title("Alert").set_description("Check this out!")
80
+ client.send_message(content="Hey team!", embed=embed)
81
+ ```
82
+
83
+ ---
84
+
85
+ ## Embed Builder
86
+
87
+ All methods return `self`, so you can **chain** them fluently.
88
+
89
+ ### Title & Description
90
+
91
+ ```python
92
+ embed = (
93
+ Embed()
94
+ .set_title("My Title")
95
+ .set_description("This is the body of the embed.")
96
+ .set_url("https://example.com") # makes title clickable
97
+ )
98
+ ```
99
+
100
+ ### Colour
101
+
102
+ ```python
103
+ # Hex string
104
+ embed.set_color("#FF5733")
105
+
106
+ # Integer
107
+ embed.set_color(16711680)
108
+
109
+ # Named theme
110
+ embed.set_theme("success")
111
+ ```
112
+
113
+ **Available themes:**
114
+
115
+ | Theme | Colour |
116
+ |---|---|
117
+ | `default` | Discord Blurple |
118
+ | `success` | Green |
119
+ | `warning` | Yellow |
120
+ | `error` | Red |
121
+ | `info` | Blue |
122
+ | `dark` | Dark Grey |
123
+ | `light` | White |
124
+ | `purple` | Purple |
125
+ | `orange` | Orange |
126
+ | `pink` | Pink |
127
+
128
+ ### Author
129
+
130
+ ```python
131
+ embed.set_author(
132
+ name="Nur Mohammad Rafi",
133
+ url="https://github.com/yourusername",
134
+ icon_url="https://example.com/icon.png"
135
+ )
136
+ ```
137
+
138
+ ### Thumbnail & Image
139
+
140
+ ```python
141
+ embed.set_thumbnail("https://example.com/thumb.png") # top-right corner
142
+ embed.set_image("https://example.com/big-image.png") # large bottom image
143
+ ```
144
+
145
+ ### Fields
146
+
147
+ ```python
148
+ embed.add_field("Status", "Online", inline=True)
149
+ embed.add_field("Server", "Production", inline=True)
150
+ embed.add_field("Message", "All systems go.", inline=False)
151
+ ```
152
+
153
+ - `inline=True` — fields sit side by side (up to 3 per row)
154
+ - `inline=False` — field takes the full width
155
+ - Max **25 fields** per embed
156
+
157
+ ### Footer
158
+
159
+ ```python
160
+ embed.set_footer("webhooklogger v1.0", icon_url="https://example.com/icon.png")
161
+ ```
162
+
163
+ ### Timestamp
164
+
165
+ ```python
166
+ embed.set_timestamp() # use current UTC time
167
+ ```
168
+
169
+ ```python
170
+ from datetime import datetime, timezone
171
+ embed.set_timestamp(datetime(2025, 1, 1, tzinfo=timezone.utc)) # custom time
172
+ ```
173
+
174
+ ---
175
+
176
+ ## Full Example
177
+
178
+ ```python
179
+ from webhooklogger import WebhookClient, Embed
180
+
181
+ # Create and configure client
182
+ client = WebhookClient("https://discord.com/api/webhooks/...")
183
+ client.set_profile(username="AlertBot", avatar_url="https://example.com/bot.png")
184
+
185
+ # Build embed
186
+ embed = (
187
+ Embed()
188
+ .set_title("🚨 System Alert")
189
+ .set_description("A critical event has been detected on the server.")
190
+ .set_theme("error")
191
+ .set_author("Monitor System", icon_url="https://example.com/icon.png")
192
+ .set_thumbnail("https://example.com/thumb.png")
193
+ .add_field("Host", "prod-server-01", inline=True)
194
+ .add_field("Status", "❌ Down", inline=True)
195
+ .add_field("Region", "US-East", inline=True)
196
+ .add_field("Details", "Connection timeout after 30s", inline=False)
197
+ .set_image("https://example.com/graph.png")
198
+ .set_footer("webhooklogger • Auto-alert system")
199
+ .set_timestamp()
200
+ )
201
+
202
+ # Send it
203
+ client.send_embed(embed)
204
+ ```
205
+
206
+ ---
207
+
208
+ ## Error Handling
209
+
210
+ ```python
211
+ from webhooklogger import WebhookClient, WebhookError
212
+
213
+ client = WebhookClient("https://discord.com/api/webhooks/...")
214
+
215
+ try:
216
+ client.send("Test message")
217
+ except WebhookError as e:
218
+ print(f"Failed: HTTP {e.status_code} — {e.message}")
219
+ ```
220
+
221
+ ---
222
+
223
+ ## API Reference
224
+
225
+ ### `WebhookClient`
226
+
227
+ | Method | Description |
228
+ |---|---|
229
+ | `__init__(url)` | Create client with webhook URL |
230
+ | `set_profile(username, avatar_url)` | Override display name & avatar |
231
+ | `send(content)` | Send plain text message |
232
+ | `send_embed(embed)` | Send a single Embed |
233
+ | `send_embeds(embeds)` | Send a list of Embeds (max 10) |
234
+ | `send_message(content, embed)` | Send text + embed together |
235
+
236
+ ### `Embed`
237
+
238
+ | Method | Description |
239
+ |---|---|
240
+ | `set_title(title)` | Embed title |
241
+ | `set_description(text)` | Embed body text |
242
+ | `set_url(url)` | Make title a hyperlink |
243
+ | `set_color(color)` | Hex string or int |
244
+ | `set_colour(color)` | Alias for `set_color` |
245
+ | `set_theme(theme)` | Named colour theme |
246
+ | `set_author(name, url, icon_url)` | Author block |
247
+ | `set_thumbnail(url)` | Small top-right image |
248
+ | `set_image(url)` | Large bottom image |
249
+ | `set_footer(text, icon_url)` | Footer text & icon |
250
+ | `set_timestamp(dt)` | Timestamp (default: now) |
251
+ | `add_field(name, value, inline)` | Add a field |
252
+ | `clear_fields()` | Remove all fields |
253
+ | `build()` | Return raw dict for API |
254
+
255
+ ---
256
+
257
+ ## License
258
+
259
+ MIT © Nur Mohammad Rafi
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,29 @@
1
+ from setuptools import setup, find_packages
2
+
3
+ with open("README.md", "r", encoding="utf-8") as f:
4
+ long_description = f.read()
5
+
6
+ setup(
7
+ name="webhookloggerx",
8
+ version="2.0.0",
9
+ packages=find_packages(),
10
+ install_requires=[
11
+ "requests>=2.20.0",
12
+ ],
13
+ author="Nur Mohammad Rafi",
14
+ author_email="",
15
+ description="A clean, user-friendly Python package for sending Discord webhook messages and rich embeds.",
16
+ long_description=long_description,
17
+ long_description_content_type="text/markdown",
18
+ # url="https://github.com/yourusername/webhooklogger",
19
+ classifiers=[
20
+ "Programming Language :: Python :: 3",
21
+ "License :: OSI Approved :: MIT License",
22
+ "Operating System :: OS Independent",
23
+ "Intended Audience :: Developers",
24
+ "Topic :: Internet :: WWW/HTTP",
25
+ "Topic :: Communications",
26
+ ],
27
+ python_requires=">=3.7",
28
+ keywords="discord webhook embed notification bot",
29
+ )
@@ -0,0 +1,32 @@
1
+ """
2
+ webhooklogger
3
+ ~~~~~~~~~~~~~
4
+ A clean, user-friendly Python package for sending Discord webhook messages
5
+ and rich embeds.
6
+
7
+ Usage::
8
+
9
+ from webhooklogger import WebhookClient, Embed
10
+
11
+ client = WebhookClient("https://discord.com/api/webhooks/...")
12
+ client.set_profile(username="MyBot")
13
+ client.send("Hello from webhooklogger!")
14
+
15
+ embed = (
16
+ Embed()
17
+ .set_title("Notification")
18
+ .set_theme("success")
19
+ .set_description("Everything is running smoothly.")
20
+ .set_timestamp()
21
+ )
22
+ client.send_embed(embed)
23
+
24
+ :license: MIT
25
+ """
26
+
27
+ from .client import WebhookClient, WebhookError
28
+ from .embed import Embed
29
+
30
+ __all__ = ["WebhookClient", "WebhookError", "Embed"]
31
+ __version__ = "1.0.0"
32
+ __author__ = "Nur Mohammad Rafi"
@@ -0,0 +1,179 @@
1
+ """
2
+ webhooklogger.client
3
+ ~~~~~~~~~~~~~~~~~~~~
4
+ WebhookClient — send messages and embeds to Discord webhooks.
5
+ """
6
+
7
+ import requests
8
+ from .embed import Embed
9
+
10
+
11
+ class WebhookError(Exception):
12
+ """Raised when the Discord API returns a non-2xx response."""
13
+
14
+ def __init__(self, status_code: int, message: str):
15
+ self.status_code = status_code
16
+ self.message = message
17
+ super().__init__(f"[HTTP {status_code}] {message}")
18
+
19
+
20
+ class WebhookClient:
21
+ """
22
+ Main client for sending data to a Discord webhook URL.
23
+
24
+ Example::
25
+
26
+ from webhooklogger import WebhookClient, Embed
27
+
28
+ client = WebhookClient("https://discord.com/api/webhooks/...")
29
+ client.set_profile(username="MyBot", avatar_url="https://...")
30
+ client.send("Hello, world!")
31
+
32
+ embed = (
33
+ Embed()
34
+ .set_title("Alert")
35
+ .set_color("error")
36
+ .set_description("Something went wrong!")
37
+ .set_timestamp()
38
+ )
39
+ client.send_embed(embed)
40
+ """
41
+
42
+ BASE_URL = "https://discord.com/api/webhooks/"
43
+
44
+ def __init__(self, url: str):
45
+ """
46
+ :param url: Full Discord webhook URL.
47
+ """
48
+ if not url or "discord.com/api/webhooks" not in url:
49
+ raise ValueError(
50
+ "Invalid webhook URL. "
51
+ "It must be a Discord webhook URL "
52
+ "(https://discord.com/api/webhooks/...)."
53
+ )
54
+ self._url = url.rstrip("/") + "?wait=true"
55
+ self._username: str = None
56
+ self._avatar_url: str = None
57
+
58
+ # ------------------------------------------------------------------ #
59
+ # Profile #
60
+ # ------------------------------------------------------------------ #
61
+
62
+ def set_profile(
63
+ self,
64
+ username: str = None,
65
+ avatar_url: str = None,
66
+ ) -> "WebhookClient":
67
+ """
68
+ Override the webhook's display name and/or avatar.
69
+
70
+ :param username: Custom display name shown in Discord.
71
+ :param avatar_url: URL to an image used as the avatar.
72
+
73
+ Returns ``self`` so you can chain calls::
74
+
75
+ client.set_profile(username="AlertBot", avatar_url="https://...")
76
+ """
77
+ if username:
78
+ self._username = str(username)
79
+ if avatar_url:
80
+ self._avatar_url = str(avatar_url)
81
+ return self
82
+
83
+ # ------------------------------------------------------------------ #
84
+ # Internal helpers #
85
+ # ------------------------------------------------------------------ #
86
+
87
+ def _base_payload(self) -> dict:
88
+ payload = {}
89
+ if self._username:
90
+ payload["username"] = self._username
91
+ if self._avatar_url:
92
+ payload["avatar_url"] = self._avatar_url
93
+ return payload
94
+
95
+ def _post(self, payload: dict) -> dict:
96
+ """Send the payload and raise WebhookError on failure."""
97
+ response = requests.post(self._url, json=payload, timeout=10)
98
+ if not response.ok:
99
+ try:
100
+ detail = response.json().get("message", response.text)
101
+ except Exception:
102
+ detail = response.text
103
+ raise WebhookError(response.status_code, detail)
104
+ return response.json()
105
+
106
+ # ------------------------------------------------------------------ #
107
+ # Public send methods #
108
+ # ------------------------------------------------------------------ #
109
+
110
+ def send(self, content: str) -> dict:
111
+ """
112
+ Send a plain text message.
113
+
114
+ :param content: The message text (max 2000 chars).
115
+ :returns: The response dict from Discord.
116
+ """
117
+ if not content:
118
+ raise ValueError("content cannot be empty")
119
+ payload = self._base_payload()
120
+ payload["content"] = str(content)
121
+ return self._post(payload)
122
+
123
+ def send_embed(self, embed: Embed) -> dict:
124
+ """
125
+ Send a single :class:`~webhooklogger.Embed`.
126
+
127
+ :param embed: An :class:`~webhooklogger.Embed` instance.
128
+ :returns: The response dict from Discord.
129
+ """
130
+ if not isinstance(embed, Embed):
131
+ raise TypeError("embed must be an Embed instance")
132
+ payload = self._base_payload()
133
+ payload["embeds"] = [embed.build()]
134
+ return self._post(payload)
135
+
136
+ def send_embeds(self, embeds: list) -> dict:
137
+ """
138
+ Send multiple embeds in a single request (up to 10).
139
+
140
+ :param embeds: A list of :class:`~webhooklogger.Embed` instances.
141
+ :returns: The response dict from Discord.
142
+ """
143
+ if not embeds:
144
+ raise ValueError("embeds list cannot be empty")
145
+ if len(embeds) > 10:
146
+ raise ValueError("Discord allows a maximum of 10 embeds per message")
147
+ for i, e in enumerate(embeds):
148
+ if not isinstance(e, Embed):
149
+ raise TypeError(f"Item at index {i} is not an Embed instance")
150
+ payload = self._base_payload()
151
+ payload["embeds"] = [e.build() for e in embeds]
152
+ return self._post(payload)
153
+
154
+ def send_message(self, content: str = None, embed: Embed = None) -> dict:
155
+ """
156
+ Send a message with optional text content AND an embed together.
157
+
158
+ :param content: Optional plain text message.
159
+ :param embed: Optional :class:`~webhooklogger.Embed`.
160
+ :returns: The response dict from Discord.
161
+ """
162
+ if content is None and embed is None:
163
+ raise ValueError("Provide at least content or an embed")
164
+ payload = self._base_payload()
165
+ if content:
166
+ payload["content"] = str(content)
167
+ if embed:
168
+ if not isinstance(embed, Embed):
169
+ raise TypeError("embed must be an Embed instance")
170
+ payload["embeds"] = [embed.build()]
171
+ return self._post(payload)
172
+
173
+ # ------------------------------------------------------------------ #
174
+ # Repr #
175
+ # ------------------------------------------------------------------ #
176
+
177
+ def __repr__(self) -> str:
178
+ name = self._username or "<default>"
179
+ return f"<WebhookClient username={name!r}>"
@@ -0,0 +1,209 @@
1
+ """
2
+ webhooklogger.embed
3
+ ~~~~~~~~~~~~~~~~~~~
4
+ Chainable Embed builder for Discord webhooks.
5
+ """
6
+
7
+ from datetime import datetime, timezone
8
+
9
+
10
+ class Embed:
11
+ """
12
+ Build a Discord embed with a clean, fluent interface.
13
+
14
+ Example::
15
+
16
+ embed = (
17
+ Embed()
18
+ .set_title("Hello!")
19
+ .set_description("This is a webhook embed.")
20
+ .set_color("#5865F2")
21
+ .add_field("Status", "Online", inline=True)
22
+ .set_footer("Sent by webhooklogger")
23
+ .set_timestamp()
24
+ )
25
+ """
26
+
27
+ # ------------------------------------------------------------------ #
28
+ # Preset colour themes #
29
+ # ------------------------------------------------------------------ #
30
+ THEMES = {
31
+ "default": 0x5865F2, # Discord blurple
32
+ "success": 0x57F287, # green
33
+ "warning": 0xFEE75C, # yellow
34
+ "error": 0xED4245, # red
35
+ "info": 0x5DADE2, # blue
36
+ "dark": 0x2C2F33, # dark grey
37
+ "light": 0xFFFFFF, # white
38
+ "purple": 0x9B59B6,
39
+ "orange": 0xE67E22,
40
+ "pink": 0xFF69B4,
41
+ }
42
+
43
+ def __init__(self):
44
+ self._data = {}
45
+
46
+ # ------------------------------------------------------------------ #
47
+ # Core setters #
48
+ # ------------------------------------------------------------------ #
49
+
50
+ def set_title(self, title: str) -> "Embed":
51
+ """Set the embed title (max 256 chars)."""
52
+ self._data["title"] = str(title)
53
+ return self
54
+
55
+ def set_description(self, description: str) -> "Embed":
56
+ """Set the embed description / body text (max 4096 chars)."""
57
+ self._data["description"] = str(description)
58
+ return self
59
+
60
+ def set_url(self, url: str) -> "Embed":
61
+ """Make the title a clickable hyperlink."""
62
+ self._data["url"] = str(url)
63
+ return self
64
+
65
+ # ------------------------------------------------------------------ #
66
+ # Colour / theme #
67
+ # ------------------------------------------------------------------ #
68
+
69
+ def set_color(self, color) -> "Embed":
70
+ """
71
+ Set the left-border colour.
72
+
73
+ Accepts:
74
+ - Hex string → ``"#FF5733"`` or ``"FF5733"``
75
+ - Integer → ``16711680``
76
+ - Theme name → ``"success"``, ``"error"``, ``"warning"``, etc.
77
+ """
78
+ if isinstance(color, str):
79
+ color = color.strip().lstrip("#")
80
+ if color.lower() in self.THEMES:
81
+ self._data["color"] = self.THEMES[color.lower()]
82
+ else:
83
+ self._data["color"] = int(color, 16)
84
+ elif isinstance(color, int):
85
+ self._data["color"] = color
86
+ else:
87
+ raise TypeError("color must be a hex string or int")
88
+ return self
89
+
90
+ # Alias for British English users
91
+ set_colour = set_color
92
+
93
+ def set_theme(self, theme: str) -> "Embed":
94
+ """
95
+ Apply a named colour theme.
96
+
97
+ Available themes: ``default``, ``success``, ``warning``,
98
+ ``error``, ``info``, ``dark``, ``light``, ``purple``,
99
+ ``orange``, ``pink``.
100
+ """
101
+ theme = theme.lower()
102
+ if theme not in self.THEMES:
103
+ raise ValueError(
104
+ f"Unknown theme '{theme}'. "
105
+ f"Available: {', '.join(self.THEMES)}"
106
+ )
107
+ self._data["color"] = self.THEMES[theme]
108
+ return self
109
+
110
+ # ------------------------------------------------------------------ #
111
+ # Author #
112
+ # ------------------------------------------------------------------ #
113
+
114
+ def set_author(
115
+ self,
116
+ name: str,
117
+ url: str = None,
118
+ icon_url: str = None,
119
+ ) -> "Embed":
120
+ """Set the author block shown above the title."""
121
+ author = {"name": str(name)}
122
+ if url:
123
+ author["url"] = url
124
+ if icon_url:
125
+ author["icon_url"] = icon_url
126
+ self._data["author"] = author
127
+ return self
128
+
129
+ # ------------------------------------------------------------------ #
130
+ # Images #
131
+ # ------------------------------------------------------------------ #
132
+
133
+ def set_thumbnail(self, url: str) -> "Embed":
134
+ """Set the small thumbnail image (top-right corner)."""
135
+ self._data["thumbnail"] = {"url": str(url)}
136
+ return self
137
+
138
+ def set_image(self, url: str) -> "Embed":
139
+ """Set the large image shown below the body."""
140
+ self._data["image"] = {"url": str(url)}
141
+ return self
142
+
143
+ # ------------------------------------------------------------------ #
144
+ # Footer #
145
+ # ------------------------------------------------------------------ #
146
+
147
+ def set_footer(self, text: str, icon_url: str = None) -> "Embed":
148
+ """Set the footer text and optional icon."""
149
+ footer = {"text": str(text)}
150
+ if icon_url:
151
+ footer["icon_url"] = icon_url
152
+ self._data["footer"] = footer
153
+ return self
154
+
155
+ # ------------------------------------------------------------------ #
156
+ # Timestamp #
157
+ # ------------------------------------------------------------------ #
158
+
159
+ def set_timestamp(self, dt: datetime = None) -> "Embed":
160
+ """
161
+ Add a timestamp to the footer.
162
+
163
+ Pass a :class:`datetime` object, or leave blank to use **now** (UTC).
164
+ """
165
+ if dt is None:
166
+ dt = datetime.now(tz=timezone.utc)
167
+ self._data["timestamp"] = dt.isoformat()
168
+ return self
169
+
170
+ # ------------------------------------------------------------------ #
171
+ # Fields #
172
+ # ------------------------------------------------------------------ #
173
+
174
+ def add_field(
175
+ self,
176
+ name: str,
177
+ value: str,
178
+ inline: bool = False,
179
+ ) -> "Embed":
180
+ """
181
+ Add an inline or block field (up to 25 fields per embed).
182
+
183
+ :param name: Field title.
184
+ :param value: Field body text.
185
+ :param inline: If ``True``, fields sit side-by-side (up to 3 per row).
186
+ """
187
+ fields = self._data.setdefault("fields", [])
188
+ if len(fields) >= 25:
189
+ raise ValueError("Discord embeds support a maximum of 25 fields")
190
+ fields.append({"name": str(name), "value": str(value), "inline": inline})
191
+ return self
192
+
193
+ def clear_fields(self) -> "Embed":
194
+ """Remove all previously added fields."""
195
+ self._data.pop("fields", None)
196
+ return self
197
+
198
+ # ------------------------------------------------------------------ #
199
+ # Build #
200
+ # ------------------------------------------------------------------ #
201
+
202
+ def build(self) -> dict:
203
+ """Return the raw embed dictionary ready for the Discord API."""
204
+ return dict(self._data)
205
+
206
+ def __repr__(self) -> str:
207
+ title = self._data.get("title", "<no title>")
208
+ fields = len(self._data.get("fields", []))
209
+ return f"<Embed title={title!r} fields={fields}>"
@@ -0,0 +1,2 @@
1
+ def hello():
2
+ print("Hello From Webhook Logger!")
@@ -0,0 +1,284 @@
1
+ Metadata-Version: 2.4
2
+ Name: webhookloggerx
3
+ Version: 2.0.0
4
+ Summary: A clean, user-friendly Python package for sending Discord webhook messages and rich embeds.
5
+ Author: Nur Mohammad Rafi
6
+ Author-email:
7
+ Keywords: discord webhook embed notification bot
8
+ Classifier: Programming Language :: Python :: 3
9
+ Classifier: License :: OSI Approved :: MIT License
10
+ Classifier: Operating System :: OS Independent
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: Topic :: Internet :: WWW/HTTP
13
+ Classifier: Topic :: Communications
14
+ Requires-Python: >=3.7
15
+ Description-Content-Type: text/markdown
16
+ Requires-Dist: requests>=2.20.0
17
+ Dynamic: author
18
+ Dynamic: classifier
19
+ Dynamic: description
20
+ Dynamic: description-content-type
21
+ Dynamic: keywords
22
+ Dynamic: requires-dist
23
+ Dynamic: requires-python
24
+ Dynamic: summary
25
+
26
+ # webhooklogger-python-rafi
27
+
28
+ A clean, user-friendly Python package for sending Discord webhook messages and rich embeds.
29
+
30
+ ## Installation
31
+
32
+ ```bash
33
+ pip install webhookloggerx
34
+ ```
35
+
36
+ ## Quick Start
37
+
38
+ ```python
39
+ from webhooklogger import WebhookClient, Embed
40
+
41
+ client = WebhookClient("https://discord.com/api/webhooks/YOUR_ID/YOUR_TOKEN")
42
+ client.send("Hello from webhooklogger!")
43
+ ```
44
+
45
+ ---
46
+
47
+ ## Features
48
+
49
+ - ✅ Send plain text messages
50
+ - ✅ Send rich Discord embeds
51
+ - ✅ Send multiple embeds in one request
52
+ - ✅ Customise webhook display name & avatar
53
+ - ✅ Embed builder with fluent (chainable) API
54
+ - ✅ Built-in colour themes (`success`, `error`, `warning`, etc.)
55
+ - ✅ Set embed title, description, author, footer, images, timestamp, fields
56
+
57
+ ---
58
+
59
+ ## WebhookClient
60
+
61
+ ### Create a Client
62
+
63
+ ```python
64
+ from webhooklogger import WebhookClient
65
+
66
+ client = WebhookClient("https://discord.com/api/webhooks/...")
67
+ ```
68
+
69
+ ### Set Webhook Profile (name & avatar)
70
+
71
+ ```python
72
+ client.set_profile(
73
+ username="MyBot",
74
+ avatar_url="https://example.com/avatar.png"
75
+ )
76
+ ```
77
+
78
+ ### Send a Plain Text Message
79
+
80
+ ```python
81
+ client.send("This is a plain message.")
82
+ ```
83
+
84
+ ### Send an Embed
85
+
86
+ ```python
87
+ from webhooklogger import Embed
88
+
89
+ embed = Embed().set_title("Hello!").set_description("Embed body here.")
90
+ client.send_embed(embed)
91
+ ```
92
+
93
+ ### Send Multiple Embeds
94
+
95
+ ```python
96
+ embed1 = Embed().set_title("First").set_theme("success")
97
+ embed2 = Embed().set_title("Second").set_theme("error")
98
+ client.send_embeds([embed1, embed2])
99
+ ```
100
+
101
+ ### Send Text + Embed Together
102
+
103
+ ```python
104
+ embed = Embed().set_title("Alert").set_description("Check this out!")
105
+ client.send_message(content="Hey team!", embed=embed)
106
+ ```
107
+
108
+ ---
109
+
110
+ ## Embed Builder
111
+
112
+ All methods return `self`, so you can **chain** them fluently.
113
+
114
+ ### Title & Description
115
+
116
+ ```python
117
+ embed = (
118
+ Embed()
119
+ .set_title("My Title")
120
+ .set_description("This is the body of the embed.")
121
+ .set_url("https://example.com") # makes title clickable
122
+ )
123
+ ```
124
+
125
+ ### Colour
126
+
127
+ ```python
128
+ # Hex string
129
+ embed.set_color("#FF5733")
130
+
131
+ # Integer
132
+ embed.set_color(16711680)
133
+
134
+ # Named theme
135
+ embed.set_theme("success")
136
+ ```
137
+
138
+ **Available themes:**
139
+
140
+ | Theme | Colour |
141
+ |---|---|
142
+ | `default` | Discord Blurple |
143
+ | `success` | Green |
144
+ | `warning` | Yellow |
145
+ | `error` | Red |
146
+ | `info` | Blue |
147
+ | `dark` | Dark Grey |
148
+ | `light` | White |
149
+ | `purple` | Purple |
150
+ | `orange` | Orange |
151
+ | `pink` | Pink |
152
+
153
+ ### Author
154
+
155
+ ```python
156
+ embed.set_author(
157
+ name="Nur Mohammad Rafi",
158
+ url="https://github.com/yourusername",
159
+ icon_url="https://example.com/icon.png"
160
+ )
161
+ ```
162
+
163
+ ### Thumbnail & Image
164
+
165
+ ```python
166
+ embed.set_thumbnail("https://example.com/thumb.png") # top-right corner
167
+ embed.set_image("https://example.com/big-image.png") # large bottom image
168
+ ```
169
+
170
+ ### Fields
171
+
172
+ ```python
173
+ embed.add_field("Status", "Online", inline=True)
174
+ embed.add_field("Server", "Production", inline=True)
175
+ embed.add_field("Message", "All systems go.", inline=False)
176
+ ```
177
+
178
+ - `inline=True` — fields sit side by side (up to 3 per row)
179
+ - `inline=False` — field takes the full width
180
+ - Max **25 fields** per embed
181
+
182
+ ### Footer
183
+
184
+ ```python
185
+ embed.set_footer("webhooklogger v1.0", icon_url="https://example.com/icon.png")
186
+ ```
187
+
188
+ ### Timestamp
189
+
190
+ ```python
191
+ embed.set_timestamp() # use current UTC time
192
+ ```
193
+
194
+ ```python
195
+ from datetime import datetime, timezone
196
+ embed.set_timestamp(datetime(2025, 1, 1, tzinfo=timezone.utc)) # custom time
197
+ ```
198
+
199
+ ---
200
+
201
+ ## Full Example
202
+
203
+ ```python
204
+ from webhooklogger import WebhookClient, Embed
205
+
206
+ # Create and configure client
207
+ client = WebhookClient("https://discord.com/api/webhooks/...")
208
+ client.set_profile(username="AlertBot", avatar_url="https://example.com/bot.png")
209
+
210
+ # Build embed
211
+ embed = (
212
+ Embed()
213
+ .set_title("🚨 System Alert")
214
+ .set_description("A critical event has been detected on the server.")
215
+ .set_theme("error")
216
+ .set_author("Monitor System", icon_url="https://example.com/icon.png")
217
+ .set_thumbnail("https://example.com/thumb.png")
218
+ .add_field("Host", "prod-server-01", inline=True)
219
+ .add_field("Status", "❌ Down", inline=True)
220
+ .add_field("Region", "US-East", inline=True)
221
+ .add_field("Details", "Connection timeout after 30s", inline=False)
222
+ .set_image("https://example.com/graph.png")
223
+ .set_footer("webhooklogger • Auto-alert system")
224
+ .set_timestamp()
225
+ )
226
+
227
+ # Send it
228
+ client.send_embed(embed)
229
+ ```
230
+
231
+ ---
232
+
233
+ ## Error Handling
234
+
235
+ ```python
236
+ from webhooklogger import WebhookClient, WebhookError
237
+
238
+ client = WebhookClient("https://discord.com/api/webhooks/...")
239
+
240
+ try:
241
+ client.send("Test message")
242
+ except WebhookError as e:
243
+ print(f"Failed: HTTP {e.status_code} — {e.message}")
244
+ ```
245
+
246
+ ---
247
+
248
+ ## API Reference
249
+
250
+ ### `WebhookClient`
251
+
252
+ | Method | Description |
253
+ |---|---|
254
+ | `__init__(url)` | Create client with webhook URL |
255
+ | `set_profile(username, avatar_url)` | Override display name & avatar |
256
+ | `send(content)` | Send plain text message |
257
+ | `send_embed(embed)` | Send a single Embed |
258
+ | `send_embeds(embeds)` | Send a list of Embeds (max 10) |
259
+ | `send_message(content, embed)` | Send text + embed together |
260
+
261
+ ### `Embed`
262
+
263
+ | Method | Description |
264
+ |---|---|
265
+ | `set_title(title)` | Embed title |
266
+ | `set_description(text)` | Embed body text |
267
+ | `set_url(url)` | Make title a hyperlink |
268
+ | `set_color(color)` | Hex string or int |
269
+ | `set_colour(color)` | Alias for `set_color` |
270
+ | `set_theme(theme)` | Named colour theme |
271
+ | `set_author(name, url, icon_url)` | Author block |
272
+ | `set_thumbnail(url)` | Small top-right image |
273
+ | `set_image(url)` | Large bottom image |
274
+ | `set_footer(text, icon_url)` | Footer text & icon |
275
+ | `set_timestamp(dt)` | Timestamp (default: now) |
276
+ | `add_field(name, value, inline)` | Add a field |
277
+ | `clear_fields()` | Remove all fields |
278
+ | `build()` | Return raw dict for API |
279
+
280
+ ---
281
+
282
+ ## License
283
+
284
+ MIT © Nur Mohammad Rafi
@@ -0,0 +1,11 @@
1
+ README.md
2
+ setup.py
3
+ webhooklogger/__init__.py
4
+ webhooklogger/client.py
5
+ webhooklogger/embed.py
6
+ webhooklogger/main.py
7
+ webhookloggerx.egg-info/PKG-INFO
8
+ webhookloggerx.egg-info/SOURCES.txt
9
+ webhookloggerx.egg-info/dependency_links.txt
10
+ webhookloggerx.egg-info/requires.txt
11
+ webhookloggerx.egg-info/top_level.txt
@@ -0,0 +1 @@
1
+ requests>=2.20.0
@@ -0,0 +1 @@
1
+ webhooklogger