Skip to content

Commit 79cc4e2

Browse files
committed
Add PrivilegeConfig#canMemberRun
1 parent 39e1a6e commit 79cc4e2

File tree

1 file changed

+203
-3
lines changed

1 file changed

+203
-3
lines changed

src/main/java/net/dv8tion/jda/api/interactions/commands/PrivilegeConfig.java

Lines changed: 203 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,17 +17,22 @@
1717
package net.dv8tion.jda.api.interactions.commands;
1818

1919
import net.dv8tion.jda.api.JDA;
20+
import net.dv8tion.jda.api.Permission;
2021
import net.dv8tion.jda.api.entities.Guild;
22+
import net.dv8tion.jda.api.entities.Member;
23+
import net.dv8tion.jda.api.entities.Role;
2124
import net.dv8tion.jda.api.entities.SelfUser;
25+
import net.dv8tion.jda.api.entities.channel.middleman.GuildChannel;
2226
import net.dv8tion.jda.api.interactions.commands.privileges.IntegrationPrivilege;
2327
import net.dv8tion.jda.internal.utils.Checks;
28+
import net.dv8tion.jda.internal.utils.PermissionUtil;
2429
import org.jetbrains.annotations.Unmodifiable;
2530

2631
import javax.annotation.Nonnull;
2732
import javax.annotation.Nullable;
28-
import java.util.Collections;
29-
import java.util.List;
30-
import java.util.Map;
33+
import java.util.*;
34+
import java.util.function.Predicate;
35+
import java.util.stream.Collectors;
3136

