UNPKG

twitch-toolkit

Version:

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

205 lines (190 loc) 8.13 kB
'use strict'; const tmi = require('tmi.js'); const util = require('util'); const helpers = require('./helpers'); 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} options * @param {object} logger The logger object. */ function TwitchChatEmitter(options, logger) { options.wordTriggers = options.wordTriggers || {}; options.wordTriggers.basic = options.wordTriggers.basic || {}; options.wordTriggers.events = options.wordTriggers.events || []; options.chatCommands = options.chatCommands || {}; options.chatCommands.basic = options.chatCommands.basic || {}; options.chatCommands.events = options.chatCommands.events || []; tmi.Client.call(this, options); if (logger) { this.log = logger; } this.on('chat', _handleChatMessage.bind(this)); this.on('whisper', _handleWhisperMessage.bind(this)); 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); return tmi.Client.prototype.connect.call(this); } catch (err) { throw err; } }; /** * 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. */ function _handleChatMessage(channel, userstate, message, self) { if ( !this.getOptions().options.ignoreSelf || (!self && userstate.username.toLowerCase() != this.getOptions().username.toLowerCase()) ) { message = message.trim(); let finalMessage = message; //Check if its a command or message by looking the prefix if ( this.getOptions().chatCommands && message.indexOf(this.getOptions().chatCommands.prefix) === 0 ) { let command = helpers.getFirstWord(message.substring(1)); //Check if its a basic command or a event command if (this.getOptions().chatCommands.basic[command]) { this.say( channel, _replaceTextVars( this.getOptions().chatCommands.basic[command], '@' + userstate.username ) ); } else { /** * The chat command event, triggered when the specified command is sent. This event is dynamic and the name will change according to the options passed to the toolkit. * @event TwitchChatEmitter#Chat:chat_cmd_COMMAND * @param {string} channel The channel in which the command was sent. * @param {string} username The name of the user who sent the command. * @param {string} command The triggered command. * @param {bool} self Whether the command was sent to the user bot or not. */ this.emit( 'chat_cmd_' + command.toLowerCase(), channel, userstate.username, command, self ); } } else { let words = message.replace(/[^\w\s]/g, '').split(/\W/g); for (let i = 0; i < words.length; i++) { let word = words[i]; if (this.getOptions().wordTriggers.basic[word]) { this.say( channel, _replaceTextVars( this.getOptions().wordTriggers.basic[word], '@' + userstate.username ) ); } if (this.chatEmotes[word]) { let emoteHtml = '<img class="chat-image" src="' + encodeURI( 'https://static-cdn.jtvnw.net/emoticons/v1/' + this.chatEmotes[word].id + '/1.0' ) + '">'; finalMessage = finalMessage.replace(word, 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. */ this.emit('chat_parsed', channel, userstate, finalMessage, self); } } /** * Handle a whisper message sent to the bot user. * @private * @param {string} from The username who sent the whisper. * @param {object} userstate The userstate object who sent the whisper. * @param {string} message The whispered message. * @param {bool} self Whether the command was sent to the user bot or not. */ function _handleWhisperMessage(from, userstate, message, self) { if ( !this.getOptions().options.ignoreSelf || (!self && userstate.username.toLowerCase() != this.getOptions().username.toLowerCase()) ) { message = message.trim(); //Check if its a command or message by looking the prefix if (message.indexOf(this.getOptions().whisperCommands.prefix) === 0) { let command = helpers.getFirstWord(message.substring(1)); //Check if its a basic command or a event command if (this.getOptions().whisperCommands.basic[command]) { this.whisper( from, _replaceTextVars( this.getOptions().whisperCommands.basic[command], '@' + userstate.username ) ); } else { let commandMessage = message .substring(command.length + 2) .trim(); /** * The whisper command event, triggered when the specified command is sent. This event is dynamic and the name will change according to the options passed to the toolkit. * @event TwitchChatEmitter#Chat:whisper_cmd_COMMAND * @param {string} userstate The userstate object for the user who sent the whisper. * @param {string} command The triggered command. * @param {string} commandMessage The message sent with the command. * @param {bool} self Whether the command was sent to the user bot or not. */ this.emit( 'whisper_cmd_' + command.toLowerCase(), userstate, command, commandMessage, self ); } } } } function _replaceTextVars(text, username) { return helpers.replaceAllOccurrences(text, '@user', username); } util.inherits(TwitchChatEmitter, tmi.Client); /* test code */ if (process.env.NODE_ENV === 'test') { TwitchChatEmitter.prototype._handleChatMessage = _handleChatMessage; TwitchChatEmitter.prototype._handleWhisperMessage = _handleWhisperMessage; } /* end-test code */ module.exports = TwitchChatEmitter;