Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/build-alpha.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ jobs:
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: 8
dotnet-version: 9
- name: Package Project
run: |
dotnet pack -c Release -o build -p:Alpha=${{ github.run_number }}
Expand Down
6 changes: 3 additions & 3 deletions .github/workflows/build-commit.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ jobs:
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: 8
dotnet-version: 9
- name: Build Project
run: dotnet build
- name: Test Changes
Expand All @@ -50,7 +50,7 @@ jobs:
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: 8
dotnet-version: 9
- name: Get Nightly Version
id: nightly
run: printf "version=%0*d" 5 $(( 1195 + 691 + ${{ github.run_number }} )) >> "$GITHUB_OUTPUT"
Expand Down Expand Up @@ -95,7 +95,7 @@ jobs:
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: 8
dotnet-version: 9
- name: Build Project
run: |
dotnet build
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/build-pr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ jobs:
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: 8
dotnet-version: 9
- name: Build Project
run: dotnet build
- name: Test Changes
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ jobs:
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: 8
dotnet-version: 9
- name: Build Project
run: dotnet build
package-commit:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@ namespace DSharpPlus.Commands.ContextChecks;
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method | AttributeTargets.Delegate)]
public class RequirePermissionsAttribute : RequireGuildAttribute
{
public DiscordPermissions BotPermissions { get; init; }
public DiscordPermissions UserPermissions { get; init; }
public DiscordPermission[] BotPermissions { get; init; }
public DiscordPermission[] UserPermissions { get; init; }

public RequirePermissionsAttribute(DiscordPermissions permissions) => this.BotPermissions = this.UserPermissions = permissions;
public RequirePermissionsAttribute(DiscordPermissions botPermissions, DiscordPermissions userPermissions)
public RequirePermissionsAttribute(params DiscordPermission[] permissions) => this.BotPermissions = this.UserPermissions = permissions;
public RequirePermissionsAttribute(DiscordPermission[] botPermissions, DiscordPermission[] userPermissions)
{
this.BotPermissions = botPermissions;
this.UserPermissions = userPermissions;
Expand Down
9 changes: 6 additions & 3 deletions DSharpPlus.Commands/ContextChecks/RequirePermissionsCheck.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,12 @@ internal sealed class RequirePermissionsCheck : IContextCheck<RequirePermissions
{
public ValueTask<string?> ExecuteCheckAsync(RequirePermissionsAttribute attribute, CommandContext context)
{
DiscordPermissions requiredBotPermissions = new(attribute.BotPermissions);
DiscordPermissions requiredUserPermissions = new(attribute.UserPermissions);

if (context is SlashCommandContext slashContext)
{
if (!slashContext.Interaction.AppPermissions.HasPermission(attribute.BotPermissions))
if (!slashContext.Interaction.AppPermissions.HasAllPermissions(requiredBotPermissions))
{
return ValueTask.FromResult<string?>("The bot did not have the needed permissions to execute this command.");
}
Expand All @@ -24,11 +27,11 @@ internal sealed class RequirePermissionsCheck : IContextCheck<RequirePermissions
{
return ValueTask.FromResult<string?>(RequireGuildCheck.ErrorMessage);
}
else if (!context.Guild!.CurrentMember.PermissionsIn(context.Channel).HasFlag(attribute.BotPermissions))
else if (!context.Guild!.CurrentMember.PermissionsIn(context.Channel).HasAllPermissions(requiredBotPermissions))
{
return ValueTask.FromResult<string?>("The bot did not have the needed permissions to execute this command.");
}
else if (!context.Member!.PermissionsIn(context.Channel).HasFlag(attribute.UserPermissions))
else if (!context.Member!.PermissionsIn(context.Channel).HasAllPermissions(requiredUserPermissions))
{
return ValueTask.FromResult<string?>("The executing user did not have the needed permissions to execute this command.");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -177,13 +177,18 @@ public async Task<DiscordApplicationCommand> ToApplicationCommandAsync(Command c
nameLocalizations = await this.slashCommandProcessor.ExecuteLocalizerAsync(localizerAttribute.LocalizerType, $"{command.FullName}.name");
}

return new(
DiscordPermission[]? userPermissions = command.Attributes.OfType<RequirePermissionsAttribute>().FirstOrDefault()?.UserPermissions;

return new
(
name: command.Attributes.OfType<DisplayNameAttribute>().FirstOrDefault()?.DisplayName ?? command.FullName,
description: string.Empty,
type: DiscordApplicationCommandType.MessageContextMenu,
name_localizations: nameLocalizations,
allowDMUsage: command.Attributes.Any(x => x is AllowDMUsageAttribute),
defaultMemberPermissions: command.Attributes.OfType<RequirePermissionsAttribute>().FirstOrDefault()?.UserPermissions ?? DiscordPermissions.UseApplicationCommands,
defaultMemberPermissions: userPermissions is not null
? new(userPermissions)
: new DiscordPermissions(DiscordPermission.UseApplicationCommands),
nsfw: command.Attributes.Any(x => x is RequireNsfwAttribute),
contexts: command.Attributes.OfType<InteractionAllowedContextsAttribute>().FirstOrDefault()?.AllowedContexts,
integrationTypes: command.Attributes.OfType<InteractionInstallTypeAttribute>().FirstOrDefault()?.InstallTypes
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -248,16 +248,21 @@ public async ValueTask<DiscordApplicationCommand> ToApplicationCommandAsync(Comm
description = "No description provided.";
}

DiscordPermission[]? userPermissions = command.Attributes.OfType<RequirePermissionsAttribute>().FirstOrDefault()?.UserPermissions;

// Create the top level application command.
return new(
return new
(
name: this.Configuration.NamingPolicy.TransformText(command.Name, CultureInfo.InvariantCulture),
description: description,
options: options,
type: DiscordApplicationCommandType.SlashCommand,
name_localizations: nameLocalizations,
description_localizations: descriptionLocalizations,
allowDMUsage: command.Attributes.Any(x => x is AllowDMUsageAttribute),
defaultMemberPermissions: command.Attributes.OfType<RequirePermissionsAttribute>().FirstOrDefault()?.UserPermissions ?? DiscordPermissions.UseApplicationCommands,
defaultMemberPermissions: userPermissions is not null
? new(userPermissions)
: new DiscordPermissions(DiscordPermission.UseApplicationCommands),
nsfw: command.Attributes.Any(x => x is RequireNsfwAttribute),
contexts: command.Attributes.OfType<InteractionAllowedContextsAttribute>().FirstOrDefault()?.AllowedContexts,
integrationTypes: command.Attributes.OfType<InteractionInstallTypeAttribute>().FirstOrDefault()?.InstallTypes
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -177,13 +177,18 @@ public async Task<DiscordApplicationCommand> ToApplicationCommandAsync(Command c
nameLocalizations = await this.slashCommandProcessor.ExecuteLocalizerAsync(localizerAttribute.LocalizerType, $"{command.FullName}.name");
}

return new(
DiscordPermission[]? userPermissions = command.Attributes.OfType<RequirePermissionsAttribute>().FirstOrDefault()?.UserPermissions;

return new
(
name: command.Attributes.OfType<DisplayNameAttribute>().FirstOrDefault()?.DisplayName ?? command.FullName,
description: string.Empty,
type: DiscordApplicationCommandType.UserContextMenu,
name_localizations: nameLocalizations,
allowDMUsage: command.Attributes.Any(x => x is AllowDMUsageAttribute),
defaultMemberPermissions: command.Attributes.OfType<RequirePermissionsAttribute>().FirstOrDefault()?.UserPermissions ?? DiscordPermissions.UseApplicationCommands,
defaultMemberPermissions: userPermissions is not null
? new(userPermissions)
: new DiscordPermissions(DiscordPermission.UseApplicationCommands),
nsfw: command.Attributes.Any(x => x is RequireNsfwAttribute),
contexts: command.Attributes.OfType<InteractionAllowedContextsAttribute>().FirstOrDefault()?.AllowedContexts,
integrationTypes: command.Attributes.OfType<InteractionInstallTypeAttribute>().FirstOrDefault()?.InstallTypes
Expand Down
2 changes: 2 additions & 0 deletions DSharpPlus.Commands/Trees/CommandParameterBuilder.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
#pragma warning disable CA2264

using System;
using System.Collections.Generic;
using System.ComponentModel;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ public sealed class RequireBotPermissionsAttribute : CheckBaseAttribute
/// <summary>
/// Gets the permissions required by this attribute.
/// </summary>
public DiscordPermissions Permissions { get; }
public DiscordPermission[] Permissions { get; }

/// <summary>
/// Gets this check's behaviour in DMs. True means the check will always pass in DMs, whereas false means that it will always fail.
Expand All @@ -25,7 +25,7 @@ public sealed class RequireBotPermissionsAttribute : CheckBaseAttribute
/// </summary>
/// <param name="permissions">Permissions required to execute this command.</param>
/// <param name="ignoreDms">Sets this check's behaviour in DMs. True means the check will always pass in DMs, whereas false means that it will always fail.</param>
public RequireBotPermissionsAttribute(DiscordPermissions permissions, bool ignoreDms = true)
public RequireBotPermissionsAttribute(bool ignoreDms = true, params DiscordPermission[] permissions)
{
this.Permissions = permissions;
this.IgnoreDms = ignoreDms;
Expand All @@ -51,6 +51,6 @@ public override async Task<bool> ExecuteCheckAsync(CommandContext ctx, bool help

DiscordPermissions pbot = ctx.Channel.PermissionsFor(bot);

return (pbot & DiscordPermissions.Administrator) != 0 || (pbot & this.Permissions) == this.Permissions;
return pbot.HasAllPermissions(this.Permissions);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ public sealed class RequirePermissionsAttribute : CheckBaseAttribute
/// <summary>
/// Gets the permissions required by this attribute.
/// </summary>
public DiscordPermissions Permissions { get; }
public DiscordPermission[] Permissions { get; }

/// <summary>
/// Gets this check's behaviour in DMs. True means the check will always pass in DMs, whereas false means that it will always fail.
Expand All @@ -25,7 +25,7 @@ public sealed class RequirePermissionsAttribute : CheckBaseAttribute
/// </summary>
/// <param name="permissions">Permissions required to execute this command.</param>
/// <param name="ignoreDms">Sets this check's behaviour in DMs. True means the check will always pass in DMs, whereas false means that it will always fail.</param>
public RequirePermissionsAttribute(DiscordPermissions permissions, bool ignoreDms = true)
public RequirePermissionsAttribute(bool ignoreDms = true, params DiscordPermission[] permissions)
{
this.Permissions = permissions;
this.IgnoreDms = ignoreDms;
Expand Down Expand Up @@ -59,12 +59,12 @@ public override async Task<bool> ExecuteCheckAsync(CommandContext ctx, bool help

if (!usrok)
{
usrok = (pusr & DiscordPermissions.Administrator) != 0 || (pusr & this.Permissions) == this.Permissions;
usrok = pusr.HasAllPermissions(this.Permissions);
}

if (!botok)
{
botok = (pbot & DiscordPermissions.Administrator) != 0 || (pbot & this.Permissions) == this.Permissions;
botok = pusr.HasAllPermissions(this.Permissions);
}

return usrok && botok;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ public sealed class RequireUserPermissionsAttribute : CheckBaseAttribute
/// <summary>
/// Gets the permissions required by this attribute.
/// </summary>
public DiscordPermissions Permissions { get; }
public DiscordPermission[] Permissions { get; }

/// <summary>
/// Gets this check's behaviour in DMs. True means the check will always pass in DMs, whereas false means that it will always fail.
Expand All @@ -25,7 +25,7 @@ public sealed class RequireUserPermissionsAttribute : CheckBaseAttribute
/// </summary>
/// <param name="permissions">Permissions required to execute this command.</param>
/// <param name="ignoreDms">Sets this check's behaviour in DMs. True means the check will always pass in DMs, whereas false means that it will always fail.</param>
public RequireUserPermissionsAttribute(DiscordPermissions permissions, bool ignoreDms = true)
public RequireUserPermissionsAttribute(bool ignoreDms = true, params DiscordPermission[] permissions)
{
this.Permissions = permissions;
this.IgnoreDms = ignoreDms;
Expand All @@ -51,8 +51,6 @@ public override Task<bool> ExecuteCheckAsync(CommandContext ctx, bool help)

DiscordPermissions pusr = ctx.Channel.PermissionsFor(usr);

return (pusr & DiscordPermissions.Administrator) != 0
? Task.FromResult(true)
: (pusr & this.Permissions) == this.Permissions ? Task.FromResult(true) : Task.FromResult(false);
return Task.FromResult(pusr.HasAllPermissions(this.Permissions));
}
}
2 changes: 1 addition & 1 deletion DSharpPlus.Interactivity/EventHandling/Paginator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@ private static async Task ResetReactionsAsync(IPaginationRequest p)
DiscordGuild? gld = chn?.Guild;
DiscordMember? mbr = gld?.CurrentMember;

if (mbr != null /* == is guild and cache is valid */ && (chn.PermissionsFor(mbr) & DiscordPermissions.ManageChannels) != 0) /* == has permissions */
if (mbr != null /* == is guild and cache is valid */ && chn.PermissionsFor(mbr).HasPermission(DiscordPermission.ManageChannels)) /* == has permissions */
{
await msg.DeleteAllReactionsAsync("Pagination");
}
Expand Down
2 changes: 1 addition & 1 deletion DSharpPlus.Interactivity/EventHandling/Poller.cs
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ internal Task HandleReactionAdd(DiscordClient client, MessageReactionAddedEventA
else
{
Entities.DiscordMember member = await eventargs.Channel.Guild.GetMemberAsync(client.CurrentUser.Id);
if (eventargs.Channel.PermissionsFor(member).HasPermission(DiscordPermissions.ManageMessages))
if (eventargs.Channel.PermissionsFor(member).HasPermission(DiscordPermission.ManageMessages))
{
await eventargs.Message.DeleteReactionAsync(eventargs.Emoji, eventargs.User);
}
Expand Down
2 changes: 1 addition & 1 deletion DSharpPlus.Interactivity/InteractivityExtension.cs
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ public async Task<ReadOnlyCollection<PollEmoji>> DoPollAsync(DiscordMessage m, I
PollBehaviour pollbehaviour = behaviour ?? this.Config.PollBehaviour;
DiscordMember thismember = await m.Channel.Guild.GetMemberAsync(this.Client.CurrentUser.Id);

if (pollbehaviour == PollBehaviour.DeleteEmojis && m.Channel.PermissionsFor(thismember).HasPermission(DiscordPermissions.ManageMessages))
if (pollbehaviour == PollBehaviour.DeleteEmojis && m.Channel.PermissionsFor(thismember).HasPermission(DiscordPermission.ManageMessages))
{
await m.DeleteAllReactionsAsync();
}
Expand Down
4 changes: 2 additions & 2 deletions DSharpPlus.Rest/DiscordRestClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1095,7 +1095,7 @@ public async Task<DiscordDmChannel> CreateDmAsync(ulong recipientId)
/// </summary>
/// <param name="channelId">ID of the channel to follow</param>
/// <param name="webhookChannelId">ID of the channel to crosspost messages to</param>
/// <exception cref="UnauthorizedException">Thrown when the current user doesn't have <see cref="DiscordPermissions.ManageWebhooks"/> on the target channel</exception>
/// <exception cref="UnauthorizedException">Thrown when the current user doesn't have <see cref="DiscordPermission.ManageWebhooks"/> on the target channel</exception>
public async Task<DiscordFollowedChannel> FollowChannelAsync(ulong channelId, ulong webhookChannelId)
=> await this.ApiClient.FollowChannelAsync(channelId, webhookChannelId);

Expand All @@ -1105,7 +1105,7 @@ public async Task<DiscordFollowedChannel> FollowChannelAsync(ulong channelId, ul
/// <param name="channelId">ID of the news channel the message to crosspost belongs to</param>
/// <param name="messageId">ID of the message to crosspost</param>
/// <exception cref="UnauthorizedException">
/// Thrown when the current user doesn't have <see cref="DiscordPermissions.ManageWebhooks"/> and/or <see cref="DiscordPermissions.SendMessages"/>
/// Thrown when the current user doesn't have <see cref="DiscordPermission.ManageWebhooks"/> and/or <see cref="DiscordPermission.SendMessages"/>
/// </exception>
public async Task<DiscordMessage> CrosspostMessageAsync(ulong channelId, ulong messageId)
=> await this.ApiClient.CrosspostMessageAsync(channelId, messageId);
Expand Down
2 changes: 1 addition & 1 deletion DSharpPlus.Tests/DSharpPlus.Tests.csproj
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<TargetFramework>net9.0</TargetFramework>
<Nullable>enable</Nullable>
<IsPackable>false</IsPackable>
<IsTestProject>true</IsTestProject>
Expand Down
2 changes: 1 addition & 1 deletion DSharpPlus.VoiceNext/VoiceNextExtension.cs
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ public async Task<VoiceNextConnection> ConnectAsync(DiscordChannel channel)
throw new ArgumentException("Invalid channel specified; needs to be guild channel", nameof(channel));
}

if (!channel.PermissionsFor(channel.Guild.CurrentMember).HasPermission(DiscordPermissions.AccessChannels | DiscordPermissions.UseVoice))
if (!channel.PermissionsFor(channel.Guild.CurrentMember).HasAllPermissions(DiscordPermission.ViewChannel, DiscordPermission.Connect))
{
throw new InvalidOperationException("You need AccessChannels and UseVoice permission to connect to this voice channel");
}
Expand Down
Loading