diff --git a/src/gateway/handlers/index.ts b/src/gateway/handlers/index.ts index 9974fff..87e014e 100644 --- a/src/gateway/handlers/index.ts +++ b/src/gateway/handlers/index.ts @@ -13,32 +13,34 @@ import { guildBanRemove } from './guildBanRemove.ts' import { messageCreate } from './messageCreate.ts' import { resume } from './resume.ts' import { reconnect } from './reconnect.ts' -import { messageDelete } from "./messageDelete.ts" -import { messageUpdate } from "./messageUpdate.ts" -import { guildEmojiUpdate } from "./guildEmojiUpdate.ts" -import { guildMemberAdd } from "./guildMemberAdd.ts" -import { guildMemberRemove } from "./guildMemberRemove.ts" -import { guildMemberUpdate } from "./guildMemberUpdate.ts" -import { guildRoleCreate } from "./guildRoleCreate.ts" -import { guildRoleDelete } from "./guildRoleDelete.ts" -import { guildRoleUpdate } from "./guildRoleUpdate.ts" -import { guildIntegrationsUpdate } from "./guildIntegrationsUpdate.ts" -import { webhooksUpdate } from "./webhooksUpdate.ts" -import { messageDeleteBulk } from "./messageDeleteBulk.ts" -import { userUpdate } from "./userUpdate.ts" -import { typingStart } from "./typingStart.ts" -import { Channel } from "../../structures/channel.ts" -import { GuildTextChannel, TextChannel } from "../../structures/textChannel.ts" -import { Guild } from "../../structures/guild.ts" -import { User } from "../../structures/user.ts" -import { Emoji } from "../../structures/emoji.ts" -import { Member } from "../../structures/member.ts" -import { Role } from "../../structures/role.ts" -import { Message } from "../../structures/message.ts" -import { Collection } from "../../utils/collection.ts" -import { voiceServerUpdate } from "./voiceServerUpdate.ts" -import { voiceStateUpdate } from "./voiceStateUpdate.ts" -import { VoiceState } from "../../structures/voiceState.ts" +import { messageDelete } from './messageDelete.ts' +import { messageUpdate } from './messageUpdate.ts' +import { guildEmojiUpdate } from './guildEmojiUpdate.ts' +import { guildMemberAdd } from './guildMemberAdd.ts' +import { guildMemberRemove } from './guildMemberRemove.ts' +import { guildMemberUpdate } from './guildMemberUpdate.ts' +import { guildRoleCreate } from './guildRoleCreate.ts' +import { guildRoleDelete } from './guildRoleDelete.ts' +import { guildRoleUpdate } from './guildRoleUpdate.ts' +import { guildIntegrationsUpdate } from './guildIntegrationsUpdate.ts' +import { webhooksUpdate } from './webhooksUpdate.ts' +import { messageDeleteBulk } from './messageDeleteBulk.ts' +import { userUpdate } from './userUpdate.ts' +import { typingStart } from './typingStart.ts' +import { Channel } from '../../structures/channel.ts' +import { GuildTextChannel, TextChannel } from '../../structures/textChannel.ts' +import { Guild } from '../../structures/guild.ts' +import { User } from '../../structures/user.ts' +import { Emoji } from '../../structures/emoji.ts' +import { Member } from '../../structures/member.ts' +import { Role } from '../../structures/role.ts' +import { Message } from '../../structures/message.ts' +import { Collection } from '../../utils/collection.ts' +import { voiceServerUpdate } from './voiceServerUpdate.ts' +import { voiceStateUpdate } from './voiceStateUpdate.ts' +import { VoiceState } from '../../structures/voiceState.ts' +import { inviteCreate } from './inviteCreate.ts' +import { inviteDelete } from './inviteDelete.ts' export const gatewayHandlers: { [eventCode in GatewayEvents]: GatewayEventHandler | undefined @@ -64,8 +66,8 @@ export const gatewayHandlers: { GUILD_ROLE_CREATE: guildRoleCreate, GUILD_ROLE_UPDATE: guildRoleUpdate, GUILD_ROLE_DELETE: guildRoleDelete, - INVITE_CREATE: undefined, - INVITE_DELETE: undefined, + INVITE_CREATE: inviteCreate, + INVITE_DELETE: inviteDelete, MESSAGE_CREATE: messageCreate, MESSAGE_UPDATE: messageUpdate, MESSAGE_DELETE: messageDelete, @@ -126,4 +128,4 @@ export interface ClientEvents extends EventTypes { 'voiceStateRemove': (state: VoiceState) => void 'voiceStateUpdate': (state: VoiceState, after: VoiceState) => void 'webhooksUpdate': (guild: Guild, channel: GuildTextChannel) => void -} \ No newline at end of file +} diff --git a/src/gateway/handlers/inviteCreate.ts b/src/gateway/handlers/inviteCreate.ts new file mode 100644 index 0000000..b1e0f5d --- /dev/null +++ b/src/gateway/handlers/inviteCreate.ts @@ -0,0 +1,40 @@ +import { Gateway, GatewayEventHandler } from '../index.ts' +import { Guild } from '../../structures/guild.ts' +import { InviteCreatePayload } from '../../types/gateway.ts' +import { ChannelPayload, GuildPayload, InvitePayload } from '../../../mod.ts' + +export const inviteCreate: GatewayEventHandler = async ( + gateway: Gateway, + d: InviteCreatePayload +) => { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + const guild: Guild | undefined = await gateway.client.guilds.get(d.guild_id!) + + // Weird case, shouldn't happen + if (guild === undefined) return + + /** + * TODO(DjDeveloperr): Add _get method in BaseChildManager + */ + const cachedChannel = await gateway.client.channels._get(d.channel_id) + + // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion + const cachedGuild: GuildPayload | undefined = + d.guild_id === undefined + ? undefined + : await guild.client.guilds._get(d.guild_id) + + const dataConverted: InvitePayload = { + code: d.code, + guild: cachedGuild, + // had to use `as ChannelPayload` because the _get method returned `ChannelPayload | undefined` which errored + channel: (cachedChannel as unknown) as ChannelPayload, + inviter: d.inviter, + target_user: d.target_user, + target_user_type: d.target_user_type, + } + + await guild.invites.set(d.code, dataConverted) + const invite = await guild.invites.get(d.code) + gateway.client.emit('inviteCreate', invite) +} diff --git a/src/gateway/handlers/inviteDelete.ts b/src/gateway/handlers/inviteDelete.ts new file mode 100644 index 0000000..8e77d13 --- /dev/null +++ b/src/gateway/handlers/inviteDelete.ts @@ -0,0 +1,34 @@ +import { Gateway, GatewayEventHandler } from '../index.ts' +import { Guild } from '../../structures/guild.ts' +import { InviteDeletePayload } from '../../types/gateway.ts' +import { PartialInvitePayload } from '../../types/invite.ts' +import { Channel } from '../../../mod.ts' + +export const inviteDelete: GatewayEventHandler = async ( + gateway: Gateway, + d: InviteDeletePayload +) => { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + const guild: Guild | undefined = await gateway.client.guilds.get(d.guild_id!) + + // Weird case, shouldn't happen + if (guild === undefined) return + + const cachedInvite = await guild.invites.get(d.code) + const cachedChannel = await gateway.client.channels.get(d.channel_id) + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + const cachedGuild = await gateway.client.guilds.get(d.guild_id!) + + // TODO(DjDeveloperr): Make it support self-bots and make Guild not always defined + if (cachedInvite === undefined) { + const uncachedInvite: PartialInvitePayload = { + guild: (cachedGuild as unknown) as Guild, + channel: (cachedChannel as unknown) as Channel, + code: d.code, + } + return gateway.client.emit('inviteDeleteUncached', uncachedInvite) + } else { + await guild.invites.delete(d.code) + gateway.client.emit('inviteDelete', cachedInvite) + } +} diff --git a/src/managers/invites.ts b/src/managers/invites.ts new file mode 100644 index 0000000..6af3896 --- /dev/null +++ b/src/managers/invites.ts @@ -0,0 +1,41 @@ +import { Client } from '../models/client.ts' +import { Guild } from '../structures/guild.ts' +import { Invite } from '../structures/invite.ts' +import { GUILD_INVITES } from '../types/endpoint.ts' +import { InvitePayload } from '../types/invite.ts' +import { BaseManager } from './base.ts' + +export class InviteManager extends BaseManager { + guild: Guild + + constructor(client: Client, guild: Guild) { + super(client, `invites:${guild.id}`, Invite) + this.guild = guild + } + + async get(key: string): Promise { + const raw = await this._get(key) + if (raw === undefined) return + return new Invite(this.client, raw) + } + + async fetch(id: string): Promise { + return await new Promise((resolve, reject) => { + this.client.rest + .get(GUILD_INVITES(this.guild.id)) + .then(async (data) => { + this.set(id, data as InvitePayload) + const newInvite = await this.get(data.code) + resolve(newInvite) + }) + .catch((e) => reject(e)) + }) + } + + async fromPayload(invites: InvitePayload[]): Promise { + for (const invite of invites) { + await this.set(invite.code, invite) + } + return true + } +} diff --git a/src/structures/guild.ts b/src/structures/guild.ts index 0a5c4c9..d21ceb1 100644 --- a/src/structures/guild.ts +++ b/src/structures/guild.ts @@ -10,6 +10,7 @@ import { import { PresenceUpdatePayload } from '../types/gateway.ts' import { Base } from './base.ts' import { RolesManager } from '../managers/roles.ts' +import { InviteManager } from '../managers/invites.ts' import { GuildChannelsManager } from '../managers/guildChannels.ts' import { MembersManager } from '../managers/members.ts' import { Role } from './role.ts' @@ -132,6 +133,7 @@ export class Guild extends Base { explicitContentFilter?: string roles: RolesManager emojis: GuildEmojisManager + invites: InviteManager features?: GuildFeatures[] mfaLevel?: string applicationID?: string @@ -174,6 +176,7 @@ export class Guild extends Base { ) this.roles = new RolesManager(this.client, this) this.emojis = new GuildEmojisManager(this.client, this.client.emojis, this) + this.invites = new InviteManager(this.client, this) if (!this.unavailable) { this.name = data.name diff --git a/src/test/cmd.ts b/src/test/cmd.ts index 68f0052..f1c1f8b 100644 --- a/src/test/cmd.ts +++ b/src/test/cmd.ts @@ -1,10 +1,18 @@ -import { Command, CommandClient, Intents, GuildChannel, CommandContext, Extension } from '../../mod.ts' +import { + Command, + CommandClient, + Intents, + GuildChannel, + CommandContext, + Extension, +} from '../../mod.ts' +import { Invite } from '../structures/invite.ts' import { TOKEN } from './config.ts' const client = new CommandClient({ - prefix: ["pls", "!"], + prefix: ['pls', '!'], spacesAfterPrefix: true, - mentionPrefix: true + mentionPrefix: true, }) client.on('debug', console.log) @@ -55,10 +63,21 @@ client.on('webhooksUpdate', (guild, channel) => { console.log(`Webhooks Updated in #${channel.name} from ${guild.name}`) }) -client.on("commandError", console.error) +client.on('inviteCreate', (invite: Invite) => { + console.log(`Invite Create: ${invite.code}`) +}) + +client.on('inviteDelete', (invite: Invite) => { + console.log(`Invite Delete: ${invite.code}`) +}) + +client.on('inviteDeleteUncached', (invite: Invite) => { + console.log(invite) +}) + +client.on('commandError', console.error) class ChannelLog extends Extension { - onChannelCreate(ext: Extension, channel: GuildChannel): void { console.log(`Channel Created: ${channel.name}`) } @@ -81,15 +100,27 @@ class ChannelLog extends Extension { client.extensions.load(ChannelLog) client.on('messageDeleteBulk', (channel, messages, uncached) => { - console.log(`=== Message Delete Bulk ===\nMessages: ${messages.map(m => m.id).join(', ')}\nUncached: ${[...uncached.values()].join(', ')}`) + console.log( + `=== Message Delete Bulk ===\nMessages: ${messages + .map((m) => m.id) + .join(', ')}\nUncached: ${[...uncached.values()].join(', ')}` + ) }) client.on('channelUpdate', (before, after) => { - console.log(`Channel Update: ${(before as GuildChannel).name}, ${(after as GuildChannel).name}`) + console.log( + `Channel Update: ${(before as GuildChannel).name}, ${ + (after as GuildChannel).name + }` + ) }) client.on('typingStart', (user, channel, at, guildData) => { - console.log(`${user.tag} started typing in ${channel.id} at ${at}${guildData !== undefined ? `\nGuild: ${guildData.guild.name}` : ''}`) + console.log( + `${user.tag} started typing in ${channel.id} at ${at}${ + guildData !== undefined ? `\nGuild: ${guildData.guild.name}` : '' + }` + ) }) client.on('voiceStateAdd', (state) => { @@ -114,4 +145,4 @@ for (const file of files) { console.log(`Loaded ${client.commands.count} commands!`) -client.connect(TOKEN, Intents.create(['GUILD_MEMBERS', 'GUILD_PRESENCES'])) \ No newline at end of file +client.connect(TOKEN, Intents.create(['GUILD_MEMBERS', 'GUILD_PRESENCES'])) diff --git a/src/types/invite.ts b/src/types/invite.ts index 6120980..8969897 100644 --- a/src/types/invite.ts +++ b/src/types/invite.ts @@ -1,3 +1,4 @@ +import { Channel, Guild } from "../../mod.ts" import { ChannelPayload } from './channel.ts' import { GuildPayload } from './guild.ts' import { UserPayload } from './user.ts' @@ -12,3 +13,9 @@ export interface InvitePayload { approximate_presence_count?: number approximate_member_count?: number } + +export interface PartialInvitePayload { + code: string + channel: Channel + guild?: Guild +} \ No newline at end of file