///|
/// A decoded Gateway payload after Hello, reduced to what the read loop acts
/// on. Malformed and unhandled payloads decode to `Ignored`.
priv enum Frame {
Dispatch(t~ : String, seq~ : Int64?, d~ : Json)
HeartbeatRequest
Reconnect
InvalidSession(resumable~ : Bool)
HeartbeatAck
Ignored
}
///|
/// The heartbeat interval from opcode 10 Hello, or `None` for any other
/// payload.
fn decode_hello(frame : Json) -> Int32? {
guard frame
is {
"op": Number(10, ..),
"d": { "heartbeat_interval": Number(interval, ..), .. },
..
} else {
return None
}
Some(interval.to_int())
}
///|
fn decode_frame(frame : Json) -> Frame {
guard frame is { "op": Number(op, ..), .. } else { return Ignored }
match op.to_int() {
0 => {
guard frame is { "t": String(t), "s": s, "d": d, .. } else {
return Ignored
}
let seq = if s is Number(seq, ..) { Some(seq.to_int64()) } else { None }
Dispatch(t~, seq~, d~)
}
1 => HeartbeatRequest
7 => Reconnect
9 => InvalidSession(resumable=frame is { "d": True, .. })
11 => HeartbeatAck
_ => Ignored
}
}
///|
/// The session READY opens; a READY without a sequence starts at 0.
fn ready_session(d : Json, seq : Int64?) -> Session? {
guard d
is {
"session_id": String(session_id),
"resume_gateway_url": String(resume_url),
..
} else {
return None
}
Some({ id: session_id, resume_url, sequence: seq.unwrap_or(0L), })
}
///|
/// Opcode 2 Identify. Capabilities are sent only when opted in.
fn identify_payload(
token~ : String,
intents~ : @model.Intents,
shard_id~ : Int32,
shard_count~ : Int32,
capabilities~ : @model.GatewayCapabilities,
presence~ : @model.UpdatePresence?,
) -> Json {
let data : Map[String, Json] = {
"token": Json::string(token),
"intents": intents.to_json(),
"shard": [
Json::number(shard_id.to_double()),
Json::number(shard_count.to_double()),
],
"properties": {
"os": Json::string(identify_os()),
"browser": "discord.mbt",
"device": "discord.mbt",
},
}
if capabilities != @model.GatewayCapabilities::none() {
data["capabilities"] = capabilities.to_json()
}
if presence is Some(presence) {
data["presence"] = presence.to_json()
}
{ "op": 2, "d": Json::object(data) }
}
///|
/// Opcode 6 Resume.
fn resume_payload(token~ : String, session : Session) -> Json {
{
"op": 6,
"d": {
"token": Json::string(token),
"session_id": Json::string(session.id),
"seq": Json::number(session.sequence.to_double()),
},
}
}