Solve Community · Discord applications

Privileged Intent Evidence

Document type: implementation evidence pack
Reviewed: October 28, 2026
Operator: the Solve community application owner, Discord username nagisa_sano
This page documents implementation evidence for features that require privileged Discord Gateway data. The Admin section describes the reviewed repository implementation and requested use; it does not establish the deployed version or Discord approval. Excerpts are deliberately limited to the relevant control flow and omit credentials, internal configuration values, and unrelated source code.

1. Requested scope at a glance

Solve・Rôleplay

Application 1392136292107681792

Requested: Guild Members, Message Content

Not requested: Presence

Solve・Douane

Application 1373374958012465152

Requested: Guild Members, Message Content

Not requested: Presence

Solve・Admin requests only Message Content for ticket archives; Guild Members and Presence are not requested. This is a description of the proposed request, not confirmation of Discord approval or activation. Solve・Naruto is covered by a separate, already-reviewed request and is listed in the shared privacy policy.

2. Solve・Rôleplay — Guild Members

Solve・Rôleplay synchronizes a defined set of roleplay roles between two controlled Solve guilds. When a member joins the second guild, the bot receives the member-join event, finds the same Discord user in the first guild, reads only the configured source roles, and applies their configured equivalents. Authorized administrators can also run a complete reconciliation over existing members after configuration changes or downtime.

Member joins RP #2→Match same user in RP #1→ Read mapped roles→Apply equivalents in RP #2

Relevant implementation excerpt

@commands.Cog.listener()
async def on_member_join(self, member: disnake.Member):
    if member.guild.id != RP2_GUILD_ID:
        return
    await self.sync_member_roles(member)

# Administrator-only full reconciliation
members_to_sync = [member for member in rp2_guild.members if not member.bot]
for member in members_to_sync:
    await self.sync_member_roles(member)

The event-driven join flow requires GUILD_MEMBER_ADD. The reconciliation flow requires a complete, current guild member list. Interaction payloads and one-off REST lookups do not provide equivalent event coverage or a complete reconciliation source.

3. Solve・Douane — Guild Members

Solve・Douane operates the whitelist and staff workflows shared by the controlled Solve Naruto and Douane guilds. It reacts to member joins and role changes, reconciles whitelist/blacklist and staff roles, and computes the current staff or voice-queue membership used by operational tools. These features use Discord user IDs and role membership; they do not use presence data.

Member joins or roles change→Validate configured state→ Synchronize or protect roles→Write restricted audit log

Relevant implementation excerpts

@commands.Cog.listener()
async def on_member_update(self, before: disnake.Member, after: disnake.Member):
    if after.guild.id != GUILD_NARUTO_ID:
        return
    # Verify and, where necessary, correct a protected whitelist-role change.

# Staff-role reconciliation between the two controlled guilds
authorized_member_ids = {member.id for member in source_role.members}
await self._sync_douane_role(target_guild, authorized_member_ids, target_role, ...)

# Voice-queue cleanup needs the current guild membership
for member in guild.members:
    await reconcile_queue_nickname(member)

The join/update listeners require member Gateway events. Staff-role and administrative reconciliation require complete role-member and guild-member collections. Access is scoped to the two configured Solve guilds and is used only for those operational workflows.

4. Solve・Douane — Message Content

Message content is processed only in explicitly configured operational channels and for four deterministic features. It is not used for advertising, profiling, general analytics, or training machine-learning or artificial-intelligence models.

FeatureWhy ordinary message content is necessaryScope
Absence forwarding Copies a staff member’s absence notice between the configured Naruto and Douane absence channels. Two fixed operational channels; messages from bots are ignored.
Staff mention routing Detects one configured staff-role mention and forwards the accompanying context to the restricted staff destination. Configured guild/channel/role only.
Douane guidance Applies deterministic keyword and mention rules to route questions and respond to unauthorized staff pings. No AI or semantic profiling; fixed rules only.
Ticket transcripts Creates the staff audit transcript when an authorized user closes a Douane ticket. Ticket channels only; restricted log destination.

Relevant implementation excerpts

@commands.Cog.listener()
async def on_message(self, message: disnake.Message):
    if message.author.bot:
        return
    if message.guild.id == DOUANE_GUILD_ID and message.channel.id == ABSENCE_CHANNEL_DOUANE:
        forwarded_text = f"**Absence de :** <@{message.author.id}>\n{message.content}"
        await configured_naruto_absence_channel.send(forwarded_text)

