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

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.
package/README.md CHANGED
@@ -17,29 +17,28 @@ Gets Twitch, BTTV, FFZ and 7TV emotes as well as parsing text to emotes!
17
17
 
18
18
  ### List of breaking changes from 2.x to 3.x
19
19
 
20
- - Node.js 20 is required; we've set the minimum to 20.18.1.
20
+ - Node.js 20 is required; we have set the minimum to 20.18.1.
21
21
  *This library is now an ECMAScript module, so you can use proper `import {...} from '...'` imports.*
22
22
  - The initialization of `EmoteFetcher` changed to only use an object as the first parameter for options.
23
23
  - API keys for Twitch must now be set with `twitchAppID` and `twitchAppSecret` properties.
24
24
  - The previously available `apiClient` is now set in this object too.
25
- - The defaults for `EmoteParser` changed to use the `html` template, and it doesn't require `:colons:` by default (using `/(\w+)/` to match any words).
26
- - The default `html` template doesn't have `twitch-emote-{size}` anymore in its `class` attribute.
27
- *The `size` isn't consistent between the different sources, so it can't reliably be used.*
25
+ - The defaults for `EmoteParser` changed to use the `html` template, and it does not require `:colons:` by default (using `/(\w+)/` to match any words).
26
+ - The default `html` template does not have `twitch-emote-{size}` anymore in its `class` attribute.
27
+ *The `size` is inconsistent between the different sources, so it cannot be reliably used.*
28
28
  - The `EmoteFetcher.fetchSevenTVEmotes()`, `Emote.toLink()`, and `EmoteParse.parse()` methods now have their options as an object.
29
29
  - `fetcher.fetchSevenTVEmotes(null, { format: 'avif' })` - The first parameter is still the Twitch user ID (or `null` for global).
30
30
  - `emote.toLink({ size: 1, forceStatic: true, themeMode: 'light' })`
31
31
  - `parser.parse('Kappa', { size: 2, forceStatic: true, themeMode: 'dark' })` - The first parameter is still the input text.
32
32
  - The `owner` getter for `Emote`s has been removed.
33
- *It isn't reliable to get the `Channel` object, more so with 3rd-party providers since emotes might be owned by a channel that we never fetched (for shared/public emotes).*
34
- - If you've exported 7TV emotes, do note that the `sizes` array changed to not include the leading `x.<format>`.
35
- - **More may come for the final release, as this is still a work in progress.**
33
+ *It is not reliable to get the `Channel` object, more so with 3rd-party providers since emotes might be owned by a channel that we never fetched (for shared/public emotes).*
34
+ - If you have exported 7TV emotes, do note that the `sizes` array changed to not include the leading `x.<format>`.
35
+ *The rest of the export/import is still compatible with the current implementation.*
36
36
 
37
37
 
38
38
  ### Example of code changes
39
39
 
40
- Our examples are running in an ESM-based project (`"type": "module"`).
41
-
42
- We export a CommonJS-compatible build, so you can still use `require(...)`:
40
+ Our examples are running in an ESM-based project (`"type": "module"`).
41
+ Do note that we export a CommonJS-compatible build, so you can still use `require(...)`:
43
42
  ```js
44
43
  const { EmoteFetcher, EmoteParser } = require('@mkody/twitch-emoticons')
45
44
  ```
@@ -53,12 +52,12 @@ const { EmoteFetcher, EmoteParser } = TwitchEmoticons // but you should move awa
53
52
 
54
53
  const fetcher = new EmoteFetcher('<your app ID>', '<your app secret>') // <- The first two parameters were for the Twitch app ID/secret
55
54
 
56
- // Those next three lines didn't have breaking changes
57
- await fetcher.fetchTwitchEmotes() // Do note that CommonJS doesn't handle `await` at the top level
55
+ // Those next three lines do not have breaking changes
56
+ await fetcher.fetchTwitchEmotes() // Do note that CommonJS does not handle `await` at the top level
58
57
  const parser = new EmoteParser(fetcher)
