2020-06-17 05:34:23 +00:00
|
|
|
import websocket, asyncnet, asyncdispatch, json, httpClient, eventdispatcher, strformat, eventhandler, streams, nimcordutils, discordobject
|
2020-05-28 05:50:53 +00:00
|
|
|
|
|
|
|
type
|
2020-05-29 06:20:39 +00:00
|
|
|
DiscordOpCode = enum
|
|
|
|
opDispatch = 0,
|
|
|
|
opHeartbeat = 1,
|
|
|
|
opIdentify = 2,
|
|
|
|
opPresenceUpdate = 3,
|
|
|
|
opVoiceStateUpdate = 4,
|
|
|
|
opResume = 6,
|
|
|
|
opReconnect = 7,
|
|
|
|
opRequestGuildMembers = 8,
|
|
|
|
opInvalidSession = 9,
|
|
|
|
opHello = 10,
|
|
|
|
opHeartbeatAck = 11
|
|
|
|
|
2020-05-28 05:50:53 +00:00
|
|
|
DiscordClient* = ref object ## Discord Client
|
|
|
|
token*: string
|
|
|
|
#user*: User
|
|
|
|
#cache: Cache
|
|
|
|
ws: AsyncWebSocket
|
|
|
|
httpClient: AsyncHttpClient
|
2020-05-29 06:20:39 +00:00
|
|
|
heartbeatInterval: int
|
|
|
|
heartbeatAcked: bool
|
|
|
|
lastSequence: int
|
|
|
|
|
2020-06-17 05:34:23 +00:00
|
|
|
var globalClient: DiscordClient
|
|
|
|
|
|
|
|
proc defaultHeaders*(client: DiscordClient, added: HttpHeaders = newHttpHeaders()): HttpHeaders =
|
|
|
|
added.add("Authorization", fmt("Bot {client.token}"))
|
|
|
|
added.add("User-Agent", "NimCord (https://github.com/SeanOMik/nimcord, v0.0.0)")
|
|
|
|
added.add("X-RateLimit-Precision", "millisecond")
|
|
|
|
return added;
|
|
|
|
|
|
|
|
proc sendRequest*(endpoint: string, httpMethod: HttpMethod, headers: HttpHeaders, objectID: snowflake = 0, bucketType: RateLimitBucketType = global, jsonBody: JsonNode = %*{}): JsonNode =
|
|
|
|
var client = newHttpClient()
|
|
|
|
# Add headers
|
|
|
|
client.headers = headers
|
|
|
|
|
|
|
|
var strPayload: string
|
|
|
|
if ($jsonBody == "{}"):
|
|
|
|
strPayload = ""
|
|
|
|
else:
|
|
|
|
strPayload = $jsonBody
|
|
|
|
|
|
|
|
echo "Sending GET request, URL: ", endpoint, ", body: ", strPayload
|
|
|
|
|
|
|
|
waitForRateLimits(objectID, bucketType)
|
|
|
|
|
|
|
|
return handleResponse(client.request(endpoint, httpMethod, strPayload), objectId, bucketType)
|
|
|
|
|
2020-05-29 06:20:39 +00:00
|
|
|
proc sendGatewayRequest*(client: DiscordClient, request: JsonNode, msg: string = "") {.async.} =
|
|
|
|
if (msg.len == 0):
|
|
|
|
echo "Sending gateway payload: ", request
|
|
|
|
else:
|
|
|
|
echo msg
|
|
|
|
|
|
|
|
discard client.ws.sendText($request)
|
2020-05-28 05:50:53 +00:00
|
|
|
|
2020-05-29 06:20:39 +00:00
|
|
|
proc handleHeartbeat(client: DiscordClient) {.async.} =
|
2020-05-28 05:50:53 +00:00
|
|
|
while true:
|
2020-05-29 06:20:39 +00:00
|
|
|
var heartbeatPayload: JsonNode
|
|
|
|
if (client.lastSequence == 0):
|
|
|
|
heartbeatPayload = %* { "d": nil, "op": ord(DiscordOpCode.opHeartbeat) }
|
|
|
|
else:
|
|
|
|
heartbeatPayload = %* { "d": client.lastSequence, "op": ord(DiscordOpCode.opHeartbeat) }
|
2020-05-28 05:50:53 +00:00
|
|
|
|
2020-05-29 06:20:39 +00:00
|
|
|
await client.sendGatewayRequest(heartbeatPayload, fmt("Sending heartbeat payload: {$heartbeatPayload}"))
|
|
|
|
client.heartbeatAcked = true
|
|
|
|
|
|
|
|
echo "Waiting ", client.heartbeatInterval, " ms until next heartbeat..."
|
|
|
|
await sleepAsync(client.heartbeatInterval)
|
|
|
|
|
|
|
|
proc getIdentifyPacket(client: DiscordClient): JsonNode =
|
|
|
|
return %* { "op": ord(DiscordOpCode.opIdentify), "d": { "token": client.token, "properties": { "$os": system.hostOS, "$browser": "NimCord", "$device": "NimCord" } } }
|
|
|
|
|
|
|
|
proc handleWebsocketPacket(client: DiscordClient) {.async.} =
|
2020-05-28 05:50:53 +00:00
|
|
|
while true:
|
|
|
|
var packet: tuple[opcode: Opcode, data: string]
|
|
|
|
|
|
|
|
packet = await client.ws.readData();
|
2020-05-29 06:20:39 +00:00
|
|
|
echo "Received gateway payload: ", packet.data
|
2020-05-28 05:50:53 +00:00
|
|
|
|
|
|
|
var json: JsonNode = parseJson(packet.data);
|
|
|
|
|
2020-05-29 06:20:39 +00:00
|
|
|
if (json.contains("s")):
|
|
|
|
client.lastSequence = json["s"].getInt()
|
|
|
|
|
|
|
|
case json["op"].getInt()
|
|
|
|
of ord(DiscordOpCode.opHello):
|
|
|
|
client.heartbeatInterval = json["d"]["heartbeat_interval"].getInt()
|
|
|
|
discard client.sendGatewayRequest(client.getIdentifyPacket())
|
|
|
|
|
|
|
|
asyncCheck client.handleHeartbeat()
|
|
|
|
client.heartbeatAcked = true
|
|
|
|
of ord(DiscordOpCode.opHeartbeatAck):
|
|
|
|
client.heartbeatAcked = true
|
2020-05-30 04:59:23 +00:00
|
|
|
of ord(DiscordOpCode.opDispatch):
|
|
|
|
handleDiscordEvent(json["d"], json["t"].getStr())
|
2020-05-28 05:50:53 +00:00
|
|
|
else:
|
|
|
|
discard
|
|
|
|
|
2020-05-29 06:20:39 +00:00
|
|
|
proc startConnection*(client: DiscordClient) {.async.} =
|
2020-06-17 05:34:23 +00:00
|
|
|
globalClient = client
|
|
|
|
|
2020-05-28 05:50:53 +00:00
|
|
|
client.httpClient = newAsyncHttpClient()
|
|
|
|
client.httpClient.headers = newHttpHeaders({"Authorization": fmt("Bot {client.token}")})
|
|
|
|
|
2020-06-17 05:34:23 +00:00
|
|
|
let urlResult = sendRequest(endpoint("/gateway/bot"), HttpMethod.HttpGet, client.defaultHeaders())
|
2020-05-29 06:20:39 +00:00
|
|
|
if (urlResult.contains("url")):
|
|
|
|
let url = urlResult["url"].getStr()
|
2020-05-28 05:50:53 +00:00
|
|
|
|
2020-06-17 05:34:23 +00:00
|
|
|
client.ws = await newAsyncWebsocketClient(url[6..url.high], Port 443,
|
2020-05-28 05:50:53 +00:00
|
|
|
path = "/v=6&encoding=json", true)
|
|
|
|
echo "Connected!"
|
|
|
|
|
2020-05-29 06:20:39 +00:00
|
|
|
asyncCheck client.handleWebsocketPacket()
|
2020-05-28 05:50:53 +00:00
|
|
|
runForever()
|
|
|
|
else:
|
|
|
|
var e: ref IOError
|
|
|
|
new(e)
|
|
|
|
e.msg = "Failed to get gateway url, token may of been incorrect!"
|
|
|
|
raise e
|
|
|
|
|
2020-05-31 06:14:56 +00:00
|
|
|
var tokenStream = newFileStream("token.txt", fmRead)
|
2020-05-31 06:13:57 +00:00
|
|
|
var tkn: string
|
|
|
|
if (not isNil(tokenStream)):
|
|
|
|
discard tokenStream.readLine(tkn)
|
2020-05-31 06:14:56 +00:00
|
|
|
echo "Read token from the file: ", tkn
|
|
|
|
|
2020-05-31 06:13:57 +00:00
|
|
|
tokenStream.close()
|
|
|
|
|
|
|
|
var bot = DiscordClient(token: tkn)
|
|
|
|
|
|
|
|
registerEventListener(EventType.evtReady, proc(bEvt: BaseEvent) =
|
|
|
|
let event = ReadyEvent(bEvt)
|
|
|
|
echo "Ready and connected 1!"
|
|
|
|
)
|
2020-05-30 04:59:23 +00:00
|
|
|
|
|
|
|
registerEventListener(EventType.evtReady, proc(bEvt: BaseEvent) =
|
2020-05-31 06:13:57 +00:00
|
|
|
let event = ReadyEvent(bEvt)
|
|
|
|
echo "Ready and connected 2!"
|
|
|
|
)
|
|
|
|
|
|
|
|
registerEventListener(EventType.evtMessageCreate, proc(bEvt: BaseEvent) =
|
|
|
|
let event = MessageCreateEvent(bEvt)
|
|
|
|
echo "Message was created!"
|
2020-05-30 04:59:23 +00:00
|
|
|
)
|
|
|
|
|
2020-05-28 05:50:53 +00:00
|
|
|
waitFor bot.startConnection()
|