Skip to content

Commit

Permalink
Refactor into separate modules
Browse files Browse the repository at this point in the history
  • Loading branch information
Wesley Chalmers committed Jan 7, 2021
1 parent ee3d8f3 commit 46cf31c
Show file tree
Hide file tree
Showing 6 changed files with 223 additions and 201 deletions.
15 changes: 15 additions & 0 deletions flask_discord_interactions/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
from .command import CommandOptionType, SlashCommand
from .context import InteractionContext
from .discord import DiscordInteractions, DiscordInteractionsBlueprint
from .response import InteractionResponseType, InteractionResponse


__all__ = [
CommandOptionType,
SlashCommand,
InteractionContext,
DiscordInteractions,
DiscordInteractionsBlueprint,
InteractionResponseType,
InteractionResponse
]
33 changes: 33 additions & 0 deletions flask_discord_interactions/command.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
from .context import InteractionContext


class CommandOptionType:
SUB_COMMAND = 1
SUB_COMMAND_GROUP = 2
STRING = 3
INTEGER = 4
BOOLEAN = 5
USER = 6
CHANNEL = 7
ROLE = 8


class SlashCommand:
def __init__(self, command, name, description, options):
self.command = command
self.name = name
self.description = description
self.options = options

def create_kwargs(self, data):
if "options" not in data["data"]:
return {}

kwargs = {}
for option in data["data"]["options"]:
kwargs[option["name"]] = option["value"]
return kwargs

def run(self, discord, app, data):
context = InteractionContext(discord, app, data)
return self.command(context, **self.create_kwargs(data))
87 changes: 87 additions & 0 deletions flask_discord_interactions/context.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import requests

from .response import InteractionResponse


class InteractionContext:
class InteractionAuthor:
def __init__(self, data=None):
if data:
self.id = data["user"]["id"]
self.username = data["user"]["username"]
self.discriminator = data["user"]["discriminator"]
self.avatar_hash = data["user"]["avatar"]
self.bot = data["user"].get("bot", False)
self.system = data["user"].get("system", False)
self.mfa_enabled = data["user"].get("mfa_enabled", False)
self.locale = data["user"].get("locale")
self.flags = data["user"].get("flags")
self.premium_type = data["user"].get("premium_type")
self.public_flags = data["user"].get("public_flags")

self.nick = data["nick"]
self.roles = data["roles"]
self.joined_at = data["joined_at"]
self.premium_since = data.get("premium_since")
self.deaf = data["deaf"]
self.mute = data["mute"]
self.pending = data.get("pending")

@property
def display_name(self):
return self.nick or self.username

@property
def avatar_url(self):
return ("https://cdn.discordapp.com/avatars/"
f"{self.id}/{self.avatar_hash}.png")

def __init__(self, discord, app, data=None):
self.client_id = app.config["DISCORD_CLIENT_ID"]
self.auth_headers = discord.auth_headers(app)

if data:
self.author = self.InteractionAuthor(data["member"])
self.id = data["id"]
self.token = data["token"]
self.channel_id = data["channel_id"]
self.guild_id = data["guild_id"]
self.options = data["data"].get("options")
self.command_name = data["data"]["name"]
self.command_id = data["data"]["id"]

def followup_url(self, message=None):
url = ("https://discord.com/api/v8/webhooks/"
f"{self.client_id}/{self.token}")
if message is not None:
url += f"/messages/{message}"

return url

def edit(self, response, message="@original"):
response = InteractionResponse.from_return_value(response)

response = requests.patch(
self.followup_url(message),
json=response.dump_followup(),
headers=self.auth_headers
)
response.raise_for_status()

def delete(self, message="@original"):
response = requests.delete(
self.followup_url(message),
headers=self.auth_headers
)
response.raise_for_status()

def send(self, response):
response = InteractionResponse.from_return_value(response)

response = requests.post(
self.followup_url(),
headers=self.auth_headers,
**response.dump_multipart()
)
response.raise_for_status()
return response.json()["id"]
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import time
import json

import requests

Expand All @@ -8,211 +7,15 @@
from nacl.exceptions import BadSignatureError
from nacl.signing import VerifyKey

from .command import SlashCommand
from .response import InteractionResponse, InteractionResponseType


class InteractionType:
PING = 1
APPLICATION_COMMAND = 2


class InteractionResponseType:
PONG = 1
ACKNOWLEDGE = 2
CHANNEL_MESSAGE = 3
CHANNEL_MESSAGE_WITH_SOURCE = 4
ACKNOWLEDGE_WITH_SOURCE = 5


class CommandOptionType:
SUB_COMMAND = 1
SUB_COMMAND_GROUP = 2
STRING = 3
INTEGER = 4
BOOLEAN = 5
USER = 6
CHANNEL = 7
ROLE = 8


