seyfert/vendor/gateway/shard/startHeartbeating.ts
2022-06-21 20:25:17 -05:00

65 lines
2.7 KiB
TypeScript

import { GatewayOpcodes } from "../../types/shared.ts";
import { Shard, ShardSocketCloseCodes, ShardState } from "./types.ts";
export function startHeartbeating(shard: Shard, interval: number) {
// gateway.debug("GW HEARTBEATING_STARTED", { shardId, interval });
shard.heart.interval = interval;
// Only set the shard's state to `Unidentified`
// if heartbeating has not been started due to an identify or resume action.
if ([ShardState.Disconnected, ShardState.Offline].includes(shard.state)) {
shard.state = ShardState.Unidentified;
}
// The first heartbeat needs to be send with a random delay between `0` and `interval`
// Using a `setTimeout(_, jitter)` here to accomplish that.
// `Math.random()` can be `0` so we use `0.5` if this happens
// Reference: https://discord.com/developers/docs/topics/gateway#heartbeating
const jitter = Math.ceil(shard.heart.interval * (Math.random() || 0.5));
shard.heart.timeoutId = setTimeout(() => {
// Using a direct socket.send call here because heartbeat requests are reserved by us.
shard.socket?.send(JSON.stringify({
op: GatewayOpcodes.Heartbeat,
d: shard.previousSequenceNumber,
}));
shard.heart.lastBeat = Date.now();
shard.heart.acknowledged = false;
// After the random heartbeat jitter we can start a normal interval.
shard.heart.intervalId = setInterval(async () => {
// gateway.debug("GW DEBUG", `Running setInterval in heartbeat file. Shard: ${shardId}`);
// gateway.debug("GW HEARTBEATING", { shardId, shard: currentShard });
// The Shard did not receive a heartbeat ACK from Discord in time,
// therefore we have to assume that the connection has failed or got "zombied".
// The Shard needs to start a re-identify action accordingly.
// Reference: https://discord.com/developers/docs/topics/gateway#heartbeating-example-gateway-heartbeat-ack
if (!shard.heart.acknowledged) {
shard.close(
ShardSocketCloseCodes.ZombiedConnection,
"Zombied connection, did not receive an heartbeat ACK in time.",
);
return await shard.identify();
}
shard.heart.acknowledged = false;
// Using a direct socket.send call here because heartbeat requests are reserved by us.
shard.socket?.send(
JSON.stringify({
op: GatewayOpcodes.Heartbeat,
d: shard.previousSequenceNumber,
}),
);
shard.heart.lastBeat = Date.now();
shard.events.heartbeat?.(shard);
}, shard.heart.interval);
}, jitter);
}