Repository: brazier-dev/hikari-toolbox (language: python)

Issue:
Add util method to check validity of an Embed object 
### Summary

Embeds have a character limit for various fields and the embed itself collectively, 
> - Embed title is limited to 256 characters
> - Embed description is limited to 4096 characters
> - An embed can contain a maximum of 25 fields
> - A field name/title is limited to 256 character and the value of the field is limited to 1024 characters
> - Embed footer is limited to 2048 characters
> - Embed author name is limited to 256 characters
> - The total of characters allowed in an embed is 6000


having a function that checks if an Embed is send-able can be handy. 



### Why is this needed?

Invalid Form Body requests can be avoided with a proper check before sending the embed.

### Ideal implementation

Returning a bool object would be ideal.

```py
def check_embed_limit(embed: hikari.Embed) -> bool: 
    if <exceeds any limit>: # multiple checks like this...
         return False
    return True
```

### Checklist

- [X] I have searched the issue tracker and have made sure it's not a duplicate. If it is a follow up of another issue, I have specified it.


Discussion / hints:

Fix patch:
diff --git a/toolbox/errors.py b/toolbox/errors.py
index 7949893..955b174 100644
--- a/toolbox/errors.py
+++ b/toolbox/errors.py
@@ -1,18 +1,18 @@
 import typing as t
 
-__all__: t.Sequence[str] = ("ToolboxError", "CacheFailureError")
+__all__: t.Sequence[str] = ("ToolboxError", "CacheFailureError", "EmbedValidationError")
 
 
 class ToolboxError(Exception):
     """Base class for exceptions in this module."""
 
-    ...
-
 
 class CacheFailureError(ToolboxError):
     """Exception raised when a cache lookup fails."""
 
-    ...
+
+class EmbedValidationError(ToolboxError):
+    """Exception raised when a embed validation fails."""
 
 
 # MIT License
diff --git a/toolbox/utils.py b/toolbox/utils.py
index 1d0ae21..9c6a3ce 100644
--- a/toolbox/utils.py
+++ b/toolbox/utils.py
@@ -7,6 +7,7 @@
 import hikari
 
 from .errors import CacheFailureError
+from .errors import EmbedValidationError
 
 __all__: t.Sequence[str] = (
     "format_dt",
@@ -21,6 +22,7 @@
     "calculate_permissions",
     "can_moderate",
     "as_command_choices",
+    "validate_embed",
 )
 
 VALID_TIMESTAMP_STYLES: t.Sequence[str] = ("t", "T", "d", "D", "f", "F", "R")
@@ -464,6 +466,56 @@ def as_command_choices(*args: t.Any, **kwargs: t.Any) -> t.Sequence[hikari.Comma
     return _list_to_command_choices(choices)
 
 
+def validate_embed(embed: hikari.Embed) -> hikari.Embed:
+    """Validate an embed, checking the length of all fields.
+
+    Parameters
+    ----------
+    embed : hikari.Embed
+        The embed to validate.
+
+    Raises
+    ------
+    EmbedValidationError
+        Raised when the embed is invalid.
+
+    Returns
+    -------
+    hikari.Embed
+        The embed that was validated.
+    """
+    if (length := embed.total_length()) > 6000:
+        raise EmbedValidationError(f"Embed total length must be less than 6000 characters, got {length}.")
+
+    if embed.title and (length := len(embed.title)) > 256:
+        raise EmbedValidationError(f"Embed title must be less than 256 characters, got {length}.")
+
+    if embed.description and (length := len(embed.description)) > 4096:
+        raise EmbedValidationError(f"Embed description must be less than 4096 characters, got {length}.")
+
+    if embed.footer and embed.footer.text and (length := len(embed.footer.text)) > 2048:
+        raise EmbedValidationError(f"Embed footer text must be less than 2048 characters, got {length}.")
+
+    if embed.author and embed.author.name and (length := len(embed.author.name)) > 256:
+        raise EmbedValidationError(f"Embed author name must be less than 256 characters, got {length}.")
+
+    if embed.fields:
+        if (field_count := len(embed.fields)) > 25:
+            raise EmbedValidationError(f"Embed must have less than 25 fields, got {field_count}.")
+
+        for i, field in enumerate(embed.fields):
+            if (length := len(field.name)) > 256:
+                raise EmbedValidationError(
+                    f"Embed field {i} ({field.name}): name must be less than 256 characters, got {length}."
+                )
+            if (length := len(field.value)) > 1024:
+                raise EmbedValidationError(
+                    f"Embed field {i} ({field.name}): value must be less than 1024 characters, got {length}."
+                )
+
+    return embed
+
+
 # MIT License
 #
 # Copyright (c) 2022-present HyperGH


Test patch:
diff --git a/tests/toolbox/test_validate_embed.py b/tests/toolbox/test_validate_embed.py
new file mode 100644
index 0000000..8da4600
--- /dev/null
+++ b/tests/toolbox/test_validate_embed.py
@@ -0,0 +1,63 @@
+import hikari
+import pytest
+
+import toolbox
+
+
+def test_validate_embed_valid():
+    toolbox.validate_embed(
+        hikari.Embed(
+            title="a" * 256,
+            description="a" * 1024,
+        )
+        .add_field(name="a" * 256, value="a" * 1024)
+        .set_footer(text="a" * 2048)
+        .set_author(name="a" * 256)
+    )
+
+
+def test_validate_embed_total_length():
+    with pytest.raises(toolbox.EmbedValidationError):
+        toolbox.validate_embed(
+            hikari.Embed(
+                title="a" * 256,
+                description="a" * 4096,
+            )
+            .add_field(name="a" * 256, value="a" * 1024)
+            .set_footer(text="a" * 2048)
+            .set_author(name="a" * 256)
+        )
+
+
+def test_validate_embed_fields():
+    with pytest.raises(toolbox.EmbedValidationError):
+        embed = hikari.Embed()
+        for _ in range(100):
+            embed.add_field(name="a" * 256, value="a" * 1024)
+        toolbox.validate_embed(embed)
+
+    with pytest.raises(toolbox.EmbedValidationError):
+        embed = hikari.Embed()
+        for _ in range(3):
+            embed.add_field(name="a" * 300, value="a" * 1200)
+        toolbox.validate_embed(embed)
+
+
+def test_validate_embed_title():
+    with pytest.raises(toolbox.EmbedValidationError):
+        toolbox.validate_embed(hikari.Embed(title="a" * 300))
+
+
+def test_validate_embed_description():
+    with pytest.raises(toolbox.EmbedValidationError):
+        toolbox.validate_embed(hikari.Embed(description="a" * 5000))
+
+
+def test_validate_embed_footer():
+    with pytest.raises(toolbox.EmbedValidationError):
+        toolbox.validate_embed(hikari.Embed().set_footer(text="a" * 3000))
+
+
+def test_validate_embed_author():
+    with pytest.raises(toolbox.EmbedValidationError):
+        toolbox.validate_embed(hikari.Embed().set_author(name="a" * 300))