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_ROLE_UPDATE": self._parse_guild_role_update,
"TYPING_START": self._parse_typing_start,
"VOICE_STATE_UPDATE": self._parse_voice_state_update,
}
def _parse_message_create(self, data: Dict[str, Any]) -> Message:
@ -111,6 +112,13 @@ class EventDispatcher:
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]):
"""Parses raw reaction data into a Reaction object."""

View File

@ -2484,7 +2484,7 @@ class PresenceUpdate:
return f"<PresenceUpdate user_id='{self.user.id}' guild_id='{self.guild_id}' status='{self.status}'>"
class TypingStart:
class TypingStart:
"""Represents a TYPING_START event."""
def __init__(
@ -2497,10 +2497,39 @@ class TypingStart:
self.timestamp: int = data["timestamp"]
self.member: Optional[Member] = (
Member(data["member"], client_instance) if data.get("member") else None
)
def __repr__(self) -> str:
return f"<TypingStart channel_id='{self.channel_id}' user_id='{self.user_id}'>"
)
def __repr__(self) -> str:
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:

View File

@ -131,3 +131,13 @@ a dictionary with the shard ID.
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
from disagreement.event_dispatcher import EventDispatcher
from disagreement.models import PresenceUpdate, TypingStart
from disagreement.models import PresenceUpdate, TypingStart, VoiceStateUpdate
@pytest.mark.asyncio
@ -15,8 +15,12 @@ async def test_presence_and_typing_parsing(dummy_client):
async def on_typing(typing):
events["typing"] = typing
async def on_voice(state):
events["voice"] = state
dispatcher.register("PRESENCE_UPDATE", on_presence)
dispatcher.register("TYPING_START", on_typing)
dispatcher.register("VOICE_STATE_UPDATE", on_voice)
presence_data = {
"user": {"id": "1", "username": "u", "discriminator": "0001"},
@ -30,10 +34,25 @@ async def test_presence_and_typing_parsing(dummy_client):
"user_id": "1",
"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("TYPING_START", typing_data)
await dispatcher.dispatch("VOICE_STATE_UPDATE", voice_data)
assert isinstance(events.get("presence"), PresenceUpdate)
assert events["presence"].status == "online"
assert isinstance(events.get("typing"), TypingStart)
assert events["typing"].channel_id == "c"
assert isinstance(events.get("voice"), VoiceStateUpdate)
assert events["voice"].session_id == "s"