Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Add soundboard sounds #2753

Open
wants to merge 31 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
c4d0930
Add soundboard sounds via GUILD_CREATE events
freya022 Oct 15, 2024
2f457c0
Add routes
freya022 Oct 15, 2024
d5f8d3b
Add JDA to SoundboardSound
freya022 Oct 15, 2024
5f3f2c6
Add SoundboardSound#sendTo
freya022 Oct 15, 2024
8847924
Add Guild#createSoundboardSound
freya022 Oct 15, 2024
2cc2645
Add JDA#retrieveDefaultSoundboardSounds
freya022 Oct 15, 2024
3ad6f89
Fix tests
freya022 Oct 15, 2024
76ad02d
Add SoundboardSound#delete
freya022 Oct 16, 2024
2e15253
Handle create/update/delete
freya022 Oct 16, 2024
1f653c5
Add generic/create/delete events
freya022 Oct 16, 2024
8f506cd
Cache and replay soundboard sound updates if not created yet
freya022 Oct 16, 2024
4dbab04
Add update events
freya022 Oct 16, 2024
b761b93
Handle bulk update
freya022 Oct 16, 2024
fd1f621
Add getting soundboard sound by id
freya022 Oct 16, 2024
c01d908
Add SoundboardSoundManager
freya022 Oct 16, 2024
6c612a8
Add VoiceChannelEffectSendEvent
freya022 Oct 16, 2024
390793b
Add error responses
freya022 Oct 17, 2024
e518d13
Restrict SoundboardSound#sendTo to VoiceChannel
freya022 Oct 17, 2024
d9ef6fc
Fix cache getter name
freya022 Oct 17, 2024
afba643
More checks
freya022 Oct 17, 2024
7612983
Fix cache getter name
freya022 Oct 17, 2024
71032d9
More checks
freya022 Oct 17, 2024
7f0d2e9
More docs
freya022 Oct 17, 2024
c92e893
Move VoiceChannel.Effect to VoiceChannelEffect
freya022 Oct 17, 2024
e8c90ff
Nullable emoji while creating
freya022 Oct 17, 2024
1658089
Add checks to manager
freya022 Oct 17, 2024
cda847b
Add missing check
freya022 Oct 18, 2024
2357647
Improve docs
freya022 Oct 18, 2024
78994a1
Event docs
freya022 Oct 18, 2024
f9245b4
Clean up voice channel effect send
freya022 Oct 18, 2024
0fd3723
Last docs
freya022 Oct 18, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions src/main/java/net/dv8tion/jda/api/JDA.java
Original file line number Diff line number Diff line change
Expand Up @@ -1845,6 +1845,15 @@ default RestAction<ApplicationEmoji> retrieveApplicationEmojiById(long emojiId)
@CheckReturnValue
RestAction<@Unmodifiable List<StickerPack>> retrieveNitroStickerPacks();

/**
* Retrieves a list of the default {@link SoundboardSound SoundboardSounds}.
*
* @return {@link RestAction} - Type: List of {@link SoundboardSound}
*/
@Nonnull
@CheckReturnValue
RestAction<@Unmodifiable List<SoundboardSound>> retrieveDefaultSoundboardSounds();

/**
* The EventManager used by this JDA instance.
*
Expand Down
135 changes: 135 additions & 0 deletions src/main/java/net/dv8tion/jda/api/entities/Guild.java
Original file line number Diff line number Diff line change
Expand Up @@ -2052,6 +2052,104 @@ default List<GuildSticker> getStickersByName(@Nonnull String name, boolean ignor
@Nonnull
SnowflakeCacheView<GuildSticker> getStickerCache();

/**
* Gets a {@link SoundboardSound} from this guild that has the same id as the
* one provided.
* <br>If there is no {@link SoundboardSound} with an id that matches the provided
* one, then this returns {@code null}.
*
* <p>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.
* <br>If there is no {@link SoundboardSound} with an id that matches the provided
* one, then this returns {@code null}.
*
* <p>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.
* <br>Soundboard sounds are not ordered in any specific way in the returned list.
*
* <p>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.
*
* <p>This requires the {@link CacheFlag#SOUNDBOARD_SOUNDS} to be enabled!
*
* @return An immutable List of {@link SoundboardSound SoundboardSounds}.
*/
@Nonnull
@Unmodifiable
default List<SoundboardSound> getSoundboardSounds()
{
return getSoundboardSoundCache().asList();
}