59
58
  const emote = emoteFetcher.emotes.get('Kappa')
60
59
 
61
- console.log(emote.toLink(2)) // <- Only the size was available and was set as the first parameter
60
+ console.log(emote.toLink(2)) // <- Only the size was available and set as the first parameter
62
61
  // https://static-cdn.jtvnw.net/emoticons/v2/25/default/dark/3.0
63
62
 
64
63
  console.log(parser.parse('Hello :CoolCat:!')) // <- Used colons and returned Markdown by default
@@ -76,7 +75,7 @@ const fetcher = new EmoteFetcher({ // <- Uses an object!
76
75
  twitchAppSecret: '<your app secret>',
77
76
  })
78
77
 
79
- // Those next three lines didn't have breaking changes
78
+ // Those next three lines do not have breaking changes
80
79
  await fetcher.fetchTwitchEmotes()
81
80
  const parser = new EmoteParser(fetcher)
82
81
  const emote = emoteFetcher.emotes.get('Kappa')
@@ -84,7 +83,7 @@ const emote = emoteFetcher.emotes.get('Kappa')
84
83
  console.log(emote.toLink({ size: 2 })) // <- Uses an object!
85
84
  // https://static-cdn.jtvnw.net/emoticons/v2/25/default/dark/3.0
86
85
 
87
- console.log(parser.parse('Hello CoolCat!')) // <- Doesn't require :colons: and returns HTML by default!
86
+ console.log(parser.parse('Hello CoolCat!')) // <- Does not require :colons: and returns HTML by default!
88
87
  // Hello <img alt="CoolCat" title="CoolCat" class="twitch-emote" src="https://static-cdn.jtvnw.net/emoticons/v2/58127/default/dark/1.0">
89
88
  ```
90
89
 
@@ -93,8 +92,8 @@ console.log(parser.parse('Hello CoolCat!')) // <- Doesn't require :colons: and r
93
92
 
94
93
  ## Prerequisites
95
94
 
96
- To fetch "native" Twitch emotes, you need to [create an app here](https://dev.twitch.tv/console/apps/create), it's free.
97
- If you are only using BetterTTV, FrankerFaceZ and 7TV you don't need to provide Twitch app keys.
95
+ To fetch "native" emotes from Twitch.tv, you need to [create an app here](https://dev.twitch.tv/console/apps/create), it is free.
96
+ If you are only using BetterTTV, FrankerFaceZ and 7TV you do not need to provide Twitch app keys.
98
97
 
99
98
  You must use a Twitch user ID instead of the username to fetch users' emotes.
100
99
  You can use [this page to manually convert them](https://s.kdy.ch/twitchid/).
@@ -122,13 +121,13 @@ npx jsr add @mkody/twitch-emoticons
122
121
  pnpm add jsr:@mkody/twitch-emoticons
123
122
  # or
124
123
  yarn add jsr:@mkody/twitch-emoticons
125
- # or (version has to be specified while it's a pre-release)
126
- deno add jsr:@mkody/twitch-emoticons@3.0.0-beta.1
124
+ # or (version has to be specified while it is a pre-release)
125
+ deno add jsr:@mkody/twitch-emoticons@3.0.0-beta.3
127
126
  ```
128
127
 
129
- [npm]: https://www.npmjs.com/package/@mkody/twitch-emoticons/v/3.0.0-beta.1
130
- [browse on npmx]: https://npmx.dev/package/@mkody/twitch-emoticons/v/3.0.0-beta.1
131
- [jsr]: https://jsr.io/@mkody/twitch-emoticons@3.0.0-beta.1
128
+ [npm]: https://www.npmjs.com/package/@mkody/twitch-emoticons/v/3.0.0-beta.3
129
+ [browse on npmx]: https://npmx.dev/package/@mkody/twitch-emoticons/v/3.0.0-beta.3
130
+ [jsr]: https://jsr.io/@mkody/twitch-emoticons@3.0.0-beta.3
132
131
 
