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

215 lines
8.9 KiB
Nim
Raw Normal View History

import websocket, asyncdispatch, json, httpClient, eventdispatcher, strformat
import eventhandler, streams, nimcordutils, discordobject, user, cache, clientobjects
2020-06-20 18:59:49 +00:00
import strutils, channel, options, message, emoji, guild, embed, os
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" } } }
2020-06-20 19:29:45 +00:00
#TODO: Reconnecting. It should be pretty easy tbh.
2020-05-29 06:20:39 +00:00
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-06-20 06:33:45 +00:00
elif (event.message.content.startsWith("?testEmbed")):
var channel: Channel = event.message.getMessageChannel(event.client.cache)
if (channel != nil):
var embed = Embed()
embed.setTitle("This embed is being sent from Nimcord!")
embed.setDescription("Nimcord was developed in about a week of actual work so there will likely be issues.")
2020-06-20 17:48:59 +00:00
embed.addField("Title", "value")
embed.addField("Inline-0", "This is an inline field 0", true)
embed.addField("Inline-1", "This is an inline field 1", true)
embed.setColor(0xffb900)
2020-06-20 06:33:45 +00:00
discard channel.sendMessage("", false, embed)
2020-06-20 18:59:49 +00:00
elif (event.message.content.startsWith("?sendFile")):
var channel: Channel = event.message.getMessageChannel(event.client.cache)
if (channel != nil):
let filePath = event.message.content.substr(10)
let splitFile = splitFile(filePath)
let fileName = splitFile.name & splitFile.ext
let file = DiscordFile(filePath: filePath, fileName: fileName)
discard channel.sendMessage("", false, nil, @[file])
elif (event.message.content.startsWith("?sendImage")):
var channel: Channel = event.message.getMessageChannel(event.client.cache)
if (channel != nil):
let filePath = event.message.content.substr(11)
let splitFile = splitFile(filePath)
let fileName = splitFile.name & splitFile.ext
let file = DiscordFile(filePath: filePath, fileName: fileName)
var embed = Embed()
embed.setTitle("Image attachment test.")
embed.setImage("attachment://" & fileName)
discard channel.sendMessage("", false, embed, @[file])
2020-05-30 04:59:23 +00:00
)
waitFor bot.startConnection()