feat: better support for applications and their resources (#328)

* feat: stuff

* fix: :b

* chore: apply formatting

* fix: lol

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
This commit is contained in:
Marcos Susaña 2025-02-14 18:15:39 -04:00 committed by GitHub
parent 7849ad1235
commit a677ec795c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
19 changed files with 392 additions and 118 deletions

View File

@ -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<RESTGetCurrentApplicationResult>;
patch(args: RestArguments<RESTPatchCurrentApplicationJSONBody>): Promise<RESTPatchCurrentApplicationResult>;
'activity-instances': (id: string) => {
get(args?: RestArgumentsNoBody): Promise<RestGetAPIApplicationActivityInstanceResult>;
};
guilds: (id: string) => {
commands: {
get(

View File

@ -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<any, APIEmoji> {
export class Emojis extends GuildRelatedResource<any, APIEmoji | APIApplicationEmoji> {
namespace = 'emoji';
//@ts-expect-error
@ -12,19 +12,25 @@ export class Emojis extends GuildRelatedResource<any, APIEmoji> {
return true;
}
override get(id: string): ReturnCache<GuildEmojiStructure | undefined> {
return fakePromise(super.get(id)).then(rawEmoji =>
rawEmoji ? Transformers.GuildEmoji(this.client, rawEmoji, rawEmoji.guild_id) : undefined,
);
override get(id: string): ReturnCache<GuildEmojiStructure | ApplicationEmojiStructure | undefined> {
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<APIEmoji | undefined> {
return super.get(id);
}
override bulk(ids: string[]): ReturnCache<GuildEmojiStructure[]> {
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<any, APIEmoji> {
return super.bulk(ids);
}
override values(guild: string): ReturnCache<GuildEmojiStructure[]> {
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);
}
}

View File

@ -36,6 +36,14 @@ export class Client<Ready extends boolean = boolean> extends BaseClient {
super(options);
}
get applicationId() {
return this.me?.application.id ?? super.applicationId;
}
set applicationId(id: string) {
super.applicationId = id;
}
setServices({
gateway,
...rest

View File

@ -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<Poll, 'Poll'>;
export type ClientUserStructure = InferCustomStructure<ClientUser, 'ClientUser'>;
export type ApplicationStructure = InferCustomStructure<Application, 'Application'>;
export type ApplicationEmojiStructure = InferCustomStructure<ApplicationEmoji, 'ApplicationEmoji'>;
export type AnonymousGuildStructure = InferCustomStructure<AnonymousGuild, 'AnonymousGuild'>;
export type AutoModerationRuleStructure = InferCustomStructure<AutoModerationRule, 'AutoModerationRule'>;
export type BaseChannelStructure = InferCustomStructure<BaseChannel<ChannelType>, 'BaseChannel'>;
@ -52,6 +57,7 @@ export type NewsChannelStructure = InferCustomStructure<NewsChannel, 'NewsChanne
export type DirectoryChannelStructure = InferCustomStructure<DirectoryChannel, 'DirectoryChannel'>;
export type GuildStructure<State extends StructStates = 'api'> = InferCustomStructure<Guild<State>, 'Guild'>;
export type GuildBanStructure = InferCustomStructure<GuildBan, 'GuildBan'>;
export type EmojiStructure = InferCustomStructure<Emoji, 'Emoji'>;
export type GuildEmojiStructure = InferCustomStructure<GuildEmoji, 'GuildEmoji'>;
export type GuildMemberStructure = InferCustomStructure<GuildMember, 'GuildMember'>;
export type InteractionGuildMemberStructure = InferCustomStructure<InteractionGuildMember, 'InteractionGuildMember'>;
@ -67,6 +73,12 @@ export type OptionResolverStructure = InferCustomStructure<OptionResolver, 'Opti
export type EntitlementStructure = InferCustomStructure<Entitlement, 'Entitlement'>;
export const Transformers = {
Application(...args: ConstructorParameters<typeof Application>): ApplicationStructure {
return new Application(...args);
},
ApplicationEmoji(...args: ConstructorParameters<typeof ApplicationEmoji>): ApplicationEmojiStructure {
return new ApplicationEmoji(...args);
},
AnonymousGuild(...args: ConstructorParameters<typeof AnonymousGuild>): AnonymousGuildStructure {
return new AnonymousGuild(...args);
},
@ -118,6 +130,9 @@ export const Transformers = {
GuildBan(...args: ConstructorParameters<typeof GuildBan>): GuildBanStructure {
return new GuildBan(...args);
},
Emoji(...args: ConstructorParameters<typeof Emoji>): EmojiStructure {
return new Emoji(...args);
},
GuildEmoji(...args: ConstructorParameters<typeof GuildEmoji>): GuildEmojiStructure {
return new GuildEmoji(...args);
},

View File

@ -96,6 +96,14 @@ export class WorkerClient<Ready extends boolean = boolean> 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) {

View File

@ -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<ApplicationEmojiStructure[]> {
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<ApplicationEmojiStructure> {
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<EntitlementStructure[]> {
listEntitlements(query?: RESTGetAPIEntitlementsQuery): Promise<EntitlementStructure[]> {
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<EntitlementStructure> {
createTestEntitlement(body: RESTPostAPIEntitlementBody): Promise<EntitlementStructure> {
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();
}
}

View File

@ -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<GuildBanStructure[]> {
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(

View File

@ -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<APIChannel> {
let channel: APIChannel | ReturnType<Channels['raw']>;
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<ChannelType>).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]);

View File

@ -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<GuildEmojiStructure[]> {
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<GuildEmojiStructure> {
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);
}

View File

@ -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<APIPartialEmoji> | `<${string | undefined}:${string}:${string}>`;
export type GuildMemberResolvable = string | Partial<GuildMember> | 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 }
>;

View File

@ -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<APIApplication> {}
/**
* Due to current limitations, this is exclusively for the current application.
*/
export class Application extends DiscordBase<APIApplication> {
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),
};
}

View File

@ -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<ClientUserStructure> {
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<ClientUserStructure> {
const data = await this.api.users('@me').patch({ body });
return Transformers.ClientUser(this.client, data, this.application);
return this.__patchThis(data);
}
}

View File

@ -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<Omit<APIEmoji, 'id'>> {}
export interface Emoji extends DiscordBase, ObjectToLower<Omit<APIEmoji, 'id' | 'user'>> {}
export class GuildEmoji extends DiscordBase {
constructor(
client: UsingClient,
data: APIEmoji,
readonly guildId: string,
) {
export class Emoji<T extends boolean = false> extends DiscordBase {
user: When<T, User>;
constructor(client: UsingClient, data: APIEmoji) {
super(client, { ...data, id: data.id! });
}
guild(mode?: 'rest' | 'flow'): Promise<GuildStructure<'cached' | 'api'>>;
guild(mode: 'cache'): ReturnCache<GuildStructure<'cached'> | 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<GuildEmojiStructure> {
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<GuildEmojiStructure> {
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<GuildStructure<'cached' | 'api'>>;
guild(mode: 'cache'): ReturnCache<GuildStructure<'cached'> | 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<GuildEmojiStructure> {
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<GuildEmojiStructure> {
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<true> {
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);
}
}

View File

@ -14,6 +14,6 @@ export class Entitlement extends DiscordBase<APIEntitlement> {
}
consume() {
return this.client.applications.consumeEntitlement(this.applicationId, this.id);
return this.client.applications.consumeEntitlement(this.id);
}
}

View File

@ -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';

View File

@ -46,14 +46,20 @@ export class BaseGuild extends DiscordBase<APIPartialGuild> {
}
}
/**
* 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<APIPartialGuild> {
* @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<APIPartialGuild> {
* @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);
}

View File

@ -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';

View File

@ -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<APIEmoji, 'id' | 'user'>;

View File

@ -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<RESTPostAPIGuildEmojiJSON
/**
* https://discord.com/developers/docs/resources/emoji#create-application-emoji
*/
export type RESTPostAPIApplicationEmojiResult = APIEmoji;
export type RESTPostAPIApplicationEmojiResult = APIApplicationEmoji;
/**
* https://discord.com/developers/docs/resources/emoji#modify-application-emoji
@ -75,9 +80,14 @@ export type RESTPatchAPIApplicationEmojiJSONBody = Pick<RESTPatchAPIGuildEmojiJS
/**
* https://discord.com/developers/docs/resources/emoji#modify-application-emoji
*/
export type RESTPatchAPIApplicationEmojiResult = APIEmoji;
export type RESTPatchAPIApplicationEmojiResult = APIApplicationEmoji;
/**
* https://discord.com/developers/docs/resources/emoji#delete-application-emoji
*/
export type RESTDeleteAPIApplicationEmojiResult = never;
/**
* https://discord.com/developers/docs/resources/application#get-application-activity-instance
*/
export type RestGetAPIApplicationActivityInstanceResult = APIActivityInstance | undefined;