133
132
 
134
133
  ## Quick docs
@@ -136,7 +135,7 @@ deno add jsr:@mkody/twitch-emoticons@3.0.0-beta.1
136
135
  <details>
137
136
  <summary>Click here to toggle the docs</summary>
138
137
 
139
- Here's some quick documentation to explain our two classes, the principal methods, and the settings.
138
+ Here is some quick documentation to explain our two classes, the principal methods, and some settings.
140
139
 
141
140
  > **NOTE:**
142
141
  > If you want a more complete documentation, see: https://mkody.github.io/twitch-emoticons/
@@ -148,18 +147,19 @@ First, you need to load a list of emotes, and for that you create a new `EmoteFe
148
147
 
149
148
  ```js
150
149
  const fetcher = new EmoteFetcher({
151
- // If you want to use emotes from twitch.tv, you'll need to be authenticated to use their API. You have two options:
150
+ // If you want to use emotes from twitch.tv, you will need to be authenticated to use their API.
152
151
  // Option 1: Provide your app ID and secret here (get them at https://dev.twitch.tv/console/apps).
153
152
  twitchAppID, // <string>
154
153
  twitchAppSecret, // <string>
155
- // Option 2: If you need a different way to auth or already use `@twurple/api`, you can provide your ApiClient object here.
154
+ // Option 2: If you need a different way to auth or already use `@twurple/api`,
155
+ // you can provide your ApiClient object here.
156
156
  apiClient, // <ApiClient>
157
157
 
158
158
  // Force emotes to be static (non-animated).
159
159
  forceStatic, // <boolean> - Default: false
160
160
 
161
161
  // Theme mode (background color) preference for Twitch emotes.
162
- twitchThemeMode // <'dark' | 'light'> - Default: 'dark'
162
+ twitchThemeMode, // <'dark' | 'light'> - Default: 'dark'
163
163
  })
164
164
  ```
165
165
 
@@ -173,7 +173,7 @@ There is one method per platform; all of them return Promises (so you can use `a
173
173
  - `fetcher.fetchSevenTVEmotes()`
174
174
 
175
175
  The first parameter is the Twitch user ID of the channel you want to load emotes from.
176
- If not provided or it's `null`/falsy, it loads what we call "global emotes", which are available to all users of the platform.
176
+ If not provided or it is `null`/falsy, it loads what we call "global emotes", which are available to all users of the platform.
177
177
 
178
178
  Do note that `fetchSevenTVEmotes()` accepts a second parameter with an object:
179
179
 
@@ -191,8 +191,8 @@ await fetcher.fetchSevenTVEmotes(null, { format: 'avif' }) // Example of loading
191
191
 
192
192
  ### Parse strings to include emotes with `EmoteParser`
193
193
 
194
- And now that we have our list of emotes that we can expect to find, let's use it!
195
- Let's create our `EmoteParser` object:
194
+ And now that we have our list of emotes that we can expect to find, we should use it!
195
+ Create an `EmoteParser` object:
196
196
 
197
197
  ```js
198
198
  const parser = new EmoteParser(
@@ -203,16 +203,18 @@ const parser = new EmoteParser(
203
203
  {
204
204
  // What output should be used when you parse messages? There are two ways to set that up:
205
205
  // Option 1: Use one of the provided templates:
206
- // - `html`: `<img alt="{name}" title="{name}" class="twitch-emote" src="{link}">`
207
- // - `markdown`: `![{name}]({link} "{name}")`
208
- // - `bbcode`: `[img]{link}[/img]`
209
- // - `plain`: `{link}`
206
+ // - 'html': `<img alt="{name}" title="{name}" class="twitch-emote" src="{link}">`
207
+ // - 'markdown': `![{name}]({link} "{name}")`
208
+ // - 'bbcode': `[img]{link}[/img]`
209
+ // - 'plain': `{link}`
210
210
  type, // <'html' | 'markdown' | 'bbcode' | 'plain'> - Default: 'html'
211
- // Option 2: Make your own template; it has priority over option 1. You can use those: `{link}`, `{name}`, `{size}`, `{creator}`.
211
+ // Option 2: Make your own template; this has priority over option 1.
212
+ // You can use those: `{link}`, `{name}`, `{size}`, `{creator}`,
213
+ // `{is-animated}`, `{is-nsfw}`, `{is-zero-width}`.
212
214
  template, // <string> - Default: ''
213
215
 
214
216
  // You can customize the regular expression used to find possible emotes.
215
- match, // <RegExp>
217
+ match, // <RegExp> - Default: /(\w+)/g
216
218
  },
217
219
  )
218
220
  ```