# On authorized ticket closure
with temporary_transcript.open("w", encoding="utf-8") as transcript:
    async for message in ticket_channel.history(limit=None, oldest_first=True):
        transcript.write(format_ticket_line(message))
await restricted_log_channel.send(file=disnake.File(temporary_transcript))
temporary_transcript.unlink(missing_ok=True)

Slash commands cannot replace these passive workflows because the triggering objects are ordinary staff notices, role mentions, questions, and ticket messages. The bot first checks the configured guild, channel, role, author, or ticket context before using content.

Solve・Rôleplay — Message Content for ticket transcripts

Application 1392136292107681792 operates roleplay tickets (character creation, in-game technique training follow-ups, Bingo Book entries, and the XP they award). A ticket is closed by its owner, a game master or an administrator (close button or slash command), or automatically after at least 72 hours of player inactivity: a final reminder is sent, then the ticket closes at 23:00 that day if the player still has not answered. On closure the bot reads the ticket channel's history, builds a plain-text transcript in memory (timestamp, author display name, message text, attachment filenames) and posts it with a closure embed in the restricted staff log channel of the same guild. The ticket channel is deleted only after the transcript has been delivered; otherwise the channel is retained.

Message Content is requested for ordinary messages in those ticket channels, including staff messages. A slash command can trigger closure but does not supply the text of earlier free-form messages. Without Message Content the bot keeps closure suspended (button, command and inactivity auto-close) so no ticket history is lost. It reads message content nowhere else and its SQLite ticket database stores no message content.

Ticket closed→Read ticket history→ Post transcript to staff log→Delete ticket channel

Relevant implementation excerpt

# Simplified from the Rôleplay ticket closure implementation
if not bot.intents.message_content:
    return await inter.response.send_message(CLOSE_SUSPENDED_MESSAGE, ephemeral=True)

lines = []
async for msg in ticket_channel.history(limit=None, oldest_first=True):
    lines.append(f"[{timestamp}] {msg.author.display_name}: {msg.content}")
transcript = disnake.File(io.StringIO("\n".join(lines)), filename=f"transcript_{ticket_id}.txt")
await restricted_log_channel.send(embed=close_embed, file=transcript)
await ticket_channel.delete()  # only after the transcript was delivered

Solve・Admin — Message Content for ticket archives

Application 1378096450613018716 operates support and roleplay tickets. The reviewed implementation restricts /close to authorized staff roles and configured ticket categories, and requires an explicit closure confirmation. It then reads the ticket channel's history, writes an archive containing message text, timestamps, author tags and opening-ticket embed content, and sends the archive to the configured Discord log channel. The ticket channel is deleted only after history retrieval, local file creation and archive delivery succeed. Otherwise the channel is retained.

Message Content is requested for ordinary messages in those ticket channels, including staff messages. A slash command can initiate closure but does not supply the text of pre-existing ordinary messages needed for the complete transcript. Direct messages addressed to the bot and modal-based replies do not themselves require this intent. The reviewed code keeps closure suspended when Message Content is unavailable. It does not require Guild Members or Presence for this workflow.

Relevant implementation flow

// Simplified from the reviewed Admin closure implementation:
// Check authorized staff role and allowed ticket category; request confirmation.
// If Message Content is unavailable, preserve the ticket channel.
const fetched = await channel.messages.fetch({ limit: 100, before: lastId });
// Repeat pagination to collect the complete history.
fs.writeFileSync(filePath, embedText + messageLines.join("\n"), "utf8");
await logCh.send({ embeds: [logEmbed], files: [new AttachmentBuilder(filePath)] });
// Delete the ticket channel only after the complete archive is confirmed.
// There is no routine local archive-file deletion after upload.

Admin retention: the operator confirmed that local .log archives and their Discord log-channel copies are retained indefinitely, with no planned routine or scheduled deletion. The temporary-file deletion behavior documented for Douane does not apply to Admin. Access, correction and deletion requests remain available through the privacy-policy contact. These statements describe repository evidence and the operator's retention practice; the currently deployed revision has not been independently verified.

5. Data handling and user controls

6. Contact and policy

Questions about this implementation may be sent to Discord username nagisa_sano. The complete retention, security, recipient, and deletion disclosures are available in the Solve Discord Applications Privacy Policy.