Skip to content

feat(bot): add /ticket-setup for support category and agent role - #1863

Merged
LucasSantana-Dev merged 4 commits into
LucasSantana-Dev:mainfrom
Adolanium:feat/1804-ticket-setup
Jul 27, 2026
Merged

LucasSantana-Dev merged 4 commits into
LucasSantana-Dev:mainfrom
Adolanium:feat/1804-ticket-setup

Conversation

@Adolanium

@Adolanium Adolanium commented Jul 21, 2026 •

Copy link
Copy Markdown
Contributor

Description

Tickets (/ticket open) already read supportCategoryId and supportAgentRoleId from GuildSettings, but there was no first-class way to set them. Admins had to use the generic settings API.

Fix

  • New ManageGuild-gated /ticket-setup command with set, clear, and show subcommands (same shape as /djrole).
  • set takes a category channel and an agent role, then persists both via guildSettingsService.setGuildSettings.
  • Unconfigured /ticket open error now points admins at /ticket-setup set.

Verification

  • Unit tests for set/clear/show and requireGuild early return.
  • Pattern matches the existing /djrole command and GuildSettings fields already used by /ticket.

Checklist

  • Tests pass locally (ticket-setup.spec.ts added; ticket open copy still matches existing assertion)
  • CHANGELOG.md updated (if user-facing)
  • TypeScript builds cleanly (no shared type changes)

Destructive / irreversible interaction (Tier A)

Not applicable. No Discord message deletion, bans, or other destructive actions.

Feature-removal sweep

Not applicable. Additive command only.

Fixes #1804


Summary by cubic

Adds a ManageGuild-gated /ticket-setup command to configure the ticket category and agent role with strong privacy guards. /ticket open now directs admins to /ticket-setup set. Fixes #1804.

  • New Features

    • Added /ticket-setup with set, clear, and show to manage support category and agent role.
  • Bug Fixes

    • Prevent public tickets: reject @everyone and managed roles; require Manage Channels in the chosen category; clear error when the bot member is uncached.
    • Ensure /ticket open never grants @everyone in overwrites so stale configs stay private.
    • Clearing settings writes SQL NULL so tickets and the DJ role can be disabled; log guildId when settings writes fail.

Written for commit 817b856. Summary will update on new commits.

Review in cubic

Summary by CodeRabbit

  • New Features
    • Added /ticket-setup with set, clear, and show to configure support ticket category and agent role.
  • Bug Fixes
    • Ticket setup now validates agent role (rejects @everyone and managed/integration roles) and checks required category permissions.
    • Updated ticket “not configured” messaging to direct admins to /ticket-setup set.
    • Ensured clearing settings persists explicit null values (tickets and DJ role), and prevented agent overwrites from impacting @everyone.
  • Tests
    • Expanded coverage for /ticket-setup, guild settings null/error handling, /ticket open, and DJ role clearing.

@github-actions github-actions Bot added the bot label Jul 21, 2026
@coderabbitai

coderabbitai Bot commented Jul 21, 2026 •

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Adds /ticket-setup with set, clear, and show subcommands. Guild settings now distinguish omitted fields from explicit null clearing, with related ticket permission, DJ role, logging, and test updates.

Changes

Ticket setup configuration

Layer / File(s) Summary
Nullable settings persistence
packages/shared/src/services/GuildSettingsService.ts, packages/shared/src/services/index.ts, packages/bot/src/functions/music/commands/djrole.ts, packages/shared/src/services/GuildSettingsService.spec.ts, packages/bot/src/functions/music/commands/djrole.spec.ts
Guild settings patches preserve explicit null values in upsert payloads, log guild context on failures, and clear DJ roles with null.
Ticket setup command flow
packages/bot/src/functions/general/commands/ticket-setup.ts, packages/bot/src/functions/general/commands/ticket.ts
Adds the ManageGuild-gated set, clear, and show subcommands, validates roles and category permissions, and updates ticket setup guidance and overwrites.
Command and persistence validation
packages/bot/src/functions/general/commands/ticket-setup.spec.ts, packages/bot/src/functions/general/commands/ticket.spec.ts
Tests command metadata, guild validation, setting and clearing behavior, persistence failures, displayed configuration states, and protection against granting access to @everyone.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Admin
  participant TicketSetupCommand
  participant GuildSettingsService
  participant DiscordInteraction
  Admin->>TicketSetupCommand: Run set, clear, or show
  TicketSetupCommand->>GuildSettingsService: Persist or fetch guild settings
  GuildSettingsService-->>TicketSetupCommand: Return result or current settings
  TicketSetupCommand->>DiscordInteraction: Send ephemeral status embed
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Out of Scope Changes check ⚠️ Warning The PR also changes the unrelated music djrole command files, which are outside the ticket-setup feature scope. Move the djrole updates to a separate PR unless they are required for the ticket-setup feature.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed The PR adds a ManageGuild-gated /ticket-setup command that lets admins configure the support category and agent role, matching #1804.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: adding the /ticket-setup bot command for configuring support category and agent role.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