@@ -221,11 +223,20 @@ Now that you have an EmoteParser object, you can do two things with it:
221
223
  look up an `Emote` or parse text and get the output as set by `type` or `template`.
222
224
 
223
225
  To grab a specific emote, you can do `fetcher.emotes.get('...')`, with the case-sensitive name of the emote in place of the ellipsis.
224
- From there, you can read properties like `.code`, `.animated`, `.imageType`, or `.type`,
225
- but you can also use the `.toLink()` method to… get a link!
226
+ From there, you can read properties like `.id`, `.code`, `.ownerName`,
227
+ `.animated`, `.imageType`, or `.type`, but you can also use the `.toLink()`
228
+ method to… get a link!
229
+
230
+ > Note: Some extended `Emote`s have additional properties:
231
+ > - FFZ:
232
+ > - `.zeroWidth` (boolean, can overlay an another emote)
233
+ > - `.modifier` (boolean, emote effects, should be hidden)
234
+ > - 7TV:
235
+ > - `.zeroWidth` (boolean, can overlay an another emote)
236
+ > - `.nsfw` (boolean, flagged "Sexual" or "Twitch disallowed")
226
237
 
227
238
  To parse text, you use the `parser.parse()` method.
228
- The first parameter is the input text. There's also an optional second parameter
239
+ The first parameter is the input text. There is also an optional second parameter
229
240
  where you can provide a few settings (which can overwrite the same ones set in the `EmoteFetcher`).
230
241
 
231
242
  ```js
@@ -235,19 +246,21 @@ const parsed = parser.parse(
235
246
 
236
247
  // The second parameter is an *optional* object with the settings.
237
248
  {
238
- // Size (scale) for emotes. It varies by provider, and not all share the same resolution in pixels. Play with this value if you'd like, but no guarantees!
249
+ // Size (scale) for emotes.
250
+ // It varies by provider, and not all share the same resolution in pixels.
251
+ // Play with this value if you would like, but no guarantees!
239
252
  size, // <number>
240
253
 
241
254
  // Force emotes to be static (non-animated).
242
- forceStatic, // <boolean> - Default: what's in EmoteFetcher or false
255
+ forceStatic, // <boolean> - Default: value in EmoteFetcher or false
243
256
 
244
257
  // Theme mode (background color) preference for Twitch emotes.
245
- twitchThemeMode // <'dark' | 'light'> - Default: what's in EmoteFetcher or 'dark'
258
+ twitchThemeMode // <'dark' | 'light'> - Default: value in EmoteFetcher or 'dark'
246
259
  },
247
260
  )
248
261
  ```
249
262
 
250
- And that's it for the essentials!
263
+ And that is it for the essentials!
251
264
  You can go through the examples below if you need to see more complete code and direct usage.
252
265
 
253
266
  </details>
@@ -325,14 +338,21 @@ const fetcher = new EmoteFetcher({
325
338
  })
326
339
  const parser = new EmoteParser(fetcher, {
327
340
  // Custom HTML format
328
- template: '<img class="emote" alt="{name}" src="{link}">',
329
- // Otherwise, just use our provided template
330
- // type: 'html',
341
+ template: `
342
+ <img
343
+ class="emote"
344
+ alt="{name}"
345
+ src="{link}"
346
+ data-scale="{size}"
347
+ data-animated="{is-animated}"
348
+ data-overlay="{is-zero-width}"
349
+ data-nsfw="{is-nsfw}"
350
+ >`,
331
351
  // Matches words (like \w) but also dashes
332
352
  match: /([a-zA-Z0-9_\-]+)/g,
333
353
  })
