diff --git a/structures/baseStruct.js b/structures/baseStruct.js new file mode 100644 index 0000000..0d6157f --- /dev/null +++ b/structures/baseStruct.js @@ -0,0 +1,6 @@ +export class BaseStruct { + /** @type {import('./client/client.js').Client} */ + client; + + constructor(c) { this.client = c; } +} \ No newline at end of file diff --git a/structures/client/client.js b/structures/client/client.js index 2362ab7..17c1f3b 100644 --- a/structures/client/client.js +++ b/structures/client/client.js @@ -8,6 +8,7 @@ import axios from 'axios'; import { exit } from 'process'; import Guild from '../guilds/Guild.js'; import user from '../messages/User.js'; +import { Thread } from '../guilds/ThreadManager.js'; @@ -39,6 +40,8 @@ export class Client extends EventEmitter { /** @type {Map} */ guilds; + /** @type {import('axios').AxiosInstance} */ + axiosCustom; /** * @param {opts} input @@ -74,16 +77,24 @@ export class Client extends EventEmitter { this.emit("messageRecieved", msg); } - ready() { + ready(response) { + this.user_profile = response.profile; + this.user_settings = response.config; + this.id = response.profile.id; this.emit('ready'); } - customError(err) { - this.emit('error', err); - } + customError(err) { this.emit('error', err); } - interactionRecieved(interaction) { - this.emit('interactionRecieved', interaction); + interactionRecieved(data, response) { + if (data["d"]["member"] && data["d"]["member"]["id"] != this.user_profile.id) { + response.interaction.guild = this.guilds.get(response.interaction.guild_id); + response.interaction.user = new user(data["d"]["member"]["user"]); + this.emit('interactionRecieved', response.interaction); + } + else if (data["d"]["user"]["id"] != this.user_profile.id) { + this.emit('interactionRecieved', response.interaction); + } } /** @@ -103,6 +114,30 @@ export class Client extends EventEmitter { guildMemberAdd(member) { this.emit('guildMemberAdd', member); } + + + threadCreate(threadRaw) { + const guild = this.guilds.get(threadRaw.guild_id); + const newThread = new Thread(threadRaw, guild, this.#token); + guild.threads.cache.set(newThread.id, newThread); + } + + threadEdit(threadRaw) { + if (!this.guilds.has(threadRaw.guild_id)) return; + const guild = this.guilds.get(threadRaw.guild_id); + + if (!guild.threads.cache.has(threadRaw.id)) return; + console.log(threadRaw); + } + + ThreadDelete(threadRaw) { + if (!this.guilds.has(threadRaw.guild_id)) return; + const guild = this.guilds.get(threadRaw.guild_id); + + if (!guild.threads.cache.has(threadRaw.id)) return; + guild.threads.cache.delete() + } + //#endregion @@ -111,6 +146,10 @@ export class Client extends EventEmitter { */ async login(token, isUser = false) { if (!isUser) token = "Bot " + token; + this.axiosCustom = axios.create({ + baseURL: "https://discord.com/api/", + headers: { Authorization: this.#token } + }); return new Promise((resolve, reject) => { this.ws = new WebSocket("wss://gateway.discord.gg/?v=10&encoding=json"); @@ -143,35 +182,43 @@ export class Client extends EventEmitter { const data = JSON.parse(msg.toString()); const response = await handleResponses(data, token, this.id); - if (response.op == 10) { this.#startHeartBeat(response.heartBeat, token); } + if (response.op == 10) { return this.#startHeartBeat(response.heartBeat, token); } + else if (response.op == 0) { - if (response.t == gateWayEvents.Ready) { - this.user_profile = response.profile; - this.user_settings = response.config; - this.id = response.profile.id; - // console.log(response.guilds); - this.ready(); + switch(response.t) { + case gateWayEvents.Ready: this.ready(response); + break; + + case gateWayEvents.MessageCreate: + if (data["d"]["author"]["id"] != this.user_profile.id){ + response.message.guild = this.guilds.get(response.message.guild_id); + this.messageRecieved(response.message); + } + break; + + case gateWayEvents.InteractionCreate: this.interactionRecieved(data, response); + break; + + case gateWayEvents.GuildCreate: this.guildCreate(response.guild); + break; + + case gateWayEvents.GuildDelete: this.guildDelete(response.guild); + break; + + case gateWayEvents.GuildMemberAdd: this.guildMemberAdd(response.member); + break; + + case gateWayEvents.ThreadCreate: this.threadCreate(response.threadRaw); + break; + + case gateWayEvents.ThreadUpdate: this.threadEdit(response.threadRaw); + break; + + case gateWayEvents.ThreadDelete: this.ThreadDelete(response.threadRaw); + break; + + default: // console.log(response.t); } - else if (response.t == gateWayEvents.MessageCreate) { - if (data["d"]["author"]["id"] != this.user_profile.id){ - response.message.guild = this.guilds.get(response.message.guild_id); - this.messageRecieved(response.message); - } - } - else if (response.t == gateWayEvents.InteractionCreate) { - if (data["d"]["member"] && data["d"]["member"]["id"] != this.user_profile.id) { - response.interaction.guild = this.guilds.get(response.interaction.guild_id); - response.interaction.user = new user(data["d"]["member"]["user"]); - this.interactionRecieved(response.interaction); - } - else if (data["d"]["user"]["id"] != this.user_profile.id) { - this.interactionRecieved(response.interaction); - } - } - else if (response.t == gateWayEvents.GuildCreate) this.guildCreate(response.guild); - else if (response.t == gateWayEvents.GuildDelete) this.guildDelete(response.guild); - else if (response.t == gateWayEvents.GuildMemberAdd) this.guildMemberAdd(response.member); - // else console.log(response.t); } else { // commmented to avoid heartbeats // console.log(response.t); diff --git a/structures/client/handleEvents.js b/structures/client/handleEvents.js index d2c2014..32de61f 100644 --- a/structures/client/handleEvents.js +++ b/structures/client/handleEvents.js @@ -15,25 +15,39 @@ export default async function handleEvents(msgObj, token, id) { const t = msgObj["t"]; if (op == 10) return resolve({op: op, heartBeat: msgObj["d"]["heartbeat_interval"]}); + else if (op != 0) { return resolve(false); } // https://discord.com/developers/docs/topics/opcodes-and-status-codes#gateway-gateway-opcodes - else if (op != 0) { resolve(false); } + switch(t) { + case gateWayEvents.Ready: + resolve({op: op, t: t, config: msgObj["d"]["user_settings"], profile: msgObj["d"]["user"]}); //, guilds: msgObj["d"]["guilds"] + break; - else if (t == gateWayEvents.Ready) { - resolve({op: op, t: t, config: msgObj["d"]["user_settings"], profile: msgObj["d"]["user"]}); //, guilds: msgObj["d"]["guilds"] - } - else if (t == gateWayEvents.MessageCreate) { - const msg = new message(msgObj["d"], token); - resolve({op: op, t: t, message: msg}); - } - else if (t == gateWayEvents.InteractionCreate) { - resolve({op: op, t: t, interaction: new Interaction(msgObj["d"], token, id)}); - } - else if (t == gateWayEvents.GuildCreate) { - resolve({op: op, t: t, guild: new Guild(msgObj["d"], token)}); - } - - else { - // console.log(t); + case gateWayEvents.MessageCreate: + const msg = new message(msgObj["d"], token); + resolve({op: op, t: t, message: msg}); + break; + + case gateWayEvents.InteractionCreate: + resolve({op: op, t: t, interaction: new Interaction(msgObj["d"], token, id)}); + break; + + case gateWayEvents.GuildCreate: + resolve({op: op, t: t, guild: new Guild(msgObj["d"], token)}); + break; + + case gateWayEvents.ThreadCreate: + resolve({op: op, t: t, threadRaw: msgObj["d"]}); + break; + + case gateWayEvents.ThreadDelete: + resolve({op: op, t: t, threadRaw: msgObj["d"]}); + break; + + case gateWayEvents.ThreadUpdate: + resolve({op: op, t: t, threadRaw: msgObj["d"]}); + break; + + default: console.log(t); } }); } \ No newline at end of file diff --git a/structures/guilds/Channel.js b/structures/guilds/Channel.js index ae5116d..bf390f0 100644 --- a/structures/guilds/Channel.js +++ b/structures/guilds/Channel.js @@ -1,7 +1,8 @@ import axios from 'axios'; import {message} from '../messages/message.js'; +import { BaseStruct } from '../baseStruct.js'; -export class Channel { +export class Channel extends BaseStruct { /** @type {String} */ id; @@ -56,6 +57,8 @@ export class Channel { constructor(channel, guild, token = null) { + super(); + this.#token = token; for (const k in this) { if (channel[k]) this[k] = channel[k]; diff --git a/structures/guilds/GuildChannelManager.js b/structures/guilds/GuildChannelManager.js index fd11ada..f52af95 100644 --- a/structures/guilds/GuildChannelManager.js +++ b/structures/guilds/GuildChannelManager.js @@ -1,8 +1,10 @@ import axios from 'axios'; import { Channel } from './Channel.js'; import Guild from './Guild.js'; +import { BaseStruct } from '../baseStruct.js'; -export class GuildChannelManager { + +export class GuildChannelManager extends BaseStruct { #token; /** @type {Guild} */ @@ -91,8 +93,9 @@ export class GuildChannelManager { }); } - constructor(token, guild, newCache) { + super(); + this.#token = token; this.guild = guild; this.cache = newCache; diff --git a/structures/guilds/GuildStickers.js b/structures/guilds/GuildStickers.js index f8ee4b2..9f9964e 100644 --- a/structures/guilds/GuildStickers.js +++ b/structures/guilds/GuildStickers.js @@ -1,8 +1,10 @@ import axios from "axios"; import user from "../messages/User.js"; import Guild from "./Guild.js"; +import { BaseStruct } from "../baseStruct.js"; -export class guildSticker { + +export class guildSticker extends BaseStruct { #token; /** @type {String} */ diff --git a/structures/guilds/ThreadManager.js b/structures/guilds/ThreadManager.js new file mode 100644 index 0000000..56c39fc --- /dev/null +++ b/structures/guilds/ThreadManager.js @@ -0,0 +1,80 @@ +import axios from "axios"; +import { Channel } from "./Channel.js"; + +export class Thread extends Channel { + /** @type {Number} */ + total_message_sent; + + /** @type {{locked: Boolean, create_timestamp: String, auto_archive_duration: Number, archived: Boolean, archive_timestamp: String}} */ + thread_metadata; + + /** @type {Number} */ + message_count; + + + constructor(o, guild, token) { + super(o, guild, token); + this.last_message_sent = o['total_message_sent']; + this.thread_metadata = o['thread_metadata']; + this.message_count = o['thread_metadata']; + } +} + + +export class ThreadManager { + #token; + /** @type {Map} */ + cache; + + /** + * @returns {Thread | Boolean} The deleted thread or false if the operation failed + * @param {Thread | String} threadId + */ + has(thread) { + const tid = (typeof thread == 'string') ? thread : thread.id; + if (!this.cache.has(tid)) return false; + const threadToDel = this.cache.get(tid); + this.cache.delete(tid); + return threadToDel; + } + + + /** + * @description returns the deleted thread if successful + * @param {Thread} thread + * @param {String} reason + * @returns {promise} + */ +async delete(thread, reason=null) { + return new Promise(async (resolve) => { + try { + if (!this.cache.has(thread.id)) throw "This thread does not exist!"; + + const config = { + headers: { + Authorization: this.#token + } + } + + await axios.delete(`https://discord.com/api/channels/${thread.id}`, config); + const newChannel = this.cache.get(thread.id); + this.cache.delete(thread.id); + resolve(newChannel); + } catch (err) { + throw err; + } + }); +} + + + + constructor(o, guild, token) { + this.cache = new Map(); + this.#token = token; + + for (const k of o) { + const newThread = new Thread(k, guild, token); + this.cache.set(newThread.id, newThread); + } + } +} \ No newline at end of file diff --git a/structures/guilds/guild.js b/structures/guilds/guild.js index 7b6bd2d..fb4d3df 100644 --- a/structures/guilds/guild.js +++ b/structures/guilds/guild.js @@ -6,10 +6,12 @@ import guildInvite from './guildInvite.js'; import { guildSticker, guildStickerManager } from './GuildStickers.js'; import { GuildChannelManager } from './GuildChannelManager.js'; import { Channel } from './Channel.js'; +import { ThreadManager } from './ThreadManager.js'; +import { BaseStruct } from '../baseStruct.js'; //See https://discord.com/developers/docs/resources/guild -export default class Guild { +export default class Guild extends BaseStruct { #token; //#region Vars @@ -79,8 +81,8 @@ export default class Guild { /** @type {Number} */ mfa_level; - // /** @type {String} */ //FIXME - // threads; + /** @type {ThreadManager} */ + threads; /** @type {Number} */ system_channel_flags; @@ -217,6 +219,7 @@ export default class Guild { * @param {String} token */ constructor(o, token) { + super(); this.members = new Map(); this.channels = new Map(); this.stickers = []; @@ -236,6 +239,9 @@ export default class Guild { else if (field == 'stickers') { this.stickers = new guildStickerManager(this, o[field], token); } + else if (field == 'threads') { + this.threads = new ThreadManager(o[field], this, token); + } else { this[field] = o[field]; } diff --git a/structures/guilds/guildInvite.js b/structures/guilds/guildInvite.js index c6516c4..1dfc2a3 100644 --- a/structures/guilds/guildInvite.js +++ b/structures/guilds/guildInvite.js @@ -2,9 +2,10 @@ import author from '../messages/User.js'; import Guild from './Guild.js' import { Channel } from './Channel.js'; import axios from 'axios'; +import { BaseStruct } from '../baseStruct.js'; -export default class invite { +export default class invite extends BaseStruct { #token; code; @@ -49,6 +50,8 @@ export default class invite { } constructor(o, guild, token) { + super(); + this.#token = token; for (const k in this) { if (o[k]) { diff --git a/structures/guilds/guildRoles.js b/structures/guilds/guildRoles.js index 5b876e1..a69d1ba 100644 --- a/structures/guilds/guildRoles.js +++ b/structures/guilds/guildRoles.js @@ -1,5 +1,6 @@ import axios from 'axios'; import Guild from './Guild.js'; +import { BaseStruct } from '../baseStruct.js'; // Maybe add support for this // https://discord.com/developers/docs/resources/guild#modify-guild-role-positions @@ -97,7 +98,7 @@ export class newGuildRoleObj { } -export class guildMemberRoleManager { +export class guildMemberRoleManager extends BaseStruct { #uid; #token; @@ -161,6 +162,7 @@ export class guildMemberRoleManager { * @param {String} uid UID or GuildId */ constructor(roles, uid, token) { + super(); this.#token = token; this.#uid = uid; this.cache = new Map(); @@ -169,7 +171,7 @@ export class guildMemberRoleManager { } -export class guildRoleManager { +export class guildRoleManager extends BaseStruct { #uid; #token; @@ -248,6 +250,7 @@ export class guildRoleManager { * @param {String} uid UID or GuildId */ constructor(roles, uid, token) { + super(); this.#token = token; this.#uid = uid; this.cache = new Map(); diff --git a/structures/guilds/member.js b/structures/guilds/member.js index 5ac4760..25314f9 100644 --- a/structures/guilds/member.js +++ b/structures/guilds/member.js @@ -1,9 +1,10 @@ import axios from 'axios'; import {guildRole, guildMemberRoleManager} from "./guildRoles.js"; +import { BaseStruct } from '../baseStruct.js'; // https://discord.com/developers/docs/resources/guild#modify-guild-member -export default class member { +export default class member extends BaseStruct { /** @type {Object} */ user; @@ -38,6 +39,8 @@ export default class member { avatar; constructor(o, roles) { + super(); + this.roles = roles; for (const k in this) { if (o[k] && k != 'roles') { diff --git a/structures/interactions/interaction.js b/structures/interactions/interaction.js index a66e0f4..c906986 100644 --- a/structures/interactions/interaction.js +++ b/structures/interactions/interaction.js @@ -4,8 +4,10 @@ import { message } from '../messages/message.js'; import { Channel } from '../guilds/Channel.js'; import {Embed} from '../messages/embed.js'; import Guild from '../guilds/Guild.js'; +import { BaseStruct } from '../baseStruct.js'; -class interactionOptions { + +class interactionOptions extends BaseStruct { /** @type {String} */ name; @@ -25,7 +27,7 @@ class interactionOptions { } } -export class Interaction { +export class Interaction extends BaseStruct { /** @type {author} */ user; diff --git a/structures/messages/message.js b/structures/messages/message.js index 728eb16..28f89ff 100644 --- a/structures/messages/message.js +++ b/structures/messages/message.js @@ -1,6 +1,7 @@ import author from './User.js'; import axios from 'axios'; import { Channel } from '../guilds/Channel.js'; +import Guild from '../guilds/Guild.js'; export class message { diff --git a/tests/guildTests.js b/tests/guildTests.js index b655dd5..ebc8c82 100644 --- a/tests/guildTests.js +++ b/tests/guildTests.js @@ -41,10 +41,16 @@ export default async function temp(c) { } } - const newChannel = await guild.channels.create({name: "temptemp"}); - await delay(1000); - guild.channels.edit(newChannel.id, {name: "NEW-NAME!"}); + // const newChannel = await guild.channels.create({name: "temptemp"}); + // await delay(1000); + // guild.channels.cache.forEach((channel) => { + // if (channel.name == "new-name") { + // guild.channels.delete(channel.id); + // } + // }) + + // guild.channels.edit(newChannel.id, {name: "NEW-NAME!"}); // const delConf = await guild.channels.delete(newChannel.id); // console.log(delConf); } \ No newline at end of file diff --git a/tests/messageTests.js b/tests/messageTests.js index a109257..d0f782e 100644 --- a/tests/messageTests.js +++ b/tests/messageTests.js @@ -22,8 +22,9 @@ export default async (message) => { //Get channel messages const messages = await response.channel.getMessages(); - console.log(messages); + // console.log(messages); } else { - console.log(message); + // console.log(message); + console.log(`Guild message recieved from "${message.guild.name}"`); } } \ No newline at end of file