import { Client } from '../models/client.ts' import { GuildBanPayload, GuildFeatures, GuildIntegrationPayload, GuildPayload, GuildWidgetPayload, IntegrationAccountPayload, IntegrationExpireBehavior, Verification, GuildChannels, GuildPreview, MessageNotification, ContentFilter, GuildModifyOptions, GuildGetPruneCountPayload, GuildPruneCountPayload, GuildBeginPrunePayload } from '../types/guild.ts' import { Base } from './base.ts' import { CreateGuildRoleOptions, RolesManager } from '../managers/roles.ts' import { InviteManager } from '../managers/invites.ts' import { CreateChannelOptions, GuildChannelsManager } from '../managers/guildChannels.ts' import { MembersManager } from '../managers/members.ts' import { Role } from './role.ts' import { GuildEmojisManager } from '../managers/guildEmojis.ts' import { Member } from './member.ts' import { User } from './user.ts' import { Application } from './application.ts' import { GUILD_BAN, GUILD_BANS, GUILD_INTEGRATIONS, GUILD_PRUNE } from '../types/endpoint.ts' import { GuildVoiceStatesManager } from '../managers/guildVoiceStates.ts' import { RequestMembersOptions } from '../gateway/index.ts' import { GuildPresencesManager } from '../managers/presences.ts' import { TemplatePayload } from '../types/template.ts' import { Template } from './template.ts' import { DiscordAPIError } from '../models/rest.ts' export class GuildBan extends Base { guild: Guild reason?: string user: User constructor(client: Client, data: GuildBanPayload, guild: Guild) { super(client, data) this.guild = guild this.reason = data.reason === null ? undefined : data.reason this.user = new User(client, data.user) } } export class GuildBans { client: Client guild: Guild constructor(client: Client, guild: Guild) { this.client = client this.guild = guild } /** * Gets all bans in the Guild. */ async all(): Promise { const res = await this.client.rest.get(GUILD_BANS(this.guild.id)) if (typeof res !== 'object' || !Array.isArray(res)) throw new Error('Failed to fetch Guild Bans') const bans = (res as GuildBanPayload[]).map( (ban) => new GuildBan(this.client, ban, this.guild) ) return bans } /** * Gets ban details of a User if any. * @param user User to get ban of, ID or User object. */ async get(user: string | User): Promise { const res = await this.client.rest.get( GUILD_BAN(this.guild.id, typeof user === 'string' ? user : user.id) ) if (typeof res !== 'object') throw new Error('Failed to fetch Guild Ban') return new GuildBan(this.client, res, this.guild) } /** * Bans a User. * @param user User to ban, ID or User object. * @param reason Reason for the Ban. * @param deleteMessagesDays Delete Old Messages? If yes, how much days. */ async add( user: string | User, reason?: string, deleteMessagesDays?: number ): Promise { const res = await this.client.rest.put( GUILD_BAN(this.guild.id, typeof user === 'string' ? user : user.id), { reason, delete_message_days: deleteMessagesDays }, undefined, null, true ) if (res.response.status !== 204) throw new Error('Failed to Add Guild Ban') } /** * Unbans (removes ban from) a User. * @param user User to unban, ID or User object. */ async remove(user: string | User): Promise { const res = await this.client.rest.delete( GUILD_BAN(this.guild.id, typeof user === 'string' ? user : user.id), undefined, undefined, null, true ) if (res.response.status !== 204) return false else return true } } export class Guild extends Base { id: string name?: string icon?: string iconHash?: string splash?: string discoverySplash?: string owner?: boolean ownerID?: string permissions?: string region?: string afkChannelID?: string afkTimeout?: number widgetEnabled?: boolean widgetChannelID?: string verificationLevel?: Verification defaultMessageNotifications?: MessageNotification explicitContentFilter?: ContentFilter roles: RolesManager emojis: GuildEmojisManager invites: InviteManager features?: GuildFeatures[] mfaLevel?: string applicationID?: string systemChannelID?: string systemChannelFlags?: string rulesChannelID?: string joinedAt?: string large?: boolean unavailable: boolean memberCount?: number voiceStates: GuildVoiceStatesManager members: MembersManager channels: GuildChannelsManager presences: GuildPresencesManager maxPresences?: number maxMembers?: number vanityURLCode?: string description?: string banner?: string premiumTier?: number premiumSubscriptionCount?: number preferredLocale?: string publicUpdatesChannelID?: string maxVideoChannelUsers?: number approximateNumberCount?: number approximatePresenceCount?: number bans: GuildBans constructor(client: Client, data: GuildPayload) { super(client, data) this.id = data.id this.unavailable = data.unavailable this.readFromData(data) this.bans = new GuildBans(client, this) this.members = new MembersManager(this.client, this) this.voiceStates = new GuildVoiceStatesManager(client, this) this.presences = new GuildPresencesManager(client, this) this.channels = new GuildChannelsManager( this.client, this.client.channels, this ) this.roles = new RolesManager(this.client, this) this.emojis = new GuildEmojisManager(this.client, this.client.emojis, this) this.invites = new InviteManager(this.client, this) } readFromData(data: GuildPayload): void { this.id = data.id ?? this.id this.unavailable = data.unavailable ?? this.unavailable if (!this.unavailable) { this.name = data.name ?? this.name this.icon = data.icon ?? this.icon this.iconHash = data.icon_hash ?? this.iconHash this.splash = data.splash ?? this.splash this.discoverySplash = data.discovery_splash ?? this.discoverySplash this.owner = data.owner ?? this.owner this.ownerID = data.owner_id ?? this.ownerID this.permissions = data.permissions ?? this.permissions this.region = data.region ?? this.region this.afkTimeout = data.afk_timeout ?? this.afkTimeout this.afkChannelID = data.afk_channel_id ?? this.afkChannelID this.widgetEnabled = data.widget_enabled ?? this.widgetEnabled this.widgetChannelID = data.widget_channel_id ?? this.widgetChannelID this.verificationLevel = data.verification_level ?? this.verificationLevel this.defaultMessageNotifications = data.default_message_notifications ?? this.defaultMessageNotifications this.explicitContentFilter = data.explicit_content_filter ?? this.explicitContentFilter this.features = data.features ?? this.features this.mfaLevel = data.mfa_level ?? this.mfaLevel this.systemChannelID = data.system_channel_id ?? this.systemChannelID this.systemChannelFlags = data.system_channel_flags ?? this.systemChannelFlags this.rulesChannelID = data.rules_channel_id ?? this.rulesChannelID this.joinedAt = data.joined_at ?? this.joinedAt this.large = data.large ?? this.large this.memberCount = data.member_count ?? this.memberCount this.maxPresences = data.max_presences ?? this.maxPresences this.maxMembers = data.max_members ?? this.maxMembers this.vanityURLCode = data.vanity_url_code ?? this.vanityURLCode this.description = data.description ?? this.description this.banner = data.banner ?? this.banner this.premiumTier = data.premium_tier ?? this.premiumTier this.premiumSubscriptionCount = data.premium_subscription_count ?? this.premiumSubscriptionCount this.preferredLocale = data.preferred_locale ?? this.preferredLocale this.publicUpdatesChannelID = data.public_updates_channel_id ?? this.publicUpdatesChannelID this.maxVideoChannelUsers = data.max_video_channel_users ?? this.maxVideoChannelUsers this.approximateNumberCount = data.approximate_number_count ?? this.approximateNumberCount this.approximatePresenceCount = data.approximate_presence_count ?? this.approximatePresenceCount } } /** * Gets Everyone role of the Guild */ async getEveryoneRole(): Promise { // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion return (await this.roles.get(this.id)) as Role } /** * Gets current client's member in the Guild */ async me(): Promise { const get = await this.members.get(this.client.user?.id as string) if (get === undefined) throw new Error('Guild#me is not cached') return get } /** * Fetches Guild's Integrations (Webhooks, Bots, etc.) */ async fetchIntegrations(): Promise { const raw = (await this.client.rest.get( GUILD_INTEGRATIONS(this.id) )) as GuildIntegrationPayload[] return raw.map((e) => new GuildIntegration(this.client, e)) } /** Create a new Guild Channel */ async createChannel(options: CreateChannelOptions): Promise { return this.channels.create(options) } /** Create a new Guild Role */ async createRole(options?: CreateGuildRoleOptions): Promise { return this.roles.create(options) } /** * Chunks the Guild Members, i.e. cache them. * @param options Options regarding the Members Request * @param wait Whether to wait for all Members to come before resolving Promise or not. * @param timeout Configurable timeout to cancel the wait to safely remove listener. */ async chunk( options: RequestMembersOptions, wait: boolean = false, timeout: number = 60000 ): Promise { return await new Promise((resolve, reject) => { this.client.gateway?.requestMembers(this.id, options) if (!wait) return resolve(this) else { let chunked = false const listener = (guild: Guild): void => { if (guild.id === this.id) { chunked = true this.client.off('guildMembersChunked', listener) resolve(this) } } this.client.on('guildMembersChunked', listener) setTimeout(() => { if (!chunked) { this.client.off('guildMembersChunked', listener) } }, timeout) } }) } /** * Fulfills promise when guild becomes available * @param timeout Configurable timeout to cancel the wait to safely remove listener. */ async awaitAvailability(timeout: number = 1000): Promise { return await new Promise((resolve, reject) => { if (!this.unavailable) resolve(this) const listener = (guild: Guild): void => { if (guild.id === this.id) { this.client.off('guildLoaded', listener) resolve(this) } } this.client.on('guildLoaded', listener) setTimeout(() => { this.client.off('guildLoaded', listener) reject(Error("Timeout. Guild didn't arrive in time.")) }, timeout) }) } /** Attach an integration object from the current user to the guild. */ async createIntegration(id: string, type: string): Promise { await this.client.rest.api.guilds[this.id].integrations.post({ id, type }) return this } /** Modify the behavior and settings of an integration object for the guild. */ async editIntegration( id: string, data: { expireBehavior?: number | null expireGracePeriod?: number | null enableEmoticons?: boolean | null } ): Promise { await this.client.rest.api.guilds[this.id].integrations[id].patch({ expire_behaviour: data.expireBehavior, expire_grace_period: data.expireGracePeriod, enable_emoticons: data.enableEmoticons }) return this } /** Delete the attached integration object for the guild. Deletes any associated webhooks and kicks the associated bot if there is one. */ async deleteIntegration(id: string): Promise { await this.client.rest.api.guilds[this.id].integrations[id].delete() return this } /** Sync an integration. */ async syncIntegration(id: string): Promise { await this.client.rest.api.guilds[this.id].integrations[id].sync.post() return this } /** Returns the widget for the guild. */ async getWidget(): Promise { return this.client.rest.api.guilds[this.id]['widget.json'].get() } /** Modify a guild widget object for the guild. */ async editWidget(data: { enabled?: boolean channel?: string | GuildChannels }): Promise { await this.client.rest.api.guilds[this.id].widget.patch({ enabled: data.enabled, channel_id: typeof data.channel === 'object' ? data.channel.id : data.channel }) return this } /** Returns a partial invite object for guilds with that feature enabled. */ async getVanity(): Promise<{ code: string | null; uses: number }> { try { const value = await this.client.rest.api.guilds[this.id][ 'vanity-url' ].get() return value } catch (error) { if (error instanceof DiscordAPIError) { if (error.error?.code === 50020) { return { code: null, uses: 0 } } } throw error } } /** Returns a PNG (URL) image widget for the guild. */ getWidgetImageURL( style?: 'shield' | 'banner1' | 'banner2' | 'banner3' | 'banner4' ): string { return `https://discord.com/api/v${this.client.rest.version ?? 8}/guilds/${ this.id }/widget.png${style !== undefined ? `?style=${style}` : ''}` } /** Leave a Guild. */ async leave(): Promise { await this.client.rest.api.users['@me'].guilds[this.id].delete() return this.client } /** Returns an array of template objects. */ async getTemplates(): Promise { return this.client.rest.api.guilds[this.id].templates .get() .then((temps: TemplatePayload[]) => temps.map((temp) => new Template(this.client, temp)) ) } /** Creates a template for the guild. */ async createTemplate( name: string, description?: string | null ): Promise