334
354
 
335
- // Fetch all emotes at once and wait for all of them to complete
355
+ // Fetch all the emotes we want and wait for everything to complete
336
356
  Promise.all([
337
357
  // Twitch global
338
358
  fetcher.fetchTwitchEmotes(),
@@ -467,9 +487,9 @@ console.log(kappa)
467
487
 
468
488
  ### 6. 7TV formats
469
489
 
470
- 7TV v3 delivers emotes in either WEBP or AVIF.
490
+ 7TV delivers emotes in either WEBP or AVIF.
471
491
 
472
- By default we'll return WEBP emotes, but you can override this.
492
+ By default we will return WEBP emotes, but you can override this.
473
493
 
474
494
  ```js
475
495
  // (setup)
@@ -485,14 +505,14 @@ await fetcher.fetchSevenTVEmotes(44317909)
485
505
  // ... which is currently the same as
486
506
  await fetcher.fetchSevenTVEmotes(44317909, { format: 'webp' })
487
507
 
488
- // Fetch Anatole's emotes in AVIF
489
- await fetcher.fetchSevenTVEmotes(24377667, { format: 'avif' })
508
+ // Fetch Nerixyz's emotes in AVIF
509
+ await fetcher.fetchSevenTVEmotes(129546453, { format: 'avif' })
490
510
  ```
491
511
 
492
512
 
493
513
  ### 7. Export and import emote data
494
514
 
495
- This can be useful to save the emotes in a cache or for offline content.
515
+ This can be useful to save the emotes in a cache or for offline access.
496
516
 
497
517
  ```js
498
518
  // (setup)
@@ -503,7 +523,7 @@ const fetcher = new EmoteFetcher()
503
523
  await fetcher.fetchSevenTVEmotes(null, { format: 'avif' })
504
524
 
505
525
  // Then you can use .toObject() on an `Emote` to export its data.
506
- // Here's a map to get them all in a single array.
526
+ // Here is a map to get them all in a single array.
507
527
  const emotes = fetcher.emotes.map((emote) => emote.toObject())
508
528
 
509
529
  // Later, with or without a fresh `EmoteFetcher`, you can use .fromObject() on the fetcher.
@@ -511,7 +531,7 @@ fetcher.fromObject(emotes)
511
531
  ```
512
532
 
513
533
  > **NOTE:**
514
- > For offline content, you'll still need to download emotes and proxy their URLs.
534
+ > For offline access, you' will still need to download emotes and proxy their URLs.
515
535
 
516
536
  </details>
517
537
 
@@ -527,4 +547,4 @@ This library uses the following:
527
547
  - [Twurple](https://twurple.js.org/) and the [Twitch API](https://dev.twitch.tv/)
528
548
  - [BetterTTV API](https://betterttv.com/developers/api)
529
549
  - [FrankerFaceZ API](https://api.frankerfacez.com/docs/)
530
- - [7TV API](https://7tv.io/)
550
+ - [7TV API (v3 via GraphQL)](https://github.com/SevenTV/SevenTV/tree/main/apps/api/src/http/v3/gql)
@@ -100,8 +100,56 @@ var Constants_default = {
100
100
  CDN: (id, size = 0, forceStatic = false) => `https://cdn.betterttv.net/emote/${id}/${size + 1}x.${forceStatic ? "png" : "webp"}`
101
101
  },
102
102
  SevenTV: {
103
- Global: "https://7tv.io/v3/emote-sets/global",
104
- Channel: (id) => `https://7tv.io/v3/users/twitch/${id}`,
103
+ GQL: "https://7tv.io/v3/gql",
104
+ GlobalQuery: `
105
+ query GetGlobalEmotes($format: [ImageFormat!]) {
106
+ namedEmoteSet(name: GLOBAL) {
107
+ emotes {
108
+ id
109
+ name
110
+ flags
111
+ data {
112
+ name
113
+ flags
114
+ animated
115
+ host {
116
+ files(formats: $format) {
117
+ name
118
+ }
119
+ }
120
+ owner {
121
+ display_name
122
+ }
123
+ }
124
+ }
125
+ }
126
+ }`,
127
+ ChannelQuery: `
128
+ query GetChannelEmotes($id: String!, $format: [ImageFormat!]) {
129
+ userByConnection(platform: TWITCH, id: $id) {
130
+ emote_sets(entitled: false) {
131
+ flags
132
+ emotes {
133
+ id
134
+ name
135
+ flags
136
+ data {
137
+ name
138
+ flags
139
+ animated
140
+ host {
141
+ files(formats: $format) {
142
+ name
143
+ }
144
+ }
145
+ owner {
146
+ display_name
147
+ }
148
+ }
149
+ }
150
+ }
151
+ }
152
+ }`,
105
153
  CDN: (id, format, size = 1, forceStatic = false) => `https://cdn.7tv.app/emote/${id}/${size}x${forceStatic ? "_static" : ""}.${format}`
106
154
  },
107
155
  FFZ: {
@@ -277,7 +325,7 @@ var Channel = class {
277
325
  this.channel_id = id || null;
278
326
  this.emotes = new Collection_default();
279
327
  }
280
- /* There are tests that those are returning Collections, but c8 doesn't get it. */
328
+ /* There are tests that those are returning Collections, but c8 does not get it. */
281
329
  /* c8 ignore start */
282
330
  /**
283
331
  * Fetches the BTTV emotes for this channel.
@@ -317,12 +365,12 @@ var FFZEmote = class _FFZEmote extends Emote_default {
317
365
  this.type = "ffz";
318
366
  }
319
367
  _setup(data) {
320
- super._setup(data);
321
368
  this.code = data.name;
322
369
  this.ownerName = "owner" in data ? data.owner.name : null;
323
370
  this.sizes = "animated" in data ? Object.keys(data.animated) : Object.keys(data.urls);
324
371
  this.animated = "animated" in data;
325
372
  this.imageType = "animated" in data ? "webp" : "png";
373
+ this.zeroWidth = data.modifier && data.modifier_flags === 0;
326
374
  this.modifier = data.modifier && (data.modifier_flags & 1) !== 0;
327
375
  }
328
376
  /**
@@ -353,6 +401,7 @@ var FFZEmote = class _FFZEmote extends Emote_default {
353
401
  sizes: this.sizes,
354
402
  ownerName: this.ownerName,
355
403
  type: this.type,
404
+ zeroWidth: this.zeroWidth,
356
405
  modifier: this.modifier
357
406
  };
358
407
  }
@@ -363,10 +412,13 @@ var FFZEmote = class _FFZEmote extends Emote_default {
363
412
  * @returns {FFZEmote} - A FFZEmote instance.
364
413
  */
365
414
  static fromObject(emoteObject, channel) {
366
- const sizesObj = emoteObject.sizes.reduce((acc, curr) => {
367
- acc[curr] = curr;
368
- return acc;
369
- }, {});
415
+ const sizesObj = emoteObject.sizes.reduce(
416
+ (acc, curr) => {
417
+ acc[curr] = curr;
418
+ return acc;
419
+ },
420
+ {}
421
+ );
370
422
  return new _FFZEmote(
371
423
  channel,
372
424
  emoteObject.id,
@@ -376,8 +428,8 @@ var FFZEmote = class _FFZEmote extends Emote_default {
376
428
  urls: sizesObj,
377
429
  ...emoteObject.animated ? { animated: sizesObj } : {},
378
430
  owner: { name: emoteObject.ownerName },
379
- modifier: emoteObject.modifier,
380
- modifier_flags: emoteObject.modifier
431
+ modifier: emoteObject.zeroWidth || emoteObject.modifier,
432
+ modifier_flags: emoteObject.modifier ? 1 : 0
381
433
  }
382
434
  );
383
435
  }
@@ -397,11 +449,41 @@ var SevenTVEmote = class _SevenTVEmote extends Emote_default {
397
449
  this.type = "7tv";
398
450
  }
399
451
  _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;
452
+ const ActiveEmoteFlags = {
453
+ ZeroWidth: 1,
454
+ // 1 << 0
455
+ OverrideTwitchGlobal: 65536,
456
+ // 1 << 16
457
+ OverrideTwitchSubscriber: 131072,
458
+ // 1 << 17
459
+ OverrideBetterTTV: 262144
460
+ // 1 << 18
461
+ };
462
+ const EmoteFlags = {
463
+ Private: 1,
464
+ // 1 << 0
465
+ Authentic: 2,
466
+ // 1 << 1
467
+ ZeroWidth: 256,
468
+ // 1 << 8
469
+ Sexual: 65536,
470
+ // 1 << 16
471
+ Epilepsy: 131072,
472
+ // 1 << 17
473
+ Edgy: 262144,
474
+ // 1 << 18
475
+ TwitchDisallowed: 16777216
476
+ // 1 << 24
477
+ };
478
+ this.code = data.name || data.data.name;
479
+ this.ownerName = data.data.owner?.display_name || null;
480
+ this.sizes = data.data.host.files.map((el) => el.name.replace(/x\.(\w+)/, "")).sort((a, b) => Number(a) - Number(b));
481
+ if (this.sizes.length === 0) {
482
+ this.sizes = ["1", "2", "3", "4"];
483
+ }
484
+ this.animated = Boolean(data.data.animated);
485
+ this.nsfw = (data.data.flags & EmoteFlags.Sexual) !== 0 || (data.data.flags & EmoteFlags.TwitchDisallowed) !== 0;
486
+ this.zeroWidth = (data.flags & ActiveEmoteFlags.ZeroWidth) !== 0 || (data.data.flags & EmoteFlags.ZeroWidth) !== 0;
405
487
  this.imageType = this.channel.format;
406
488
  }
407
489
  /**
@@ -428,6 +510,8 @@ var SevenTVEmote = class _SevenTVEmote extends Emote_default {
428
510
  return {
429
511
  ...super.toObject(),
430
512
  animated: this.animated,
513
+ nsfw: this.nsfw,
514
+ zeroWidth: this.zeroWidth,
431
515
  sizes: this.sizes,
432
516
  ownerName: this.ownerName,
433
517
  type: this.type,
@@ -442,21 +526,22 @@ var SevenTVEmote = class _SevenTVEmote extends Emote_default {
442
526
  */
443
527
  static fromObject(emoteObject, channel) {
444
528
  const sizes = emoteObject.sizes.map((size) => {
445
- return { format: channel.format.toUpperCase(), name: size };
529
+ return { name: size };
446
530
  });
531
+ const flags = (emoteObject.nsfw ? 65536 : 0) | (emoteObject.zeroWidth ? 256 : 0);
447
532
  return new _SevenTVEmote(
448
533
  channel,
449
534
  emoteObject.id,
450
535
  {
451
- code: emoteObject.code,
452
536
  name: emoteObject.code,
453
537
  data: {
454
538
  animated: emoteObject.animated,
455
- owner: {
456
- display_name: emoteObject.ownerName
457
- },
539
+ flags,
458
540
  host: {
459
541
  files: sizes
542
+ },
543
+ owner: {
544
+ display_name: emoteObject.ownerName
460
545
  }
461
546
  }
462
547
  }
@@ -480,8 +565,8 @@ var TwitchEmote = class _TwitchEmote extends Emote_default {
480
565
  _setup(data) {
481
566
  super._setup(data);
482
567
  this.set = data.emoticon_set;
483
- this.animated = "animated" in data.formats;
484
- this.imageType = "animated" in data.formats ? "gif" : "png";
568
+ this.animated = data.formats?.includes("animated") || false;
569
+ this.imageType = data.formats?.includes("animated") ? "gif" : "png";
485
570
  }
486
571
  /**
487
572
  * Gets the image link of the emote.
@@ -526,7 +611,7 @@ var TwitchEmote = class _TwitchEmote extends Emote_default {
526
611
  code: emoteObject.code,
527
612
  animated: emoteObject.animated,
528
613
  emoticon_set: emoteObject.set,
529
- formats: emoteObject.animated ? { animated: emoteObject.animated } : {}
614
+ formats: emoteObject.animated ? ["static", "animated"] : ["static"]
530
615
  }
531
616
  );
532
617
  }
@@ -698,18 +783,30 @@ var EmoteFetcher = class {
698
783
  * Gets the raw 7TV emotes data for a channel.
699
784
  * @private
700
785
  * @param {number} [id] - ID of the channel.
786
+ * @param {('webp'|'avif')} [format] - The type file format to use (webp/avif).
701
787
  * @returns {Promise<object[]>} - A promise that resolves to an array of raw 7TV emote data.
702
788
  */
703
- _getRawSevenTVEmotes(id) {
704
- const endpoint = id ? Constants_default.SevenTV.Channel(id) : Constants_default.SevenTV.Global;
705
- return fetch(endpoint).then((response) => response.json());
789
+ _getRawSevenTVEmotes(id, format) {
790
+ return fetch(
791
+ Constants_default.SevenTV.GQL,
792
+ {
793
+ method: "POST",
794
+ headers: {
795
+ "content-type": "application/json"
796
+ },
797
+ body: JSON.stringify({
798
+ query: id ? Constants_default.SevenTV.ChannelQuery : Constants_default.SevenTV.GlobalQuery,
799
+ variables: id ? { id: String(id), format: [format.toUpperCase()] } : { format: [format.toUpperCase()] }
800
+ })
801
+ }
802
+ ).then((response) => response.json());
706
803
  }
707
804
  /**
708
805
  * Converts and caches a raw 7TV emote.
709
806
  * @private
710
807
  * @param {number} channelId - ID of the channel.
711
808
  * @param {object} data - Raw data.
712
- * @param {string} format - The type file format to use (webp/avif).
809
+ * @param {('webp'|'avif')} format - The type file format to use (webp/avif).
713
810
  * @param {SevenTVEmote} [existingEmote] - Existing emote to cache.
714
811
  * @returns {SevenTVEmote} - A SevenTVEmote instance.
715
812
  */
@@ -792,15 +889,10 @@ var EmoteFetcher = class {
792
889
  const {
793
890
  format = "webp"
794
891
  } = 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
- }
892
+ return this._getRawSevenTVEmotes(channel, format).then((rawEmotes) => {
893
+ const emoteItems = channel ? rawEmotes?.data?.userByConnection?.emote_sets?.find((set) => set.flags === 0)?.emotes : rawEmotes?.data?.namedEmoteSet?.emotes;
894
+ for (const data of Array.isArray(emoteItems) ? emoteItems : []) {
895
+ this._cacheSevenTVEmote(channel, data, format);
804
896
  }
805
897
  return this.channels.get(channel).emotes.filter((e) => e.type === "7tv");
806
898
  });
@@ -914,7 +1006,7 @@ var EmoteParser = class {
914
1006
  if (emote.modifier) return "";
915
1007
  const template = this.options.template || Constants_default.Templates[this.options.type];
916
1008
  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");
1009
+ const res = template.replaceAll("{link}", link).replaceAll("{name}", emote.code).replaceAll("{size}", size).replaceAll("{creator}", emote.ownerName || "global").replaceAll("{is-animated}", emote.animated ? "true" : "false").replaceAll("{is-zero-width}", emote.zeroWidth ? "true" : "false").replaceAll("{is-nsfw}", emote.nsfw ? "true" : "false");
918
1010
  return res;
919
1011
  });
920
1012
  return parsed;