feat: remove dynamicAvatarURL, option exclude added in GuildMember#avatarURL

This commit is contained in:
MARCROCK22 2024-06-01 17:42:26 +00:00
parent 9c2b37316a
commit 0fd02117ce
4 changed files with 405 additions and 401 deletions

View File

@ -1,83 +1,83 @@
import { CDN_URL } from '../common'; import { CDN_URL } from '../common';
import type { APIRoutes, ApiHandler, CDNRoute } from './index'; import type { APIRoutes, ApiHandler, CDNRoute } from './index';
import type { HttpMethods, ImageExtension, ImageSize, StickerExtension } from './shared'; import type { HttpMethods, ImageExtension, ImageSize, StickerExtension } from './shared';
export enum ProxyRequestMethod { export enum ProxyRequestMethod {
Delete = 'delete', Delete = 'delete',
Get = 'get', Get = 'get',
Patch = 'patch', Patch = 'patch',
Post = 'post', Post = 'post',
Put = 'put', Put = 'put',
} }
const ArrRequestsMethods = Object.freeze(Object.values(ProxyRequestMethod)) as string[]; const ArrRequestsMethods = Object.freeze(Object.values(ProxyRequestMethod)) as string[];
export class Router { export class Router {
noop = () => { noop = () => {
return; return;
}; };
constructor(private rest: ApiHandler) {} constructor(private rest: ApiHandler) {}
createProxy(route = [] as string[]): APIRoutes { createProxy(route = [] as string[]): APIRoutes {
return new Proxy(this.noop, { return new Proxy(this.noop, {
get: (_, key: string) => { get: (_, key: string) => {
if (ArrRequestsMethods.includes(key)) { if (ArrRequestsMethods.includes(key)) {
return (...options: any[]) => return (...options: any[]) =>
this.rest.request(key.toUpperCase() as HttpMethods, `/${route.join('/')}`, ...options); this.rest.request(key.toUpperCase() as HttpMethods, `/${route.join('/')}`, ...options);
} }
return this.createProxy([...route, key]); return this.createProxy([...route, key]);
}, },
apply: (...[, _, args]) => { apply: (...[, _, args]) => {
return this.createProxy([...route, ...args]); return this.createProxy([...route, ...args]);
}, },
}) as unknown as APIRoutes; }) as unknown as APIRoutes;
} }
} }
export const CDNRouter = { export const CDNRouter = {
createProxy(route = [] as string[]): CDNRoute { createProxy(route = [] as string[]): CDNRoute {
const noop = () => { const noop = () => {
return; return;
}; };
return new Proxy(noop, { return new Proxy(noop, {
get: (_, key: string) => { get: (_, key: string) => {
if (key === 'get') { if (key === 'get') {
return (value: string | CDNUrlOptions | undefined, options?: CDNUrlOptions) => { return (value: string | CDNUrlOptions | undefined, options?: CDNUrlOptions) => {
const lastRoute = `${CDN_URL}/${route.join('/')}`; const lastRoute = `${CDN_URL}/${route.join('/')}`;
let routeResult = lastRoute; let routeResult = lastRoute;
if (typeof value === 'string') { if (typeof value === 'string' || typeof value === 'number') {
routeResult = `${lastRoute}${value ? `/${value}` : ''}`; routeResult = `${lastRoute}${value ? `/${value}` : ''}`;
return parseCDNURL(routeResult, options); return parseCDNURL(routeResult, options);
} }
return parseCDNURL(routeResult, value); return parseCDNURL(routeResult, value);
}; };
} }
return this.createProxy([...route, key]); return this.createProxy([...route, key]);
}, },
apply: (...[, _, args]) => { apply: (...[, _, args]) => {
return this.createProxy([...route, ...args]); return this.createProxy([...route, ...args]);
}, },
}) as unknown as CDNRoute; }) as unknown as CDNRoute;
}, },
}; };
export interface BaseCDNUrlOptions { export interface BaseCDNUrlOptions {
extension?: ImageExtension | StickerExtension | undefined; extension?: ImageExtension | StickerExtension | undefined;
size?: ImageSize; size?: ImageSize;
} }
export interface CDNUrlOptions extends BaseCDNUrlOptions { export interface CDNUrlOptions extends BaseCDNUrlOptions {
forceStatic?: boolean; forceStatic?: boolean;
} }
export function parseCDNURL(route: string, options: CDNUrlOptions = {}) { export function parseCDNURL(route: string, options: CDNUrlOptions = {}) {
if (options.forceStatic && route.includes('a_')) options.extension = 'png'; if (options.forceStatic && route.includes('a_')) options.extension = 'png';
if (!options.extension && route.includes('a_')) options.extension = 'gif'; if (!options.extension && route.includes('a_')) options.extension = 'gif';
const url = new URL(`${route}.${options.extension || 'png'}`); const url = new URL(`${route}.${options.extension || 'png'}`);
if (options.size) url.searchParams.set('size', `${options.size}`); if (options.size) url.searchParams.set('size', `${options.size}`);
return url.toString(); return url.toString();
} }

