Skip to content

Commit

Permalink
Merge pull request #6 from interactions-py/unstable
Browse files Browse the repository at this point in the history
Merge unstable to main
  • Loading branch information
Damego authored Oct 4, 2022
2 parents 9d0fc4e + b3a3247 commit f63e985
Show file tree
Hide file tree
Showing 7 changed files with 103 additions and 111 deletions.
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -129,3 +129,7 @@ dmypy.json
.pyre/

.idea

logs/*
Lavalink.jar

2 changes: 1 addition & 1 deletion interactions/ext/lavalink/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
from interactions.ext.version import Version, VersionAuthor

__all__ = ["version", "base"]
__version__ = "0.1.0"
__version__ = "0.1.1"

version = Version(
version=__version__, author=VersionAuthor(name="Damego", email="damego.dev@gmail.com")
Expand Down
66 changes: 30 additions & 36 deletions interactions/ext/lavalink/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

from lavalink import Client as LavalinkClient

from interactions import Client, LibraryException, Snowflake
from interactions import Client, Snowflake

from .models import VoiceState
from .player import Player
Expand All @@ -16,8 +16,8 @@ class VoiceClient(Client):
def __init__(self, token: str, **kwargs):
super().__init__(token, **kwargs)

self._websocket = VoiceWebSocketClient(token, self._intents)
self.lavalink_client = LavalinkClient(int(self.me.id), player=Player)
self._websocket = VoiceWebSocketClient(self, token, self._intents)
self.lavalink_client: LavalinkClient = None

self._websocket._dispatch.register(
self.__raw_voice_state_update, "on_raw_voice_state_update"
Expand All @@ -26,8 +26,13 @@ def __init__(self, token: str, **kwargs):
self.__raw_voice_server_update, "on_raw_voice_server_update"
)

self._websocket._http._bot_var = self
async def _login(self) -> None:
self._http._bot_var = self
self.lavalink_client = LavalinkClient(int(self.me.id), player=Player)

self.__register_lavalink_listeners()

await super()._login()

async def __raw_voice_state_update(self, data: dict):
lavalink_data = {"t": "VOICE_STATE_UPDATE", "d": data}
Expand All @@ -47,42 +52,36 @@ async def connect(
"""
Connects to voice channel and creates player.
:param guild_id: The guild id to connect.
:type guild_id: Union[Snowflake, int, str]
:param channel_id: The channel id to connect.
:type channel_id: Union[Snowflake, int, str]
:param self_deaf: Whether bot is self deafened
:type self_deaf: bool
:param self_mute: Whether bot is self muted
:type self_mute: bool
:param Union[Snowflake, int, str] guild_id: The guild id to connect.
:param Union[Snowflake, int, str] channel_id: The channel id to connect.
:param bool self_deaf: Whether bot is self deafened
:param bool self_mute: Whether bot is self muted
:return: Created guild player.
:rtype: Player
"""
# Discord will fire INVALID_SESSION if channel_id is None
if guild_id is None:
raise LibraryException(message="Missed requirement argument: guild_id")
raise TypeError("guild_id cannot be NoneType")
if channel_id is None:
raise LibraryException(message="Missed requirement argument: channel_id")
raise TypeError("channel_id cannot be NoneType for connect method")

await self._websocket.connect_voice_channel(guild_id, channel_id, self_deaf, self_mute)
await self._websocket.update_voice_state(guild_id, channel_id, self_deaf, self_mute)
player = self.lavalink_client.player_manager.get(int(guild_id))
if player is None:
player = self.lavalink_client.player_manager.create(int(guild_id))
return player

async def disconnect(self, guild_id: Union[Snowflake, int]):
if guild_id is None:
raise LibraryException(message="Missed requirement argument: guild_id")
raise TypeError("guild_id cannot be NoneType")

await self._websocket.disconnect_voice_channel(int(guild_id))
await self._websocket.update_voice_state(int(guild_id))
await self.lavalink_client.player_manager.destroy(int(guild_id))

def get_player(self, guild_id: Union[Snowflake, int]) -> Player:
"""
Returns current player in guild.
:param guild_id: The guild id
:type guild_id: Union[Snowflake, int]
:param Union[Snowflake, int] guild_id: The guild id
:return: Guild player
:rtype: Player
"""
Expand All @@ -97,8 +96,7 @@ def get_user_voice_state(self, user_id: Union[Snowflake, int]) -> Optional[Voice
"""
Returns user voice state.
:param user_id: The user id
:type user_id: Union[Snowflake, int]
:param Union[Snowflake, int] user_id: The user id
:return: Founded user voice state else nothing
:rtype: Optional[VoiceState]
"""
Expand All @@ -110,8 +108,7 @@ def get_guild_voice_states(self, guild_id: Union[Snowflake, int]) -> Optional[Li
"""
Returns guild voice states.
:param guild_id: The channel id
:type guild_id: Union[Snowflake, int]
:param Union[Snowflake, int] guild_id: The channel id
:return: Founded channel voice states else nothing
:rtype: Optional[List[VoiceState]]
"""
Expand All @@ -129,8 +126,7 @@ def get_channel_voice_states(
"""
Returns channel voice states.
:param channel_id: The channel id
:type channel_id: Union[Snowflake, int]
:param Union[Snowflake, int] channel_id: The channel id
:return: Founded channel voice states else nothing
:rtype: Optional[List[VoiceState]]
"""
Expand All @@ -145,16 +141,14 @@ def get_channel_voice_states(
def __register_lavalink_listeners(self):
for extension in self._extensions.values():
for name, func in getmembers(extension):
if hasattr(func, "__lavalink__"):
name = func.__lavalink__[3:]
event_name = "".join(word.capitalize() for word in name.split("_")) + "Event"
if event_name not in self.lavalink_client._event_hooks:
self.lavalink_client._event_hooks[event_name] = []
self.lavalink_client._event_hooks[event_name].append(func)

async def _ready(self) -> None:
self.__register_lavalink_listeners()
await super()._ready()
if not hasattr(func, "__lavalink__"):
continue
name = func.__lavalink__[3:]
event_name = "".join(word.capitalize() for word in name.split("_")) + "Event"
event_hooks = self.lavalink_client._event_hooks
if event_name not in event_hooks:
event_hooks[event_name] = []
event_hooks[event_name].append(func)


def listener(func=None, *, name: str = None):
Expand Down
59 changes: 22 additions & 37 deletions interactions/ext/lavalink/models.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from datetime import datetime
from typing import TYPE_CHECKING, Optional

from interactions.api.models.attrs_utils import ClientSerializerMixin, define, field
from interactions.utils.attrs_utils import ClientSerializerMixin, define, field

from interactions import Channel, Guild, LibraryException, Member, Snowflake

Expand All @@ -20,37 +20,19 @@ class VoiceState(ClientSerializerMixin):
This class creates an object every time the event ``VOICE_STATE_UPDATE`` is received from the discord API.
It contains information about the user's update voice information.
Attributes:
-----------
_json : dict
All data of the object stored as dictionary
member : Member
The member whose VoiceState was updated
user_id : int
The id of the user whose VoiceState was updated. This is technically the same as the "member id",
but it is called `user_id` because of API terminology.
suppress : bool
Whether the user is muted by the current user(-> bot)
session_id : int
The id of the session
self_video : bool
Whether the user's camera is enabled.
self_mute : bool
Whether the user is muted by themselves
self_deaf : bool
Whether the user is deafened by themselves
self_stream : bool
Whether the user is streaming in the current channel
request_to_speak_timestamp : datetime
Only for stage-channels; when the user requested permissions to speak in the stage channel
mute : bool
Whether the user's microphone is muted by the server
guild_id : int
The id of the guild in what the update took action
deaf : bool
Whether the user is deafened by the guild
channel_id : int
The id of the channel the update took action
:ivar Member member: The member whose VoiceState was updated
:ivar int user_id: The id of the user whose VoiceState was updated.
:ivar bool suppress: Whether the user is muted by the current user(-> bot)
:ivar int session_id: The id of the session
:ivar bool self_video: Whether the user's camera is enabled.
:ivar bool self_mute: Whether the user is muted by themselves
:ivar bool self_deaf: Whether the user is deafened by themselves
:ivar bool self_stream: Whether the user is streaming in the current channel
:ivar datetime request_to_speak_timestamp: Only for stage-channels; when the user requested permissions to speak in the stage channel
:ivar bool mute: Whether the user's microphone is muted by the server
:ivar int guild_id: The id of the guild in what the update took action
:ivar bool deaf: Whether the user is deafened by the guild
:ivar int channel_id: The id of the channel the update took action
"""

guild_id: Optional[Snowflake] = field(converter=Snowflake, default=None)
Expand Down Expand Up @@ -78,7 +60,7 @@ def joined(self) -> bool:
"""
return self.channel_id is not None

async def mute_member(self, reason: Optional[str]) -> Member:
async def mute_member(self, reason: Optional[str] = None) -> Member:
"""
Mutes the current member.
Expand All @@ -89,7 +71,7 @@ async def mute_member(self, reason: Optional[str]) -> Member:
"""
return await self.member.modify(guild_id=int(self.guild_id), mute=True, reason=reason)

async def deafen_member(self, reason: Optional[str]) -> Member:
async def deafen_member(self, reason: Optional[str] = None) -> Member:
"""
Deafens the current member.
Expand All @@ -100,7 +82,7 @@ async def deafen_member(self, reason: Optional[str]) -> Member:
"""
return await self.member.modify(guild_id=int(self.guild_id), deaf=True, reason=reason)

async def move_member(self, channel_id: int, *, reason: Optional[str]) -> Member:
async def move_member(self, channel_id: int, *, reason: Optional[str] = None) -> Member:
"""
Moves the member to another channel.
Expand Down Expand Up @@ -141,7 +123,7 @@ async def connect(self, self_deaf: bool = False, self_mute: bool = False) -> "Pl
if not self.channel_id:
raise LibraryException(message="User not connected to the voice channel!")

await self._client._bot_var._websocket.connect_voice_channel(
await self._client._bot_var._websocket.update_voice_state(
self.guild_id, self.channel_id, self_deaf, self_mute
)
player = self._client._bot_var.lavalink_client.player_manager.get(int(self.guild_id))
Expand All @@ -154,7 +136,10 @@ async def connect(self, self_deaf: bool = False, self_mute: bool = False) -> "Pl
class VoiceServer(ClientSerializerMixin):
"""
A class object representing the gateway event ``VOICE_SERVER_UPDATE``.
This class creates an object every time the event ``VOICE_SERVER_UPDATE`` is received from the discord API.
:ivar str endpoint: Voice connection token
:ivar Snowflake guild_id: Guild this voice server update is for
:ivar str token: Voice server host
"""

endpoint: str = field()
Expand Down
76 changes: 44 additions & 32 deletions interactions/ext/lavalink/websocket.py
Original file line number Diff line number Diff line change
@@ -1,20 +1,44 @@
from interactions import OpCodeType, Storage, WebSocketClient
from typing import TYPE_CHECKING

from interactions import OpCodeType, Storage, WebSocketClient, HTTPClient

from .models import VoiceServer, VoiceState

if TYPE_CHECKING:
from .client import VoiceClient

__all__ = ["VoiceWebSocketClient"]


class VoiceWebSocketClient(WebSocketClient):
def __init__(self, *args):
def __init__(self, bot_var: "VoiceClient", *args):
self._bot_var: "VoiceClient" = bot_var
super().__init__(*args)

async def run(self) -> None:
"""
Handles the client's connection with the Gateway.
"""

if isinstance(self._http, str):
self._http = HTTPClient(self._http)
self._http._bot_var = self._bot_var

await super().run()

def _dispatch_event(self, event: str, data: dict) -> None:
"""
Dispatches VOICE_STATE_UPDATE and VOICE_SERVER_UPDATE events from the Gateway.
:param str event: The name of the event.
:param dict data: The data for the event.
"""
if event not in ("VOICE_STATE_UPDATE", "VOICE_SERVER_UPDATE"):
return super()._dispatch_event(event, data)

self._dispatch.dispatch(f"on_raw_{event.lower()}", data)
_cache: Storage = self._http.cache[VoiceState]

if event == "VOICE_SERVER_UPDATE":
model = VoiceServer(**data, _client=self._http)
self._dispatch.dispatch("on_voice_server_update", model)
Expand All @@ -24,44 +48,32 @@ def _dispatch_event(self, event: str, data: dict) -> None:
self._dispatch.dispatch("on_voice_state_update", old, model)
_cache.add(model, model.user_id)

async def connect_voice_channel(
self, guild_id: int, channel_id: int, self_deaf: bool, self_mute: bool
async def update_voice_state(
self,
guild_id: int,
channel_id: int = None,
self_deaf: bool = None,
self_mute: bool = None,
):
"""
Sends packet to websocket for connection to voice channel.
:param guild_id: The guild id to connect.
:type guild_id: int
:param channel_id: The channel id to connect.
:type channel_id: int
:param self_deaf: Whether bot is self deafened
:type self_deaf: bool
:param self_mute: Whether bot is self muted
:type self_mute: bool
Sends VOICE_STATE packet to websocket.
:param int guild_id: The guild id.
:param int channel_id: The channel id.
:param bool self_deaf: Whether bot is self deafened
:param bool self_mute: Whether bot is self muted
"""

payload = {
"op": OpCodeType.VOICE_STATE,
"d": {
"channel_id": str(channel_id),
"guild_id": str(guild_id),
"self_deaf": self_deaf,
"self_mute": self_mute,
"channel_id": str(channel_id) if channel_id is not None else None,
},
}

await self._send_packet(payload)

async def disconnect_voice_channel(self, guild_id: int):
"""
Sends packet to websocket for disconnecting from voice channel.
:param guild_id: The guild id
:type guild_id: int
"""
payload = {
"op": OpCodeType.VOICE_STATE,
"d": {"channel_id": None, "guild_id": str(guild_id)},
}
if self_deaf is not None:
payload["d"]["self_deaf"] = self_deaf
if self_mute is not None:
payload["d"]["self_mute"] = self_mute

await self._send_packet(payload)
2 changes: 1 addition & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
discord-py-interactions>=4.3.0, <4.3.2
discord-py-interactions>=4.3.2
lavalink~=4.0.1
Loading

0 comments on commit f63e985

Please sign in to comment.