Giter Site home page Giter Site logo

breqdev / flask-discord-interactions Goto Github PK

View Code? Open in Web Editor NEW
57.0 4.0 15.0 1.39 MB

A Flask extension to enable declarative definitions for Discord slash commands.

Home Page: https://flask-discord-interactions.readthedocs.io

License: MIT License

Python 100.00%
flask-discord-interactions discord-slash-commands discord flask flask-extension discord-interactions discord-bot python3 discord-python asyncio

flask-discord-interactions's Introduction

Flask-Discord-Interactions

This is a Flask extension that lets you write Discord Application Commands using a decorator syntax similar to Flask's @app.route() or Discord.py's @bot.command().

@discord.command()
def ping(ctx):
    "Respond with a friendly 'pong'!"
    return "Pong!"

Full documentation is available on readthedocs.

flask-discord-interactions's People

Contributors

akiller avatar alexpbrown avatar bradday4 avatar breqdev avatar etrotta avatar fmdaboville avatar nimon77 avatar rouven0 avatar williamhatcher avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

flask-discord-interactions's Issues

Member (and maybe more?) option types do not work.

Docs show an example of:

@discord.command()
def has_role(ctx, user: Member, role: Role):
    if role.id in user.roles:
        return f"Yes, user {user.display_name} has role {role.name}."
    else:
        return f"No, user {user.display_name} does not have role {role.name}."

However trying this results in:

  File "C:\Program Files\Python38\lib\site-packages\flask_discord_interactions\context.py", line 401, in create_args_recursive
    member_data = resolved["members"][option["value"]]
KeyError: 'members'

discord.command_group() does not work.

According to the documentation syntax like the following can be used:

comic = discord.command_group("comic")


@comic.command()
def xkcd(ctx, number: int):
    return f"https://xkcd.com/{number}/"

However doing this results in the following in the logs:
AttributeError: 'SlashCommandGroup' object has no attribute 'default_permission'
if discord.update_slash_commands() is being used

Support for multiple bots on one Flask server?

It should be possible to run more than one bot on the same Flask app, by giving each bot its own interactions route/URL.

This library should document this use case. Also, we need to verify if this actually works -- can the library handle multiple separate DiscordInteractions instances for one Flask app?

Support Modal forms

Is your feature request related to a problem? Please describe.
Discord recently released Modals, a new form of interaction, but they are not yet implemented.

From the announcement message in the Discord Developers server:

Modal Interactions Are Here

Have you ever been in that annoying situation where you're trying to upload your new Rift Apart / Twilight crossover fanfic but the string input type in slash commands is holding you back? Worry no more, because modal interactions are here!

You can now respond to interactions with the new MODAL type, which will present the user with a popup of inputs for them to fill in and submit, which will send you a handy MODAL_SUBMIT interaction. Check out the docs for more info and discuss in interaction-modals.

Describe the solution you'd like
Support for them.

Describe alternatives you've considered
I've implemented them in https://github.com/etrotta/flask-discord-interactions/tree/modal (with an Example file, no Tests yet) but figured it was better to open an Issue before making a Pull Request, because of some significant changes to the API structure and the way the Components in the Context are structured.

Proposed Changes

  1. Instead of always creating a Message in response to a Slash Command and other interaction types such as Buttons, the proposed way to implement them will instead check if the function returned a Modal instance, and if so, present the Modal instead of a Message.
  2. Adds a components field to the Context objects. However, instead of just passing the raw components we get from the Discord POST request, I instead propose that we store the raw response under _components and format it in a more user friendly way through a @property. This might have significant drawbacks in the future though, if new fields added to Modals do not fit that format very well.
  3. Move the ResponseType class from messages to another file. I opted to move it to a new folder called "enums", but it could be somewhere under models.

The other changes should be fairly straightforward and in line with the existing API, but I somewhat expect for these three changes to be questioned.

Additional context
Image from the previously mentioned announcement:
image

DM application command - command option type user

A application command with an user command option type raises a KeyError when used in a dm
Like this:

@discord.command(name="pick", description="Pick a user", options=[{
        "name": "user",
        "description": "The user to pick",
        "type": CommandOptionType.USER,
        "required": True
    }])

The Error:

