diff --git a/src/api/Routes/applications.ts b/src/api/Routes/applications.ts index 3a91978..fb7133a 100644 --- a/src/api/Routes/applications.ts +++ b/src/api/Routes/applications.ts @@ -14,12 +14,15 @@ import type { RESTGetAPIEntitlementsResult, RESTGetAPIGuildApplicationCommandsPermissionsResult, RESTGetAPISKUsResult, + RESTGetCurrentApplicationResult, RESTPatchAPIApplicationCommandJSONBody, RESTPatchAPIApplicationCommandResult, RESTPatchAPIApplicationEmojiJSONBody, RESTPatchAPIApplicationEmojiResult, RESTPatchAPIApplicationGuildCommandJSONBody, RESTPatchAPIApplicationGuildCommandResult, + RESTPatchCurrentApplicationJSONBody, + RESTPatchCurrentApplicationResult, RESTPostAPIApplicationCommandsJSONBody, RESTPostAPIApplicationCommandsResult, RESTPostAPIApplicationEmojiJSONBody, @@ -36,11 +39,17 @@ import type { RESTPutAPIApplicationRoleConnectionMetadataJSONBody, RESTPutAPIApplicationRoleConnectionMetadataResult, RESTPutAPIGuildApplicationCommandsPermissionsResult, + RestGetAPIApplicationActivityInstanceResult, } from '../../types'; import type { RestArguments, RestArgumentsNoBody } from '../api'; export interface ApplicationRoutes { applications: (id: string) => { + get(args?: RestArgumentsNoBody): Promise; + patch(args: RestArguments): Promise; + 'activity-instances': (id: string) => { + get(args?: RestArgumentsNoBody): Promise; + }; guilds: (id: string) => { commands: { get( diff --git a/src/cache/resources/emojis.ts b/src/cache/resources/emojis.ts index 32bd077..b52ee42 100644 --- a/src/cache/resources/emojis.ts +++ b/src/cache/resources/emojis.ts @@ -1,10 +1,10 @@ -import type { CacheFrom, ReturnCache } from '../..'; +import type { ApplicationEmojiStructure, CacheFrom, ReturnCache } from '../..'; import { type GuildEmojiStructure, Transformers } from '../../client/transformers'; import { fakePromise } from '../../common'; -import type { APIEmoji } from '../../types'; +import type { APIApplicationEmoji, APIEmoji } from '../../types'; import { GuildRelatedResource } from './default/guild-related'; -export class Emojis extends GuildRelatedResource { +export class Emojis extends GuildRelatedResource { namespace = 'emoji'; //@ts-expect-error @@ -12,19 +12,25 @@ export class Emojis extends GuildRelatedResource { return true; } - override get(id: string): ReturnCache { - return fakePromise(super.get(id)).then(rawEmoji => - rawEmoji ? Transformers.GuildEmoji(this.client, rawEmoji, rawEmoji.guild_id) : undefined, - ); + override get(id: string): ReturnCache { + return fakePromise(super.get(id)).then(rawEmoji => { + if (!rawEmoji) return undefined; + if (rawEmoji.guild_id === this.client.applicationId) return Transformers.ApplicationEmoji(this.client, rawEmoji); + return Transformers.GuildEmoji(this.client, rawEmoji, rawEmoji.guild_id); + }); } raw(id: string): ReturnCache { return super.get(id); } - override bulk(ids: string[]): ReturnCache { + override bulk(ids: string[]): ReturnCache<(GuildEmojiStructure | ApplicationEmojiStructure)[]> { return fakePromise(super.bulk(ids) as (APIEmoji & { id: string; guild_id: string })[]).then(emojis => - emojis.map(rawEmoji => Transformers.GuildEmoji(this.client, rawEmoji, rawEmoji.guild_id)), + emojis.map(rawEmoji => { + if (rawEmoji.guild_id === this.client.applicationId) + return Transformers.ApplicationEmoji(this.client, rawEmoji as APIApplicationEmoji); + return Transformers.GuildEmoji(this.client, rawEmoji, rawEmoji.guild_id); + }), ); } @@ -32,13 +38,19 @@ export class Emojis extends GuildRelatedResource { return super.bulk(ids); } - override values(guild: string): ReturnCache { + override values(guild: string): ReturnCache<(GuildEmojiStructure | ApplicationEmojiStructure)[]> { return fakePromise(super.values(guild) as (APIEmoji & { id: string; guild_id: string })[]).then(emojis => - emojis.map(rawEmoji => Transformers.GuildEmoji(this.client, rawEmoji, rawEmoji.guild_id)), + emojis.map(rawEmoji => { + if (rawEmoji.guild_id === this.client.applicationId) + return Transformers.ApplicationEmoji(this.client, rawEmoji as APIApplicationEmoji); + return Transformers.GuildEmoji(this.client, rawEmoji, rawEmoji.guild_id); + }), ); } - valuesRaw(guild: string): ReturnCache<(APIEmoji & { id: string; guild_id: string })[]> { + valuesRaw( + guild: string, + ): ReturnCache<(((APIEmoji & { id: string }) | APIApplicationEmoji) & { guild_id: string })[]> { return super.values(guild); } } diff --git a/src/client/client.ts b/src/client/client.ts index 0582727..346dcf4 100644 --- a/src/client/client.ts +++ b/src/client/client.ts @@ -36,6 +36,14 @@ export class Client extends BaseClient { super(options); } + get applicationId() { + return this.me?.application.id ?? super.applicationId; + } + + set applicationId(id: string) { + super.applicationId = id; + } + setServices({ gateway, ...rest diff --git a/src/client/transformers.ts b/src/client/transformers.ts index 6a0f96f..e60f3b7 100644 --- a/src/client/transformers.ts +++ b/src/client/transformers.ts @@ -2,6 +2,7 @@ import { type CustomStructures, OptionResolver } from '../commands'; import type { StructStates } from '../common/'; import { AnonymousGuild, + ApplicationEmoji, AutoModerationRule, BaseChannel, BaseGuildChannel, @@ -9,6 +10,7 @@ import { ClientUser, DMChannel, DirectoryChannel, + Emoji, Entitlement, ForumChannel, Guild, @@ -32,10 +34,13 @@ import { Webhook, WebhookMessage, } from '../structures'; +import { Application } from '../structures/Application'; import type { ChannelType } from '../types'; export type PollStructure = InferCustomStructure; export type ClientUserStructure = InferCustomStructure; +export type ApplicationStructure = InferCustomStructure; +export type ApplicationEmojiStructure = InferCustomStructure; export type AnonymousGuildStructure = InferCustomStructure; export type AutoModerationRuleStructure = InferCustomStructure; export type BaseChannelStructure = InferCustomStructure, 'BaseChannel'>; @@ -52,6 +57,7 @@ export type NewsChannelStructure = InferCustomStructure; export type GuildStructure = InferCustomStructure, 'Guild'>; export type GuildBanStructure = InferCustomStructure; +export type EmojiStructure = InferCustomStructure; export type GuildEmojiStructure = InferCustomStructure; export type GuildMemberStructure = InferCustomStructure; export type InteractionGuildMemberStructure = InferCustomStructure; @@ -67,6 +73,12 @@ export type OptionResolverStructure = InferCustomStructure; export const Transformers = { + Application(...args: ConstructorParameters): ApplicationStructure { + return new Application(...args); + }, + ApplicationEmoji(...args: ConstructorParameters): ApplicationEmojiStructure { + return new ApplicationEmoji(...args); + }, AnonymousGuild(...args: ConstructorParameters): AnonymousGuildStructure { return new AnonymousGuild(...args); }, @@ -118,6 +130,9 @@ export const Transformers = { GuildBan(...args: ConstructorParameters): GuildBanStructure { return new GuildBan(...args); }, + Emoji(...args: ConstructorParameters): EmojiStructure { + return new Emoji(...args); + }, GuildEmoji(...args: ConstructorParameters): GuildEmojiStructure { return new GuildEmoji(...args); }, diff --git a/src/client/workerclient.ts b/src/client/workerclient.ts index 1772fa4..2519151 100644 --- a/src/client/workerclient.ts +++ b/src/client/workerclient.ts @@ -96,6 +96,14 @@ export class WorkerClient extends BaseClient { return acc / this.shards.size; } + get applicationId() { + return this.me?.application.id ?? super.applicationId; + } + + set applicationId(id: string) { + super.applicationId = id; + } + setServices(rest: ServicesOptions) { super.setServices(rest); if (this.options.postMessage && rest.cache?.adapter instanceof WorkerAdapter) { diff --git a/src/common/shorters/application.ts b/src/common/shorters/application.ts index b949128..a9c51eb 100644 --- a/src/common/shorters/application.ts +++ b/src/common/shorters/application.ts @@ -1,90 +1,141 @@ -import { type EntitlementStructure, Transformers } from '../../client'; +import { CacheFrom, resolveImage } from '../..'; +import { type ApplicationEmojiStructure, type EntitlementStructure, Transformers } from '../../client'; import type { APIEntitlement, RESTGetAPIEntitlementsQuery, - RESTPostAPIApplicationEmojiJSONBody, + RESTPatchAPIApplicationEmojiJSONBody, + RESTPatchCurrentApplicationJSONBody, RESTPostAPIEntitlementBody, } from '../../types'; +import type { ApplicationEmojiResolvable } from '../types/resolvables'; import { BaseShorter } from './base'; export class ApplicationShorter extends BaseShorter { /** * Lists the emojis for the application. - * @param applicationId The ID of the application. * @returns The emojis. */ - listEmojis(applicationId: string) { - return this.client.proxy.applications(applicationId).emojis.get(); + async listEmojis(force = false): Promise { + if (!force) { + const cached = (await this.client.cache.emojis?.values(this.client.applicationId)) as + | ApplicationEmojiStructure[] + | undefined; + if (cached?.length) return cached; + } + const data = await this.client.proxy.applications(this.client.applicationId).emojis.get(); + this.client.cache.emojis?.set( + CacheFrom.Rest, + data.items.map(e => [e.id, e]), + this.client.applicationId, + ); + return data.items.map(e => Transformers.ApplicationEmoji(this.client, e)); } /** * Gets an emoji for the application. - * @param applicationId The ID of the application. * @param emojiId The ID of the emoji. * @returns The emoji. */ - getEmoji(applicationId: string, emojiId: string) { - return this.client.proxy.applications(applicationId).emojis(emojiId).get(); + async getEmoji(emojiId: string, force = false): Promise { + if (!force) { + const cached = (await this.client.cache.emojis?.get(emojiId)) as ApplicationEmojiStructure; + if (cached) return cached; + } + const data = await this.client.proxy.applications(this.client.applicationId).emojis(emojiId).get(); + this.client.cache.emojis?.set(CacheFrom.Rest, data.id, this.client.applicationId, data); + return Transformers.ApplicationEmoji(this.client, data); } /** * Creates a new emoji for the application. - * @param applicationId The ID of the application. * @param body.name The name of the emoji. * @param body.image The [image data string](https://discord.com/developers/docs/reference#image-data) of the emoji. * @returns The created emoji. */ - createEmoji(applicationId: string, body: RESTPostAPIApplicationEmojiJSONBody) { - return this.client.proxy.applications(applicationId).emojis.post({ body }); + async createEmoji(raw: ApplicationEmojiResolvable) { + const data = await this.client.proxy + .applications(this.client.applicationId) + .emojis.post({ body: { ...raw, image: await resolveImage(raw.image) } }); + this.client.cache.emojis?.set(CacheFrom.Rest, data.id, this.client.applicationId, data); + return Transformers.ApplicationEmoji(this.client, data); + } + + /** + * Edits an emoji for the application. + * @param emojiId The ID of the emoji. + * @param body.name The new name of the emoji. + * @returns The edited emoji. + */ + async editEmoji(emojiId: string, body: RESTPatchAPIApplicationEmojiJSONBody) { + const data = await this.client.proxy.applications(this.client.applicationId).emojis(emojiId).patch({ body }); + this.client.cache.emojis?.patch(CacheFrom.Rest, emojiId, this.client.applicationId, data); + return Transformers.ApplicationEmoji(this.client, data); + } + + /** + * Deletes an emoji for the application. + * @param emojiId The ID of the emoji. + */ + deleteEmoji(emojiId: string) { + return this.client.proxy.applications(this.client.applicationId).emojis(emojiId).delete(); } /** * Lists the entitlements for the application. - * @param applicationId The ID of the application. * @param [query] The query parameters. */ - listEntitlements(applicationId: string, query?: RESTGetAPIEntitlementsQuery): Promise { + listEntitlements(query?: RESTGetAPIEntitlementsQuery): Promise { return this.client.proxy - .applications(applicationId) + .applications(this.client.applicationId) .entitlements.get({ query }) .then(et => et.map(e => Transformers.Entitlement(this.client, e))); } /** * Consumes an entitlement for the application. - * @param applicationId The ID of the application. * @param entitlementId The ID of the entitlement. */ - consumeEntitlement(applicationId: string, entitlementId: string) { - return this.client.proxy.applications(applicationId).entitlements(entitlementId).consume.post(); + consumeEntitlement(entitlementId: string) { + return this.client.proxy.applications(this.client.applicationId).entitlements(entitlementId).consume.post(); } /** * Creates a test entitlement for the application. - * @param applicationId The ID of the application. * @param body The body of the request. */ - createTestEntitlement(applicationId: string, body: RESTPostAPIEntitlementBody): Promise { + createTestEntitlement(body: RESTPostAPIEntitlementBody): Promise { return this.client.proxy - .applications(applicationId) + .applications(this.client.applicationId) .entitlements.post({ body }) .then(et => Transformers.Entitlement(this.client, et as APIEntitlement)); } /** * Deletes a test entitlement for the application. - * @param applicationId The ID of the application. * @param entitlementId The ID of the entitlement. */ - deleteTestEntitlement(applicationId: string, entitlementId: string) { - return this.client.proxy.applications(applicationId).entitlements(entitlementId).delete(); + deleteTestEntitlement(entitlementId: string) { + return this.client.proxy.applications(this.client.applicationId).entitlements(entitlementId).delete(); } /** * Lists the SKUs for the application. - * @param applicationId The ID of the application. * @returns The SKUs. */ - listSKUs(applicationId: string) { - return this.client.proxy.applications(applicationId).skus.get(); + listSKUs() { + return this.client.proxy.applications(this.client.applicationId).skus.get(); + } + + async fetch() { + const data = await this.client.proxy.applications('@me').get(); + return Transformers.Application(this.client, data); + } + + async edit(body: RESTPatchCurrentApplicationJSONBody) { + const data = await this.client.proxy.applications('@me').patch({ body }); + return Transformers.Application(this.client, data); + } + + getActivityInstance(instanceId: string) { + return this.client.proxy.applications(this.client.applicationId)['activity-instances'](instanceId).get(); } } diff --git a/src/common/shorters/bans.ts b/src/common/shorters/bans.ts index 801bd77..6efbbcc 100644 --- a/src/common/shorters/bans.ts +++ b/src/common/shorters/bans.ts @@ -17,7 +17,9 @@ export class BanShorter extends BaseShorter { */ async bulkCreate(guildId: string, body: RESTPostAPIGuildBulkBanJSONBody, reason?: string) { const bans = await this.client.proxy.guilds(guildId)['bulk-bans'].post({ reason, body }); - for (const id of bans.banned_users) this.client.cache.members?.removeIfNI('GuildModeration', id, guildId); + await Promise.all( + bans.banned_users.map(id => this.client.cache.members?.removeIfNI('GuildModeration', id, guildId)), + ); return bans; } @@ -70,12 +72,11 @@ export class BanShorter extends BaseShorter { * @returns A Promise that resolves to an array of listed bans. */ async list(guildId: string, query?: RESTGetAPIGuildBansQuery, force = false): Promise { - let bans: APIBan[] | GuildBanStructure[]; if (!force) { - bans = (await this.client.cache.bans?.values(guildId)) ?? []; - if (bans.length) return bans; + const bans = await this.client.cache.bans?.values(guildId); + if (bans?.length) return bans; } - bans = await this.client.proxy.guilds(guildId).bans.get({ + const bans = await this.client.proxy.guilds(guildId).bans.get({ query, }); await this.client.cache.bans?.set( diff --git a/src/common/shorters/channels.ts b/src/common/shorters/channels.ts index fa94af8..716dd6f 100644 --- a/src/common/shorters/channels.ts +++ b/src/common/shorters/channels.ts @@ -1,5 +1,4 @@ import { CacheFrom } from '../../cache'; -import type { Channels } from '../../cache/resources/channels'; import type { Overwrites } from '../../cache/resources/overwrites'; import { type MessageStructure, type ThreadChannelStructure, Transformers } from '../../client/transformers'; import { type AllChannels, BaseChannel, type GuildMember, type GuildRole, channelFrom } from '../../structures'; @@ -29,9 +28,8 @@ export class ChannelShorter extends BaseShorter { } async raw(id: string, force?: boolean): Promise { - let channel: APIChannel | ReturnType; if (!force) { - channel = await this.client.cache.channels?.raw(id); + const channel = await this.client.cache.channels?.raw(id); const overwrites = await this.client.cache.overwrites?.raw(id); if (channel) { if (overwrites) (channel as APIGuildChannel).permission_overwrites = overwrites; @@ -39,7 +37,7 @@ export class ChannelShorter extends BaseShorter { } } - channel = await this.client.proxy.channels(id).get(); + const channel = await this.client.proxy.channels(id).get(); await this.client.cache.channels?.patch( CacheFrom.Rest, id, @@ -198,11 +196,11 @@ export class ChannelShorter extends BaseShorter { if (checkAdmin && role.permissions.has([PermissionFlagsBits.Administrator])) { return new PermissionsBitField(PermissionsBitField.All); } - const channelOverwrites = (await this.client.cache.overwrites?.get(channelId)) ?? []; - + const permissions = new PermissionsBitField(role.permissions.bits); + const channelOverwrites = await this.client.cache.overwrites?.get(channelId); + if (!channelOverwrites) return permissions; const everyoneOverwrites = channelOverwrites.find(x => x.id === role.guildId); const roleOverwrites = channelOverwrites.find(x => x.id === role.id); - const permissions = new PermissionsBitField(role.permissions.bits); permissions.remove([everyoneOverwrites?.deny.bits ?? 0n]); permissions.add([everyoneOverwrites?.allow.bits ?? 0n]); diff --git a/src/common/shorters/emojis.ts b/src/common/shorters/emojis.ts index 839e327..7a90827 100644 --- a/src/common/shorters/emojis.ts +++ b/src/common/shorters/emojis.ts @@ -14,14 +14,11 @@ export class EmojiShorter extends BaseShorter { * @returns A Promise that resolves to an array of emojis. */ async list(guildId: string, force = false): Promise { - let emojis: APIEmoji[] | GuildEmojiStructure[]; if (!force) { - emojis = (await this.client.cache.emojis?.values(guildId)) ?? []; - if (emojis.length) { - return emojis; - } + const cached = (await this.client.cache.emojis?.values(guildId)) as GuildEmojiStructure[] | undefined; + if (cached?.length) return cached; } - emojis = await this.client.proxy.guilds(guildId).emojis.get(); + const emojis = await this.client.proxy.guilds(guildId).emojis.get(); await this.client.cache.emojis?.set( CacheFrom.Rest, emojis.map<[string, APIEmoji]>(x => [x.id!, x]), @@ -58,12 +55,11 @@ export class EmojiShorter extends BaseShorter { * @returns A Promise that resolves to the fetched emoji. */ async fetch(guildId: string, emojiId: string, force = false): Promise { - let emoji: APIEmoji | GuildEmojiStructure | undefined; if (!force) { - emoji = await this.client.cache.emojis?.get(emojiId); + const emoji = (await this.client.cache.emojis?.get(emojiId)) as GuildEmojiStructure | undefined; if (emoji) return emoji; } - emoji = await this.client.proxy.guilds(guildId).emojis(emojiId).get(); + const emoji = await this.client.proxy.guilds(guildId).emojis(emojiId).get(); return Transformers.GuildEmoji(this.client, emoji, guildId); } diff --git a/src/common/types/resolvables.ts b/src/common/types/resolvables.ts index ffdc7e6..6dbb36a 100644 --- a/src/common/types/resolvables.ts +++ b/src/common/types/resolvables.ts @@ -1,9 +1,14 @@ -import type { EmbedColors } from '..'; +import type { EmbedColors, OmitInsert } from '..'; import type { Attachment, AttachmentDataType, AttachmentResolvable } from '../../builders'; import type { GuildMember } from '../../structures'; -import type { APIGuildMember, APIPartialEmoji } from '../../types'; +import type { APIGuildMember, APIPartialEmoji, RESTPostAPIApplicationEmojiJSONBody } from '../../types'; export type EmojiResolvable = string | Partial | `<${string | undefined}:${string}:${string}>`; export type GuildMemberResolvable = string | Partial | APIGuildMember; export type ColorResolvable = `#${string}` | number | keyof typeof EmbedColors | 'Random' | [number, number, number]; export type ImageResolvable = { data: AttachmentResolvable; type: AttachmentDataType } | Attachment; +export type ApplicationEmojiResolvable = OmitInsert< + RESTPostAPIApplicationEmojiJSONBody, + 'image', + { image: ImageResolvable } +>; diff --git a/src/structures/Application.ts b/src/structures/Application.ts new file mode 100644 index 0000000..ca82cb3 --- /dev/null +++ b/src/structures/Application.ts @@ -0,0 +1,61 @@ +import type { UsingClient } from '..'; +import type { ApplicationEmojiResolvable, ObjectToLower } from '../common'; +import type { + APIApplication, + RESTPatchAPIApplicationEmojiJSONBody, + RESTPatchCurrentApplicationJSONBody, +} from '../types'; +import { DiscordBase } from './extra/DiscordBase'; + +export interface Application extends ObjectToLower {} + +/** + * Due to current limitations, this is exclusively for the current application. + */ +export class Application extends DiscordBase { + constructor(client: UsingClient, data: APIApplication) { + // override any id for safety + data.id = client.applicationId; + super(client, data); + } + /** + * Fetch the current application. + */ + fetch() { + return this.client.applications.fetch(); + } + + /** + * Edit the current application. + */ + edit(data: RESTPatchCurrentApplicationJSONBody) { + return this.client.applications.edit(data); + } + + /** + * Get an activity instance. + */ + getActivityInstance(instanceId: string) { + return this.client.applications.getActivityInstance(instanceId); + } + + emojis = { + /** + * Get an application emoji. + */ + fetch: (id: string) => this.client.applications.getEmoji(id), + /** + * Get the application emojis. + */ + list: () => this.client.applications.listEmojis(), + /** + * Create an application emoji. + */ + create: (data: ApplicationEmojiResolvable) => this.client.applications.createEmoji(data), + /** + * Edit an application emoji. + */ + edit: (emojiId: string, body: RESTPatchAPIApplicationEmojiJSONBody) => + this.client.applications.editEmoji(emojiId, body), + }; +} diff --git a/src/structures/ClientUser.ts b/src/structures/ClientUser.ts index d2850e8..ecf8b79 100644 --- a/src/structures/ClientUser.ts +++ b/src/structures/ClientUser.ts @@ -1,25 +1,54 @@ -import { type ClientUserStructure, Transformers } from '../client'; +import type { ClientUserStructure } from '../client'; import type { UsingClient } from '../commands'; import type { GatewayReadyDispatchData, RESTPatchAPICurrentUserJSONBody } from '../types'; import { User } from './User'; +/** + * Represents a client user that extends the base User class. + * This class is used to interact with the authenticated user. + * + * @extends User + */ export class ClientUser extends User { + /** + * Indicates if the user is a bot. + * @type {true} + */ bot = true; + + /** + * Creates an instance of ClientUser. + * + * @param client - The client instance used for making API requests. + * @param data - The user data received from the gateway. + * @param application - The application data received from the gateway. + */ constructor( client: UsingClient, data: GatewayReadyDispatchData['user'], - public application: GatewayReadyDispatchData['application'], + readonly application: GatewayReadyDispatchData['application'], ) { super(client, data); } + /** + * Fetches the current user data from the API. + * + * @returns A promise that resolves to the ClientUserStructure. + */ async fetch(): Promise { const data = await this.api.users('@me').get(); - return Transformers.ClientUser(this.client, data, this.application); + return this.__patchThis(data); } + /** + * Edits the current user data. + * + * @param body - The data to update the user with. + * @returns A promise that resolves to the updated ClientUserStructure. + */ async edit(body: RESTPatchAPICurrentUserJSONBody): Promise { const data = await this.api.users('@me').patch({ body }); - return Transformers.ClientUser(this.client, data, this.application); + return this.__patchThis(data); } } diff --git a/src/structures/GuildEmoji.ts b/src/structures/Emoji.ts similarity index 63% rename from src/structures/GuildEmoji.ts rename to src/structures/Emoji.ts index 6cdaeae..0195c21 100644 --- a/src/structures/GuildEmoji.ts +++ b/src/structures/Emoji.ts @@ -2,45 +2,23 @@ import type { BaseCDNUrlOptions } from '../api'; import type { ReturnCache } from '../cache'; import type { GuildEmojiStructure, GuildStructure } from '../client'; import type { UsingClient } from '../commands'; -import { type EmojiShorter, Formatter, type MethodContext, type ObjectToLower } from '../common'; -import type { APIEmoji, RESTPatchAPIChannelJSONBody, RESTPatchAPIGuildEmojiJSONBody } from '../types'; +import { type EmojiShorter, Formatter, type MethodContext, type ObjectToLower, type When } from '../common'; +import type { + APIApplicationEmoji, + APIEmoji, + RESTPatchAPIApplicationEmojiJSONBody, + RESTPatchAPIGuildEmojiJSONBody, +} from '../types'; +import { User } from './User'; import { DiscordBase } from './extra/DiscordBase'; -export interface GuildEmoji extends DiscordBase, ObjectToLower> {} +export interface Emoji extends DiscordBase, ObjectToLower> {} -export class GuildEmoji extends DiscordBase { - constructor( - client: UsingClient, - data: APIEmoji, - readonly guildId: string, - ) { +export class Emoji extends DiscordBase { + user: When; + constructor(client: UsingClient, data: APIEmoji) { super(client, { ...data, id: data.id! }); - } - - guild(mode?: 'rest' | 'flow'): Promise>; - guild(mode: 'cache'): ReturnCache | undefined>; - guild(mode: 'cache' | 'rest' | 'flow' = 'flow'): unknown { - switch (mode) { - case 'cache': - return ( - this.client.cache.guilds?.get(this.guildId) || - (this.client.cache.adapter.isAsync ? (Promise.resolve() as any) : undefined) - ); - default: - return this.client.guilds.fetch(this.guildId, mode === 'rest'); - } - } - - edit(body: RESTPatchAPIChannelJSONBody, reason?: string): Promise { - return this.client.emojis.edit(this.guildId, this.id, body, reason); - } - - delete(reason?: string) { - return this.client.emojis.delete(this.guildId, this.id, reason); - } - - fetch(force = false): Promise { - return this.client.emojis.fetch(this.guildId, this.id, force); + this.user = (data.user && new User(client, data.user)) as never; } url(options?: BaseCDNUrlOptions) { @@ -58,6 +36,44 @@ export class GuildEmoji extends DiscordBase { animated: !!this.animated, }; } +} + +export interface GuildEmoji extends Emoji {} + +export class GuildEmoji extends Emoji { + constructor( + client: UsingClient, + data: APIEmoji, + readonly guildId: string, + ) { + super(client, data); + } + + guild(mode?: 'rest' | 'flow'): Promise>; + guild(mode: 'cache'): ReturnCache | undefined>; + guild(mode: 'cache' | 'rest' | 'flow' = 'flow'): unknown { + switch (mode) { + case 'cache': + return ( + this.client.cache.guilds?.get(this.guildId) || + (this.client.cache.adapter.isAsync ? (Promise.resolve() as any) : undefined) + ); + default: + return this.client.guilds.fetch(this.guildId, mode === 'rest'); + } + } + + edit(body: RESTPatchAPIGuildEmojiJSONBody, reason?: string): Promise { + return this.client.emojis.edit(this.guildId, this.id, body, reason); + } + + delete(reason?: string) { + return this.client.emojis.delete(this.guildId, this.id, reason); + } + + fetch(force = false): Promise { + return this.client.emojis.fetch(this.guildId, this.id, force); + } static methods({ client, guildId }: MethodContext<{ guildId: string }>) { return { @@ -71,3 +87,21 @@ export class GuildEmoji extends DiscordBase { }; } } + +export class ApplicationEmoji extends Emoji { + constructor(client: UsingClient, data: APIApplicationEmoji) { + super(client, data); + } + + fetch() { + return this.client.applications.getEmoji(this.id); + } + + edit(body: RESTPatchAPIApplicationEmojiJSONBody) { + return this.client.applications.editEmoji(this.id, body); + } + + delete() { + return this.client.applications.deleteEmoji(this.id); + } +} diff --git a/src/structures/Entitlement.ts b/src/structures/Entitlement.ts index bbe32de..52e983c 100644 --- a/src/structures/Entitlement.ts +++ b/src/structures/Entitlement.ts @@ -14,6 +14,6 @@ export class Entitlement extends DiscordBase { } consume() { - return this.client.applications.consumeEntitlement(this.applicationId, this.id); + return this.client.applications.consumeEntitlement(this.id); } } diff --git a/src/structures/Guild.ts b/src/structures/Guild.ts index f5983d8..f4b2c3e 100644 --- a/src/structures/Guild.ts +++ b/src/structures/Guild.ts @@ -3,8 +3,8 @@ import type { UsingClient } from '../commands'; import type { ObjectToLower, StructPropState, StructStates, ToClass } from '../common/types/util'; import type { APIGuild, APIPartialGuild, GatewayGuildCreateDispatchData, RESTPatchAPIGuildJSONBody } from '../types'; import { AutoModerationRule } from './AutoModerationRule'; +import { GuildEmoji } from './Emoji'; import { GuildBan } from './GuildBan'; -import { GuildEmoji } from './GuildEmoji'; import { GuildMember } from './GuildMember'; import { GuildRole } from './GuildRole'; import { GuildTemplate } from './GuildTemplate'; diff --git a/src/structures/extra/BaseGuild.ts b/src/structures/extra/BaseGuild.ts index d82a983..9512aff 100644 --- a/src/structures/extra/BaseGuild.ts +++ b/src/structures/extra/BaseGuild.ts @@ -46,14 +46,20 @@ export class BaseGuild extends DiscordBase { } } + /** + * Leave the guild. + */ + leave() { + return this.client.guilds.leave(this.id); + } + /** * iconURL gets the current guild icon. * @link https://discord.com/developers/docs/reference#image-formatting */ iconURL(options?: ImageOptions): string | undefined { - if (!this.icon) { - return; - } + if (!this.icon) return; + return this.rest.cdn.icons(this.id).get(this.icon, options); } @@ -64,9 +70,8 @@ export class BaseGuild extends DiscordBase { * @returns Splash url or void. */ splashURL(options?: ImageOptions): string | undefined { - if (!this.splash) { - return; - } + if (!this.splash) return; + return this.rest.cdn['discovery-splashes'](this.id).get(this.splash, options); } @@ -77,9 +82,8 @@ export class BaseGuild extends DiscordBase { * @returns Banner url or void */ bannerURL(options?: ImageOptions): string | undefined { - if (!this.banner) { - return; - } + if (!this.banner) return; + return this.rest.cdn.banners(this.id).get(this.banner, options); } diff --git a/src/structures/index.ts b/src/structures/index.ts index 3f8bb3f..3426842 100644 --- a/src/structures/index.ts +++ b/src/structures/index.ts @@ -2,7 +2,7 @@ export * from './AnonymousGuild'; export * from './AutoModerationRule'; export * from './ClientUser'; export * from './Guild'; -export * from './GuildEmoji'; +export * from './Emoji'; export * from './GuildMember'; export * from './GuildPreview'; export * from './GuildRole'; diff --git a/src/types/payloads/application.ts b/src/types/payloads/application.ts index 32643a4..4abd10e 100644 --- a/src/types/payloads/application.ts +++ b/src/types/payloads/application.ts @@ -2,8 +2,9 @@ * Types extracted from https://discord.com/developers/docs/resources/application */ -import type { LocalizationMap } from '.'; +import type { APIEmoji, LocalizationMap } from '.'; import type { Permissions, Snowflake } from '..'; +import type { MakeRequired } from '../../common'; import type { APIPartialGuild } from './guild'; import type { ApplicationIntegrationType } from './interactions'; import type { OAuth2Scopes } from './oauth2'; @@ -292,3 +293,35 @@ export enum ApplicationRoleConnectionMetadataType { */ BooleanNotEqual, } + +/** + * https://discord.com/developers/docs/resources/application#get-application-activity-instance-activity-instance-object + */ +export interface APIActivityInstance { + application_id: string; + instance_id: string; + launch_id: string; + /** Location the instance is runnning in */ + location: APIActivityLocation; + users: string[]; +} + +/** + * https://discord.com/developers/docs/resources/application#get-application-activity-instance-activity-location-object + */ +export interface APIActivityLocation { + id: string; + /** Enum describing kind of location */ + kind: ActivityLocation; + channel_id: string; + guild_id?: string | null; +} +/** + * https://discord.com/developers/docs/resources/application#get-application-activity-instance-activity-location-kind-enum + */ +export enum ActivityLocation { + GuildChannel = 'gc', + PrivateChannel = 'pc', +} + +export type APIApplicationEmoji = MakeRequired; diff --git a/src/types/rest/application.ts b/src/types/rest/application.ts index f762090..e3b1957 100644 --- a/src/types/rest/application.ts +++ b/src/types/rest/application.ts @@ -1,4 +1,9 @@ -import type { APIApplication, APIApplicationRoleConnectionMetadata, APIEmoji } from '../payloads'; +import type { + APIActivityInstance, + APIApplication, + APIApplicationEmoji, + APIApplicationRoleConnectionMetadata, +} from '../payloads'; import type { Nullable, StrictPartial } from '../utils'; import type { RESTPatchAPIGuildEmojiJSONBody, RESTPostAPIGuildEmojiJSONBody } from './emoji'; @@ -49,13 +54,13 @@ export type RESTPatchCurrentApplicationResult = APIApplication; * https://discord.com/developers/docs/resources/emoji#list-application-emojis */ export interface RESTGetAPIApplicationEmojisResult { - emojis: APIEmoji[]; + items: APIApplicationEmoji[]; } /** * https://discord.com/developers/docs/resources/emoji#get-application-emoji */ -export type RESTGetAPIApplicationEmojiResult = APIEmoji; +export type RESTGetAPIApplicationEmojiResult = APIApplicationEmoji; /** * https://discord.com/developers/docs/resources/emoji#create-application-emoji-json-params @@ -65,7 +70,7 @@ export type RESTPostAPIApplicationEmojiJSONBody = Pick