Skip to content

Commit

Permalink
Move requestToSpeak to StageChannel (#1978)
Browse files Browse the repository at this point in the history
  • Loading branch information
MinnDevelopment committed Jan 29, 2022
1 parent 2958e68 commit 0c0d2ac
Show file tree
Hide file tree
Showing 10 changed files with 116 additions and 85 deletions.
4 changes: 2 additions & 2 deletions src/main/java/net/dv8tion/jda/api/entities/Guild.java
Original file line number Diff line number Diff line change
Expand Up @@ -2841,7 +2841,7 @@ default RestAction<Ban> retrieveBan(@Nonnull User bannedUser)
AudioManager getAudioManager();

/**
* Once the currently logged in account is connected to a {@link StageChannel} with an active {@link StageInstance},
* Once the currently logged in account is connected to a {@link StageChannel},
* this will trigger a {@link GuildVoiceState#getRequestToSpeakTimestamp() Request-to-Speak} (aka raise your hand).
*
* <p>This will set an internal flag to automatically request to speak once the bot joins a stage channel.
Expand All @@ -2868,7 +2868,7 @@ default RestAction<Ban> retrieveBan(@Nonnull User bannedUser)
* Cancels the {@link #requestToSpeak() Request-to-Speak}.
* <br>This can also be used to move back to the audience if you are currently a speaker.
*
* <p>If there is no request to speak or the member is not currently connected to an active {@link StageInstance}, this does nothing.
* <p>If there is no request to speak or the member is not currently connected to a {@link StageChannel}, this does nothing.
*
* @return {@link Task} representing the request to speak cancellation.
* Calling {@link Task#get()} can result in deadlocks and should be avoided at all times.
Expand Down
33 changes: 33 additions & 0 deletions src/main/java/net/dv8tion/jda/api/entities/StageChannel.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import net.dv8tion.jda.api.Permission;
import net.dv8tion.jda.api.exceptions.InsufficientPermissionException;
import net.dv8tion.jda.api.managers.channel.concrete.StageChannelManager;
import net.dv8tion.jda.api.requests.RestAction;
import net.dv8tion.jda.api.requests.restaction.ChannelAction;
import net.dv8tion.jda.api.requests.restaction.StageInstanceAction;
import net.dv8tion.jda.internal.requests.restaction.StageInstanceActionImpl;
Expand Down Expand Up @@ -123,4 +124,36 @@ default ChannelAction<StageChannel> createCopy()
@Nonnull
@Override
StageChannelManager getManager();

/**
* Sends a {@link GuildVoiceState#getRequestToSpeakTimestamp() request-to-speak} indicator to the stage instance moderators.
* <p>If the self member has {@link Permission#VOICE_MUTE_OTHERS} this will immediately promote them to speaker.
*
* @throws IllegalStateException
* If the self member is not currently connected to the channel
*
* @return {@link RestAction}
*
* @see #cancelRequestToSpeak()
*/
@Nonnull
@CheckReturnValue
RestAction<Void> requestToSpeak();

/**
* Cancels the {@link #requestToSpeak() Request-to-Speak}.
* <br>This can also be used to move back to the audience if you are currently a speaker.
*
* <p>If there is no request to speak or the member is not currently connected to an active {@link StageInstance}, this does nothing.
*
* @throws IllegalStateException
* If the self member is not currently connected to the channel
*
* @return {@link RestAction}
*
* @see #requestToSpeak()
*/
@Nonnull
@CheckReturnValue
RestAction<Void> cancelRequestToSpeak();
}
49 changes: 15 additions & 34 deletions src/main/java/net/dv8tion/jda/api/entities/StageInstance.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@

package net.dv8tion.jda.api.entities;

import net.dv8tion.jda.api.Permission;
import net.dv8tion.jda.annotations.DeprecatedSince;
import net.dv8tion.jda.annotations.ForRemoval;
import net.dv8tion.jda.api.managers.StageInstanceManager;
import net.dv8tion.jda.api.requests.RestAction;

Expand Down Expand Up @@ -69,7 +70,12 @@ public interface StageInstance extends ISnowflake
* Whether this stage instance can be found in stage discovery.
*
* @return True if this is a public stage that can be found in stage discovery
*
* @deprecated Stage discovery has been removed from the platform
*/
@Deprecated
@ForRemoval
@DeprecatedSince("5.0.0")
boolean isDiscoverable();