@cubic-dev-ai cubic-dev-ai Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

All reported issues were addressed across 3 files

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread packages/bot/src/functions/general/commands/ticket-setup.ts Outdated
@LucasSantana-Dev

Copy link
Copy Markdown
Owner

Kimi review (kimi-code/kimi-for-coding, via local subscription)

• VERDICT: ISSUES

  • [SEVERITY medium] packages/shared/src/services/GuildSettingsService.ts:setGuildSettings — The catch block returns false without logging the underlying DB error. With ticket-setup and djrole now relying on this boolean to show user-facing errors, production failures become invisible to operators. Fix: log the error (e.g., logger.error({ err, guildId }, 'setGuildSettings failed')) before returning false.

Head 7bdccb3. Posted by kimi-review-watch (launchd).

@Adolanium

Copy link
Copy Markdown
Contributor Author

Addressed in 29df7b1. setGuildSettings now logs the original database error with the guild ID before returning false. The focused GuildSettingsService suite passes with 37 tests. The branch is up to date with main.

@LucasSantana-Dev LucasSantana-Dev left a comment •

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

Really solid PR. The null versus undefined split is a genuine bug fix, not just tidying: GuildSettingsPatch narrows exactly the three nullable columns, the comment on djRoleId: null explains why it matters so the next person doesn't undo it, and writes null for nullable columns so clearers can disable features locks the behaviour in. Gating on ManageGuild and adding the guild id to the errorLog context are both right. I checked that toPrismaData already had copy('supportCategoryId') and copy('supportAgentRoleId') rather than assuming it, and it does, so the new columns persist correctly.

There's one thing I need fixed before this can go in.

/ticket-setup set role:@everyone makes every ticket public

The set branch takes any role with no validation:

const role = interaction.options.getRole('role', true)
await guildSettingsService.setGuildSettings(guildId, {
    supportCategoryId: category.id,
    supportAgentRoleId: role.id,
})

Discord's role picker includes @everyone. That id then reaches buildTicketOverwrites (packages/bot/src/functions/general/commands/ticket.ts:156):

return [
    { id: guild.roles.everyone.id, deny: [PermissionFlagsBits.ViewChannel] },
    { id: requestorId, allow },
    { id: agentRoleId, allow },   // allow = ViewChannel, SendMessages, ReadMessageHistory
]

A channel carries at most one overwrite per target id, so when agentRoleId is the everyone role the third entry replaces the first. The deny is gone and @everyone gets ViewChannel, SendMessages and ReadMessageHistory on every ticket created from that point on. Tickets are exactly where people paste account details and complaints about other members, so that's the worst channel in the server to accidentally open up.

I don't think this is an exotic mistake either. @everyone sits at the top of the role list and the option description ("Role granted access to every open ticket") reads like it could plausibly be the right answer.

Rejecting it in set is enough:

if (role.id === interaction.guildId) {
    // @everyone would make every ticket world-readable
    return
}

Worth considering whether buildTicketOverwrites should defend itself too, since a stored value can predate this command, but the command-level guard is the part I'd want in this PR.

Two optional extras while you're in there:

  • role.managed roles can't be assigned to members, so picking one produces a config that grants access to nobody and fails silently. A check with a clear error would save someone an afternoon.
  • set stores category.id without checking the bot has ManageChannels there. Today that surfaces much later at /ticket open as a channel-creation error with nothing pointing back at setup. Probing at configure time turns it into an immediate, actionable message.

