diff --git a/src/structures/guild.ts b/src/structures/guild.ts index 68135984d..82070249e 100644 --- a/src/structures/guild.ts +++ b/src/structures/guild.ts @@ -11,13 +11,22 @@ import { leaveGuild } from "../helpers/guilds/leave_guild.ts"; import { getInvites } from "../helpers/invites/get_invites.ts"; import { banMember } from "../helpers/members/ban_member.ts"; import { unbanMember } from "../helpers/members/unban_member.ts"; +import { GetGuildAuditLog } from "../types/audit_log/get_guild_audit_log.ts"; +import { CreateGuildBan } from "../types/guilds/create_guild_ban.ts"; +import { DiscordGuild, Guild } from "../types/guilds/guild.ts"; +import { DiscordGuildFeatures } from "../types/guilds/guild_features.ts"; +import { GuildMember } from "../types/guilds/guild_member.ts"; +import { ModifyGuild } from "../types/guilds/modify_guild.ts"; +import { DiscordImageFormat } from "../types/misc/image_format.ts"; +import { DiscordImageSize } from "../types/misc/image_size.ts"; +import { PresenceUpdate } from "../types/misc/presence_update.ts"; import { Collection } from "../util/collection.ts"; -import { createNewProp } from "../util/utils.ts"; -import { Role, structures } from "./mod.ts"; +import { createNewProp, snakeKeysToCamelCase } from "../util/utils.ts"; +import { RoleStruct, structures } from "./mod.ts"; -export const initialMemberLoadQueue = new Map(); +export const initialMemberLoadQueue = new Map(); -const baseGuild: Partial = { +const baseGuild: Partial = { get members() { return cache.members.filter((member) => member.guilds.has(this.id!)); }, @@ -49,13 +58,13 @@ const baseGuild: Partial = { return cache.members.get(this.ownerId!); }, get partnered() { - return Boolean(this.features?.includes("PARTNERED")); + return Boolean(this.features?.includes(DiscordGuildFeatures.PARTNERED)); }, get verified() { - return Boolean(this.features?.includes("VERIFIED")); + return Boolean(this.features?.includes(DiscordGuildFeatures.VERIFIED)); }, bannerURL(size, format) { - return guildBannerURL(this as Guild, size, format); + return guildBannerURL(this as unknown as Guild, size, format); }, delete() { return deleteServer(this.id!); @@ -82,7 +91,7 @@ const baseGuild: Partial = { return getInvites(this.id!); }, iconURL(size, format) { - return guildIconURL(this as Guild, size, format); + return guildIconURL(this as unknown as Guild, size, format); }, leave() { return leaveGuild(this.id!); @@ -90,92 +99,48 @@ const baseGuild: Partial = { }; export async function createGuildStruct( - data: CreateGuildPayload, - shardId: number + data: DiscordGuild, + shardId: number, ) { const { - disovery_splash: discoverySplash, - default_message_notifications: defaultMessageNotifications, - explicit_content_filter: explicitContentFilter, - system_channel_flags: systemChannelFlags, - rules_channel_id: rulesChannelId, - public_updates_channel_id: publicUpdatesChannelId, - max_video_channel_users: maxVideoChannelUsers, - approximate_member_count: approximateMemberCount, - approximate_presence_count: approximatePresenceCount, - owner_id: ownerId, - afk_channel_id: afkChannelId, - afk_timeout: afkTimeout, - widget_enabled: widgetEnabled, - widget_channel_id: widgetChannelId, - verification_level: verificationLevel, - mfa_level: mfaLevel, - system_channel_id: systemChannelId, - max_presences: maxPresences, - max_members: maxMembers, - vanity_url_code: vanityURLCode, - premium_tier: premiumTier, - premium_subscription_count: premiumSubscriptionCount, - preferred_locale: preferredLocale, - joined_at: joinedAt, - member_count: memberCount = 0, - voice_states: voiceStates = [], + memberCount = 0, + voiceStates = [], channels = [], - members, presences = [], + joinedAt = "", emojis, + members = [], ...rest - } = data; + } = snakeKeysToCamelCase(data) as Guild; const roles = await Promise.all( data.roles.map((role) => structures.createRoleStruct(role)) ); - await Promise.all( - channels.map(async (channel) => { - const channelStruct = await structures.createChannelStruct( - channel, - rest.id - ); - return cacheHandlers.set("channels", channelStruct.id, channelStruct); - }) - ); + await Promise.all(channels.map(async (channel) => { + const channelStruct = await structures.createChannelStruct( + channel, + rest.id, + ); - const restProps: Record> = {}; + return cacheHandlers.set("channels", channelStruct.id, channelStruct); + })); + + const props: Record> = {}; for (const key of Object.keys(rest)) { // @ts-ignore index signature - restProps[key] = createNewProp(rest[key]); + props[key] = createNewProp(rest[key]); } const guild = Object.create(baseGuild, { - ...restProps, - discoverySplash: createNewProp(discoverySplash), - defaultMessageNotifications: createNewProp(defaultMessageNotifications), - explicitContentFilter: createNewProp(explicitContentFilter), - rulesChannelId: createNewProp(rulesChannelId), - publicUpdatesChannelId: createNewProp(publicUpdatesChannelId), - maxVideoChannelUsers: createNewProp(maxVideoChannelUsers), - approximateMemberCount: createNewProp(approximateMemberCount), - approximatePresenceCount: createNewProp(approximatePresenceCount), + ...props, shardId: createNewProp(shardId), - ownerId: createNewProp(ownerId), - afkChannelId: createNewProp(afkChannelId), - afkTimeout: createNewProp(afkTimeout), - widgetEnabled: createNewProp(widgetEnabled), - widgetChannelId: createNewProp(widgetChannelId), - verificationLevel: createNewProp(verificationLevel), - mfaLevel: createNewProp(mfaLevel), - systemChannelId: createNewProp(systemChannelId), - maxPresences: createNewProp(maxPresences), - maxMembers: createNewProp(maxMembers), - vanityURLCode: createNewProp(vanityURLCode), - premiumTier: createNewProp(premiumTier), - premiumSubscriptionCount: createNewProp(premiumSubscriptionCount), - preferredLocale: createNewProp(preferredLocale), - roles: createNewProp(new Collection(roles.map((r: Role) => [r.id, r]))), + roles: createNewProp( + new Collection(roles.map((r: RoleStruct) => [r.id, r])), + ), joinedAt: createNewProp(Date.parse(joinedAt)), presences: createNewProp( - new Collection(presences.map((p: Presence) => [p.user.id, p])) + new Collection(presences.map((p) => [p.user?.id, p])), ), memberCount: createNewProp(memberCount), emojis: createNewProp( @@ -183,20 +148,8 @@ export async function createGuildStruct( ), voiceStates: createNewProp( new Collection( - voiceStates.map((vs: VoiceState) => [ - vs.user_id, - { - ...vs, - guildId: vs.guild_id, - channelId: vs.channel_id, - userId: vs.user_id, - sessionId: vs.session_id, - selfDeaf: vs.self_deaf, - selfMute: vs.self_mute, - selfStream: vs.self_stream, - }, - ]) - ) + voiceStates.map((vs) => [vs.userId, vs]), + ), ), }); @@ -216,5 +169,75 @@ export async function createGuildStruct( ); } - return guild as Guild; + return guild as GuildStruct; +} + +export interface GuildStruct extends + Omit< + Guild, + "roles" | "presences" | "voiceStates" | "members" | "channels" + > { + /** The roles in the guild */ + roles: Collection; + /** The presences of all the users in the guild. */ + presences: Collection; + /** The Voice State data for each user in a voice channel in this server. */ + voiceStates: Collection; + + // GETTERS + /** Members in this guild. */ + members: Collection; + /** Channels in this guild. */ + channels: Collection; + /** The afk channel if one is set */ + afkChannel?: ChannelStruct; + /** The public update channel if one is set */ + publicUpdatesChannel?: ChannelStruct; + /** The rules channel in this guild if one is set */ + rulesChannel?: ChannelStruct; + /** The system channel in this guild if one is set */ + systemChannel?: ChannelStruct; + /** The bot member in this guild if cached */ + bot?: MemberStruct; + /** The bot guild member in this guild if cached */ + botMember?: GuildMember; + /** The bots voice state if there is one in this guild */ + botVoice?: CleanVoiceState; + /** The owner member of this guild */ + owner?: MemberStruct; + /** Whether or not this guild is partnered */ + partnered: boolean; + /** Whether or not this guild is verified */ + verified: boolean; + + // METHODS + + /** The banner url for this server */ + bannerURL( + size?: DiscordImageSize, + format?: DiscordImageFormat, + ): string | undefined; + /** The full URL of the icon from Discords CDN. Undefined when no icon is set. */ + iconURL( + size?: DiscordImageSize, + format?: DiscordImageFormat, + ): string | undefined; + /** Delete a guild permanently. User must be owner. Returns 204 No Content on success. Fires a Guild Delete Gateway event. */ + delete(): ReturnType; + /** Leave a guild */ + leave(): ReturnType; + /** Edit the server. Requires the MANAGE_GUILD permission. */ + edit(options: ModifyGuild): ReturnType; + /** Returns the audit logs for the guild. Requires VIEW AUDIT LOGS permission */ + auditLogs(options: GetGuildAuditLog): ReturnType; + /** Returns a ban object for the given user or a 404 not found if the ban cannot be found. Requires the BAN_MEMBERS permission. */ + getBan(memberID: string): ReturnType; + /** Returns a list of ban objects for the users banned from this guild. Requires the BAN_MEMBERS permission. */ + bans(): ReturnType; + /** Ban a user from the guild and optionally delete previous messages sent by the user. Requires the BAN_MEMBERS permission. */ + ban(memberID: string, options: CreateGuildBan): ReturnType; + /** Remove the ban for a user. Requires BAN_MEMBERS permission */ + unban(memberID: string): ReturnType; + /** Get all the invites for this guild. Requires MANAGE_GUILD permission */ + invites(): ReturnType; } diff --git a/src/types/misc/image_format.ts b/src/types/misc/image_format.ts new file mode 100644 index 000000000..89ad25afb --- /dev/null +++ b/src/types/misc/image_format.ts @@ -0,0 +1,2 @@ +/** https://discord.com/developers/docs/reference#image-formatting */ +export type DiscordImageFormat = "jpg" | "jpeg" | "png" | "webp" | "gif"; diff --git a/src/types/misc/image_size.ts b/src/types/misc/image_size.ts new file mode 100644 index 000000000..488eb6557 --- /dev/null +++ b/src/types/misc/image_size.ts @@ -0,0 +1,2 @@ +/** https://discord.com/developers/docs/reference#image-formatting */ +export type DiscordImageSize = 16 | 32 | 64 | 128 | 256 | 512 | 1024 | 2048;