patched vow system

This commit is contained in:
Subinacls
2026-06-09 07:39:43 -04:00
parent 154735e133
commit 3034a474be
2 changed files with 130 additions and 16 deletions
+11 -4
View File
@@ -30,6 +30,8 @@ from utils.config import (
from cogs.moderation import (
get_or_create_xcom_role,
get_or_create_xcom_channel,
get_or_create_xcom_mod_channel,
_post_to_mod_channel,
VOTE_XCOM_THRESHOLD,
VOTE_RESET_DAYS,
)
@@ -157,9 +159,9 @@ class ExcommunicadoBot(commands.Bot):
result = add_keyword_points(
message.guild.id, author.id, matched_weight, reset_days=VOTE_RESET_DAYS
)
print(
f"[KEYWORD] {author} matched {matched_terms} (+{matched_weight}) "
f"-> score {result['score']} in {message.guild.name}"
logger.info(
"Keyword match: user=%s terms=%s (+%d) -> score=%d guild=%s",
author, matched_terms, matched_weight, result["score"], message.guild.name,
)
me = message.guild.me
can_manage = me is not None and author.top_role < me.top_role
@@ -178,10 +180,15 @@ class ExcommunicadoBot(commands.Bot):
timestamp=now,
)
log_embed.add_field(name="Matched", value=", ".join(matched_terms[:10]) or "", inline=False)
await _post_to_mod_channel(message.guild, None, log_embed)
await self.log_action(message.guild, log_embed)
logger.warning(
"Keyword auto-XCOM: user=%s guild=%s score=%d",
author, message.guild.name, result["score"],
)
return
except Exception as kw_err:
print(f"[KEYWORD] Auto-XCOM failed: {kw_err}")
logger.exception("Keyword auto-XCOM failed: %s", kw_err)
times = USER_MESSAGE_TIMES[message.author.id]
times.append(now)
+119 -12
View File
@@ -9,6 +9,8 @@ Commands:
from datetime import datetime, timezone
from typing import Optional
import logging
import discord
from discord.ext import commands
from discord import app_commands
@@ -28,6 +30,8 @@ from utils.config import (
get_keywords,
)
logger = logging.getLogger("excommunicado.moderation")
# --- Community vote configuration ---
VOTE_XCOM_THRESHOLD = 10 # net score required to auto-XCOM a user
VOTE_ADMIN_WEIGHT = 2 # admins / moderators count double
@@ -87,6 +91,79 @@ async def get_or_create_xcom_channel(guild: discord.Guild, role: discord.Role) -
return channel
def _staff_roles(guild: discord.Guild):
"""Yield roles that should be treated as moderation staff.
A role counts as staff if it grants administrator or any common
moderation permission. Used to auto-grant access to the private
xcom-mod channel.
"""
for r in guild.roles:
p = r.permissions
if (p.administrator or p.manage_guild or p.manage_roles
or p.ban_members or p.kick_members or p.manage_messages):
yield r
async def get_or_create_xcom_mod_channel(guild: discord.Guild, role: Optional[discord.Role] = None) -> discord.TextChannel:
"""Get or create the private 'xcom-mod' staff-only review channel.
This channel is hidden from everyone (including the Excommunicado role) and
is where the bot relays vows, vote/keyword auto-XCOM alerts, and other
moderation notices. Isolated users can never see it; the bot posts on their
behalf, so they never need write access here.
"""
if role is None:
role = await get_or_create_xcom_role(guild)
data = get_guild_data(guild.id)
ch_id = data.get("xcom_mod_channel_id")
if ch_id:
channel = guild.get_channel(ch_id)
if channel and isinstance(channel, discord.TextChannel):
return channel
overwrites = {
# Hidden from regular members...
guild.default_role: discord.PermissionOverwrite(view_channel=False),
# ...and explicitly hidden from isolated users.
role: discord.PermissionOverwrite(view_channel=False),
# The bot must be able to post relayed vows and alerts here.
guild.me: discord.PermissionOverwrite(
view_channel=True, send_messages=True, embed_links=True, read_message_history=True
),
}
# Grant staff roles read/write access.
for staff_role in _staff_roles(guild):
overwrites[staff_role] = discord.PermissionOverwrite(
view_channel=True, send_messages=True, read_message_history=True, embed_links=True
)
channel = await guild.create_text_channel(
name="xcom-mod",
overwrites=overwrites,
topic="Private staff channel: XCOM vows, auto-XCOM alerts, and moderation review. Not visible to isolated users.",
reason="Creating private staff review channel for Excommunicado",
)
update_guild_data(guild.id, "xcom_mod_channel_id", channel.id)
return channel
async def _post_to_mod_channel(guild: discord.Guild, role: Optional[discord.Role], embed: discord.Embed) -> bool:
"""Relay an embed to the private xcom-mod channel, creating it if needed.
Returns True if the message was delivered.
"""
try:
channel = await get_or_create_xcom_mod_channel(guild, role)
await channel.send(embed=embed)
return True
except discord.Forbidden:
logger.warning("Missing permission to create/post in xcom-mod for guild %s", guild.id)
except discord.HTTPException as err:
logger.warning("Failed to post to xcom-mod for guild %s: %s", guild.id, err)
return False
def _keyword_list_embed(guild: discord.Guild) -> discord.Embed:
"""Build an embed listing the guild's auto-score keywords."""
keywords = get_keywords(guild.id)
@@ -274,6 +351,10 @@ class ModerationCog(commands.Cog):
guild = interaction.guild
role = await get_or_create_xcom_role(guild)
channel = await get_or_create_xcom_channel(guild, role)
try:
mod_channel = await get_or_create_xcom_mod_channel(guild, role)
except discord.Forbidden:
mod_channel = None
embed = discord.Embed(
title="🪐 XCOM System Ready",
@@ -281,10 +362,23 @@ class ModerationCog(commands.Cog):
color=discord.Color.orange()
)
embed.add_field(name="Role Created", value=f"{role.mention} (ID: {role.id})", inline=True)
embed.add_field(name="Channel Created", value=f"{channel.mention}", inline=True)
embed.add_field(name="Lounge Channel", value=f"{channel.mention}", inline=True)
embed.add_field(
name="Staff Review Channel",
value=(mod_channel.mention if mod_channel
else "⚠️ Could not create `xcom-mod` (missing Manage Channels permission)."),
inline=True,
)
embed.add_field(
name="⚠️ Stealth Mode Active",
value="The **Excommunicado** role is now only a marker + grants access to the shared moderated room.\n\n**Do NOT** deny View/Send on public channels (user must keep seeing the normal server).\nNormal members cannot see xcom-lounge (already configured).\n\nStaff members you want to 'share the room' with the isolated user can be given the Excommunicado role manually.",
value=(
"The **Excommunicado** role is now only a marker + grants access to the shared moderated room.\n\n"
"**Do NOT** deny View/Send on public channels (user must keep seeing the normal server).\n"
"Normal members cannot see xcom-lounge (already configured).\n\n"
"Staff members you want to 'share the room' with the isolated user can be given the Excommunicado role manually.\n\n"
"🔒 **xcom-mod** is a private staff channel (hidden from members and isolated users). "
"The bot relays vows and auto-XCOM alerts there — isolated users never see it."
),
inline=False
)
embed.set_footer(text="After setup, use /xcom @user to excommunicate offenders. They will not realize they have been moved.")
@@ -417,7 +511,6 @@ class ModerationCog(commands.Cog):
guild = interaction.guild
data = get_guild_data(guild.id)
xcom_role_id = data.get("xcom_role_id")
xcom_channel_id = data.get("xcom_channel_id")
if not xcom_role_id or xcom_role_id not in [r.id for r in interaction.user.roles]:
await interaction.response.send_message("You must be under XCOM to submit a vow.", ephemeral=True)
@@ -427,17 +520,30 @@ class ModerationCog(commands.Cog):
await interaction.response.send_message("Vow is too long (max 200 characters).", ephemeral=True)
return
channel = guild.get_channel(xcom_channel_id) if xcom_channel_id else None
if channel:
vow_embed = discord.Embed(
title="📜 XCOM Vow Submitted",
description=f"**{interaction.user.mention}** has made the following vow:\n\n> {vow}",
color=discord.Color.dark_red()
role = guild.get_role(xcom_role_id)
vow_embed = discord.Embed(
title="📜 XCOM Vow Submitted",
description=f"**{interaction.user.mention}** ({interaction.user.id}) has made the following vow:\n\n> {vow}",
color=discord.Color.dark_red(),
timestamp=datetime.now(timezone.utc),
)
vow_embed.set_footer(text="Relayed by the bot to staff — the user cannot see this channel.")
# The bot relays the vow into the private staff channel, so the isolated
# user never needs (or gets) access to xcom-mod themselves.
delivered = await _post_to_mod_channel(guild, role, vow_embed) if role else False
if delivered:
logger.info("Vow relayed to xcom-mod: guild=%s user=%s", guild.id, interaction.user.id)
await interaction.response.send_message(
"Your vow has been submitted to staff for review.", ephemeral=True
)
await channel.send(embed=vow_embed)
await interaction.response.send_message("Your vow has been submitted to staff.", ephemeral=True)
else:
await interaction.response.send_message("XCOM lounge not found.", ephemeral=True)
await interaction.response.send_message(
"Could not deliver your vow — staff have been unable to set up the review channel. "
"Please try again later.",
ephemeral=True,
)
@app_commands.command(name="xcom-witness", description="Add a staff member to the xcom-lounge so they can support a specific user")
@app_commands.checks.has_permissions(manage_roles=True)
@@ -618,6 +724,7 @@ class ModerationCog(commands.Cog):
)
log_embed.add_field(name="Final score", value=str(score), inline=True)
log_embed.add_field(name="Distinct voters", value=str(unique_voters), inline=True)
await _post_to_mod_channel(guild, role, log_embed)
if hasattr(self.bot, "log_action"):
await self.bot.log_action(guild, log_embed)