Create channel, cache, and split client types into seperate nim file
This commit is contained in:
parent
947dc6884a
commit
b3dc748a64
|
@ -0,0 +1,29 @@
|
|||
import sequtils, message, member, channel, guild, discordobject, nimcordutils, clientobjects, httpcore
|
||||
|
||||
type Cache* = ref object
|
||||
members*: seq[GuildMember]
|
||||
messages*: seq[Message]
|
||||
channels*: seq[Channel]
|
||||
guilds*: seq[Guild]
|
||||
|
||||
proc getMessageChannel*(msg: Message, cache: Cache): Channel =
|
||||
for index, channel in cache.channels:
|
||||
if (channel.id == msg.channelID):
|
||||
return channel
|
||||
|
||||
return nil
|
||||
|
||||
proc getChannelGuild*(channel: Channel, cache: Cache): Guild =
|
||||
for index, guild in cache.guilds:
|
||||
if (guild.id == channel.guildID):
|
||||
return guild
|
||||
|
||||
return nil
|
||||
|
||||
proc getChannel*(cache: Cache, id: snowflake): Channel =
|
||||
for index, channel in cache.channels:
|
||||
if (channel.id == id):
|
||||
return channel
|
||||
|
||||
return newChannel(sendRequest(endpoint("/channels/" & $id), HttpGet,
|
||||
defaultHeaders(), id, RateLimitBucketType.channel))
|
|
@ -0,0 +1,141 @@
|
|||
import json, discordobject, user, options, nimcordutils, message, httpcore, asyncdispatch, asyncfutures, strutils
|
||||
|
||||
type
|
||||
ChannelType* = enum
|
||||
chanTypeNIL = -1,
|
||||
chanTypeGuildText = 0,
|
||||
chanTypeDM = 1,
|
||||
chanTypeGuildVoice = 2,
|
||||
chanTypeGroupDM = 3,
|
||||
chanTypeGuildCategory = 4,
|
||||
chanTypeGuildNews = 5,
|
||||
chanTypeGuildStore = 6
|
||||
|
||||
Channel* = ref object of DiscordObject
|
||||
`type`*: ChannelType ## The type of channel.
|
||||
guildID*: snowflake ## The id of the guild.
|
||||
position*: int ## Sorting position of the channel.
|
||||
#permissionOverwrites*: seq[Permissions] ## Explicit permission overwrites for members and roles.
|
||||
name*: string ## The name of the channel (2-100 characters).
|
||||
topic*: string ## The channel topic (0-1024 characters).
|
||||
nsfw*: bool ## Whether the channel is nsfw.
|
||||
lastMessageID*: snowflake ## The id of the last message sent in this channel (may not point to an existing or valid message).
|
||||
bitrate*: int ## The bitrate (in bits) of the voice channel.
|
||||
userLimit*: int ## The user limit of the voice channel.
|
||||
rateLimitPerUser*: int ## Amount of seconds a user has to wait before sending another message (0-21600); bots, as well as users with the permission manage_messages or manage_channel, are unaffected.
|
||||
recipients*: seq[User] ## The recipients of the DM
|
||||
icon*: string ## Icon hash
|
||||
ownerID: snowflake ## ID of the DM creator
|
||||
applicationID: snowflake ## Application id of the group DM creator if it is bot-created
|
||||
parentID: snowflake ## ID of the parent category for a channel
|
||||
lastPinTimestamp: string ## When the last pinned message was pinned
|
||||
|
||||
ChannelModify* {.requiresInit.} = ref object
|
||||
## Use this type to modify a channel by setting each fields.
|
||||
name*: Option[string]
|
||||
`type`*: Option[ChannelType]
|
||||
position*: Option[int]
|
||||
topic*: Option[string]
|
||||
nsfw*: Option[bool]
|
||||
rateLimitPerUser*: Option[int]
|
||||
bitrate*: Option[int]
|
||||
userLimit*: Option[int]
|
||||
#permissionOverwrites*: seq[Permissions] ## Explicit permission overwrites for members and roles.
|
||||
parentID*: Option[snowflake]
|
||||
|
||||
#[ proc newChannelModify*(name: Option[string], `type`: Option[ChannelType], position: Option[int],
|
||||
topic: Option[string], nsfw: Option[bool], rateLimitPerUser: Option[int], bitrate: Option[int],
|
||||
userLimit: Option[int], parentID: Option[snowflake]): ChannelModify =
|
||||
|
||||
return ChannelModify(name: name.get, `type`:`type`, position: position, nsfw: nsfw,
|
||||
rateLimitPerUser: rateLimitPerUser, bitrate: bitrate, userLimit: userLimit, parentID: parentID) ]#
|
||||
|
||||
proc newChannel*(channel: JsonNode): Channel {.inline.} =
|
||||
var chan = Channel(
|
||||
id: getIDFromJson(channel["id"].getStr()),
|
||||
`type`: ChannelType(channel["type"].getInt()),
|
||||
)
|
||||
|
||||
if (channel.contains("guild_id")):
|
||||
chan.guildID = getIDFromJson(channel["guild_id"].getStr())
|
||||
if (channel.contains("position")):
|
||||
chan.position = channel["position"].getInt()
|
||||
if (channel.contains("permission_overwrites")):
|
||||
echo "permission_overwrites"
|
||||
if (channel.contains("name")):
|
||||
chan.name = channel["name"].getStr()
|
||||
if (channel.contains("topic")):
|
||||
chan.topic = channel["topic"].getStr()
|
||||
if (channel.contains("nsfw")):
|
||||
chan.nsfw = channel["nsfw"].getBool()
|
||||
if (channel.contains("last_message_id")):
|
||||
chan.lastMessageID = getIDFromJson(channel["last_message_id"].getStr())
|
||||
if (channel.contains("bitrate")):
|
||||
chan.bitrate = channel["bitrate"].getInt()
|
||||
if (channel.contains("user_limit")):
|
||||
chan.userLimit = channel["user_limit"].getInt()
|
||||
if (channel.contains("rate_limit_per_user")):
|
||||
chan.rateLimitPerUser = channel["rate_limit_per_user"].getInt()
|
||||
if (channel.contains("recipients")):
|
||||
for recipient in channel["recipients"]:
|
||||
chan.recipients.insert(newUser(recipient))
|
||||
if (channel.contains("icon")):
|
||||
chan.icon = channel["icon"].getStr()
|
||||
if (channel.contains("owner_id")):
|
||||
chan.ownerID = getIDFromJson(channel["owner_id"].getStr())
|
||||
if (channel.contains("application_id")):
|
||||
chan.applicationID = getIDFromJson(channel["application_id"].getStr())
|
||||
if (channel.contains("parent_id")):
|
||||
chan.parentID = getIDFromJson(channel["parent_id"].getStr())
|
||||
if (channel.contains("last_pin_timestamp")):
|
||||
chan.lastPinTimestamp = channel["last_pin_timestamp"].getStr()
|
||||
|
||||
return chan
|
||||
|
||||
proc sendMessage*(channel: Channel, content: string, tts: bool = false): Message =
|
||||
let messagePayload = %*{"content": content, "tts": tts}
|
||||
|
||||
return newMessage(sendRequest(endpoint("/channels/" & $channel.id & "/messages"), HttpPost,
|
||||
defaultHeaders(newHttpHeaders({"Content-Type": "application/json"})), channel.id,
|
||||
RateLimitBucketType.channel, messagePayload))
|
||||
|
||||
proc modifyChannel*(channel: Channel, modify: ChannelModify): Future[Channel] {.async.} =
|
||||
var modifyPayload = %*{}
|
||||
|
||||
if (modify.name.isSome):
|
||||
modifyPayload.add("name", %modify.name.get())
|
||||
|
||||
if (modify.`type`.isSome):
|
||||
modifyPayload.add("type", %modify.`type`.get())
|
||||
|
||||
if (modify.position.isSome):
|
||||
modifyPayload.add("position", %modify.position.get())
|
||||
|
||||
if (modify.topic.isSome):
|
||||
modifyPayload.add("topic", %modify.topic.get())
|
||||
|
||||
if (modify.nsfw.isSome):
|
||||
modifyPayload.add("nsfw", %modify.nsfw.get())
|
||||
|
||||
if (modify.rateLimitPerUser.isSome):
|
||||
modifyPayload.add("rate_limit_per_user", %modify.rateLimitPerUser.get())
|
||||
|
||||
if (modify.bitrate.isSome):
|
||||
modifyPayload.add("bitrate", %modify.bitrate.get())
|
||||
|
||||
if (modify.userLimit.isSome):
|
||||
modifyPayload.add("user_limit", %modify.userLimit.get())
|
||||
|
||||
#[ if (modify.name.isSome):
|
||||
modifyPayload.add("permission_overwrites", %modify.parentID.get()0 ]#
|
||||
|
||||
if (modify.parentID.isSome):
|
||||
modifyPayload.add("parent_id", %modify.parentID.get())
|
||||
|
||||
return newChannel(sendRequest(endpoint("/channels/" & $channel.id), HttpPatch,
|
||||
defaultHeaders(newHttpHeaders({"Content-Type": "application/json"})),
|
||||
channel.id, RateLimitBucketType.channel, modifyPayload))
|
||||
|
||||
proc deleteChannel*(channel: Channel) {.async.} =
|
||||
discard sendRequest(endpoint("/channels/" & $channel.id), HttpDelete,
|
||||
defaultHeaders(), channel.id, RateLimitBucketType.channel)
|
|
@ -1,4 +1,11 @@
|
|||
import websocket, asyncnet, asyncdispatch, json, httpClient, eventdispatcher, strformat, eventhandler, streams, nimcordutils, discordobject
|
||||
import websocket, asyncnet, asyncdispatch, json, httpClient, eventdispatcher, strformat
|
||||
import eventhandler, streams, nimcordutils, discordobject, user, cache, clientobjects
|
||||
import strutils, channel, options
|
||||
|
||||
const
|
||||
nimcordMajor = 0
|
||||
nimcordMinor = 0
|
||||
nimcordMicro = 0
|
||||
|
||||
type
|
||||
DiscordOpCode = enum
|
||||
|
@ -14,41 +21,12 @@ type
|
|||
opHello = 10,
|
||||
opHeartbeatAck = 11
|
||||
|
||||
DiscordClient* = ref object ## Discord Client
|
||||
token*: string
|
||||
#user*: User
|
||||
#cache: Cache
|
||||
ws: AsyncWebSocket
|
||||
httpClient: AsyncHttpClient
|
||||
heartbeatInterval: int
|
||||
heartbeatAcked: bool
|
||||
lastSequence: int
|
||||
|
||||
var globalClient: DiscordClient
|
||||
|
||||
proc defaultHeaders*(client: DiscordClient, added: HttpHeaders = newHttpHeaders()): HttpHeaders =
|
||||
added.add("Authorization", fmt("Bot {client.token}"))
|
||||
added.add("User-Agent", "NimCord (https://github.com/SeanOMik/nimcord, v0.0.0)")
|
||||
added.add("X-RateLimit-Precision", "millisecond")
|
||||
return added;
|
||||
|
||||
proc sendRequest*(endpoint: string, httpMethod: HttpMethod, headers: HttpHeaders, objectID: snowflake = 0, bucketType: RateLimitBucketType = global, jsonBody: JsonNode = %*{}): JsonNode =
|
||||
var client = newHttpClient()
|
||||
# Add headers
|
||||
client.headers = headers
|
||||
|
||||
var strPayload: string
|
||||
if ($jsonBody == "{}"):
|
||||
strPayload = ""
|
||||
else:
|
||||
strPayload = $jsonBody
|
||||
|
||||
echo "Sending GET request, URL: ", endpoint, ", body: ", strPayload
|
||||
|
||||
waitForRateLimits(objectID, bucketType)
|
||||
|
||||
return handleResponse(client.request(endpoint, httpMethod, strPayload), objectId, bucketType)
|
||||
|
||||
proc sendGatewayRequest*(client: DiscordClient, request: JsonNode, msg: string = "") {.async.} =
|
||||
if (msg.len == 0):
|
||||
echo "Sending gateway payload: ", request
|
||||
|
@ -96,16 +74,11 @@ proc handleWebsocketPacket(client: DiscordClient) {.async.} =
|
|||
of ord(DiscordOpCode.opHeartbeatAck):
|
||||
client.heartbeatAcked = true
|
||||
of ord(DiscordOpCode.opDispatch):
|
||||
handleDiscordEvent(json["d"], json["t"].getStr())
|
||||
discard handleDiscordEvent(client, json["d"], json["t"].getStr())
|
||||
else:
|
||||
discard
|
||||
|
||||
proc startConnection*(client: DiscordClient) {.async.} =
|
||||
globalClient = client
|
||||
|
||||
client.httpClient = newAsyncHttpClient()
|
||||
client.httpClient.headers = newHttpHeaders({"Authorization": fmt("Bot {client.token}")})
|
||||
|
||||
let urlResult = sendRequest(endpoint("/gateway/bot"), HttpMethod.HttpGet, client.defaultHeaders())
|
||||
if (urlResult.contains("url")):
|
||||
let url = urlResult["url"].getStr()
|
||||
|
@ -122,6 +95,14 @@ proc startConnection*(client: DiscordClient) {.async.} =
|
|||
e.msg = "Failed to get gateway url, token may of been incorrect!"
|
||||
raise e
|
||||
|
||||
proc newDiscordClient(tkn: string): DiscordClient =
|
||||
globalToken = tkn
|
||||
|
||||
var cac: Cache
|
||||
new(cac)
|
||||
|
||||
result = DiscordClient(token: tkn, cache: cac)
|
||||
|
||||
var tokenStream = newFileStream("token.txt", fmRead)
|
||||
var tkn: string
|
||||
if (not isNil(tokenStream)):
|
||||
|
@ -130,21 +111,45 @@ if (not isNil(tokenStream)):
|
|||
|
||||
tokenStream.close()
|
||||
|
||||
var bot = DiscordClient(token: tkn)
|
||||
var bot = newDiscordClient(tkn)
|
||||
|
||||
registerEventListener(EventType.evtReady, proc(bEvt: BaseEvent) =
|
||||
let event = ReadyEvent(bEvt)
|
||||
echo "Ready and connected 1!"
|
||||
)
|
||||
bot.clientUser = event.clientUser
|
||||
|
||||
registerEventListener(EventType.evtReady, proc(bEvt: BaseEvent) =
|
||||
let event = ReadyEvent(bEvt)
|
||||
echo "Ready and connected 2!"
|
||||
echo "Ready! (v", nimcordMajor, ".", nimcordMinor, ".", nimcordMicro, ")"
|
||||
echo "Logged in as: ", bot.clientUser.username, "#", bot.clientUser.discriminator
|
||||
echo "ID: ", bot.clientUser.id
|
||||
echo "--------------------"
|
||||
)
|
||||
|
||||
registerEventListener(EventType.evtMessageCreate, proc(bEvt: BaseEvent) =
|
||||
let event = MessageCreateEvent(bEvt)
|
||||
echo "Message was created!"
|
||||
|
||||
if (event.message.content == "?ping"):
|
||||
var channel: Channel = event.message.getMessageChannel(event.client.cache)
|
||||
if (channel != nil):
|
||||
discard channel.sendMessage("PONG")
|
||||
elif (event.message.content.startsWith("?modifyChannelTopic")):
|
||||
let modifyTopic = event.message.content.substr(20)
|
||||
var channel: Channel = event.message.getMessageChannel(event.client.cache)
|
||||
if (channel != nil):
|
||||
discard channel.sendMessage("Modifing Channel!")
|
||||
discard channel.modifyChannel(ChannelModify(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)
|
||||
if (channel != nil):
|
||||
discard channel.sendMessage("Deleting Channel!")
|
||||
discard channel.deleteChannel()
|
||||
discard channel.sendMessage("Deleted Channel!")
|
||||
)
|
||||
|
||||
#[ registerEventListener(EventType.evtGuildCreate, proc(bEvt: BaseEvent) =
|
||||
let event = GuildCreateEvent(bEvt)
|
||||
|
||||
echo "Guild has ", event.guild.members.len, " members and ", event.guild.channels.len, " channels..."
|
||||
) ]#
|
||||
|
||||
waitFor bot.startConnection()
|
|
@ -0,0 +1,10 @@
|
|||
import websocket, cache, user
|
||||
|
||||
type DiscordClient* = ref object ## Discord Client
|
||||
token*: string
|
||||
clientUser*: User
|
||||
cache*: Cache
|
||||
ws*: AsyncWebSocket
|
||||
heartbeatInterval*: int
|
||||
heartbeatAcked*: bool
|
||||
lastSequence*: int
|
Reference in New Issue