Skip to content

Commit 755f9b6

Browse files
committed
feat: support animated WebP
1 parent 8605fc8 commit 755f9b6

File tree

6 files changed

+55
-26
lines changed

6 files changed

+55
-26
lines changed

packages/discord.js/src/client/Client.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -779,6 +779,11 @@ exports.Client = Client;
779779
* @see {@link https://discord.js.org/docs/packages/rest/stable/ImageURLOptions:Interface}
780780
*/
781781

782+
/**
783+
* @external EmojiURLOptions
784+
* @see {@link https://discord.js.org/docs/packages/rest/stable/EmojiURLOptions:TypeAlias}
785+
*/
786+
782787
/**
783788
* @external BaseImageURLOptions
784789
* @see {@link https://discord.js.org/docs/packages/rest/stable/BaseImageURLOptions:Interface}

packages/discord.js/src/structures/BaseGuildEmoji.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ class BaseGuildEmoji extends Emoji {
5858
* @method imageURL
5959
* @memberof BaseGuildEmoji
6060
* @instance
61-
* @param {ImageURLOptions} [options={}] Options for the image URL
61+
* @param {EmojiURLOptions} [options={}] Options for the emoji URL
6262
* @returns {string}
6363
*/
6464

packages/discord.js/src/structures/Emoji.js

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,11 +42,20 @@ class Emoji extends Base {
4242

4343
/**
4444
* Returns a URL for the emoji or `null` if this is not a custom emoji.
45-
* @param {ImageURLOptions} [options={}] Options for the image URL
45+
* @param {EmojiURLOptions} [options={}] Options for the emoji URL
4646
* @returns {?string}
4747
*/
4848
imageURL(options = {}) {
49-
return this.id && this.client.rest.cdn.emoji(this.id, this.animated, options);
49+
if (!this.id) return null;
50+
51+
// Return a dynamic extension depending on whether the emoji is animated.
52+
const resolvedOptions = { extension: options.extension, size: options.size };
53+
54+
if (!options.extension || options.extension === 'webp') {
55+
resolvedOptions.animated = options.animated ?? (this.animated || undefined);
56+
}
57+
58+
return this.client.rest.cdn.emoji(this.id, resolvedOptions);
5059
}
5160

5261
/**

packages/discord.js/typings/index.d.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { Awaitable, JSONEncodable } from '@discordjs/util';
22
import { Collection, ReadonlyCollection } from '@discordjs/collection';
3-
import { BaseImageURLOptions, ImageURLOptions, RawFile, REST, RESTOptions } from '@discordjs/rest';
3+
import { BaseImageURLOptions, EmojiURLOptions, ImageURLOptions, RawFile, REST, RESTOptions } from '@discordjs/rest';
44
import { WebSocketManager, WebSocketManagerOptions } from '@discordjs/ws';
55
import { AsyncEventEmitter } from '@vladfrangu/async_event_emitter';
66
import {
@@ -610,7 +610,7 @@ export abstract class BaseGuild extends Base {
610610

611611
export class BaseGuildEmoji extends Emoji {
612612
protected constructor(client: Client<true>, data: RawGuildEmojiData, guild: Guild | GuildPreview);
613-
public imageURL(options?: ImageURLOptions): string;
613+
public imageURL(options?: EmojiURLOptions): string;
614614
public get url(): string;
615615
public available: boolean | null;
616616
public get createdAt(): Date;
@@ -1276,7 +1276,7 @@ export class Emoji extends Base {
12761276
public id: Snowflake | null;
12771277
public name: string | null;
12781278
public get identifier(): string;
1279-
public imageURL(options?: ImageURLOptions): string | null;
1279+
public imageURL(options?: EmojiURLOptions): string | null;
12801280
public get url(): string | null;
12811281
public toJSON(): unknown;
12821282
public toString(): string;

packages/rest/__tests__/CDN.test.ts

Lines changed: 5 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -50,28 +50,16 @@ test('discoverySplash default', () => {
5050
expect(cdn.discoverySplash(id, hash)).toEqual(`${baseCDN}/discovery-splashes/${id}/${hash}.webp`);
5151
});
5252

53-
test('emoji static', () => {
54-
expect(cdn.emoji(id, false)).toEqual(`${baseCDN}/emojis/${id}.webp`);
55-
});
56-
57-
test('emoji static with JPG extension', () => {
58-
expect(cdn.emoji(id, false, { extension: 'jpg' })).toEqual(`${baseCDN}/emojis/${id}.jpg`);
59-
});
60-
61-
test('emoji static with JPG extension with force static', () => {
62-
expect(cdn.emoji(id, false, { extension: 'jpg', forceStatic: true })).toEqual(`${baseCDN}/emojis/${id}.jpg`);
53+
test('emoji', () => {
54+
expect(cdn.emoji(id)).toEqual(`${baseCDN}/emojis/${id}.webp`);
6355
});
6456

6557
test('emoji animated', () => {
66-
expect(cdn.emoji(id, true)).toEqual(`${baseCDN}/emojis/${id}.gif`);
67-
});
68-
69-
test('emoji animated with JPG extension', () => {
70-
expect(cdn.emoji(id, true, { extension: 'jpg' })).toEqual(`${baseCDN}/emojis/${id}.gif`);
58+
expect(cdn.emoji(id, { animated: true })).toEqual(`${baseCDN}/emojis/${id}.webp?animated=true`);
7159
});
7260

73-
test('emoji animated with JPG extension with force static', () => {
74-
expect(cdn.emoji(id, true, { extension: 'jpg', forceStatic: true })).toEqual(`${baseCDN}/emojis/${id}.jpg`);
61+
test('emoji with GIF format', () => {
62+
expect(cdn.emoji(id, { extension: 'gif' })).toEqual(`${baseCDN}/emojis/${id}.gif`);
7563
});
7664

7765
test('guildMemberAvatar default', () => {

packages/rest/src/lib/CDN.ts

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,25 @@ export interface BaseImageURLOptions {
2626
size?: ImageSize;
2727
}
2828

29+
interface EmojiURLOptionsWebp extends BaseImageURLOptions {
30+
/**
31+
* Whether to use the `animated` query parameter.
32+
*
33+
* @remarks An animated custom emoji with the WebP format utilises this query paramter to be animated.
34+
*/
35+
animated?: boolean;
36+
extension?: 'webp';
37+
}
38+
39+
interface EmojiURLOptionsNotWebP extends BaseImageURLOptions {
40+
extension: Exclude<ImageExtension, 'webp'>;
41+
}
42+
43+
/**
44+
* The options used for emoji URLs.
45+
*/
46+
export type EmojiURLOptions = EmojiURLOptionsNotWebP | EmojiURLOptionsWebp;
47+
2948
/**
3049
* The options used for image URLs with animated content
3150
*/
@@ -44,6 +63,10 @@ export interface MakeURLOptions {
4463
* The allowed extensions that can be used
4564
*/
4665
allowedExtensions?: readonly string[];
66+
/**
67+
* Whether to use the `animated` query parameter
68+
*/
69+
animated?: boolean;
4770
/**
4871
* The base URL.
4972
*
@@ -162,11 +185,10 @@ export class CDN {
162185
* Generates an emoji's URL.
163186
*
164187
* @param emojiId - The emoji id
165-
* @param animated - Whether the emoji is animated
166188
* @param options - Optional options for the emoji
167189
*/
168-
public emoji(emojiId: string, animated: boolean, options?: Readonly<ImageURLOptions>): string {
169-
return this.dynamicMakeURL(`/emojis/${emojiId}`, animated ? 'a_' : '', options);
190+
public emoji(emojiId: string, options?: Readonly<EmojiURLOptions>): string {
191+
return this.makeURL(`/emojis/${emojiId}`, options);
170192
}
171193

172194
/**
@@ -326,6 +348,7 @@ export class CDN {
326348
base = this.cdn,
327349
extension = 'webp',
328350
size,
351+
animated,
329352
}: Readonly<MakeURLOptions> = {},
330353
): string {
331354
// eslint-disable-next-line no-param-reassign
@@ -345,6 +368,10 @@ export class CDN {
345368
url.searchParams.set('size', String(size));
346369
}
347370

371+
if (animated !== undefined) {
372+
url.searchParams.set('animated', String(animated));
373+
}
374+
348375
return url.toString();
349376
}
350377
}

0 commit comments

Comments
 (0)