mirror of
https://github.com/ZHANGTIANYAO1/teamspeak-music-bot.git
synced 2026-10-02 04:52:50 +08:00
fix(auto-pause): auto-resume on a listener's return via clientEnter event
Follow-up to the auto-pause fix: resume never fired when someone came back. Root cause (verified live against a TS3 server): the full-client library's command/response channel is dead whenever >=2 clients are connected anywhere on the server — clientlist, channellist and channelclientlist ALL time out (confirmed even with the two clients in different channels). So the moment a listener returns is exactly the moment occupancy can no longer be queried, and the query-based refreshOccupancy() can never observe the return -> no resume. Event channelID is also unusable (library reads notify `cid` but enter-view carries `ctid`, so it's always 0), so per-channel membership can't be derived from events either. Fix (minimal, asymmetric): keep PAUSE on the authoritative clientlist path (reliable precisely because it only succeeds when the bot is alone on the server — the only state pause should fire), and arm RESUME directly from the clientEnter push event. Because the bot only auto-pauses while alone, the sole way occupancy can return while autoPaused is set is a fresh connection, which arrives reliably as clientEnter. New pure predicate shouldResumeOnReturn() + _resumeIfReturning() resume iff autoPaused && paused; the resume branch routes through handleOccupancy(1) and NEVER pauses (userCount>0), so a spurious enter can only harmlessly resume. The bot's own enter at connect is a no-op (autoPaused is already false). This deliberately does NOT adopt a full event-tracked peer set: events don't reliably seed clients already present when the bot joins, so a count-from-events ==0 would reintroduce the false-pause bug we just fixed, and reconcile can't heal it (clientlist only works when alone). Pause must trust only the authoritative query; resume can trust the event. Net semantics: pause when the server is empty (bot alone), resume when someone connects. Channel granularity is impossible with this library. UI copy updated to say "服务器" instead of "频道", and the Settings toggle default corrected to false to match the backend default. cmdVote intentionally left as-is. Verified live: auto-paused bot + a real client connecting -> resume fires with no clientlist call in the path; bot's own enter and not-auto-paused enters do not resume. 311 unit tests pass. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
ba11519fdb
commit
3a34c01abb
5 files changed
+128
-8
No files matched your search
@@ -99,3 +99,40 @@ track *we* auto-paused gets auto-resumed; a user-paused track stays paused when
|
||||
- No per-bot toggle (global only). No change to idle-disconnect behavior. No new dependency.
|
||||
- Reaction relies on events the bot can already see (same-channel members are always in view);
|
||||
no extra channel subscription needed.
|
||||
|
||||
---
|
||||
|
||||
## Update (2026-06): occupancy is event-driven & server-wide, not channel-filtered
|
||||
|
||||
Live testing against a real TS3 server (with `@honeybbq/teamspeak-client` 0.2.2)
|
||||
invalidated two assumptions above. Recording the corrected model here so nobody
|
||||
reintroduces the old design:
|
||||
|
||||
- **Default is OFF**, not on. See `getDefaultConfig()` in `src/data/config.ts`
|
||||
and the rationale comment there.
|
||||
- **Query commands are unusable when others are present.** `clientlist`,
|
||||
`channellist`, and `channelclientlist` ALL time out (~5–10s) whenever ≥2
|
||||
clients are connected to the **server** (verified even when the two clients
|
||||
are in *different* channels). They succeed only when the bot is the sole
|
||||
client on the whole server. So `getClientsInChannel()` returns `[]` exactly
|
||||
when occupancy matters, and `occupancyFromClientList(0)` returns `null`
|
||||
("unknown") so callers skip the decision rather than mis-reading it as empty.
|
||||
- **PAUSE** therefore only ever fires when the bot becomes alone on the server
|
||||
(the one state where the query works). This is reliable and stays on the
|
||||
query path (`refreshOccupancy()` + the 30s idle poller).
|
||||
- **RESUME** is armed directly from the `clientEnter` push event
|
||||
(`shouldResumeOnReturn()` + `_resumeIfReturning()` in `instance.ts`), NOT from
|
||||
a query. Because the bot only auto-pauses while alone, the sole way occupancy
|
||||
can return while `autoPaused` is set is a fresh connection — delivered as
|
||||
`clientEnter`. The resume branch never pauses (userCount is always > 0).
|
||||
- **Net semantics:** "pause when the server is empty (bot alone), resume when
|
||||
someone connects." Channel granularity is **impossible** with this library:
|
||||
`clientEnter`'s channel field is always `0` (library reads notify param `cid`
|
||||
but enter-view carries `ctid`), and `clientMoved` delivery is flaky. Do NOT
|
||||
attempt to layer `clientMoved.targetChannelID` channel-accuracy on top — it is
|
||||
systematically wrong for direct-connect clients and reintroduces unreliability.
|
||||
The correct path to true channel scoping is an upstream library fix.
|
||||
- **Knock-on:** idle-disconnect shares the same signal and is likewise
|
||||
server-wide. UI copy in `web/src/views/Settings.vue` was updated to say
|
||||
"服务器" rather than "频道" to match. `cmdVote` was intentionally left on the
|
||||
query path (out of scope; switching it would inherit the same timeout).
|
||||
Reference in new issue
Block a user