Skip to content

Commit

Permalink
implement get voice state feature
Browse files Browse the repository at this point in the history
  • Loading branch information
PascalNB committed Sep 12, 2024
1 parent 18c9508 commit 8aa241a
Show file tree
Hide file tree
Showing 4 changed files with 139 additions and 19 deletions.
85 changes: 85 additions & 0 deletions src/main/java/net/dv8tion/jda/api/entities/Guild.java
Original file line number Diff line number Diff line change
Expand Up @@ -2584,6 +2584,91 @@ default RestAction<RichCustomEmoji> retrieveEmoji(@Nonnull CustomEmoji emoji)
@Nonnull
List<GuildVoiceState> getVoiceStates();

/**
* Load the member's voice state for the specified user.
* <br>If the member is already loaded it will be retrieved from {@link #getMemberById(long)} and
* the voice state is immediately provided by {@link Member#getVoiceState()}.
* The cache consistency directly relies on the enabled {@link GatewayIntent GatewayIntents} as {@link GatewayIntent#GUILD_MEMBERS GatewayIntent.GUILD_MEMBERS}
* and {@link GatewayIntent#GUILD_VOICE_STATES GatewayIntent.GUILD_VOICE_STATES} are required to keep the cache updated with the latest information.
* You can use {@link CacheRestAction#useCache(boolean) useCache(false)} to always
* make a new request, which is the default behavior if the required intents are disabled.
*
* <p>Possible {@link net.dv8tion.jda.api.exceptions.ErrorResponseException ErrorResponseExceptions} include:
* <ul>
* <li>{@link net.dv8tion.jda.api.requests.ErrorResponse#UNKNOWN_VOICE_STATE}
* <br>The specified user does not exist, is not a member of this guild or is not connected to a voice channel</li>
* </ul>
*
* @param id
* The user id to load the voice state from
*
* @return {@link RestAction} - Type: {@link GuildVoiceState}
*/
@Nonnull
CacheRestAction<GuildVoiceState> retrieveMemberVoiceStateById(long id);

/**
* Load the member's voice state for the specified user.
* <br>If the member is already loaded it will be retrieved from {@link #getMemberById(long)} and
* the voice state is immediately provided by {@link Member#getVoiceState()}.
* The cache consistency directly relies on the enabled {@link GatewayIntent GatewayIntents} as {@link GatewayIntent#GUILD_MEMBERS GatewayIntent.GUILD_MEMBERS}
* and {@link GatewayIntent#GUILD_VOICE_STATES GatewayIntent.GUILD_VOICE_STATES} are required to keep the cache updated with the latest information.
* You can use {@link CacheRestAction#useCache(boolean) useCache(false)} to always
* make a new request, which is the default behavior if the required intents are disabled.
*
* <p>Possible {@link net.dv8tion.jda.api.exceptions.ErrorResponseException ErrorResponseExceptions} include:
* <ul>
* <li>{@link net.dv8tion.jda.api.requests.ErrorResponse#UNKNOWN_VOICE_STATE}
* <br>The specified user does not exist, is not a member of this guild or is not connected to a voice channel</li>
* </ul>
*
* @param id
* The user id to load the voice state from
*
* @throws IllegalArgumentException
* If the provided id is empty or null
* @throws NumberFormatException
* If the provided id is not a snowflake
*
* @return {@link RestAction} - Type: {@link GuildVoiceState}
*/
@Nonnull
default CacheRestAction<GuildVoiceState> retrieveMemberVoiceStateById(@Nonnull String id)
{
return retrieveMemberVoiceStateById(MiscUtil.parseSnowflake(id));
}

