twitchChatEmitter.js

'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) {
    tmi.Client.call(this, options);
    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 () {
    tmi.Client.prototype.connect.call(this);
    try {
        let emotes = await request({
            url: 'https://twitchemotes.com/api_cache/v3/global.json',
            method: 'GET'
        });
        this.chatEmotes = JSON.parse(emotes);
    } 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 (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);

module.exports = TwitchChatEmitter;