"""Query who is currently connected to voice channels in a Discord server."""
from __future__ import annotations
import logging
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from tool_context import ToolContext
logger = logging.getLogger(__name__)
TOOL_NAME = "get_voice_states"
TOOL_DESCRIPTION = (
"List all voice channels in a Discord server and the members "
"currently connected to each one. Useful for situational awareness "
"of who is hanging out in voice chat."
)
TOOL_PARAMETERS = {
"type": "object",
"properties": {
"server_id": {
"type": "string",
"description": (
"The Discord server (guild) ID. If omitted, uses "
"the server the message was sent in."
),
},
},
"required": [],
}
[docs]
async def run(
server_id: str | None = None,
ctx: ToolContext | None = None,
) -> str:
"""Return a formatted list of voice channels and their connected members.
Args:
server_id: Discord guild ID. Falls back to the context guild.
ctx: Tool execution context.
Returns:
Formatted string describing voice channel occupancy.
"""
from tools._discord_helpers import require_discord_client
client = require_discord_client(ctx)
if isinstance(client, str):
return client
# Resolve guild
guild = None
if server_id:
try:
guild = client.get_guild(int(server_id))
except ValueError:
return f"Error: Invalid server ID: '{server_id}'."
elif ctx and ctx.server_id:
try:
guild = client.get_guild(int(ctx.server_id))
except (ValueError, TypeError):
pass
if guild is None:
return "Error: Could not resolve a server. Please provide a server_id."
lines = [f"**Voice Channel States for '{guild.name}'**\n"]
occupied = 0
for vc in sorted(guild.voice_channels, key=lambda c: c.position):
members = vc.members
if members:
occupied += 1
member_lines = []
for m in members:
parts = [m.display_name]
vs = m.voice
if vs:
if vs.self_mute or vs.mute:
parts.append("🔇")
if vs.self_deaf or vs.deaf:
parts.append("🔕")
if vs.self_stream:
parts.append("📺")
if vs.self_video:
parts.append("📹")
member_lines.append(" • " + " ".join(parts))
lines.append(f"🔊 **{vc.name}** ({len(members)} connected)")
lines.extend(member_lines)
else:
lines.append(f"🔇 **{vc.name}** (empty)")
total_vc = len(guild.voice_channels)
lines.append(
f"\n{occupied}/{total_vc} voice channels occupied, "
f"{sum(len(vc.members) for vc in guild.voice_channels)} total users in voice."
)
return "\n".join(lines)