Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Move requestToSpeak to StageChannel #1978

Merged
merged 3 commits into from
Jan 29, 2022
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
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")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if they'll kill off the entire "privacy level" concept

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));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why are these GatewayTasks when the actual request is a RestAction?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it just so that requestToSpeak and cancelRequestToSpeak have a mirrored return type?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Symmetry and my hopes that this will in the future be a request you can do through the gateway

}

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