/**
* Load the member's voice state for the specified {@link UserSnowflake}.
* <br>If the member is already loaded it will be retrieved from {@link #getMemberById(long)} and
* the voice state is immediately provided by {@link Member#getVoiceState()}.
* The cache consistency directly relies on the enabled {@link GatewayIntent GatewayIntents} as {@link GatewayIntent#GUILD_MEMBERS GatewayIntent.GUILD_MEMBERS}
* and {@link GatewayIntent#GUILD_VOICE_STATES GatewayIntent.GUILD_VOICE_STATES} are required to keep the cache updated with the latest information.
* You can use {@link CacheRestAction#useCache(boolean) useCache(false)} to always
* make a new request, which is the default behavior if the required intents are disabled.
*
* <p>Possible {@link net.dv8tion.jda.api.exceptions.ErrorResponseException ErrorResponseExceptions} include:
* <ul>
* <li>{@link net.dv8tion.jda.api.requests.ErrorResponse#UNKNOWN_VOICE_STATE}
* <br>The specified user does not exist, is not a member of this guild or is not connected to a voice channel</li>
* </ul>
*
* @param user
* The {@link UserSnowflake} for the member's voice state to retrieve.
* This can be a member or user instance or {@link User#fromId(long)}.
*
* @throws IllegalArgumentException
* If provided with null
*
* @return {@link RestAction} - Type: {@link GuildVoiceState}
*/
@Nonnull
default CacheRestAction<GuildVoiceState> retrieveMemberVoiceState(UserSnowflake user)
{
Checks.notNull(user, "User");
return retrieveMemberVoiceStateById(user.getId());
}

