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 14bb51c556..4626d123d7 100644 --- a/src/main/java/net/dv8tion/jda/api/entities/Guild.java +++ b/src/main/java/net/dv8tion/jda/api/entities/Guild.java @@ -5028,6 +5028,10 @@ default AuditableRestAction createSticker(@Nonnull String name, @N @CheckReturnValue AuditableRestAction deleteSticker(@Nonnull StickerSnowflake id); + @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/requests/restaction/SoundboardSoundCreateAction.java b/src/main/java/net/dv8tion/jda/api/requests/restaction/SoundboardSoundCreateAction.java new file mode 100644 index 0000000000..b884626f6a --- /dev/null +++ b/src/main/java/net/dv8tion/jda/api/requests/restaction/SoundboardSoundCreateAction.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.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); + + @Nonnull + @CheckReturnValue + SoundboardSoundCreateAction setVolume(double volume); + + @Nonnull + @CheckReturnValue + SoundboardSoundCreateAction setEmoji(@Nonnull Emoji emoji); +} 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 71fc7d4b97..d3cf4fd27a 100644 --- a/src/main/java/net/dv8tion/jda/internal/entities/GuildImpl.java +++ b/src/main/java/net/dv8tion/jda/internal/entities/GuildImpl.java @@ -1967,6 +1967,17 @@ 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.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() 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..5959ca188d --- /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 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(@Nonnull Emoji emoji) + { + Checks.notNull(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())); + } +}