[2021-12-16 19:14:37,564] ERROR in app: Exception on /interactions [POST]
Traceback (most recent call last):
  File "/usr/local/lib/python3.8/dist-packages/flask/app.py", line 2073, in wsgi_app
    response = self.full_dispatch_request()
  File "/usr/local/lib/python3.8/dist-packages/flask/app.py", line 1518, in full_dispatch_request
    rv = self.handle_user_exception(e)
  File "/usr/local/lib/python3.8/dist-packages/flask/app.py", line 1516, in full_dispatch_request
    rv = self.dispatch_request()
  File "/usr/local/lib/python3.8/dist-packages/flask/app.py", line 1502, in dispatch_request
    return self.ensure_sync(self.view_functions[rule.endpoint])(**req.view_args)
  File "/home/heroclay/.local/lib/python3.8/site-packages/flask_discord_interactions/discord.py", line 514, in interactions
    return jsonify(self.run_command(request.json).dump())
  File "/home/heroclay/.local/lib/python3.8/site-packages/flask_discord_interactions/discord.py", line 434, in run_command
    return command.make_context_and_run(self, current_app, data)
  File "/home/heroclay/.local/lib/python3.8/site-packages/flask_discord_interactions/command.py", line 175, in make_context_and_run
    args, kwargs = context.create_args()
  File "/home/heroclay/.local/lib/python3.8/site-packages/flask_discord_interactions/context.py", line 178, in create_args
    return self.create_args_chat_input()
  File "/home/heroclay/.local/lib/python3.8/site-packages/flask_discord_interactions/context.py", line 228, in create_args_chat_input
    return create_args_recursive({"options": self.options}, self.resolved)
  File "/home/heroclay/.local/lib/python3.8/site-packages/flask_discord_interactions/context.py", line 210, in create_args_recursive
    member_data = resolved["members"][option["value"]]
KeyError: 'members'

init_app doesnt set app

Hey mate

Found a bug while using the library, When calling init_app, the library doesnt set self.app to the app variable thats passed in causing an error when registering the route

Handle Rate-Limiting Automatically

Frequently, when a bot is started and discord.update_commands() is called, an error like this one can occur:

requests.exceptions.HTTPError: 429 Client Error: Too Many Requests for url: https://discord.com/api/v8/applications/[...]/guilds/[...]/commands
[...]
429 {
  "global": false, 
  "message": "You are being rate limited.", 
  "retry_after": 18.672
}

These errors should be handled automatically so that the bot can reliably start up.

Too many requests

Hey,
I keep receiving this error, every time I use discord.update_commands().

requests.exceptions.HTTPError: 429 Client Error: Too Many Requests for url: https://discord.com/api/v9/oauth2/token 

User commands don't work on non-members

Describe the bug
Attempting to use a context menu user command on a user that is not a member of the server will result in an error. The same issue also occurs when attempting to use a user command in the bot's DMs.

Categorization
This issue impacts...

  • The library itself
  • Documentation
  • Examples

To Reproduce
Steps to reproduce the behavior:

  1. Create a context menu user command.
  2. Attempt to use the command on a user that is not a member of the server or in the bot's DMs.

Expected behavior
The context menu command should work as normal, without any errors.

