const _ = require('lodash');
const commander = require('commander');
const EmojiAdminList = require('./lib/emoji-admin-list');
const EmojiAdd = require('./lib/emoji-add');
const FileUtils = require('./lib/util/file-utils');
const Helpers = require('./lib/util/helpers');
const Cli = require('./lib/util/cli');
/** @module add */
/**
* The add response object, like other response objects, is organized by input subdomain.
* @typedef {object} addResponseObject
* @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 added to `subdomain`, with each element reflecting the parameters passed in to `add`
* @property {emojiList[]} subdomain.collisions if `options.avoidCollisions` is `false`, emoji that cannot be uploaded due to existing conflicting emoji names will exist here
*/
/**
* Add emoji described by parameters within options to the specified subdomain(s).
*
* Note that options can accept both aliases and original emoji at the same time, but ordering can get complicated and honestly I'd skip it if I were you. For each emoji, make sure that every descriptor (src, name, aliasFor) has a value, using `null`s for fields that are not relevant to the current emoji.
*
* @async
* @param {string|string[]} subdomains a single or list of subdomains to add emoji to. 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 singleton or arrays of emoji descriptors.
* @param {string|string[]} [options.src] source image files for the emoji to be added. If no corresponding `options.name` is given, the filename will be used
* @param {string|string[]} [options.name] names of the emoji to be added, overriding filenames if given, and becoming the alias name if an `options.aliasFor` is given
* @param {string|string[]} [options.aliasFor] names of emoji to be aliased to `options.name`
* @param {boolean} [options.allowCollisions] if `true`, emoji being uploaded will not be checked against existing emoji. This will take less time up front but may cause more errors.
* @param {boolean} [options.avoidCollisions] if `true`, emoji being added will be renamed to not collide with existing emoji. See {@link lib/util/helpers.avoidCollisions} for logic and details // TODO: fix this link, maybe link to tests which has better examples
* @param {string} [options.prefix] string to prefix all emoji being uploaded
* @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
*
* @returns {Promise<addResponseObject>} addResponseObject result object
*
* @example
var addOptions = {
src: ['./emoji1.jpg', 'http://example.com/emoji2.png'], // upload these two images
name: ['myLocalEmoji', 'myOnlineEmoji'], // call them these two names
bustCache: false, // don't bother redownloading existing emoji
avoidCollisions: true, // if there are similarly named emoji, change my new emoji names
output: false // don't write any files
};
var subdomains = ['mySubdomain1', 'mySubdomain2'] // can add one or multiple
var tokens = ['myToken1', 'myToken2'] // can add one or multiple
var cookies = ['myCookie1', 'myCookie2'] // can add one or multiple
var addResults = await emojme.add(subdomains, tokens, cookies, addOptions);
console.log(userStatsResults);
// {
// mySubomain1: {
// collisions: [], // only defined if avoidCollisons = false
// emojiList: [
// { name: 'myLocalEmoji', ... },
// { name: 'myOnlineEmoji', ... },
// ]
// },
// mySubomain2: {
// collisions: [], // only defined if avoidCollisons = false
// emojiList: [
// { name: 'myLocalEmoji', ... },
// { name: 'myOnlineEmoji', ... },
// ]
// }
// }
*/
async function add(subdomains, tokens, cookies, options) {
subdomains = Helpers.arrayify(subdomains);
tokens = Helpers.arrayify(tokens);
cookies = Helpers.arrayify(cookies);
options = options || {};
const aliases = Helpers.arrayify(options.aliasFor);
const names = Helpers.arrayify(options.name);
const sources = Helpers.arrayify(options.src);
let inputEmoji = []; let name; let alias; let
source;
while (aliases.length || sources.length) {
name = names.shift();
if (source = sources.shift()) {
inputEmoji.push({
is_alias: 0,
url: source,
name: name || source.match(/(?:.*\/)?(.*).(jpg|jpeg|png|gif)/)[1],
});
} else {
alias = aliases.shift();
inputEmoji.push({
is_alias: 1,
alias_for: alias,
name,
});
}
}
if (names.length || _.find(inputEmoji, ['name', undefined])) {
return Promise.reject(new Error('Invalid input. Either not all inputs have been consumed, or not all emoji are well formed. Consider simplifying input, or padding input with `null` values.'));
}
const [authTuples] = Helpers.zipAuthTuples(subdomains, tokens, cookies);
const addPromises = authTuples.map(async (authTuple) => {
let emojiToUpload = []; let
collisions = [];
if (options.prefix) {
inputEmoji = Helpers.applyPrefix(inputEmoji, options.prefix);
}
if (options.allowCollisions) {
emojiToUpload = inputEmoji;
} else {
const existingEmojiList = await new EmojiAdminList(...authTuple, options.output)
.get(options.bustCache);
const existingNameList = existingEmojiList.map(e => e.name);
if (options.avoidCollisions) {
inputEmoji = Helpers.avoidCollisions(inputEmoji, existingEmojiList);
}
[collisions, emojiToUpload] = _.partition(inputEmoji,
emoji => existingNameList.includes(emoji.name));
}
const emojiAdd = new EmojiAdd(...authTuple);
return emojiAdd.upload(emojiToUpload).then((uploadResult) => {
if (uploadResult.errorList && uploadResult.errorList.length > 1 && options.output) {
FileUtils.writeJson(`./build/${this.subdomain}.emojiUploadErrors.json`, uploadResult.errorList);
}
return Object.assign({}, uploadResult, { collisions });
});
});
return Helpers.formatResultsHash(await Promise.all(addPromises));
}
function addCli() {
const program = new commander.Command();
Cli.requireAuth(program);
Cli.allowIoControl(program);
Cli.allowEmojiAlterations(program)
.option('--src <value>', 'source image/gif/#content for emoji you\'d like to upload', Cli.list, null)
.option('--name <value>', 'name of the emoji from --src that you\'d like to upload', Cli.list, null)
.option('--alias-for <value>', 'name of the emoji you\'d like --name to be an alias of. Specifying this will negate --src', Cli.list, null)
.parse(process.argv);
Cli.unpackAuthJson(program);
return add(program.subdomain, program.token, program.cookie, {
src: program.src,
name: program.name,
aliasFor: program.aliasFor,
bustCache: program.bustCache,
allowCollisions: program.allowCollisions,
avoidCollisions: program.avoidCollisions,
prefix: program.prefix,
output: program.output,
});
}
if (require.main === module) {
addCli();
}
module.exports = {
add,
addCli,
};