3237
/**
3338
* A PrivilegeConfig is the collection of moderator defined {@link IntegrationPrivilege privileges} set on a specific application and its commands
@@ -144,4 +149,199 @@ public Map<String, List<IntegrationPrivilege>> getAsMap()
144149
{
145150
return privileges;
146151
}
152+
153+
/**
154+
* Determines whether the {@link Command} can be run by the {@link Member} in the {@link GuildChannel}.
155+
*
156+
* <p>This will always return {@code true} for guild {@link Member#isOwner() Owners} and {@link Permission#ADMINISTRATOR Administrators}.
157+
*
158+
* <p>Implements <a href="https://discord.com/assets/6da3bd6082744a5eca59bb032c890092.svg" target="_blank">Discord's flow chart for command permissions logic</a>.
159+
*
160+
* @param channel
161+
* The channel in which the command would run in
162+
* @param member
163+
* The member which would run the command
164+
* @param command
165+
* The command which should be tested for
166+
*
167+
* @return {@code true} if the command can be run by the specified member, in the guild channel, {@code false} otherwise.
168+
*/
169+
public boolean canMemberRun(@Nonnull GuildChannel channel, @Nonnull Member member, @Nonnull Command command)
170+
{
171+
Checks.notNull(channel, "Channel");
172+
Checks.notNull(member, "Member");
173+
Checks.notNull(command, "Command");
174+
if (member.hasPermission(channel, Permission.ADMINISTRATOR))
175+
return true;
176+
return isCommandAllowedInChannel(channel, member, command);
177+
}
178+
179+
private boolean isCommandAllowedInChannel(GuildChannel channel, Member member, Command command)
180+
{
181+
final IntegrationPrivilege commandChannelPermissions = findPrivilege(getCommandPrivileges(command), matchingChannel(channel));
182+
if (commandChannelPermissions != null)
183+
{
184+
if (commandChannelPermissions.isDisabled())
185+
return false;
186+
return userOrRolePermission(channel, member, command);
187+
}
188+
else
189+
return isCommandAllowedInAllChannels(channel, member, command);
190+
}
191+
192+
private boolean isCommandAllowedInAllChannels(GuildChannel channel, Member member, Command command)
193+
{
194+
final IntegrationPrivilege commandAllChannelsPermissions = findPrivilege(getCommandPrivileges(command), IntegrationPrivilege::targetsAllChannels);
195+
196+
if (commandAllChannelsPermissions != null)
197+
{
198+
if (commandAllChannelsPermissions.isEnabled())
199+
return userOrRolePermission(channel, member, command);
200+
return false;
201+
}
202+
else
203+
return isAppAllowedInChannel(channel, member, command);
204+
}
205+
206+
private boolean userOrRolePermission(GuildChannel channel, Member member, Command command)
207+
{
208+
final List<IntegrationPrivilege> commandPrivileges = getCommandPrivileges(command);
209+
final IntegrationPrivilege commandUserPermissions = findPrivilege(commandPrivileges, matchingMember(member));
210+
if (commandUserPermissions != null)
211+
return commandUserPermissions.isEnabled();
212+
else
213+
{
214+
// If there's a role override, then at least one needs to be enabled
215+
// If there's no role override, check @everyone
216+
final List<IntegrationPrivilege> commandRolePermissionList = member.getRoles().stream()
217+
.map(r -> findPrivilege(commandPrivileges, matchingRole(r)))
218+
.filter(Objects::nonNull)
219+
.collect(Collectors.toList());
220+
if (commandRolePermissionList.isEmpty())
221+
return commandEveryonePermission(channel, member, command);
222+
223+
for (IntegrationPrivilege integrationPrivilege : commandRolePermissionList)
224+
{
225+
if (integrationPrivilege.isEnabled())
226+
return true;
227+
}
228+
return false;
229+
}
230+
}
231+
232+
private boolean commandEveryonePermission(GuildChannel channel, Member member, Command command)
233+
{
234+
final IntegrationPrivilege commandEveryonePermissions = findPrivilege(getCommandPrivileges(command), matchingRole(channel.getGuild().getPublicRole()));
235+
if (commandEveryonePermissions != null)
236+
return commandEveryonePermissions.isEnabled();
237+
return appLevelUserOrRolePermission(channel, member, command);
238+
}
239+
240+
private boolean appLevelUserOrRolePermission(GuildChannel channel, Member member, Command command)
241+
{
242+
final List<IntegrationPrivilege> applicationPrivileges = getApplicationPrivileges();
243+
final IntegrationPrivilege appUserPermissions = findPrivilege(applicationPrivileges, matchingMember(member));
244+
if (appUserPermissions != null)
245+
{
246+
if (appUserPermissions.isEnabled())
247+
return hasDefaultMemberPermissions(channel, member, command);
248+
}
249+
else
250+
{
251+
// If there's a role override, then at least one needs to be enabled
252+
// If there's no role override, check @everyone
253+
final List<IntegrationPrivilege> commandRolePermissionList = member.getRoles().stream()
254+
.map(r -> findPrivilege(applicationPrivileges, matchingRole(r)))
255+
.filter(Objects::nonNull)
256+
.collect(Collectors.toList());
257+
if (commandRolePermissionList.isEmpty())
258+
return isAppAllowingEveryone(channel, member, command);
259+
260+
for (IntegrationPrivilege integrationPrivilege : commandRolePermissionList)
261+
{
262+
if (integrationPrivilege.isEnabled())
263+
return hasDefaultMemberPermissions(channel, member, command);
264+
}
265+
}
266+
return false;
267+
}
268+
269+
private boolean isAppAllowingEveryone(GuildChannel channel, Member member, Command command)
270+
{
271+
final IntegrationPrivilege commandEveryonePermissions = findPrivilege(getCommandPrivileges(command), matchingRole(channel.getGuild().getPublicRole()));
272+
if (commandEveryonePermissions != null)
273+
{
274+
if (commandEveryonePermissions.isEnabled())
275+
return hasDefaultMemberPermissions(channel, member, command);
276+
return false;
277+
}
278+
return appLevelUserOrRolePermission(channel, member, command);
279+
}
280+
281+
private boolean hasDefaultMemberPermissions(GuildChannel channel, Member member, Command command)
282+
{
283+
final Long rawPermissions = command.getDefaultPermissions().getPermissionsRaw();
284+
// No permissions requires
285+
if (rawPermissions == null)
286+
return true;
287+
// Admins only, already checked in [[PrivilegeConfig#canMemberRun]]
288+
if (rawPermissions == 0)
289+
return false;
290+
return ((PermissionUtil.getEffectivePermission(channel, member) & rawPermissions) == rawPermissions);
291+
}
292+
293+
private boolean isAppAllowedInChannel(GuildChannel channel, Member member, Command command)
294+
{
295+
final IntegrationPrivilege appChannelPermissions = findPrivilege(getApplicationPrivileges(), matchingChannel(channel));
296+
if (appChannelPermissions != null)
297+
{
298+
if (appChannelPermissions.isEnabled())
299+
return userOrRolePermission(channel, member, command);
300+
return false;
301+
}
302+
else
303+
return isAppAllowedInAllChannels(channel, member, command);
304+
}
305+
306+
private boolean isAppAllowedInAllChannels(GuildChannel channel, Member member, Command command)
307+
{
308+
final IntegrationPrivilege appChannelPermissions = findPrivilege(getApplicationPrivileges(), IntegrationPrivilege::targetsAllChannels);
309+
if (appChannelPermissions != null)
310+
{
311+
if (appChannelPermissions.isEnabled())
312+
return userOrRolePermission(channel, member, command);
313+
return false;
314+
}
315+
else
316+
return userOrRolePermission(channel, member, command);
317+
}
318+
319+
@Nullable
320+
private static IntegrationPrivilege findPrivilege(@Nullable Collection<IntegrationPrivilege> privileges, @Nonnull Predicate<IntegrationPrivilege> predicate)
321+
{
322+
if (privileges == null)
323+
return null;
324+
return privileges.stream()
325+
.filter(predicate)
326+
.findAny()
327+
.orElse(null);
328+
}
329+
330+
@Nonnull
331+
private static Predicate<IntegrationPrivilege> matchingChannel(@Nonnull GuildChannel channel)
332+
{
333+
return p -> p.getType() == IntegrationPrivilege.Type.CHANNEL && p.getIdLong() == channel.getIdLong();
334+
}
335+
336+
@Nonnull
337+
private static Predicate<IntegrationPrivilege> matchingMember(@Nonnull Member member)
338+
{
339+
return p -> p.getType() == IntegrationPrivilege.Type.USER && p.getIdLong() == member.getIdLong();
340+
}
341+
342+
@Nonnull
343+
private static Predicate<IntegrationPrivilege> matchingRole(@Nonnull Role role)
344+
{
345+
return p -> p.getType() == IntegrationPrivilege.Type.ROLE && p.getIdLong() == role.getIdLong();
346+
}
147347
}

0 commit comments

Comments
 (0)