Lots of interactions & commands

This commit is contained in:
Vankka 2022-07-10 17:33:47 +03:00
parent 07554e8126
commit d8f5f89c19
No known key found for this signature in database
GPG Key ID: 6E50CB7A29B96AD0
26 changed files with 1086 additions and 220 deletions

View File

@ -21,10 +21,9 @@
* SOFTWARE.
*/
package com.discordsrv.api.discord.entity.component;
package com.discordsrv.api.discord.entity;
import com.discordsrv.api.discord.entity.DiscordUser;
import com.discordsrv.api.discord.entity.JDAEntity;
import com.discordsrv.api.discord.entity.component.impl.Modal;
import com.discordsrv.api.discord.entity.message.ReceivedDiscordMessage;
import com.discordsrv.api.discord.entity.message.SendableDiscordMessage;
import net.dv8tion.jda.api.interactions.InteractionHook;

View File

@ -0,0 +1,56 @@
/*
* This file is part of the DiscordSRV API, licensed under the MIT License
* Copyright (c) 2016-2022 Austin "Scarsz" Shapiro, Henri "Vankka" Schubin and DiscordSRV contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.discordsrv.api.discord.entity.channel;
import com.discordsrv.api.discord.entity.JDAEntity;
import net.dv8tion.jda.api.entities.ChannelType;
/**
* Represents a Discord channel type.
*/
public enum DiscordChannelType implements JDAEntity<ChannelType> {
TEXT(ChannelType.TEXT),
PRIVATE(ChannelType.PRIVATE),
VOICE(ChannelType.VOICE),
GROUP(ChannelType.GROUP),
CATEGORY(ChannelType.CATEGORY),
NEWS(ChannelType.NEWS),
STAGE(ChannelType.STAGE),
GUILD_NEWS_THREAD(ChannelType.GUILD_NEWS_THREAD),
GUILD_PUBLIC_THREAD(ChannelType.GUILD_PUBLIC_THREAD),
GUILD_PRIVATE_THREAD(ChannelType.GUILD_PRIVATE_THREAD),
;
private final ChannelType jda;
DiscordChannelType(ChannelType jda) {
this.jda = jda;
}
@Override
public ChannelType asJDA() {
return jda;
}
}

View File

@ -40,4 +40,8 @@ public interface DiscordDMChannel extends DiscordMessageChannel, JDAEntity<Priva
@Nullable
DiscordUser getUser();
@Override
default DiscordChannelType getType() {
return DiscordChannelType.PRIVATE;
}
}

View File

