diff --git a/src/main/java/net/dv8tion/jda/core/managers/ChannelManager.java b/src/main/java/net/dv8tion/jda/core/managers/ChannelManager.java index 97d92346101..e51faf538dd 100644 --- a/src/main/java/net/dv8tion/jda/core/managers/ChannelManager.java +++ b/src/main/java/net/dv8tion/jda/core/managers/ChannelManager.java @@ -16,19 +16,24 @@ package net.dv8tion.jda.core.managers; +import gnu.trove.map.hash.TLongObjectHashMap; +import gnu.trove.set.TLongSet; +import gnu.trove.set.hash.TLongHashSet; import net.dv8tion.jda.core.Permission; -import net.dv8tion.jda.core.entities.Category; -import net.dv8tion.jda.core.entities.Channel; -import net.dv8tion.jda.core.entities.ChannelType; -import net.dv8tion.jda.core.entities.Guild; +import net.dv8tion.jda.core.entities.*; +import net.dv8tion.jda.core.entities.impl.AbstractChannelImpl; import net.dv8tion.jda.core.exceptions.InsufficientPermissionException; import net.dv8tion.jda.core.managers.impl.ManagerBase; import net.dv8tion.jda.core.requests.Route; +import net.dv8tion.jda.core.requests.restaction.PermOverrideData; import net.dv8tion.jda.core.utils.Checks; import okhttp3.RequestBody; import org.json.JSONObject; import javax.annotation.CheckReturnValue; +import java.util.Collection; +import java.util.HashSet; +import java.util.Set; /** * Manager providing functionality to update one or more fields for a {@link net.dv8tion.jda.core.entities.Channel Guild Channel}. @@ -64,6 +69,8 @@ public class ChannelManager extends ManagerBase public static final long USERLIMIT = 0x20; /** Used to reset the bitrate field */ public static final long BITRATE = 0x40; + /** Used to reset the permission field */ + public static final long PERMISSION = 0x80; protected final Channel channel; @@ -75,6 +82,10 @@ public class ChannelManager extends ManagerBase protected int userlimit; protected int bitrate; + protected final Object lock = new Object(); + protected final TLongObjectHashMap overridesAdd; + protected final TLongSet overridesRem; + /** * Creates a new ChannelManager instance * @@ -89,8 +100,15 @@ public ChannelManager(Channel channel) this.channel = channel; if (isPermissionChecksEnabled()) checkPermissions(); + this.overridesAdd = new TLongObjectHashMap<>(); + this.overridesRem = new TLongHashSet(); } + /** + * The {@link net.dv8tion.jda.core.entities.ChannelType ChannelType} + * + * @return The ChannelType + */ public ChannelType getType() { return channel.getType(); @@ -133,6 +151,7 @@ public Guild getGuild() *
  • {@link #NSFW}
  • *
  • {@link #USERLIMIT}
  • *
  • {@link #BITRATE}
  • + *
  • {@link #PERMISSION}
  • * * * @param fields @@ -151,6 +170,14 @@ public ChannelManager reset(long fields) this.parent = null; if ((fields & TOPIC) == TOPIC) this.topic = null; + if ((fields & PERMISSION) == PERMISSION) + { + withLock(lock, (lock) -> + { + this.overridesRem.clear(); + this.overridesAdd.clear(); + }); + } return this; } @@ -167,6 +194,7 @@ public ChannelManager reset(long fields) *
  • {@link #NSFW}
  • *
  • {@link #USERLIMIT}
  • *
  • {@link #BITRATE}
  • + *
  • {@link #PERMISSION}
  • * * * @param fields @@ -195,6 +223,133 @@ public ChannelManager reset() this.name = null; this.parent = null; this.topic = null; + withLock(lock, (lock) -> + { + this.overridesRem.clear(); + this.overridesAdd.clear(); + }); + return this; + } + + /** + * Clears the overrides added via {@link #putPermissionOverride(IPermissionHolder, Collection, Collection)}. + * + * @return ChannelManager for chaining convenience + */ + public ChannelManager clearOverridesAdded() + { + withLock(lock, (lock) -> + { + this.overridesAdd.clear(); + if (this.overridesRem.isEmpty()) + set &= ~PERMISSION; + }); + return this; + } + + /** + * Clears the overrides removed via {@link #removePermissionOverride(IPermissionHolder)}. + * + * @return ChannelManager for chaining convenience + */ + public ChannelManager clearOverridesRemoved() + { + withLock(lock, (lock) -> + { + this.overridesRem.clear(); + if (this.overridesAdd.isEmpty()) + set &= ~PERMISSION; + }); + return this; + } + + /** + * Adds an override for the specified {@link net.dv8tion.jda.core.entities.IPermissionHolder IPermissionHolder} + * with the provided raw bitmasks as allowed and denied permissions. If the permission holder already + * had an override on this channel it will be updated instead. + * + * @param permHolder + * The permission holder + * @param allow + * The bitmask to grant + * @param deny + * The bitmask to deny + * + * @throws java.lang.IllegalArgumentException + * If the provided permission holder is {@code null} + * + * @return ChannelManager for chaining convenience + * + * @see #putPermissionOverride(IPermissionHolder, Collection, Collection) + * @see net.dv8tion.jda.core.Permission#getRaw(Permission...) Permission.getRaw(Permission...) + */ + public ChannelManager putPermissionOverride(IPermissionHolder permHolder, long allow, long deny) + { + Checks.notNull(permHolder, "PermissionHolder"); + Checks.check(permHolder.getGuild().equals(getGuild()), "PermissionHolder is not from the same Guild!"); + if (isPermissionChecksEnabled() && !getGuild().getSelfMember().hasPermission(channel, Permission.MANAGE_PERMISSIONS)) + throw new InsufficientPermissionException(Permission.MANAGE_PERMISSIONS); + final long id = getId(permHolder); + final int type = permHolder instanceof Role ? PermOverrideData.ROLE_TYPE : PermOverrideData.MEMBER_TYPE; + withLock(lock, (lock) -> + { + this.overridesRem.remove(id); + this.overridesAdd.put(id, new PermOverrideData(type, id, allow, deny)); + set |= PERMISSION; + }); + return this; + } + + /** + * Adds an override for the specified {@link net.dv8tion.jda.core.entities.IPermissionHolder IPermissionHolder} + * with the provided permission sets as allowed and denied permissions. If the permission holder already + * had an override on this channel it will be updated instead. + *
    Example: {@code putPermissionOverride(guild.getSelfMember(), EnumSet.of(Permission.MESSAGE_WRITE, Permission.MESSAGE_READ), null)} + * + * @param permHolder + * The permission holder + * @param allow + * The permissions to grant, or null + * @param deny + * The permissions to deny, or null + * + * @throws java.lang.IllegalArgumentException + * If the provided permission holder is {@code null} + * + * @return ChannelManager for chaining convenience + * + * @see #putPermissionOverride(IPermissionHolder, long, long) + * @see java.util.EnumSet EnumSet + */ + public ChannelManager putPermissionOverride(IPermissionHolder permHolder, Collection allow, Collection deny) + { + long allowRaw = allow == null ? 0 : Permission.getRaw(allow); + long denyRaw = deny == null ? 0 : Permission.getRaw(deny); + return putPermissionOverride(permHolder, allowRaw, denyRaw); + } + + /** + * Removes the {@link net.dv8tion.jda.core.entities.PermissionOverride PermissionOverride} for the specified + * {@link net.dv8tion.jda.core.entities.IPermissionHolder IPermissionHolder}. If no override existed for this member + * this does nothing. + * + * @param permHolder + * The permission holder + * + * @throws java.lang.IllegalArgumentException + * If the provided permission holder is {@code null} + * + * @return ChannelManager for chaining convenience + */ + public ChannelManager removePermissionOverride(IPermissionHolder permHolder) + { + final long id = getId(permHolder); + withLock(lock, (lock) -> + { + this.overridesRem.add(id); + this.overridesAdd.remove(id); + set |= PERMISSION; + }); return this; } @@ -409,6 +564,11 @@ protected RequestBody finalizeData() frame.put("bitrate", bitrate); if (shouldUpdate(PARENT)) frame.put("parent_id", opt(parent)); + withLock(lock, (lock) -> + { + if (shouldUpdate(PERMISSION)) + frame.put("permission_overwrites", getOverrides()); + }); reset(); return getRequestBody(frame); @@ -417,8 +577,34 @@ protected RequestBody finalizeData() @Override protected boolean checkPermissions() { - if (!getGuild().getSelfMember().hasPermission(channel, Permission.MANAGE_CHANNEL)) + final Member selfMember = getGuild().getSelfMember(); + if (!selfMember.hasPermission(channel, Permission.MANAGE_CHANNEL)) throw new InsufficientPermissionException(Permission.MANAGE_CHANNEL); return super.checkPermissions(); } + + protected Set getOverrides() + { + //note: overridesAdd and overridesRem are mutually disjoint + TLongObjectHashMap data = new TLongObjectHashMap<>(this.overridesAdd); + + AbstractChannelImpl impl = (AbstractChannelImpl) channel; + impl.getOverrideMap().forEachEntry((id, override) -> + { + //removed by not adding them here, this data set overrides the existing one + //we can use remove because it will be reset afterwards either way + if (!overridesRem.remove(id) && !data.containsKey(id)) + data.put(id, new PermOverrideData(override)); + return true; + }); + return new HashSet<>(data.valueCollection()); + } + + protected long getId(IPermissionHolder holder) + { + if (holder instanceof Role) + return ((Role) holder).getIdLong(); + else + return ((Member) holder).getUser().getIdLong(); + } } diff --git a/src/main/java/net/dv8tion/jda/core/requests/restaction/PermOverrideData.java b/src/main/java/net/dv8tion/jda/core/requests/restaction/PermOverrideData.java index cd988b923e4..e52f4087002 100644 --- a/src/main/java/net/dv8tion/jda/core/requests/restaction/PermOverrideData.java +++ b/src/main/java/net/dv8tion/jda/core/requests/restaction/PermOverrideData.java @@ -16,19 +16,20 @@ package net.dv8tion.jda.core.requests.restaction; +import net.dv8tion.jda.core.entities.PermissionOverride; import org.json.JSONObject; import org.json.JSONString; -class PermOverrideData implements JSONString +public class PermOverrideData implements JSONString { public static final int ROLE_TYPE = 0; public static final int MEMBER_TYPE = 1; - protected final int type; - protected final long id; - protected final long allow; - protected final long deny; + public final int type; + public final long id; + public final long allow; + public final long deny; - protected PermOverrideData(int type, long id, long allow, long deny) + public PermOverrideData(int type, long id, long allow, long deny) { this.type = type; this.id = id; @@ -36,6 +37,22 @@ protected PermOverrideData(int type, long id, long allow, long deny) this.deny = deny; } + public PermOverrideData(PermissionOverride override) + { + if (override.isMemberOverride()) + { + this.id = override.getMember().getUser().getIdLong(); + this.type = MEMBER_TYPE; + } + else + { + this.id = override.getRole().getIdLong(); + this.type = ROLE_TYPE; + } + this.allow = override.getAllowedRaw(); + this.deny = override.getDeniedRaw(); + } + @Override public String toJSONString() {