""" Discord Helper Utilities Common Discord-related helper functions for channel lookups, message sending, and formatting. """ from typing import Optional, List import discord from discord.ext import commands from config import get_config from models.play import Play from models.team import Team from utils.logging import get_contextual_logger logger = get_contextual_logger(__name__) async def get_channel_by_name( bot: commands.Bot, channel_name: str ) -> Optional[discord.TextChannel]: """ Get a text channel by name from the configured guild. Args: bot: Discord bot instance channel_name: Name of the channel to find Returns: TextChannel if found, None otherwise """ config = get_config() guild_id = config.guild_id if not guild_id: logger.error("GUILD_ID not configured") return None guild = bot.get_guild(guild_id) if not guild: logger.error(f"Guild {guild_id} not found") return None channel = discord.utils.get(guild.text_channels, name=channel_name) if not channel: logger.warning(f"Channel '{channel_name}' not found in guild {guild_id}") return None return channel async def send_to_channel( bot: commands.Bot, channel_name: str, content: Optional[str] = None, embed: Optional[discord.Embed] = None, ) -> bool: """ Send a message to a channel by name. Args: bot: Discord bot instance channel_name: Name of the channel content: Text content to send embed: Embed to send Returns: True if message sent successfully, False otherwise """ channel = await get_channel_by_name(bot, channel_name) if not channel: logger.error(f"Cannot send to channel '{channel_name}' - not found") return False try: # Build kwargs to avoid passing None for embed kwargs = {} if content is not None: kwargs["content"] = content if embed is not None: kwargs["embed"] = embed await channel.send(**kwargs) logger.info(f"Sent message to #{channel_name}") return True except Exception as e: logger.error(f"Failed to send message to #{channel_name}: {e}") return False def format_key_plays(plays: List[Play], away_team: Team, home_team: Team) -> str: """ Format top plays into embed field text. Args: plays: List of Play objects (should be sorted by WPA) away_team: Away team object home_team: Home team object Returns: Formatted string for embed field, or empty string if no plays """ if not plays: return "" key_plays_text = "" for play in plays: # Use the Play.descriptive_text() method (already includes score) play_description = play.descriptive_text(away_team, home_team) key_plays_text += f"{play_description}\n" return key_plays_text async def set_channel_visibility( channel: discord.TextChannel, visible: bool, reason: Optional[str] = None ) -> bool: """ Set channel visibility for @everyone. The bot's permissions are based on its role, not @everyone, so the bot will retain access even when @everyone view permission is removed. Args: channel: Discord text channel to modify visible: If True, grant @everyone view permission; if False, deny it reason: Optional reason for audit log Returns: True if permissions updated successfully, False otherwise """ try: guild = channel.guild everyone_role = guild.default_role if visible: # Grant @everyone permission to view channel default_reason = "Channel made visible to all members" await channel.set_permissions( everyone_role, view_channel=True, reason=reason or default_reason ) logger.info(f"Set #{channel.name} to VISIBLE for @everyone") else: # Remove @everyone view permission default_reason = "Channel hidden from members" await channel.set_permissions( everyone_role, view_channel=False, reason=reason or default_reason ) logger.info(f"Set #{channel.name} to HIDDEN for @everyone") return True except discord.Forbidden: logger.error(f"Missing permissions to modify #{channel.name} permissions") return False except Exception as e: logger.error(f"Error setting channel visibility for #{channel.name}: {e}") return False