@ -40,6 +40,12 @@ import java.util.concurrent.CompletableFuture;
*/
public interface DiscordMessageChannel extends Snowflake {
/**
* Returns the type of channel this is.
* @return the type of the channel
*/
DiscordChannelType getType();
/**
* Sends the provided message to the channel.
*

View File

@ -26,4 +26,11 @@ package com.discordsrv.api.discord.entity.channel;
import com.discordsrv.api.discord.entity.JDAEntity;
import net.dv8tion.jda.api.entities.NewsChannel;
public interface DiscordNewsChannel extends DiscordGuildMessageChannel, DiscordThreadContainer, JDAEntity<NewsChannel> {}
public interface DiscordNewsChannel extends DiscordGuildMessageChannel, DiscordThreadContainer, JDAEntity<NewsChannel> {
@Override
default DiscordChannelType getType() {
return DiscordChannelType.NEWS;
}
}

View File

@ -39,4 +39,8 @@ public interface DiscordTextChannel extends DiscordGuildMessageChannel, DiscordT
@Nullable
String getTopic();
@Override
default DiscordChannelType getType() {
return DiscordChannelType.TEXT;
}
}

View File

@ -0,0 +1,377 @@
/*
* This file is part of the DiscordSRV API, licensed under the MIT License
* Copyright (c) 2016-2022 Austin "Scarsz" Shapiro, Henri "Vankka" Schubin and DiscordSRV contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.discordsrv.api.discord.entity.command;
import com.discordsrv.api.discord.entity.JDAEntity;
import net.dv8tion.jda.api.Permission;
import net.dv8tion.jda.api.interactions.commands.DefaultMemberPermissions;
import net.dv8tion.jda.api.interactions.commands.build.*;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.Unmodifiable;
import java.util.*;
public class Command implements JDAEntity<CommandData> {
/**
* Creates a chat input or slash command builder.
*
* @param name the name of the command
* @param description the description of the command
* @return a new chat input command builder
*/
public static ChatInputBuilder chatInput(String name, String description) {
return new ChatInputBuilder(name, description);
}
/**
* Creates a new user context menu command.
*
* @param name the name of the command
* @return a new command builder
*/
public static Builder user(String name) {
return new Builder(Type.USER, name);
}
/**
* Creates a new message context menu command.
*
* @param name the name of the command
* @return a new command builder
*/
public static Builder message(String name) {
return new Builder(Type.MESSAGE, name);
}
private final Type type;
private final Map<Locale, String> nameTranslations;
private final Map<Locale, String> descriptionTranslations;
private final List<SubCommandGroup> subCommandGroups;
private final List<Command> subCommands;
private final List<CommandOption> options;
private final boolean guildOnly;
private final DefaultPermission defaultPermission;
private Command(
Type type,
Map<Locale, String> nameTranslations,
Map<Locale, String> descriptionTranslations,
List<SubCommandGroup> subCommandGroups,
List<Command> subCommands,
List<CommandOption> options,
boolean guildOnly,
DefaultPermission defaultPermission
) {
this.type = type;
this.nameTranslations = nameTranslations;
this.descriptionTranslations = descriptionTranslations;
this.subCommandGroups = subCommandGroups;
this.subCommands = subCommands;
this.options = options;
this.guildOnly = guildOnly;
this.defaultPermission = defaultPermission;
}
@NotNull
public Type getType() {
return type;
}
@NotNull
public String getName() {
return nameTranslations.get(Locale.ROOT);
}
@NotNull
@Unmodifiable
public Map<Locale, String> getNameTranslations() {
return Collections.unmodifiableMap(nameTranslations);
}
@Nullable
public String getDescription() {
return descriptionTranslations.get(Locale.ROOT);
}
@NotNull
@Unmodifiable
public Map<Locale, String> getDescriptionTranslations() {
return Collections.unmodifiableMap(descriptionTranslations);
}
@NotNull
@Unmodifiable
public List<SubCommandGroup> getSubCommandGroups() {
return Collections.unmodifiableList(subCommandGroups);
}
@NotNull
@Unmodifiable
public List<CommandOption> getOptions() {
return Collections.unmodifiableList(options);
}
public boolean isGuildOnly() {
return guildOnly;
}
@NotNull
public DefaultPermission getDefaultPermission() {
return defaultPermission;
}
@Override
public CommandData asJDA() {
CommandData commandData;
switch (type) {
case USER:
commandData = Commands.user(getName());
break;
case MESSAGE:
commandData = Commands.message(getName());
break;
case CHAT_INPUT:
SlashCommandData slashCommandData = Commands.slash(getName(), Objects.requireNonNull(getDescription()));
slashCommandData.addSubcommandGroups(subCommandGroups.stream().map(JDAEntity::asJDA).toArray(SubcommandGroupData[]::new));
slashCommandData.addSubcommands(subCommands.stream().map(Command::asJDASubcommand).toArray(SubcommandData[]::new));
slashCommandData.addOptions(options.stream().map(JDAEntity::asJDA).toArray(OptionData[]::new));
commandData = slashCommandData;
break;
default:
throw new IllegalStateException("Missing switch case");
}
commandData.setGuildOnly(guildOnly);
commandData.setDefaultPermissions(defaultPermission.asJDA());
return commandData;
}
public SubcommandData asJDASubcommand() {
SubcommandData data = new SubcommandData(nameTranslations.get(Locale.ROOT), descriptionTranslations.get(Locale.ROOT));
data.addOptions(options.stream().map(JDAEntity::asJDA).toArray(OptionData[]::new));
return data;
}
public static class ChatInputBuilder extends Builder {
private final Map<Locale, String> descriptionTranslations = new LinkedHashMap<>();
private final List<SubCommandGroup> subCommandGroups = new ArrayList<>();
private final List<Command> subCommands = new ArrayList<>();
private final List<CommandOption> options = new ArrayList<>();
private ChatInputBuilder(String name, String description) {
super(Type.CHAT_INPUT, name);
this.descriptionTranslations.put(Locale.ROOT, description);
}
/**
* Adds a description translation for this command.
* @param locale the language
* @param translation the translation
* @return this builder, useful for chaining
* @throws IllegalStateException if this isn't a {@link Type#CHAT_INPUT} command
*/
@NotNull
public ChatInputBuilder addDescriptionTranslation(@NotNull Locale locale, @NotNull String translation) {
if (type != Type.CHAT_INPUT) {
throw new IllegalStateException("Descriptions are only available for CHAT_INPUT commands");
}
this.descriptionTranslations.put(locale, translation);
return this;
}
/**
* Adds a sub command group to this command.
*
* @param subCommandGroup the sub command group
* @return this builder, useful for chaining
*/
@NotNull
public ChatInputBuilder addSubCommandGroup(@NotNull SubCommandGroup subCommandGroup) {
this.subCommandGroups.add(subCommandGroup);
return this;
}
/**
* Adds a sub command to this command.
*
* @param command the sub command
* @return this builder, useful for chaining
*/
@NotNull
public ChatInputBuilder addSubCommand(@NotNull Command command) {
this.subCommands.add(command);
return this;
}
/**
* Adds an option to this command.
*
* @param option the option
* @return this builder, useful for chaining
*/
@NotNull
public ChatInputBuilder addOption(@NotNull CommandOption option) {
this.options.add(option);
return this;
}
@Override
public Command build() {
return new Command(
type,
nameTranslations,
descriptionTranslations,
subCommandGroups,
subCommands,
options,
guildOnly,
defaultPermission
);
}
}
public static class Builder {
protected final Type type;
protected final Map<Locale, String> nameTranslations = new LinkedHashMap<>();
protected boolean guildOnly = true;
protected DefaultPermission defaultPermission = DefaultPermission.EVERYONE;
private Builder(Type type, String name) {
this.type = type;
this.nameTranslations.put(Locale.ROOT, name);
}
/**
* Adds a name translation for this command.
* @param locale the language
* @param translation the translation
* @return this builder, useful for chaining
*/
@NotNull
public Builder addNameTranslation(@NotNull Locale locale, @NotNull String translation) {
this.nameTranslations.put(locale, translation);
return this;
}
/**
* Sets if this command is limited to Discord servers.
* @param guildOnly if this command is limited to Discord servers
* @return this builder, useful for chaining
*/
@NotNull
public Builder setGuildOnly(boolean guildOnly) {
this.guildOnly = guildOnly;
return this;
}
/**
* Sets the permission level required to use the command by default.
* @param defaultPermission the permission level
*/
@NotNull
public Builder setDefaultPermission(@NotNull DefaultPermission defaultPermission) {
this.defaultPermission = defaultPermission;
return this;
}
public Command build() {
return new Command(
type,
nameTranslations,
Collections.emptyMap(),
Collections.emptyList(),
Collections.emptyList(),
Collections.emptyList(),
guildOnly,
defaultPermission
);
}
}
public interface DefaultPermission extends JDAEntity<DefaultMemberPermissions> {
DefaultPermission EVERYONE = new Simple(true);
DefaultPermission ADMINISTRATOR = new Simple(false);
DefaultPermission BAN_MEMBERS = Permissions.fromJDA(Permission.BAN_MEMBERS);
DefaultPermission MODERATE_MEMBERS = Permissions.fromJDA(Permission.MODERATE_MEMBERS);
DefaultPermission MANAGE_PERMISSIONS = Permissions.fromJDA(Permission.MANAGE_PERMISSIONS);
DefaultPermission MESSAGE_MANAGE = Permissions.fromJDA(Permission.MESSAGE_MANAGE);
class Simple implements DefaultPermission {
private final boolean value;
private Simple(boolean value) {
this.value = value;
}
@Override
public DefaultMemberPermissions asJDA() {
return value ? DefaultMemberPermissions.ENABLED : DefaultMemberPermissions.DISABLED;
}
}
class Permissions implements DefaultPermission {
public static Permissions fromJDA(Permission... permissions) {
return new Permissions(Permission.getRaw(permissions));
}
private final long permissions;
public Permissions(long permissions) {
this.permissions = permissions;
}
@Override
public DefaultMemberPermissions asJDA() {
return DefaultMemberPermissions.enabledFor(permissions);
}
}
}
public enum Type implements JDAEntity<net.dv8tion.jda.api.interactions.commands.Command.Type> {
CHAT_INPUT(net.dv8tion.jda.api.interactions.commands.Command.Type.SLASH),
USER(net.dv8tion.jda.api.interactions.commands.Command.Type.USER),
MESSAGE(net.dv8tion.jda.api.interactions.commands.Command.Type.MESSAGE);
private final net.dv8tion.jda.api.interactions.commands.Command.Type jda;
Type(net.dv8tion.jda.api.interactions.commands.Command.Type jda) {
this.jda = jda;
}
@Override
public net.dv8tion.jda.api.interactions.commands.Command.Type asJDA() {
return jda;
}
}
}

View File

