Skip to content

User Script Guide

StarX ships a built-in user script system since 1.5.4: extend the module with JavaScript without waiting for a module release.

  • Run scripts in global, exam, homework, in-class quiz, chapter quiz, video and sign-in contexts, or several of them at once
  • Call the capabilities the module already has: AI answers, question bank lookup, answer query (including images)
  • Answer a question yourself the moment it is dispatched, taking priority over AI and the question bank - the exam pipeline included
  • Send your own requests with StarX.http and persist data with StarX.storage
  • Install your own native hooks (requires Xposed framework API 102 or newer)
  • Talk to other scripts over the event bus

Scripts run on Rhino (ES6 language level, interpreted only) and cannot see any Java class. Only the APIs documented here are available.

/storage/emulated/0/Download/StarX/
└── scripts/
├── scripts.json enable/disable index (maintained by the module)
├── my_script.js script source
├── data/
│ └── my_script.json StarX.storage backing file
└── logs/
└── my_script.log runtime log (rotates after 256 KB)

Notes:

  • The base folder is /storage/emulated/0/Download/StarX, falling back to /sdcard/Download/StarX when not writable.
  • The file name without .js is the script ID. Only a-z A-Z 0-9 . _ - are kept; other characters become _. Prefer ASCII file names.
  • Editing a file hot-reloads it within 2 seconds. MT Manager, VS Code or MTP from a PC all work; no restart of the host app is needed.
  • A missing or broken index means “disabled” - scripts never run by accident.
  1. Open StarX and switch to the User Scripts tab
  2. Tap New script
  3. Name it and write the code in the editor (the template already contains a metadata header and an example)
  4. Tap Save; the module writes it into scripts/ and reports “hot reload within 2 seconds”

Each script card offers enable/disable, Edit, Log and Delete. The Usage button shows the scope and event cheat sheet.

Create xxx.js inside scripts/ with a metadata header. The module loads it within 2 seconds; new scripts are enabled by default.

Drop the .js file into scripts/. Scripts can only use the APIs documented here and cannot see Java classes, but they can still make arbitrary network requests, read and write the clipboard, and read and write their own storage file. Only run scripts you trust.

The header follows the Tampermonkey ==UserScript== comment style. ==StarXScript== is an equivalent alias.

