Open
Conversation
…odels and commands. Add parental consent verification and role assignment for flagged users. Update configuration for minor review channels and roles.
…oles for users reaching 18, implement consent check with secure payload, and improve report handling in the UI. Update related database models and logging for better traceability.
…pers, and UI components. Implement unit tests for flagging minors, database interactions, and consent checks to ensure robust functionality and error handling.
…cation functions and enhance mock setups for channel interactions. This improves test reliability and aligns with recent code structure changes.
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #172 +/- ##
==========================================
+ Coverage 59.49% 60.88% +1.39%
==========================================
Files 50 56 +6
Lines 2903 3556 +653
==========================================
+ Hits 1727 2165 +438
- Misses 1176 1391 +215 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
…f minor roles and member join behavior. Enhance test coverage for flagging minors and minor reviewers, ensuring robust handling of parental consent and role assignments.
…o enhance test coverage and ensure proper handling of user interactions.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Types of changes
What types of changes does your code introduce?
Put an
xin the boxes that apply.Parental Consent Flow - Feature Documentation
Overview
This feature implements a comprehensive system for handling potentially underage users on the HTB Discord server. When moderators flag a verified user as potentially underage, the system creates a report that authorized reviewers can approve, deny, or check for parental consent.
The flow ensures compliance with platform policies by requiring parental consent for users under 18, with automatic ban management and role assignment.
Key Components
Commands
/flag_minor(MOD+): Flag a verified user as potentially underage/minor_reviewers(ADMIN): Manage who can review minor reportsadd <user>: Add a reviewerremove <user>: Remove a reviewerlist: Show all current reviewersseed: Initialize with a default set of reviewersDatabase Tables
minor_reportTracks each flagged user and the report lifecycle.
iduser_idreporter_idsuspected_ageevidencereport_message_idstatusreviewer_idcreated_atupdated_atassociated_ban_idminor_review_reviewerStores authorized reviewers (configurable at runtime).
iduser_idadded_bycreated_atConfiguration
Required environment variables in
.env:The Cloud Function checks if a parental consent form exists in Google Drive for a given HTB account UUID. Requests must be signed with HMAC-SHA1.
Flow Sequences
1. Flag Minor - Initial Report Creation
sequenceDiagram actor Mod as Moderator participant Bot participant DB participant CF as Cloud Function participant Channel as Review Channel Mod->>Bot: /flag_minor user age evidence Bot->>Bot: Validate user has VERIFIED role Bot->>DB: Check htb_discord_link for user alt No HTB account linked Bot->>Mod: Error: User must be verified first else Account found Bot->>Mod: Processing... Bot->>CF: POST /check-file<br/>{file_name: account_uuid}<br/>X-Signature: hmac_sha1 CF->>Bot: {"exist": false} alt Consent already exists Bot->>Bot: Assign VERIFIED_MINOR role Bot->>Mod: Consent on file, role assigned else No consent Bot->>DB: Create MinorReport (status=pending) Bot->>Channel: Send report embed with buttons Bot->>Mod: Report created end end2. Approve Ban Flow
sequenceDiagram actor Rev as Reviewer participant View as MinorReportView participant Modal as ApproveBanModal participant Ban as Ban System participant DB participant Embed as Report Message Rev->>View: Click "Approve Ban" View->>Rev: Show modal (duration input) Rev->>Modal: Submit duration (e.g. "3y") Modal->>Ban: ban_member_with_epoch() Ban->>DB: Create Ban record Ban->>Modal: Ban created/queued for approval Modal->>DB: Update MinorReport<br/>status=approved<br/>associated_ban_id=<ban_id> Modal->>Rev: Ban submitted, awaiting SR_MOD approval Modal->>Embed: Update embed + disable Approve/Deny<br/>Relabel: "Check Consent & Unban"3. Check Consent & Unban Flow
sequenceDiagram actor Rev as Reviewer participant View as MinorReportView participant DB participant CF as Cloud Function participant Guild as Discord Guild participant Embed as Report Message Rev->>View: Click "Check Consent & Unban" View->>View: Defer response View->>DB: Get account_identifier for user View->>CF: POST /check-file<br/>{file_name: uuid}<br/>X-Signature: hmac CF->>View: {"exist": true/false} alt Consent found (exist=true) View->>DB: Get associated Ban record alt User in guild View->>Guild: Assign VERIFIED_MINOR role View->>Guild: Unban user (if banned) View->>Rev: User unbanned, role assigned else User not in guild View->>Guild: Unban user (by ID) View->>Rev: User unbanned, role assigned on rejoin end View->>DB: Update status=consent_verified View->>Embed: Update embed, remove all buttons else No consent (exist=false) View->>Rev: Consent still not found View->>Embed: Add status note, keep buttons end4. Auto-Remove Minor Role at Age 18
sequenceDiagram participant Scheduler as Scheduled Task participant DB participant Guild as Discord Guild loop Every 1 minute Scheduler->>DB: Get all reports (status=approved/consent_verified) loop For each report Scheduler->>Scheduler: Calculate expires_at<br/>(created_at + years_until_18) alt now >= expires_at Scheduler->>Guild: Get member by user_id alt Member found & has VERIFIED_MINOR Scheduler->>Guild: Remove VERIFIED_MINOR role Scheduler->>Scheduler: Log: User aged out end end end end5. Auto-Assign Minor Role on Rejoin
sequenceDiagram actor User participant Guild as Discord Guild participant Listener as on_member_join participant DB User->>Guild: Joins server Guild->>Listener: on_member_join event Listener->>DB: Check MinorReport for user<br/>(status=consent_verified) alt Report exists Listener->>Listener: Calculate expires_at<br/>(created_at + years_until_18) alt now < expires_at (still under 18) Listener->>Guild: Assign VERIFIED_MINOR role Listener->>Listener: Log: Minor role assigned on rejoin else Already 18 or older Listener->>Listener: Skip (expired) end endEdge Cases
1. User leaves server while banned
Scenario: User is flagged, ban approved, then they leave the server before consent is submitted.
Handling:
Noneor aUserobject (notMember)unban_member(guild, discord.Object(id=user_id))→ Discord unbans by IDon_member_joinhandler checks for aCONSENT_VERIFIEDreportVERIFIED_MINORautomatically2. Consent arrives before any ban
Scenario: Mod runs
/flag_minor, but consent form already exists in Google Drive.Handling:
check_parental_consentreturnsTrue:VERIFIED_MINORrole immediately3. Multiple reviewers try to approve the same report
Scenario: Two reviewers click "Approve Ban" at nearly the same time.
Handling:
report.status == PENDINGbefore showing the modalPENDING, the button replies: "This report is no longer pending."status=approved4. Report updated while pending
Scenario: A report already exists for a user, and a moderator runs
/flag_minoragain with updated info.Handling:
/flag_minorchecks for an active report viaget_active_minor_report(user_id)suspected_age,evidence,reporter_id,updated_atin the database5. User turns 18 while banned
Scenario: User was flagged at age 15, banned for 3 years, consent arrives after 6 months, they're unbanned and assigned the minor role, then 2.5 years pass.
Handling:
auto_remove_minor_roleruns every minute:expires_at = report.created_at + timedelta(days=365 * years_until_18(suspected_age))now >= expires_at:VERIFIED_MINORCONSENT_VERIFIEDstate (historical record), but the user is no longer treated as a minor6. Reviewer not in authorized list
Scenario: A moderator (not in
minor_review_reviewertable) tries to interact with a report.Handling:
MinorReportView.interaction_checkis called before any button callbackis_minor_review_moderator(user_id)which queries the DB (with 60-second cache)7. Cloud Function timeout or error
Scenario: The parental consent check fails (network error, timeout, or CF returns non-200).
Handling:
check_parental_consentwraps the HTTP call in try/except:False(no consent found)8. Report message deleted or not found
Scenario: The report message in the review channel is manually deleted by a moderator.
Handling:
get_report_by_message_id(interaction.message.id)interaction_checkreturnsFalsewith message: "Report not found or already resolved."/flag_minortries to edit an existing report's message:9. Interaction token expires (>3 seconds)
Scenario:
/flag_minortakes too long before responding (DB + HTTP calls).Handling:
10. User has both VERIFIED and VERIFIED_MINOR
Scenario: Through some edge case, a user ends up with both roles.
Handling:
/flag_minorvalidates early:Complete Flow Diagram
flowchart TD Start([Moderator runs /flag_minor]) --> ValidateUser{User has<br/>VERIFIED role?} ValidateUser -->|No| ErrorNotVerified[Error: Only verified users can be flagged] ValidateUser -->|Yes| HasMinorRole{User already has<br/>VERIFIED_MINOR?} HasMinorRole -->|Yes| ErrorAlreadyMinor[Error: Already has verified-minor status] HasMinorRole -->|No| SendStatus[Send status message:<br/>Processing...] SendStatus --> GetAccount[Get HTB account identifier from DB] GetAccount --> AccountFound{Account found?} AccountFound -->|No| ErrorNoAccount[Edit status: Must be verified first] AccountFound -->|Yes| CheckConsent[Call Cloud Function:<br/>check_parental_consent] CheckConsent --> HasConsent{Consent exists?} HasConsent -->|Yes| AssignRole[Assign VERIFIED_MINOR role] AssignRole --> UpdateStatusEarly[Edit status: Consent on file,<br/>no report created] HasConsent -->|No| CheckExisting{Active report<br/>exists for user?} CheckExisting -->|Yes| UpdateReport[Update existing report:<br/>age, evidence, reporter] UpdateReport --> EditMessage[Edit existing review message] EditMessage --> NotifyUpdate[Edit status: Report updated] CheckExisting -->|No| CreateReport[Create MinorReport in DB<br/>status=pending] CreateReport --> SendReview[Send embed + view to review channel] SendReview --> RefreshReport[Refresh report to get ID] RefreshReport --> EditEmbed[Edit embed with Report ID] EditEmbed --> NotifyCreated[Edit status: Report created] NotifyCreated --> ReviewFlow[Review Channel Flow] NotifyUpdate --> ReviewFlow ReviewFlow --> ReviewerAction{Reviewer Action} ReviewerAction -->|Approve Ban| ShowModal[Show Approve Ban Modal] ShowModal --> ValidateDuration[Validate ban duration] ValidateDuration --> CreateBan[Create ban via<br/>ban_member_with_epoch] CreateBan --> UpdateToApproved[Update status=approved<br/>Save ban_id] UpdateToApproved --> DisableButtons[Disable Approve + Deny<br/>Rename Recheck to<br/>Check Consent & Unban] DisableButtons --> WaitConsent{Later: Check<br/>Consent & Unban} ReviewerAction -->|Deny Report| ShowDenyModal[Show Deny Report Modal] ShowDenyModal --> SaveDenial[Update status=denied<br/>Add user note] SaveDenial --> RemoveButtonsDeny[Remove all buttons] RemoveButtonsDeny --> TerminalDenied([TERMINAL: DENIED]) ReviewerAction -->|Recheck Consent| RecheckPending[Check Cloud Function] RecheckPending --> ConsentFoundPending{Consent exists?} ConsentFoundPending -->|Yes| AssignRolePending[Assign VERIFIED_MINOR<br/>if user in guild] AssignRolePending --> SetVerified1[Update status=consent_verified] SetVerified1 --> RemoveButtons1[Remove all buttons] RemoveButtons1 --> Terminal1([TERMINAL: CONSENT_VERIFIED]) ConsentFoundPending -->|No| AddRecheckNote[Add status note:<br/>Recheck no consent] AddRecheckNote --> KeepButtons[Keep buttons active] KeepButtons --> ReviewerAction WaitConsent -->|Consent arrives| CheckConsentUnban[Call Cloud Function] CheckConsentUnban --> ConsentFoundApproved{Consent exists?} ConsentFoundApproved -->|Yes| UnbanFlow{User in guild?} UnbanFlow -->|Yes| UnbanAndRole[Unban user<br/>Assign VERIFIED_MINOR] UnbanFlow -->|No| UnbanOnly[Unban by user ID<br/>Role assigned on rejoin] UnbanAndRole --> SetVerified2[Update status=consent_verified] UnbanOnly --> SetVerified2 SetVerified2 --> RemoveButtons2[Remove all buttons] RemoveButtons2 --> Terminal2([TERMINAL: CONSENT_VERIFIED]) ConsentFoundApproved -->|No| AddRecheckNote2[Add status note:<br/>Recheck no consent] AddRecheckNote2 --> KeepButtonsApproved[Keep Check Consent & Unban button] KeepButtonsApproved --> WaitConsentAutomated Background Tasks
1. Remove Minor Role at Age 18
Frequency: Every 1 minute (part of
ScheduledTasks.all_tasks)Logic:
Purpose: Automatically clean up the minor role when users age out.
2. Assign Minor Role on Rejoin
Trigger:
on_member_joineventLogic:
Purpose: Assign the minor role to users with verified consent who rejoin the server before turning 18.
User Journey Examples
Scenario A: Clean path with consent on file
/flag_minor @User 15 "evidence"VERIFIED_MINORroleScenario B: Flag, approve, consent arrives later, user in guild
/flag_minor @User 15 "evidence"pending)approved{"exist": true})VERIFIED_MINORroleconsent_verifiedScenario C: Flag, approve, consent arrives, user not in guild
/flag_minor @User 15 "evidence"pending)approvedconsent_verified, all buttons removedon_member_joinhandler detectsCONSENT_VERIFIEDreport, user still under 18VERIFIED_MINORrole automaticallyScenario D: False positive - reviewer denies
/flag_minor @User 16 "evidence"pending)deniedSecurity & Access Control
Reviewer Authorization
minor_review_reviewertable can interact with report buttonsis_minor_review_moderator(user_id)/minor_reviewers add|remove|listCloud Function Authentication
hmac.new(PARENTAL_CONSENT_SECRET, json_body_bytes, hashlib.sha1).hexdigest()X-Signatureheader400 {"error":"Missing signature"}or403 {"error":"Invalid signature"}Command Permissions
/flag_minor: RequiresALL_ADMINSorALL_MODSroles/minor_reviewers: RequiresALL_ADMINSonlyTechnical Implementation Details
Pattern B for Interaction Handling
To prevent "Unknown interaction" errors when
/flag_minortakes >3 seconds:Send an immediate ephemeral response after quick validation:
Perform heavy operations (DB queries, Cloud Function calls)
Edit the status message with the final result:
This acknowledges the interaction immediately while allowing time for async operations.
Persistent Views
MinorReportViewis registered as a persistent view in theFlagMinorCog.on_readyhandlerminor_report_approve,minor_report_deny,minor_report_recheck)report_message_idmatchinginteraction.message.idDatabase Relationships
This links a minor report to the ban it created, enabling:
Reviewer Cache
To avoid DB queries on every button click:
/minor_reviewers add|removeis usedSummary
This parental consent flow provides a complete lifecycle for handling potentially underage users:
/flag_minorThe implementation handles edge cases like users leaving the server, consent arriving at various points in the flow, interaction timeouts, and authorization checks, making it robust for production use.