class InteractionContext:
class InteractionAuthor:
def __init__(self, data=None):
if data:
self.id = data["user"]["id"]
self.username = data["user"]["username"]
self.discriminator = data["user"]["discriminator"]
self.avatar_hash = data["user"]["avatar"]
self.bot = data["user"].get("bot", False)
self.system = data["user"].get("system", False)
self.mfa_enabled = data["user"].get("mfa_enabled", False)
self.locale = data["user"].get("locale")
self.flags = data["user"].get("flags")
self.premium_type = data["user"].get("premium_type")
self.public_flags = data["user"].get("public_flags")

self.nick = data["nick"]
self.roles = data["roles"]
self.joined_at = data["joined_at"]
self.premium_since = data.get("premium_since")
self.deaf = data["deaf"]
self.mute = data["mute"]
self.pending = data.get("pending")

@property
def display_name(self):
return self.nick or self.username

@property
def avatar_url(self):
return ("https://cdn.discordapp.com/avatars/"
f"{self.id}/{self.avatar_hash}.png")

def __init__(self, discord, app, data=None):
self.client_id = app.config["DISCORD_CLIENT_ID"]
self.auth_headers = discord.auth_headers(app)

if data:
self.author = self.InteractionAuthor(data["member"])
self.id = data["id"]
self.token = data["token"]
self.channel_id = data["channel_id"]
self.guild_id = data["guild_id"]
self.options = data["data"].get("options")
self.command_name = data["data"]["name"]
self.command_id = data["data"]["id"]

def followup_url(self, message=None):
url = ("https://discord.com/api/v8/webhooks/"
f"{self.client_id}/{self.token}")
if message is not None:
url += f"/messages/{message}"

return url

def edit(self, response, message="@original"):
response = InteractionResponse.from_return_value(response)

response = requests.patch(
self.followup_url(message),
json=response.dump_followup(),
headers=self.auth_headers
)
response.raise_for_status()

def delete(self, message="@original"):
response = requests.delete(
self.followup_url(message),
headers=self.auth_headers
)
response.raise_for_status()

def send(self, response):
response = InteractionResponse.from_return_value(response)

response = requests.post(
self.followup_url(),
headers=self.auth_headers,
**response.dump_multipart()
)
response.raise_for_status()
return response.json()["id"]


class InteractionResponse:
def __init__(self, content=None, *, tts=False, embed=None, embeds=None,
allowed_mentions={"parse": ["roles", "users", "everyone"]},
with_source=True, file=None, files=None):
self.content = content
self.tts = tts

if embed is not None and embeds is not None:
raise ValueError("Specify only one of embed or embeds")
if embed is not None:
embeds = [embed]
self.embeds = embeds

if file is not None and files is not None:
raise ValueError("Specify only one of file or files")
if file is not None:
files = [file]
self.files = files

self.allowed_mentions = allowed_mentions

if self.embeds is not None or self.content is not None:
if with_source:
self.response_type = \
InteractionResponseType.CHANNEL_MESSAGE_WITH_SOURCE
else:
self.response_type = InteractionResponseType.CHANNEL_MESSAGE
else:
if with_source:
self.response_type = \
InteractionResponseType.ACKNOWLEDGE_WITH_SOURCE
else:
self.response_type = InteractionResponseType.ACKNOWLEDGE

@staticmethod
def from_return_value(result):
if result is None:
return InteractionResponse()
elif isinstance(result, InteractionResponse):
return result
else:
return InteractionResponse(str(result))

def dump(self):
return {
"type": self.response_type,
"data": {
"content": self.content,
"tts": self.tts,
"embeds": self.embeds,
"allowed_mentions": self.allowed_mentions
}
}

def dump_followup(self):
return {
"content": self.content,
"tts": self.tts,
"embeds": self.embeds,
"allowed_mentions": self.allowed_mentions
}

def dump_multipart(self):
if self.files:
payload_json = json.dumps(self.dump_followup())

multipart = []
for file in self.files:
multipart.append(("file", file))

return {"data": {"payload_json": payload_json}, "files": multipart}
else:
return {"json": self.dump_followup()}


class SlashCommand:
def __init__(self, command, name, description, options):
self.command = command
self.name = name
self.description = description
self.options = options

def create_kwargs(self, data):
if "options" not in data["data"]:
return {}

kwargs = {}
for option in data["data"]["options"]:
kwargs[option["name"]] = option["value"]
return kwargs

def run(self, discord, app, data):
context = InteractionContext(discord, app, data)
return self.command(context, **self.create_kwargs(data))


class DiscordInteractionsBlueprint:
def __init__(self):
self.discord_commands = {}
Expand Down
Loading

0 comments on commit 46cf31c

Please sign in to comment.