Commit Graph
1786 Commits
Author SHA1 Message Date
legop3 cc2e85d174 assignment adjustments and ui tweakings 2026-08-18 22:36:42 -04:00
legop3 737760ff56 big boy webui new new new new new 100 files changed 80 years 2026-08-18 22:01:25 -04:00
legop3 0f0f82e5f6 newdrive planning 2026-08-17 02:54:01 -04:00
legop3 3807b8bb13 Merge branch 'main' of https://github.com/legop3/MultiRoombaRover 2026-08-14 23:22:28 -04:00
legop3 9f2f819bbd neato alerts and better commands 2026-08-14 23:22:27 -04:00
legop3 60329e1036 Enhance MIDI player task with detailed research points
Expanded on the first task to improve the MIDI player with detailed research points and objectives.
2026-08-14 23:11:04 -04:00
legop3 4430517af5 neato updates 2026-08-14 20:25:07 -04:00
legop3 b24d453ad1 redo and move rover ranking a little 2026-08-11 21:34:29 -04:00
legop3 b5e8d775a2 home assistant always turn lights on at full brightness trying to fix weird bulb 2026-08-11 21:25:16 -04:00
legop3 e5830533ba arm powered steam link ?? 2026-08-11 20:50:15 -04:00
legop3 dfd674a447 arm powered steam deck 2026-08-11 20:36:55 -04:00
legop3 bd65f93756 green adjustment 2026-08-09 00:31:11 -04:00
legop3 0083c887f4 fix 2026-08-09 00:13:38 -04:00
legop3 70f71b2d1e green mode slop 1 2026-08-08 23:59:47 -04:00
legop3 c8742dbbd6 slop planning 2026-08-08 00:20:08 -04:00
legop3 6a6dec5540 crocs 2026-08-07 23:25:56 -04:00
legop3 6c69c583c5 moving audio gain perms around 2026-08-07 22:34:04 -04:00
legop3 9702cf0f82 gruh i hate fun!! 2026-08-07 15:54:28 -04:00
legop3 a55257dd51 Merge pull request #22 from legop3/transportswap
Transportswap merge
2026-08-05 14:55:30 -04:00
legop3 8d1761afe2 forgot to build roverd binary lol lol 2026-08-04 02:08:56 -04:00
legop3 c7c52eb39c switched rover <-> server streams to rtsptcp, and moved mediamtx to be a server owned and configured child process! 2026-08-04 01:47:03 -04:00
legop3 28fcbad902 better replays panels 2026-08-03 03:48:30 -04:00
legop3 208b89fd7f add identity to socket auth hopefully will fix a lot of probem 2026-08-03 01:09:21 -04:00
legop3 15a60a58e6 moving a LOT of stuff around in web ui 2026-08-02 23:06:30 -04:00
legop3 3ebd1c7f9c adding added ad slot support for adddss 2026-08-01 01:58:34 -04:00
legop3 7fdcb53041 update 2026-07-31 22:28:35 -04:00
legop3 a2dde4fd3d image updates 2026-07-31 22:18:57 -04:00
legop3 8c5d98bed6 analytics and embed meta update!!! 2026-07-31 22:12:04 -04:00
legop3 1aecab66e7 Merge pull request #19 from Saul5662/feat/fun-commands
feat(commands): add a fun command category with 18 public `rs` commands
2026-07-29 01:55:28 -04:00
legop3 ef18ef89e0 Merge pull request #20 from Saul5662/fix/gain-ambiguous-selector
fix(commands): resolve duplicate nicknames in `rs gain`, add a help subcommand
2026-07-29 00:10:01 -04:00
Saul5662andClaude Opus 5 46bbe5c531 fix(commands): resolve duplicate nicknames in rs gain, add a help subcommand
`rs gain grant Saul` failed with "Selector matched multiple records. Suggestions:
Saul, cu_a28...33ab5c, Saul, cu_5a5...b6add3." Nicknames are not unique — one
person re-verifying from a new browser produces a second verified record with the
same name — so an exact nickname match can legitimately return several records,
and the shared resolver refuses on ambiguity.

That refusal is correct for deter, kick, and verify, where acting on the wrong of
two plausible targets is a moderation mistake. It is wrong for gain: granting a
volume ceiling to the wrong account belonging to the same person is recoverable.
So the disambiguation is local to this command and resolvers.js is untouched.

Order of preference on an exact match with several hits:

1. The account that is currently online, since that is who the admin is reacting
   to. Sockets are deduped to user ids first, so extra tabs do not matter.
2. Otherwise the first stored record.

Either way the reply says which happened and how many accounts shared the name.
A selector with no exact match still goes through the shared fuzzy resolver, so
typo tolerance and the existing error text are unchanged.

