Compare commits

..

1 Commits

Author SHA1 Message Date
Mai fc16231b9a Merge branch 'patch-3' into 'master'
Channel names no longer end with a dash

See merge request litecord/litecord!70
2022-02-06 06:32:10 +00:00
4 changed files with 24 additions and 54 deletions

View File

@ -66,9 +66,6 @@ class Config:
#: Postgres credentials #: Postgres credentials
POSTGRES = {} POSTGRES = {}
#: Shared secret for LVSP
LVSP_SECRET = ""
class Development(Config): class Development(Config):
DEBUG = True DEBUG = True

View File

@ -10,8 +10,6 @@ LVSP runs over a *long-lived* websocket with TLS. The encoding is JSON.
"client" is litecord. "server" is the voice server. "client" is litecord. "server" is the voice server.
note: only the opcode is sent in a message, so the names are determined by the implementation of LVSP.
| opcode | name | sent by | | opcode | name | sent by |
| --: | :-- | :-- | | --: | :-- | :-- |
| 0 | HELLO | server | | 0 | HELLO | server |
@ -121,17 +119,15 @@ are laid on.
### InfoType Enum ### InfoType Enum
note: this enum is only ever identified by its opcode, so the `name` field can differ from the values in this enum without error.
| value | name | description | | value | name | description |
| --: | :-- | :-- | | --: | :-- | :-- |
| 0 | CHANNEL\_REQ | channel assignment request | | 0 | CHANNEL\_REQ | channel assignment request |
| 1 | CHANNEL\_ASSIGN | channel assignment reply | | 1 | CHANNEL\_ASSIGN | channel assignment reply |
| 2 | CHANNEL\_DESTROY | channel destroy | | 2 | CHANNEL\_DESTROY | channel destroy |
| 3 | VOICE\_STATE\_CREATE | voice state create request | | 3 | VST\_CREATE | voice state create request |
| 4 | VOICE\_STATE\_DONE | voice state created | | 4 | VST\_DONE | voice state created |
| 5 | VOICE\_STATE\_DESTROY | voice state destroy | | 5 | VST\_UPDATE | voice state update |
| 6 | VOICE\_STATE\_UPDATE | voice state update | | 6 | VST\_LEAVE | voice state leave |
### CHANNEL\_REQ ### CHANNEL\_REQ
@ -162,7 +158,7 @@ a channel being deleted, or all members in it leaving.
Same data as CHANNEL\_ASSIGN, but without `token`. Same data as CHANNEL\_ASSIGN, but without `token`.
### VOICE\_STATE\_CREATE ### VST\_CREATE
Sent by the client to create a voice state. Sent by the client to create a voice state.
@ -172,25 +168,21 @@ Sent by the client to create a voice state.
| channel\_id | snowflake | channel id | | channel\_id | snowflake | channel id |
| guild\_id | Optional[snowflake] | guild id. not provided if dm / group dm | | guild\_id | Optional[snowflake] | guild id. not provided if dm / group dm |
### VOICE\_STATE\_DONE ### VST\_DONE
Sent by the server to indicate the success of a VOICE\_STATE\_CREATE. Sent by the server to indicate the success of a VST\_CREATE.
Has the same fields as VOICE\_STATE\_CREATE, but with extras: Has the same fields as VST\_CREATE, but with extras:
| field | type | description | | field | type | description |
| --: | :-- | :-- | | --: | :-- | :-- |
| session\_id | string | session id for the voice state | | session\_id | string | session id for the voice state |
### VOICE\_STATE\_DESTROY ### VST\_DESTROY
Sent by the client when a user is leaving a channel OR moving between channels Sent by the client when a user is leaving a channel OR moving between channels
in a guild. More on state transitions later on. in a guild. More on state transitions later on.
### VOICE\_STATE\_UPDATE
Sent to update an existing voice state. Potentially unused.
| field | type | description | | field | type | description |
| --: | :-- | :-- | | --: | :-- | :-- |
| session\_id | string | session id for the voice state | | session\_id | string | session id for the voice state |
@ -203,11 +195,11 @@ Since the channel is unitialized, both logic on initialization AND
user join is here. user join is here.
- Client will send a CHANNEL\_REQ. - Client will send a CHANNEL\_REQ.
- Client MAY send a VOICE\_STATE\_CREATE right after as well. - Client MAY send a VST\_CREATE right after as well.
- The Server MUST process CHANNEL\_REQ first, so the Server can keep - The Server MUST process CHANNEL\_REQ first, so the Server can keep
a lock on channel operations while it is initialized. a lock on channel operations while it is initialized.
- Reply with CHANNEL\_ASSIGN once initialization is done. - Reply with CHANNEL\_ASSIGN once initialization is done.
- Process VOICE\_STATE\_CREATE - Process VST\_CREATE
### Updating a voice channel ### Updating a voice channel
@ -222,15 +214,15 @@ user join is here.
### User joining an (initialized) voice channel ### User joining an (initialized) voice channel
- Client sends VOICE\_STATE\_CREATE - Client sends VST\_CREATE
- Server sends VOICE\_STATE\_DONE - Server sends VST\_DONE
### User leaves a channel ### User leaves a channel
- Client sends VOICE\_STATE\_DESTROY with the old fields - Client sends VST\_DESTROY with the old fields
### User moves a channel ### User moves a channel
- Client sends VOICE\_STATE\_DESTROY with the old fields - Client sends VST\_DESTROY with the old fields
- Client sends VOICE\_STATE\_CREATE with the new fields - Client sends VST\_CREATE with the new fields
- Server sends VOICE\_STATE\_DONE - Server sends VST\_DONE

