The following approach is needed because Discord has effectively abandoned the voice status feature.
It is not documented in the official Discord API documentation, which means it is considered unstable and may change at any time without notice.
That level of uncertainty is not ideal for a library like discord.py. The behavior described below is not necessarily undocumented or explicitly discouraged, but you are largely on your own. You are working directly with the raw data that the Discord API sends to the library, before it is parsed or exposed through any higher-level abstractions.
For more information or questions about this approach, you can ask in the discord.py server.
Also see discord/discord-api-docs#6398
import json
from typing import Any
import discord
intents = discord.Intents.default()
client = discord.Client(
intents=intents,
enable_debug_events=True, # Required to receive on_socket_raw_receive. Same applies to commands.Bot().
)
token = "YOUR-BOT-TOKEN"
@client.event # or bot.event or commands.Cog.listener()
async def on_socket_raw_receive(data: str) -> None:
data_dict: dict[str, Any] = json.loads(data)
event_name: str | None = data_dict.get("t")
event_data: dict[str, Any] = data_dict.get("d", {})
if event_name == "GUILD_AUDIT_LOG_ENTRY_CREATE":
event_type = event_data.get("action_type")
if event_type == 192: # voice_channel_status_update
options = event_data.get("options", {})
status: str | None = options.get("status")
guild_id: int | None = (
int(gid) if (gid := event_data.get("guild_id")) else None
)
changed_by_user_id: int | None = (
int(uid) if (uid := event_data.get("user_id")) else None
)
channel_id: int | None = (
int(tid) if (tid := event_data.get("target_id")) else None
)
guild = client.get_guild(guild_id) if guild_id else None
channel = client.get_channel(channel_id) if channel_id else None
member = (
guild.get_member(changed_by_user_id)
if guild and changed_by_user_id
else None
)
print(
f"{member} ({changed_by_user_id}) changed the status of "
f"{channel} ({channel_id}) in {guild} ({guild_id}) to {status}"
)
client.run(token)