/**
Expand Down Expand Up @@ -134,38 +140,6 @@ default List<Member> getAudience()
@CheckReturnValue
RestAction<Void> delete();

/**
* Sends a {@link GuildVoiceState#getRequestToSpeakTimestamp() request-to-speak} indicator to the stage instance moderators.
* <p>If the self member has {@link Permission#VOICE_MUTE_OTHERS} this will immediately promote them to speaker.
*
* @throws IllegalStateException
* If the self member is not currently connected to the channel of this stage instance
*
* @return {@link RestAction}
*
* @see #cancelRequestToSpeak()
*/
@Nonnull
@CheckReturnValue
RestAction<Void> requestToSpeak();

/**
* Cancels the {@link #requestToSpeak() Request-to-Speak}.
* <br>This can also be used to move back to the audience if you are currently a speaker.
*
* <p>If there is no request to speak or the member is not currently connected to an active {@link StageInstance}, this does nothing.
*
* @throws IllegalStateException
* If the self member is not currently connected to the channel of this stage instance
*
* @return {@link RestAction}
*
* @see #requestToSpeak()
*/
@Nonnull
@CheckReturnValue
RestAction<Void> cancelRequestToSpeak();

/**
* The {@link StageInstanceManager} used to update this stage instance.
* <p>This can be used to update multiple fields such as topic and privacy level in one request
Expand All @@ -190,7 +164,14 @@ enum PrivacyLevel
{
/** Placeholder for future privacy levels, indicates that this version of JDA does not support this privacy level yet */
UNKNOWN(-1),
/** This stage instance can be accessed by lurkers, meaning users that are not active members of the guild */
/**
* This stage instance can be accessed by lurkers, meaning users that are not active members of the guild
*
* @deprecated Public stage instances are no longer supported by discord
*/
@Deprecated
@ForRemoval
@DeprecatedSince("5.0.0")
PUBLIC(1),
/** This stage instance can only be accessed by guild members */
GUILD_ONLY(2);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -116,11 +116,13 @@ public interface StageInstanceManager extends Manager<StageInstanceManager>
* The privacy level
*
* @throws IllegalArgumentException
* If the privacy level is null or {@link net.dv8tion.jda.api.entities.StageInstance.PrivacyLevel#UNKNOWN UNKNOWN}
* If the privacy level is null, {@link net.dv8tion.jda.api.entities.StageInstance.PrivacyLevel#UNKNOWN UNKNOWN},
* or {@link net.dv8tion.jda.api.entities.StageInstance.PrivacyLevel#PUBLIC PUBLIC}.
*
* @return StageInstanceManager for chaining convenience
*/
@Nonnull
@CheckReturnValue
@SuppressWarnings("deprecation")
StageInstanceManager setPrivacyLevel(@Nonnull StageInstance.PrivacyLevel level);
}
Original file line number Diff line number Diff line change
Expand Up @@ -68,11 +68,13 @@ public interface StageInstanceAction extends RestAction<StageInstance>
* The {@link net.dv8tion.jda.api.entities.StageInstance.PrivacyLevel}
*
* @throws IllegalArgumentException
* If the provided level is null or {@link net.dv8tion.jda.api.entities.StageInstance.PrivacyLevel#UNKNOWN UNKNOWN}
* If the privacy level is null, {@link net.dv8tion.jda.api.entities.StageInstance.PrivacyLevel#UNKNOWN UNKNOWN},
* or {@link net.dv8tion.jda.api.entities.StageInstance.PrivacyLevel#PUBLIC PUBLIC}.
*
* @return The StageInstanceAction for chaining
*/
@Nonnull
@CheckReturnValue
@SuppressWarnings("deprecation")
StageInstanceAction setPrivacyLevel(@Nonnull StageInstance.PrivacyLevel level);
}
28 changes: 17 additions & 11 deletions src/main/java/net/dv8tion/jda/internal/entities/GuildImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@
import net.dv8tion.jda.api.requests.restaction.order.ChannelOrderAction;
import net.dv8tion.jda.api.requests.restaction.order.RoleOrderAction;
import net.dv8tion.jda.api.requests.restaction.pagination.AuditLogPaginationAction;
import net.dv8tion.jda.api.utils.TimeUtil;
import net.dv8tion.jda.api.utils.cache.*;
import net.dv8tion.jda.api.utils.concurrent.Task;
import net.dv8tion.jda.api.utils.data.DataArray;
Expand Down Expand Up @@ -936,11 +935,13 @@ public synchronized Task<Void> cancelRequestToSpeak()
}

AudioChannel channel = getSelfMember().getVoiceState().getChannel();
StageInstance instance = channel instanceof StageChannel ? ((StageChannel) channel).getStageInstance() : null;
if (instance == null)
return new GatewayTask<>(CompletableFuture.completedFuture(null), () -> {});
CompletableFuture<Void> future = instance.cancelRequestToSpeak().submit();
return new GatewayTask<>(future, () -> future.cancel(false));
if (channel instanceof StageChannel)
{
CompletableFuture<Void> future = ((StageChannel) channel).cancelRequestToSpeak().submit();
return new GatewayTask<>(future, () -> future.cancel(false));
}

return new GatewayTask<>(CompletableFuture.completedFuture(null), () -> {});
}

@Nonnull
Expand Down Expand Up @@ -1785,14 +1786,19 @@ public synchronized void updateRequestToSpeak()
if (!(connectedChannel instanceof StageChannel))
return;
StageChannel stage = (StageChannel) connectedChannel;
StageInstance instance = stage.getStageInstance();
if (instance == null)
return;

CompletableFuture<Void> future = pendingRequestToSpeak;
pendingRequestToSpeak = null;

instance.requestToSpeak().queue((v) -> future.complete(null), future::completeExceptionally);
try
{
stage.requestToSpeak().queue((v) -> future.complete(null), future::completeExceptionally);
}
catch (Throwable ex)
{
future.completeExceptionally(ex);
if (ex instanceof Error)
throw ex;
}
}

// ---- Setters -----
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,19 +21,24 @@
import net.dv8tion.jda.api.entities.*;
import net.dv8tion.jda.api.exceptions.InsufficientPermissionException;
import net.dv8tion.jda.api.managers.channel.concrete.StageChannelManager;
import net.dv8tion.jda.api.requests.RestAction;
import net.dv8tion.jda.api.requests.restaction.ChannelAction;
import net.dv8tion.jda.api.requests.restaction.StageInstanceAction;
import net.dv8tion.jda.api.utils.MiscUtil;
import net.dv8tion.jda.api.utils.data.DataObject;
import net.dv8tion.jda.internal.entities.mixin.channel.attribute.ICategorizableChannelMixin;
import net.dv8tion.jda.internal.entities.mixin.channel.attribute.IInviteContainerMixin;
import net.dv8tion.jda.internal.entities.mixin.channel.attribute.IPositionableChannelMixin;
import net.dv8tion.jda.internal.entities.mixin.channel.middleman.AudioChannelMixin;
import net.dv8tion.jda.internal.managers.channel.concrete.StageChannelManagerImpl;
import net.dv8tion.jda.internal.requests.RestActionImpl;
import net.dv8tion.jda.internal.requests.Route;
import net.dv8tion.jda.internal.requests.restaction.StageInstanceActionImpl;
import net.dv8tion.jda.internal.utils.Checks;
import org.jetbrains.annotations.Nullable;

import javax.annotation.Nonnull;
import java.time.OffsetDateTime;
import java.util.ArrayList;
import java.util.Collections;
import java.util.EnumSet;
Expand Down Expand Up @@ -151,6 +156,40 @@ public StageChannelManager getManager()
return new StageChannelManagerImpl(this);
}

