twitch-emoticons
Gets Twitch, BTTV, FFZ and 7TV emotes as well as parsing text to emotes!
[!IMPORTANT]
This branch is for the currently pre-released 3.0 version.
If you want to see the latest stable version (as of this commit),
switch to the version/2.9 branch.
Migrate to 3.x
This release introduces breaking changes!
Click here to toggle for details
List of breaking changes from 2.x to 3.x
- Node.js 20 is required; we have set the minimum to 20.18.1.
This library is now an ECMAScript module, so you can use proper import {...} from '...' imports.
- The initialization of
EmoteFetcher changed to only use an object as the first parameter for options.
- API keys for Twitch must now be set with
twitchAppID and twitchAppSecret properties.
- The previously available
apiClient is now set in this object too.
- The defaults for
EmoteParser changed to use the html template, and it does not require :colons: by default (using /([^\s]+)/g to match non-whitespaces).
- The default
html template does not have twitch-emote-{size} anymore in its class attribute.
The size is inconsistent between the different sources, so it cannot be reliably used.
- The
EmoteParser now has an allowNSFW option, set to false by default.
This prevents rendering “NSFW” emotes using EmoteParser.parse(), which is a flag currently only available to emotes from 7TV when they are flagged “Sexual”/“Twitch disallowed” or they are unlisted.
- If you somehow used
EmoteFetcher.globalChannel, it has now been removed.
Directly use EmoteFetcher.channels.get(null) instead.
- The
EmoteFetcher.fetchSevenTVEmotes(), Emote.toLink(), and EmoteParser.parse() methods now have their options as an object.
fetcher.fetchSevenTVEmotes(null, { format: 'avif' }) - The first parameter is still the Twitch user ID (or null for global).
emote.toLink({ size: 1, forceStatic: true, themeMode: 'light' })
parser.parse('Kappa', { size: 2, forceStatic: true, themeMode: 'dark' }) - The first parameter is still the input text.
- The
owner getter for Emotes has been removed.
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).
- If you have exported 7TV emotes, do note that the
sizes array changed to not include the leading x.<format>.
The rest of the export/import is still compatible with the current implementation.
Example of code changes
Our examples are running in an ESM-based project ("type": "module").
Do note that we export a CommonJS-compatible build, so you can still use require(...):
const { EmoteFetcher, EmoteParser } = require('@mkody/twitch-emoticons')
Before (2.x)
import TwitchEmoticons from '@mkody/twitch-emoticons'
const { EmoteFetcher, EmoteParser } = TwitchEmoticons
const fetcher = new EmoteFetcher('<your app ID>', '<your app secret>')
await fetcher.fetchTwitchEmotes()
const parser = new EmoteParser(fetcher)
const emote = emoteFetcher.emotes.get('Kappa')
console.log(emote.toLink(2))
console.log(parser.parse('Hello :CoolCat:!'))
After (3.x)
import { EmoteFetcher, EmoteParser } from '@mkody/twitch-emoticons'
const fetcher = new EmoteFetcher({
twitchAppID: '<your app ID>',
twitchAppSecret: '<your app secret>',
})
await fetcher.fetchTwitchEmotes()
const parser = new EmoteParser(fetcher)
const emote = emoteFetcher.emotes.get('Kappa')
console.log(emote.toLink({ size: 2 }))
console.log(parser.parse('Hello CoolCat!'))
Prerequisites
To fetch “native” emotes from Twitch.tv, you need to create an app here, it is free.
If you are only using BetterTTV, FrankerFaceZ and 7TV you do not need to provide Twitch app keys.
You must use a Twitch user ID instead of the username to fetch users’ emotes.
If you just need to get one or a very few, you can use this page to manually convert them (please do not automate/abuse it).
Install
From npm (browse on npmx; use the @next tag):
npm install @mkody/twitch-emoticons@next
# or
pnpm add @mkody/twitch-emoticons@next
# or
yarn add @mkody/twitch-emoticons@next
# or
deno add npm:@mkody/twitch-emoticons@next
From jsr:
npx jsr add @mkody/twitch-emoticons
# or
pnpm add jsr:@mkody/twitch-emoticons
# or
yarn add jsr:@mkody/twitch-emoticons
# or (version has to be specified while it is a pre-release)
deno add jsr:@mkody/[email protected]
Client-side usage
While not recommended (please do not expose your Twitch secrets!), you can use this library directly in the browser.
CJS:
<script src="./dist/TwitchEmoticons.min.js"></script>
<script>
const fetcher = new TwitchEmoticons.EmoteFetcher()
const parser = new TwitchEmoticons.EmoteParser(fetcher)
</script>
ESM:
<script type="module">
import { EmoteFetcher, EmoteParser } from './dist/TwitchEmoticons.esm.min.js'
const fetcher = new EmoteFetcher()
const parser = new EmoteParser(fetcher)
</script>
Demos: https://s.kdy.ch/twitch-emoticons/
Quick docs
Click here to toggle the docs
Here is some quick documentation to explain our two classes, the principal methods, and some settings.
NOTE:
If you want a more complete documentation, see: https://mkody.github.io/twitch-emoticons/
Grab emotes with EmoteFetcher
First, you need to load a list of emotes, and for that you create a new EmoteFetcher object:
const fetcher = new EmoteFetcher({
twitchAppID,
twitchAppSecret,
apiClient,
forceStatic,
twitchThemeMode,
})
And then you need to make the calls to load what you want.
There is one method per platform; all of them return Promises (so you can use await or callback chains):
fetcher.fetchTwitchEmotes()
fetcher.fetchBTTVEmotes()
fetcher.fetchFFZEmotes()
fetcher.fetchSevenTVEmotes()
The first parameter is the Twitch user ID of the channel you want to load emotes from.
If not provided or it is null/falsy, it loads what we call “global emotes”, which are available to all users of the platform.
Do note that fetchSevenTVEmotes() accepts a second parameter with an object:
format: Set the image type used, either webp or avif. Default: webp
So if you want to load all global emotes, you can do it that way:
await fetcher.fetchTwitchEmotes()
await fetcher.fetchBTTVEmotes()
await fetcher.fetchFFZEmotes()
await fetcher.fetchSevenTVEmotes(null, { format: 'avif' })
With the emotes now fetched, you can look them up!
To grab a specific emote, you can do fetcher.emotes.get('...'), with the case-sensitive name of the emote in place of the ellipsis.
From there, you can read properties like .id, .code, .ownerName,
.animated, .imageType, or .type, but you can also use the .toLink()
method to… get a link!
Note: Some extended Emotes have additional properties:
- FFZ:
.zeroWidth (boolean, can overlay an another emote)
.modifier (boolean, emote effects, should be hidden)
- 7TV:
.zeroWidth (boolean, can overlay an another emote)
.nsfw (boolean, flagged “Sexual”/“Twitch disallowed” or unlisted)
Parse strings to include emotes with EmoteParser
And now that we have our list of emotes that we can expect to find, we should use it.
Create an EmoteParser object:
const parser = new EmoteParser(
fetcher,
{
allowNSFW,
type,
template,
match,
},
)
And now, we can parse text and get the output as set by type or template, using the parser.parse() method.
Just pass the input text as the first parameter. There is also an optional second parameter
where you can provide a few settings (which can overwrite the same ones set in the EmoteFetcher).
const parsed = parser.parse(
text,
{
size,
forceStatic,
twitchThemeMode
},
)
And that is it for the essentials!
You can go through the examples below if you need to see more complete code and direct usage.
Examples
Click here to toggle the examples
1. Basic Twitch emote parsing
import { EmoteFetcher, EmoteParser } from '@mkody/twitch-emoticons'
const fetcher = new EmoteFetcher({
twitchAppID: '<your app ID>',
twitchAppSecret: '<your app secret>',
})
const parser = new EmoteParser(fetcher, {
type: 'markdown',
match: /:(.+?):/g,
})
await fetcher.fetchTwitchEmotes(null)
const kappa = fetcher.emotes.get('Kappa').toLink()
console.log(kappa)
const text = 'Hello :CoolCat:!'
const parsed = parser.parse(text)
console.log(parsed)
2. Bring your own @twurple/api
If you already use Twurple in your project and manage authentication
(i.e., with user tokens), you can reuse it in this project by setting apiClient with your
ApiClient object.
import { ApiClient } from '@twurple/api'
import { StaticAuthProvider } from '@twurple/auth'
import { EmoteFetcher } from '@mkody/twitch-emoticons'
const authProvider = new StaticAuthProvider('<your app ID>', '<access token>')
const myCustomApiClient = new ApiClient({ authProvider })
const fetcher = new EmoteFetcher({
apiClient: myCustomApiClient,
})
3. All providers, global + channel, custom template and match pattern
import { EmoteFetcher, EmoteParser } from '@mkody/twitch-emoticons'
const channelId = 44317909
const fetcher = new EmoteFetcher({
twitchAppID: '<your app ID>',
twitchAppSecret: '<your app secret>',
forceStatic: false,
twitchThemeMode: 'dark',
})
const parser = new EmoteParser(fetcher, {
allowNSFW: true,
template: `
<img
class="emote"
alt="{name}"
src="{link}"
data-scale="{size}"
data-animated="{is-animated}"
data-overlay="{is-zero-width}"
data-nsfw="{is-nsfw}"
>`,
match: /([a-zA-Z0-9_\-]+)/g,
})
Promise.all([
fetcher.fetchTwitchEmotes(),
fetcher.fetchTwitchEmotes(channelId),
fetcher.fetchBTTVEmotes(),
fetcher.fetchBTTVEmotes(channelId),
fetcher.fetchSevenTVEmotes(),
fetcher.fetchSevenTVEmotes(channelId),
fetcher.fetchFFZEmotes(),
fetcher.fetchFFZEmotes(channelId),
])
.then(() => {
const globalEmotes = parser.parse('EZ Clap way too easy LUL now for the last boss monkaS LaterSooner')
console.log(globalEmotes)
const channelEmotes = parser.parse('KEKW that was 3Head TeriPoint')
console.log(channelEmotes)
})
.catch((err) => {
console.error('Error loading emotes...')
console.error(err)
})
4. Force static images
All providers let you ask to only get static versions of their emotes.
By default, we allow animated emotes to be used,
but you can override this at the EmoteFetcher level:
import { EmoteFetcher } from '@mkody/twitch-emoticons'
const fetcher = new EmoteFetcher({
twitchAppID: '<your app ID>',
twitchAppSecret: '<your app secret>',
forceStatic: true,
})
await fetcher.fetchTwitchEmotes()
const dinoDance = fetcher.emotes.get('DinoDance').toLink()
console.log(dinoDance)
NOTE:
Q: Why set this to the EmoteFetcher and not EmoteParser?
A: Because Emote.toLink() (that you get from the fetcher) uses that info!
It is also possible to force that using the Emote’s toLink() method:
const dinoDance = fetcher.emotes.get('DinoDance').toLink({ forceStatic: true })
console.log(dinoDance)
Or when using the EmoteParser’s parse():
const dinoDance = parser.parse('DinoDance', { forceStatic: true })
console.log(dinoDance)
WARNING:
Forcing static images might make the imageType of the Emote not match with your expectations!
(Twitch: gif => png; BTTV: webp => png.)
5. Twitch “theme mode” preference
Some Twitch emotes are optimized for light or dark backgrounds.
By default, emotes are fetched for dark backgrounds,
but you can specify a preference at the EmoteFetcher level:
import { EmoteFetcher } from '@mkody/twitch-emoticons'
const fetcherDark = new EmoteFetcher({
twitchAppID: '<your app ID>',
twitchAppSecret: '<your app secret>',
twitchThemeMode: 'dark',
})
const fetcherLight = new EmoteFetcher({
twitchAppID: '<your app ID>',
twitchAppSecret: '<your app secret>',
twitchThemeMode: 'light',
})
await fetcherLight.fetchTwitchEmotes()
const kappa = fetcherLight.emotes.get('Kappa').toLink()
console.log(kappa)
NOTE:
Q: Why set this to the EmoteFetcher and not EmoteParser?
A: Because Emote.toLink() (that you get from the fetcher) uses that info!
It is also possible to force that using the Emote’s toLink() method:
const kappa = fetcher.emotes.get('Kappa').toLink({ themeMode: 'light' })
console.log(kappa)
Or when using the EmoteParser’s parse():
const kappa = parser.parse('Kappa', { themeMode: 'light' })
console.log(kappa)
7TV delivers emotes in either WEBP or AVIF.
By default we will return WEBP emotes, but you can override this.
import { EmoteFetcher } from '@mkody/twitch-emoticons'
const fetcher = new EmoteFetcher()
await fetcher.fetchSevenTVEmotes(null, { format: 'avif' })
await fetcher.fetchSevenTVEmotes(44317909)
await fetcher.fetchSevenTVEmotes(44317909, { format: 'webp' })
await fetcher.fetchSevenTVEmotes(129546453, { format: 'avif' })
7. Export and import emote data
This can be useful to save the emotes in a cache or for offline access.
import { EmoteFetcher } from '@mkody/twitch-emoticons'
const fetcher = new EmoteFetcher()
await fetcher.fetchSevenTVEmotes(null, { format: 'avif' })
const emotes = fetcher.emotes.map((emote) => emote.toObject())
fetcher.fromObject(emotes)
NOTE:
For offline access, you will still need to download emotes and proxy their URLs.
Links
This library uses the following:
评论