Add a fail-open Python sidecar (livekit/voice-limit-guard.py) that fronts
lk-jwt-service to enforce per-room voice participant caps for ALL Matrix
clients, not just Lotus Chat:
- lk-jwt-service moved to :8071 (systemd drop-in), guard owns :8070 so NPM's
existing /sfu/get + /get_token proxy targets are unchanged
- guard reads io.lotus.voice_limit.max_users (Synapse admin API, cached),
forwards to lk-jwt-service, and on an issued token decodes the LiveKit alias
+ requester, counts distinct Matrix users via LiveKit ListParticipants, and
returns 403 when the room is full (rejoins/extra devices allowed)
- any error fails open (returns upstream response) so calls never break
- systemd/voice-limit-guard.service; README documents ports, setup, revert
Also update landing page: voice limit is now server-enforced for all clients.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Feature description paragraph: added AFK auto-mute (1–30 min voice idle
timeout) and knock-to-join admin badge (live count on Members button)
- Comparison table: new AFK auto-mute row in Voice & Video section
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
New table row for 5 animated CSS wallpapers (rain, stars, grid pulse,
aurora, fireflies). Feature blurb updated to mention the animated
backgrounds and the glassmorphism body-background fix.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds inline GIF preview and collapsible long messages rows to the
feature comparison table; extends the June 2026 feature list with all
five newly completed items (P3-5, P3-9, P5-19, P5-23, P5-26).
Includes pre-staged README additions for presence tracking, encrypted
search, privacy settings, draft persistence, and PiP persistence.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Update comparison table date to June 2026
- Add push-to-deafen (M key), night light filter, message length
counter, and TDS orange typing dots to also-available paragraph
- PTT row notes M = push-to-deafen
- Add Night Light row to UX & Extras comparison table
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
README.md:
- Replaced the stale 'Auto-revert spotlight on screenshare' entry
(that 600ms revert-to-grid code was removed — it caused fullscreen
to show avatars instead of the screenshare)
- Added accurate entries for all four features added this cycle:
Screenshare fullscreen, PiP screenshare focus, Screenshare audio
mute, Custom status message
landing/index.html:
- Updated Lotus Fork feature description paragraph to mention
screenshare fullscreen, screenshare audio mute, PTT, and custom
status messages
- Added PTT row to Voice & Video comparison table
- Updated Screenshare row for Lotus Chat to note fullscreen + audio mute
- Added 'Custom status message' row to UX & Extras section
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
LXC 139 NPM proxy host 49 now proxies both /sfu/get and /get_token
to lk-jwt-service (port 8070). Note that re-saving via NPM UI will
overwrite the conf and require re-adding the location blocks.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Document the new member list presence indicators and per-member device
sessions panel with per-device SAS verification in both the landing
page feature list and the README custom features table.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The runner intermittently times out (5m) downloading the gitleaks binary
from GitHub. Add --retry 3 --retry-delay 5 --max-time 120 so transient
network blips don't fail the job.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add per-message read receipts comparison row (8-col, including new official
Cinny column from dc8f588). Update Lotus Chat feature description to include
per-message read receipt avatars and chat wallpaper in calls.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add chat.lotusguild.org as the featured Cinny fork column and cinny.in
as a second comparison column. Adds official Cinny card in Other
Clients section and updates table colspan to 8.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
OOM observed during rendering-chunks phase at 896MB and 3072MB.
6144MB heap with 8GB LXC memory is confirmed working.
Also update README rebuild command to match.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add !cancel command (anyone cancels own blackjack; PL50+ clears all room games)
- Add !wordlestats top-level command (wraps wordle stats function)
- Add !cleanwelcome admin command to purge stale welcome DM records
- !help now hides management section from sub-PL50 users, hides !health from non-admins
- !announce uses nio room cache for join_rule instead of an API call per room
- Fix _INVITEALL_BLOCKED comment (Commands is knock-gated, not restricted)
- welcome.py: skip duplicate DM if a pending welcome already exists for the user
- welcome.py: add clean_stale_dm_messages() helper
- welcome.py: replace no-op post_welcome_message with log_ready()
- bot.py: update import/call to match welcome.py rename
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Commands has join_rule=restricted so the join-rule filter didn't catch
it. Added _INVITEALL_BLOCKED set with the Commands room ID — any room
in that set is skipped regardless of join rule. Invite-only rooms
(Management, Cool Kids, Spam and Stuff) are still excluded by the
existing join_rule check.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
!setpl now iterates every Space room via the hierarchy API and updates
the power_levels state event in each. Setting a user to the room's
users_default cleans up the explicit entry rather than leaving a
stale PL0. Rooms where the bot lacks permission are counted and
reported but don't block the rest.
!inviteall skips rooms with join_rule=invite (Management, Cool Kids,
Spam and Stuff) — only public/restricted rooms get the invite. Also
skips rooms where the target is already a member.
_get_space_room_ids() fetches the Space child list via the v1 hierarchy
API with pagination support.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Five new commands, all gated behind is_elevated() (power level >= 50):
!mkroom <name> — clone #general's power levels, join rules, encryption,
history visibility, and avatar into a fresh v12 room,
auto-adds it to the Lotus Guild Space, and invites
the caller.
!roominfo — show room display name, ID, member count, join rule,
encryption status, and all users with PL > 0.
!topic [text] — set or clear the current room's topic.
!invite @user — invite any Matrix user to the current room.
!setpl @user <n> — update a user's power level (0-100); cannot exceed
the caller's own level.
Also adds urllib.parse.quote and MATRIX_HOMESERVER to imports, and
adds a "Management (PL50+)" section to !help.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add is_elevated() helper that reads the room power level from the nio
client store. Users at PL50+ (Nerdy Council and above) skip the cooldown
check entirely. The timestamp is still recorded so cooldown applies
if their power level is later reduced below 50.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Each player now gets exactly one attempt per question. If their guess
is wrong it's recorded and they're locked out for that round. If both
players have guessed wrong the correct answer is revealed and the game
moves to the next question immediately (no need to wait for the 45s
timeout).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Instead of a raw 6-word slice (which left dangling fragments like
'Partly - Not exclusively American; has'), extract the first complete
sentence (up to 10 words). Falls back to the 6-word cap only if no
sentence boundary is found in the response.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Reduce word cap from 12 to 6 and add explicit instructions not to use
proper nouns, brand names, or place names in answers. Fixes the case
where the model blurted 'The Grand Canyon State (Arizona)' in response
to a geography question.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add a rolling cache of the last 30 answers (persisted to
twentyq_cache.json) and pass the recent list to the model as an
explicit avoid clause. Also prompt the model to vary categories
each round. If the model still returns a cached answer it is
rejected and one retry is attempted automatically.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Allow open-ended questions like "What color is it?" or "How big is it?"
The model now gives brief descriptive answers (capped at 12 words) for
open questions while still answering Yes/No/Sometimes/Partly for binary
ones. Updated command descriptions and in-game prompts accordingly.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Import MATRIX_USER_ID in commands.py (was missing — caused !ttt and
!triviaduel to crash with NameError on every invocation)
- Blackjack is now per-player per-room: multiple players can each run
their own game simultaneously; !hit and !stand operate on the caller's
own game only
- !hottake: pick a random topic from 20 categories and pass it to the
model so takes aren't all nostalgia-flavoured
- !nhie: tighter prompt with topic rotation and a word-count cap so
generated scenarios are simpler and more relatable
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
New commands: numguess/ng, wordchain/wc/endwc, acronym/ac,
20q/q/answer, nhie, hottake, ttt/move, blackjack/hit/stand,
triviaduel/da. All per-room with AI-generated content where
applicable. callbacks.py wired up for new reaction handlers
(acronym votes, nhie, hottake). Help and README updated with
full command reference.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The old \O/ ears row looked like 6 limbs when combined with /|\ arms.
New extended progression:
0-6: same as standard (head → body → arms → legs)
7: left foot (/ in the previously empty row below legs)
8: both feet (/ \)
9: @ head (anguish — full figure visible)
10: X head (dead)
Each stage is visually distinct with no overlapping limb confusion.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
-r requirements.txt causes pip-audit to spawn an internal venv which
calls ensurepip, failing with exit 127 on the standalone Python build.
--local avoids the venv. CVE-2026-3219 is in pip itself (not our deps)
so we ignore it explicitly with --ignore-vuln.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- !help Games section now includes !guess
- !8ball description mentions --debug flag
- pip-audit now scans only requirements.txt instead of --local (which
was flagging CVE-2026-3219 in pip itself, not our dependencies)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Mirrors riddle/trivia cache pattern: loads hangman_cache.json on startup,
appends each new word, caps at 30, saves after each game. Recent words
are passed to the model prompt to avoid repeats.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Silent failures (word too long/short, has hyphens, empty hint) are now
logged as warnings showing the actual word/hint returned. Retry up to
2 times before giving up, matching riddle's behavior.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The creative model was producing fortune-cookie mysticism ("navigate
the curve of fate") instead of 8-ball answers. New system prompt
explicitly requires YES/NO/UNCERTAIN category answers, 2-6 words,
funny and direct — no cryptic prophecies.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>