π« Tickets & Recruitment¶
Magikal has a panel-based ticket system for Support and Recruitment.
It is designed to keep user-facing flows simple while still giving staff proper logs, private channels, role handling, and permanent recruitment records.
π§ What this system does¶
| Area | What it does |
|---|---|
| π Support tickets | Lets users open a private support channel from a panel button |
| π Recruitment tickets | Runs a guided application flow for Member/Affiliate applicants |
| β Quick Q&A | Collects extra recruitment answers through a modal |
| π§Ύ Permanent records | Posts long-term recruitment application records to a configured channel |
| β Staff decisions | Supports approve, decline, close, and finalise actions |
| π Staff pings | Pings configured support/recruitment staff roles |
| π°οΈ RSI integration | Can show RSI profile/org info and optionally sync nickname on approve |
Live source
This page is based on the current live files:
bot/cogs/tickets.pybot/cogs/recruit_config.pybot/storage/pg_config.pybot/database.py
π Support tickets¶
Support tickets are opened from a static support panel.
The user clicks Open Support Ticket, fills in an opening question, and the bot creates a private ticket channel.
Support flow¶
User clicks Support panel
β
Bot opens modal for opening question
β
Bot creates private ticket channel
β
Optional support role is pinged
β
Staff resolve the ticket
β
Bot logs the result and deletes the channel
Support controls¶
| Control | Who uses it | Purpose |
|---|---|---|
| Open Support Ticket | Members | Opens a private support ticket |
| Resolve (Staff) | Staff with Manage Channels | Opens a resolution modal, logs the result, then closes/deletes the ticket |
Support ticket channels are deleted
Support ticket channels are deleted when resolved.
Users should save anything important before staff close the ticket.
π Recruitment tickets¶
Recruitment tickets are more detailed than support tickets.
They are used for Member and Affiliate applications and include a guided application wizard, Q&A, staff review, RSI acceptance, and finalisation.
Recruitment flow¶
Applicant clicks Recruitment panel
β
Private recruitment channel is created
β
Applicant completes Step 1
β
Applicant completes Quick Q&A
β
Staff review the application
β
Staff approve or decline
β
If approved: applicant applies/gets accepted on RSI
β
Staff finalise the ticket
π§© Recruitment Step 1¶
When a recruitment ticket opens, the applicant completes Step 1 inside the private ticket channel.
They select:
| Field | Purpose |
|---|---|
| Member or Affiliate | Decides which org role they are applying for |
| Main area/focus | Example: medical, salvage, logistics, general |
| Time zone | Used to understand availability |
| RSI handle | Used for RSI lookup and optional nickname sync |
| Short intro/notes | Free text from the applicant |
The available area/focus options are managed by staff with /tickets add-option, /tickets remove-option, and /tickets list-options.
Keep options simple
Good recruitment options are short and obvious.
Examples: medical, salvage, logistics, security, industrial, general.
β Quick Q&A¶
After Step 1, the bot posts a Quick Q&A button in the applicantβs private ticket.
The applicant clicks Answer Q&A and fills in the configured questions.
Default Q&A questions¶
If no custom Q&A is configured, the bot falls back to a safe default set:
| Question | Type |
|---|---|
| Strengths | Keywords |
| Availability | Time range |
| Voice comms / mic ready | Yes/No |
| Anything else we should know | Paragraph |
Q&A result¶
When Q&A is submitted, the bot:
| Action | Result |
|---|---|
| Saves answers | Merges them into the recruitment application payload |
| Updates private ticket | Shows Q&A as completed |
| Updates permanent record | Marks the central embed as Q&A received |
| Removes the active button | The Q&A tile becomes completed |
Timezone handling
Availability is asked in the applicantβs local time.
The bot also stores a UK-time version where possible, using the selected time zone.
π§Ύ Permanent recruitment records¶
Recruitment applications can be posted to a configured permanent record channel.
This is separate from the temporary private ticket channel.
The permanent record contains:
| Field | Purpose |
|---|---|
| Status | Pending, Q&A received, Approved, Declined, Finalised, Removed |
| Applicant | Discord applicant mention |
| Requested role | Member or Affiliate |
| Area/focus | Selected recruitment option |
| RSI handle | Applicantβs RSI handle |
| Ticket link | Link to the private ticket while it exists |
| Time zone | Local/UTC offset info |
| Notes/Q&A | Applicant notes and Q&A answers |
Permanent record matters
The private ticket channel may be deleted, but the permanent recruitment record remains.
This is the main place staff should use for long-term application tracking.
β Approve, decline, and finalise¶
Recruitment uses a two-step approval model.
Approve¶
When staff click Approve, the bot:
| Step | Result |
|---|---|
| Assigns role | Uses AutoRoles to grant Member/Affiliate role |
| Updates logs | Sends an action log |
| Updates record | Marks the application as approved |
| Keeps ticket open | The channel remains open for RSI acceptance |
| Posts finalise control | Staff get a Finalize β RSI accepted button |
Approve does not delete the ticket
Approval is only Step 1.
The ticket stays open until staff confirm the RSI-side acceptance and click Finalize β RSI accepted.
Decline¶
When staff click Reject, the bot asks for a required decline reason.
The bot then:
| Step | Result |
|---|---|
| Saves reason | Stores the staff reason on the permanent record |
| Updates status | Marks the application as declined |
| Logs action | Sends an action log |
| Deletes channel | Closes and deletes the private ticket |
| DMs applicant | Sends a generic rejection DM |
Decline reason
The decline reason is saved on the permanent embed.
The applicant DM is generic and does not include sensitive internal notes.
Finalise¶
When staff click Finalize β RSI accepted, the bot:
| Step | Result |
|---|---|
| Updates permanent record | Marks the application as finalised |
| Logs action | Sends a finalisation log |
| Closes ticket | Deletes the private ticket channel |
π Staff pings¶
Ticket pings are configured separately for support and recruitment.
| Command | Purpose |
|---|---|
/tickets set-ping |
Set support and recruitment ping roles |
/tickets show-ping |
Show current ping roles |
The ping roles are also granted access to the private ticket channels.
π Panel channels and ticket categories¶
Ticket panels and ticket creation categories are configured with:
/tickets set-targets
This command can set:
| Target | Purpose |
|---|---|
| Support panel channel | Where the Support panel is posted |
| Recruitment panel channel | Where the Recruitment panel is posted |
| Support category | Where support ticket channels are created |
| Recruitment category | Where recruitment ticket channels are created |
The command also posts fresh panel messages and tries to remove older duplicate panel messages.
Use this to check current setup:
/tickets show-targets
Panel target behaviour
/tickets set-targets is not just a config command.
It also posts/refreshes the public support and recruitment panel messages.
π°οΈ RSI org settings¶
Recruitment embeds can include a configured RSI org page and image.
| Command | Purpose |
|---|---|
/tickets set-org |
Set RSI org URL and optional image |
/tickets show-org |
Show current RSI org settings |
The recruitment panel can include an Apply on RSI button when an org URL is configured.
𧬠RSI nickname sync¶
Nickname sync is controlled by:
/tickets set-nick-sync enabled:true
When enabled, approval attempts to set the memberβs nickname from their RSI handle/profile.
Nickname sync depends on Discord permissions
The bot must have Manage Nicknames and must be higher than the target member in the role hierarchy.
If Discord blocks the nickname change, the application can still continue.
π¨ DM-on-close behaviour¶
DM-on-close is controlled by:
/tickets set-dm-on-close enabled:true
Current behaviour:
| Ticket type | DM behaviour |
|---|---|
| Support | Uses the configured DM-on-close setting |
| Recruitment declined | Sends a generic rejection DM |
| Recruitment finalised/closed | No DM |
π οΈ Admin command summary¶
Ticket setup commands¶
| Command | Purpose |
|---|---|
/tickets set-targets |
Set panel channels and ticket categories |
/tickets show-targets |
Show current panel/category setup |
/tickets set-ping |
Set support/recruitment ping roles |
/tickets show-ping |
Show current ping roles |
/tickets set-dm-on-close |
Enable/disable support close DMs |
/tickets set-nick-sync |
Enable/disable RSI nickname sync |
/tickets set-org |
Set RSI org URL/image |
/tickets show-org |
Show RSI org URL/image |
/tickets post-support-about |
Post a static support explainer embed |
Recruitment option commands¶
| Command | Purpose |
|---|---|
/tickets add-option |
Add a selectable recruitment area |
/tickets remove-option |
Remove a selectable recruitment area |
/tickets list-options |
List recruitment options and ping roles |
Q&A recovery commands¶
| Command | Purpose |
|---|---|
/tickets reopen-qna |
Repost Q&A button in the current recruitment ticket |
!reopen_qna |
Prefix fallback for reposting Q&A |
βοΈ Recruitment config commands¶
Recruitment configuration is handled by:
/recruit-config
These commands require the recruitment capability.
| Command | Purpose |
|---|---|
/recruit-config set-log-channel |
Set permanent application record channel |
/recruit-config set-access-mode |
Configure applicant access mode after approve |
/recruit-config qa-list |
Show current Q&A questions |
/recruit-config qa-add |
Add a Q&A question |
/recruit-config qa-edit |
Edit a Q&A question |
/recruit-config qa-move |
Reorder a Q&A question |
/recruit-config qa-remove |
Remove a Q&A question |
/recruit-config qa-reset-defaults |
Restore the recommended Q&A set |
/recruit-config remove-member |
Mark a member as removed and update the record |
Known access note
/recruit-config uses the recruitment capability.
Make sure the Recruitment role is correctly mapped in /roles set, otherwise intended staff may not be able to use these commands.
π§± Storage model¶
Tickets and recruitment use Postgres via PgConfigStorage.
| Data | Table/model area |
|---|---|
| Ticket rows | cfg.tickets |
| Ticket ping roles | cfg.ticket_notify_roles |
| Ticket panel/category config | cfg.ticket_panel_config |
| Recruitment options | cfg.ticket_recruitment_options |
| RSI org panel settings | cfg.recruit_panel_settings |
| Recruitment settings/Q&A | cfg.recruit_settings |
| Recruitment applications | cfg.recruit_applications |
User-related records use PIDs where stored long-term.
π Privacy and logging¶
The ticket system follows the project privacy model:
| Area | Rule |
|---|---|
| Discord embeds | Mentions and readable names are allowed for staff-facing Discord context |
| Database records | Applicant/staff identity is stored with PIDs where applicable |
| Logs | Should use structured key/value logs and avoid raw user IDs |
| Channel topics | May contain operational markers such as TID, REQ, APP, TZ, and OPENER |
Topic markers are operational
Ticket channel topics store markers used by persistent buttons after restarts.
Do not manually remove markers such as TID:, REQ:, APP:, TZ:, or OPENER: unless you know exactly why.
π©Ί Troubleshooting¶
Q&A button is missing¶
Use this in the recruitment ticket channel:
/tickets reopen-qna
or fallback:
!reopen_qna
Approve says requested role is missing¶
Check the ticket channel topic contains:
REQ:member
or:
REQ:affiliate
If the topic marker is missing, the bot may try to recover from the application record.
Finalise says Ticket ID is missing¶
Check the topic contains:
TID:<ticket_id>
APP:<app_id>
The bot has fallback recovery from the application payload, but old tickets or manually edited topics can still lose context.
Applicant cannot see ticket¶
Check:
| Item | What to check |
|---|---|
| Category permissions | Category is not blocking the user |
| Channel overwrites | Applicant has View Channel and Send Messages |
| Ping role | Recruitment/support role has access |
| Bot role | Bot can Manage Channels |
Staff cannot click controls¶
Most staff controls require Discord permissions such as:
| Control | Likely required permission |
|---|---|
| Resolve support | Manage Channels |
| Approve/reject recruitment | Manage Channels |
| Finalise recruitment | Manage Guild |
Safe edit warnings after deletion¶
The bot uses a debounced safe edit helper to reduce Discord PATCH spam.
If a ticket closes while an edit is pending, the code is designed to skip missing-channel edits instead of treating them as fatal.
β Testing checklist¶
After ticket changes, test:
- Support panel opens a private channel
- Support resolve logs and deletes the channel
- Recruitment panel opens a private channel
- Step 1 wizard works
- Q&A saves and updates both ticket and permanent record
- Approve grants the correct role
- Decline requires a reason and updates the permanent record
- Finalise updates the permanent record and deletes the channel
- Staff pings are correct
- Panel channels and categories are correct
- Logs do not expose raw Discord IDs
- Permanent record channel receives application embeds
π§ Rule¶
Ticket channels are temporary.
Recruitment records are permanent.
The bot should keep the applicant flow simple, give staff clear controls, and preserve important recruitment history in the configured record channel.