@mkody/twitch-emoticons 2.9.6 → 3.0.0-beta.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (80) hide show
  1. package/README.md +530 -169
  2. package/dist/TwitchEmoticons.cjs +950 -0
  3. package/dist/TwitchEmoticons.esm.min.js +7 -0
  4. package/dist/TwitchEmoticons.esm.min.js.map +7 -0
  5. package/dist/TwitchEmoticons.min.js +2 -20
  6. package/dist/TwitchEmoticons.min.js.map +4 -4
  7. package/package.json +99 -72
  8. package/src/index.js +38 -12
  9. package/src/struct/BTTVEmote.js +72 -74
  10. package/src/struct/Channel.js +48 -48
  11. package/src/struct/Emote.js +71 -71
  12. package/src/struct/EmoteFetcher.js +358 -322
  13. package/src/struct/EmoteParser.js +95 -84
  14. package/src/struct/EmoteTypeMapper.js +9 -9
  15. package/src/struct/FFZEmote.js +91 -93
  16. package/src/struct/SevenTVEmote.js +88 -90
  17. package/src/struct/TwitchEmote.js +72 -71
  18. package/src/util/Collection.js +69 -55
  19. package/src/util/Constants.js +30 -30
  20. package/typings/index.d.cts +254 -0
  21. package/typings/index.d.ts +216 -99
  22. package/.jsdoc.json +0 -46
  23. package/.nvmrc +0 -1
  24. package/docs/BTTVEmote.html +0 -1532
  25. package/docs/Channel.html +0 -824
  26. package/docs/Collection.html +0 -798
  27. package/docs/Emote.html +0 -832
  28. package/docs/EmoteFetcher.html +0 -3741
  29. package/docs/EmoteParser.html +0 -1187
  30. package/docs/FFZEmote.html +0 -1686
  31. package/docs/SevenTVEmote.html +0 -1596
  32. package/docs/TwitchEmote.html +0 -1528
  33. package/docs/fonts/Montserrat/Montserrat-Bold.eot +0 -0
  34. package/docs/fonts/Montserrat/Montserrat-Bold.ttf +0 -0
  35. package/docs/fonts/Montserrat/Montserrat-Bold.woff +0 -0
  36. package/docs/fonts/Montserrat/Montserrat-Bold.woff2 +0 -0
  37. package/docs/fonts/Montserrat/Montserrat-Regular.eot +0 -0
  38. package/docs/fonts/Montserrat/Montserrat-Regular.ttf +0 -0
  39. package/docs/fonts/Montserrat/Montserrat-Regular.woff +0 -0
  40. package/docs/fonts/Montserrat/Montserrat-Regular.woff2 +0 -0
  41. package/docs/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.eot +0 -0
  42. package/docs/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.svg +0 -978
  43. package/docs/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.ttf +0 -0
  44. package/docs/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.woff +0 -0
  45. package/docs/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.woff2 +0 -0
  46. package/docs/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.eot +0 -0
  47. package/docs/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.svg +0 -1049
  48. package/docs/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.ttf +0 -0
  49. package/docs/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.woff +0 -0
  50. package/docs/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.woff2 +0 -0
  51. package/docs/index.html +0 -233
  52. package/docs/scripts/collapse.js +0 -39
  53. package/docs/scripts/commonNav.js +0 -28
  54. package/docs/scripts/linenumber.js +0 -25
  55. package/docs/scripts/nav.js +0 -12
  56. package/docs/scripts/polyfill.js +0 -4
  57. package/docs/scripts/prettify/Apache-License-2.0.txt +0 -202
  58. package/docs/scripts/prettify/lang-css.js +0 -2
  59. package/docs/scripts/prettify/prettify.js +0 -28
  60. package/docs/scripts/search.js +0 -99
  61. package/docs/struct_BTTVEmote.js.html +0 -162
  62. package/docs/struct_Channel.js.html +0 -132
  63. package/docs/struct_Emote.js.html +0 -159
  64. package/docs/struct_EmoteFetcher.js.html +0 -429
  65. package/docs/struct_EmoteParser.js.html +0 -172
  66. package/docs/struct_FFZEmote.js.html +0 -185
  67. package/docs/struct_SevenTVEmote.js.html +0 -180
  68. package/docs/struct_TwitchEmote.js.html +0 -159
  69. package/docs/styles/jsdoc.css +0 -776
  70. package/docs/styles/prettify.css +0 -80
  71. package/docs/util_Collection.js.html +0 -151
  72. package/eslint.config.mjs +0 -215
  73. package/jest.config.js +0 -198
  74. package/test/BTTV.test.js +0 -48
  75. package/test/FFZ.test.js +0 -71
  76. package/test/SevenTV.test.js +0 -71
  77. package/test/ToFromObject.test.js +0 -156
  78. package/test/Twitch.test.js +0 -64
  79. package/test/__snapshots__/ToFromObject.test.js.snap +0 -121
  80. package/test/other.test.js +0 -72
