diff --git a/src/main/java/net/dv8tion/jda/api/entities/Guild.java b/src/main/java/net/dv8tion/jda/api/entities/Guild.java index 8c07ec5325..fc8aef66eb 100644 --- a/src/main/java/net/dv8tion/jda/api/entities/Guild.java +++ b/src/main/java/net/dv8tion/jda/api/entities/Guild.java @@ -2584,6 +2584,76 @@ default RestAction retrieveEmoji(@Nonnull CustomEmoji emoji) @Nonnull List getVoiceStates(); + /** + * Load the member's voice state for the specified user. + * + *

Possible {@link net.dv8tion.jda.api.exceptions.ErrorResponseException ErrorResponseExceptions} include: + *

+ * + * @param id + * The user id to load the voice state from + * + * @return {@link RestAction} - Type: {@link GuildVoiceState} + */ + @Nonnull + @CheckReturnValue + RestAction retrieveMemberVoiceStateById(long id); + + /** + * Load the member's voice state for the specified user. + * + *

Possible {@link net.dv8tion.jda.api.exceptions.ErrorResponseException ErrorResponseExceptions} include: + *

    + *
  • {@link net.dv8tion.jda.api.requests.ErrorResponse#UNKNOWN_VOICE_STATE} + *
    The specified user does not exist, is not a member of this guild or is not connected to a voice channel
  • + *
+ * + * @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 + @CheckReturnValue + default RestAction retrieveMemberVoiceStateById(@Nonnull String id) + { + return retrieveMemberVoiceStateById(MiscUtil.parseSnowflake(id)); + } + + /** + * Load the member's voice state for the specified {@link UserSnowflake}. + * + *

Possible {@link net.dv8tion.jda.api.exceptions.ErrorResponseException ErrorResponseExceptions} include: + *

    + *
  • {@link net.dv8tion.jda.api.requests.ErrorResponse#UNKNOWN_VOICE_STATE} + *
    The specified user does not exist, is not a member of this guild or is not connected to a voice channel
  • + *
+ * + * @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 + @CheckReturnValue + default RestAction retrieveMemberVoiceState(@Nonnull UserSnowflake user) + { + Checks.notNull(user, "User"); + return retrieveMemberVoiceStateById(user.getIdLong()); + } + /** * 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. diff --git a/src/main/java/net/dv8tion/jda/api/requests/Route.java b/src/main/java/net/dv8tion/jda/api/requests/Route.java index 0e165f9f85..ff9e95e800 100644 --- a/src/main/java/net/dv8tion/jda/api/requests/Route.java +++ b/src/main/java/net/dv8tion/jda/api/requests/Route.java @@ -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"); diff --git a/src/main/java/net/dv8tion/jda/internal/entities/EntityBuilder.java b/src/main/java/net/dv8tion/jda/internal/entities/EntityBuilder.java index 687b1df795..d1ebe1e371 100644 --- a/src/main/java/net/dv8tion/jda/internal/entities/EntityBuilder.java +++ b/src/main/java/net/dv8tion/jda/internal/entities/EntityBuilder.java @@ -658,39 +658,51 @@ 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); + { + if (member.getVoiceState() != 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 newRoles) diff --git a/src/main/java/net/dv8tion/jda/internal/entities/GuildImpl.java b/src/main/java/net/dv8tion/jda/internal/entities/GuildImpl.java index 2ab78beceb..f3781b885b 100644 --- a/src/main/java/net/dv8tion/jda/internal/entities/GuildImpl.java +++ b/src/main/java/net/dv8tion/jda/internal/entities/GuildImpl.java @@ -1153,6 +1153,23 @@ public List getVoiceStates() .collect(Helpers.toUnmodifiableList()); } + @Nonnull + @Override + @CheckReturnValue + public RestAction retrieveMemberVoiceStateById(long id) + { + JDAImpl jda = getJDA(); + 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(); + MemberImpl member = entityBuilder.createMember(this, voiceStateData.getObject("member"), null, null); + entityBuilder.updateMemberCache(member); + return entityBuilder.createGuildVoiceState(member, voiceStateData); + }); + } + @Nonnull @Override public VerificationLevel getVerificationLevel()