Skip to content

Commit

Permalink
Merge pull request #17 from Breq16/feature/response-dataclass
Browse files Browse the repository at this point in the history
Response and Embed Dataclasses
  • Loading branch information
breqdev authored May 5, 2021
2 parents 67f1a91 + eb3a2e4 commit 7fe26f1
Show file tree
Hide file tree
Showing 8 changed files with 405 additions and 60 deletions.
80 changes: 76 additions & 4 deletions docs/response.rst
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
.. _response-page:

Response
========
Responses
=========

Response objects
----------------

Response objects are used whenever your bot responds to an Interaction or sends
or edits a followup message. They can contain content, embeds, files, and
Expand Down Expand Up @@ -77,9 +80,9 @@ bot is echoing user input. You can read more about this parameter on the
`Discord API docs <https://discord.com/developers/docs/resources/channel#allowed-mentions-object>`_.

Full API
--------
^^^^^^^^

.. autoclass:: flask_discord_interactions.Response
.. autoclass:: flask_discord_interactions.Response(**kwargs)
:members:

|
Expand All @@ -89,4 +92,73 @@ Full API
:undoc-members:
:member-order: bysource

Embeds
------

Embed objects let you represent Embeds which you can return as part of
responses.

.. code-block:: python
from flask_discord_interactions import Embed, embed
@discord.command()
def my_embed(ctx):
"Embeds!"
return Response(embed=Embed(
title="Embeds!",
description="Embeds can be specified as Embed objects.",
fields=[
embed.Field(
name="Can they use markdown?",
value="**Yes!** [link](https://google.com/)"
),
embed.Field(
name="Where do I learn about how to format this object?",
value=("[Try this visualizer!]"
"(https://leovoel.github.io/embed-visualizer/)")
)
]
))
The :class:`.Embed` class represents a single Embed. You can also use
:class:`.embed.Field`, :class:`.embed.Author`, :class:`.embed.Footer`, or
:class:`.embed.Provider` for those fields. :class:`.embed.Media` can be used
for images, videos, and thumbnails.

Full API
^^^^^^^^

.. autoclass:: flask_discord_interactions.Embed(**kwargs)
:members:

|
.. autoclass:: flask_discord_interactions.embed.Field
:members:
:undoc-members:

|
.. autoclass:: flask_discord_interactions.embed.Author
:members:
:undoc-members:

|
.. autoclass:: flask_discord_interactions.embed.Footer
:members:
:undoc-members:

|
.. autoclass:: flask_discord_interactions.embed.Provider
:members:
:undoc-members:

|
.. autoclass:: flask_discord_interactions.embed.Media
:members:
:undoc-members:
29 changes: 25 additions & 4 deletions examples/response.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
sys.path.insert(1, ".")

from flask_discord_interactions import (DiscordInteractions, # noqa: E402
Response)
Response, Embed, embed)


app = Flask(__name__)
Expand All @@ -31,13 +31,34 @@ def markdown(ctx):
return "All *the* **typical** ~~discord~~ _markdown_ `works` ***too.***"


@discord.command()
def embed(ctx):
@discord.command(name="embed")
def embed_(ctx):
"Embeds!"

return Response(embed=Embed(
title="Embeds!",
description="Embeds can be specified as Embed objects.",
fields=[
embed.Field(
name="Can they use markdown?",
value="**Yes!** [link](https://google.com/)"
),
embed.Field(
name="Where do I learn about how to format this object?",
value=("[Try this visualizer!]"
"(https://leovoel.github.io/embed-visualizer/)")
)
]
))


@discord.command()
def dict_embed(ctx):
"Embeds as dict objects!"

return Response(embed={
"title": "Embeds!",
"description": "Embeds must be specified as JSON objects.",
"description": "Embeds can also be specified as JSON objects.",
"fields": [
{
"name": "Can they use markdown?",
Expand Down
5 changes: 5 additions & 0 deletions flask_discord_interactions/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
)

from flask_discord_interactions.response import Response, ResponseType
import flask_discord_interactions.embed as embed
from flask_discord_interactions.embed import Embed
from flask_discord_interactions.client import Client


Expand All @@ -29,6 +31,8 @@


__all__ = [
"embed",

"SlashCommand",
"SlashCommandSubgroup",
"SlashCommandGroup",
Expand All @@ -43,6 +47,7 @@
"DiscordInteractionsBlueprint",
"Response",
"ResponseType",
"Embed",
"Client",

"InteractionResponse",
Expand Down
11 changes: 10 additions & 1 deletion flask_discord_interactions/command.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

from flask_discord_interactions.context import (Context, CommandOptionType,
User, Member, Channel, Role)
from flask_discord_interactions.response import Response


class SlashCommand:
Expand Down Expand Up @@ -128,12 +129,20 @@ def make_context_and_run(self, discord, app, data):
The Flask app used to receive this interaction.
data
The incoming interaction data.
Returns
-------
Response
The response by the command, converted to a Response object.
"""

context = Context.from_data(discord, app, data)
args, kwargs = context.create_args(
data["data"], resolved=data["data"].get("resolved"))
return self.run(context, *args, **kwargs)

result = self.run(context, *args, **kwargs)

return Response.from_return_value(result)

def run(self, context, *args, **kwargs):
"""
Expand Down
6 changes: 1 addition & 5 deletions flask_discord_interactions/discord.py
Original file line number Diff line number Diff line change
Expand Up @@ -376,8 +376,4 @@ def interactions():
"type": ResponseType.PONG
})

result = self.run_command(request.json)

response = Response.from_return_value(result)

return jsonify(response.dump())
return jsonify(self.run_command(request.json).dump())
87 changes: 87 additions & 0 deletions flask_discord_interactions/embed.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
from dataclasses import dataclass, asdict
from typing import List

@dataclass
class Footer:
"Represents the footer of an Embed."
text: str
icon_url: str = None
proxy_icon_url: str = None

@dataclass
class Field:
"Represents a field on an Embed."
name: str
value: str
inline: bool = False

@dataclass
class Media:
"Represents a thumbnail, image, or video on an Embed."
url: str = None
proxy_url: str = None
height: int = None
width: int = None

@dataclass
class Provider:
"Represents a provider of an Embed."
name: str = None
url: str = None

@dataclass
class Author:
"Represents an author of an embed."
name: str = None
url: str = None
icon_url: str = None
proxy_icon_url: str = None

@dataclass
class Embed:
"""
Represents an Embed to be sent as part of a Response.
Attributes
----------
title
The title of the embed.
description
The description in the embed.
url
The URL that the embed title links to.
timestamp
An ISO8601 timestamp included in the embed.
color
An integer representing the color of the sidebar of the embed.
footer
image
thumbnail
video
provider
author
fields
"""
title: str = None
description: str = None
url: str = None
timestamp: str = None
color: int = None
footer: Footer = None
image: Media = None
thumbnail: Media = None
video: Media = None
provider: Provider = None
author: Author = None
fields: List[Field] = None

def dump(self):
"Returns this Embed as a dictionary, removing fields which are None."
def filter_none(d):
if isinstance(d, dict):
return {k: filter_none(v)
for k, v in d.items() if v is not None}
else:
return d

return filter_none(asdict(self))
Loading

0 comments on commit 7fe26f1

Please sign in to comment.