Merge branch 'master' into deglobalize
This commit is contained in:
commit
733b004fab
|
@ -4,11 +4,10 @@ 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, "?")
|
||||
var bot = newDiscordClient(tkn, "?", newLog(ord(LoggerFlags.loggerFlagDebugSeverity)))
|
||||
|
||||
let pingCommand = Command(name: "ping", commandBody: proc(ctx: CommandContext) =
|
||||
discard ctx.channel.sendMessage("PONG")
|
||||
|
@ -88,7 +87,7 @@ let sendImageCommand = Command(name: "sendImage", commandBody: proc(ctx: Command
|
|||
|
||||
# You can even register commands like this:
|
||||
registerCommand(Command(name: "ping2", commandBody: proc(ctx: CommandContext) =
|
||||
discard ctx.channel.sendMessage("PONG3")
|
||||
discard ctx.channel.sendMessage("PONG 2")
|
||||
))
|
||||
|
||||
# Listen for the ready event.
|
||||
|
@ -96,12 +95,13 @@ registerEventListener(EventType.evtReady, proc(bEvt: BaseEvent) =
|
|||
# Cast the BaseEvent to the ReadyEvent which is what we're listening to.
|
||||
let event = ReadyEvent(bEvt)
|
||||
|
||||
echo "Ready! (v", 0, ".", 0, ".", 1, ")"
|
||||
echo "Logged in as: ", bot.clientUser.username, "#", bot.clientUser.discriminator
|
||||
echo "ID: ", bot.clientUser.id
|
||||
echo "--------------------"
|
||||
event.shard.client.log.info("Ready!")
|
||||
event.shard.client.log.info("Logged in as: " & bot.clientUser.username & "#" & $bot.clientUser.discriminator)
|
||||
event.shard.client.log.info("ID: " & $bot.clientUser.id)
|
||||
event.shard.client.log.info("--------------------")
|
||||
|
||||
let presence = newPresence("with Nimcord", activityTypeGame, clientStatusIdle, false)
|
||||
let presence = newPresence("with Nimcord", ActivityType.activityTypeGame,
|
||||
ClientStatus.clientStatusIdle, false)
|
||||
asyncCheck event.shard.updateClientPresence(presence)
|
||||
|
||||
# Register commands. You don't need to register them in EventReady.
|
||||
|
|
|
@ -16,11 +16,11 @@
|
|||
import nimcord/[cache, channel, client, clientobjects, discordobject]
|
||||
import nimcord/[embed, emoji, eventdispatcher, eventhandler, guild]
|
||||
import nimcord/[image, member, message, nimcordutils, permission]
|
||||
import nimcord/[presence, role, user, commandsystem]
|
||||
import nimcord/[presence, role, user, commandsystem, log]
|
||||
|
||||
export cache, channel, client, clientobjects, discordobject
|
||||
export embed, emoji, eventdispatcher, eventhandler, guild
|
||||
export image, member, message, nimcordutils, permission
|
||||
export presence, role, user, commandsystem
|
||||
export presence, role, user, commandsystem, log
|
||||
|
||||
const NimCordVersion = "v0.0.1"
|
|
@ -163,7 +163,8 @@ proc sendMessage*(channel: Channel, content: string, tts: bool = false, embed: E
|
|||
raise newException(IOError, "Failed to open file for sending: " & file.filePath)
|
||||
multipart.add("payload_json", $messagePayload, "", "application/json", false)
|
||||
|
||||
echo "Sending POST request, URL: ", endpoint, ", headers: ", client.headers, " payload_json: ", messagePayload
|
||||
# TODO: Send this through the logger:
|
||||
#echo "Sending POST request, URL: ", endpoint, ", headers: ", client.headers, " payload_json: ", messagePayload
|
||||
|
||||
waitForRateLimits(channel.id, RateLimitBucketType.channel)
|
||||
let response: Response = client.post(endpoint, "", multipart)
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import websocket, asyncdispatch, json, httpClient, eventdispatcher, strformat
|
||||
import nimcordutils, cache, clientobjects
|
||||
import strutils, options, presence
|
||||
import nimcordutils, cache, clientobjects, strutils, options, presence, log
|
||||
|
||||
type
|
||||
DiscordOpCode = enum
|
||||
|
@ -22,7 +21,7 @@ proc getIdentifyPacket(shard: Shard): JsonNode
|
|||
proc handleGatewayDisconnect(shard: Shard, error: string) {.async.}
|
||||
proc handleHeartbeat(shard: Shard) {.async.}
|
||||
proc handleWebsocketPacket(shard: Shard) {.async.}
|
||||
proc newDiscordClient*(tkn: string, commandPrefix: string): DiscordClient
|
||||
proc newDiscordClient*(tkn: string, commandPrefix: string, log: Log = newLog(ord(LoggerFlags.loggerFlagWarnSeverity) or ord(LoggerFlags.loggerFlagInfoSeverity) or ord(LoggerFlags.loggerFlagErrorSeverity))): DiscordClient
|
||||
proc newShard(shardID: int, client: DiscordClient): Shard
|
||||
proc reconnectShard(shard: Shard) {.async.}
|
||||
proc sendGatewayRequest*(shard: Shard, request: JsonNode, msg: string = "") {.async.}
|
||||
|
@ -33,11 +32,11 @@ proc sendGatewayRequest*(shard: Shard, request: JsonNode, msg: string = "") {.as
|
|||
## Send a gateway request.
|
||||
## Don't use this unless you know what you're doing!
|
||||
if msg.len == 0:
|
||||
echo "Sending gateway payload: ", request
|
||||
shard.client.log.debug("[SHARD " & $shard.id & "] Sending gateway payload: " & $request)
|
||||
else:
|
||||
echo msg
|
||||
shard.client.log.debug(msg)
|
||||
|
||||
await shard.ws.sendText($request)
|
||||
await shard.ws.sendText("[SHARD " & $shard.id & "] " & $request)
|
||||
|
||||
proc handleHeartbeat(shard: Shard) {.async.} =
|
||||
while true:
|
||||
|
@ -50,7 +49,7 @@ proc handleHeartbeat(shard: Shard) {.async.} =
|
|||
await shard.sendGatewayRequest(heartbeatPayload, fmt("Sending heartbeat payload: {$heartbeatPayload}"))
|
||||
shard.heartbeatAcked = true
|
||||
|
||||
echo "Waiting ", shard.heartbeatInterval, " ms until next heartbeat..."
|
||||
shard.client.log.debug("[SHARD " & $shard.id & "] Waiting " & $shard.heartbeatInterval & " ms until next heartbeat...")
|
||||
await sleepAsync(shard.heartbeatInterval)
|
||||
|
||||
proc getIdentifyPacket(shard: Shard): JsonNode =
|
||||
|
@ -70,11 +69,11 @@ proc getIdentifyPacket(shard: Shard): JsonNode =
|
|||
result.add("shard", %*[shard.id, shard.client.shardCount])
|
||||
|
||||
proc closeConnection*(shard: Shard, code: int = 1000) {.async.} =
|
||||
echo "Disconnecting with code: ", code
|
||||
shard.client.log.warn("[SHARD " & $shard.id & "] Disconnecting with code: " & $code)
|
||||
await shard.ws.close(code)
|
||||
|
||||
proc reconnectShard(shard: Shard) {.async.} =
|
||||
echo "Reconnecting..."
|
||||
shard.client.log.info("[SHARD " & $shard.id & "] Reconnecting...")
|
||||
shard.reconnecting = true
|
||||
await shard.ws.close(1000)
|
||||
|
||||
|
@ -89,7 +88,7 @@ proc reconnectShard(shard: Shard) {.async.} =
|
|||
proc handleGatewayDisconnect(shard: Shard, error: string) {.async.} =
|
||||
let disconnectData = extractCloseData(error)
|
||||
|
||||
echo "Discord gateway disconnected! Error code: ", disconnectData.code, ", msg: ", disconnectData.reason
|
||||
shard.client.log.warn("[SHARD " & $shard.id & "] Discord gateway disconnected! Error code: " & $disconnectData.code & ", msg: " & disconnectData.reason)
|
||||
|
||||
shard.heartbeatAcked = false
|
||||
|
||||
|
@ -98,12 +97,12 @@ proc handleGatewayDisconnect(shard: Shard, error: string) {.async.} =
|
|||
|
||||
# 4003, 4004, 4005, 4007, 4010, 4011, 4012, 4013 are not reconnectable.
|
||||
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."
|
||||
shard.client.log.error("[SHARD " & $shard.id & "] The Discord gateway sent a disconnect code that we cannot reconnect to.")
|
||||
else:
|
||||
if not shard.reconnecting:
|
||||
waitFor shard.reconnectShard()
|
||||
else:
|
||||
echo "Gateway is cannot reconnect due to already reconnecting..."
|
||||
shard.client.log.debug("[SHARD " & $shard.id & "] Gateway cannot reconnect due to already reconnecting...")
|
||||
|
||||
#TODO: Reconnecting may be done, just needs testing.
|
||||
proc handleWebsocketPacket(shard: Shard) {.async.} =
|
||||
|
@ -111,7 +110,7 @@ proc handleWebsocketPacket(shard: Shard) {.async.} =
|
|||
var packet: tuple[opcode: Opcode, data: string]
|
||||
|
||||
packet = await shard.ws.readData()
|
||||
echo "[SHARD ", $shard.id, "] Received gateway payload: ", packet.data
|
||||
shard.client.log.debug("[SHARD " & $shard.id & "] Received gateway payload: " & $packet.data)
|
||||
|
||||
if packet.opcode == Opcode.Close:
|
||||
await shard.handleGatewayDisconnect(packet.data)
|
||||
|
@ -122,7 +121,7 @@ proc handleWebsocketPacket(shard: Shard) {.async.} =
|
|||
try:
|
||||
json = parseJson(packet.data)
|
||||
except:
|
||||
echo "Failed to parse websocket payload: ", packet.data
|
||||
shard.client.log.error("[SHARD " & $shard.id & "] Failed to parse websocket payload: " & $packet.data)
|
||||
continue
|
||||
|
||||
if json.contains("s"):
|
||||
|
@ -131,7 +130,7 @@ proc handleWebsocketPacket(shard: Shard) {.async.} =
|
|||
case json["op"].getInt()
|
||||
of ord(DiscordOpCode.opHello):
|
||||
if shard.reconnecting:
|
||||
echo "Reconnected!"
|
||||
shard.client.log.info("[SHARD " & $shard.id & "Reconnected!")
|
||||
shard.reconnecting = false
|
||||
|
||||
let resume = %* {
|
||||
|
@ -189,7 +188,7 @@ proc startConnection*(client: DiscordClient, shardAmount: int = 1) {.async.} =
|
|||
## tokenStream.close()
|
||||
##
|
||||
## var bot = newDiscordClient(tkn)
|
||||
echo "Connecting..."
|
||||
client.log.info("[CLIENT] Connecting...")
|
||||
|
||||
# let urlResult = sendRequest(endpoint("/gateway/bot"), HttpMethod.HttpGet, defaultHeaders())
|
||||
let urlResult = sendRequest(endpoint("/gateway"), HttpMethod.HttpGet, defaultHeaders())
|
||||
|
@ -223,7 +222,7 @@ proc startConnection*(client: DiscordClient, shardAmount: int = 1) {.async.} =
|
|||
|
||||
asyncCheck shard.handleWebsocketPacket()
|
||||
|
||||
# Now just wait. Dont poll while we're reconnecting
|
||||
# Just wait. Don't poll while we're reconnecting
|
||||
while true:
|
||||
if not shard.reconnecting:
|
||||
poll()
|
||||
|
@ -253,15 +252,17 @@ proc getClientInstance*(instanceID: uint8): DiscordClient =
|
|||
## Get a client instance with instance id. Mainly used internally.
|
||||
return clientInstances[instanceID]
|
||||
|
||||
proc newDiscordClient*(tkn: string, commandPrefix: string): DiscordClient =
|
||||
proc newDiscordClient*(tkn: string, commandPrefix: string, log: Log = newLog(ord(LoggerFlags.loggerFlagWarnSeverity) or ord(LoggerFlags.loggerFlagInfoSeverity) or ord(LoggerFlags.loggerFlagErrorSeverity))): DiscordClient =
|
||||
## Create a DiscordClient using a token.
|
||||
##
|
||||
## Sets globalDiscordClient to the newly created client.
|
||||
## Sets globalToken to the newly created client's token.
|
||||
globalToken = tkn
|
||||
globalLog = log
|
||||
|
||||
var cac: Cache
|
||||
new(cac)
|
||||
|
||||
result = DiscordClient(token: tkn, cache: cac, commandPrefix: commandPrefix, log: log)
|
||||
result = DiscordClient(token: tkn, cache: cac, commandPrefix: commandPrefix, instanceID: nextInstanceId)
|
||||
clientInstances.add(nextInstanceId, result)
|
||||
nextInstanceId++
|
|
@ -1,4 +1,4 @@
|
|||
import websocket, cache, user
|
||||
import websocket, cache, user, log
|
||||
|
||||
type
|
||||
DiscordClient* = ref object
|
||||
|
@ -11,6 +11,7 @@ type
|
|||
endpoint*: string
|
||||
commandPrefix*: string
|
||||
instanceID*: uint8
|
||||
log*: Log
|
||||
|
||||
Shard* = ref object
|
||||
id*: int
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import eventhandler, json, tables, message, emoji, user, member, role
|
||||
import guild, channel, nimcordutils, httpClient, strformat, cache
|
||||
import sequtils, asyncdispatch, clientobjects, discordobject, presence
|
||||
import commandsystem
|
||||
import commandsystem, log
|
||||
|
||||
proc readyEvent(shard: Shard, json: JsonNode) =
|
||||
var readyEvent = ReadyEvent(shard: shard, readyPayload: json, name: $EventType.evtReady)
|
||||
|
@ -12,7 +12,7 @@ proc readyEvent(shard: Shard, json: JsonNode) =
|
|||
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: {}"
|
||||
shard.client.log.debug("[SHARD " & $shard.id & "] Sending GET request, URL: body: {}")
|
||||
|
||||
waitForRateLimits(0, RateLimitBucketType.global)
|
||||
var userJson = handleResponse(client.request(endpoint("/users/@me"), HttpGet, ""), 0, RateLimitBucketType.global)
|
||||
|
@ -512,5 +512,5 @@ proc handleDiscordEvent*(shard: Shard, json: JsonNode, eventName: string) {.asyn
|
|||
let eventProc: proc(shard: Shard, json: JsonNode) = internalEventTable[eventName]
|
||||
eventProc(shard, json)
|
||||
else:
|
||||
echo "Failed to find event: ", eventName
|
||||
shard.client.log.error("[SHARD " & $shard.id & "] Failed to find event: " & eventName)
|
||||
|
||||
|
|
|
@ -245,29 +245,22 @@ proc registerEventListener*(event: EventType, listener: proc(event: BaseEvent))
|
|||
## .. code-block:: nim
|
||||
## registerEventListener(EventType.evtReady, proc(bEvt: BaseEvent) =
|
||||
## let event = ReadyEvent(bEvt)
|
||||
## bot.clientUser = event.clientUser
|
||||
##
|
||||
## echo "Ready! (v", nimcordMajor, ".", nimcordMinor, ".", nimcordMicro, ")"
|
||||
## echo "Logged in as: ", bot.clientUser.username, "#", bot.clientUser.discriminator
|
||||
## echo "ID: ", bot.clientUser.id
|
||||
## echo "--------------------"
|
||||
## event.shard.client.log.info("Ready!")
|
||||
## event.shard.client.log.info("Logged in as: " & bot.clientUser.username & "#" & $bot.clientUser.discriminator)
|
||||
## event.shard.client.log.info("ID: " & $bot.clientUser.id)
|
||||
## event.shard.client.log.info("--------------------")
|
||||
## )
|
||||
if eventListeners.hasKey($event):
|
||||
eventListeners[$event].add(cast[proc(event: BaseEvent)](listener))
|
||||
|
||||
echo "Added other event listener: ", $event
|
||||
else:
|
||||
let tmp = @[listener]
|
||||
eventListeners.add($event, tmp)
|
||||
|
||||
echo "Added new event listener: ", $event
|
||||
|
||||
proc dispatchEvent*[T: BaseEvent](event: T) =
|
||||
## Dispatches an event so something can listen to it.
|
||||
if eventListeners.hasKey(event.name):
|
||||
let listeners = eventListeners[event.name]
|
||||
echo "Dispatching event: ", event.name
|
||||
|
||||
for index, eventListener in listeners.pairs:
|
||||
eventListener(event)
|
||||
else:
|
||||
echo "No event listeners for event: ", event.name
|
||||
|
|
|
@ -21,7 +21,7 @@ type
|
|||
logSevDebug = 3
|
||||
|
||||
proc newLog*(flags: int, filePath: string = ""): Log =
|
||||
## Create a new file. Colors in a file is printed as "fgYellow".
|
||||
## Create a new log. Colors in a file is printed as "fgYellow".
|
||||
var log = Log(flags: flags)
|
||||
if filePath.len > 0:
|
||||
log.logFile = newFileStream(filePath, fmWrite)
|
||||
|
@ -59,37 +59,36 @@ proc severityToString(sev: LogSeverity): string =
|
|||
of LogSeverity.logSevDebug:
|
||||
return "DEBUG"
|
||||
|
||||
#TODO: Remove colors from file.
|
||||
template autoLog(log: Log, sev: LogSeverity, args: varargs[untyped]) =
|
||||
proc autoLog(log: Log, sev: LogSeverity, text: string) =
|
||||
if log.canLog(sev):
|
||||
let timeFormated = getTime().format("[HH:mm:ss]")
|
||||
let sevStr = "[" & severityToString(sev) & "]"
|
||||
|
||||
let logHeader = timeFormated & " " & sevStr & " "
|
||||
|
||||
terminal.styledEcho(logHeader, args)
|
||||
terminal.styledEcho(logHeader, text)
|
||||
|
||||
if log.logFile != nil:
|
||||
log.logFile.writeLine(logHeader, args)
|
||||
log.logFile.writeLine(logHeader, text)
|
||||
|
||||
template debug*(log: Log, args: varargs[untyped]) =
|
||||
proc debug*(log: Log, text: string) =
|
||||
## Log debug severity. Example output: `[22:34:31] [DEBUG] Test`
|
||||
log.autoLog(logSevDebug, args)
|
||||
log.autoLog(logSevDebug, text)
|
||||
|
||||
template warn*(log: Log, args: varargs[untyped]) =
|
||||
proc warn*(log: Log, text: string) =
|
||||
## Log warning severity. Example output: `[22:34:31] [WARN] Test`
|
||||
log.autoLog(logSevWarn, args)
|
||||
log.autoLog(logSevWarn, text)
|
||||
|
||||
template error*(log: Log, args: varargs[untyped]) =
|
||||
proc error*(log: Log, text: string) =
|
||||
## Log error severity. Example output: `[22:34:31] [ERROR] Test`
|
||||
log.autoLog(logSevError, args)
|
||||
log.autoLog(logSevError, text)
|
||||
|
||||
template info*(log: Log, args: varargs[untyped]) =
|
||||
proc info*(log: Log, text: string) =
|
||||
## Log info severity. Example output: `[22:34:31] [INFO] Test`
|
||||
log.autoLog(logSevInfo, args)
|
||||
log.autoLog(logSevInfo, text)
|
||||
|
||||
proc closeLog*(log: Log) =
|
||||
## Close log file if it was ever open.
|
||||
if log.logFile != nil:
|
||||
log.info(fgYellow, "Closing log...")
|
||||
log.info("Closing log...")
|
||||
log.logFile.close()
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import parseutils, json, httpClient, strformat, tables, times, asyncdispatch, strutils
|
||||
import parseutils, json, httpClient, strformat, tables, times, asyncdispatch, strutils, log
|
||||
from discordobject import Snowflake
|
||||
|
||||
type ImageType* = enum
|
||||
|
@ -22,6 +22,7 @@ proc endpoint*(url: string): string =
|
|||
return fmt("https://discord.com/api/v6{url}")
|
||||
|
||||
var globalToken*: string
|
||||
var globalLog*: Log
|
||||
|
||||
proc defaultHeaders*(added: HttpHeaders = newHttpHeaders()): HttpHeaders =
|
||||
added.add("Authorization", fmt("Bot {globalToken}"))
|
||||
|
@ -87,7 +88,7 @@ proc handleRateLimits*(headers: HttpHeaders, objectID: Snowflake, bucketType: Ra
|
|||
|
||||
|
||||
proc handleResponse*(response: Response, objectID: Snowflake, bucketType: RateLimitBucketType): JsonNode =
|
||||
echo fmt("Received requested payload: {response.body}")
|
||||
globalLog.debug(fmt("Received requested payload: {response.body}"))
|
||||
|
||||
handleRateLimits(response.headers, objectID, bucketType)
|
||||
|
||||
|
@ -124,7 +125,7 @@ proc waitForRateLimits*(objectID: Snowflake, bucketType: RateLimitBucketType) =
|
|||
let millisecondTime: float = rlmt.ratelimitReset * 1000 - epochTime() * 1000
|
||||
|
||||
if millisecondTime > 0:
|
||||
echo fmt("Rate limit wait time: {millisecondTime} miliseconds")
|
||||
globalLog.debug(fmt("Rate limit wait time: {millisecondTime} miliseconds"))
|
||||
waitFor sleepAsync(millisecondTime)
|
||||
|
||||
proc sendRequest*(endpoint: string, httpMethod: HttpMethod, headers: HttpHeaders, objectID: Snowflake = 0,
|
||||
|
@ -138,7 +139,7 @@ proc sendRequest*(endpoint: string, httpMethod: HttpMethod, headers: HttpHeaders
|
|||
strPayload = ""
|
||||
else:
|
||||
strPayload = $jsonBody
|
||||
echo "Sending ", httpMethod, " request, URL: ", endpoint, ", headers: ", $headers, " body: ", strPayload
|
||||
globalLog.debug("Sending " & $httpMethod & " request, URL: " & endpoint & ", headers: " & $headers & " body: " & strPayload)
|
||||
|
||||
waitForRateLimits(objectID, bucketType)
|
||||
let response = client.request(endpoint, httpMethod, strPayload)
|
||||
|
|
Reference in New Issue