@@ -0,0 +1,950 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
3
+ var __getOwnPropNames = Object.getOwnPropertyNames;
4
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
5
+ var __export = (target, all) => {
6
+ for (var name in all)
7
+ __defProp(target, name, { get: all[name], enumerable: true });
8
+ };
9
+ var __copyProps = (to, from, except, desc) => {
10
+ if (from && typeof from === "object" || typeof from === "function") {
11
+ for (let key of __getOwnPropNames(from))
12
+ if (!__hasOwnProp.call(to, key) && key !== except)
13
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
14
+ }
15
+ return to;
16
+ };
17
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
18
+
19
+ // src/index.js
20
+ var index_exports = {};
21
+ __export(index_exports, {
22
+ BTTVEmote: () => BTTVEmote_default,
23
+ Channel: () => Channel_default,
24
+ Collection: () => Collection_default,
25
+ Constants: () => Constants_default,
26
+ Emote: () => Emote_default,
27
+ EmoteFetcher: () => EmoteFetcher_default,
28
+ EmoteParser: () => EmoteParser_default,
29
+ FFZEmote: () => FFZEmote_default,
30
+ SevenTVEmote: () => SevenTVEmote_default,
31
+ TwitchEmote: () => TwitchEmote_default,
32
+ default: () => index_default
33
+ });
34
+ module.exports = __toCommonJS(index_exports);
35
+
36
+ // src/struct/Emote.js
37
+ var Emote = class _Emote {
38
+ /**
39
+ * Base class for emotes.
40
+ * This constructor is not to be used.
41
+ * @param {Channel} channel - Channel this emote belongs to.
42
+ * @param {string} id - ID of the emote.
43
+ * @param {data} data - The raw emote data.
44
+ * @throws {Error} When trying to use the base Emote class directly.
45
+ */
46
+ constructor(channel, id, data) {
47
+ if (new.target.name === _Emote.name) {
48
+ throw new Error("Base Emote class cannot be used");
49
+ }
50
+ this.fetcher = channel.fetcher;
51
+ this.channel = channel;
52
+ this.id = id;
53
+ this.type = null;
54
+ this._setup(data);
55
+ }
56
+ _setup(data) {
57
+ this.code = data.code;
58
+ }
59
+ /**
60
+ * Gets the image link of the emote.
61
+ * @param {number} size - The size of the image.
62
+ * @returns {string} - The URL to the emote.
63
+ */
64
+ /* c8 ignore next 3 */
65
+ toLink() {
66
+ return null;
67
+ }
68
+ /**
69
+ * Override for `toString`.
70
+ * Will give the emote's name.
71
+ * @returns {string} - The emote code.
72
+ */
73
+ /* c8 ignore next 3 */
74
+ toString() {
75
+ return this.code;
76
+ }
77
+ /**
78
+ * Override for `toObject`.
79
+ * Will result in an Object representation of an Emote
80
+ * @returns {object} - Object representation of the Emote.
81
+ */
82
+ toObject() {
83
+ return {
84
+ code: this.code,
85
+ id: this.id,
86
+ channel_id: this.channel.channel_id
87
+ };
88
+ }
89
+ };
90
+ var Emote_default = Emote;
91
+
92
+ // src/util/Constants.js
93
+ var Constants_default = {
94
+ Twitch: {
95
+ CDN: (id, size = 0, forceStatic = false, theme = "dark") => `https://static-cdn.jtvnw.net/emoticons/v2/${id}/${forceStatic ? "static" : "default"}/${theme}/${size + 1}.0`
96
+ },
97
+ BTTV: {
98
+ Global: "https://api.betterttv.net/3/cached/emotes/global",
99
+ Channel: (id) => `https://api.betterttv.net/3/cached/users/twitch/${id}`,
100
+ CDN: (id, size = 0, forceStatic = false) => `https://cdn.betterttv.net/emote/${id}/${size + 1}x.${forceStatic ? "png" : "webp"}`
101
+ },
102
+ SevenTV: {
103
+ Global: "https://7tv.io/v3/emote-sets/global",
104
+ Channel: (id) => `https://7tv.io/v3/users/twitch/${id}`,
105
+ CDN: (id, format, size = 1, forceStatic = false) => `https://cdn.7tv.app/emote/${id}/${size}x${forceStatic ? "_static" : ""}.${format}`
106
+ },
107
+ FFZ: {
108
+ sets: {
109
+ Global: 3,
110
+ Modifiers: 1532818
111
+ },
112
+ Set: (id) => `https://api.frankerfacez.com/v1/set/${id}`,
113
+ Channel: (id) => `https://api.frankerfacez.com/v1/room/id/${id}`,
114
+ CDN: (id, size = 1) => `https://cdn.frankerfacez.com/emote/${id}/${size}`,
115
+ CDNAnimated: (id, size = 1) => `https://cdn.frankerfacez.com/emote/${id}/animated/${size}.webp`
116
+ },
117
+ Templates: {
118
+ html: '<img alt="{name}" title="{name}" class="twitch-emote" src="{link}">',
119
+ markdown: '![{name}]({link} "{name}")',
120
+ bbcode: "[img]{link}[/img]",
121
+ plain: "{link}"
122
+ }
123
+ };
124
+
125
+ // src/struct/BTTVEmote.js
126
+ var BTTVEmote = class _BTTVEmote extends Emote_default {
127
+ /**
128
+ * A BTTV emote.
129
+ * @param {Channel} channel - Channel this emote belongs to.
130
+ * @param {string} id - ID of the emote.
131
+ * @param {data} data - The raw emote data.
132
+ */
133
+ constructor(channel, id, data) {
134
+ super(channel, id, data);
135
+ this.type = "bttv";
136
+ }
137
+ _setup(data) {
138
+ super._setup(data);
139
+ this.ownerName = "user" in data ? data.user.name : null;
140
+ this.animated = data.animated;
141
+ this.imageType = "webp";
142
+ }
143
+ /**
144
+ * Gets the image link of the emote.
145
+ * @param {object} [options] - Options for the link.
146
+ * @param {number} [options.size=0] - Size (scale) for the emote.
147
+ * @param {boolean} [options.forceStatic] - Whether to force the emote to be static (non-animated). Defaults to the fetcher's forceStatic or `false`.
148
+ * @returns {string} - The URL to the emote.
149
+ */
150
+ toLink(options) {
151
+ const {
152
+ size = 0,
153
+ forceStatic = this.fetcher.forceStatic || false
154
+ } = options || {};
155
+ return Constants_default.BTTV.CDN(this.id, size, forceStatic);
156
+ }
157
+ /**
158
+ * Override for `toObject`.
159
+ * Will result in an Object representation of a BTTVEmote
160
+ * @returns {object} - Object representation of the BTTVEmote.
161
+ */
162
+ toObject() {
163
+ return {
164
+ ...super.toObject(),
165
+ animated: this.animated,
166
+ ownerName: this.ownerName,
167
+ type: this.type
168
+ };
169
+ }
170
+ /**
171
+ * Converts an emote Object into a BTTVEmote
172
+ * @param {object} [emoteObject] - Object representation of this emote
173
+ * @param {Channel} [channel] - Channel this emote belongs to.
174
+ * @returns {BTTVEmote} - A BTTVEmote instance.
175
+ */
176
+ static fromObject(emoteObject, channel) {
177
+ return new _BTTVEmote(
178
+ channel,
179
+ emoteObject.id,
180
+ {
181
+ code: emoteObject.code,
182
+ animated: emoteObject.animated,
183
+ user: {
184
+ name: emoteObject.ownerName
185
+ }
186
+ }
187
+ );
188
+ }
189
+ };
190
+ var BTTVEmote_default = BTTVEmote;
191
+
192
+ // src/util/Collection.js
193
+ var Collection = class extends Map {
194
+ /**
195
+ * Finds first matching value by property or function.
196
+ * Same as `Array#find`.
197
+ * @param {string|Function} propOrFunc - Property or function to test.
198
+ * @param {any} [value] - Value to find.
199
+ * @returns {any|null} - The found item or null if none found.
200
+ */
201
+ find(propOrFunc, value) {
202
+ if (typeof propOrFunc === "string") {
203
+ if (value === void 0) {
204
+ return null;
205
+ }
206
+ for (const item of this.values()) {
207
+ if (item[propOrFunc] === value) {
208
+ return item;
209
+ }
210
+ }
211
+ return null;
212
+ }
213
+ if (typeof propOrFunc === "function") {
214
+ let i = 0;
215
+ for (const item of this.values()) {
216
+ if (propOrFunc(item, i, this)) {
217
+ return item;
218
+ }
219
+ i++;
220
+ }
221
+ return null;
222
+ }
223
+ return null;
224
+ }
225
+ /**
226
+ * Filters cache by function.
227
+ * Same as `Array#filter`.
228
+ * @param {Function} func - Function to test.
229
+ * @param {any} [thisArg] - The context for the function.
230
+ * @returns {Collection} - A new collection with the filtered items.
231
+ */
232
+ filter(func, thisArg) {
233
+ if (thisArg) {
234
+ func = func.bind(thisArg);
235
+ }
236
+ const results = new this.constructor();
237
+ let i = 0;
238
+ for (const [key, item] of this) {
239
+ if (func(item, i, this)) {
240
+ results.set(key, item);
241
+ }
242
+ i++;
243
+ }
244
+ return results;
245
+ }
246
+ /**
247
+ * Maps cache by function.
248
+ * Same as `Array#map`.
249
+ * @param {Function} func - Function to use.
250
+ * @param {any} [thisArg] - The context for the function.
251
+ * @returns {any[]} - An array with the mapped items.
252
+ */
253
+ map(func, thisArg) {
254
+ if (thisArg) {
255
+ func = func.bind(thisArg);
256
+ }
257
+ const array = new Array(this.size);
258
+ let i = 0;
259
+ for (const item of this.values()) {
260
+ array[i] = func(item, i, this);
261
+ i++;
262
+ }
263
+ return array;
264
+ }
265
+ };
266
+ var Collection_default = Collection;
267
+
268
+ // src/struct/Channel.js
269
+ var Channel = class {
270
+ /**
271
+ * A Twitch channel.
272
+ * @param {EmoteFetcher} fetcher - The emote fetcher.
273
+ * @param {number} id - ID of the channel.
274
+ */
275
+ constructor(fetcher, id) {
276
+ this.fetcher = fetcher;
277
+ this.channel_id = id || null;
278
+ this.emotes = new Collection_default();
279
+ }
280
+ /* There are tests that those are returning Collections, but c8 doesn't get it. */
281
+ /* c8 ignore start */
282
+ /**
283
+ * Fetches the BTTV emotes for this channel.
284
+ * @returns {Promise<Collection<string, BTTVEmote>>} - A promise that resolves to a collection of BTTVEmotes.
285
+ */
286
+ fetchBTTVEmotes() {
287
+ return this.fetcher.fetchBTTVEmotes(this.id);
288
+ }
289
+ /**
290
+ * Fetches the FFZ emotes for this channel.
291
+ * @returns {Promise<Collection<string, FFZEmote>>} - A promise that resolves to a collection of FFZEmotes.
292
+ */
293
+ fetchFFZEmotes() {
294
+ return this.fetcher.fetchFFZEmotes(this.id);
295
+ }
296
+ /**
297
+ * Fetches the 7TV emotes for this channel.
298
+ * @returns {Promise<Collection<string, SevenTVEmote>>} - A promise that resolves to a collection of SevenTVEmotes.
299
+ */
300
+ fetchSevenTVEmotes() {
301
+ return this.fetcher.fetchSevenTVEmotes(this.id);
302
+ }
303
+ /* c8 ignore end */
304
+ };
305
+ var Channel_default = Channel;
306
+
307
+ // src/struct/FFZEmote.js
308
+ var FFZEmote = class _FFZEmote extends Emote_default {
309
+ /**
310
+ * An FFZ emote.
311
+ * @param {Channel} channel - Channel this emote belongs to.
312
+ * @param {string} id - ID of the emote.
313
+ * @param {data} data - The raw emote data.
314
+ */
315
+ constructor(channel, id, data) {
316
+ super(channel, id, data);
317
+ this.type = "ffz";
318
+ }
319
+ _setup(data) {
320
+ super._setup(data);
321
+ this.code = data.name;
322
+ this.ownerName = "owner" in data ? data.owner.name : null;
323
+ this.sizes = "animated" in data ? Object.keys(data.animated) : Object.keys(data.urls);
324
+ this.animated = "animated" in data;
325
+ this.imageType = "animated" in data ? "webp" : "png";
326
+ this.modifier = data.modifier && (data.modifier_flags & 1) !== 0;
327
+ }
328
+ /**
329
+ * Gets the image link of the emote.
330
+ * @param {object} [options] - Options for the link.
331
+ * @param {number} [options.size=0] - Size (scale) for the emote.
332
+ * @param {boolean} [options.forceStatic] - Whether to force the emote to be static (non-animated). Defaults to the fetcher's forceStatic or `false`.
333
+ * @returns {string} - The URL to the emote.
334
+ */
335
+ toLink(options) {
336
+ const {
337
+ size = 0,
338
+ forceStatic = this.fetcher.forceStatic || false
339
+ } = options || {};
340
+ const sizeKey = this.sizes[size];
341
+ if (this.animated && !forceStatic) return Constants_default.FFZ.CDNAnimated(this.id, sizeKey);
342
+ return Constants_default.FFZ.CDN(this.id, sizeKey);
343
+ }
344
+ /**
345
+ * Override for `toObject`.
346
+ * Will result in an Object representation of a FFZEmote
347
+ * @returns {object} - Object representation of the FFZEmote.
348
+ */
349
+ toObject() {
350
+ return {
351
+ ...super.toObject(),
352
+ animated: this.animated,
353
+ sizes: this.sizes,
354
+ ownerName: this.ownerName,
355
+ type: this.type,
356
+ modifier: this.modifier
357
+ };
358
+ }
359
+ /**
360
+ * Converts an emote Object into a FFZEmote
361
+ * @param {object} [emoteObject] - Object representation of this emote
362
+ * @param {Channel} [channel] - Channel this emote belongs to.
363
+ * @returns {FFZEmote} - A FFZEmote instance.
364
+ */
365
+ static fromObject(emoteObject, channel) {
366
+ const sizesObj = emoteObject.sizes.reduce((acc, curr) => {
367
+ acc[curr] = curr;
368
+ return acc;
369
+ }, {});
370
+ return new _FFZEmote(
371
+ channel,
372
+ emoteObject.id,
373
+ {
374
+ code: emoteObject.code,
375
+ name: emoteObject.code,
376
+ urls: sizesObj,
377
+ ...emoteObject.animated ? { animated: sizesObj } : {},
378
+ owner: { name: emoteObject.ownerName },
379
+ modifier: emoteObject.modifier,
380
+ modifier_flags: emoteObject.modifier
381
+ }
382
+ );
383
+ }
384
+ };
385
+ var FFZEmote_default = FFZEmote;
386
+
387
+ // src/struct/SevenTVEmote.js
388
+ var SevenTVEmote = class _SevenTVEmote extends Emote_default {
389
+ /**
390
+ * A 7TV emote.
391
+ * @param {Channel} channel - Channel this emote belongs to.
392
+ * @param {string} id - ID of the emote.
393
+ * @param {data} data - The raw emote data.
394
+ */
395
+ constructor(channel, id, data) {
396
+ super(channel, id, data);
397
+ this.type = "7tv";
398
+ }
399
+ _setup(data) {
400
+ super._setup(data);
401
+ this.code = data.name;
402
+ this.ownerName = "owner" in data.data ? data.data.owner.display_name : null;
403
+ this.sizes = data.data.host.files.filter((el) => el.format === this.channel.format.toUpperCase()).map((el) => el.name.replace(/x\.(\w+)/, ""));
404
+ this.animated = data.data.animated;
405
+ this.imageType = this.channel.format;
406
+ }
407
+ /**
408
+ * Gets the image link of the emote.
409
+ * @param {object} [options] - Options for the link.
410
+ * @param {number} [options.size=0] - Size (scale) for the emote.
411
+ * @param {boolean} [options.forceStatic] - Whether to force the emote to be static (non-animated). Defaults to the fetcher's forceStatic or `false`.
412
+ * @returns {string} - The URL to the emote.
413
+ */
414
+ toLink(options) {
415
+ const {
416
+ size = 0,
417
+ forceStatic = this.fetcher.forceStatic || false
418
+ } = options || {};
419
+ const sizeKey = this.sizes[size];
420
+ return Constants_default.SevenTV.CDN(this.id, this.imageType, sizeKey, forceStatic);
421
+ }
422
+ /**
423
+ * Override for `toObject`.
424
+ * Will result in an Object representation of a SevenTVEmote
425
+ * @returns {object} - Object representation of the SevenTVEmote.
426
+ */
427
+ toObject() {
428
+ return {
429
+ ...super.toObject(),
430
+ animated: this.animated,
431
+ sizes: this.sizes,
432
+ ownerName: this.ownerName,
433
+ type: this.type,
434
+ imageType: this.imageType
435
+ };
436
+ }
437
+ /**
438
+ * Converts an emote Object into a SevenTVEmote
439
+ * @param {object} [emoteObject] - Object representation of this emote
440
+ * @param {Channel} [channel] - Channel this emote belongs to.
441
+ * @returns {SevenTVEmote} - A SevenTVEmote instance.
442
+ */
443
+ static fromObject(emoteObject, channel) {
444
+ const sizes = emoteObject.sizes.map((size) => {
445
+ return { format: channel.format.toUpperCase(), name: size };
446
+ });
447
+ return new _SevenTVEmote(
448
+ channel,
449
+ emoteObject.id,
450
+ {
451
+ code: emoteObject.code,
452
+ name: emoteObject.code,
453
+ data: {
454
+ animated: emoteObject.animated,
455
+ owner: {
456
+ display_name: emoteObject.ownerName
457
+ },
458
+ host: {
459
+ files: sizes
460
+ }
461
+ }
462
+ }
463
+ );
464
+ }
465
+ };
466
+ var SevenTVEmote_default = SevenTVEmote;
467
+
468
+ // src/struct/TwitchEmote.js
469
+ var TwitchEmote = class _TwitchEmote extends Emote_default {
470
+ /**
471
+ * A Twitch emote.
472
+ * @param {Channel} channel - Channel this emote belongs to.
473
+ * @param {string} id - ID of the emote.
474
+ * @param {data} data - The raw emote data.
475
+ */
476
+ constructor(channel, id, data) {
477
+ super(channel, id, data);
478
+ this.type = "twitch";
479
+ }
480
+ _setup(data) {
481
+ super._setup(data);
482
+ this.set = data.emoticon_set;
483
+ this.animated = "animated" in data.formats;
484
+ this.imageType = "animated" in data.formats ? "gif" : "png";
485
+ }
486
+ /**
487
+ * Gets the image link of the emote.
488
+ * @param {object} [options] - Options for the link.
489
+ * @param {number} [options.size=0] - Size (scale) for the emote.
490
+ * @param {boolean} [options.forceStatic] - Whether to force the emote to be static (non-animated). Defaults to the fetcher's forceStatic or `false`.
491
+ * @param {'dark' | 'light'} [options.themeMode] - Only for Twitch: the preferred theme mode. Defaults to the fetcher's twitchThemeMode or `dark`.
492
+ * @returns {string} - The URL to the emote.
493
+ */
494
+ toLink(options) {
495
+ const {
496
+ size = 0,
497
+ forceStatic = this.fetcher.forceStatic || false,
498
+ themeMode = this.fetcher.twitchThemeMode || "dark"
499
+ } = options || {};
500
+ return Constants_default.Twitch.CDN(this.id, size, forceStatic, themeMode);
501
+ }
502
+ /**
503
+ * Override for `toObject`.
504
+ * Will result in an Object representation of a TwitchEmote
505
+ * @returns {object} - Object representation of the TwitchEmote.
506
+ */
507
+ toObject() {
508
+ return {
509
+ ...super.toObject(),
510
+ animated: this.animated,
511
+ set: this.set,
512
+ type: this.type
513
+ };
514
+ }
515
+ /**
516
+ * Converts an emote Object into a TwitchEmote
517
+ * @param {object} [emoteObject] - Object representation of this emote
518
+ * @param {Channel} [channel] - Channel this emote belongs to.
519
+ * @returns {TwitchEmote} - A TwitchEmote instance.
520
+ */
521
+ static fromObject(emoteObject, channel) {
522
+ return new _TwitchEmote(
523
+ channel,
524
+ emoteObject.id,
525
+ {
526
+ code: emoteObject.code,
527
+ animated: emoteObject.animated,
528
+ emoticon_set: emoteObject.set,
529
+ formats: emoteObject.animated ? { animated: emoteObject.animated } : {}
530
+ }
531
+ );
532
+ }
533
+ };
534
+ var TwitchEmote_default = TwitchEmote;
535
+
536
+ // src/struct/EmoteFetcher.js
537
+ var import_api = require("@twurple/api");
538
+ var import_auth = require("@twurple/auth");
539
+ var EmoteFetcher = class {
540
+ /**
541
+ * Fetches and caches emotes.
542
+ * @param {object} [options={}] Fetcher's options.
543
+ * @param {string} [options.twitchAppID] Your app ID for the Twitch API.
544
+ * @param {string} [options.twitchAppSecret] Your app secret for the Twitch API.
545
+ * @param {ApiClient} [options.apiClient] - Bring your own Twurple ApiClient.
546
+ * @param {boolean} [options.forceStatic=false] - Force emotes to be static (non-animated).
547
+ * @param {'dark' | 'light'} [options.twitchThemeMode='dark'] - Theme mode (background color) preference for Twitch emotes.
548
+ */
549
+ constructor(options = {}) {
550
+ if (options.apiClient) {
551
+ this.apiClient = options.apiClient;
552
+ } else if (options.twitchAppID && options.twitchAppSecret) {
553
+ const authProvider = new import_auth.AppTokenAuthProvider(options.twitchAppID, options.twitchAppSecret);
554
+ this.apiClient = new import_api.ApiClient({ authProvider });
555
+ }
556
+ this.forceStatic = options.forceStatic || false;
557
+ this.twitchThemeMode = options.twitchThemeMode || "dark";
558
+ this.emotes = new Collection_default();
559
+ this.channels = new Collection_default();
560
+ this.ffzModifiersFetched = false;
561
+ }
562
+ /**
563
+ * The global channel for Twitch, BTTV and 7TV.
564
+ * @readonly
565
+ * @type {?Channel}
566
+ */
567
+ get globalChannel() {
568
+ return this.channels.get(null);
569
+ }
570
+ /**
571
+ * Sets up a channel
572
+ * @private
573
+ * @param {number} channelId - ID of the channel.
574
+ * @param {string} [format] - The type file format to use (webp/avif).
575
+ * @throws {Error} When Twitch Client ID or Client Secret were not provided.
576
+ * @returns {Channel} - A Channel instance.
577
+ */
578
+ _setupChannel(channelId, format) {
579
+ let channel = this.channels.get(channelId);
580
+ if (!channel) {
581
+ channel = new Channel_default(this, channelId);
582
+ this.channels.set(channelId, channel);
583
+ }
584
+ if (format) channel.format = format.toLowerCase();
585
+ return channel;
586
+ }
587
+ /**
588
+ * Gets the raw Twitch emotes data for a channel.
589
+ * @private
590
+ * @param {number} id - ID of the channel.
591
+ * @returns {Promise<object[]>} - A promise that resolves to an array of raw Twitch emote data.
592
+ */
593
+ _getRawTwitchEmotes(id) {
594
+ if (!this.apiClient) {
595
+ throw new Error("Client id or client secret not provided.");
596
+ }
597
+ if (id) {
598
+ return this.apiClient.chat.getChannelEmotes(id);
599
+ } else {
600
+ return this.apiClient.chat.getGlobalEmotes();
601
+ }
602
+ }
603
+ /**
604
+ * Converts and caches a raw twitch emote.
605
+ * @private
606
+ * @param {number} channelId - ID of the channel.
607
+ * @param {object} data - Raw data.
608
+ * @param {TwitchEmote} [existingEmote] - Existing emote to cache.
609
+ * @returns {TwitchEmote} - A TwitchEmote instance.
610
+ */
611
+ _cacheTwitchEmote(channelId, data, existingEmote) {
612
+ const channel = this._setupChannel(channelId);
613
+ const emote = existingEmote || new TwitchEmote_default(channel, data.id, data);
614
+ this.emotes.set(emote.code, emote);
615
+ channel.emotes.set(emote.code, emote);
616
+ return emote;
617
+ }
618
+ /**
619
+ * Gets the raw BTTV emotes data for a channel.
620
+ * Use `null` for the global emotes channel.
621
+ * @private
622
+ * @param {number} [id] - ID of the channel.
623
+ * @returns {Promise<object[]>} - A promise that resolves to an array of raw BTTV emote data.
624
+ */
625
+ _getRawBTTVEmotes(id) {
626
+ const endpoint = id ? Constants_default.BTTV.Channel(id) : Constants_default.BTTV.Global;
627
+ return fetch(endpoint).then((response) => response.json()).then((data) => {
628
+ if (Array.isArray(data)) return data;
629
+ if (data?.channelEmotes && data?.sharedEmotes) {
630
+ return [
631
+ ...data.channelEmotes,
632
+ ...data.sharedEmotes
633
+ ];
634
+ }
635
+ return data || [];
636
+ });
637
+ }
638
+ /**
639
+ * Converts and caches a raw BTTV emote.
640
+ * @private
641
+ * @param {number} channelId - ID of the channel.
642
+ * @param {object} data - Raw data.
643
+ * @param {BTTVEmote} [existingEmote] - Existing emote to cache.
644
+ * @returns {BTTVEmote} - A BTTVEmote instance.
645
+ */
646
+ _cacheBTTVEmote(channelId, data, existingEmote) {
647
+ const channel = this._setupChannel(channelId);
648
+ const emote = existingEmote || new BTTVEmote_default(channel, data.id, data);
649
+ this.emotes.set(emote.code, emote);
650
+ channel.emotes.set(emote.code, emote);
651
+ return emote;
652
+ }
653
+ /**
654
+ * Gets the raw FFZ emote data from a set.
655
+ * @private
656
+ * @param {number} id - ID of the set.
657
+ * @returns {Promise<object[]>} - A promise that resolves to an array of raw FFZ emote data.
658
+ */
659
+ _getRawFFZEmoteSet(id) {
660
+ const endpoint = Constants_default.FFZ.Set(id);
661
+ return fetch(endpoint).then((response) => response.json()).then((data) => {
662
+ return data.set.emoticons;
663
+ });
664
+ }
665
+ /**
666
+ * Gets the raw FFZ emotes data for a channel.
667
+ * @private
668
+ * @param {number} id - ID of the channel.
669
+ * @returns {Promise<object[]>} - A promise that resolves to an array of raw FFZ emote data.
670
+ */
671
+ _getRawFFZEmotes(id) {
672
+ const endpoint = Constants_default.FFZ.Channel(id);
673
+ return fetch(endpoint).then((response) => response.json()).then((data) => {
674
+ const emotes = [];
675
+ for (const key of Object.keys(data.sets)) {
676
+ const set = data.sets[key];
677
+ emotes.push(...set.emoticons);
678
+ }
679
+ return emotes;
680
+ });
681
+ }
682
+ /**
683
+ * Converts and caches a raw FFZ emote.
684
+ * @private
685
+ * @param {number} channelId - ID of the channel.
686
+ * @param {object} data - Raw data.
687
+ * @param {FFZEmote} [existingEmote] - Existing emote to cache.
688
+ * @returns {FFZEmote} - A FFZEmote instance.
689
+ */
690
+ _cacheFFZEmote(channelId, data, existingEmote) {
691
+ const channel = this._setupChannel(channelId);
692
+ const emote = existingEmote || new FFZEmote_default(channel, data.id, data);
693
+ this.emotes.set(emote.code, emote);
694
+ channel.emotes.set(emote.code, emote);
695
+ return emote;
696
+ }
697
+ /**
698
+ * Gets the raw 7TV emotes data for a channel.
699
+ * @private
700
+ * @param {number} [id] - ID of the channel.
701
+ * @returns {Promise<object[]>} - A promise that resolves to an array of raw 7TV emote data.
702
+ */
703
+ _getRawSevenTVEmotes(id) {
704
+ const endpoint = id ? Constants_default.SevenTV.Channel(id) : Constants_default.SevenTV.Global;
705
+ return fetch(endpoint).then((response) => response.json());
706
+ }
707
+ /**
708
+ * Converts and caches a raw 7TV emote.
709
+ * @private
710
+ * @param {number} channelId - ID of the channel.
711
+ * @param {object} data - Raw data.
712
+ * @param {string} format - The type file format to use (webp/avif).
713
+ * @param {SevenTVEmote} [existingEmote] - Existing emote to cache.
714
+ * @returns {SevenTVEmote} - A SevenTVEmote instance.
715
+ */
716
+ _cacheSevenTVEmote(channelId, data, format, existingEmote) {
717
+ const channel = this._setupChannel(channelId, format);
718
+ const emote = existingEmote || new SevenTVEmote_default(channel, data.id, data);
719
+ this.emotes.set(emote.code, emote);
720
+ channel.emotes.set(emote.code, emote);
721
+ return emote;
722
+ }
723
+ /**
724
+ * Fetches the Twitch emotes for a channel.
725
+ * Use `null` for the global emotes channel.
726
+ * @param {number} [channel] - ID of the channel.
727
+ * @returns {Promise<Collection<string, TwitchEmote>>} - A promise that resolves to a collection of TwitchEmotes.
728
+ */
729
+ fetchTwitchEmotes(channel) {
730
+ return this._getRawTwitchEmotes(channel).then((rawEmotes) => {
731
+ for (const emote of rawEmotes) {
732
+ this._cacheTwitchEmote(channel, {
733
+ code: emote.name,
734
+ id: emote.id,
735
+ formats: emote.formats
736
+ });
737
+ }
738
+ return this.channels.get(channel).emotes.filter((e) => e.type === "twitch");
739
+ });
740
+ }
741
+ /**
742
+ * Fetches the BTTV emotes for a channel.
743
+ * Use `null` for the global emotes channel.
744
+ * @param {number} [channel] - ID of the channel.
745
+ * @returns {Promise<Collection<string, BTTVEmote>>} - A promise that resolves to a collection of BTTVEmotes.
746
+ */
747
+ fetchBTTVEmotes(channel) {
748
+ return this._getRawBTTVEmotes(channel).then((rawEmotes) => {
749
+ for (const data of rawEmotes) {
750
+ this._cacheBTTVEmote(channel, data);
751
+ }
752
+ return this.channels.get(channel).emotes.filter((e) => e.type === "bttv");
753
+ });
754
+ }
755
+ /**
756
+ * Fetches the FFZ emotes for a channel.
757
+ * @param {number} [channel] - ID of the channel.
758
+ * @returns {Promise<Collection<string, FFZEmote>>} - A promise that resolves to a collection of FFZEmotes.
759
+ */
760
+ async fetchFFZEmotes(channel) {
761
+ if (!this.ffzModifiersFetched) {
762
+ this.ffzModifiersFetched = true;
763
+ await this._getRawFFZEmoteSet(Constants_default.FFZ.sets.Modifiers).then((rawEmotes) => {
764
+ for (const data of rawEmotes) {
765
+ this._cacheFFZEmote(null, data);
766
+ }
767
+ });
768
+ }
769
+ if (!channel) {
770
+ return this._getRawFFZEmoteSet(Constants_default.FFZ.sets.Global).then((rawEmotes) => {
771
+ for (const data of rawEmotes) {
772
+ this._cacheFFZEmote(channel, data);
773
+ }
774
+ return this.channels.get(channel).emotes.filter((e) => e.type === "ffz");
775
+ });
776
+ }
777
+ return this._getRawFFZEmotes(channel).then((rawEmotes) => {
778
+ for (const data of rawEmotes) {
779
+ this._cacheFFZEmote(channel, data);
780
+ }
781
+ return this.channels.get(channel).emotes.filter((e) => e.type === "ffz");
782
+ });
783
+ }
784
+ /**
785
+ * Fetches the 7TV emotes for a channel.
786
+ * @param {number} [channel] - ID of the channel.
787
+ * @param {object} [options] - Options for fetching.
788
+ * @param {('webp'|'avif')} [options.format] - The type file format to use (webp/avif).
789
+ * @returns {Promise<Collection<string, SevenTVEmote>>} - A promise that resolves to a collection of SevenTVEmotes.
790
+ */
791
+ fetchSevenTVEmotes(channel, options) {
792
+ const {
793
+ format = "webp"
794
+ } = options || {};
795
+ return this._getRawSevenTVEmotes(channel).then((rawEmotes) => {
796
+ if (Object.hasOwn(rawEmotes, "emotes")) {
797
+ for (const data of rawEmotes.emotes) {
798
+ this._cacheSevenTVEmote(channel, data, format);
799
+ }
800
+ } else {
801
+ for (const data of rawEmotes.emote_set.emotes) {
802
+ this._cacheSevenTVEmote(channel, data, format);
803
+ }
804
+ }
805
+ return this.channels.get(channel).emotes.filter((e) => e.type === "7tv");
806
+ });
807
+ }
808
+ /**
809
+ * Converts emote Objects to emotes
810
+ * @param {object} [emotesArray] - An array of emote objects
811
+ * @throws {TypeError} When an emote has an unknown type.
812
+ * @returns {Emote[]} - An array of Emote instances.
813
+ */
814
+ fromObject(emotesArray) {
815
+ const emotes = [];
816
+ const classMap = {
817
+ "bttv": { class: BTTVEmote_default, cache: (emoteObject, channelId, existingEmote) => this._cacheBTTVEmote(channelId, null, existingEmote) },
818
+ "ffz": { class: FFZEmote_default, cache: (emoteObject, channelId, existingEmote) => this._cacheFFZEmote(channelId, null, existingEmote) },
819
+ "7tv": { class: SevenTVEmote_default, cache: (emoteObject, channelId, existingEmote) => this._cacheSevenTVEmote(channelId, null, emoteObject.imageType, existingEmote) },
820
+ "twitch": { class: TwitchEmote_default, cache: (emoteObject, channelId, existingEmote) => this._cacheTwitchEmote(channelId, null, existingEmote) }
821
+ };
822
+ for (const emoteObject of emotesArray) {
823
+ const { type } = emoteObject;
824
+ if (!Object.keys(classMap).includes(type)) {
825
+ throw new TypeError(`Unknown type: ${type}`);
826
+ }
827
+ const emoteClass = classMap[type].class;
828
+ this._setupChannel(emoteObject.channel_id, type === "7tv" ? emoteObject.imageType : null);
829
+ const emote = emoteClass.fromObject(emoteObject, this.channels.get(emoteObject.channel_id));
830
+ classMap[type].cache(emoteObject, emoteObject.channel_id, emote);
831
+ emotes.push(emote);
832
+ }
833
+ return emotes;
834
+ }
835
+ };
836
+ var EmoteFetcher_default = EmoteFetcher;
837
+
838
+ // src/struct/EmoteParser.js
839
+ var EmoteParser = class {
840
+ /**
841
+ * A parser to replace text with emotes.
842
+ * @param {EmoteFetcher} fetcher - The fetcher to use the cache of.
843
+ * @param {object} [options={}] - Options for the parser.
844
+ * @param {string} [options.template=''] - The template to be used.
845
+ * The strings that can be interpolated are:
846
+ * - `{link}` The link of the emote.
847
+ * - `{name}` The name of the emote.
848
+ * - `{size}` The size index of the image.
849
+ * - `{creator}` The channel/owner name of the emote.
850
+ * @param {'html' | 'markdown' | 'bbcode' | 'plain'} [options.type='html'] - The type of the parser.
851
+ * Can be one of `html`, `markdown`, `bbcode`, or `plain`.
852
+ * If the `template` option is provided, this is ignored.
853
+ * @param {RegExp} [options.match=/(\w+)/g] - The regular expression that matches an emote.
854
+ * Must be a global regex, with one capture group for the emote code.
855
+ */
856
+ constructor(fetcher, options = {}) {
857
+ this.fetcher = fetcher;
858
+ this.options = {
859
+ template: "",
860
+ type: "html",
861
+ match: /(\w+)/g,
862
+ ...options
863
+ };
864
+ this._validateOptions(this.options);
865
+ }
866
+ /**
867
+ * Validates the parser options.
868
+ * @private
869
+ * @param {object} [options] - Options for the parser.
870
+ * @param {string} [options.template] - The template to be used.
871
+ * The strings that can be interpolated are:
872
+ * - `{link}` The link of the emote.
873
+ * - `{name}` The name of the emote.
874
+ * - `{size}` The size of the image.
875
+ * - `{creator}` The channel/owner name of the emote.
876
+ * @param {'html' | 'markdown' | 'bbcode' | 'plain'} [options.type] - The type of the parser.
877
+ * Can be one of `html`, `markdown`, `bbcode`, or `plain`.
878
+ * If the `template` option is provided, this is ignored.
879
+ * @param {RegExp} [options.match] - The regular expression that matches an emote.
880
+ * Must be a global regex, with one capture group for the emote code.
881
+ * @throws {TypeError} When template is not a string.
882
+ * @throws {TypeError} When type is not one of the supported types.
883
+ * @throws {TypeError} When match is not a global RegExp.
884
+ */
885
+ _validateOptions(options) {
886
+ if (options.template && typeof options.template !== "string") {
887
+ throw new TypeError("Template must be a string");
888
+ }
889
+ if (!["html", "markdown", "bbcode", "plain"].includes(options.type)) {
890
+ throw new TypeError("Parse type must be one of `html`, `markdown`, `bbcode`, or `plain`");
891
+ }
892
+ if (!(options.match instanceof RegExp) || !options.match.global) {
893
+ throw new TypeError("Match must be a global RegExp.");
894
+ }
895
+ }
896
+ /**
897
+ * Parses text.
898
+ * @param {string} text - Text to parse.
899
+ * @param {object} [options] - Parameters for parsing.
900
+ * @param {number} [options.size] - Size (scale) for emotes.
901
+ * @param {boolean} [options.forceStatic] - Whether to force the emote to be static (non-animated). Defaults to the fetcher's forceStatic or `false`.
902
+ * @param {'dark' | 'light'} [options.themeMode] - Only for Twitch: the preferred theme mode. Defaults to the fetcher's twitchThemeMode or `dark`.
903
+ * @returns {string} - The parsed text.
904
+ */
905
+ parse(text, options) {
906
+ const {
907
+ size,
908
+ forceStatic,
909
+ themeMode
910
+ } = options || {};
911
+ const parsed = text.replace(this.options.match, (matched, id) => {
912
+ const emote = this.fetcher.emotes.get(id);
913
+ if (!emote) return matched;
914
+ if (emote.modifier) return "";
915
+ const template = this.options.template || Constants_default.Templates[this.options.type];
916
+ const link = emote.toLink({ size, forceStatic, themeMode });
917
+ const res = template.replaceAll("{link}", link).replaceAll("{name}", emote.code).replaceAll("{size}", size).replaceAll("{creator}", emote.ownerName || "global");
918
+ return res;
919
+ });
920
+ return parsed;
921
+ }
922
+ };
923
+ var EmoteParser_default = EmoteParser;
924
+
925
+ // src/index.js
926
+ var index_default = {
927
+ BTTVEmote: BTTVEmote_default,
928
+ Channel: Channel_default,
929
+ Collection: Collection_default,
930
+ Constants: Constants_default,
931
+ Emote: Emote_default,
932
+ EmoteFetcher: EmoteFetcher_default,
933
+ EmoteParser: EmoteParser_default,
934
+ FFZEmote: FFZEmote_default,
935
+ SevenTVEmote: SevenTVEmote_default,
936
+ TwitchEmote: TwitchEmote_default
937
+ };
938
+ // Annotate the CommonJS export names for ESM import in node:
939
+ 0 && (module.exports = {
940
+ BTTVEmote,
941
+ Channel,
942
+ Collection,
943
+ Constants,
944
+ Emote,
945
+ EmoteFetcher,
946
+ EmoteParser,
947
+ FFZEmote,
948
+ SevenTVEmote,
949
+ TwitchEmote
950
+ });