Skip to content

Commit 4ba6148

Browse files
simonhampclaude
andauthored
Show Discord integration to Ultra subscribers and rename role to "Ultra" (#385)
Widens the Discord role gate from `hasMaxAccess()` to also include `hasUltraAccess()` so Ultra subscribers see the integration banner and can claim the role. Renames the role end-to-end (config key, env var, DiscordApi methods, jobs, Livewire state, user-facing copy) from "Max" to "Ultra" to match the new branding. Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent fa83fe6 commit 4ba6148

14 files changed

Lines changed: 209 additions & 64 deletions

app/Console/Commands/RemoveExpiredDiscordRoles.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ class RemoveExpiredDiscordRoles extends Command
1010
{
1111
protected $signature = 'discord:remove-expired-roles';
1212

13-
protected $description = 'Remove Discord Max role for users whose Max licenses have expired';
13+
protected $description = 'Remove Discord Ultra role for users whose Max licenses or Ultra subscriptions have ended';
1414

1515
public function handle(): int
1616
{
@@ -23,8 +23,8 @@ public function handle(): int
2323
->get();
2424

2525
foreach ($users as $user) {
26-
if (! $user->hasMaxAccess()) {
27-
$success = $discord->removeMaxRole($user->discord_id);
26+
if (! $user->hasMaxAccess() && ! $user->hasUltraAccess()) {
27+
$success = $discord->removeUltraRole($user->discord_id);
2828

2929
if ($success) {
3030
$user->update([

app/Http/Controllers/DiscordIntegrationController.php

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -75,10 +75,10 @@ public function handleCallback(): RedirectResponse
7575

7676
$rolesAssigned = [];
7777

78-
if ($user->hasMaxAccess()) {
79-
if ($discord->assignMaxRole($discordUser['id'])) {
78+
if ($user->hasMaxAccess() || $user->hasUltraAccess()) {
79+
if ($discord->assignUltraRole($discordUser['id'])) {
8080
$user->update(['discord_role_granted_at' => now()]);
81-
$rolesAssigned[] = 'Max';
81+
$rolesAssigned[] = 'Ultra';
8282
}
8383
}
8484

@@ -117,7 +117,7 @@ public function disconnect(): RedirectResponse
117117
$discord = DiscordApi::make();
118118

119119
if ($user->discord_role_granted_at) {
120-
$discord->removeMaxRole($user->discord_id);
120+
$discord->removeUltraRole($user->discord_id);
121121
}
122122

123123
if ($user->discord_early_adopter_role_granted_at) {
Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
use Illuminate\Queue\SerializesModels;
1212
use Illuminate\Support\Facades\Log;
1313

14-
class AssignDiscordMaxRoleJob implements ShouldQueue
14+
class AssignDiscordUltraRoleJob implements ShouldQueue
1515
{
1616
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
1717

@@ -31,8 +31,8 @@ public function handle(): void
3131
return;
3232
}
3333

34-
if (! $this->user->hasMaxAccess()) {
35-
Log::info('Skipping Discord role assignment - user has no Max access', [
34+
if (! $this->user->hasMaxAccess() && ! $this->user->hasUltraAccess()) {
35+
Log::info('Skipping Discord role assignment - user has no Max or Ultra access', [
3636
'user_id' => $this->user->id,
3737
]);
3838

@@ -50,17 +50,17 @@ public function handle(): void
5050
return;
5151
}
5252

53-
$success = $discord->assignMaxRole($this->user->discord_id);
53+
$success = $discord->assignUltraRole($this->user->discord_id);
5454

5555
if ($success) {
5656
$this->user->update(['discord_role_granted_at' => now()]);
5757

58-
Log::info('Discord Max role assigned successfully', [
58+
Log::info('Discord Ultra role assigned successfully', [
5959
'user_id' => $this->user->id,
6060
'discord_id' => $this->user->discord_id,
6161
]);
6262
} else {
63-
Log::error('Failed to assign Discord Max role', [
63+
Log::error('Failed to assign Discord Ultra role', [
6464
'user_id' => $this->user->id,
6565
'discord_id' => $this->user->discord_id,
6666
]);
Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
use Illuminate\Queue\SerializesModels;
1212
use Illuminate\Support\Facades\Log;
1313

14-
class RemoveDiscordMaxRoleJob implements ShouldQueue
14+
class RemoveDiscordUltraRoleJob implements ShouldQueue
1515
{
1616
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
1717

@@ -33,17 +33,17 @@ public function handle(): void
3333

3434
$discord = DiscordApi::make();
3535

36-
$success = $discord->removeMaxRole($this->user->discord_id);
36+
$success = $discord->removeUltraRole($this->user->discord_id);
3737

3838
if ($success) {
3939
$this->user->update(['discord_role_granted_at' => null]);
4040

41-
Log::info('Discord Max role removed successfully', [
41+
Log::info('Discord Ultra role removed successfully', [
4242
'user_id' => $this->user->id,
4343
'discord_id' => $this->user->discord_id,
4444
]);
4545
} else {
46-
Log::warning('Failed to remove Discord Max role (user may not have role)', [
46+
Log::warning('Failed to remove Discord Ultra role (user may not have role)', [
4747
'user_id' => $this->user->id,
4848
'discord_id' => $this->user->discord_id,
4949
]);

app/Jobs/RevokeMaxAccessJob.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -82,17 +82,17 @@ private function revokeDiscordRole(User $user): void
8282
}
8383

8484
$discord = DiscordApi::make();
85-
$success = $discord->removeMaxRole($user->discord_id);
85+
$success = $discord->removeUltraRole($user->discord_id);
8686

8787
if ($success) {
8888
$user->update(['discord_role_granted_at' => null]);
8989

90-
Log::info('Discord Max role revoked for user', [
90+
Log::info('Discord Ultra role revoked for user', [
9191
'user_id' => $user->id,
9292
'discord_id' => $user->discord_id,
9393
]);
9494
} else {
95-
Log::warning('Failed to revoke Discord Max role for user', [
95+
Log::warning('Failed to revoke Discord Ultra role for user', [
9696
'user_id' => $user->id,
9797
'discord_id' => $user->discord_id,
9898
]);

app/Listeners/StripeWebhookReceivedListener.php

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
use App\Jobs\CreateUserFromStripeCustomer;
66
use App\Jobs\HandleInvoicePaidJob;
7-
use App\Jobs\RemoveDiscordMaxRoleJob;
7+
use App\Jobs\RemoveDiscordUltraRoleJob;
88
use App\Jobs\RevokeTeamUserAccessJob;
99
use App\Jobs\SuspendTeamJob;
1010
use App\Jobs\UnsuspendTeamJob;
@@ -75,7 +75,7 @@ private function handleSubscriptionDeleted(WebhookReceived $event): void
7575
return;
7676
}
7777

78-
$this->removeDiscordRoleIfNoMaxLicense($user);
78+
$this->removeDiscordRoleIfNoAccess($user);
7979

8080
dispatch(new SuspendTeamJob($user->id));
8181
dispatch(new RevokeTeamUserAccessJob($user->id));
@@ -101,7 +101,7 @@ private function handleSubscriptionUpdated(WebhookReceived $event): void
101101
}
102102

103103
if (in_array($status, ['canceled', 'unpaid', 'past_due', 'incomplete_expired'])) {
104-
$this->removeDiscordRoleIfNoMaxLicense($user);
104+
$this->removeDiscordRoleIfNoAccess($user);
105105
dispatch(new SuspendTeamJob($user->id));
106106
dispatch(new RevokeTeamUserAccessJob($user->id));
107107
}
@@ -112,16 +112,16 @@ private function handleSubscriptionUpdated(WebhookReceived $event): void
112112
}
113113
}
114114

115-
private function removeDiscordRoleIfNoMaxLicense(User $user): void
115+
private function removeDiscordRoleIfNoAccess(User $user): void
116116
{
117117
if (! $user->discord_id) {
118118
return;
119119
}
120120

121-
if ($user->hasMaxAccess()) {
121+
if ($user->hasMaxAccess() || $user->hasUltraAccess()) {
122122
return;
123123
}
124124

125-
dispatch(new RemoveDiscordMaxRoleJob($user));
125+
dispatch(new RemoveDiscordUltraRoleJob($user));
126126
}
127127
}

app/Livewire/DiscordAccessBanner.php

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ class DiscordAccessBanner extends Component
1010
{
1111
public bool $inline = false;
1212

13-
public bool $hasMaxRole = false;
13+
public bool $hasUltraRole = false;
1414

1515
public bool $hasEarlyAdopterRole = false;
1616

@@ -27,7 +27,7 @@ public function checkRoleStatus(): void
2727
$user = auth()->user();
2828

2929
if (! $user || ! $user->discord_id) {
30-
$this->hasMaxRole = false;
30+
$this->hasUltraRole = false;
3131
$this->hasEarlyAdopterRole = false;
3232
$this->isGuildMember = false;
3333

@@ -41,16 +41,16 @@ public function checkRoleStatus(): void
4141

4242
return [
4343
'isGuildMember' => $discord->isGuildMember($user->discord_id),
44-
'hasMaxRole' => $discord->hasMaxRole($user->discord_id),
44+
'hasUltraRole' => $discord->hasUltraRole($user->discord_id),
4545
'hasEarlyAdopterRole' => $discord->hasEarlyAdopterRole($user->discord_id),
4646
];
4747
});
4848

4949
$this->isGuildMember = $status['isGuildMember'];
50-
$this->hasMaxRole = $status['hasMaxRole'];
50+
$this->hasUltraRole = $status['hasUltraRole'];
5151
$this->hasEarlyAdopterRole = $status['hasEarlyAdopterRole'];
5252

53-
if ($this->hasMaxRole && ! $user->discord_role_granted_at) {
53+
if ($this->hasUltraRole && ! $user->discord_role_granted_at) {
5454
$user->update(['discord_role_granted_at' => now()]);
5555
}
5656

@@ -70,7 +70,7 @@ public function refreshStatus(): void
7070
$this->checkRoleStatus();
7171
}
7272

73-
public function requestMaxRole(): void
73+
public function requestUltraRole(): void
7474
{
7575
$user = auth()->user();
7676

@@ -80,8 +80,8 @@ public function requestMaxRole(): void
8080
return;
8181
}
8282

83-
if (! $user->hasMaxAccess()) {
84-
session()->flash('error', 'You need an active Max license to receive the Max role.');
83+
if (! $user->hasMaxAccess() && ! $user->hasUltraAccess()) {
84+
session()->flash('error', 'You need an active Max license or Ultra subscription to receive the Ultra role.');
8585

8686
return;
8787
}
@@ -94,15 +94,15 @@ public function requestMaxRole(): void
9494
return;
9595
}
9696

97-
$success = $discord->assignMaxRole($user->discord_id);
97+
$success = $discord->assignUltraRole($user->discord_id);
9898

9999
if ($success) {
100100
$user->update(['discord_role_granted_at' => now()]);
101101
Cache::forget("discord_role_status_{$user->id}");
102102
$this->checkRoleStatus();
103-
session()->flash('success', 'Max role assigned successfully!');
103+
session()->flash('success', 'Ultra role assigned successfully!');
104104
} else {
105-
session()->flash('error', 'Failed to assign Max role. Please try again later.');
105+
session()->flash('error', 'Failed to assign Ultra role. Please try again later.');
106106
}
107107
}
108108

app/Support/DiscordApi.php

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ class DiscordApi
1212
public function __construct(
1313
private ?string $botToken,
1414
private ?string $guildId,
15-
private ?string $maxRoleId,
15+
private ?string $ultraRoleId,
1616
private ?string $earlyAdopterRoleId
1717
) {}
1818

@@ -21,7 +21,7 @@ public static function make(): static
2121
return new static(
2222
config('services.discord.bot_token', ''),
2323
config('services.discord.guild_id', ''),
24-
config('services.discord.max_role_id', ''),
24+
config('services.discord.ultra_role_id', ''),
2525
config('services.discord.early_adopter_role_id', '')
2626
);
2727
}
@@ -71,19 +71,19 @@ public function isGuildMember(string $discordUserId): bool
7171
return true;
7272
}
7373

74-
public function assignMaxRole(string $discordUserId): bool
74+
public function assignUltraRole(string $discordUserId): bool
7575
{
76-
return $this->assignRole($discordUserId, $this->maxRoleId, 'Max');
76+
return $this->assignRole($discordUserId, $this->ultraRoleId, 'Ultra');
7777
}
7878

79-
public function removeMaxRole(string $discordUserId): bool
79+
public function removeUltraRole(string $discordUserId): bool
8080
{
81-
return $this->removeRole($discordUserId, $this->maxRoleId, 'Max');
81+
return $this->removeRole($discordUserId, $this->ultraRoleId, 'Ultra');
8282
}
8383

84-
public function hasMaxRole(string $discordUserId): bool
84+
public function hasUltraRole(string $discordUserId): bool
8585
{
86-
return $this->hasRole($discordUserId, $this->maxRoleId);
86+
return $this->hasRole($discordUserId, $this->ultraRoleId);
8787
}
8888

8989
public function assignEarlyAdopterRole(string $discordUserId): bool

config/services.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@
5656
'redirect' => env('APP_URL').'/auth/discord/callback',
5757
'bot_token' => env('DISCORD_BOT_TOKEN'),
5858
'guild_id' => env('DISCORD_GUILD_ID'),
59-
'max_role_id' => env('DISCORD_MAX_ROLE_ID'),
59+
'ultra_role_id' => env('DISCORD_ULTRA_ROLE_ID'),
6060
'early_adopter_role_id' => env('DISCORD_EARLY_ADOPTER_ROLE_ID'),
6161
],
6262

resources/views/livewire/customer/integrations.blade.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929
<div class="mt-4 prose dark:prose-invert prose-sm max-w-none">
3030
<ul class="list-disc list-inside space-y-2">
3131
<li><strong>GitHub:</strong> Max license holders can access the private <code>nativephp/mobile</code> repository. Plugin Dev Kit license holders and Ultra subscribers can access <code>nativephp/claude-code</code>.</li>
32-
<li><strong>Discord:</strong> Max license holders receive a special "Max" role in the NativePHP Discord server. Early Access Program customers receive the "Early Adopter" role.</li>
32+
<li><strong>Discord:</strong> Max license holders and Ultra subscribers receive a special "Ultra" role in the NativePHP Discord server. Early Access Program customers receive the "Early Adopter" role.</li>
3333
</ul>
3434
<p class="mt-4">
3535
Need help? Join our <a href="https://discord.gg/nativephp" target="_blank" class="text-blue-600 hover:underline dark:text-blue-400">Discord community</a>.
@@ -47,7 +47,7 @@
4747
<livewire:git-hub-access-banner :inline="true" />
4848
@endif
4949

50-
@if(auth()->user()->isEapCustomer())
50+
@if(auth()->user()->hasMaxAccess() || auth()->user()->hasUltraAccess() || auth()->user()->isEapCustomer())
5151
<livewire:discord-access-banner :inline="true" />
5252
@endif
5353
</div>

0 commit comments

Comments
 (0)