feat(agent-server): add quest-helper and /logout endpoints#1774
Conversation
Need programmatic quest selection from the ironman-guide skill; Quest Helper's plugin panel was the only way to start a quest. Adds a handler that wraps QuestHelperPlugin.getQuestManager().startUpQuest so the skill can pick a quest over HTTP, plus fixes the crashes and wrong-click behavior that surfaced while driving QH auto-play during the BRUHsailer run. agent-server: - QuestHelperHandler: /questhelper/start, /stop, /active, /list. Resolves quest by enum name or display name via QuestHelperQuest.valueOf / getByName. QuestScript (reachability + action selection): - Require line-of-sight in addition to canReach() before clicking an NPC or object — canReach pathfinds through closed doors, so a true return doesn't mean a direct click will land. - When LOS fails, walk to the target (lets the walker open the door en route) instead of firing a click that will miss. - Fallback to the object/NPC/inventory item's first non-empty action when no step text matches an action name, so QH doesn't emit an empty menu op. Null-guards on MenuOptionClicked.getMenuOption() in: - PouchScript, Rs2Gembag, QuestBankTabInterface, IncantationStep (all previously NPE'd on synthetic/agent-driven menu events where menuOption is null)
POST /logout calls Rs2Player.logout() on the script thread and polls client.getGameState() == LOGIN_SCREEN; GET returns current login state. Symmetric to LoginHandler. Used by ironguide to gate Microbot ↔ DreamBot handoffs (logout via Microbot before launching DreamBot, log back in after).
Iterates QuestHelperQuest.values() on the client thread and returns
{enum, displayName, state} per quest where state ∈ {NOT_STARTED,
IN_PROGRESS, FINISHED} via QuestHelperQuest.getState(Client). Lets
external tooling bulk-audit per-quest completion without the side
effects of /quest-helper/start+stop (which set TurnOn=true).
|
Important Review skippedAuto incremental reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Repository UI Review profile: CHILL Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
WalkthroughThis PR extends the microbot agent-server HTTP API with two independent handler groups. The Possibly related PRs
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In
`@runelite-client/src/main/java/net/runelite/client/plugins/microbot/agentserver/handler/LogoutHandler.java`:
- Around line 69-77: In LogoutHandler, validate the type of body.get("timeout")
before casting: check if body contains "timeout" and then if the value is an
instance of Number use ((Number) value).intValue(), else if it's a String try
parsing an integer with Integer.parseInt (handling NumberFormatException) or
ignore/ fallback; cap with MAX_TIMEOUT_SECONDS and apply DEFAULT_TIMEOUT_SECONDS
when <=0; ensure timeoutSeconds assignment uses these guarded conversions to
avoid ClassCastException.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: 8db48ce1-93d1-4aa9-a4ec-49d4ca17bc97
📒 Files selected for processing (4)
runelite-client/src/main/java/net/runelite/client/plugins/microbot/agentserver/AgentServerPlugin.javarunelite-client/src/main/java/net/runelite/client/plugins/microbot/agentserver/handler/LogoutHandler.javarunelite-client/src/main/java/net/runelite/client/plugins/microbot/agentserver/handler/QuestHelperHandler.javarunelite-client/src/main/java/net/runelite/client/plugins/microbot/questhelper/QuestScript.java
| if (body != null) { | ||
| if (body.containsKey("wait")) { | ||
| wait = Boolean.TRUE.equals(body.get("wait")); | ||
| } | ||
| if (body.containsKey("timeout")) { | ||
| timeoutSeconds = Math.min(((Number) body.get("timeout")).intValue(), MAX_TIMEOUT_SECONDS); | ||
| if (timeoutSeconds <= 0) timeoutSeconds = DEFAULT_TIMEOUT_SECONDS; | ||
| } | ||
| } |
There was a problem hiding this comment.
Validate type before casting to prevent ClassCastException.
Line 74 casts body.get("timeout") to Number without validating the type. If a caller sends {"timeout": "30"} (string) or another non-Number type, this will throw ClassCastException.
🛡️ Suggested fix
if (body.containsKey("timeout")) {
- timeoutSeconds = Math.min(((Number) body.get("timeout")).intValue(), MAX_TIMEOUT_SECONDS);
- if (timeoutSeconds <= 0) timeoutSeconds = DEFAULT_TIMEOUT_SECONDS;
+ Object timeoutObj = body.get("timeout");
+ if (timeoutObj instanceof Number) {
+ timeoutSeconds = Math.min(((Number) timeoutObj).intValue(), MAX_TIMEOUT_SECONDS);
+ if (timeoutSeconds <= 0) timeoutSeconds = DEFAULT_TIMEOUT_SECONDS;
+ }
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| if (body != null) { | |
| if (body.containsKey("wait")) { | |
| wait = Boolean.TRUE.equals(body.get("wait")); | |
| } | |
| if (body.containsKey("timeout")) { | |
| timeoutSeconds = Math.min(((Number) body.get("timeout")).intValue(), MAX_TIMEOUT_SECONDS); | |
| if (timeoutSeconds <= 0) timeoutSeconds = DEFAULT_TIMEOUT_SECONDS; | |
| } | |
| } | |
| if (body != null) { | |
| if (body.containsKey("wait")) { | |
| wait = Boolean.TRUE.equals(body.get("wait")); | |
| } | |
| if (body.containsKey("timeout")) { | |
| Object timeoutObj = body.get("timeout"); | |
| if (timeoutObj instanceof Number) { | |
| timeoutSeconds = Math.min(((Number) timeoutObj).intValue(), MAX_TIMEOUT_SECONDS); | |
| if (timeoutSeconds <= 0) timeoutSeconds = DEFAULT_TIMEOUT_SECONDS; | |
| } | |
| } | |
| } |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In
`@runelite-client/src/main/java/net/runelite/client/plugins/microbot/agentserver/handler/LogoutHandler.java`
around lines 69 - 77, In LogoutHandler, validate the type of body.get("timeout")
before casting: check if body contains "timeout" and then if the value is an
instance of Number use ((Number) value).intValue(), else if it's a String try
parsing an integer with Integer.parseInt (handling NumberFormatException) or
ignore/ fallback; cap with MAX_TIMEOUT_SECONDS and apply DEFAULT_TIMEOUT_SECONDS
when <=0; ensure timeoutSeconds assignment uses these guarded conversions to
avoid ClassCastException.
Body values are parsed by Gson into native JSON types, so a client sending
{"timeout": "30"}, {"timeout": true}, or {"timeout": null} would crash the
handler on the (Number) cast. Accept Number or numeric String, fall back to
the default for anything else.
Summary
/logoutendpoint mirroring the existing/loginhandler so an agent can end a session cleanly.Endpoints added
/quest-helper(QuestHelperHandler)/quest-helper/list— list available quests/quest-helper/start— start a quest/quest-helper/stop— stop the quest helper/quest-helper/status— status of the current quest run/quest-helper/states— bulk state read across all quests in a single response/logout(LogoutHandler)LoginHandlersemantics; registered alongside it inAgentServerPlugin.buildHandlers.Other changes
QuestScript.java: 1-line whitespace fix on the line introduced by the recentclearWalkingRoute(...)migration. No behavior change; the fix is a side-effect of resolving the cherry-pick against the olderRs2Walker.setTarget(null)call this branch was built on.Test plan
GET /quest-helper/listreturns the available questsPOST /quest-helper/startwith a known quest name starts the helperGET /quest-helper/statusreflects the running questPOST /quest-helper/stophalts the helper cleanlyGET /quest-helper/statesreturns the full per-quest state map in one responsePOST /logoutends the session in the same way/loginbegins it