@Nonnull
@Override
public RestAction<Void> requestToSpeak()
{
Guild guild = getGuild();
Route.CompiledRoute route = Route.Guilds.UPDATE_VOICE_STATE.compile(guild.getId(), "@me");
DataObject body = DataObject.empty().put("channel_id", getId());
// Stage moderators can bypass the request queue by just unsuppressing
if (guild.getSelfMember().hasPermission(this, Permission.VOICE_MUTE_OTHERS))
body.putNull("request_to_speak_timestamp").put("suppress", false);
else
body.put("request_to_speak_timestamp", OffsetDateTime.now().toString());

if (!this.equals(guild.getSelfMember().getVoiceState().getChannel()))
throw new IllegalStateException("Cannot request to speak without being connected to the stage channel!");
return new RestActionImpl<>(getJDA(), route, body);
}

@Nonnull
@Override
public RestAction<Void> cancelRequestToSpeak()
{
Guild guild = getGuild();
Route.CompiledRoute route = Route.Guilds.UPDATE_VOICE_STATE.compile(guild.getId(), "@me");
DataObject body = DataObject.empty()
.putNull("request_to_speak_timestamp")
.put("suppress", true)
.put("channel_id", getId());

if (!this.equals(guild.getSelfMember().getVoiceState().getChannel()))
throw new IllegalStateException("Cannot cancel request to speak without being connected to the stage channel!");
return new RestActionImpl<>(getJDA(), route, body);
}

