mirror of
https://github.com/Sheldan/abstracto.git
synced 2026-03-27 14:23:56 +00:00
refactored the naming in channel service
added initial version of closing and logging the thread
This commit is contained in:
@@ -34,7 +34,7 @@ public class AnonReply extends AbstractConditionableCommand {
|
||||
@Override
|
||||
public CommandResult execute(CommandContext commandContext) {
|
||||
ModMailThread thread = modMailThreadManagementService.getByChannel(commandContext.getUserInitiatedContext().getChannel());
|
||||
modMailThreadService.relayMessageToDm(thread, commandContext.getMessage(), true);
|
||||
modMailThreadService.relayMessageToDm(thread, commandContext.getMessage(), true, commandContext.getChannel());
|
||||
return CommandResult.fromSuccess();
|
||||
}
|
||||
|
||||
|
||||
@@ -3,14 +3,20 @@ package dev.sheldan.abstracto.modmail.commands;
|
||||
import dev.sheldan.abstracto.core.command.condition.AbstractConditionableCommand;
|
||||
import dev.sheldan.abstracto.core.command.condition.CommandCondition;
|
||||
import dev.sheldan.abstracto.core.command.config.CommandConfiguration;
|
||||
import dev.sheldan.abstracto.core.command.config.HelpInfo;
|
||||
import dev.sheldan.abstracto.core.command.execution.CommandContext;
|
||||
import dev.sheldan.abstracto.core.command.execution.CommandResult;
|
||||
import dev.sheldan.abstracto.core.config.FeatureEnum;
|
||||
import dev.sheldan.abstracto.modmail.commands.condition.RequiresModMailCondition;
|
||||
import dev.sheldan.abstracto.modmail.config.ModMailFeatures;
|
||||
import dev.sheldan.abstracto.modmail.models.database.ModMailThread;
|
||||
import dev.sheldan.abstracto.modmail.service.ModMailThreadService;
|
||||
import dev.sheldan.abstracto.modmail.service.management.ModMailThreadManagementService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@Component
|
||||
@@ -19,14 +25,32 @@ public class Close extends AbstractConditionableCommand {
|
||||
@Autowired
|
||||
private RequiresModMailCondition requiresModMailCondition;
|
||||
|
||||
@Autowired
|
||||
private ModMailThreadManagementService modMailThreadManagementService;
|
||||
|
||||
@Autowired
|
||||
private ModMailThreadService modMailThreadService;
|
||||
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public CommandResult execute(CommandContext commandContext) {
|
||||
return null;
|
||||
ModMailThread thread = modMailThreadManagementService.getByChannel(commandContext.getUserInitiatedContext().getChannel());
|
||||
modMailThreadService.closeModMailThread(thread, commandContext.getChannel());
|
||||
return CommandResult.fromSuccess();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommandConfiguration getConfiguration() {
|
||||
return null;
|
||||
HelpInfo helpInfo = HelpInfo.builder().templated(true).build();
|
||||
return CommandConfiguration.builder()
|
||||
.name("close")
|
||||
.module(ModMailModuleInterface.MODMAIL)
|
||||
.parameters(new ArrayList<>())
|
||||
.help(helpInfo)
|
||||
.templated(true)
|
||||
.causesReaction(true)
|
||||
.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -53,7 +53,7 @@ public class Contact extends AbstractConditionableCommand {
|
||||
ModMailThreadExistsModel model = (ModMailThreadExistsModel) ContextConverter.fromCommandContext(commandContext, ModMailThreadExistsModel.class);
|
||||
model.setExistingModMailThread(existingThread);
|
||||
MessageToSend messageToSend = templateService.renderEmbedTemplate("modmail_thread_already_exists", model);
|
||||
channelService.sendMessageToEndInTextChannel(messageToSend, commandContext.getChannel());
|
||||
channelService.sendMessageToSendToChannel(messageToSend, commandContext.getChannel());
|
||||
} else {
|
||||
FullUser fullUser = FullUser
|
||||
.builder()
|
||||
|
||||
@@ -34,7 +34,7 @@ public class Reply extends AbstractConditionableCommand {
|
||||
@Override
|
||||
public CommandResult execute(CommandContext commandContext) {
|
||||
ModMailThread thread = modMailThreadManagementService.getByChannel(commandContext.getUserInitiatedContext().getChannel());
|
||||
modMailThreadService.relayMessageToDm(thread, commandContext.getMessage(), false);
|
||||
modMailThreadService.relayMessageToDm(thread, commandContext.getMessage(), false, commandContext.getChannel());
|
||||
return CommandResult.fromSuccess();
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
package dev.sheldan.abstracto.modmail.config;
|
||||
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.PropertySource;
|
||||
|
||||
@Configuration
|
||||
@PropertySource("classpath:config/modmail.properties")
|
||||
public class ModMailProperties {
|
||||
}
|
||||
@@ -1,9 +1,13 @@
|
||||
package dev.sheldan.abstracto.modmail.repository;
|
||||
|
||||
import dev.sheldan.abstracto.modmail.models.database.ModMailMessage;
|
||||
import dev.sheldan.abstracto.modmail.models.database.ModMailThread;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Repository
|
||||
public interface ModMailMessageRepository extends JpaRepository<ModMailMessage, Long> {
|
||||
List<ModMailMessage> findByThreadReference(ModMailThread modMailThread);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
package dev.sheldan.abstracto.modmail.service;
|
||||
|
||||
import dev.sheldan.abstracto.core.models.ServerChannelMessage;
|
||||
import dev.sheldan.abstracto.core.service.BotService;
|
||||
import dev.sheldan.abstracto.modmail.models.database.ModMailMessage;
|
||||
import dev.sheldan.abstracto.modmail.models.database.ModMailThread;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import net.dv8tion.jda.api.entities.Message;
|
||||
import net.dv8tion.jda.api.entities.TextChannel;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
@Component
|
||||
@Slf4j
|
||||
public class ModMailMessageServiceBean implements ModMailMessageService {
|
||||
|
||||
@Autowired
|
||||
private BotService botService;
|
||||
|
||||
@Override
|
||||
public List<CompletableFuture<Message>> loadModMailMessages(List<ModMailMessage> modMailMessages) {
|
||||
if(modMailMessages.size() == 0) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
ModMailThread thread = modMailMessages.get(0).getThreadReference();
|
||||
List<ServerChannelMessage> messageIds = new ArrayList<>();
|
||||
modMailMessages.forEach(modMailMessage -> {
|
||||
ServerChannelMessage.ServerChannelMessageBuilder serverChannelMessageBuilder = ServerChannelMessage
|
||||
.builder()
|
||||
.messageId(modMailMessage.getMessageId());
|
||||
if(!modMailMessage.getDmChannel()) {
|
||||
serverChannelMessageBuilder
|
||||
.channelId(modMailMessage.getThreadReference().getChannel().getId())
|
||||
.serverId(modMailMessage.getThreadReference().getServer().getId());
|
||||
}
|
||||
messageIds.add(serverChannelMessageBuilder.build());
|
||||
});
|
||||
List<CompletableFuture<Message>> messageFutures = new ArrayList<>();
|
||||
modMailMessages.forEach(modMailMessage -> {
|
||||
messageFutures.add(new CompletableFuture<>());
|
||||
});
|
||||
Optional<TextChannel> textChannelFromServer = botService.getTextChannelFromServer(thread.getServer().getId(), thread.getChannel().getId());
|
||||
if(textChannelFromServer.isPresent()) {
|
||||
TextChannel modMailThread = textChannelFromServer.get();
|
||||
botService.getInstance().openPrivateChannelById(thread.getUser().getUserReference().getId()).queue(privateChannel -> {
|
||||
Iterator<CompletableFuture<Message>> iterator = messageFutures.iterator();
|
||||
messageIds.forEach(serverChannelMessage -> {
|
||||
if(serverChannelMessage.getChannelId() == null){
|
||||
privateChannel.retrieveMessageById(serverChannelMessage.getMessageId()).queue(message -> {
|
||||
iterator.next().complete(message);
|
||||
}, throwable -> {
|
||||
log.info("Failed to load message in private channel with user {}", thread.getUser().getUserReference().getId());
|
||||
iterator.next().complete(null);
|
||||
});
|
||||
} else {
|
||||
modMailThread.retrieveMessageById(serverChannelMessage.getMessageId()).queue(message -> {
|
||||
iterator.next().complete(message);
|
||||
}, throwable -> {
|
||||
log.info("Failed to load message {} in thread {}", serverChannelMessage.getMessageId(), modMailThread.getIdLong());
|
||||
iterator.next().complete(null);
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
return messageFutures;
|
||||
}
|
||||
}
|
||||
@@ -5,13 +5,14 @@ import com.jagrosh.jdautilities.menu.ButtonMenu;
|
||||
import dev.sheldan.abstracto.core.models.FullGuild;
|
||||
import dev.sheldan.abstracto.core.models.FullUser;
|
||||
import dev.sheldan.abstracto.core.models.database.*;
|
||||
import dev.sheldan.abstracto.core.service.BotService;
|
||||
import dev.sheldan.abstracto.core.service.ChannelService;
|
||||
import dev.sheldan.abstracto.core.service.ConfigService;
|
||||
import dev.sheldan.abstracto.core.service.*;
|
||||
import dev.sheldan.abstracto.core.service.management.ChannelManagementService;
|
||||
import dev.sheldan.abstracto.core.service.management.UserInServerManagementService;
|
||||
import dev.sheldan.abstracto.modmail.models.database.ModMailMessage;
|
||||
import dev.sheldan.abstracto.modmail.models.database.ModMailThread;
|
||||
import dev.sheldan.abstracto.modmail.models.database.ModMailThreadState;
|
||||
import dev.sheldan.abstracto.modmail.models.dto.ServerChoice;
|
||||
import dev.sheldan.abstracto.modmail.models.template.ModMailExceptionModel;
|
||||
import dev.sheldan.abstracto.modmail.models.template.ModMailModeratorReplyModel;
|
||||
import dev.sheldan.abstracto.modmail.models.template.ModMailServerChooserModel;
|
||||
import dev.sheldan.abstracto.modmail.service.management.ModMailMessageManagementService;
|
||||
@@ -33,6 +34,7 @@ import java.util.concurrent.ExecutionException;
|
||||
public class ModMailThreadServiceBean implements ModMailThreadService {
|
||||
|
||||
public static final String MODMAIL_CATEGORY = "modmailCategory";
|
||||
public static final String MODMAIL_LOG_POSTTARGET = "modmaillog";
|
||||
@Autowired
|
||||
private ModMailThreadManagementService modMailThreadManagementService;
|
||||
|
||||
@@ -57,6 +59,12 @@ public class ModMailThreadServiceBean implements ModMailThreadService {
|
||||
@Autowired
|
||||
private ModMailMessageManagementService modMailMessageManagementService;
|
||||
|
||||
@Autowired
|
||||
private ModMailMessageService modMailMessageService;
|
||||
|
||||
@Autowired
|
||||
private PostTargetService postTargetService;
|
||||
|
||||
@Autowired
|
||||
private ModMailThreadServiceBean self;
|
||||
|
||||
@@ -147,7 +155,9 @@ public class ModMailThreadServiceBean implements ModMailThreadService {
|
||||
@Override
|
||||
public void sendWelcomeMessage(TextChannel channel, FullUser aUserInAServer) {
|
||||
String text = templateService.renderTemplate("modmail_welcome_message", new Object());
|
||||
channel.sendMessage(text).queue();
|
||||
channel.sendMessage(text).queue(message -> {}, throwable -> {
|
||||
log.warn("Failed to send welcome message to user {}", aUserInAServer.getAUserInAServer().getUserReference().getId());
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -172,8 +182,8 @@ public class ModMailThreadServiceBean implements ModMailThreadService {
|
||||
.postedMessage(message)
|
||||
.threadUser(fullUser)
|
||||
.build();
|
||||
MessageToSend messageToSend = templateService.renderEmbedTemplate("modmail_user_message_embed", modMailUserReplyModel);
|
||||
List<CompletableFuture<Message>> completableFutures = channelService.sendMessageToEndInTextChannel(messageToSend, textChannel);
|
||||
MessageToSend messageToSend = templateService.renderEmbedTemplate("modmail_user_message", modMailUserReplyModel);
|
||||
List<CompletableFuture<Message>> completableFutures = channelService.sendMessageToSendToChannel(messageToSend, textChannel);
|
||||
List<Message> messages = new ArrayList<>();
|
||||
completableFutures.forEach(messageCompletableFuture -> {
|
||||
try {
|
||||
@@ -182,22 +192,104 @@ public class ModMailThreadServiceBean implements ModMailThreadService {
|
||||
} catch (InterruptedException | ExecutionException e) {
|
||||
log.error("Error while executing future to retrieve reaction.", e);
|
||||
}
|
||||
self.saveMessageIds(messages, modMailThread, modMailThread.getUser(), false);
|
||||
self.saveMessageIds(messages, modMailThread, modMailThread.getUser(), false, false);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void relayMessageToDm(ModMailThread modMailThread, Message message, Boolean anonymous) {
|
||||
public void relayMessageToDm(ModMailThread modMailThread, Message message, Boolean anonymous, MessageChannel feedBack) {
|
||||
User userById = botService.getInstance().getUserById(modMailThread.getUser().getUserReference().getId());
|
||||
if(userById != null) {
|
||||
userById.openPrivateChannel().queue(privateChannel -> {
|
||||
self.sendReply(modMailThread, message, privateChannel, anonymous);
|
||||
self.sendReply(modMailThread, message, privateChannel, anonymous, feedBack);
|
||||
}, throwable -> {
|
||||
log.warn("Failed to open private channel with user {}", userById.getIdLong());
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private void sendModMailFailure(String template, ModMailThread modMailThread, MessageChannel channel, Throwable throwable) {
|
||||
try {
|
||||
FullUser fullUser = FullUser
|
||||
.builder()
|
||||
.aUserInAServer(modMailThread.getUser())
|
||||
.member(botService.getMemberInServer(modMailThread.getUser()))
|
||||
.build();
|
||||
ModMailExceptionModel modMailExceptionModel = ModMailExceptionModel
|
||||
.builder()
|
||||
.modMailThread(modMailThread)
|
||||
.user(fullUser)
|
||||
.throwable(throwable)
|
||||
.build();
|
||||
channelService.sendTemplateInChannel(template, modMailExceptionModel, channel);
|
||||
} catch (Exception e) {
|
||||
log.error("Failed to notify about mod mail exception.", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void closeModMailThread(ModMailThread modMailThread, MessageChannel feedBack) {
|
||||
List<ModMailMessage> modMailMessages = modMailMessageManagementService.getMessagesOfThread(modMailThread);
|
||||
|
||||
List<CompletableFuture<Message>> messages = modMailMessageService.loadModMailMessages(modMailMessages);
|
||||
for (int i = 0; i < messages.size(); i++) {
|
||||
CompletableFuture<Message> messageCompletableFuture = messages.get(i);
|
||||
Long messageId = modMailMessages.get(i).getMessageId();
|
||||
messageCompletableFuture.exceptionally(throwable -> {
|
||||
log.warn("Failed to load message {} in mod mail thread {}", messageId, modMailThread.getId());
|
||||
return null;
|
||||
});
|
||||
}
|
||||
CompletableFuture.allOf(messages.toArray(new CompletableFuture[0])).thenAccept(aVoid -> {
|
||||
handledRetrievedMessages(modMailThread, messages);
|
||||
}).exceptionally(throwable -> {
|
||||
log.warn("Failed to load some mod mail messages for mod mail thread {}. Still trying to post the ones we got.", modMailThread.getId(), throwable);
|
||||
handledRetrievedMessages(modMailThread, messages);
|
||||
return null;
|
||||
});
|
||||
|
||||
|
||||
}
|
||||
|
||||
private void handledRetrievedMessages(ModMailThread modMailThread, List<CompletableFuture<Message>> messages) {
|
||||
List<Message> loadedMessages = new ArrayList<>();
|
||||
messages.forEach(future -> {
|
||||
try {
|
||||
if(!future.isCompletedExceptionally()) {
|
||||
Message loadedMessage = future.get();
|
||||
if(loadedMessage != null) {
|
||||
loadedMessages.add(loadedMessage);
|
||||
}
|
||||
}
|
||||
} catch (InterruptedException | ExecutionException e) {
|
||||
log.error("Error while executing future to retrieve reaction.", e);
|
||||
}
|
||||
});
|
||||
List<CompletableFuture<Message>> completableFutures = self.sendMessagesToPostTarget(modMailThread, loadedMessages);
|
||||
CompletableFuture.allOf(completableFutures.toArray(new CompletableFuture[0])).thenAccept(aVoid -> {
|
||||
channelService.deleteTextChannel(modMailThread.getChannel()).thenAccept(o -> {
|
||||
self.closeModMailThreadInDb(modMailThread);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public void sendReply(ModMailThread modMailThread, Message message, PrivateChannel privateChannel, Boolean anonymous) {
|
||||
public void closeModMailThreadInDb(ModMailThread modMailThread) {
|
||||
modMailThreadManagementService.setModMailThreadState(modMailThread, ModMailThreadState.CLOSED);
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public List<CompletableFuture<Message>> sendMessagesToPostTarget(ModMailThread modMailThread, List<Message> loadedMessages) {
|
||||
List<CompletableFuture<Message>> messageFutures = new ArrayList<>();
|
||||
loadedMessages.forEach(message -> {
|
||||
CompletableFuture<Message> logFuture = postTargetService.sendMessageInPostTarget(message, MODMAIL_LOG_POSTTARGET, modMailThread.getServer().getId());
|
||||
messageFutures.add(logFuture);
|
||||
});
|
||||
return messageFutures;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public void sendReply(ModMailThread modMailThread, Message message, PrivateChannel privateChannel, Boolean anonymous, MessageChannel feedBack) {
|
||||
AUserInAServer moderator = userInServerManagementService.loadUser(message.getMember());
|
||||
Member userInGuild = botService.getMemberInServer(modMailThread.getUser());
|
||||
FullUser moderatorUser = FullUser
|
||||
@@ -218,7 +310,7 @@ public class ModMailThreadServiceBean implements ModMailThreadService {
|
||||
.moderator(moderatorUser)
|
||||
.build();
|
||||
MessageToSend messageToSend = templateService.renderEmbedTemplate("modmail_staff_message", modMailUserReplyModel);
|
||||
List<CompletableFuture<Message>> completableFutures = channelService.sendMessageToEndInTextChannel(messageToSend, privateChannel);
|
||||
List<CompletableFuture<Message>> completableFutures = channelService.sendMessageToSendToChannel(messageToSend, privateChannel);
|
||||
CompletableFuture.allOf(completableFutures.toArray(new CompletableFuture[0])).thenAccept(aVoid -> {
|
||||
List<Message> messages = new ArrayList<>();
|
||||
completableFutures.forEach(messageCompletableFuture -> {
|
||||
@@ -226,17 +318,21 @@ public class ModMailThreadServiceBean implements ModMailThreadService {
|
||||
Message messageToAdd = messageCompletableFuture.get();
|
||||
messages.add(messageToAdd);
|
||||
} catch (InterruptedException | ExecutionException e) {
|
||||
log.error("Error while executing send message to reply to user.", e);
|
||||
log.error("A future when sending the message to the user was interrupted.", e);
|
||||
}
|
||||
});
|
||||
self.saveMessageIds(messages, modMailThread, moderator, anonymous);
|
||||
self.saveMessageIds(messages, modMailThread, moderator, anonymous, true);
|
||||
}).exceptionally(throwable -> {
|
||||
log.error("Failed to send message to user {}", modMailThread.getUser().getUserReference().getId());
|
||||
sendModMailFailure("modmail_exception_cannot_message_user", modMailThread, feedBack, throwable);
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public void saveMessageIds(List<Message> messages, ModMailThread modMailThread, AUserInAServer author, Boolean anonymous) {
|
||||
public void saveMessageIds(List<Message> messages, ModMailThread modMailThread, AUserInAServer author, Boolean anonymous, Boolean inDmChannel) {
|
||||
messages.forEach(message -> {
|
||||
modMailMessageManagementService.addMessageToThread(modMailThread, message, author, false);
|
||||
modMailMessageManagementService.addMessageToThread(modMailThread, message, author, anonymous, inDmChannel);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,8 @@ import net.dv8tion.jda.api.entities.Message;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Component
|
||||
public class ModMailMessageManagementServiceBean implements ModMailMessageManagementService {
|
||||
|
||||
@@ -15,11 +17,12 @@ public class ModMailMessageManagementServiceBean implements ModMailMessageManage
|
||||
private ModMailMessageRepository modMailMessageRepository;
|
||||
|
||||
@Override
|
||||
public ModMailMessage addMessageToThread(ModMailThread modMailThread, Message message, AUserInAServer author, Boolean anonymous) {
|
||||
public ModMailMessage addMessageToThread(ModMailThread modMailThread, Message message, AUserInAServer author, Boolean anonymous, Boolean dmChannel) {
|
||||
ModMailMessage modMailMessage = ModMailMessage
|
||||
.builder()
|
||||
.author(author)
|
||||
.messageId(message.getIdLong())
|
||||
.dmChannel(dmChannel)
|
||||
.threadReference(modMailThread)
|
||||
.anonymous(anonymous)
|
||||
.build();
|
||||
@@ -27,4 +30,9 @@ public class ModMailMessageManagementServiceBean implements ModMailMessageManage
|
||||
modMailMessageRepository.save(modMailMessage);
|
||||
return modMailMessage;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ModMailMessage> getMessagesOfThread(ModMailThread modMailThread) {
|
||||
return modMailMessageRepository.findByThreadReference(modMailThread);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -67,4 +67,9 @@ public class ModMailThreadManagementServiceBean implements ModMailThreadManageme
|
||||
|
||||
modMailThreadRepository.save(thread);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setModMailThreadState(ModMailThread modMailThread, ModMailThreadState newState) {
|
||||
modMailThread.setState(newState);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
abstracto.postTargets.modmail=modmaillog
|
||||
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"author": {
|
||||
"name": "${user.member.effectiveName}",
|
||||
"avatar": "${user.member.user.effectiveAvatarUrl}"
|
||||
},
|
||||
"color" : {
|
||||
"r": 200,
|
||||
"g": 0,
|
||||
"b": 255
|
||||
},
|
||||
"description": "<#include "reply_exception_cannot_send_message_to_use_en_US.ftl">"
|
||||
}
|
||||
Reference in New Issue
Block a user