EveCogs/voicemeister/utils.py
everestmcarthur 499c2bb291 feat: Add VoiceMeister v1.0.0 — Ultimate temporary voice channel management
- Join-to-Create system with auto-spawn temp voice channels
- Persistent 20+ button control panel (survives restarts)
- Name templates, game detection, auto-cleanup, logging
- Blacklist/whitelist, cooldowns, max-channels-per-user
- Interactive setup wizard + full [p]vc user commands
2026-05-08 13:23:03 +00:00

193 lines
7.1 KiB
Python
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""VoiceMeister — shared utilities, embed builders, helpers."""
from __future__ import annotations
import datetime
from typing import Optional
import discord
# ── Colour palette ─────────────────────────────────────────────────────────
class Clr:
PRIMARY = discord.Colour(0x5865F2) # blurple
SUCCESS = discord.Colour(0x57F287) # green
WARNING = discord.Colour(0xFEE75C) # yellow
ERROR = discord.Colour(0xED4245) # red
INFO = discord.Colour(0x3498DB) # light blue
VOICE = discord.Colour(0x9B59B6) # purple — main theme
PANEL = discord.Colour(0x2F3136) # dark — panel background
# ── Quick embeds ───────────────────────────────────────────────────────────
def ok_embed(desc: str, *, title: str | None = None) -> discord.Embed:
e = discord.Embed(description=f"✅ {desc}", colour=Clr.SUCCESS)
if title:
e.title = title
return e
def err_embed(desc: str, *, title: str | None = None) -> discord.Embed:
e = discord.Embed(description=f"❌ {desc}", colour=Clr.ERROR)
if title:
e.title = title
return e
def info_embed(desc: str, *, title: str | None = None) -> discord.Embed:
e = discord.Embed(description=desc, colour=Clr.INFO)
if title:
e.title = title
return e
def warn_embed(desc: str, *, title: str | None = None) -> discord.Embed:
e = discord.Embed(description=f"⚠️ {desc}", colour=Clr.WARNING)
if title:
e.title = title
return e
# ── Timestamp helpers ──────────────────────────────────────────────────────
def ts_now() -> int:
return int(datetime.datetime.now(datetime.timezone.utc).timestamp())
def ts_relative(ts: int) -> str:
return f"<t:{ts}:R>"
# ── Name template renderer ────────────────────────────────────────────────
def render_name(
template: str,
*,
member: discord.Member,
count: int = 1,
custom: str = "",
) -> str:
"""Render a channel name template with variable substitution."""
game = None
for activity in member.activities:
if activity.type == discord.ActivityType.playing:
game = activity.name
break
name = template.replace("{user}", member.display_name)
name = name.replace("{game}", game or "Chilling")
name = name.replace("{count}", str(count))
name = name.replace("{custom}", custom or member.display_name)
# Discord channel name limit is 100 chars
return name[:100]
# ── Permission helpers ─────────────────────────────────────────────────────
def is_channel_owner(member: discord.Member, owner_id: int) -> bool:
return member.id == owner_id
def can_manage(member: discord.Member, owner_id: int) -> bool:
"""Check if member is channel owner or has Manage Channels."""
return member.id == owner_id or member.guild_permissions.manage_channels
async def safe_send(dest, content=None, **kwargs) -> discord.Message | None:
try:
return await dest.send(content, **kwargs)
except (discord.HTTPException, discord.Forbidden):
return None
async def safe_dm(user: discord.User, content=None, **kwargs) -> discord.Message | None:
try:
return await user.send(content, **kwargs)
except (discord.HTTPException, discord.Forbidden):
return None
# ── Panel embed builder ───────────────────────────────────────────────────
def build_panel_embed(guild: discord.Guild) -> discord.Embed:
"""Build the main control panel embed."""
embed = discord.Embed(
title="🎙️ VoiceMeister — Channel Control Panel",
description=(
"Click any button below to manage your temporary voice channel.\n"
"You must be *in* a VoiceMeister channel and be its *owner* (or have Manage Channels).\n\n"
"━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
),
colour=Clr.VOICE,
)
embed.add_field(
name="🔐 Access Controls",
value=(
"🔒 **Lock** — Prevent others from joining\n"
"🔓 **Unlock** — Allow anyone to join\n"
"👻 **Ghost** — Hide + Lock combined\n"
"👁️ **Reveal** — Unhide + Unlock combined\n"
"👤 **Hide** — Make invisible in channel list\n"
"👁️‍🗨️ **Unhide** — Make visible again"
),
inline=True,
)
embed.add_field(
name="👥 User Management",
value=(
"➕ **Permit** — Allow a specific user\n"
"➖ **Reject** — Block a specific user\n"
"👢 **Kick** — Kick a user from channel\n"
"🔨 **Ban** — Permanently block a user\n"
"🔇 **Mute All** — Server-mute everyone\n"
"🔊 **Unmute All** — Remove server-mute"
),
inline=True,
)
embed.add_field(name="\u200b", value="\u200b", inline=False)
embed.add_field(
name="⚙️ Channel Settings",
value=(
"✏️ **Rename** — Change channel name\n"
"👥 **Limit** — Set user limit (0 = ∞)\n"
"📡 **Bitrate** — Change audio quality\n"
"🌍 **Region** — Set voice region"
),
inline=True,
)
embed.add_field(
name="👑 Ownership",
value=(
"👑 **Claim** — Claim if owner left\n"
"🔄 **Transfer** — Give ownership to another\n"
"ℹ️ **Info** — View channel details\n"
"🗑️ **Delete** — Destroy the channel"
),
inline=True,
)
embed.set_footer(text="VoiceMeister v1.0.0 • Buttons persist across restarts")
return embed
# ── Log embed builder ─────────────────────────────────────────────────────
def log_embed(
action: str,
*,
member: discord.Member,
channel: discord.VoiceChannel | None = None,
detail: str = "",
) -> discord.Embed:
"""Build a log embed for voice actions."""
embed = discord.Embed(
description=f"**{action}**\n{detail}" if detail else f"**{action}**",
colour=Clr.VOICE,
timestamp=datetime.datetime.now(datetime.timezone.utc),
)
embed.set_author(name=str(member), icon_url=member.display_avatar.url)
if channel:
embed.add_field(name="Channel", value=channel.mention, inline=True)
embed.set_footer(text=f"User ID: {member.id}")
return embed