Discord Application Review Evidence

Intent and Data Implementation

This page gives Discord reviewers a technical, human-readable summary of how the private ThanHub Rover | V1 and ThanHub Rover | V2 applications use privileged Gateway intents.

Implementation reviewed: June 15, 2026

Sanitized implementation excerpts. The examples below reflect production control flow while intentionally excluding tokens, secrets, database credentials, private channel IDs, and proprietary business logic.
THANHUB ROVER | V1

Members and Commands

Uses Guild Members for join/leave verification workflows and Message Content for established th prefix commands and numerical activity counts.

THANHUB ROVER | V2

Tickets and Safety

Uses Message Content only in private ticket workflows and a designated scam-trap channel. Guild Members and Presence are not required.

1. Requested Intents

V1 required
Server Members Intent
V1 and V2 required
Message Content Intent
Not requested
Presence Intent

V2 starts without privileged intents unless the matching environment setting is explicitly enabled after approval in the Discord Developer Portal. This prevents accidental use of disallowed intents.

const intents = [GatewayIntentBits.Guilds, GatewayIntentBits.GuildMessages];

if (messageContentApproved) intents.push(GatewayIntentBits.MessageContent);
if (guildMembersApproved) intents.push(GatewayIntentBits.GuildMembers);

2. V1 Server Members Intent

The application reacts to member join and leave events. It records a limited event history and uses server configuration to send verification instructions. Role assignment and removal are performed only for configured ThanHub verification and premium workflows.

client.on("guildMemberAdd", async member => {
  await memberHistory.insert({
    guildId: member.guild.id,
    userId: member.user.id,
    username: member.user.username,
    eventType: "join"
  });

  const verification = await getVerificationConfig(member.guild.id);
  if (verification) await sendVerificationInstructions(member, verification);
});

client.on("guildMemberRemove", async member => {
  await memberHistory.insert({
    guildId: member.guild.id,
    userId: member.user.id,
    username: member.user.username,
    eventType: "leave"
  });
});
Stored
User ID, guild ID, username, event type.
Purpose
Verification, member lifecycle history, configured role management.
Not used for
Advertising, presence tracking, or external profiling.

3. V1 Message Content Intent

V1 needs message content for existing prefix commands beginning with th. Ordinary messages increment numerical daily, weekly, and total counters; their text is not written to the activity-count table.

client.on("messageCreate", async message => {
  if (message.author.bot || !message.guild) return;

  await incrementNumericActivityCounts(message.author.id, message.guild.id);

  if (!message.content.toLowerCase().startsWith("th")) return;
  const [commandName, ...args] = parsePrefixCommand(message.content);
  await runApprovedCommunityCommand(commandName, args);
});

4. V2 Private Ticket Processing

V2 immediately ignores bots, DMs, and non-ticket channels. Purchase and giveaway tickets are manual. Support-ticket AI remains disabled until the ticket owner selects Request AI Help.

async function handleTicketMessage(message) {
  if (!message.guild || message.author.bot || !isTicketChannel(message.channel)) {
    return false;
  }
  if (isPurchaseTicket(message.channel) || isGiveawayTicket(message.channel)) {
    return true; // Manual staff workflow only
  }
  if (ticketIsClosed(message.channel) || !aiHelpWasRequested(message.channel)) {
    return true;
  }
  queueSupportReply(message);
  return true;
}

Casual or insufficient messages are filtered before any Gemini request. Relevant support text is sent only for immediate inference and is not used by ThanHub to train or fine-tune models.

5. V2 Scam-Trap Moderation

The safety handler processes messages only when they are posted in one specifically configured scam-trap channel. Bot messages and members with the configured exemption role are ignored.

async function handleScamTrapMessage(message) {
  if (!message.guild || message.author.bot || message.channelId !== SCAM_TRAP_CHANNEL) {
    return false;
  }
  if (memberHasExemptRole(message.member)) return false;

  await message.delete();
  if (message.member.moderatable) {
    await message.member.timeout(CONFIGURED_DURATION, MODERATION_REASON);
  }
  await notifyUserAndWriteModerationLog(message);
  return true;
}

6. Ticket Transcript Storage

When authorized staff closes a ticket, the bot gathers ticket messages, creates a random unguessable token, and sends the transcript to a protected API authenticated by a server-side secret. The secret is never exposed in Discord or browser code.

1. Staff closesPermission and ticket state are validated.
2. Messages fetchedOnly the private ticket channel is read.
3. Protected uploadA secret-authenticated API stores the transcript.
4. Logged and deletedStaff receive a transcript link and the channel is removed.

Stored transcript records may include ticket owner ID, channel and guild IDs, channel name, staff claimer, closer, timestamps, message text, and attachment links. Access is limited to authorized ThanHub staff and protected infrastructure.

7. Retention, Security, and Deletion

8. Reviewer Evidence Checklist

The following in-server demonstrations correspond directly to the implementation above:

Related public documents: Bot Data Usage Policy, Privacy Policy, and Terms of Service.