/**
* Returns the verification-Level of this Guild. Verification level is one of the factors that determines if a Member
* can send messages in a Guild.
Expand Down
1 change: 1 addition & 0 deletions src/main/java/net/dv8tion/jda/api/requests/Route.java
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@ public static class Guilds
public static final Route GET_GUILD_EMOJIS = new Route(GET, "guilds/{guild_id}/emojis");
public static final Route GET_AUDIT_LOGS = new Route(GET, "guilds/{guild_id}/audit-logs");
public static final Route GET_VOICE_REGIONS = new Route(GET, "guilds/{guild_id}/regions");
public static final Route GET_VOICE_STATE = new Route(GET, "guilds/{guild_id}/voice-states/{user_id}");
public static final Route UPDATE_VOICE_STATE = new Route(PATCH, "guilds/{guild_id}/voice-states/{user_id}");

public static final Route GET_INTEGRATIONS = new Route(GET, "guilds/{guild_id}/integrations");
Expand Down
46 changes: 27 additions & 19 deletions src/main/java/net/dv8tion/jda/internal/entities/EntityBuilder.java
Original file line number Diff line number Diff line change
Expand Up @@ -616,13 +616,13 @@ public MemberImpl createMember(GuildImpl guild, DataObject memberJson, DataObjec
member.setFlags(memberJson.getInt("flags"));

long boostTimestamp = memberJson.isNull("premium_since")
? 0
: Helpers.toTimestamp(memberJson.getString("premium_since"));
? 0
: Helpers.toTimestamp(memberJson.getString("premium_since"));
member.setBoostDate(boostTimestamp);

long timeOutTimestamp = memberJson.isNull("communication_disabled_until")
? 0
: Helpers.toTimestamp(memberJson.getString("communication_disabled_until"));
? 0
: Helpers.toTimestamp(memberJson.getString("communication_disabled_until"));
member.setTimeOutEnd(timeOutTimestamp);

if (!memberJson.isNull("pending"))
Expand Down Expand Up @@ -658,39 +658,47 @@ public MemberImpl createMember(GuildImpl guild, DataObject memberJson, DataObjec

// Load voice state and presence if necessary
if (voiceStateJson != null && member.getVoiceState() != null)
createVoiceState(guild, voiceStateJson, user, member);
createGuildVoiceState(member, voiceStateJson);
if (presence != null)
createPresence(member, presence);
return member;
}

private void createVoiceState(GuildImpl guild, DataObject voiceStateJson, User user, MemberImpl member)
{
public GuildVoiceState createGuildVoiceState(MemberImpl member, DataObject voiceStateJson) {
GuildVoiceStateImpl voiceState = (GuildVoiceStateImpl) member.getVoiceState();
if (voiceState == null)
voiceState = new GuildVoiceStateImpl(member);
updateGuildVoiceState(voiceState, voiceStateJson, member);
return voiceState;
}

private void updateGuildVoiceState(GuildVoiceStateImpl oldVoiceState, DataObject newVoiceStateJson, MemberImpl member)
{
Guild guild = member.getGuild();

final long channelId = voiceStateJson.getLong("channel_id");
final long channelId = newVoiceStateJson.getLong("channel_id");
AudioChannel audioChannel = (AudioChannel) guild.getGuildChannelById(channelId);
if (audioChannel != null)
((AudioChannelMixin<?>) audioChannel).getConnectedMembersMap().put(member.getIdLong(), member);
else
LOG.error("Received a GuildVoiceState with a channel ID for a non-existent channel! ChannelId: {} GuildId: {} UserId: {}",
channelId, guild.getId(), user.getId());
channelId, guild.getId(), member.getId());

String requestToSpeak = voiceStateJson.getString("request_to_speak_timestamp", null);
String requestToSpeak = newVoiceStateJson.getString("request_to_speak_timestamp", null);
OffsetDateTime timestamp = null;
if (requestToSpeak != null)
timestamp = OffsetDateTime.parse(requestToSpeak);

// VoiceState is considered volatile so we don't expect anything to actually exist
voiceState.setSelfMuted(voiceStateJson.getBoolean("self_mute"))
.setSelfDeafened(voiceStateJson.getBoolean("self_deaf"))
.setGuildMuted(voiceStateJson.getBoolean("mute"))
.setGuildDeafened(voiceStateJson.getBoolean("deaf"))
.setSuppressed(voiceStateJson.getBoolean("suppress"))
.setSessionId(voiceStateJson.getString("session_id"))
.setStream(voiceStateJson.getBoolean("self_stream"))
.setRequestToSpeak(timestamp)
.setConnectedChannel(audioChannel);
oldVoiceState.setSelfMuted(newVoiceStateJson.getBoolean("self_mute"))
.setSelfDeafened(newVoiceStateJson.getBoolean("self_deaf"))
.setGuildMuted(newVoiceStateJson.getBoolean("mute"))
.setGuildDeafened(newVoiceStateJson.getBoolean("deaf"))
.setSuppressed(newVoiceStateJson.getBoolean("suppress"))
.setSessionId(newVoiceStateJson.getString("session_id"))
.setStream(newVoiceStateJson.getBoolean("self_stream"))
.setRequestToSpeak(timestamp)
.setConnectedChannel(audioChannel);
}

public void updateMember(GuildImpl guild, MemberImpl member, DataObject content, List<Role> newRoles)
Expand Down
26 changes: 26 additions & 0 deletions src/main/java/net/dv8tion/jda/internal/entities/GuildImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -1153,6 +1153,32 @@ public List<GuildVoiceState> getVoiceStates()
.collect(Helpers.toUnmodifiableList());
}

@Nonnull
@Override
public CacheRestAction<GuildVoiceState> retrieveMemberVoiceStateById(long id)
{
JDAImpl jda = getJDA();
return new DeferredRestAction<>(jda, GuildVoiceState.class,
() ->
{
MemberImpl member = (MemberImpl) getMemberById(id);
return member == null ? null : member.getVoiceState();
},
() ->
{
Route.CompiledRoute route = Route.Guilds.GET_VOICE_STATE.compile(getId(), Long.toUnsignedString(id));
return new RestActionImpl<>(jda, route, (response, request) ->
{
EntityBuilder entityBuilder = jda.getEntityBuilder();
DataObject voiceStateData = response.getObject();
//Creates voice state if VOICE_STATE cache flag is set
MemberImpl member = entityBuilder.createMember(this, voiceStateData.getObject("member"), voiceStateData, null);
entityBuilder.updateMemberCache(member);
return entityBuilder.createGuildVoiceState(member, voiceStateData);
});
}).useCache(jda.isIntent(GatewayIntent.GUILD_MEMBERS) && jda.isIntent(GatewayIntent.GUILD_VOICE_STATES));
}

@Nonnull
@Override
public VerificationLevel getVerificationLevel()
Expand Down

0 comments on commit 8aa241a

Please sign in to comment.