UNPKG

twitch-toolkit

Version:

A set of tools to integrate with Twitch API, Twitch Chat and Twitch WebHooks.

352 lines (308 loc) 18 kB
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width,initial-scale=1"> <title>twitchChatEmitter.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="https://code.ionicframework.com/ionicons/2.0.1/css/ionicons.min.css"> <link type="text/css" rel="stylesheet" href="styles/prettify-tomorrow.css"> <link type="text/css" rel="stylesheet" href="styles/jsdoc-default.css"> </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> <li class="nav-link nav-home-link"><a href="index.html">Home</a></li><li class="nav-heading">Classes</li><li class="nav-heading"><span class="nav-item-type type-class">C</span><span class="nav-item-name"><a href="TwitchApi.html">TwitchApi</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="TwitchApi.html#getAccessToken">getAccessToken</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="TwitchApi.html#getGames">getGames</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="TwitchApi.html#getStreams">getStreams</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="TwitchApi.html#getStreamsMetadata">getStreamsMetadata</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="TwitchApi.html#getUsers">getUsers</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="TwitchApi.html#getUsersFollows">getUsersFollows</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="TwitchApi.html#getVideos">getVideos</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="TwitchApi.html#isLive">isLive</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="TwitchApi.html#updateUser">updateUser</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="TwitchApi.html#validateAccessToken">validateAccessToken</a></span></li><li class="nav-heading"><span class="nav-item-type type-class">C</span><span class="nav-item-name"><a href="TwitchChatEmitter.html">TwitchChatEmitter</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="TwitchChatEmitter.html#connect">connect</a></span></li><li class="nav-heading"><span class="nav-item-type type-class">C</span><span class="nav-item-name"><a href="TwitchWebhook.html">TwitchWebhook</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="TwitchWebhook.html#destroy">destroy</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="TwitchWebhook.html#handleRequest">handleRequest</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="TwitchWebhook.html#subscribe">subscribe</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="TwitchWebhook.html#topicStreamUpDownSubscribe">topicStreamUpDownSubscribe</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="TwitchWebhook.html#topicUserFollowsSubscribe">topicUserFollowsSubscribe</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="TwitchWebhook.html#unsubscribe">unsubscribe</a></span></li><li class="nav-heading">Events</li><li class="nav-heading"><span class="nav-item-type type-event">E</span><span class="nav-item-name"><a href="TwitchChatEmitter.html#event:Chat:chat_parsed">Chat:chat_parsed</a></span></li><li class="nav-heading"><span class="nav-item-type type-event">E</span><span class="nav-item-name"><a href="TwitchWebhook.html#event:Webhook:stream_up_down">Webhook:stream_up_down</a></span></li><li class="nav-heading"><span class="nav-item-type type-event">E</span><span class="nav-item-name"><a href="TwitchWebhook.html#event:Webhook:user_follows">Webhook:user_follows</a></span></li> </nav> <div id="main"> <h1 class="page-title">twitchChatEmitter.js</h1> <section> <article> <pre class="prettyprint source linenums"><code>'use strict'; const tmi = require('tmi.js'); const util = require('util'); const request = require('request-promise'); /** * @class TwitchChatEmitter * The Twitch chat implementation, based on the already existing module tmi.js (tmijs.org), adding more features and handlers to chat interation. * * @param {object} config The config object * @param {boolean} config.debug Debug mode. If true, will show the debug messages. * @param {object} config.logger The logger instance. If empty, the default logger will be used. * @param {string[]} config.channels The twitch channels that will be used. This is required. * @param {string} config.username The chat bot username. This is required. * @param {string} config.password The chat bot OAUTH token password. You can get it at http://twitchapps.com/tmi/ . This is required. * @param {boolean} config.reconnect Reconnect to Twitch when disconnected from server. Default: false * @param {number} config.maxReconnectAttempts Max number of reconnection attempts (Default: Infinity) * @param {number} config.maxReconnectInterval Max number of ms to delay a reconnection (Default: 30000) * @param {number} config.reconnectDecay The rate of increase of the reconnect delay (Default: 1.5) * @param {number} config.reconnectInterval Number of ms before attempting to reconnect (Default: 1000) * @param {boolean} config.secure Use secure connection (SSL / HTTPS) (Overrides port to 443) * @param {number} config.timeout Number of ms to disconnect if no responses from server (Default: 9999) * @param {string} config.commandPrefix The prefix caracter for chat command. Default is "!". * * @param {object[]} config.triggers The triggers object array. * @param {string} config.triggers[].name The name of the trigger. This is required. * @param {string} config.triggers[].type The type of the trigger. Can be one 'word' or 'command'. This is required * @param {string} config.channel The channel in which the trigger will be used. This is required. * @param {boolean} config.triggers[].chatTrigger Define if its a chat trigger. (Default: true) * @param {boolean} config.triggers[].whisperTrigger Define if its a whisper trigger. (Default: false) * @param {number} config.triggers[].minDelay The delay before the action is triggered again. (Default: 0) * @param {string} config.triggers[].eventName The name of the event that will be triggered. If this is empty, no event will be emitted. * @param {string} config.triggers[].responseText The text that will be sent to chat/whisper if the action is triggered. If this is empty, nothing will be sent. * * @param {object[]} config.timedMessages The timed messages array. * @param {string} config.timedMessages[].message The message to be sent. * @param {string} config.channel The channel in which the timed message will be sent. This is required. * @param {number} config.timedMessages[].minDelay The minimum delay, in seconds, between this kind of message. This is required. * @param {number} config.timedMessages[].minChatMessages The minimum ammount of messages in chat to send this message. * */ function TwitchChatEmitter(config) { if (!config.channels) throw new Error('Missing channels value.'); if (!config.username) throw new Error('Missing username value.'); if (!config.password) throw new Error('Missing password value.'); let options = { options: { debug: config.debug || (process.env.LOG_LEVEL &amp;&amp; process.env.LOG_LEVEL === 'debug'), ignoreSelf: config.ignoreSelf }, connection: { reconnect: config.reconnect, maxReconnectAttempts: config.maxReconnectAttempts, maxReconnectInterval: config.maxReconnectInterval, reconnectDecay: config.reconnectDecay, reconnectInterval: config.reconnectInterval, secure: config.secure, timeout: config.timeout }, identity: { username: config.username, password: config.password }, channels: config.channels, logger: config.logger, commandPrefix: config.commandPrefix || '!', timedMessages: config.timedMessages }; //Create the local variables this.chatMessageCount = 0; this.triggersMap = new Map(); //Create the triggers maps if (config.triggers) { for (let i in config.triggers) { let trigger = config.triggers[i]; let name = trigger.type === 'command' ? options.commandPrefix + trigger.name : trigger.name; let triggerObj = {}; if (this.triggersMap.has(name)) { triggerObj = this.triggersMap.get(name); } else { triggerObj.items = []; triggerObj.lastDate = null; } triggerObj.items.push(trigger); this.triggersMap.set(name, triggerObj); } } tmi.Client.call(this, options); if (config.logger) { this.log = config.logger; } this.on('chat', (channel, userstate, message, self) => { _handleMessage(this, 'chat', channel, userstate, message, self); }); this.on('whisper', (from, userstate, message, self) => { _handleMessage(this, 'whisper', null, userstate, message, self); }); this.chatEmotes = null; } /** * Connect to the chat. */ TwitchChatEmitter.prototype.connect = async function() { try { let emotes = await request({ url: 'https://twitchemotes.com/api_cache/v3/global.json', method: 'GET' }); this.chatEmotes = JSON.parse(emotes); let result = await tmi.Client.prototype.connect.call(this); //Prepare the timed messages if (this.getOptions().timedMessages) { this.getOptions().timedMessagesTimerIds = []; for (let i in this.getOptions().timedMessages) { let message = this.getOptions().timedMessages[i]; if (!message.minDelay) { throw new Error( 'Missing minimum delay constraint for timed message.' ); } if (!message.channel) { throw new Error( 'Missing the channel for the timed message.' ); } let timedMessage = { message: message.message, channel: message.channel, minDelay: message.minDelay, minChatMessages: message.minChatMessages, messageCount: 0, //Number of messages sent before the last time it was sent. lastTrigger: null }; if (message.minDelay) { let timerId = setInterval(() => { _triggerTimedMessage(this, timedMessage); }, message.minDelay * 1000); this.getOptions().timedMessagesTimerIds.push(timerId); } } } return result; } catch (err) { throw err; } }; TwitchChatEmitter.prototype.disconnect = async function() { if (this.getOptions().timedMessagesTimerIds) { for (let i in this.getOptions().timedMessagesTimerIds) { clearInterval(this.getOptions().timedMessagesTimerIds[i]); } } return tmi.Client.prototype.disconnect.call(this); }; /** * Handle a chat message, firing the specific events. * @private * @param {string} channel The channel in which the message was sent. * @param {object} userstate The userstate object as described in the tmi 'chat 'event. * @param {string} message The chat message. * @param {bool} self Whether is the bot user or not. */ async function _handleMessage(chat, type, channel, userstate, message, self) { message = message.trim(); let finalMessage = message; try { if (!self || process.env.NODE_ENV === 'test') { let words = _getMessageWords( message, chat.getOptions().commandPrefix ); for (let i in words) { if (chat.triggersMap.has(words[i])) { let triggers = chat.triggersMap.get(words[i]); for (let j in triggers.items) { let trigger = triggers.items[j]; if ( !trigger.minDelay || !triggers.lastDate || new Date().getTime() - trigger.minDelay > triggers.lastDate.getTime() ) { if (trigger.eventName) { chat.emit( trigger.eventName, channel, userstate, message, self ); } if (trigger.responseText) { if (type === 'chat') { await chat.say( channel, trigger.responseText ); } else if (type === 'whisper') { await chat.whisper( userstate.username, trigger.responseText ); } } } } triggers.lastDate = new Date(); } if (chat.chatEmotes[words[i]]) { let emoteHtml = '&lt;img class="chat-image" src="' + encodeURI( 'https://static-cdn.jtvnw.net/emoticons/v1/' + chat.chatEmotes[words[i]].id + '/1.0' ) + '">'; finalMessage = finalMessage.replace(words[i], emoteHtml); } } } /** * The chat message parsed to html, with twitch emotes. * @event TwitchChatEmitter#Chat:chat_parsed * @param {string} channel The channel in which the command was sent. * @param {object} userstate The userstate object. * @param {string} message The parsed message. * @param {bool} self Whether the command was sent to the user bot or not. */ if (type === 'chat') { chat.messageCount++; chat.emit('chat_parsed', channel, userstate, finalMessage, self); } } catch (err) { chat.log.error(err); } } function _getMessageWords(message, commandPrefix) { let reg = new RegExp( '[.,!?@#$%¨&amp;*(){}\\[\\]|\\/:;&lt;>]'.replace(commandPrefix, ''), 'g' ); return message.replace(reg, '').split(' '); } function _triggerTimedMessage(chat, timedMessage) { if ( !timedMessage.minChatMessages || chat.chatMessageCount >= timedMessage.minChatMessages * (timedMessage.messageCount + 1) ) { timedMessage.messageCount++; timedMessage.lastTrigger = new Date(); chat.say(timedMessage.channel, timedMessage.message); } } util.inherits(TwitchChatEmitter, tmi.Client); /* test code */ if (process.env.NODE_ENV === 'test') { TwitchChatEmitter.prototype._handleMessage = _handleMessage; TwitchChatEmitter.prototype._getMessageWords = _getMessageWords; TwitchChatEmitter.prototype._triggerTimedMessage = _triggerTimedMessage; } /* end-test code */ module.exports = TwitchChatEmitter; </code></pre> </article> </section> </div> <br class="clear"> <footer> Generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 3.5.5</a> on Fri Aug 17 2018 18:58:48 GMT-0300 (E. South America Standard Time) using the Minami theme. </footer> <script>prettyPrint();</script> <script src="scripts/linenumber.js"></script> </body> </html>