Skip to content

fix(bot-discord): guild guard and Profile ensure#303

Merged
gvieira18 merged 2 commits into
4.xfrom
fix/bot-discord-slash-command-null-guards
Jun 3, 2026
Merged

fix(bot-discord): guild guard and Profile ensure#303
gvieira18 merged 2 commits into
4.xfrom
fix/bot-discord-slash-command-null-guards

Conversation

@gvieira18
Copy link
Copy Markdown
Member

@gvieira18 gvieira18 commented Jun 3, 2026

Summary

  • Guild guard: Added maybeHandle() override in AbstractSlashCommand that rejects interactions without guild_id (DM context), responding with an ephemeral message
  • Unified hierarchy: All 6 slash commands that extended Laracord's SlashCommand directly now extend AbstractSlashCommand, so every command gets the guild guard
  • Profile::ensureExists(): Extracted firstOrCreate logic into a static method on Profile, replacing both the TenantUserObserver inline call and IntroductionCommand's firstOrFail() — fixes ModelNotFoundException for users whose TenantUser pivot predates the observer

Root cause

Slash commands were registered globally (no $guild property), making them available in DMs where $interaction->guild_id and $interaction->member are null. Additionally, syncWithoutDetaching() only fires the created pivot event for new records — users who already had a TenantUser pivot but no Profile (created before the observer existed) hit firstOrFail() crash.

Test plan

  • php artisan test --compact --filter=IntroductionCommand — 3 tests pass
  • php artisan test --compact --filter=EditProfileCommand — 4 tests pass
  • vendor/bin/pint --dirty — clean
  • vendor/bin/phpstan analyse app-modules/bot-discord/src/SlashCommands/ — 0 errors
  • Verify /apresentar works for users with existing TenantUser pivot but no Profile (e.g. andredss, garreiz)
  • Verify slash commands are rejected gracefully if somehow invoked from DM

Description

Guards Discord slash commands from DM (guild-less) invocations by adding a guild check in AbstractSlashCommand::maybeHandle(), updates six slash commands to extend the new base, and adds Profile::ensureExists() to avoid ModelNotFoundException for users whose TenantUser pivot predates the Profile observer.

References

Dependencies & Requirements

  • No new dependencies or environment/configuration changes required.

Contributor Summary

Contributor Lines Added Lines Removed Files Changed
gvieira18 35 22 9

Changes Summary

