diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/breakhandler/breakhandlerv2/BreakHandlerV2Config.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/breakhandler/breakhandlerv2/BreakHandlerV2Config.java index f32cfd33134..0e75f3bebd2 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/breakhandler/breakhandlerv2/BreakHandlerV2Config.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/breakhandler/breakhandlerv2/BreakHandlerV2Config.java @@ -66,6 +66,124 @@ default int maxBreakDuration() { return 15; } + @ConfigItem( + keyName = "enableLongBreaks", + name = "Enable Long Breaks", + description = "Enable a second independent long-break timer", + position = 4, + section = breakTimingSettings + ) + default boolean enableLongBreaks() { + return false; + } + + @ConfigItem( + keyName = "minLongBreakInterval", + name = "Min Long Break Interval (minutes)", + description = "Minimum time to play before a long break can trigger", + position = 5, + section = breakTimingSettings + ) + @Range(min = 1, max = 600) + default int minLongBreakInterval() { + return 20; + } + + @ConfigItem( + keyName = "maxLongBreakInterval", + name = "Max Long Break Interval (minutes)", + description = "Maximum time to play before a long break can trigger", + position = 6, + section = breakTimingSettings + ) + @Range(min = 1, max = 600) + default int maxLongBreakInterval() { + return 30; + } + + @ConfigItem( + keyName = "minLongBreakDuration", + name = "Min Long Break Duration (minutes)", + description = "Minimum long-break duration", + position = 7, + section = breakTimingSettings + ) + @Range(min = 1, max = 600) + default int minLongBreakDuration() { + return 8; + } + + @ConfigItem( + keyName = "maxLongBreakDuration", + name = "Max Long Break Duration (minutes)", + description = "Maximum long-break duration", + position = 8, + section = breakTimingSettings + ) + @Range(min = 1, max = 600) + default int maxLongBreakDuration() { + return 10; + } + + @ConfigItem( + keyName = "enableMegaBreaks", + name = "Enable Mega Breaks", + description = "Enable a third independent mega-break timer", + position = 9, + section = breakTimingSettings + ) + default boolean enableMegaBreaks() { + return false; + } + + @ConfigItem( + keyName = "minMegaBreakInterval", + name = "Min Mega Break Interval (minutes)", + description = "Minimum time to play before a mega break can trigger", + position = 10, + section = breakTimingSettings + ) + @Range(min = 1, max = 600) + default int minMegaBreakInterval() { + return 120; + } + + @ConfigItem( + keyName = "maxMegaBreakInterval", + name = "Max Mega Break Interval (minutes)", + description = "Maximum time to play before a mega break can trigger", + position = 11, + section = breakTimingSettings + ) + @Range(min = 1, max = 600) + default int maxMegaBreakInterval() { + return 180; + } + + @ConfigItem( + keyName = "minMegaBreakDuration", + name = "Min Mega Break Duration (minutes)", + description = "Minimum mega-break duration", + position = 12, + section = breakTimingSettings + ) + @Range(min = 1, max = 600) + default int minMegaBreakDuration() { + return 20; + } + + @ConfigItem( + keyName = "maxMegaBreakDuration", + name = "Max Mega Break Duration (minutes)", + description = "Maximum mega-break duration", + position = 13, + section = breakTimingSettings + ) + @Range(min = 1, max = 600) + default int maxMegaBreakDuration() { + return 30; + } + // ========== BREAK BEHAVIOR SECTION ========== @ConfigSection( name = "Break Behavior", diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/breakhandler/breakhandlerv2/BreakHandlerV2Overlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/breakhandler/breakhandlerv2/BreakHandlerV2Overlay.java index 579afdaeb8d..0a2777c40d6 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/breakhandler/breakhandlerv2/BreakHandlerV2Overlay.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/breakhandler/breakhandlerv2/BreakHandlerV2Overlay.java @@ -67,6 +67,18 @@ public Dimension render(Graphics2D graphics) { .rightColor(Color.GRAY) .build()); + panelComponent.getChildren().add(LineComponent.builder() + .left("Runtime:") + .right(formatDuration(script.getScriptActiveSeconds())) + .rightColor(Color.WHITE) + .build()); + + panelComponent.getChildren().add(LineComponent.builder() + .left("Breaks:") + .right(String.valueOf(script.getBreaksActivatedCount())) + .rightColor(Color.WHITE) + .build()); + // Show play schedule info if enabled if (config.usePlaySchedule()) { panelComponent.getChildren().add(LineComponent.builder() @@ -88,14 +100,30 @@ public Dimension render(Graphics2D graphics) { .rightColor(Color.GREEN) .build()); } + long secondsUntilLongBreak = script.getTimeUntilLongBreak(); + if (config.enableLongBreaks() && secondsUntilLongBreak >= 0) { + panelComponent.getChildren().add(LineComponent.builder() + .left("Long break:") + .right(formatDuration(secondsUntilLongBreak)) + .rightColor(Color.ORANGE) + .build()); + } + long secondsUntilMegaBreak = script.getTimeUntilMegaBreak(); + if (config.enableMegaBreaks() && secondsUntilMegaBreak >= 0) { + panelComponent.getChildren().add(LineComponent.builder() + .left("Mega break:") + .right(formatDuration(secondsUntilMegaBreak)) + .rightColor(Color.MAGENTA) + .build()); + } } else if (BreakHandlerV2State.isBreakActive()) { long secondsRemaining = script.getBreakTimeRemaining(); if (secondsRemaining >= 0) { String timeStr = formatDuration(secondsRemaining); panelComponent.getChildren().add(LineComponent.builder() - .left("Break ends:") + .left(script.isCurrentBreakMega() ? "Mega break ends:" : script.isCurrentBreakLong() ? "Long break ends:" : "Break ends:") .right(timeStr) - .rightColor(Color.ORANGE) + .rightColor(script.isCurrentBreakMega() ? Color.MAGENTA : Color.ORANGE) .build()); } } @@ -163,7 +191,9 @@ public Dimension render(Graphics2D graphics) { // Break configuration panelComponent.getChildren().add(LineComponent.builder() .left("Break type:") - .right(config.logoutOnBreak() ? "Logout" : "Stay logged in") + .right((config.logoutOnBreak() ? "Logout" : "Stay logged in") + + (config.enableLongBreaks() ? " + long" : "") + + (config.enableMegaBreaks() ? " + mega" : "")) .rightColor(Color.LIGHT_GRAY) .build()); diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/breakhandler/breakhandlerv2/BreakHandlerV2Script.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/breakhandler/breakhandlerv2/BreakHandlerV2Script.java index 697b3a24769..c60d30d3d7e 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/breakhandler/breakhandlerv2/BreakHandlerV2Script.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/breakhandler/breakhandlerv2/BreakHandlerV2Script.java @@ -45,6 +45,8 @@ public BreakHandlerV2Script() { // Timing variables (volatile for thread visibility from overlay/UI threads) private volatile Instant nextBreakTime; + private volatile Instant nextLongBreakTime; + private volatile Instant nextMegaBreakTime; private volatile Instant breakEndTime; private volatile Instant loginAttemptTime; @@ -60,6 +62,8 @@ public BreakHandlerV2Script() { private String stoppedPluginClassName = PluginStopOption.NONE_VALUE; private Instant pluginStopEarliestTime = Instant.MIN; private Instant pluginRestartAllowedAt = Instant.MIN; + private volatile Instant scriptStartedAt; + private volatile int breaksActivatedCount = 0; // Persisted break keys private static final String PERSISTED_BREAK_END_KEY = "persistedBreakEnd"; @@ -68,6 +72,10 @@ public BreakHandlerV2Script() { // Break duration in milliseconds private long currentBreakDuration = 0; private boolean logoutBreakActive = false; + private boolean longBreakDue = false; + private boolean megaBreakDue = false; + private boolean currentBreakIsLong = false; + private boolean currentBreakIsMega = false; // Login retry backoff constants private static final int MAX_LOGIN_ATTEMPTS = 10; @@ -78,17 +86,19 @@ public BreakHandlerV2Script() { private static final int MAX_SAFETY_CHECK_ATTEMPTS = 60; private static final int SAFETY_CHECK_DELAY_MS = 5000; // 5 seconds between checks - public static String version = "2.0.1"; + public static String version = "2.0.6"; /** * Run the break handler script */ public boolean run(BreakHandlerV2Config config) { this.config = config; + scriptStartedAt = Instant.now(); + breaksActivatedCount = 0; BreakHandlerV2State.setState(BreakHandlerV2State.WAITING_FOR_BREAK); // Initialize next break time immediately to prevent null values in overlay - scheduleNextBreak(); + scheduleNextBreak(true, true); log.info("[BreakHandlerV2] Initial break scheduled for {}", nextBreakTime); // Load active profile loadActiveProfile(); @@ -107,6 +117,7 @@ public boolean run(BreakHandlerV2Config config) { // Detect unexpected logout while waiting for break detectUnexpectedLogout(); enforceLogoutDuringActiveBreak(); + mergeSecondaryBreaksIntoActiveBreakIfDue(); updateWindowTitle(); // Main state machine @@ -191,7 +202,30 @@ private void handleWaitingForBreak() { } // Check if it's time for a break (only when play schedule is disabled) + if (config.enableMegaBreaks() && nextMegaBreakTime != null && !Instant.now().isBefore(nextMegaBreakTime)) { + megaBreakDue = true; + nextMegaBreakTime = null; + longBreakDue = config.enableLongBreaks() && nextLongBreakTime != null && !Instant.now().isBefore(nextLongBreakTime); + if (longBreakDue) { + nextLongBreakTime = null; + } + log.info("[BreakHandlerV2] Mega break time reached, requesting mega break"); + transitionToState(BreakHandlerV2State.BREAK_REQUESTED); + return; + } + + if (config.enableLongBreaks() && nextLongBreakTime != null && !Instant.now().isBefore(nextLongBreakTime)) { + longBreakDue = true; + nextLongBreakTime = null; + megaBreakDue = false; + log.info("[BreakHandlerV2] Long break time reached, requesting long break"); + transitionToState(BreakHandlerV2State.BREAK_REQUESTED); + return; + } + if (nextBreakTime != null && Instant.now().isAfter(nextBreakTime)) { + longBreakDue = false; + megaBreakDue = false; log.info("[BreakHandlerV2] Break time reached, requesting break"); transitionToState(BreakHandlerV2State.BREAK_REQUESTED); } @@ -243,6 +277,7 @@ private void handleInitiatingBreak() { if (!Microbot.isLoggedIn()) { log.info("[BreakHandlerV2] Already logged out, transitioning to LOGGED_OUT"); setBreakTimer(true); + recordBreakActivated(); sendBreakStartedNotification(true); safetyCheckAttempts = 0; // Reset counter transitionToState(BreakHandlerV2State.LOGGED_OUT); @@ -468,6 +503,9 @@ private void handleBreakEnding() { startConfiguredPluginIfNeeded(); + boolean completedLongBreak = currentBreakIsLong || longBreakDue; + boolean completedMegaBreak = currentBreakIsMega || megaBreakDue; + // Reset variables breakEndTime = null; loginAttemptTime = null; @@ -482,8 +520,8 @@ private void handleBreakEnding() { // Unpause scripts Microbot.pauseAllScripts.set(false); - // Schedule next break - scheduleNextBreak(); + // Schedule next break. A normal break must not reset the pending long-break timer. + scheduleNextBreak(completedLongBreak, completedMegaBreak); String breakMessage = nextBreakTime != null ? "Next break scheduled for " + nextBreakTime @@ -577,6 +615,71 @@ private void enforceLogoutDuringActiveBreak() { } } + /** + * If a secondary break timer expires while another break is active, fold it into the + * current break instead of dropping it or starting a competing break cycle. + */ + private void mergeSecondaryBreaksIntoActiveBreakIfDue() { + if (config == null || breakEndTime == null || !BreakHandlerV2State.isBreakActive()) { + return; + } + + Instant now = Instant.now(); + boolean merged = false; + StringBuilder mergedTypes = new StringBuilder(); + + if (config.enableLongBreaks() && nextLongBreakTime != null && !now.isBefore(nextLongBreakTime)) { + longBreakDue = true; + nextLongBreakTime = null; + mergedTypes.append("long"); + + if (!currentBreakIsLong && !currentBreakIsMega) { + currentBreakIsLong = true; + long longBreakDuration = calculateLongBreakDuration(); + Instant mergedBreakEndTime = now.plus(longBreakDuration, ChronoUnit.MILLIS); + if (mergedBreakEndTime.isAfter(breakEndTime)) { + breakEndTime = mergedBreakEndTime; + currentBreakDuration = Math.max(0, now.until(breakEndTime, ChronoUnit.MILLIS)); + merged = true; + } + } + } + + if (config.enableMegaBreaks() && nextMegaBreakTime != null && !now.isBefore(nextMegaBreakTime)) { + megaBreakDue = true; + nextMegaBreakTime = null; + if (mergedTypes.length() > 0) { + mergedTypes.append(" + "); + } + mergedTypes.append("mega"); + + if (!currentBreakIsMega) { + currentBreakIsMega = true; + currentBreakIsLong = false; + long megaBreakDuration = calculateMegaBreakDuration(); + Instant mergedBreakEndTime = now.plus(megaBreakDuration, ChronoUnit.MILLIS); + if (mergedBreakEndTime.isAfter(breakEndTime)) { + breakEndTime = mergedBreakEndTime; + currentBreakDuration = Math.max(0, now.until(breakEndTime, ChronoUnit.MILLIS)); + merged = true; + } + } + } + + if (mergedTypes.length() == 0) { + return; + } + + if (merged) { + persistBreakState(logoutBreakActive); + } + + log.info("[BreakHandlerV2] {} break timer(s) merged into active break; break now ends at {}", mergedTypes, breakEndTime); + sendDiscordNotification("Break Timer Merged", + "A " + mergedTypes + " break became due during an active break.\nNew remaining duration: " + + Math.max(0, Instant.now().until(breakEndTime, ChronoUnit.MINUTES)) + " minutes"); + } + /** * Select world based on configuration and profile */ @@ -702,15 +805,23 @@ private Integer resolveProfilePreferredWorld(WorldRegion region) { /** * Schedule the next break */ - private void scheduleNextBreak() { + private void scheduleNextBreak(boolean rescheduleLongBreak, boolean rescheduleMegaBreak) { + longBreakDue = false; + megaBreakDue = false; + currentBreakIsLong = false; + currentBreakIsMega = false; if (config.usePlaySchedule()) { if (!config.playSchedule().isOutsideSchedule()) { Duration timeUntilEnd = config.playSchedule().timeUntilScheduleEnds(); nextBreakTime = Instant.now().plus(timeUntilEnd); + nextLongBreakTime = null; + nextMegaBreakTime = null; log.info("[BreakHandlerV2] Play schedule active ({}), break when schedule ends in {} minutes", config.playSchedule().name(), timeUntilEnd.toMinutes()); } else { nextBreakTime = null; + nextLongBreakTime = null; + nextMegaBreakTime = null; log.info("[BreakHandlerV2] Outside play schedule ({}), currently on break", config.playSchedule().name()); } @@ -726,6 +837,34 @@ private void scheduleNextBreak() { log.info("[BreakHandlerV2] Next break in {} minutes", playtimeMinutes); + if (config.enableLongBreaks()) { + if (!rescheduleLongBreak && nextLongBreakTime != null) { + log.info("[BreakHandlerV2] Keeping pending long break for {}", nextLongBreakTime); + } else { + int minLongMinutes = Math.min(config.minLongBreakInterval(), config.maxLongBreakInterval()); + int maxLongMinutes = Math.max(config.minLongBreakInterval(), config.maxLongBreakInterval()); + int longBreakMinutes = Rs2Random.between(minLongMinutes, maxLongMinutes); + nextLongBreakTime = Instant.now().plus(longBreakMinutes, ChronoUnit.MINUTES); + log.info("[BreakHandlerV2] Next long break in {} minutes", longBreakMinutes); + } + } else { + nextLongBreakTime = null; + } + + if (config.enableMegaBreaks()) { + if (!rescheduleMegaBreak && nextMegaBreakTime != null) { + log.info("[BreakHandlerV2] Keeping pending mega break for {}", nextMegaBreakTime); + } else { + int minMegaMinutes = Math.min(config.minMegaBreakInterval(), config.maxMegaBreakInterval()); + int maxMegaMinutes = Math.max(config.minMegaBreakInterval(), config.maxMegaBreakInterval()); + int megaBreakMinutes = Rs2Random.between(minMegaMinutes, maxMegaMinutes); + nextMegaBreakTime = Instant.now().plus(megaBreakMinutes, ChronoUnit.MINUTES); + log.info("[BreakHandlerV2] Next mega break in {} minutes", megaBreakMinutes); + } + } else { + nextMegaBreakTime = null; + } + updatePluginStopLeadTime(); } @@ -739,8 +878,9 @@ private void updatePluginStopLeadTime() { } int leadSeconds = Math.max(0, config.stopPluginLeadSeconds()); - if (nextBreakTime != null && leadSeconds > 0) { - pluginStopEarliestTime = nextBreakTime.minusSeconds(leadSeconds); + Instant nextStopRelevantBreakTime = getNextScheduledBreakTime(); + if (nextStopRelevantBreakTime != null && leadSeconds > 0) { + pluginStopEarliestTime = nextStopRelevantBreakTime.minusSeconds(leadSeconds); } else { pluginStopEarliestTime = Instant.MIN; } @@ -771,6 +911,8 @@ private void applyPreBreakPluginStopLead() { private long calculateBreakDuration() { // If outside play schedule, break until next play time if (isOutsidePlaySchedule()) { + currentBreakIsLong = false; + currentBreakIsMega = false; Duration timeUntilPlaySchedule = config.playSchedule().timeUntilNextSchedule(); long durationMs = timeUntilPlaySchedule.toMillis(); log.info("[BreakHandlerV2] Play schedule break duration: {} minutes (until next scheduled play time)", @@ -778,15 +920,47 @@ private long calculateBreakDuration() { return durationMs; } - int minMinutes = config.minBreakDuration(); - int maxMinutes = config.maxBreakDuration(); + int minMinutes; + int maxMinutes; + if (megaBreakDue && config.enableMegaBreaks()) { + currentBreakIsMega = true; + currentBreakIsLong = false; + minMinutes = Math.min(config.minMegaBreakDuration(), config.maxMegaBreakDuration()); + maxMinutes = Math.max(config.minMegaBreakDuration(), config.maxMegaBreakDuration()); + } else if (longBreakDue && config.enableLongBreaks()) { + currentBreakIsLong = true; + currentBreakIsMega = false; + minMinutes = Math.min(config.minLongBreakDuration(), config.maxLongBreakDuration()); + maxMinutes = Math.max(config.minLongBreakDuration(), config.maxLongBreakDuration()); + } else { + currentBreakIsLong = false; + currentBreakIsMega = false; + minMinutes = Math.min(config.minBreakDuration(), config.maxBreakDuration()); + maxMinutes = Math.max(config.minBreakDuration(), config.maxBreakDuration()); + } int breakMinutes = Rs2Random.between(minMinutes, maxMinutes); - log.info("[BreakHandlerV2] Break duration: {} minutes", breakMinutes); + log.info("[BreakHandlerV2] {} duration: {} minutes", getCurrentBreakTypeName(), breakMinutes); return breakMinutes * 60000L; // Convert to milliseconds } + private long calculateLongBreakDuration() { + int minMinutes = Math.min(config.minLongBreakDuration(), config.maxLongBreakDuration()); + int maxMinutes = Math.max(config.minLongBreakDuration(), config.maxLongBreakDuration()); + int breakMinutes = Rs2Random.between(minMinutes, maxMinutes); + log.info("[BreakHandlerV2] Merged long break duration: {} minutes", breakMinutes); + return breakMinutes * 60000L; + } + + private long calculateMegaBreakDuration() { + int minMinutes = Math.min(config.minMegaBreakDuration(), config.maxMegaBreakDuration()); + int maxMinutes = Math.max(config.minMegaBreakDuration(), config.maxMegaBreakDuration()); + int breakMinutes = Rs2Random.between(minMinutes, maxMinutes); + log.info("[BreakHandlerV2] Merged mega break duration: {} minutes", breakMinutes); + return breakMinutes * 60000L; + } + /** * Stops a configured Microbot plugin once per break cycle. */ @@ -945,6 +1119,57 @@ public long getTimeUntilBreak() { return Instant.now().until(nextBreakTime, ChronoUnit.SECONDS); } + public long getTimeUntilLongBreak() { + if (nextLongBreakTime == null) { + return -1; + } + return Instant.now().until(nextLongBreakTime, ChronoUnit.SECONDS); + } + + public long getTimeUntilMegaBreak() { + if (nextMegaBreakTime == null) { + return -1; + } + return Instant.now().until(nextMegaBreakTime, ChronoUnit.SECONDS); + } + + public long getTimeUntilNextScheduledBreak() { + Instant nextScheduledBreak = getNextScheduledBreakTime(); + if (nextScheduledBreak == null) { + return -1; + } + return Instant.now().until(nextScheduledBreak, ChronoUnit.SECONDS); + } + + public boolean isCurrentBreakLong() { + return currentBreakIsLong; + } + + public boolean isCurrentBreakMega() { + return currentBreakIsMega; + } + + public String getCurrentBreakTypeName() { + if (currentBreakIsMega) { + return "Mega break"; + } + if (currentBreakIsLong) { + return "Long break"; + } + return "Break"; + } + + private Instant getNextScheduledBreakTime() { + Instant nextScheduledBreak = nextBreakTime; + if (nextLongBreakTime != null && (nextScheduledBreak == null || nextLongBreakTime.isBefore(nextScheduledBreak))) { + nextScheduledBreak = nextLongBreakTime; + } + if (nextMegaBreakTime != null && (nextScheduledBreak == null || nextMegaBreakTime.isBefore(nextScheduledBreak))) { + nextScheduledBreak = nextMegaBreakTime; + } + return nextScheduledBreak; + } + /** * Get time remaining in break in seconds */ @@ -973,6 +1198,8 @@ public void shutdown() { // Clear timers nextBreakTime = null; + nextLongBreakTime = null; + nextMegaBreakTime = null; breakEndTime = null; loginAttemptTime = null; @@ -981,6 +1208,10 @@ public void shutdown() { loginRetryCount = 0; safetyCheckAttempts = 0; logoutBreakActive = false; + longBreakDue = false; + megaBreakDue = false; + currentBreakIsLong = false; + currentBreakIsMega = false; pluginStopTriggered = false; pluginRestartPending = false; stoppedPluginClassName = PluginStopOption.NONE_VALUE; @@ -1043,6 +1274,7 @@ private void initializeBreakIfOutsideSchedule() { } setBreakTimer(true); + recordBreakActivated(); stopConfiguredPluginIfNeeded(); sendBreakStartedNotification(true); log.info("[BreakHandlerV2] Outside play schedule on startup, enforcing break until {}", breakEndTime); @@ -1079,12 +1311,14 @@ private void extendBreakUntilSchedule() { private void beginPauseBreak() { setBreakTimer(false); + recordBreakActivated(); sendBreakStartedNotification(false); Microbot.pauseAllScripts.set(true); } private void beginLogoutBreak() { setBreakTimer(true); + recordBreakActivated(); sendBreakStartedNotification(true); transitionToState(BreakHandlerV2State.LOGOUT_REQUESTED); } @@ -1095,10 +1329,27 @@ private void setBreakTimer(boolean logoutBreak) { persistBreakState(logoutBreak); } + private void recordBreakActivated() { + breaksActivatedCount++; + log.info("[BreakHandlerV2] Breaks activated this run: {}", breaksActivatedCount); + } + + public long getScriptActiveSeconds() { + if (scriptStartedAt == null) { + return 0; + } + return Math.max(0, scriptStartedAt.until(Instant.now(), ChronoUnit.SECONDS)); + } + + public int getBreaksActivatedCount() { + return breaksActivatedCount; + } + private void sendBreakStartedNotification(boolean logoutBreak) { + String breakType = getCurrentBreakTypeName(); String message = logoutBreak - ? "Type: Logout break\nDuration: " + (currentBreakDuration / 60000) + " minutes" - : "Duration: " + (currentBreakDuration / 60000) + " minutes (no logout)"; + ? "Type: " + breakType + " (logout)\nDuration: " + (currentBreakDuration / 60000) + " minutes" + : "Type: " + breakType + "\nDuration: " + (currentBreakDuration / 60000) + " minutes (no logout)"; sendDiscordNotification("Break Started", message); }