From b262de1ab4299103fafdf10dddd56ef6e0820846 Mon Sep 17 00:00:00 2001 From: Max Date: Thu, 16 Apr 2026 10:11:43 -0700 Subject: [PATCH 1/2] fix(Fletching): match "Logs" item name when using LOG material (#396) The bank lookup constructed the secondary item name as `material.getName() + " logs"`, producing "Log logs" for LOG material which doesn't match the actual OSRS item name "Logs". Plain logs therefore were never found in the bank and the script shut down with "logs not found". Added FletchingMaterial.getLogItemName() that returns "Logs" for LOG and " logs" for everything else, and routed all three call sites through it. Co-authored-by: dev --- .../client/plugins/microbot/fletching/FletchingPlugin.java | 2 +- .../client/plugins/microbot/fletching/FletchingScript.java | 6 +++--- .../plugins/microbot/fletching/enums/FletchingMaterial.java | 3 +++ 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/main/java/net/runelite/client/plugins/microbot/fletching/FletchingPlugin.java b/src/main/java/net/runelite/client/plugins/microbot/fletching/FletchingPlugin.java index 4955ed7e2b..50e1ad279b 100644 --- a/src/main/java/net/runelite/client/plugins/microbot/fletching/FletchingPlugin.java +++ b/src/main/java/net/runelite/client/plugins/microbot/fletching/FletchingPlugin.java @@ -27,7 +27,7 @@ @Slf4j public class FletchingPlugin extends Plugin { - public static final String version = "1.6.3"; + public static final String version = "1.6.4"; @Inject private FletchingConfig config; diff --git a/src/main/java/net/runelite/client/plugins/microbot/fletching/FletchingScript.java b/src/main/java/net/runelite/client/plugins/microbot/fletching/FletchingScript.java index ab1df412ef..2c72f70cb0 100644 --- a/src/main/java/net/runelite/client/plugins/microbot/fletching/FletchingScript.java +++ b/src/main/java/net/runelite/client/plugins/microbot/fletching/FletchingScript.java @@ -71,7 +71,7 @@ public void run(FletchingConfig config) { primaryItemToFletch = fletchingMode.getItemName(); if (fletchingMode == FletchingMode.PROGRESSIVE) { - secondaryItemToFletch = (model.getFletchingMaterial().getName() + " logs").trim(); + secondaryItemToFletch = model.getFletchingMaterial().getLogItemName(); hasRequirementsToFletch = Rs2Inventory.hasItem(primaryItemToFletch) && Rs2Inventory.hasItemAmount(secondaryItemToFletch, model.getFletchingItem().getAmountRequired()); hasRequirementsToBank = !Rs2Inventory.hasItem(primaryItemToFletch) @@ -84,7 +84,7 @@ public void run(FletchingConfig config) { } else { secondaryItemToFletch = fletchingMode == FletchingMode.STRUNG ? config.fletchingMaterial().getName() + " " + config.fletchingItem().getContainsInventoryName() + " (u)" - : (config.fletchingMaterial().getName() + " logs").trim(); + : config.fletchingMaterial().getLogItemName(); hasRequirementsToFletch = Rs2Inventory.hasItem(primaryItemToFletch) && Rs2Inventory.hasItemAmount(secondaryItemToFletch, config.fletchingItem().getAmountRequired()); hasRequirementsToBank = !Rs2Inventory.hasItem(primaryItemToFletch) @@ -115,7 +115,7 @@ private void bankItems(FletchingConfig config) { case PROGRESSIVE: Rs2Bank.depositAll(model.getFletchingItem().getContainsInventoryName()); calculateItemToFletch(); - secondaryItemToFletch = (model.getFletchingMaterial().getName() + " logs").trim(); + secondaryItemToFletch = model.getFletchingMaterial().getLogItemName(); break; case PROGRESSIVE_STRUNG: Rs2Bank.depositAll(); diff --git a/src/main/java/net/runelite/client/plugins/microbot/fletching/enums/FletchingMaterial.java b/src/main/java/net/runelite/client/plugins/microbot/fletching/enums/FletchingMaterial.java index e8f0be64e0..0e15b44504 100644 --- a/src/main/java/net/runelite/client/plugins/microbot/fletching/enums/FletchingMaterial.java +++ b/src/main/java/net/runelite/client/plugins/microbot/fletching/enums/FletchingMaterial.java @@ -18,6 +18,9 @@ public enum FletchingMaterial private final String name; + public String getLogItemName() { + return this == LOG ? "Logs" : name + " logs"; + } @Override public String toString() From 109b63fb77d9ca805bc723fb2ac06bf30581360e Mon Sep 17 00:00:00 2001 From: Max Date: Thu, 16 Apr 2026 10:21:47 -0700 Subject: [PATCH 2/2] feat(LeaguesToolkit): add Leagues utility plugin with anti-AFK (#395) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit New standalone plugin for Leagues quality-of-life utilities. First feature is anti-AFK which presses a configurable input (random arrow key, backspace, or camera rotation) whenever the client's idle ticks approach the idle-timeout threshold — prevents logout during long auto-banking skilling sessions (e.g. mining with Endless Harvest relic). Configurable trigger buffer window (randomized min/max ticks before threshold) and input method. Adds [DV] prefix to PluginConstants. Co-authored-by: dev Co-authored-by: chsami --- .../leaguestoolkit/AntiAfkMethod.java | 7 ++ .../leaguestoolkit/LeaguesToolkitConfig.java | 69 +++++++++++++++++++ .../leaguestoolkit/LeaguesToolkitPlugin.java | 45 ++++++++++++ .../leaguestoolkit/LeaguesToolkitScript.java | 62 +++++++++++++++++ .../microbot/leaguestoolkit/docs/README.md | 16 +++++ 5 files changed, 199 insertions(+) create mode 100644 src/main/java/net/runelite/client/plugins/microbot/leaguestoolkit/AntiAfkMethod.java create mode 100644 src/main/java/net/runelite/client/plugins/microbot/leaguestoolkit/LeaguesToolkitConfig.java create mode 100644 src/main/java/net/runelite/client/plugins/microbot/leaguestoolkit/LeaguesToolkitPlugin.java create mode 100644 src/main/java/net/runelite/client/plugins/microbot/leaguestoolkit/LeaguesToolkitScript.java create mode 100644 src/main/resources/net/runelite/client/plugins/microbot/leaguestoolkit/docs/README.md diff --git a/src/main/java/net/runelite/client/plugins/microbot/leaguestoolkit/AntiAfkMethod.java b/src/main/java/net/runelite/client/plugins/microbot/leaguestoolkit/AntiAfkMethod.java new file mode 100644 index 0000000000..29763dc22d --- /dev/null +++ b/src/main/java/net/runelite/client/plugins/microbot/leaguestoolkit/AntiAfkMethod.java @@ -0,0 +1,7 @@ +package net.runelite.client.plugins.microbot.leaguestoolkit; + +public enum AntiAfkMethod { + RANDOM_ARROW_KEY, + BACKSPACE, + CAMERA_ROTATION +} diff --git a/src/main/java/net/runelite/client/plugins/microbot/leaguestoolkit/LeaguesToolkitConfig.java b/src/main/java/net/runelite/client/plugins/microbot/leaguestoolkit/LeaguesToolkitConfig.java new file mode 100644 index 0000000000..0e6dbc4fb3 --- /dev/null +++ b/src/main/java/net/runelite/client/plugins/microbot/leaguestoolkit/LeaguesToolkitConfig.java @@ -0,0 +1,69 @@ +package net.runelite.client.plugins.microbot.leaguestoolkit; + +import net.runelite.client.config.Config; +import net.runelite.client.config.ConfigGroup; +import net.runelite.client.config.ConfigInformation; +import net.runelite.client.config.ConfigItem; +import net.runelite.client.config.ConfigSection; +import net.runelite.client.config.Range; + +@ConfigGroup("LeaguesToolkit") +@ConfigInformation("

Leagues Toolkit

" + + "

Version: " + LeaguesToolkitPlugin.version + "

" + + "

A grab-bag of Leagues-focused utilities. Start with Anti-AFK to keep long, " + + "auto-banking skilling sessions from getting logged out.

") +public interface LeaguesToolkitConfig extends Config { + + @ConfigSection( + name = "Anti-AFK", + description = "Prevents the idle-timeout logout during long AFK sessions", + position = 0 + ) + String antiAfkSection = "antiAfkSection"; + + @ConfigItem( + keyName = "enableAntiAfk", + name = "Enable anti-AFK", + description = "Periodically triggers input to reset the idle timer so you never get logged out", + position = 0, + section = antiAfkSection + ) + default boolean enableAntiAfk() { + return true; + } + + @ConfigItem( + keyName = "antiAfkMethod", + name = "Input method", + description = "What kind of input to send. Random arrow keys look most natural.", + position = 1, + section = antiAfkSection + ) + default AntiAfkMethod antiAfkMethod() { + return AntiAfkMethod.RANDOM_ARROW_KEY; + } + + @Range(min = 50, max = 5000) + @ConfigItem( + keyName = "antiAfkBufferMin", + name = "Trigger buffer min (ticks)", + description = "Minimum ticks before the client's AFK threshold at which to fire input", + position = 2, + section = antiAfkSection + ) + default int antiAfkBufferMin() { + return 500; + } + + @Range(min = 50, max = 5000) + @ConfigItem( + keyName = "antiAfkBufferMax", + name = "Trigger buffer max (ticks)", + description = "Maximum ticks before the client's AFK threshold at which to fire input", + position = 3, + section = antiAfkSection + ) + default int antiAfkBufferMax() { + return 1500; + } +} diff --git a/src/main/java/net/runelite/client/plugins/microbot/leaguestoolkit/LeaguesToolkitPlugin.java b/src/main/java/net/runelite/client/plugins/microbot/leaguestoolkit/LeaguesToolkitPlugin.java new file mode 100644 index 0000000000..bbec460109 --- /dev/null +++ b/src/main/java/net/runelite/client/plugins/microbot/leaguestoolkit/LeaguesToolkitPlugin.java @@ -0,0 +1,45 @@ +package net.runelite.client.plugins.microbot.leaguestoolkit; + +import com.google.inject.Provides; +import lombok.extern.slf4j.Slf4j; +import net.runelite.client.config.ConfigManager; +import net.runelite.client.plugins.Plugin; +import net.runelite.client.plugins.PluginDescriptor; +import net.runelite.client.plugins.microbot.PluginConstants; + +import javax.inject.Inject; + +@PluginDescriptor( + name = PluginConstants.DV + "Leagues Toolkit", + description = "Quality-of-life utilities for Leagues (anti-AFK, and more to come)", + tags = {"leagues", "microbot", "utility", "afk"}, + version = LeaguesToolkitPlugin.version, + minClientVersion = "2.0.13", + enabledByDefault = PluginConstants.DEFAULT_ENABLED, + isExternal = PluginConstants.IS_EXTERNAL +) +@Slf4j +public class LeaguesToolkitPlugin extends Plugin { + public static final String version = "1.0.0"; + + @Inject + private LeaguesToolkitConfig config; + + @Inject + private LeaguesToolkitScript leaguesToolkitScript; + + @Provides + LeaguesToolkitConfig provideConfig(ConfigManager configManager) { + return configManager.getConfig(LeaguesToolkitConfig.class); + } + + @Override + protected void startUp() { + leaguesToolkitScript.run(config); + } + + @Override + protected void shutDown() { + leaguesToolkitScript.shutdown(); + } +} diff --git a/src/main/java/net/runelite/client/plugins/microbot/leaguestoolkit/LeaguesToolkitScript.java b/src/main/java/net/runelite/client/plugins/microbot/leaguestoolkit/LeaguesToolkitScript.java new file mode 100644 index 0000000000..fafd6f3760 --- /dev/null +++ b/src/main/java/net/runelite/client/plugins/microbot/leaguestoolkit/LeaguesToolkitScript.java @@ -0,0 +1,62 @@ +package net.runelite.client.plugins.microbot.leaguestoolkit; + +import lombok.extern.slf4j.Slf4j; +import net.runelite.client.plugins.microbot.Microbot; +import net.runelite.client.plugins.microbot.Script; +import net.runelite.client.plugins.microbot.util.camera.Rs2Camera; +import net.runelite.client.plugins.microbot.util.keyboard.Rs2Keyboard; +import net.runelite.client.plugins.microbot.util.math.Rs2Random; +import net.runelite.client.plugins.microbot.util.player.Rs2Player; + +import java.awt.event.KeyEvent; +import java.util.concurrent.TimeUnit; + +@Slf4j +public class LeaguesToolkitScript extends Script { + + private static final int[] ARROW_KEYS = { + KeyEvent.VK_LEFT, KeyEvent.VK_RIGHT, KeyEvent.VK_UP, KeyEvent.VK_DOWN + }; + + public boolean run(LeaguesToolkitConfig config) { + mainScheduledFuture = scheduledExecutorService.scheduleWithFixedDelay(() -> { + try { + if (!super.run()) return; + if (!Microbot.isLoggedIn()) return; + + if (config.enableAntiAfk()) { + runAntiAfk(config); + } + } catch (Exception ex) { + log.error("LeaguesToolkitScript loop error", ex); + } + }, 0, 1000, TimeUnit.MILLISECONDS); + return true; + } + + private void runAntiAfk(LeaguesToolkitConfig config) { + int minBuffer = Math.min(config.antiAfkBufferMin(), config.antiAfkBufferMax()); + int maxBuffer = Math.max(config.antiAfkBufferMin(), config.antiAfkBufferMax()); + long buffer = Rs2Random.between(minBuffer, maxBuffer); + + if (!Rs2Player.checkIdleLogout(buffer)) return; + + switch (config.antiAfkMethod()) { + case BACKSPACE: + Rs2Keyboard.keyPress(KeyEvent.VK_BACK_SPACE); + break; + case CAMERA_ROTATION: + Rs2Camera.setYaw(Rs2Random.between(0, 2047)); + break; + case RANDOM_ARROW_KEY: + default: + Rs2Keyboard.keyPress(ARROW_KEYS[Rs2Random.between(0, ARROW_KEYS.length - 1)]); + break; + } + } + + @Override + public void shutdown() { + super.shutdown(); + } +} diff --git a/src/main/resources/net/runelite/client/plugins/microbot/leaguestoolkit/docs/README.md b/src/main/resources/net/runelite/client/plugins/microbot/leaguestoolkit/docs/README.md new file mode 100644 index 0000000000..224c1e9d6e --- /dev/null +++ b/src/main/resources/net/runelite/client/plugins/microbot/leaguestoolkit/docs/README.md @@ -0,0 +1,16 @@ +# Leagues Toolkit + +Quality-of-life utilities for OSRS Leagues. + +## Features + +### Anti-AFK +Prevents the idle-timeout logout during long AFK skilling sessions (e.g. mining with the auto-bank relic where inventory never fills and no interaction happens between rock respawns). Periodically triggers a configurable input right before the client's AFK threshold so the session stays alive indefinitely. + +Configurable: +- **Input method** — random arrow key (default, most natural), backspace, or camera yaw rotation +- **Trigger buffer min/max** — how many ticks before the idle-timeout to fire input (randomized between min and max) + +## Roadmap + +More Leagues-focused utilities planned. Open an issue or PR with ideas.