UNPKG

@mkody/twitch-emoticons

Version:

Gets Twitch, BTTV, FFZ and 7TV emotes as well as parsing text to emotes!

430 lines (360 loc) 16.9 kB
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <title>struct/EmoteFetcher.js - Documentation</title> <script src="scripts/prettify/prettify.js"></script> <script src="scripts/prettify/lang-css.js"></script> <!--[if lt IE 9]> <script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script> <![endif]--> <link type="text/css" rel="stylesheet" href="styles/prettify.css"> <link type="text/css" rel="stylesheet" href="styles/jsdoc.css"> <script src="scripts/nav.js" defer></script> <meta name="viewport" content="width=device-width, initial-scale=1.0"> </head> <body> <input type="checkbox" id="nav-trigger" class="nav-trigger" /> <label for="nav-trigger" class="navicon-button x"> <div class="navicon"></div> </label> <label for="nav-trigger" class="overlay"></label> <nav > <h2><a href="index.html">Home</a></h2><h2><a href="https://github.com/mkody/twitch-emoticons" target="_blank" class="menu-item" id="github_link" >GitHub</a></h2><h3>Classes</h3><ul><li><a href="BTTVEmote.html">BTTVEmote</a><ul class='methods'><li data-type='method'><a href="BTTVEmote.html#toLink">toLink</a></li><li data-type='method'><a href="BTTVEmote.html#toObject">toObject</a></li><li data-type='method'><a href="BTTVEmote.html#.fromObject">fromObject</a></li></ul></li><li><a href="Channel.html">Channel</a><ul class='methods'><li data-type='method'><a href="Channel.html#fetchBTTVEmotes">fetchBTTVEmotes</a></li><li data-type='method'><a href="Channel.html#fetchFFZEmotes">fetchFFZEmotes</a></li><li data-type='method'><a href="Channel.html#fetchSevenTVEmotes">fetchSevenTVEmotes</a></li></ul></li><li><a href="Emote.html">Emote</a><ul class='methods'><li data-type='method'><a href="Emote.html#toObject">toObject</a></li></ul></li><li><a href="EmoteFetcher.html">EmoteFetcher</a><ul class='methods'><li data-type='method'><a href="EmoteFetcher.html#fetchTwitchEmotes">fetchTwitchEmotes</a></li><li data-type='method'><a href="EmoteFetcher.html#fetchBTTVEmotes">fetchBTTVEmotes</a></li><li data-type='method'><a href="EmoteFetcher.html#fetchFFZEmotes">fetchFFZEmotes</a></li><li data-type='method'><a href="EmoteFetcher.html#fetchSevenTVEmotes">fetchSevenTVEmotes</a></li><li data-type='method'><a href="EmoteFetcher.html#fromObject">fromObject</a></li></ul></li><li><a href="EmoteParser.html">EmoteParser</a><ul class='methods'><li data-type='method'><a href="EmoteParser.html#parse">parse</a></li></ul></li><li><a href="FFZEmote.html">FFZEmote</a><ul class='methods'><li data-type='method'><a href="FFZEmote.html#toLink">toLink</a></li><li data-type='method'><a href="FFZEmote.html#toObject">toObject</a></li><li data-type='method'><a href="FFZEmote.html#.fromObject">fromObject</a></li></ul></li><li><a href="SevenTVEmote.html">SevenTVEmote</a><ul class='methods'><li data-type='method'><a href="SevenTVEmote.html#toLink">toLink</a></li><li data-type='method'><a href="SevenTVEmote.html#toObject">toObject</a></li><li data-type='method'><a href="SevenTVEmote.html#.fromObject">fromObject</a></li></ul></li><li><a href="TwitchEmote.html">TwitchEmote</a><ul class='methods'><li data-type='method'><a href="TwitchEmote.html#toLink">toLink</a></li><li data-type='method'><a href="TwitchEmote.html#toObject">toObject</a></li><li data-type='method'><a href="TwitchEmote.html#.fromObject">fromObject</a></li></ul></li><li><a href="Collection.html">Collection</a><ul class='methods'><li data-type='method'><a href="Collection.html#find">find</a></li><li data-type='method'><a href="Collection.html#filter">filter</a></li><li data-type='method'><a href="Collection.html#map">map</a></li></ul></li></ul> </nav> <div id="main"> <h1 class="page-title">struct/EmoteFetcher.js</h1> <section> <article> <pre class="prettyprint source linenums"><code>const BTTVEmote = require('./BTTVEmote'); const Channel = require('./Channel'); const Collection = require('../util/Collection'); const Constants = require('../util/Constants'); const FFZEmote = require('./FFZEmote'); const SevenTVEmote = require('./SevenTVEmote'); const TwitchEmote = require('./TwitchEmote'); const axios = require('axios'); const { ApiClient } = require('@twurple/api'); const { AppTokenAuthProvider } = require('@twurple/auth'); class EmoteFetcher { /** * Fetches and caches emotes. * @param {string} clientId The client id for the twitch api. * @param {string} clientSecret The client secret for the twitch api. * @param {object} options Additional options. * @param {ApiClient} options.apiClient - Bring your own Twurple ApiClient. */ constructor(clientId, clientSecret, options) { if (options &amp;&amp; options.apiClient) { this.apiClient = options.apiClient; } else if (clientId !== undefined &amp;&amp; clientSecret !== undefined) { const authProvider = new AppTokenAuthProvider(clientId, clientSecret); /** * Twitch api client. */ this.apiClient = new ApiClient({ authProvider }); } /** * Cached emotes. * Collectionped by emote code to Emote instance. * @type {Collection&lt;string, Emote>} */ this.emotes = new Collection(); /** * Cached channels. * Collectionped by name to Channel instance. * @type {Collection&lt;string, Channel>} */ this.channels = new Collection(); /** * Save if we fetched FFZ's modifier emotes once. * @type {boolean} */ this.ffzModifiersFetched = false; } /** * The global channel for Twitch, BTTV and 7TV. * @readonly * @type {?Channel} */ get globalChannel() { return this.channels.get(null); } /** * Sets up a channel * @private * @param {int} channel_id - ID of the channel. * @param {string} [format=null] - The type file format to use (webp/avif). * @throws {Error} When Twitch Client ID or Client Secret were not provided. * @returns {Channel} */ _setupChannel(channel_id, format = null) { let channel = this.channels.get(channel_id); if (!channel) { channel = new Channel(this, channel_id); this.channels.set(channel_id, channel); } if (format) channel.format = format; return channel; } /** * Gets the raw Twitch emotes data for a channel. * @private * @param {int} id - ID of the channel. * @returns {Promise&lt;object[]>} */ _getRawTwitchEmotes(id) { if (!this.apiClient) { throw new Error('Client id or client secret not provided.'); } if (id) { return this.apiClient.chat.getChannelEmotes(id); } else { return this.apiClient.chat.getGlobalEmotes(); } } /** * Converts and caches a raw twitch emote. * @private * @param {int} channel_id - ID of the channel. * @param {object} data - Raw data. * @param {TwitchEmote} [existing_emote=null] - Existing emote to cache. * @returns {TwitchEmote} */ _cacheTwitchEmote(channel_id, data, existing_emote = null) { const channel = this._setupChannel(channel_id); const emote = existing_emote || new TwitchEmote(channel, data.id, data); this.emotes.set(emote.code, emote); channel.emotes.set(emote.code, emote); return emote; } /** * Gets the raw BTTV emotes data for a channel. * Use `null` for the global emotes channel. * @private * @param {int} [id=null] - ID of the channel. * @returns {Promise&lt;object[]>} */ _getRawBTTVEmotes(id) { const endpoint = !id ? Constants.BTTV.Global : Constants.BTTV.Channel(id); // eslint-disable-line new-cap return axios.get(endpoint).then(req => { // Global emotes if (req.data instanceof Array) return req.data; // Channel emotes return [...req.data.channelEmotes, ...req.data.sharedEmotes]; }); } /** * Converts and caches a raw BTTV emote. * @private * @param {int} channel_id - ID of the channel. * @param {object} data - Raw data. * @param {BTTVEmote} [existing_emote=null] - Existing emote to cache. * @returns {BTTVEmote} */ _cacheBTTVEmote(channel_id, data, existing_emote = null) { const channel = this._setupChannel(channel_id); const emote = existing_emote || new BTTVEmote(channel, data.id, data); this.emotes.set(emote.code, emote); channel.emotes.set(emote.code, emote); return emote; } /** * Gets the raw FFZ emote data from a set. * @private * @param {int} id - ID of the set. * @returns {Promise&lt;object[]>} */ _getRawFFZEmoteSet(id) { const endpoint = Constants.FFZ.Set(id); // eslint-disable-line new-cap return axios.get(endpoint).then(req => { return req.data.set.emoticons; }); } /** * Gets the raw FFZ emotes data for a channel. * @private * @param {int} id - ID of the channel. * @returns {Promise&lt;object[]>} */ _getRawFFZEmotes(id) { const endpoint = Constants.FFZ.Channel(id); // eslint-disable-line new-cap return axios.get(endpoint).then(req => { const emotes = []; for (const key of Object.keys(req.data.sets)) { const set = req.data.sets[key]; emotes.push(...set.emoticons); } return emotes; }); } /** * Converts and caches a raw FFZ emote. * @private * @param {int} channel_id - ID of the channel. * @param {object} data - Raw data. * @param {FFZEmote} [existing_emote=null] - Existing emote to cache. * @returns {FFZEmote} */ _cacheFFZEmote(channel_id, data, existing_emote = null) { const channel = this._setupChannel(channel_id); const emote = existing_emote || new FFZEmote(channel, data.id, data); this.emotes.set(emote.code, emote); channel.emotes.set(emote.code, emote); return emote; } /** * Gets the raw 7TV emotes data for a channel. * @private * @param {int} [id=null] - ID of the channel. * @returns {Promise&lt;object[]>} */ _getRawSevenTVEmotes(id) { const endpoint = !id ? Constants.SevenTV.Global : Constants.SevenTV.Channel(id); // eslint-disable-line new-cap return axios.get(endpoint).then(req => req.data); } /** * Converts and caches a raw 7TV emote. * @private * @param {int} channel_id - ID of the channel. * @param {object} data - Raw data. * @param {string} format - The type file format to use (webp/avif). * @param {SevenTVEmote} [existing_emote=null] - Existing emote to cache. * @returns {SevenTVEmote} */ _cacheSevenTVEmote(channel_id, data, format, existing_emote = null) { const channel = this._setupChannel(channel_id, format); const emote = existing_emote || new SevenTVEmote(channel, data.id, data); this.emotes.set(emote.code, emote); channel.emotes.set(emote.code, emote); return emote; } /** * Fetches the Twitch emotes for a channel. * Use `null` for the global emotes channel. * @param {int} [channel=null] - ID of the channel. * @returns {Promise&lt;Collection&lt;string, TwitchEmote>>} */ fetchTwitchEmotes(channel = null) { return this._getRawTwitchEmotes(channel).then(rawEmotes => { for (const emote of rawEmotes) { this._cacheTwitchEmote(channel, { code: emote.name, id: emote.id, formats: emote.formats }); } return this.channels.get(channel).emotes.filter(e => e.type === 'twitch'); }); } /** * Fetches the BTTV emotes for a channel. * Use `null` for the global emotes channel. * @param {int} [channel=null] - ID of the channel. * @returns {Promise&lt;Collection&lt;string, BTTVEmote>>} */ fetchBTTVEmotes(channel = null) { return this._getRawBTTVEmotes(channel).then(rawEmotes => { for (const data of rawEmotes) { this._cacheBTTVEmote(channel, data); } return this.channels.get(channel).emotes.filter(e => e.type === 'bttv'); }); } /** * Fetches the FFZ emotes for a channel. * @param {int} [channel=null] - ID of the channel. * @returns {Promise&lt;Collection&lt;string, FFZEmote>>} */ async fetchFFZEmotes(channel = null) { // Fetch modifier emotes at least once if (!this.ffzModifiersFetched) { this.ffzModifiersFetched = true; await this._getRawFFZEmoteSet(Constants.FFZ.sets.Modifiers).then(rawEmotes => { for (const data of rawEmotes) { this._cacheFFZEmote(null, data); } }); } // If no channel specified, fetch the Global set if (!channel) { return this._getRawFFZEmoteSet(Constants.FFZ.sets.Global).then(rawEmotes => { for (const data of rawEmotes) { this._cacheFFZEmote(channel, data); } return this.channels.get(channel).emotes.filter(e => e.type === 'ffz'); }); } return this._getRawFFZEmotes(channel).then(rawEmotes => { for (const data of rawEmotes) { this._cacheFFZEmote(channel, data); } return this.channels.get(channel).emotes.filter(e => e.type === 'ffz'); }); } /** * Fetches the 7TV emotes for a channel. * @param {int} [channel=null] - ID of the channel. * @param {('webp'|'avif')} [format='webp'] - The type file format to use (webp/avif). * @returns {Promise&lt;Collection&lt;string, SevenTVEmote>>} */ fetchSevenTVEmotes(channel = null, format = 'webp') { return this._getRawSevenTVEmotes(channel).then(rawEmotes => { if ('emotes' in rawEmotes) { // From an emote set (like "global") for (const data of rawEmotes.emotes) { this._cacheSevenTVEmote(channel, data, format); } } else { // From users for (const data of rawEmotes.emote_set.emotes) { this._cacheSevenTVEmote(channel, data, format); } } return this.channels.get(channel).emotes.filter(e => e.type === '7tv'); }); } /** * Converts emote Objects to emotes * @param {object} [emotesArray] - An array of emote objects * @throws {TypeError} When an emote has an unknown type. * @returns {Emote[]} */ fromObject(emotesArray) { const emotes = []; const classMap = { bttv: { class: BTTVEmote, cache: (emoteObject, channel_id, existing_emote) => this._cacheBTTVEmote(channel_id, null, existing_emote) }, ffz: { class: FFZEmote, cache: (emoteObject, channel_id, existing_emote) => this._cacheFFZEmote(channel_id, null, existing_emote) }, '7tv': { class: SevenTVEmote, cache: (emoteObject, channel_id, existing_emote) => this._cacheSevenTVEmote(channel_id, null, emoteObject.imageType, existing_emote) }, twitch: { class: TwitchEmote, cache: (emoteObject, channel_id, existing_emote) => this._cacheTwitchEmote(channel_id, null, existing_emote) } }; for (const emoteObject of emotesArray) { const { type } = emoteObject; if (!Object.keys(classMap).includes(type)) { throw new TypeError(`Unknown type: ${type}`); } const emoteClass = classMap[type].class; this._setupChannel(emoteObject.channel_id, type === '7tv' ? emoteObject.imageType : null); const emote = emoteClass.fromObject(emoteObject, this.channels.get(emoteObject.channel_id)); classMap[type].cache(emoteObject, emoteObject.channel_id, emote); emotes.push(emote); } return emotes; } } module.exports = EmoteFetcher; </code></pre> </article> </section> </div> <br class="clear"> <footer> Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 4.0.5</a> using the <a href="https://github.com/clenemt/docdash">docdash</a> theme. </footer> <script>prettyPrint();</script> <script src="scripts/polyfill.js"></script> <script src="scripts/linenumber.js"></script> </body> </html>