/**
* Gets a list of all {@link SoundboardSound SoundboardSounds} in this Guild that have the same
* name as the one provided.
* <br>If there are no {@link SoundboardSound SoundboardSounds} with the provided name, then this returns an empty list.
*
* <p>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<SoundboardSound> getSoundboardSoundsByName(@Nonnull String name, boolean ignoreCase)
{
return getSoundboardSoundCache().getElementsByName(name, ignoreCase);
}

/**
* {@link SnowflakeCacheView} of all cached {@link SoundboardSound SoundboardSounds} of this Guild.
* <br>This does not include {@link JDA#retrieveDefaultSoundboardSounds() default sounds}.
*
* <p>This will be empty if {@link CacheFlag#SOUNDBOARD_SOUNDS} is disabled!
*
* @return {@link SnowflakeCacheView} - Type: {@link SoundboardSound}
*
* @see JDA#retrieveDefaultSoundboardSounds()
*/
@Nonnull
SnowflakeCacheView<SoundboardSound> getSoundboardSoundCache();

/**
* Retrieves an immutable list of Custom Emojis together with their respective creators.
*
Expand Down Expand Up @@ -5011,6 +5109,43 @@ default AuditableRestAction<GuildSticker> createSticker(@Nonnull String name, @N
@CheckReturnValue
AuditableRestAction<Void> deleteSticker(@Nonnull StickerSnowflake id);

/**
* Creates a soundboard sound in the guild.
*
* <p>The returned {@link RestAction} can encounter the following {@link net.dv8tion.jda.api.requests.ErrorResponse ErrorResponses}:
* <ul>
* <li>{@link net.dv8tion.jda.api.requests.ErrorResponse#INVALID_FILE_EXCEEDS_MAXIMUM_LENGTH INVALID_FILE_EXCEEDS_MAXIMUM_LENGTH}
* <br>The provided file exceeds the duration of 5.2 seconds</li>
* <li>{@link net.dv8tion.jda.api.requests.ErrorResponse#MAX_SOUNDBOARD_SOUNDS MAX_SOUNDBOARD_SOUNDS}
* <br>The maximum amount of soundboard sounds have been created, depends on the server boosts</li>
* <li>{@link net.dv8tion.jda.api.requests.ErrorResponse#INVALID_EMOJI INVALID_EMOJI}
* <br>The emoji is invalid</li>
* <li>{@link net.dv8tion.jda.api.requests.ErrorResponse#INVALID_FORM_BODY INVALID_FORM_BODY}
* <br>The file is too large</li>
* <li>{@link net.dv8tion.jda.api.requests.ErrorResponse#INVALID_FILE INVALID_FILE}
* <br>The file is malformed</li>
* </ul>
*
* @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
* <ul>
* <li>If {@code null} is provided</li>
* <li>If {@code name} is not between 2-32 characters</li>
* <li>If the file is not of the correct type</li>
* </ul>
* @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}.
Expand Down
200 changes: 200 additions & 0 deletions src/main/java/net/dv8tion/jda/api/entities/SoundboardSound.java
Original file line number Diff line number Diff line change
@@ -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 <a href="https://discord.com/developers/docs/resources/soundboard" target="_blank">Discord's Soundboard Docs</a>
*/
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.
*
* <p>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.
* <br>You must be connected to the voice channel to use this method,
* as well as be able to speak and hear.
*
* <p>The returned {@link RestAction} can encounter the following {@link ErrorResponse ErrorResponses}:
* <ul>
* <li>{@link ErrorResponse#MISSING_PERMISSIONS MISSING_PERMISSIONS}
* <br>The sound cannot be sent due to a permission discrepancy</li>
* <li>{@link ErrorResponse#CANNOT_SEND_VOICE_EFFECT CANNOT_SEND_VOICE_EFFECT}
* <br>The sound cannot be sent as the bot is either muted, deafened or suppressed</li>
* <li>{@link ErrorResponse#UNKNOWN_SOUND UNKNOWN_SOUND}
* <br>The sound was deleted</li>
* </ul>
*
* @param channel
* The channel to send this sound on
*
* @throws InsufficientPermissionException
* If the bot does not have the following permissions:
* <ul>
* <li>{@link Permission#VOICE_SPEAK VOICE_SPEAK}, {@link Permission#VOICE_USE_SOUNDBOARD VOICE_USE_SOUNDBOARD}</li>
* <li>When used in other guilds, {@link Permission#VOICE_USE_EXTERNAL_SOUNDS VOICE_USE_EXTERNAL_SOUNDS} permission</li>
* </ul>
* @throws IllegalArgumentException
* If the provided channel is {@code null}
* @throws IllegalStateException
* <ul>
* <li>If {@link GatewayIntent#GUILD_VOICE_STATES} is not enabled</li>
* <li>If the sound is not {@link #isAvailable() available}</li>
* <li>If the bot is not connected to the specified channel</li>
* <li>If the bot is deafened, muted or suppressed in the target guild</li>
* </ul>
*
* @return {@link RestAction} - Type: {@link Void}
*/
@Nonnull
@CheckReturnValue
RestAction<Void> sendTo(VoiceChannel channel);

/**
* Deletes this soundboard sound.
*
* <p>The returned {@link RestAction} can encounter the following {@link ErrorResponse ErrorResponses}:
* <ul>
* <li>{@link ErrorResponse#MISSING_PERMISSIONS MISSING_PERMISSIONS}
* <br>The sound cannot be deleted due to a permission discrepancy</li>
* <li>{@link ErrorResponse#UNKNOWN_SOUND UNKNOWN_SOUND}
* <br>The sound was deleted</li>
* </ul>
*
* @throws InsufficientPermissionException
* <ul>
* <li>If the bot does not own this sound and does not have {@link Permission#MANAGE_GUILD_EXPRESSIONS}</li>
* <li>If the bot owns this sound and does not have {@link Permission#MANAGE_GUILD_EXPRESSIONS} or {@link Permission#CREATE_GUILD_EXPRESSIONS}</li>
* </ul>
*
* @return {@link RestAction} - Type: {@link Void}
*/
@Nonnull
@CheckReturnValue
RestAction<Void> delete();

/**
* The {@link SoundboardSoundManager} for this soundboard sound, in which you can modify values.
* <br>You modify multiple fields in one request by chaining setters before calling {@link RestAction#queue()}.
*
* <p>The returned {@link RestAction} can encounter the following {@link ErrorResponse ErrorResponses}:
* <ul>
* <li>{@link ErrorResponse#MISSING_PERMISSIONS MISSING_PERMISSIONS}
* <br>The sound cannot be edited due to a permission discrepancy</li>
* <li>{@link ErrorResponse#UNKNOWN_SOUND UNKNOWN_SOUND}
* <br>The sound was deleted</li>
* </ul>
*
* @throws InsufficientPermissionException
* <ul>
* <li>If the bot does not own this sound and does not have {@link Permission#MANAGE_GUILD_EXPRESSIONS}</li>
* <li>If the bot owns this sound and does not have {@link Permission#MANAGE_GUILD_EXPRESSIONS} or {@link Permission#CREATE_GUILD_EXPRESSIONS}</li>
* </ul>
*
* @return The SoundboardSoundManager of this soundboard sound
*/
@Nonnull
@CheckReturnValue
SoundboardSoundManager getManager();
}
Loading