Skip to content

Commit

Permalink
Added support for bulk updating permission overrides
Browse files Browse the repository at this point in the history
  • Loading branch information
MinnDevelopment committed Mar 15, 2018
1 parent 9e593c5 commit 5dad08d
Show file tree
Hide file tree
Showing 2 changed files with 214 additions and 11 deletions.
196 changes: 191 additions & 5 deletions src/main/java/net/dv8tion/jda/core/managers/ChannelManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -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}.
Expand Down Expand Up @@ -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;

Expand All @@ -75,6 +82,10 @@ public class ChannelManager extends ManagerBase
protected int userlimit;
protected int bitrate;

protected final Object lock = new Object();
protected final TLongObjectHashMap<PermOverrideData> overridesAdd;
protected final TLongSet overridesRem;

/**
* Creates a new ChannelManager instance
*
Expand All @@ -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();
Expand Down Expand Up @@ -133,6 +151,7 @@ public Guild getGuild()
* <li>{@link #NSFW}</li>
* <li>{@link #USERLIMIT}</li>
* <li>{@link #BITRATE}</li>
* <li>{@link #PERMISSION}</li>
* </ul>
*
* @param fields
Expand All @@ -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;
}

Expand All @@ -167,6 +194,7 @@ public ChannelManager reset(long fields)
* <li>{@link #NSFW}</li>
* <li>{@link #USERLIMIT}</li>
* <li>{@link #BITRATE}</li>
* <li>{@link #PERMISSION}</li>
* </ul>
*
* @param fields
Expand Down Expand Up @@ -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.
* <br>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<Permission> allow, Collection<Permission> 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;
}

Expand Down Expand Up @@ -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);
Expand All @@ -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<PermOverrideData> getOverrides()
{
//note: overridesAdd and overridesRem are mutually disjoint
TLongObjectHashMap<PermOverrideData> 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();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,26 +16,43 @@

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;
this.allow = allow;
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()
{
Expand Down

0 comments on commit 5dad08d

Please sign in to comment.