View File

@ -266,6 +266,8 @@ export class CommandHandler extends BaseHandler {
this.client.options?.commands?.defaults?.onPermissionsFail; this.client.options?.commands?.defaults?.onPermissionsFail;
option.botPermissions ??= commandInstance.botPermissions; option.botPermissions ??= commandInstance.botPermissions;
option.defaultMemberPermissions ??= commandInstance.defaultMemberPermissions; option.defaultMemberPermissions ??= commandInstance.defaultMemberPermissions;
option.contexts ??= commandInstance.contexts;
option.integrationTypes ??= commandInstance.integrationTypes;
} }
} }

View File

@ -1,257 +1,255 @@
import { DiscordBase } from './extra/DiscordBase'; import { DiscordBase } from './extra/DiscordBase';
export type GuildMemberData = export type GuildMemberData =
| APIGuildMember | APIGuildMember
| GatewayGuildMemberUpdateDispatchData | GatewayGuildMemberUpdateDispatchData
| GatewayGuildMemberAddDispatchData | GatewayGuildMemberAddDispatchData
| APIInteractionDataResolvedGuildMember; | APIInteractionDataResolvedGuildMember;
import type { import type {
APIGuildMember, APIGuildMember,
APIInteractionDataResolvedGuildMember, APIInteractionDataResolvedGuildMember,
APIUser, APIUser,
GatewayGuildMemberAddDispatchData, GatewayGuildMemberAddDispatchData,
GatewayGuildMemberUpdateDispatchData, GatewayGuildMemberUpdateDispatchData,
RESTGetAPIGuildMembersQuery, RESTGetAPIGuildMembersQuery,
RESTGetAPIGuildMembersSearchQuery, RESTGetAPIGuildMembersSearchQuery,
RESTPatchAPIGuildMemberJSONBody, RESTPatchAPIGuildMemberJSONBody,
RESTPutAPIGuildBanJSONBody, RESTPutAPIGuildBanJSONBody,
RESTPutAPIGuildMemberJSONBody, RESTPutAPIGuildMemberJSONBody,
} from 'discord-api-types/v10'; } from 'discord-api-types/v10';
import type { UsingClient } from '../commands'; import type { UsingClient } from '../commands';
import type { MakeRequired, MessageCreateBodyRequest, ObjectToLower, ToClass } from '../common'; import type { MakeRequired, MessageCreateBodyRequest, ObjectToLower, ToClass } from '../common';
import type { ImageOptions, MethodContext } from '../common/types/options'; import type { ImageOptions, MethodContext } from '../common/types/options';
import type { GuildMemberResolvable } from '../common/types/resolvables'; import type { GuildMemberResolvable } from '../common/types/resolvables';
import { User } from './User'; import { User } from './User';
import { PermissionsBitField } from './extra/Permissions'; import { PermissionsBitField } from './extra/Permissions';
export type GatewayGuildMemberAddDispatchDataFixed<Pending extends boolean> = Pending extends true export type GatewayGuildMemberAddDispatchDataFixed<Pending extends boolean> = Pending extends true
? Omit<GatewayGuildMemberAddDispatchData, 'user'> & { id: string } ? Omit<GatewayGuildMemberAddDispatchData, 'user'> & { id: string }
: MakeRequired<GatewayGuildMemberAddDispatchData, 'user'>; : MakeRequired<GatewayGuildMemberAddDispatchData, 'user'>;
export interface BaseGuildMember extends DiscordBase, ObjectToLower<Omit<APIGuildMember, 'user' | 'roles'>> {} export interface BaseGuildMember extends DiscordBase, ObjectToLower<Omit<APIGuildMember, 'user' | 'roles'>> {}
export class BaseGuildMember extends DiscordBase { export class BaseGuildMember extends DiscordBase {
private _roles: string[]; private _roles: string[];
joinedTimestamp?: number; joinedTimestamp?: number;
communicationDisabledUntilTimestamp?: number | null; communicationDisabledUntilTimestamp?: number | null;
constructor( constructor(
client: UsingClient, client: UsingClient,
data: GuildMemberData, data: GuildMemberData,
id: string, id: string,
/** the choosen guild id */ /** the choosen guild id */
readonly guildId: string, readonly guildId: string,
) { ) {
const { roles, ...dataN } = data; const { roles, ...dataN } = data;
super(client, { ...dataN, id }); super(client, { ...dataN, id });
this._roles = data.roles; this._roles = data.roles;
this.patch(data); this.patch(data);
} }
guild(force = false) { guild(force = false) {
return this.client.guilds.fetch(this.id, force); return this.client.guilds.fetch(this.id, force);
} }
fetch(force = false) { fetch(force = false) {
return this.client.members.fetch(this.guildId, this.id, force); return this.client.members.fetch(this.guildId, this.id, force);
} }
ban(body?: RESTPutAPIGuildBanJSONBody, reason?: string) { ban(body?: RESTPutAPIGuildBanJSONBody, reason?: string) {
return this.client.members.ban(this.guildId, this.id, body, reason); return this.client.members.ban(this.guildId, this.id, body, reason);
} }
kick(reason?: string) { kick(reason?: string) {
return this.client.members.kick(this.guildId, this.id, reason); return this.client.members.kick(this.guildId, this.id, reason);
} }
edit(body: RESTPatchAPIGuildMemberJSONBody, reason?: string) { edit(body: RESTPatchAPIGuildMemberJSONBody, reason?: string) {
return this.client.members.edit(this.guildId, this.id, body, reason); return this.client.members.edit(this.guildId, this.id, body, reason);
} }
presence() { presence() {
return this.client.members.presence(this.id); return this.client.members.presence(this.id);
} }
voice() { voice() {
return this.client.members.voice(this.guildId, this.id); return this.client.members.voice(this.guildId, this.id);
} }
toString() { toString() {
return `<@${this.id}>`; return `<@${this.id}>`;
} }
private patch(data: GuildMemberData) { private patch(data: GuildMemberData) {
if ('joined_at' in data && data.joined_at) { if ('joined_at' in data && data.joined_at) {
this.joinedTimestamp = Date.parse(data.joined_at); this.joinedTimestamp = Date.parse(data.joined_at);
} }
if ('communication_disabled_until' in data) { if ('communication_disabled_until' in data) {
this.communicationDisabledUntilTimestamp = data.communication_disabled_until?.length this.communicationDisabledUntilTimestamp = data.communication_disabled_until?.length
? Date.parse(data.communication_disabled_until) ? Date.parse(data.communication_disabled_until)
: null; : null;
} }
} }
get roles() { get roles() {
return { return {
keys: Object.freeze(this._roles.concat(this.guildId)) as string[], keys: Object.freeze(this._roles.concat(this.guildId)) as string[],
list: (force = false) => list: (force = false) =>
this.client.roles this.client.roles
.list(this.guildId, force) .list(this.guildId, force)
.then(roles => roles.filter(role => this.roles.keys.includes(role.id))), .then(roles => roles.filter(role => this.roles.keys.includes(role.id))),
add: (id: string) => this.client.members.addRole(this.guildId, this.id, id), add: (id: string) => this.client.members.addRole(this.guildId, this.id, id),
remove: (id: string) => this.client.members.removeRole(this.guildId, this.id, id), remove: (id: string) => this.client.members.removeRole(this.guildId, this.id, id),
permissions: (force = false) => permissions: (force = false) =>
this.roles.list(force).then(roles => new PermissionsBitField(roles.map(x => BigInt(x.permissions.bits)))), this.roles.list(force).then(roles => new PermissionsBitField(roles.map(x => BigInt(x.permissions.bits)))),
sorted: (force = false) => this.roles.list(force).then(roles => roles.sort((a, b) => b.position - a.position)), sorted: (force = false) => this.roles.list(force).then(roles => roles.sort((a, b) => b.position - a.position)),
highest: (force = false) => this.roles.sorted(force).then(roles => roles[0]), highest: (force = false) => this.roles.sorted(force).then(roles => roles[0]),
}; };
} }
static methods({ client, guildId }: MethodContext<{ guildId: string }>) { static methods({ client, guildId }: MethodContext<{ guildId: string }>) {
return { return {
resolve: (resolve: GuildMemberResolvable) => client.members.resolve(guildId, resolve), resolve: (resolve: GuildMemberResolvable) => client.members.resolve(guildId, resolve),
search: (query?: RESTGetAPIGuildMembersSearchQuery) => client.members.search(guildId, query), search: (query?: RESTGetAPIGuildMembersSearchQuery) => client.members.search(guildId, query),
unban: (id: string, body?: RESTPutAPIGuildBanJSONBody, reason?: string) => unban: (id: string, body?: RESTPutAPIGuildBanJSONBody, reason?: string) =>
client.members.unban(guildId, id, body, reason), client.members.unban(guildId, id, body, reason),
ban: (id: string, body?: RESTPutAPIGuildBanJSONBody, reason?: string) => ban: (id: string, body?: RESTPutAPIGuildBanJSONBody, reason?: string) =>
client.members.ban(guildId, id, body, reason), client.members.ban(guildId, id, body, reason),
kick: (id: string, reason?: string) => client.members.kick(guildId, id, reason), kick: (id: string, reason?: string) => client.members.kick(guildId, id, reason),
edit: (id: string, body: RESTPatchAPIGuildMemberJSONBody, reason?: string) => edit: (id: string, body: RESTPatchAPIGuildMemberJSONBody, reason?: string) =>
client.members.edit(guildId, id, body, reason), client.members.edit(guildId, id, body, reason),
add: (id: string, body: RESTPutAPIGuildMemberJSONBody) => client.members.add(guildId, id, body), add: (id: string, body: RESTPutAPIGuildMemberJSONBody) => client.members.add(guildId, id, body),
fetch: (memberId: string, force = false) => client.members.fetch(guildId, memberId, force), fetch: (memberId: string, force = false) => client.members.fetch(guildId, memberId, force),
list: (query?: RESTGetAPIGuildMembersQuery, force = false) => client.members.list(guildId, query, force), list: (query?: RESTGetAPIGuildMembersQuery, force = false) => client.members.list(guildId, query, force),
}; };
} }
} }
export interface GuildMember extends ObjectToLower<Omit<APIGuildMember, 'user' | 'roles'>> {} export interface GuildMember extends ObjectToLower<Omit<APIGuildMember, 'user' | 'roles'>> {}
/** /**
* Represents a guild member * Represents a guild member
* @link https://discord.com/developers/docs/resources/guild#guild-member-object * @link https://discord.com/developers/docs/resources/guild#guild-member-object
*/ */
export class GuildMember extends BaseGuildMember { export class GuildMember extends BaseGuildMember {
user: User; user: User;
private __me?: GuildMember; private __me?: GuildMember;
constructor( constructor(
client: UsingClient, client: UsingClient,
data: GuildMemberData, data: GuildMemberData,
user: APIUser | User, user: APIUser | User,
/** the choosen guild id */ /** the choosen guild id */
readonly guildId: string, readonly guildId: string,
) { ) {
super(client, data, user.id, guildId); super(client, data, user.id, guildId);
this.user = user instanceof User ? user : new User(client, user); this.user = user instanceof User ? user : new User(client, user);
} }
get tag() { get tag() {
return this.user.tag; return this.user.tag;
} }
get bot() { get bot() {
return this.user.bot; return this.user.bot;
} }
get name() { get name() {
return this.user.name; return this.user.name;
} }
get username() { get username() {
return this.user.username; return this.user.username;
} }
get globalName() { get globalName() {
return this.user.globalName; return this.user.globalName;
} }
/** gets the nickname or the username */ /** gets the nickname or the username */
get displayName() { get displayName() {
return this.nick ?? this.globalName ?? this.username; return this.nick ?? this.globalName ?? this.username;
} }
dm(force = false) { dm(force = false) {
return this.user.dm(force); return this.user.dm(force);
} }
write(body: MessageCreateBodyRequest) { write(body: MessageCreateBodyRequest) {
return this.user.write(body); return this.user.write(body);
} }
avatarURL(options?: ImageOptions) { defaultAvatarURL() {
if (!this.avatar) { return this.user.defaultAvatarURL();
return null; }
}
avatarURL(options: ImageOptions & { exclude: true }): string | null;
return this.rest.cdn.guilds(this.guildId).users(this.id).avatars(this.avatar).get(options); avatarURL(options?: ImageOptions & { exclude?: false }): string;
} avatarURL(options?: ImageOptions & { exclude?: boolean }): string | null {
if (!this.avatar) {
dynamicAvatarURL(options?: ImageOptions) { return options?.exclude ? null : this.user.avatarURL();
if (!this.avatar) { }
return this.user.avatarURL(options);
} return this.rest.cdn.guilds(this.guildId).users(this.id).avatars(this.avatar).get(options);
}
return this.rest.cdn.guilds(this.guildId).users(this.id).avatars(this.avatar).get(options);
} bannerURL(options?: ImageOptions) {
return this.user.bannerURL(options);
bannerURL(options?: ImageOptions) { }
return this.user.bannerURL(options);
} async fetchPermissions(force = false) {
if ('permissions' in this) return this.permissions as PermissionsBitField;
async fetchPermissions(force = false) { return this.roles.permissions(force);
if ('permissions' in this) return this.permissions as PermissionsBitField; }
return this.roles.permissions(force);
} async manageable(force = false) {
this.__me = await this.client.guilds.fetchSelf(this.guildId, force);
async manageable(force = false) { const ownerId = (await this.client.guilds.fetch(this.guildId, force)).ownerId;
this.__me = await this.client.guilds.fetchSelf(this.guildId, force); if (this.user.id === ownerId) return false;
const ownerId = (await this.client.guilds.fetch(this.guildId, force)).ownerId; if (this.user.id === this.client.botId) return false;
if (this.user.id === ownerId) return false; if (this.client.botId === ownerId) return true;
if (this.user.id === this.client.botId) return false; return (await this.__me!.roles.highest()).position > (await this.roles.highest(force)).position;
if (this.client.botId === ownerId) return true; }
return (await this.__me!.roles.highest()).position > (await this.roles.highest(force)).position;
} async bannable(force = false) {
return (await this.manageable(force)) && (await this.__me!.fetchPermissions(force)).has('BanMembers');
async bannable(force = false) { }
return (await this.manageable(force)) && (await this.__me!.fetchPermissions(force)).has('BanMembers');
} async kickable(force = false) {
return (await this.manageable(force)) && (await this.__me!.fetchPermissions(force)).has('KickMembers');
async kickable(force = false) { }
return (await this.manageable(force)) && (await this.__me!.fetchPermissions(force)).has('KickMembers');
} async moderatable(force = false) {
return (
async moderatable(force = false) { !(await this.roles.permissions(force)).has('Administrator') &&
return ( (await this.manageable(force)) &&
!(await this.roles.permissions(force)).has('Administrator') && (await this.__me!.fetchPermissions(force)).has('KickMembers')
(await this.manageable(force)) && );
(await this.__me!.fetchPermissions(force)).has('KickMembers') }
); }
}
} export interface UnavailableMember {
pending: true;
export interface UnavailableMember { }
pending: true;
} export class UnavailableMember extends BaseGuildMember {}
export class UnavailableMember extends BaseGuildMember {} export interface InteractionGuildMember
extends ObjectToLower<Omit<APIInteractionDataResolvedGuildMember, 'roles' | 'deaf' | 'mute' | 'permissions'>> {}
export interface InteractionGuildMember /**
extends ObjectToLower<Omit<APIInteractionDataResolvedGuildMember, 'roles' | 'deaf' | 'mute' | 'permissions'>> {} * Represents a guild member
/** * @link https://discord.com/developers/docs/resources/guild#guild-member-object
* Represents a guild member */
* @link https://discord.com/developers/docs/resources/guild#guild-member-object export class InteractionGuildMember extends (GuildMember as unknown as ToClass<
*/ Omit<GuildMember, 'deaf' | 'mute'>,
export class InteractionGuildMember extends (GuildMember as unknown as ToClass< InteractionGuildMember
Omit<GuildMember, 'deaf' | 'mute'>, >) {
InteractionGuildMember permissions: PermissionsBitField;
>) { constructor(
permissions: PermissionsBitField; client: UsingClient,
constructor( data: APIInteractionDataResolvedGuildMember,
client: UsingClient, user: APIUser | User,
data: APIInteractionDataResolvedGuildMember, /** the choosen guild id */
user: APIUser | User, guildId: string,
/** the choosen guild id */ ) {
guildId: string, super(client, data, user, guildId);
) { this.permissions = new PermissionsBitField(Number(data.permissions));
super(client, data, user, guildId); }
this.permissions = new PermissionsBitField(Number(data.permissions)); }
}
}

