Skip to content

Wire up voice channel XP tracking #307

@gvieira18

Description

@gvieira18

Context

Voice XP tracking is completely non-functional. The infrastructure exists
but nothing is wired together — users in voice channels always get 0 XP.

What exists today

  • DynamicVoiceEvent captures Discord VOICE_STATE_UPDATE and calls
    PersistVoiceStateAction, which stores records with
    obtained_experience = 0 always.
  • NewVoiceMessage (the action that would calculate and award XP) is
    never called anywhere — dead code.
  • IncrementExperience::incrementByVoiceMessage() works correctly and
    is tested, but never invoked for voice.
  • Mute/deaf state is never captured — Discord sends self_mute /
    self_deaf but the code only tracks join/leave.
  • Schema mismatchvoice_messages.state stores 'joined'/'left'
    strings, but VoiceStatesEnum expects {disabled, muted, unmuted}.

How the old bot handled it

The previous Node.js bot (he4rt-bot-next) used a ticker/polling
approach that worked well:

// Every VOICE_COUNTER_XP_IN_MINUTES, scan all members in voice channels
const isUnmuted = !member.voice.selfMute && !member.voice.serverMute
const isAble = !member.voice.selfDeaf && !member.voice.serverDeaf

state: isAble && !isUnmuted ? 'muted'
     : isAble && isUnmuted  ? 'unmuted'
     : 'disabled'

This maps directly to the existing VoiceStatesEnum multipliers:

  • unmuted → 5 × level
  • muted → 3 × level
  • disabled → 0 × level

What needs to happen

  1. Decide approach: replicate the ticker/polling model (scheduled
    command that scans voice channels every N minutes) or fix the
    event-driven flow. Ticker is simpler and proven.
  2. Capture mute/deaf state from Discord's voice state data and map
    to VoiceStatesEnum.
  3. Call IncrementExperience::incrementByVoiceMessage() to actually
    award XP.
  4. Fix or replace NewVoiceMessage — currently depends on the old
    FindExternalIdentity pattern and request()->tenant_id (breaks in
    queue/scheduler context). Should use ResolveUserContext like
    NewMessage does.
  5. Reconcile schemavoice_messages.state should store
    VoiceStatesEnum values (disabled/muted/unmuted), not
    joined/left.
  6. Exclude AFK channel — old bot skipped the AFK voice channel, same
    logic should apply.
  7. Add tests for the full voice XP flow.

Metadata

Metadata

Assignees

Labels

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions