import Client from '../module/Client' import { endpoints } from '../constants/discord' import { formatImageURL } from '../utils/cdn' import { createRole } from './role' import { createEmoji } from './emoji' import { createVoiceState } from './voiceState' import { createMember } from './member' import { createChannel } from './channel' import { createPresence } from './presence' 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 /** 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 */ ownerID: string /** The voice region id for the guild */ region: string /** The afk channel id */ afkChannelID: string | null /** AFK Timeout in seconds. */ afkTimeout: number /** The verification level required for the guild */ verificationLevel: 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 */ mfaLevel: number /** The id of the channel to which system mesages are sent */ systemChannelID: string | null /** When this guild was joined at */ joinedAt: number /** Whether this is considered a large guild */ large: boolean /** Whether this guild is unavailable */ unavailable: boolean /** Total number of members in this guild */ memberCount: number voiceStates: 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.) */ maxPresences?: number | null /** The maximum amount of members for the guild */ maxMembers?: number /** The vanity url code for the guild */ vanityURLCode: string | null /** The description for the guild */ description: string | null /** The banner hash */ banner: string | null /** The premium tier */ premiumTier: number /** The total number of users currently boosting this server. */ premiumSubscriptionCount: number /** The preferred local of this guild only set if guild has the DISCOVERABLE feature, defaults to en-US */ preferredLocale: string /** The full URL of the icon from Discords CDN. Undefined when no icon is set. */ iconURL(): string | undefined /** The full URL of the splash from Discords CDN. Undefined if no splash is set. */ splashURL(): string | undefined /** The full URL of the banner from Discords CDN. Undefined if no banner is set. */ bannerURL(): string | undefined } export type ImageSize = 16 | 32 | 64 | 128 | 256 | 512 | 1024 | 2048 export type ImageFormats = '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 ChannelCreateOptions { /** 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 CreateEmojisOptions { /** 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 EditEmojisOptions { /** 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 CreateRoleOptions { name?: string permissions?: Permission[] color?: number hoist?: boolean mentionable?: boolean } export interface PrunePayload { pruned: number } export const createGuild = (data: CreateGuildPayload, client: Client) => { const guild: Guild = { id: data.id, name: data.name, icon: data.icon, splash: data.splash, ownerID: data.owner_id, region: data.region, afkChannelID: data.afk_channel_id, afkTimeout: data.afk_timeout, verificationLevel: data.verification_level, roles: data.roles.map(role => createRole(role)), emojis: data.emojis.map(emoji => createEmoji(emoji)), features: data.features, mfaLevel: data.mfa_level, systemChannelID: data.system_channel_id, joinedAt: Date.parse(data.joined_at), large: data.large, unavailable: data.unavailable, memberCount: data.member_count, voiceStates: data.voice_states.map(voiceState => createVoiceState(voiceState)), members: data.members.map(member => createMember(member)), channels: data.channels.map(channel => createChannel(channel)), presences: data.presences.map(presence => createPresence(presence)), maxPresences: data.max_presences, maxMembers: data.max_members, vanityURLCode: data.vanity_url_code, description: data.description, banner: data.banner, premiumTier: data.premium_tier, premiumSubscriptionCount: data.premium_subscription_count, preferredLocale: data.preferred_locale, iconURL: (size, format) => data.icon ? formatImageURL(endpoints.GUILD_ICON(data.id, data.icon), size, format) : undefined, splashURL: (size, format) => data.splash ? formatImageURL(endpoints.GUILD_SPLASH(data.id, data.splash), size, format) : undefined, bannerURL: (size, format) => data.banner ? formatImageURL(endpoints.GUILD_BANNER(data.id, data.banner), size, format) : undefined, createChannel: (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 }) }, createEmoji: (name, image, options) => { // TODO: Check if the bot has `MANAGE_EMOJIS` permission return client.RequestManager.post(endpoints.GUILD_EMOJIS(data.id), { ...options, name, image }) }, editEmoji: (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 }) }, deleteEmoji: (id, reason) => { // TODO: check if the bot has `MANAGE_EMOJIS` permission return client.RequestManager.delete(endpoints.GUILD_EMOJI(data.id, id), { reason }) }, createRole: 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 return role }, editRole: (id, options) => { return client.RequestManager.patch(endpoints.GUILD_ROLE(data.id, id), options) }, deleteRole: id => { return client.RequestManager.delete(endpoints.GUILD_ROLE(data.id, id)) }, getPruneCount: 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 }, pruneMembers: 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 }) }, getAuditLogs: 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 }) }, leaveVoiceChannel: () => {} } return guild }