Skip to content

Add Discord Master role for Masterclass purchasers - #479

Draft
simonhamp wants to merge 1 commit into
mainfrom
discord-masterclass-btn
Draft

Add Discord Master role for Masterclass purchasers#479
simonhamp wants to merge 1 commit into
mainfrom
discord-masterclass-btn

Conversation

@simonhamp

Copy link
Copy Markdown
Member

What

Masterclass students can now claim a Master role in the NativePHP Discord server from the Integrations page, unlocking private channels. This follows the existing Ultra / Early Adopter role pattern end to end.

  • DiscordApi gains assignMasterRole() / removeMasterRole() / hasMasterRole().
  • New discord_master_role_granted_at column on users, mirroring the other two role timestamps.
  • User::hasPurchasedMasterclass() checks for a ProductLicense against the nativephp-masterclass product.
  • DiscordAccessBanner folds $hasMasterRole into the existing 5-minute cached status call, adds a requestMasterRole() action, and backfills the granted-at timestamp when the role is detected on Discord's side.
  • The banner renders Master Eligible / Master Role Active badges and a Request Master Role button.
  • DiscordIntegrationController assigns Master automatically on Discord connect for existing owners, and revokes it on disconnect.

Why the entitlement check is direct-ownership

hasPurchasedMasterclass() deliberately checks the user's own productLicenses rather than reusing hasProductLicense(), which also grants access via the team owner. The course itself is gated on direct ownership (Product::isOwnedBy()), so this keeps the Discord role tracking course access exactly — Ultra team members don't get the Master role, because they can't watch the course either.

Drive-by fix

The Discord banner's visibility gate on the Integrations page previously only admitted Max / Ultra / EAP customers, so someone who had bought only the Masterclass saw no Discord section at all. Masterclass owners now pass the gate.

Deployment note

Requires DISCORD_MASTER_ROLE_ID to be set in the environment. The Master role needs creating in Discord with the relevant private-channel permissions, and the bot's own role must sit above Master in the server's role hierarchy or the assign call will 403.

Not included

Nothing grants the role at purchase time — a buyer who already has Discord connected has to visit Integrations and click the button. That matches how the existing roles behave. If we'd rather it be automatic, the hook point is HandleInvoicePaidJob::createProductLicense(), alongside the existing GitHub repo-access grant (plus products:grant and the Filament comp action).

Testing

28 tests passing across DiscordIntegrationTest and DiscordAccessBannerTest, covering the happy path, non-owners, not-in-guild, disconnect revocation, and page-level gating in both directions. 118 related tests (course content, purchase history, teams, Ultra access) still pass. Pint clean.

🤖 Generated with Claude Code

Masterclass students can now claim a "Master" role in the NativePHP
Discord server, unlocking private channels. Follows the existing Ultra
and Early Adopter role pattern.

The entitlement check uses direct product ownership rather than the
team-inheriting hasProductLicense(), so the role tracks course access
exactly: Ultra team members don't get it, since they can't watch the
course either.

Also fixes the banner's visibility gate on the Integrations page, which
previously only admitted Max/Ultra/EAP customers — a course-only
customer saw no Discord section at all.

Requires DISCORD_MASTER_ROLE_ID to be set in the environment.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant