mirror of
https://github.com/Sheldan/abstracto.git
synced 2026-01-25 03:04:50 +00:00
added current values to post post target
added support for custom setup steps for each feature added mod mail category setup step
This commit is contained in:
@@ -0,0 +1,28 @@
|
||||
package dev.sheldan.abstracto.modmail.setup;
|
||||
|
||||
import dev.sheldan.abstracto.core.interactive.DelayedAction;
|
||||
import dev.sheldan.abstracto.core.interactive.DelayedActionConfig;
|
||||
import dev.sheldan.abstracto.core.service.ConfigService;
|
||||
import dev.sheldan.abstracto.modmail.service.ModMailThreadServiceBean;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
public class ModMailCategoryDelayedAction implements DelayedAction {
|
||||
|
||||
|
||||
@Autowired
|
||||
private ConfigService configService;
|
||||
|
||||
@Override
|
||||
public void execute(DelayedActionConfig delayedActionConfig) {
|
||||
ModMailCategoryDelayedActionConfig concrete = (ModMailCategoryDelayedActionConfig) delayedActionConfig;
|
||||
configService.setConfigValue(ModMailThreadServiceBean.MODMAIL_CATEGORY, concrete.getServerId(), concrete.getValue());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean handles(DelayedActionConfig delayedActionConfig) {
|
||||
return delayedActionConfig instanceof ModMailCategoryDelayedActionConfig;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package dev.sheldan.abstracto.modmail.setup;
|
||||
|
||||
import dev.sheldan.abstracto.core.interactive.DelayedActionConfig;
|
||||
import dev.sheldan.abstracto.core.models.database.AConfig;
|
||||
import dev.sheldan.abstracto.modmail.models.template.ModMailCategoryActionModel;
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import net.dv8tion.jda.api.entities.Category;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@Builder
|
||||
public class ModMailCategoryDelayedActionConfig implements DelayedActionConfig {
|
||||
private Long serverId;
|
||||
private AConfig value;
|
||||
private Category category;
|
||||
|
||||
@Override
|
||||
public String getTemplateName() {
|
||||
return "setup_modmail_category_action";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getTemplateModel() {
|
||||
return ModMailCategoryActionModel
|
||||
.builder()
|
||||
.category(this.category)
|
||||
.categoryId(value.getLongValue())
|
||||
.build();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,140 @@
|
||||
package dev.sheldan.abstracto.modmail.setup;
|
||||
|
||||
import dev.sheldan.abstracto.core.exception.AbstractoRunTimeException;
|
||||
import dev.sheldan.abstracto.core.exception.ChannelNotFoundException;
|
||||
import dev.sheldan.abstracto.core.interactive.*;
|
||||
import dev.sheldan.abstracto.core.models.AServerChannelUserId;
|
||||
import dev.sheldan.abstracto.core.models.FeatureValidationResult;
|
||||
import dev.sheldan.abstracto.core.models.database.AChannel;
|
||||
import dev.sheldan.abstracto.core.models.database.AConfig;
|
||||
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
|
||||
import dev.sheldan.abstracto.core.service.BotService;
|
||||
import dev.sheldan.abstracto.core.service.ConfigService;
|
||||
import dev.sheldan.abstracto.core.service.management.ChannelManagementService;
|
||||
import dev.sheldan.abstracto.core.service.management.ConfigManagementService;
|
||||
import dev.sheldan.abstracto.core.service.management.UserInServerManagementService;
|
||||
import dev.sheldan.abstracto.modmail.models.template.SetupModMailCategoryMessageModel;
|
||||
import dev.sheldan.abstracto.modmail.service.ModMailThreadServiceBean;
|
||||
import dev.sheldan.abstracto.modmail.validator.ModMailFeatureValidator;
|
||||
import dev.sheldan.abstracto.templating.service.TemplateService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import net.dv8tion.jda.api.entities.Category;
|
||||
import net.dv8tion.jda.api.entities.Guild;
|
||||
import net.dv8tion.jda.api.entities.Message;
|
||||
import net.dv8tion.jda.api.events.message.MessageReceivedEvent;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
@Component
|
||||
@Slf4j
|
||||
public class ModMailCategorySetupBean implements ModMailCategorySetup {
|
||||
|
||||
@Autowired
|
||||
private InteractiveUtils interactiveUtils;
|
||||
|
||||
@Autowired
|
||||
private TemplateService templateService;
|
||||
|
||||
@Autowired
|
||||
private ChannelManagementService channelManagementService;
|
||||
|
||||
@Autowired
|
||||
private UserInServerManagementService userInServerManagementService;
|
||||
|
||||
@Autowired
|
||||
private InteractiveService interactiveService;
|
||||
|
||||
@Autowired
|
||||
private ConfigService configService;
|
||||
|
||||
@Autowired
|
||||
private ConfigManagementService configManagementService;
|
||||
|
||||
@Autowired
|
||||
private ModMailFeatureValidator modMailFeatureValidator;
|
||||
|
||||
@Autowired
|
||||
private BotService botService;
|
||||
|
||||
@Override
|
||||
public CompletableFuture<SetupStepResult> execute(AServerChannelUserId user, SetupStepParameter parameter) {
|
||||
String messageTemplateKey = "setup_modmail_category_message";
|
||||
SetupModMailCategoryMessageModel model = SetupModMailCategoryMessageModel
|
||||
.builder()
|
||||
.build();
|
||||
if(configManagementService.configExists(user.getGuildId(), ModMailThreadServiceBean.MODMAIL_CATEGORY)) {
|
||||
Guild guild = botService.getGuildByIdNullable(user.getGuildId());
|
||||
Long categoryId = configService.getLongValue(ModMailThreadServiceBean.MODMAIL_CATEGORY, user.getGuildId());
|
||||
Category category = guild.getCategoryById(categoryId);
|
||||
model.setCategory(category);
|
||||
}
|
||||
String messageText = templateService.renderTemplate(messageTemplateKey, model);
|
||||
Optional<AChannel> channel = channelManagementService.loadChannel(user.getChannelId());
|
||||
CompletableFuture<SetupStepResult> future = new CompletableFuture<>();
|
||||
AUserInAServer aUserInAServer = userInServerManagementService.loadUser(user.getGuildId(), user.getUserId());
|
||||
|
||||
if(channel.isPresent()) {
|
||||
Runnable finalAction = getTimeoutRunnable(user.getGuildId(), user.getChannelId());
|
||||
Consumer<MessageReceivedEvent> configAction = (MessageReceivedEvent event) -> {
|
||||
try {
|
||||
|
||||
SetupStepResult result;
|
||||
Message message = event.getMessage();
|
||||
if(checkForExit(message)) {
|
||||
result = SetupStepResult.fromCancelled();
|
||||
} else {
|
||||
String messageContent = event.getMessage().getContentRaw();
|
||||
Long categoryId = Long.parseLong(messageContent);
|
||||
Guild guild = botService.getGuildByIdNullable(user.getGuildId());
|
||||
FeatureValidationResult featureValidationResult = FeatureValidationResult.builder().validationResult(true).build();
|
||||
modMailFeatureValidator.validateModMailCategory(featureValidationResult, guild, categoryId);
|
||||
if(featureValidationResult.getValidationResult()) {
|
||||
AConfig fakeValue = configService.getFakeConfigForValue(ModMailThreadServiceBean.MODMAIL_CATEGORY, user.getGuildId(), messageContent);
|
||||
ModMailCategoryDelayedActionConfig build = ModMailCategoryDelayedActionConfig
|
||||
.builder()
|
||||
.serverId(user.getGuildId())
|
||||
.category(guild.getCategoryById(categoryId))
|
||||
.value(fakeValue)
|
||||
.build();
|
||||
List<DelayedActionConfig> delayedSteps = Arrays.asList(build);
|
||||
result = SetupStepResult
|
||||
.builder()
|
||||
.result(SetupStepResultType.SUCCESS)
|
||||
.delayedActionConfigList(delayedSteps)
|
||||
.build();
|
||||
} else {
|
||||
throw new AbstractoRunTimeException("Category id does not conform.");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
future.complete(result);
|
||||
} catch (Exception e) {
|
||||
log.error("Failed to handle post target step.", e);
|
||||
future.completeExceptionally(e);
|
||||
}
|
||||
};
|
||||
interactiveService.createMessageWithResponse(messageText, aUserInAServer, channel.get(), parameter.getPreviousMessageId(), configAction, finalAction);
|
||||
} else {
|
||||
future.completeExceptionally(new ChannelNotFoundException(user.getGuildId(), user.getChannelId()));
|
||||
}
|
||||
return future;
|
||||
}
|
||||
|
||||
protected Runnable getTimeoutRunnable(Long serverId, Long channelId) {
|
||||
return () -> {
|
||||
interactiveUtils.sendTimeoutMessage(serverId, channelId);
|
||||
};
|
||||
}
|
||||
|
||||
protected boolean checkForExit(Message message) {
|
||||
return message.getContentRaw().trim().equalsIgnoreCase("exit");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -35,16 +35,20 @@ public class ModMailFeatureValidatorBean implements ModMailFeatureValidator {
|
||||
boolean checkSucceeded = featureValidatorService.checkSystemConfig(ModMailThreadServiceBean.MODMAIL_CATEGORY, server, validationResult);
|
||||
if(checkSucceeded) {
|
||||
Long modMailCategory = configService.getLongValue(ModMailThreadServiceBean.MODMAIL_CATEGORY, server.getId());
|
||||
Category categoryById = guild.getCategoryById(modMailCategory);
|
||||
if(categoryById == null) {
|
||||
validationResult.setValidationResult(false);
|
||||
ModMailCategoryValidationError newError = ModMailCategoryValidationError
|
||||
.builder()
|
||||
.currentCategoryId(modMailCategory)
|
||||
.build();
|
||||
validationResult.getValidationErrors().add(newError);
|
||||
}
|
||||
validateModMailCategory(validationResult, guild, modMailCategory);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void validateModMailCategory(FeatureValidationResult validationResult, Guild guild, Long modMailCategory) {
|
||||
Category categoryById = guild.getCategoryById(modMailCategory);
|
||||
if(categoryById == null) {
|
||||
validationResult.setValidationResult(false);
|
||||
ModMailCategoryValidationError newError = ModMailCategoryValidationError
|
||||
.builder()
|
||||
.currentCategoryId(modMailCategory)
|
||||
.build();
|
||||
validationResult.getValidationErrors().add(newError);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
<#assign category>${param.category.name}</#assign><#include "setup_modmail_category_action_display">
|
||||
@@ -0,0 +1 @@
|
||||
<#assign categoryName><#if category?has_content>${category.name}<#else><#include "setup_modmail_category_message_no_category">></#if></#assign><#include "setup_modmail_category_message_display">
|
||||
@@ -4,7 +4,9 @@ import dev.sheldan.abstracto.core.config.FeatureConfig;
|
||||
import dev.sheldan.abstracto.core.config.FeatureEnum;
|
||||
import dev.sheldan.abstracto.core.config.FeatureMode;
|
||||
import dev.sheldan.abstracto.core.config.PostTargetEnum;
|
||||
import dev.sheldan.abstracto.core.interactive.SetupStep;
|
||||
import dev.sheldan.abstracto.core.service.FeatureValidator;
|
||||
import dev.sheldan.abstracto.modmail.setup.ModMailCategorySetup;
|
||||
import dev.sheldan.abstracto.modmail.validator.ModMailFeatureValidator;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
@@ -18,6 +20,9 @@ public class ModMailFeature implements FeatureConfig {
|
||||
@Autowired
|
||||
private ModMailFeatureValidator modMailFeatureValidator;
|
||||
|
||||
@Autowired
|
||||
private ModMailCategorySetup modMailCategorySetup;
|
||||
|
||||
@Override
|
||||
public FeatureEnum getFeature() {
|
||||
return ModMailFeatures.MOD_MAIL;
|
||||
@@ -47,4 +52,9 @@ public class ModMailFeature implements FeatureConfig {
|
||||
public List<String> getRequiredSystemConfigKeys() {
|
||||
return Arrays.asList("modMailClosingText");
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<SetupStep> getCustomSetupSteps() {
|
||||
return Arrays.asList(modMailCategorySetup);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
package dev.sheldan.abstracto.modmail.models.template;
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import net.dv8tion.jda.api.entities.Category;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@Builder
|
||||
public class ModMailCategoryActionModel {
|
||||
private Category category;
|
||||
private Long categoryId;
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package dev.sheldan.abstracto.modmail.models.template;
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import net.dv8tion.jda.api.entities.Category;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@Builder
|
||||
public class SetupModMailCategoryMessageModel {
|
||||
private Category category;
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
package dev.sheldan.abstracto.modmail.setup;
|
||||
|
||||
import dev.sheldan.abstracto.core.interactive.SetupStep;
|
||||
|
||||
public interface ModMailCategorySetup extends SetupStep {
|
||||
}
|
||||
@@ -1,6 +1,9 @@
|
||||
package dev.sheldan.abstracto.modmail.validator;
|
||||
|
||||
import dev.sheldan.abstracto.core.models.FeatureValidationResult;
|
||||
import dev.sheldan.abstracto.core.service.FeatureValidator;
|
||||
import net.dv8tion.jda.api.entities.Guild;
|
||||
|
||||
public interface ModMailFeatureValidator extends FeatureValidator {
|
||||
void validateModMailCategory(FeatureValidationResult validationResult, Guild guild, Long modMailCategory);
|
||||
}
|
||||
|
||||
@@ -4,8 +4,12 @@ import dev.sheldan.abstracto.core.exception.ChannelNotFoundException;
|
||||
import dev.sheldan.abstracto.core.models.AServerChannelUserId;
|
||||
import dev.sheldan.abstracto.core.models.database.AChannel;
|
||||
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
|
||||
import dev.sheldan.abstracto.core.models.database.PostTarget;
|
||||
import dev.sheldan.abstracto.core.models.template.commands.SetupPostTargetMessageModel;
|
||||
import dev.sheldan.abstracto.core.service.BotService;
|
||||
import dev.sheldan.abstracto.core.service.ConfigService;
|
||||
import dev.sheldan.abstracto.core.service.management.ChannelManagementService;
|
||||
import dev.sheldan.abstracto.core.service.management.PostTargetManagement;
|
||||
import dev.sheldan.abstracto.core.service.management.UserInServerManagementService;
|
||||
import dev.sheldan.abstracto.templating.service.TemplateService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
@@ -43,11 +47,29 @@ public class PostTargetSetupStep extends AbstractConfigSetupStep {
|
||||
@Autowired
|
||||
private TemplateService templateService;
|
||||
|
||||
@Autowired
|
||||
private BotService botService;
|
||||
|
||||
@Autowired
|
||||
private PostTargetManagement postTargetManagement;
|
||||
|
||||
@Override
|
||||
public CompletableFuture<SetupStepResult> execute(AServerChannelUserId user, SetupStepParameter parameter) {
|
||||
PostTargetStepParameter systemConfigStepParameter = (PostTargetStepParameter) parameter;
|
||||
String messageTemplateKey = "setup_posttarget_" + systemConfigStepParameter.getPostTargetKey();
|
||||
String messageText = templateService.renderSimpleTemplate(messageTemplateKey);
|
||||
PostTargetStepParameter postTargetStepParameter = (PostTargetStepParameter) parameter;
|
||||
TextChannel currentTextChannel;
|
||||
if(postTargetManagement.postTargetExists(postTargetStepParameter.getPostTargetKey(), user.getGuildId())) {
|
||||
PostTarget postTarget = postTargetManagement.getPostTarget(postTargetStepParameter.getPostTargetKey(), user.getGuildId());
|
||||
currentTextChannel = botService.getTextChannelFromServer(user.getGuildId(), postTarget.getChannelReference().getId()).orElse(null);
|
||||
} else {
|
||||
currentTextChannel = null;
|
||||
}
|
||||
SetupPostTargetMessageModel model = SetupPostTargetMessageModel
|
||||
.builder()
|
||||
.postTargetKey(postTargetStepParameter.getPostTargetKey())
|
||||
.currentTextChannel(currentTextChannel)
|
||||
.build();
|
||||
String messageTemplateKey = "setup_post_target_message";
|
||||
String messageText = templateService.renderTemplate(messageTemplateKey, model);
|
||||
Optional<AChannel> channel = channelManagementService.loadChannel(user.getChannelId());
|
||||
CompletableFuture<SetupStepResult> future = new CompletableFuture<>();
|
||||
AUserInAServer aUserInAServer = userInServerManagementService.loadUser(user.getGuildId(), user.getUserId());
|
||||
@@ -63,11 +85,12 @@ public class PostTargetSetupStep extends AbstractConfigSetupStep {
|
||||
} else {
|
||||
if(message.getMentionedChannels().size() == 0) {
|
||||
future.completeExceptionally(new RuntimeException());
|
||||
return;
|
||||
}
|
||||
TextChannel textChannel = message.getMentionedChannels().get(0);
|
||||
PostTargetDelayedActionConfig build = PostTargetDelayedActionConfig
|
||||
.builder()
|
||||
.postTargetKey(systemConfigStepParameter.getPostTargetKey())
|
||||
.postTargetKey(postTargetStepParameter.getPostTargetKey())
|
||||
.serverId(user.getGuildId())
|
||||
.textChannel(textChannel)
|
||||
.channelId(textChannel.getIdLong())
|
||||
|
||||
@@ -62,6 +62,14 @@ public class SetupServiceBean implements SetupService {
|
||||
.build();
|
||||
steps.add(execution);
|
||||
});
|
||||
featureConfig.getCustomSetupSteps().forEach(setupStep -> {
|
||||
SetupExecution execution = SetupExecution
|
||||
.builder()
|
||||
.step(setupStep)
|
||||
.parameter(EmptySetupParameter.builder().build())
|
||||
.build();
|
||||
steps.add(execution);
|
||||
});
|
||||
for (int i = 0; i < steps.size(); i++) {
|
||||
SetupExecution setupExecution = steps.get(i);
|
||||
setupExecution.getParameter().setPreviousMessageId(initialMessageId);
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
<#assign currentTarget><#if currentTextChannel?has_content>${currentTextChannel.asMention}<#else><#include "setup_post_target_no_channel_set"></#if></#assign><#include "setup_posttarget_${postTargetKey}">
|
||||
@@ -1,5 +1,6 @@
|
||||
package dev.sheldan.abstracto.core.config;
|
||||
|
||||
import dev.sheldan.abstracto.core.interactive.SetupStep;
|
||||
import dev.sheldan.abstracto.core.service.FeatureValidator;
|
||||
|
||||
import java.util.Collections;
|
||||
@@ -18,4 +19,5 @@ public interface FeatureConfig {
|
||||
default List<FeatureValidator> getAdditionalFeatureValidators() { return Collections.emptyList(); }
|
||||
default List<String> getRequiredEmotes() { return Collections.emptyList(); }
|
||||
default List<FeatureMode> getAvailableModes() { return Collections.emptyList(); };
|
||||
default List<SetupStep> getCustomSetupSteps() { return Collections.emptyList(); }
|
||||
}
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
package dev.sheldan.abstracto.core.interactive;
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@Builder
|
||||
public class EmptySetupParameter implements SetupStepParameter {
|
||||
private Long previousMessageId;
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package dev.sheldan.abstracto.core.models.template.commands;
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import net.dv8tion.jda.api.entities.TextChannel;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@Builder
|
||||
public class SetupPostTargetMessageModel {
|
||||
private String postTargetKey;
|
||||
private TextChannel currentTextChannel;
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
No channel set
|
||||
@@ -1 +1 @@
|
||||
The channel in which where bans should be logged to.
|
||||
The channel in which where bans should be logged to. Currently: ${currentTarget}
|
||||
@@ -1 +1 @@
|
||||
The channel in which decayed warnings should be logged to.
|
||||
The channel in which decayed warnings should be logged to. Currently: ${currentTarget}
|
||||
@@ -1 +1 @@
|
||||
The channel in which deleted messages should be logged to.
|
||||
The channel in which deleted messages should be logged to. Currently: ${currentTarget}
|
||||
@@ -1 +1 @@
|
||||
The channel in which edited messages should be logged to.
|
||||
The channel in which edited messages should be logged to. Currently: ${currentTarget}
|
||||
@@ -1 +1 @@
|
||||
The channel in which guild joins should be logged to.
|
||||
The channel in which guild joins should be logged to. Currently: ${currentTarget}
|
||||
@@ -1 +1 @@
|
||||
The channel in which kicks should be logged to.
|
||||
The channel in which kicks should be logged to. Currently: ${currentTarget}
|
||||
@@ -1 +1 @@
|
||||
The channel where guild leavers should be logged to.
|
||||
The channel where guild leavers should be logged to. Currently: ${currentTarget}
|
||||
@@ -1 +1 @@
|
||||
The channel where mutes should be logged to. Un-mutes will also be in this channel.
|
||||
The channel where mutes should be logged to. Un-mutes will also be in this channel. Currently: ${currentTarget}
|
||||
@@ -1 +1 @@
|
||||
The channel where warnings should be logged to.
|
||||
The channel where warnings should be logged to. Currently: ${currentTarget}
|
||||
@@ -0,0 +1 @@
|
||||
Mod mail threads will be created in category `${category}`.
|
||||
@@ -0,0 +1 @@
|
||||
The ID of the category you want to create the mod mail threads in. Currently: ${categoryName}
|
||||
@@ -0,0 +1 @@
|
||||
No category set
|
||||
@@ -1 +1 @@
|
||||
The channel towards which the closed mod mail threads should be logged to. (In the appropriate mode)
|
||||
The channel towards which the closed mod mail threads should be logged to. (In the appropriate mode) Currently: ${currentTarget}
|
||||
@@ -1 +1 @@
|
||||
The channel which should be used to notify the responsible people about new mod mail threads.
|
||||
The channel which should be used to notify the responsible people about new mod mail threads. Currently: ${currentTarget}
|
||||
@@ -0,0 +1 @@
|
||||
Currently: ${currentTarget}
|
||||
@@ -0,0 +1 @@
|
||||
Currently: ${currentTarget}
|
||||
Reference in New Issue
Block a user