This repository has been archived on 2023-04-26. You can view files and clone it, but cannot push or open issues or pull requests.
nimcord/src/client.nim

179 lines
7.0 KiB
Nim
Raw Normal View History

import websocket, asyncdispatch, json, httpClient, eventdispatcher, strformat
import eventhandler, streams, nimcordutils, discordobject, user, cache, clientobjects
import strutils, channel, options, message, emoji, guild
const
nimcordMajor = 0
nimcordMinor = 0
nimcordMicro = 0
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
proc sendGatewayRequest*(client: DiscordClient, request: JsonNode, msg: string = "") {.async.} =
## Send a gateway request.
## Don't use this unless you know what you're doing!
2020-05-29 06:20:39 +00:00
if (msg.len == 0):
echo "Sending gateway payload: ", request
else:
echo msg
discard client.ws.sendText($request)
2020-05-29 06:20:39 +00:00
proc handleHeartbeat(client: DiscordClient) {.async.} =
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-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.} =
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
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):
asyncCheck(handleDiscordEvent(client, json["d"], json["t"].getStr()))
else:
discard
2020-05-29 06:20:39 +00:00
proc startConnection*(client: DiscordClient) {.async.} =
## Start a bot connection.
##
## Examples:
##
## .. code-block:: nim
## var tokenStream = newFileStream("token.txt", fmRead)
## var tkn: string
## if (not isNil(tokenStream)):
## discard tokenStream.readLine(tkn)
## echo "Read token from the file: ", tkn
##
## tokenStream.close()
##
## var bot = newDiscordClient(tkn)
let urlResult = sendRequest(endpoint("/gateway/bot"), HttpMethod.HttpGet, defaultHeaders())
2020-05-29 06:20:39 +00:00
if (urlResult.contains("url")):
let url = urlResult["url"].getStr()
2020-06-17 05:34:23 +00:00
client.ws = await newAsyncWebsocketClient(url[6..url.high], Port 443,
path = "/v=6&encoding=json", true)
echo "Connected!"
2020-05-29 06:20:39 +00:00
asyncCheck client.handleWebsocketPacket()
runForever()
else:
2020-06-18 04:50:17 +00:00
raise newException(IOError, "Failed to get gateway url, token may of been incorrect!")
proc newDiscordClient(tkn: string): DiscordClient =
## Create a DiscordClient using a token.
##
## Sets globalDiscordClient to the newly created client.
globalToken = tkn
var cac: Cache
new(cac)
result = DiscordClient(token: tkn, cache: cac)
globalDiscordClient = result
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)
echo "Read token from the file: ", tkn
2020-05-31 06:13:57 +00:00
tokenStream.close()
var bot = newDiscordClient(tkn)
2020-05-31 06:13:57 +00:00
registerEventListener(EventType.evtReady, proc(bEvt: BaseEvent) =
let event = ReadyEvent(bEvt)
bot.clientUser = event.clientUser
2020-05-30 04:59:23 +00:00
echo "Ready! (v", nimcordMajor, ".", nimcordMinor, ".", nimcordMicro, ")"
echo "Logged in as: ", bot.clientUser.username, "#", bot.clientUser.discriminator
echo "ID: ", bot.clientUser.id
echo "--------------------"
2020-05-31 06:13:57 +00:00
)
registerEventListener(EventType.evtMessageCreate, proc(bEvt: BaseEvent) =
let event = MessageCreateEvent(bEvt)
if (event.message.content == "?ping"):
var channel: Channel = event.message.getMessageChannel(event.client.cache)
if (channel != nil):
discard channel.sendMessage("PONG")
elif (event.message.content.startsWith("?modifyChannelTopic")):
let modifyTopic = event.message.content.substr(20)
2020-06-18 04:51:38 +00:00
var channel: Channel = event.message.getMessageChannel(event.client.cache)
if (channel != nil):
discard channel.sendMessage("Modifing Channel!")
discard channel.modifyChannel(ChannelFields(topic: some(modifyTopic)))
elif (event.message.content.startsWith("?deleteChannel")):
let channelID = getIDFromJson(event.message.content.substr(15))
var channel: Channel = event.client.cache.getChannel(channelID)
2020-06-18 04:51:38 +00:00
if (channel != nil):
discard channel.sendMessage("Deleting Channel!")
discard channel.deleteChannel()
discard channel.sendMessage("Deleted Channel!")
2020-06-18 04:51:38 +00:00
elif (event.message.content.startsWith("?getMessages")):
var channel: Channel = event.message.getMessageChannel(event.client.cache)
if (channel != nil):
discard channel.getMessages(MessagesGetRequest(limit: some(15), before: some(event.message.id)))
elif (event.message.content.startsWith("?bulkDeleteMessages")):
var channel: Channel = event.message.getMessageChannel(event.client.cache)
if (channel != nil):
var amount: int = 25
if (event.message.content.len > 19):
amount = parseIntEasy(event.message.content.substr(20))
let messages = channel.getMessages(MessagesGetRequest(limit: some(amount), before: some(event.message.id)))
discard channel.bulkDeleteMessages(messages)
elif (event.message.content.startsWith("?reactToMessage")):
var channel: Channel = event.message.getMessageChannel(event.client.cache)
if (channel != nil):
let emojis = @[newEmoji("⏮️"), newEmoji("⬅️"), newEmoji("⏹️"), newEmoji("➡️"), newEmoji("⏭️")]
for emoji in emojis:
discard event.message.addReaction(emoji)
2020-05-30 04:59:23 +00:00
)
waitFor bot.startConnection()