@ -0,0 +1,198 @@
/*
* This file is part of the DiscordSRV API, licensed under the MIT License
* Copyright (c) 2016-2022 Austin "Scarsz" Shapiro, Henri "Vankka" Schubin and DiscordSRV contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.discordsrv.api.discord.entity.command;
import com.discordsrv.api.discord.entity.JDAEntity;
import net.dv8tion.jda.api.interactions.commands.OptionType;
import net.dv8tion.jda.api.interactions.commands.build.OptionData;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Unmodifiable;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.Map;
public class CommandOption implements JDAEntity<OptionData> {
/**
* Creates a new command option builder.
*
* @param type the type of the command option
* @param name the name of the command option
* @param description the description of the command option
* @return the new command option builder
*/
@NotNull
public static Builder builder(@NotNull Type type, @NotNull String name, @NotNull String description) {
return new Builder(type, name, description);
}
private final Type type;
private final String name;
private final String description;
private final Map<String, Object> choices;
public CommandOption(Type type, String name, String description, Map<String, Object> choices) {
this.type = type;
this.name = name;
this.description = description;
this.choices = choices;
}
@NotNull
public Type getType() {
return type;
}
@NotNull
public String getName() {
return name;
}
@NotNull
public String getDescription() {
return description;
}
@NotNull
@Unmodifiable
public Map<String, Object> getChoices() {
return Collections.unmodifiableMap(choices);
}
@Override
public OptionData asJDA() {
OptionData data = new OptionData(type.asJDA(), name, description);
for (Map.Entry<String, Object> entry : choices.entrySet()) {
String key = entry.getKey();
Object value = entry.getValue();
if (value instanceof String) {
data.addChoice(key, (String) value);
} else if (value instanceof Integer) {
data.addChoice(key, (Integer) value);
} else if (value instanceof Double) {
data.addChoice(key, (Double) value);
} else {
throw new IllegalStateException("Not a String, Integer or Double choice value");
}
}
return data;
}
public static class Builder {
private final Type type;
private final String name;
private final String description;
private final Map<String, Object> choices = new LinkedHashMap<>();
private Builder(Type type, String name, String description) {
this.type = type;
this.name = name;
this.description = description;
}
/**
* Adds a String choice, type must be {@link Type#STRING}.
*
* @param name the name of the choice, this will be returned via the event
* @param stringValue the choice
* @return this builder, useful for chaining
*/
@NotNull
public Builder addChoice(String name, String stringValue) {
if (type != Type.DOUBLE) {
throw new IllegalStateException("Must be of type STRING");
}
this.choices.put(name, stringValue);
return this;
}
/**
* Adds a String choice, type must be {@link Type#INTEGER}.
*
* @param name the name of the choice, this will be returned via the event
* @param integerValue the choice
* @return this builder, useful for chaining
*/
@NotNull
public Builder addChoice(String name, int integerValue) {
if (type != Type.INTEGER) {
throw new IllegalStateException("Must be of type INTEGER");
}
this.choices.put(name, integerValue);
return this;
}
/**
* Adds a String choice, type must be {@link Type#DOUBLE}.
*
* @param name the name of the choice, this will be returned via the event
* @param doubleValue the choice
* @return this builder, useful for chaining
*/
@NotNull
public Builder addChoice(String name, double doubleValue) {
if (type != Type.DOUBLE) {
throw new IllegalStateException("Must be of type DOUBLE");
}
this.choices.put(name, doubleValue);
return this;
}
@NotNull
public CommandOption build() {
return new CommandOption(type, name, description, choices);
}
}
public enum Type implements JDAEntity<OptionType> {
STRING(OptionType.STRING),
INTEGER(OptionType.INTEGER),
DOUBLE(OptionType.NUMBER),
BOOLEAN(OptionType.BOOLEAN),
USER(OptionType.USER),
CHANNEL(OptionType.CHANNEL),
ROLE(OptionType.ROLE),
MENTIONABLE(OptionType.MENTIONABLE),
ATTACHMENT(OptionType.ATTACHMENT);
private final OptionType optionType;
Type(OptionType optionType) {
this.optionType = optionType;
}
public boolean isSupportsChoices() {
return optionType.canSupportChoices();
}
@Override
public OptionType asJDA() {
return optionType;
}
}
}

View File

@ -0,0 +1,81 @@
/*
* This file is part of the DiscordSRV API, licensed under the MIT License
* Copyright (c) 2016-2022 Austin "Scarsz" Shapiro, Henri "Vankka" Schubin and DiscordSRV contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.discordsrv.api.discord.entity.command;
import com.discordsrv.api.discord.entity.JDAEntity;
import net.dv8tion.jda.api.interactions.commands.build.SubcommandData;
import net.dv8tion.jda.api.interactions.commands.build.SubcommandGroupData;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Unmodifiable;
import java.util.Arrays;
import java.util.List;
public class SubCommandGroup implements JDAEntity<SubcommandGroupData> {
/**
* Creates a sub command group.
*
* @param name the sub command group name
* @param description the sub command group description
* @param commands the commands within the sub command group
* @return a new sub command group
*/
@NotNull
public static SubCommandGroup of(@NotNull String name, @NotNull String description, @NotNull Command... commands) {
return new SubCommandGroup(name, description, Arrays.asList(commands));
}
private final String name;
private final String description;
private final List<Command> commands;
private SubCommandGroup(String name, String description, List<Command> commands) {
this.name = name;
this.description = description;
this.commands = commands;
}
@NotNull
public String getName() {
return name;
}
@NotNull
public String getDescription() {
return description;
}
@NotNull
@Unmodifiable
public List<Command> getCommands() {
return commands;
}
@Override
public SubcommandGroupData asJDA() {
return new SubcommandGroupData(name, description)
.addSubcommands(commands.stream().map(Command::asJDASubcommand).toArray(SubcommandData[]::new));
}
}

View File