Environment
I am using... (check any that apply, leave unchecked if you don't know)

  • Quart and/or Asyncio
  • A "Bot User"
  • Additional Flask extensions (SQLAlchemy, Flask-CORS, etc.)
  • Additional backend services (Redis, RQ, Celery)

Additional context
This can easily be fixed by modifying the parse_target function in the context.py file with the following code.

if self.target_id in self.members:
    self.target = self.members[self.target_id]
else:
    self.target = self.users[self.target_id]

Sub Command Group Context Issue

When trying to access the ctx (Context) from a sub command, it looks like the Context isn't being passed correctly.

I noticed this when trying to access the "display_name" property.
image

Here is a screenshot of the properties and value.
image

If you need any other info please let me know, or if I am doing something wrong.

Suddenly getting 405 error

Everything was working fine yesterday and now when I try to run my flask app locally I get this error
requests.exceptions.HTTPError: 400 Client Error: Bad Request for url: https://discord.com/api/v10/oauth2/token

When I click the link I get this description
{ message: "405: Method Not Allowed", code: 0 }

At a loss for what's happening here, I did some googling and stackoverflow says to use post and not get but the code here uses a post method. Tried updating my secret key as well but I didn't really think that was the issue.

Add support for attachments in the initial response

As far as I'm aware, it's not possible to send attachments immediately as a response to a command, since response data needs to be JSON. Even if you could, the three second interaction timeout that Discord enforces would make it difficult to upload the file without exceeding the time limit. Therefore, sending a deferred message and then following up with the file is the supported way.

Let me know if this works for your use case.

According to the discord docs it should work. I haven't played around with it yet but it seems like you can also return multipart as direct interaction response. I'll let you know if I find something out.

Originally posted by @therealr5 in #92 (comment)

avatar_url property doesn't work for default or animated avatars

Describe the bug
Using the avatar_url property for a user that has a default or animated avatar will not function correctly. The avatar_hash will be None for default avatars, and for animated avatars the file extension of the avatar will be .png when it should be .gif.

Categorization
This issue impacts...

  • The library itself
  • Documentation
  • Examples

To Reproduce
Steps to reproduce the behavior:

  1. Use the avatar_url property on a user that has a default or animated avatar.

Expected behavior
If a user has a default or animated avatar, the correct link is returned using this property.

Environment
I am using... (check any that apply, leave unchecked if you don't know)

  • Quart and/or Asyncio
  • A "Bot User"
  • Additional Flask extensions (SQLAlchemy, Flask-CORS, etc.)
  • Additional backend services (Redis, RQ, Celery)

Additional context
This can easily be fixed by modifying the avatar_url property in the user.py file with the following code.

if self.avatar_hash is None:
    return f"https://cdn.discordapp.com/embed/avatars/{int(self.discriminator) % 5}.png"
elif str(self.avatar_hash).startswith("a_"):
    return f"https://cdn.discordapp.com/avatars/{self.id}/{self.avatar_hash}.gif"
else:
    return f"https://cdn.discordapp.com/avatars/{self.id}/{self.avatar_hash}.png"

Support for Command Permissions

This feature would allow users of this library to restrict commands to certain users only, as provided by the Discord permissions API. This would show commands as greyed-out in the command picker if a member is not explicitly granted access on a role or user level.

I have some hesitance about implementing the permissions system, for the following reasons:

  1. Commands being visible but "greyed out" is arguably not a great user experience. As an alternative, bots could handle permissions validation on the server side. They could compare user or guild IDs to those in a database on the server. If a user is not allowed to use the command, the bot could send a more helpful error message, possibly as an ephemeral response.
  2. The Permissions API for Slash Commands somewhat ironically does not allow restricting a command on the basis of a user's permissions integer. For many bots, it makes more sense to handle permissions using Discord's built in permissions framework. For instance, configuration for a guild could be restricted to those who have the "Manage Server" permission. This is a better user experience than requiring guild owners set up additional roles for bot-related permissions. Restricting commands on the server side based on guild permissions is trivial as the permissions integer is present in the initial interaction data.
  3. The Permissions API requires handling permissions on a per-guild level. Making HTTP requests to handle each guild individually consumes excessive resources.

I will leave this issue in case anyone wants this functionality or wants to discuss further, or in case Discord updates their permissions API. At the moment, I do not plan on implementing this feature.

Support for Message Components

Discord has introduced Message Components, a feature that enables embedding clickable buttons in messages. These buttons can send Interactions when triggered.

This change includes:

  • A new message field, components here that can include ComponentObjects defined here.
  • A new interaction type, MessageComponent here that defines interactions that were sent in response to a message component.
  • New response types DeferredUpdateMessage and UpdateMessage here.

Support RQ for background tasks / followup messages

The documentation and examples for followup messages use threading.Thread to manually create a new thread that handles the background task. This approach uses additional resources (for all the thread setup and teardown) and doesn't allow users to run their background tasks in a separate process / container / server. Most production Flask apps use a library like Celery or RQ to handle background tasks. This should be supported and documented by Flask-Discord-Interactions.

another suggestion for multiple workers issue

thanks for this extension! it's super easy to use within Flask!

I'm not an expert, but I use the following command to register commands. would this be something useful for the documentation?

from flask import Flask
from flask_discord_interactions import DiscordInteractions
from dotenv import load_dotenv

app = Flask(__name__)
discord = DiscordInteractions(app)

@app.before_first_request
def register_commands():
    print("registering commands!")
    discord.update_commands(guild_id=os.environ.get("GUILD_ID"))

Sending multiple Embeds results in no error but they don't get sent

I'm sending multiple embeds with this code:

@discord.command(annotations={"region": "Choose region"})
def region(ctx, region: Regions):
    "Clubs from region"
    #gets list of dictionaries to process
    clubs = get_data.get_clubs(region=region, country=None, type=None, members=None)
    #makes list of embeds (max 10, all dont exceed any limit)
    embeds = clubs_to_embeds(clubs, f"Title")
    return Response(embeds=embeds)

When I send one or two embeds everything is fine and they get sent.
But when the embeds get longer or there are more of them, slash command in Discord stays stuck at "Sending command..." and unfortunately no error appears.

I'm making sure no embed exceeds character limits and there are not more than 10 of them.
I wasn't able to find any other limit mentioned in docs
When I send them separtely as followup responses they do get sent fine.

usage in blueprint

Can you use those into blueprint? It's working in a single file, but not viable for large app.

Easier image

Is your feature request related to a problem? Please describe.
The docs is quite confusing, I don't know how I can send file myself.

Describe the solution you'd like
Explain how to send file instead of only leaving "tuple" type, or make a file object like what discord.py did.

Sub Commands Issue when Sending from Context.

When trying to send message from sub command group, receiving and invalid schema error.
It seems like the context (ctx) isn't parsed correctly, the below echo works correctly if using @bp.command()

image

image

Sorry I can't send test case, or pull request, still learning to use python.

Permissions overwrites fail to execute

Describe the bug
Attempting to use ctx.overwrite_permission fails with an HTTP 403.

Categorization
This issue impacts...

  • The library itself
  • Documentation
  • Examples

To Reproduce
Steps to reproduce the behavior:

  1. Run the examples/permissions.py example.

Expected behavior
Command permissions overwrites are handled properly without throwing errors.

Environment
I am using... (check any that apply, leave unchecked if you don't know)

  • Quart and/or Asyncio
  • A "Bot User"
  • Additional Flask extensions (SQLAlchemy, Flask-CORS, etc.)
  • Additional backend services (Redis, RQ, Celery)

Additional context
This is related to the rollout of Permissions V2: discord/discord-api-docs#4830

Couldn't handle select menu

Describe the bug
A clear and concise description of what the bug is.
Couldn't handle select menu. After select option raises this error

4|wh       |   File "/usr/local/lib/python3.9/dist-packages/flask_discord_interactions/context.py", line 153, in from_data
4|wh       |     result.parse_message(data)
4|wh       |   File "/usr/local/lib/python3.9/dist-packages/flask_discord_interactions/context.py", line 197, in parse_message
4|wh       |     self.message = Message.from_dict(data["message"])
4|wh       |   File "/usr/local/lib/python3.9/dist-packages/flask_discord_interactions/models/utils.py", line 16, in from_dict
4|wh       |     return cls(
4|wh       |   File "<string>", line 19, in __init__
4|wh       |   File "/usr/local/lib/python3.9/dist-packages/flask_discord_interactions/models/message.py", line 121, in __post_init__
4|wh       |     self.author = Member.from_dict(self.author)
4|wh       |   File "/usr/local/lib/python3.9/dist-packages/flask_discord_interactions/models/user.py", line 43, in from_dict
4|wh       |     return super().from_dict(data)
4|wh       |   File "/usr/local/lib/python3.9/dist-packages/flask_discord_interactions/models/utils.py", line 16, in from_dict
4|wh       |     return cls(
4|wh       | TypeError: __init__() missing 1 required positional argument: 'public_flags'

Categorization
This issue impacts...

  • The library itself
  • Documentation
  • Examples

To Reproduce
Steps to reproduce the behavior:

  1. ...
    Run this code
@discord.command(name="quiz")
def quiz(ctx):
    emojis = ["๐Ÿ”บ", "๐Ÿ”ท", "๐ŸŸก", "๐ŸŸฉ"]
    def get_quiz():
        q = random.choice(questions)
        random.shuffle(q["answers"])
        select = SelectMenu(
            placeholder=f'{questions.index(q)}. {q["question"]}',
            custom_id=handle_quiz_answer,
            options=[
                SelectMenuOption(
                    label=i,
                    value=f'{questions.index(q)}:{q["answers"].index(i)}',
                    emoji={"name": emojis[q["answers"].index(i)]},
                )
                for i in q["answers"]
            ],
            max_values=1,
        )
        return ctx.edit(Message(components=[ActionRow(components=[select])],update=True))

Expected behavior
A clear and concise description of what you expected to happen.
After select option run handle_quiz_answer
Environment
I am using... (check any that apply, leave unchecked if you don't know)

  • Quart and/or Asyncio
  • A "Bot User"
  • Additional Flask extensions (SQLAlchemy, Flask-CORS, etc.)
  • Additional backend services (Redis, RQ, Celery)

Additional context
Add any other context about the problem here.

dm people

Is your feature request related to a problem? Please describe.
i am kinda confused to dm someone myself

Describe the solution you'd like
a native method to dm people, possiblity User.send
Describe alternatives you've considered
trying to use the raw api

Additional context
Add any other context or screenshots about the feature request here.

Can't seem to get if channel is NSFW or not

Using your example code I can't seem to get if a channel is NSFW or not, it seems

# Channel info, too!
@discord.command()
def channel_info(ctx, channel: Channel):
    return Response(embed={
        "title": channel.name,
        "description": channel.topic,
        "fields": [
            {
                "name": "Channel ID",
                "value": channel.id
            },
            {
                "name": "NSFW?",
                "value": "Yes" if channel.nsfw else "No"
            }
        ]
    })

leads to TypeError: channel_info() missing 1 required positional argument: 'channel'

Pull request template not working

Describe the bug
The pull request template appears to be broken and is not showing at all when opening any pull request. For my past requests, I always had to copy the file manually.

Categorization
This issue impacts...

  • The library itself
  • Documentation
  • Examples
  • The repository itself

Proposed change
I'd suggest moving .github/PULL_REQUEST_TEMPLATE/pull_request_template.md -> .github/pull_request_template.md. This should fix the issue

Double logging flask with CLI

Hello,

When running my app withh the flask CLI I have an issue with logging, which is printed twice :

flask run                                 
 * Serving Flask app "app" (lazy loading)
 * Environment: development
 * Debug mode: on
 * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
 * Restarting with stat
 * Debugger is active!
 * Debugger PIN: 582-652-287
127.0.0.1 - - [11/Dec/2021 00:36:32] "GET /hello HTTP/1.1" 200 -
2021-12-11 00:36:32: 127.0.0.1 - - [11/Dec/2021 00:36:32] "GET /hello HTTP/1.1" 200 -

Log lines are transformed like this but when I use python to run the app :

python3 /home/ubuntu/workspace/app.py
 * Serving Flask app "app" (lazy loading)
 * Environment: development
 * Debug mode: on
2021-12-11 00:29:46:  * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
2021-12-11 00:29:46:  * Restarting with stat
2021-12-11 00:30:46:  * Debugger is active!
2021-12-11 00:30:46:  * Debugger PIN: 118-580-155
2021-12-11 00:30:47: 127.0.0.1 - - [11/Dec/2021 00:30:47] "GET /hello HTTP/1.1" 200 -

The code is almost the same, I only add this when running without CLI :

if __name__ == '__main__':
     app.run()

Do you know how is configured the logger ?
Is the application aim to work with flask CLI ?

Thank you for your support !

Can't get message author nickname

Describe the bug
Can't get message author nickname

Categorization
This issue impacts...

  • The library itself
  • Documentation
  • Examples

To Reproduce
Steps to reproduce the behavior:

  1. Create an ApplicationCommandType.MESSAGE
  2. Try to get the nickname of the message author
  3. We get message.author.nick=None

Expected behavior
message.author.nick not None

Environment
I am using... (check any that apply, leave unchecked if you don't know)

  • Quart and/or Asyncio
  • A "Bot User"
  • Additional Flask extensions (SQLAlchemy, Flask-CORS, etc.)
  • Additional backend services (Redis, RQ, Celery)

Additional context

AttributeError: 'Quart' object has no attribute 'discord_client_session'

Trying to create an async command with Quart ends up in this error message. Here is my code:

import asyncio
import quart.flask_patch
from quart import Quart
from flask_discord_interactions import DiscordInteractions, Message

app = Quart(__name__)
discord = DiscordInteractions(app)

load_dotenv()

app.config["DISCORD_CLIENT_ID"] = os.getenv("DISCORD_CLIENT_ID")
app.config["DISCORD_PUBLIC_KEY"] = os.getenv("DISCORD_PUBLIC_KEY")
app.config["DISCORD_CLIENT_SECRET"] = os.getenv("DISCORD_CLIENT_SECRET")
PORT = int(os.environ.get("PORT", 5000))

@discord.command()
async def wake(ctx):
    "Wake the bot up. Not necessary but very kind indeed."
    await asyncio.sleep(1)
    return "I'm awake"

discord.set_route("/interactions")
discord.update_commands(guild_id=os.getenv("TEST_SERVER_ID"))


if __name__ == '__main__':
    app.run(port=PORT)

And here is all of the stack trace:

[2021-11-17 17:56:39,313] ERROR in app: Exception on request POST /interactions
Traceback (most recent call last):
  File "F:\Projects\minecraft-server-bot-v3\venv\lib\site-packages\quart\app.py", line 1488, in handle_request
    return await self.full_dispatch_request(request_context)
  File "F:\Projects\minecraft-server-bot-v3\venv\lib\site-packages\quart\flask_patch\app.py", line 27, in new_full_dispatch_request
    return await old_full_dispatch_request(self, request_context)
  File "F:\Projects\minecraft-server-bot-v3\venv\lib\site-packages\quart\app.py", line 1513, in full_dispatch_request
    result = await self.handle_user_exception(error)
  File "F:\Projects\minecraft-server-bot-v3\venv\lib\site-packages\quart\app.py", line 963, in handle_user_exception
    raise error
  File "F:\Projects\minecraft-server-bot-v3\venv\lib\site-packages\quart\app.py", line 1511, in full_dispatch_request
    result = await self.dispatch_request(request_context)
  File "F:\Projects\minecraft-server-bot-v3\venv\lib\site-packages\quart\app.py", line 1556, in dispatch_request
    return await self.ensure_async(handler)(**request_.view_args)
  File "F:\Projects\minecraft-server-bot-v3\venv\lib\site-packages\quart\flask_patch\app.py", line 43, in _wrapper
    result = func(*args, **kwargs)
  File "F:\Projects\minecraft-server-bot-v3\venv\lib\site-packages\flask_discord_interactions\discord.py", line 514, in interactions
    return jsonify(self.run_command(request.json).dump())
  File "F:\Projects\minecraft-server-bot-v3\venv\lib\site-packages\flask_discord_interactions\discord.py", line 434, in run_command
    return command.make_context_and_run(self, current_app, data)
  File "F:\Projects\minecraft-server-bot-v3\venv\lib\site-packages\flask_discord_interactions\command.py", line 172, in make_context_and_run
    context = AsyncContext.from_data(discord, app, data)
  File "F:\Projects\minecraft-server-bot-v3\venv\lib\site-packages\flask_discord_interactions\context.py", line 89, in from_data
    result = cls(
  File "<string>", line 24, in __init__
  File "F:\Projects\minecraft-server-bot-v3\venv\lib\site-packages\flask_discord_interactions\context.py", line 432, in __post_init__
    self.session = self.app.discord_client_session
AttributeError: 'Quart' object has no attribute 'discord_client_session'

Ratelimiting issues when running with a multi-worker deployment server

when running with a multi-worker deployment server, i run into rate-limiting issues. In my case i was running with gunicorn and meinheld, as i usually do for flask apps.

[2021-07-28 13:44:58 +0000] [12] [ERROR] Exception in worker process
Traceback (most recent call last):
  File "/usr/local/lib/python3.8/site-packages/flask_discord_interactions/discord.py", line 273, in update_slash_commands
    response.raise_for_status()
  File "/usr/local/lib/python3.8/site-packages/requests/models.py", line 953, in raise_for_status
    raise HTTPError(http_error_msg, response=self)
requests.exceptions.HTTPError: 429 Client Error: Too Many Requests for url: https://discord.com/api/v9/applications/869932770145763460/guilds/457205628754984960/commands

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/usr/local/lib/python3.8/site-packages/gunicorn/arbiter.py", line 583, in spawn_worker
    worker.init_process()
  File "/usr/local/lib/python3.8/site-packages/gunicorn/workers/base.py", line 119, in init_process
    self.load_wsgi()
  File "/usr/local/lib/python3.8/site-packages/gunicorn/workers/base.py", line 144, in load_wsgi
    self.wsgi = self.app.wsgi()
  File "/usr/local/lib/python3.8/site-packages/gunicorn/app/base.py", line 67, in wsgi
    self.callable = self.load()
  File "/usr/local/lib/python3.8/site-packages/gunicorn/app/wsgiapp.py", line 49, in load
    return self.load_wsgiapp()
  File "/usr/local/lib/python3.8/site-packages/gunicorn/app/wsgiapp.py", line 39, in load_wsgiapp
    return util.import_app(self.app_uri)
  File "/usr/local/lib/python3.8/site-packages/gunicorn/util.py", line 358, in import_app
    mod = importlib.import_module(module)
  File "/usr/local/lib/python3.8/importlib/__init__.py", line 127, in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
  File "<frozen importlib._bootstrap>", line 1014, in _gcd_import
  File "<frozen importlib._bootstrap>", line 991, in _find_and_load
  File "<frozen importlib._bootstrap>", line 975, in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 671, in _load_unlocked
  File "<frozen importlib._bootstrap_external>", line 783, in exec_module
  File "<frozen importlib._bootstrap>", line 219, in _call_with_frames_removed
  File "/app/main.py", line 27, in <module>
    discord.update_slash_commands(guild_id=457205628754984960)
  File "/usr/local/lib/python3.8/site-packages/flask_discord_interactions/discord.py", line 275, in update_slash_commands
    raise ValueError(
ValueError: Unable to register commands:429 {
  "global": false, 
  "message": "You are being rate limited.", 
  "retry_after": 9.517
}

[2021-07-28 13:44:58 +0000] [11] [INFO] Worker exiting (pid: 11)
[2021-07-28 13:44:58 +0000] [12] [INFO] Worker exiting (pid: 12)

Could there perhaps be some checking done to ensure that API calls are only executed once when running multiple workers? it's common for flask apps to run with many workers, so this is quite crucial.

Persistent buttons

It seems like this is possible in the full python Discord api but I was wondering if there'd be a way to leverage custom IDs and interaction with a database to allow for buttons to always perform the same action no matter what.

Unclosed client session warning when using async (Quart) commands

I made a test Quart app that registers a few Discord commands (based on async_quart.py example). The app works and commands also return the correct response, but every time I use async command I get a warning. This does not happen with sync commands.

Although this seems to only be a warning and app still works, I think it would be nice if this warning is fixed or suppressed.

My code:

import os
import quart.flask_patch

from flask_discord_interactions import DiscordInteractions
from quart import Quart

app = Quart(__name__)
discord = DiscordInteractions(app)

app.config["DISCORD_CLIENT_ID"] = os.environ["DISCORD_CLIENT_ID"]
app.config["DISCORD_PUBLIC_KEY"] = os.environ["DISCORD_PUBLIC_KEY"]
app.config["DISCORD_CLIENT_SECRET"] = os.environ["DISCORD_CLIENT_SECRET"]

discord.update_slash_commands()

@discord.command()
async def ping(ctx):
    return "Pong!"

@discord.command()
def pong(ctx):
    return "Ping!"

discord.set_route_async("/interactions")
discord.update_slash_commands(guild_id=os.environ["TESTING_GUILD"])


if __name__ == '__main__':
    app.run()

And the warning (which happens when I use /ping):

$ python main.py
 * Serving Quart app 'main'
 * Environment: production
 * Please use an ASGI server (e.g. Hypercorn) directly in production
 * Debug mode: False
 * Running on http://127.0.0.1:5000 (CTRL + C to quit)
[2021-08-11 18:29:56,356] Running on http://127.0.0.1:5000 (CTRL + C to quit)
Unclosed client session
client_session: <aiohttp.client.ClientSession object at 0x0000018CB8BF4910>
[2021-08-11 18:30:21,070] 127.0.0.1:3050 POST /interactions 1.1 200 149 5213

Versions:

aiohttp==3.7.4.post0
Flask==2.0.1
Flask-Discord-Interactions==1.0.0
Quart==0.15.1

how to Use Flask-discord-interactions..?

I'm trying to use this library, but I have trouble using the bot command is a guide / documentation for this library, here is mycode

import os
from dotenv import load_dotenv
from flask import Flask
from flask_discord_interactions import DiscordInteractions

app = Flask(__name__)
discord = DiscordInteractions(app)

# load dotenv
load_dotenv('.env')

# configuration
app.config["DISCORD_CLIENT_ID"] = os.getenv("DISCORD_CLIENT_ID")
app.config["DISCORD_PUBLIC_KEY"] = os.getenv("DISCORD_PUBLIC_KEY")
app.config["DISCORD_CLIENT_SECRET"] = os.getenv("DISCORD_CLIENT_SECRET")

@discord.command()
def ping(ctx):
    "Respond with a friendly 'pong'!"
    return "Pong!"


discord.set_route("/interactions")


if __name__ == '__main__':
    app.run()

Support Bulk Overwrite for Command Registration

Discord has added new API endpoints, "Bulk Overwrite Global Application Commands" and "Bulk Overwrite Guild Application Commands," documented here.

This library should make use of these endpoints to register new commands without using as many HTTP requests. This should speed up development considerably as users will no longer need to wait for the rate limit after each request.

Sending PNG image attachments causes utf-8 error

Describe the bug
When responding with a PNG file attachment, I am getting a UnicodeDecodeError: 'utf-8' codec can't decode byte 0x89 in position 0: invalid start byte error.

@discord.command(name="test", description="Test command!")
def command_test(ctx):
    return Message(
        embed = Embed(
            title = "Embed Title",
            description = "Description",
            color = 2728830,
            image = {"url": "attachment://google.png"},
        ),
        file = ("google.png", open("google.png", "r"), "image/png")
    )

Stack trace:

10.0.2.2 - - [12/Nov/2022 18:42:19] "POST /discord HTTP/1.1" 500 -
Traceback (most recent call last):
  File "/home/endendragon/.local/lib/python3.7/site-packages/flask/app.py", line 2548, in __call__
    return self.wsgi_app(environ, start_response)
  File "/home/endendragon/.local/lib/python3.7/site-packages/flask/app.py", line 2528, in wsgi_app
    response = self.handle_exception(e)
  File "/home/endendragon/.local/lib/python3.7/site-packages/flask/app.py", line 2525, in wsgi_app
    response = self.full_dispatch_request()
  File "/home/endendragon/.local/lib/python3.7/site-packages/flask/app.py", line 1822, in full_dispatch_request
    rv = self.handle_user_exception(e)
  File "/home/endendragon/.local/lib/python3.7/site-packages/flask/app.py", line 1820, in full_dispatch_request
    rv = self.dispatch_request()
  File "/home/endendragon/.local/lib/python3.7/site-packages/flask/app.py", line 1796, in dispatch_request
    return self.ensure_sync(self.view_functions[rule.endpoint])(**view_args)
  File "/home/endendragon/.local/lib/python3.7/site-packages/flask_discord_interactions/discord.py", line 851, in interactions
    response, mimetype = result.encode()
  File "/home/endendragon/.local/lib/python3.7/site-packages/flask_discord_interactions/models/message.py", line 244, in encode
    return (multipart.to_string().decode(), multipart.content_type)
  File "/home/endendragon/.local/lib/python3.7/site-packages/requests_toolbelt/multipart/encoder.py", line 297, in to_string
    return self.read()
  File "/home/endendragon/.local/lib/python3.7/site-packages/requests_toolbelt/multipart/encoder.py", line 314, in read
    self._load(bytes_to_load)
  File "/home/endendragon/.local/lib/python3.7/site-packages/requests_toolbelt/multipart/encoder.py", line 203, in _load
    written += part.write_to(self._buffer, amount)
  File "/home/endendragon/.local/lib/python3.7/site-packages/requests_toolbelt/multipart/encoder.py", line 528, in write_to
    written += buffer.append(self.body.read(amount_to_read))
  File "/home/endendragon/.local/lib/python3.7/site-packages/requests_toolbelt/multipart/encoder.py", line 576, in read
    return self.fd.read(length)
  File "/usr/lib/python3.7/codecs.py", line 322, in decode
    (result, consumed) = self._buffer_decode(data, self.errors, final)
UnicodeDecodeError: 'utf-8' codec can't decode byte 0x89 in position 0: invalid start byte

Categorization
This issue impacts...

  • The library itself
  • Documentation
  • Examples

To Reproduce
A mockup of the source is provided. To test, here is a sample image you may download and put aside next to the script: https://www.google.com/images/branding/googlelogo/2x/googlelogo_color_272x92dp.png

Expected behavior
Image to be sent along with the rich embed.

Environment
I am using... (check any that apply, leave unchecked if you don't know)

  • Quart and/or Asyncio
  • A "Bot User"
  • Additional Flask extensions (SQLAlchemy, Flask-CORS, etc.)
  • Additional backend services (Redis, RQ, Celery)

Additional context
I understand there is a 3 second limit in Discord. We'll look into message deferrals later. However the utf8 error is the first barrier in sending image attachments.

Missing author info from Message for ApplicationCommandType.MESSAGE

Thanks for building out this awesome package, I'm finding a lot of value from it.

I have an Application Command of Type message (the right click drop down Apps > {command} location). I'm trying to grab nicks for the comment author. I can grab nicks for the person who right-clicked and used the command and their server nick is correct, but not for the comment author themselves. It just seems it isn't included in the message context for some reason.

"author": {
  "avatar": "1b324faadb6337f96cee3a737ee8f9a8",
  "discriminator": "****",
  "id": "********",
  "public_flags": 0,
  "username": "********"
}

Is there a way to get the nick of this user using your package? The alternative I was going to go down was to use fetch_token() and make an additional request using the user ID. I might have to acquire actual bot credentials to do that, I'm not sure.

Thanks for any help ahead of time!

Adding autocomplete interactions

Not long ago discord released autocomplete for slash command options and I'd really appreciate seeing them implemented into this library.

Example use case
You have a database with about 1000 different Cities and want the user to select one. With autocomplete you can deliver matches fitting to what the user is typing into that option field.

A Solution I'd like
I'd appreciate a decorator based solutions like it's done with the Components. Maybe something like:

@discord.command(
    name="city-select"
    options=[
        Option(
            type=3, 
            name="country", 
            autocomplete=True
        ),
        Option(
            type=3, 
            name="city",
            autocomplete=True
        )
    ]
)
def city_select(ctx, country: str, city: str):
    return f"You selected {country}/{city}"
    

@discord.autocomplete(command="city-select",)
def city_select_autocomplete(country: Option, city: Option) -> list[Choices]:
    # In some cases we get more than one field delivered so we have to check whether they are focused or not
    # If there is only one option with autocomplete we can leave that check out
    
    if country and country.focused:
        return get_matching_countries(country.value)
    elif cty and city.focused:
        return get_matching_cities(city.value)

    

Alternatives I've considered
There are the default choices but those are limited to 25 per option. Autocomplete allows way more.

Further resources and context
https://devsnek.notion.site/devsnek/Application-Command-Option-Autocomplete-Interactions-dacc980320c948768cec5ae3a96a5886

https://discord.com/developers/docs/interactions/receiving-and-responding#interaction-response-object-autocomplete

Feature request/question: How do we signal an error from a command?

Example:

@discord.command()
def hextodec(ctx, value: str):
    try:
        return str(int(value, 16))
    except ValueError:
        raise # ???

Just allowing the exception to continue results in this:

image

I would like to be able to supply a custom error message here while keeping the result visible only to the calling user. Is this possible? I couldn't find anything in the docs about it.

Support for Select Menus

Discord has added a new Interaction type, the "Select Menu." This is a dropdown/checkbox style input that allows users to select one or multiple values in a list. Here is the relevant documentation.

This library should add support for Select Menus. This should be implemented in component.py, to allow users to define SelectMenus similarly to ActionRows and Buttons. A SelectMenu class could assemble the JSON object to include in the message, similarly to how the Button class builds the JSON to create a button.

It appears that incoming Select Menu interactions are similar to incoming Button interactions, with an additional response["data"]["values"] list containing the selected values. This list should be passed as a parameter to the relevant custom ID handler.

KeyError: 'TESTING_GUILD'

Hi,

It seems there's in an error with the discord.update_slash_commands(guild_id=os.environ["TESTING_GUILD"]) you have in your example. When added, it prevents Flask to run...

Without it, I wasn't able to also have the bot going live on Discord... so wondering if this is related to this specific issue or there's another part I missed...

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.