Add VoiceStateUpdate event (#70)
Some checks failed
Deploy MkDocs / deploy (push) Has been cancelled

This commit is contained in:
Slipstream 2025-06-11 18:24:16 -06:00 committed by GitHub
parent c099466024
commit e693f00abe
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 72 additions and 6 deletions

View File

@ -60,6 +60,7 @@ class EventDispatcher:
"GUILD_BAN_REMOVE": self._parse_guild_ban_remove, "GUILD_BAN_REMOVE": self._parse_guild_ban_remove,
"GUILD_ROLE_UPDATE": self._parse_guild_role_update, "GUILD_ROLE_UPDATE": self._parse_guild_role_update,
"TYPING_START": self._parse_typing_start, "TYPING_START": self._parse_typing_start,
"VOICE_STATE_UPDATE": self._parse_voice_state_update,
} }
def _parse_message_create(self, data: Dict[str, Any]) -> Message: def _parse_message_create(self, data: Dict[str, Any]) -> Message:
@ -111,6 +112,13 @@ class EventDispatcher:
return TypingStart(data, client_instance=self._client) return TypingStart(data, client_instance=self._client)
def _parse_voice_state_update(self, data: Dict[str, Any]):
"""Parses raw VOICE_STATE_UPDATE data into a VoiceStateUpdate object."""
from .models import VoiceStateUpdate
return VoiceStateUpdate(data, client_instance=self._client)
def _parse_message_reaction(self, data: Dict[str, Any]): def _parse_message_reaction(self, data: Dict[str, Any]):
"""Parses raw reaction data into a Reaction object.""" """Parses raw reaction data into a Reaction object."""

View File

@ -2503,6 +2503,35 @@ class TypingStart:
return f"<TypingStart channel_id='{self.channel_id}' user_id='{self.user_id}'>" return f"<TypingStart channel_id='{self.channel_id}' user_id='{self.user_id}'>"
class VoiceStateUpdate:
"""Represents a VOICE_STATE_UPDATE event."""
def __init__(
self, data: Dict[str, Any], client_instance: Optional["Client"] = None
):
self._client = client_instance
self.guild_id: Optional[str] = data.get("guild_id")
self.channel_id: Optional[str] = data.get("channel_id")
self.user_id: str = data["user_id"]
self.member: Optional[Member] = (
Member(data["member"], client_instance) if data.get("member") else None
)
self.session_id: str = data["session_id"]
self.deaf: bool = data.get("deaf", False)
self.mute: bool = data.get("mute", False)
self.self_deaf: bool = data.get("self_deaf", False)
self.self_mute: bool = data.get("self_mute", False)
self.self_stream: Optional[bool] = data.get("self_stream")
self.self_video: bool = data.get("self_video", False)
self.suppress: bool = data.get("suppress", False)
def __repr__(self) -> str:
return (
f"<VoiceStateUpdate guild_id='{self.guild_id}' user_id='{self.user_id}' "
f"channel_id='{self.channel_id}'>"
)
class Reaction: class Reaction:
"""Represents a message reaction event.""" """Represents a message reaction event."""

View File

@ -131,3 +131,13 @@ a dictionary with the shard ID.
async def on_shard_resume(info: dict): async def on_shard_resume(info: dict):
... ...
``` ```
## VOICE_STATE_UPDATE
Triggered when a user's voice connection state changes, such as joining or leaving a voice channel. The callback receives a `VoiceStateUpdate` model.
```python
@client.event
async def on_voice_state_update(state: disagreement.VoiceStateUpdate):
...
```

View File

@ -1,7 +1,7 @@
import pytest import pytest
from disagreement.event_dispatcher import EventDispatcher from disagreement.event_dispatcher import EventDispatcher
from disagreement.models import PresenceUpdate, TypingStart from disagreement.models import PresenceUpdate, TypingStart, VoiceStateUpdate
@pytest.mark.asyncio @pytest.mark.asyncio
@ -15,8 +15,12 @@ async def test_presence_and_typing_parsing(dummy_client):
async def on_typing(typing): async def on_typing(typing):
events["typing"] = typing events["typing"] = typing
async def on_voice(state):
events["voice"] = state
dispatcher.register("PRESENCE_UPDATE", on_presence) dispatcher.register("PRESENCE_UPDATE", on_presence)
dispatcher.register("TYPING_START", on_typing) dispatcher.register("TYPING_START", on_typing)
dispatcher.register("VOICE_STATE_UPDATE", on_voice)
presence_data = { presence_data = {
"user": {"id": "1", "username": "u", "discriminator": "0001"}, "user": {"id": "1", "username": "u", "discriminator": "0001"},
@ -30,10 +34,25 @@ async def test_presence_and_typing_parsing(dummy_client):
"user_id": "1", "user_id": "1",
"timestamp": 123, "timestamp": 123,
} }
voice_data = {
"guild_id": "g",
"channel_id": "c",
"user_id": "1",
"session_id": "s",
"deaf": False,
"mute": False,
"self_deaf": False,
"self_mute": False,
"self_video": False,
"suppress": False,
}
await dispatcher.dispatch("PRESENCE_UPDATE", presence_data) await dispatcher.dispatch("PRESENCE_UPDATE", presence_data)
await dispatcher.dispatch("TYPING_START", typing_data) await dispatcher.dispatch("TYPING_START", typing_data)
await dispatcher.dispatch("VOICE_STATE_UPDATE", voice_data)
assert isinstance(events.get("presence"), PresenceUpdate) assert isinstance(events.get("presence"), PresenceUpdate)
assert events["presence"].status == "online" assert events["presence"].status == "online"
assert isinstance(events.get("typing"), TypingStart) assert isinstance(events.get("typing"), TypingStart)
assert events["typing"].channel_id == "c" assert events["typing"].channel_id == "c"
assert isinstance(events.get("voice"), VoiceStateUpdate)
assert events["voice"].session_id == "s"