mirror of
https://github.com/discordjs/discord.js.git
synced 2024-08-21 23:14:44 +12:00
fix: Correct base path for GIF stickers (#10330)
* fix: correct base path for GIF stickers * test: add sticker GIF --------- Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
This commit is contained in:
parent
7f60a8fc5d
commit
599ad3eab5
5 changed files with 65 additions and 34 deletions
|
@ -1,114 +1,119 @@
|
||||||
import { test, expect } from 'vitest';
|
import { test, expect } from 'vitest';
|
||||||
import { CDN } from '../src/index.js';
|
import { CDN } from '../src/index.js';
|
||||||
|
|
||||||
const base = 'https://discord.com';
|
const baseCDN = 'https://cdn-discord.com';
|
||||||
|
const baseMedia = 'https://media-discord.com';
|
||||||
const id = '123456';
|
const id = '123456';
|
||||||
const hash = 'abcdef';
|
const hash = 'abcdef';
|
||||||
const animatedHash = 'a_bcdef';
|
const animatedHash = 'a_bcdef';
|
||||||
const defaultAvatar = 1_234 % 5;
|
const defaultAvatar = 1_234 % 5;
|
||||||
|
|
||||||
const cdn = new CDN(base);
|
const cdn = new CDN(baseCDN, baseMedia);
|
||||||
|
|
||||||
test('appAsset default', () => {
|
test('appAsset default', () => {
|
||||||
expect(cdn.appAsset(id, hash)).toEqual(`${base}/app-assets/${id}/${hash}.webp`);
|
expect(cdn.appAsset(id, hash)).toEqual(`${baseCDN}/app-assets/${id}/${hash}.webp`);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('appIcon default', () => {
|
test('appIcon default', () => {
|
||||||
expect(cdn.appIcon(id, hash)).toEqual(`${base}/app-icons/${id}/${hash}.webp`);
|
expect(cdn.appIcon(id, hash)).toEqual(`${baseCDN}/app-icons/${id}/${hash}.webp`);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('avatar default', () => {
|
test('avatar default', () => {
|
||||||
expect(cdn.avatar(id, hash)).toEqual(`${base}/avatars/${id}/${hash}.webp`);
|
expect(cdn.avatar(id, hash)).toEqual(`${baseCDN}/avatars/${id}/${hash}.webp`);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('avatar dynamic-animated', () => {
|
test('avatar dynamic-animated', () => {
|
||||||
expect(cdn.avatar(id, animatedHash)).toEqual(`${base}/avatars/${id}/${animatedHash}.gif`);
|
expect(cdn.avatar(id, animatedHash)).toEqual(`${baseCDN}/avatars/${id}/${animatedHash}.gif`);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('avatar dynamic-not-animated', () => {
|
test('avatar dynamic-not-animated', () => {
|
||||||
expect(cdn.avatar(id, hash)).toEqual(`${base}/avatars/${id}/${hash}.webp`);
|
expect(cdn.avatar(id, hash)).toEqual(`${baseCDN}/avatars/${id}/${hash}.webp`);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('avatar decoration default', () => {
|
test('avatar decoration default', () => {
|
||||||
expect(cdn.avatarDecoration(id, hash)).toEqual(`${base}/avatar-decorations/${id}/${hash}.webp`);
|
expect(cdn.avatarDecoration(id, hash)).toEqual(`${baseCDN}/avatar-decorations/${id}/${hash}.webp`);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('avatar decoration preset', () => {
|
test('avatar decoration preset', () => {
|
||||||
expect(cdn.avatarDecoration(hash)).toEqual(`${base}/avatar-decoration-presets/${hash}.png`);
|
expect(cdn.avatarDecoration(hash)).toEqual(`${baseCDN}/avatar-decoration-presets/${hash}.png`);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('banner default', () => {
|
test('banner default', () => {
|
||||||
expect(cdn.banner(id, hash)).toEqual(`${base}/banners/${id}/${hash}.webp`);
|
expect(cdn.banner(id, hash)).toEqual(`${baseCDN}/banners/${id}/${hash}.webp`);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('channelIcon default', () => {
|
test('channelIcon default', () => {
|
||||||
expect(cdn.channelIcon(id, hash)).toEqual(`${base}/channel-icons/${id}/${hash}.webp`);
|
expect(cdn.channelIcon(id, hash)).toEqual(`${baseCDN}/channel-icons/${id}/${hash}.webp`);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('defaultAvatar default', () => {
|
test('defaultAvatar default', () => {
|
||||||
expect(cdn.defaultAvatar(defaultAvatar)).toEqual(`${base}/embed/avatars/${defaultAvatar}.png`);
|
expect(cdn.defaultAvatar(defaultAvatar)).toEqual(`${baseCDN}/embed/avatars/${defaultAvatar}.png`);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('discoverySplash default', () => {
|
test('discoverySplash default', () => {
|
||||||
expect(cdn.discoverySplash(id, hash)).toEqual(`${base}/discovery-splashes/${id}/${hash}.webp`);
|
expect(cdn.discoverySplash(id, hash)).toEqual(`${baseCDN}/discovery-splashes/${id}/${hash}.webp`);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('emoji default', () => {
|
test('emoji default', () => {
|
||||||
expect(cdn.emoji(id)).toEqual(`${base}/emojis/${id}.webp`);
|
expect(cdn.emoji(id)).toEqual(`${baseCDN}/emojis/${id}.webp`);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('emoji gif', () => {
|
test('emoji gif', () => {
|
||||||
expect(cdn.emoji(id, 'gif')).toEqual(`${base}/emojis/${id}.gif`);
|
expect(cdn.emoji(id, 'gif')).toEqual(`${baseCDN}/emojis/${id}.gif`);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('guildMemberAvatar default', () => {
|
test('guildMemberAvatar default', () => {
|
||||||
expect(cdn.guildMemberAvatar(id, id, hash)).toEqual(`${base}/guilds/${id}/users/${id}/avatars/${hash}.webp`);
|
expect(cdn.guildMemberAvatar(id, id, hash)).toEqual(`${baseCDN}/guilds/${id}/users/${id}/avatars/${hash}.webp`);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('guildMemberAvatar dynamic-animated', () => {
|
test('guildMemberAvatar dynamic-animated', () => {
|
||||||
expect(cdn.guildMemberAvatar(id, id, animatedHash)).toEqual(
|
expect(cdn.guildMemberAvatar(id, id, animatedHash)).toEqual(
|
||||||
`${base}/guilds/${id}/users/${id}/avatars/${animatedHash}.gif`,
|
`${baseCDN}/guilds/${id}/users/${id}/avatars/${animatedHash}.gif`,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('guildMemberAvatar dynamic-not-animated', () => {
|
test('guildMemberAvatar dynamic-not-animated', () => {
|
||||||
expect(cdn.guildMemberAvatar(id, id, hash)).toEqual(`${base}/guilds/${id}/users/${id}/avatars/${hash}.webp`);
|
expect(cdn.guildMemberAvatar(id, id, hash)).toEqual(`${baseCDN}/guilds/${id}/users/${id}/avatars/${hash}.webp`);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('guildScheduledEventCover default', () => {
|
test('guildScheduledEventCover default', () => {
|
||||||
expect(cdn.guildScheduledEventCover(id, hash)).toEqual(`${base}/guild-events/${id}/${hash}.webp`);
|
expect(cdn.guildScheduledEventCover(id, hash)).toEqual(`${baseCDN}/guild-events/${id}/${hash}.webp`);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('icon default', () => {
|
test('icon default', () => {
|
||||||
expect(cdn.icon(id, hash)).toEqual(`${base}/icons/${id}/${hash}.webp`);
|
expect(cdn.icon(id, hash)).toEqual(`${baseCDN}/icons/${id}/${hash}.webp`);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('icon dynamic-animated', () => {
|
test('icon dynamic-animated', () => {
|
||||||
expect(cdn.icon(id, animatedHash)).toEqual(`${base}/icons/${id}/${animatedHash}.gif`);
|
expect(cdn.icon(id, animatedHash)).toEqual(`${baseCDN}/icons/${id}/${animatedHash}.gif`);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('icon dynamic-not-animated', () => {
|
test('icon dynamic-not-animated', () => {
|
||||||
expect(cdn.icon(id, hash)).toEqual(`${base}/icons/${id}/${hash}.webp`);
|
expect(cdn.icon(id, hash)).toEqual(`${baseCDN}/icons/${id}/${hash}.webp`);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('role icon default', () => {
|
test('role icon default', () => {
|
||||||
expect(cdn.roleIcon(id, hash)).toEqual(`${base}/role-icons/${id}/${hash}.webp`);
|
expect(cdn.roleIcon(id, hash)).toEqual(`${baseCDN}/role-icons/${id}/${hash}.webp`);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('splash default', () => {
|
test('splash default', () => {
|
||||||
expect(cdn.splash(id, hash)).toEqual(`${base}/splashes/${id}/${hash}.webp`);
|
expect(cdn.splash(id, hash)).toEqual(`${baseCDN}/splashes/${id}/${hash}.webp`);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('sticker default', () => {
|
test('sticker default', () => {
|
||||||
expect(cdn.sticker(id)).toEqual(`${base}/stickers/${id}.png`);
|
expect(cdn.sticker(id)).toEqual(`${baseCDN}/stickers/${id}.png`);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('sticker GIF', () => {
|
||||||
|
expect(cdn.sticker(id, 'gif')).toEqual(`${baseMedia}/stickers/${id}.gif`);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('stickerPackBanner default', () => {
|
test('stickerPackBanner default', () => {
|
||||||
expect(cdn.stickerPackBanner(id)).toEqual(`${base}/app-assets/710982414301790216/store/${id}.webp`);
|
expect(cdn.stickerPackBanner(id)).toEqual(`${baseCDN}/app-assets/710982414301790216/store/${id}.webp`);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('teamIcon default', () => {
|
test('teamIcon default', () => {
|
||||||
expect(cdn.teamIcon(id, hash)).toEqual(`${base}/team-icons/${id}/${hash}.webp`);
|
expect(cdn.teamIcon(id, hash)).toEqual(`${baseCDN}/team-icons/${id}/${hash}.webp`);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('makeURL throws on invalid size', () => {
|
test('makeURL throws on invalid size', () => {
|
||||||
|
@ -122,5 +127,5 @@ test('makeURL throws on invalid extension', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('makeURL valid size', () => {
|
test('makeURL valid size', () => {
|
||||||
expect(cdn.avatar(id, animatedHash, { size: 512 })).toEqual(`${base}/avatars/${id}/${animatedHash}.gif?size=512`);
|
expect(cdn.avatar(id, animatedHash, { size: 512 })).toEqual(`${baseCDN}/avatars/${id}/${animatedHash}.gif?size=512`);
|
||||||
});
|
});
|
||||||
|
|
|
@ -46,6 +46,12 @@ export interface MakeURLOptions {
|
||||||
* The allowed extensions that can be used
|
* The allowed extensions that can be used
|
||||||
*/
|
*/
|
||||||
allowedExtensions?: readonly string[];
|
allowedExtensions?: readonly string[];
|
||||||
|
/**
|
||||||
|
* The base URL.
|
||||||
|
*
|
||||||
|
* @defaultValue `DefaultRestOptions.cdn`
|
||||||
|
*/
|
||||||
|
base?: string;
|
||||||
/**
|
/**
|
||||||
* The extension to use for the image URL
|
* The extension to use for the image URL
|
||||||
*
|
*
|
||||||
|
@ -62,7 +68,10 @@ export interface MakeURLOptions {
|
||||||
* The CDN link builder
|
* The CDN link builder
|
||||||
*/
|
*/
|
||||||
export class CDN {
|
export class CDN {
|
||||||
public constructor(private readonly base: string = DefaultRestOptions.cdn) {}
|
public constructor(
|
||||||
|
private readonly cdn: string = DefaultRestOptions.cdn,
|
||||||
|
private readonly mediaProxy: string = DefaultRestOptions.mediaProxy,
|
||||||
|
) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generates an app asset URL for a client's asset.
|
* Generates an app asset URL for a client's asset.
|
||||||
|
@ -287,10 +296,15 @@ export class CDN {
|
||||||
* @param stickerId - The sticker id
|
* @param stickerId - The sticker id
|
||||||
* @param extension - The extension of the sticker
|
* @param extension - The extension of the sticker
|
||||||
* @privateRemarks
|
* @privateRemarks
|
||||||
* Stickers cannot have a `.webp` extension, so we default to a `.png`
|
* Stickers cannot have a `.webp` extension, so we default to a `.png`.
|
||||||
|
* Sticker GIFs do not use the CDN base URL.
|
||||||
*/
|
*/
|
||||||
public sticker(stickerId: string, extension: StickerExtension = 'png'): string {
|
public sticker(stickerId: string, extension: StickerExtension = 'png'): string {
|
||||||
return this.makeURL(`/stickers/${stickerId}`, { allowedExtensions: ALLOWED_STICKER_EXTENSIONS, extension });
|
return this.makeURL(`/stickers/${stickerId}`, {
|
||||||
|
allowedExtensions: ALLOWED_STICKER_EXTENSIONS,
|
||||||
|
base: extension === 'gif' ? this.mediaProxy : this.cdn,
|
||||||
|
extension,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -352,7 +366,12 @@ export class CDN {
|
||||||
*/
|
*/
|
||||||
private makeURL(
|
private makeURL(
|
||||||
route: string,
|
route: string,
|
||||||
{ allowedExtensions = ALLOWED_EXTENSIONS, extension = 'webp', size }: Readonly<MakeURLOptions> = {},
|
{
|
||||||
|
allowedExtensions = ALLOWED_EXTENSIONS,
|
||||||
|
base = this.cdn,
|
||||||
|
extension = 'webp',
|
||||||
|
size,
|
||||||
|
}: Readonly<MakeURLOptions> = {},
|
||||||
): string {
|
): string {
|
||||||
// eslint-disable-next-line no-param-reassign
|
// eslint-disable-next-line no-param-reassign
|
||||||
extension = String(extension).toLowerCase();
|
extension = String(extension).toLowerCase();
|
||||||
|
@ -365,7 +384,7 @@ export class CDN {
|
||||||
throw new RangeError(`Invalid size provided: ${size}\nMust be one of: ${ALLOWED_SIZES.join(', ')}`);
|
throw new RangeError(`Invalid size provided: ${size}\nMust be one of: ${ALLOWED_SIZES.join(', ')}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const url = new URL(`${this.base}${route}.${extension}`);
|
const url = new URL(`${base}${route}.${extension}`);
|
||||||
|
|
||||||
if (size) {
|
if (size) {
|
||||||
url.searchParams.set('size', String(size));
|
url.searchParams.set('size', String(size));
|
||||||
|
|
|
@ -75,7 +75,7 @@ export class REST extends AsyncEventEmitter<RestEvents> {
|
||||||
|
|
||||||
public constructor(options: Partial<RESTOptions> = {}) {
|
public constructor(options: Partial<RESTOptions> = {}) {
|
||||||
super();
|
super();
|
||||||
this.cdn = new CDN(options.cdn ?? DefaultRestOptions.cdn);
|
this.cdn = new CDN(options.cdn ?? DefaultRestOptions.cdn, options.mediaProxy ?? DefaultRestOptions.mediaProxy);
|
||||||
this.options = { ...DefaultRestOptions, ...options };
|
this.options = { ...DefaultRestOptions, ...options };
|
||||||
this.globalRemaining = Math.max(1, this.options.globalRequestsPerSecond);
|
this.globalRemaining = Math.max(1, this.options.globalRequestsPerSecond);
|
||||||
this.agent = options.agent ?? null;
|
this.agent = options.agent ?? null;
|
||||||
|
|
|
@ -31,6 +31,7 @@ export const DefaultRestOptions = {
|
||||||
async makeRequest(...args): Promise<ResponseLike> {
|
async makeRequest(...args): Promise<ResponseLike> {
|
||||||
return getDefaultStrategy()(...args);
|
return getDefaultStrategy()(...args);
|
||||||
},
|
},
|
||||||
|
mediaProxy: 'https://media.discordapp.net',
|
||||||
} as const satisfies Required<RESTOptions>;
|
} as const satisfies Required<RESTOptions>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -85,6 +85,12 @@ export interface RESTOptions {
|
||||||
* For example, to use global fetch, simply provide `makeRequest: fetch`
|
* For example, to use global fetch, simply provide `makeRequest: fetch`
|
||||||
*/
|
*/
|
||||||
makeRequest(url: string, init: RequestInit): Promise<ResponseLike>;
|
makeRequest(url: string, init: RequestInit): Promise<ResponseLike>;
|
||||||
|
/**
|
||||||
|
* The media proxy path
|
||||||
|
*
|
||||||
|
* @defaultValue `'https://media.discordapp.net'`
|
||||||
|
*/
|
||||||
|
mediaProxy: string;
|
||||||
/**
|
/**
|
||||||
* The extra offset to add to rate limits in milliseconds
|
* The extra offset to add to rate limits in milliseconds
|
||||||
*
|
*
|
||||||
|
|
Loading…
Reference in a new issue