bluandClaude Opus 5 9ea036bc35 Presets, requirements and a verdict per report
Everything that varies per club is a preset now: its filters (one of each
kind, pickable rather than pasted ids), the requirements it places on a
joiner, its own channels and its own formats, including a Discord embed the
user edits field by field. A join is reported once per matching preset.

Each report carries the worst verdict of the preset's checks: green when
everything was verified and holds, orange when something could not be checked,
red when a check failed. Templates read it as {result}, {resultEmoji} and
{resultColor}.

Facts come from ctx.vrchat, so the hand-written event narrowers are gone and
the manifest no longer needs host:actions or any event name.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-27 13:10:15 +02:00
2026-09-27 02:16:28 +02:00
2026-09-27 02:16:28 +02:00

Club Security

Reports who joins your instance, and whether they meet your club's rules.

Everything that varies per club is a preset: which instances it watches, what it expects of a joiner, and where its reports go. A join is reported once per enabled preset whose filters match the instance, so one account can run the door for several clubs at once.

Every report carries a verdict, and it is the worst of the preset's checks:

Verdict Means
✅ green Every requirement was checked and holds.
⚠️ orange Something could not be checked: an unknown avatar rank, a hidden age status, a membership the player does not show publicly.
⛔ red A requirement was checked and does not hold: a VeryPoor avatar under a Medium floor, a verified account that is not 18+, a non-friend where friendship is required.

The verdict reaches your templates as {result}, {resultText}, {resultEmoji} and {resultColor} — the last one colours the Discord embed's bar.

A preset

Section Setting Meaning
Identity Preset name, Enabled The name appears in every report as {preset}.
Filters Instance types, Group, Worlds One of each kind. Empty means "any". A pickable group and worlds, not pasted ids.
Requirements Require 18+, PC / Quest avatar rank at least, Must be a member of, Must be on my friend list Each one becomes a check with its own verdict.
Channels In-app toast, Desktop, VR overlay, Discord Per preset, so a strict club can post to Discord while a relaxed one only toasts.
Formats Report template, VR overlay template, Discord embed The text report, the plain-text one for VR, and the embed.

The default report

✅ Tupper joined · Saturday Night
✅ 18+ verified: 18+
✅ PC avatar rank: Good
✅ Group member: member
Avatar: Ava (PC Good · Quest Poor)

First line = title, rest = body. A line whose placeholders all came out empty is left out, which is why the rejoin line only appears for someone who has been in that instance before.

{name} is short for {{ name }}; the full template language (conditions, filters, {% if %} blocks) is in the plugin system's API reference. The VR template is separate because WayVR draws with a single font and shows nothing for emoji.

Kind Variables
Verdict result resultText resultEmoji resultColor checksText checksPlainText failedText unverifiedText
Player name playerId platform platformEmoji isFriend friendText ageVerified ageVerifiedText ageVerifiedEmoji ageStatus
Avatar avatar avatarId avatarImageUrl pcRank pcRankText pcRankEmoji questRank questRankText questRankEmoji
Club preset inGroup inGroupText inGroupEmoji
History rejoin rejoinText rejoinEmoji rejoinAgo rejoinSince rejoinAt
Place and time world worldId instanceType instanceId location time date timestamp

Booleans (ageVerified, inGroup, rejoin, isFriend) are empty when unknown, so {{ "yes" if rejoin else "no" }} and {% if inGroup == false %}…{% endif %} both behave. A template that does not parse is reported in the log and the default is used instead.

Where each fact comes from

Everything goes through ctx.vrchat, which reads VRCNext's data without opening its dialogs.

Fact Source Caveat
18+ verification The joiner's profile. 18+ passes, verified without 18+ fails, hidden or missing is unverified. Legacy accounts without a usr_ id cannot be looked up at all.
Avatar and ranks The avatar the player wears, resolved through VRCNext's avatar databases, then its performance ranks. Only works when one of the databases knows the avatar; an unknown rank is unverified, never a failure.
Group membership The groups the user shows publicly. A member who hides the membership is unverified, not a failure.
Friendship Your friend list. —
Rejoin VRCNext's timeline: the player's ten most recent events, each with its location. Yes when one of them is this exact instance (same world and instance id) from before this join. Survives restarts and reaches back to when VRCNext was installed, but only ten events deep per player.

Each lookup runs in parallel and degrades to "unknown" on its own timeout rather than holding up the report.

Your own joins

The signed-in account comes from ctx.vrchat.self(). VRChat logs an OnPlayerJoined line for the local player too, and right after it one line for every player already in the instance. The plugin ignores your own line and treats joins inside the settle window after it (or after a world change) as "already here".

Layout and permissions

Flat, like every plugin: plugin.json, main.ts, src/. The manifest declares gamelog (joins and world changes), vrchat (the read-only data above), notifications (toast, Windows tray toast), native (bridge targets) and network with discord.com as its only host. No VRCNext actions and no raw events: the vrchat capability covers everything this plugin reads.

S
Description
Club Security plugin for VRCNext: reports who joins your instance
Readme Unlicense
149 KiB
0 Stars 1 Watchers 0 Forks
Languages
TypeScript 100%