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
This commit is contained in:
Luna 2019-03-04 22:46:09 -03:00
parent 00100f9abb
commit 579a71dd9b
5 changed files with 83 additions and 18 deletions

View File

@ -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 The Server MUST reply back with a CHANNEL\_ASSIGN when resources are
allocated for the channel. 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 ### CHANNEL\_ASSIGN
Sent by the Server to signal the successful creation of a voice channel. 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 ### CHANNEL\_UPDATE
Sent by the client to signal an update to the properties of a channel, Sent by the client to signal an update to the properties of a channel,
such as its bitrate. such as its bitrate.
**TODO:** fields Same data as CHANNEL\_REQ.
### CHANNEL\_DESTROY ### CHANNEL\_DESTROY
Sent by the client to signal the destruction of a voice channel. Be it Sent by the client to signal the destruction of a voice channel. Be it
a channel being deleted, or all members in it leaving. a channel being deleted, or all members in it leaving.
**TODO:** fields Same data as CHANNEL\_ASSIGN.

View File

@ -19,11 +19,12 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
import json import json
import asyncio import asyncio
from typing import Dict
import websockets import websockets
from logbook import Logger 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__) log = Logger(__name__)
@ -65,6 +66,16 @@ class LVSPConnection:
'd': data '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): async def _heartbeater(self, hb_interval: int):
try: try:
await asyncio.sleep(hb_interval) await asyncio.sleep(hb_interval)
@ -121,6 +132,35 @@ class LVSPConnection:
await self._update_health(msg['health']) await self._update_health(msg['health'])
self._start_hb() 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): async def _loop(self):
while True: while True:
msg = await self.recv() msg = await self.recv()

View File

@ -41,8 +41,8 @@ class LVSPManager:
# maps regions to server hostnames # maps regions to server hostnames
self.servers = defaultdict(list) self.servers = defaultdict(list)
# maps guilds to server hostnames # maps Union[GuildID, DMId, GroupDMId] to server hostnames
self.guild_servers = {} self.assign = {}
self.app.loop.create_task(self._spawn()) self.app.loop.create_task(self._spawn())
@ -119,12 +119,12 @@ class LVSPManager:
return conn.health 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 """Get a voice server for the given guild, assigns
one if there isn't any.""" one if there isn't any"""
try: try:
hostname = self.guild_servers[guild_id] hostname = self.assign[guild_id]
except KeyError: except KeyError:
region = await self.guild_region(guild_id) region = await self.guild_region(guild_id)
@ -137,3 +137,7 @@ class LVSPManager:
hostname = sorted_servers[0] hostname = sorted_servers[0]
return hostname 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

View File

@ -26,3 +26,16 @@ class OPCodes:
heartbeat = 4 heartbeat = 4
heartbeat_ack = 5 heartbeat_ack = 5
info = 6 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()}

View File

@ -156,24 +156,19 @@ class VoiceManager:
await self.create_state(old_voice_key, {'channel_id': channel_id}) await self.create_state(old_voice_key, {'channel_id': channel_id})
async def _lvsp_info_guild(self, guild_id, info_type, info_data): 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) conn = self.lvsp.get_conn(hostname)
# TODO: impl send_info
await conn.send_info(info_type, info_data) await conn.send_info(info_type, info_data)
async def _create_ctx_guild(self, guild_id, channel_id): async def _create_ctx_guild(self, guild_id, channel_id):
chan = await self.app.storage.get_channel(channel_id) chan = await self.app.storage.get_channel(channel_id)
# TODO: this, but properly await self._lvsp_info_guild(guild_id, 'CHANNEL_REQ', {
# 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', {
'guild_id': str(guild_id), 'guild_id': str(guild_id),
'channel_id': str(channel_id), 'channel_id': str(channel_id),
'channel_properties': { 'channel_properties': {
'bitrate': chan['bitrate'] 'bitrate': chan.get('bitrate', 96)
} }
}) })