File Path Change Description
app-modules/bot-discord/src/SlashCommands/AbstractSlashCommand.php Added public override maybeHandle() to reject guild-less (DM) interactions with an ephemeral Portuguese message.
app-modules/bot-discord/src/SlashCommands/CargoDelasCommand.php Switched base class to AbstractSlashCommand (inheritance change).
app-modules/bot-discord/src/SlashCommands/CodeCommand.php Switched base class to AbstractSlashCommand (inheritance change).
app-modules/bot-discord/src/SlashCommands/DontAskCommand.php Switched base class to AbstractSlashCommand (inheritance change).
app-modules/bot-discord/src/SlashCommands/DynamicVoiceCommand.php Switched base class to AbstractSlashCommand (inheritance change).
app-modules/bot-discord/src/SlashCommands/EditVoiceChannelLimitCommand.php Switched base class to AbstractSlashCommand (inheritance change).
app-modules/bot-discord/src/SlashCommands/IntroductionCommand.php Switched base class to AbstractSlashCommand and replaced firstOrFail() profile lookup with Profile::ensureExists().
app-modules/identity/src/Tenant/Observers/TenantUserObserver.php Replaced inline firstOrCreate with Profile::ensureExists() in created() observer.
app-modules/profile/src/Models/Profile.php Added public static ensureExists(string

… and ensure Profile exists

Slash commands were registered globally, making them available in DMs
where guild_id and member are null. Added a guild guard in
AbstractSlashCommand::maybeHandle() and unified all slash commands
under AbstractSlashCommand to prevent null-access crashes.

Also extracted Profile::ensureExists() to handle users whose
TenantUser pivot predates the observer, fixing ModelNotFoundException
in IntroductionCommand::persistData().
@gvieira18 gvieira18 requested a review from a team June 3, 2026 16:59
@gvieira18 gvieira18 changed the title fix(bot-discord): guard slash commands against non-guild interactions and ensure Profile exists fix(bot-discord): guild guard and Profile ensure Jun 3, 2026
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Jun 3, 2026

Review Change Stack

Warning

Review limit reached

@gvieira18, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 13 minutes and 4 seconds. Learn how PR review limits work.

Your organization has run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository YAML (base), Central YAML (inherited)

Review profile: CHILL

Plan: Pro

Run ID: 25e5c64f-429b-48d2-b4b3-3f8f096eb2d1

📥 Commits

Reviewing files that changed from the base of the PR and between b53af72 and fb0148a.

📒 Files selected for processing (1)
  • app-modules/identity/src/Tenant/Observers/TenantUserObserver.php
📝 Walkthrough

Walkthrough

This PR centralizes Profile provisioning with a new Profile::ensureExists(userId, tenantId) helper and adds guild-id validation to AbstractSlashCommand via a maybeHandle override that rejects interactions missing guild_id. Several slash command classes are migrated to extend AbstractSlashCommand, IntroductionCommand adopts the Profile helper, and TenantUserObserver now uses the same helper for provisioning.

Possibly related PRs

  • he4rt/heartdevs.com#302: Overlaps with fixes that ensure the tenant-pivot is created in ResolveUserContext, which interacts with TenantUserObserver provisioning updated here.

Suggested reviewers

  • Clintonrocha98
  • thalesmengue
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 20.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main changes: adding a guild guard (maybeHandle override in AbstractSlashCommand) and Profile ensure (ensureExists helper method).
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@app-modules/bot-discord/src/SlashCommands/AbstractSlashCommand.php`:
- Around line 23-35: The current maybeHandle(Interaction $interaction) only
blocks DMs but still calls parent::maybeHandle(), which leads into
beforePipeline() where tenant lookup can return null and dereference
$this->tenantProvider->tenant_id; update maybeHandle (or beforePipeline) to
reject guilds that have no mapped Tenant: perform the tenant lookup for
$interaction->guild_id (same logic used in beforePipeline) and if no Tenant is
found respond with the "only usable in a server" / appropriate error and return
instead of calling parent::maybeHandle(); alternatively, add a null-check in
beforePipeline to handle a missing first() result and throw/return early to
prevent dereferencing $this->tenantProvider->tenant_id.

In `@app-modules/profile/src/Models/Profile.php`:
- Around line 59-70: ensureExists uses firstOrCreate which is not atomic and can
still trigger unique-constraint violations under concurrency; update
Profile::ensureExists to use an atomic approach (e.g., perform an upsert via
query()->updateOrCreate()/upsert() or run a transaction with an
insert-ignore/upsert) or explicitly catch the unique constraint integrity
exception around the insert and retry a select, ensuring you reference the
Profile::ensureExists method and the model's query() call when implementing the
change.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository YAML (base), Central YAML (inherited)

Review profile: CHILL

Plan: Pro

Run ID: 419472e0-4cb1-41f3-8341-e249f95dd5d7

📥 Commits

Reviewing files that changed from the base of the PR and between 2523ccb and 6085e3b.

📒 Files selected for processing (9)
  • app-modules/bot-discord/src/SlashCommands/AbstractSlashCommand.php
  • app-modules/bot-discord/src/SlashCommands/CargoDelasCommand.php
  • app-modules/bot-discord/src/SlashCommands/CodeCommand.php
  • app-modules/bot-discord/src/SlashCommands/DontAskCommand.php
  • app-modules/bot-discord/src/SlashCommands/DynamicVoiceCommand.php
  • app-modules/bot-discord/src/SlashCommands/EditVoiceChannelLimitCommand.php
  • app-modules/bot-discord/src/SlashCommands/IntroductionCommand.php
  • app-modules/identity/src/Tenant/Observers/TenantUserObserver.php
  • app-modules/profile/src/Models/Profile.php

Comment thread app-modules/bot-discord/src/SlashCommands/AbstractSlashCommand.php
Comment thread app-modules/profile/src/Models/Profile.php
in ensureExists

Pivot models pass LazyUuidFromString instead
of plain strings for user_id and tenant_id.
@gvieira18 gvieira18 force-pushed the fix/bot-discord-slash-command-null-guards branch from b53af72 to fb0148a Compare June 3, 2026 17:16
@gvieira18 gvieira18 merged commit 01f60a2 into 4.x Jun 3, 2026
6 checks passed
@gvieira18 gvieira18 deleted the fix/bot-discord-slash-command-null-guards branch June 3, 2026 17:37
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.

3 participants