[AB-xxx] enabling localization by adding the localization as a system config key

fixing methods which did not provide the server id to template rendering
refactoring channel service method to remove a duplicate
fixing template loading method to not throw an exception when template was not found
This commit is contained in:
Sheldan
2024-11-22 21:54:13 +01:00
parent 0e95ddf198
commit 453378f0b6
72 changed files with 181 additions and 172 deletions

View File

@@ -47,7 +47,7 @@ public class ConditionPostExecution implements PostCommandExecution {
.member(commandContext.getAuthor())
.build())
.build();
channelService.sendEmbedTemplateInTextChannelList(GENERIC_COMMAND_EXCEPTION_MODEL_KEY, conditionModel, commandContext.getChannel());
channelService.sendEmbedTemplateInMessageChannel(GENERIC_COMMAND_EXCEPTION_MODEL_KEY, conditionModel, commandContext.getChannel());
}
}

View File

@@ -61,14 +61,14 @@ public class ExceptionServiceBean implements ExceptionService {
private void reportGenericException(Throwable throwable, CommandContext context) {
GenericExceptionModel exceptionModel = buildCommandModel(throwable, context);
channelService.sendEmbedTemplateInTextChannelList("generic_command_exception", exceptionModel, context.getChannel());
channelService.sendEmbedTemplateInMessageChannel("generic_command_exception", exceptionModel, context.getChannel());
}
@Override
public void reportExceptionToGuildMessageReceivedContext(Throwable exception, MessageReceivedEvent event) {
if(exception instanceof Templatable){
GenericExceptionModel model = buildMemberContext(exception, event.getMember());
String text = templateService.renderTemplate(MODEL_WRAPPER_TEMPLATE_KEY, model);
String text = templateService.renderTemplate(MODEL_WRAPPER_TEMPLATE_KEY, model, event.getGuild().getIdLong());
channelService.sendTextToChannel(text, event.getChannel());
} else {
channelService.sendTextToChannel(exception.getLocalizedMessage(), event.getChannel());
@@ -79,7 +79,7 @@ public class ExceptionServiceBean implements ExceptionService {
public void reportExceptionToPrivateMessageReceivedContext(Throwable exception, MessageReceivedEvent event) {
if(exception instanceof Templatable){
GenericExceptionModel model = buildPrivateMessageReceivedModel(exception, event.getAuthor());
String text = templateService.renderTemplate(MODEL_WRAPPER_TEMPLATE_KEY, model);
String text = templateService.renderTemplate(MODEL_WRAPPER_TEMPLATE_KEY, model, event.getGuild().getIdLong());
channelService.sendTextToChannel(text, event.getChannel());
} else {
channelService.sendTextToChannel(exception.getLocalizedMessage(), event.getChannel());
@@ -90,7 +90,7 @@ public class ExceptionServiceBean implements ExceptionService {
public void reportExceptionToChannel(Throwable exception, MessageChannel channel, Member member) {
if(exception instanceof Templatable){
GenericExceptionModel model = buildMemberContext(exception, member);
String text = templateService.renderTemplate(MODEL_WRAPPER_TEMPLATE_KEY, model);
String text = templateService.renderTemplate(MODEL_WRAPPER_TEMPLATE_KEY, model, member.getGuild().getIdLong());
channelService.sendTextToChannel(text, channel);
} else {
channelService.sendTextToChannel(exception.getLocalizedMessage(), channel);

View File

@@ -74,7 +74,7 @@ public class FeatureModes extends AbstractConditionableCommand {
.builder()
.featureModes(featureModes)
.build();
return FutureUtils.toSingleFutureGeneric(channelService.sendEmbedTemplateInTextChannelList(FEATURE_MODES_RESPONSE_TEMPLATE_KEY, model, commandContext.getChannel()))
return FutureUtils.toSingleFutureGeneric(channelService.sendEmbedTemplateInMessageChannel(FEATURE_MODES_RESPONSE_TEMPLATE_KEY, model, commandContext.getChannel()))
.thenApply(aVoid -> CommandResult.fromIgnored());
}

View File

@@ -42,7 +42,7 @@ public class ShowEffects extends AbstractConditionableCommand {
@Override
public CompletableFuture<CommandResult> executeAsync(CommandContext commandContext) {
ShowEffectsModel model = getModel();
return FutureUtils.toSingleFutureGeneric(channelService.sendEmbedTemplateInTextChannelList(SHOW_EFFECTS_RESPONSE_TEMPLATE,
return FutureUtils.toSingleFutureGeneric(channelService.sendEmbedTemplateInMessageChannel(SHOW_EFFECTS_RESPONSE_TEMPLATE,
model, commandContext.getChannel()))
.thenApply(unused -> CommandResult.fromSuccess());
}

View File

@@ -32,7 +32,7 @@ public class Documentation extends AbstractConditionableCommand {
@Override
public CompletableFuture<CommandResult> executeAsync(CommandContext commandContext) {
return FutureUtils.toSingleFutureGeneric(channelService.sendEmbedTemplateInMessageChannelList(DOCUMENTATION_RESPONSE_TEMPLATE_KEY, new Object(), commandContext.getChannel()))
return FutureUtils.toSingleFutureGeneric(channelService.sendEmbedTemplateInMessageChannel(DOCUMENTATION_RESPONSE_TEMPLATE_KEY, new Object(), commandContext.getChannel()))
.thenApply(unused -> CommandResult.fromIgnored());
}

View File

@@ -34,7 +34,7 @@ public class Ping extends AbstractConditionableCommand {
@Override
public CompletableFuture<CommandResult> executeAsync(CommandContext commandContext) {
PingModel model = buildModel(commandContext.getJda());
return channelService.sendTextTemplateInTextChannel(PING_TEMPLATE, model, commandContext.getChannel())
return channelService.sendTextTemplateInMessageChannel(PING_TEMPLATE, model, commandContext.getChannel())
.thenApply(message -> CommandResult.fromIgnored());
}

View File

@@ -39,7 +39,7 @@ public class Uptime extends AbstractConditionableCommand {
@Override
public CompletableFuture<CommandResult> executeAsync(CommandContext commandContext) {
UptimeModel model = getModel();
return FutureUtils.toSingleFutureGeneric(channelService.sendEmbedTemplateInTextChannelList(UPTIME_RESPONSE_TEMPLATE_KEY, model, commandContext.getChannel()))
return FutureUtils.toSingleFutureGeneric(channelService.sendEmbedTemplateInMessageChannel(UPTIME_RESPONSE_TEMPLATE_KEY, model, commandContext.getChannel()))
.thenApply(unused -> CommandResult.fromSuccess());
}

View File

@@ -7,8 +7,10 @@ import lombok.Setter;
@Setter
public class ServerContext {
private Long serverId;
private String locale;
public void clear() {
this.serverId = null;
this.locale = null;
}
}

View File

@@ -61,7 +61,7 @@ public class SlashCommandServiceBean implements SlashCommandService {
if(!isTemplated) {
description = commandConfiguration.getDescription();
} else {
description = templateService.renderSimpleTemplate(internalCommandName + "_description");
description = templateService.renderSimpleTemplate(internalCommandName + "_description", serverId);
}
String rootName = slashConfig.getSlashCompatibleRootName();
String groupName = slashConfig.getSlashCompatibleGroupName();
@@ -125,33 +125,33 @@ public class SlashCommandServiceBean implements SlashCommandService {
for (int i = 0; i < parameter.getListSize(); i++) {
for (OptionType type : types) {
String parameterName = slashCommandParameterService.getFullQualifiedParameterName(parameter.getSlashCompatibleName(), type) + "_" + i;
String parameterDescription = isTemplated ? templateService.renderSimpleTemplate(internalCommandName + "_parameter_" + parameter.getName()) : parameter.getDescription();
String parameterDescription = isTemplated ? templateService.renderSimpleTemplate(internalCommandName + "_parameter_" + parameter.getName(), serverId) : parameter.getDescription();
OptionData optionData = new OptionData(type, parameterName, parameterDescription, false);
addChoices(optionData, parameter, internalCommandName, isTemplated);
addChoices(optionData, parameter, internalCommandName, isTemplated, serverId);
optionalParameters.add(optionData);
}
}
} else {
types.forEach(type -> {
String parameterName = slashCommandParameterService.getFullQualifiedParameterName(parameter.getSlashCompatibleName(), type);
String parameterDescription = isTemplated ? templateService.renderSimpleTemplate(internalCommandName + "_parameter_" + parameter.getName()) : parameter.getDescription();
String parameterDescription = isTemplated ? templateService.renderSimpleTemplate(internalCommandName + "_parameter_" + parameter.getName(), serverId) : parameter.getDescription();
OptionData optionData = new OptionData(type, parameterName, parameterDescription, false);
addChoices(optionData, parameter, internalCommandName, isTemplated);
addChoices(optionData, parameter, internalCommandName, isTemplated, serverId);
optionalParameters.add(optionData);
});
}
} else {
OptionType type = types.get(0);
String parameterDescription = isTemplated ? templateService.renderSimpleTemplate(internalCommandName + "_parameter_" + parameter.getName()) : parameter.getDescription();
String parameterDescription = isTemplated ? templateService.renderSimpleTemplate(internalCommandName + "_parameter_" + parameter.getName(), serverId) : parameter.getDescription();
if(parameter.isListParam()) {
for (int i = 0; i < parameter.getListSize(); i++) {
OptionData optionData = new OptionData(type, parameter.getSlashCompatibleName() + "_" + i, parameterDescription, false);
addChoices(optionData, parameter, internalCommandName, isTemplated);
addChoices(optionData, parameter, internalCommandName, isTemplated, serverId);
optionalParameters.add(optionData);
}
} else {
OptionData optionData = new OptionData(type, parameter.getSlashCompatibleName(), parameterDescription, !parameter.isOptional(), parameter.getSupportsAutoComplete());
addChoices(optionData, parameter, internalCommandName, isTemplated);
addChoices(optionData, parameter, internalCommandName, isTemplated, serverId);
requiredParameters.add(optionData);
}
}
@@ -160,9 +160,9 @@ public class SlashCommandServiceBean implements SlashCommandService {
return requiredParameters;
}
private void addChoices(OptionData optionData, Parameter parameter, String commandName, boolean isTemplated) {
private void addChoices(OptionData optionData, Parameter parameter, String commandName, boolean isTemplated, Long serverId) {
parameter.getChoices().forEach(choiceKey -> {
String value = isTemplated ? templateService.renderSimpleTemplate(commandName + "_parameter_" + parameter.getName() + "_choice_" + choiceKey) : choiceKey;
String value = isTemplated ? templateService.renderSimpleTemplate(commandName + "_parameter_" + parameter.getName() + "_choice_" + choiceKey, serverId) : choiceKey;
optionData.addChoice(value, choiceKey);
});
}

View File

@@ -465,7 +465,7 @@ public class ChannelServiceBean implements ChannelService {
@Override
@Transactional
public List<CompletableFuture<Message>> sendEmbedTemplateInTextChannelList(String templateKey, Object model, MessageChannel channel) {
public List<CompletableFuture<Message>> sendEmbedTemplateInMessageChannel(String templateKey, Object model, MessageChannel channel) {
MessageToSend messageToSend;
if(channel instanceof GuildChannel) {
messageToSend = templateService.renderEmbedTemplate(templateKey, model, ((GuildChannel)channel).getGuild().getIdLong());
@@ -476,14 +476,7 @@ public class ChannelServiceBean implements ChannelService {
}
@Override
public List<CompletableFuture<Message>> sendEmbedTemplateInMessageChannelList(String templateKey, Object model, MessageChannel channel) {
// message channel on its own, does not have a guild, so we cant say for which server we want to render the template
MessageToSend messageToSend = templateService.renderEmbedTemplate(templateKey, model);
return sendMessageToSendToChannel(messageToSend, channel);
}
@Override
public CompletableFuture<Message> sendTextTemplateInTextChannel(String templateKey, Object model, MessageChannel channel) {
public CompletableFuture<Message> sendTextTemplateInMessageChannel(String templateKey, Object model, MessageChannel channel) {
String text;
if(channel instanceof GuildChannel) {
text = templateService.renderTemplate(templateKey, model, ((GuildChannel)channel).getGuild().getIdLong());
@@ -494,13 +487,6 @@ public class ChannelServiceBean implements ChannelService {
return sendTextToChannel(text, channel);
}
@Override
public CompletableFuture<Message> sendTextTemplateInMessageChannel(String templateKey, Object model, MessageChannel channel) {
// message channel on its own, does not have a guild, so we cant say for which server we want to render the template
String text = templateService.renderTemplate(templateKey, model);
return sendTextToChannel(text, channel);
}
@Override
public CompletableFuture<Void> deleteMessagesInChannel(MessageChannel messageChannel, List<Message> messages) {
metricService.incrementCounter(CHANNEL_MESSAGE_BULK_DELETE_METRIC);
@@ -580,7 +566,7 @@ public class ChannelServiceBean implements ChannelService {
public CompletableFuture<Message> sendSimpleTemplateToChannel(Long serverId, Long channelId, String template) {
GuildMessageChannel foundChannel = getMessageChannelFromServer(serverId, channelId);
if(foundChannel != null) {
return sendTextTemplateInTextChannel(template, new Object(), foundChannel);
return sendTextTemplateInMessageChannel(template, new Object(), foundChannel);
} else {
log.info("Channel {} in server {} not found.", channelId, serverId);
throw new IllegalArgumentException("Incorrect channel type.");

View File

@@ -181,7 +181,7 @@ public class MessageServiceBean implements MessageService {
@Override
public CompletableFuture<Void> sendEmbedToUser(User user, String template, Object model) {
return openPrivateChannelForUser(user).thenCompose(privateChannel ->
FutureUtils.toSingleFutureGeneric(channelService.sendEmbedTemplateInMessageChannelList(template, model, privateChannel)));
FutureUtils.toSingleFutureGeneric(channelService.sendEmbedTemplateInMessageChannel(template, model, privateChannel)));
}
public CompletableFuture<PrivateChannel> openPrivateChannelForUser(User user) {
@@ -192,7 +192,7 @@ public class MessageServiceBean implements MessageService {
public CompletableFuture<Message> sendEmbedToUserWithMessage(User user, String template, Object model) {
log.debug("Sending direct message with template {} to user {}.", template, user.getIdLong());
return openPrivateChannelForUser(user).thenCompose(privateChannel ->
channelService.sendEmbedTemplateInMessageChannelList(template, model, privateChannel).get(0));
channelService.sendEmbedTemplateInMessageChannel(template, model, privateChannel).get(0));
}
@Override

View File

@@ -102,7 +102,7 @@ public class PaginatorServiceBean implements PaginatorService {
.build();
String embedConfig = templateService.renderTemplate(templateKey + "_paginator", wrapperModel, serverId);
PaginatorConfiguration configuration = gson.fromJson(embedConfig, PaginatorConfiguration.class);
setupFooters(configuration);
setupFooters(configuration, serverId);
configuration.setPaginatorId(componentService.generateComponentId());
configuration.setSinglePage(configuration.getEmbedConfigs().size() < 2);
@@ -138,14 +138,14 @@ public class PaginatorServiceBean implements PaginatorService {
.thenAccept(message -> self.setupButtonPayloads(message, setup, serverId));
}
private void setupFooters(PaginatorConfiguration configuration) {
private void setupFooters(PaginatorConfiguration configuration, Long serverId) {
for (int i = 0; i < configuration.getEmbedConfigs().size(); i++) {
PaginatorFooterModel paginatorModel = PaginatorFooterModel
.builder()
.page(i + 1)
.pageCount(configuration.getEmbedConfigs().size())
.build();
String footerText = templateService.renderTemplate(PAGINATOR_FOOTER_TEMPLATE_KEY, paginatorModel);
String footerText = templateService.renderTemplate(PAGINATOR_FOOTER_TEMPLATE_KEY, paginatorModel, serverId);
MessageConfiguration messageConfig = configuration.getEmbedConfigs().get(i);
if(messageConfig.getEmbeds() == null || messageConfig.getEmbeds().isEmpty()) {
messageConfig.setEmbeds(new ArrayList<>(Arrays.asList(EmbedConfiguration.builder().build())));

View File

@@ -20,9 +20,6 @@ import java.util.Optional;
@Component
public class DatabaseTemplateLoader implements TemplateLoader {
@Autowired
private TemplateService templateService;
@Autowired
private EffectiveTemplateManagementService effectiveTemplateManagementService;
@@ -43,7 +40,7 @@ public class DatabaseTemplateLoader implements TemplateLoader {
} else {
templateByKey = effectiveTemplateManagementService.getTemplateByKey(s);
}
return templateByKey.orElseThrow(() -> new IOException(String.format("Failed to load template. %s", s)));
return templateByKey.orElse(null);
}
@Override

View File

@@ -502,12 +502,21 @@ public class TemplateServiceBean implements TemplateService {
public MessageToSend renderEmbedTemplate(String key, Object model, Long serverId) {
try {
serverContext.setServerId(serverId);
initLocaleFromServer();
return renderEmbedTemplate(key, model);
} finally {
serverContext.clear();
}
}
// FIXME not thread safe
private void initLocaleFromServer() {
if(serverContext.getServerId() != null) {
String localeConfig = configService.getStringValueOrConfigDefault(CoreFeatureConfig.LOCALE_CONFIG_KEY, serverContext.getServerId());
serverContext.setLocale(localeConfig);
}
}
private boolean isEmptyEmbed(EmbedConfiguration configuration) {
if (configuration.getMetaConfig() != null && configuration.getMetaConfig().isPreventEmptyEmbed()) {
return configuration.getFields() == null && configuration.getDescription() == null && configuration.getImageUrl() == null;
@@ -524,6 +533,7 @@ public class TemplateServiceBean implements TemplateService {
public MessageToSend renderTemplateToMessageToSend(String key, Object model, Long serverId) {
try {
serverContext.setServerId(serverId);
initLocaleFromServer();
return renderTemplateToMessageToSend(key, model);
} finally {
serverContext.clear();
@@ -729,6 +739,7 @@ public class TemplateServiceBean implements TemplateService {
public String renderTemplateWithMap(String key, HashMap<String, Object> parameters, Long serverId) {
try {
serverContext.setServerId(serverId);
initLocaleFromServer();
return renderTemplateWithMap(key, parameters);
} finally {
serverContext.clear();
@@ -756,6 +767,7 @@ public class TemplateServiceBean implements TemplateService {
public String renderTemplate(String key, Object model, Long serverId) {
try {
serverContext.setServerId(serverId);
initLocaleFromServer();
return renderTemplate(key, model);
} finally {
serverContext.clear();
@@ -773,7 +785,11 @@ public class TemplateServiceBean implements TemplateService {
*/
private String renderTemplateToString(String key, Object model) throws IOException, TemplateException {
StringWriter result = new StringWriter();
Template template = configuration.getTemplate(key, null, serverContext.getServerId(), null, true, false);
Locale locale = null;
if(serverContext.getLocale() != null) {
locale = Locale.forLanguageTag(serverContext.getLocale());
}
Template template = configuration.getTemplate(key, locale, serverContext.getServerId(), null, true, false);
template.process(model, result);
return result.toString();
}
@@ -793,6 +809,7 @@ public class TemplateServiceBean implements TemplateService {
public String renderSimpleTemplate(String key, Long serverId) {
try {
serverContext.setServerId(serverId);
initLocaleFromServer();
return renderSimpleTemplate(key);
} finally {
serverContext.clear();
@@ -814,6 +831,7 @@ public class TemplateServiceBean implements TemplateService {
public String renderTemplatable(Templatable templatable, Long serverId) {
try {
serverContext.setServerId(serverId);
initLocaleFromServer();
return renderTemplatable(templatable);
} finally {
serverContext.clear();
@@ -824,6 +842,7 @@ public class TemplateServiceBean implements TemplateService {
public String renderDuration(Duration duration, Long serverId) {
try {
serverContext.setServerId(serverId);
initLocaleFromServer();
return renderDuration(duration);
} finally {
serverContext.clear();

View File

@@ -12,6 +12,9 @@ abstracto.systemConfigs.prefix.stringValue=!
abstracto.systemConfigs.noCommandFoundReporting.name=noCommandFoundReporting
abstracto.systemConfigs.noCommandFoundReporting.stringValue=true
abstracto.systemConfigs.locale.name=locale
abstracto.systemConfigs.locale.stringValue=en-US
abstracto.systemConfigs.confirmationTimeout.name=confirmationTimeout
abstracto.systemConfigs.confirmationTimeout.longValue=120

View File

@@ -65,7 +65,7 @@ public class FeatureModesTest {
AServer server = Mockito.mock(AServer.class);
when(serverManagementService.loadServer(noParameters.getGuild())).thenReturn(server);
when(featureModeService.getEffectiveFeatureModes(server)).thenReturn(featureModeDisplays);
when(channelService.sendEmbedTemplateInTextChannelList(eq(FeatureModes.FEATURE_MODES_RESPONSE_TEMPLATE_KEY), modelCaptor.capture(), eq(noParameters.getChannel()))).thenReturn(new ArrayList<>());
when(channelService.sendEmbedTemplateInMessageChannel(eq(FeatureModes.FEATURE_MODES_RESPONSE_TEMPLATE_KEY), modelCaptor.capture(), eq(noParameters.getChannel()))).thenReturn(new ArrayList<>());
CompletableFuture<CommandResult> commandResultCompletableFuture = testUnit.executeAsync(noParameters);
CommandTestUtilities.checkSuccessfulCompletionAsync(commandResultCompletableFuture);
List<FeatureModeDisplay> usedDisplays = modelCaptor.getValue().getFeatureModes();
@@ -86,7 +86,7 @@ public class FeatureModesTest {
AServer server = Mockito.mock(AServer.class);
when(serverManagementService.loadServer(noParameters.getGuild())).thenReturn(server);
when(featureModeService.getEffectiveFeatureModes(server, feature)).thenReturn(featureModeDisplays);
when(channelService.sendEmbedTemplateInTextChannelList(eq(FeatureModes.FEATURE_MODES_RESPONSE_TEMPLATE_KEY), modelCaptor.capture(), eq(noParameters.getChannel()))).thenReturn(new ArrayList<>());
when(channelService.sendEmbedTemplateInMessageChannel(eq(FeatureModes.FEATURE_MODES_RESPONSE_TEMPLATE_KEY), modelCaptor.capture(), eq(noParameters.getChannel()))).thenReturn(new ArrayList<>());
CompletableFuture<CommandResult> commandResultCompletableFuture = testUnit.executeAsync(noParameters);
CommandTestUtilities.checkSuccessfulCompletionAsync(commandResultCompletableFuture);
List<FeatureModeDisplay> usedDisplays = modelCaptor.getValue().getFeatureModes();