Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
- Script loop (`Script.run()` implementations) executes on scheduled executors → queries caches via Queryable API → performs interactions through utilities (`Rs2Inventory`, `Rs2Walker`, etc.) → waits with `sleepUntil` helpers.
- Blocking events (`BlockingEventManager`) continuously validate (e.g., welcome screen, bank popups) → if triggered, they run on a dedicated executor and block script progression until resolved.
- Telemetry flow: session/version/fact/plugin telemetry is skipped when telemetry is disabled; failures are logged at debug level.
- Error telemetry: `diagnostics/ScriptErrorReporter` is a root logback appender that groups ERROR events by exception type and top stack frames, attributes them to the owning Hub plugin and version, sanitises messages with `DiagnosticReport.clean`, and posts a batch to `/plugintelemetry/errors` every 5 minutes. It is skipped by `--disable-telemetry`, `-Dmicrobot.disableTelemetry=true` or the config toggle. `-Dmicrobot.apiUrl` points `MicrobotApi` calls (session, plugin and error telemetry) at another base URL for local testing.

## Runtime Boundaries
- **Threads**: Client thread (never block/sleep); script/executor threads (automation logic, sleeps allowed); blocking-event executor (resolves UI blockers). Use `ClientThread.runOnClientThreadOptional` for safe client access.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
*/
package net.runelite.client;

import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import net.runelite.api.Client;
import net.runelite.api.GameState;
Expand Down Expand Up @@ -54,7 +55,8 @@ public class ClientSessionManager
private ScheduledFuture<?> scheduledFutureMicroBot;

private UUID sessionId = UUID.randomUUID();
private UUID microbotSessionId;
@Getter
private volatile UUID microbotSessionId;
private MicrobotApi microbotApi;

@Inject
Expand Down Expand Up @@ -172,7 +174,7 @@ private void microbotPing()
catch (IOException ex)
{
log.warn("Resetting session", ex);
sessionId = null;
microbotSessionId = null;
}

}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
import lombok.extern.slf4j.Slf4j;
import net.runelite.client.RuneLiteProperties;
import net.runelite.http.api.RuneLiteAPI;
import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
Expand All @@ -30,7 +32,7 @@ public class MicrobotApi {
private final Gson gson;
private final String pluginTelemetryToken;

private final String microbotApiUrl = "https://microbot.cloud/api";
private final String microbotApiUrl = System.getProperty("microbot.apiUrl", "https://microbot.cloud/api");
@Inject
MicrobotApi(OkHttpClient client, Gson gson) {
this.client = client;
Expand Down Expand Up @@ -105,6 +107,36 @@ public void increasePluginInstall(String internalName, String displayName, Strin
}
}

public void submitErrors(JsonObject payload, Runnable onRejected)
{
Request request = new Request.Builder()
.url(microbotApiUrl + "/plugintelemetry/errors")
.header("X-Plugin-Telemetry-Token", pluginTelemetryToken)
.post(RequestBody.create(RuneLiteAPI.JSON, gson.toJson(payload)))
.build();

client.newCall(request).enqueue(new Callback()
{
@Override
public void onFailure(Call call, IOException e)
{
log.debug("Error telemetry upload failed", e);
onRejected.run();
}

@Override
public void onResponse(Call call, Response response)
{
if (!response.isSuccessful())
{
log.debug("Error telemetry upload rejected: HTTP {}", response.code());
onRejected.run();
}
response.close();
Comment thread
coderabbitai[bot] marked this conversation as resolved.
}
});
}

/**
* Sends a ping request to the microbot API to update the session status.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -222,7 +222,7 @@ default boolean showCacheInfo() {
@ConfigItem(
keyName = keyDisableTelemetry,
name = "Disable telemetry",
description = "Stop outbound calls to microbot.cloud (update check, random-fact splash, session ping). " +
description = "Stop outbound calls to microbot.cloud (update check, random-fact splash, session ping, script error reports). " +
"Equivalent to launching with -Dmicrobot.disableTelemetry=true.",
position = 6,
section = generalSection
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import net.runelite.client.events.RuneScapeProfileChanged;
import net.runelite.client.plugins.Plugin;
import net.runelite.client.plugins.PluginDescriptor;
import net.runelite.client.plugins.microbot.diagnostics.ScriptErrorReporter;
import net.runelite.client.plugins.microbot.pouch.PouchOverlay;
import net.runelite.client.plugins.microbot.ui.MicrobotPluginConfigurationDescriptor;
import net.runelite.client.plugins.microbot.ui.MicrobotPluginListPanel;
Expand Down Expand Up @@ -125,6 +126,9 @@ MicrobotConfig provideConfig(ConfigManager configManager)

@Inject
private MicrobotVersionChecker microbotVersionChecker;

@Inject
private ScriptErrorReporter scriptErrorReporter;

// Widget change tracking for overlay cache invalidation
private volatile boolean widgetLayoutChanged = false;
Expand Down Expand Up @@ -153,6 +157,10 @@ protected void startUp() throws AWTException
gameChatAppender.setContext(context);
context.getLogger(Logger.ROOT_LOGGER_NAME).addAppender(gameChatAppender);

scriptErrorReporter.setContext(context);
scriptErrorReporter.start();
context.getLogger(Logger.ROOT_LOGGER_NAME).addAppender(scriptErrorReporter);

// Start appender if logging is enabled
if (microbotConfig.enableGameChatLogging()) {
gameChatAppender.start();
Expand Down Expand Up @@ -218,6 +226,8 @@ protected void shutDown()
overlayManager.remove(pouchOverlay);
clientToolbar.removeNavigation(navButton);
if (gameChatAppender.isStarted()) gameChatAppender.stop();
((LoggerContext) LoggerFactory.getILoggerFactory()).getLogger(Logger.ROOT_LOGGER_NAME).detachAppender(scriptErrorReporter);
scriptErrorReporter.stop();
microbotVersionChecker.shutdown();
}

Expand Down Expand Up @@ -634,6 +644,12 @@ public void onGameTick(GameTick event)

// Start Leagues teleport calibration ASAP after login (non-blocking; prompts for consent once).
Rs2LeaguesTransport.tickLeaguesCalibration();

Player localPlayer = Microbot.getClient().getLocalPlayer();
if (localPlayer != null)
{
scriptErrorReporter.rememberPlayerName(localPlayer.getName());
}
}

@Subscribe(priority = 100)
Expand Down
Loading
Loading