From 579a71dd9b1344a2f32d689db0b42a440bc7dfe2 Mon Sep 17 00:00:00 2001 From: Luna Date: Mon, 4 Mar 2019 22:46:09 -0300 Subject: [PATCH] lvsp_conn: add basic INFO handling for channel assign - lvsp: finish defining data for channel info messages - lvsp_conn: add send_info() - lvsp_opcodes: add InfoTable, InfoReverse - voice.manager: handle channels without bitrate --- docs/lvsp.md | 21 +++++++++++++---- litecord/voice/lvsp_conn.py | 42 +++++++++++++++++++++++++++++++++- litecord/voice/lvsp_manager.py | 14 ++++++++---- litecord/voice/lvsp_opcodes.py | 13 +++++++++++ litecord/voice/manager.py | 11 +++------ 5 files changed, 83 insertions(+), 18 deletions(-) diff --git a/docs/lvsp.md b/docs/lvsp.md index de30b8a..a022f25 100644 --- a/docs/lvsp.md +++ b/docs/lvsp.md @@ -175,24 +175,37 @@ Request a channel to be created inside the voice server. The Server MUST reply back with a CHANNEL\_ASSIGN when resources are allocated for the channel. -**TODO:** fields +| field | type | description | +| --: | :-- | :-- | +| channel\_id | snowflake | channel id | +| guild\_id | Optional[snowflake] | guild id, not provided if dm / group dm | +| channel\_properties | ChannelProperties | channel properties | + +#### ChannelProperties + +| field | type | description | +| --: | :-- | :-- | +| bitrate | integer | channel bitrate | ### CHANNEL\_ASSIGN Sent by the Server to signal the successful creation of a voice channel. -**TODO:** fields +| field | type | description | +| --: | :-- | :-- | +| channel\_id | snowflake | channel id | +| guild\_id | Optional[snowflake] | guild id, not provided if dm / group dm | ### CHANNEL\_UPDATE Sent by the client to signal an update to the properties of a channel, such as its bitrate. -**TODO:** fields +Same data as CHANNEL\_REQ. ### CHANNEL\_DESTROY Sent by the client to signal the destruction of a voice channel. Be it a channel being deleted, or all members in it leaving. -**TODO:** fields +Same data as CHANNEL\_ASSIGN. diff --git a/litecord/voice/lvsp_conn.py b/litecord/voice/lvsp_conn.py index 143ba97..3902674 100644 --- a/litecord/voice/lvsp_conn.py +++ b/litecord/voice/lvsp_conn.py @@ -19,11 +19,12 @@ along with this program. If not, see . import json import asyncio +from typing import Dict import websockets from logbook import Logger -from litecord.voice.lvsp_opcodes import OPCodes as OP +from litecord.voice.lvsp_opcodes import OPCodes as OP, InfoTable, InfoReverse log = Logger(__name__) @@ -65,6 +66,16 @@ class LVSPConnection: 'd': data }) + async def send_info(self, info_type: str, info_data: Dict): + """Send an INFO message down the websocket.""" + await self.send({ + 'op': OP.info, + 'd': { + 'type': InfoTable[info_type.upper()], + 'data': info_data + } + }) + async def _heartbeater(self, hb_interval: int): try: await asyncio.sleep(hb_interval) @@ -121,6 +132,35 @@ class LVSPConnection: await self._update_health(msg['health']) self._start_hb() + async def _handle_6(self, msg): + """Handle INFO messages.""" + info = msg['d'] + info_type_str = InfoReverse[info['type']].lower() + + try: + info_handler = getattr(self, f'_handle_info_{info_type_str}') + except AttributeError: + return + + await info_handler(info['data']) + + async def _handle_info_channel_assign(self, data: dict): + """called by the server once we got a channel assign.""" + try: + channel_id = data['channel_id'] + channel_id = int(channel_id) + except (TypeError, ValueError): + return + + try: + guild_id = data['guild_id'] + guild_id = int(guild_id) + except (TypeError, ValueError): + guild_id = None + + main_key = guild_id if guild_id is not None else channel_id + await self.lvsp.assign(main_key, self.hostname) + async def _loop(self): while True: msg = await self.recv() diff --git a/litecord/voice/lvsp_manager.py b/litecord/voice/lvsp_manager.py index a40ff83..7436941 100644 --- a/litecord/voice/lvsp_manager.py +++ b/litecord/voice/lvsp_manager.py @@ -41,8 +41,8 @@ class LVSPManager: # maps regions to server hostnames self.servers = defaultdict(list) - # maps guilds to server hostnames - self.guild_servers = {} + # maps Union[GuildID, DMId, GroupDMId] to server hostnames + self.assign = {} self.app.loop.create_task(self._spawn()) @@ -119,12 +119,12 @@ class LVSPManager: return conn.health - async def get_server(self, guild_id: int) -> str: + async def get_guild_server(self, guild_id: int) -> str: """Get a voice server for the given guild, assigns - one if there isn't any.""" + one if there isn't any""" try: - hostname = self.guild_servers[guild_id] + hostname = self.assign[guild_id] except KeyError: region = await self.guild_region(guild_id) @@ -137,3 +137,7 @@ class LVSPManager: hostname = sorted_servers[0] return hostname + + async def assign_conn(self, key: int, hostname: str): + """Assign a connection to a given key in the assign map""" + self.assign[key] = hostname diff --git a/litecord/voice/lvsp_opcodes.py b/litecord/voice/lvsp_opcodes.py index 4b19249..4cd0b3c 100644 --- a/litecord/voice/lvsp_opcodes.py +++ b/litecord/voice/lvsp_opcodes.py @@ -26,3 +26,16 @@ class OPCodes: heartbeat = 4 heartbeat_ack = 5 info = 6 + + +InfoTable = { + 'CHANNEL_REQ': 0, + 'CHANNEL_ASSIGN': 1, + 'CHANNEL_UPDATE': 2, + 'CHANNEL_DESTROY': 3, + 'VST_CREATE': 4, + 'VST_UPDATE': 5, + 'VST_LEAVE': 6, +} + +InfoReverse = {v: k for k, v in InfoTable.items()} diff --git a/litecord/voice/manager.py b/litecord/voice/manager.py index bd4f48f..063ebce 100644 --- a/litecord/voice/manager.py +++ b/litecord/voice/manager.py @@ -156,24 +156,19 @@ class VoiceManager: await self.create_state(old_voice_key, {'channel_id': channel_id}) async def _lvsp_info_guild(self, guild_id, info_type, info_data): - hostname = await self.lvsp.get_server(guild_id) + hostname = await self.lvsp.get_guild_server(guild_id) conn = self.lvsp.get_conn(hostname) - - # TODO: impl send_info await conn.send_info(info_type, info_data) async def _create_ctx_guild(self, guild_id, channel_id): chan = await self.app.storage.get_channel(channel_id) - # TODO: this, but properly - # TODO: when the server sends a reply to CHAN_REQ, we need to update - # LVSPManager.guild_servers. - await self._lvsp_info_guild(guild_id, 'CHAN_REQ', { + await self._lvsp_info_guild(guild_id, 'CHANNEL_REQ', { 'guild_id': str(guild_id), 'channel_id': str(channel_id), 'channel_properties': { - 'bitrate': chan['bitrate'] + 'bitrate': chan.get('bitrate', 96) } })