mirror of https://gitlab.com/litecord/litecord.git
Compare commits
8 Commits
fc16231b9a
...
9ca4480a22
| Author | SHA1 | Date |
|---|---|---|
|
|
9ca4480a22 | |
|
|
e85e38ddb7 | |
|
|
3d127b405f | |
|
|
c1a6d9a53a | |
|
|
21e9b782a4 | |
|
|
70a0379615 | |
|
|
7d8488adc7 | |
|
|
c579ade99c |
|
|
@ -65,6 +65,9 @@ class Config:
|
||||||
|
|
||||||
#: Postgres credentials
|
#: Postgres credentials
|
||||||
POSTGRES = {}
|
POSTGRES = {}
|
||||||
|
|
||||||
|
#: Shared secret for LVSP
|
||||||
|
LVSP_SECRET = ""
|
||||||
|
|
||||||
|
|
||||||
class Development(Config):
|
class Development(Config):
|
||||||
|
|
|
||||||
44
docs/lvsp.md
44
docs/lvsp.md
|
|
@ -8,7 +8,9 @@ LVSP runs over a *long-lived* websocket with TLS. The encoding is JSON.
|
||||||
|
|
||||||
## OP code table
|
## OP code table
|
||||||
|
|
||||||
"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 |
|
||||||
| --: | :-- | :-- |
|
| --: | :-- | :-- |
|
||||||
|
|
@ -119,15 +121,17 @@ 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 | VST\_CREATE | voice state create request |
|
| 3 | VOICE\_STATE\_CREATE | voice state create request |
|
||||||
| 4 | VST\_DONE | voice state created |
|
| 4 | VOICE\_STATE\_DONE | voice state created |
|
||||||
| 5 | VST\_UPDATE | voice state update |
|
| 5 | VOICE\_STATE\_DESTROY | voice state destroy |
|
||||||
| 6 | VST\_LEAVE | voice state leave |
|
| 6 | VOICE\_STATE\_UPDATE | voice state update |
|
||||||
|
|
||||||
### CHANNEL\_REQ
|
### CHANNEL\_REQ
|
||||||
|
|
||||||
|
|
@ -158,7 +162,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`.
|
||||||
|
|
||||||
### VST\_CREATE
|
### VOICE\_STATE\_CREATE
|
||||||
|
|
||||||
Sent by the client to create a voice state.
|
Sent by the client to create a voice state.
|
||||||
|
|
||||||
|
|
@ -168,21 +172,25 @@ 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 |
|
||||||
|
|
||||||
### VST\_DONE
|
### VOICE\_STATE\_DONE
|
||||||
|
|
||||||
Sent by the server to indicate the success of a VST\_CREATE.
|
Sent by the server to indicate the success of a VOICE\_STATE\_CREATE.
|
||||||
|
|
||||||
Has the same fields as VST\_CREATE, but with extras:
|
Has the same fields as VOICE\_STATE\_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 |
|
||||||
|
|
||||||
### VST\_DESTROY
|
### VOICE\_STATE\_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 |
|
||||||
|
|
@ -195,11 +203,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 VST\_CREATE right after as well.
|
- Client MAY send a VOICE\_STATE\_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 VST\_CREATE
|
- Process VOICE\_STATE\_CREATE
|
||||||
|
|
||||||
### Updating a voice channel
|
### Updating a voice channel
|
||||||
|
|
||||||
|
|
@ -214,15 +222,15 @@ user join is here.
|
||||||
|
|
||||||
### User joining an (initialized) voice channel
|
### User joining an (initialized) voice channel
|
||||||
|
|
||||||
- Client sends VST\_CREATE
|
- Client sends VOICE\_STATE\_CREATE
|
||||||
- Server sends VST\_DONE
|
- Server sends VOICE\_STATE\_DONE
|
||||||
|
|
||||||
### User leaves a channel
|
### User leaves a channel
|
||||||
|
|
||||||
- Client sends VST\_DESTROY with the old fields
|
- Client sends VOICE\_STATE\_DESTROY with the old fields
|
||||||
|
|
||||||
### User moves a channel
|
### User moves a channel
|
||||||
|
|
||||||
- Client sends VST\_DESTROY with the old fields
|
- Client sends VOICE\_STATE\_DESTROY with the old fields
|
||||||
- Client sends VST\_CREATE with the new fields
|
- Client sends VOICE\_STATE\_CREATE with the new fields
|
||||||
- Server sends VST\_DONE
|
- Server sends VOICE\_STATE\_DONE
|
||||||
|
|
|
||||||
|
|
@ -887,6 +887,9 @@ 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)
|
||||||
|
|
||||||
|
|
@ -904,6 +907,10 @@ 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.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,9 @@ 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__)
|
||||||
|
|
@ -59,7 +62,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.dumps(msg)
|
msg = json.loads(msg)
|
||||||
return msg
|
return msg
|
||||||
|
|
||||||
async def send_op(self, opcode: int, data: dict):
|
async def send_op(self, opcode: int, data: dict):
|
||||||
|
|
@ -100,10 +103,15 @@ 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"]
|
||||||
|
|
||||||
# TODO: send identify
|
token = hmac.new(
|
||||||
|
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."""
|
||||||
|
|
@ -112,7 +120,7 @@ class LVSPConnection:
|
||||||
await self.app.db.execute(
|
await self.app.db.execute(
|
||||||
"""
|
"""
|
||||||
UPDATE voice_servers
|
UPDATE voice_servers
|
||||||
SET health = $1
|
SET last_health = $1
|
||||||
WHERE hostname = $2
|
WHERE hostname = $2
|
||||||
""",
|
""",
|
||||||
new_health,
|
new_health,
|
||||||
|
|
@ -124,13 +132,17 @@ class LVSPConnection:
|
||||||
|
|
||||||
We only start heartbeating after READY.
|
We only start heartbeating after READY.
|
||||||
"""
|
"""
|
||||||
await self._update_health(msg["health"])
|
data = msg["d"]
|
||||||
|
|
||||||
|
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()
|
||||||
await self._update_health(msg["health"])
|
data = msg["d"]
|
||||||
|
|
||||||
|
await self._update_health(data["health"])
|
||||||
self._start_hb()
|
self._start_hb()
|
||||||
|
|
||||||
async def _handle_6(self, msg):
|
async def _handle_6(self, msg):
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue