diff --git a/README.md b/README.md index d08e88b..bdeec18 100644 --- a/README.md +++ b/README.md @@ -45,6 +45,6 @@ You can view examples in the [examples](examples) directory. - [ ] Channel - [ ] Guild - [ ] Misc. -- [ ] Sharding. +- [x] Sharding. - [ ] Audit log. - [ ] Voice. \ No newline at end of file diff --git a/examples/basic.nim b/examples/basic.nim index 0d8aa5b..238a58a 100644 --- a/examples/basic.nim +++ b/examples/basic.nim @@ -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() \ No newline at end of file +waitFor bot.startConnection(2) \ No newline at end of file diff --git a/src/nimcord/client.nim b/src/nimcord/client.nim index 4d18257..d037e9c 100644 --- a/src/nimcord/client.nim +++ b/src/nimcord/client.nim @@ -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) \ No newline at end of file + result = DiscordClient(token: tkn, cache: cac) \ No newline at end of file diff --git a/src/nimcord/clientobjects.nim b/src/nimcord/clientobjects.nim index 0549fd0..ec73ef7 100644 --- a/src/nimcord/clientobjects.nim +++ b/src/nimcord/clientobjects.nim @@ -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 \ No newline at end of file +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 \ No newline at end of file diff --git a/src/nimcord/eventdispatcher.nim b/src/nimcord/eventdispatcher.nim index 5f6d722..81b5553 100644 --- a/src/nimcord/eventdispatcher.nim +++ b/src/nimcord/eventdispatcher.nim @@ -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 \ No newline at end of file diff --git a/src/nimcord/eventhandler.nim b/src/nimcord/eventhandler.nim index dff17b1..5dcfa89 100644 --- a/src/nimcord/eventhandler.nim +++ b/src/nimcord/eventhandler.nim @@ -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