diff --git a/src/main/java/com/softawii/capivara/config/SpringConfig.java b/src/main/java/com/softawii/capivara/config/SpringConfig.java index 0835ba6..6efe69f 100644 --- a/src/main/java/com/softawii/capivara/config/SpringConfig.java +++ b/src/main/java/com/softawii/capivara/config/SpringConfig.java @@ -9,6 +9,7 @@ import net.dv8tion.jda.api.utils.cache.CacheFlag; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -77,23 +78,27 @@ public JDA jda() { } @Bean - public Curupira curupira(JDA jda) { - String pkg = "com.softawii.capivara.listeners"; - String resetEnv = env.getProperty("curupira.reset", "false"); - boolean reset = Boolean.parseBoolean(resetEnv); - LOGGER.info("curupira.reset: " + reset); - - - CapivaraExceptionHandler exceptionHandler = null; - String logChannelId = env.getProperty("log.channel.id"); - String logDirectory = env.getProperty("log_directory"); + public CapivaraExceptionHandler capivaraExceptionHandler() { + String logChannelId = env.getProperty("log.channel.id"); + String logDirectory = env.getProperty("log_directory"); if (logChannelId != null) { Path logPath = null; if (logDirectory != null) { logPath = Path.of(logDirectory); } - exceptionHandler = new CapivaraExceptionHandler(logChannelId, logPath); + return new CapivaraExceptionHandler(logChannelId, logPath); } + + return null; + } + + @Bean + public Curupira curupira(JDA jda, @Autowired(required = false) CapivaraExceptionHandler exceptionHandler) { + String pkg = "com.softawii.capivara.listeners"; + String resetEnv = env.getProperty("curupira.reset", "false"); + boolean reset = Boolean.parseBoolean(resetEnv); + LOGGER.info("curupira.reset: " + reset); + return new Curupira(jda, reset, exceptionHandler, pkg); } diff --git a/src/main/java/com/softawii/capivara/listeners/events/VoiceEvents.java b/src/main/java/com/softawii/capivara/listeners/events/VoiceEvents.java index cbbd82a..3be3e84 100644 --- a/src/main/java/com/softawii/capivara/listeners/events/VoiceEvents.java +++ b/src/main/java/com/softawii/capivara/listeners/events/VoiceEvents.java @@ -4,12 +4,14 @@ import com.softawii.capivara.core.VoiceManager; import com.softawii.capivara.entity.VoiceHive; import com.softawii.capivara.exceptions.KeyNotFoundException; +import com.softawii.capivara.utils.CapivaraExceptionHandler; import net.dv8tion.jda.api.JDA; import net.dv8tion.jda.api.entities.Member; import net.dv8tion.jda.api.entities.channel.ChannelType; import net.dv8tion.jda.api.entities.channel.concrete.Category; import net.dv8tion.jda.api.entities.channel.concrete.VoiceChannel; import net.dv8tion.jda.api.entities.channel.middleman.GuildMessageChannel; +import net.dv8tion.jda.api.events.Event; import net.dv8tion.jda.api.events.channel.ChannelDeleteEvent; import net.dv8tion.jda.api.events.channel.update.ChannelUpdateParentEvent; import net.dv8tion.jda.api.events.channel.update.GenericChannelUpdateEvent; @@ -28,15 +30,15 @@ public class VoiceEvents extends ListenerAdapter { private final DroneManager droneManager; private final Logger LOGGER = LogManager.getLogger(VoiceEvents.class); - private final JDA jda; + private final CapivaraExceptionHandler exceptionHandler; - public VoiceEvents(JDA jda, VoiceManager voiceManager, DroneManager droneManager) { + public VoiceEvents(JDA jda, VoiceManager voiceManager, DroneManager droneManager, CapivaraExceptionHandler exceptionHandler) { this.voiceManager = voiceManager; this.droneManager = droneManager; - this.jda = jda; + this.exceptionHandler = exceptionHandler; this.droneManager.checkEmptyDrones(); this.voiceManager.checkRemovedHives(); - this.jda.addEventListener(this); + jda.addEventListener(this); } //region Voice Events @@ -69,6 +71,7 @@ public void onGuildVoiceUpdate(@NotNull GuildVoiceUpdateEvent event) { } } catch (Exception e) { LOGGER.error("Error on onGuildVoiceUpdate", e); + handleException(e, event); } } @@ -114,6 +117,7 @@ public void onChannelDelete(@NotNull ChannelDeleteEvent event) { } } catch (Exception e) { LOGGER.error("Error on ChannelDeleteEvent", e); + handleException(e, event); } } @@ -141,6 +145,7 @@ public void onChannelUpdateParent(@NotNull ChannelUpdateParentEvent event) { } } catch (Exception e) { LOGGER.error("Error on ChannelUpdateParentEvent", e); + handleException(e, event); } } @@ -157,10 +162,12 @@ public void onGenericPermissionOverride(@NotNull GenericPermissionOverrideEvent } catch (KeyNotFoundException e) { // Not Found... LOGGER.debug(method + " : error : " + e.getMessage()); + handleException(e, event); } } } catch (Exception e) { LOGGER.error("Error on " + method, e); + handleException(e, event); } } @@ -175,14 +182,20 @@ public void onGenericChannelUpdate(@NotNull GenericChannelUpdateEvent event) } catch (KeyNotFoundException e) { // Not Found... LOGGER.debug("onGenericChannelUpdate : error : " + e.getMessage()); + handleException(e, event); } } } catch (Exception e) { LOGGER.error("Error on GenericChannelUpdateEvent", e); + handleException(e, event); } } - - //endregion + private void handleException(Exception exception, Event event) { + if (exceptionHandler != null) { + exceptionHandler.handle(exception, event); + } + } + } diff --git a/src/main/java/com/softawii/capivara/utils/CapivaraExceptionHandler.java b/src/main/java/com/softawii/capivara/utils/CapivaraExceptionHandler.java index 72d753d..b842995 100644 --- a/src/main/java/com/softawii/capivara/utils/CapivaraExceptionHandler.java +++ b/src/main/java/com/softawii/capivara/utils/CapivaraExceptionHandler.java @@ -5,6 +5,9 @@ import net.dv8tion.jda.api.JDA; import net.dv8tion.jda.api.entities.MessageEmbed; import net.dv8tion.jda.api.entities.channel.concrete.TextChannel; +import net.dv8tion.jda.api.events.Event; +import net.dv8tion.jda.api.events.channel.GenericChannelEvent; +import net.dv8tion.jda.api.events.guild.GenericGuildEvent; import net.dv8tion.jda.api.interactions.Interaction; import net.dv8tion.jda.api.requests.restaction.MessageCreateAction; import net.dv8tion.jda.api.utils.FileUpload; @@ -68,6 +71,62 @@ public void handle(Throwable throwable, Interaction interaction) { } } + public void handle(Throwable throwable, Event event) { + InputStream logFileBytes = null; + if (logDirectory != null) { + Path logFile = logDirectory.resolve("capivara.log"); + if (Files.isDirectory(logDirectory) && Files.exists(logFile) && Files.isRegularFile(logFile)) { + try { + logFileBytes = Files.newInputStream(logFile); + } catch (IOException e) { + LOGGER.warn(e.getMessage(), e); + } + } + } + JDA jda = event.getJDA(); + TextChannel channel = jda.getTextChannelById(channelId); + if (channel != null) { + String stackTrace = getStackTrace(throwable); + String now = OffsetDateTime.now(ZoneId.of("America/Sao_Paulo")).format(DateTimeFormatter.ISO_OFFSET_DATE_TIME); + String stackTraceFileName = String.format("capivara-stacktrace-%s.log", now); + String logFileName = String.format("capivara-log-%s.log", now); + MessageEmbed interactionContext = getContext(event); + try { + MessageCreateAction messageAction = channel.sendFiles(FileUpload.fromData(stackTrace.getBytes(StandardCharsets.UTF_8), stackTraceFileName)).setEmbeds(interactionContext); + if (logFileBytes != null) { + messageAction = messageAction.addFiles(FileUpload.fromData(logFileBytes, logFileName)); + } + messageAction.submit(); + } catch (IllegalArgumentException e) { + LOGGER.warn(e.getMessage(), e); + channel.sendMessage(getStackTrace(e)).submit(); + } + } + } + + private MessageEmbed getContext(Event event) { + EmbedBuilder builder = new EmbedBuilder(); + builder.setColor(Color.RED) + .setTitle("Unhandled Exception"); + + if (event instanceof GenericChannelEvent channelEvent) { + if (channelEvent.isFromGuild()) { + builder.addField("Guild ID", channelEvent.getGuild().getId(), true) + .addField("Guild Name", channelEvent.getGuild().getName(), true) + .addField("Guild Members", String.valueOf(channelEvent.getGuild().getMembers().size()), true); + } + builder.addField("Channel Type", channelEvent.getChannelType().toString(), true) + .addField("Channel ID", channelEvent.getChannel().getId(), true) + .addField("Channel Name", channelEvent.getChannel().getName(), true); + } else if (event instanceof GenericGuildEvent guildEvent) { + builder.addField("Guild ID", guildEvent.getGuild().getId(), true) + .addField("Guild Name", guildEvent.getGuild().getName(), true) + .addField("Guild Members", String.valueOf(guildEvent.getGuild().getMembers().size()), true); + } + + return builder.build(); + } + private String getStackTrace(Throwable throwable) { StringWriter stringWriter = new StringWriter(); PrintWriter printWriter = new PrintWriter(stringWriter);