Also adds `rs gain help`, which lists the subcommands and explains the selector
and the online-wins rule. The previous unknown-subcommand fallback listed the
subcommands inline; it now shares the same help text.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-29 05:08:25 +01:00
Saul5662andClaude Opus 5 dc8267073b feat(commands): play a bonk sound on the bonked user's rover
`rs bonk` now publishes a `fun.bonked` event carrying the rover the target is
currently driving, and a new audioForwardService listener plays a sound file on
it. Wired as an event rather than a direct call so the command layer stays
unaware of ffmpeg, matching how the charging-complete cue is already done.

The sound file goes at `server/assets/bonk.wav` and is NOT committed here. Note
that `server/assets` is the correct home rather than `server/public`: the webui
builds to `../server/public` with `emptyOutDir: true`, so anything stored there
is deleted by the next build.

Details:

- The audio is rate limited per rover on a 20s window, separate from the 4s text
  cooldown. Playback interrupts whatever that rover is forwarding, including a
  live microphone, so a group of people cannot chain it against one driver.
- No sound plays if the target is not currently driving, is not a real user, or
  is the caller themselves. The text bonk and the tally still work in all cases.
- A missing sound file logs once and skips, so the command works on a server that
  never installs one. A playback failure is caught and logged rather than
  surfacing as a failed chat command.
- Discord bonks play the sound too; only commands needing the caller's own socket
  are unavailable from there.

Server suite: 138 passing, 0 failing.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-29 05:05:36 +01:00
Saul5662andClaude Opus 5 eb0db3508f fix(commands): remove a stray NUL byte from the pairSeed separator
`pair.join('\0')` was written where `pair.join(' ')` was meant. The NUL made git
classify funHelpers.js as a binary file, so it showed as `Bin 0 -> 6397 bytes`
instead of a reviewable diff. The seed stayed deterministic either way, so no
behavior changes and the tests were already passing.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-29 04:58:24 +01:00
legop3 6aee49bfdb built webui lol 2026-07-28 23:57:43 -04:00
Saul5662andClaude Opus 5 a9428d3d72 test(commands): cover fun commands, cooldowns, and the dispatcher permission gate
89 new node:test cases. The dispatcher suite is the important one: it pins the
behavior the registry-driven permission gate replaced a hardcoded action list
with, asserting that admin-only commands are still admin-only, that the
self-policing commands (goal/reason/verify/deter) still reach their own handlers
as a non-admin, that unknown actions are still not public, and that `rsvp` is
still not a command.

Also covered:

- cooldown boundaries, including that a refused call does not extend the window
- actor identity keying across transports, and that extra browser tabs do not
  make a target ambiguous
- honk/spin refusing without drive control and being unreachable from Discord
- spin honouring applyPrivateDriveSafety instead of bypassing it
- boo speaking only canned text, never anything the caller typed
- disco obeying the room-light lock and restoring the lights when it ends
- mention sanitizing on replies and on stored nicknames rendered by bonkboard
- the stats store degrading to empty on a corrupt or wrong-shaped file

Full server suite: 126 passing, 0 failing.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-29 04:57:17 +01:00
legop3 30e961d727 Merge pull request #18 from Saul5662/feat/user-volume-gains
feat(audio): per-user horn/TTS/forward gains with admin-capped ceilings and a VIP boost flag
2026-07-28 23:55:29 -04:00
Saul5662andClaude Opus 5 a2fbbc100e feat(commands): add a fun command category with 18 public rs commands
Adds a `fun` category to the operator command registry, reachable identically
from site chat and Discord:

- text: bonk, hug, slap, 8ball, roll, coin, ship, rate, uwu, wanted
- counters: bonkboard, pet, snitch
- hardware: honk, boo, spin, disco, vibecheck

Supporting pieces:

- `permission: 'public'` in the registry. The dispatcher previously decided
  non-admin access with a hardcoded chain of `action !== '...'` comparisons, so
  every new public command needed a dispatcher edit. That chain is replaced by a
  registry lookup plus SELF_GATED_ACTIONS, which names the commands that enforce
  their own permissions internally (goal/reason are read-public write-admin;
  verify/deter reject non-lockdown-admins themselves). Existing behavior for
  every pre-existing command is unchanged.
- `cooldowns.js`, a per-actor per-command in-memory gate. Site chat's own rate
  limit is per-socket-per-message and does not bound a specific command, so
  without this one person could turn `rs honk` into a siren. Site chat rebuilds
  its router per message, so the gate is created at module scope there and
  injected.
- `funStatsService`, a small JSON store for the persistent tallies. Counters are
  keyed by an actor key spanning transports (`user:<id>` / `discord:<id>`), and a
  Discord id has no row in `users`, so `user_feature_state` could not hold them
  without violating its foreign key.

Safety notes:

- `issueCommand` is the raw rover transport and performs none of the ownership,
  deterrence, or private-safety checks the socket `command` handler applies, so
  honk and spin re-check `canDrive` themselves and spin re-applies
  `applyPrivateDriveSafety`. Both are therefore site-chat only: a Discord message
  has no socket and can never satisfy those checks.
