Solve・Rôleplay
Application 1392136292107681792
Requested: Guild Members, Message Content
Not requested: Presence
Solve Community · Discord applications
Application 1392136292107681792
Requested: Guild Members, Message Content
Not requested: Presence
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.
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.
@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.
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.
@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.
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.
| Feature | Why ordinary message content is necessary | Scope |
|---|---|---|
| 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. |
@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.
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.
# 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
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.
// 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.
nagisa_sano on Discord or through
an official Solve support ticket, as described in the privacy 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.