@ -0,0 +1,102 @@
/*
* This file is part of the DiscordSRV API, licensed under the MIT License
* Copyright (c) 2016-2022 Austin "Scarsz" Shapiro, Henri "Vankka" Schubin and DiscordSRV contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.discordsrv.api.discord.entity.component;
import org.intellij.lang.annotations.Pattern;
import org.jetbrains.annotations.NotNull;
import java.util.Objects;
/**
* An identifier for components to match up with interaction events.
*/
public class ComponentIdentifier {
public static final String ID_PREFIX = "DiscordSRV/";
private static final String REGEX = "[\\w\\d-_]{1,40}";
private static final java.util.regex.Pattern PATTERN = java.util.regex.Pattern.compile(REGEX);
/**
* Creates a new {@link ComponentIdentifier}.
*
* @param extensionName the name of the plugin or mod that owns this identifier (1-40 characters, a-z, A-Z, 0-9, -, _)
* @param identifier the identifier of this component (1-40 characters, a-z, A-Z, 0-9, -, _)
* @return a new {@link ComponentIdentifier}
* @throws IllegalArgumentException if the extension name or identifier does not match the required constraints
*/
@NotNull
public static ComponentIdentifier of(
@NotNull @Pattern(REGEX) String extensionName,
@NotNull @Pattern(REGEX) String identifier
) {
if (!PATTERN.matcher(extensionName).matches()) {
throw new IllegalArgumentException("Extension name does not match the required pattern");
} else if (!PATTERN.matcher(identifier).matches()) {
throw new IllegalArgumentException("Identifier does not match the required pattern");
}
return new ComponentIdentifier(extensionName, identifier);
}
private final String extensionName;
private final String identifier;
private ComponentIdentifier(String extensionName, String identifier) {
this.extensionName = extensionName;
this.identifier = identifier;
}
public String getExtensionName() {
return extensionName;
}
public String getIdentifier() {
return identifier;
}
public String getDiscordIdentifier() {
return ID_PREFIX + getExtensionName() + ":" + getIdentifier();
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
ComponentIdentifier that = (ComponentIdentifier) o;
return Objects.equals(extensionName, that.extensionName) && Objects.equals(identifier, that.identifier);
}
@Override
public int hashCode() {
return Objects.hash(extensionName, identifier);
}
@Override
public String toString() {
return "ComponentIdentifier{" +
"extensionName='" + extensionName + '\'' +
", identifier='" + identifier + '\'' +
'}';
}
}

View File

