More improvements to console appending

This commit is contained in:
Vankka 2023-09-16 16:02:47 +03:00
parent a2f0896260
commit 9e7f043c4e
No known key found for this signature in database
GPG Key ID: 6E50CB7A29B96AD0
9 changed files with 139 additions and 59 deletions

View File

@ -115,6 +115,12 @@ public interface SendableDiscordMessage {
*/ */
boolean isSuppressedNotifications(); boolean isSuppressedNotifications();
/**
* If embeds for this message are suppressed.
* @return if embeds for this message are suppressed
*/
boolean isSuppressedEmbeds();
Map<InputStream, String> getAttachments(); Map<InputStream, String> getAttachments();
@SuppressWarnings("UnusedReturnValue") // API @SuppressWarnings("UnusedReturnValue") // API
@ -275,6 +281,19 @@ public interface SendableDiscordMessage {
*/ */
boolean isSuppressedNotifications(); boolean isSuppressedNotifications();
/**
* Sets if this message's embeds will be suppressed.
* @param suppressedEmbeds if embeds should be suppressed
* @return this builder, useful for chaining
*/
Builder setSuppressedEmbeds(boolean suppressedEmbeds);
/**
* Checks if this builder has embeds suppressed.
* @return {@code true} if embeds should be suppressed for this message
*/
boolean isSuppressedEmbeds();
/** /**
* Builds a {@link SendableDiscordMessage} from this builder. * Builds a {@link SendableDiscordMessage} from this builder.
* @return the new {@link SendableDiscordMessage} * @return the new {@link SendableDiscordMessage}

View File

@ -52,6 +52,7 @@ public class SendableDiscordMessageImpl implements SendableDiscordMessage {
private final String webhookUsername; private final String webhookUsername;
private final String webhookAvatarUrl; private final String webhookAvatarUrl;
private final boolean suppressedNotifications; private final boolean suppressedNotifications;
private final boolean suppressedEmbeds;
private final Map<InputStream, String> attachments; private final Map<InputStream, String> attachments;
protected SendableDiscordMessageImpl( protected SendableDiscordMessageImpl(
@ -62,6 +63,7 @@ public class SendableDiscordMessageImpl implements SendableDiscordMessage {
String webhookUsername, String webhookUsername,
String webhookAvatarUrl, String webhookAvatarUrl,
boolean suppressedNotifications, boolean suppressedNotifications,
boolean suppressedEmbeds,
Map<InputStream, String> attachments Map<InputStream, String> attachments
) { ) {
this.content = content; this.content = content;
@ -71,6 +73,7 @@ public class SendableDiscordMessageImpl implements SendableDiscordMessage {
this.webhookUsername = webhookUsername; this.webhookUsername = webhookUsername;
this.webhookAvatarUrl = webhookAvatarUrl; this.webhookAvatarUrl = webhookAvatarUrl;
this.suppressedNotifications = suppressedNotifications; this.suppressedNotifications = suppressedNotifications;
this.suppressedEmbeds = suppressedEmbeds;
this.attachments = Collections.unmodifiableMap(attachments); this.attachments = Collections.unmodifiableMap(attachments);
} }
@ -110,6 +113,11 @@ public class SendableDiscordMessageImpl implements SendableDiscordMessage {
return suppressedNotifications; return suppressedNotifications;
} }
@Override
public boolean isSuppressedEmbeds() {
return suppressedEmbeds;
}
@Override @Override
public Map<InputStream, String> getAttachments() { public Map<InputStream, String> getAttachments() {
return attachments; return attachments;
@ -124,6 +132,7 @@ public class SendableDiscordMessageImpl implements SendableDiscordMessage {
private String webhookUsername; private String webhookUsername;
private String webhookAvatarUrl; private String webhookAvatarUrl;
private boolean suppressedNotifications; private boolean suppressedNotifications;
private boolean suppressedEmbeds;
private final Map<InputStream, String> attachments = new LinkedHashMap<>(); private final Map<InputStream, String> attachments = new LinkedHashMap<>();
@Override @Override
@ -241,6 +250,17 @@ public class SendableDiscordMessageImpl implements SendableDiscordMessage {
return this; return this;
} }
@Override
public boolean isSuppressedEmbeds() {
return suppressedEmbeds;
}
@Override
public Builder setSuppressedEmbeds(boolean suppressedEmbeds) {
this.suppressedEmbeds = suppressedEmbeds;
return this;
}
@Override @Override
public boolean isSuppressedNotifications() { public boolean isSuppressedNotifications() {
return suppressedNotifications; return suppressedNotifications;
@ -248,7 +268,7 @@ public class SendableDiscordMessageImpl implements SendableDiscordMessage {
@Override @Override
public @NotNull SendableDiscordMessage build() { public @NotNull SendableDiscordMessage build() {
return new SendableDiscordMessageImpl(content, embeds, actionRows, allowedMentions, webhookUsername, webhookAvatarUrl, suppressedNotifications, attachments); return new SendableDiscordMessageImpl(content, embeds, actionRows, allowedMentions, webhookUsername, webhookAvatarUrl, suppressedNotifications, suppressedEmbeds, attachments);
} }
@Override @Override

View File

@ -50,7 +50,6 @@ import com.discordsrv.common.debug.data.OnlineMode;
import com.discordsrv.common.messageforwarding.game.minecrafttodiscord.MinecraftToDiscordChatModule; import com.discordsrv.common.messageforwarding.game.minecrafttodiscord.MinecraftToDiscordChatModule;
import com.discordsrv.common.plugin.PluginManager; import com.discordsrv.common.plugin.PluginManager;
import net.kyori.adventure.platform.bukkit.BukkitAudiences; import net.kyori.adventure.platform.bukkit.BukkitAudiences;
import org.bukkit.ChatColor;
import org.bukkit.Server; import org.bukkit.Server;
import org.bukkit.plugin.ServicePriority; import org.bukkit.plugin.ServicePriority;
import org.bukkit.plugin.java.JavaPlugin; import org.bukkit.plugin.java.JavaPlugin;

View File

@ -25,6 +25,12 @@ public class ConsoleConfig {
+ "- plain_content: Plain text") + "- plain_content: Plain text")
public OutputMode outputMode = OutputMode.ANSI; public OutputMode outputMode = OutputMode.ANSI;
@Comment("In \"diff\" mode, should exception lines have the prefix character as well")
public boolean diffExceptions = true;
@Comment("If urls should have embeds disabled")
public boolean disableLinkEmbeds = true;
@Comment("Avoids sending new messages by editing the most recent message until it reaches it's maximum length") @Comment("Avoids sending new messages by editing the most recent message until it reaches it's maximum length")
public boolean useEditing = true; public boolean useEditing = true;

View File

@ -40,7 +40,7 @@ public class ConsoleModule extends AbstractModule<DiscordSRV> implements LogAppe
List<ConsoleConfig> configs = discordSRV.config().console; List<ConsoleConfig> configs = discordSRV.config().console;
for (ConsoleConfig config : configs) { for (ConsoleConfig config : configs) {
handlers.add(new SingleConsoleHandler(discordSRV, config)); handlers.add(new SingleConsoleHandler(discordSRV, logger(), config));
} }
} }

View File

@ -9,6 +9,7 @@ import com.discordsrv.common.console.entry.LogEntry;
import com.discordsrv.common.console.entry.LogMessage; import com.discordsrv.common.console.entry.LogMessage;
import com.discordsrv.common.console.message.ConsoleMessage; import com.discordsrv.common.console.message.ConsoleMessage;
import com.discordsrv.common.logging.LogLevel; import com.discordsrv.common.logging.LogLevel;
import com.discordsrv.common.logging.Logger;
import net.dv8tion.jda.api.entities.Message; import net.dv8tion.jda.api.entities.Message;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.exception.ExceptionUtils; import org.apache.commons.lang3.exception.ExceptionUtils;
@ -16,7 +17,7 @@ import org.apache.commons.lang3.exception.ExceptionUtils;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Queue; import java.util.Queue;
import java.util.concurrent.ExecutionException; import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Future; import java.util.concurrent.Future;
import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
@ -26,9 +27,10 @@ public class SingleConsoleHandler {
private static final int MESSAGE_MAX_LENGTH = Message.MAX_CONTENT_LENGTH; private static final int MESSAGE_MAX_LENGTH = Message.MAX_CONTENT_LENGTH;
private final DiscordSRV discordSRV; private final DiscordSRV discordSRV;
private final Logger logger;
private final ConsoleConfig config; private final ConsoleConfig config;
private final Future<?> queueProcessingFuture;
private final Queue<LogEntry> queue = new LinkedBlockingQueue<>(); private final Queue<LogEntry> queue = new LinkedBlockingQueue<>();
private Future<?> queueProcessingFuture;
// Editing // Editing
private final List<LogMessage> messageCache; private final List<LogMessage> messageCache;
@ -36,14 +38,15 @@ public class SingleConsoleHandler {
// Preventing concurrent sends // Preventing concurrent sends
private final Object sendLock = new Object(); private final Object sendLock = new Object();
private Future<?> sendFuture; private CompletableFuture<?> sendFuture;
public SingleConsoleHandler(DiscordSRV discordSRV, ConsoleConfig config) { public SingleConsoleHandler(DiscordSRV discordSRV, Logger logger, ConsoleConfig config) {
this.discordSRV = discordSRV; this.discordSRV = discordSRV;
this.logger = logger;
this.config = config; this.config = config;
this.messageCache = config.appender.useEditing ? new ArrayList<>() : null; this.messageCache = config.appender.useEditing ? new ArrayList<>() : null;
this.queueProcessingFuture = discordSRV.scheduler().runAtFixedRate(this::processQueue, 2, TimeUnit.SECONDS); timeQueueProcess();
} }
public void queue(LogEntry entry) { public void queue(LogEntry entry) {
@ -57,43 +60,55 @@ public class SingleConsoleHandler {
mostRecentMessageId = null; mostRecentMessageId = null;
} }
private void timeQueueProcess() {
this.queueProcessingFuture = discordSRV.scheduler().runLater(this::processQueue, 2, TimeUnit.SECONDS);
}
private void processQueue() { private void processQueue() {
if (sendFuture != null && !sendFuture.isDone()) { try {
// Previous send still in progress. ConsoleConfig.Appender appenderConfig = config.appender;
return; ConsoleConfig.OutputMode outputMode = appenderConfig.outputMode;
}
ConsoleConfig.Appender appenderConfig = config.appender; Queue<LogMessage> currentBuffer = new LinkedBlockingQueue<>();
ConsoleConfig.OutputMode outputMode = appenderConfig.outputMode; LogEntry entry;
while ((entry = queue.poll()) != null) {
String level = entry.level().name();
if (appenderConfig.levels.levels.contains(level) == appenderConfig.levels.blacklist) {
// Ignored level
continue;
}
Queue<LogMessage> currentBuffer = new LinkedBlockingQueue<>(); String loggerName = entry.loggerName();
LogEntry entry; if (StringUtils.isEmpty(loggerName)) loggerName = "NONE";
while ((entry = queue.poll()) != null) { if (appenderConfig.loggers.loggers.contains(loggerName) == appenderConfig.loggers.blacklist) {
String level = entry.level().name(); // Ignored logger
if (appenderConfig.levels.levels.contains(level) == appenderConfig.levels.blacklist) { continue;
// Ignored level }
continue;
}
String loggerName = entry.loggerName(); List<String> messages = formatEntry(entry, outputMode, config.appender.diffExceptions);
if (StringUtils.isEmpty(loggerName)) loggerName = "NONE"; if (messages.size() == 1) {
if (appenderConfig.loggers.loggers.contains(loggerName) == appenderConfig.loggers.blacklist) { LogMessage message = new LogMessage(entry, messages.get(0));
// Ignored logger currentBuffer.add(message);
continue; } else {
} clearBuffer(currentBuffer, outputMode);
for (String message : messages) {
List<String> messages = formatEntry(entry, outputMode); send(message, true, outputMode);
if (messages.size() == 1) { }
LogMessage message = new LogMessage(entry, messages.get(0));
currentBuffer.add(message);
} else {
clearBuffer(currentBuffer, outputMode);
for (String message : messages) {
send(message, true, outputMode);
} }
} }
clearBuffer(currentBuffer, outputMode);
} catch (Exception ex) {
logger.error("Failed to process console lines", ex);
}
if (sendFuture != null) {
sendFuture.whenComplete((__, ___) -> {
sendFuture = null;
timeQueueProcess();
});
} else {
timeQueueProcess();
} }
clearBuffer(currentBuffer, outputMode);
} }
private void clearBuffer(Queue<LogMessage> currentBuffer, ConsoleConfig.OutputMode outputMode) { private void clearBuffer(Queue<LogMessage> currentBuffer, ConsoleConfig.OutputMode outputMode) {
@ -136,19 +151,17 @@ public class SingleConsoleHandler {
SendableDiscordMessage sendableMessage = SendableDiscordMessage.builder() SendableDiscordMessage sendableMessage = SendableDiscordMessage.builder()
.setContent(outputMode.prefix() + message + outputMode.suffix()) .setContent(outputMode.prefix() + message + outputMode.suffix())
.setSuppressedNotifications(config.appender.silentMessages) .setSuppressedNotifications(config.appender.silentMessages)
.setSuppressedEmbeds(config.appender.disableLinkEmbeds)
.build(); .build();
synchronized (sendLock) { synchronized (sendLock) {
if (sendFuture != null && !sendFuture.isDone()) { CompletableFuture<?> future = sendFuture != null ? sendFuture : CompletableFuture.completedFuture(null);
try {
sendFuture.get();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} catch (ExecutionException ignored) {}
}
sendFuture = discordSRV.discordAPI() sendFuture = future
.findOrCreateDestinations(config.channel.asDestination(), true, true, true) .thenCompose(__ ->
discordSRV.discordAPI()
.findOrCreateDestinations(config.channel.asDestination(), true, true, true)
)
.thenApply(channels -> { .thenApply(channels -> {
if (channels.isEmpty()) { if (channels.isEmpty()) {
throw new IllegalStateException("No channel"); throw new IllegalStateException("No channel");
@ -173,20 +186,21 @@ public class SingleConsoleHandler {
} }
} }
private List<String> formatEntry(LogEntry entry, ConsoleConfig.OutputMode outputMode) { private List<String> formatEntry(LogEntry entry, ConsoleConfig.OutputMode outputMode, boolean diffExceptions) {
int blockLength = outputMode.blockLength(); int blockLength = outputMode.blockLength();
int maximumPart = MESSAGE_MAX_LENGTH - blockLength - "\n".length(); int maximumPart = MESSAGE_MAX_LENGTH - blockLength - "\n".length();
String parsedMessage; String parsedMessage;
ConsoleMessage consoleMessage = new ConsoleMessage(discordSRV, entry.message());
switch (outputMode) { switch (outputMode) {
case ANSI: case ANSI:
parsedMessage = new ConsoleMessage(discordSRV, entry.message()).asAnsi(); parsedMessage = consoleMessage.asAnsi();
break; break;
case MARKDOWN: case MARKDOWN:
parsedMessage = new ConsoleMessage(discordSRV, entry.message()).asMarkdown(); parsedMessage = consoleMessage.asMarkdown();
break; break;
default: default:
parsedMessage = entry.message(); parsedMessage = consoleMessage.asPlain();
break; break;
} }
@ -194,14 +208,26 @@ public class SingleConsoleHandler {
config.appender.lineFormat, config.appender.lineFormat,
entry, entry,
new SinglePlaceholder("message", parsedMessage) new SinglePlaceholder("message", parsedMessage)
) + "\n"; );
Throwable thrown = entry.throwable();
String throwable = thrown != null ? ExceptionUtils.getStackTrace(thrown) : StringUtils.EMPTY;
if (outputMode == ConsoleConfig.OutputMode.DIFF) { if (outputMode == ConsoleConfig.OutputMode.DIFF) {
message = getLogLevelDiffCharacter(entry.level()) + message; String diff = getLogLevelDiffCharacter(entry.level());
// TODO: also format throwable? if (!message.isEmpty()) {
message = diff + message.replace("\n", "\n" + diff);
}
String exceptionCharacter = diffExceptions ? diff : "";
if (!throwable.isEmpty()) {
throwable = exceptionCharacter + throwable.replace("\n", "\n" + exceptionCharacter);
}
} }
String throwable = ExceptionUtils.getMessage(entry.throwable()); if (!message.isEmpty()) {
message += "\n";
}
if (!throwable.isEmpty()) { if (!throwable.isEmpty()) {
throwable += "\n"; throwable += "\n";
} }
@ -257,10 +283,10 @@ public class SingleConsoleHandler {
private String getLogLevelDiffCharacter(LogLevel level) { private String getLogLevelDiffCharacter(LogLevel level) {
if (level == LogLevel.StandardLogLevel.WARNING) { if (level == LogLevel.StandardLogLevel.WARNING) {
return "+"; return "+ ";
} else if (level == LogLevel.StandardLogLevel.ERROR) { } else if (level == LogLevel.StandardLogLevel.ERROR) {
return "-"; return "- ";
} }
return " "; return " ";
} }
} }

View File

@ -58,6 +58,11 @@ public class ConsoleMessage {
return discordSRV.componentFactory().ansiSerializer().serialize(component); return discordSRV.componentFactory().ansiSerializer().serialize(component);
} }
public String asPlain() {
Component component = builder.build();
return discordSRV.componentFactory().plainSerializer().serialize(component);
}
private void parse(String input) { private void parse(String input) {
Matcher matcher = PATTERN.matcher(input); Matcher matcher = PATTERN.matcher(input);

View File

@ -89,7 +89,11 @@ public abstract class AbstractDiscordGuildMessageChannel<T extends GuildMessageC
.setAvatarUrl(message.getWebhookAvatarUrl()) .setAvatarUrl(message.getWebhookAvatarUrl())
); );
} else { } else {
createRequest = CompletableFuture.completedFuture(((R) channel.sendMessage(createData))); createRequest = CompletableFuture.completedFuture(
((R) channel.sendMessage(createData)
// JDA doesn't properly grab this from MessageCreateData
.setSuppressEmbeds(createData.isSuppressEmbeds()))
);
} }
return createRequest return createRequest

View File

@ -73,6 +73,7 @@ public final class SendableDiscordMessageUtil {
.setContent(message.getContent()) .setContent(message.getContent())
.setEmbeds(embeds) .setEmbeds(embeds)
.setAllowedMentions(allowedTypes) .setAllowedMentions(allowedTypes)
.setSuppressEmbeds(message.isSuppressedEmbeds())
.mentionUsers(allowedUsers.stream().mapToLong(l -> l).toArray()) .mentionUsers(allowedUsers.stream().mapToLong(l -> l).toArray())
.mentionRoles(allowedRoles.stream().mapToLong(l -> l).toArray()) .mentionRoles(allowedRoles.stream().mapToLong(l -> l).toArray())
.setFiles(uploads); .setFiles(uploads);