@Override
public TLongObjectMap<PermissionOverride> getPermissionOverrideMap()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,11 @@
import net.dv8tion.jda.api.exceptions.InsufficientPermissionException;
import net.dv8tion.jda.api.managers.StageInstanceManager;
import net.dv8tion.jda.api.requests.RestAction;
import net.dv8tion.jda.api.utils.data.DataObject;
import net.dv8tion.jda.internal.managers.StageInstanceManagerImpl;
import net.dv8tion.jda.internal.requests.RestActionImpl;
import net.dv8tion.jda.internal.requests.Route;

import javax.annotation.Nonnull;
import java.time.OffsetDateTime;
import java.util.EnumSet;

public class StageInstanceImpl implements StageInstance
Expand Down Expand Up @@ -100,40 +98,6 @@ public RestAction<Void> delete()
return new RestActionImpl<>(channel.getJDA(), route);
}

@Nonnull
@Override
public RestAction<Void> requestToSpeak()
{
Guild guild = getGuild();
Route.CompiledRoute route = Route.Guilds.UPDATE_VOICE_STATE.compile(guild.getId(), "@me");
DataObject body = DataObject.empty().put("channel_id", channel.getId());
// Stage moderators can bypass the request queue by just unsuppressing
if (guild.getSelfMember().hasPermission(getChannel(), Permission.VOICE_MUTE_OTHERS))
body.putNull("request_to_speak_timestamp").put("suppress", false);
else
body.put("request_to_speak_timestamp", OffsetDateTime.now().toString());

if (!channel.equals(guild.getSelfMember().getVoiceState().getChannel()))
throw new IllegalStateException("Cannot request to speak without being connected to the stage channel!");
return new RestActionImpl<>(channel.getJDA(), route, body);
}

@Nonnull
@Override
public RestAction<Void> cancelRequestToSpeak()
{
Guild guild = getGuild();
Route.CompiledRoute route = Route.Guilds.UPDATE_VOICE_STATE.compile(guild.getId(), "@me");
DataObject body = DataObject.empty()
.putNull("request_to_speak_timestamp")
.put("suppress", true)
.put("channel_id", channel.getId());

if (!channel.equals(guild.getSelfMember().getVoiceState().getChannel()))
throw new IllegalStateException("Cannot cancel request to speak without being connected to the stage channel!");
return new RestActionImpl<>(channel.getJDA(), route, body);
}

@Nonnull
@Override
public StageInstanceManager getManager()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,10 +64,12 @@ public StageInstanceManager setTopic(@Nullable String topic)

@Nonnull
@Override
@SuppressWarnings("deprecation")
public StageInstanceManager setPrivacyLevel(@Nonnull StageInstance.PrivacyLevel level)
{
Checks.notNull(level, "PrivacyLevel");
Checks.check(level != StageInstance.PrivacyLevel.UNKNOWN, "PrivacyLevel must not be UNKNOWN!");
Checks.check(level != StageInstance.PrivacyLevel.PUBLIC, "Cannot create PUBLIC stage instances anymore.");
this.privacyLevel = level;
set |= PRIVACY_LEVEL;
return this;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,10 +77,12 @@ public StageInstanceAction setTopic(@Nonnull String topic)

@Nonnull
@Override
@SuppressWarnings("deprecation")
public StageInstanceAction setPrivacyLevel(@Nonnull StageInstance.PrivacyLevel level)
{
Checks.notNull(level, "PrivacyLevel");
Checks.check(level != StageInstance.PrivacyLevel.UNKNOWN, "The PrivacyLevel must not be UNKNOWN!");
Checks.check(level != StageInstance.PrivacyLevel.PUBLIC, "Cannot create PUBLIC stage instances anymore.");
this.level = level;
return this;
}
Expand Down

0 comments on commit 0c0d2ac

Please sign in to comment.