Code for %{query} command


#!/usr/bin/env node
'use strict';

const got = require('got');
const utils = require('../utils/utils.js');

let cache = [];

// clear cache every 5 hours
setInterval(() => {
  cache.length = 0;
}, 18000000);

module.exports = {
  name: 'kb emotes',
  invocation: async (channel, user, message, platform) => {
    try {
      let _message = message.split(' ')[1].toLowerCase();
      _message = _message.replace(/\bemotesearch\b|\bes\b/g, 'emote');

      const api = await utils.Get.api().url(message);

      let msg =
        _message === 'removed' || _message === 'emote'
          ? utils.getParam(message, 1)
          : utils.getParam(message);

      if (msg[0] === 'list' || msg[0] === 'link' || msg[0] === '-list' || msg[0] === '-link') {
        if (platform === 'whisper') {
          return 'This usage is disabled on this platform';
        }
        return `${
          user['username']
        }, see full list of emotes: https://kunszg.com/emotes?search=${channel.replace(
          '#',
          ''
        )}`;
      }

      if (msg[0] === 'api') {
        if (platform === 'whisper') {
          return 'This usage is disabled on this platform';
        }
        if ((await utils.checkPermissions(user['username'])) < 1) {
          return `${user['username']}, You don't have permissions to use this parameter :(`;
        }

        const userId = await utils.query(
          `
                    SELECT *
                    FROM channels
                    WHERE channel=?`,
          [channel.replace('#', '')]
        );

        return `${user['username']}, //FFZ ${api[0]}/room/id/${userId[0].userId}
                    //BTTV ${api[1]}/cached/users/twitch/${userId[0].userId}
                    //7TV ${api[2]}/users/${userId[0].userId}/emotes`;
      }

      if (msg[0] === 'yoink' || msg[0] === 'reload') {
        if ((await utils.checkPermissions(user['username'])) < 1) {
          return `${user['username']}, You don't have permissions to use this parameter :(`;
        }

        if (platform === 'whisper') {
          return 'This usage is disabled on this platform';
        }

        const channelName = channel.replace('#', '');

        const emotes = await utils.query(
          `
                    SELECT *
                    FROM emotes
                    WHERE channel=?`,
          [channelName]
        );

        const userId = await utils.query(
          `
                    SELECT *
                    FROM channels_logger
                    WHERE channel=?`,
          [channelName]
        );

        let errMessage = '';

        // 7TV

        try {
          const seventvEmotes = await got(`${api[2]}/users/${userId[0].userId}/emotes`).json();

          if (!seventvEmotes.length) {
            throw 'err';
          }

          // check if emote exists in database
          // if not, add it
          const checkForRepeatedEmotesSeventv = async (emote, id, type) => {
            if (typeof seventvEmotes.message != 'undefined' || !seventvEmotes) {
              return '';
            }

            const updateEmotes = emotes.find(i => i.sevenTvId === id);
            if (!updateEmotes) {
              const findEmote = seventvEmotes.filter(i => i.id === id);
              const emoteLink = findEmote[0].urls[0][1];

              await utils.query(
                `
                                INSERT INTO emotes (userId, channel, emote, url, type, sevenTvId, date)
                                VALUES (?, ?, ?, ?, ?, ?, CURRENT_TIMESTAMP)`,
                [userId[0].userId, channelName, emote, emoteLink, type, id]
              );
            }
          };

          // iterate through 7TV emotes
          // and check if any of the emotes doesn't exist in the set anymore
          for (let j = 0; j < emotes.length; j++) {
            if (typeof seventvEmotes.message != 'undefined' || !seventvEmotes) {
              return '';
            }

            if (emotes[j].type === '7tv') {
              if (!seventvEmotes.some(i => emotes[j].sevenTvId === i.id)) {
                await utils.query(
                  `
                                    DELETE FROM emotes
                                    WHERE ID=?`,
                  [emotes[j].ID]
                );

                await utils.query(
                  `
                                    INSERT INTO emotes_removed (userId, channel, emote, url, sevenTvId, date, type)
                                    VALUES (?, ?, ?, ?, ?, CURRENT_TIMESTAMP, "7tv")`,
                  [
                    userId[0].userId,
                    channelName,
                    emotes[j].emote,
                    emotes[j].url,
                    emotes[j].sevenTvId,
                  ]
                );
              }
            }
          }
          seventvEmotes.map(i => checkForRepeatedEmotesSeventv(i.name, i.id, '7tv'));
        } catch (err) {
          errMessage += '7tv ';
        }

        // BTTV
        try {
          const bttvEmotes = await got(
            `${api[1]}/cached/users/twitch/${userId[0].userId}`
          ).json();

          const allBttvEmotes = bttvEmotes.channelEmotes.concat(bttvEmotes.sharedEmotes);

          // check if emote exists in database
          // if not, add it
          const checkForRepeatedEmotesBttv = async (emote, id, type) => {
            if (typeof bttvEmotes.message != 'undefined' || !bttvEmotes) {
              return '';
            }

            const updateEmotes = emotes.find(
              i =>
                i.url.replace('https://cdn.betterttv.net/emote/', '').replace('/1x', '') === id
            );
            if (!updateEmotes) {
              const findEmote = allBttvEmotes.filter(i => i.id === id);
              const emoteLink = `https://cdn.betterttv.net/emote/${findEmote[0].id}/1x`;

              await utils.query(
                `
                                INSERT INTO emotes (userId, channel, emote, url, type, date)
                                VALUES (?, ?, ?, ?, ?, CURRENT_TIMESTAMP)`,
                [userId[0].userId, channelName, emote, emoteLink, type]
              );
            }
          };

          // iterate through BTTV emotes
          // and check if any of the emotes doesn't exist in the set anymore
          for (let j = 0; j < emotes.length; j++) {
            if (typeof bttvEmotes.message != 'undefined' || !bttvEmotes) {
              break;
            }

            if (emotes[j].type === 'bttv') {
              if (
                !allBttvEmotes.some(
                  i =>
                    emotes[j].url
                      .replace('https://cdn.betterttv.net/emote/', '')
                      .replace('/1x', '') === i.id
                )
              ) {
                await utils.query(
                  `
                                    DELETE FROM emotes
                                    WHERE ID=?`,
                  [emotes[j].ID]
                );

                await utils.query(
                  `
                                    INSERT INTO emotes_removed (userId, channel, emote, url, date, type)
                                    VALUES (?, ?, ?, ?, CURRENT_TIMESTAMP, "bttv")`,
                  [userId[0].userId, channelName, emotes[j].emote, emotes[j].url]
                );
              }
            }
          }
          allBttvEmotes.map(i => checkForRepeatedEmotesBttv(i.code, i.id, 'bttv'));
        } catch (err) {
          errMessage += 'bttv ';
        }

        // FFZ

        try {
          const ffzEmotes = await got(`${api[0]}/room/id/${userId[0].userId}`).json();

          const setId = Object.keys(ffzEmotes.sets)[0];

          const checkForRepeatedEmotesFfz = async (emote, id, type) => {
            if (typeof ffzEmotes.error != 'undefined' || !ffzEmotes) {
              return '';
            }

            const updateEmotes = emotes.find(i => i.emoteId === id);
            if (!updateEmotes) {
              const findEmote = ffzEmotes.sets[setId].emoticons.filter(i => i.id === id);

              await utils.query(
                `
                                INSERT INTO emotes (userId, channel, emote, url, emoteId, type, date)
                                VALUES (?, ?, ?, ?, ?, ?, CURRENT_TIMESTAMP)`,
                [userId[0].userId, channelName, emote, findEmote[0].urls['1'], id, type]
              );
            }
          };

          // iterate through FFZ emotes
          // and check if any of the emotes doesn't exist in the set anymore
          for (let j = 0; j < emotes.length; j++) {
            if (typeof ffzEmotes.error != 'undefined' || !ffzEmotes) {
              break;
            }

            if (emotes[j].type === 'ffz') {
              if (!ffzEmotes.sets[setId].emoticons.some(i => emotes[j].emoteId === i.id)) {
                await utils.query(
                  `
                                    DELETE FROM emotes
                                    WHERE ID=?`,
                  [emotes[j].ID]
                );

                await utils.query(
                  `
                                    INSERT INTO emotes_removed (userId, channel, emote, url, emoteId, date, type)
                                    VALUES (?, ?, ?, ?, ?, CURRENT_TIMESTAMP, "ffz",)`,
                  [
                    userId[0].userId,
                    channelName,
                    emotes[j].emote,
                    emotes[j].url,
                    emotes[j].emoteId,
                  ]
                );
              }
            }
          }

          ffzEmotes.sets[setId].emoticons.map(i =>
            checkForRepeatedEmotesFfz(i.name, i.id, 'ffz')
          );
        } catch (err) {
          errMessage += 'ffz';
        }

        if (errMessage) {
          return `${user['username']}, could not fetch emotes from following services: ${errMessage}, command completed successfully.`;
        }

        return `${user['username']}, done TriHard`;
      }

      const formatDate = timestamp => {
        const time = Date.now() - Date.parse(timestamp);

        const seconds = Number(time / 1000);
        const d = Math.floor(seconds / (3600 * 24));
        const h = Math.floor((seconds % (3600 * 24)) / 3600);
        const m = Math.floor((seconds % 3600) / 60);
        const s = Math.floor(seconds % 60);

        const dDisplay = d > 0 ? d + 'd ' : '';
        const hDisplay = h > 0 ? h + 'h ' : '';
        const mDisplay = m > 0 ? m + 'm ' : '';
        const sDisplay = s > 0 ? s + 's ' : '';

        if (seconds > 86400) {
          // day in seconds
          return `${dDisplay}${hDisplay} ago`;
        }
        if (seconds > 3600) {
          // hour in seconds
          return `${hDisplay}${mDisplay} ago`;
        }
        return `${mDisplay}${sDisplay} ago`;
      };

      if ((msg?.[0] ?? false) && _message === 'emote') {
        if (!msg[1]) {
          return `${user['username']}, you have to provide an emote to search for.`;
        }

        const userMsg = msg.filter(i => !i.match(/i:\[?(\d+)\]?/))[1];

        if (userMsg.length < 3) {
          return `${user['username']}, too little characters provided.`;
        }

        const service = msg.find(i => i.match(/\b(-)?(ffz|bttv)\b/i));

        if (!cache.filter(i => i[userMsg]).length) {
          const emotesCurr = await utils.query(
            `
                        SELECT DISTINCT url, emoteId, type, emote
                        FROM emotes
                        WHERE url IS NOT NULL AND emote=? AND type="ffz"
                        ORDER BY DATE desc`,
            [userMsg]
          );

          const emotesRem = await utils.query(
            `
                        SELECT DISTINCT url, emoteId, type, emote
                        FROM emotes_removed
                        WHERE url IS NOT NULL AND emote=? AND type="ffz"`,
            [userMsg]
          );

          const emotesBttvApi = [];

          for (let limit = 100, offset = 0; offset < 1500; ) {
            const list = await got(
              encodeURI(
                `https://api.betterttv.net/3/emotes/shared/search?query=${userMsg}&offset=${offset}&limit=${limit}`
              )
            ).json();

            if (list.length) {
              emotesBttvApi.push(list);
              offset += limit;
            } else {
              break;
            }
          }

          const _emotesBttvApi = emotesBttvApi.flat();

          let bttvEmoteSet = _emotesBttvApi.map(i => `https://betterttv.com/emotes/${i.id}`);

          let emoteSets = emotesCurr.concat(emotesRem);

          let ffzEmoteSet = emoteSets
            .filter(i => i.url != null)
            .map(i => `https://www.frankerfacez.com/emoticon/${i.emoteId}-${i.emote}`);

          emoteSets = bttvEmoteSet.concat(ffzEmoteSet);

          const data = JSON.parse(
            `{${JSON.stringify(userMsg)}: [{
                            "bttv": ${JSON.stringify(bttvEmoteSet)},
                            "ffz": ${JSON.stringify(ffzEmoteSet)},
                            "emoteSets": ${JSON.stringify(emoteSets)}
                        }]}`
          );

          cache.push(data);
        }

        const filter = cache.filter(i => i[userMsg])[0][userMsg][0];

        const ffzEmoteSet = filter.ffz;
        const bttvEmoteSet = filter.bttv;
        const emoteSets = filter.emoteSets;

        if (!emoteSets.length) {
          return `${user['username']}, no emotes were found.`;
        }

        const results = () => {
          if (service) {
            if (service.toLowerCase().replace('-', '') === 'bttv') {
              return bttvEmoteSet.length;
            }
            return ffzEmoteSet.length;
          }
          return emoteSets.length;
        };

        const emote = index => {
          if (service) {
            if (service.toLowerCase().replace('-', '') === 'bttv') {
              return bttvEmoteSet[index];
            }
            return ffzEmoteSet[index];
          }
          return emoteSets[index];
        };

        if (msg.join(' ').match(/i:\[?(\d+)\]?/)) {
          const index = msg
            .join(' ')
            .match(/i:\[?(\d+)\]?/)[0]
            .replace(/i:|\[|\]/g, '');

          if (typeof emoteSets[Number(index)] === 'undefined') {
            return `${user['username']}, this index does not exist.`;
          }

          return `${user['username']}, i:${index} : ${emote(index)} `;
        }

        if (emoteSets.length > 1) {
          return `${
            user['username']
          }, ${results()} results found, use i:[number] as parameter to get them. i:0 : ${emote(
            0
          )}`;
        }

        return `${user['username']}, ${emote(0)}`;
      }

      const findParam = msg
        .filter(i => i.match(/\b-?(bttv|ffz|7tv)\b/gi))
        .map(i => i.toLowerCase());

      msg = msg.filter(i => !i.match(/\b-?(bttv|ffz|7tv)\b/gi));

      const type = findParam.length ? findParam[0].replace('-', '').toUpperCase() : '';

      if ((msg?.[0] ?? false) && msg[0] === 'removed') {
        const userSpecifiedChannel = msg.filter(i => i.startsWith('#'));
        if (userSpecifiedChannel.length) {
          // check if channel exists in the database
          const checkChannel = await utils.query('SHOW TABLES LIKE ?', [
            `logs_${userSpecifiedChannel[0].toLowerCase().replace('#', '')}`,
          ]);

          if (!checkChannel.length) {
            return `${user['username']}, I'm not logging the channel you specified :/`;
          }

          let latestRemovedEmotes = await utils.query(
            `
                        SELECT *
                        FROM emotes_removed
                        WHERE channel=?
                        ORDER BY date
                        DESC`,
            [userSpecifiedChannel[0].toLowerCase().replace('#', '')]
          );

          if (findParam.length) {
            latestRemovedEmotes = await utils.query(
              `
                            SELECT *
                            FROM emotes_removed
                            WHERE channel=? AND type=?
                            ORDER BY date
                            DESC`,
              [
                userSpecifiedChannel[0].toLowerCase().replace('#', ''),
                findParam[0].replace(/-/g, ''),
              ]
            );
          }

          if (!latestRemovedEmotes.length) {
            return `${user['username']}, this channel does not have any removed emotes registered in my database yet.`;
          }

          const emotes = latestRemovedEmotes
            .map(i => `${i.emote} (${formatDate(i.date)})`)
            .slice(0, 6);
          const userNoPing = userSpecifiedChannel[0]
            .toLowerCase()
            .replace('#', '')
            .replace(/^(.{2})/, '$1\u{E0000}');

          return `${
            user['username']
          }, recently removed ${type} emotes in channel ${userNoPing}: ${emotes.join(', ')}`;
        }

        if (platform === 'whisper') {
          return 'This usage is disabled on this platform';
        }

        let latestRemovedEmotes = await utils.query(
          `
                    SELECT *
                    FROM emotes_removed
                    WHERE channel=?
                    ORDER BY date
                    DESC`,
          [channel.replace('#', '')]
        );

        if (findParam.length) {
          latestRemovedEmotes = await utils.query(
            `
                        SELECT *
                        FROM emotes_removed
                        WHERE channel=? AND type=?
                        ORDER BY date
                        DESC`,
            [channel.replace('#', ''), findParam[0].replace(/-/g, '')]
          );
        }

        if (!latestRemovedEmotes.length) {
          return `${user['username']}, this channel does not have any removed emotes registered in my database yet.`;
        }

        const emotes = latestRemovedEmotes
          .map(i => `${i.emote} (${formatDate(i.date)})`)
          .slice(0, 6);

        const emoteList =
          emotes.join(', ').length > 430
            ? emotes.join(', ').substring(0, 400) + ' (...)'
            : emotes.join(', ');

        return `${user['username']}, recently removed ${type} emotes in this channel: ${emoteList}`;
      }

      if (msg[0]) {
        // check if channel exists in the database
        const checkChannel = await utils.query('SHOW TABLES LIKE ?', [
          `logs_${msg[0].toLowerCase().replace('#', '')}`,
        ]);

        if (!checkChannel.length) {
          return `${user['username']}, I'm not logging the channel you specified :/`;
        }

        let latestEmotes = await utils.query(
          `
                    SELECT *
                    FROM emotes
                    WHERE channel=?
                    ORDER BY date
                    DESC`,
          [msg[0].toLowerCase().replace('#', '')]
        );

        if (findParam.length) {
          latestEmotes = await utils.query(
            `
                        SELECT *
                        FROM emotes
                        WHERE channel=? AND type=?
                        ORDER BY date
                        DESC`,
            [msg[0].toLowerCase().replace('#', ''), findParam[0].replace(/-/g, '')]
          );
        }

        if (!latestEmotes.length) {
          return `${user['username']}, this channel does not have any added emotes registered yet.`;
        }

        const emotes = latestEmotes.map(i => `${i.emote} (${formatDate(i.date)})`).slice(0, 6);

        const userNoPing = msg[0]
          .toLowerCase()
          .replace('#', '')
          .replace(/^(.{2})/, '$1\u{E0000}');

        const emoteList =
          emotes.join(', ').length > 430
            ? emotes.join(', ').substring(0, 400) + ' (...)'
            : emotes.join(', ');

        return `${user['username']}, recently added ${type} emotes in channel ${userNoPing}: ${emoteList}`;
      }

      if (platform === 'whisper') {
        return 'This usage is disabled on this platform';
      }

      let latestEmotes = await utils.query(
        `
				SELECT *
				FROM emotes
				WHERE channel=?
				ORDER BY date
				DESC`,
        [channel.replace('#', '')]
      );

      if (findParam.length) {
        latestEmotes = await utils.query(
          `
                    SELECT *
                    FROM emotes
                    WHERE channel=? AND type=?
                    ORDER BY date
                    DESC`,
          [channel.replace('#', ''), findParam[0].replace(/-/g, '')]
        );
      }

      if (!latestEmotes.length) {
        return `${user['username']}, this channel does not have any added emotes registered yet.`;
      }

      const emotes = latestEmotes.map(i => `${i.emote} (${formatDate(i.date)})`).slice(0, 6);

      return `${
        user['username']
      }, recently added ${type} emotes in this channel: ${emotes.join(', ')}`;
    } catch (err) {
      utils.errorLog(err);
      return `${user['username']}, ${err} FeelsDankMan !!!`;
    }
  },
};