Implement channel name management in ExtendedAudio cog. Add functionality to store and reset original channel names when enabling/disabling voice channel status updates. Enhance status format configuration with additional variables and validation. Introduce cleanup tasks on cog unload and voice state updates for improved user experience.
Some checks are pending
Run pre-commit / Run pre-commit (push) Waiting to run

This commit is contained in:
Valerie 2025-05-24 07:44:15 -04:00
parent 8d79407cb4
commit bddbd49106

View file

@ -5,10 +5,17 @@ from typing import Optional
import discord
from redbot.core import Config, commands
from redbot.core.bot import Red
from redbot.core.utils.chat_formatting import box
from redbot.core.utils.chat_formatting import box, humanize_list
from redbot.core.utils.predicates import MessagePredicate
log = logging.getLogger("red.valerie.extendedaudio")
SUPPORTED_VARIABLES = {
"title": "Song title",
"requester": "User who requested the song",
"duration": "Duration of the song"
}
class ExtendedAudio(commands.Cog):
"""Extends Red's core Audio cog with additional features"""
@ -18,6 +25,7 @@ class ExtendedAudio(commands.Cog):
default_guild = {
"channel_status": True, # Whether to update voice channel name with current song
"status_format": "🎵 {title}", # Format for the channel name
"original_name": None, # Store the original channel name
}
self.config.register_guild(**default_guild)
self.task = self.bot.loop.create_task(self.initialize())
@ -37,13 +45,47 @@ class ExtendedAudio(commands.Cog):
"""Cleanup when cog unloads"""
if self.task:
self.task.cancel()
# Reset all modified channel names
asyncio.create_task(self._cleanup_on_unload())
async def _cleanup_on_unload(self):
"""Reset all modified channel names when cog unloads"""
for guild in self.bot.guilds:
try:
original_name = await self.config.guild(guild).original_name()
if original_name and guild.voice_client and guild.voice_client.channel:
await guild.voice_client.channel.edit(name=original_name)
await self.config.guild(guild).original_name.set(None)
except:
continue
async def cog_check(self, ctx: commands.Context):
"""Check if Audio cog is loaded"""
if not self.audio:
self.audio = self.bot.get_cog("Audio")
if not self.audio:
await ctx.send("The Audio cog is not loaded. Please load it first with `[p]load audio`")
return False
return True
@commands.group(aliases=["eaudioset"])
@commands.guild_only()
@commands.admin_or_permissions(manage_guild=True)
async def extendedaudioset(self, ctx: commands.Context):
"""Configure ExtendedAudio settings"""
pass
if not ctx.invoked_subcommand:
guild = ctx.guild
conf = await self.config.guild(guild).all()
enabled = conf["channel_status"]
format_str = conf["status_format"]
msg = (
f"**Current Settings**\n"
f"Channel Status: {'Enabled' if enabled else 'Disabled'}\n"
f"Status Format: {format_str}\n\n"
f"Available Variables: {humanize_list([f'{{' + k + '}}' for k in SUPPORTED_VARIABLES.keys()])}\n"
f"Use `{ctx.prefix}extendedaudioset statusformat` to change the format."
)
await ctx.send(box(msg))
@extendedaudioset.command(name="channelstatus")
async def set_channel_status(self, ctx: commands.Context, enabled: bool):
@ -53,21 +95,73 @@ class ExtendedAudio(commands.Cog):
Example: [p]extendedaudioset channelstatus true
"""
await self.config.guild(ctx.guild).channel_status.set(enabled)
guild = ctx.guild
# Check if bot has required permissions
if not guild.me.guild_permissions.manage_channels:
await ctx.send("I need the `Manage Channels` permission to update channel names.")
return
# Store original channel name if enabling and not stored
if enabled:
voice_client = guild.voice_client
if voice_client and voice_client.channel:
current_name = voice_client.channel.name
if not await self.config.guild(guild).original_name():
await self.config.guild(guild).original_name.set(current_name)
await self.config.guild(guild).channel_status.set(enabled)
state = "enabled" if enabled else "disabled"
await ctx.send(f"Voice channel status updates {state}.")
# Reset channel name if disabling
if not enabled:
try:
voice_client = guild.voice_client
if voice_client and voice_client.channel:
original_name = await self.config.guild(guild).original_name()
if original_name:
await voice_client.channel.edit(name=original_name)
await self.config.guild(guild).original_name.set(None)
except discord.Forbidden:
await ctx.send("I don't have permission to edit the channel name.")
except Exception as e:
log.error(f"Error resetting channel name: {e}")
@extendedaudioset.command(name="statusformat")
async def set_status_format(self, ctx: commands.Context, *, format_str: str):
"""Set the format for voice channel status updates
Available variables:
{title} - Song title
{requester} - User who requested the song
{duration} - Duration of the song
Example: [p]extendedaudioset statusformat 🎵 {title}
"""
# Validate format string
try:
# Test with sample data
test_data = {
"title": "Test Song",
"requester": "Test User",
"duration": "3:45"
}
format_str.format(**test_data)
except KeyError as e:
invalid_var = str(e).strip("'")
supported = humanize_list([f"{{" + k + "}}" for k in SUPPORTED_VARIABLES.keys()])
await ctx.send(
f"Invalid variable `{invalid_var}` in format string.\n"
f"Supported variables are: {supported}"
)
return
except Exception as e:
await ctx.send(f"Invalid format string: {e}")
return
await self.config.guild(ctx.guild).status_format.set(format_str)
example = format_str.format(title="Never Gonna Give You Up")
example = format_str.format(title="Never Gonna Give You Up", requester="Rick Astley", duration="3:32")
await ctx.send(
f"Status format set! Example:\n"
f"{box(example)}"
@ -91,12 +185,31 @@ class ExtendedAudio(commands.Cog):
channel = voice_client.channel
# Store original name if not stored
if not await self.config.guild(guild).original_name():
await self.config.guild(guild).original_name.set(channel.name)
# Get the format and create the new name
format_str = await self.config.guild(guild).status_format()
new_name = format_str.format(title=track.get("title", "Unknown"))
# Update channel name if different
if channel.name != new_name:
# Prepare variables
duration = track.get("length", 0)
minutes = duration // 60
seconds = duration % 60
duration_str = f"{minutes}:{seconds:02d}"
new_name = format_str.format(
title=track.get("title", "Unknown"),
requester=requester.display_name,
duration=duration_str
)
# Ensure name length is valid (Discord limit is 100 characters)
if len(new_name) > 100:
new_name = new_name[:97] + "..."
# Update channel name if different and we have permissions
if channel.name != new_name and guild.me.guild_permissions.manage_channels:
await channel.edit(name=new_name)
except discord.Forbidden:
@ -122,12 +235,52 @@ class ExtendedAudio(commands.Cog):
channel = voice_client.channel
# Reset channel name if it was modified (contains 🎵)
if "🎵" in channel.name:
original_name = channel.name.split("🎵")[-1].strip()
# Reset to original name if stored and we have permissions
original_name = await self.config.guild(guild).original_name()
if original_name and guild.me.guild_permissions.manage_channels:
await channel.edit(name=original_name)
except discord.Forbidden:
log.warning(f"Missing permissions to edit channel name in {guild.name}")
except Exception as e:
log.error(f"Error resetting channel status in {guild.name}: {e}")
log.error(f"Error resetting channel status in {guild.name}: {e}")
@commands.Cog.listener()
async def on_voice_state_update(self, member: discord.Member, before: discord.VoiceState, after: discord.VoiceState):
"""Reset channel name when bot leaves voice channel"""
if not member.guild:
return
# Check if this is the bot disconnecting
if member.id == self.bot.user.id and before.channel and not after.channel:
try:
# Reset channel name if we have the original stored and permissions
guild = member.guild
if not guild.me.guild_permissions.manage_channels:
return
original_name = await self.config.guild(guild).original_name()
if original_name and before.channel:
await before.channel.edit(name=original_name)
await self.config.guild(guild).original_name.set(None)
except Exception as e:
log.error(f"Error resetting channel name on disconnect: {e}")
@commands.Cog.listener()
async def on_red_audio_player_auto_disconnect(self, guild: discord.Guild):
"""Handle auto-disconnect cleanup"""
try:
if guild.me.guild_permissions.manage_channels:
original_name = await self.config.guild(guild).original_name()
if original_name:
voice_client = guild.voice_client
if voice_client and voice_client.channel:
await voice_client.channel.edit(name=original_name)
await self.config.guild(guild).original_name.set(None)
except Exception as e:
log.error(f"Error handling auto-disconnect cleanup: {e}")
@commands.Cog.listener()
async def on_red_audio_player_auto_pause(self, guild: discord.Guild):
"""Handle auto-pause"""
# We don't change the channel name on pause to maintain consistency