// ==UserScript==
// @name Exam keyword answer
// @namespace local.example
// @version 1.0.0
// @description Answers locally on keyword hits
// @author you
// @scope exam
// @grant ai,tiku
// ==/UserScript==
FieldMeaning
@nameDisplay name; file name when omitted
@namespaceNamespace, identifier only
@versionVersion shown on the script card
@descriptionOne-line description
@authorAuthor
@scopeScopes, separated by commas, spaces or `
@match / @includePage match rules (Tampermonkey semantics); a hit makes the script receive page events
@excludeExcludes URLs from @match hits
@grantDeclares usable API groups; omitting it allows everything, declaring it allows only the listed groups
@requireRemote JS loaded and executed synchronously before the script (512 KB per file, cached per process)
@run-atReserved, metadata only
@noframesReserved

Match rules: * matches any length, ? a single character, and /regex/ is used as a regular expression (an invalid regex degrades to a literal match).

@scope decides where the script runs. Without @scope it defaults to global.

ValuePositionWhat it receives today
globalGlobaltick (5 s heartbeat), questions published via question.publish, events you emit
examExamquestion (single-question pages, full-paper preview batches, composite sub-questions)
workHomework / chapter quizquestion. Chapter quizzes currently travel the homework pipeline, so use work to intercept them
quizIn-class quizquestion (every question of the batch)
chapterChapter pagespage (URL routing, see below)
videoVideovideo.report (on natural progress reports)
signSign-insign.active (when a sign-in activity ID is captured)
allEverything aboveSame as listing them all

URL routing table for page events:

ScopeURL fragments
sign/newsign/, /widget/sign/, /sign/
exam/exam
work/dohomework, /work/
quiz/inclass, /quiz
video/ananas/
chapter/mycourse/studentstudy, /coursedata/, /chapter/

Scripts declaring @match receive page events on URL hits and do not need @scope.

Subscribe with StarX.on(name, fn) and unsubscribe with StarX.off(name, fn).

Fired when a question is dispatched:

{
scope: 'exam', // exam / work / quiz
id: '123456789', // question id; composite questions use qid + "#" + sub index
type: 0, // type code, see 6.6
stem: 'question text',
options: ['option A', 'option B'], // may be an empty array on some pipelines
ts: 1789015379277
}

Call StarX.question.answer(...) inside this callback to answer:

StarX.on('question', function (q) {
if (q.stem.indexOf('TCP') >= 0) {
StarX.question.answer('A'); // single choice / true-false
}
if (q.type === 1) {
StarX.question.answer(['A', 'C']); // multiple choice -> "AC"
}
if (q.type === 2 || q.type === 4) {
StarX.question.answer('three-way handshake'); // fill-in / short answer
}
});
  • Arrays are collapsed: all single-character elements join into AC, otherwise they join with commas.
  • Returning false from answer() means “no usable answer this time”.
  • The first script that submits a non-empty answer wins; the module then skips AI and the question bank.
  • The callback is synchronous and runs on the caller thread (the exam/homework parsing thread). Do not block for long.

Fired when a WebView opens a page: { url, ts }.

{ activeId, url, source, ts } where source is okhttp or webview.

{ objectId, passed, source, preview, ts }.

tick fires every 5 seconds with { ts } for scripts declaring global (or all).

StarX.emit('my-event', { hello: 1 }); // broadcast to other scripts (not back to yourself)
StarX.on('my-event', function (p) { StarX.log(p.hello); });
ValueType
0Single choice
1Multiple choice
2Fill in the blank
3True / false
4Short answer
-1Unknown

7. Complete example: answer exam questions yourself

Section titled “7. Complete example: answer exam questions yourself”
// ==UserScript==
// @name Exam keyword answer
// @version 1.0.0
// @description Answers from local rules, otherwise asks the AI
// @author you
// @scope exam,work
// ==/UserScript==
var RULES = {
'three-way handshake': 'three-way handshake'
};
StarX.on('question', function (q) {
for (var key in RULES) {
if (q.stem.indexOf(key) >= 0) {
StarX.log('hit rule:', key, '=>', RULES[key]);
StarX.question.answer(RULES[key]);
return;
}
}
if (!StarX.ai.isConfigured()) return;
var ans = StarX.ai.ask(q.stem, q.type, q.options.join('\n'));
if (ans) StarX.question.answer(ans);
});

Omitting @grant allows every API. Once declared, only the listed groups are allowed:

GroupAPIs
aiStarX.ai.*
tikuStarX.tiku.*
answerStarX.answer.*
httpStarX.http.*
storageStarX.storage.*
clipboardStarX.clipboard.*
hookStarX.hook.*
pageStarX.page.*
questionStarX.question.*
timersetTimeout / setInterval / clearTimeout / clearInterval
uiStarX.toast

A denied call returns null and writes a denied by @grant line to the script log.

  • File edits: reloaded within 2 seconds; nothing needs to be restarted.
  • Module setting changes: the module app asks the framework to hot reload and tells you to restart the host app when the framework cannot.
  • The script runtime is independent of the framework version - Rhino runs inside the module process, so API < 102 still works. Only StarX.hook.* requires framework API 102 or newer; check StarX.hook.available().
  • StarX.log(...) and StarX.console.log / warn / error / debug(...) write to scripts/logs/<id>.log and to logcat under the StarX tag.
  • The Log button on a script card shows recent lines and lets you clear them.
  • A script that fails to load (syntax error and so on) shows the error on its card and does not affect other scripts.
  • Exceptions thrown inside event callbacks are caught and logged as event xxx handler failed: ... without breaking the business pipeline.

Security model (facts):

  • Each script gets its own Rhino scope and its own single-thread executor; scripts cannot see each other’s variables.
  • A ClassShutter denies every Java class, so scripts cannot reflect into Java through Rhino.
  • The full native surface is the operation list documented here - there is no other entry point.
  • Script exceptions are isolated and do not affect the business pipeline or other scripts.

Limits (facts):

  • Scripts can still make arbitrary HTTP requests, use the clipboard and read/write their own data/<id>.json. Only run scripts you trust.
  • @run-at and @noframes are metadata only today and do not affect injection scheduling.
  • The chapter scope only receives page events; chapter quiz questions travel the work scope.
  • There is no GM_* compatibility layer; write against the StarX.* API documented here.
  • @grant only trims API groups, it does not restrict network destinations.