- `boo` speaks a canned taunt rather than caller-supplied text, so it cannot
  become an unmoderated TTS channel aimed at whoever is nearest a rover.
- `disco` obeys the existing room-light lock and the homeAssistant feature gate.
- The whole fun category is suspended in lockdown mode.
- Mute and deterrence already stop command-shaped chat before the router runs.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-29 04:51:40 +01:00
Saul5662andClaude Opus 5 42a7cefeba build: drop server/public build artifacts from this branch
The committed bundle was produced by a local `vite build`, which runs without
the injected analytics snippet. That stripped the page-wide `window.roverAnalytics`
Umami adapter and both analytics.otter.land script tags out of
server/public/index.html, and repointed the bundle hash at a local build.

Revert server/public to its origin/main state so this branch is source-only.
server/public should be rebuilt on deploy, where the analytics snippet exists.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-29 04:36:51 +01:00
Saul5662andClaude Opus 5 7272c8b4fa fix(commands): say "User not found" instead of "Selector not found"
The identity resolver's fuzzy-miss error was labelled "Selector", which is
internal jargon — the operator running `rs gain grant <vip>` or `rs kick
<user>` typed a username, not a "selector". Relabel that one message to
"User" so the chat reply reads plainly.

The sibling "Selector matched multiple records." and "Selector required."
messages are intentionally left alone for now.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-29 04:34:19 +01:00
Saul5662andClaude 0e1ad8c6a0 feat(webui): Volume settings section and admin boost-cap editor
Adds a server-backed Volume card to page settings with horn, TTS, and
microphone sliders. Each slider is a 0-100% share of the ceiling the
server resolved for that user, and the card shows the resulting
multiplier plus whether the user is on the normal global limit or a
raised VIP limit. A slider whose ceiling is zero renders disabled rather
than pretending to do something.

The admin section gains sliders for the VIP boost hard caps beside the
existing global gains. Both editors now render from one GAIN_FIELDS list
through a shared GainSlider instead of six copied slider blocks.

Includes the rebuilt bundle so the served UI matches the source.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-07-29 04:10:26 +01:00
Saul5662andClaude 09c1257578 feat(commands): add 'rs gain' to grant the VIP audio gain boost
Admins can now run 'rs gain list|grant <vip>|revoke <vip>' from web chat or
Discord. Grant matches only against the verified list and revoke only
against current holders, so a nickname shared with an unverified visitor
reports not-found rather than resolving to someone ineligible. The action
joins the moderation set so lockdown narrows it to lockdown admins.

Also extracts the ceiling math into audioLevelsService/gainMath.js and
covers both it and the command with node:test suites.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-07-29 04:07:44 +01:00
Saul5662andClaude f3b349bb4a feat(audio): per-user horn/TTS/forward volume with admin-capped ceilings
Every user now gets a personal 0-1 volume for horn, TTS, and mic forward.
The value is stored as a fraction of the ceiling that applies to them, so
lowering the global admin gain quiets everyone immediately instead of
leaving stale absolute values behind.

Ceilings resolve in three layers: the global admin gain is the default
ceiling; the audioGainBoost flag raises it to an admin-editable hard cap
(default 0.5x horn, 0.8x TTS, 0.4x forward); Math.max keeps the flag from
ever lowering a ceiling if the global gain is set higher than a cap.

Preferences live in identity feature state rather than a cookie so they
follow the user and cannot be raised client-side. The rover exposes gain
as three ALSA masters, so the resolved gains pushed to a rover are those
of the socket currently holding audio control -- re-pushed on driver
join/leave and every turn rotation.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-07-29 04:04:22 +01:00
Saul5662andClaude 81f52c29d8 feat(identity): add audioGainBoost user flag
Adds an audio_gain_boost_enabled status column (plus _at/_by audit
fields) to user_status, exposed as user.audioGainBoost and copied onto
sockets as socket.data.hasAudioGainBoost. The flag marks VIPs allowed to
raise their personal horn/TTS/mic gain ceiling past the global admin gain
settings.

Grant/revoke goes through verificationService so socket flags refresh and
an event is published. Resolution is restricted to verified users, so an
unverified visitor sharing a nickname can never be matched.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-07-29 03:59:47 +01:00
legop3 d000b8f4f8 disable hidden video disconnect by default 2026-07-26 21:08:24 -04:00
legop3 0f9bed9c99 slopodometering 2026-07-25 16:37:58 -04:00
legop3 8642fbac3c slopreporting 2026-07-25 15:54:38 -04:00
legop3 3a26b4871a slopfixing queue stuffs 2026-07-21 23:38:57 -04:00
legop3 b26daed93f better banning, new deter mute, replay discord fixes. 2026-07-21 22:50:12 -04:00
legop3 fb9565faf9 hapticks 3 3 3 3 3 3 2026-07-21 18:38:01 -04:00
legop3 358aa0b1d6 replay timestamps in filenamess 2026-07-21 16:29:10 -04:00