This commit is contained in:
SeanOMik 2020-06-23 00:08:25 -05:00
parent 1fe136186d
commit 8a9436f831
No known key found for this signature in database
GPG Key ID: FA4D55AC05268A88
6 changed files with 258 additions and 205 deletions

View File

@ -45,6 +45,6 @@ You can view examples in the [examples](examples) directory.
- [ ] Channel
- [ ] Guild
- [ ] Misc.
- [ ] Sharding.
- [x] Sharding.
- [ ] Audit log.
- [ ] Voice.

View File

@ -1,4 +1,4 @@
import nimcord, asyncdispatch, streams, os, strutils, options
import ../src/nimcord, asyncdispatch, streams, os, strutils, options, websocket
var tokenStream = newFileStream("token.txt", fmRead)
var tkn: string
@ -19,37 +19,37 @@ registerEventListener(EventType.evtReady, proc(bEvt: BaseEvent) =
echo "--------------------"
let presence = newPresence("with Nimcord", activityTypeGame, clientStatusIdle, false)
asyncCheck event.client.updateClientPresence(presence)
asyncCheck event.shard.updateClientPresence(presence)
)
registerEventListener(EventType.evtMessageCreate, proc(bEvt: BaseEvent) =
let event = MessageCreateEvent(bEvt)
if (event.message.content == "?ping"):
var channel: Channel = event.message.getMessageChannel(event.client.cache)
var channel: Channel = event.message.getMessageChannel(event.shard.client.cache)
if (channel != nil):
discard channel.sendMessage("PONG")
elif (event.message.content.startsWith("?modifyChannelTopic")):
let modifyTopic = event.message.content.substr(20)
var channel: Channel = event.message.getMessageChannel(event.client.cache)
var channel: Channel = event.message.getMessageChannel(event.shard.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)
var channel: Channel = event.shard.client.cache.getChannel(channelID)
if (channel != nil):
discard channel.sendMessage("Deleting Channel!")
discard channel.deleteChannel()
discard channel.sendMessage("Deleted Channel!")
elif (event.message.content.startsWith("?getMessages")):
var channel: Channel = event.message.getMessageChannel(event.client.cache)
var channel: Channel = event.message.getMessageChannel(event.shard.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)
var channel: Channel = event.message.getMessageChannel(event.shard.client.cache)
if (channel != nil):
var amount: int = 25
if (event.message.content.len > 19):
@ -57,13 +57,13 @@ registerEventListener(EventType.evtMessageCreate, proc(bEvt: BaseEvent) =
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)
var channel: Channel = event.message.getMessageChannel(event.shard.client.cache)
if (channel != nil):
let emojis = @[newEmoji("⏮️"), newEmoji("⬅️"), newEmoji("⏹️"), newEmoji("➡️"), newEmoji("⏭️")]
for emoji in emojis:
discard event.message.addReaction(emoji)
elif (event.message.content.startsWith("?testEmbed")):
var channel: Channel = event.message.getMessageChannel(event.client.cache)
var channel: Channel = event.message.getMessageChannel(event.shard.client.cache)
if (channel != nil):
var embed = Embed()
embed.setTitle("This embed is being sent from Nimcord!")
@ -74,7 +74,7 @@ registerEventListener(EventType.evtMessageCreate, proc(bEvt: BaseEvent) =
embed.setColor(0xffb900)
discard channel.sendMessage("", false, embed)
elif (event.message.content.startsWith("?sendFile")):
var channel: Channel = event.message.getMessageChannel(event.client.cache)
var channel: Channel = event.message.getMessageChannel(event.shard.client.cache)
if (channel != nil):
let filePath = event.message.content.substr(10)
@ -84,7 +84,7 @@ registerEventListener(EventType.evtMessageCreate, proc(bEvt: BaseEvent) =
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)
var channel: Channel = event.message.getMessageChannel(event.shard.client.cache)
if (channel != nil):
let filePath = event.message.content.substr(11)
@ -99,4 +99,4 @@ registerEventListener(EventType.evtMessageCreate, proc(bEvt: BaseEvent) =
discard channel.sendMessage("", false, embed, @[file])
)
waitFor bot.startConnection()
waitFor bot.startConnection(2)

View File

@ -16,7 +16,7 @@ type
opHello = 10,
opHeartbeatAck = 11
proc sendGatewayRequest*(client: DiscordClient, request: JsonNode, msg: string = "") {.async.} =
proc sendGatewayRequest*(shard: Shard, request: JsonNode, msg: string = "") {.async.} =
## Send a gateway request.
## Don't use this unless you know what you're doing!
if (msg.len == 0):
@ -24,50 +24,62 @@ proc sendGatewayRequest*(client: DiscordClient, request: JsonNode, msg: string =
else:
echo msg
await client.ws.sendText($request)
await shard.ws.sendText($request)
proc handleHeartbeat(client: DiscordClient) {.async.} =
proc handleHeartbeat(shard: Shard) {.async.} =
while true:
var heartbeatPayload: JsonNode
if (client.lastSequence == 0):
if (shard.lastSequence == 0):
heartbeatPayload = %* { "d": nil, "op": ord(DiscordOpCode.opHeartbeat) }
else:
heartbeatPayload = %* { "d": client.lastSequence, "op": ord(DiscordOpCode.opHeartbeat) }
heartbeatPayload = %* { "d": shard.lastSequence, "op": ord(DiscordOpCode.opHeartbeat) }
await client.sendGatewayRequest(heartbeatPayload, fmt("Sending heartbeat payload: {$heartbeatPayload}"))
client.heartbeatAcked = true
await shard.sendGatewayRequest(heartbeatPayload, fmt("Sending heartbeat payload: {$heartbeatPayload}"))
shard.heartbeatAcked = true
echo "Waiting ", client.heartbeatInterval, " ms until next heartbeat..."
await sleepAsync(client.heartbeatInterval)
echo "Waiting ", shard.heartbeatInterval, " ms until next heartbeat..."
await sleepAsync(shard.heartbeatInterval)
proc getIdentifyPacket(client: DiscordClient): JsonNode =
return %* { "op": ord(DiscordOpCode.opIdentify), "d": { "token": client.token, "properties": { "$os": system.hostOS, "$browser": "NimCord", "$device": "NimCord" } } }
proc getIdentifyPacket(shard: Shard): JsonNode =
return %* {
"op": ord(DiscordOpCode.opIdentify),
"d": {
"token": shard.client.token,
"shard": [shard.id, shard.client.shardCount],
"properties": {
"$os": system.hostOS,
"$browser": "NimCord",
"$device": "NimCord"
}
}
}
proc startConnection*(client: DiscordClient) {.async.}
# For some reason this shows as an error in VSCode, but it compiles fine.
#proc startConnection*(client: DiscordClient, shardCount: int = 1) {.async.}
proc closeConnection*(client: DiscordClient, code: int = 1000) {.async.} =
proc closeConnection*(shard: Shard, code: int = 1000) {.async.} =
echo "Disconnecting with code: ", code
await client.ws.close(code)
await shard.ws.close(code)
proc reconnectClient(client: DiscordClient) {.async.} =
proc reconnectShard(shard: Shard) {.async.} =
echo "Reconnecting..."
client.reconnecting = true
await client.ws.close(1000)
shard.reconnecting = true
await shard.ws.close(1000)
client.ws = await newAsyncWebsocketClient(client.endpoint[6..client.endpoint.high], Port 443,
shard.ws = await newAsyncWebsocketClient(shard.client.endpoint[6..shard.client.endpoint.high], Port 443,
path = "/v=6&encoding=json", true)
client.reconnecting = false
client.heartbeatAcked = true
shard.reconnecting = false
shard.heartbeatAcked = true
#waitFor client.startConnection()
# Handle discord disconnect. If it detects that we can reconnect, it will.
proc handleDiscordDisconnect(client: DiscordClient, error: string) {.async.} =
proc handleGatewayDisconnect(shard: Shard, error: string) {.async.} =
let disconnectData = extractCloseData(error)
echo "Discord gateway disconnected! Error code: ", disconnectData.code, ", msg: ", disconnectData.reason
client.heartbeatAcked = false
shard.heartbeatAcked = false
# Get the disconnect code
let c = disconnectData.code
@ -76,22 +88,22 @@ proc handleDiscordDisconnect(client: DiscordClient, error: string) {.async.} =
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):
waitFor client.reconnectClient()
if (not shard.reconnecting):
waitFor shard.reconnectShard()
else:
echo "Gateway is cannot reconnect due to already reconnecting..."
#TODO: Reconnecting may be done, just needs testing.
proc handleWebsocketPacket(client: DiscordClient) {.async.} =
proc handleWebsocketPacket(shard: Shard) {.async.} =
while true:
var packet: tuple[opcode: Opcode, data: string]
packet = await client.ws.readData();
echo "Received gateway payload: ", packet.data
packet = await shard.ws.readData();
echo "[SHARD ", $shard.id, "] Received gateway payload: ", packet.data
if packet.opcode == Opcode.Close:
await client.handleDiscordDisconnect(packet.data)
await shard.handleGatewayDisconnect(packet.data)
var json: JsonNode
@ -103,52 +115,55 @@ proc handleWebsocketPacket(client: DiscordClient) {.async.} =
continue
if (json.contains("s")):
client.lastSequence = json["s"].getInt()
shard.lastSequence = json["s"].getInt()
case json["op"].getInt()
of ord(DiscordOpCode.opHello):
if client.reconnecting:
if shard.reconnecting:
echo "Reconnected!"
client.reconnecting = false
shard.reconnecting = false
let resume = %* {
"op": ord(opResume),
"d": {
"token": client.token,
"session_id": client.sessionID,
"seq": client.lastSequence
"token": shard.client.token,
"session_id": shard.sessionID,
"seq": shard.lastSequence
}
}
await client.sendGatewayRequest(resume)
await shard.sendGatewayRequest(resume)
else:
client.heartbeatInterval = json["d"]["heartbeat_interval"].getInt()
await client.sendGatewayRequest(client.getIdentifyPacket())
shard.heartbeatInterval = json["d"]["heartbeat_interval"].getInt()
await shard.sendGatewayRequest(shard.getIdentifyPacket())
asyncCheck client.handleHeartbeat()
client.heartbeatAcked = true
asyncCheck shard.handleHeartbeat()
shard.heartbeatAcked = true
of ord(DiscordOpCode.opHeartbeatAck):
client.heartbeatAcked = true
shard.heartbeatAcked = true
of ord(DiscordOpCode.opDispatch):
asyncCheck handleDiscordEvent(client, json["d"], json["t"].getStr())
asyncCheck handleDiscordEvent(shard, json["d"], json["t"].getStr())
of ord(DiscordOpCode.opReconnect):
asyncCheck client.reconnectClient()
asyncCheck shard.reconnectShard()
of ord(DiscordOpCode.opInvalidSession):
# If the json field `d` is true then the session may be resumable.
if json["d"].getBool():
let resume = %* {
"op": ord(opResume),
"session_id": client.sessionID,
"seq": client.lastSequence
"session_id": shard.sessionID,
"seq": shard.lastSequence
}
await client.sendGatewayRequest(resume)
await shard.sendGatewayRequest(resume)
else:
asyncCheck client.reconnectClient()
asyncCheck shard.reconnectShard()
else:
discard
proc startConnection*(client: DiscordClient) {.async.} =
proc newShard(shardID: int, client: DiscordClient): Shard =
return Shard(id: shardID, client: client)
proc startConnection*(client: DiscordClient, shardAmount: int = 1) {.async.} =
## Start a bot connection.
##
## Examples:
@ -170,24 +185,55 @@ proc startConnection*(client: DiscordClient) {.async.} =
let url = urlResult["url"].getStr()
client.endpoint = url
client.ws = await newAsyncWebsocketClient(url[6..url.high], Port 443,
var shardCount = shardAmount
if (shardCount < urlResult["shards"].getInt()):
shardCount = urlResult["shards"].getInt()
client.shardCount = shardCount
if shardCount > 1:
for index in 0..shardCount - 2:
var shard = newShard(index, client)
client.shards.add(shard)
shard.ws = await newAsyncWebsocketClient(url[6..url.high], Port 443,
path = "/v=6&encoding=json", true)
asyncCheck shard.handleWebsocketPacket()
# Theres a 5 second delay on identify payloads.
await sleepAsync(5500)
var shard = newShard(shardCount - 1, client)
client.shards.add(shard)
shard.ws = await newAsyncWebsocketClient(url[6..url.high], Port 443,
path = "/v=6&encoding=json", true)
asyncCheck shard.handleWebsocketPacket()
# Now just wait. Dont poll while we're reconnecting
while true:
if not shard.reconnecting:
poll()
#[ client.ws = await newAsyncWebsocketClient(url[6..url.high], Port 443,
path = "/v=6&encoding=json", true)
asyncCheck client.handleWebsocketPacket()
# Now just wait. Dont poll for new events while we're reconnecting
while true:
if not client.reconnecting:
poll()
poll() ]#
else:
raise newException(IOError, "Failed to get gateway url, token may of been incorrect!")
proc updateClientPresence*(client: DiscordClient, presence: Presence) {.async.} =
proc updateClientPresence*(shard: Shard, presence: Presence) {.async.} =
let jsonPayload = %* {
"op": ord(opPresenceUpdate),
"d": presence.presenceToJson()
}
await client.sendGatewayRequest(jsonPayload)
await shard.sendGatewayRequest(jsonPayload)
proc newDiscordClient*(tkn: string): DiscordClient =
## Create a DiscordClient using a token.
@ -198,4 +244,4 @@ proc newDiscordClient*(tkn: string): DiscordClient =
var cac: Cache
new(cac)
result = DiscordClient(token: tkn, cache: cac, reconnecting: false)
result = DiscordClient(token: tkn, cache: cac)

View File

@ -1,14 +1,21 @@
import websocket, cache, user
type DiscordClient* = ref object
## Discord Client
token*: string
clientUser*: User
cache*: Cache
ws*: AsyncWebSocket
heartbeatInterval*: int
heartbeatAcked*: bool
lastSequence*: int
endpoint*: string
reconnecting*: bool
sessionID*: string
type
DiscordClient* = ref object
## Discord Client
token*: string
clientUser*: User
cache*: Cache
shards*: seq[Shard]
shardCount*: int
endpoint*: string
Shard* = ref object
id*: int
client*: DiscordClient
ws*: AsyncWebSocket
heartbeatInterval*: int
heartbeatAcked*: bool
lastSequence*: int
reconnecting*: bool
sessionID*: string

View File

@ -2,13 +2,13 @@ import eventhandler, json, tables, message, emoji, user, member, role
import guild, channel, nimcordutils, httpClient, strformat, cache
import sequtils, asyncdispatch, clientobjects, discordobject, presence
proc readyEvent(discordClient: DiscordClient, json: JsonNode) =
var readyEvent = ReadyEvent(client: discordClient, readyPayload: json, name: $EventType.evtReady)
proc readyEvent(shard: Shard, json: JsonNode) =
var readyEvent = ReadyEvent(shard: shard, readyPayload: json, name: $EventType.evtReady)
# Get client user
var client = newHttpClient()
# Add headers
client.headers = newHttpHeaders({"Authorization": fmt("Bot {discordClient.token}"),
client.headers = newHttpHeaders({"Authorization": fmt("Bot {shard.client.token}"),
"User-Agent": "NimCord (https://github.com/SeanOMik/nimcord, v0.0.0)",
"X-RateLimit-Precision": "millisecond"})
echo "Sending GET request, URL: body: {}"
@ -16,30 +16,30 @@ proc readyEvent(discordClient: DiscordClient, json: JsonNode) =
waitForRateLimits(0, RateLimitBucketType.global)
var userJson = handleResponse(client.request(endpoint("/users/@me"), HttpGet, ""), 0, RateLimitBucketType.global)
discordClient.clientUser = newUser(userJson)
discordClient.sessionID = json["session_id"].getStr()
shard.client.clientUser = newUser(userJson)
shard.sessionID = json["session_id"].getStr()
dispatchEvent(readyEvent)
proc channelCreateEvent(discordClient: DiscordClient, json: JsonNode) =
proc channelCreateEvent(shard: Shard, json: JsonNode) =
let chnl = newChannel(json)
let channelCreateEvent = ChannelCreateEvent(client: discordClient, channel: chnl, name: $EventType.evtChannelCreate)
let channelCreateEvent = ChannelCreateEvent(shard: shard, channel: chnl, name: $EventType.evtChannelCreate)
# Add the channel to its guild's `channels` field
if (chnl.guildID != 0):
discordClient.cache.cacheGuildChannel(chnl.guildID, chnl)
discordClient.cache.channels[chnl.id] = chnl
shard.client.cache.cacheGuildChannel(chnl.guildID, chnl)
shard.client.cache.channels[chnl.id] = chnl
dispatchEvent(channelCreateEvent)
proc channelUpdateEvent(discordClient: DiscordClient, json: JsonNode) =
proc channelUpdateEvent(shard: Shard, json: JsonNode) =
let chnl = newChannel(json)
let channelUpdateEvent = ChannelUpdateEvent(client: discordClient, channel: chnl, name: $EventType.evtChannelUpdate)
let channelUpdateEvent = ChannelUpdateEvent(shard: shard, channel: chnl, name: $EventType.evtChannelUpdate)
discordClient.cache.channels[chnl.id] = chnl
shard.client.cache.channels[chnl.id] = chnl
if (chnl.guildID != 0):
let g = discordClient.cache.getGuild(chnl.guildID)
let g = shard.client.cache.getGuild(chnl.guildID)
var index = -1
for i, channel in g.channels:
@ -55,81 +55,81 @@ proc channelUpdateEvent(discordClient: DiscordClient, json: JsonNode) =
dispatchEvent(channelUpdateEvent)
proc channelDeleteEvent(discordClient: DiscordClient, json: JsonNode) =
proc channelDeleteEvent(shard: Shard, json: JsonNode) =
let chnl = newChannel(json)
let channelDeleteEvent = ChannelDeleteEvent(client: discordClient, channel: chnl, name: $EventType.evtChannelDelete)
let channelDeleteEvent = ChannelDeleteEvent(shard: shard, channel: chnl, name: $EventType.evtChannelDelete)
var removedChnl: Channel
discard discordClient.cache.channels.pop(chnl.id, removedChnl)
discard shard.client.cache.channels.pop(chnl.id, removedChnl)
dispatchEvent(channelDeleteEvent)
proc channelPinsUpdate(discordClient: DiscordClient, json: JsonNode) =
proc channelPinsUpdate(shard: Shard, json: JsonNode) =
let channelID = getIDFromJson(json["channel_id"].getStr())
var channel: Channel
if (discordClient.cache.channels.hasKey(channelID)):
channel = discordClient.cache.channels[channelID]
if (shard.client.cache.channels.hasKey(channelID)):
channel = shard.client.cache.channels[channelID]
channel.lastPinTimestamp = json["last_pin_timestamp"].getStr()
let channelPinsUpdateEvent = ChannelPinsUpdateEvent(client: discordClient, channel: channel, name: $EventType.evtChannelPinsUpdate)
let channelPinsUpdateEvent = ChannelPinsUpdateEvent(shard: shard, channel: channel, name: $EventType.evtChannelPinsUpdate)
dispatchEvent(channelPinsUpdateEvent)
proc guildCreateEvent(discordClient: DiscordClient, json: JsonNode) =
proc guildCreateEvent(shard: Shard, json: JsonNode) =
let g = newGuild(json)
let guildCreateEvnt = GuildCreateEvent(client: discordClient, guild: g, name: $EventType.evtGuildCreate)
let guildCreateEvnt = GuildCreateEvent(shard: shard, guild: g, name: $EventType.evtGuildCreate)
# Add guild and its channels and members in cache.
discordClient.cache.guilds[g.id] = g
shard.client.cache.guilds[g.id] = g
for channel in g.channels:
discordClient.cache.channels[channel.id] = channel
shard.client.cache.channels[channel.id] = channel
for member in g.members:
discordClient.cache.members[member.id] = member
shard.client.cache.members[member.id] = member
dispatchEvent(guildCreateEvnt)
proc guildUpdateEvent(discordClient: DiscordClient, json: JsonNode) =
proc guildUpdateEvent(shard: Shard, json: JsonNode) =
let g = newGuild(json)
let guildUpdateEvent = GuildUpdateEvent(client: discordClient, guild: g, name: $EventType.evtGuildUpdate)
let guildUpdateEvent = GuildUpdateEvent(shard: shard, guild: g, name: $EventType.evtGuildUpdate)
# Update guild in cache.
discordClient.cache.guilds[g.id] = g
shard.client.cache.guilds[g.id] = g
dispatchEvent(guildUpdateEvent)
proc guildDeleteEvent(discordClient: DiscordClient, json: JsonNode) =
proc guildDeleteEvent(shard: Shard, json: JsonNode) =
let g = newGuild(json)
let guildDeleteEvent = GuildDeleteEvent(client: discordClient, guild: g, name: $EventType.evtGuildDelete)
let guildDeleteEvent = GuildDeleteEvent(shard: shard, guild: g, name: $EventType.evtGuildDelete)
# Remove guild from cache
var removedGuild: Guild
discard discordClient.cache.guilds.pop(g.id, removedGuild)
discard shard.client.cache.guilds.pop(g.id, removedGuild)
dispatchEvent(guildDeleteEvent)
proc guildBanAddEvent(discordClient: DiscordClient, json: JsonNode) =
let g = discordClient.cache.getGuild(getIDFromJson(json["guild_id"].getStr()))
proc guildBanAddEvent(shard: Shard, json: JsonNode) =
let g = shard.client.cache.getGuild(getIDFromJson(json["guild_id"].getStr()))
let user = newUser(json["user"])
let guildBanAddEvent = GuildBanAddEvent(client: discordClient, guild: g, bannedUser: user, name: $EventType.evtGuildBanAdd)
let guildBanAddEvent = GuildBanAddEvent(shard: shard, guild: g, bannedUser: user, name: $EventType.evtGuildBanAdd)
dispatchEvent(guildBanAddEvent)
proc guildBanRemoveEvent(discordClient: DiscordClient, json: JsonNode) =
let g = discordClient.cache.getGuild(getIDFromJson(json["guild_id"].getStr()))
proc guildBanRemoveEvent(shard: Shard, json: JsonNode) =
let g = shard.client.cache.getGuild(getIDFromJson(json["guild_id"].getStr()))
let user = newUser(json["user"])
let guildBanRemoveEvent = GuildBanRemoveEvent(client: discordClient, guild: g, unbannedUser: user, name: $EventType.evtGuildBanRemove)
let guildBanRemoveEvent = GuildBanRemoveEvent(shard: shard, guild: g, unbannedUser: user, name: $EventType.evtGuildBanRemove)
dispatchEvent(guildBanRemoveEvent)
proc guildEmojisUpdateEvent(discordClient: DiscordClient, json: JsonNode) =
var g = discordClient.cache.getGuild(getIDFromJson(json["guild_id"].getStr()))
proc guildEmojisUpdateEvent(shard: Shard, json: JsonNode) =
var g = shard.client.cache.getGuild(getIDFromJson(json["guild_id"].getStr()))
# Empty g.emojis and fill it with the newly updated emojis
g.emojis = @[]
for emoji in json["emojis"]:
g.emojis.add(newEmoji(emoji, g.id))
let guildEmojisUpdateEvent = GuildEmojisUpdateEvent(client: discordClient, guild: g, emojis: g.emojis, name: $EventType.evtGuildEmojisUpdate)
let guildEmojisUpdateEvent = GuildEmojisUpdateEvent(shard: shard, guild: g, emojis: g.emojis, name: $EventType.evtGuildEmojisUpdate)
dispatchEvent(guildEmojisUpdateEvent)
#[ var updatedEmojis: Table[snowflake, Emoji] = initTable[snowflake, Emoji]()
@ -143,28 +143,28 @@ proc guildEmojisUpdateEvent(discordClient: DiscordClient, json: JsonNode) =
#g.emojis.apply
proc guildIntegrationsUpdate(discordClient: DiscordClient, json: JsonNode) =
var g = discordClient.cache.getGuild(getIDFromJson(json["guild_id"].getStr()))
proc guildIntegrationsUpdate(shard: Shard, json: JsonNode) =
var g = shard.client.cache.getGuild(getIDFromJson(json["guild_id"].getStr()))
let guildIntegrationsUpdateEvent = GuildIntegrationsUpdateEvent(client: discordClient, guild: g, name: $EventType.evtGuildIntegrationsUpdate)
let guildIntegrationsUpdateEvent = GuildIntegrationsUpdateEvent(shard: shard, guild: g, name: $EventType.evtGuildIntegrationsUpdate)
dispatchEvent(guildIntegrationsUpdateEvent)
proc guildMemberAdd(discordClient: DiscordClient, json: JsonNode) =
var g = discordClient.cache.getGuild(getIDFromJson(json["guild_id"].getStr()))
proc guildMemberAdd(shard: Shard, json: JsonNode) =
var g = shard.client.cache.getGuild(getIDFromJson(json["guild_id"].getStr()))
var newMember = newGuildMember(json, g.id)
let guildMemberAddEvent = GuildMemberAddEvent(client: discordClient, guild: g, member: newMember, name: $EventType.evtGuildMemberAdd)
let guildMemberAddEvent = GuildMemberAddEvent(shard: shard, guild: g, member: newMember, name: $EventType.evtGuildMemberAdd)
dispatchEvent(guildMemberAddEvent)
proc guildMemberRemove(discordClient: DiscordClient, json: JsonNode) =
var g = discordClient.cache.getGuild(getIDFromJson(json["guild_id"].getStr()))
proc guildMemberRemove(shard: Shard, json: JsonNode) =
var g = shard.client.cache.getGuild(getIDFromJson(json["guild_id"].getStr()))
var removedUser = newUser(json["user"])
let guildMemberRemoveEvent = GuildMemberRemoveEvent(client: discordClient, guild: g, user: removedUser, name: $EventType.evtGuildMemberRemove)
let guildMemberRemoveEvent = GuildMemberRemoveEvent(shard: shard, guild: g, user: removedUser, name: $EventType.evtGuildMemberRemove)
dispatchEvent(guildMemberRemoveEvent)
proc guildMemberUpdate(discordClient: DiscordClient, json: JsonNode) =
var g = discordClient.cache.getGuild(getIDFromJson(json["guild_id"].getStr()))
proc guildMemberUpdate(shard: Shard, json: JsonNode) =
var g = shard.client.cache.getGuild(getIDFromJson(json["guild_id"].getStr()))
var updatedMember = g.getGuildMember(getIDFromJson(json["user"]["id"].getStr()))
updatedMember.user = newUser(json["user"])
@ -179,13 +179,13 @@ proc guildMemberUpdate(discordClient: DiscordClient, json: JsonNode) =
if json.contains("premium_since"):
updatedMember.premiumSince = json["premium_since"].getStr()
let guildMemberUpdateEvent = GuildMemberUpdateEvent(client: discordClient, guild: g, member: updatedMember, name: $EventType.evtGuildMemberUpdate)
let guildMemberUpdateEvent = GuildMemberUpdateEvent(shard: shard, guild: g, member: updatedMember, name: $EventType.evtGuildMemberUpdate)
dispatchEvent(guildMemberUpdateEvent)
proc guildMembersChunk(discordClient: DiscordClient, json: JsonNode) =
var g = discordClient.cache.getGuild(getIDFromJson(json["guild_id"].getStr()))
proc guildMembersChunk(shard: Shard, json: JsonNode) =
var g = shard.client.cache.getGuild(getIDFromJson(json["guild_id"].getStr()))
var event = GuildMembersChunkEvent(client: discordClient, guild: g, name: $EventType.evtGuildMembersChunk)
var event = GuildMembersChunkEvent(shard: shard, guild: g, name: $EventType.evtGuildMembersChunk)
#var members: seq[GuildMember]
for member in json["members"]:
@ -207,17 +207,17 @@ proc guildMembersChunk(discordClient: DiscordClient, json: JsonNode) =
dispatchEvent(event)
proc guildRoleCreate(discordClient: DiscordClient, json: JsonNode) =
var g = discordClient.cache.getGuild(getIDFromJson(json["guild_id"].getStr()))
proc guildRoleCreate(shard: Shard, json: JsonNode) =
var g = shard.client.cache.getGuild(getIDFromJson(json["guild_id"].getStr()))
let role = newRole(json["role"], g.id)
g.roles.add(role)
var event = GuildRoleUpdateEvent(client: discordClient, guild: g, role: role, name: $EventType.evtGuildRoleUpdate)
var event = GuildRoleUpdateEvent(shard: shard, guild: g, role: role, name: $EventType.evtGuildRoleUpdate)
dispatchEvent(event)
proc guildRoleUpdate(discordClient: DiscordClient, json: JsonNode) =
var g = discordClient.cache.getGuild(getIDFromJson(json["guild_id"].getStr()))
proc guildRoleUpdate(shard: Shard, json: JsonNode) =
var g = shard.client.cache.getGuild(getIDFromJson(json["guild_id"].getStr()))
let role = newRole(json["role"], g.id)
var index = -1
@ -227,11 +227,11 @@ proc guildRoleUpdate(discordClient: DiscordClient, json: JsonNode) =
g.roles[index] = role
var event = GuildRoleUpdateEvent(client: discordClient, guild: g, role: role, name: $EventType.evtGuildRoleUpdate)
var event = GuildRoleUpdateEvent(shard: shard, guild: g, role: role, name: $EventType.evtGuildRoleUpdate)
dispatchEvent(event)
proc guildRoleDelete(discordClient: DiscordClient, json: JsonNode) =
var g = discordClient.cache.getGuild(getIDFromJson(json["guild_id"].getStr()))
proc guildRoleDelete(shard: Shard, json: JsonNode) =
var g = shard.client.cache.getGuild(getIDFromJson(json["guild_id"].getStr()))
let roleID = getIDFromJson(json["role_id"].getStr())
var role: Role
@ -244,53 +244,53 @@ proc guildRoleDelete(discordClient: DiscordClient, json: JsonNode) =
if index != -1:
g.roles.delete(index)
var event = GuildRoleDeleteEvent(client: discordClient, guild: g, role: role, name: $EventType.evtGuildRoleDelete)
var event = GuildRoleDeleteEvent(shard: shard, guild: g, role: role, name: $EventType.evtGuildRoleDelete)
dispatchEvent(event)
proc inviteCreate(discordClient: DiscordClient, json: JsonNode) =
proc inviteCreate(shard: Shard, json: JsonNode) =
var invite = newInvite(json)
invite.channel = discordClient.cache.getChannel(getIDFromJson(json["channel_id"].getStr()))
invite.channel = shard.client.cache.getChannel(getIDFromJson(json["channel_id"].getStr()))
if (json.contains("guild_id")):
invite.guildID =getIDFromJson(json["guild_id"].getStr())
var event = InviteCreateEvent(client: discordClient, invite: invite, name: $EventType.evtInviteCreate)
var event = InviteCreateEvent(shard: shard, invite: invite, name: $EventType.evtInviteCreate)
dispatchEvent(event)
proc inviteDelete(discordClient: DiscordClient, json: JsonNode) =
var event = InviteDeleteEvent(client: discordClient, name: $EventType.evtInviteDelete)
proc inviteDelete(shard: Shard, json: JsonNode) =
var event = InviteDeleteEvent(shard: shard, name: $EventType.evtInviteDelete)
event.channel = discordClient.cache.getChannel(getIDFromJson(json["channel_id"].getStr()))
event.channel = shard.client.cache.getChannel(getIDFromJson(json["channel_id"].getStr()))
event.code = json["code"].getStr()
if (json.contains("guild_id")):
event.guild = discordClient.cache.getGuild(getIDFromJson(json["guild_id"].getStr()))
event.guild = shard.client.cache.getGuild(getIDFromJson(json["guild_id"].getStr()))
dispatchEvent(event)
proc messageCreateEvent(discordClient: DiscordClient, json: JsonNode) =
proc messageCreateEvent(shard: Shard, json: JsonNode) =
let msg = newMessage(json)
discordClient.cache.messages[msg.id] = msg
shard.client.cache.messages[msg.id] = msg
let messageCreateEvnt = MessageCreateEvent(client: discordClient, message: msg, name: $EventType.evtMessageCreate)
let messageCreateEvnt = MessageCreateEvent(shard: shard, message: msg, name: $EventType.evtMessageCreate)
dispatchEvent(messageCreateEvnt)
proc messageUpdateEvent(discordClient: DiscordClient, json: JsonNode) =
proc messageUpdateEvent(shard: Shard, json: JsonNode) =
let msg = newMessage(json)
discordClient.cache.messages[msg.id] = msg
shard.client.cache.messages[msg.id] = msg
let event = MessageCreateEvent(client: discordClient, message: msg, name: $EventType.evtMessageUpdate)
let event = MessageCreateEvent(shard: shard, message: msg, name: $EventType.evtMessageUpdate)
dispatchEvent(event)
proc messageDeleteEvent(discordClient: DiscordClient, json: JsonNode) =
proc messageDeleteEvent(shard: Shard, json: JsonNode) =
let msgID = getIDFromJson(json["id"].getStr())
var msg: Message
if discordClient.cache.messages.hasKey(msgID):
discard discordClient.cache.messages.pop(msgID, msg)
if shard.client.cache.messages.hasKey(msgID):
discard shard.client.cache.messages.pop(msgID, msg)
else:
msg = Message(id: msgID)
@ -298,27 +298,27 @@ proc messageDeleteEvent(discordClient: DiscordClient, json: JsonNode) =
if (json.contains("guild_id")):
msg.guildID = getIDFromJson(json["guild_id"].getStr())
let event = MessageDeleteEvent(client: discordClient, message: msg, name: $EventType.evtMessageDelete)
let event = MessageDeleteEvent(shard: shard, message: msg, name: $EventType.evtMessageDelete)
dispatchEvent(event)
proc messageDeleteBulkEvent(discordClient: DiscordClient, json: JsonNode) =
var event = MessageDeleteBulkEvent(client: discordClient, name: $EventType.evtMessageDeleteBulk)
proc messageDeleteBulkEvent(shard: Shard, json: JsonNode) =
var event = MessageDeleteBulkEvent(shard: shard, name: $EventType.evtMessageDeleteBulk)
event.channel = discordClient.cache.getChannel(getIDFromJson(json["channel_id"].getStr()))
event.channel = shard.client.cache.getChannel(getIDFromJson(json["channel_id"].getStr()))
if (json.contains("guild_id")):
event.guild = discordClient.cache.getGuild(getIDFromJson(json["guild_id"].getStr()))
event.guild = shard.client.cache.getGuild(getIDFromJson(json["guild_id"].getStr()))
for msgIDJson in json["ids"]:
let msgID = getIDFromJson(msgIDJson.getStr())
var msg: Message
if discordClient.cache.messages.hasKey(msgID):
discard discordClient.cache.messages.pop(msgID, msg)
if shard.client.cache.messages.hasKey(msgID):
discard shard.client.cache.messages.pop(msgID, msg)
else:
let channelID = getIDFromJson(json["channel_id"].getStr())
msg = Message(id: msgID, channelID: channelID)
event.channel = discordClient.cache.getChannel(msg.channelID)
event.channel = shard.client.cache.getChannel(msg.channelID)
if (json.contains("guild_id")):
msg.guildID = getIDFromJson(json["guild_id"].getStr())
@ -326,13 +326,13 @@ proc messageDeleteBulkEvent(discordClient: DiscordClient, json: JsonNode) =
dispatchEvent(event)
proc messageReactionAdd(discordClient: DiscordClient, json: JsonNode) =
var event = MessageReactionAddEvent(client: discordClient, name: $EventType.evtMessageReactionAdd)
proc messageReactionAdd(shard: Shard, json: JsonNode) =
var event = MessageReactionAddEvent(shard: shard, name: $EventType.evtMessageReactionAdd)
let msgID = getIDFromJson(json["message_id"].getStr())
var msg: Message
if discordClient.cache.messages.hasKey(msgID):
msg = discordClient.cache.messages[msgID]
if shard.client.cache.messages.hasKey(msgID):
msg = shard.client.cache.messages[msgID]
else:
msg = Message(id: msgID)
@ -340,7 +340,7 @@ proc messageReactionAdd(discordClient: DiscordClient, json: JsonNode) =
if (json.contains("guild_id")):
msg.guildID = getIDFromJson(json["guild_id"].getStr())
event.user = discordClient.cache.getUser(getIDFromJson(json["user_id"].getStr()))
event.user = shard.client.cache.getUser(getIDFromJson(json["user_id"].getStr()))
if (json.contains("member")):
event.member = newGuildMember(json["member"], msg.guildID)
@ -349,13 +349,13 @@ proc messageReactionAdd(discordClient: DiscordClient, json: JsonNode) =
dispatchEvent(event)
proc messageReactionRemove(discordClient: DiscordClient, json: JsonNode) =
var event = MessageReactionRemoveEvent(client: discordClient, name: $EventType.evtMessageReactionRemove)
proc messageReactionRemove(shard: Shard, json: JsonNode) =
var event = MessageReactionRemoveEvent(shard: shard, name: $EventType.evtMessageReactionRemove)
let msgID = getIDFromJson(json["message_id"].getStr())
var msg: Message
if discordClient.cache.messages.hasKey(msgID):
msg = discordClient.cache.messages[msgID]
if shard.client.cache.messages.hasKey(msgID):
msg = shard.client.cache.messages[msgID]
else:
msg = Message(id: msgID)
@ -363,19 +363,19 @@ proc messageReactionRemove(discordClient: DiscordClient, json: JsonNode) =
if (json.contains("guild_id")):
msg.guildID = getIDFromJson(json["guild_id"].getStr())
event.user = discordClient.cache.getUser(getIDFromJson(json["user_id"].getStr()))
event.user = shard.client.cache.getUser(getIDFromJson(json["user_id"].getStr()))
event.emoji = newEmoji(json["emoji"], msg.guildID)
dispatchEvent(event)
proc messageReactionRemoveAll(discordClient: DiscordClient, json: JsonNode) =
var event = MessageReactionRemoveAllEvent(client: discordClient, name: $EventType.evtMessageReactionRemoveAll)
proc messageReactionRemoveAll(shard: Shard, json: JsonNode) =
var event = MessageReactionRemoveAllEvent(shard: shard, name: $EventType.evtMessageReactionRemoveAll)
let msgID = getIDFromJson(json["message_id"].getStr())
var msg: Message
if discordClient.cache.messages.hasKey(msgID):
msg = discordClient.cache.messages[msgID]
if shard.client.cache.messages.hasKey(msgID):
msg = shard.client.cache.messages[msgID]
else:
msg = Message(id: msgID)
@ -385,13 +385,13 @@ proc messageReactionRemoveAll(discordClient: DiscordClient, json: JsonNode) =
dispatchEvent(event)
proc messageReactionRemoveEmoji(discordClient: DiscordClient, json: JsonNode) =
var event = MessageReactionRemoveEmojiEvent(client: discordClient, name: $EventType.evtMessageReactionRemoveEmoji)
proc messageReactionRemoveEmoji(shard: Shard, json: JsonNode) =
var event = MessageReactionRemoveEmojiEvent(shard: shard, name: $EventType.evtMessageReactionRemoveEmoji)
let msgID = getIDFromJson(json["message_id"].getStr())
var msg: Message
if discordClient.cache.messages.hasKey(msgID):
msg = discordClient.cache.messages[msgID]
if shard.client.cache.messages.hasKey(msgID):
msg = shard.client.cache.messages[msgID]
else:
msg = Message(id: msgID)
@ -403,10 +403,10 @@ proc messageReactionRemoveEmoji(discordClient: DiscordClient, json: JsonNode) =
dispatchEvent(event)
proc presenceUpdate(discordClient: DiscordClient, json: JsonNode) =
proc presenceUpdate(shard: Shard, json: JsonNode) =
# This proc doesn't actually dispatch any events,
# it just updates member.presence
var g = discordClient.cache.getGuild(getIDFromJson(json["guild_id"].getStr()))
var g = shard.client.cache.getGuild(getIDFromJson(json["guild_id"].getStr()))
var member = g.getGuildMember(getIDFromJson(json["user"]["id"].getStr()))
# Make sure some member fields are upto date.
@ -421,51 +421,51 @@ proc presenceUpdate(discordClient: DiscordClient, json: JsonNode) =
member.presence = newPresence(json)
proc typingStart(discordClient: DiscordClient, json: JsonNode) =
var event = TypingStartEvent(client: discordClient, name: $EventType.evtTypingStart)
proc typingStart(shard: Shard, json: JsonNode) =
var event = TypingStartEvent(shard: shard, name: $EventType.evtTypingStart)
event.channel = discordClient.cache.getChannel(getIDFromJson(json["channel_id"].getStr()))
event.channel = shard.client.cache.getChannel(getIDFromJson(json["channel_id"].getStr()))
if (json.contains("guild_id")):
event.channel.guildID = getIDFromJson(json["guild_id"].getStr())
event.user = discordClient.cache.getUser(getIDFromJson(json["user_id"].getStr()))
event.user = shard.client.cache.getUser(getIDFromJson(json["user_id"].getStr()))
if (json.contains("member")):
event.member = newGuildMember(json["member"], event.channel.guildID)
dispatchEvent(event)
proc userUpdate(discordClient: DiscordClient, json: JsonNode) =
var event = UserUpdateEvent(client: discordClient, name: $EventType.evtUserUpdate)
proc userUpdate(shard: Shard, json: JsonNode) =
var event = UserUpdateEvent(shard: shard, name: $EventType.evtUserUpdate)
event.user = newUser(json)
dispatchEvent(event)
proc voiceStateUpdate(discordClient: DiscordClient, json: JsonNode) =
var event = VoiceStateUpdateEvent(client: discordClient, name: $EventType.evtVoiceStateUpdate)
proc voiceStateUpdate(shard: Shard, json: JsonNode) =
var event = VoiceStateUpdateEvent(shard: shard, name: $EventType.evtVoiceStateUpdate)
dispatchEvent(event)
proc voiceServerUpdate(discordClient: DiscordClient, json: JsonNode) =
var event = VoiceServerUpdateEvent(client: discordClient, name: $EventType.evtVoiceServerUpdate)
proc voiceServerUpdate(shard: Shard, json: JsonNode) =
var event = VoiceServerUpdateEvent(shard: shard, name: $EventType.evtVoiceServerUpdate)
event.token = json["token"].getStr()
event.guild = discordClient.cache.getGuild(getIDFromJson(json["guild_id"].getStr()))
event.guild = shard.client.cache.getGuild(getIDFromJson(json["guild_id"].getStr()))
event.endpoint = json["endpoint"].getStr()
dispatchEvent(event)
proc webhooksUpdate(discordClient: DiscordClient, json: JsonNode) =
var event = WebhooksUpdateEvent(client: discordClient, name: $EventType.evtWebhooksUpdate)
proc webhooksUpdate(shard: Shard, json: JsonNode) =
var event = WebhooksUpdateEvent(shard: shard, name: $EventType.evtWebhooksUpdate)
event.guild = discordClient.cache.getGuild(getIDFromJson(json["guild_id"].getStr()))
event.channel = discordClient.cache.getChannel(getIDFromJson(json["channel_id"].getStr()))
event.guild = shard.client.cache.getGuild(getIDFromJson(json["guild_id"].getStr()))
event.channel = shard.client.cache.getChannel(getIDFromJson(json["channel_id"].getStr()))
dispatchEvent(event)
let internalEventTable: Table[string, proc(discordClient: DiscordClient, json: JsonNode) {.nimcall.}] = {
let internalEventTable: Table[string, proc(shard: Shard, json: JsonNode) {.nimcall.}] = {
"READY": readyEvent,
"CHANNEL_CREATE": channelCreateEvent,
"CHANNEL_UPDATE": channelUpdateEvent,
@ -502,11 +502,11 @@ let internalEventTable: Table[string, proc(discordClient: DiscordClient, json: J
"WEBHOOKS_UPDATE": webhooksUpdate
}.toTable
proc handleDiscordEvent*(discordClient: DiscordClient, json: JsonNode, eventName: string) {.async.} =
proc handleDiscordEvent*(shard: Shard, json: JsonNode, eventName: string) {.async.} =
## Handles, and dispatches, a gateway event. Only used internally.
if (internalEventTable.hasKey(eventName)):
let eventProc: proc(discordClient: DiscordClient, json: JsonNode) = internalEventTable[eventName]
eventProc(discordClient, json)
let eventProc: proc(shard: Shard, json: JsonNode) = internalEventTable[eventName]
eventProc(shard, json)
else:
echo "Failed to find event: ", eventName

View File

@ -46,7 +46,7 @@ type
BaseEvent* = object of RootObj
## Base event that all events inherit from.
## It stores a reference to the DiscordClient and name of the event.
client*: DiscordClient
shard*: Shard
name*: string
# Socket Events