View File

@ -1,61 +1,65 @@
import type { APIUser } from 'discord-api-types/v10'; import type { APIUser } from 'discord-api-types/v10';
import { calculateUserDefaultAvatarIndex } from '../api'; import { calculateUserDefaultAvatarIndex } from '../api';
import type { MessageCreateBodyRequest, ObjectToLower } from '../common'; import type { MessageCreateBodyRequest, ObjectToLower } from '../common';
import type { ImageOptions } from '../common/types/options'; import type { ImageOptions } from '../common/types/options';
import { DiscordBase } from './extra/DiscordBase'; import { DiscordBase } from './extra/DiscordBase';
export interface User extends ObjectToLower<APIUser> {} export interface User extends ObjectToLower<APIUser> {}
export class User extends DiscordBase<APIUser> { export class User extends DiscordBase<APIUser> {
get tag() { get tag() {
return this.globalName ?? `${this.username}#${this.discriminator}`; return this.globalName ?? `${this.username}#${this.discriminator}`;
} }
get name() { get name() {
return this.globalName ?? this.username; return this.globalName ?? this.username;
} }
/** /**
* Fetch user * Fetch user
*/ */
fetch(force = false) { fetch(force = false) {
return this.client.users.fetch(this.id, force); return this.client.users.fetch(this.id, force);
} }
/** /**
* Open a DM with the user * Open a DM with the user
*/ */
dm(force = false) { dm(force = false) {
return this.client.users.createDM(this.id, force); return this.client.users.createDM(this.id, force);
} }
write(body: MessageCreateBodyRequest) { write(body: MessageCreateBodyRequest) {
return this.client.users.write(this.id, body); return this.client.users.write(this.id, body);
} }
avatarURL(options?: ImageOptions) { defaultAvatarURL() {
if (!this.avatar) { return this.rest.cdn.embed.avatars.get(calculateUserDefaultAvatarIndex(this.id, this.discriminator));
return this.rest.cdn.embed.avatars.get(calculateUserDefaultAvatarIndex(this.id, this.discriminator)); }
}
avatarURL(options?: ImageOptions) {
return this.rest.cdn.avatars(this.id).get(this.avatar, options); if (!this.avatar) {
} return this.defaultAvatarURL();
}
avatarDecorationURL(options?: ImageOptions) {
if (!this.avatarDecoration) return; return this.rest.cdn.avatars(this.id).get(this.avatar, options);
return this.rest.cdn['avatar-decorations'](this.id).get(this.avatarDecoration, options); }
}
avatarDecorationURL(options?: ImageOptions) {
bannerURL(options?: ImageOptions) { if (!this.avatarDecoration) return;
if (!this.banner) return; return this.rest.cdn['avatar-decorations'](this.id).get(this.avatarDecoration, options);
return this.rest.cdn.banners(this.id).get(this.banner, options); }
}
bannerURL(options?: ImageOptions) {
presence() { if (!this.banner) return;
return this.client.members.presence(this.id); return this.rest.cdn.banners(this.id).get(this.banner, options);
} }
toString() { presence() {
return `<@${this.id}>`; return this.client.members.presence(this.id);
} }
}
toString() {
return `<@${this.id}>`;
}
}