From a772bbe14f560ae4ee52f648560b5d097a2252b8 Mon Sep 17 00:00:00 2001 From: Skillz Date: Thu, 28 May 2020 20:48:51 -0400 Subject: [PATCH] remove methods from structs --- module/shardingManager.ts | 4 +- structures/channel.ts | 219 ++----------------- structures/guild.ts | 450 ++++---------------------------------- structures/member.ts | 200 +++-------------- structures/message.ts | 159 ++------------ 5 files changed, 107 insertions(+), 925 deletions(-) diff --git a/module/shardingManager.ts b/module/shardingManager.ts index ee412e9ae..b06ceb144 100644 --- a/module/shardingManager.ts +++ b/module/shardingManager.ts @@ -286,7 +286,7 @@ async function handleDiscordPayload(data: DiscordPayload, shardID: number) { const newMemberData = { ...options, premium_since: options.premium_since || undefined, - joined_at: new Date(cachedMember?.joined_at || Date.now()) + joined_at: new Date(cachedMember?.joinedAt || Date.now()) .toISOString(), deaf: cachedMember?.deaf || false, mute: cachedMember?.mute || false, @@ -389,7 +389,7 @@ async function handleDiscordPayload(data: DiscordPayload, shardID: number) { if (data.t === "MESSAGE_CREATE") { const options = data.d as MessageCreateOptions; const channel = cache.channels.get(options.channel_id); - if (channel) channel.last_message_id = options.id; + if (channel) channel.lastMessageID = options.id; const message = createMessage(options); // Cache the message diff --git a/structures/channel.ts b/structures/channel.ts index a1e953058..fdc310db3 100644 --- a/structures/channel.ts +++ b/structures/channel.ts @@ -1,25 +1,6 @@ -import { - ChannelCreatePayload, - GetMessagesAfter, - GetMessagesAround, - GetMessages, - GetMessagesBefore, - MessageContent, - CreateInviteOptions, - ChannelEditOptions, -} from "../types/channel.ts"; -import { updateChannelCache } from "../module/client.ts"; -import { endpoints } from "../constants/discord.ts"; -import { createMessage } from "./message.ts"; -import { MessageCreateOptions } from "../types/message.ts"; -import { - calculatePermissions, - botHasPermission, -} from "../utils/permissions.ts"; -import { Permissions } from "../types/permission.ts"; -import { Errors } from "../types/errors.ts"; -import { RequestManager } from "../module/requestManager.ts"; -import { logYellow } from "../utils/logger.ts"; +import { ChannelCreatePayload } from "../types/channel.ts"; +import { calculatePermissions } from "../utils/permissions.ts"; +import { cache } from "../utils/cache.ts"; export function createChannel(data: ChannelCreatePayload, guildID?: string) { const channel = { @@ -48,185 +29,25 @@ export function createChannel(data: ChannelCreatePayload, guildID?: string) { nsfw: data.nsfw || false, /** The mention of the channel */ mention: `<#${data.id}>`, - - /** Checks if a user id or a role id has permission in this channel */ - hasPermission: function (id: string, permissions: Permissions[]) { - const overwrite = data.permission_overwrites?.find((perm) => - perm.id === id - ) || - data.permission_overwrites?.find((perm) => perm.id === channel.guildID); - - return permissions.every((perm) => { - if (overwrite) { - if (overwrite.deny & perm) return false; - if (overwrite.allow & perm) return true; - } - if (channel.guildID) { - return botHasPermission(channel.guildID, [perm]); - } - return false; - }); - }, - /** Fetch a single message from the server. Requires VIEW_CHANNEL and READ_MESSAGE_HISTORY */ - getMessage: async (id: string) => { - if (data.guild_id) { - if ( - !botHasPermission(data.guild_id, [Permissions.VIEW_CHANNEL]) - ) { - throw new Error(Errors.MISSING_VIEW_CHANNEL); - } - if ( - !botHasPermission( - data.guild_id, - [Permissions.READ_MESSAGE_HISTORY], - ) - ) { - throw new Error(Errors.MISSING_READ_MESSAGE_HISTORY); - } - } - const result = await RequestManager.get( - endpoints.CHANNEL_MESSAGE(data.id, id), - ) as MessageCreateOptions; - return createMessage(result); - }, - /** Fetches between 2-100 messages. Requires VIEW_CHANNEL and READ_MESSAGE_HISTORY */ - getMessages: async ( - options?: - | GetMessagesAfter - | GetMessagesBefore - | GetMessagesAround - | GetMessages, - ) => { - if (data.guild_id) { - if ( - !botHasPermission(data.guild_id, [Permissions.VIEW_CHANNEL]) - ) { - throw new Error(Errors.MISSING_VIEW_CHANNEL); - } - if ( - !botHasPermission( - data.guild_id, - [Permissions.READ_MESSAGE_HISTORY], - ) - ) { - throw new Error(Errors.MISSING_READ_MESSAGE_HISTORY); - } - } - - if (options?.limit && options.limit > 100) return; - - const result = (await RequestManager.get( - endpoints.CHANNEL_MESSAGES(data.id), - options, - )) as MessageCreateOptions[]; - return result.map((res) => createMessage(res)); - }, - /** Get pinned messages in this channel. */ - getPins: async () => { - const result = (await RequestManager.get( - endpoints.CHANNEL_PINS(data.id), - )) as MessageCreateOptions[]; - return result.map((res) => createMessage(res)); - }, - /** Send a message to the channel. Requires SEND_MESSAGES permission. */ - sendMessage: async (content: string | MessageContent) => { - if (typeof content === "string") content = { content }; - - if (data.guild_id) { - if ( - !botHasPermission(data.guild_id, [Permissions.SEND_MESSAGES]) - ) { - throw new Error(Errors.MISSING_SEND_MESSAGES); - } - if ( - content.tts && - !botHasPermission( - data.guild_id, - [Permissions.SEND_TTS_MESSAGES], - ) - ) { - throw new Error(Errors.MISSING_SEND_TTS_MESSAGE); - } - } - - if (content.content && content.content.length > 2000) { - throw new Error(Errors.MESSAGE_MAX_LENGTH); - } - - const result = await RequestManager.post( - endpoints.CHANNEL_MESSAGES(data.id), - content, - ); - - return createMessage(result as MessageCreateOptions); - }, - - /** Delete messages from the channel. 2-100. Requires the MANAGE_MESSAGES permission */ - deleteMessages: (ids: string[], reason?: string) => { - if ( - data.guild_id && - !botHasPermission(data.guild_id, [Permissions.MANAGE_MESSAGES]) - ) { - throw new Error(Errors.MISSING_MANAGE_MESSAGES); - } - if (ids.length < 2) throw new Error(Errors.DELETE_MESSAGES_MIN); - - if (ids.length > 100) { - logYellow( - `This endpoint only accepts a maximum of 100 messages. Deleting the first 100 message ids provided.`, - ); - } - - return RequestManager.post(endpoints.CHANNEL_BULK_DELETE(data.id), { - messages: ids.splice(0, 100), - reason, - }); - }, - /** Gets the invites for this channel. Requires MANAGE_CHANNEL */ - getInvites: () => { - if ( - data.guild_id && - !botHasPermission(data.guild_id, [Permissions.MANAGE_CHANNELS]) - ) { - throw new Error(Errors.MISSING_MANAGE_CHANNELS); - } - return RequestManager.get(endpoints.CHANNEL_INVITES(data.id)); - }, - /** Creates a new invite for this channel. Requires CREATE_INSTANT_INVITE */ - createInvite: (options: CreateInviteOptions) => { - if ( - data.guild_id && - !botHasPermission( - data.guild_id, - [Permissions.CREATE_INSTANT_INVITE], - ) - ) { - throw new Error(Errors.MISSING_CREATE_INSTANT_INVITE); - } - return RequestManager.post(endpoints.CHANNEL_INVITES(data.id), options); - }, - /** Gets the webhooks for this channel. Requires MANAGE_WEBHOOKS */ - getWebhooks: () => { - if ( - data.guild_id && - !botHasPermission(data.guild_id, [Permissions.MANAGE_WEBHOOKS]) - ) { - throw new Error(Errors.MISSING_MANAGE_WEBHOOKS); - } - return RequestManager.get(endpoints.CHANNEL_WEBHOOKS(data.id)); - }, - edit: (options: ChannelEditOptions) => { - return RequestManager.patch(endpoints.GUILD_CHANNELS(data.id), options); - }, - // TODO: after learning opus and stuff - /** Join a voice channel. */ - // join: () => {}, - /** Leave a voice channel */ - // leave: () => {} }; - updateChannelCache(data.id, channel); + // Remove excess properties to preserve cache. + delete channel.guild_id; + delete channel.last_message_id; + delete channel.rate_limit_per_user; + delete channel.last_pin_timestamp; + delete channel.user_limit; + + cache.channels.set(data.id, channel); return channel; } -export interface Channel extends ReturnType {} +export interface Channel extends + Omit< + ReturnType, + | "guild_id" + | "last_message_id" + | "rate_limit_per_user" + | "last_pin_timestamp" + | "user_limit" + > {} diff --git a/structures/guild.ts b/structures/guild.ts index dd0c95287..f00343e51 100644 --- a/structures/guild.ts +++ b/structures/guild.ts @@ -1,35 +1,7 @@ -import { identifyPayload } from "../module/client.ts"; -import { endpoints } from "../constants/discord.ts"; -import { formatImageURL } from "../utils/cdn.ts"; -import { - CreateGuildPayload, - PrunePayload, - PositionSwap, - GetAuditLogsOptions, - EditIntegrationOptions, - BanOptions, - GuildEditOptions, - CreateEmojisOptions, - EditEmojisOptions, - CreateRoleOptions, - FetchMembersOptions, -} from "../types/guild.ts"; +import { CreateGuildPayload } from "../types/guild.ts"; import { createRole } from "./role.ts"; import { createMember, Member } from "./member.ts"; import { createChannel } from "./channel.ts"; -import { - CreateChannelOptions, - ChannelTypes, - ChannelCreatePayload, -} from "../types/channel.ts"; -import { ImageSize, ImageFormats } from "../types/cdn.ts"; -import { Permissions, Permission } from "../types/permission.ts"; -import { botHasPermission } from "../utils/permissions.ts"; -import { Errors } from "../types/errors.ts"; -import { RequestManager } from "../module/requestManager.ts"; -import { RoleData } from "../types/role.ts"; -import { Intents } from "../types/options.ts"; -import { requestAllMembers } from "../module/shardingManager.ts"; export const createGuild = (data: CreateGuildPayload, shardID: number) => { const guild = { @@ -90,392 +62,52 @@ export const createGuild = (data: CreateGuildPayload, shardID: number) => { selfMute: vs.self_mute, selfStream: vs.self_stream, }])), - - /** Gets an array of all the channels ids that are the children of this category. */ - categoryChildrenIDs: (id: string) => - data.channels.filter((c) => c.parent_id === id).map((c) => c.id), - /** The full URL of the icon from Discords CDN. Undefined when no icon is set. */ - iconURL: (size: ImageSize = 128, format?: ImageFormats) => - data.icon - ? formatImageURL(endpoints.GUILD_ICON(data.id, data.icon), size, format) - : undefined, - /** The full URL of the splash from Discords CDN. Undefined if no splash is set. */ - splashURL: (size: ImageSize = 128, format?: ImageFormats) => - data.splash - ? formatImageURL( - endpoints.GUILD_SPLASH(data.id, data.splash), - size, - format, - ) - : undefined, - /** The full URL of the banner from Discords CDN. Undefined if no banner is set. */ - bannerURL: (size: ImageSize = 128, format?: ImageFormats) => - data.banner - ? formatImageURL( - endpoints.GUILD_BANNER(data.id, data.banner), - size, - format, - ) - : undefined, - /** Create a channel in your server. Bot needs MANAGE_CHANNEL permissions in the server. */ - createChannel: async (name: string, options: CreateChannelOptions) => { - if (!botHasPermission(data.id, [Permissions.MANAGE_CHANNELS])) { - throw new Error(Errors.MISSING_MANAGE_CHANNELS); - } - const result = - (await RequestManager.post(endpoints.GUILD_CHANNELS(data.id), { - name, - permission_overwrites: options?.permission_overwrites - ? options.permission_overwrites.map((perm) => ({ - ...perm, - allow: perm.allow.map((p) => Permissions[p]), - deny: perm.deny.map((p) => Permissions[p]), - })) - : undefined, - ...options, - type: options.type ? ChannelTypes[options.type] : undefined, - })) as ChannelCreatePayload; - - const channel = createChannel(result); - guild.channels.set(result.id, channel); - return channel; - }, - /** Returns a list of guild channel objects. - * - * ⚠️ **If you need this, you are probably doing something wrong. This is not intended for use. Your channels will be cached in your guild.** - */ - getChannels: () => { - return RequestManager.get(endpoints.GUILD_CHANNELS(data.id)); - }, - /** Modify the positions of channels on the guild. Requires MANAGE_CHANNELS permisison. */ - swapChannels: (channelPositions: PositionSwap[]) => { - if (channelPositions.length < 2) { - throw "You must provide atleast two channels to be swapped."; - } - return RequestManager.patch( - endpoints.GUILD_CHANNELS(data.id), - channelPositions, - ); - }, - /** Returns a guild member object for the specified user. - * - * ⚠️ **If you need this, you are probably doing something wrong. This is not intended for use. Your members will be cached in your guild.** - */ - getMember: (id: string) => { - return RequestManager.get(endpoints.GUILD_MEMBER(data.id, id)); - }, - /** Create an emoji in the server. Emojis and animated emojis have a maximum file size of 256kb. Attempting to upload an emoji larger than this limit will fail and return 400 Bad Request and an error message, but not a JSON status code. */ - createEmoji: ( - name: string, - image: string, - options: CreateEmojisOptions, - ) => { - if ( - !botHasPermission(data.id, [Permissions.MANAGE_EMOJIS]) - ) { - throw new Error(Errors.MISSING_MANAGE_EMOJIS); - } - return RequestManager.post(endpoints.GUILD_EMOJIS(data.id), { - ...options, - name, - image, - }); - }, - /** Modify the given emoji. Requires the MANAGE_EMOJIS permission. */ - editEmoji: (id: string, options: EditEmojisOptions) => { - if ( - !botHasPermission(data.id, [Permissions.MANAGE_EMOJIS]) - ) { - throw new Error(Errors.MISSING_MANAGE_EMOJIS); - } - return RequestManager.patch(endpoints.GUILD_EMOJI(data.id, id), { - name: options.name, - roles: options.roles, - }); - }, - /** Delete the given emoji. Requires the MANAGE_EMOJIS permission. Returns 204 No Content on success. */ - deleteEmoji: (id: string, reason?: string) => { - if ( - !botHasPermission(data.id, [Permissions.MANAGE_EMOJIS]) - ) { - throw new Error(Errors.MISSING_MANAGE_EMOJIS); - } - return RequestManager.delete( - endpoints.GUILD_EMOJI(data.id, id), - { reason }, - ); - }, - /** Create a new role for the guild. Requires the MANAGE_ROLES permission. */ - createRole: async (options: CreateRoleOptions, reason?: string) => { - if ( - !botHasPermission(data.id, [Permissions.MANAGE_ROLES]) - ) { - throw new Error(Errors.MISSING_MANAGE_ROLES); - } - const role_data = await RequestManager.post( - endpoints.GUILD_ROLES(data.id), - { - ...options, - permissions: options.permissions?.map((perm) => Permissions[perm]), - reason, - }, - ); - - const roleData = role_data as RoleData; - const role = createRole(roleData); - guild.roles.set(roleData.id, role); - return role; - }, - /** Edit a guild role. Requires the MANAGE_ROLES permission. */ - editRole: (id: string, options: CreateRoleOptions) => { - if ( - !botHasPermission(data.id, [Permissions.MANAGE_ROLES]) - ) { - throw new Error(Errors.MISSING_MANAGE_ROLES); - } - return RequestManager.patch(endpoints.GUILD_ROLE(data.id, id), options); - }, - /** Delete a guild role. Requires the MANAGE_ROLES permission. */ - deleteRole: (id: string) => { - if ( - !botHasPermission(data.id, [Permissions.MANAGE_ROLES]) - ) { - throw new Error(Errors.MISSING_MANAGE_ROLES); - } - return RequestManager.delete(endpoints.GUILD_ROLE(data.id, id)); - }, - /** Returns a list of role objects for the guild. - * - * ⚠️ **If you need this, you are probably doing something wrong. This is not intended for use. Your roles will be cached in your guild.** - */ - getRoles: () => { - if ( - !botHasPermission(data.id, [Permissions.MANAGE_ROLES]) - ) { - throw new Error(Errors.MISSING_MANAGE_ROLES); - } - return RequestManager.get(endpoints.GUILD_ROLES(data.id)); - }, - /** Modify the positions of a set of role objects for the guild. Requires the MANAGE_ROLES permission. */ - swapRoles: (rolePositons: PositionSwap) => { - if ( - !botHasPermission(data.id, [Permissions.MANAGE_ROLES]) - ) { - throw new Error(Errors.MISSING_MANAGE_ROLES); - } - return RequestManager.patch(endpoints.GUILD_ROLES(data.id), rolePositons); - }, - /** Check how many members would be removed from the server in a prune operation. Requires the KICK_MEMBERS permission */ - getPruneCount: async (days: number) => { - if (days < 1) throw new Error(Errors.PRUNE_MIN_DAYS); - if ( - !botHasPermission(data.id, [Permissions.KICK_MEMBERS]) - ) { - throw new Error(Errors.MISSING_KICK_MEMBERS); - } - const result = (await RequestManager.get( - endpoints.GUILD_PRUNE(data.id), - { days }, - )) as PrunePayload; - return result.pruned; - }, - /** Begin pruning all members in the given time period */ - pruneMembers: (days: number) => { - if (days < 1) throw new Error(Errors.PRUNE_MIN_DAYS); - if ( - !botHasPermission(data.id, [Permissions.KICK_MEMBERS]) - ) { - throw new Error(Errors.MISSING_KICK_MEMBERS); - } - return RequestManager.post(endpoints.GUILD_PRUNE(data.id), { days }); - }, - fetchMembers: (options?: FetchMembersOptions) => { - if (!(identifyPayload.intents & Intents.GUILD_MEMBERS)) { - throw new Error(Errors.MISSING_INTENT_GUILD_MEMBERS); - } - - return new Promise((resolve) => { - requestAllMembers(guild, resolve, options); - }); - }, - /** Returns the audit logs for the guild. Requires VIEW AUDIT LOGS permission */ - getAuditLogs: (options: GetAuditLogsOptions) => { - if (!botHasPermission(data.id, [Permissions.VIEW_AUDIT_LOG])) { - throw new Error(Errors.MISSING_VIEW_AUDIT_LOG); - } - - return RequestManager.get(endpoints.GUILD_AUDIT_LOGS(data.id), { - ...options, - limit: options.limit && options.limit >= 1 && options.limit <= 100 - ? options.limit - : 50, - }); - }, - /** Returns the guild embed object. Requires the MANAGE_GUILD permission. */ - getEmbed: () => { - if ( - !botHasPermission(data.id, [Permissions.MANAGE_GUILD]) - ) { - throw new Error(Errors.MISSING_MANAGE_GUILD); - } - return RequestManager.get(endpoints.GUILD_EMBED(data.id)); - }, - /** Modify a guild embed object for the guild. Requires the MANAGE_GUILD permission. */ - editEmbed: (enabled: boolean, channelID?: string | null) => { - if ( - !botHasPermission(data.id, [Permissions.MANAGE_GUILD]) - ) { - throw new Error(Errors.MISSING_MANAGE_GUILD); - } - return RequestManager.patch( - endpoints.GUILD_EMBED(data.id), - { enabled, channel_id: channelID }, - ); - }, - /** Returns the code and uses of the vanity url for this server if it is enabled. Requires the MANAGE_GUILD permission. */ - getVanityURL: () => { - return RequestManager.get(endpoints.GUILD_VANITY_URL(data.id)); - }, - /** Returns a list of integrations for the guild. Requires the MANAGE_GUILD permission. */ - getIntegrations: () => { - if ( - !botHasPermission(data.id, [Permissions.MANAGE_GUILD]) - ) { - throw new Error(Errors.MISSING_MANAGE_GUILD); - } - return RequestManager.get(endpoints.GUILD_INTEGRATIONS(data.id)); - }, - /** Modify the behavior and settings of an integration object for the guild. Requires the MANAGE_GUILD permission. */ - editIntegration: (id: string, options: EditIntegrationOptions) => { - if ( - !botHasPermission(data.id, [Permissions.MANAGE_GUILD]) - ) { - throw new Error(Errors.MISSING_MANAGE_GUILD); - } - return RequestManager.patch( - endpoints.GUILD_INTEGRATION(data.id, id), - options, - ); - }, - /** Delete the attached integration object for the guild with this id. Requires MANAGE_GUILD permission. */ - deleteIntegration: (id: string) => { - if ( - !botHasPermission(data.id, [Permissions.MANAGE_GUILD]) - ) { - throw new Error(Errors.MISSING_MANAGE_GUILD); - } - return RequestManager.delete(endpoints.GUILD_INTEGRATION(data.id, id)); - }, - /** Sync an integration. Requires teh MANAGE_GUILD permission. */ - syncIntegration: (id: string) => { - if ( - !botHasPermission(data.id, [Permissions.MANAGE_GUILD]) - ) { - throw new Error(Errors.MISSING_MANAGE_GUILD); - } - return RequestManager.post(endpoints.GUILD_INTEGRATION_SYNC(data.id, id)); - }, - /** Returns a list of ban objects for the users banned from this guild. Requires the BAN_MEMBERS permission. */ - getBans: () => { - if ( - !botHasPermission(data.id, [Permissions.BAN_MEMBERS]) - ) { - throw new Error(Errors.MISSING_BAN_MEMBERS); - } - return RequestManager.get(endpoints.GUILD_BANS(data.id)); - }, - /** Ban a user from the guild and optionally delete previous messages sent by the user. Requires teh BAN_MEMBERS permission. */ - ban: (id: string, options: BanOptions) => { - if ( - !botHasPermission(data.id, [Permissions.BAN_MEMBERS]) - ) { - throw new Error(Errors.MISSING_BAN_MEMBERS); - } - return RequestManager.put(endpoints.GUILD_BAN(data.id, id), options); - }, - /** Remove the ban for a user. REquires BAN_MEMBERS permission */ - unban: (id: string) => { - if ( - !botHasPermission(data.id, [Permissions.BAN_MEMBERS]) - ) { - throw new Error(Errors.MISSING_BAN_MEMBERS); - } - return RequestManager.delete(endpoints.GUILD_BAN(data.id, id)); - }, - /** Check whether a member has certain permissions in this channel. */ - channelHasPermissions: ( - channelID: string, - memberID: string, - permissions: Permission[], - ) => { - if (memberID === data.owner_id) return true; - - const member = guild.members.get(memberID); - if (!member) { - throw "Invalid member id provided. This member was not found in the cache. Please fetch them with getMember on guild."; - } - - const channel = guild.channels.get(channelID); - if (!channel) { - throw "Invalid channel id provided. This channel was not found in the cache."; - } - - let permissionBits = member.roles.reduce((bits, roleID) => { - const role = guild.roles.get(roleID); - if (!role) return bits; - - bits |= role.permissions; - - return bits; - }, 0); - - if (permissionBits & Permissions.ADMINISTRATOR) return true; - - return permissions.every((permission) => - permissionBits & Permissions[permission] - ); - }, - /** Modify a guilds settings. Requires the MANAGE_GUILD permission. */ - edit: (options: GuildEditOptions) => { - if ( - !botHasPermission(data.id, [Permissions.MANAGE_GUILD]) - ) { - throw new Error(Errors.MISSING_MANAGE_GUILD); - } - return RequestManager.patch(endpoints.GUILD(data.id), options); - }, - /** Get all the invites for this guild. Requires MANAGE_GUILD permission */ - getInvites: () => { - if ( - !botHasPermission(data.id, [Permissions.MANAGE_GUILD]) - ) { - throw new Error(Errors.MISSING_MANAGE_GUILD); - } - return RequestManager.get(endpoints.GUILD_INVITES(data.id)); - }, - /** Leave a guild */ - leave: () => { - return RequestManager.delete(endpoints.GUILD_LEAVE(data.id)); - }, - /** Returns a list of voice region objects for the guild. Unlike the similar /voice route, this returns VIP servers when the guild is VIP-enabled. */ - getVoiceRegions: () => { - return RequestManager.get(endpoints.GUILD_REGIONS(data.id)); - }, - /** Returns a list of guild webhooks objects. Requires the MANAGE_WEBHOOKs permission. */ - getWebhooks: () => { - if (!botHasPermission(data.id, [Permissions.MANAGE_WEBHOOKS])) { - throw new Error(Errors.MISSING_MANAGE_WEBHOOKS); - } - - return RequestManager.get(endpoints.GUILD_WEBHOOKS(data.id)); - }, }; data.members.forEach((m) => guild.members.set(m.user.id, createMember(m, guild)) ); + // Remove excess properties to preserve cache. + delete guild.owner_id; + delete guild.afk_channel_id; + delete guild.afk_timeout; + delete guild.embed_enabled; + delete guild.embed_channel_id; + delete guild.verification_level; + delete guild.mfa_level; + delete guild.system_channel_id; + delete guild.max_presences; + delete guild.max_members; + delete guild.vanity_url_code; + delete guild.premium_tier; + delete guild.premium_subscription_count; + delete guild.preferred_locale; + delete guild.joined_at; + delete guild.member_count; + delete guild.voice_states; return guild; }; -export interface Guild extends ReturnType {} +export interface Guild + extends + Omit< + ReturnType, + | "owner_id" + | "afk_channel_id" + | "afk_timeout" + | "embed_enabled" + | "embed_channel_id" + | "verification_level" + | "mfa_level" + | "system_channel_id" + | "max_presences" + | "max_members" + | "vanity_url_code" + | "premium_tier" + | "premium_subscription_count" + | "preferred_locale" + | "joined_at" + | "member_count" + | "voice_states" + > {} diff --git a/structures/member.ts b/structures/member.ts index eb21e6819..022ed2476 100644 --- a/structures/member.ts +++ b/structures/member.ts @@ -1,176 +1,38 @@ -import { endpoints } from "../constants/discord.ts"; -import { formatImageURL } from "../utils/cdn.ts"; -import { MemberCreatePayload, EditMemberOptions } from "../types/member.ts"; -import { ImageSize, ImageFormats } from "../types/cdn.ts"; -import { Permission, Permissions } from "../types/permission.ts"; -import { - memberHasPermission, - botHasPermission, - highestRole, - higherRolePosition, -} from "../utils/permissions.ts"; -import { Errors } from "../types/errors.ts"; -import { RequestManager } from "../module/requestManager.ts"; -import { botID } from "../module/client.ts"; +import { MemberCreatePayload } from "../types/member.ts"; import { Guild } from "./guild.ts"; import { cache } from "../utils/cache.ts"; -import { MessageContent, DMChannelCreatePayload } from "../types/channel.ts"; -import { createChannel } from "./channel.ts"; -export const createMember = (data: MemberCreatePayload, guild: Guild) => ({ - ...data, - /** When the user joined the guild */ - joinedAt: Date.parse(data.joined_at), - /** When the user used their nitro boost on the server. */ - premiumSince: data.premium_since ? Date.parse(data.premium_since) : undefined, - /** The full username#discriminator */ - tag: `${data.user.username}#${data.user.discriminator}`, - /** The user mention with nickname if possible */ - mention: `<@!${data.user.id}>`, - /** The guild id where this member exists */ - guildID: guild.id, - /** Whether or not this user has 2FA enabled. */ - mfaEnabled: data.user.mfa_enabled, - /** The premium type for this user */ - premiumType: data.user.premium_type, +export const createMember = (data: MemberCreatePayload, guild: Guild) => { + const member = { + ...data, + /** When the user joined the guild */ + joinedAt: Date.parse(data.joined_at), + /** When the user used their nitro boost on the server. */ + premiumSince: data.premium_since + ? Date.parse(data.premium_since) + : undefined, + /** The full username#discriminator */ + tag: `${data.user.username}#${data.user.discriminator}`, + /** The user mention with nickname if possible */ + mention: `<@!${data.user.id}>`, + /** The guild id where this member exists */ + guildID: guild.id, + /** Whether or not this user has 2FA enabled. */ + mfaEnabled: data.user.mfa_enabled, + /** The premium type for this user */ + premiumType: data.user.premium_type, - /** Gets the guild object from cache for this member. This is a method instead of a prop to preserve memory. */ - guild: () => cache.guilds.get(guild.id)!, - /** Send a message to a users DM. Note: this takes 2 API calls. 1 is to fetch the users dm channel. 2 is to send a message to that channel. */ - sendMessage: async function (content: string | MessageContent) { - let dmChannel = cache.channels.get(data.user.id); - if (!dmChannel) { - // If not available in cache create a new one. - const dmChannelData = await RequestManager.post( - endpoints.USER_CREATE_DM, - { recipient_id: data.user.id }, - ) as DMChannelCreatePayload; - // Channel create event will have added this channel to the cache - cache.channels.delete(dmChannelData.id); - const channel = createChannel(dmChannelData); - // Recreate the channel and add it undert he users id - cache.channels.set(data.user.id, channel); - dmChannel = channel; - } + /** Gets the guild object from cache for this member. This is a method instead of a prop to preserve memory. */ + guild: () => cache.guilds.get(guild.id)!, + }; - // If it does exist try sending a message to this user - return dmChannel?.sendMessage(content); - }, - /** The users custom avatar or the default avatar */ - avatarURL: (size: ImageSize = 128, format?: ImageFormats) => - data.user.avatar - ? formatImageURL( - endpoints.USER_AVATAR(data.user.id, data.user.avatar), - size, - format, - ) - : endpoints.USER_DEFAULT_AVATAR(Number(data.user.discriminator) % 5), - /** Add a role to the member */ - addRole: (roleID: string, reason?: string) => { - const botsHighestRole = highestRole(guild.id, botID); - if ( - botsHighestRole && - !higherRolePosition(guild.id, botsHighestRole.id, roleID) - ) { - throw new Error(Errors.BOTS_HIGHEST_ROLE_TOO_LOW); - } + // Remove excess properties to preserve cache. + delete member.joined_at; + delete member.premium_since; + delete member.user.mfa_enabled; + delete member.user.premium_type; - if (!botHasPermission(guild.id, [Permissions.MANAGE_ROLES])) { - throw new Error(Errors.MISSING_MANAGE_ROLES); - } + return member; +}; - return RequestManager.put( - endpoints.GUILD_MEMBER_ROLE(guild.id, data.user.id, roleID), - { reason }, - ); - }, - /** Remove a role from the member */ - removeRole: (roleID: string, reason?: string) => { - const botsHighestRole = highestRole(guild.id, botID); - if ( - botsHighestRole && - !higherRolePosition(guild.id, botsHighestRole.id, roleID) - ) { - throw new Error(Errors.BOTS_HIGHEST_ROLE_TOO_LOW); - } - - if (!botHasPermission(guild.id, [Permissions.MANAGE_ROLES])) { - throw new Error(Errors.MISSING_MANAGE_ROLES); - } - return RequestManager.delete( - endpoints.GUILD_MEMBER_ROLE(guild.id, data.user.id, roleID), - { reason }, - ); - }, - /** Kick a member from the server */ - kick: (reason?: string) => { - const botsHighestRole = highestRole(guild.id, botID); - const membersHighestRole = highestRole(guild.id, data.user.id); - if ( - botsHighestRole && membersHighestRole && - botsHighestRole.position <= membersHighestRole.position - ) { - throw new Error(Errors.BOTS_HIGHEST_ROLE_TOO_LOW); - } - - if (!botHasPermission(guild.id, [Permissions.KICK_MEMBERS])) { - throw new Error(Errors.MISSING_KICK_MEMBERS); - } - return RequestManager.delete( - endpoints.GUILD_MEMBER(guild.id, data.user.id), - { reason }, - ); - }, - /** Edit the member */ - edit: (options: EditMemberOptions) => { - if (options.nick) { - if (options.nick.length > 32) { - throw new Error(Errors.NICKNAMES_MAX_LENGTH); - } - if (!botHasPermission(guild.id, [Permissions.MANAGE_NICKNAMES])) { - throw new Error(Errors.MISSING_MANAGE_NICKNAMES); - } - } - - if ( - options.roles && - !botHasPermission(guild.id, [Permissions.MANAGE_ROLES]) - ) { - throw new Error(Errors.MISSING_MANAGE_ROLES); - } - - if (options.mute) { - // TODO: This should check if the member is in a voice channel - if ( - !botHasPermission(guild.id, [Permissions.MUTE_MEMBERS]) - ) { - throw new Error(Errors.MISSING_MUTE_MEMBERS); - } - } - - if ( - options.deaf && - !botHasPermission(guild.id, [Permissions.DEAFEN_MEMBERS]) - ) { - throw new Error(Errors.MISSING_DEAFEN_MEMBERS); - } - - // TODO: if channel id is provided check if the bot has CONNECT and MOVE in channel and current channel - - return RequestManager.patch( - endpoints.GUILD_MEMBER(guild.id, data.user.id), - options, - ); - }, - /** Checks if the member has this permission. If the member is an owner or has admin perms it will always be true. */ - hasPermissions: (permissions: Permission[]) => { - return memberHasPermission( - data.user.id, - guild, - data.roles, - permissions, - ); - }, -}); - -export interface Member extends ReturnType {} +export interface Member extends Omit, 'joined_at' | 'premium_since'> {} diff --git a/structures/message.ts b/structures/message.ts index 0e4ee6196..64326833b 100644 --- a/structures/message.ts +++ b/structures/message.ts @@ -1,12 +1,4 @@ import { MessageCreateOptions } from "../types/message.ts"; -import { endpoints } from "../constants/discord.ts"; -import { MessageContent } from "../types/channel.ts"; -import { UserPayload } from "../types/guild.ts"; -import { botHasPermission } from "../utils/permissions.ts"; -import { Errors } from "../types/errors.ts"; -import { Permissions } from "../types/permission.ts"; -import { RequestManager } from "../module/requestManager.ts"; -import { botID } from "../module/client.ts"; import { cache } from "../utils/cache.ts"; export function createMessage(data: MessageCreateOptions) { @@ -25,147 +17,22 @@ export function createMessage(data: MessageCreateOptions) { : undefined, channel: cache.channels.get(data.channel_id)!, guild: () => data.guild_id ? cache.guilds.get(data.guild_id) : undefined, - member: () => message.guild()?.members.get(data.author.id)!, + member: () => message.guild()?.members.get(data.author.id), mentions: () => - data.mentions.map((mention) => - message.guild()?.members.get(mention.id)! - ), - - /** Delete a message */ - delete: (reason?: string) => { - if (data.author.id !== botID) { - // This needs to check the channels permission not the guild permission - if ( - !message.guildID || - !message.channel.hasPermission(botID, [Permissions.MANAGE_MESSAGES]) - ) { - throw new Error(Errors.MISSING_MANAGE_MESSAGES); - } - } - - return RequestManager.delete( - endpoints.CHANNEL_MESSAGE(data.channel_id, data.id), - { reason }, - ); - }, - /** Pin a message in a channel. Requires MANAGE_MESSAGES. Max pins allowed in a channel = 50. */ - pin: () => { - if ( - data.guild_id && - !botHasPermission(data.guild_id, [Permissions.MANAGE_MESSAGES]) - ) { - throw new Error(Errors.MISSING_MANAGE_MESSAGES); - } - RequestManager.put(endpoints.CHANNEL_MESSAGE(data.channel_id, data.id)); - }, - /** Unpin a message in a channel. Requires MANAGE_MESSAGES. */ - unpin: () => { - if ( - data.guild_id && - !botHasPermission(data.guild_id, [Permissions.MANAGE_MESSAGES]) - ) { - throw new Error(Errors.MISSING_MANAGE_MESSAGES); - } - RequestManager.delete( - endpoints.CHANNEL_MESSAGE(data.channel_id, data.id), - ); - }, - /** Create a reaction for the message. Reaction takes the form of **name:id** for custom guild emoji, or Unicode characters. Requires READ_MESSAGE_HISTORY and ADD_REACTIONS */ - addReaction: (reaction: string) => { - RequestManager.put( - endpoints.CHANNEL_MESSAGE_REACTION_ME( - data.channel_id, - data.id, - reaction, - ), - ); - }, - /** Removes a reaction from the bot on this message. Reaction takes the form of **name:id** for custom guild emoji, or Unicode characters. */ - removeReaction: (reaction: string) => { - RequestManager.delete( - endpoints.CHANNEL_MESSAGE_REACTION_ME( - data.channel_id, - data.id, - reaction, - ), - ); - }, - /** Removes all reactions for all emojis on this message. */ - removeAllReactions: () => { - if ( - data.guild_id && - !botHasPermission(data.guild_id, [Permissions.MANAGE_MESSAGES]) - ) { - throw new Error(Errors.MISSING_MANAGE_MESSAGES); - } - RequestManager.delete( - endpoints.CHANNEL_MESSAGE_REACTIONS(data.channel_id, data.id), - ); - }, - /** Removes all reactions for a single emoji on this message. Reaction takes the form of **name:id** for custom guild emoji, or Unicode characters. */ - removeReactionEmoji: (reaction: string) => { - if ( - data.guild_id && - !botHasPermission(data.guild_id, [Permissions.MANAGE_MESSAGES]) - ) { - throw new Error(Errors.MISSING_MANAGE_MESSAGES); - } - RequestManager.delete( - endpoints.CHANNEL_MESSAGE_REACTION(data.channel_id, data.id, reaction), - ); - }, - /** Get a list of users that reacted with this emoji. */ - getReactions: async (reaction: string) => { - const result = (await RequestManager.get( - endpoints.CHANNEL_MESSAGE_REACTION(data.channel_id, data.id, reaction), - )) as UserPayload[]; - const guild = message.guild(); - - return result.map((res) => { - return guild?.members.get(res.id) || res; - }); - }, - /** Edit the message. */ - edit: async (content: string | MessageContent) => { - if ( - data.author.id !== botID - ) { - throw "You can only edit a message that was sent by the bot."; - } - - if (typeof content === "string") content = { content }; - - if (data.guild_id) { - if ( - !botHasPermission(data.guild_id, [Permissions.SEND_MESSAGES]) - ) { - throw new Error(Errors.MISSING_SEND_MESSAGES); - } - - if ( - content.tts && - !botHasPermission( - data.guild_id, - [Permissions.SEND_TTS_MESSAGES], - ) - ) { - throw new Error(Errors.MISSING_SEND_TTS_MESSAGE); - } - } - - if (content.content && content.content.length > 2000) { - throw new Error(Errors.MESSAGE_MAX_LENGTH); - } - - const result = await RequestManager.patch( - endpoints.CHANNEL_MESSAGE(data.channel_id, data.id), - content, - ); - return createMessage(result as MessageCreateOptions); - }, + data.mentions.map((mention) => message.guild()?.members.get(mention.id)!), }; + // Remove excess properties to preserve cache. + delete message.channel_id; + delete message.guild_id; + delete message.mentions_everyone; + delete message.mention_channels; + delete message.mention_roles; + delete message.webhook_id; + delete message.message_reference; + delete message.edited_timestamp; + return message; } -export interface Message extends ReturnType {} +export interface Message extends Omit, 'channel_id' | 'guild_id' | 'mentions_everyone' | 'mention_channels' | 'mention_roles' | 'webhook_id' | 'message_reference' | 'edited_timestamp'> {}