Add replying to api, use reply in console handler

This commit is contained in:
Vankka 2023-10-06 22:08:51 +03:00
parent 4a04bd7dcd
commit 097edb4d9e
No known key found for this signature in database
GPG Key ID: 6E50CB7A29B96AD0
7 changed files with 159 additions and 36 deletions

View File

@ -159,6 +159,15 @@ public interface ReceivedDiscordMessage extends Snowflake {
*/
CompletableFuture<ReceivedDiscordMessage> edit(@NotNull SendableDiscordMessage message);
/**
* Send the provided message in the channel this message was sent in, replying to this message.
*
* @param message the message
* @return a future that will fail if the request fails, otherwise the new message provided by the request response
* @throws IllegalArgumentException if the provided message is a webhook message
*/
CompletableFuture<ReceivedDiscordMessage> reply(@NotNull SendableDiscordMessage message);
class Attachment {
private final String fileName;

View File

@ -109,6 +109,12 @@ public interface SendableDiscordMessage {
return getWebhookUsername() != null;
}
/**
* Gets the raw inputs streams and file names for attachments, for this message.
* @return the map of input streams to file names
*/
Map<InputStream, String> getAttachments();
/**
* If notifications for this message are suppressed.
* @return if sending this message doesn't cause a notification
@ -121,7 +127,19 @@ public interface SendableDiscordMessage {
*/
boolean isSuppressedEmbeds();
Map<InputStream, String> getAttachments();
/**
* Gets the id for the message this message is in reply to
* @return the message id
*/
Long getMessageIdToReplyTo();
/**
* Creates a copy of this {@link SendableDiscordMessage} with the specified reply message id.
*
* @param replyingToMessageId the reply message id
* @return a new {@link SendableDiscordMessage} identical to the current instance except for the reply message id
*/
SendableDiscordMessage withReplyingToMessageId(Long replyingToMessageId);
@SuppressWarnings("UnusedReturnValue") // API
interface Builder {
@ -262,12 +280,6 @@ public interface SendableDiscordMessage {
*/
Builder addAttachment(InputStream inputStream, String fileName);
/**
* Checks if this builder has any sendable content.
* @return {@code true} if there is no sendable content
*/
boolean isEmpty();
/**
* Sets if this message's notifications will be suppressed.
* @param suppressedNotifications if notifications should be suppressed
@ -294,6 +306,34 @@ public interface SendableDiscordMessage {
*/
boolean isSuppressedEmbeds();
/**
* Sets the message this message should be in reply to.
* @param messageId the id for the message this is in reply to
* @return this builder, useful for chaining
*/
Builder setMessageIdToReplyTo(Long messageId);
/**
* Sets the message this message should be in reply to.
* @param message the message this is in reply to
* @return this builder, useful for chaining
*/
default Builder setMessageToReplyTo(@NotNull ReceivedDiscordMessage message) {
return setMessageIdToReplyTo(message.getId());
}
/**
* Gets the id for the message this message is in reply to
* @return the message id
*/
Long getMessageIdToReplyTo();
/**
* Checks if this builder has any sendable content.
* @return {@code true} if there is no sendable content
*/
boolean isEmpty();
/**
* Builds a {@link SendableDiscordMessage} from this builder.
* @return the new {@link SendableDiscordMessage}

View File

@ -51,9 +51,10 @@ public class SendableDiscordMessageImpl implements SendableDiscordMessage {
private final Set<AllowedMention> allowedMentions;
private final String webhookUsername;
private final String webhookAvatarUrl;
private final Map<InputStream, String> attachments;
private final boolean suppressedNotifications;
private final boolean suppressedEmbeds;
private final Map<InputStream, String> attachments;
private final Long replyingToMessageId;
protected SendableDiscordMessageImpl(
String content,
@ -62,9 +63,10 @@ public class SendableDiscordMessageImpl implements SendableDiscordMessage {
Set<AllowedMention> allowedMentions,
String webhookUsername,
String webhookAvatarUrl,
Map<InputStream, String> attachments,
boolean suppressedNotifications,
boolean suppressedEmbeds,
Map<InputStream, String> attachments
Long replyingToMessageId
) {
this.content = content;
this.embeds = Collections.unmodifiableList(embeds);
@ -72,9 +74,25 @@ public class SendableDiscordMessageImpl implements SendableDiscordMessage {
this.allowedMentions = Collections.unmodifiableSet(allowedMentions);
this.webhookUsername = webhookUsername;
this.webhookAvatarUrl = webhookAvatarUrl;
this.attachments = Collections.unmodifiableMap(attachments);
this.suppressedNotifications = suppressedNotifications;
this.suppressedEmbeds = suppressedEmbeds;
this.attachments = Collections.unmodifiableMap(attachments);
this.replyingToMessageId = replyingToMessageId;
}
public SendableDiscordMessageImpl withReplyingToMessageId(Long replyingToMessageId) {
return new SendableDiscordMessageImpl(
content,
embeds,
actionRows,
allowedMentions,
webhookUsername,
webhookAvatarUrl,
attachments,
suppressedNotifications,
suppressedEmbeds,
replyingToMessageId
);
}
@Override
@ -118,6 +136,11 @@ public class SendableDiscordMessageImpl implements SendableDiscordMessage {
return suppressedEmbeds;
}
@Override
public Long getMessageIdToReplyTo() {
return replyingToMessageId;
}
@Override
public Map<InputStream, String> getAttachments() {
return attachments;
@ -131,9 +154,10 @@ public class SendableDiscordMessageImpl implements SendableDiscordMessage {
private final Set<AllowedMention> allowedMentions = new LinkedHashSet<>();
private String webhookUsername;
private String webhookAvatarUrl;
private final Map<InputStream, String> attachments = new LinkedHashMap<>();
private boolean suppressedNotifications;
private boolean suppressedEmbeds;
private final Map<InputStream, String> attachments = new LinkedHashMap<>();
private Long replyingToMessageId;
@Override
public String getContent() {
@ -255,6 +279,17 @@ public class SendableDiscordMessageImpl implements SendableDiscordMessage {
return suppressedEmbeds;
}
@Override
public Builder setMessageIdToReplyTo(Long messageId) {
replyingToMessageId = messageId;
return this;
}
@Override
public Long getMessageIdToReplyTo() {
return replyingToMessageId;
}
@Override
public Builder setSuppressedEmbeds(boolean suppressedEmbeds) {
this.suppressedEmbeds = suppressedEmbeds;
@ -268,7 +303,7 @@ public class SendableDiscordMessageImpl implements SendableDiscordMessage {
@Override
public @NotNull SendableDiscordMessage build() {
return new SendableDiscordMessageImpl(content, embeds, actionRows, allowedMentions, webhookUsername, webhookAvatarUrl, suppressedNotifications, suppressedEmbeds, attachments);
return new SendableDiscordMessageImpl(content, embeds, actionRows, allowedMentions, webhookUsername, webhookAvatarUrl, attachments, suppressedNotifications, suppressedEmbeds, replyingToMessageId);
}
@Override

View File

@ -1,7 +1,10 @@
package com.discordsrv.common.console;
import com.discordsrv.api.discord.entity.DiscordUser;
import com.discordsrv.api.discord.entity.channel.*;
import com.discordsrv.api.discord.entity.channel.DiscordGuildChannel;
import com.discordsrv.api.discord.entity.channel.DiscordGuildMessageChannel;
import com.discordsrv.api.discord.entity.channel.DiscordMessageChannel;
import com.discordsrv.api.discord.entity.channel.DiscordThreadChannel;
import com.discordsrv.api.discord.entity.guild.DiscordGuildMember;
import com.discordsrv.api.discord.entity.message.ReceivedDiscordMessage;
import com.discordsrv.api.discord.entity.message.SendableDiscordMessage;
@ -22,9 +25,7 @@ import net.dv8tion.jda.api.entities.Message;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.exception.ExceptionUtils;
import java.util.ArrayList;
import java.util.List;
import java.util.Queue;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Future;
import java.util.concurrent.LinkedBlockingQueue;
@ -48,6 +49,9 @@ public class SingleConsoleHandler {
private final Object sendLock = new Object();
private CompletableFuture<?> sendFuture;
// Don't annoy console users twice about using /
private final Set<Long> warnedSlashUsageUserIds = new HashSet<>();
public SingleConsoleHandler(DiscordSRV discordSRV, Logger logger, ConsoleConfig config) {
this.discordSRV = discordSRV;
this.logger = logger;
@ -102,12 +106,24 @@ public class SingleConsoleHandler {
GameCommandExecutionHelper helper = discordSRV.executeHelper();
if (command.startsWith("/") && config.commandExecution.enableSlashWarning) {
// TODO: reply, translation
messageChannel.sendMessage(
SendableDiscordMessage.builder()
.setContent("Your command was prefixed with `/`, but normally commands in the Minecraft server console should **not** begin with `/`")
.build()
);
long userId = user.getId();
boolean newUser;
synchronized (warnedSlashUsageUserIds) {
newUser = !warnedSlashUsageUserIds.contains(userId);
if (newUser) {
warnedSlashUsageUserIds.add(userId);
}
}
if (newUser) {
// TODO: translation
message.reply(
SendableDiscordMessage.builder()
.setContent("Your command was prefixed with `/`, but normally commands in the Minecraft server console should **not** begin with `/`")
.build()
);
}
}
boolean pass = false;
@ -119,8 +135,8 @@ public class SingleConsoleHandler {
}
if (!pass) {
if (!user.isBot()) {
// TODO: reply, translation
messageChannel.sendMessage(
// TODO: translation
message.reply(
SendableDiscordMessage.builder()
.setContent("You are not allowed to run that command")
.build()

View File

@ -29,6 +29,7 @@ import net.dv8tion.jda.api.entities.Message;
import net.dv8tion.jda.api.entities.WebhookClient;
import net.dv8tion.jda.api.entities.channel.middleman.GuildMessageChannel;
import net.dv8tion.jda.api.requests.RestAction;
import net.dv8tion.jda.api.requests.restaction.MessageCreateAction;
import net.dv8tion.jda.api.utils.messages.MessageCreateData;
import net.dv8tion.jda.api.utils.messages.MessageCreateRequest;
import net.dv8tion.jda.api.utils.messages.MessageEditData;
@ -89,11 +90,15 @@ public abstract class AbstractDiscordGuildMessageChannel<T extends GuildMessageC
.setAvatarUrl(message.getWebhookAvatarUrl())
);
} else {
createRequest = CompletableFuture.completedFuture(
((R) channel.sendMessage(createData)
// JDA doesn't properly grab this from MessageCreateData
.setSuppressEmbeds(createData.isSuppressEmbeds()))
);
MessageCreateAction action = channel.sendMessage(createData)
// JDA doesn't properly grab this from MessageCreateData
.setSuppressEmbeds(createData.isSuppressEmbeds());
Long referencedMessageId = message.getMessageIdToReplyTo();
if (referencedMessageId != null) {
action = action.setMessageReference(referencedMessageId);
}
createRequest = CompletableFuture.completedFuture((R) action);
}
return createRequest

View File

@ -56,9 +56,13 @@ public class DiscordDMChannelImpl extends AbstractDiscordMessageChannel<PrivateC
MessageCreateAction action = channel.sendMessage(SendableDiscordMessageUtil.toJDASend(message));
Long referencedMessageId = message.getMessageIdToReplyTo();
if (referencedMessageId != null) {
action = action.setMessageReference(referencedMessageId);
}
CompletableFuture<ReceivedDiscordMessage> future = action.submit()
.thenApply(msg -> ReceivedDiscordMessageImpl.fromJDA(discordSRV, msg));
return discordSRV.discordAPI().mapExceptions(future);
}

View File

@ -226,12 +226,12 @@ public class ReceivedDiscordMessageImpl implements ReceivedDiscordMessage {
@Override
public @NotNull CompletableFuture<Void> delete() {
DiscordTextChannel textChannel = discordSRV.discordAPI().getTextChannelById(channelId);
if (textChannel == null) {
DiscordMessageChannel messageChannel = discordSRV.discordAPI().getMessageChannelById(channelId);
if (messageChannel == null) {
return CompletableFutureUtil.failed(new RestErrorResponseException(ErrorResponse.UNKNOWN_CHANNEL));
}
return textChannel.deleteMessageById(getId(), fromSelf && webhookMessage);
return messageChannel.deleteMessageById(getId(), fromSelf && webhookMessage);
}
@Override
@ -242,12 +242,26 @@ public class ReceivedDiscordMessageImpl implements ReceivedDiscordMessage {
throw new IllegalArgumentException("Cannot edit a non-webhook message into a webhook message");
}
DiscordTextChannel textChannel = discordSRV.discordAPI().getTextChannelById(channelId);
if (textChannel == null) {
DiscordMessageChannel messageChannel = discordSRV.discordAPI().getMessageChannelById(channelId);
if (messageChannel == null) {
return CompletableFutureUtil.failed(new RestErrorResponseException(ErrorResponse.UNKNOWN_CHANNEL));
}
return textChannel.editMessageById(getId(), message);
return messageChannel.editMessageById(getId(), message);
}
@Override
public CompletableFuture<ReceivedDiscordMessage> reply(@NotNull SendableDiscordMessage message) {
if (message.isWebhookMessage()) {
throw new IllegalStateException("Webhook messages cannot be used as replies");
}
DiscordMessageChannel messageChannel = discordSRV.discordAPI().getMessageChannelById(channelId);
if (messageChannel == null) {
return CompletableFutureUtil.failed(new RestErrorResponseException(ErrorResponse.UNKNOWN_CHANNEL));
}
return messageChannel.sendMessage(message.withReplyingToMessageId(id));
}
//