View File

@ -887,9 +887,6 @@ class GatewayWebsocket:
# they CAN NOT enter two channels in a single guild. # they CAN NOT enter two channels in a single guild.
# this state id format takes care of that. # this state id format takes care of that.
#
# TODO voice_key should have a type as a 0th element to prevent
# code from having to call get_guild(id2).
voice_key = (self.state.user_id, state_id2) voice_key = (self.state.user_id, state_id2)
voice_state = await self.app.voice.get_state(voice_key) voice_state = await self.app.voice.get_state(voice_key)
@ -907,10 +904,6 @@ class GatewayWebsocket:
if same_guild and not same_channel: if same_guild and not same_channel:
return await self.app.voice.move_state(voice_state, channel_id) return await self.app.voice.move_state(voice_state, channel_id)
# TODO: this is an edge case. we're trying to move guilds in
# a single message, perhaps?
log.warning("vsu payload does not appear logical")
async def _handle_5(self, payload: Dict[str, Any]): async def _handle_5(self, payload: Dict[str, Any]):
"""Handle OP 5 Voice Server Ping. """Handle OP 5 Voice Server Ping.

View File

@ -24,9 +24,6 @@ from typing import Dict
import websockets import websockets
from logbook import Logger from logbook import Logger
import hmac
import hashlib
from litecord.voice.lvsp_opcodes import OPCodes as OP, InfoTable, InfoReverse from litecord.voice.lvsp_opcodes import OPCodes as OP, InfoTable, InfoReverse
log = Logger(__name__) log = Logger(__name__)
@ -62,7 +59,7 @@ class LVSPConnection:
"""Receive a payload.""" """Receive a payload."""
assert self.conn is not None assert self.conn is not None
msg = await self.conn.recv() msg = await self.conn.recv()
msg = json.loads(msg) msg = json.dumps(msg)
return msg return msg
async def send_op(self, opcode: int, data: dict): async def send_op(self, opcode: int, data: dict):
@ -103,15 +100,10 @@ class LVSPConnection:
"""Handle HELLO message.""" """Handle HELLO message."""
data = msg["d"] data = msg["d"]
# nonce = data['nonce']
self._hb_interval = data["heartbeat_interval"] self._hb_interval = data["heartbeat_interval"]
token = hmac.new( # TODO: send identify
self.app.config.get("LVSP_SECRET").encode(),
data["nonce"].encode(),
hashlib.sha256,
).hexdigest()
await self.send_op(OP.identify, {"token": token})
async def _update_health(self, new_health: float): async def _update_health(self, new_health: float):
"""Update the health value of a given voice server.""" """Update the health value of a given voice server."""
@ -120,7 +112,7 @@ class LVSPConnection:
await self.app.db.execute( await self.app.db.execute(
""" """
UPDATE voice_servers UPDATE voice_servers
SET last_health = $1 SET health = $1
WHERE hostname = $2 WHERE hostname = $2
""", """,
new_health, new_health,
@ -132,17 +124,13 @@ class LVSPConnection:
We only start heartbeating after READY. We only start heartbeating after READY.
""" """
data = msg["d"] await self._update_health(msg["health"])
await self._update_health(data["health"])
self._start_hb() self._start_hb()
async def _handle_5(self, msg): async def _handle_5(self, msg):
"""Handle HEARTBEAT_ACK.""" """Handle HEARTBEAT_ACK."""
self._stop_hb() self._stop_hb()
data = msg["d"] await self._update_health(msg["health"])
await self._update_health(data["health"])
self._start_hb() self._start_hb()
async def _handle_6(self, msg): async def _handle_6(self, msg):