@@ -29,6 +29,33 @@ const schema = JSON.parse(
2929 * @typedef {{ name: string, url: URL } } Emote
3030 */
3131
32+ /**
33+ * A Map of emote names to URLs.
34+ *
35+ * @augments {Map<string, URL> }
36+ */
37+ class EmoteMap extends Map {
38+ /**
39+ * Add an emote to the map. If an emote with the same name already exists,
40+ * this tries to add a numeric suffix to distinguish them.
41+ *
42+ * @param {Emote } emote
43+ */
44+ insert ( emote ) {
45+ const prevUrl = this . get ( emote . name ) ;
46+ if ( prevUrl && prevUrl . href !== emote . url . href ) {
47+ for ( let i = 1 ; i < 20 ; i += 1 ) {
48+ if ( ! this . has ( `${ emote . name } ~${ i } ` ) ) {
49+ this . set ( `${ emote . name } ~${ i } ` , emote . url ) ;
50+ break ;
51+ }
52+ }
53+ } else {
54+ this . set ( emote . name , emote . url ) ;
55+ }
56+ }
57+ }
58+
3259/**
3360 * @template {object} T
3461 * @param {URL|string } url
@@ -220,8 +247,7 @@ class Emotes {
220247
221248 #logger;
222249
223- /** @type {Record<string, URL> } */
224- #emotes = Object . create ( null ) ;
250+ #emotes = new EmoteMap ( ) ;
225251
226252 #ready = Promise . resolve ( ) ;
227253
@@ -244,19 +270,25 @@ class Emotes {
244270 this . #ready = this . #reloadEmotes( ) ;
245271 }
246272
273+ /** Get all known emotes as an array. */
247274 async getEmotes ( ) {
248275 await this . #ready;
249276
250- return Object . entries ( this . #emotes) . map ( ( [ name , url ] ) => ( { name, url : url . toString ( ) } ) ) ;
277+ const emotes = [ ] ;
278+ for ( const [ name , url ] of this . #emotes) {
279+ emotes . push ( { name, url : url . toString ( ) } ) ;
280+ }
281+
282+ return emotes ;
251283 }
252284
253285 /**
254286 * @param {TwitchSettings } options
255- * @returns {Promise<Record<string, URL> > }
287+ * @returns {Promise<EmoteMap > }
256288 */
257289 async #loadTTVEmotes( options ) {
258290 if ( ! options . clientId || ! options . clientSecret ) {
259- return { } ;
291+ return new EmoteMap ( ) ;
260292 }
261293
262294 const client = new ApiClient ( {
@@ -292,14 +324,13 @@ class Emotes {
292324 promises . push ( getSevenTVEmotes ( channels ) ) ;
293325 }
294326
295- /** @type {Record<string, URL> } */
296- const emotes = { } ;
327+ const emotes = new EmoteMap ( ) ;
297328
298329 const results = await Promise . allSettled ( promises ) ;
299330 for ( const result of results ) {
300331 if ( result . status === 'fulfilled' ) {
301332 for ( const emote of result . value ) {
302- emotes [ emote . name ] = emote . url ;
333+ emotes . insert ( emote ) ;
303334 }
304335 } else {
305336 this . #logger. warn ( result . reason ) ;
0 commit comments