diff --git a/litecord/blueprints/guilds.py b/litecord/blueprints/guilds.py index 29a479e..af5c04f 100644 --- a/litecord/blueprints/guilds.py +++ b/litecord/blueprints/guilds.py @@ -223,6 +223,12 @@ async def _update_guild(guild_id): """, j['name'], guild_id) if 'region' in j: + is_vip = app.voice.lvsp.region(j['region']).vip + can_vip = await app.storage.has_feature(guild_id, 'VIP_REGIONS') + + if is_vip and not can_vip: + raise BadRequest('can not assign guild to vip-only region') + await app.db.execute(""" UPDATE guilds SET region = $1 diff --git a/litecord/voice/lvsp_manager.py b/litecord/voice/lvsp_manager.py index 78484e0..9a2eb41 100644 --- a/litecord/voice/lvsp_manager.py +++ b/litecord/voice/lvsp_manager.py @@ -19,6 +19,7 @@ along with this program. If not, see . from typing import Optional from collections import defaultdict +from dataclasses import dataclass from logbook import Logger @@ -26,6 +27,14 @@ from litecord.voice.lvsp_conn import LVSPConnection log = Logger(__name__) + +@dataclass +class Region: + """Voice region data.""" + id: str + vip: bool + + class LVSPManager: """Manager class for Litecord Voice Server Protocol (LVSP) connections. @@ -44,46 +53,52 @@ class LVSPManager: # maps Union[GuildID, DMId, GroupDMId] to server hostnames self.assign = {} + # quick storage for Region dataclass instances. + self._regions = {} + self.app.loop.create_task(self._spawn()) async def _spawn(self): """Spawn LVSPConnection for each region.""" regions = await self.app.db.fetch(""" - SELECT id + SELECT id, vip FROM voice_regions WHERE deprecated = false """) - regions = [r['id'] for r in regions] + regions = [Region(r['id'], r['vip']) for r in regions] if not regions: log.warning('no regions are setup') return for region in regions: + # store it locally for region() function + self._regions[region.id] = region + self.app.loop.create_task( self._spawn_region(region) ) - async def _spawn_region(self, region: str): + async def _spawn_region(self, region: Region): """Spawn a region. Involves fetching all the hostnames for the regions and spawning a LVSPConnection for each.""" servers = await self.app.db.fetch(""" SELECT hostname FROM voice_servers WHERE region_id = $1 - """, region) + """, region.id) if not servers: log.warning('region {} does not have servers', region) return servers = [r['hostname'] for r in servers] - self.servers[region] = servers + self.servers[region.id] = servers for hostname in servers: - conn = LVSPConnection(self, region, hostname) + conn = LVSPConnection(self, region.id, hostname) self.conns[hostname] = conn self.app.loop.create_task( @@ -144,3 +159,7 @@ class LVSPManager: async def assign_conn(self, key: int, hostname: str): """Assign a connection to a given key in the assign map""" self.assign[key] = hostname + + def region(self, region_id: str) -> Optional[Region]: + """Get a :class:`Region` instance:wq:wq""" + return self._regions.get(region_id)