From 159d26541dea96be924fd1578722853310e79258 Mon Sep 17 00:00:00 2001 From: Skillz Date: Tue, 11 Feb 2020 14:49:18 -0500 Subject: [PATCH 01/18] some small fixes --- structures/guild.ts | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/structures/guild.ts b/structures/guild.ts index f6d6c2af4..cdada2e19 100644 --- a/structures/guild.ts +++ b/structures/guild.ts @@ -93,9 +93,9 @@ interface Guild { /** The verification level required for the guild */ verification_level: number /** The roles in the guild */ - roles: Role[] + roles: Map /** The custom guild emojis */ - emojis: Emoji[] + emojis: Map /** Enabled guild features */ features: GuildFeatures[] /** Required MFA level for the guild */ @@ -110,9 +110,9 @@ interface Guild { unavailable: boolean /** Total number of members in this guild */ member_count: number - voice_states: VoiceState[] + voice_states: Map /** Users in the guild */ - members: Member[] + members: Map /** Channels in the guild */ channels: Channel[] presences: Presence[] @@ -478,11 +478,11 @@ export interface PrunePayload { export const createGuild = (data: CreateGuildPayload, client: Client) => { const guild: Guild = { ...data, - roles: data.roles.map(role => create_role(role)), - emojis: data.emojis.map(emoji => create_emoji(emoji)), + roles: new Map(data.roles.map(r => [r.id, create_role(r)])), + emojis: new Map(data.emojis.map(e => [e.id, create_emoji(e)])), joinedAt: Date.parse(data.joined_at), - voiceStates: data.voice_states.map(voiceState => createVoiceState(voiceState)), - members: data.members.map(member => createMember(member)), + voiceStates: new Map(data.voice_states.map(vs => [vs.id, create_voice_state(vs)])), + members: new Map(data.members.map(m => [m.id, create_member(m)])), channels: data.channels.map(channel => create_channel(channel)), presences: data.presences.map(presence => createPresence(presence)), icon_url: (size, format) => From 8d3f6ce57eab44618fcc98e6b5391e27379cdec6 Mon Sep 17 00:00:00 2001 From: Skillz Date: Tue, 11 Feb 2020 14:51:42 -0500 Subject: [PATCH 02/18] finalize guild cache system --- structures/guild.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/structures/guild.ts b/structures/guild.ts index 409a04c41..cd7a974e2 100644 --- a/structures/guild.ts +++ b/structures/guild.ts @@ -114,8 +114,8 @@ interface Guild { /** Users in the guild */ members: Map /** Channels in the guild */ - channels: Channel[] - presences: Presence[] + channels: Map + presences: Map /** The maximum amount of presences for the guild(the default value, currently 5000 is in effect when null is returned.) */ max_presences?: number | null /** The maximum amount of members for the guild */ @@ -483,8 +483,8 @@ export const createGuild = (data: CreateGuildPayload, client: Client) => { joinedAt: Date.parse(data.joined_at), voiceStates: new Map(data.voice_states.map(vs => [vs.id, create_voice_state(vs)])), members: new Map(data.members.map(m => [m.id, create_member(m)])), - channels: data.channels.map(channel => create_channel(channel)), - presences: data.presences.map(presence => createPresence(presence)), + channels: new Map(data.channels.map(c => [c.id, create_channel(c)])), + presences: new Map(data.presences.map(p => [p.id, create_presence(p)])), icon_url: (size, format) => data.icon ? formatImageURL(endpoints.GUILD_ICON(data.id, data.icon), size, format) : undefined, splash_url: (size, format) => From 2da8317c1dff9b24ff83532bed4da5360ad731ce Mon Sep 17 00:00:00 2001 From: Skillz Date: Tue, 11 Feb 2020 23:50:05 -0500 Subject: [PATCH 03/18] more guild stuff --- constants/discord.ts | 5 +++-- structures/guild.ts | 52 +++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 54 insertions(+), 3 deletions(-) diff --git a/constants/discord.ts b/constants/discord.ts index 0e8b84e8b..97bc5fb9d 100644 --- a/constants/discord.ts +++ b/constants/discord.ts @@ -10,12 +10,13 @@ export const endpoints = { GUILD_BANNER: (id: string, icon: string) => `${baseEndpoints.CDN_URL}/banners/${id}/${icon}`, GUILD_CHANNELS: (id: string) => `${baseEndpoints.BASE_URL}/guilds/${id}/channels`, GUILD_EMBED: (id: string) => `${baseEndpoints.BASE_URL}/guilds/${id}/embed`, - GUILD_EMOJI: (id: string, emojiID: string) => `${baseEndpoints.BASE_URL}/guilds/${id}/emojis/${emojiID}`, + GUILD_EMOJI: (id: string, emoji_id: string) => `${baseEndpoints.BASE_URL}/guilds/${id}/emojis/${emoji_id}`, GUILD_EMOJIS: (id: string) => `${baseEndpoints.BASE_URL}/guilds/${id}/emojis`, GUILD_ICON: (id: string, icon: string) => `${baseEndpoints.CDN_URL}/icons/${id}/${icon}`, + GUILD_INTEGRATION: (id: string, integration_id: string) => `${baseEndpoints.BASE_URL}/guilds/${id}/integrations/${integration_id}`, GUILD_INTEGRATIONS: (id: string) => `${baseEndpoints.BASE_URL}/guilds/${id}/integrations`, GUILD_PRUNE: (id: string) => `${baseEndpoints.BASE_URL}/guilds/${id}/prune`, - GUILD_ROLE: (id: string, roleID: string) => `${baseEndpoints.BASE_URL}/guilds/${id}/roles/${roleID}`, + GUILD_ROLE: (id: string, role_id: string) => `${baseEndpoints.BASE_URL}/guilds/${id}/roles/${role_id}`, GUILD_ROLES: (id: string) => `${baseEndpoints.BASE_URL}/guilds/${id}/roles`, GUILD_SPLASH: (id: string, icon: string) => `${baseEndpoints.CDN_URL}/splashes/${id}/${icon}`, GUILD_VANITY_URL: (id: string) => `${baseEndpoints.BASE_URL}/guilds/${id}/vanity-url` diff --git a/structures/guild.ts b/structures/guild.ts index cd7a974e2..ff1b76b1c 100644 --- a/structures/guild.ts +++ b/structures/guild.ts @@ -166,10 +166,50 @@ interface Guild { get_vanity_url(): Promise /** Returns a list of integrations for the guild. Requires the MANAGE_GUILD permission. */ get_integrations(): Promise - + /** Modify the behavior and settings of an integration object for the guild. Requires the MANAGE_GUILD permission. */ + edit_integration(id: string, options: EditIntegrationOptions): Promise + /** Delete the attached integration object for the guild with this id. Requires MANAGE_GUILD permission. */ + delete_integration(id: string): Promise + edit(options: GuildEditOptions): Promise leave_voice_channel(): Promise } +export interface GuildEditOptions { + /** The guild name */ + name?: string + /** The guild voice region id */ + region?: string + /** The verification level. 0 is UNRESTRICTED. 1 is Verified email. 2 is 5 minutes user. 3 is 10 minutes member in guild. 4 is verified phone number */ + verification_level?: 0 | 1 | 2 | 3 + /** The default message notification level. 0 is ALL_MESSAGES and 1 is ONLY_MENTINS */ + default_message_notifications?: 0 | 1 + /** Explicit content filter level. 0 is DISABLED 1 is members without roles. 2 is all members */ + explicit_content_filter?: 0 | 1 | 2 + /** The id for the afk channel. */ + afk_channel_id?: string + /** The afk timeout in seconds. */ + afk_timeout?: number + /** base64 1024x1024 png/jpeg/gif image for the guild icon (can be animated gif when the server has ANIMATED_ICON feature) */ + icon?: string + /** user id to transfer guild ownership to (must be owner) */ + owner_id?: string + /** base64 16:9 png/jpeg image for the guild splash (when the server has INVITE_SPLASH feature) */ + splash?: string + /** base64 16:9 png/jpeg image for the guild banner (when the server has BANNER feature) */ + banner?: string + /** the id of the channel to which system messages are sent */ + system_channel_id?: string +} + +export interface EditIntegrationOptions { + /** The behavior when an integration subscription lapses. */ + expire_behavior: number + /** The period in seconds where the integration will ignore lapsed subscriptions */ + expire_grace_period: number + /** Whether emoticons should be synced for this integrations (twitch only currently) */ + enable_emoticons: boolean +} + export interface Guild_Integration { /** The integrations unique id */ id: string @@ -577,8 +617,18 @@ export const createGuild = (data: CreateGuildPayload, client: Client) => { // TODO: requires the MANAGE_GUILD permission return client.RequestManager.get(endpoints.GUILD_INTEGRATIONS(data.id)) }, + edit_integration: (id, options) => { + // TODO: requires the MANAGE_GUILD permission + return client.RequestManager.patch(endpoints.GUILD_INTEGRATION(data.id, id), options) + }, + delete_integration: id => { + // TODO: requires the MANAGE_GUILD permission + return client.RequestManager.delete(endpoints.GUILD_INTEGRATION(data.id, id)) + }, leave_voice_channel: () => {} } + guild.edit({ default_message_notifications: 5 }) + return guild } From 97488441be4dae5907b9d589c4f14dffb3bd7ef5 Mon Sep 17 00:00:00 2001 From: Skillz Date: Wed, 12 Feb 2020 11:31:27 -0500 Subject: [PATCH 04/18] phase 1 of guilds complete --- constants/discord.ts | 47 +++++++++------ structures/guild.ts | 139 +++++++++++++++++++++++++++++++++++++++---- structures/member.ts | 43 ++++++++++++- 3 files changed, 199 insertions(+), 30 deletions(-) diff --git a/constants/discord.ts b/constants/discord.ts index 97bc5fb9d..32710b16b 100644 --- a/constants/discord.ts +++ b/constants/discord.ts @@ -1,23 +1,34 @@ export const baseEndpoints = { - /** Although, the version can be defaulted, keep the v6 as it can be changed to test newer versions when necessary. */ - BASE_URL: "https://discordapp.com/api/v6", - CDN_URL: "https://cdn.discordapp.com", + /** Although, the version can be defaulted, keep the v6 as it can be changed to test newer versions when necessary. */ + BASE_URL: 'https://discordapp.com/api/v6', + CDN_URL: 'https://cdn.discordapp.com' } export const endpoints = { - GATEWAY_BOT: `${baseEndpoints.BASE_URL}/gateway/bot`, - GUILD_AUDIT_LOGS: (id: string) => `${baseEndpoints.BASE_URL}/guilds/${id}/audit-logs`, - GUILD_BANNER: (id: string, icon: string) => `${baseEndpoints.CDN_URL}/banners/${id}/${icon}`, - GUILD_CHANNELS: (id: string) => `${baseEndpoints.BASE_URL}/guilds/${id}/channels`, - GUILD_EMBED: (id: string) => `${baseEndpoints.BASE_URL}/guilds/${id}/embed`, - GUILD_EMOJI: (id: string, emoji_id: string) => `${baseEndpoints.BASE_URL}/guilds/${id}/emojis/${emoji_id}`, - GUILD_EMOJIS: (id: string) => `${baseEndpoints.BASE_URL}/guilds/${id}/emojis`, - GUILD_ICON: (id: string, icon: string) => `${baseEndpoints.CDN_URL}/icons/${id}/${icon}`, - GUILD_INTEGRATION: (id: string, integration_id: string) => `${baseEndpoints.BASE_URL}/guilds/${id}/integrations/${integration_id}`, - GUILD_INTEGRATIONS: (id: string) => `${baseEndpoints.BASE_URL}/guilds/${id}/integrations`, - GUILD_PRUNE: (id: string) => `${baseEndpoints.BASE_URL}/guilds/${id}/prune`, - GUILD_ROLE: (id: string, role_id: string) => `${baseEndpoints.BASE_URL}/guilds/${id}/roles/${role_id}`, - GUILD_ROLES: (id: string) => `${baseEndpoints.BASE_URL}/guilds/${id}/roles`, - GUILD_SPLASH: (id: string, icon: string) => `${baseEndpoints.CDN_URL}/splashes/${id}/${icon}`, - GUILD_VANITY_URL: (id: string) => `${baseEndpoints.BASE_URL}/guilds/${id}/vanity-url` + GATEWAY_BOT: `${baseEndpoints.BASE_URL}/gateway/bot`, + GUILD: (id: string) => `${baseEndpoints.BASE_URL}/guilds/${id}`, + GUILD_AUDIT_LOGS: (id: string) => `${baseEndpoints.BASE_URL}/guilds/${id}/audit-logs`, + GUILD_BAN: (id: string, user_id: string) => `${baseEndpoints.BASE_URL}/guilds/${id}/bans/${user_id}`, + GUILD_BANS: (id: string) => `${baseEndpoints.BASE_URL}/guilds/${id}/bans`, + GUILD_BANNER: (id: string, icon: string) => `${baseEndpoints.CDN_URL}/banners/${id}/${icon}`, + GUILD_CHANNELS: (id: string) => `${baseEndpoints.BASE_URL}/guilds/${id}/channels`, + GUILD_EMBED: (id: string) => `${baseEndpoints.BASE_URL}/guilds/${id}/embed`, + GUILD_EMOJI: (id: string, emoji_id: string) => `${baseEndpoints.BASE_URL}/guilds/${id}/emojis/${emoji_id}`, + GUILD_EMOJIS: (id: string) => `${baseEndpoints.BASE_URL}/guilds/${id}/emojis`, + GUILD_ICON: (id: string, icon: string) => `${baseEndpoints.CDN_URL}/icons/${id}/${icon}`, + GUILD_INTEGRATION: (id: string, integration_id: string) => + `${baseEndpoints.BASE_URL}/guilds/${id}/integrations/${integration_id}`, + GUILD_INTEGRATION_SYNC: (id: string, integration_id: string) => + `${baseEndpoints.BASE_URL}/guilds/${id}/integrations/${integration_id}/sync`, + GUILD_INTEGRATIONS: (id: string) => `${baseEndpoints.BASE_URL}/guilds/${id}/integrations`, + GUILD_INVITES: (id: string) => `${baseEndpoints.BASE_URL}/guilds/${id}/invites`, + GUILD_LEAVE: (id: string) => `${baseEndpoints.BASE_URL}/users/@me/guilds/${id}`, + GUILD_MEMBER: (id: string, member_id: string) => `${baseEndpoints.BASE_URL}/guilds/${id}/members/${member_id}`, + GUILD_PRUNE: (id: string) => `${baseEndpoints.BASE_URL}/guilds/${id}/prune`, + GUILD_REGIONS: (id: string) => `${baseEndpoints.BASE_URL}/guilds/${id}/regions`, + GUILD_ROLE: (id: string, role_id: string) => `${baseEndpoints.BASE_URL}/guilds/${id}/roles/${role_id}`, + GUILD_ROLES: (id: string) => `${baseEndpoints.BASE_URL}/guilds/${id}/roles`, + GUILD_SPLASH: (id: string, icon: string) => `${baseEndpoints.CDN_URL}/splashes/${id}/${icon}`, + GUILD_VANITY_URL: (id: string) => `${baseEndpoints.BASE_URL}/guilds/${id}/vanity-url`, + GUILD_WEBHOOKS: (id: string) => `${baseEndpoints.BASE_URL}/guilds/${id}/webhooks` } diff --git a/structures/guild.ts b/structures/guild.ts index ff1b76b1c..2bba4b065 100644 --- a/structures/guild.ts +++ b/structures/guild.ts @@ -73,7 +73,7 @@ interface CreateGuildPayload { preferred_locale: string } -interface Guild { +export interface Guild { /** The guild id */ id: string /** The guild name 2-100 characters */ @@ -140,6 +140,18 @@ interface Guild { banner_url(size?: Image_Size, format?: Image_Formats): string | undefined /** Create a channel in your server. Bot needs MANAGE_CHANNEL permissions in the server. */ create_channel(name: string, options: ChannelCreate_Options): Promise + /** 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.** + */ + get_channels(): Promise + /** Modify the positions of channels on the guild. Requires MANAGE_CHANNELS permisison. */ + swap_channels(channel_positions: Position_Swap[]): Promise + /** 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.** + */ + get_member(id: string): Promise /** 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. */ create_emoji(name: string, image: string, options: Create_Emojis_Options): Promise /** Modify the given emoji. Requires the MANAGE_EMOJIS permission. */ @@ -152,6 +164,13 @@ interface Guild { edit_role(id: string, options: Create_Role_Options): Promise /** Delete a guild role. Requires the MANAGE_ROLES permission. */ delete_role(id: string): Promise + /** 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.** + */ + get_roles(): Promise + /** Modify the positions of a set of role objects for the guild. Requires the MANAGE_ROLES permission. */ + swap_roles(role_positions: Position_Swap): Promise /** Check how many members would be removed from the server in a prune operation. Requires the KICK_MEMBERS permission */ get_prune_count(days: number): Promise /** Begin pruning all members in the given time period */ @@ -167,14 +186,63 @@ interface Guild { /** Returns a list of integrations for the guild. Requires the MANAGE_GUILD permission. */ get_integrations(): Promise /** Modify the behavior and settings of an integration object for the guild. Requires the MANAGE_GUILD permission. */ - edit_integration(id: string, options: EditIntegrationOptions): Promise + edit_integration(id: string, options: Edit_Integration_Options): Promise /** Delete the attached integration object for the guild with this id. Requires MANAGE_GUILD permission. */ delete_integration(id: string): Promise - edit(options: GuildEditOptions): Promise + /** Sync an integration. Requires teh MANAGE_GUILD permission. */ + sync_integration(id: string): Promise + /** Returns a list of ban objects for the users banned from this guild. Requires the BAN_MEMBERS permission. */ + get_bans(): Promise + /** 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): Promise + /** Remove the ban for a user. REquires BAN_MEMBERS permission */ + unban(id: string): Promise + /** Modify a guilds settings. Requires the MANAGE_GUILD permission. */ + edit(options: Guild_Edit_Options): Promise + /** Leave a guild */ + leave(): Promise + /** 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. */ + get_voice_regions(): Promise leave_voice_channel(): Promise } -export interface GuildEditOptions { +export interface Voice_Region { + /** unique ID for the region */ + id: string + /** name of the region */ + name: string + /** true if this is a vip-only server */ + vip: boolean + /** true for a single server that is closest to the current user's client */ + optimal: boolean + /** whether this is a deprecated voice region (avoid switching to these) */ + deprecated: boolean + /** whether this is a custom voice region (used for events/etc) */ + custom: boolean +} + +export interface BanOptions { + /** number of days to delete messages for (0-7) */ + delete_message_days?: 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 + /** The reason for the ban. */ + reason?: string +} + +export interface BannedUser { + /** The reason for the ban */ + reason?: string + /** The banned user object */ + user: User +} + +export interface Position_Swap { + /** The unique id */ + id: string + /** The sorting position number. */ + position: number +} + +export interface Guild_Edit_Options { /** The guild name */ name?: string /** The guild voice region id */ @@ -201,7 +269,7 @@ export interface GuildEditOptions { system_channel_id?: string } -export interface EditIntegrationOptions { +export interface Edit_Integration_Options { /** The behavior when an integration subscription lapses. */ expire_behavior: number /** The period in seconds where the integration will ignore lapsed subscriptions */ @@ -291,7 +359,7 @@ export interface Guild_Embed { enabled: boolean } -export interface Get_audit_logsOptions { +export interface Get_Audit_Logs_Options { /** Filter the logs for actions made by this user. */ user_id?: string /** The type of audit log. */ @@ -546,6 +614,16 @@ export const createGuild = (data: CreateGuildPayload, client: Client) => { ...options }) }, + get_channels: () => { + return client.RequestManager.get(endpoints.GUILD_CHANNELS(data.id)) + }, + swap_channels: channel_positions => { + if (channel_positions.length < 2) throw 'You must provide atleast two channels to be swapped.' + return client.RequestManager.patch(endpoints.GUILD_CHANNELS(data.id), channel_positions) + }, + get_member: id => { + return client.RequestManager.get(endpoints.GUILD_MEMBER(data.id, id)) + }, create_emoji: (name, image, options) => { // TODO: Check if the bot has `MANAGE_EMOJIS` permission return client.RequestManager.post(endpoints.GUILD_EMOJIS(data.id), { @@ -576,11 +654,21 @@ export const createGuild = (data: CreateGuildPayload, client: Client) => { return role }, edit_role: (id, options) => { + // TODO: check if the bot has the `MANAGE_ROLES` permission. return client.RequestManager.patch(endpoints.GUILD_ROLE(data.id, id), options) }, delete_role: id => { + // TODO: check if the bot has the `MANAGE_ROLES` permission. return client.RequestManager.delete(endpoints.GUILD_ROLE(data.id, id)) }, + get_roles: () => { + // TODO: check if the bot has the `MANAGE_ROLES` permission. + return client.RequestManager.get(endpoints.GUILD_ROLES(data.id)) + }, + swap_roles: rolePositons => { + // TODO: check if the bot has the `MANAGE_ROLES` permission. + return client.RequestManager.patch(endpoints.GUILD_ROLES(data.id), rolePositons) + }, get_prune_count: async days => { if (days < 1) throw `The number of days to count prune for must be 1 or more.` // TODO: check if the bot has `KICK_MEMBERS` permission @@ -592,7 +680,7 @@ export const createGuild = (data: CreateGuildPayload, client: Client) => { // TODO: check if the bot has `KICK_MEMBERS` permission. return client.RequestManager.post(endpoints.GUILD_PRUNE(data.id), { days }) }, - fetchAllMembers: () => { + fetch_all_members: () => { // TODO: REQUEST THIS OVER WEBSOCKET WITH GET_GUILD_MEMBERS ENDPOINT }, get_audit_logs: options => { @@ -625,10 +713,41 @@ export const createGuild = (data: CreateGuildPayload, client: Client) => { // TODO: requires the MANAGE_GUILD permission return client.RequestManager.delete(endpoints.GUILD_INTEGRATION(data.id, id)) }, - leave_voice_channel: () => {} + sync_integration: id => { + // TODO: requires MANAGE_GUILD + return client.RequestManager.post(endpoints.GUILD_INTEGRATION_SYNC(data.id, id)) + }, + get_bans: () => { + // TODO: requires the BAN_MEMBERS permission + return client.RequestManager.get(endpoints.GUILD_BANS(data.id)) + }, + ban: (id, options) => { + // TODO: requires the BAN_MEMBERS permission + return client.RequestManager.put(endpoints.GUILD_BAN(data.id, id), options) + }, + unban: id => { + // TODO: requires the BAN_MEMBERS permission + return client.RequestManager.delete(endpoints.GUILD_BAN(data.id, id)) + }, + edit: options => { + // TODO: requires the MANAGE_GUILD permission + return client.RequestManager.patch(endpoints.GUILD(data.id), options) + }, + get_invites: () => { + // TODO: requires MANAGE_GUILD permission + return client.RequestManager.get(endpoints.GUILD_INVITES(data.id)) + }, + leave: () => { + return client.RequestManager.delete(endpoints.GUILD_LEAVE(data.id)) + }, + get_voice_regions: () => { + return client.RequestManager.get(endpoints.GUILD_REGIONS(data.id)) + }, + get_webhooks: () => { + // TODO: requires MANAGE_WEBHOOKS + return client.RequestManager.get(endpoints.GUILD_WEBHOOKS(data.id)) + } } - guild.edit({ default_message_notifications: 5 }) - return guild } diff --git a/structures/member.ts b/structures/member.ts index 49aee74dd..da13d6569 100644 --- a/structures/member.ts +++ b/structures/member.ts @@ -1,3 +1,42 @@ -export const createMember = (data: unknown) => { - console.log(data) +import Client from "../module/client" +import { endpoints } from "../constants/discord" + +export interface Edit_Member_Options { + /** Value to set users nickname to. Requires MANAGE_NICKNAMES permission. */ + nick?: string + /** Array of role ids the member is assigned. Requires MANAGE_ROLES permission. */ + roles?: string[] + /** Whether the user is muted in voice channels. Requires MUTE_MEMBERS permission. */ + mute?: boolean + /** Whether the user is deafened in voice channels. Requires DEAFEN_MEMBERS permission. */ + deaf?: boolean + /** The id of the channel to move user to if they are connected to voice. To kick the user from their current channel, set to null. Requires MOVE_MEMBERS permission. When moving members to channels, must have permissions to both CONNECT to the channel and have the MOVE_MEMBER permission. */ + channel_id?: string | null +} + +export interface Member { + edit(options: Edit_Member_Options): Promise +} + +export const createMember = (data: unknown, guild: Guild, client: Client) => { + const Member: Member = { + edit: (options) => { + // TODO: check if has MANAGE_NICKNAME Permission + // TODO: check if it is a valid nickname like 32 characters + options.nick = undefined + + // TODO: check if has MANAGE_ROLES permission + options.roles = undefined + + // TODO: This should check if the member is in a voice channel + // TODO: CHeck if has MUTE_MEMBERS permission + options.mute = undefined + // TODO: check if has DEAFEN_MEMBERS permission + options.deaf = undefined + // TODO: if channel id is provided check if the bot has CONNECT and MOVE in channel and current channel + options.channel_id = undefined + + return client.RequestManager.patch(endpoints.GUILD_MEMBER(guild.id, data.id), options), + } + } } From 1d0fcedf221d1e7627e9ceac1785074427e63bcf Mon Sep 17 00:00:00 2001 From: Skillz Date: Wed, 12 Feb 2020 11:37:10 -0500 Subject: [PATCH 05/18] cleanup of guild structure file --- structures/guild.ts | 592 +------------------------------------------- types/guild.ts | 576 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 582 insertions(+), 586 deletions(-) create mode 100644 types/guild.ts diff --git a/structures/guild.ts b/structures/guild.ts index 2bba4b065..f64b93a81 100644 --- a/structures/guild.ts +++ b/structures/guild.ts @@ -1,595 +1,15 @@ import Client from '../module/client' import { endpoints } from '../constants/discord' import { formatImageURL } from '../utils/cdn' -import { create_role } from './role' -import { create_emoji } from './emoji' -import { createVoiceState } from './voiceState' -import { createMember } from './member' -import { create_channel } from './channel' -import { createPresence } from './presence' - -interface CreateGuildPayload { - /** The guild id */ - id: string - /** The guild name 2-100 characters */ - name: string - /** The guild icon image hash */ - icon: string | null - /** The guild splash image hash */ - splash: string | null - /** The id of the owner */ - owner_id: string - /** The voice region id for the guild */ - region: string - /** The afk channel id */ - afk_channel_id: string | null - /** AFK Timeout in seconds. */ - afk_timeout: number - /** Whether this guild is embeddable (widget) */ - embed_enabled?: boolean - /** If not null, the channel id that the widge will generate an invite to. */ - embed_channel_id?: string | null - /** The verification level required for the guild */ - verification_level: number - /** The roles in the guild */ - roles: Role[] - /** The custom guild emojis */ - emojis: Emoji[] - /** Enabled guild features */ - features: GuildFeatures[] - /** Required MFA level for the guild */ - mfa_level: number - /** The id of the channel to which system mesages are sent */ - system_channel_id: string | null - /** When this guild was joined at */ - joined_at: string - /** Whether this is considered a large guild */ - large: boolean - /** Whether this guild is unavailable */ - unavailable: boolean - /** Total number of members in this guild */ - member_count: number - voice_states: VoiceState[] - /** Users in the guild */ - members: Member[] - /** Channels in the guild */ - channels: Channel[] - presences: Presence[] - /** The maximum amount of presences for the guild(the default value, currently 5000 is in effect when null is returned.) */ - max_presences?: number | null - /** The maximum amount of members for the guild */ - max_members?: number - /** The vanity url code for the guild */ - vanity_url_code: string | null - /** The description for the guild */ - description: string | null - /** The banner hash */ - banner: string | null - /** The premium tier */ - premium_tier: number - /** The total number of users currently boosting this server. */ - premium_subscription_count: number - /** The preferred local of this guild only set if guild has the DISCOVERABLE feature, defaults to en-US */ - preferred_locale: string -} - -export interface Guild { - /** The guild id */ - id: string - /** The guild name 2-100 characters */ - name: string - /** The guild icon image hash */ - icon: string | null - /** The guild splash image hash */ - splash: string | null - /** The id of the owner */ - owner_id: string - /** The voice region id for the guild */ - region: string - /** The afk channel id */ - afk_channel_id: string | null - /** AFK Timeout in seconds. */ - afk_timeout: number - /** The verification level required for the guild */ - verification_level: number - /** The roles in the guild */ - roles: Map - /** The custom guild emojis */ - emojis: Map - /** Enabled guild features */ - features: GuildFeatures[] - /** Required MFA level for the guild */ - mfa_level: number - /** The id of the channel to which system mesages are sent */ - system_channel_id: string | null - /** When this guild was joined at */ - joined_at: number - /** Whether this is considered a large guild */ - large: boolean - /** Whether this guild is unavailable */ - unavailable: boolean - /** Total number of members in this guild */ - member_count: number - voice_states: Map - /** Users in the guild */ - members: Map - /** Channels in the guild */ - channels: Map - presences: Map - /** The maximum amount of presences for the guild(the default value, currently 5000 is in effect when null is returned.) */ - max_presences?: number | null - /** The maximum amount of members for the guild */ - max_members?: number - /** The vanity url code for the guild */ - vanity_url_code: string | null - /** The description for the guild */ - description: string | null - /** The banner hash */ - banner: string | null - /** The premium tier */ - premium_tier: number - /** The total number of users currently boosting this server. */ - premium_subscription_count: number - /** The preferred local of this guild only set if guild has the DISCOVERABLE feature, defaults to en-US */ - preferred_locale: string - /** The full URL of the icon from Discords CDN. Undefined when no icon is set. */ - icon_url(size?: Image_Size, format?: Image_Formats): string | undefined - /** The full URL of the splash from Discords CDN. Undefined if no splash is set. */ - splash_url(size?: Image_Size, format?: Image_Formats): string | undefined - /** The full URL of the banner from Discords CDN. Undefined if no banner is set. */ - banner_url(size?: Image_Size, format?: Image_Formats): string | undefined - /** Create a channel in your server. Bot needs MANAGE_CHANNEL permissions in the server. */ - create_channel(name: string, options: ChannelCreate_Options): Promise - /** 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.** - */ - get_channels(): Promise - /** Modify the positions of channels on the guild. Requires MANAGE_CHANNELS permisison. */ - swap_channels(channel_positions: Position_Swap[]): Promise - /** 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.** - */ - get_member(id: string): Promise - /** 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. */ - create_emoji(name: string, image: string, options: Create_Emojis_Options): Promise - /** Modify the given emoji. Requires the MANAGE_EMOJIS permission. */ - edit_emoji(id: string, options: Edit_Emojis_Options): Promise - /** Delete the given emoji. Requires the MANAGE_EMOJIS permission. Returns 204 No Content on success. */ - delete_emoji(id: string, reason?: string): Promise - /** Create a new role for the guild. Requires the MANAGE_ROLES permission. */ - create_role(options: Create_Role_Options): Promise - /** Edi a guild role. Requires the MANAGE_ROLES permission. */ - edit_role(id: string, options: Create_Role_Options): Promise - /** Delete a guild role. Requires the MANAGE_ROLES permission. */ - delete_role(id: string): Promise - /** 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.** - */ - get_roles(): Promise - /** Modify the positions of a set of role objects for the guild. Requires the MANAGE_ROLES permission. */ - swap_roles(role_positions: Position_Swap): Promise - /** Check how many members would be removed from the server in a prune operation. Requires the KICK_MEMBERS permission */ - get_prune_count(days: number): Promise - /** Begin pruning all members in the given time period */ - prune_members(days: number): Promise - /** Returns the audit logs for the guild. Requires VIEW AUDIT LOGS permission */ - get_audit_logs(options: Get_audit_logsOptions): Promise - /** Returns the guild embed object. Requires the MANAGE_GUILD permission. */ - get_embed(): Promise - /** Modify a guild embed object for the guild. Requires the MANAGE_GUILD permission. */ - edit_embed(enabled: boolean, channel_id?: string | null): Promise - /** Returns the code and uses of the vanity url for this server if it is enabled. Requires the MANAGE_GUILD permission. */ - get_vanity_url(): Promise - /** Returns a list of integrations for the guild. Requires the MANAGE_GUILD permission. */ - get_integrations(): Promise - /** Modify the behavior and settings of an integration object for the guild. Requires the MANAGE_GUILD permission. */ - edit_integration(id: string, options: Edit_Integration_Options): Promise - /** Delete the attached integration object for the guild with this id. Requires MANAGE_GUILD permission. */ - delete_integration(id: string): Promise - /** Sync an integration. Requires teh MANAGE_GUILD permission. */ - sync_integration(id: string): Promise - /** Returns a list of ban objects for the users banned from this guild. Requires the BAN_MEMBERS permission. */ - get_bans(): Promise - /** 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): Promise - /** Remove the ban for a user. REquires BAN_MEMBERS permission */ - unban(id: string): Promise - /** Modify a guilds settings. Requires the MANAGE_GUILD permission. */ - edit(options: Guild_Edit_Options): Promise - /** Leave a guild */ - leave(): Promise - /** 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. */ - get_voice_regions(): Promise - leave_voice_channel(): Promise -} - -export interface Voice_Region { - /** unique ID for the region */ - id: string - /** name of the region */ - name: string - /** true if this is a vip-only server */ - vip: boolean - /** true for a single server that is closest to the current user's client */ - optimal: boolean - /** whether this is a deprecated voice region (avoid switching to these) */ - deprecated: boolean - /** whether this is a custom voice region (used for events/etc) */ - custom: boolean -} - -export interface BanOptions { - /** number of days to delete messages for (0-7) */ - delete_message_days?: 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 - /** The reason for the ban. */ - reason?: string -} - -export interface BannedUser { - /** The reason for the ban */ - reason?: string - /** The banned user object */ - user: User -} - -export interface Position_Swap { - /** The unique id */ - id: string - /** The sorting position number. */ - position: number -} - -export interface Guild_Edit_Options { - /** The guild name */ - name?: string - /** The guild voice region id */ - region?: string - /** The verification level. 0 is UNRESTRICTED. 1 is Verified email. 2 is 5 minutes user. 3 is 10 minutes member in guild. 4 is verified phone number */ - verification_level?: 0 | 1 | 2 | 3 - /** The default message notification level. 0 is ALL_MESSAGES and 1 is ONLY_MENTINS */ - default_message_notifications?: 0 | 1 - /** Explicit content filter level. 0 is DISABLED 1 is members without roles. 2 is all members */ - explicit_content_filter?: 0 | 1 | 2 - /** The id for the afk channel. */ - afk_channel_id?: string - /** The afk timeout in seconds. */ - afk_timeout?: number - /** base64 1024x1024 png/jpeg/gif image for the guild icon (can be animated gif when the server has ANIMATED_ICON feature) */ - icon?: string - /** user id to transfer guild ownership to (must be owner) */ - owner_id?: string - /** base64 16:9 png/jpeg image for the guild splash (when the server has INVITE_SPLASH feature) */ - splash?: string - /** base64 16:9 png/jpeg image for the guild banner (when the server has BANNER feature) */ - banner?: string - /** the id of the channel to which system messages are sent */ - system_channel_id?: string -} - -export interface Edit_Integration_Options { - /** The behavior when an integration subscription lapses. */ - expire_behavior: number - /** The period in seconds where the integration will ignore lapsed subscriptions */ - expire_grace_period: number - /** Whether emoticons should be synced for this integrations (twitch only currently) */ - enable_emoticons: boolean -} - -export interface Guild_Integration { - /** The integrations unique id */ - id: string - /** the integrations name */ - name: string - /** The integration type like twitch, youtube etc */ - type: string - /** Is this integration enabled */ - enabled: boolean - /** is this integration syncing */ - syncing: boolean - /** id that this integration uses for "subscribers" */ - role_id: string - /** The behavior of expiring subscribers */ - expire_behavior: number - /** The grace period before expiring subscribers */ - expire_grace_period: number - /** The user for this integration */ - user: User_Data - /** The integration account information */ - account: Account - /** When this integration was last synced */ - synced_at: string -} - -export interface User_Data { - /** The user's id */ - id: string - /** the user's username, not unique across the platform */ - username: string - /** The user's 4 digit discord tag */ - discriminator: string - /** The user's avatar hash */ - avatar: string | null - /** Whether the user is a bot */ - bot?: boolean - /** Whether the user is an official discord system user (part of the urgent message system.) */ - system?: boolean - /** Whether the user has two factor enabled on their account */ - mfa_enabled?: boolean - /** the user's chosen language option */ - locale?: string - /** Whether the email on this account has been verified */ - verified?: boolean - /** The user's email */ - email?: string - /** The flags on a user's account. */ - flags?: number - /** The type of Nitro subscription on a user's account. */ - premium_type?: number -} - -export enum User_Flags { - NONE, - DISCORD_EMPLOYEE, - DISCORD_PARTNER, - HYPE_SQUAD_EVENTS = 1 << 2, - BUG_HUNTER = 1 << 3, - HOUSE_BRAVERY = 1 << 6, - HOUSE_BRILLIANCE = 1 << 7, - HOUSE_BALANCE = 1 << 8, - EARLY_SUPPORTER = 1 << 9, - TEAM_USER = 1 << 10, - SYSTEM = 1 << 12 -} - -export enum Nitro_Types { - NITRO_CLASSIC = 1, - NITRO -} - -export interface Vanity_Invite { - code: string | null - uses: number -} - -export interface Guild_Embed { - /** Whether the embed is enbaled. */ - enabled: boolean -} - -export interface Get_Audit_Logs_Options { - /** Filter the logs for actions made by this user. */ - user_id?: string - /** The type of audit log. */ - action_type?: AuditLogType - /** Filter the logs before a certain log entry. */ - before?: string - /** How many entries are returned. Between 1-100. Default 50. */ - limit?: number -} - -export type AuditLogType = - | `GUILD_UPDATE` - | `CHANNEL_CREATE` - | `CHANNEL_UPDATE` - | `CHANNEL_DELETE` - | `CHANNEL_OVERWRITE_CREATE` - | `CHANNEL_OVERWRITE_UPDATE` - | `CHANNEL_OVERWRITE_DELETE` - | `MEMBER_KICK` - | `MEMBER_PRUNE` - | `MEMBER_BAN_ADD` - | `MEMBER_BAN_REMOVE` - | `MEMBER_UPDATE` - | `MEMBER_ROLE_UPDATE` - | `MEMBER_MOVE` - | `MEMBER_DISCONNECT` - | `BOT_ADD` - | `ROLE_CREATE` - | `ROLE_UPDATE` - | `ROLE_DELETE` - | `INVITE_CREATE` - | `INVITE_UPDATE` - | `INVITE_DELETE` - | `WEBHOOK_CREATE` - | `WEBHOOK_UPDATE` - | `WEBHOOK_DELETE` - | `EMOJI_CREATE` - | `EMOJI_UPDATE` - | `EMOJI_DELETE` - | `MESSAGE_DELETE` - | `MESSAGE_BULK_DELETE` - | `MESSAGE_PIN` - | `MESSAGE_UNPIN` - | `INTEGRATION_CREATE` - | `INTEGRATION_UPDATE` - | `INTEGRATION_DELETE` - -export enum AuditLogs { - GUILD_UPDATE = 1, - CHANNEL_CREATE = 10, - CHANNEL_UPDATE, - CHANNEL_DELETE, - CHANNEL_OVERWRITE_CREATE, - CHANNEL_OVERWRITE_UPDATE, - CHANNEL_OVERWRITE_DELETE, - MEMBER_KICK = 20, - MEMBER_PRUNE, - MEMBER_BAN_ADD, - MEMBER_BAN_REMOVE, - MEMBER_UPDATE, - MEMBER_ROLE_UPDATE, - MEMBER_MOVE, - MEMBER_DISCONNECT, - BOT_ADD, - ROLE_CREATE = 30, - ROLE_UPDATE, - ROLE_DELETE, - INVITE_CREATE = 40, - INVITE_UPDATE, - INVITE_DELETE, - WEBHOOK_CREATE = 50, - WEBHOOK_UPDATE, - WEBHOOK_DELETE, - EMOJI_CREATE = 60, - EMOJI_UPDATE, - EMOJI_DELETE, - MESSAGE_DELETE = 72, - MESSAGE_BULK_DELETE, - MESSAGE_PIN, - MESSAGE_UNPIN, - INTEGRATION_CREATE = 80, - INTEGRATION_UPDATE, - INTEGRATION_DELETE -} - -export type Image_Size = 16 | 32 | 64 | 128 | 256 | 512 | 1024 | 2048 -export type Image_Formats = 'jpg' | 'jpeg' | 'png' | 'webp' | 'gif' -export type ChannelType = 'text' | 'dm' | 'news' | 'voice' | 'category' | 'store' - -export type Permission = - | `CREATE_INSTANT_INVITE` - | `KICK_MEMBERS` - | `BAN_MEMBERS` - | `ADMINISTRATOR` - | `MANAGE_CHANNELS` - | `MANAGE_GUILD` - | `ADD_REACTIONS` - | `VIEW_AUDIT_LOG` - | `VIEW_CHANNEL` - | `SEND_MESSAGES` - | `SEND_TTS_MESSAGES` - | `MANAGE_MESSAGES` - | `EMBED_LINKS` - | `ATTACH_FILES` - | `READ_MESSAGE_HISTORY` - | `MENTION_EVERYONE` - | `USE_EXTERNAL_EMOJIS` - | `CONNECT` - | `SPEAK` - | `MUTE_MEMBERS` - | `DEAFEN_MEMBERS` - | `MOVE_MEMBERS` - | `USE_VAD` - | `PRIORITY_SPEAKER` - | `STREAM` - | `CHANGE_NICKNAME` - | `MANAGE_NICKNAMES` - | `MANAGE_ROLES` - | `MANAGE_WEBHOOKS` - | `MANAGE_EMOJIS` - -export interface Overwrite { - /** The role or user id */ - id: string - /** Whether this is a role or a member */ - type: 'role' | 'member' - /** The permissions that this id is allowed to do. (This will mark it as a green check.) */ - allow: Permission[] - /** The permissions that this id is NOT allowed to do. (This will mark it as a red x.) */ - deny: Permission[] -} - -export enum ChannelTypes { - text, - dm, - voice, - category = 4, - news, - store -} - -export enum Permissions { - CREATE_INSTANT_INVITE = 0x00000001, - KICK_MEMBERS = 0x00000002, - BAN_MEMBERS = 0x00000004, - ADMINISTRATOR = 0x00000008, - MANAGE_CHANNELS = 0x00000010, - MANAGE_GUILD = 0x00000020, - ADD_REACTIONS = 0x00000040, - VIEW_AUDIT_LOG = 0x00000080, - VIEW_CHANNEL = 0x00000400, - SEND_MESSAGES = 0x00000800, - SEND_TTS_MESSAGES = 0x00001000, - MANAGE_MESSAGES = 0x00002000, - EMBED_LINKS = 0x00004000, - ATTACH_FILES = 0x00008000, - READ_MESSAGE_HISTORY = 0x00010000, - MENTION_EVERYONE = 0x00020000, - USE_EXTERNAL_EMOJIS = 0x00040000, - CONNECT = 0x00100000, - SPEAK = 0x00200000, - MUTE_MEMBERS = 0x00400000, - DEAFEN_MEMBERS = 0x00800000, - MOVE_MEMBERS = 0x01000000, - USE_VAD = 0x02000000, - PRIORITY_SPEAKER = 0x00000100, - STREAM = 0x00000200, - CHANGE_NICKNAME = 0x04000000, - MANAGE_NICKNAMES = 0x08000000, - MANAGE_ROLES = 0x10000000, - MANAGE_WEBHOOKS = 0x20000000, - MANAGE_EMOJIS = 0x40000000 -} - -export interface ChannelCreate_Options { - /** The type of the channel */ - type?: ChannelType - /** The channel topic. (0-1024 characters) */ - topic?: string - /** The bitrate(in bits) of the voice channel. */ - bitrate?: number - /** The user limit of the voice channel. */ - user_limit?: number - /** The amount of seconds a user has to wait before sending another message. (0-21600 seconds). Bots, as well as users with the permission `manage_messages or manage_channel` are unaffected. */ - rate_limit_per_user?: number - /** The sorting position of the channel */ - position?: number - /** The channel's permission overwrites */ - permission_overwrites?: Overwrite[] - /** The id of the parent category for the channel */ - parent_id?: string - /** Whether the channel is nsfw */ - nsfw?: boolean - /** The reason to add in the Audit Logs. */ - reason?: string -} - -export interface Create_Emojis_Options { - /** The roles for which this emoji will be whitelisted. Only the users with one of these roles can use this emoji. */ - roles: string[] - /** The reason to have in the Audit Logs. */ - reason: string -} - -export interface Edit_Emojis_Options { - /** The name of the emoji */ - name: string - /** The roles for which this emoji will be whitelisted. Only the users with one of these roles can use this emoji. */ - roles: string[] -} - -export interface Create_Role_Options { - name?: string - permissions?: Permission[] - color?: number - hoist?: boolean - mentionable?: boolean -} - -export interface PrunePayload { - pruned: number -} +import { Guild, CreateGuildPayload, ChannelTypes, Permissions, PrunePayload } from '../types/guild' export const createGuild = (data: CreateGuildPayload, client: Client) => { const guild: Guild = { ...data, roles: new Map(data.roles.map(r => [r.id, create_role(r)])), emojis: new Map(data.emojis.map(e => [e.id, create_emoji(e)])), - joinedAt: Date.parse(data.joined_at), - voiceStates: new Map(data.voice_states.map(vs => [vs.id, create_voice_state(vs)])), + joined_at: Date.parse(data.joined_at), + voice_states: new Map(data.voice_states.map(vs => [vs.id, create_voice_state(vs)])), members: new Map(data.members.map(m => [m.id, create_member(m)])), channels: new Map(data.channels.map(c => [c.id, create_channel(c)])), presences: new Map(data.presences.map(p => [p.id, create_presence(p)])), @@ -680,9 +100,9 @@ export const createGuild = (data: CreateGuildPayload, client: Client) => { // TODO: check if the bot has `KICK_MEMBERS` permission. return client.RequestManager.post(endpoints.GUILD_PRUNE(data.id), { days }) }, - fetch_all_members: () => { - // TODO: REQUEST THIS OVER WEBSOCKET WITH GET_GUILD_MEMBERS ENDPOINT - }, + // TODO: REQUEST THIS OVER WEBSOCKET WITH GET_GUILD_MEMBERS ENDPOINT + // fetch_all_members: () => { + // }, get_audit_logs: options => { // TODO: check if the bot has VIEW_AUDIT_LOGS permission return client.RequestManager.get(endpoints.GUILD_AUDIT_LOGS(data.id), { diff --git a/types/guild.ts b/types/guild.ts new file mode 100644 index 000000000..322964dce --- /dev/null +++ b/types/guild.ts @@ -0,0 +1,576 @@ +export interface CreateGuildPayload { + /** The guild id */ + id: string + /** The guild name 2-100 characters */ + name: string + /** The guild icon image hash */ + icon: string | null + /** The guild splash image hash */ + splash: string | null + /** The id of the owner */ + owner_id: string + /** The voice region id for the guild */ + region: string + /** The afk channel id */ + afk_channel_id: string | null + /** AFK Timeout in seconds. */ + afk_timeout: number + /** Whether this guild is embeddable (widget) */ + embed_enabled?: boolean + /** If not null, the channel id that the widge will generate an invite to. */ + embed_channel_id?: string | null + /** The verification level required for the guild */ + verification_level: number + /** The roles in the guild */ + roles: Role[] + /** The custom guild emojis */ + emojis: Emoji[] + /** Enabled guild features */ + features: GuildFeatures[] + /** Required MFA level for the guild */ + mfa_level: number + /** The id of the channel to which system mesages are sent */ + system_channel_id: string | null + /** When this guild was joined at */ + joined_at: string + /** Whether this is considered a large guild */ + large: boolean + /** Whether this guild is unavailable */ + unavailable: boolean + /** Total number of members in this guild */ + member_count: number + voice_states: VoiceState[] + /** Users in the guild */ + members: Member[] + /** Channels in the guild */ + channels: Channel[] + presences: Presence[] + /** The maximum amount of presences for the guild(the default value, currently 5000 is in effect when null is returned.) */ + max_presences?: number | null + /** The maximum amount of members for the guild */ + max_members?: number + /** The vanity url code for the guild */ + vanity_url_code: string | null + /** The description for the guild */ + description: string | null + /** The banner hash */ + banner: string | null + /** The premium tier */ + premium_tier: number + /** The total number of users currently boosting this server. */ + premium_subscription_count: number + /** The preferred local of this guild only set if guild has the DISCOVERABLE feature, defaults to en-US */ + preferred_locale: string +} + +export interface Guild { + /** The guild id */ + id: string + /** The guild name 2-100 characters */ + name: string + /** The guild icon image hash */ + icon: string | null + /** The guild splash image hash */ + splash: string | null + /** The id of the owner */ + owner_id: string + /** The voice region id for the guild */ + region: string + /** The afk channel id */ + afk_channel_id: string | null + /** AFK Timeout in seconds. */ + afk_timeout: number + /** The verification level required for the guild */ + verification_level: number + /** The roles in the guild */ + roles: Map + /** The custom guild emojis */ + emojis: Map + /** Enabled guild features */ + features: GuildFeatures[] + /** Required MFA level for the guild */ + mfa_level: number + /** The id of the channel to which system mesages are sent */ + system_channel_id: string | null + /** When this guild was joined at */ + joined_at: number + /** Whether this is considered a large guild */ + large: boolean + /** Whether this guild is unavailable */ + unavailable: boolean + /** Total number of members in this guild */ + member_count: number + voice_states: Map + /** Users in the guild */ + members: Map + /** Channels in the guild */ + channels: Map + presences: Map + /** The maximum amount of presences for the guild(the default value, currently 5000 is in effect when null is returned.) */ + max_presences?: number | null + /** The maximum amount of members for the guild */ + max_members?: number + /** The vanity url code for the guild */ + vanity_url_code: string | null + /** The description for the guild */ + description: string | null + /** The banner hash */ + banner: string | null + /** The premium tier */ + premium_tier: number + /** The total number of users currently boosting this server. */ + premium_subscription_count: number + /** The preferred local of this guild only set if guild has the DISCOVERABLE feature, defaults to en-US */ + preferred_locale: string + /** The full URL of the icon from Discords CDN. Undefined when no icon is set. */ + icon_url(size?: Image_Size, format?: Image_Formats): string | undefined + /** The full URL of the splash from Discords CDN. Undefined if no splash is set. */ + splash_url(size?: Image_Size, format?: Image_Formats): string | undefined + /** The full URL of the banner from Discords CDN. Undefined if no banner is set. */ + banner_url(size?: Image_Size, format?: Image_Formats): string | undefined + /** Create a channel in your server. Bot needs MANAGE_CHANNEL permissions in the server. */ + create_channel(name: string, options: ChannelCreate_Options): Promise + /** 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.** + */ + get_channels(): Promise + /** Modify the positions of channels on the guild. Requires MANAGE_CHANNELS permisison. */ + swap_channels(channel_positions: Position_Swap[]): Promise + /** 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.** + */ + get_member(id: string): Promise + /** 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. */ + create_emoji(name: string, image: string, options: Create_Emojis_Options): Promise + /** Modify the given emoji. Requires the MANAGE_EMOJIS permission. */ + edit_emoji(id: string, options: Edit_Emojis_Options): Promise + /** Delete the given emoji. Requires the MANAGE_EMOJIS permission. Returns 204 No Content on success. */ + delete_emoji(id: string, reason?: string): Promise + /** Create a new role for the guild. Requires the MANAGE_ROLES permission. */ + create_role(options: Create_Role_Options): Promise + /** Edi a guild role. Requires the MANAGE_ROLES permission. */ + edit_role(id: string, options: Create_Role_Options): Promise + /** Delete a guild role. Requires the MANAGE_ROLES permission. */ + delete_role(id: string): Promise + /** 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.** + */ + get_roles(): Promise + /** Modify the positions of a set of role objects for the guild. Requires the MANAGE_ROLES permission. */ + swap_roles(role_positions: Position_Swap): Promise + /** Check how many members would be removed from the server in a prune operation. Requires the KICK_MEMBERS permission */ + get_prune_count(days: number): Promise + /** Begin pruning all members in the given time period */ + prune_members(days: number): Promise + /** Returns the audit logs for the guild. Requires VIEW AUDIT LOGS permission */ + get_audit_logs(options: Get_audit_logsOptions): Promise + /** Returns the guild embed object. Requires the MANAGE_GUILD permission. */ + get_embed(): Promise + /** Modify a guild embed object for the guild. Requires the MANAGE_GUILD permission. */ + edit_embed(enabled: boolean, channel_id?: string | null): Promise + /** Returns the code and uses of the vanity url for this server if it is enabled. Requires the MANAGE_GUILD permission. */ + get_vanity_url(): Promise + /** Returns a list of integrations for the guild. Requires the MANAGE_GUILD permission. */ + get_integrations(): Promise + /** Modify the behavior and settings of an integration object for the guild. Requires the MANAGE_GUILD permission. */ + edit_integration(id: string, options: Edit_Integration_Options): Promise + /** Delete the attached integration object for the guild with this id. Requires MANAGE_GUILD permission. */ + delete_integration(id: string): Promise + /** Sync an integration. Requires teh MANAGE_GUILD permission. */ + sync_integration(id: string): Promise + /** Returns a list of ban objects for the users banned from this guild. Requires the BAN_MEMBERS permission. */ + get_bans(): Promise + /** 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): Promise + /** Remove the ban for a user. REquires BAN_MEMBERS permission */ + unban(id: string): Promise + /** Modify a guilds settings. Requires the MANAGE_GUILD permission. */ + edit(options: Guild_Edit_Options): Promise + /** Get all the invites for this guild. Requires MANAGE_GUILD permission */ + get_invites(): Promise + /** Leave a guild */ + leave(): Promise + /** 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. */ + get_voice_regions(): Promise + leave_voice_channel(): Promise +} + +export interface Voice_Region { + /** unique ID for the region */ + id: string + /** name of the region */ + name: string + /** true if this is a vip-only server */ + vip: boolean + /** true for a single server that is closest to the current user's client */ + optimal: boolean + /** whether this is a deprecated voice region (avoid switching to these) */ + deprecated: boolean + /** whether this is a custom voice region (used for events/etc) */ + custom: boolean +} + +export interface BanOptions { + /** number of days to delete messages for (0-7) */ + delete_message_days?: 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 + /** The reason for the ban. */ + reason?: string +} + +export interface BannedUser { + /** The reason for the ban */ + reason?: string + /** The banned user object */ + user: User +} + +export interface Position_Swap { + /** The unique id */ + id: string + /** The sorting position number. */ + position: number +} + +export interface Guild_Edit_Options { + /** The guild name */ + name?: string + /** The guild voice region id */ + region?: string + /** The verification level. 0 is UNRESTRICTED. 1 is Verified email. 2 is 5 minutes user. 3 is 10 minutes member in guild. 4 is verified phone number */ + verification_level?: 0 | 1 | 2 | 3 + /** The default message notification level. 0 is ALL_MESSAGES and 1 is ONLY_MENTINS */ + default_message_notifications?: 0 | 1 + /** Explicit content filter level. 0 is DISABLED 1 is members without roles. 2 is all members */ + explicit_content_filter?: 0 | 1 | 2 + /** The id for the afk channel. */ + afk_channel_id?: string + /** The afk timeout in seconds. */ + afk_timeout?: number + /** base64 1024x1024 png/jpeg/gif image for the guild icon (can be animated gif when the server has ANIMATED_ICON feature) */ + icon?: string + /** user id to transfer guild ownership to (must be owner) */ + owner_id?: string + /** base64 16:9 png/jpeg image for the guild splash (when the server has INVITE_SPLASH feature) */ + splash?: string + /** base64 16:9 png/jpeg image for the guild banner (when the server has BANNER feature) */ + banner?: string + /** the id of the channel to which system messages are sent */ + system_channel_id?: string +} + +export interface Edit_Integration_Options { + /** The behavior when an integration subscription lapses. */ + expire_behavior: number + /** The period in seconds where the integration will ignore lapsed subscriptions */ + expire_grace_period: number + /** Whether emoticons should be synced for this integrations (twitch only currently) */ + enable_emoticons: boolean +} + +export interface Guild_Integration { + /** The integrations unique id */ + id: string + /** the integrations name */ + name: string + /** The integration type like twitch, youtube etc */ + type: string + /** Is this integration enabled */ + enabled: boolean + /** is this integration syncing */ + syncing: boolean + /** id that this integration uses for "subscribers" */ + role_id: string + /** The behavior of expiring subscribers */ + expire_behavior: number + /** The grace period before expiring subscribers */ + expire_grace_period: number + /** The user for this integration */ + user: User_Data + /** The integration account information */ + account: Account + /** When this integration was last synced */ + synced_at: string +} + +export interface User_Data { + /** The user's id */ + id: string + /** the user's username, not unique across the platform */ + username: string + /** The user's 4 digit discord tag */ + discriminator: string + /** The user's avatar hash */ + avatar: string | null + /** Whether the user is a bot */ + bot?: boolean + /** Whether the user is an official discord system user (part of the urgent message system.) */ + system?: boolean + /** Whether the user has two factor enabled on their account */ + mfa_enabled?: boolean + /** the user's chosen language option */ + locale?: string + /** Whether the email on this account has been verified */ + verified?: boolean + /** The user's email */ + email?: string + /** The flags on a user's account. */ + flags?: number + /** The type of Nitro subscription on a user's account. */ + premium_type?: number +} + +export enum User_Flags { + NONE, + DISCORD_EMPLOYEE, + DISCORD_PARTNER, + HYPE_SQUAD_EVENTS = 1 << 2, + BUG_HUNTER = 1 << 3, + HOUSE_BRAVERY = 1 << 6, + HOUSE_BRILLIANCE = 1 << 7, + HOUSE_BALANCE = 1 << 8, + EARLY_SUPPORTER = 1 << 9, + TEAM_USER = 1 << 10, + SYSTEM = 1 << 12 +} + +export enum Nitro_Types { + NITRO_CLASSIC = 1, + NITRO +} + +export interface Vanity_Invite { + code: string | null + uses: number +} + +export interface Guild_Embed { + /** Whether the embed is enbaled. */ + enabled: boolean +} + +export interface Get_Audit_Logs_Options { + /** Filter the logs for actions made by this user. */ + user_id?: string + /** The type of audit log. */ + action_type?: AuditLogType + /** Filter the logs before a certain log entry. */ + before?: string + /** How many entries are returned. Between 1-100. Default 50. */ + limit?: number +} + +export type AuditLogType = + | `GUILD_UPDATE` + | `CHANNEL_CREATE` + | `CHANNEL_UPDATE` + | `CHANNEL_DELETE` + | `CHANNEL_OVERWRITE_CREATE` + | `CHANNEL_OVERWRITE_UPDATE` + | `CHANNEL_OVERWRITE_DELETE` + | `MEMBER_KICK` + | `MEMBER_PRUNE` + | `MEMBER_BAN_ADD` + | `MEMBER_BAN_REMOVE` + | `MEMBER_UPDATE` + | `MEMBER_ROLE_UPDATE` + | `MEMBER_MOVE` + | `MEMBER_DISCONNECT` + | `BOT_ADD` + | `ROLE_CREATE` + | `ROLE_UPDATE` + | `ROLE_DELETE` + | `INVITE_CREATE` + | `INVITE_UPDATE` + | `INVITE_DELETE` + | `WEBHOOK_CREATE` + | `WEBHOOK_UPDATE` + | `WEBHOOK_DELETE` + | `EMOJI_CREATE` + | `EMOJI_UPDATE` + | `EMOJI_DELETE` + | `MESSAGE_DELETE` + | `MESSAGE_BULK_DELETE` + | `MESSAGE_PIN` + | `MESSAGE_UNPIN` + | `INTEGRATION_CREATE` + | `INTEGRATION_UPDATE` + | `INTEGRATION_DELETE` + +export enum AuditLogs { + GUILD_UPDATE = 1, + CHANNEL_CREATE = 10, + CHANNEL_UPDATE, + CHANNEL_DELETE, + CHANNEL_OVERWRITE_CREATE, + CHANNEL_OVERWRITE_UPDATE, + CHANNEL_OVERWRITE_DELETE, + MEMBER_KICK = 20, + MEMBER_PRUNE, + MEMBER_BAN_ADD, + MEMBER_BAN_REMOVE, + MEMBER_UPDATE, + MEMBER_ROLE_UPDATE, + MEMBER_MOVE, + MEMBER_DISCONNECT, + BOT_ADD, + ROLE_CREATE = 30, + ROLE_UPDATE, + ROLE_DELETE, + INVITE_CREATE = 40, + INVITE_UPDATE, + INVITE_DELETE, + WEBHOOK_CREATE = 50, + WEBHOOK_UPDATE, + WEBHOOK_DELETE, + EMOJI_CREATE = 60, + EMOJI_UPDATE, + EMOJI_DELETE, + MESSAGE_DELETE = 72, + MESSAGE_BULK_DELETE, + MESSAGE_PIN, + MESSAGE_UNPIN, + INTEGRATION_CREATE = 80, + INTEGRATION_UPDATE, + INTEGRATION_DELETE +} + +export type Image_Size = 16 | 32 | 64 | 128 | 256 | 512 | 1024 | 2048 +export type Image_Formats = 'jpg' | 'jpeg' | 'png' | 'webp' | 'gif' +export type ChannelType = 'text' | 'dm' | 'news' | 'voice' | 'category' | 'store' + +export type Permission = + | `CREATE_INSTANT_INVITE` + | `KICK_MEMBERS` + | `BAN_MEMBERS` + | `ADMINISTRATOR` + | `MANAGE_CHANNELS` + | `MANAGE_GUILD` + | `ADD_REACTIONS` + | `VIEW_AUDIT_LOG` + | `VIEW_CHANNEL` + | `SEND_MESSAGES` + | `SEND_TTS_MESSAGES` + | `MANAGE_MESSAGES` + | `EMBED_LINKS` + | `ATTACH_FILES` + | `READ_MESSAGE_HISTORY` + | `MENTION_EVERYONE` + | `USE_EXTERNAL_EMOJIS` + | `CONNECT` + | `SPEAK` + | `MUTE_MEMBERS` + | `DEAFEN_MEMBERS` + | `MOVE_MEMBERS` + | `USE_VAD` + | `PRIORITY_SPEAKER` + | `STREAM` + | `CHANGE_NICKNAME` + | `MANAGE_NICKNAMES` + | `MANAGE_ROLES` + | `MANAGE_WEBHOOKS` + | `MANAGE_EMOJIS` + +export interface Overwrite { + /** The role or user id */ + id: string + /** Whether this is a role or a member */ + type: 'role' | 'member' + /** The permissions that this id is allowed to do. (This will mark it as a green check.) */ + allow: Permission[] + /** The permissions that this id is NOT allowed to do. (This will mark it as a red x.) */ + deny: Permission[] +} + +export enum ChannelTypes { + text, + dm, + voice, + category = 4, + news, + store +} + +export enum Permissions { + CREATE_INSTANT_INVITE = 0x00000001, + KICK_MEMBERS = 0x00000002, + BAN_MEMBERS = 0x00000004, + ADMINISTRATOR = 0x00000008, + MANAGE_CHANNELS = 0x00000010, + MANAGE_GUILD = 0x00000020, + ADD_REACTIONS = 0x00000040, + VIEW_AUDIT_LOG = 0x00000080, + VIEW_CHANNEL = 0x00000400, + SEND_MESSAGES = 0x00000800, + SEND_TTS_MESSAGES = 0x00001000, + MANAGE_MESSAGES = 0x00002000, + EMBED_LINKS = 0x00004000, + ATTACH_FILES = 0x00008000, + READ_MESSAGE_HISTORY = 0x00010000, + MENTION_EVERYONE = 0x00020000, + USE_EXTERNAL_EMOJIS = 0x00040000, + CONNECT = 0x00100000, + SPEAK = 0x00200000, + MUTE_MEMBERS = 0x00400000, + DEAFEN_MEMBERS = 0x00800000, + MOVE_MEMBERS = 0x01000000, + USE_VAD = 0x02000000, + PRIORITY_SPEAKER = 0x00000100, + STREAM = 0x00000200, + CHANGE_NICKNAME = 0x04000000, + MANAGE_NICKNAMES = 0x08000000, + MANAGE_ROLES = 0x10000000, + MANAGE_WEBHOOKS = 0x20000000, + MANAGE_EMOJIS = 0x40000000 +} + +export interface ChannelCreate_Options { + /** The type of the channel */ + type?: ChannelType + /** The channel topic. (0-1024 characters) */ + topic?: string + /** The bitrate(in bits) of the voice channel. */ + bitrate?: number + /** The user limit of the voice channel. */ + user_limit?: number + /** The amount of seconds a user has to wait before sending another message. (0-21600 seconds). Bots, as well as users with the permission `manage_messages or manage_channel` are unaffected. */ + rate_limit_per_user?: number + /** The sorting position of the channel */ + position?: number + /** The channel's permission overwrites */ + permission_overwrites?: Overwrite[] + /** The id of the parent category for the channel */ + parent_id?: string + /** Whether the channel is nsfw */ + nsfw?: boolean + /** The reason to add in the Audit Logs. */ + reason?: string +} + +export interface Create_Emojis_Options { + /** The roles for which this emoji will be whitelisted. Only the users with one of these roles can use this emoji. */ + roles: string[] + /** The reason to have in the Audit Logs. */ + reason: string +} + +export interface Edit_Emojis_Options { + /** The name of the emoji */ + name: string + /** The roles for which this emoji will be whitelisted. Only the users with one of these roles can use this emoji. */ + roles: string[] +} + +export interface Create_Role_Options { + name?: string + permissions?: Permission[] + color?: number + hoist?: boolean + mentionable?: boolean +} + +export interface PrunePayload { + pruned: number +} From cb4d3cee86d2b9697f8b8434d5c10cef95403a62 Mon Sep 17 00:00:00 2001 From: Skillz Date: Wed, 12 Feb 2020 13:06:54 -0500 Subject: [PATCH 06/18] tiny cleanup --- types/guild.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/types/guild.ts b/types/guild.ts index 322964dce..1ab02b598 100644 --- a/types/guild.ts +++ b/types/guild.ts @@ -166,7 +166,7 @@ export interface Guild { /** Begin pruning all members in the given time period */ prune_members(days: number): Promise /** Returns the audit logs for the guild. Requires VIEW AUDIT LOGS permission */ - get_audit_logs(options: Get_audit_logsOptions): Promise + get_audit_logs(options: Get_Audit_Logs_Options): Promise /** Returns the guild embed object. Requires the MANAGE_GUILD permission. */ get_embed(): Promise /** Modify a guild embed object for the guild. Requires the MANAGE_GUILD permission. */ @@ -194,8 +194,9 @@ export interface Guild { /** Leave a guild */ leave(): Promise /** 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. */ - get_voice_regions(): Promise - leave_voice_channel(): Promise + get_voice_regions(): Promise + /** Returns a list of guild webhooks objects. Requires the MANAGE_WEBHOOKs permission. */ + get_webhooks(): Promise } export interface Voice_Region { From 5fe4e4e82264011383f425e9136005cb3d6ffd6b Mon Sep 17 00:00:00 2001 From: Skillz Date: Wed, 12 Feb 2020 13:37:28 -0500 Subject: [PATCH 07/18] finished role and emojis --- structures/guild.ts | 3 ++- structures/role.ts | 11 +++++++++-- types/emoji.ts | 16 ++++++++++++++++ types/guild.ts | 31 ++++++++++++++++++++++++------- 4 files changed, 51 insertions(+), 10 deletions(-) create mode 100644 types/emoji.ts diff --git a/structures/guild.ts b/structures/guild.ts index f64b93a81..66f78ee4f 100644 --- a/structures/guild.ts +++ b/structures/guild.ts @@ -2,12 +2,13 @@ import Client from '../module/client' import { endpoints } from '../constants/discord' import { formatImageURL } from '../utils/cdn' import { Guild, CreateGuildPayload, ChannelTypes, Permissions, PrunePayload } from '../types/guild' +import { create_role } from './role' export const createGuild = (data: CreateGuildPayload, client: Client) => { const guild: Guild = { ...data, roles: new Map(data.roles.map(r => [r.id, create_role(r)])), - emojis: new Map(data.emojis.map(e => [e.id, create_emoji(e)])), + emojis: new Map(data.emojis.map(e => [e.id, e])), joined_at: Date.parse(data.joined_at), voice_states: new Map(data.voice_states.map(vs => [vs.id, create_voice_state(vs)])), members: new Map(data.members.map(m => [m.id, create_member(m)])), diff --git a/structures/role.ts b/structures/role.ts index e10ad2b2e..4c05dd72a 100644 --- a/structures/role.ts +++ b/structures/role.ts @@ -1,3 +1,10 @@ -export const createRole = (data: unknown) => { - console.log(data) +import { Role, Role_Data } from "../types/role" + +export const create_role = (data: Role_Data) => { + const role: Role = { + ...data, + mention: () => `<@&${data.id}>` + } + + return role } diff --git a/types/emoji.ts b/types/emoji.ts new file mode 100644 index 000000000..9240e7cda --- /dev/null +++ b/types/emoji.ts @@ -0,0 +1,16 @@ +export interface Emoji { + /** emoji id. It will be null for default discord emojis. */ + id: string | null + /** The name of the emoji. (can be null only in reaction emoji objects when the custom emoji doesnt exist anymore) */ + name: string | null + /** array of role ids roles this emoji is whitelisted to */ + roles?: string[] + /** User that created this emoji */ + user?: User + /** whether this emoji must be wrapped in colons */ + require_colons?: boolean + /** whether this emoji is managed */ + managed?: boolean + /** whether this emoji is animated */ + animated?: boolean +} diff --git a/types/guild.ts b/types/guild.ts index 1ab02b598..dbd00bee6 100644 --- a/types/guild.ts +++ b/types/guild.ts @@ -1,3 +1,6 @@ +import { Role } from './role' +import { Emoji } from './discord' + export interface CreateGuildPayload { /** The guild id */ id: string @@ -26,7 +29,7 @@ export interface CreateGuildPayload { /** The custom guild emojis */ emojis: Emoji[] /** Enabled guild features */ - features: GuildFeatures[] + features: Guild_Features[] /** Required MFA level for the guild */ mfa_level: number /** The id of the channel to which system mesages are sent */ @@ -188,17 +191,31 @@ export interface Guild { /** Remove the ban for a user. REquires BAN_MEMBERS permission */ unban(id: string): Promise /** Modify a guilds settings. Requires the MANAGE_GUILD permission. */ - edit(options: Guild_Edit_Options): Promise - /** Get all the invites for this guild. Requires MANAGE_GUILD permission */ - get_invites(): Promise + edit(options: Guild_Edit_Options): Promise + /** Get all the invites for this guild. Requires MANAGE_GUILD permission */ + get_invites(): Promise /** Leave a guild */ leave(): Promise /** 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. */ - get_voice_regions(): Promise - /** Returns a list of guild webhooks objects. Requires the MANAGE_WEBHOOKs permission. */ - get_webhooks(): Promise + get_voice_regions(): Promise + /** Returns a list of guild webhooks objects. Requires the MANAGE_WEBHOOKs permission. */ + get_webhooks(): Promise } +export type Guild_Features = + | `INVITE_SPLASH` + | `VIP_REGIONS` + | `VANITY_URL` + | `VERIFIED` + | `PARTNERED` + | `PUBLIC` + | `COMMERCE` + | `NEWS` + | `DISCOVERABLE` + | `FEATURABLE` + | `ANIMATED_ICON` + | `BANNER` + export interface Voice_Region { /** unique ID for the region */ id: string From 7569499ff639387e8f2901dc5ff6b6d5959da1bc Mon Sep 17 00:00:00 2001 From: Skillz Date: Wed, 12 Feb 2020 13:37:36 -0500 Subject: [PATCH 08/18] role types --- types/role.ts | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 types/role.ts diff --git a/types/role.ts b/types/role.ts new file mode 100644 index 000000000..b9628be43 --- /dev/null +++ b/types/role.ts @@ -0,0 +1,39 @@ +export interface Role_Data { + /** role id */ + id: string + /** role name */ + name: string + /** integer representation of hexadecimal color code */ + color: number + /** if this role is pinned in the user listing */ + hoist: boolean + /** position of this role */ + position: number + /** permission bit set */ + permissions: number + /** whether this role is managed by an integration */ + managed: boolean + /** whether this role is mentionable */ + mentionable: boolean +} + +export interface Role { + /** role id */ + id: string + /** role name */ + name: string + /** integer representation of hexadecimal color code */ + color: number + /** if this role is pinned in the user listing */ + hoist: boolean + /** position of this role */ + position: number + /** permission bit set */ + permissions: number + /** whether this role is managed by an integration */ + managed: boolean + /** whether this role is mentionable */ + mentionable: boolean + /** The @ mention of the role in a string. */ + mention(): string +} From 37bd07a96f3c44ff8c9c4d03e0e4af2ff8e02347 Mon Sep 17 00:00:00 2001 From: Skillz Date: Thu, 13 Feb 2020 12:08:02 -0500 Subject: [PATCH 09/18] p1 of member completed --- constants/discord.ts | 9 ++- structures/guild.ts | 2 +- structures/member.ts | 132 +++++++++++++++++++++++++++++++++++++++---- types/guild.ts | 4 +- 4 files changed, 131 insertions(+), 16 deletions(-) diff --git a/constants/discord.ts b/constants/discord.ts index 32710b16b..32eb1e340 100644 --- a/constants/discord.ts +++ b/constants/discord.ts @@ -6,6 +6,8 @@ export const baseEndpoints = { export const endpoints = { GATEWAY_BOT: `${baseEndpoints.BASE_URL}/gateway/bot`, + + // Guild Endpoints GUILD: (id: string) => `${baseEndpoints.BASE_URL}/guilds/${id}`, GUILD_AUDIT_LOGS: (id: string) => `${baseEndpoints.BASE_URL}/guilds/${id}/audit-logs`, GUILD_BAN: (id: string, user_id: string) => `${baseEndpoints.BASE_URL}/guilds/${id}/bans/${user_id}`, @@ -24,11 +26,16 @@ export const endpoints = { GUILD_INVITES: (id: string) => `${baseEndpoints.BASE_URL}/guilds/${id}/invites`, GUILD_LEAVE: (id: string) => `${baseEndpoints.BASE_URL}/users/@me/guilds/${id}`, GUILD_MEMBER: (id: string, member_id: string) => `${baseEndpoints.BASE_URL}/guilds/${id}/members/${member_id}`, + GUILD_MEMBER_ROLE: (id: string, member_id: string, role_id: string) => `${baseEndpoints.BASE_URL}/guilds/${id}/members/${member_id}/roles/${role_id}`, GUILD_PRUNE: (id: string) => `${baseEndpoints.BASE_URL}/guilds/${id}/prune`, GUILD_REGIONS: (id: string) => `${baseEndpoints.BASE_URL}/guilds/${id}/regions`, GUILD_ROLE: (id: string, role_id: string) => `${baseEndpoints.BASE_URL}/guilds/${id}/roles/${role_id}`, GUILD_ROLES: (id: string) => `${baseEndpoints.BASE_URL}/guilds/${id}/roles`, GUILD_SPLASH: (id: string, icon: string) => `${baseEndpoints.CDN_URL}/splashes/${id}/${icon}`, GUILD_VANITY_URL: (id: string) => `${baseEndpoints.BASE_URL}/guilds/${id}/vanity-url`, - GUILD_WEBHOOKS: (id: string) => `${baseEndpoints.BASE_URL}/guilds/${id}/webhooks` + GUILD_WEBHOOKS: (id: string) => `${baseEndpoints.BASE_URL}/guilds/${id}/webhooks`, + + // User endpoints + USER_AVATAR: (id: string, icon: string) => `${baseEndpoints.CDN_URL}/avatars/${id}/${icon}`, + USER_DEFAULT_AVATAR: (icon: number) => `${baseEndpoints.CDN_URL}/embed/avatars${icon}.png` } diff --git a/structures/guild.ts b/structures/guild.ts index 66f78ee4f..3977d6232 100644 --- a/structures/guild.ts +++ b/structures/guild.ts @@ -8,7 +8,7 @@ export const createGuild = (data: CreateGuildPayload, client: Client) => { const guild: Guild = { ...data, roles: new Map(data.roles.map(r => [r.id, create_role(r)])), - emojis: new Map(data.emojis.map(e => [e.id, e])), + emojis: data.emojis, joined_at: Date.parse(data.joined_at), voice_states: new Map(data.voice_states.map(vs => [vs.id, create_voice_state(vs)])), members: new Map(data.members.map(m => [m.id, create_member(m)])), diff --git a/structures/member.ts b/structures/member.ts index da13d6569..15019c71e 100644 --- a/structures/member.ts +++ b/structures/member.ts @@ -1,5 +1,7 @@ -import Client from "../module/client" -import { endpoints } from "../constants/discord" +import Client from '../module/client' +import { endpoints } from '../constants/discord' +import { Guild, Image_Size, Image_Formats, Permissions, Permission } from '../types/guild' +import { formatImageURL } from '../utils/cdn' export interface Edit_Member_Options { /** Value to set users nickname to. Requires MANAGE_NICKNAMES permission. */ @@ -15,28 +17,134 @@ export interface Edit_Member_Options { } export interface Member { + /** The unique user id */ + id: string + /** The user's guild nickname if one is set. */ + nick(): string | undefined + /** Array of role ids that the member has */ + roles(): string[] + /** When the user joined the guild. */ + joined_at(): number + /** When the user used their nitro boost on the server. */ + premium_since(): number | undefined + /** Whether the user is deafened in voice channels */ + deaf(): boolean + /** Whether the user is muted in voice channels */ + mute(): boolean edit(options: Edit_Member_Options): Promise + /** The username of the this member. */ + username(): string + /** The 4 digit unique identifier */ + discriminator(): string + /** The full username#discriminator */ + tag(): string + /** The users custom avatar or the default avatar */ + avatarURL(size: Image_Size, format: Image_Formats): string + /** The user mention with nickname if possible */ + mention(): string + /** Whether the member is a bot */ + bot(): boolean + /** When the user created their account */ + created_at(): number + /** Add a role to the member */ + addRole(role_id: string, reason: string): Promise + /** Remove a role from the member */ + removeRole(role_id: string, reason: string): Promise + /** Kick a member from the server */ + kick(reason: string): Promise + /** Ban a member from the server */ + ban(delete_message_days?: 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7, reason?: string): Promise + has_permissions(permissions: Permission[]): boolean } -export const createMember = (data: unknown, guild: Guild, client: Client) => { - const Member: Member = { - edit: (options) => { - // TODO: check if has MANAGE_NICKNAME Permission - // TODO: check if it is a valid nickname like 32 characters - options.nick = undefined +export interface Member_Create_Payload { + /** The user this guild member represents */ + user: User + /** The user's guild nickname if one is set. */ + nick?: string + /** Array of role ids that the member has */ + roles: string[] + /** When the user joined the guild. */ + joined_at: string + /** When the user used their nitro boost on the server. */ + premium_since?: string + /** Whether the user is deafened in voice channels */ + deaf: boolean + /** Whether the user is muted in voice channels */ + mute: boolean +} - // TODO: check if has MANAGE_ROLES permission +export const create_member = (data: Member_Create_Payload, guild: Guild, client: Client) => { + const member: Member = { + id: data.user.id, + roles: () => data.roles, + nick: () => data.nick, + joined_at: () => Date.parse(data.joined_at), + premium_since: () => (data.premium_since ? Date.parse(data.premium_since) : undefined), + deaf: () => data.deaf, + mute: () => data.mute, + username: () => data.user.username, + discriminator: () => data.user.discriminator, + tag: () => `${data.user.username}#${data.user.discriminator}`, + game: () => data.game, + status: () => data.status, + client_status: () => data.client_status, + activities: () => data.activites, + avatarURL: (size, format) => + data.user.avatar + ? formatImageURL(endpoints.USER_AVATAR(data.user.id, data.user.avatar), size, format) + : endpoints.USER_DEFAULT_AVATAR(data.user.discriminator % 5), + mention: () => `<@!${data.user.id}>`, + bot: () => data.user.bot, + created_at: () => data.user.created_at, + addRole: (role_id, reason) => { + // TODO: check if the bot has MANAGE_ROLE and its highest role is above this one + return client.RequestManager.put(endpoints.GUILD_MEMBER_ROLE(guild.id, data.user.id, role_id), { reason }) + }, + removeRole: (role_id, reason) => { + // TODO: check if the bot has MANAGE_ROLE permissions and its highest role is above this role + return client.RequestManager.delete(endpoints.GUILD_MEMBER_ROLE(guild.id, data.user.id, role_id), { reason }) + }, + kick: reason => { + // TODO: check if bot has KICK_MEMBER permissions + return client.RequestManager.delete(endpoints.GUILD_MEMBER(guild.id, data.user.id), { reason }) + }, + ban: (delete_message_days = 0, reason) => { + return guild.ban(data.user.id, { delete_message_days, reason }) + }, + has_permissions: permissions => { + if (data.user.id === guild.owner_id) return true + + const permissionBits = data.roles.reduce((bits, role_id) => { + const role = guild.roles.get(role_id) + if (!role) return bits + + bits |= role.permissions + + return bits + }, 0) + + return permissions.every(permission => permissionBits & Permissions[permission]) + }, + edit: options => { + // TODO: check if has MANAGE_NICKNAME Permission + // TODO: check if it is a valid nickname like 32 characters + options.nick = undefined + + // TODO: check if has MANAGE_ROLES permission options.roles = undefined // TODO: This should check if the member is in a voice channel - // TODO: CHeck if has MUTE_MEMBERS permission + // TODO: CHeck if has MUTE_MEMBERS permission options.mute = undefined - // TODO: check if has DEAFEN_MEMBERS permission + // TODO: check if has DEAFEN_MEMBERS permission options.deaf = undefined // TODO: if channel id is provided check if the bot has CONNECT and MOVE in channel and current channel options.channel_id = undefined - return client.RequestManager.patch(endpoints.GUILD_MEMBER(guild.id, data.id), options), + return client.RequestManager.patch(endpoints.GUILD_MEMBER(guild.id, data.id), options) } } + + return member } diff --git a/types/guild.ts b/types/guild.ts index dbd00bee6..fdc08b2fa 100644 --- a/types/guild.ts +++ b/types/guild.ts @@ -88,9 +88,9 @@ export interface Guild { /** The roles in the guild */ roles: Map /** The custom guild emojis */ - emojis: Map + emojis: Emoji[] /** Enabled guild features */ - features: GuildFeatures[] + features: Guild_Features[] /** Required MFA level for the guild */ mfa_level: number /** The id of the channel to which system mesages are sent */ From 9696a0c4ac54821f9c1f0714d25157d5a33b130d Mon Sep 17 00:00:00 2001 From: Skillz Date: Thu, 13 Feb 2020 12:08:14 -0500 Subject: [PATCH 10/18] small fix --- types/guild.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/types/guild.ts b/types/guild.ts index fdc08b2fa..dddfb56f3 100644 --- a/types/guild.ts +++ b/types/guild.ts @@ -1,5 +1,6 @@ import { Role } from './role' import { Emoji } from './discord' +import { Member } from '../structures/member' export interface CreateGuildPayload { /** The guild id */ From be906d6f572c9c2f74a87910c82e28d446cdcd28 Mon Sep 17 00:00:00 2001 From: Skillz Date: Fri, 14 Feb 2020 14:05:05 -0500 Subject: [PATCH 11/18] channels p1 --- constants/discord.ts | 4 ++ structures/channel.ts | 126 +++++++++++++++++++++++++++++++++++++++++- structures/user.ts | 2 +- types/channel.ts | 51 +++++++++++++++++ types/guild.ts | 11 ++++ 5 files changed, 191 insertions(+), 3 deletions(-) create mode 100644 types/channel.ts diff --git a/constants/discord.ts b/constants/discord.ts index 32eb1e340..9977bdf1d 100644 --- a/constants/discord.ts +++ b/constants/discord.ts @@ -7,6 +7,10 @@ export const baseEndpoints = { export const endpoints = { GATEWAY_BOT: `${baseEndpoints.BASE_URL}/gateway/bot`, + // Channel Endpoints + CHANNEL_MESSAGE: (id: string, message_id: string) => `${baseEndpoints.BASE_URL}/channels/${id}/messages/${message_id}`, + CHANNEL_MESSAGES: (id: string) => `${baseEndpoints.BASE_URL}/channels/${id}` + // Guild Endpoints GUILD: (id: string) => `${baseEndpoints.BASE_URL}/guilds/${id}`, GUILD_AUDIT_LOGS: (id: string) => `${baseEndpoints.BASE_URL}/guilds/${id}/audit-logs`, diff --git a/structures/channel.ts b/structures/channel.ts index 696770aba..811807a7d 100644 --- a/structures/channel.ts +++ b/structures/channel.ts @@ -1,3 +1,125 @@ -export const createChannel = (data: unknown) => { - console.log(data) +import { Channel_Create_Options, Channel_Types } from '../types/channel' +import { Guild, Permission, Permissions } from '../types/guild' +import Client from '../module/client' +import { endpoints } from '../constants/discord' + +export interface MessageContent { + /** The message contents, up to 2000 characters */ + content?: string + /** A nonce that can be used for optimistic message sending. */ + nonce?: number | string + /** Whether this is a TextToSpeech message */ + tts?: boolean + /** The contents of the file being sent */ + file?: File_Content + /** Embed object */ + embed?: Embed_Object + /** JSON encoded body of any additional request fields. */ + payload_json?: string +} + +export const create_channel = (data: Channel_Create_Options, guild: Guild, client: Client) => { + const base_channel = { + id: data.id, + type: () => data.type, + guild_id: () => data.guild_id + } + + const base_text_channel = { + messages: new Map(), + last_message_id: () => data.last_message_id, + get_message: async (id: string) => { + // TODO: check if the user has VIEW_CHANNEL and READ_MESSAGE_HISTORY + const result = await client.RequestManager.get(endpoints.CHANNEL_MESSAGE(data.id, id)) + return create_message(result, client) + }, + get_messages: async (options?: Get_Messages_After | Get_Messages_Before | Get_Messages_Around | Get_Messages) => { + // TODO: check if the user has VIEW_CHANNEL and READ_MESSAGE_HISTORY + if (!options.limit || options.limit <= 100) { + const result = await client.RequestManager.get(endpoints.CHANNEL_MESSAGES(data.id), options) + return result.map(res => create_message(res, client)) + } + + const fetch_messages = () => {} + const + return client.RequestManager.get(endpoints.CHANNEL_MESSAGES(data.id), options) + }, + send_message: async (content: string | MessageContent) => { + if (data.type !== Channel_Types.DM) { + // TODO: check if the bot has SEND_MESSAGES permission + } + + if (typeof content === 'string') content = { content } + if (content.tts) { + // TODO: check if the bot has SEND_TTS_MESSAGE + } + + const result = await client.RequestManager.post(endpoints.CHANNEL_MESSAGES(data.id), content) + return create_message(result, client) + } + } + + // If it is a dm channel + if (data.type === Channel_Types.DM) + return { + ...base_channel, + ...base_text_channel + } + + // GUILD CHANNEL ONLY + const base_guild_channel = { + ...base_channel, + nsfw: () => data.nsfw!, + position: () => data.position!, + parent_id: () => data.parent_id, + // TODO: fix this from being number on allow and deny to being array of strings + permission_overwrites: () => data.permission_overwrites, + has_permissions: (id: string, permissions: Permission[]) => { + if (id === guild.owner_id) return true + + const member = guild.members.get(id) + if (!member) + throw 'Invalid member id provided. This member was not found in the cache. Please fetch them with getMember on guild.' + + let permissionBits = member.roles().reduce((bits, role_id) => { + const role = guild.roles.get(role_id) + if (!role) return bits + + bits |= role.permissions + + return bits + }, 0) + + data.permission_overwrites?.forEach(overwrite => { + permissionBits = (permissionBits & ~overwrite.deny) | overwrite.allow + }) + + return permissions.every(permission => permissionBits & Permissions[permission]) + } + } + + // Guild Text Channel + if ([Channel_Types.GUILD_TEXT, Channel_Types.GUILD_NEWS].includes(data.type)) + return { + ...base_guild_channel, + ...base_text_channel, + mention: () => `<#${data.id}>` + } + + if (data.type === Channel_Types.GUILD_CATEGORY) + return { + ...base_guild_channel, + children_ids: () => + Object.keys(guild.channels).filter(channel => guild.channels.get(channel).parent_id === data.id) + } + + if (data.type === Channel_Types.GUILD_VOICE) + return { + ...base_guild_channel + } + + return { + ...data, + mention: () => `<#${data.id}>` + } } diff --git a/structures/user.ts b/structures/user.ts index 3231c3333..f9d884fe8 100644 --- a/structures/user.ts +++ b/structures/user.ts @@ -31,4 +31,4 @@ export interface UserPayload { export const enum PremiumType { NitroClassic = 1, Nitro -} \ No newline at end of file +} diff --git a/types/channel.ts b/types/channel.ts new file mode 100644 index 000000000..96ff23b04 --- /dev/null +++ b/types/channel.ts @@ -0,0 +1,51 @@ +import { Raw_Overwrite } from './guild' + +export interface Channel_Create_Options { + /** The id of this channel */ + id: string + /** The type of the channel */ + type: Channel_Type + /** The id of the guild */ + guild_id?: string + /** Sorting position of the channel */ + position?: number + /** Explicit permission overwrites for members and roles */ + permission_overwrites?: Raw_Overwrite[] + /** The name of the channel (2-100 characters) */ + name?: string + /** The channel topic (0-1024 characters) */ + topic?: string + /** Whether the channel is nsfw */ + nsfw?: boolean + /** The id of the last message sent in this channel (may not point to an existing or valid message) */ + last_message_id?: string | null + /** The bitrate (in bits) of the voice channel */ + bitrate?: number + /** The user limit of the voice channel */ + user_limit?: number + /** Amount of seconds a user has to wait before sending another message (0-21600) Bots and users with the permission MANAGE_MESSAGES or MANAGE_CHANNEL are unaffected. */ + rate_limit_per_user?: number + /** The parent category id */ + parent_id?: string | null + /** When the last pinned message was pinned */ + last_pin_timestamp?: string +} + +export type Channel_Type = 0 | 1 | 2 | 4 | 5 | 6 + +export enum Channel_Types { + /** A text channel within a server */ + GUILD_TEXT, + /** A direct message between users */ + DM, + /** A voice channel within a server */ + GUILD_VOICE, + /** A direct message between multiple users. */ + GROUP_DM, + /** An organizational category that contains channels */ + GUILD_CATEGORY, + /** A channel that users can follow and crosspost into their own server. */ + GUILD_NEWS, + /** A channel in which game developers can sell their game on Discord. */ + GUILD_STORE +} diff --git a/types/guild.ts b/types/guild.ts index dddfb56f3..7f45b3bb0 100644 --- a/types/guild.ts +++ b/types/guild.ts @@ -503,6 +503,17 @@ export interface Overwrite { deny: Permission[] } +export interface Raw_Overwrite { + /** The role or user id */ + id: string + /** Whether this is a role or a member */ + type: 'role' | 'member' + /** The permissions that this id is allowed to do. (This will mark it as a green check.) */ + allow: number + /** The permissions that this id is NOT allowed to do. (This will mark it as a red x.) */ + deny: number +} + export enum ChannelTypes { text, dm, From 45dfc137709bf704aff15f19f494046d3e2ece1e Mon Sep 17 00:00:00 2001 From: Skillz Date: Sun, 16 Feb 2020 14:15:48 -0500 Subject: [PATCH 12/18] finalizing channels structure --- constants/discord.ts | 56 +++++++++++++---------- structures/channel.ts | 103 ++++++++++++++++++++++++++++++++++++++---- 2 files changed, 125 insertions(+), 34 deletions(-) diff --git a/constants/discord.ts b/constants/discord.ts index 9977bdf1d..7e8d91fa1 100644 --- a/constants/discord.ts +++ b/constants/discord.ts @@ -4,40 +4,46 @@ export const baseEndpoints = { CDN_URL: 'https://cdn.discordapp.com' } +const GUILDS_BASE = (id: string) => `${baseEndpoints.BASE_URL}/guilds/${id}` + export const endpoints = { GATEWAY_BOT: `${baseEndpoints.BASE_URL}/gateway/bot`, // Channel Endpoints - CHANNEL_MESSAGE: (id: string, message_id: string) => `${baseEndpoints.BASE_URL}/channels/${id}/messages/${message_id}`, - CHANNEL_MESSAGES: (id: string) => `${baseEndpoints.BASE_URL}/channels/${id}` + CHANNEL_MESSAGE: (id: string, message_id: string) => + `${baseEndpoints.BASE_URL}/channels/${id}/messages/${message_id}`, + CHANNEL_MESSAGES: (id: string) => `${baseEndpoints.BASE_URL}/channels/${id}`, + CHANNEL_PINS: (id: string) => `${baseEndpoints.BASE_URL}/channels/${id}/pins`, + CHANNEL_BULK_DELETE: (id: string) => `${baseEndpoints.BASE_URL}/channels/${id}/messages/bulk-delete`, + CHANNEL_INVITES: (id: string) => `${baseEndpoints.BASE_URL}/channels/${id}/invites`, + CHANNEL_WEBHOOKS: (id: string) => `${baseEndpoints.BASE_URL}/channels/${id}/webhooks`, // Guild Endpoints - GUILD: (id: string) => `${baseEndpoints.BASE_URL}/guilds/${id}`, - GUILD_AUDIT_LOGS: (id: string) => `${baseEndpoints.BASE_URL}/guilds/${id}/audit-logs`, - GUILD_BAN: (id: string, user_id: string) => `${baseEndpoints.BASE_URL}/guilds/${id}/bans/${user_id}`, - GUILD_BANS: (id: string) => `${baseEndpoints.BASE_URL}/guilds/${id}/bans`, + GUILD: (id: string) => `${GUILDS_BASE(id)}`, + GUILD_AUDIT_LOGS: (id: string) => `${GUILDS_BASE(id)}/audit-logs`, + GUILD_BAN: (id: string, user_id: string) => `${GUILDS_BASE(id)}/bans/${user_id}`, + GUILD_BANS: (id: string) => `${GUILDS_BASE(id)}/bans`, GUILD_BANNER: (id: string, icon: string) => `${baseEndpoints.CDN_URL}/banners/${id}/${icon}`, - GUILD_CHANNELS: (id: string) => `${baseEndpoints.BASE_URL}/guilds/${id}/channels`, - GUILD_EMBED: (id: string) => `${baseEndpoints.BASE_URL}/guilds/${id}/embed`, - GUILD_EMOJI: (id: string, emoji_id: string) => `${baseEndpoints.BASE_URL}/guilds/${id}/emojis/${emoji_id}`, - GUILD_EMOJIS: (id: string) => `${baseEndpoints.BASE_URL}/guilds/${id}/emojis`, + GUILD_CHANNELS: (id: string) => `${GUILDS_BASE(id)}/channels`, + GUILD_EMBED: (id: string) => `${GUILDS_BASE(id)}/embed`, + GUILD_EMOJI: (id: string, emoji_id: string) => `${GUILDS_BASE(id)}/emojis/${emoji_id}`, + GUILD_EMOJIS: (id: string) => `${GUILDS_BASE(id)}/emojis`, GUILD_ICON: (id: string, icon: string) => `${baseEndpoints.CDN_URL}/icons/${id}/${icon}`, - GUILD_INTEGRATION: (id: string, integration_id: string) => - `${baseEndpoints.BASE_URL}/guilds/${id}/integrations/${integration_id}`, - GUILD_INTEGRATION_SYNC: (id: string, integration_id: string) => - `${baseEndpoints.BASE_URL}/guilds/${id}/integrations/${integration_id}/sync`, - GUILD_INTEGRATIONS: (id: string) => `${baseEndpoints.BASE_URL}/guilds/${id}/integrations`, - GUILD_INVITES: (id: string) => `${baseEndpoints.BASE_URL}/guilds/${id}/invites`, - GUILD_LEAVE: (id: string) => `${baseEndpoints.BASE_URL}/users/@me/guilds/${id}`, - GUILD_MEMBER: (id: string, member_id: string) => `${baseEndpoints.BASE_URL}/guilds/${id}/members/${member_id}`, - GUILD_MEMBER_ROLE: (id: string, member_id: string, role_id: string) => `${baseEndpoints.BASE_URL}/guilds/${id}/members/${member_id}/roles/${role_id}`, - GUILD_PRUNE: (id: string) => `${baseEndpoints.BASE_URL}/guilds/${id}/prune`, - GUILD_REGIONS: (id: string) => `${baseEndpoints.BASE_URL}/guilds/${id}/regions`, - GUILD_ROLE: (id: string, role_id: string) => `${baseEndpoints.BASE_URL}/guilds/${id}/roles/${role_id}`, - GUILD_ROLES: (id: string) => `${baseEndpoints.BASE_URL}/guilds/${id}/roles`, + GUILD_INTEGRATION: (id: string, integration_id: string) => `${GUILDS_BASE(id)}/integrations/${integration_id}`, + GUILD_INTEGRATION_SYNC: (id: string, integration_id: string) => `${GUILDS_BASE(id)}/integrations/${integration_id}/sync`, + GUILD_INTEGRATIONS: (id: string) => `${GUILDS_BASE(id)}/integrations`, + GUILD_INVITES: (id: string) => `${GUILDS_BASE(id)}/invites`, + GUILD_LEAVE: (id: string) => `${baseEndpoints.BASE_URL}/users/@me/guilds/${id}`, + GUILD_MEMBER: (id: string, member_id: string) => `${GUILDS_BASE(id)}/members/${member_id}`, + GUILD_MEMBER_ROLE: (id: string, member_id: string, role_id: string) => + `${GUILDS_BASE(id)}/members/${member_id}/roles/${role_id}`, + GUILD_PRUNE: (id: string) => `${GUILDS_BASE(id)}/prune`, + GUILD_REGIONS: (id: string) => `${GUILDS_BASE(id)}/regions`, + GUILD_ROLE: (id: string, role_id: string) => `${GUILDS_BASE(id)}/roles/${role_id}`, + GUILD_ROLES: (id: string) => `${GUILDS_BASE(id)}/roles`, GUILD_SPLASH: (id: string, icon: string) => `${baseEndpoints.CDN_URL}/splashes/${id}/${icon}`, - GUILD_VANITY_URL: (id: string) => `${baseEndpoints.BASE_URL}/guilds/${id}/vanity-url`, - GUILD_WEBHOOKS: (id: string) => `${baseEndpoints.BASE_URL}/guilds/${id}/webhooks`, + GUILD_VANITY_URL: (id: string) => `${GUILDS_BASE(id)}/vanity-url`, + GUILD_WEBHOOKS: (id: string) => `${GUILDS_BASE(id)}/webhooks`, // User endpoints USER_AVATAR: (id: string, icon: string) => `${baseEndpoints.CDN_URL}/avatars/${id}/${icon}`, diff --git a/structures/channel.ts b/structures/channel.ts index 811807a7d..a34de2c87 100644 --- a/structures/channel.ts +++ b/structures/channel.ts @@ -18,32 +18,72 @@ export interface MessageContent { payload_json?: string } +export interface Get_Messages { + /** Max number of messages to return(1-100). Defaults to 50. */ + limit?: number +} + +export interface Get_Messages_After extends Get_Messages { + /** Get messages after this message id */ + after: string +} + +export interface Get_Messages_Before extends Get_Messages { + /** Get messages before this message id */ + before: string +} + +export interface Get_Messages_Around extends Get_Messages { + /** Get messages around this message id. */ + around: string +} + +export interface Create_Invite_Options { + /** Duration of invite in seconds before expiry, or 0 for never. Defaults to 86400 (24 hours) */ + max_age: number + /** Max number of uses or 0 for unlimited. Default 0 */ + max_uses: number + /** Whether this invite only grants temporary membership. */ + temporary: boolean + /** If true, don't try to reuse a similar invite (useful for creating many unique one time use invites.) */ + unique: boolean +} + export const create_channel = (data: Channel_Create_Options, guild: Guild, client: Client) => { const base_channel = { + /** The unique id of the channel */ id: data.id, + /** The type of the channel. */ type: () => data.type, + /** The id of the guild where this channel exists */ guild_id: () => data.guild_id } const base_text_channel = { + /** A short collection of recently sent messages since bot started. */ messages: new Map(), + /** The last message id in this channel */ last_message_id: () => data.last_message_id, + /** Fetch a single message from the server. Requires VIEW_CHANNEL and READ_MESSAGE_HISTORY */ get_message: async (id: string) => { // TODO: check if the user has VIEW_CHANNEL and READ_MESSAGE_HISTORY const result = await client.RequestManager.get(endpoints.CHANNEL_MESSAGE(data.id, id)) return create_message(result, client) }, + /** Fetches between 2-100 messages. Requires VIEW_CHANNEL and READ_MESSAGE_HISTORY */ get_messages: async (options?: Get_Messages_After | Get_Messages_Before | Get_Messages_Around | Get_Messages) => { // TODO: check if the user has VIEW_CHANNEL and READ_MESSAGE_HISTORY - if (!options.limit || options.limit <= 100) { - const result = await client.RequestManager.get(endpoints.CHANNEL_MESSAGES(data.id), options) - return result.map(res => create_message(res, client)) - } + if (options?.limit && options.limit > 100) return - const fetch_messages = () => {} - const - return client.RequestManager.get(endpoints.CHANNEL_MESSAGES(data.id), options) + const result = await client.RequestManager.get(endpoints.CHANNEL_MESSAGES(data.id), options) + return result.map(res => create_message(res, client)) }, + /** Get pinned messages in this channel. */ + get_pins: async () => { + const result = await client.RequestManager.get(endpoints.CHANNEL_PINS(data.id)) + return result.map(res => create_message(res, client)) + }, + /** Send a message to the channel. Requires SEND_MESSAGES permission. */ send_message: async (content: string | MessageContent) => { if (data.type !== Channel_Types.DM) { // TODO: check if the bot has SEND_MESSAGES permission @@ -54,6 +94,8 @@ export const create_channel = (data: Channel_Create_Options, guild: Guild, clien // TODO: check if the bot has SEND_TTS_MESSAGE } + // TODO: Check content length + const result = await client.RequestManager.post(endpoints.CHANNEL_MESSAGES(data.id), content) return create_message(result, client) } @@ -69,11 +111,16 @@ export const create_channel = (data: Channel_Create_Options, guild: Guild, clien // GUILD CHANNEL ONLY const base_guild_channel = { ...base_channel, + /** Whether this channel NSFW enabled. */ nsfw: () => data.nsfw!, + /** The position of the channel in the server. */ position: () => data.position!, + /** The category id for this channel. */ parent_id: () => data.parent_id, // TODO: fix this from being number on allow and deny to being array of strings + /** Fetch the permission overwrites */ permission_overwrites: () => data.permission_overwrites, + /** Check whether a member has certain permissions in this channel. */ has_permissions: (id: string, permissions: Permission[]) => { if (id === guild.owner_id) return true @@ -103,23 +150,61 @@ export const create_channel = (data: Channel_Create_Options, guild: Guild, clien return { ...base_guild_channel, ...base_text_channel, - mention: () => `<#${data.id}>` + /** The topic of the channel */ + topic: () => data.topic, + /** The mention of the channel */ + mention: () => `<#${data.id}>`, + /** Delete messages from the channel. 2-100. Requires the MANAGE_MESSAGES permission */ + delete_messages: (ids: string[], reason?: string) => { + // TODO: Requires the MANAGE_MESSAGES permission. + if (ids.length < 2) throw 'This endpoint will only accept 2-100 message ids.' + if (ids.length > 100) + console.warn( + `This endpoint only accepts a maximum of 100 messages. Deleting the first 100 message ids provided.` + ) + return client.RequestManager.POST(endpoints.CHANNEL_BULK_DELETE(data.id), { + messages: ids.splice(0, 100), + reason + }) + }, + /** Gets the invites for this channel. Requires MANAGE_CHANNEL */ + get_invites: () => { + // TODO: Requires the MANAGE_CHANNELS permission + return client.RequestManager.get(endpoints.CHANNEL_INVITES(data.id)) + }, + /** Creates a new invite for this channel. Requires CREATE_INSTANT_INVITE */ + create_invite: (options: Create_Invite_Options) => { + // TODO: Requires CREATE_INSTANT_INVITE permissin. + return client.RequestManager.post(endpoints.CHANNEL_INVITES(data.id), options) + }, + /** Gets the webhooks for this channel. Requires MANAGE_WEBHOOKS */ + get_webhooks: () => { + // TODO: Requires MANAGE_WEBHOOKS + return client.RequestManager.get(endpoints.CHANNEL_WEBHOOKS(data.id)) + } } if (data.type === Channel_Types.GUILD_CATEGORY) return { ...base_guild_channel, + /** Gets an array of all the channels ids that are the children of this category. */ children_ids: () => Object.keys(guild.channels).filter(channel => guild.channels.get(channel).parent_id === data.id) } if (data.type === Channel_Types.GUILD_VOICE) return { - ...base_guild_channel + ...base_guild_channel, + // TODO: after learning opus and stuff + /** Join a voice channel. */ + join: () => {}, + /** Leave a voice channel */ + leave: () => {} } return { ...data, + /** The channel mention */ mention: () => `<#${data.id}>` } } From 399bad1e78a8f198899d57623e3b3646dbbe23df Mon Sep 17 00:00:00 2001 From: Skillz Date: Tue, 18 Feb 2020 10:47:07 -0500 Subject: [PATCH 13/18] more work on structures --- structures/channel.ts | 48 +------- structures/guild.ts | 4 +- structures/message.ts | 30 +++++ types/channel.ts | 46 ++++++++ types/message.ts | 267 ++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 347 insertions(+), 48 deletions(-) create mode 100644 structures/message.ts create mode 100644 types/message.ts diff --git a/structures/channel.ts b/structures/channel.ts index a34de2c87..91efc333c 100644 --- a/structures/channel.ts +++ b/structures/channel.ts @@ -1,54 +1,8 @@ -import { Channel_Create_Options, Channel_Types } from '../types/channel' +import { Channel_Create_Options, Channel_Types, Get_Messages_After, Get_Messages_Around, Get_Messages, Get_Messages_Before, MessageContent, Create_Invite_Options } from '../types/channel' import { Guild, Permission, Permissions } from '../types/guild' import Client from '../module/client' import { endpoints } from '../constants/discord' -export interface MessageContent { - /** The message contents, up to 2000 characters */ - content?: string - /** A nonce that can be used for optimistic message sending. */ - nonce?: number | string - /** Whether this is a TextToSpeech message */ - tts?: boolean - /** The contents of the file being sent */ - file?: File_Content - /** Embed object */ - embed?: Embed_Object - /** JSON encoded body of any additional request fields. */ - payload_json?: string -} - -export interface Get_Messages { - /** Max number of messages to return(1-100). Defaults to 50. */ - limit?: number -} - -export interface Get_Messages_After extends Get_Messages { - /** Get messages after this message id */ - after: string -} - -export interface Get_Messages_Before extends Get_Messages { - /** Get messages before this message id */ - before: string -} - -export interface Get_Messages_Around extends Get_Messages { - /** Get messages around this message id. */ - around: string -} - -export interface Create_Invite_Options { - /** Duration of invite in seconds before expiry, or 0 for never. Defaults to 86400 (24 hours) */ - max_age: number - /** Max number of uses or 0 for unlimited. Default 0 */ - max_uses: number - /** Whether this invite only grants temporary membership. */ - temporary: boolean - /** If true, don't try to reuse a similar invite (useful for creating many unique one time use invites.) */ - unique: boolean -} - export const create_channel = (data: Channel_Create_Options, guild: Guild, client: Client) => { const base_channel = { /** The unique id of the channel */ diff --git a/structures/guild.ts b/structures/guild.ts index 3977d6232..003241d60 100644 --- a/structures/guild.ts +++ b/structures/guild.ts @@ -3,6 +3,8 @@ import { endpoints } from '../constants/discord' import { formatImageURL } from '../utils/cdn' import { Guild, CreateGuildPayload, ChannelTypes, Permissions, PrunePayload } from '../types/guild' import { create_role } from './role' +import { create_member } from './member' +import { create_channel } from './channel' export const createGuild = (data: CreateGuildPayload, client: Client) => { const guild: Guild = { @@ -12,7 +14,7 @@ export const createGuild = (data: CreateGuildPayload, client: Client) => { joined_at: Date.parse(data.joined_at), voice_states: new Map(data.voice_states.map(vs => [vs.id, create_voice_state(vs)])), members: new Map(data.members.map(m => [m.id, create_member(m)])), - channels: new Map(data.channels.map(c => [c.id, create_channel(c)])), + channels: new Map(data.channels.map(c => [c.id, create_channel(c, data, client)])), presences: new Map(data.presences.map(p => [p.id, create_presence(p)])), icon_url: (size, format) => data.icon ? formatImageURL(endpoints.GUILD_ICON(data.id, data.icon), size, format) : undefined, diff --git a/structures/message.ts b/structures/message.ts new file mode 100644 index 000000000..612f623a4 --- /dev/null +++ b/structures/message.ts @@ -0,0 +1,30 @@ +import Client from '../module/client' +import { Message_Create_Options } from '../types/message' + +export const create_message = (data: Message_Create_Options, client: Client) => { + const base_message = { + type: () => data.type, + timestamp: () => Date.parse(data.timestamp), + content: () => data.content, + reactions: () => data.reactions || [], + guild_id: () => data.guild_id, + webhook_id: () => data.webhook_id, + message_reference: () => ({ + channel_id: data.message_reference?.channel_id, + guild_id: data.message_reference?.guild_id, + message_id: data.message_reference?.message_id, + }), + flags: () => data.flags || 0, + channel_id: () => data.channel_id + } + + if (!data.guild_id) { + return { + ...base_message + } + } + + return { + + } +} diff --git a/types/channel.ts b/types/channel.ts index 96ff23b04..488955c29 100644 --- a/types/channel.ts +++ b/types/channel.ts @@ -49,3 +49,49 @@ export enum Channel_Types { /** A channel in which game developers can sell their game on Discord. */ GUILD_STORE } + +export interface MessageContent { + /** The message contents, up to 2000 characters */ + content?: string + /** A nonce that can be used for optimistic message sending. */ + nonce?: number | string + /** Whether this is a TextToSpeech message */ + tts?: boolean + /** The contents of the file being sent */ + file?: File_Content + /** Embed object */ + embed?: Embed_Object + /** JSON encoded body of any additional request fields. */ + payload_json?: string +} + +export interface Get_Messages { + /** Max number of messages to return(1-100). Defaults to 50. */ + limit?: number +} + +export interface Get_Messages_After extends Get_Messages { + /** Get messages after this message id */ + after: string +} + +export interface Get_Messages_Before extends Get_Messages { + /** Get messages before this message id */ + before: string +} + +export interface Get_Messages_Around extends Get_Messages { + /** Get messages around this message id. */ + around: string +} + +export interface Create_Invite_Options { + /** Duration of invite in seconds before expiry, or 0 for never. Defaults to 86400 (24 hours) */ + max_age: number + /** Max number of uses or 0 for unlimited. Default 0 */ + max_uses: number + /** Whether this invite only grants temporary membership. */ + temporary: boolean + /** If true, don't try to reuse a similar invite (useful for creating many unique one time use invites.) */ + unique: boolean +} diff --git a/types/message.ts b/types/message.ts new file mode 100644 index 000000000..099a7a95a --- /dev/null +++ b/types/message.ts @@ -0,0 +1,267 @@ +import { Member } from '../structures/member' +import { ChannelType } from './guild' + +export interface MentionedUser extends User { + member: Member +} + +export interface Mentioned_Channel { + /** The id of the channel */ + id: string + /** The id of the guild containing the channel */ + guild_id: string + /** The type of the channel. */ + type: ChannelType + /** The name of the channel. */ + name: string +} + +export interface Attachment { + /** Attachment id */ + id: string + /** The name of the file attached */ + filename: string + /** The size of file in bytes */ + size: number + /** Source url of file */ + url: string + /** A proxied url of file */ + proxy_url: string + /** The height of file if an image */ + height: number | null + /** The width of the file if an image */ + width: number | null +} + +export interface Embed { + /** The title of the embed */ + title?: string + /** The type of embed (always rich for webhook embeds) */ + type?: string + /** The description of embeds */ + description?: string + /** The url of embed */ + url?: string + /** The timestap of the embed content */ + timestamp?: string + /** The color code of the embed */ + color?: number + /** The footer information */ + footer?: Embed_Footer + /** The image information */ + image?: Embed_Image + /** The thumbnail information */ + thumbnail?: Embed_Thumbnail + /** The video information */ + video?: Embed_Video + /** Provider information */ + provider?: Embed_Provider + /** Author information */ + author?: Embed_Author + /** Fields information */ + fields?: Embed_Field[] +} + +export interface Embed_Footer { + /** The text of the footer */ + text: string + /** The url of the footer icon. Only supports http(s) and attachments */ + icon_url?: string + /** A proxied url of footer icon */ + proxy_icon_url?: string +} + +export interface Embed_Image { + /** The source url of image (only supports http(s) and attachments) */ + url?: string + /** A proxied url of the image */ + proxy_url?: string + /** The height of image */ + height?: number + /** The width of the image */ + width?: number +} + +export interface Embed_Thumbnail { + /** The source url of image (only supports http(s) and attachments) */ + url?: string + /** A proxied url of the thumbnail */ + proxy_url?: string + /** The height of the thumbnail */ + height?: number + /** The width of the thumbnail */ + width?: number +} + +export interface Embed_Video { + /** The source url of video */ + url?: string + /** The height of the video */ + height?: number + /** The width of the video */ + width?: number +} + +export interface Embed_Provider { + /** The name of the provider */ + name?: string + /** The url of the provider */ + url?: string +} + +export interface Embed_Author { + /** The name of the author */ + name?: string + /** The url of the author */ + url?: string + /** The url of the author icon (supports http(s) and attachments) */ + icon_url?: string + /** A proxied url of author icon */ + proxy_icon_url?: string +} + +export interface Embed_Field { + /** The name of the field */ + name: string + /** The value of the field */ + value: string + /** Whether or not this field should display inline */ + inline?: boolean +} + +export interface Reaction { + /** The times this emoji has been used to react */ + count: number + /** Whether the current user reacted using this emoji */ + me: boolean + /** The emoji information. Can be partial. */ + emoji: Emoji +} + +export enum Message_Types { + DEFAULT, + RECIPIENT_ADD, + RECIPIENT_REMOVE, + CALL, + CHANNEL_NAME_CHANGE, + CHANNEL_ICON_CHANGE, + CHANNEL_PINNED_MESSAGE, + GUILD_MEMBER_JOIN, + USER_PREMIUM_GUILD_SUBSCRIPTION, + USER_PREMIUM_GUILD_SUBSCRIPTION_TIER_1, + USER_PREMIUM_GUILD_SUBSCRIPTION_TIER_2, + USER_PREMIUM_GUILD_SUBSCRIPTION_TIER_3, + CHANNEL_FOLLOW_ADD, +} + +export enum Activity_Types { + JOIN = 1, + SPECTATE, + LISTEN, + JOIN_REQUEST = 5 +} + +export interface Activity { + /** The type of message activity */ + type: 1 | 2 | 3 | 5 + /** The party id from a rich presence event */ + party_id?: string +} + +export interface Application { + /** The id of the application */ + id: string + /** The id of the embed's image asset */ + cover_image?: string + /** The application's description */ + description: string + /** The id of the application's icon */ + icon: string | null + /** The name of the application */ + name: string +} + +export interface Reference { + /** The id of the originating message */ + message_id?: string + /** The id of the originating message's channel */ + channel_id: string + /** The id of the originating message's guild */ + guild_id?: string +} + +export enum Message_Flags { + CROSSPOSTED = 1 << 0, + IS_CROSSPOST = 1 << 1, + SUPPRESS_EMBEDS = 1 << 2, + SOURCE_MESSAGE_DELETED = 1 << 3, + URGENT = 1 << 4 +} + +export interface Emoji { + /** The emoji id. */ + id?: string + /** The emoji name. Null in reaction emoji object if emoji is no longer on the server */ + name: string | null + /** The roles this emoji is whitelisted to */ + roles?: string[] + /** The user that created this emoji */ + user?: User + /** Whether this emoji must be wrapped in colons */ + require_colons?: boolean + /** Whether this emoji is managed */ + managed?: boolean + /** Whether this emoji is animated */ + animated?: boolean +} + +export interface Message_Create_Options { + /** The id of the message */ + id: string + /** The id of the channel the message was sent in */ + channel_id: string + /** The id of the guild the message was sent in */ + guild_id?: string + /** The author of this message (not guaranteed to be a valid user such as a webhook.) */ + author: User + /** The member properties for this message's author. Can be partial. */ + member?: Member + /** The contents of the message */ + content: string + /** When this message was sent */ + timestamp: string + /** When this message was edited (if it was not edited, null) */ + edited_timestamp: string | null + /** Whether this was a TextToSpeech message. */ + tts: boolean + /** Whether this message mentions everyone */ + mentions_everyone: boolean + /** Users specifically mentioned in the message. */ + mentions: MentionedUser[] + /** Roles specifically mentioned in this message */ + mention_roles: string[] + /** Channels specifically mentioned in this message */ + mention_channels?: Mentioned_Channel[] + /** Any attached files */ + attachments: Attachment[] + /** Any embedded content */ + embeds: Embed[] + /** Reactions to the message */ + reactions?: Reaction[] + /** Used for validating a message was sent */ + nonce?: number | string + /** Whether this message is pinned */ + pinned: boolean + /** If the message is generated by a webhook, this is the webhooks id */ + webhook_id?: string + /** The type of message */ + type: 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 + /** The activities sent with Rich Presence-related chat embeds. */ + activity?: Activity + /** Applications that sent with Rich Presence related chat embeds. */ + applications?: Application + /** The reference data sent with crossposted messages */ + message_reference?: Reference + /** The message flags combined like permission bits describe extra features of the message */ + flags?: 1 | 2 | 4 | 8 | 16 +} From a85bad4563fbb1a180db6e880918735df2bf0e8a Mon Sep 17 00:00:00 2001 From: Skillz Date: Tue, 18 Feb 2020 19:29:32 -0500 Subject: [PATCH 14/18] more work on message --- constants/discord.ts | 4 +++ module/client.ts | 5 +-- structures/channel.ts | 1 + structures/message.ts | 84 ++++++++++++++++++++++++++++++++++++++++--- utils/cache.ts | 7 ++++ 5 files changed, 95 insertions(+), 6 deletions(-) create mode 100644 utils/cache.ts diff --git a/constants/discord.ts b/constants/discord.ts index 7e8d91fa1..62f4c97fb 100644 --- a/constants/discord.ts +++ b/constants/discord.ts @@ -17,6 +17,10 @@ export const endpoints = { CHANNEL_BULK_DELETE: (id: string) => `${baseEndpoints.BASE_URL}/channels/${id}/messages/bulk-delete`, CHANNEL_INVITES: (id: string) => `${baseEndpoints.BASE_URL}/channels/${id}/invites`, CHANNEL_WEBHOOKS: (id: string) => `${baseEndpoints.BASE_URL}/channels/${id}/webhooks`, + CHANNEL_MESSAGE_REACTION_ME: (id: string, message_id: string, emoji: string) => `${baseEndpoints.BASE_URL}/channels/${id}/messages/${message_id}/reactions/${emoji}/@me`, + CHANNEL_MESSAGE_REACTIONS: (id: string, message_id: string) => `${baseEndpoints.BASE_URL}/channels/${id}/messages/${message_id}/reactions`, + CHANNEL_MESSAGE_REACTION: (id: string, message_id: string, emoji: string) => `${baseEndpoints.BASE_URL}/channels/${id}/messages/${message_id}/reactions/${emoji}`, + // Guild Endpoints GUILD: (id: string) => `${GUILDS_BASE(id)}`, diff --git a/module/client.ts b/module/client.ts index 0f2c5268b..4c9a3293e 100644 --- a/module/client.ts +++ b/module/client.ts @@ -6,14 +6,14 @@ import { connectWebSocket, isWebSocketCloseEvent, isWebSocketPingEvent, - isWebSocketPongEvent, - WebSocket + isWebSocketPongEvent } from 'https://deno.land/std/ws/mod.ts' import Gateway from './gateway.ts' import { ClientOptions, FulfilledClientOptions } from '../types/options.ts' import { CollectedMessageType } from '../types/message-type.ts' class Client { + bot_id: string /** The bot's token. This should never be used by end users. It is meant to be used internally to make requests to the Discord API. */ token: string /** The Rate limit manager to handle all outgoing requests to discord. Not meant to be used by users. */ @@ -39,6 +39,7 @@ class Client { }, options ) + this.bot_id = options.bot_id this.token = options.token this.authorization = `Bot ${this.options.token}` this.RequestManager = new RequestManager(this, this.authorization) diff --git a/structures/channel.ts b/structures/channel.ts index 91efc333c..7c352a466 100644 --- a/structures/channel.ts +++ b/structures/channel.ts @@ -2,6 +2,7 @@ import { Channel_Create_Options, Channel_Types, Get_Messages_After, Get_Messages import { Guild, Permission, Permissions } from '../types/guild' import Client from '../module/client' import { endpoints } from '../constants/discord' +import { create_message } from './message' export const create_channel = (data: Channel_Create_Options, guild: Guild, client: Client) => { const base_channel = { diff --git a/structures/message.ts b/structures/message.ts index 612f623a4..9bf53234b 100644 --- a/structures/message.ts +++ b/structures/message.ts @@ -1,30 +1,106 @@ import Client from '../module/client' import { Message_Create_Options } from '../types/message' +import { endpoints } from '../constants/discord' +import { Channel_Types, MessageContent } from '../types/channel' +import { cache } from '../utils/cache' export const create_message = (data: Message_Create_Options, client: Client) => { const base_message = { + raw: () => data, type: () => data.type, timestamp: () => Date.parse(data.timestamp), content: () => data.content, reactions: () => data.reactions || [], guild_id: () => data.guild_id, webhook_id: () => data.webhook_id, + mentions_everyone: () => data.mentions_everyone, + mentions: () => data.mentions.map(m => m.member.id), + mention_roles: () => data.mention_roles, + mention_channels: () => data.mention_channels?.map(c => c.id) || [], + pinned: () => data.pinned, + edited_timestamp: () => (data.edited_timestamp ? Date.parse(data.edited_timestamp) : undefined), + tts: () => data.tts, + attachments: () => data.attachments, + embeds: () => data.embeds, + activity: () => data.activity, + applications: () => data.applications, message_reference: () => ({ channel_id: data.message_reference?.channel_id, guild_id: data.message_reference?.guild_id, - message_id: data.message_reference?.message_id, + message_id: data.message_reference?.message_id }), flags: () => data.flags || 0, - channel_id: () => data.channel_id + channel_id: () => data.channel_id, + channel: () => client.channels.get(data.channel_id), + + delete: (reason: string) => { + // TODO: Requires MANAGE_MESSAGES + if (data.author.id !== client.bot_id) checkPermission() + + client.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: () => { + // TODO: Requires MANAGE_MESSAGES + client.RequestManager.put(endpoints.CHANNEL_MESSAGE(data.channel_id, data.id)) + }, + unpin: () => { + // TODO: Requires MANAGE_MESSAGES + client.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 */ + add_reaction: (reaction: string) => { + client.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. */ + remove_reaction: (reaction: string) => { + client.RequestManager.delete(endpoints.CHANNEL_MESSAGE_REACTION_ME(data.channel_id, data.id, reaction)) + }, + /** Removes all reactions for all emojis on this message. */ + remove_all_reactions: () => { + // TODO: Requires MANAGE_MESSAGES + client.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. */ + remove_reaction_emoji: (reaction: string) => { + // TODO: Requires MANAGE_MESSAGES + client.RequestManager.delete(endpoints.CHANNEL_MESSAGE_REACTION(data.channel_id, data.id, reaction)) + }, + /** Get a list of users that reacted with this emoji. */ + get_reactions: (reaction: string) => { + return client.RequestManager.get(endpoints.CHANNEL_MESSAGE_REACTION(data.channel_id, data.id, reaction)) as User[] + }, + /** Edit the message. */ + edit: async (content: string | MessageContent) => { + if (data.author.id !== client.bot_id) throw 'You can only edit a message that was sent by the bot.' + if (data.type !== Channel_Types.DM) { + // TODO: check if the bot has SEND_MESSAGES permission + } + + if (typeof content === 'string') content = { content } + if (content.tts) { + // TODO: check if the bot has SEND_TTS_MESSAGE + } + + // TODO: Check content length + + const result = await client.RequestManager.patch(endpoints.CHANNEL_MESSAGES(data.id), content) + return create_message(result, client) + } } if (!data.guild_id) { return { - ...base_message + ...base_message, + author: create_user(data.author, client) } } - return { + const guild = cache.guilds.get(data.guild_id) + return { + ...base_message, + guild: () => guild, + member: () => data.member } } diff --git a/utils/cache.ts b/utils/cache.ts new file mode 100644 index 000000000..e0d66532d --- /dev/null +++ b/utils/cache.ts @@ -0,0 +1,7 @@ +import { Guild } from "../types/guild"; + +export const cache = { + guilds: new Map(), + users: new Map(), + channels: new Map(), +} From 039c10afa78438bb887546c6e651c9f6247d8413 Mon Sep 17 00:00:00 2001 From: Skillz Date: Wed, 19 Feb 2020 12:11:35 -0500 Subject: [PATCH 15/18] work work work --- README.md | 27 ++- structures/channel.ts | 12 +- structures/guild.ts | 444 ++++++++++++++++++++++++++---------------- structures/member.ts | 136 ++++--------- structures/message.ts | 14 +- structures/role.ts | 32 ++- structures/user.ts | 68 ++++--- types/channel.ts | 48 +++-- types/emoji.ts | 2 + types/general.ts | 2 + types/guild.ts | 210 +------------------- types/member.ts | 31 +++ types/message.ts | 5 +- types/permission.ts | 64 ++++++ types/role.ts | 21 -- utils/cache.ts | 4 +- utils/cdn.ts | 2 +- 17 files changed, 572 insertions(+), 550 deletions(-) create mode 100644 types/general.ts create mode 100644 types/member.ts create mode 100644 types/permission.ts diff --git a/README.md b/README.md index 1f668b577..c133a7bca 100644 --- a/README.md +++ b/README.md @@ -4,4 +4,29 @@ Discord API library wrapper in Deno ## TODO - [Review compression of payloads with GZIP](https://discordapp.com/developers/docs/topics/gateway#sending-payloads-example-gateway-dispatch) -- +- + +## Motivations/Features + +This project began out of the desire to want to learn and enhance my developer skills. As I was building it, I encountered so many issues that other libraries have that I wanted to change in my library. + +- **TYPESCRIPT:** + - First class support for Typescript! +- **SECURITY:** + - Check all permissions necessary before sending a request to the API. + - Prevent supporting self-bots and abusive behavior. +- **Functional API:** + - This will overall make a cleaner and more performant API, while removing the headaches of extending built-in classes, and inheritance. + - Avoid as many headaches and issues related to `class` and `this` + - Avoid EventEmitter to not have potential of memory leaks or bot crashes because of too many listeners or other silly issues. + - Avoid for loops, while loops etc... +- **MINIMALISTIC:** + - Prevent as many "options" for the sake of customizability. Prefer defaults that Discord recommends. +- **DOCUMENTATION:** + - All of Discord API Documentation available inside your VSC while you code. + - The entire libraries documentation is automatically available to you throw intellisense. +- **LATEST AND GREATEST JAVASCRIPT:** + - Backwards compatibility is the death of code. It causes clutter and uglyness to pile up and makes developers lazier. + - There will be no such thing as backwards compatibility reasons in Discordeno. + - We will always support the latest and greatest of JS. The end! + - That said, we don't expect many things to be changing drastically after v1. As you can imagine Typescript allows the latest and greatest of JS so we will be ahead of the curve for years to come. diff --git a/structures/channel.ts b/structures/channel.ts index 7c352a466..1e17511f3 100644 --- a/structures/channel.ts +++ b/structures/channel.ts @@ -1,8 +1,10 @@ import { Channel_Create_Options, Channel_Types, Get_Messages_After, Get_Messages_Around, Get_Messages, Get_Messages_Before, MessageContent, Create_Invite_Options } from '../types/channel' -import { Guild, Permission, Permissions } from '../types/guild' +import { Permission, Permissions } from '../types/guild' import Client from '../module/client' import { endpoints } from '../constants/discord' -import { create_message } from './message' +import { create_message, Message } from './message' +import { Guild } from './guild' +import { Message_Create_Options } from '../types/message' export const create_channel = (data: Channel_Create_Options, guild: Guild, client: Client) => { const base_channel = { @@ -30,12 +32,12 @@ export const create_channel = (data: Channel_Create_Options, guild: Guild, clien // TODO: check if the user has VIEW_CHANNEL and READ_MESSAGE_HISTORY if (options?.limit && options.limit > 100) return - const result = await client.RequestManager.get(endpoints.CHANNEL_MESSAGES(data.id), options) + const result = await client.RequestManager.get(endpoints.CHANNEL_MESSAGES(data.id), options) as Message_Create_Options[] return result.map(res => create_message(res, client)) }, /** Get pinned messages in this channel. */ get_pins: async () => { - const result = await client.RequestManager.get(endpoints.CHANNEL_PINS(data.id)) + const result = await client.RequestManager.get(endpoints.CHANNEL_PINS(data.id)) as Message_Create_Options[] return result.map(res => create_message(res, client)) }, /** Send a message to the channel. Requires SEND_MESSAGES permission. */ @@ -163,3 +165,5 @@ export const create_channel = (data: Channel_Create_Options, guild: Guild, clien mention: () => `<#${data.id}>` } } + +export type Channel = ReturnType diff --git a/structures/guild.ts b/structures/guild.ts index 003241d60..501a967ba 100644 --- a/structures/guild.ts +++ b/structures/guild.ts @@ -1,176 +1,286 @@ import Client from '../module/client' import { endpoints } from '../constants/discord' -import { formatImageURL } from '../utils/cdn' -import { Guild, CreateGuildPayload, ChannelTypes, Permissions, PrunePayload } from '../types/guild' +import { format_image_url } from '../utils/cdn' +import { + Create_Guild_Payload, + ChannelTypes, + Permissions, + PrunePayload, + Image_Size, + Image_Formats, + Position_Swap, + Get_Audit_Logs_Options, + Edit_Integration_Options, + BanOptions, + Guild_Edit_Options, + Create_Emojis_Options, + Edit_Emojis_Options, + Create_Role_Options +} from '../types/guild' import { create_role } from './role' import { create_member } from './member' import { create_channel } from './channel' +import { Channel_Create_Options } from '../types/channel' -export const createGuild = (data: CreateGuildPayload, client: Client) => { - const guild: Guild = { - ...data, - roles: new Map(data.roles.map(r => [r.id, create_role(r)])), - emojis: data.emojis, - joined_at: Date.parse(data.joined_at), - voice_states: new Map(data.voice_states.map(vs => [vs.id, create_voice_state(vs)])), - members: new Map(data.members.map(m => [m.id, create_member(m)])), - channels: new Map(data.channels.map(c => [c.id, create_channel(c, data, client)])), - presences: new Map(data.presences.map(p => [p.id, create_presence(p)])), - icon_url: (size, format) => - data.icon ? formatImageURL(endpoints.GUILD_ICON(data.id, data.icon), size, format) : undefined, - splash_url: (size, format) => - data.splash ? formatImageURL(endpoints.GUILD_SPLASH(data.id, data.splash), size, format) : undefined, - banner_url: (size, format) => - data.banner ? formatImageURL(endpoints.GUILD_BANNER(data.id, data.banner), size, format) : undefined, - create_channel: (name, options) => { - // TODO: Check if the bot has `MANAGE_CHANNELS` permission before making a channel - return client.RequestManager.post(endpoints.GUILD_CHANNELS(data.id), { - name, - type: options?.type ? ChannelTypes[options.type] : undefined, - 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 - }) - }, - get_channels: () => { - return client.RequestManager.get(endpoints.GUILD_CHANNELS(data.id)) - }, - swap_channels: channel_positions => { - if (channel_positions.length < 2) throw 'You must provide atleast two channels to be swapped.' - return client.RequestManager.patch(endpoints.GUILD_CHANNELS(data.id), channel_positions) - }, - get_member: id => { - return client.RequestManager.get(endpoints.GUILD_MEMBER(data.id, id)) - }, - create_emoji: (name, image, options) => { - // TODO: Check if the bot has `MANAGE_EMOJIS` permission - return client.RequestManager.post(endpoints.GUILD_EMOJIS(data.id), { - ...options, - name, - image - }) - }, - edit_emoji: (id, options) => { - // TODO: check if the bot has `MANAGE_EMOJIS` permission - return client.RequestManager.patch(endpoints.GUILD_EMOJI(data.id, id), { - name: options.name, - roles: options.roles - }) - }, - delete_emoji: (id, reason) => { - // TODO: check if the bot has `MANAGE_EMOJIS` permission - return client.RequestManager.delete(endpoints.GUILD_EMOJI(data.id, id), { reason }) - }, - create_role: async options => { - // TODO: check if the bot has the `MANAGE_ROLES` permission. - const role = await client.RequestManager.post(endpoints.GUILD_ROLES(data.id), { - ...options, - permissions: options.permissions?.map(perm => Permissions[perm]) - }) - // TODO: cache this role +export const create_guild = (data: Create_Guild_Payload, client: Client) => ({ + /** The raw create guild payload data. */ + raw: () => data, + /** The guild id */ + id: () => data.id, + /** The guild name. 2-100 characters */ + name: () => data.name, + /** The guild icon image hash */ + icon: () => data.icon, + /** The guild splash image hash */ + splash: () => data.splash, + /** The id of the guild owner */ + owner_id: () => data.owner_id, + /** The voice region id for the guild */ + region: () => data.region, + /** The afk channel id */ + afk_channel_id: () => data.afk_channel_id, + /** The AFK timeout in seconds */ + afk_timeout: () => data.afk_timeout, + /** The verification level required for the guild */ + verification_level: () => data.verification_level, + /** The roles in the guild */ + roles: new Map(data.roles.map(r => [r.id, create_role(r)])), + /** The custom guild emojis */ + emojis: () => data.emojis, + /** The enabled guild features. */ + features: () => data.features, + /** The required MFA level for the guild. */ + mfa_level: () => data.mfa_level, + /** The id of the channel to which system messages are sent. */ + system_channel_id: () => data.system_channel_id, + /** When this guild was joined at. */ + joined_at: Date.parse(data.joined_at), + /** Whether this is considered a large guild. */ + large: () => data.large, + /** Whether this guild is unavailable */ + unavailable: () => data.unavailable, + /** The total number of members in this guild. */ + member_count: () => data.member_count, + /** The current open voice states in the guild. */ + voice_states: new Map(data.voice_states.map(vs => [vs.id, create_voice_state(vs)])), + /** The users in this guild. */ + members: new Map(data.members.map(m => [m.id, create_member(m)])), + /** The channels in the guild */ + channels: new Map(data.channels.map(c => [c.id, create_channel(c, data, client)])), + /** The presences of all the users in the guild. */ + presences: new Map(data.presences.map(p => [p.id, create_presence(p)])), + /** The maximum amount of presences for the guild(the default value, currently 5000 is in effect when null is returned.) */ + max_presences: () => data.max_presences, + /** The maximum amount of members for the guild */ + max_members: () => data.max_members, + /** The vanity url code for the guild */ + vanity_url_code: () => data.vanity_url_code, + /** The description for the guild */ + description: () => data.description, + /** The banner hash */ + banner: () => data.banner, + /** The current premium tier of the guild */ + premium_tier: () => data.premium_tier, + /** The total number of users currently boosting this server. */ + premium_subscription_count: () => data.premium_subscription_count, + /** The preferred locale of this guild only set if the guild has the DISCOVERABLE feature, defaults to en-US */ + preferred_locale: () => data.preferred_locale, + /** The full URL of the icon from Discords CDN. Undefined when no icon is set. */ + icon_url: (size: Image_Size = 128, format?: Image_Formats) => + data.icon ? format_image_url(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. */ + splash_url: (size: Image_Size = 128, format?: Image_Formats) => + data.splash ? format_image_url(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. */ + banner_url: (size: Image_Size = 128, format?: Image_Formats) => + data.banner ? format_image_url(endpoints.GUILD_BANNER(data.id, data.banner), size, format) : undefined, + /** Create a channel in your server. Bot needs MANAGE_CHANNEL permissions in the server. */ + create_channel: (name: string, options: Channel_Create_Options) => { + // TODO: Check if the bot has `MANAGE_CHANNELS` permission before making a channel + return client.RequestManager.post(endpoints.GUILD_CHANNELS(data.id), { + name, + type: options?.type ? ChannelTypes[options.type] : undefined, + 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 + }) + }, + /** 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.** + */ + get_channels: () => { + return client.RequestManager.get(endpoints.GUILD_CHANNELS(data.id)) + }, + /** Modify the positions of channels on the guild. Requires MANAGE_CHANNELS permisison. */ + swap_channels: (channel_positions: Position_Swap[]) => { + if (channel_positions.length < 2) throw 'You must provide atleast two channels to be swapped.' + return client.RequestManager.patch(endpoints.GUILD_CHANNELS(data.id), channel_positions) + }, + /** 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.** + */ + get_member: (id: string) => { + return client.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. */ + create_emoji: (name: string, image: string, options: Create_Emojis_Options) => { + // TODO: Check if the bot has `MANAGE_EMOJIS` permission + return client.RequestManager.post(endpoints.GUILD_EMOJIS(data.id), { + ...options, + name, + image + }) + }, + /** Modify the given emoji. Requires the MANAGE_EMOJIS permission. */ + edit_emoji: (id: string, options: Edit_Emojis_Options) => { + // TODO: check if the bot has `MANAGE_EMOJIS` permission + return client.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. */ + delete_emoji: (id: string, reason?: string) => { + // TODO: check if the bot has `MANAGE_EMOJIS` permission + return client.RequestManager.delete(endpoints.GUILD_EMOJI(data.id, id), { reason }) + }, + /** Create a new role for the guild. Requires the MANAGE_ROLES permission. */ + create_role: async (options: Create_Role_Options) => { + // TODO: check if the bot has the `MANAGE_ROLES` permission. + const role = await client.RequestManager.post(endpoints.GUILD_ROLES(data.id), { + ...options, + permissions: options.permissions?.map(perm => Permissions[perm]) + }) + // TODO: cache this role - return role - }, - edit_role: (id, options) => { - // TODO: check if the bot has the `MANAGE_ROLES` permission. - return client.RequestManager.patch(endpoints.GUILD_ROLE(data.id, id), options) - }, - delete_role: id => { - // TODO: check if the bot has the `MANAGE_ROLES` permission. - return client.RequestManager.delete(endpoints.GUILD_ROLE(data.id, id)) - }, - get_roles: () => { - // TODO: check if the bot has the `MANAGE_ROLES` permission. - return client.RequestManager.get(endpoints.GUILD_ROLES(data.id)) - }, - swap_roles: rolePositons => { - // TODO: check if the bot has the `MANAGE_ROLES` permission. - return client.RequestManager.patch(endpoints.GUILD_ROLES(data.id), rolePositons) - }, - get_prune_count: async days => { - if (days < 1) throw `The number of days to count prune for must be 1 or more.` - // TODO: check if the bot has `KICK_MEMBERS` permission - const result = (await client.RequestManager.get(endpoints.GUILD_PRUNE(data.id), { days })) as PrunePayload - return result.pruned - }, - prune_members: days => { - if (days < 1) throw `The number of days must be 1 or more.` - // TODO: check if the bot has `KICK_MEMBERS` permission. - return client.RequestManager.post(endpoints.GUILD_PRUNE(data.id), { days }) - }, - // TODO: REQUEST THIS OVER WEBSOCKET WITH GET_GUILD_MEMBERS ENDPOINT - // fetch_all_members: () => { - // }, - get_audit_logs: options => { - // TODO: check if the bot has VIEW_AUDIT_LOGS permission - return client.RequestManager.get(endpoints.GUILD_AUDIT_LOGS(data.id), { - ...options, - limit: options.limit && options.limit >= 1 && options.limit <= 100 ? options.limit : 50 - }) - }, - get_embed: () => { - // TODO: check if the bot has the MANAGE_GUILD permission - return client.RequestManager.get(endpoints.GUILD_EMBED(data.id)) - }, - edit_embed: (enabled, channel_id) => { - // TODO: Requires the MANAGE_GUILD permission. - return client.RequestManager.patch(endpoints.GUILD_EMBED(data.id), { enabled, channel_id }) - }, - get_vanity_url: () => { - return client.RequestManager.get(endpoints.GUILD_VANITY_URL(data.id)) - }, - get_integrations: () => { - // TODO: requires the MANAGE_GUILD permission - return client.RequestManager.get(endpoints.GUILD_INTEGRATIONS(data.id)) - }, - edit_integration: (id, options) => { - // TODO: requires the MANAGE_GUILD permission - return client.RequestManager.patch(endpoints.GUILD_INTEGRATION(data.id, id), options) - }, - delete_integration: id => { - // TODO: requires the MANAGE_GUILD permission - return client.RequestManager.delete(endpoints.GUILD_INTEGRATION(data.id, id)) - }, - sync_integration: id => { - // TODO: requires MANAGE_GUILD - return client.RequestManager.post(endpoints.GUILD_INTEGRATION_SYNC(data.id, id)) - }, - get_bans: () => { - // TODO: requires the BAN_MEMBERS permission - return client.RequestManager.get(endpoints.GUILD_BANS(data.id)) - }, - ban: (id, options) => { - // TODO: requires the BAN_MEMBERS permission - return client.RequestManager.put(endpoints.GUILD_BAN(data.id, id), options) - }, - unban: id => { - // TODO: requires the BAN_MEMBERS permission - return client.RequestManager.delete(endpoints.GUILD_BAN(data.id, id)) - }, - edit: options => { - // TODO: requires the MANAGE_GUILD permission - return client.RequestManager.patch(endpoints.GUILD(data.id), options) - }, - get_invites: () => { - // TODO: requires MANAGE_GUILD permission - return client.RequestManager.get(endpoints.GUILD_INVITES(data.id)) - }, - leave: () => { - return client.RequestManager.delete(endpoints.GUILD_LEAVE(data.id)) - }, - get_voice_regions: () => { - return client.RequestManager.get(endpoints.GUILD_REGIONS(data.id)) - }, - get_webhooks: () => { - // TODO: requires MANAGE_WEBHOOKS - return client.RequestManager.get(endpoints.GUILD_WEBHOOKS(data.id)) - } + return role + }, + /** Edit a guild role. Requires the MANAGE_ROLES permission. */ + edit_role: (id: string, options: Create_Role_Options) => { + // TODO: check if the bot has the `MANAGE_ROLES` permission. + return client.RequestManager.patch(endpoints.GUILD_ROLE(data.id, id), options) + }, + /** Delete a guild role. Requires the MANAGE_ROLES permission. */ + delete_role: (id: string) => { + // TODO: check if the bot has the `MANAGE_ROLES` permission. + return client.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.** + */ + get_roles: () => { + // TODO: check if the bot has the `MANAGE_ROLES` permission. + return client.RequestManager.get(endpoints.GUILD_ROLES(data.id)) + }, + /** Modify the positions of a set of role objects for the guild. Requires the MANAGE_ROLES permission. */ + swap_roles: (rolePositons: Position_Swap) => { + // TODO: check if the bot has the `MANAGE_ROLES` permission. + return client.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 */ + get_prune_count: async (days: number) => { + if (days < 1) throw `The number of days to count prune for must be 1 or more.` + // TODO: check if the bot has `KICK_MEMBERS` permission + const result = (await client.RequestManager.get(endpoints.GUILD_PRUNE(data.id), { days })) as PrunePayload + return result.pruned + }, + /** Begin pruning all members in the given time period */ + prune_members: (days: number) => { + if (days < 1) throw `The number of days must be 1 or more.` + // TODO: check if the bot has `KICK_MEMBERS` permission. + return client.RequestManager.post(endpoints.GUILD_PRUNE(data.id), { days }) + }, + // TODO: REQUEST THIS OVER WEBSOCKET WITH GET_GUILD_MEMBERS ENDPOINT + // fetch_all_members: () => { + // }, + /** Returns the audit logs for the guild. Requires VIEW AUDIT LOGS permission */ + get_audit_logs: (options: Get_Audit_Logs_Options) => { + // TODO: check if the bot has VIEW_AUDIT_LOGS permission + return client.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. */ + get_embed: () => { + // TODO: check if the bot has the MANAGE_GUILD permission + return client.RequestManager.get(endpoints.GUILD_EMBED(data.id)) + }, + /** Modify a guild embed object for the guild. Requires the MANAGE_GUILD permission. */ + edit_embed: (enabled: boolean, channel_id?: string | null) => { + // TODO: Requires the MANAGE_GUILD permission. + return client.RequestManager.patch(endpoints.GUILD_EMBED(data.id), { enabled, channel_id }) + }, + /** Returns the code and uses of the vanity url for this server if it is enabled. Requires the MANAGE_GUILD permission. */ + get_vanity_url: () => { + return client.RequestManager.get(endpoints.GUILD_VANITY_URL(data.id)) + }, + /** Returns a list of integrations for the guild. Requires the MANAGE_GUILD permission. */ + get_integrations: () => { + // TODO: requires the MANAGE_GUILD permission + return client.RequestManager.get(endpoints.GUILD_INTEGRATIONS(data.id)) + }, + /** Modify the behavior and settings of an integration object for the guild. Requires the MANAGE_GUILD permission. */ + edit_integration: (id: string, options: Edit_Integration_Options) => { + // TODO: requires the MANAGE_GUILD permission + return client.RequestManager.patch(endpoints.GUILD_INTEGRATION(data.id, id), options) + }, + /** Delete the attached integration object for the guild with this id. Requires MANAGE_GUILD permission. */ + delete_integration: (id: string) => { + // TODO: requires the MANAGE_GUILD permission + return client.RequestManager.delete(endpoints.GUILD_INTEGRATION(data.id, id)) + }, + /** Sync an integration. Requires teh MANAGE_GUILD permission. */ + sync_integration: (id: string) => { + // TODO: requires MANAGE_GUILD + return client.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. */ + get_bans: () => { + // TODO: requires the BAN_MEMBERS permission + return client.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) => { + // TODO: requires the BAN_MEMBERS permission + return client.RequestManager.put(endpoints.GUILD_BAN(data.id, id), options) + }, + /** Remove the ban for a user. REquires BAN_MEMBERS permission */ + unban: (id: string) => { + // TODO: requires the BAN_MEMBERS permission + return client.RequestManager.delete(endpoints.GUILD_BAN(data.id, id)) + }, + /** Modify a guilds settings. Requires the MANAGE_GUILD permission. */ + edit: (options: Guild_Edit_Options) => { + // TODO: requires the MANAGE_GUILD permission + return client.RequestManager.patch(endpoints.GUILD(data.id), options) + }, + /** Get all the invites for this guild. Requires MANAGE_GUILD permission */ + et_invites: () => { + // TODO: requires MANAGE_GUILD permission + return client.RequestManager.get(endpoints.GUILD_INVITES(data.id)) + }, + /** Leave a guild */ + leave: () => { + return client.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. */ + get_voice_regions: () => { + return client.RequestManager.get(endpoints.GUILD_REGIONS(data.id)) + }, + /** Returns a list of guild webhooks objects. Requires the MANAGE_WEBHOOKs permission. */ + get_webhooks: () => { + // TODO: requires MANAGE_WEBHOOKS + return client.RequestManager.get(endpoints.GUILD_WEBHOOKS(data.id)) } +}) - return guild -} +export type Guild = ReturnType diff --git a/structures/member.ts b/structures/member.ts index 15019c71e..2e1ed7b5a 100644 --- a/structures/member.ts +++ b/structures/member.ts @@ -1,118 +1,67 @@ import Client from '../module/client' import { endpoints } from '../constants/discord' -import { Guild, Image_Size, Image_Formats, Permissions, Permission } from '../types/guild' -import { formatImageURL } from '../utils/cdn' +import { format_image_url } from '../utils/cdn' +import { Member_Create_Payload, Edit_Member_Options } from '../types/member' +import { Image_Size, Image_Formats } from '../types/general' +import { Permission, Permissions } from '../types/permission' +import { cache } from '../utils/cache' -export interface Edit_Member_Options { - /** Value to set users nickname to. Requires MANAGE_NICKNAMES permission. */ - nick?: string - /** Array of role ids the member is assigned. Requires MANAGE_ROLES permission. */ - roles?: string[] - /** Whether the user is muted in voice channels. Requires MUTE_MEMBERS permission. */ - mute?: boolean - /** Whether the user is deafened in voice channels. Requires DEAFEN_MEMBERS permission. */ - deaf?: boolean - /** The id of the channel to move user to if they are connected to voice. To kick the user from their current channel, set to null. Requires MOVE_MEMBERS permission. When moving members to channels, must have permissions to both CONNECT to the channel and have the MOVE_MEMBER permission. */ - channel_id?: string | null -} - -export interface Member { - /** The unique user id */ - id: string - /** The user's guild nickname if one is set. */ - nick(): string | undefined - /** Array of role ids that the member has */ - roles(): string[] - /** When the user joined the guild. */ - joined_at(): number - /** When the user used their nitro boost on the server. */ - premium_since(): number | undefined - /** Whether the user is deafened in voice channels */ - deaf(): boolean - /** Whether the user is muted in voice channels */ - mute(): boolean - edit(options: Edit_Member_Options): Promise - /** The username of the this member. */ - username(): string - /** The 4 digit unique identifier */ - discriminator(): string - /** The full username#discriminator */ - tag(): string - /** The users custom avatar or the default avatar */ - avatarURL(size: Image_Size, format: Image_Formats): string - /** The user mention with nickname if possible */ - mention(): string - /** Whether the member is a bot */ - bot(): boolean - /** When the user created their account */ - created_at(): number - /** Add a role to the member */ - addRole(role_id: string, reason: string): Promise - /** Remove a role from the member */ - removeRole(role_id: string, reason: string): Promise - /** Kick a member from the server */ - kick(reason: string): Promise - /** Ban a member from the server */ - ban(delete_message_days?: 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7, reason?: string): Promise - has_permissions(permissions: Permission[]): boolean -} - -export interface Member_Create_Payload { - /** The user this guild member represents */ - user: User - /** The user's guild nickname if one is set. */ - nick?: string - /** Array of role ids that the member has */ - roles: string[] - /** When the user joined the guild. */ - joined_at: string - /** When the user used their nitro boost on the server. */ - premium_since?: string - /** Whether the user is deafened in voice channels */ - deaf: boolean - /** Whether the user is muted in voice channels */ - mute: boolean -} - -export const create_member = (data: Member_Create_Payload, guild: Guild, client: Client) => { - const member: Member = { - id: data.user.id, +export const create_member = (data: Member_Create_Payload, guild_id: string, client: Client) => { + const guild = cache.guilds.get(guild_id) + + return { + /** The complete raw data from the member create payload */ + raw: () => data, + /** The unique user id */ + id: () => data.user.id, + /** The user's guild nickname if one is set. */ roles: () => data.roles, + /** Array of role ids that the member has */ nick: () => data.nick, + /** When the user joined the guild. */ joined_at: () => Date.parse(data.joined_at), + /** When the user used their nitro boost on the server. */ premium_since: () => (data.premium_since ? Date.parse(data.premium_since) : undefined), + /** Whether the user is deafened in voice channels */ deaf: () => data.deaf, + /** Whether the user is muted in voice channels */ mute: () => data.mute, + /** The username of the this member. */ username: () => data.user.username, + /** The 4 digit unique identifier */ discriminator: () => data.user.discriminator, + /** The full username#discriminator */ tag: () => `${data.user.username}#${data.user.discriminator}`, - game: () => data.game, - status: () => data.status, - client_status: () => data.client_status, - activities: () => data.activites, - avatarURL: (size, format) => + /** The users custom avatar or the default avatar */ + avatar_url: (size: Image_Size = 128, format?: Image_Formats) => data.user.avatar - ? formatImageURL(endpoints.USER_AVATAR(data.user.id, data.user.avatar), size, format) - : endpoints.USER_DEFAULT_AVATAR(data.user.discriminator % 5), + ? format_image_url(endpoints.USER_AVATAR(data.user.id, data.user.avatar), size, format) + : endpoints.USER_DEFAULT_AVATAR(Number(data.user.discriminator) % 5), + /** The user mention with nickname if possible */ mention: () => `<@!${data.user.id}>`, + /** Whether the member is a bot */ bot: () => data.user.bot, - created_at: () => data.user.created_at, - addRole: (role_id, reason) => { + /** Add a role to the member */ + add_role: (role_id: string, reason?: string) => { // TODO: check if the bot has MANAGE_ROLE and its highest role is above this one return client.RequestManager.put(endpoints.GUILD_MEMBER_ROLE(guild.id, data.user.id, role_id), { reason }) }, - removeRole: (role_id, reason) => { + /** Remove a role from the member */ + remove_role: (role_id: string, reason?: string) => { // TODO: check if the bot has MANAGE_ROLE permissions and its highest role is above this role return client.RequestManager.delete(endpoints.GUILD_MEMBER_ROLE(guild.id, data.user.id, role_id), { reason }) }, - kick: reason => { + /** Kick a member from the server */ + kick: (reason?: string) => { // TODO: check if bot has KICK_MEMBER permissions return client.RequestManager.delete(endpoints.GUILD_MEMBER(guild.id, data.user.id), { reason }) }, - ban: (delete_message_days = 0, reason) => { + /** Ban a member from the server */ + ban: (delete_message_days = 0, reason?: string) => { return guild.ban(data.user.id, { delete_message_days, reason }) }, - has_permissions: permissions => { + /** Checks if the member has this permission. If the member is an owner or has admin perms it will always be true. */ + has_permissions: (permissions: Permission[]) => { if (data.user.id === guild.owner_id) return true const permissionBits = data.roles.reduce((bits, role_id) => { @@ -126,7 +75,8 @@ export const create_member = (data: Member_Create_Payload, guild: Guild, client: return permissions.every(permission => permissionBits & Permissions[permission]) }, - edit: options => { + /** Edit the member */ + edit: (options: Edit_Member_Options) => { // TODO: check if has MANAGE_NICKNAME Permission // TODO: check if it is a valid nickname like 32 characters options.nick = undefined @@ -142,9 +92,9 @@ export const create_member = (data: Member_Create_Payload, guild: Guild, client: // TODO: if channel id is provided check if the bot has CONNECT and MOVE in channel and current channel options.channel_id = undefined - return client.RequestManager.patch(endpoints.GUILD_MEMBER(guild.id, data.id), options) + return client.RequestManager.patch(endpoints.GUILD_MEMBER(guild.id, data.user.id), options) } } - - return member } + +export type Member = ReturnType diff --git a/structures/message.ts b/structures/message.ts index 9bf53234b..19bc03976 100644 --- a/structures/message.ts +++ b/structures/message.ts @@ -3,6 +3,7 @@ import { Message_Create_Options } from '../types/message' import { endpoints } from '../constants/discord' import { Channel_Types, MessageContent } from '../types/channel' import { cache } from '../utils/cache' +import { create_user, User_Payload } from './user' export const create_message = (data: Message_Create_Options, client: Client) => { const base_message = { @@ -31,11 +32,11 @@ export const create_message = (data: Message_Create_Options, client: Client) => }), flags: () => data.flags || 0, channel_id: () => data.channel_id, - channel: () => client.channels.get(data.channel_id), + channel: () => cache.channels.get(data.channel_id), delete: (reason: string) => { // TODO: Requires MANAGE_MESSAGES - if (data.author.id !== client.bot_id) checkPermission() + if (data.author.id !== client.bot_id) {} client.RequestManager.delete(endpoints.CHANNEL_MESSAGE(data.channel_id, data.id), { reason }) }, @@ -67,8 +68,9 @@ export const create_message = (data: Message_Create_Options, client: Client) => client.RequestManager.delete(endpoints.CHANNEL_MESSAGE_REACTION(data.channel_id, data.id, reaction)) }, /** Get a list of users that reacted with this emoji. */ - get_reactions: (reaction: string) => { - return client.RequestManager.get(endpoints.CHANNEL_MESSAGE_REACTION(data.channel_id, data.id, reaction)) as User[] + get_reactions: async (reaction: string) => { + const result = await client.RequestManager.get(endpoints.CHANNEL_MESSAGE_REACTION(data.channel_id, data.id, reaction)) as User_Payload[] + return result.map(res => create_user(res)) }, /** Edit the message. */ edit: async (content: string | MessageContent) => { @@ -92,7 +94,7 @@ export const create_message = (data: Message_Create_Options, client: Client) => if (!data.guild_id) { return { ...base_message, - author: create_user(data.author, client) + author: create_user({ ...data.author, avatar: data.author.avatar || '' }) } } @@ -104,3 +106,5 @@ export const create_message = (data: Message_Create_Options, client: Client) => member: () => data.member } } + +export type Message = ReturnType diff --git a/structures/role.ts b/structures/role.ts index 4c05dd72a..d2e217b6a 100644 --- a/structures/role.ts +++ b/structures/role.ts @@ -1,10 +1,26 @@ -import { Role, Role_Data } from "../types/role" +import { Role_Data } from '../types/role' -export const create_role = (data: Role_Data) => { - const role: Role = { - ...data, - mention: () => `<@&${data.id}>` - } +export const create_role = (data: Role_Data) => ({ + /** The entire raw Role data */ + raw: () => data, + /** role id */ + id: () => data.id, + /** role name */ + name: () => data.name, + /** integer representation of hexadecimal color code */ + color: () => data.color, + /** if this role is pinned in the user listing */ + hoist: () => data.hoist, + /** position of this role */ + position: () => data.position, + /** permission bit set */ + permissions: () => data.permissions, + /** whether this role is managed by an integration */ + managed: () => data.managed, + /** whether this role is mentionable */ + mentionable: () => data.mentionable, + /** The @ mention of the role in a string. */ + mention: () => `<@&${data.id}>` +}) - return role -} +export type Role = ReturnType diff --git a/structures/user.ts b/structures/user.ts index f9d884fe8..983620fd1 100644 --- a/structures/user.ts +++ b/structures/user.ts @@ -1,34 +1,58 @@ -export interface UserPayload { - /** The user's id */ - id: string; +import { Image_Formats, Image_Size } from '../types/guild' +import { format_image_url } from '../utils/cdn' +import { endpoints } from '../constants/discord' - /** The user's username, not unique across the platform */ - username: string; +export interface User_Payload { + /** The user's id */ + id: string - /** The user's 4-digit discord-tag */ - discriminator: string; + /** The user's username, not unique across the platform */ + username: string - /** The user's avatar hash */ - avatar: string; + /** The user's 4-digit discord-tag */ + discriminator: string - /** Whether the user belongs to an OAuth2 application */ - bot?: boolean; + /** The user's avatar hash */ + avatar: string - /** Whether the user is an Official Discord System user (part of the urgent message system) */ - system?: boolean; + /** Whether the user belongs to an OAuth2 application */ + bot?: boolean - /** Whether the user has two factor enabled on their account */ - mfa_enabled?: boolean; + /** Whether the user is an Official Discord System user (part of the urgent message system) */ + system?: boolean - // Types with "email" scope intentionally left out. - /** The flags on a user's account */ - flags?: number; + /** Whether the user has two factor enabled on their account */ + mfa_enabled?: boolean - /** The type of Nitro subscription on a user's account */ - premium_type?: PremiumType; + // Types with "email" scope intentionally left out. + /** The flags on a user's account */ + flags?: number + + /** The type of Nitro subscription on a user's account */ + premium_type?: PremiumType } export const enum PremiumType { - NitroClassic = 1, - Nitro + NitroClassic = 1, + Nitro } + +export const create_user = (data: User_Payload) => ({ + id: () => data.id, + mention: () => `<@!${data.id}>`, + username: () => data.username, + discriminator: () => data.discriminator, + tag: () => `${data.username}#${data.discriminator}`, + avatar: () => data.avatar, + avatar_url: (size: Image_Size, format: Image_Formats) => + data.avatar + ? format_image_url(endpoints.USER_AVATAR(data.id, data.avatar), size, format) + : endpoints.USER_DEFAULT_AVATAR(Number(data.discriminator) % 5), + bot: () => data.bot, + system: () => data.system, + mfa_enabled: () => data.mfa_enabled, + flags: () => data.flags, + premium_type: () => data.premium_type +}) + +export type User = ReturnType diff --git a/types/channel.ts b/types/channel.ts index 488955c29..9ce091d31 100644 --- a/types/channel.ts +++ b/types/channel.ts @@ -1,6 +1,6 @@ -import { Raw_Overwrite } from './guild' +import { Raw_Overwrite, Overwrite } from './guild' -export interface Channel_Create_Options { +export interface Base_Channel_Create { /** The id of this channel */ id: string /** The type of the channel */ @@ -9,26 +9,34 @@ export interface Channel_Create_Options { guild_id?: string /** Sorting position of the channel */ position?: number + /** The name of the channel (2-100 characters) */ + name?: string + /** The channel topic (0-1024 characters) */ + topic?: string + /** Whether the channel is nsfw */ + nsfw?: boolean + /** The id of the last message sent in this channel (may not point to an existing or valid message) */ + last_message_id?: string | null + /** The bitrate (in bits) of the voice channel */ + bitrate?: number + /** The user limit of the voice channel */ + user_limit?: number + /** Amount of seconds a user has to wait before sending another message (0-21600) Bots and users with the permission MANAGE_MESSAGES or MANAGE_CHANNEL are unaffected. */ + rate_limit_per_user?: number + /** The parent category id */ + parent_id?: string | null + /** When the last pinned message was pinned */ + last_pin_timestamp?: string +} + +export interface Channel_Create_Payload extends Base_Channel_Create { /** Explicit permission overwrites for members and roles */ permission_overwrites?: Raw_Overwrite[] - /** The name of the channel (2-100 characters) */ - name?: string - /** The channel topic (0-1024 characters) */ - topic?: string - /** Whether the channel is nsfw */ - nsfw?: boolean - /** The id of the last message sent in this channel (may not point to an existing or valid message) */ - last_message_id?: string | null - /** The bitrate (in bits) of the voice channel */ - bitrate?: number - /** The user limit of the voice channel */ - user_limit?: number - /** Amount of seconds a user has to wait before sending another message (0-21600) Bots and users with the permission MANAGE_MESSAGES or MANAGE_CHANNEL are unaffected. */ - rate_limit_per_user?: number - /** The parent category id */ - parent_id?: string | null - /** When the last pinned message was pinned */ - last_pin_timestamp?: string +} + +export interface Channel_Create_Options extends Base_Channel_Create { + /** Explicit permission overwrites for members and roles */ + permission_overwrites?: Overwrite[] } export type Channel_Type = 0 | 1 | 2 | 4 | 5 | 6 diff --git a/types/emoji.ts b/types/emoji.ts index 9240e7cda..b717060c9 100644 --- a/types/emoji.ts +++ b/types/emoji.ts @@ -1,3 +1,5 @@ +import { User } from "../structures/user"; + export interface Emoji { /** emoji id. It will be null for default discord emojis. */ id: string | null diff --git a/types/general.ts b/types/general.ts new file mode 100644 index 000000000..53f878e8e --- /dev/null +++ b/types/general.ts @@ -0,0 +1,2 @@ +export type Image_Size = 16 | 32 | 64 | 128 | 256 | 512 | 1024 | 2048 +export type Image_Formats = 'jpg' | 'jpeg' | 'png' | 'webp' | 'gif' diff --git a/types/guild.ts b/types/guild.ts index 7f45b3bb0..67d5dfbf6 100644 --- a/types/guild.ts +++ b/types/guild.ts @@ -1,8 +1,11 @@ -import { Role } from './role' import { Emoji } from './discord' import { Member } from '../structures/member' +import { User } from '../structures/user' +import { Permission } from './permission' +import { Role } from '../structures/role' +import { Channel } from '../structures/channel' -export interface CreateGuildPayload { +export interface Create_Guild_Payload { /** The guild id */ id: string /** The guild name 2-100 characters */ @@ -67,142 +70,6 @@ export interface CreateGuildPayload { preferred_locale: string } -export interface Guild { - /** The guild id */ - id: string - /** The guild name 2-100 characters */ - name: string - /** The guild icon image hash */ - icon: string | null - /** The guild splash image hash */ - splash: string | null - /** The id of the owner */ - owner_id: string - /** The voice region id for the guild */ - region: string - /** The afk channel id */ - afk_channel_id: string | null - /** AFK Timeout in seconds. */ - afk_timeout: number - /** The verification level required for the guild */ - verification_level: number - /** The roles in the guild */ - roles: Map - /** The custom guild emojis */ - emojis: Emoji[] - /** Enabled guild features */ - features: Guild_Features[] - /** Required MFA level for the guild */ - mfa_level: number - /** The id of the channel to which system mesages are sent */ - system_channel_id: string | null - /** When this guild was joined at */ - joined_at: number - /** Whether this is considered a large guild */ - large: boolean - /** Whether this guild is unavailable */ - unavailable: boolean - /** Total number of members in this guild */ - member_count: number - voice_states: Map - /** Users in the guild */ - members: Map - /** Channels in the guild */ - channels: Map - presences: Map - /** The maximum amount of presences for the guild(the default value, currently 5000 is in effect when null is returned.) */ - max_presences?: number | null - /** The maximum amount of members for the guild */ - max_members?: number - /** The vanity url code for the guild */ - vanity_url_code: string | null - /** The description for the guild */ - description: string | null - /** The banner hash */ - banner: string | null - /** The premium tier */ - premium_tier: number - /** The total number of users currently boosting this server. */ - premium_subscription_count: number - /** The preferred local of this guild only set if guild has the DISCOVERABLE feature, defaults to en-US */ - preferred_locale: string - /** The full URL of the icon from Discords CDN. Undefined when no icon is set. */ - icon_url(size?: Image_Size, format?: Image_Formats): string | undefined - /** The full URL of the splash from Discords CDN. Undefined if no splash is set. */ - splash_url(size?: Image_Size, format?: Image_Formats): string | undefined - /** The full URL of the banner from Discords CDN. Undefined if no banner is set. */ - banner_url(size?: Image_Size, format?: Image_Formats): string | undefined - /** Create a channel in your server. Bot needs MANAGE_CHANNEL permissions in the server. */ - create_channel(name: string, options: ChannelCreate_Options): Promise - /** 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.** - */ - get_channels(): Promise - /** Modify the positions of channels on the guild. Requires MANAGE_CHANNELS permisison. */ - swap_channels(channel_positions: Position_Swap[]): Promise - /** 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.** - */ - get_member(id: string): Promise - /** 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. */ - create_emoji(name: string, image: string, options: Create_Emojis_Options): Promise - /** Modify the given emoji. Requires the MANAGE_EMOJIS permission. */ - edit_emoji(id: string, options: Edit_Emojis_Options): Promise - /** Delete the given emoji. Requires the MANAGE_EMOJIS permission. Returns 204 No Content on success. */ - delete_emoji(id: string, reason?: string): Promise - /** Create a new role for the guild. Requires the MANAGE_ROLES permission. */ - create_role(options: Create_Role_Options): Promise - /** Edi a guild role. Requires the MANAGE_ROLES permission. */ - edit_role(id: string, options: Create_Role_Options): Promise - /** Delete a guild role. Requires the MANAGE_ROLES permission. */ - delete_role(id: string): Promise - /** 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.** - */ - get_roles(): Promise - /** Modify the positions of a set of role objects for the guild. Requires the MANAGE_ROLES permission. */ - swap_roles(role_positions: Position_Swap): Promise - /** Check how many members would be removed from the server in a prune operation. Requires the KICK_MEMBERS permission */ - get_prune_count(days: number): Promise - /** Begin pruning all members in the given time period */ - prune_members(days: number): Promise - /** Returns the audit logs for the guild. Requires VIEW AUDIT LOGS permission */ - get_audit_logs(options: Get_Audit_Logs_Options): Promise - /** Returns the guild embed object. Requires the MANAGE_GUILD permission. */ - get_embed(): Promise - /** Modify a guild embed object for the guild. Requires the MANAGE_GUILD permission. */ - edit_embed(enabled: boolean, channel_id?: string | null): Promise - /** Returns the code and uses of the vanity url for this server if it is enabled. Requires the MANAGE_GUILD permission. */ - get_vanity_url(): Promise - /** Returns a list of integrations for the guild. Requires the MANAGE_GUILD permission. */ - get_integrations(): Promise - /** Modify the behavior and settings of an integration object for the guild. Requires the MANAGE_GUILD permission. */ - edit_integration(id: string, options: Edit_Integration_Options): Promise - /** Delete the attached integration object for the guild with this id. Requires MANAGE_GUILD permission. */ - delete_integration(id: string): Promise - /** Sync an integration. Requires teh MANAGE_GUILD permission. */ - sync_integration(id: string): Promise - /** Returns a list of ban objects for the users banned from this guild. Requires the BAN_MEMBERS permission. */ - get_bans(): Promise - /** 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): Promise - /** Remove the ban for a user. REquires BAN_MEMBERS permission */ - unban(id: string): Promise - /** Modify a guilds settings. Requires the MANAGE_GUILD permission. */ - edit(options: Guild_Edit_Options): Promise - /** Get all the invites for this guild. Requires MANAGE_GUILD permission */ - get_invites(): Promise - /** Leave a guild */ - leave(): Promise - /** 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. */ - get_voice_regions(): Promise - /** Returns a list of guild webhooks objects. Requires the MANAGE_WEBHOOKs permission. */ - get_webhooks(): Promise -} - export type Guild_Features = | `INVITE_SPLASH` | `VIP_REGIONS` @@ -456,42 +323,8 @@ export enum AuditLogs { INTEGRATION_DELETE } -export type Image_Size = 16 | 32 | 64 | 128 | 256 | 512 | 1024 | 2048 -export type Image_Formats = 'jpg' | 'jpeg' | 'png' | 'webp' | 'gif' export type ChannelType = 'text' | 'dm' | 'news' | 'voice' | 'category' | 'store' -export type Permission = - | `CREATE_INSTANT_INVITE` - | `KICK_MEMBERS` - | `BAN_MEMBERS` - | `ADMINISTRATOR` - | `MANAGE_CHANNELS` - | `MANAGE_GUILD` - | `ADD_REACTIONS` - | `VIEW_AUDIT_LOG` - | `VIEW_CHANNEL` - | `SEND_MESSAGES` - | `SEND_TTS_MESSAGES` - | `MANAGE_MESSAGES` - | `EMBED_LINKS` - | `ATTACH_FILES` - | `READ_MESSAGE_HISTORY` - | `MENTION_EVERYONE` - | `USE_EXTERNAL_EMOJIS` - | `CONNECT` - | `SPEAK` - | `MUTE_MEMBERS` - | `DEAFEN_MEMBERS` - | `MOVE_MEMBERS` - | `USE_VAD` - | `PRIORITY_SPEAKER` - | `STREAM` - | `CHANGE_NICKNAME` - | `MANAGE_NICKNAMES` - | `MANAGE_ROLES` - | `MANAGE_WEBHOOKS` - | `MANAGE_EMOJIS` - export interface Overwrite { /** The role or user id */ id: string @@ -523,39 +356,6 @@ export enum ChannelTypes { store } -export enum Permissions { - CREATE_INSTANT_INVITE = 0x00000001, - KICK_MEMBERS = 0x00000002, - BAN_MEMBERS = 0x00000004, - ADMINISTRATOR = 0x00000008, - MANAGE_CHANNELS = 0x00000010, - MANAGE_GUILD = 0x00000020, - ADD_REACTIONS = 0x00000040, - VIEW_AUDIT_LOG = 0x00000080, - VIEW_CHANNEL = 0x00000400, - SEND_MESSAGES = 0x00000800, - SEND_TTS_MESSAGES = 0x00001000, - MANAGE_MESSAGES = 0x00002000, - EMBED_LINKS = 0x00004000, - ATTACH_FILES = 0x00008000, - READ_MESSAGE_HISTORY = 0x00010000, - MENTION_EVERYONE = 0x00020000, - USE_EXTERNAL_EMOJIS = 0x00040000, - CONNECT = 0x00100000, - SPEAK = 0x00200000, - MUTE_MEMBERS = 0x00400000, - DEAFEN_MEMBERS = 0x00800000, - MOVE_MEMBERS = 0x01000000, - USE_VAD = 0x02000000, - PRIORITY_SPEAKER = 0x00000100, - STREAM = 0x00000200, - CHANGE_NICKNAME = 0x04000000, - MANAGE_NICKNAMES = 0x08000000, - MANAGE_ROLES = 0x10000000, - MANAGE_WEBHOOKS = 0x20000000, - MANAGE_EMOJIS = 0x40000000 -} - export interface ChannelCreate_Options { /** The type of the channel */ type?: ChannelType diff --git a/types/member.ts b/types/member.ts new file mode 100644 index 000000000..5d841ecd7 --- /dev/null +++ b/types/member.ts @@ -0,0 +1,31 @@ +import { User_Payload } from "../structures/user"; + +export interface Edit_Member_Options { + /** Value to set users nickname to. Requires MANAGE_NICKNAMES permission. */ + nick?: string + /** Array of role ids the member is assigned. Requires MANAGE_ROLES permission. */ + roles?: string[] + /** Whether the user is muted in voice channels. Requires MUTE_MEMBERS permission. */ + mute?: boolean + /** Whether the user is deafened in voice channels. Requires DEAFEN_MEMBERS permission. */ + deaf?: boolean + /** The id of the channel to move user to if they are connected to voice. To kick the user from their current channel, set to null. Requires MOVE_MEMBERS permission. When moving members to channels, must have permissions to both CONNECT to the channel and have the MOVE_MEMBER permission. */ + channel_id?: string | null +} + +export interface Member_Create_Payload { + /** The user this guild member represents */ + user: User_Payload + /** The user's guild nickname if one is set. */ + nick?: string + /** Array of role ids that the member has */ + roles: string[] + /** When the user joined the guild. */ + joined_at: string + /** When the user used their nitro boost on the server. */ + premium_since?: string + /** Whether the user is deafened in voice channels */ + deaf: boolean + /** Whether the user is muted in voice channels */ + mute: boolean +} diff --git a/types/message.ts b/types/message.ts index 099a7a95a..f1b1ef4b6 100644 --- a/types/message.ts +++ b/types/message.ts @@ -1,5 +1,6 @@ import { Member } from '../structures/member' -import { ChannelType } from './guild' +import { ChannelType, User_Data } from './guild' +import { User } from '../structures/user' export interface MentionedUser extends User { member: Member @@ -223,7 +224,7 @@ export interface Message_Create_Options { /** The id of the guild the message was sent in */ guild_id?: string /** The author of this message (not guaranteed to be a valid user such as a webhook.) */ - author: User + author: User_Data /** The member properties for this message's author. Can be partial. */ member?: Member /** The contents of the message */ diff --git a/types/permission.ts b/types/permission.ts new file mode 100644 index 000000000..559e880b6 --- /dev/null +++ b/types/permission.ts @@ -0,0 +1,64 @@ +export type Permission = + | `CREATE_INSTANT_INVITE` + | `KICK_MEMBERS` + | `BAN_MEMBERS` + | `ADMINISTRATOR` + | `MANAGE_CHANNELS` + | `MANAGE_GUILD` + | `ADD_REACTIONS` + | `VIEW_AUDIT_LOG` + | `VIEW_CHANNEL` + | `SEND_MESSAGES` + | `SEND_TTS_MESSAGES` + | `MANAGE_MESSAGES` + | `EMBED_LINKS` + | `ATTACH_FILES` + | `READ_MESSAGE_HISTORY` + | `MENTION_EVERYONE` + | `USE_EXTERNAL_EMOJIS` + | `CONNECT` + | `SPEAK` + | `MUTE_MEMBERS` + | `DEAFEN_MEMBERS` + | `MOVE_MEMBERS` + | `USE_VAD` + | `PRIORITY_SPEAKER` + | `STREAM` + | `CHANGE_NICKNAME` + | `MANAGE_NICKNAMES` + | `MANAGE_ROLES` + | `MANAGE_WEBHOOKS` + | `MANAGE_EMOJIS` + +export enum Permissions { + CREATE_INSTANT_INVITE = 0x00000001, + KICK_MEMBERS = 0x00000002, + BAN_MEMBERS = 0x00000004, + ADMINISTRATOR = 0x00000008, + MANAGE_CHANNELS = 0x00000010, + MANAGE_GUILD = 0x00000020, + ADD_REACTIONS = 0x00000040, + VIEW_AUDIT_LOG = 0x00000080, + VIEW_CHANNEL = 0x00000400, + SEND_MESSAGES = 0x00000800, + SEND_TTS_MESSAGES = 0x00001000, + MANAGE_MESSAGES = 0x00002000, + EMBED_LINKS = 0x00004000, + ATTACH_FILES = 0x00008000, + READ_MESSAGE_HISTORY = 0x00010000, + MENTION_EVERYONE = 0x00020000, + USE_EXTERNAL_EMOJIS = 0x00040000, + CONNECT = 0x00100000, + SPEAK = 0x00200000, + MUTE_MEMBERS = 0x00400000, + DEAFEN_MEMBERS = 0x00800000, + MOVE_MEMBERS = 0x01000000, + USE_VAD = 0x02000000, + PRIORITY_SPEAKER = 0x00000100, + STREAM = 0x00000200, + CHANGE_NICKNAME = 0x04000000, + MANAGE_NICKNAMES = 0x08000000, + MANAGE_ROLES = 0x10000000, + MANAGE_WEBHOOKS = 0x20000000, + MANAGE_EMOJIS = 0x40000000 +} diff --git a/types/role.ts b/types/role.ts index b9628be43..c442169c8 100644 --- a/types/role.ts +++ b/types/role.ts @@ -16,24 +16,3 @@ export interface Role_Data { /** whether this role is mentionable */ mentionable: boolean } - -export interface Role { - /** role id */ - id: string - /** role name */ - name: string - /** integer representation of hexadecimal color code */ - color: number - /** if this role is pinned in the user listing */ - hoist: boolean - /** position of this role */ - position: number - /** permission bit set */ - permissions: number - /** whether this role is managed by an integration */ - managed: boolean - /** whether this role is mentionable */ - mentionable: boolean - /** The @ mention of the role in a string. */ - mention(): string -} diff --git a/utils/cache.ts b/utils/cache.ts index e0d66532d..22ad15263 100644 --- a/utils/cache.ts +++ b/utils/cache.ts @@ -1,4 +1,6 @@ -import { Guild } from "../types/guild"; +import { User } from "../structures/user"; +import { Channel } from "../structures/channel"; +import { Guild } from "../structures/guild"; export const cache = { guilds: new Map(), diff --git a/utils/cdn.ts b/utils/cdn.ts index ae221007f..0e8ab4425 100644 --- a/utils/cdn.ts +++ b/utils/cdn.ts @@ -1,5 +1,5 @@ import { ImageSize, ImageFormats } from '../structures/guild' -export const formatImageURL = (url: string, size: ImageSize = 128, format?: ImageFormats) => { +export const format_image_url = (url: string, size: ImageSize = 128, format?: ImageFormats) => { return `${url}.${format || url.includes('/a_') ? 'gif' : 'jpg'}/?size=${size}` } From a665a62796dadb161283f0bc1f111522d65c36ea Mon Sep 17 00:00:00 2001 From: Skillz Date: Wed, 19 Feb 2020 12:33:05 -0500 Subject: [PATCH 16/18] cleanup on isle mess --- structures/channel.ts | 4 +- structures/guild.ts | 529 ++++++++++++++++++----------------- structures/member.ts | 4 +- structures/user.ts | 4 +- structures/voiceState.ts | 3 - types/{general.ts => cdn.ts} | 0 types/guild.ts | 35 ++- 7 files changed, 304 insertions(+), 275 deletions(-) delete mode 100644 structures/voiceState.ts rename types/{general.ts => cdn.ts} (100%) diff --git a/structures/channel.ts b/structures/channel.ts index 1e17511f3..eff73c329 100644 --- a/structures/channel.ts +++ b/structures/channel.ts @@ -1,10 +1,10 @@ import { Channel_Create_Options, Channel_Types, Get_Messages_After, Get_Messages_Around, Get_Messages, Get_Messages_Before, MessageContent, Create_Invite_Options } from '../types/channel' -import { Permission, Permissions } from '../types/guild' import Client from '../module/client' import { endpoints } from '../constants/discord' import { create_message, Message } from './message' -import { Guild } from './guild' import { Message_Create_Options } from '../types/message' +import { Permission, Permissions } from '../types/permission' +import { Guild } from '../types/guild' export const create_channel = (data: Channel_Create_Options, guild: Guild, client: Client) => { const base_channel = { diff --git a/structures/guild.ts b/structures/guild.ts index 501a967ba..e3b6db5d7 100644 --- a/structures/guild.ts +++ b/structures/guild.ts @@ -4,10 +4,7 @@ import { format_image_url } from '../utils/cdn' import { Create_Guild_Payload, ChannelTypes, - Permissions, PrunePayload, - Image_Size, - Image_Formats, Position_Swap, Get_Audit_Logs_Options, Edit_Integration_Options, @@ -17,270 +14,276 @@ import { Edit_Emojis_Options, Create_Role_Options } from '../types/guild' -import { create_role } from './role' +import { create_role, Role } from './role' import { create_member } from './member' import { create_channel } from './channel' import { Channel_Create_Options } from '../types/channel' +import { Image_Size, Image_Formats } from '../types/cdn' +import { Permissions } from '../types/permission' -export const create_guild = (data: Create_Guild_Payload, client: Client) => ({ - /** The raw create guild payload data. */ - raw: () => data, - /** The guild id */ - id: () => data.id, - /** The guild name. 2-100 characters */ - name: () => data.name, - /** The guild icon image hash */ - icon: () => data.icon, - /** The guild splash image hash */ - splash: () => data.splash, - /** The id of the guild owner */ - owner_id: () => data.owner_id, - /** The voice region id for the guild */ - region: () => data.region, - /** The afk channel id */ - afk_channel_id: () => data.afk_channel_id, - /** The AFK timeout in seconds */ - afk_timeout: () => data.afk_timeout, - /** The verification level required for the guild */ - verification_level: () => data.verification_level, - /** The roles in the guild */ - roles: new Map(data.roles.map(r => [r.id, create_role(r)])), - /** The custom guild emojis */ - emojis: () => data.emojis, - /** The enabled guild features. */ - features: () => data.features, - /** The required MFA level for the guild. */ - mfa_level: () => data.mfa_level, - /** The id of the channel to which system messages are sent. */ - system_channel_id: () => data.system_channel_id, - /** When this guild was joined at. */ - joined_at: Date.parse(data.joined_at), - /** Whether this is considered a large guild. */ - large: () => data.large, - /** Whether this guild is unavailable */ - unavailable: () => data.unavailable, - /** The total number of members in this guild. */ - member_count: () => data.member_count, - /** The current open voice states in the guild. */ - voice_states: new Map(data.voice_states.map(vs => [vs.id, create_voice_state(vs)])), - /** The users in this guild. */ - members: new Map(data.members.map(m => [m.id, create_member(m)])), - /** The channels in the guild */ - channels: new Map(data.channels.map(c => [c.id, create_channel(c, data, client)])), - /** The presences of all the users in the guild. */ - presences: new Map(data.presences.map(p => [p.id, create_presence(p)])), - /** The maximum amount of presences for the guild(the default value, currently 5000 is in effect when null is returned.) */ - max_presences: () => data.max_presences, - /** The maximum amount of members for the guild */ - max_members: () => data.max_members, - /** The vanity url code for the guild */ - vanity_url_code: () => data.vanity_url_code, - /** The description for the guild */ - description: () => data.description, - /** The banner hash */ - banner: () => data.banner, - /** The current premium tier of the guild */ - premium_tier: () => data.premium_tier, - /** The total number of users currently boosting this server. */ - premium_subscription_count: () => data.premium_subscription_count, - /** The preferred locale of this guild only set if the guild has the DISCOVERABLE feature, defaults to en-US */ - preferred_locale: () => data.preferred_locale, - /** The full URL of the icon from Discords CDN. Undefined when no icon is set. */ - icon_url: (size: Image_Size = 128, format?: Image_Formats) => - data.icon ? format_image_url(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. */ - splash_url: (size: Image_Size = 128, format?: Image_Formats) => - data.splash ? format_image_url(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. */ - banner_url: (size: Image_Size = 128, format?: Image_Formats) => - data.banner ? format_image_url(endpoints.GUILD_BANNER(data.id, data.banner), size, format) : undefined, - /** Create a channel in your server. Bot needs MANAGE_CHANNEL permissions in the server. */ - create_channel: (name: string, options: Channel_Create_Options) => { - // TODO: Check if the bot has `MANAGE_CHANNELS` permission before making a channel - return client.RequestManager.post(endpoints.GUILD_CHANNELS(data.id), { - name, - type: options?.type ? ChannelTypes[options.type] : undefined, - 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 - }) - }, - /** 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.** - */ - get_channels: () => { - return client.RequestManager.get(endpoints.GUILD_CHANNELS(data.id)) - }, - /** Modify the positions of channels on the guild. Requires MANAGE_CHANNELS permisison. */ - swap_channels: (channel_positions: Position_Swap[]) => { - if (channel_positions.length < 2) throw 'You must provide atleast two channels to be swapped.' - return client.RequestManager.patch(endpoints.GUILD_CHANNELS(data.id), channel_positions) - }, - /** 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.** - */ - get_member: (id: string) => { - return client.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. */ - create_emoji: (name: string, image: string, options: Create_Emojis_Options) => { - // TODO: Check if the bot has `MANAGE_EMOJIS` permission - return client.RequestManager.post(endpoints.GUILD_EMOJIS(data.id), { - ...options, - name, - image - }) - }, - /** Modify the given emoji. Requires the MANAGE_EMOJIS permission. */ - edit_emoji: (id: string, options: Edit_Emojis_Options) => { - // TODO: check if the bot has `MANAGE_EMOJIS` permission - return client.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. */ - delete_emoji: (id: string, reason?: string) => { - // TODO: check if the bot has `MANAGE_EMOJIS` permission - return client.RequestManager.delete(endpoints.GUILD_EMOJI(data.id, id), { reason }) - }, - /** Create a new role for the guild. Requires the MANAGE_ROLES permission. */ - create_role: async (options: Create_Role_Options) => { - // TODO: check if the bot has the `MANAGE_ROLES` permission. - const role = await client.RequestManager.post(endpoints.GUILD_ROLES(data.id), { - ...options, - permissions: options.permissions?.map(perm => Permissions[perm]) - }) - // TODO: cache this role +export const create_guild = (data: Create_Guild_Payload, client: Client) => { + const guild = { + /** The raw create guild payload data. */ + raw: () => data, + /** The guild id */ + id: () => data.id, + /** The guild name. 2-100 characters */ + name: () => data.name, + /** The guild icon image hash */ + icon: () => data.icon, + /** The guild splash image hash */ + splash: () => data.splash, + /** The id of the guild owner */ + owner_id: () => data.owner_id, + /** The voice region id for the guild */ + region: () => data.region, + /** The afk channel id */ + afk_channel_id: () => data.afk_channel_id, + /** The AFK timeout in seconds */ + afk_timeout: () => data.afk_timeout, + /** The verification level required for the guild */ + verification_level: () => data.verification_level, + /** The roles in the guild */ + roles: new Map(), + /** The custom guild emojis */ + emojis: () => data.emojis, + /** The enabled guild features. */ + features: () => data.features, + /** The required MFA level for the guild. */ + mfa_level: () => data.mfa_level, + /** The id of the channel to which system messages are sent. */ + system_channel_id: () => data.system_channel_id, + /** When this guild was joined at. */ + joined_at: Date.parse(data.joined_at), + /** Whether this is considered a large guild. */ + large: () => data.large, + /** Whether this guild is unavailable */ + unavailable: () => data.unavailable, + /** The total number of members in this guild. */ + member_count: () => data.member_count, + /** The current open voice states in the guild. */ + voice_states: () => data.voice_states, + /** The users in this guild. */ + members: new Map(data.members.map(m => [m.id, create_member(m)])), + /** The channels in the guild */ + channels: new Map(data.channels.map(c => [c.id, create_channel(c, data, client)])), + /** The presences of all the users in the guild. */ + presences: new Map(data.presences.map(p => [p.id, create_presence(p)])), + /** The maximum amount of presences for the guild(the default value, currently 5000 is in effect when null is returned.) */ + max_presences: () => data.max_presences, + /** The maximum amount of members for the guild */ + max_members: () => data.max_members, + /** The vanity url code for the guild */ + vanity_url_code: () => data.vanity_url_code, + /** The description for the guild */ + description: () => data.description, + /** The banner hash */ + banner: () => data.banner, + /** The current premium tier of the guild */ + premium_tier: () => data.premium_tier, + /** The total number of users currently boosting this server. */ + premium_subscription_count: () => data.premium_subscription_count, + /** The preferred locale of this guild only set if the guild has the DISCOVERABLE feature, defaults to en-US */ + preferred_locale: () => data.preferred_locale, + /** The full URL of the icon from Discords CDN. Undefined when no icon is set. */ + icon_url: (size: Image_Size = 128, format?: Image_Formats) => + data.icon ? format_image_url(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. */ + splash_url: (size: Image_Size = 128, format?: Image_Formats) => + data.splash ? format_image_url(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. */ + banner_url: (size: Image_Size = 128, format?: Image_Formats) => + data.banner ? format_image_url(endpoints.GUILD_BANNER(data.id, data.banner), size, format) : undefined, + /** Create a channel in your server. Bot needs MANAGE_CHANNEL permissions in the server. */ + create_channel: (name: string, options: Channel_Create_Options) => { + // TODO: Check if the bot has `MANAGE_CHANNELS` permission before making a channel + return client.RequestManager.post(endpoints.GUILD_CHANNELS(data.id), { + name, + type: options?.type ? ChannelTypes[options.type] : undefined, + 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 + }) + }, + /** 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.** + */ + get_channels: () => { + return client.RequestManager.get(endpoints.GUILD_CHANNELS(data.id)) + }, + /** Modify the positions of channels on the guild. Requires MANAGE_CHANNELS permisison. */ + swap_channels: (channel_positions: Position_Swap[]) => { + if (channel_positions.length < 2) throw 'You must provide atleast two channels to be swapped.' + return client.RequestManager.patch(endpoints.GUILD_CHANNELS(data.id), channel_positions) + }, + /** 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.** + */ + get_member: (id: string) => { + return client.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. */ + create_emoji: (name: string, image: string, options: Create_Emojis_Options) => { + // TODO: Check if the bot has `MANAGE_EMOJIS` permission + return client.RequestManager.post(endpoints.GUILD_EMOJIS(data.id), { + ...options, + name, + image + }) + }, + /** Modify the given emoji. Requires the MANAGE_EMOJIS permission. */ + edit_emoji: (id: string, options: Edit_Emojis_Options) => { + // TODO: check if the bot has `MANAGE_EMOJIS` permission + return client.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. */ + delete_emoji: (id: string, reason?: string) => { + // TODO: check if the bot has `MANAGE_EMOJIS` permission + return client.RequestManager.delete(endpoints.GUILD_EMOJI(data.id, id), { reason }) + }, + /** Create a new role for the guild. Requires the MANAGE_ROLES permission. */ + create_role: async (options: Create_Role_Options) => { + // TODO: check if the bot has the `MANAGE_ROLES` permission. + const role = await client.RequestManager.post(endpoints.GUILD_ROLES(data.id), { + ...options, + permissions: options.permissions?.map(perm => Permissions[perm]) + }) + // TODO: cache this role - return role - }, - /** Edit a guild role. Requires the MANAGE_ROLES permission. */ - edit_role: (id: string, options: Create_Role_Options) => { - // TODO: check if the bot has the `MANAGE_ROLES` permission. - return client.RequestManager.patch(endpoints.GUILD_ROLE(data.id, id), options) - }, - /** Delete a guild role. Requires the MANAGE_ROLES permission. */ - delete_role: (id: string) => { - // TODO: check if the bot has the `MANAGE_ROLES` permission. - return client.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.** - */ - get_roles: () => { - // TODO: check if the bot has the `MANAGE_ROLES` permission. - return client.RequestManager.get(endpoints.GUILD_ROLES(data.id)) - }, - /** Modify the positions of a set of role objects for the guild. Requires the MANAGE_ROLES permission. */ - swap_roles: (rolePositons: Position_Swap) => { - // TODO: check if the bot has the `MANAGE_ROLES` permission. - return client.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 */ - get_prune_count: async (days: number) => { - if (days < 1) throw `The number of days to count prune for must be 1 or more.` - // TODO: check if the bot has `KICK_MEMBERS` permission - const result = (await client.RequestManager.get(endpoints.GUILD_PRUNE(data.id), { days })) as PrunePayload - return result.pruned - }, - /** Begin pruning all members in the given time period */ - prune_members: (days: number) => { - if (days < 1) throw `The number of days must be 1 or more.` - // TODO: check if the bot has `KICK_MEMBERS` permission. - return client.RequestManager.post(endpoints.GUILD_PRUNE(data.id), { days }) - }, - // TODO: REQUEST THIS OVER WEBSOCKET WITH GET_GUILD_MEMBERS ENDPOINT - // fetch_all_members: () => { - // }, - /** Returns the audit logs for the guild. Requires VIEW AUDIT LOGS permission */ - get_audit_logs: (options: Get_Audit_Logs_Options) => { - // TODO: check if the bot has VIEW_AUDIT_LOGS permission - return client.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. */ - get_embed: () => { - // TODO: check if the bot has the MANAGE_GUILD permission - return client.RequestManager.get(endpoints.GUILD_EMBED(data.id)) - }, - /** Modify a guild embed object for the guild. Requires the MANAGE_GUILD permission. */ - edit_embed: (enabled: boolean, channel_id?: string | null) => { - // TODO: Requires the MANAGE_GUILD permission. - return client.RequestManager.patch(endpoints.GUILD_EMBED(data.id), { enabled, channel_id }) - }, - /** Returns the code and uses of the vanity url for this server if it is enabled. Requires the MANAGE_GUILD permission. */ - get_vanity_url: () => { - return client.RequestManager.get(endpoints.GUILD_VANITY_URL(data.id)) - }, - /** Returns a list of integrations for the guild. Requires the MANAGE_GUILD permission. */ - get_integrations: () => { - // TODO: requires the MANAGE_GUILD permission - return client.RequestManager.get(endpoints.GUILD_INTEGRATIONS(data.id)) - }, - /** Modify the behavior and settings of an integration object for the guild. Requires the MANAGE_GUILD permission. */ - edit_integration: (id: string, options: Edit_Integration_Options) => { - // TODO: requires the MANAGE_GUILD permission - return client.RequestManager.patch(endpoints.GUILD_INTEGRATION(data.id, id), options) - }, - /** Delete the attached integration object for the guild with this id. Requires MANAGE_GUILD permission. */ - delete_integration: (id: string) => { - // TODO: requires the MANAGE_GUILD permission - return client.RequestManager.delete(endpoints.GUILD_INTEGRATION(data.id, id)) - }, - /** Sync an integration. Requires teh MANAGE_GUILD permission. */ - sync_integration: (id: string) => { - // TODO: requires MANAGE_GUILD - return client.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. */ - get_bans: () => { - // TODO: requires the BAN_MEMBERS permission - return client.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) => { - // TODO: requires the BAN_MEMBERS permission - return client.RequestManager.put(endpoints.GUILD_BAN(data.id, id), options) - }, - /** Remove the ban for a user. REquires BAN_MEMBERS permission */ - unban: (id: string) => { - // TODO: requires the BAN_MEMBERS permission - return client.RequestManager.delete(endpoints.GUILD_BAN(data.id, id)) - }, - /** Modify a guilds settings. Requires the MANAGE_GUILD permission. */ - edit: (options: Guild_Edit_Options) => { - // TODO: requires the MANAGE_GUILD permission - return client.RequestManager.patch(endpoints.GUILD(data.id), options) - }, - /** Get all the invites for this guild. Requires MANAGE_GUILD permission */ - et_invites: () => { - // TODO: requires MANAGE_GUILD permission - return client.RequestManager.get(endpoints.GUILD_INVITES(data.id)) - }, - /** Leave a guild */ - leave: () => { - return client.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. */ - get_voice_regions: () => { - return client.RequestManager.get(endpoints.GUILD_REGIONS(data.id)) - }, - /** Returns a list of guild webhooks objects. Requires the MANAGE_WEBHOOKs permission. */ - get_webhooks: () => { - // TODO: requires MANAGE_WEBHOOKS - return client.RequestManager.get(endpoints.GUILD_WEBHOOKS(data.id)) + return role + }, + /** Edit a guild role. Requires the MANAGE_ROLES permission. */ + edit_role: (id: string, options: Create_Role_Options) => { + // TODO: check if the bot has the `MANAGE_ROLES` permission. + return client.RequestManager.patch(endpoints.GUILD_ROLE(data.id, id), options) + }, + /** Delete a guild role. Requires the MANAGE_ROLES permission. */ + delete_role: (id: string) => { + // TODO: check if the bot has the `MANAGE_ROLES` permission. + return client.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.** + */ + get_roles: () => { + // TODO: check if the bot has the `MANAGE_ROLES` permission. + return client.RequestManager.get(endpoints.GUILD_ROLES(data.id)) + }, + /** Modify the positions of a set of role objects for the guild. Requires the MANAGE_ROLES permission. */ + swap_roles: (rolePositons: Position_Swap) => { + // TODO: check if the bot has the `MANAGE_ROLES` permission. + return client.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 */ + get_prune_count: async (days: number) => { + if (days < 1) throw `The number of days to count prune for must be 1 or more.` + // TODO: check if the bot has `KICK_MEMBERS` permission + const result = (await client.RequestManager.get(endpoints.GUILD_PRUNE(data.id), { days })) as PrunePayload + return result.pruned + }, + /** Begin pruning all members in the given time period */ + prune_members: (days: number) => { + if (days < 1) throw `The number of days must be 1 or more.` + // TODO: check if the bot has `KICK_MEMBERS` permission. + return client.RequestManager.post(endpoints.GUILD_PRUNE(data.id), { days }) + }, + // TODO: REQUEST THIS OVER WEBSOCKET WITH GET_GUILD_MEMBERS ENDPOINT + // fetch_all_members: () => { + // }, + /** Returns the audit logs for the guild. Requires VIEW AUDIT LOGS permission */ + get_audit_logs: (options: Get_Audit_Logs_Options) => { + // TODO: check if the bot has VIEW_AUDIT_LOGS permission + return client.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. */ + get_embed: () => { + // TODO: check if the bot has the MANAGE_GUILD permission + return client.RequestManager.get(endpoints.GUILD_EMBED(data.id)) + }, + /** Modify a guild embed object for the guild. Requires the MANAGE_GUILD permission. */ + edit_embed: (enabled: boolean, channel_id?: string | null) => { + // TODO: Requires the MANAGE_GUILD permission. + return client.RequestManager.patch(endpoints.GUILD_EMBED(data.id), { enabled, channel_id }) + }, + /** Returns the code and uses of the vanity url for this server if it is enabled. Requires the MANAGE_GUILD permission. */ + get_vanity_url: () => { + return client.RequestManager.get(endpoints.GUILD_VANITY_URL(data.id)) + }, + /** Returns a list of integrations for the guild. Requires the MANAGE_GUILD permission. */ + get_integrations: () => { + // TODO: requires the MANAGE_GUILD permission + return client.RequestManager.get(endpoints.GUILD_INTEGRATIONS(data.id)) + }, + /** Modify the behavior and settings of an integration object for the guild. Requires the MANAGE_GUILD permission. */ + edit_integration: (id: string, options: Edit_Integration_Options) => { + // TODO: requires the MANAGE_GUILD permission + return client.RequestManager.patch(endpoints.GUILD_INTEGRATION(data.id, id), options) + }, + /** Delete the attached integration object for the guild with this id. Requires MANAGE_GUILD permission. */ + delete_integration: (id: string) => { + // TODO: requires the MANAGE_GUILD permission + return client.RequestManager.delete(endpoints.GUILD_INTEGRATION(data.id, id)) + }, + /** Sync an integration. Requires teh MANAGE_GUILD permission. */ + sync_integration: (id: string) => { + // TODO: requires MANAGE_GUILD + return client.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. */ + get_bans: () => { + // TODO: requires the BAN_MEMBERS permission + return client.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) => { + // TODO: requires the BAN_MEMBERS permission + return client.RequestManager.put(endpoints.GUILD_BAN(data.id, id), options) + }, + /** Remove the ban for a user. REquires BAN_MEMBERS permission */ + unban: (id: string) => { + // TODO: requires the BAN_MEMBERS permission + return client.RequestManager.delete(endpoints.GUILD_BAN(data.id, id)) + }, + /** Modify a guilds settings. Requires the MANAGE_GUILD permission. */ + edit: (options: Guild_Edit_Options) => { + // TODO: requires the MANAGE_GUILD permission + return client.RequestManager.patch(endpoints.GUILD(data.id), options) + }, + /** Get all the invites for this guild. Requires MANAGE_GUILD permission */ + et_invites: () => { + // TODO: requires MANAGE_GUILD permission + return client.RequestManager.get(endpoints.GUILD_INVITES(data.id)) + }, + /** Leave a guild */ + leave: () => { + return client.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. */ + get_voice_regions: () => { + return client.RequestManager.get(endpoints.GUILD_REGIONS(data.id)) + }, + /** Returns a list of guild webhooks objects. Requires the MANAGE_WEBHOOKs permission. */ + get_webhooks: () => { + // TODO: requires MANAGE_WEBHOOKS + return client.RequestManager.get(endpoints.GUILD_WEBHOOKS(data.id)) + } } -}) -export type Guild = ReturnType + data.roles.forEach(r => guild.roles.set(r.id, create_role(r))) + + return guild +} diff --git a/structures/member.ts b/structures/member.ts index 2e1ed7b5a..8127246b1 100644 --- a/structures/member.ts +++ b/structures/member.ts @@ -2,13 +2,13 @@ import Client from '../module/client' import { endpoints } from '../constants/discord' import { format_image_url } from '../utils/cdn' import { Member_Create_Payload, Edit_Member_Options } from '../types/member' -import { Image_Size, Image_Formats } from '../types/general' +import { Image_Size, Image_Formats } from '../types/cdn' import { Permission, Permissions } from '../types/permission' import { cache } from '../utils/cache' export const create_member = (data: Member_Create_Payload, guild_id: string, client: Client) => { const guild = cache.guilds.get(guild_id) - + return { /** The complete raw data from the member create payload */ raw: () => data, diff --git a/structures/user.ts b/structures/user.ts index 983620fd1..4c9e9c01d 100644 --- a/structures/user.ts +++ b/structures/user.ts @@ -1,6 +1,6 @@ -import { Image_Formats, Image_Size } from '../types/guild' import { format_image_url } from '../utils/cdn' import { endpoints } from '../constants/discord' +import { Image_Size, Image_Formats } from '../types/cdn' export interface User_Payload { /** The user's id */ @@ -44,7 +44,7 @@ export const create_user = (data: User_Payload) => ({ discriminator: () => data.discriminator, tag: () => `${data.username}#${data.discriminator}`, avatar: () => data.avatar, - avatar_url: (size: Image_Size, format: Image_Formats) => + avatar_url: (size: Image_Size = 128, format?: Image_Formats) => data.avatar ? format_image_url(endpoints.USER_AVATAR(data.id, data.avatar), size, format) : endpoints.USER_DEFAULT_AVATAR(Number(data.discriminator) % 5), diff --git a/structures/voiceState.ts b/structures/voiceState.ts deleted file mode 100644 index ef78632d1..000000000 --- a/structures/voiceState.ts +++ /dev/null @@ -1,3 +0,0 @@ -export const createVoiceState = (data: unknown) => { - console.log(data) -} diff --git a/types/general.ts b/types/cdn.ts similarity index 100% rename from types/general.ts rename to types/cdn.ts diff --git a/types/guild.ts b/types/guild.ts index 67d5dfbf6..b43cd271c 100644 --- a/types/guild.ts +++ b/types/guild.ts @@ -2,8 +2,10 @@ import { Emoji } from './discord' import { Member } from '../structures/member' import { User } from '../structures/user' import { Permission } from './permission' -import { Role } from '../structures/role' import { Channel } from '../structures/channel' +import { create_guild } from '../structures/guild' +import { Role_Data } from './role' +import { Member_Create_Payload } from './member' export interface Create_Guild_Payload { /** The guild id */ @@ -29,7 +31,7 @@ export interface Create_Guild_Payload { /** The verification level required for the guild */ verification_level: number /** The roles in the guild */ - roles: Role[] + roles: Role_Data[] /** The custom guild emojis */ emojis: Emoji[] /** Enabled guild features */ @@ -46,7 +48,7 @@ export interface Create_Guild_Payload { unavailable: boolean /** Total number of members in this guild */ member_count: number - voice_states: VoiceState[] + voice_states: Voice_State[] /** Users in the guild */ members: Member[] /** Channels in the guild */ @@ -404,3 +406,30 @@ export interface Create_Role_Options { export interface PrunePayload { pruned: number } + +export interface Voice_State { + /** the guild id this voice state is for */ + guild_id?: string + /** the channel id this user is connected to */ + channel_id: string | null + /** the user id this voice state is for */ + user_id: string + /** the guild member this voice state is for */ + member?: Member_Create_Payload + /** the session id for this voice state */ + session_id: string + /** whether this user is deafened by the server */ + deaf: boolean + /** whether this user is muted by the server */ + mute: boolean + /** whether this user is locally deafened */ + self_deaf: boolean + /** whether this user is locally muted */ + self_mute: boolean + /** whether this user is streaming using "Go Live" */ + self_stream?: boolean + /** whether this user is muted by the current user */ + suppress: boolean +} + +export type Guild = ReturnType From 6e9a271bdd15b36c2d0608dc62e5b2b5a2680009 Mon Sep 17 00:00:00 2001 From: Skillz Date: Wed, 19 Feb 2020 14:09:08 -0500 Subject: [PATCH 17/18] more fixes --- structures/channel.ts | 24 +++++++++++++++++------- structures/guild.ts | 12 +++++++----- structures/member.ts | 7 ++----- types/channel.ts | 3 +++ types/discord.ts | 4 +++- types/guild.ts | 28 +++++++++++++++++++++++----- types/member.ts | 3 +++ utils/cache.ts | 4 ++-- 8 files changed, 60 insertions(+), 25 deletions(-) diff --git a/structures/channel.ts b/structures/channel.ts index eff73c329..c66f60d35 100644 --- a/structures/channel.ts +++ b/structures/channel.ts @@ -1,4 +1,13 @@ -import { Channel_Create_Options, Channel_Types, Get_Messages_After, Get_Messages_Around, Get_Messages, Get_Messages_Before, MessageContent, Create_Invite_Options } from '../types/channel' +import { + Channel_Create_Options, + Channel_Types, + Get_Messages_After, + Get_Messages_Around, + Get_Messages, + Get_Messages_Before, + MessageContent, + Create_Invite_Options +} from '../types/channel' import Client from '../module/client' import { endpoints } from '../constants/discord' import { create_message, Message } from './message' @@ -32,12 +41,15 @@ export const create_channel = (data: Channel_Create_Options, guild: Guild, clien // TODO: check if the user has VIEW_CHANNEL and READ_MESSAGE_HISTORY if (options?.limit && options.limit > 100) return - const result = await client.RequestManager.get(endpoints.CHANNEL_MESSAGES(data.id), options) as Message_Create_Options[] + const result = (await client.RequestManager.get( + endpoints.CHANNEL_MESSAGES(data.id), + options + )) as Message_Create_Options[] return result.map(res => create_message(res, client)) }, /** Get pinned messages in this channel. */ get_pins: async () => { - const result = await client.RequestManager.get(endpoints.CHANNEL_PINS(data.id)) as Message_Create_Options[] + const result = (await client.RequestManager.get(endpoints.CHANNEL_PINS(data.id))) as Message_Create_Options[] return result.map(res => create_message(res, client)) }, /** Send a message to the channel. Requires SEND_MESSAGES permission. */ @@ -79,7 +91,7 @@ export const create_channel = (data: Channel_Create_Options, guild: Guild, clien permission_overwrites: () => data.permission_overwrites, /** Check whether a member has certain permissions in this channel. */ has_permissions: (id: string, permissions: Permission[]) => { - if (id === guild.owner_id) return true + if (id === guild.owner_id()) return true const member = guild.members.get(id) if (!member) @@ -89,7 +101,7 @@ export const create_channel = (data: Channel_Create_Options, guild: Guild, clien const role = guild.roles.get(role_id) if (!role) return bits - bits |= role.permissions + bits |= role.permissions() return bits }, 0) @@ -165,5 +177,3 @@ export const create_channel = (data: Channel_Create_Options, guild: Guild, clien mention: () => `<#${data.id}>` } } - -export type Channel = ReturnType diff --git a/structures/guild.ts b/structures/guild.ts index e3b6db5d7..98a39454b 100644 --- a/structures/guild.ts +++ b/structures/guild.ts @@ -17,9 +17,10 @@ import { import { create_role, Role } from './role' import { create_member } from './member' import { create_channel } from './channel' -import { Channel_Create_Options } from '../types/channel' +import { Channel_Create_Options, Channel } from '../types/channel' import { Image_Size, Image_Formats } from '../types/cdn' import { Permissions } from '../types/permission' +import { Member } from '../types/member' export const create_guild = (data: Create_Guild_Payload, client: Client) => { const guild = { @@ -64,11 +65,11 @@ export const create_guild = (data: Create_Guild_Payload, client: Client) => { /** The current open voice states in the guild. */ voice_states: () => data.voice_states, /** The users in this guild. */ - members: new Map(data.members.map(m => [m.id, create_member(m)])), + members: new Map(), /** The channels in the guild */ - channels: new Map(data.channels.map(c => [c.id, create_channel(c, data, client)])), + channels: new Map(), /** The presences of all the users in the guild. */ - presences: new Map(data.presences.map(p => [p.id, create_presence(p)])), + presences: new Map(data.presences.map(p => [p.user.id, p])), /** The maximum amount of presences for the guild(the default value, currently 5000 is in effect when null is returned.) */ max_presences: () => data.max_presences, /** The maximum amount of members for the guild */ @@ -284,6 +285,7 @@ export const create_guild = (data: Create_Guild_Payload, client: Client) => { } data.roles.forEach(r => guild.roles.set(r.id, create_role(r))) - + data.members.forEach(m => guild.members.set(m.user.id, create_member(m, guild, client))) + data.channels.forEach(c => guild.channels.set(c.id, create_channel(c, guild, client))) return guild } diff --git a/structures/member.ts b/structures/member.ts index 8127246b1..657d5599c 100644 --- a/structures/member.ts +++ b/structures/member.ts @@ -4,11 +4,8 @@ import { format_image_url } from '../utils/cdn' import { Member_Create_Payload, Edit_Member_Options } from '../types/member' import { Image_Size, Image_Formats } from '../types/cdn' import { Permission, Permissions } from '../types/permission' -import { cache } from '../utils/cache' - -export const create_member = (data: Member_Create_Payload, guild_id: string, client: Client) => { - const guild = cache.guilds.get(guild_id) +export const create_member = (data: Member_Create_Payload, guild: Guild, client: Client) => { return { /** The complete raw data from the member create payload */ raw: () => data, @@ -97,4 +94,4 @@ export const create_member = (data: Member_Create_Payload, guild_id: string, cli } } -export type Member = ReturnType + diff --git a/types/channel.ts b/types/channel.ts index 9ce091d31..5b58c45df 100644 --- a/types/channel.ts +++ b/types/channel.ts @@ -1,4 +1,5 @@ import { Raw_Overwrite, Overwrite } from './guild' +import { create_channel } from '../structures/channel' export interface Base_Channel_Create { /** The id of this channel */ @@ -103,3 +104,5 @@ export interface Create_Invite_Options { /** If true, don't try to reuse a similar invite (useful for creating many unique one time use invites.) */ unique: boolean } + +export type Channel = ReturnType diff --git a/types/discord.ts b/types/discord.ts index 68750defa..77cd26f71 100644 --- a/types/discord.ts +++ b/types/discord.ts @@ -180,7 +180,9 @@ export enum StatusType { Offline = 'offline' } +export type Status_Type = 'online' | 'dnd' | 'idle' | 'invisible' | 'offline' + export interface Status { afk: boolean; status: StatusType; -} \ No newline at end of file +} diff --git a/types/guild.ts b/types/guild.ts index b43cd271c..b6a2ed87e 100644 --- a/types/guild.ts +++ b/types/guild.ts @@ -1,11 +1,12 @@ -import { Emoji } from './discord' -import { Member } from '../structures/member' +import { Emoji, StatusType } from './discord' import { User } from '../structures/user' import { Permission } from './permission' -import { Channel } from '../structures/channel' import { create_guild } from '../structures/guild' import { Role_Data } from './role' import { Member_Create_Payload } from './member' +import { Activity } from './message' +import { ClientStatusPayload } from '../structures/presence' +import { Channel_Create_Options } from './channel' export interface Create_Guild_Payload { /** The guild id */ @@ -50,9 +51,9 @@ export interface Create_Guild_Payload { member_count: number voice_states: Voice_State[] /** Users in the guild */ - members: Member[] + members: Member_Create_Payload[] /** Channels in the guild */ - channels: Channel[] + channels: Channel_Create_Options[] presences: Presence[] /** The maximum amount of presences for the guild(the default value, currently 5000 is in effect when null is returned.) */ max_presences?: number | null @@ -432,4 +433,21 @@ export interface Voice_State { suppress: boolean } +export interface Presence { + /** The user presence is being updated for */ + user: User_Data + /** The roles this user is in */ + roles: string[] + /** null, or the user's current activity */ + game: Activity | null + /** The id of the guild */ + guild_id: string + /** Either idle */ + status: StatusType + activities: Activity[] + client_status: ClientStatusPayload + premium_since?: string | null + nick?: string | null +} + export type Guild = ReturnType diff --git a/types/member.ts b/types/member.ts index 5d841ecd7..73b8ffd44 100644 --- a/types/member.ts +++ b/types/member.ts @@ -1,4 +1,5 @@ import { User_Payload } from "../structures/user"; +import { create_member } from "../structures/member"; export interface Edit_Member_Options { /** Value to set users nickname to. Requires MANAGE_NICKNAMES permission. */ @@ -29,3 +30,5 @@ export interface Member_Create_Payload { /** Whether the user is muted in voice channels */ mute: boolean } + +export type Member = ReturnType diff --git a/utils/cache.ts b/utils/cache.ts index 22ad15263..1098d2785 100644 --- a/utils/cache.ts +++ b/utils/cache.ts @@ -1,6 +1,6 @@ import { User } from "../structures/user"; -import { Channel } from "../structures/channel"; -import { Guild } from "../structures/guild"; +import { Guild } from "../types/guild"; +import { Channel } from "../types/channel"; export const cache = { guilds: new Map(), From e74bca224f55dc78c0556a98146cd1e5aa334bda Mon Sep 17 00:00:00 2001 From: Skillz Date: Thu, 20 Feb 2020 15:20:43 -0500 Subject: [PATCH 18/18] latest --- structures/channel.ts | 1 + structures/guild.ts | 10 +++--- structures/member.ts | 13 +++++--- tsconfig.json | 71 ++++++------------------------------------- 4 files changed, 23 insertions(+), 72 deletions(-) diff --git a/structures/channel.ts b/structures/channel.ts index c66f60d35..2420ae6a3 100644 --- a/structures/channel.ts +++ b/structures/channel.ts @@ -15,6 +15,7 @@ import { Message_Create_Options } from '../types/message' import { Permission, Permissions } from '../types/permission' import { Guild } from '../types/guild' + export const create_channel = (data: Channel_Create_Options, guild: Guild, client: Client) => { const base_channel = { /** The unique id of the channel */ diff --git a/structures/guild.ts b/structures/guild.ts index 98a39454b..c7437cde4 100644 --- a/structures/guild.ts +++ b/structures/guild.ts @@ -1,6 +1,6 @@ -import Client from '../module/client' -import { endpoints } from '../constants/discord' -import { format_image_url } from '../utils/cdn' +import Client from '../module/client.ts' +import { endpoints } from '../constants/discord.ts' +import { format_image_url } from '../utils/cdn.ts' import { Create_Guild_Payload, ChannelTypes, @@ -14,7 +14,7 @@ import { Edit_Emojis_Options, Create_Role_Options } from '../types/guild' -import { create_role, Role } from './role' +import { create_role, Role } from './role.ts' import { create_member } from './member' import { create_channel } from './channel' import { Channel_Create_Options, Channel } from '../types/channel' @@ -285,7 +285,7 @@ export const create_guild = (data: Create_Guild_Payload, client: Client) => { } data.roles.forEach(r => guild.roles.set(r.id, create_role(r))) - data.members.forEach(m => guild.members.set(m.user.id, create_member(m, guild, client))) + data.members.forEach(m => guild.members.set(m.user.id, create_member(m, data.id, client))) data.channels.forEach(c => guild.channels.set(c.id, create_channel(c, guild, client))) return guild } diff --git a/structures/member.ts b/structures/member.ts index 657d5599c..c8c9dfc10 100644 --- a/structures/member.ts +++ b/structures/member.ts @@ -4,8 +4,11 @@ import { format_image_url } from '../utils/cdn' import { Member_Create_Payload, Edit_Member_Options } from '../types/member' import { Image_Size, Image_Formats } from '../types/cdn' import { Permission, Permissions } from '../types/permission' +import { cache } from '../utils/cache' + +export const create_member = (data: Member_Create_Payload, guild_id: string, client: Client) => { + const guild = cache.guilds.get(guild_id) -export const create_member = (data: Member_Create_Payload, guild: Guild, client: Client) => { return { /** The complete raw data from the member create payload */ raw: () => data, @@ -41,17 +44,17 @@ export const create_member = (data: Member_Create_Payload, guild: Guild, client: /** Add a role to the member */ add_role: (role_id: string, reason?: string) => { // TODO: check if the bot has MANAGE_ROLE and its highest role is above this one - return client.RequestManager.put(endpoints.GUILD_MEMBER_ROLE(guild.id, data.user.id, role_id), { reason }) + return client.RequestManager.put(endpoints.GUILD_MEMBER_ROLE(guild_id, data.user.id, role_id), { reason }) }, /** Remove a role from the member */ remove_role: (role_id: string, reason?: string) => { // TODO: check if the bot has MANAGE_ROLE permissions and its highest role is above this role - return client.RequestManager.delete(endpoints.GUILD_MEMBER_ROLE(guild.id, data.user.id, role_id), { reason }) + return client.RequestManager.delete(endpoints.GUILD_MEMBER_ROLE(guild_id, data.user.id, role_id), { reason }) }, /** Kick a member from the server */ kick: (reason?: string) => { // TODO: check if bot has KICK_MEMBER permissions - return client.RequestManager.delete(endpoints.GUILD_MEMBER(guild.id, data.user.id), { reason }) + return client.RequestManager.delete(endpoints.GUILD_MEMBER(guild_id, data.user.id), { reason }) }, /** Ban a member from the server */ ban: (delete_message_days = 0, reason?: string) => { @@ -89,7 +92,7 @@ export const create_member = (data: Member_Create_Payload, guild: Guild, client: // TODO: if channel id is provided check if the bot has CONNECT and MOVE in channel and current channel options.channel_id = undefined - return client.RequestManager.patch(endpoints.GUILD_MEMBER(guild.id, data.user.id), options) + return client.RequestManager.patch(endpoints.GUILD_MEMBER(guild_id, data.user.id), options) } } } diff --git a/tsconfig.json b/tsconfig.json index 54cdc8971..ee0129cf1 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,66 +1,13 @@ { "compilerOptions": { - /* Basic Options */ - // "incremental": true, /* Enable incremental compilation */ - "target": "ESNext", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */ - "module": "ESNext", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */ - "lib": ["ES7", "DOM"], /* Specify library files to be included in the compilation. */ - // "allowJs": true, /* Allow javascript files to be compiled. */ - // "checkJs": true, /* Report errors in .js files. */ - // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */ - // "declaration": true, /* Generates corresponding '.d.ts' file. */ - // "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */ - // "sourceMap": true, /* Generates corresponding '.map' file. */ - // "outFile": "./", /* Concatenate and emit output to single file. */ - // "outDir": "./", /* Redirect output structure to the directory. */ - // "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ - // "composite": true, /* Enable project compilation */ - // "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */ - // "removeComments": true, /* Do not emit comments to output. */ - // "noEmit": true, /* Do not emit outputs. */ - // "importHelpers": true, /* Import emit helpers from 'tslib'. */ - "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */ - // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */ - - /* Strict Type-Checking Options */ - "strict": true, /* Enable all strict type-checking options. */ - // "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */ - // "strictNullChecks": true, /* Enable strict null checks. */ - // "strictFunctionTypes": true, /* Enable strict checking of function types. */ - // "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */ - // "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */ - // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */ - // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */ - - /* Additional Checks */ - "noUnusedLocals": true, /* Report errors on unused locals. */ - "noUnusedParameters": true, /* Report errors on unused parameters. */ - "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ - "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ - - /* Module Resolution Options */ - // "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ - // Hack to stop VSCode from suggesting imports without ./ or ../ as a prefix. - "baseUrl": "../../", /* Base directory to resolve non-absolute module names. */ - // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ - // "typeRoots": [], /* List of folders to include type definitions from. */ - // "types": [], /* Type declaration files to be included in compilation. */ - // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ - "esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ - // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ - // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ - - /* Source Map Options */ - // "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */ - // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ - // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */ - // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */ - - /* Experimental Options */ - "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */ - "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */ - - /* Advanced Options */ - "forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */ + "target": "ESNext" /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */, + "module": "ESNext" /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */, + "strict": true /* Enable all strict type-checking options. */, + "noUnusedLocals": true /* Report errors on unused locals. */, + "noUnusedParameters": true /* Report errors on unused parameters. */, + "noImplicitReturns": true /* Report error when not all code paths in function return a value. */, + "noFallthroughCasesInSwitch": true /* Report errors for fallthrough cases in switch statement. */, + "experimentalDecorators": true /* Enables experimental support for ES7 decorators. */, + "emitDecoratorMetadata": true /* Enables experimental support for emitting type metadata for decorators. */ } }