Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Account for user installations in Channel.permissions_for #9837

Open
wants to merge 13 commits into
base: master
Choose a base branch
from

Conversation

Kile
Copy link
Contributor

@Kile Kile commented May 18, 2024

Summary

Currently Channel.permissions_for do not account for user installed apps. So in dms they will have _dm_permissions() permissions even though they cannot see the chat history and in guilds the bot is not on, the library will error because it attempts to access guild data it does not have access to. To address this, I am introducing Permissions._user_installed_permissions() which includes a default for what permissions a user installed app has. This method is used whenever permissions are checked for the Bot/App when it is not part of the server or dm the permissions are checked for.

I know Discord provides this permission information in Interaction.app_permissions and Context.bot_permissions, but I think this is still important for compatibility and "migrating" to user installed commands (the reason I encountered this in the first place because I changed one of my commands to support user installs.). Furthermore it is never great to get an internal dpy error without explanation.

Please let me know if my code style needs change or the permissions in my new method need changing.

Checklist

  • If code changes were made then they have been tested.
    • I have updated the documentation to reflect the changes.
  • This PR fixes an issue.
  • This PR adds something new (e.g. new method or parameters).
  • This PR is a breaking change (e.g. methods or parameters removed/renamed)
  • This PR is not a code change (e.g. documentation, README, ...)

Minimal reproducible example

Here is some code to reproduce the issues d.py currently has:

import discord
from discord.ext import commands
import logging

from asyncio import run

logging.basicConfig(level=logging)

class SyncingBot(commands.Bot):
    async def setup_hook(self):
        synced = await self.tree.sync()
        print(f"Synced with data:\n{synced}")

class TestCog(commands.Cog):
    def __init__(self, bot):
        self.bot = bot

    @discord.app_commands.command()
    @discord.app_commands.allowed_installs(guilds=True, users=True)
    @discord.app_commands.allowed_contexts(dms=True, guilds=True, private_channels=True)
    async def test_slash(self, interaction: discord.Interaction):
        # Inaccurate perms in dms (except with the bot), errors in servers the bot is not in
        await interaction.response.send_message(interaction.channel.permissions_for(interaction.user), ephemeral=True)

    @commands.hybrid_command()
    @discord.app_commands.allowed_installs(guilds=True, users=True)
    @discord.app_commands.allowed_contexts(dms=True, guilds=True, private_channels=True)
    async def test_hybrid(self, ctx: commands.Context):
        # Inaccurate perms in dms (except with the bot), errors in servers the bot is not in
        await ctx.send(ctx.channel.permissions_for(ctx.me), ephemeral=True)

async def main():
    bot = SyncingBot(command_prefix="!", intents=discord.Intents.default(), chunk_guilds_at_startup=False)
    await bot.add_cog(TestCog(bot))
    await bot.start("TOKEN")

run(main())

@Kile
Copy link
Contributor Author

Kile commented May 18, 2024

Ok no idea how I fix those two tests failing

@LostLuma
Copy link
Contributor

LostLuma commented May 18, 2024

Run black on channel.py, then those should pass.

@Kile
Copy link
Contributor Author

Kile commented May 18, 2024

Well that didn't work

@z03h
Copy link
Contributor

z03h commented May 18, 2024

Discord provides the permissions for your app/bot in the current channel with Interaction.app_permissions. Hybrid commands can use Context.bot_permissions.

@Kile
Copy link
Contributor Author

Kile commented May 18, 2024

That's fair, but for migrating purposes and because it seems not great to have internal error I still think this has a place

@LostLuma
Copy link
Contributor

Well that didn't work

Sorry, I forgot that discord.py runs a specific version (22.6) in CI, which formats stuff a bit different. Before your Make tests pass commit it seems the thing it doesn't like is erroneous whitespace in this docstring on the line I linked.

@Kile Kile changed the title Adjust permission values for user installations Account for user installations in Channel.permissions_for May 18, 2024
@Rapptz
Copy link
Owner

Rapptz commented May 18, 2024

That's fair, but for migrating purposes and because it seems not great to have internal error I still think this has a place

We debated this long before but I wasn't sure if it was worth going further, and I still don't. Code that is installed and used in these new contexts should just use the correct code.

@Kile
Copy link
Contributor Author

Kile commented May 18, 2024

Just crossposting from bikeshedding here

I would argue keeping Channel.permissions_for the same is, while not a breaking change of its own, very likely going to cause some issues since if someone just adds the user install decorator to their command no error will appear so they won't check the docs where it tells them not to use it (unlike for me where there was an error, otherwise I would have not figured this out) so unless the console prints a warning (which I dont think you want) this not being accurate will remain unnoticed.

By O(n) I am assuming you mean if not obj in self.recipients:. Sure, in theory O(n) is not great. But this is only for gdms and dms, so 1 < n < 10 which is not a very big n at all. I think this would be much more applicable if n would be guild members or smth but since the maximum is 9 I think that is a worthy tradeoff for accurate permissions.
This is definititely annoying from Discord to do but I don't see a big tradeoff for dpy to fix where Discord went wrong

Also regardless of migration I think you also value consistency. It would not be very consistent if Channel.permissions_for was accurate for some cases but not for others

@Kile
Copy link
Contributor Author

Kile commented May 18, 2024

As discussed I have removed this for dms and gdms. I have kept in the in_guild parameter for the new class method just because I felt like it would be misleading to leave it out, acting as though bots had tts perms (and some other guild exclusive ones) anywhere if user installed. Now to check those test failures.

@Kile
Copy link
Contributor Author

Kile commented May 18, 2024

I have also added a recommendation to use Interaction.app_permissions instead in the docs. I didn't feel like more explanation for user apps for dms was necessary since, as the docstring already mentions, dm permissions are not really a thing and the default is close enough.

Co-authored-by: owocado <24418520+owocado@users.noreply.github.com>
discord/channel.py Outdated Show resolved Hide resolved
Comment on lines 754 to +756
default = self.guild.default_role
if default is None:
return Permissions._user_installed_permissions(in_guild=True)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doesn't this need a check to ensure it's the bot user? This could return incorrect permissions if one uses permissions_for on a user other than the bot.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That is a good point.
What do you think would be the most sensible way of returning user permissions? Just 0? Also, how do I access the bot/bot ID in this method?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can get the bot id from channel._state.self_id. I don't have any better ideas other than 0.

Returns
--------
:class:`Permissions`
The user to check permissions for. This parameter is ignored
but kept for compatibility with other ``permissions_for`` methods.
The resolved permissions.
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Needs to be indented.

@LeoCx1000
Copy link
Contributor

Wouldn't this be returning wrong information? The following wouldn't work with the current permissions returned, even though it's a sane assumption to make:-

if ctx.channel.permissions_for(ctx.me).read_message_history:
    async for message in ctx.channel.history(): ...

Same goes for .send_messages- actually also for every other one since the bot isn't in the guild. This would lead to wrong assumptions potentially being made.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

6 participants