Get reconnecting done, needs testing!
This commit is contained in:
parent
0c3f1d040c
commit
133975b59d
|
@ -29,7 +29,7 @@ proc sendGatewayRequest*(client: DiscordClient, request: JsonNode, msg: string =
|
||||||
else:
|
else:
|
||||||
echo msg
|
echo msg
|
||||||
|
|
||||||
discard client.ws.sendText($request)
|
await client.ws.sendText($request)
|
||||||
|
|
||||||
proc handleHeartbeat(client: DiscordClient) {.async.} =
|
proc handleHeartbeat(client: DiscordClient) {.async.} =
|
||||||
while true:
|
while true:
|
||||||
|
@ -48,7 +48,37 @@ proc handleHeartbeat(client: DiscordClient) {.async.} =
|
||||||
proc getIdentifyPacket(client: DiscordClient): JsonNode =
|
proc getIdentifyPacket(client: DiscordClient): JsonNode =
|
||||||
return %* { "op": ord(DiscordOpCode.opIdentify), "d": { "token": client.token, "properties": { "$os": system.hostOS, "$browser": "NimCord", "$device": "NimCord" } } }
|
return %* { "op": ord(DiscordOpCode.opIdentify), "d": { "token": client.token, "properties": { "$os": system.hostOS, "$browser": "NimCord", "$device": "NimCord" } } }
|
||||||
|
|
||||||
#TODO: Reconnecting. It should be pretty easy tbh.
|
proc startConnection*(client: DiscordClient) {.async.}
|
||||||
|
|
||||||
|
proc closeConnection*(client: DiscordClient, code: int = 1000) {.async.} =
|
||||||
|
echo "Disconnecting with code: ", code
|
||||||
|
await client.ws.close(code)
|
||||||
|
|
||||||
|
proc reconnectClient(client: DiscordClient) {.async.} =
|
||||||
|
client.reconnecting = true
|
||||||
|
await client.ws.close(1000)
|
||||||
|
waitFor client.startConnection()
|
||||||
|
|
||||||
|
# Handle discord disconnect. If it detects that we can reconnect, it will.
|
||||||
|
proc handleDiscordDisconnect(client: DiscordClient, error: string) {.async.} =
|
||||||
|
let disconnectData = extractCloseData(error)
|
||||||
|
|
||||||
|
echo "Discord gateway disconnected! Error code: ", disconnectData.code, ", msg: ", disconnectData.reason
|
||||||
|
|
||||||
|
client.heartbeatAcked = false
|
||||||
|
|
||||||
|
# The disconnect code
|
||||||
|
let c = disconnectData.code
|
||||||
|
|
||||||
|
# 4003, 4004, 4005, 4007, 4010, 4011, 4012, 4013 are not reconnectable.
|
||||||
|
if ( (c >= 4003 and c <= 4005) or c == 4007 or (c >= 4010 and c <= 4013) ):
|
||||||
|
echo "The Discord gateway sent a disconnect code that we cannot reconnect to."
|
||||||
|
else:
|
||||||
|
if (not client.reconnecting):
|
||||||
|
await client.reconnectClient()
|
||||||
|
|
||||||
|
|
||||||
|
#TODO: Reconnecting may be done, just needs testing.
|
||||||
proc handleWebsocketPacket(client: DiscordClient) {.async.} =
|
proc handleWebsocketPacket(client: DiscordClient) {.async.} =
|
||||||
while true:
|
while true:
|
||||||
var packet: tuple[opcode: Opcode, data: string]
|
var packet: tuple[opcode: Opcode, data: string]
|
||||||
|
@ -56,22 +86,58 @@ proc handleWebsocketPacket(client: DiscordClient) {.async.} =
|
||||||
packet = await client.ws.readData();
|
packet = await client.ws.readData();
|
||||||
echo "Received gateway payload: ", packet.data
|
echo "Received gateway payload: ", packet.data
|
||||||
|
|
||||||
var json: JsonNode = parseJson(packet.data);
|
if packet.opcode == Opcode.Close:
|
||||||
|
asyncCheck client.handleDiscordDisconnect(packet.data)
|
||||||
|
|
||||||
|
var json: JsonNode
|
||||||
|
|
||||||
|
# If we fail to parse the json just stop this loop
|
||||||
|
try:
|
||||||
|
json = parseJson(packet.data);
|
||||||
|
except:
|
||||||
|
echo "Failed to parse websocket payload: ", packet.data
|
||||||
|
continue
|
||||||
|
|
||||||
if (json.contains("s")):
|
if (json.contains("s")):
|
||||||
client.lastSequence = json["s"].getInt()
|
client.lastSequence = json["s"].getInt()
|
||||||
|
|
||||||
case json["op"].getInt()
|
case json["op"].getInt()
|
||||||
of ord(DiscordOpCode.opHello):
|
of ord(DiscordOpCode.opHello):
|
||||||
|
if client.reconnecting:
|
||||||
|
echo "Reconnected!"
|
||||||
|
client.reconnecting = false
|
||||||
|
|
||||||
|
let resume = %* {
|
||||||
|
"op": opResume,
|
||||||
|
"session_id": client.sessionID,
|
||||||
|
"seq": client.lastSequence
|
||||||
|
}
|
||||||
|
|
||||||
|
await client.sendGatewayRequest(resume)
|
||||||
|
else:
|
||||||
client.heartbeatInterval = json["d"]["heartbeat_interval"].getInt()
|
client.heartbeatInterval = json["d"]["heartbeat_interval"].getInt()
|
||||||
discard client.sendGatewayRequest(client.getIdentifyPacket())
|
await client.sendGatewayRequest(client.getIdentifyPacket())
|
||||||
|
|
||||||
asyncCheck client.handleHeartbeat()
|
asyncCheck client.handleHeartbeat()
|
||||||
client.heartbeatAcked = true
|
client.heartbeatAcked = true
|
||||||
of ord(DiscordOpCode.opHeartbeatAck):
|
of ord(DiscordOpCode.opHeartbeatAck):
|
||||||
client.heartbeatAcked = true
|
client.heartbeatAcked = true
|
||||||
of ord(DiscordOpCode.opDispatch):
|
of ord(DiscordOpCode.opDispatch):
|
||||||
asyncCheck(handleDiscordEvent(client, json["d"], json["t"].getStr()))
|
asyncCheck handleDiscordEvent(client, json["d"], json["t"].getStr())
|
||||||
|
of ord(DiscordOpCode.opReconnect):
|
||||||
|
asyncCheck client.reconnectClient()
|
||||||
|
of ord(DiscordOpCode.opInvalidSession):
|
||||||
|
# If the json field `d` is true then the session may be resumable.
|
||||||
|
if json["d"].getBool():
|
||||||
|
let resume = %* {
|
||||||
|
"op": opResume,
|
||||||
|
"session_id": client.sessionID,
|
||||||
|
"seq": client.lastSequence
|
||||||
|
}
|
||||||
|
|
||||||
|
await client.sendGatewayRequest(resume)
|
||||||
|
else:
|
||||||
|
asyncCheck client.reconnectClient()
|
||||||
else:
|
else:
|
||||||
discard
|
discard
|
||||||
|
|
||||||
|
@ -90,14 +156,18 @@ proc startConnection*(client: DiscordClient) {.async.} =
|
||||||
## tokenStream.close()
|
## tokenStream.close()
|
||||||
##
|
##
|
||||||
## var bot = newDiscordClient(tkn)
|
## var bot = newDiscordClient(tkn)
|
||||||
|
echo "Connecting..."
|
||||||
|
|
||||||
let urlResult = sendRequest(endpoint("/gateway/bot"), HttpMethod.HttpGet, defaultHeaders())
|
let urlResult = sendRequest(endpoint("/gateway/bot"), HttpMethod.HttpGet, defaultHeaders())
|
||||||
if (urlResult.contains("url")):
|
if (urlResult.contains("url")):
|
||||||
|
|
||||||
let url = urlResult["url"].getStr()
|
let url = urlResult["url"].getStr()
|
||||||
|
client.endpoint = url
|
||||||
|
|
||||||
client.ws = await newAsyncWebsocketClient(url[6..url.high], Port 443,
|
client.ws = await newAsyncWebsocketClient(url[6..url.high], Port 443,
|
||||||
path = "/v=6&encoding=json", true)
|
path = "/v=6&encoding=json", true)
|
||||||
echo "Connected!"
|
|
||||||
|
|
||||||
|
if not client.reconnecting:
|
||||||
asyncCheck client.handleWebsocketPacket()
|
asyncCheck client.handleWebsocketPacket()
|
||||||
runForever()
|
runForever()
|
||||||
else:
|
else:
|
||||||
|
@ -219,6 +289,12 @@ registerEventListener(EventType.evtMessageCreate, proc(bEvt: BaseEvent) =
|
||||||
embed.setTitle("Image attachment test.")
|
embed.setTitle("Image attachment test.")
|
||||||
embed.setImage("attachment://" & fileName)
|
embed.setImage("attachment://" & fileName)
|
||||||
discard channel.sendMessage("", false, embed, @[file])
|
discard channel.sendMessage("", false, embed, @[file])
|
||||||
|
elif (event.message.content.startsWith("?reconnect")):
|
||||||
|
var channel: Channel = event.message.getMessageChannel(event.client.cache)
|
||||||
|
if (channel != nil):
|
||||||
|
discard channel.sendMessage("Reconnecting...")
|
||||||
|
#asyncCheck event.client.reconnectClient()
|
||||||
|
asyncCheck event.client.ws.sendText("aaaa")
|
||||||
)
|
)
|
||||||
|
|
||||||
waitFor bot.startConnection()
|
waitFor bot.startConnection()
|
|
@ -12,10 +12,10 @@ proc readyEvent(discordClient: DiscordClient, json: JsonNode) =
|
||||||
echo "Sending GET request, URL: body: {}"
|
echo "Sending GET request, URL: body: {}"
|
||||||
|
|
||||||
waitForRateLimits(0, RateLimitBucketType.global)
|
waitForRateLimits(0, RateLimitBucketType.global)
|
||||||
var json = handleResponse(client.request(endpoint("/users/@me"), HttpGet, ""), 0, RateLimitBucketType.global)
|
var userJson = handleResponse(client.request(endpoint("/users/@me"), HttpGet, ""), 0, RateLimitBucketType.global)
|
||||||
|
|
||||||
let clientUser: User = newUser(json)
|
discordClient.clientUser = newUser(userJson)
|
||||||
readyEvent.clientUser = clientUser
|
discordClient.sessionID = json["session_id"].getStr()
|
||||||
|
|
||||||
dispatchEvent(readyEvent)
|
dispatchEvent(readyEvent)
|
||||||
|
|
||||||
|
|
Reference in New Issue