diff --git a/src/main/java/net/dv8tion/jda/api/JDA.java b/src/main/java/net/dv8tion/jda/api/JDA.java index 7f50e3b45c..88d44aa98a 100644 --- a/src/main/java/net/dv8tion/jda/api/JDA.java +++ b/src/main/java/net/dv8tion/jda/api/JDA.java @@ -1845,6 +1845,15 @@ default RestAction retrieveApplicationEmojiById(long emojiId) @CheckReturnValue RestAction<@Unmodifiable List> retrieveNitroStickerPacks(); + /** + * Retrieves a list of the default {@link SoundboardSound SoundboardSounds}. + * + * @return {@link RestAction} - Type: List of {@link SoundboardSound} + */ + @Nonnull + @CheckReturnValue + RestAction<@Unmodifiable List> retrieveDefaultSoundboardSounds(); + /** * The EventManager used by this JDA instance. * 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 ab252daeed..7eae91c33d 100644 --- a/src/main/java/net/dv8tion/jda/api/entities/Guild.java +++ b/src/main/java/net/dv8tion/jda/api/entities/Guild.java @@ -2052,6 +2052,104 @@ default List getStickersByName(@Nonnull String name, boolean ignor @Nonnull SnowflakeCacheView getStickerCache(); + /** + * Gets a {@link SoundboardSound} from this guild that has the same id as the + * one provided. + *
If there is no {@link SoundboardSound} with an id that matches the provided + * one, then this returns {@code null}. + * + *

This requires the {@link CacheFlag#SOUNDBOARD_SOUNDS} to be enabled! + * + * @param id + * the sticker id + * + * @throws NumberFormatException + * If the provided {@code id} cannot be parsed by {@link Long#parseLong(String)} + * + * @return A Soundboard sound matching the specified id + */ + @Nullable + default SoundboardSound getSoundboardSoundById(String id) + { + return getSoundboardSoundCache().getElementById(id); + } + + /** + * Gets a {@link SoundboardSound} from this guild that has the same id as the + * one provided. + *
If there is no {@link SoundboardSound} with an id that matches the provided + * one, then this returns {@code null}. + * + *

This requires the {@link CacheFlag#SOUNDBOARD_SOUNDS} to be enabled! + * + * @param id + * the sticker id + * + * @throws NumberFormatException + * If the provided {@code id} cannot be parsed by {@link Long#parseLong(String)} + * + * @return A Soundboard sound matching the specified id + */ + @Nullable + default SoundboardSound getSoundboardSoundById(long id) + { + return getSoundboardSoundCache().getElementById(id); + } + + /** + * Gets all custom {@link SoundboardSound SoundboardSounds} belonging to this guild. + *
Soundboard sounds are not ordered in any specific way in the returned list. + * + *

This copies the backing store into a list. This means every call + * creates a new list with O(n) complexity. It is recommended to store this into + * a local variable or use {@link #getSoundboardSoundCache()} and use its more efficient + * versions of handling these values. + * + *

This requires the {@link CacheFlag#SOUNDBOARD_SOUNDS} to be enabled! + * + * @return An immutable List of {@link SoundboardSound SoundboardSounds}. + */ + @Nonnull + @Unmodifiable + default List getSoundboardSounds() + { + return getSoundboardSoundCache().asList(); + } + + /** + * Gets a list of all {@link SoundboardSound SoundboardSounds} in this Guild that have the same + * name as the one provided. + *
If there are no {@link SoundboardSound SoundboardSounds} with the provided name, then this returns an empty list. + * + *

This requires the {@link CacheFlag#SOUNDBOARD_SOUNDS} to be enabled! + * + * @param name + * The name used to filter the returned {@link SoundboardSound SoundboardSounds}. Without colons. + * @param ignoreCase + * Determines if the comparison ignores case when comparing. True - case insensitive. + * + * @return Possibly-empty immutable list of all SoundboardSounds that match the provided name. + */ + @Nonnull + @Unmodifiable + default List getSoundboardSoundsByName(@Nonnull String name, boolean ignoreCase) + { + return getSoundboardSoundCache().getElementsByName(name, ignoreCase); + } + + /** + * {@link SnowflakeCacheView} of all cached {@link SoundboardSound SoundboardSounds} of this Guild. + *
This does not include {@link JDA#retrieveDefaultSoundboardSounds() default sounds}. + * + *

This will be empty if {@link CacheFlag#SOUNDBOARD_SOUNDS} is disabled! + * + * @return {@link SnowflakeCacheView} - Type: {@link SoundboardSound} + * + * @see JDA#retrieveDefaultSoundboardSounds() + */ + @Nonnull + SnowflakeCacheView getSoundboardSoundCache(); + /** * Retrieves an immutable list of Custom Emojis together with their respective creators. * @@ -5011,6 +5109,43 @@ default AuditableRestAction createSticker(@Nonnull String name, @N @CheckReturnValue AuditableRestAction deleteSticker(@Nonnull StickerSnowflake id); + /** + * Creates a soundboard sound in the guild. + * + *

The returned {@link RestAction} can encounter the following {@link net.dv8tion.jda.api.requests.ErrorResponse ErrorResponses}: + *

    + *
  • {@link net.dv8tion.jda.api.requests.ErrorResponse#INVALID_FILE_EXCEEDS_MAXIMUM_LENGTH INVALID_FILE_EXCEEDS_MAXIMUM_LENGTH} + *
    The provided file exceeds the duration of 5.2 seconds
  • + *
  • {@link net.dv8tion.jda.api.requests.ErrorResponse#MAX_SOUNDBOARD_SOUNDS MAX_SOUNDBOARD_SOUNDS} + *
    The maximum amount of soundboard sounds have been created, depends on the server boosts
  • + *
  • {@link net.dv8tion.jda.api.requests.ErrorResponse#INVALID_EMOJI INVALID_EMOJI} + *
    The emoji is invalid
  • + *
  • {@link net.dv8tion.jda.api.requests.ErrorResponse#INVALID_FORM_BODY INVALID_FORM_BODY} + *
    The file is too large
  • + *
  • {@link net.dv8tion.jda.api.requests.ErrorResponse#INVALID_FILE INVALID_FILE} + *
    The file is malformed
  • + *
+ * + * @param name + * The name of the soundboard sound, must be between 2-32 characters + * @param file + * The file to use as the sound, can be an MP3 or an OGG file + * + * @throws IllegalArgumentException + *
    + *
  • If {@code null} is provided
  • + *
  • If {@code name} is not between 2-32 characters
  • + *
  • If the file is not of the correct type
  • + *
+ * @throws net.dv8tion.jda.api.exceptions.InsufficientPermissionException + * If the currently logged in account does not have {@link Permission#CREATE_GUILD_EXPRESSIONS CREATE_GUILD_EXPRESSIONS} in the guild. + * + * @return {@link SoundboardSoundCreateAction} + */ + @Nonnull + @CheckReturnValue + SoundboardSoundCreateAction createSoundboardSound(@Nonnull String name, @Nonnull FileUpload file); + /** * Creates a new {@link ScheduledEvent}. * Events created with this method will be of {@link ScheduledEvent.Type#EXTERNAL Type.EXTERNAL}. diff --git a/src/main/java/net/dv8tion/jda/api/entities/SoundboardSound.java b/src/main/java/net/dv8tion/jda/api/entities/SoundboardSound.java new file mode 100644 index 0000000000..6221ac27ae --- /dev/null +++ b/src/main/java/net/dv8tion/jda/api/entities/SoundboardSound.java @@ -0,0 +1,200 @@ +/* + * Copyright 2015 Austin Keener, Michael Ritter, Florian Spieß, and the JDA contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.dv8tion.jda.api.entities; + +import net.dv8tion.jda.api.JDA; +import net.dv8tion.jda.api.Permission; +import net.dv8tion.jda.api.entities.channel.concrete.VoiceChannel; +import net.dv8tion.jda.api.entities.emoji.EmojiUnion; +import net.dv8tion.jda.api.exceptions.InsufficientPermissionException; +import net.dv8tion.jda.api.managers.SoundboardSoundManager; +import net.dv8tion.jda.api.requests.ErrorResponse; +import net.dv8tion.jda.api.requests.GatewayIntent; +import net.dv8tion.jda.api.requests.RestAction; + +import javax.annotation.CheckReturnValue; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +/** + * Represents a soundboard sound, can be used in voice channels if they are {@link #isAvailable() available}. + * + * @see JDA#retrieveDefaultSoundboardSounds() + * @see Guild#getSoundboardSounds() + * @see Discord's Soundboard Docs + */ +public interface SoundboardSound extends ISnowflake +{ + /** Template for {@link #getUrl()}.*/ + String SOUND_URL = "https://cdn.discordapp.com/soundboard-sounds/%s"; + + /** + * Returns the {@link JDA} instance related to this SoundboardSound. + * + * @return the corresponding JDA instance + */ + @Nonnull + JDA getJDA(); + + /** + * Returns the URL to the sound asset. + * + * @return A String representing this sound's asset + */ + @Nonnull + default String getUrl() + { + return String.format(SOUND_URL, getId()); + } + + /** + * The name of this sound. + * + * @return The name of this sound + */ + @Nonnull + String getName(); + + /** + * The volume of this sound, from 0 to 1. + * + * @return The volume of this sound, from 0 to 1 + */ + double getVolume(); + + /** + * The emoji of this sound, or {@code null} if none is set. + * + * @return The emoji of this sound, or {@code null} + */ + @Nullable + EmojiUnion getEmoji(); + + /** + * The guild this sound is from. + * + * @return The guild this sound is from, or {@code null} if this is a default soundboard sound + */ + @Nullable + Guild getGuild(); + + /** + * Whether this sound can be used, may be {@code false} due to loss of server boosts. + * + * @return {@code true} if the sound can be used, {@code false} otherwise + */ + boolean isAvailable(); + + /** + * The user which created this sound. + * + *

This is present only if the {@link Guild#getSelfMember() self member} has + * the {@link Permission#CREATE_GUILD_EXPRESSIONS CREATE_GUILD_EXPRESSIONS} + * or {@link Permission#MANAGE_GUILD_EXPRESSIONS MANAGE_GUILD_EXPRESSIONS} permission. + * + * @return The user which created this sound, or {@code null} + */ + @Nullable + User getUser(); + + /** + * Sends this sound to the specified voice channel. + *
You must be connected to the voice channel to use this method, + * as well as be able to speak and hear. + * + *

The returned {@link RestAction} can encounter the following {@link ErrorResponse ErrorResponses}: + *

    + *
  • {@link ErrorResponse#MISSING_PERMISSIONS MISSING_PERMISSIONS} + *
    The sound cannot be sent due to a permission discrepancy
  • + *
  • {@link ErrorResponse#CANNOT_SEND_VOICE_EFFECT CANNOT_SEND_VOICE_EFFECT} + *
    The sound cannot be sent as the bot is either muted, deafened or suppressed
  • + *
  • {@link ErrorResponse#UNKNOWN_SOUND UNKNOWN_SOUND} + *
    The sound was deleted
  • + *
+ * + * @param channel + * The channel to send this sound on + * + * @throws InsufficientPermissionException + * If the bot does not have the following permissions: + *
    + *
  • {@link Permission#VOICE_SPEAK VOICE_SPEAK}, {@link Permission#VOICE_USE_SOUNDBOARD VOICE_USE_SOUNDBOARD}
  • + *
  • When used in other guilds, {@link Permission#VOICE_USE_EXTERNAL_SOUNDS VOICE_USE_EXTERNAL_SOUNDS} permission
  • + *
+ * @throws IllegalArgumentException + * If the provided channel is {@code null} + * @throws IllegalStateException + *
    + *
  • If {@link GatewayIntent#GUILD_VOICE_STATES} is not enabled
  • + *
  • If the sound is not {@link #isAvailable() available}
  • + *
  • If the bot is not connected to the specified channel
  • + *
  • If the bot is deafened, muted or suppressed in the target guild
  • + *
+ * + * @return {@link RestAction} - Type: {@link Void} + */ + @Nonnull + @CheckReturnValue + RestAction sendTo(VoiceChannel channel); + + /** + * Deletes this soundboard sound. + * + *

The returned {@link RestAction} can encounter the following {@link ErrorResponse ErrorResponses}: + *

    + *
  • {@link ErrorResponse#MISSING_PERMISSIONS MISSING_PERMISSIONS} + *
    The sound cannot be deleted due to a permission discrepancy
  • + *
  • {@link ErrorResponse#UNKNOWN_SOUND UNKNOWN_SOUND} + *
    The sound was deleted
  • + *
+ * + * @throws InsufficientPermissionException + *
    + *
  • If the bot does not own this sound and does not have {@link Permission#MANAGE_GUILD_EXPRESSIONS}
  • + *
  • If the bot owns this sound and does not have {@link Permission#MANAGE_GUILD_EXPRESSIONS} or {@link Permission#CREATE_GUILD_EXPRESSIONS}
  • + *
+ * + * @return {@link RestAction} - Type: {@link Void} + */ + @Nonnull + @CheckReturnValue + RestAction delete(); + + /** + * The {@link SoundboardSoundManager} for this soundboard sound, in which you can modify values. + *
You modify multiple fields in one request by chaining setters before calling {@link RestAction#queue()}. + * + *

The returned {@link RestAction} can encounter the following {@link ErrorResponse ErrorResponses}: + *

    + *
  • {@link ErrorResponse#MISSING_PERMISSIONS MISSING_PERMISSIONS} + *
    The sound cannot be edited due to a permission discrepancy
  • + *
  • {@link ErrorResponse#UNKNOWN_SOUND UNKNOWN_SOUND} + *
    The sound was deleted
  • + *
+ * + * @throws InsufficientPermissionException + *
    + *
  • If the bot does not own this sound and does not have {@link Permission#MANAGE_GUILD_EXPRESSIONS}
  • + *
  • If the bot owns this sound and does not have {@link Permission#MANAGE_GUILD_EXPRESSIONS} or {@link Permission#CREATE_GUILD_EXPRESSIONS}
  • + *
+ * + * @return The SoundboardSoundManager of this soundboard sound + */ + @Nonnull + @CheckReturnValue + SoundboardSoundManager getManager(); +} diff --git a/src/main/java/net/dv8tion/jda/api/entities/channel/VoiceChannelEffect.java b/src/main/java/net/dv8tion/jda/api/entities/channel/VoiceChannelEffect.java new file mode 100644 index 0000000000..9071497f81 --- /dev/null +++ b/src/main/java/net/dv8tion/jda/api/entities/channel/VoiceChannelEffect.java @@ -0,0 +1,165 @@ +package net.dv8tion.jda.api.entities.channel; + +import net.dv8tion.jda.api.entities.ISnowflake; +import net.dv8tion.jda.api.entities.SoundboardSound; +import net.dv8tion.jda.api.entities.User; +import net.dv8tion.jda.api.entities.channel.concrete.VoiceChannel; +import net.dv8tion.jda.api.entities.emoji.EmojiUnion; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +/** + * Represents an emoji effect or a soundboard sound effect. + */ +public class VoiceChannelEffect +{ + private final VoiceChannel channel; + private final User user; + private final EmojiUnion emoji; + private final Animation animation; + private final SoundboardSound soundboardSound; + + public VoiceChannelEffect(VoiceChannel channel, User user, EmojiUnion emoji, Animation animation, SoundboardSound soundboardSound) + { + this.channel = channel; + this.user = user; + this.emoji = emoji; + this.animation = animation; + this.soundboardSound = soundboardSound; + } + + /** + * The voice channel this effect was sent to. + * + * @return The voice channel this effect was sent to. + */ + @Nonnull + public VoiceChannel getChannel() + { + return channel; + } + + /** + * The user which sent this effect. + * + * @return The user which sent this effect. + */ + @Nonnull + public User getUser() + { + return user; + } + + /** + * The emoji sent with the effect, this is only present for emoji effects. + * + * @return The emoji sent with the effect, or {@code null} + */ + @Nullable + public EmojiUnion getEmoji() + { + return emoji; + } + + /** + * The animation of the emoji, this is only present for emoji effects. + * + * @return The animation of the emoji, or {@code null} + */ + @Nullable + public Animation getAnimation() + { + return animation; + } + + /** + * The soundboard sound sent with the effect, this is only present for soundboard sound effects. + * + * @return The soundboard sound sent with the effect, or {@code null} + */ + @Nullable + public SoundboardSound getSoundboardSound() + { + return soundboardSound; + } + + /** + * Represents the animation used in emoji effects. + */ + public static class Animation implements ISnowflake + { + public Animation(long id, Animation.Type type) + { + this.id = id; + this.type = type; + } + + private final long id; + private final Animation.Type type; + + @Override + public long getIdLong() + { + return id; + } + + /** + * The type of animation + * + * @return The type of animation + */ + @Nonnull + public Animation.Type getType() + { + return type; + } + + /** + * Represents the animation type used in emoji effects. + */ + public enum Type + { + UNKNOWN(-1), + PREMIUM(0), + BASIC(1); + + private final int value; + + Type(int value) + { + this.value = value; + } + + /** + * The raw value of this animation type. + * + * @return The raw value + */ + public int getValue() + { + return value; + } + + /** + * Retrieves the animation type from the raw value. + * + * @param value + * The raw value of the animation type + * + * @return The animation type, or {@link #UNKNOWN} for invalid values + */ + @Nonnull + public static Animation.Type fromValue(int value) + { + for (Animation.Type type : values()) + { + if (type.value == value) + return type; + } + + return UNKNOWN; + } + } + } +} diff --git a/src/main/java/net/dv8tion/jda/api/events/channel/VoiceChannelEffectSendEvent.java b/src/main/java/net/dv8tion/jda/api/events/channel/VoiceChannelEffectSendEvent.java new file mode 100644 index 0000000000..b68372c6b1 --- /dev/null +++ b/src/main/java/net/dv8tion/jda/api/events/channel/VoiceChannelEffectSendEvent.java @@ -0,0 +1,63 @@ +/* + * Copyright 2015 Austin Keener, Michael Ritter, Florian Spieß, and the JDA contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.dv8tion.jda.api.events.channel; + +import net.dv8tion.jda.api.JDA; +import net.dv8tion.jda.api.entities.channel.VoiceChannelEffect; +import net.dv8tion.jda.api.entities.channel.concrete.VoiceChannel; +import net.dv8tion.jda.api.requests.GatewayIntent; + +import javax.annotation.Nonnull; + +/** + * Indicates that a {@link VoiceChannelEffect voice channel effect} was sent in a {@link VoiceChannel}. + * + *

Requirements
+ * This event requires {@link GatewayIntent#GUILD_VOICE_STATES} to be enabled. + */ +public class VoiceChannelEffectSendEvent extends GenericChannelEvent +{ + private final VoiceChannelEffect effect; + + public VoiceChannelEffectSendEvent(@Nonnull JDA api, long responseNumber, VoiceChannelEffect effect) + { + super(api, responseNumber, effect.getChannel()); + this.effect = effect; + } + + /** + * The voice channel the effect was sent to. + * + * @return The voice channel the effect was sent to. + */ + @Nonnull + public VoiceChannel getVoiceChannel() + { + return effect.getChannel(); + } + + /** + * The effect that was sent. + * + * @return The effect that was sent. + */ + @Nonnull + public VoiceChannelEffect getEffect() + { + return effect; + } +} diff --git a/src/main/java/net/dv8tion/jda/api/events/soundboard/GenericSoundboardSoundEvent.java b/src/main/java/net/dv8tion/jda/api/events/soundboard/GenericSoundboardSoundEvent.java new file mode 100644 index 0000000000..21ff3890a0 --- /dev/null +++ b/src/main/java/net/dv8tion/jda/api/events/soundboard/GenericSoundboardSoundEvent.java @@ -0,0 +1,70 @@ +/* + * Copyright 2015 Austin Keener, Michael Ritter, Florian Spieß, and the JDA contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.dv8tion.jda.api.events.soundboard; + +import net.dv8tion.jda.api.JDA; +import net.dv8tion.jda.api.JDABuilder; +import net.dv8tion.jda.api.entities.Guild; +import net.dv8tion.jda.api.entities.SoundboardSound; +import net.dv8tion.jda.api.events.Event; +import net.dv8tion.jda.api.requests.GatewayIntent; +import net.dv8tion.jda.api.utils.cache.CacheFlag; + +import javax.annotation.Nonnull; + +/** + * Indicates that a {@link SoundboardSound} was created/deleted/updated. + * + *

Requirements
+ * These events require {@link CacheFlag#SOUNDBOARD_SOUNDS} to be enabled, + * which requires {@link GatewayIntent#GUILD_EMOJIS_AND_STICKERS}. + * + *
{@link JDABuilder#createLight(String) createLight(String)} disables that CacheFlag by default! + */ +public abstract class GenericSoundboardSoundEvent extends Event +{ + private final SoundboardSound soundboardSound; + + public GenericSoundboardSoundEvent(@Nonnull JDA api, long responseNumber, @Nonnull SoundboardSound soundboardSound) + { + super(api, responseNumber); + this.soundboardSound = soundboardSound; + } + + /** + * The {@link Guild} where the soundboard sound came from + * + * @return The origin Guild + */ + @Nonnull + @SuppressWarnings("DataFlowIssue") // This event is guild-only + public Guild getGuild() + { + return soundboardSound.getGuild(); + } + + /** + * The affected {@link SoundboardSound} for this event + * + * @return The soundboard sound + */ + @Nonnull + public SoundboardSound getSoundboardSound() + { + return soundboardSound; + } +} diff --git a/src/main/java/net/dv8tion/jda/api/events/soundboard/SoundboardSoundCreateEvent.java b/src/main/java/net/dv8tion/jda/api/events/soundboard/SoundboardSoundCreateEvent.java new file mode 100644 index 0000000000..a12f875bec --- /dev/null +++ b/src/main/java/net/dv8tion/jda/api/events/soundboard/SoundboardSoundCreateEvent.java @@ -0,0 +1,42 @@ +/* + * Copyright 2015 Austin Keener, Michael Ritter, Florian Spieß, and the JDA contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.dv8tion.jda.api.events.soundboard; + +import net.dv8tion.jda.api.JDA; +import net.dv8tion.jda.api.JDABuilder; +import net.dv8tion.jda.api.entities.SoundboardSound; +import net.dv8tion.jda.api.requests.GatewayIntent; +import net.dv8tion.jda.api.utils.cache.CacheFlag; + +import javax.annotation.Nonnull; + +/** + * Indicates that a {@link SoundboardSound} was created. + * + *

Requirements
+ * This event require {@link CacheFlag#SOUNDBOARD_SOUNDS} to be enabled, + * which requires {@link GatewayIntent#GUILD_EMOJIS_AND_STICKERS}. + * + *
{@link JDABuilder#createLight(String) createLight(String)} disables that CacheFlag by default! + */ +public class SoundboardSoundCreateEvent extends GenericSoundboardSoundEvent +{ + public SoundboardSoundCreateEvent(@Nonnull JDA api, long responseNumber, @Nonnull SoundboardSound soundboardSound) + { + super(api, responseNumber, soundboardSound); + } +} diff --git a/src/main/java/net/dv8tion/jda/api/events/soundboard/SoundboardSoundDeleteEvent.java b/src/main/java/net/dv8tion/jda/api/events/soundboard/SoundboardSoundDeleteEvent.java new file mode 100644 index 0000000000..3bc1af2024 --- /dev/null +++ b/src/main/java/net/dv8tion/jda/api/events/soundboard/SoundboardSoundDeleteEvent.java @@ -0,0 +1,42 @@ +/* + * Copyright 2015 Austin Keener, Michael Ritter, Florian Spieß, and the JDA contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.dv8tion.jda.api.events.soundboard; + +import net.dv8tion.jda.api.JDA; +import net.dv8tion.jda.api.JDABuilder; +import net.dv8tion.jda.api.entities.SoundboardSound; +import net.dv8tion.jda.api.requests.GatewayIntent; +import net.dv8tion.jda.api.utils.cache.CacheFlag; + +import javax.annotation.Nonnull; + +/** + * Indicates that a {@link SoundboardSound} was deleted. + * + *

Requirements
+ * This event require {@link CacheFlag#SOUNDBOARD_SOUNDS} to be enabled, + * which requires {@link GatewayIntent#GUILD_EMOJIS_AND_STICKERS}. + * + *
{@link JDABuilder#createLight(String) createLight(String)} disables that CacheFlag by default! + */ +public class SoundboardSoundDeleteEvent extends GenericSoundboardSoundEvent +{ + public SoundboardSoundDeleteEvent(@Nonnull JDA api, long responseNumber, @Nonnull SoundboardSound soundboardSound) + { + super(api, responseNumber, soundboardSound); + } +} diff --git a/src/main/java/net/dv8tion/jda/api/events/soundboard/package-info.java b/src/main/java/net/dv8tion/jda/api/events/soundboard/package-info.java new file mode 100644 index 0000000000..0b04068923 --- /dev/null +++ b/src/main/java/net/dv8tion/jda/api/events/soundboard/package-info.java @@ -0,0 +1,27 @@ +/* + * Copyright 2015 Austin Keener, Michael Ritter, Florian Spieß, and the JDA contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Events that track {@link net.dv8tion.jda.api.events.soundboard.SoundboardSoundCreateEvent created soundboard sounds} + * and {@link net.dv8tion.jda.api.events.soundboard.SoundboardSoundDeleteEvent deleted soundboard sounds}. + * + *

Requirements
+ * These events require {@link net.dv8tion.jda.api.utils.cache.CacheFlag#SOUNDBOARD_SOUNDS} to be enabled, + * which requires {@link net.dv8tion.jda.api.requests.GatewayIntent#GUILD_EMOJIS_AND_STICKERS}. + * + *
{@link net.dv8tion.jda.api.JDABuilder#createLight(String) createLight(String)} disables that CacheFlag by default! + */ +package net.dv8tion.jda.api.events.soundboard; diff --git a/src/main/java/net/dv8tion/jda/api/events/soundboard/update/GenericSoundboardSoundUpdateEvent.java b/src/main/java/net/dv8tion/jda/api/events/soundboard/update/GenericSoundboardSoundUpdateEvent.java new file mode 100644 index 0000000000..4cf0edc9e1 --- /dev/null +++ b/src/main/java/net/dv8tion/jda/api/events/soundboard/update/GenericSoundboardSoundUpdateEvent.java @@ -0,0 +1,82 @@ +/* + * Copyright 2015 Austin Keener, Michael Ritter, Florian Spieß, and the JDA contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.dv8tion.jda.api.events.soundboard.update; + +import net.dv8tion.jda.api.JDA; +import net.dv8tion.jda.api.JDABuilder; +import net.dv8tion.jda.api.entities.SoundboardSound; +import net.dv8tion.jda.api.events.UpdateEvent; +import net.dv8tion.jda.api.events.soundboard.GenericSoundboardSoundEvent; +import net.dv8tion.jda.api.requests.GatewayIntent; +import net.dv8tion.jda.api.utils.cache.CacheFlag; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +/** + * Indicates that a {@link SoundboardSound} was updated. + * + *

Requirements
+ * These events require {@link CacheFlag#SOUNDBOARD_SOUNDS} to be enabled, + * which requires {@link GatewayIntent#GUILD_EMOJIS_AND_STICKERS}. + * + *
{@link JDABuilder#createLight(String) createLight(String)} disables that CacheFlag by default! + */ +public abstract class GenericSoundboardSoundUpdateEvent extends GenericSoundboardSoundEvent implements UpdateEvent +{ + protected final T previous; + protected final T next; + protected final String identifier; + + public GenericSoundboardSoundUpdateEvent( + @Nonnull JDA api, long responseNumber, @Nonnull SoundboardSound soundboardSound, + @Nullable T previous, @Nullable T next, @Nonnull String identifier) + { + super(api, responseNumber, soundboardSound); + this.previous = previous; + this.next = next; + this.identifier = identifier; + } + + @Nonnull + @Override + public SoundboardSound getEntity() + { + return getSoundboardSound(); + } + + @Nonnull + @Override + public String getPropertyIdentifier() + { + return identifier; + } + + @Nullable + @Override + public T getOldValue() + { + return previous; + } + + @Nullable + @Override + public T getNewValue() + { + return next; + } +} diff --git a/src/main/java/net/dv8tion/jda/api/events/soundboard/update/SoundboardSoundUpdateEmojiEvent.java b/src/main/java/net/dv8tion/jda/api/events/soundboard/update/SoundboardSoundUpdateEmojiEvent.java new file mode 100644 index 0000000000..f9e51587c3 --- /dev/null +++ b/src/main/java/net/dv8tion/jda/api/events/soundboard/update/SoundboardSoundUpdateEmojiEvent.java @@ -0,0 +1,86 @@ +/* + * Copyright 2015 Austin Keener, Michael Ritter, Florian Spieß, and the JDA contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.dv8tion.jda.api.events.soundboard.update; + +import net.dv8tion.jda.api.JDA; +import net.dv8tion.jda.api.JDABuilder; +import net.dv8tion.jda.api.entities.SoundboardSound; +import net.dv8tion.jda.api.entities.emoji.EmojiUnion; +import net.dv8tion.jda.api.requests.GatewayIntent; +import net.dv8tion.jda.api.utils.cache.CacheFlag; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +/** + * Indicates that the emoji of a {@link SoundboardSound soundboard sound} changed. + * + *

Can be used to retrieve the old emoji. + * + *

Requirements
+ * This event require {@link CacheFlag#SOUNDBOARD_SOUNDS} to be enabled, + * which requires {@link GatewayIntent#GUILD_EMOJIS_AND_STICKERS}. + * + *
{@link JDABuilder#createLight(String) createLight(String)} disables that CacheFlag by default! + * + *

Identifier: {@value IDENTIFIER} + */ +public class SoundboardSoundUpdateEmojiEvent extends GenericSoundboardSoundUpdateEvent +{ + public static final String IDENTIFIER = "emoji"; + + public SoundboardSoundUpdateEmojiEvent(@Nonnull JDA api, long responseNumber, @Nonnull SoundboardSound soundboardSound, EmojiUnion oldEmoji) + { + super(api, responseNumber, soundboardSound, oldEmoji, soundboardSound.getEmoji(), IDENTIFIER); + } + + /** + * The old emoji + * + * @return The old emoji + */ + @Nullable + public EmojiUnion getOldEmoji() + { + return getOldValue(); + } + + /** + * The new emoji + * + * @return The new emoji + */ + @Nullable + public EmojiUnion getNewEmoji() + { + return getNewValue(); + } + + @Nullable + @Override + public EmojiUnion getOldValue() + { + return super.getOldValue(); + } + + @Nullable + @Override + public EmojiUnion getNewValue() + { + return super.getNewValue(); + } +} diff --git a/src/main/java/net/dv8tion/jda/api/events/soundboard/update/SoundboardSoundUpdateNameEvent.java b/src/main/java/net/dv8tion/jda/api/events/soundboard/update/SoundboardSoundUpdateNameEvent.java new file mode 100644 index 0000000000..e0ddbe21e3 --- /dev/null +++ b/src/main/java/net/dv8tion/jda/api/events/soundboard/update/SoundboardSoundUpdateNameEvent.java @@ -0,0 +1,86 @@ +/* + * Copyright 2015 Austin Keener, Michael Ritter, Florian Spieß, and the JDA contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.dv8tion.jda.api.events.soundboard.update; + +import net.dv8tion.jda.api.JDA; +import net.dv8tion.jda.api.JDABuilder; +import net.dv8tion.jda.api.entities.SoundboardSound; +import net.dv8tion.jda.api.requests.GatewayIntent; +import net.dv8tion.jda.api.utils.cache.CacheFlag; + +import javax.annotation.Nonnull; + +/** + * Indicates that the name of a {@link SoundboardSound soundboard sound} changed. + * + *

Can be used to retrieve the old name. + * + *

Requirements
+ * This event require {@link CacheFlag#SOUNDBOARD_SOUNDS} to be enabled, + * which requires {@link GatewayIntent#GUILD_EMOJIS_AND_STICKERS}. + * + *
{@link JDABuilder#createLight(String) createLight(String)} disables that CacheFlag by default! + * + *

Identifier: {@value IDENTIFIER} + */ +public class SoundboardSoundUpdateNameEvent extends GenericSoundboardSoundUpdateEvent +{ + public static final String IDENTIFIER = "name"; + + public SoundboardSoundUpdateNameEvent(@Nonnull JDA api, long responseNumber, @Nonnull SoundboardSound soundboardSound, @Nonnull String oldName) + { + super(api, responseNumber, soundboardSound, oldName, soundboardSound.getName(), IDENTIFIER); + } + + /** + * The old name + * + * @return The old name + */ + @Nonnull + public String getOldName() + { + return getOldValue(); + } + + /** + * The new name + * + * @return The new name + */ + @Nonnull + public String getNewName() + { + return getNewValue(); + } + + @Nonnull + @Override + @SuppressWarnings("DataFlowIssue") + public String getOldValue() + { + return super.getOldValue(); + } + + @Nonnull + @Override + @SuppressWarnings("DataFlowIssue") + public String getNewValue() + { + return super.getNewValue(); + } +} diff --git a/src/main/java/net/dv8tion/jda/api/events/soundboard/update/SoundboardSoundUpdateVolumeEvent.java b/src/main/java/net/dv8tion/jda/api/events/soundboard/update/SoundboardSoundUpdateVolumeEvent.java new file mode 100644 index 0000000000..fd3ee8dc03 --- /dev/null +++ b/src/main/java/net/dv8tion/jda/api/events/soundboard/update/SoundboardSoundUpdateVolumeEvent.java @@ -0,0 +1,84 @@ +/* + * Copyright 2015 Austin Keener, Michael Ritter, Florian Spieß, and the JDA contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.dv8tion.jda.api.events.soundboard.update; + +import net.dv8tion.jda.api.JDA; +import net.dv8tion.jda.api.JDABuilder; +import net.dv8tion.jda.api.entities.SoundboardSound; +import net.dv8tion.jda.api.requests.GatewayIntent; +import net.dv8tion.jda.api.utils.cache.CacheFlag; + +import javax.annotation.Nonnull; + +/** + * Indicates that the volume of a {@link SoundboardSound soundboard sound} changed. + * + *

Can be used to retrieve the old volume. + * + *

Requirements
+ * This event require {@link CacheFlag#SOUNDBOARD_SOUNDS} to be enabled, + * which requires {@link GatewayIntent#GUILD_EMOJIS_AND_STICKERS}. + * + *
{@link JDABuilder#createLight(String) createLight(String)} disables that CacheFlag by default! + * + *

Identifier: {@value IDENTIFIER} + */ +public class SoundboardSoundUpdateVolumeEvent extends GenericSoundboardSoundUpdateEvent +{ + public static final String IDENTIFIER = "volume"; + + public SoundboardSoundUpdateVolumeEvent(@Nonnull JDA api, long responseNumber, @Nonnull SoundboardSound soundboardSound, double oldVolume) + { + super(api, responseNumber, soundboardSound, oldVolume, soundboardSound.getVolume(), IDENTIFIER); + } + + /** + * The old volume + * + * @return The old volume + */ + public double getOldVolume() + { + return getOldValue(); + } + + /** + * The new volume + * + * @return The new volume + */ + public double getNewVolume() + { + return getNewValue(); + } + + @Nonnull + @Override + @SuppressWarnings("DataFlowIssue") + public Double getOldValue() + { + return super.getOldValue(); + } + + @Nonnull + @Override + @SuppressWarnings("DataFlowIssue") + public Double getNewValue() + { + return super.getNewValue(); + } +} diff --git a/src/main/java/net/dv8tion/jda/api/events/soundboard/update/package-info.java b/src/main/java/net/dv8tion/jda/api/events/soundboard/update/package-info.java new file mode 100644 index 0000000000..8083e7d57e --- /dev/null +++ b/src/main/java/net/dv8tion/jda/api/events/soundboard/update/package-info.java @@ -0,0 +1,26 @@ +/* + * Copyright 2015 Austin Keener, Michael Ritter, Florian Spieß, and the JDA contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Events that track updates for existing {@link net.dv8tion.jda.api.entities.SoundboardSound SoundboardSounds} + * + *

Requirements
+ * These events require {@link net.dv8tion.jda.api.utils.cache.CacheFlag#SOUNDBOARD_SOUNDS} to be enabled, + * which requires {@link net.dv8tion.jda.api.requests.GatewayIntent#GUILD_EMOJIS_AND_STICKERS}. + * + *
{@link net.dv8tion.jda.api.JDABuilder#createLight(String) createLight(String)} disables that CacheFlag by default! + */ +package net.dv8tion.jda.api.events.soundboard.update; diff --git a/src/main/java/net/dv8tion/jda/api/hooks/ListenerAdapter.java b/src/main/java/net/dv8tion/jda/api/hooks/ListenerAdapter.java index 81e5ae6a49..d35e13540d 100644 --- a/src/main/java/net/dv8tion/jda/api/hooks/ListenerAdapter.java +++ b/src/main/java/net/dv8tion/jda/api/hooks/ListenerAdapter.java @@ -20,6 +20,7 @@ import net.dv8tion.jda.api.events.channel.ChannelCreateEvent; import net.dv8tion.jda.api.events.channel.ChannelDeleteEvent; import net.dv8tion.jda.api.events.channel.GenericChannelEvent; +import net.dv8tion.jda.api.events.channel.VoiceChannelEffectSendEvent; import net.dv8tion.jda.api.events.channel.forum.ForumTagAddEvent; import net.dv8tion.jda.api.events.channel.forum.ForumTagRemoveEvent; import net.dv8tion.jda.api.events.channel.forum.GenericForumTagEvent; @@ -69,6 +70,13 @@ import net.dv8tion.jda.api.events.role.update.*; import net.dv8tion.jda.api.events.self.*; import net.dv8tion.jda.api.events.session.*; +import net.dv8tion.jda.api.events.soundboard.GenericSoundboardSoundEvent; +import net.dv8tion.jda.api.events.soundboard.SoundboardSoundCreateEvent; +import net.dv8tion.jda.api.events.soundboard.SoundboardSoundDeleteEvent; +import net.dv8tion.jda.api.events.soundboard.update.GenericSoundboardSoundUpdateEvent; +import net.dv8tion.jda.api.events.soundboard.update.SoundboardSoundUpdateEmojiEvent; +import net.dv8tion.jda.api.events.soundboard.update.SoundboardSoundUpdateNameEvent; +import net.dv8tion.jda.api.events.soundboard.update.SoundboardSoundUpdateVolumeEvent; import net.dv8tion.jda.api.events.stage.GenericStageInstanceEvent; import net.dv8tion.jda.api.events.stage.StageInstanceCreateEvent; import net.dv8tion.jda.api.events.stage.StageInstanceDeleteEvent; @@ -369,6 +377,18 @@ public void onGuildStickerUpdateTags(@Nonnull GuildStickerUpdateTagsEvent event) public void onGuildStickerUpdateDescription(@Nonnull GuildStickerUpdateDescriptionEvent event) {} public void onGuildStickerUpdateAvailable(@Nonnull GuildStickerUpdateAvailableEvent event) {} + // Soundboard sound events + public void onSoundboardSoundCreate(@Nonnull SoundboardSoundCreateEvent event) {} + public void onSoundboardSoundDelete(@Nonnull SoundboardSoundDeleteEvent event) {} + + // Soundboard sound update events + public void onSoundboardSoundUpdateName(@Nonnull SoundboardSoundUpdateNameEvent event) {} + public void onSoundboardSoundUpdateVolume(@Nonnull SoundboardSoundUpdateVolumeEvent event) {} + public void onSoundboardSoundUpdateEmoji(@Nonnull SoundboardSoundUpdateEmojiEvent event) {} + + // Voice channel effect events + public void onVoiceChannelEffectSend(@Nonnull VoiceChannelEffectSendEvent event) {} + // Entitlement events public void onEntitlementCreate(@Nonnull EntitlementCreateEvent event) {} public void onEntitlementUpdate(@Nonnull EntitlementUpdateEvent event) {} @@ -411,6 +431,8 @@ public void onGenericEmoji(@Nonnull GenericEmojiEvent event) {} public void onGenericEmojiUpdate(@Nonnull GenericEmojiUpdateEvent event) {} public void onGenericGuildSticker(@Nonnull GenericGuildStickerEvent event) {} public void onGenericGuildStickerUpdate(@Nonnull GenericGuildStickerUpdateEvent event) {} + public void onGenericSoundboardSound(@Nonnull GenericSoundboardSoundEvent event) {} + public void onGenericSoundboardSoundUpdate(@Nonnull GenericSoundboardSoundUpdateEvent event) {} public void onGenericEntitlement(@Nonnull GenericEntitlementEvent event) {} public void onGenericPermissionOverride(@Nonnull GenericPermissionOverrideEvent event) {} public void onGenericScheduledEventUpdate(@Nonnull GenericScheduledEventUpdateEvent event) {} diff --git a/src/main/java/net/dv8tion/jda/api/managers/SoundboardSoundManager.java b/src/main/java/net/dv8tion/jda/api/managers/SoundboardSoundManager.java new file mode 100644 index 0000000000..0382e085ae --- /dev/null +++ b/src/main/java/net/dv8tion/jda/api/managers/SoundboardSoundManager.java @@ -0,0 +1,154 @@ +/* + * Copyright 2015 Austin Keener, Michael Ritter, Florian Spieß, and the JDA contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.dv8tion.jda.api.managers; + +import net.dv8tion.jda.api.entities.Guild; +import net.dv8tion.jda.api.entities.SoundboardSound; +import net.dv8tion.jda.api.entities.emoji.Emoji; + +import javax.annotation.CheckReturnValue; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +/** + * Manager providing functionality to update one or more fields for a {@link SoundboardSound}. + * + *

Example + *

{@code
+ * manager.setVolume(0.33)
+ *        .setEmoji(null)
+ *        .queue();
+ * manager.reset(SoundboardSoundManager.VOLUME | SoundboardSoundManager.EMOJI)
+ *        .setVolume(1)
+ *        .setEmoji(Emoji.fromUnicode("🤔"))
+ *        .queue();
+ * }
+ * + * @see SoundboardSound#getManager() + */ +public interface SoundboardSoundManager extends Manager +{ + /** Used to reset the name field */ + long NAME = 1; + /** Used to reset the volume field */ + long VOLUME = 1 << 1; + /** Used to reset the emoji field */ + long EMOJI = 1 << 2; + + /** + * Resets the fields specified by the provided bit-flag pattern. + * You can specify a combination by using a bitwise OR concat of the flag constants. + *
Example: {@code manager.reset(SoundboardSoundManager.VOLUME | SoundboardSoundManager.EMOJI);} + * + *

Flag Constants: + *

    + *
  • {@link #NAME}
  • + *
  • {@link #VOLUME}
  • + *
  • {@link #EMOJI}
  • + *
+ * + * @param fields + * Integer value containing the flags to reset. + * + * @return SoundboardSoundManager for chaining convenience + */ + @Nonnull + @Override + @CheckReturnValue + SoundboardSoundManager reset(long fields); + + /** + * Resets the fields specified by the provided bit-flag patterns. + *
Example: {@code manager.reset(SoundboardSoundManager.VOLUME | SoundboardSoundManager.EMOJI);} + * + *

Flag Constants: + *

    + *
  • {@link #NAME}
  • + *
  • {@link #VOLUME}
  • + *
  • {@link #EMOJI}
  • + *
+ * + * @param fields + * Integer values containing the flags to reset. + * + * @return SoundboardSoundManager for chaining convenience + */ + @Nonnull + @Override + @CheckReturnValue + SoundboardSoundManager reset(long... fields); + + /** + * The target {@link SoundboardSound} for this manager + * + * @return The target SoundboardSound + */ + @Nonnull + SoundboardSound getSoundboardSound(); + + /** + * The {@link Guild} this Manager's {@link SoundboardSound} is in. + * + * @return The parent {@link Guild} + */ + @Nonnull + Guild getGuild(); + + /** + * Sets the name of the selected {@link SoundboardSound}. + * + *

A role name must not be {@code null} nor less than 2 characters or more than 32 characters long! + * + * @param name + * The new name for the selected {@link SoundboardSound} + * + * @throws IllegalArgumentException + * If the provided name is {@code null} or not between 2-32 characters long + * + * @return SoundboardSoundManager for chaining convenience + */ + @Nonnull + @CheckReturnValue + SoundboardSoundManager setName(@Nonnull String name); + + /** + * Sets the volume of the selected {@link SoundboardSound}. + * + * @param volume + * The new volume for the selected {@link SoundboardSound} + * + * @throws IllegalArgumentException + * If the provided volume is not between 0-1 + * + * @return SoundboardSoundManager for chaining convenience + */ + @Nonnull + @CheckReturnValue + SoundboardSoundManager setVolume(double volume); + + /** + * Sets the emoji of the selected {@link SoundboardSound}. + * + * @param emoji + * The new emoji for the selected {@link SoundboardSound}, can be {@code null} + * + * @return SoundboardSoundManager for chaining convenience + */ + @Nonnull + @CheckReturnValue + SoundboardSoundManager setEmoji(@Nullable Emoji emoji); +} diff --git a/src/main/java/net/dv8tion/jda/api/requests/ErrorResponse.java b/src/main/java/net/dv8tion/jda/api/requests/ErrorResponse.java index 024829c5a0..434a6c7a9d 100644 --- a/src/main/java/net/dv8tion/jda/api/requests/ErrorResponse.java +++ b/src/main/java/net/dv8tion/jda/api/requests/ErrorResponse.java @@ -54,6 +54,7 @@ public enum ErrorResponse UNKNOWN_WEBHOOK( 10015, "Unknown Webhook"), UNKNOWN_WEBHOOK_SERVICE( 10016, "Unknown Webhook Service"), UNKNOWN_SESSION( 10020, "Unknown session"), + UNKNOWN_ASSET( 10021, "Unknown Asset"), UNKNOWN_BAN( 10026, "Unknown Ban"), UNKNOWN_SKU( 10027, "Unknown SKU"), UNKNOWN_STORE_LISTING( 10028, "Unknown Store Listing"), @@ -79,6 +80,7 @@ public enum ErrorResponse UNKNOWN_SCHEDULED_EVENT( 10070, "Unknown Scheduled Event"), UNKNOWN_SCHEDULED_EVENT_USER( 10071, "Unknown Scheduled Event User"), UNKNOWN_TAG( 10087, "Unknown Tag"), + UNKNOWN_SOUND( 10097, "Unknown sound"), BOTS_NOT_ALLOWED( 20001, "Bots cannot use this endpoint"), ONLY_BOTS_ALLOWED( 20002, "Only bots can use this endpoint"), EXPLICIT_CONTENT_CANNOT_SEND_TO_RECIPIENT(20009, "Explicit content cannot be sent to the desired recipient(s)"), @@ -114,6 +116,7 @@ public enum ErrorResponse MAX_STICKERS( 30039, "Maximum number of stickers reached"), MAX_PRUNE_REQUESTS( 30040, "Maximum number of prune requests has been reached. Try again later"), MAX_GUILD_WIDGET_UPDATES( 30042, "Maximum number of guild widget settings updates has been reached. Try again later"), + MAX_SOUNDBOARD_SOUNDS( 30045, "Maximum number of soundboard sounds reached"), MAX_OLD_MESSAGE_EDITS( 30046, "Maximum number of edits to messages older than 1 hour reached. Try again later"), MAX_PINNED_THREADS_IN_FORUM( 30047, "Maximum number of pinned threads in a forum channel has been reached"), MAX_FORUM_TAGS( 30048, "Maximum number of tags in a forum channel has been reached"), @@ -192,19 +195,25 @@ public enum ErrorResponse SERVER_MONETIZATION_DISABLED( 50097, "This server needs monetization enabled in order to perform this action"), SERVER_NOT_ENOUGH_BOOSTS( 50101, "This server needs more boosts to perform this action"), INVALID_REQUEST_BODY( 50109, "The request body contains invalid JSON."), + INVALID_FILE( 50110, "The provided file is invalid."), + INVALID_FILE_TYPE( 50123, "The provided file type is invalid."), + INVALID_FILE_EXCEEDS_MAXIMUM_LENGTH( 50124, "The provided file duration exceeds maximum of 5.2 seconds."), OWNER_CANNOT_BE_PENDING( 50131, "Owner cannot be pending member"), OWNER_TRANSFER_TO_BOT( 50132, "Ownership cannot be transferred to a bot user"), CANNOT_RESIZE_BELOW_MAXIMUM( 50138, "Failed to resize asset below the maximum size: 262144"), MIXED_PREMIUM_ROLES_FOR_EMOJI( 50144, "Cannot mix subscription and non subscription roles for an emoji"), ILLEGAL_EMOJI_CONVERSION( 50145, "Cannot convert between premium emoji and normal emoji"), UNKNOWN_UPLOADED_FILE( 50146, "Uploaded file not found"), + INVALID_EMOJI( 50151, "The specified emoji is invalid"), VOICE_MESSAGE_ADDITIONAL_CONTENT( 50159, "Voice messages do not support additional content"), VOICE_MESSAGE_TOO_MANY_AUDIO_ATTACHMENTS( 50160, "Voice messages must have a single audio attachment"), VOICE_MESSAGE_MISSING_METADATA( 50161, "Voice messages must have supporting metadata"), CANNOT_EDIT_VOICE_MESSAGE( 50162, "Voice messages cannot be edited"), CANNOT_DELETE_GUILD_INTEGRATION( 50163, "Cannot delete guild subscription integration"), + CANNOT_SEND_VOICE_EFFECT( 50167, "Cannot send voice effect when user is server muted, deafened or suppressed"), CANNOT_SEND_VOICE_MESSAGE( 50173, "You cannot send voice messages in this channel"), USER_MUST_BE_VERIFIED( 50178, "The user account must first be verified"), + INVALID_FILE_DURATION( 50192, "The provided file does not have a valid duration"), CANNOT_SEND_STICKER( 50600, "You do not have permission to send this sticker"), MFA_NOT_ENABLED( 60003, "MFA auth required but not enabled"), NO_USER_WITH_TAG_EXISTS( 80004, "No users with DiscordTag exist"), 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 b92d493cf2..00d36fd3bd 100644 --- a/src/main/java/net/dv8tion/jda/api/requests/Route.java +++ b/src/main/java/net/dv8tion/jda/api/requests/Route.java @@ -178,6 +178,19 @@ public static class Stickers public static final Route LIST_PACKS = new Route(GET, "sticker-packs"); } + public static class SoundboardSounds + { + public static final Route SEND_SOUNDBOARD_SOUND = new Route(POST, "channels/{channel_id}/send-soundboard-sound"); + + public static final Route LIST_DEFAULT_SOUNDBOARD_SOUNDS = new Route(GET, "soundboard-default-sounds"); + + public static final Route LIST_GUILD_SOUNDBOARD_SOUNDS = new Route(GET, "guilds/{guild_id}/soundboard-sounds"); + public static final Route GET_GUILD_SOUNDBOARD_SOUNDS = new Route(GET, "guilds/{guild_id}/soundboard-sounds/{sound_id}"); + public static final Route CREATE_GUILD_SOUNDBOARD_SOUNDS = new Route(POST, "guilds/{guild_id}/soundboard-sounds"); + public static final Route MODIFY_GUILD_SOUNDBOARD_SOUNDS = new Route(PATCH, "guilds/{guild_id}/soundboard-sounds/{sound_id}"); + public static final Route DELETE_GUILD_SOUNDBOARD_SOUNDS = new Route(DELETE, "guilds/{guild_id}/soundboard-sounds/{sound_id}"); + } + public static class Webhooks { public static final Route GET_WEBHOOK = new Route(GET, "webhooks/{webhook_id}"); diff --git a/src/main/java/net/dv8tion/jda/api/requests/restaction/SoundboardSoundCreateAction.java b/src/main/java/net/dv8tion/jda/api/requests/restaction/SoundboardSoundCreateAction.java new file mode 100644 index 0000000000..235ccdff14 --- /dev/null +++ b/src/main/java/net/dv8tion/jda/api/requests/restaction/SoundboardSoundCreateAction.java @@ -0,0 +1,80 @@ +/* + * Copyright 2015 Austin Keener, Michael Ritter, Florian Spieß, and the JDA contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.dv8tion.jda.api.requests.restaction; + +import net.dv8tion.jda.api.entities.SoundboardSound; +import net.dv8tion.jda.api.entities.emoji.Emoji; +import net.dv8tion.jda.api.requests.RestAction; + +import javax.annotation.CheckReturnValue; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.util.concurrent.TimeUnit; +import java.util.function.BooleanSupplier; + +/** + * Specialized {@link RestAction} used to create a {@link SoundboardSound} in a guild. + */ +public interface SoundboardSoundCreateAction extends AuditableRestAction +{ + @Nonnull + @Override + @CheckReturnValue + SoundboardSoundCreateAction timeout(long timeout, @Nonnull TimeUnit unit); + + @Nonnull + @Override + @CheckReturnValue + SoundboardSoundCreateAction deadline(long timestamp); + + @Nonnull + @Override + @CheckReturnValue + SoundboardSoundCreateAction setCheck(@Nullable BooleanSupplier checks); + + @Nonnull + @Override + @CheckReturnValue + SoundboardSoundCreateAction addCheck(@Nonnull BooleanSupplier checks); + + /** + * Sets the volume of the soundboard sound being created. + * + * @param volume + * The new volume + * + * @throws IllegalArgumentException + * If the provided volume is not between 0-1 + * + * @return This action for chaining convenience + */ + @Nonnull + @CheckReturnValue + SoundboardSoundCreateAction setVolume(double volume); + + /** + * Sets the emoji of the soundboard sound being created. + * + * @param emoji + * The new emoji, can be {@code null} + * + * @return This action for chaining convenience + */ + @Nonnull + @CheckReturnValue + SoundboardSoundCreateAction setEmoji(@Nullable Emoji emoji); +} diff --git a/src/main/java/net/dv8tion/jda/api/utils/cache/CacheFlag.java b/src/main/java/net/dv8tion/jda/api/utils/cache/CacheFlag.java index b9121f8da6..7555d0fd34 100644 --- a/src/main/java/net/dv8tion/jda/api/utils/cache/CacheFlag.java +++ b/src/main/java/net/dv8tion/jda/api/utils/cache/CacheFlag.java @@ -58,6 +58,12 @@ public enum CacheFlag *

Requires {@link net.dv8tion.jda.api.requests.GatewayIntent#GUILD_EXPRESSIONS GUILD_EXPRESSIONS} intent to be enabled. */ STICKER(GatewayIntent.GUILD_EXPRESSIONS), + /** + * Enables cache for {@link Guild#getSoundboardSoundCache()} + * + *

Requires {@link net.dv8tion.jda.api.requests.GatewayIntent#GUILD_EMOJIS_AND_STICKERS GUILD_EMOJIS_AND_STICKERS} intent to be enabled. + */ + SOUNDBOARD_SOUNDS(GatewayIntent.GUILD_EMOJIS_AND_STICKERS), /** * Enables cache for {@link Member#getOnlineStatus(net.dv8tion.jda.api.entities.ClientType) Member.getOnlineStatus(ClientType)} * diff --git a/src/main/java/net/dv8tion/jda/internal/JDAImpl.java b/src/main/java/net/dv8tion/jda/internal/JDAImpl.java index 9c1be36130..6f3ccce671 100644 --- a/src/main/java/net/dv8tion/jda/internal/JDAImpl.java +++ b/src/main/java/net/dv8tion/jda/internal/JDAImpl.java @@ -84,6 +84,7 @@ import net.dv8tion.jda.internal.utils.config.ThreadingConfig; import okhttp3.OkHttpClient; import okhttp3.RequestBody; +import org.jetbrains.annotations.Unmodifiable; import org.slf4j.Logger; import org.slf4j.MDC; @@ -771,6 +772,21 @@ public RestAction> retrieveNitroStickerPacks() }); } + @Nonnull + @Override + public RestAction<@Unmodifiable List> retrieveDefaultSoundboardSounds() + { + final Route.CompiledRoute route = Route.SoundboardSounds.LIST_DEFAULT_SOUNDBOARD_SOUNDS.compile(); + return new RestActionImpl<>(this, route, (response, request) -> + { + final DataArray array = response.getArray(); + List sounds = new ArrayList<>(array.length()); + for (int i = 0; i < array.length(); i++) + sounds.add(entityBuilder.createSoundboardSound(array.getObject(i))); + return Collections.unmodifiableList(sounds); + }); + } + @Nonnull @Override public SnowflakeCacheView getScheduledEventCache() 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 7fb94a3abe..35bdb539d3 100644 --- a/src/main/java/net/dv8tion/jda/internal/entities/EntityBuilder.java +++ b/src/main/java/net/dv8tion/jda/internal/entities/EntityBuilder.java @@ -182,6 +182,23 @@ public static EmojiUnion createEmoji(DataObject emoji, String nameKey, String id return new CustomEmojiImpl(emoji.getString(nameKey, ""), id, emoji.getBoolean("animated")); } + public SoundboardSound createSoundboardSound(DataObject json) + { + String name = json.getString("name"); + long id = json.getLong("sound_id"); + double volume = json.getDouble("volume"); + EmojiUnion emoji; + if (!json.isNull("emoji_name") || !json.isNull("emoji_id")) + emoji = createEmoji(json, "emoji_name", "emoji_id"); + else + emoji = null; + Guild guild = getJDA().getGuildById(json.getLong("guild_id", 0)); + boolean available = json.getBoolean("available"); + User user = json.optObject("user").map(this::createUser).orElse(null); + + return new SoundboardSoundImpl(api, id, name, volume, emoji, guild, available, user); + } + private void createGuildEmojiPass(GuildImpl guildObj, DataArray array) { if (!getJDA().isCacheFlagSet(CacheFlag.EMOJI)) @@ -257,6 +274,30 @@ private void createGuildStickerPass(GuildImpl guildObj, DataArray array) } } + private void createGuildSoundboardSoundPass(GuildImpl guildObj, DataArray array) + { + if (!getJDA().isCacheFlagSet(CacheFlag.SOUNDBOARD_SOUNDS)) + return; + SnowflakeCacheViewImpl soundboardView = guildObj.getSoundboardSoundsView(); + try (UnlockHook hook = soundboardView.writeLock()) + { + TLongObjectMap soundboardMap = soundboardView.getMap(); + for (int i = 0; i < array.length(); i++) + { + DataObject object = array.getObject(i); + if (object.isNull("sound_id")) + { + LOG.error("Received GUILD_CREATE with a sound with a null ID. GuildId: {} JSON: {}", + guildObj.getId(), object); + continue; + } + + SoundboardSound sound = createSoundboardSound(object); + soundboardMap.put(sound.getIdLong(), sound); + } + } + } + public GuildImpl createGuild(long guildId, DataObject guildJson, TLongObjectMap members, int memberCount) { final GuildImpl guildObj = new GuildImpl(getJDA(), guildId); @@ -274,6 +315,7 @@ public GuildImpl createGuild(long guildId, DataObject guildJson, TLongObjectMap< final DataArray emojisArray = guildJson.getArray("emojis"); final DataArray voiceStateArray = guildJson.getArray("voice_states"); final Optional stickersArray = guildJson.optArray("stickers"); + final Optional soundboardSoundsArray = guildJson.optArray("soundboard_sounds"); final Optional featuresArray = guildJson.optArray("features"); final Optional presencesArray = guildJson.optArray("presences"); final long ownerId = guildJson.getUnsignedLong("owner_id", 0L); @@ -397,6 +439,7 @@ public GuildImpl createGuild(long guildId, DataObject guildJson, TLongObjectMap< createScheduledEventPass(guildObj, scheduledEventsArray); createGuildEmojiPass(guildObj, emojisArray); stickersArray.ifPresent(stickers -> createGuildStickerPass(guildObj, stickers)); + soundboardSoundsArray.ifPresent(stickers -> createGuildSoundboardSoundPass(guildObj, stickers)); guildJson.optArray("stage_instances") .map(arr -> arr.stream(DataArray::getObject)) .ifPresent(list -> list.forEach(it -> createStageInstance(guildObj, it))); 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 511ae41d7d..77b443aee6 100644 --- a/src/main/java/net/dv8tion/jda/internal/entities/GuildImpl.java +++ b/src/main/java/net/dv8tion/jda/internal/entities/GuildImpl.java @@ -108,6 +108,7 @@ public class GuildImpl implements Guild private final SortedSnowflakeCacheViewImpl roleCache = new SortedSnowflakeCacheViewImpl<>(Role.class, Role::getName, Comparator.reverseOrder()); private final SnowflakeCacheViewImpl emojicache = new SnowflakeCacheViewImpl<>(RichCustomEmoji.class, RichCustomEmoji::getName); private final SnowflakeCacheViewImpl stickerCache = new SnowflakeCacheViewImpl<>(GuildSticker.class, GuildSticker::getName); + private final SnowflakeCacheViewImpl soundboardCache = new SnowflakeCacheViewImpl<>(SoundboardSound.class, SoundboardSound::getName); private final MemberCacheViewImpl memberCache = new MemberCacheViewImpl(); private final CacheView.SimpleCacheView memberPresences; @@ -825,6 +826,13 @@ public SnowflakeCacheView getStickerCache() return stickerCache; } + @Nonnull + @Override + public SnowflakeCacheViewImpl getSoundboardSoundCache() + { + return soundboardCache; + } + @Nonnull @Override public List getChannels(boolean includeHidden) @@ -1960,6 +1968,18 @@ public AuditableRestAction deleteSticker(@Nonnull StickerSnowflake id) return new AuditableRestActionImpl<>(api, route); } + @Nonnull + @Override + public SoundboardSoundCreateAction createSoundboardSound(@Nonnull String name, @Nonnull FileUpload file) + { + checkPermission(Permission.CREATE_GUILD_EXPRESSIONS); + Checks.notNull(name, "Name"); + Checks.check(name.length() >= 2 && name.length() <= 32, "Name must be between 2 and 32 characters"); + Checks.notNull(file, "File"); + Route.CompiledRoute route = Route.SoundboardSounds.CREATE_GUILD_SOUNDBOARD_SOUNDS.compile(getId()); + return new SoundboardSoundCreateActionImpl(getJDA(), route, name, file); + } + @Nonnull @Override public ChannelOrderAction modifyCategoryPositions() @@ -2311,6 +2331,11 @@ public SnowflakeCacheViewImpl getStickersView() return stickerCache; } + public SnowflakeCacheViewImpl getSoundboardSoundsView() + { + return soundboardCache; + } + public MemberCacheViewImpl getMembersView() { return memberCache; diff --git a/src/main/java/net/dv8tion/jda/internal/entities/SoundboardSoundImpl.java b/src/main/java/net/dv8tion/jda/internal/entities/SoundboardSoundImpl.java new file mode 100644 index 0000000000..6b993e2566 --- /dev/null +++ b/src/main/java/net/dv8tion/jda/internal/entities/SoundboardSoundImpl.java @@ -0,0 +1,211 @@ +/* + * Copyright 2015 Austin Keener, Michael Ritter, Florian Spieß, and the JDA contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.dv8tion.jda.internal.entities; + +import net.dv8tion.jda.api.JDA; +import net.dv8tion.jda.api.Permission; +import net.dv8tion.jda.api.entities.*; +import net.dv8tion.jda.api.entities.channel.concrete.VoiceChannel; +import net.dv8tion.jda.api.entities.emoji.EmojiUnion; +import net.dv8tion.jda.api.exceptions.InsufficientPermissionException; +import net.dv8tion.jda.api.managers.SoundboardSoundManager; +import net.dv8tion.jda.api.requests.RestAction; +import net.dv8tion.jda.api.requests.Route; +import net.dv8tion.jda.api.utils.data.DataObject; +import net.dv8tion.jda.internal.managers.SoundboardSoundManagerImpl; +import net.dv8tion.jda.internal.requests.RestActionImpl; +import net.dv8tion.jda.internal.requests.restaction.AuditableRestActionImpl; +import net.dv8tion.jda.internal.utils.Checks; +import net.dv8tion.jda.internal.utils.EntityString; +import net.dv8tion.jda.internal.utils.PermissionUtil; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.util.Objects; + +public class SoundboardSoundImpl implements SoundboardSound +{ + private final JDA api; + private final long id; + private final String name; + private final double volume; + private final EmojiUnion emoji; + private final Guild guild; + private final boolean available; + private final User user; + + public SoundboardSoundImpl(JDA api, long id, String name, double volume, EmojiUnion emoji, Guild guild, boolean available, User user) + { + this.api = api; + this.id = id; + this.name = name; + this.volume = volume; + this.emoji = emoji; + this.guild = guild; + this.available = available; + this.user = user; + } + + @Override + public long getIdLong() + { + return this.id; + } + + @Nonnull + @Override + public JDA getJDA() + { + return api; + } + + @Nonnull + @Override + public String getName() + { + return name; + } + + @Override + public double getVolume() + { + return volume; + } + + @Nullable + @Override + public EmojiUnion getEmoji() + { + return emoji; + } + + @Nullable + @Override + public Guild getGuild() + { + return guild; + } + + public boolean isAvailable() + { + return available; + } + + @Nullable + @Override + public User getUser() + { + return user; + } + + @Nonnull + @Override + public RestAction sendTo(VoiceChannel channel) + { + Checks.notNull(channel, "Channel"); + + // Check available + if (!isAvailable()) + throw new IllegalStateException("Cannot send an unavailable sound"); + + // Check speak permissions + final Guild targetGuild = channel.getGuild(); + if (!targetGuild.getSelfMember().hasPermission(channel, Permission.VOICE_SPEAK)) + throw new InsufficientPermissionException(channel, Permission.VOICE_SPEAK); + if (!targetGuild.getSelfMember().hasPermission(channel, Permission.VOICE_USE_SOUNDBOARD)) + throw new InsufficientPermissionException(channel, Permission.VOICE_USE_SOUNDBOARD); + + if (!targetGuild.equals(getGuild()) && !targetGuild.getSelfMember().hasPermission(channel, Permission.VOICE_USE_EXTERNAL_SOUNDS)) + throw new InsufficientPermissionException(channel, Permission.VOICE_USE_EXTERNAL_SOUNDS); + + // Check voice state if possible + if (!channel.equals(targetGuild.getAudioManager().getConnectedChannel())) + throw new IllegalStateException("You must be connected to the voice channel you want to send the sound effect to"); + final GuildVoiceState voiceState = targetGuild.getSelfMember().getVoiceState(); + if (voiceState != null) + { + if (voiceState.isSuppressed()) + throw new IllegalStateException("You cannot send sound effects while you are being suppressed"); + if (voiceState.isDeafened()) + throw new IllegalStateException("You cannot send sound effects while you are deafened"); + if (voiceState.isMuted()) + throw new IllegalStateException("You cannot send sound effects while you are muted"); + } + + // Send + DataObject data = DataObject.empty() + .put("sound_id", getId()); + + if (guild != null) + data.put("source_guild_id", guild.getId()); + + return new RestActionImpl<>(api, Route.SoundboardSounds.SEND_SOUNDBOARD_SOUND.compile(channel.getId()), data); + } + + @Nonnull + @Override + public RestAction delete() + { + Checks.check(getGuild() != null, "Cannot delete default soundboard sounds"); + checkEditPermissions(); + + final Route.CompiledRoute route = Route.SoundboardSounds.DELETE_GUILD_SOUNDBOARD_SOUNDS.compile(guild.getId(), getId()); + return new AuditableRestActionImpl<>(api, route); + } + + @Nonnull + @Override + public SoundboardSoundManager getManager() { + Checks.check(getGuild() != null, "Cannot delete default soundboard sounds"); + checkEditPermissions(); + + return new SoundboardSoundManagerImpl(this); + } + + private void checkEditPermissions() + { + final Member selfMember = guild.getSelfMember(); + if (Objects.equals(getUser(), selfMember.getUser())) + PermissionUtil.requireAnyPermission(selfMember, Permission.CREATE_GUILD_EXPRESSIONS, Permission.MANAGE_GUILD_EXPRESSIONS); + else if (!selfMember.hasPermission(Permission.MANAGE_GUILD_EXPRESSIONS)) + throw new InsufficientPermissionException(guild, Permission.MANAGE_GUILD_EXPRESSIONS); + } + + @Override + public int hashCode() + { + return Long.hashCode(id); + } + + @Override + public boolean equals(Object obj) + { + if (obj == this) + return true; + if (!(obj instanceof SoundboardSoundImpl)) + return false; + return ((SoundboardSoundImpl) obj).getIdLong() == this.id; + } + + @Override + public String toString() + { + return new EntityString(this) + .setName(name) + .toString(); + } +} diff --git a/src/main/java/net/dv8tion/jda/internal/handle/EventCache.java b/src/main/java/net/dv8tion/jda/internal/handle/EventCache.java index 45e8cd5847..3abe6d8007 100644 --- a/src/main/java/net/dv8tion/jda/internal/handle/EventCache.java +++ b/src/main/java/net/dv8tion/jda/internal/handle/EventCache.java @@ -129,7 +129,7 @@ public synchronized void clear(Type type, long id) public enum Type { - USER, MEMBER, GUILD, CHANNEL, ROLE, RELATIONSHIP, CALL, SCHEDULED_EVENT + USER, MEMBER, GUILD, CHANNEL, ROLE, SOUNDBOARD_SOUND, RELATIONSHIP, CALL, SCHEDULED_EVENT } private class CacheNode diff --git a/src/main/java/net/dv8tion/jda/internal/handle/GuildSoundboardSoundCreateHandler.java b/src/main/java/net/dv8tion/jda/internal/handle/GuildSoundboardSoundCreateHandler.java new file mode 100644 index 0000000000..b5b508b855 --- /dev/null +++ b/src/main/java/net/dv8tion/jda/internal/handle/GuildSoundboardSoundCreateHandler.java @@ -0,0 +1,62 @@ +/* + * Copyright 2015 Austin Keener, Michael Ritter, Florian Spieß, and the JDA contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.dv8tion.jda.internal.handle; + +import net.dv8tion.jda.api.entities.SoundboardSound; +import net.dv8tion.jda.api.events.soundboard.SoundboardSoundCreateEvent; +import net.dv8tion.jda.api.utils.cache.CacheFlag; +import net.dv8tion.jda.api.utils.data.DataObject; +import net.dv8tion.jda.internal.JDAImpl; +import net.dv8tion.jda.internal.entities.GuildImpl; +import net.dv8tion.jda.internal.utils.UnlockHook; +import net.dv8tion.jda.internal.utils.cache.SnowflakeCacheViewImpl; + +public class GuildSoundboardSoundCreateHandler extends SocketHandler +{ + public GuildSoundboardSoundCreateHandler(JDAImpl api) + { + super(api); + } + + @Override + protected Long handleInternally(DataObject content) + { + if (!getJDA().isCacheFlagSet(CacheFlag.SOUNDBOARD_SOUNDS)) + return null; + final long guildId = content.getLong("guild_id"); + if (getJDA().getGuildSetupController().isLocked(guildId)) + return guildId; + + GuildImpl guild = (GuildImpl) getJDA().getGuildById(guildId); + if (guild == null) + { + getJDA().getEventCache().cache(EventCache.Type.GUILD, guildId, responseNumber, allContent, this::handle); + return null; + } + + final SoundboardSound soundboardSound = api.getEntityBuilder().createSoundboardSound(content); + final SnowflakeCacheViewImpl soundboardSoundsView = guild.getSoundboardSoundsView(); + try (UnlockHook unlockHook = soundboardSoundsView.writeLock()) + { + soundboardSoundsView.getMap().put(soundboardSound.getIdLong(), soundboardSound); + } + + api.handleEvent(new SoundboardSoundCreateEvent(api, responseNumber, soundboardSound)); + + return null; + } +} diff --git a/src/main/java/net/dv8tion/jda/internal/handle/GuildSoundboardSoundDeleteHandler.java b/src/main/java/net/dv8tion/jda/internal/handle/GuildSoundboardSoundDeleteHandler.java new file mode 100644 index 0000000000..d669f27dc3 --- /dev/null +++ b/src/main/java/net/dv8tion/jda/internal/handle/GuildSoundboardSoundDeleteHandler.java @@ -0,0 +1,63 @@ +/* + * Copyright 2015 Austin Keener, Michael Ritter, Florian Spieß, and the JDA contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.dv8tion.jda.internal.handle; + +import net.dv8tion.jda.api.entities.SoundboardSound; +import net.dv8tion.jda.api.events.soundboard.SoundboardSoundDeleteEvent; +import net.dv8tion.jda.api.utils.cache.CacheFlag; +import net.dv8tion.jda.api.utils.data.DataObject; +import net.dv8tion.jda.internal.JDAImpl; +import net.dv8tion.jda.internal.entities.GuildImpl; +import net.dv8tion.jda.internal.utils.UnlockHook; +import net.dv8tion.jda.internal.utils.cache.SnowflakeCacheViewImpl; + +public class GuildSoundboardSoundDeleteHandler extends SocketHandler +{ + public GuildSoundboardSoundDeleteHandler(JDAImpl api) + { + super(api); + } + + @Override + protected Long handleInternally(DataObject content) + { + if (!getJDA().isCacheFlagSet(CacheFlag.SOUNDBOARD_SOUNDS)) + return null; + final long guildId = content.getLong("guild_id"); + if (getJDA().getGuildSetupController().isLocked(guildId)) + return guildId; + + GuildImpl guild = (GuildImpl) getJDA().getGuildById(guildId); + if (guild == null) + { + getJDA().getEventCache().cache(EventCache.Type.GUILD, guildId, responseNumber, allContent, this::handle); + return null; + } + + final SnowflakeCacheViewImpl soundboardSoundsView = guild.getSoundboardSoundsView(); + final SoundboardSound removedSound; + try (UnlockHook unlockHook = soundboardSoundsView.writeLock()) + { + removedSound = soundboardSoundsView.getMap().remove(content.getLong("sound_id")); + } + + if (removedSound != null) + api.handleEvent(new SoundboardSoundDeleteEvent(api, responseNumber, removedSound)); + + return null; + } +} diff --git a/src/main/java/net/dv8tion/jda/internal/handle/GuildSoundboardSoundUpdateHandler.java b/src/main/java/net/dv8tion/jda/internal/handle/GuildSoundboardSoundUpdateHandler.java new file mode 100644 index 0000000000..02875db1ba --- /dev/null +++ b/src/main/java/net/dv8tion/jda/internal/handle/GuildSoundboardSoundUpdateHandler.java @@ -0,0 +1,82 @@ +/* + * Copyright 2015 Austin Keener, Michael Ritter, Florian Spieß, and the JDA contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.dv8tion.jda.internal.handle; + +import net.dv8tion.jda.api.entities.SoundboardSound; +import net.dv8tion.jda.api.events.soundboard.update.SoundboardSoundUpdateEmojiEvent; +import net.dv8tion.jda.api.events.soundboard.update.SoundboardSoundUpdateNameEvent; +import net.dv8tion.jda.api.events.soundboard.update.SoundboardSoundUpdateVolumeEvent; +import net.dv8tion.jda.api.utils.cache.CacheFlag; +import net.dv8tion.jda.api.utils.data.DataObject; +import net.dv8tion.jda.internal.JDAImpl; +import net.dv8tion.jda.internal.entities.GuildImpl; +import net.dv8tion.jda.internal.utils.UnlockHook; +import net.dv8tion.jda.internal.utils.cache.SnowflakeCacheViewImpl; + +import java.util.Objects; + +public class GuildSoundboardSoundUpdateHandler extends SocketHandler +{ + public GuildSoundboardSoundUpdateHandler(JDAImpl api) + { + super(api); + } + + @Override + protected Long handleInternally(DataObject content) + { + if (!getJDA().isCacheFlagSet(CacheFlag.SOUNDBOARD_SOUNDS)) + return null; + final long guildId = content.getLong("guild_id"); + if (getJDA().getGuildSetupController().isLocked(guildId)) + return guildId; + + GuildImpl guild = (GuildImpl) getJDA().getGuildById(guildId); + if (guild == null) + { + getJDA().getEventCache().cache(EventCache.Type.GUILD, guildId, responseNumber, allContent, this::handle); + return null; + } + + SnowflakeCacheViewImpl soundboardSoundsView = guild.getSoundboardSoundsView(); + long soundId = content.getLong("sound_id"); + final SoundboardSound oldSoundboardSound = soundboardSoundsView.get(soundId); + if (oldSoundboardSound == null) + { + getJDA().getEventCache().cache(EventCache.Type.SOUNDBOARD_SOUND, soundId, responseNumber, allContent, this::handle); + EventCache.LOG.debug("Received a Guild Soundboard Sound Update for SoundboardSound that is not yet cached: {}", content); + return null; + } + + final SoundboardSound soundboardSound = api.getEntityBuilder().createSoundboardSound(content); + try (UnlockHook unlockHook = soundboardSoundsView.writeLock()) + { + soundboardSoundsView.getMap().put(soundboardSound.getIdLong(), soundboardSound); + } + + if (!Objects.equals(oldSoundboardSound.getName(), soundboardSound.getName())) + api.handleEvent(new SoundboardSoundUpdateNameEvent(api, responseNumber, soundboardSound, oldSoundboardSound.getName())); + + if (oldSoundboardSound.getVolume() != soundboardSound.getVolume()) + api.handleEvent(new SoundboardSoundUpdateVolumeEvent(api, responseNumber, soundboardSound, oldSoundboardSound.getVolume())); + + if (!Objects.equals(oldSoundboardSound.getEmoji(), soundboardSound.getEmoji())) + api.handleEvent(new SoundboardSoundUpdateEmojiEvent(api, responseNumber, soundboardSound, oldSoundboardSound.getEmoji())); + + return null; + } +} diff --git a/src/main/java/net/dv8tion/jda/internal/handle/GuildSoundboardSoundsUpdateHandler.java b/src/main/java/net/dv8tion/jda/internal/handle/GuildSoundboardSoundsUpdateHandler.java new file mode 100644 index 0000000000..ee76328d02 --- /dev/null +++ b/src/main/java/net/dv8tion/jda/internal/handle/GuildSoundboardSoundsUpdateHandler.java @@ -0,0 +1,61 @@ +/* + * Copyright 2015 Austin Keener, Michael Ritter, Florian Spieß, and the JDA contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.dv8tion.jda.internal.handle; + +import net.dv8tion.jda.api.utils.cache.CacheFlag; +import net.dv8tion.jda.api.utils.data.DataArray; +import net.dv8tion.jda.api.utils.data.DataObject; +import net.dv8tion.jda.internal.JDAImpl; +import net.dv8tion.jda.internal.entities.GuildImpl; + +public class GuildSoundboardSoundsUpdateHandler extends SocketHandler +{ + public GuildSoundboardSoundsUpdateHandler(JDAImpl api) + { + super(api); + } + + @Override + protected Long handleInternally(DataObject content) + { + if (!getJDA().isCacheFlagSet(CacheFlag.SOUNDBOARD_SOUNDS)) + return null; + final long guildId = content.getLong("guild_id"); + if (getJDA().getGuildSetupController().isLocked(guildId)) + return guildId; + + GuildImpl guild = (GuildImpl) getJDA().getGuildById(guildId); + if (guild == null) + { + getJDA().getEventCache().cache(EventCache.Type.GUILD, guildId, responseNumber, allContent, this::handle); + return null; + } + + SocketHandler handler = getJDA().getClient().getHandlers().get("GUILD_SOUNDBOARD_SOUND_UPDATE"); + DataArray array = content.getArray("soundboard_sounds"); + for (int i = 0; i < array.length(); i++) + { + handler.handle(responseNumber, DataObject.empty() + .put("t", "GUILD_SOUNDBOARD_SOUND_UPDATE") + .put("d", array.getObject(i) + .put("guild_id", content.getLong("guild_id")) //Just in case + )); + } + + return null; + } +} diff --git a/src/main/java/net/dv8tion/jda/internal/handle/VoiceChannelEffectSendHandler.java b/src/main/java/net/dv8tion/jda/internal/handle/VoiceChannelEffectSendHandler.java new file mode 100644 index 0000000000..dc5620df1e --- /dev/null +++ b/src/main/java/net/dv8tion/jda/internal/handle/VoiceChannelEffectSendHandler.java @@ -0,0 +1,79 @@ +/* + * Copyright 2015 Austin Keener, Michael Ritter, Florian Spieß, and the JDA contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.dv8tion.jda.internal.handle; + +import net.dv8tion.jda.api.entities.SoundboardSound; +import net.dv8tion.jda.api.entities.User; +import net.dv8tion.jda.api.entities.channel.VoiceChannelEffect; +import net.dv8tion.jda.api.entities.channel.concrete.VoiceChannel; +import net.dv8tion.jda.api.entities.emoji.EmojiUnion; +import net.dv8tion.jda.api.events.channel.VoiceChannelEffectSendEvent; +import net.dv8tion.jda.api.utils.data.DataObject; +import net.dv8tion.jda.internal.JDAImpl; +import net.dv8tion.jda.internal.entities.EntityBuilder; +import net.dv8tion.jda.internal.entities.GuildImpl; + +public class VoiceChannelEffectSendHandler extends SocketHandler +{ + public VoiceChannelEffectSendHandler(JDAImpl api) + { + super(api); + } + + @Override + protected Long handleInternally(DataObject content) + { + final long guildId = content.getLong("guild_id"); + if (getJDA().getGuildSetupController().isLocked(guildId)) + return guildId; + + GuildImpl guild = (GuildImpl) getJDA().getGuildById(guildId); + if (guild == null) + { + getJDA().getEventCache().cache(EventCache.Type.GUILD, guildId, responseNumber, allContent, this::handle); + return null; + } + + final long channelId = content.getLong("channel_id"); + VoiceChannel channel = guild.getVoiceChannelById(channelId); + if (channel == null) + { + getJDA().getEventCache().cache(EventCache.Type.CHANNEL, channelId, responseNumber, allContent, this::handle); + return null; + } + + User user = api.getUserById(content.getString("user_id")); + EmojiUnion emoji = content.optObject("emoji").map(EntityBuilder::createEmoji).orElse(null); + VoiceChannelEffect.Animation animation = content.opt("animation_type") + .map(rawAnimationType -> + { + long animationId = content.getLong("animation_id"); + VoiceChannelEffect.Animation.Type type = VoiceChannelEffect.Animation.Type.fromValue(Integer.parseInt(rawAnimationType.toString())); + return new VoiceChannelEffect.Animation(animationId, type); + }) + .orElse(null); + SoundboardSound soundboardSound = content.opt("sound_id") + .map(soundId -> guild.getSoundboardSoundById(soundId.toString())) + .orElse(null); + + VoiceChannelEffect effect = new VoiceChannelEffect(channel, user, emoji, animation, soundboardSound); + + api.handleEvent(new VoiceChannelEffectSendEvent(api, responseNumber, effect)); + + return null; + } +} diff --git a/src/main/java/net/dv8tion/jda/internal/managers/SoundboardSoundManagerImpl.java b/src/main/java/net/dv8tion/jda/internal/managers/SoundboardSoundManagerImpl.java new file mode 100644 index 0000000000..f1fcb9ae73 --- /dev/null +++ b/src/main/java/net/dv8tion/jda/internal/managers/SoundboardSoundManagerImpl.java @@ -0,0 +1,153 @@ +/* + * Copyright 2015 Austin Keener, Michael Ritter, Florian Spieß, and the JDA contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.dv8tion.jda.internal.managers; + +import net.dv8tion.jda.api.entities.Guild; +import net.dv8tion.jda.api.entities.SoundboardSound; +import net.dv8tion.jda.api.entities.emoji.CustomEmoji; +import net.dv8tion.jda.api.entities.emoji.Emoji; +import net.dv8tion.jda.api.managers.SoundboardSoundManager; +import net.dv8tion.jda.api.requests.Route; +import net.dv8tion.jda.api.utils.data.DataObject; +import net.dv8tion.jda.internal.utils.Checks; +import okhttp3.RequestBody; + +import javax.annotation.CheckReturnValue; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +public class SoundboardSoundManagerImpl extends ManagerBase implements SoundboardSoundManager +{ + private SoundboardSound soundboardSound; + private String name; + private double volume; + private Emoji emoji; + + public SoundboardSoundManagerImpl(SoundboardSound soundboardSound) + { + super(soundboardSound.getJDA(), Route.SoundboardSounds.MODIFY_GUILD_SOUNDBOARD_SOUNDS.compile(soundboardSound.getGuild().getId(), soundboardSound.getId())); + this.soundboardSound = soundboardSound; + } + + @Nonnull + @Override + @SuppressWarnings("DataFlowIssue") + public Guild getGuild() + { + return soundboardSound.getGuild(); + } + + @Nonnull + @Override + public SoundboardSound getSoundboardSound() + { + final SoundboardSound soundboardSound = getGuild().getSoundboardSoundById(this.soundboardSound.getId()); + if (soundboardSound != null) + this.soundboardSound = soundboardSound; + return this.soundboardSound; + } + + @Nonnull + @Override + @CheckReturnValue + public SoundboardSoundManagerImpl reset(long fields) + { + super.reset(fields); + if ((fields & NAME) == NAME) + this.name = null; + if ((fields & VOLUME) == VOLUME) + this.volume = 1; + if ((fields & EMOJI) == EMOJI) + this.emoji = null; + return this; + } + + @Nonnull + @Override + @CheckReturnValue + public SoundboardSoundManagerImpl reset(long... fields) + { + super.reset(fields); + return this; + } + + @Nonnull + @Override + @CheckReturnValue + public SoundboardSoundManagerImpl reset() + { + super.reset(); + this.name = null; + this.volume = 1; + this.emoji = null; + return this; + } + + @Nonnull + @Override + public SoundboardSoundManagerImpl setName(@Nonnull String name) + { + Checks.notNull(name, "name"); + Checks.check(name.length() >= 2 && name.length() <= 32, "Name must be between 2 and 32 characters"); + this.name = name; + set |= NAME; + return this; + } + + @Nonnull + @Override + public SoundboardSoundManagerImpl setVolume(double volume) + { + Checks.check(volume >= 0 && volume <= 1, "Volume must be between 0 and 1"); + this.volume = volume; + set |= VOLUME; + return this; + } + + @Nonnull + @Override + public SoundboardSoundManagerImpl setEmoji(@Nullable Emoji emoji) + { + this.emoji = emoji; + set |= EMOJI; + return this; + } + + @Override + protected RequestBody finalizeData() + { + DataObject object = DataObject.empty().put("name", getSoundboardSound().getName()); + if (shouldUpdate(NAME)) + object.put("name", name); + if (shouldUpdate(VOLUME)) + object.put("volume", volume); + if (shouldUpdate(EMOJI)) + { + if (emoji instanceof CustomEmoji) + object.put("emoji_id", ((CustomEmoji) emoji).getId()); + else if (emoji != null) + object.put("emoji_name", emoji.getName()); + else + { + object.put("emoji_id", null); + object.put("emoji_name", null); + } + } + reset(); + return getRequestBody(object); + } +} diff --git a/src/main/java/net/dv8tion/jda/internal/requests/WebSocketClient.java b/src/main/java/net/dv8tion/jda/internal/requests/WebSocketClient.java index 401bcbf683..e518644a0f 100644 --- a/src/main/java/net/dv8tion/jda/internal/requests/WebSocketClient.java +++ b/src/main/java/net/dv8tion/jda/internal/requests/WebSocketClient.java @@ -1397,6 +1397,11 @@ protected void setupHandlers() handlers.put("GUILD_ROLE_UPDATE", new GuildRoleUpdateHandler(api)); handlers.put("GUILD_SYNC", new GuildSyncHandler(api)); handlers.put("GUILD_STICKERS_UPDATE", new GuildStickersUpdateHandler(api)); + handlers.put("GUILD_SOUNDBOARD_SOUND_CREATE", new GuildSoundboardSoundCreateHandler(api)); + handlers.put("GUILD_SOUNDBOARD_SOUND_UPDATE", new GuildSoundboardSoundUpdateHandler(api)); + handlers.put("GUILD_SOUNDBOARD_SOUNDS_UPDATE", new GuildSoundboardSoundsUpdateHandler(api)); + handlers.put("GUILD_SOUNDBOARD_SOUND_DELETE", new GuildSoundboardSoundDeleteHandler(api)); + handlers.put("VOICE_CHANNEL_EFFECT_SEND", new VoiceChannelEffectSendHandler(api)); handlers.put("GUILD_UPDATE", new GuildUpdateHandler(api)); handlers.put("INTERACTION_CREATE", new InteractionCreateHandler(api)); handlers.put("INVITE_CREATE", new InviteCreateHandler(api)); diff --git a/src/main/java/net/dv8tion/jda/internal/requests/restaction/SoundboardSoundCreateActionImpl.java b/src/main/java/net/dv8tion/jda/internal/requests/restaction/SoundboardSoundCreateActionImpl.java new file mode 100644 index 0000000000..3ffc1a4f98 --- /dev/null +++ b/src/main/java/net/dv8tion/jda/internal/requests/restaction/SoundboardSoundCreateActionImpl.java @@ -0,0 +1,162 @@ +/* + * Copyright 2015 Austin Keener, Michael Ritter, Florian Spieß, and the JDA contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.dv8tion.jda.internal.requests.restaction; + +import net.dv8tion.jda.api.JDA; +import net.dv8tion.jda.api.entities.SoundboardSound; +import net.dv8tion.jda.api.entities.emoji.CustomEmoji; +import net.dv8tion.jda.api.entities.emoji.Emoji; +import net.dv8tion.jda.api.entities.emoji.UnicodeEmoji; +import net.dv8tion.jda.api.requests.Request; +import net.dv8tion.jda.api.requests.Response; +import net.dv8tion.jda.api.requests.Route; +import net.dv8tion.jda.api.requests.restaction.SoundboardSoundCreateAction; +import net.dv8tion.jda.api.utils.FileUpload; +import net.dv8tion.jda.api.utils.data.DataObject; +import net.dv8tion.jda.internal.utils.Checks; +import net.dv8tion.jda.internal.utils.IOUtil; +import okhttp3.RequestBody; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.io.IOException; +import java.io.UncheckedIOException; +import java.nio.charset.StandardCharsets; +import java.util.Base64; +import java.util.Locale; +import java.util.concurrent.TimeUnit; +import java.util.function.BooleanSupplier; + +public class SoundboardSoundCreateActionImpl extends AuditableRestActionImpl implements SoundboardSoundCreateAction +{ + private final String name; + private final FileUpload file; + private double volume = 1; + private Emoji emoji; + + public SoundboardSoundCreateActionImpl(JDA api, Route.CompiledRoute route, String name, FileUpload file) + { + super(api, route); + this.name = name; + this.file = file; + } + + @Nonnull + @Override + public SoundboardSoundCreateAction timeout(long timeout, @Nonnull TimeUnit unit) + { + return (SoundboardSoundCreateAction) super.timeout(timeout, unit); + } + + @Nonnull + @Override + public SoundboardSoundCreateAction addCheck(@Nonnull BooleanSupplier checks) + { + return (SoundboardSoundCreateAction) super.addCheck(checks); + } + + @Nonnull + @Override + public SoundboardSoundCreateAction setCheck(BooleanSupplier checks) + { + return (SoundboardSoundCreateAction) super.setCheck(checks); + } + + @Nonnull + @Override + public SoundboardSoundCreateAction deadline(long timestamp) + { + return (SoundboardSoundCreateAction) super.deadline(timestamp); + } + + @Nonnull + @Override + public SoundboardSoundCreateAction setVolume(double volume) + { + Checks.check(volume >= 0 && volume <= 1, "Volume must be between 0 and 1"); + this.volume = volume; + return this; + } + + @Nonnull + @Override + public SoundboardSoundCreateAction setEmoji(@Nullable Emoji emoji) + { + this.emoji = emoji; + return this; + } + + @Override + protected RequestBody finalizeData() + { + try + { + final DataObject json = DataObject.empty() + .put("name", name) + .put("sound", "data:" + getMime() + ";base64," + getBase64Sound()) + .put("volume", volume); + + if (emoji instanceof UnicodeEmoji) { + json.put("emoji_name", emoji.getName()); + } else if (emoji instanceof CustomEmoji) { + json.put("emoji_id", ((CustomEmoji) emoji).getId()); + } + + return getRequestBody(json); + } + catch (IOException e) + { + throw new UncheckedIOException("Unable to get request body when creating a guild soundboard sound", e); + } + } + + @Nonnull + private String getBase64Sound() throws IOException + { + final byte[] data = IOUtil.readFully(file.getData()); + final byte[] b64 = Base64.getEncoder().encode(data); + return new String(b64, StandardCharsets.UTF_8); + } + + @Nonnull + private String getMime() + { + int index = file.getName().lastIndexOf('.'); + Checks.check(index > -1, "Filename for soundboard sound is missing file extension. Provided: '" + file.getName() + "'. Must be MP3 or OGG."); + + String extension = file.getName().substring(index + 1).toLowerCase(Locale.ROOT); + String mime; + switch (extension) + { + case "mp3": + mime = "audio/mpeg"; + break; + case "ogg": + mime = "audio/ogg"; + break; + default: + throw new IllegalArgumentException("Unsupported file extension: '." + extension + "', must be MP3 or OGG."); + } + return mime; + } + + @Override + protected void handleSuccess(Response response, Request request) + { + request.onSuccess(api.getEntityBuilder().createSoundboardSound(response.getObject())); + } +} diff --git a/src/main/java/net/dv8tion/jda/internal/utils/PermissionUtil.java b/src/main/java/net/dv8tion/jda/internal/utils/PermissionUtil.java index 67fcf28e16..913d21a8d8 100644 --- a/src/main/java/net/dv8tion/jda/internal/utils/PermissionUtil.java +++ b/src/main/java/net/dv8tion/jda/internal/utils/PermissionUtil.java @@ -23,12 +23,14 @@ import net.dv8tion.jda.api.entities.channel.middleman.GuildChannel; import net.dv8tion.jda.api.entities.channel.middleman.MessageChannel; import net.dv8tion.jda.api.entities.emoji.RichCustomEmoji; +import net.dv8tion.jda.api.exceptions.InsufficientPermissionException; import org.apache.commons.collections4.CollectionUtils; import java.util.Arrays; import java.util.List; import java.util.concurrent.atomic.AtomicLong; import java.util.stream.Collectors; +import java.util.stream.Stream; public class PermissionUtil { @@ -37,7 +39,7 @@ public class PermissionUtil Arrays.stream(Permission.values()) .filter(Permission::isChannel) .collect(Collectors.toList())); - + /** * Checks if one given Member can interact with a 2nd given Member - in a permission sense (kick/ban/modify perms). * This only checks the Role-Position and does not check the actual permission (kick/ban/manage_role/...) @@ -252,6 +254,36 @@ public static boolean checkPermission(Member member, Permission... permissions) || isApplied(effectivePerms, Permission.getRaw(permissions)); } + /** + * Checks if the member has any of the specified permissions. Also checks for owners and administrators. + * + * @param member + * The member whose permissions are being checked + * @param permissions + * The permissions being checked for + * + * @throws IllegalArgumentException + * If any of the provided parameters are null, or no permissions were given + * @throws InsufficientPermissionException + * If the member has none of the specified permissions + */ + public static void requireAnyPermission(Member member, Permission... permissions) + { + Checks.notNull(member, "Member"); + Checks.notEmpty(permissions, "Permissions"); + + for (Permission permission : permissions) + { + if (member.hasPermission(permission)) + return; + } + + String reason = Stream.of(permissions) + .map(Permission::name) + .collect(Collectors.joining(" or ")); + throw new InsufficientPermissionException(member.getGuild(), permissions[0], "You need the " + reason + " permission to perform this action!"); + } + /** * Checks to see if the {@link net.dv8tion.jda.api.entities.Member Member} has the specified {@link net.dv8tion.jda.api.Permission Permissions} * in the specified {@link IPermissionContainer GuildChannel}. This method properly deals with