Files
discourse/plugins/voice/config/settings.yml
T
Rafael dos Santos Silva 07b05ee8cd FEATURE: Enable voice badges by default and harden them first (#43190)
The team wants the voice badge set on by default. Reviewing the fixtures
and grant hooks first turned up a handful of issues that only bite once
real sites accumulate history, so this PR fixes them and flips the
default in one go.

### Fixes

- **Scheduled badges were auto-revocable.** Core's `Badge.auto_revoke`
defaults to `true` and the fixtures never overrode it. Sessions and
co-presence rows are purged after `voice_session_retention_days` (400),
and a deleted user's co-presence rows are dropped, so the daily backfill
would have silently revoked airtime, bonding, loyalty, exploration and
hosting badges (and any titles set from them) as data aged out. All 19
query badges now set `auto_revoke = false`, same as core's cumulative
badges.
- **Crowd Puller / Master of Ceremonies were farmable.** The hosting
query counted `COUNT(s.id)` across a creator's rooms, including the
creator's own joins. At the 30/min join rate limit a Gold title took ~17
minutes. It now counts `DISTINCT` visitors excluding the creator.
- **Packed House was unreachable for most rooms.** It read
`room.max_participants`, which is `nil` whenever the room is capped by
the site-wide setting. It now uses `effective_max_participants`,
matching admission.
- **Bulk toggles skipped badge maintenance.**
`enable_all!`/`disable_all!` used `update_all`, bypassing the `Badge`
callbacks that refresh featured ranks and distinct badge counts, leaving
stale profile counts. They now run that once after the flip, and
enabling also enqueues a backfill for the query badges so they show up
right away instead of after the next daily job.
- **Analytics dependency made explicit.** Every badge is computed from
`voice_sessions`; with analytics off nothing can be earned. The hooks
now check `voice_analytics_enabled` and the setting description
references it.

### Default flip

`voice_badges_enabled` now defaults to `true` and the fixtures seed
badges as `default_enabled`. Neither reaches existing sites on its own:
the settings-changed hook doesn't fire for a default change, and
`default_enabled` only applies to new records. A post-migration enables
the Voice-grouping badges on sites that have no explicit
`voice_badges_enabled = false` row.

### Tests

- New `badge_backfill_spec.rb` runs `BadgeGranter.backfill` for every
SQL family at its threshold boundary, including the retention-purge case
and the hosting self-join exclusion.
- Migration spec covers untouched, explicitly-disabled, and non-Voice
badges.
- Hooks spec updated for the site-cap fallback, analytics gate,
default-enabled seeding, and backfill enqueueing.

### Not changed (design notes from the review)

- Weekend Warrior and Loyalty use server UTC for day boundaries while
Night Owl / Early Bird use the user's timezone. Left as is; user
timezones in SQL would need per-row `AT TIME ZONE` with invalid-name
handling.
- Icebreaker largely overlaps Mic Check since co-presence rows lag five
minutes. Bronze, harmless.
- Marathoner relies on `voice_afk_disconnect_threshold_minutes` (30) to
keep idle tabs from earning a Gold title.
2026-09-04 14:00:17 -03:00

171 lines
3.7 KiB
YAML

plugins:
voice_enabled:
default: false
client: true
hidden: true
upcoming_change:
status: "experimental"
impact: "feature,all_members"
allow_enabled_for:
- "everyone"
learn_more_url: "https://meta.discourse.org/t/-/411337"
requires_plugin_enabled: false
permanent_warning: false
voice_allowed_groups:
type: group_list
list_type: compact
default: "4|5" # auto groups anonymous_users, logged_in_users
allow_any: false
refresh: true
disallowed_groups: "0"
depends_on:
- voice_enabled
voice_create_room_allowed_groups:
type: group_list
list_type: compact
default: "1|2|12" # auto groups admins, moderators, trust_level_2
allow_any: false
refresh: true
mandatory_values: "1" # auto group admins
disallowed_groups: "0|4"
depends_on:
- voice_enabled
voice_direct_calls_allowed_groups:
type: group_list
list_type: compact
default: ""
allow_any: false
refresh: true
disallowed_groups: "0|4"
depends_on:
- voice_enabled
voice_max_rooms_per_user:
default: 5
voice_max_invites_per_day:
default: 100
min: 1
max: 5000
voice_ephemeral_room_ttl_minutes:
default: 30
min: 5
max: 10080
voice_participant_ttl_seconds:
default: 30
voice_max_room_participants:
default: 50
min: 2
max: 200
voice_stun_servers:
default: "stun:stun.l.google.com:19302|stun:global.stun.twilio.com:3478"
voice_turn_servers:
default: ""
voice_turn_username:
default: ""
voice_turn_credential:
default: ""
secret: true
voice_turn_secret:
default: ""
secret: true
voice_turn_secret_servers:
default: ""
validator: "VoiceTurnSecretServersValidator"
voice_livekit_url:
default: ""
validator: "VoiceLivekitUrlValidator"
voice_livekit_api_key:
default: ""
voice_livekit_api_secret:
default: ""
secret: true
voice_livekit_room_policy:
default: "disabled"
type: enum
choices:
- disabled
- per_room
- all_rooms
validator: "VoiceLivekitPolicyValidator"
voice_livekit_room_prefix:
default: ""
voice_livekit_mesh_fallback:
default: false
voice_livekit_recording_enabled:
default: false
client: true
voice_livekit_recording_filepath:
default: "voice/{room_name}-{utc}"
voice_video_enabled:
default: true
client: true
voice_video_max_publishers:
default: 8
min: 2
max: 16
client: true
voice_video_background_blur_enabled:
default: true
client: true
voice_subtitles_enabled:
default: false
client: true
voice_stt_model_base_url:
default: ""
client: true
voice_max_voice_quality:
default: "maximum"
type: enum
client: true
choices:
- standard
- high
- maximum
voice_max_camera_quality:
default: "maximum"
type: enum
client: true
choices:
- standard
- high
- maximum
voice_max_screen_share_quality:
default: "maximum"
type: enum
client: true
choices:
- standard
- high
- maximum
voice_idle_threshold_minutes:
default: 5
min: 0
max: 60
client: true
voice_afk_auto_mute_threshold_minutes:
default: 15
min: 0
max: 120
client: true
voice_afk_disconnect_threshold_minutes:
default: 30
min: 0
max: 240
client: true
voice_auto_status_enabled:
default: true
client: true
voice_mesh_privacy_warning_enabled:
default: true
client: true
voice_badges_enabled:
default: true
voice_analytics_enabled:
default: true
voice_session_retention_days:
default: 1095
min: 7
max: 3650
voice_chat_enabled:
default: true
client: true