emojme-user-stats.js

const _ = require('lodash');
const commander = require('commander');

const EmojiAdminList = require('./lib/emoji-admin-list');

const Cli = require('./lib/util/cli');
const FileUtils = require('./lib/util/file-utils');
const Helpers = require('./lib/util/helpers');
/** @module userStats */

/**
 * The user-specific userStats response object, like other response objects, is organized by input subdomain.
 * @typedef {object} userStatsResponseObject
 * @property {object} subdomain each subdomain passed in to add will appear as a key in the response
 * @property {emojiList[]} subdomain.emojiList the list of emoji downloaded from `subdomain`
 * @property {object[]} subdomain.userStatsResults an array of user stats objects
 * @property {object} subdomain.userStatsResults.userStatsObject An object containing several maybe-useful statistics, separated by user
 * @property {string} subdomain.userStatsResults.userStatsObject.user the name of the user in question
 * @property {emojiList[]} subdomain.userStatsResults.userStatsObject.userEmoji the emojiList the user authored
 * @property {string} subdomain.userStatsResults.userStatsObject.subdomain redundant :shrug:
 * @property {Number} subdomain.userStatsResults.userStatsObject.originalCount the number of original emoji the user has created
 * @property {Number} subdomain.userStatsResults.userStatsObject.aliasCount the number of emoji aliases the user has defined
 * @property {Number} subdomain.userStatsResults.userStatsObject.totalCount the number of original and aliases the user has created
 * @property {Number} subdomain.userStatsResults.userStatsObject.percentage the percentage of emoji in the given subdomain that the user is responsible for
 */

/**
 * Get a few useful-ish statistics for either specific users, or the top-n emoji creators
 *
 * @async
 * @param {string|string[]} subdomains a single or list of subdomains to analyze. Must match respectively to `token`s and `cookie`s.
 * @param {string|string[]} tokens a single or list of tokens to add emoji to. Must match respectively to `subdomain`s and `cookie`s.
 * @param {string|string[]} cookies a single or list of cookies used to authenticate access to the given subdomain. Must match respectively to `subdomain`s and `token`s.
 * @param {object} options contains options for what stats to present
 * @param {string|string[]} [options.user] user name or array of user names you would like to retrieve user statistics on. If specified, ignores `top`
 * @param {Number} [options.top] count of top n emoji contriubtors you would like to retrieve user statistics on
 * @param {boolean} [options.bustCache] if `true`, ignore any adminList younger than 24 hours and repull slack instance's existing emoji. Can be useful for making `options.avoidCollisions` more accurate
 * @param {boolean} [options.output] if `false`, no files will be written during execution. Prevents saving of adminList for future use, as well as the writing of log files
 * @param {boolean} [options.verbose] if `true`, all messages will be written to stdout in addition to combined log file.
 *
 * @returns {Promise<userStatsResponseObject>} userStatsResponseObject result object
 *
 * @example
var userStatsOptions = {
  user: ['username_1', 'username_2'] // get me some info on these two users
};
var userStatsResults = await emojme.userStats('mySubdomain', 'myToken', 'myCookie', userStatsOptions);
console.log(userStatsResults);
// {
//   mySubdomain: {
//     userStatsResults: [
//       {
//         user: 'username_1',
//         userEmoji: [{ all username_1's emoji }],
//         subdomain: mySubdomain,
//         originalCount: x,
//         aliasCount: y,
//         totalCount: x + y,
//         percentage: (x + y) / mySubdomain's total emoji count
//       },
//       {
//         user: 'username_2',
//         userEmoji: [{ all username_2's emoji }],
//         subdomain: mySubdomain,
//         originalCount: x,
//         aliasCount: y,
//         totalCount: x + y,
//         percentage: (x + y) / mySubdomain's total emoji count
//       }
//     ]
//   }
// }
 */
async function userStats(subdomains, tokens, cookies, options) {
  subdomains = Helpers.arrayify(subdomains);
  tokens = Helpers.arrayify(tokens);
  cookies = Helpers.arrayify(cookies);
  const users = Helpers.arrayify(options.user);
  options = options || {};

  const [authTuples] = Helpers.zipAuthTuples(subdomains, tokens, cookies);

  const userStatsPromises = authTuples.map(async (authTuple) => {
    const emojiAdminList = new EmojiAdminList(...authTuple, options.output);
    const emojiList = await emojiAdminList.get(options.bustCache, options.since);
    if (users && users.length > 0) {
      const results = EmojiAdminList.summarizeUser(emojiList, authTuple[0], users);
      return results.map((result) => {
        const safeUserName = FileUtils.sanitize(result.user);
        FileUtils.writeJson(`./build/${safeUserName}.${result.subdomain}.adminList.json`, result.userEmoji, null, 3);
        return { subdomain: authTuple[0], userStatsResults: results, emojiList };
      });
    }
    const results = EmojiAdminList.summarizeSubdomain(emojiList, authTuple[0], options.top);
    results.forEach((result) => {
      const safeUserName = FileUtils.sanitize(result.user);
      FileUtils.writeJson(`./build/${safeUserName}.${result.subdomain}.adminList.json`, result.userEmoji, null, 3);
    });

    return { subdomain: authTuple[0], userStatsResults: results, emojiList };
  });

  return Helpers.formatResultsHash(_.flatten(await Promise.all(userStatsPromises)));
}

function userStatsCli() {
  const program = new commander.Command();

  Cli.requireAuth(program);
  Cli.allowIoControl(program)
    .option('--user <value>', 'slack user you\'d like to get stats on. Can be specified multiple times for multiple users.', Cli.list, null)
    .option('--top <value>', 'the top n users you\'d like user emoji statistics on', 10)
    .parse(process.argv);
  Cli.unpackAuthJson(program);

  return userStats(program.subdomain, program.token, program.cookie, {
    user: program.user,
    top: program.top,
    bustCache: program.bustCache,
    output: program.output,
    since: program.since,
  });
}

if (require.main === module) {
  userStatsCli();
}

module.exports = {
  userStats,
  userStatsCli,
};