On the red checks: not yours. Security was an unpassable repo-wide gate, kimi-review fails on every PR because an API key isn't set (#1877), and the npm ci errors come from a stale lockfile on main. Fixed in #1876; rebase once it lands.

@LucasSantana-Dev

Copy link
Copy Markdown
Owner

Heads-up: the CI blockers I mentioned are fixed on main now (#1876, merged as ca410d2c).

What that clears:

  • Security — it ran npm audit --audit-level=high across devDependencies and could never pass, which is why it was red on every PR including this one. It now audits production deps only, with an explicit allowlist pinned to individual advisory ids.
  • npm ci / Build — shared / Quality Gates — main's lockfile was stale and npm@12's stricter ci rejected it (lock file's eslint@10.7.0 does not satisfy eslint@10.8.0). Resynced.
  • compressed-size — it installs the base branch too, so it was failing on main's lockfile rather than on your changes.
  • kimi-review — removed in ci: remove kimi-review until an api key is set #1884; the API key was never set, so it failed on every PR in the repo.

Please rebase onto main and push. The auto-update workflow only touches branches whose authors enabled auto-merge, so it deliberately won't rewrite yours. A rebase should turn the checks green without any change to your code.

The review feedback above is separate and still stands.

@Adolanium
Adolanium force-pushed the feat/1804-ticket-setup branch from a6fb6ac to f371aa3 Compare July 27, 2026 03:45
@Adolanium

Adolanium commented Jul 27, 2026 •

Copy link
Copy Markdown
Contributor Author

Addressed in 94342a0 (squashed, rebased onto main).

  • /ticket-setup set rejects @everyone (role id === guild id) with a clear error.
  • Managed integration roles are rejected the same way.
  • Setup fails early if the bot lacks Manage Channels in the chosen category.
  • buildTicketOverwrites also skips the agent allow entry when the stored id is @everyone, so a stale config cannot open tickets to the whole server.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 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 `@packages/bot/src/functions/general/commands/ticket-setup.ts`:
- Around line 106-126: Update the permission validation around
interaction.guild.members.me so an unresolved bot member is handled as a setup
failure rather than bypassing the check. Require a resolved me member and verify
it has ManageChannels in categoryIdForPerms before proceeding; otherwise use the
existing interactionReply error path and return.
🪄 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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 22dea0e2-5df1-43db-b579-7c79837e6c37

📥 Commits

Reviewing files that changed from the base of the PR and between a6fb6ac and f371aa3.

📒 Files selected for processing (8)
  • packages/bot/src/functions/general/commands/ticket-setup.spec.ts
  • packages/bot/src/functions/general/commands/ticket-setup.ts
  • packages/bot/src/functions/general/commands/ticket.ts
  • packages/bot/src/functions/music/commands/djrole.spec.ts
  • packages/bot/src/functions/music/commands/djrole.ts
  • packages/shared/src/services/GuildSettingsService.spec.ts
  • packages/shared/src/services/GuildSettingsService.ts
  • packages/shared/src/services/index.ts
🚧 Files skipped from review as they are similar to previous changes (5)
  • packages/shared/src/services/index.ts
  • packages/bot/src/functions/music/commands/djrole.ts
  • packages/bot/src/functions/music/commands/djrole.spec.ts
  • packages/shared/src/services/GuildSettingsService.spec.ts
  • packages/shared/src/services/GuildSettingsService.ts

Comment thread packages/bot/src/functions/general/commands/ticket-setup.ts
@Adolanium
Adolanium force-pushed the feat/1804-ticket-setup branch from f371aa3 to 94342a0 Compare July 27, 2026 03:51

@LucasSantana-Dev LucasSantana-Dev left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

Review: approve with nits

The command is correct, well-guarded, and auto-registers cleanly.

P2 — missing test for the new overwrite guard: ticket.ts:166-178 adds the agentRoleId !== guild.roles.everyone.id guard in buildTicketOverwrites, and the PR summary advertises "ensure overwrites never grant @everyone" as a bug fix, but only the setup-side rejection is tested. The defense-in-depth branch for stale configs (set via the generic settings API before this guard existed) is exercised by nothing; a regression here silently re-opens the public-ticket hole. Suggest a ticket.spec.ts case with agentRoleId === guild.roles.everyone.id asserting only 2 overwrites are produced.

P3 (nits): in the set branch, when interaction.guild?.members.me is uncached (me is null), the Manage Channels pre-check is silently skipped and the config is saved anyway — not a failure since /ticket open catches the missing permission at channel-create time (ticket.ts:82-92), but a warning would be friendlier. Also const categoryIdForPerms = category.id is a one-use alias; inline it.

What's good: the djRoleId: undefined → null fix in djrole.ts:73 repairs a real latent bug — toPrismaData strips undefined (GuildSettingsService.ts:158), so /djrole clear previously never cleared the column — and it ships with a dedicated null-write test. The @everyone guard uses the correct Discord identity (role.id === guildId). Registration needs no plumbing: getCommandFiles auto-discovers the new file while excluding *.spec.*.

Adds set/clear/show for support category and agent role, with null (not
undefined) clears so tickets can actually be disabled. Logs guild settings
write failures. Rejects @everyone and managed roles, checks Manage Channels
at configure time, and defends buildTicketOverwrites against a world-readable
agent overwrite.
@Adolanium
Adolanium force-pushed the feat/1804-ticket-setup branch from 94342a0 to f986646 Compare July 27, 2026 16:19
@Adolanium

Copy link
Copy Markdown
Contributor Author

Addressed the nits.

  • ticket.spec.ts covers the defense-in-depth branch: when supportAgentRoleId is @everyone, open creates only 2 overwrites (everyone deny + requestor allow). No agent allow entry.
  • set now fails closed if members.me is uncached, instead of skipping the Manage Channels check.
  • Inlined the one-use categoryIdForPerms alias.

Rebased onto main.

LucasSantana-Dev added a commit that referenced this pull request Jul 27, 2026
## Summary

Two structural failures block every fork PR's required checks (seen on
#1863, #1864, #1865, #1866, #1867, #1674 after their CI was approved):

- **SonarCloud Scan (required) hard-fails on forks**: fork PRs get no
secrets, so `SONAR_TOKEN` is never present and the token-policy step
exits 1. Now the sonar job is skipped for fork PRs (a skipped required
check counts as passing). Same pattern deploy-staging already uses.
- **danger 403s on forks**: `review-tools.yml` ran on `pull_request`,
where the fork token is forced read-only and the comment POST fails with
403. Switched to `pull_request_target`; the reusable workflow checks out
and executes base-repo code only (documented in the file header, same
safety rule as the other target workflows).

## Test plan
- [x] actionlint clean on both files
- [ ] Next push to an external contributor PR: SonarCloud Scan shows
skipped, danger posts its comment

After merge I will update the seven open contributor branches to main so
they pick this up.

<!-- This is an auto-generated description by cubic. -->
---
## Summary by cubic
Unblocks fork PRs by fixing CI gates for SonarCloud and `danger`. Fork
PRs now pass required checks without secrets and get review comments.

- Bug Fixes
- Skip SonarCloud Scan on fork PRs to avoid failing when `SONAR_TOKEN`
is unavailable (skipped required check counts as passing).
- Run review tools on `pull_request_target` so `danger` can comment on
forks; workflow executes base-repo code only.

<sup>Written for commit 316f567.
Summary will update on new commits.</sup>

<a
href="https://cubic.dev/pr/LucasSantana-Dev/Lucky/pull/1898?utm_source=github"
target="_blank" rel="noopener noreferrer"
data-no-image-dialog="true"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img
alt="Review in cubic"
src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a>

<!-- End of auto-generated description by cubic. -->
@github-actions

github-actions Bot commented Jul 27, 2026 •

Copy link
Copy Markdown
Warnings
⚠️

User-facing change without a CHANGELOG.md update. Add a line under ## [Unreleased] if this should appear in release notes. (Or apply the skip-changelog label if this PR does not affect end users.)

Generated by 🚫 dangerJS against 817b856

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 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 `@packages/bot/src/functions/general/commands/ticket.spec.ts`:
- Around line 132-140: Strengthen the permission assertion in the ticket command
test by checking the “everyone” overwrite’s deny collection contains
PermissionFlagsBits.ViewChannel and its allow collection does not contain that
flag. Replace the current defined-only assertion while preserving the existing
overwrite count and ID checks.
🪄 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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 8596e0fa-f7c9-41ef-b986-b8650080a36f

📥 Commits

Reviewing files that changed from the base of the PR and between f371aa3 and f91d7aa.

📒 Files selected for processing (9)
  • packages/bot/src/functions/general/commands/ticket-setup.spec.ts
  • packages/bot/src/functions/general/commands/ticket-setup.ts
  • packages/bot/src/functions/general/commands/ticket.spec.ts
  • packages/bot/src/functions/general/commands/ticket.ts
  • packages/bot/src/functions/music/commands/djrole.spec.ts
  • packages/bot/src/functions/music/commands/djrole.ts
  • packages/shared/src/services/GuildSettingsService.spec.ts
  • packages/shared/src/services/GuildSettingsService.ts
  • packages/shared/src/services/index.ts
🚧 Files skipped from review as they are similar to previous changes (8)
  • packages/bot/src/functions/music/commands/djrole.ts
  • packages/bot/src/functions/music/commands/djrole.spec.ts
  • packages/shared/src/services/index.ts
  • packages/bot/src/functions/general/commands/ticket.ts
  • packages/shared/src/services/GuildSettingsService.spec.ts
  • packages/shared/src/services/GuildSettingsService.ts
  • packages/bot/src/functions/general/commands/ticket-setup.spec.ts
  • packages/bot/src/functions/general/commands/ticket-setup.ts

Comment thread packages/bot/src/functions/general/commands/ticket.spec.ts
@LucasSantana-Dev

Copy link
Copy Markdown
Owner

A note from the maintainer side: sorry this PR waited as long as it did for a proper review, and sorry for the rounds of branch updates and re-running checks today. The churn was on our side, not yours.

Your PRs exposed real gaps in how this repo handled external contributions: CI runs sat in a silent approval queue, some gates could never pass on fork PRs (SonarCloud, danger), and the team had no notification when external PRs arrived. Those are all fixed as of today:

  • CI auto-approves for returning contributors, no more waiting on a manual click.
  • All gates now pass on fork PRs (SonarCloud skips gracefully without a token, danger runs and comments correctly).
  • The team gets notified the moment an external PR is opened.

Your branch is up to date and the full suite is green. Thanks for the patience and for the contribution. External contributors are very welcome here.

@LucasSantana-Dev
LucasSantana-Dev merged commit 3f4af39 into LucasSantana-Dev:main Jul 27, 2026
43 checks passed
LucasSantana-Dev added a commit that referenced this pull request Jul 27, 2026
🤖 I have created a release *beep* *boop*
---


<details><summary>2.38.0</summary>

##
[2.38.0](v2.37.3...v2.38.0)
(2026-07-27)


### Features

* **bot:** add /ticket-setup for support category and agent role
([#1863](#1863))
([3f4af39](3f4af39))
* **frontend:** per-action loading and connection gating on music
controls
([#1866](#1866))
([2dda60f](2dda60f))
* **frontend:** show stale progress when music SSE lags
([#1867](#1867))
([4952e73](4952e73))
* **music:** surface recommendationReason in nowplaying and queue
([#1864](#1864))
([960fd62](960fd62))
* **ops:** blue/green zero-downtime deploys — Phase 1 web tier
([#1786](#1786))
([f5f7597](f5f7597))


### Bug Fixes

* **docker:** make compose stack boot from a fresh .env
([#1674](#1674))
([babe0ef](babe0ef))
* **docker:** treat an empty db password as missing in compose guards
([#1881](#1881))
([718c0ad](718c0ad))
* **frontend:** make landing page usable at mobile widths
([#1865](#1865))
([6190350](6190350))
* **frontend:** stop hero grid columns overflowing on narrow viewports
([#1874](#1874))
([ce5cea0](ce5cea0))
* **invite:** add /invite where cloudflare pages reads it
([#1895](#1895))
([0528f66](0528f66))
</details>

---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).
This was referenced Oct 1, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat(bot): /ticket-setup (or dashboard field) for support category + agent role

2 participants