@ -30,7 +30,7 @@ import java.util.List;
public class MessageActionRow implements ActionRow<MessageComponent> {
public static MessageActionRow message(MessageComponent... components) {
public static MessageActionRow of(MessageComponent... components) {
if (components.length == 0) {
throw new IllegalArgumentException("Must include at least one component");
}

View File

@ -21,10 +21,12 @@
* SOFTWARE.
*/
package com.discordsrv.api.discord.entity.component;
package com.discordsrv.api.discord.entity.component.impl;
import com.discordsrv.api.discord.entity.guild.DiscordEmote;
import net.dv8tion.jda.api.entities.Emoji;
import com.discordsrv.api.discord.entity.component.ComponentIdentifier;
import com.discordsrv.api.discord.entity.component.MessageComponent;
import com.discordsrv.api.discord.entity.guild.DiscordCustomEmoji;
import net.dv8tion.jda.api.entities.emoji.Emoji;
import net.dv8tion.jda.api.interactions.components.ItemComponent;
import net.dv8tion.jda.api.interactions.components.buttons.ButtonStyle;
import org.jetbrains.annotations.NotNull;
@ -35,7 +37,8 @@ import java.util.UUID;
/**
* A Discord button.
* @see #builder(Style)
* @see #builder(ComponentIdentifier, Style)
* @see #urlBuilder(String)
*/
public class Button implements MessageComponent {
@ -44,8 +47,19 @@ public class Button implements MessageComponent {
* @param style the style of the button
* @return a new button builder
*/
public static Builder builder(@NotNull Button.Style style) {
return new Builder(style);
@NotNull
public static Builder builder(@NotNull ComponentIdentifier id, @NotNull Button.Style style) {
return new Builder(id.getDiscordIdentifier(), style);
}
/**
* Creates a new Link button builder.
* @param url the link the button leads to
* @return a new button builder
*/
@NotNull
public static Builder urlBuilder(@NotNull String url) {
return new Builder(null, Style.LINK).setUrl(url);
}
private final Style buttonStyle;
@ -53,22 +67,20 @@ public class Button implements MessageComponent {
private final String label;
private final Emoji emoji;
private final boolean disabled;
private final ClickHandler clickHandler;
private Button(
String id,
Style buttonStyle,
String url,
String label,
Emoji emoji,
boolean disabled,
ClickHandler clickHandler
boolean disabled
) {
this.buttonStyle = buttonStyle;
this.idOrUrl = buttonStyle == Style.LINK ? url : UUID.randomUUID().toString();
this.idOrUrl = buttonStyle == Style.LINK ? url : id;
this.label = label;
this.emoji = emoji;
this.disabled = disabled;
this.clickHandler = clickHandler;
}
@NotNull
@ -95,10 +107,6 @@ public class Button implements MessageComponent {
return disabled;
}
public ClickHandler getClickHandler() {
return clickHandler;
}
@Override
public ItemComponent asJDA() {
return net.dv8tion.jda.api.interactions.components.buttons.Button.of(
@ -111,22 +119,18 @@ public class Button implements MessageComponent {
private static class Builder {
private final String id;
private final Style style;
private String url;
private String label;
private Emoji emoji;
private boolean disabled;
private ClickHandler clickHandler;
private Builder(Style style) {
private Builder(String id, Style style) {
this.id = id;
this.style = style;
}
@NotNull
public Style getStyle() {
return style;
}
/**
* Sets the url for this button, only works if the style is {@link Style#LINK}.
*
@ -142,10 +146,6 @@ public class Button implements MessageComponent {
return this;
}
public String getUrl() {
return url;
}
/**
* Sets the text shown on this button.
* @param label the text
@ -157,10 +157,6 @@ public class Button implements MessageComponent {
return this;
}
public String getLabel() {
return label;
}
/**
* Sets the emoji to show on this button.
* @param unicodeEmoji the unicode code point for the emoji
@ -178,15 +174,11 @@ public class Button implements MessageComponent {
* @return this builder, useful for chaining
*/
@NotNull
public Builder setEmoji(DiscordEmote emote) {
this.emoji = Emoji.fromEmote(emote.asJDA());
public Builder setEmoji(DiscordCustomEmoji emote) {
this.emoji = Emoji.fromCustom(emote.asJDA());
return this;
}
public Emoji getEmoji() {
return emoji;
}
/**
* Set if this button is disabled or not. Default is {@code false}.
* @param disabled if this button should be disabled
@ -198,28 +190,6 @@ public class Button implements MessageComponent {
return this;
}
public boolean isDisabled() {
return disabled;
}
/**
* Sets the click handler for this button, does not work if the style is {@link Style#LINK}.
* @param clickHandler the click handler
* @return this builder, useful for chaining
*/
@NotNull
public Builder setClickHandler(ClickHandler clickHandler) {
if (style == Style.LINK) {
throw new IllegalStateException("Cannot set click handler for LINK type button, use setUrl instead");
}
this.clickHandler = clickHandler;
return this;
}
public ClickHandler getClickHandler() {
return clickHandler;
}
/**
* Creates the button.
* @return a new button
@ -229,12 +199,12 @@ public class Button implements MessageComponent {
throw new IllegalStateException("No style set");
}
return new Button(
id,
style,
style == Style.LINK ? url : UUID.randomUUID().toString(),
label,
emoji,
disabled,
clickHandler
disabled
);
}
}
@ -256,11 +226,4 @@ public class Button implements MessageComponent {
return style;
}
}
@FunctionalInterface
public interface ClickHandler {
void onClick(Interaction interaction);
}
}

View File

@ -21,20 +21,22 @@
* SOFTWARE.
*/
package com.discordsrv.api.discord.entity.component;
package com.discordsrv.api.discord.entity.component.impl;
import com.discordsrv.api.discord.entity.JDAEntity;
import com.discordsrv.api.discord.entity.component.ComponentIdentifier;
import com.discordsrv.api.discord.entity.component.actionrow.ActionRow;
import com.discordsrv.api.discord.entity.component.actionrow.ModalActionRow;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Unmodifiable;
import java.util.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
/**
* A Discord modal.
* @see #builder(String)
* @see #builder(ComponentIdentifier, String)
*/
public class Modal implements JDAEntity<net.dv8tion.jda.api.interactions.components.Modal> {
@ -43,16 +45,17 @@ public class Modal implements JDAEntity<net.dv8tion.jda.api.interactions.compone
* @param title the title of the modal
* @return a new modal builder
*/
public static Builder builder(String title) {
return new Builder(title);
@NotNull
public static Builder builder(@NotNull ComponentIdentifier id, @NotNull String title) {
return new Builder(id.getDiscordIdentifier(), title);
}
private final String id;
private final String title;
private final List<ModalActionRow> rows;
private Modal(String title, List<ModalActionRow> rows) {
this.id = UUID.randomUUID().toString();
private Modal(String id, String title, List<ModalActionRow> rows) {
this.id = id;
this.title = title;
this.rows = rows;
}
@ -79,10 +82,12 @@ public class Modal implements JDAEntity<net.dv8tion.jda.api.interactions.compone
private static class Builder {
private final String id;
private final String title;
private final List<ModalActionRow> rows = new ArrayList<>();
public Builder(String title) {
public Builder(String id, String title) {
this.id = id;
this.title = title;
}
@ -108,18 +113,12 @@ public class Modal implements JDAEntity<net.dv8tion.jda.api.interactions.compone
return this;
}
@NotNull
@Unmodifiable
public List<ModalActionRow> getRows() {
return Collections.unmodifiableList(rows);
}
/**
* Builds the modal.
* @return a new modal
*/
public Modal build() {
return new Modal(title, rows);
return new Modal(id, title, rows);
}
}
}

View File

@ -21,21 +21,22 @@
* SOFTWARE.
*/
package com.discordsrv.api.discord.entity.component;
package com.discordsrv.api.discord.entity.component.impl;
import com.discordsrv.api.discord.entity.guild.DiscordEmote;
import net.dv8tion.jda.api.entities.Emoji;
import com.discordsrv.api.discord.entity.component.ComponentIdentifier;
import com.discordsrv.api.discord.entity.component.MessageComponent;
import com.discordsrv.api.discord.entity.guild.DiscordCustomEmoji;
import net.dv8tion.jda.api.entities.emoji.Emoji;
import net.dv8tion.jda.api.interactions.components.ItemComponent;
import net.dv8tion.jda.api.interactions.components.selections.SelectOption;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Unmodifiable;
import javax.annotation.CheckReturnValue;
import java.util.*;
/**
* A Discord selection menu.
* @see #builder()
* @see #builder(ComponentIdentifier)
*/
public class SelectMenu implements MessageComponent {
@ -43,8 +44,8 @@ public class SelectMenu implements MessageComponent {
* Creates a selection menu builder.
* @return a new builder
*/
public static Builder builder() {
return new Builder();
public static Builder builder(@NotNull ComponentIdentifier id) {
return new Builder(id.getDiscordIdentifier());
}
private final String id;
@ -53,16 +54,14 @@ public class SelectMenu implements MessageComponent {
private final String placeholder;
private final int minValues;
private final int maxValues;
private final SelectHandler handler;
private SelectMenu(List<Option> options, boolean disabled, String placeholder, int minValues, int maxValues, SelectHandler handler) {
this.id = UUID.randomUUID().toString();
private SelectMenu(String id, List<Option> options, boolean disabled, String placeholder, int minValues, int maxValues) {
this.id = id;
this.options = options;
this.disabled = disabled;
this.placeholder = placeholder;
this.minValues = minValues;
this.maxValues = maxValues;
this.handler = handler;
}
@Override
@ -166,8 +165,8 @@ public class SelectMenu implements MessageComponent {
* @return a new option
*/
@CheckReturnValue
public Option withEmoji(DiscordEmote emote) {
return new Option(label, value, description, Emoji.fromEmote(emote.asJDA()), defaultSelected);
public Option withEmoji(DiscordCustomEmoji emote) {
return new Option(label, value, description, Emoji.fromCustom(emote.asJDA()), defaultSelected);
}
/**
@ -183,12 +182,16 @@ public class SelectMenu implements MessageComponent {
private static class Builder {
private final String id;
private final List<Option> options = new ArrayList<>();
private boolean disabled = false;
private String placeholder;
private int minValues = 0;
private int maxValues = 1;
private SelectHandler handler;
public Builder(String id) {
this.id = id;
}
/**
* Adds an option to this selection menu.
@ -213,12 +216,6 @@ public class SelectMenu implements MessageComponent {
return this;
}
@NotNull
@Unmodifiable
public List<Option> getOptions() {
return Collections.unmodifiableList(options);
}
/**
* Sets if this selection menu should be disabled. Default is {@code false}.
* @param disabled if this selection menu should be disabled
@ -230,10 +227,6 @@ public class SelectMenu implements MessageComponent {
return this;
}
public boolean isDisabled() {
return disabled;
}
/**
* Sets the placeholder text for this selection menu.
* @param placeholder the placeholder text
@ -244,10 +237,6 @@ public class SelectMenu implements MessageComponent {
return this;
}
public String getPlaceholder() {
return placeholder;
}
/**
* Sets the minimum amount of values to select. The default is {@code 0}.
* @param minValues the minimum value amount
@ -258,10 +247,6 @@ public class SelectMenu implements MessageComponent {
return this;
}
public int getMinValues() {
return minValues;
}
/**
* Sets the maximum amount of values to select. The default is {@code 1}.
* @param maxValues the maximum value amount
@ -272,36 +257,12 @@ public class SelectMenu implements MessageComponent {
return this;
}
public int getMaxValues() {
return maxValues;
}
/**
* Sets the handler for selection changes.
* @param handler the handler
* @return this builder, useful for chaining
*/
@NotNull
public Builder setHandler(SelectHandler handler) {
this.handler = handler;
return this;
}
public SelectHandler getHandler() {
return handler;
}
/**
* Builds the selection menu.
* @return a new selection menu
*/
public SelectMenu build() {
return new SelectMenu(options, disabled, placeholder, minValues, maxValues, handler);
return new SelectMenu(id, options, disabled, placeholder, minValues, maxValues);
}
}
@FunctionalInterface
public interface SelectHandler {
void onSelect(Interaction interaction);
}
}

View File

@ -21,17 +21,19 @@
* SOFTWARE.
*/
package com.discordsrv.api.discord.entity.component;
package com.discordsrv.api.discord.entity.component.impl;
import com.discordsrv.api.discord.entity.component.ComponentIdentifier;
import com.discordsrv.api.discord.entity.component.ModalComponent;
import net.dv8tion.jda.api.interactions.components.ItemComponent;
import net.dv8tion.jda.api.interactions.components.text.TextInputStyle;
import java.util.UUID;
import org.jetbrains.annotations.NotNull;
public class TextInput implements ModalComponent {
public static Builder builder(String label, Style style) {
return new Builder(label, style);
@NotNull
public static Builder builder(@NotNull ComponentIdentifier id, @NotNull String label, @NotNull Style style) {
return new Builder(id.getDiscordIdentifier(), label, style);
}
private final String id;
@ -43,8 +45,17 @@ public class TextInput implements ModalComponent {
private final boolean required;
private final String defaultValue;
public TextInput(String label, Style style, int minLength, int maxLength, String placeholder, boolean required, String defaultValue) {
this.id = UUID.randomUUID().toString();
public TextInput(
String id,
String label,
Style style,
int minLength,
int maxLength,
String placeholder,
boolean required,
String defaultValue
) {
this.id = id;
this.label = label;
this.style = style;
this.minLength = minLength;
@ -97,6 +108,7 @@ public class TextInput implements ModalComponent {
public static class Builder {
private final String id;
private final String label;
private final Style style;
private int minLength = 0;
@ -105,61 +117,47 @@ public class TextInput implements ModalComponent {
private boolean required = true;
private String defaultValue;
private Builder(String label, Style style) {
private Builder(String id, String label, Style style) {
this.id = id;
this.label = label;
this.style = style;
}
public String getLabel() {
return label;
}
public Style getStyle() {
return style;
}
public Builder setMinLength(int minLength) {
this.minLength = minLength;
return this;
}
public int getMinLength() {
return minLength;
}
public Builder setMaxLength(int maxLength) {
this.maxLength = maxLength;
return this;
}
public int getMaxLength() {
return maxLength;
}
public Builder setPlaceholder(String placeholder) {
this.placeholder = placeholder;
return this;
}
public String getPlaceholder() {
return placeholder;
}
public Builder setRequired(boolean required) {
this.required = required;
return this;
}
public boolean isRequired() {
return required;
}
public void setDefaultValue(String defaultValue) {
this.defaultValue = defaultValue;
}
public String getDefaultValue() {
return defaultValue;
public TextInput build() {
return new TextInput(
id,
label,
style,
minLength,
maxLength,
placeholder,
required,
defaultValue
);
}
}

View File

@ -25,9 +25,9 @@ package com.discordsrv.api.discord.entity.guild;
import com.discordsrv.api.discord.entity.JDAEntity;
import com.discordsrv.api.discord.entity.Snowflake;
import net.dv8tion.jda.api.entities.Emote;
import net.dv8tion.jda.api.entities.emoji.CustomEmoji;
public interface DiscordEmote extends JDAEntity<Emote>, Snowflake {
public interface DiscordCustomEmoji extends JDAEntity<CustomEmoji>, Snowflake {
String getName();

View File

@ -31,6 +31,7 @@ import com.discordsrv.api.discord.entity.channel.DiscordTextChannel;
import com.discordsrv.api.discord.entity.guild.DiscordGuild;
import com.discordsrv.api.discord.entity.guild.DiscordGuildMember;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Unmodifiable;
import java.util.List;
import java.util.Optional;
@ -39,7 +40,28 @@ import java.util.concurrent.CompletableFuture;
/**
* A message received from Discord.
*/
public interface ReceivedDiscordMessage extends SendableDiscordMessage, Snowflake {
public interface ReceivedDiscordMessage extends Snowflake {
/**
* Gets the content of this message.
* @return the message content
*/
@NotNull
String getContent();
/**
* Gets the embeds of this message.
* @return the message embeds
*/
@NotNull
@Unmodifiable
List<DiscordMessageEmbed> getEmbeds();
/**
* Returns {@code true} if this is a webhook message, {@link #getAuthor()} to get webhook username or avatar url.
* @return {@code true} if this is a webhook message
*/
boolean isWebhookMessage();
/**
* Gets the URL to jump to this message.

View File

@ -24,9 +24,11 @@
package com.discordsrv.api.discord.entity.message;
import com.discordsrv.api.component.GameTextBuilder;
import com.discordsrv.api.discord.entity.component.actionrow.MessageActionRow;
import com.discordsrv.api.discord.entity.message.impl.SendableDiscordMessageImpl;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.Unmodifiable;
import java.util.Collection;
import java.util.List;
@ -64,13 +66,23 @@ public interface SendableDiscordMessage {
* @return the unmodifiable list of embeds in this message
*/
@NotNull
@Unmodifiable
List<DiscordMessageEmbed> getEmbeds();
/**
* Gets the allowed mentions of the message.
* @return the allowed mentions in this message
* Gets the action rows.
* @return an unmodifiable list of action rows in this message
*/
@NotNull
@Unmodifiable
List<MessageActionRow> getActionRows();
/**
* Gets the allowed mentions of the message.
* @return the unmodifiable list of allowed mentions in this message
*/
@NotNull
@Unmodifiable
Set<AllowedMention> getAllowedMentions();
/**
@ -136,6 +148,33 @@ public interface SendableDiscordMessage {
@NotNull
Builder removeEmbed(DiscordMessageEmbed embed);
/**
* Gets the action rows for this builder.
* @return the action rows
*/
List<MessageActionRow> getActionRows();
/**
* Sets the action rows for this builder.
* @param rows the action rows
* @return the builder, useful for chaining
*/
Builder setActionRows(MessageActionRow... rows);
/**
* Adds an action row to this builder.
* @param row the action row
* @return the builder, useful for chaining
*/
Builder addActionRow(MessageActionRow row);
/**
* Removes an action row from this builder.
* @param row the action row
* @return the builder, useful for chaining
*/
Builder removeActionRow(MessageActionRow row);
/**
* Gets the allowed mentions in this builder.
* @return the builder's current allowed mentions

View File

@ -24,6 +24,7 @@
package com.discordsrv.api.discord.entity.message.impl;
import com.discordsrv.api.DiscordSRVApi;
import com.discordsrv.api.discord.entity.component.actionrow.MessageActionRow;
import com.discordsrv.api.discord.entity.message.AllowedMention;
import com.discordsrv.api.discord.entity.message.DiscordMessageEmbed;
import com.discordsrv.api.discord.entity.message.SendableDiscordMessage;
@ -44,21 +45,23 @@ public class SendableDiscordMessageImpl implements SendableDiscordMessage {
private final String content;
private final List<DiscordMessageEmbed> embeds;
private final List<MessageActionRow> actionRows;
private final Set<AllowedMention> allowedMentions;
private final String webhookUsername;
private final String webhookAvatarUrl;
protected SendableDiscordMessageImpl(
String content,
List<DiscordMessageEmbed> embeds,
List<MessageActionRow> actionRows,
Set<AllowedMention> allowedMentions,
String webhookUsername,
String webhookAvatarUrl
) {
this.content = content;
this.embeds = embeds;
this.allowedMentions = allowedMentions;
this.embeds = Collections.unmodifiableList(embeds);
this.actionRows = Collections.unmodifiableList(actionRows);
this.allowedMentions = Collections.unmodifiableSet(allowedMentions);
this.webhookUsername = webhookUsername;
this.webhookAvatarUrl = webhookAvatarUrl;
}
@ -73,6 +76,12 @@ public class SendableDiscordMessageImpl implements SendableDiscordMessage {
return embeds;
}
@NotNull
@Override
public List<MessageActionRow> getActionRows() {
return actionRows;
}
@Override
public @NotNull Set<AllowedMention> getAllowedMentions() {
return allowedMentions;
@ -92,6 +101,7 @@ public class SendableDiscordMessageImpl implements SendableDiscordMessage {
private String content;
private final List<DiscordMessageEmbed> embeds = new ArrayList<>();
private final List<MessageActionRow> actionRows = new ArrayList<>();
private final Set<AllowedMention> allowedMentions = new LinkedHashSet<>();
private String webhookUsername;
private String webhookAvatarUrl;
@ -109,7 +119,7 @@ public class SendableDiscordMessageImpl implements SendableDiscordMessage {
@Override
public @NotNull List<DiscordMessageEmbed> getEmbeds() {
return embeds;
return Collections.unmodifiableList(embeds);
}
@Override
@ -124,6 +134,30 @@ public class SendableDiscordMessageImpl implements SendableDiscordMessage {
return this;
}
@Override
public List<MessageActionRow> getActionRows() {
return actionRows;
}
@Override
public Builder setActionRows(MessageActionRow... rows) {
this.actionRows.clear();
this.actionRows.addAll(Arrays.asList(rows));
return this;
}
@Override
public Builder addActionRow(MessageActionRow row) {
this.actionRows.add(row);
return this;
}
@Override
public Builder removeActionRow(MessageActionRow row) {
this.actionRows.remove(row);
return this;
}
@Override
public @NotNull Set<AllowedMention> getAllowedMentions() {
return allowedMentions;
@ -172,7 +206,7 @@ public class SendableDiscordMessageImpl implements SendableDiscordMessage {
@Override
public @NotNull SendableDiscordMessage build() {
return new SendableDiscordMessageImpl(content, embeds, allowedMentions, webhookUsername, webhookAvatarUrl);
return new SendableDiscordMessageImpl(content, embeds, actionRows, allowedMentions, webhookUsername, webhookAvatarUrl);
}
@Override

View File

@ -42,7 +42,7 @@ public class DiscordChatMessageProcessingEvent implements Cancellable, Processab
public DiscordChatMessageProcessingEvent(@NotNull ReceivedDiscordMessage discordMessage, @NotNull DiscordMessageChannel channel) {
this.discordMessage = discordMessage;
this.messageContent = discordMessage.getContent().orElse(null);
this.messageContent = discordMessage.getContent();
this.channel = channel;
if (!(channel instanceof DiscordTextChannel) && !(channel instanceof DiscordThreadChannel)) {
throw new IllegalStateException("Cannot process messages that aren't from a text channel or thread");

View File

@ -35,7 +35,7 @@ import java.util.concurrent.CompletableFuture;
import java.util.function.Function;
import java.util.stream.Collectors;
public class AbstractDiscordThreadedGuildMessageChannel<T extends GuildMessageChannel & IThreadContainer>
public abstract class AbstractDiscordThreadedGuildMessageChannel<T extends GuildMessageChannel & IThreadContainer>
extends AbstractDiscordGuildMessageChannel<T>
implements DiscordGuildMessageChannel, DiscordThreadContainer {

View File

@ -19,11 +19,13 @@
package com.discordsrv.common.discord.api.entity.channel;
import club.minnced.discord.webhook.WebhookClient;
import com.discordsrv.api.discord.entity.channel.DiscordChannelType;
import com.discordsrv.api.discord.entity.channel.DiscordTextChannel;
import com.discordsrv.api.discord.entity.channel.DiscordThreadChannel;
import com.discordsrv.api.discord.entity.guild.DiscordGuild;
import com.discordsrv.common.DiscordSRV;
import com.discordsrv.common.discord.api.entity.guild.DiscordGuildImpl;
import net.dv8tion.jda.api.entities.ChannelType;
import net.dv8tion.jda.api.entities.IThreadContainer;
import net.dv8tion.jda.api.entities.TextChannel;
import net.dv8tion.jda.api.entities.ThreadChannel;
@ -77,4 +79,15 @@ public class DiscordThreadChannelImpl extends AbstractDiscordGuildMessageChannel
public ThreadChannel asJDA() {
return channel;
}
@Override
public DiscordChannelType getType() {
ChannelType type = channel.getType();
for (DiscordChannelType value : DiscordChannelType.values()) {
if (value.asJDA() == type) {
return value;
}
}
throw new IllegalStateException("Unknown channel type");
}
}

View File

@ -19,8 +19,8 @@
package com.discordsrv.common.discord.api.entity.component;
import com.discordsrv.api.discord.entity.DiscordUser;
import com.discordsrv.api.discord.entity.component.Interaction;
import com.discordsrv.api.discord.entity.component.Modal;
import com.discordsrv.api.discord.entity.Interaction;
import com.discordsrv.api.discord.entity.component.impl.Modal;
import com.discordsrv.api.discord.entity.message.ReceivedDiscordMessage;
import com.discordsrv.api.discord.entity.message.SendableDiscordMessage;
import com.discordsrv.common.DiscordSRV;

View File

@ -22,7 +22,6 @@ import club.minnced.discord.webhook.WebhookClient;
import club.minnced.discord.webhook.receive.ReadonlyAttachment;
import club.minnced.discord.webhook.receive.ReadonlyEmbed;
import club.minnced.discord.webhook.receive.ReadonlyMessage;
import club.minnced.discord.webhook.receive.ReadonlyUser;
import club.minnced.discord.webhook.send.WebhookEmbed;
import com.discordsrv.api.color.Color;
import com.discordsrv.api.discord.entity.DiscordUser;
@ -34,7 +33,6 @@ import com.discordsrv.api.discord.entity.guild.DiscordGuildMember;
import com.discordsrv.api.discord.entity.message.DiscordMessageEmbed;
import com.discordsrv.api.discord.entity.message.ReceivedDiscordMessage;
import com.discordsrv.api.discord.entity.message.SendableDiscordMessage;
import com.discordsrv.api.discord.entity.message.impl.SendableDiscordMessageImpl;
import com.discordsrv.api.discord.exception.RestErrorResponseException;
import com.discordsrv.api.placeholder.annotation.Placeholder;
import com.discordsrv.api.placeholder.annotation.PlaceholderRemainder;
@ -49,18 +47,17 @@ import com.discordsrv.common.future.util.CompletableFutureUtil;
import net.dv8tion.jda.api.entities.Member;
import net.dv8tion.jda.api.entities.Message;
import net.dv8tion.jda.api.entities.MessageEmbed;
import net.dv8tion.jda.api.entities.User;
import net.dv8tion.jda.api.requests.ErrorResponse;
import net.kyori.adventure.text.Component;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Unmodifiable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
public class ReceivedDiscordMessageImpl extends SendableDiscordMessageImpl implements ReceivedDiscordMessage {
public class ReceivedDiscordMessageImpl implements ReceivedDiscordMessage {
public static ReceivedDiscordMessage fromJDA(DiscordSRV discordSRV, Message message) {
List<DiscordMessageEmbed> mappedEmbeds = new ArrayList<>();
@ -69,9 +66,6 @@ public class ReceivedDiscordMessageImpl extends SendableDiscordMessageImpl imple
}
boolean webhookMessage = message.isWebhookMessage();
String webhookUsername = webhookMessage ? message.getAuthor().getName() : null;
String webhookAvatarUrl = webhookMessage ? message.getAuthor().getEffectiveAvatarUrl() : null;
DiscordMessageChannel channel = AbstractDiscordMessageChannel.get(discordSRV, message.getChannel());
DiscordUser user = new DiscordUserImpl(discordSRV, message.getAuthor());
@ -118,8 +112,7 @@ public class ReceivedDiscordMessageImpl extends SendableDiscordMessageImpl imple
message.getIdLong(),
message.getContentRaw(),
mappedEmbeds,
webhookUsername,
webhookAvatarUrl
webhookMessage
);
}
@ -155,13 +148,6 @@ public class ReceivedDiscordMessageImpl extends SendableDiscordMessageImpl imple
));
}
ReadonlyUser author = webhookMessage.getAuthor();
String authorId = Long.toUnsignedString(author.getId());
String avatarId = author.getAvatarId();
String avatarUrl = avatarId != null
? String.format(User.AVATAR_URL, authorId, avatarId, avatarId.startsWith("a_") ? "gif" : "png")
: String.format(User.DEFAULT_AVATAR_URL, Integer.parseInt(author.getDiscriminator()) % 5);
DiscordMessageChannel channel = discordSRV.discordAPI().getMessageChannelById(
webhookMessage.getChannelId()).orElse(null);
DiscordUser user = discordSRV.discordAPI().getUserById(
@ -191,8 +177,7 @@ public class ReceivedDiscordMessageImpl extends SendableDiscordMessageImpl imple
webhookMessage.getId(),
webhookMessage.getContent(),
mappedEmbeds,
author.getName(),
avatarUrl
true
);
}
@ -203,6 +188,9 @@ public class ReceivedDiscordMessageImpl extends SendableDiscordMessageImpl imple
private final ReceivedDiscordMessage replyingTo;
private final DiscordGuildMember member;
private final DiscordUser author;
private final String content;
private final List<DiscordMessageEmbed> embeds;
private final boolean webhookMessage;
private final long channelId;
private final long id;
@ -218,10 +206,8 @@ public class ReceivedDiscordMessageImpl extends SendableDiscordMessageImpl imple
long id,
String content,
List<DiscordMessageEmbed> embeds,
String webhookUsername,
String webhookAvatarUrl
boolean webhookMessage
) {
super(content, embeds, Collections.emptySet(), webhookUsername, webhookAvatarUrl);
this.discordSRV = discordSRV;
this.attachments = attachments;
this.fromSelf = fromSelf;
@ -229,6 +215,9 @@ public class ReceivedDiscordMessageImpl extends SendableDiscordMessageImpl imple
this.replyingTo = replyingTo;
this.member = member;
this.author = author;
this.content = content;
this.embeds = embeds;
this.webhookMessage = webhookMessage;
this.channelId = channelId;
this.id = id;
}
@ -238,6 +227,21 @@ public class ReceivedDiscordMessageImpl extends SendableDiscordMessageImpl imple
return id;
}
@Override
public @NotNull String getContent() {
return content;
}
@Override
public @NotNull @Unmodifiable List<DiscordMessageEmbed> getEmbeds() {
return embeds;
}
@Override
public boolean isWebhookMessage() {
return webhookMessage;
}
@Override
public @NotNull String getJumpUrl() {
return String.format(
@ -299,12 +303,12 @@ public class ReceivedDiscordMessageImpl extends SendableDiscordMessageImpl imple
return CompletableFutureUtil.failed(new RestErrorResponseException(ErrorResponse.UNKNOWN_CHANNEL));
}
return textChannel.deleteMessageById(getId(), fromSelf && getWebhookUsername().isPresent());
return textChannel.deleteMessageById(getId(), fromSelf && webhookMessage);
}
@Override
public @NotNull CompletableFuture<ReceivedDiscordMessage> edit(SendableDiscordMessage message) {
if (!isWebhookMessage() && message.isWebhookMessage()) {
if (!webhookMessage && message.isWebhookMessage()) {
throw new IllegalArgumentException("Cannot edit a non-webhook message into a webhook message");
}

View File

@ -269,8 +269,7 @@ public class DiscordMessageMirroringModule extends AbstractModule<DiscordSRV> {
ReceivedDiscordMessage replyMessage = message.getReplyingTo().orElse(null);
String content = message.getContent()
.map(c -> c.replace("[", "\\[")) // Block markdown urls
.orElse("");
.replace("[", "\\["); // Block markdown urls
if (replyMessage != null) {
MessageReference matchingReference = null;

View File

@ -52,7 +52,7 @@ dependencyResolutionManagement {
library('findbugs-annotations', 'com.google.code.findbugs', 'jsr305').version('3.0.2')
// JDA
library('jda', 'net.dv8tion', 'JDA').version('5.0.0-alpha.11')
library('jda', 'net.dv8tion', 'JDA').version('5.0.0-alpha.13')
library('okhttp', 'com.squareup.okhttp3', 'okhttp').version {
prefer '3.12.13'
reject '[4,)' // Kotlin