You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
297 lines
7.6 KiB
JavaScript
297 lines
7.6 KiB
JavaScript
/* eslint-disable no-console*/
|
|
/**
|
|
* A Series of utility functions
|
|
*/
|
|
|
|
function noOp() {
|
|
}
|
|
|
|
/**
|
|
* returns the extension of a file for the given filename.
|
|
* @param {String} filename The name of the file.
|
|
* @return {String} A string that represents the file-extension.
|
|
*/
|
|
function getFileExtension (filename) {
|
|
if (!filename)
|
|
return null;
|
|
try {
|
|
let exts = filename.match(/\.\w+/g); // get the extension by using regex
|
|
if (exts)
|
|
return exts.pop(); // return the found extension
|
|
else
|
|
return null; // return null if no extension could be found
|
|
} catch (error) {
|
|
console.error(error);
|
|
return null;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Walks the path to the objects attribute and returns the value.
|
|
* @param object
|
|
* @param attributePath
|
|
* @returns {undefined/Object}
|
|
*/
|
|
function objectDeepFind (object, attributePath) {
|
|
let current = object,
|
|
paths = attributePath.split('.');
|
|
for (let path of paths)
|
|
if (current[path] !== undefined && current[path] !== null)
|
|
current = current[path];
|
|
else
|
|
return undefined;
|
|
|
|
return current;
|
|
}
|
|
|
|
/**
|
|
* Shuffles an array with Fisher-Yates Shuffle
|
|
* @param array
|
|
* @returns {Array}
|
|
*/
|
|
exports.shuffleArray = function(array) {
|
|
let currentIndex = array.length, temporaryValue, randomIndex;
|
|
|
|
// While there remain elements to shuffle...
|
|
while (0 !== currentIndex) {
|
|
|
|
// Pick a remaining element...
|
|
randomIndex = Math.floor(Math.random() * currentIndex);
|
|
currentIndex -= 1;
|
|
|
|
// And swap it with the current element.
|
|
temporaryValue = array[currentIndex];
|
|
array[currentIndex] = array[randomIndex];
|
|
array[randomIndex] = temporaryValue;
|
|
}
|
|
|
|
return array;
|
|
};
|
|
|
|
/**
|
|
* lets you define a cleanup for your program exit
|
|
* @param {Function} callback the cleanup function
|
|
* @constructor
|
|
* @author CanyonCasa & Pier-Luc Gendreau on StackOverflow
|
|
*/
|
|
function Cleanup(callback) {
|
|
|
|
// attach user callback to the process event emitter
|
|
// if no callback, it will still exit gracefully on Ctrl-C
|
|
callback = callback || noOp;
|
|
process.on('cleanup', callback);
|
|
|
|
// do app specific cleaning before exiting
|
|
process.on('exit', function () {
|
|
process.emit('cleanup');
|
|
});
|
|
|
|
// catch ctrl+c event and exit normally
|
|
process.on('SIGINT', function () {
|
|
console.log('Ctrl-C...');
|
|
process.exit(2);
|
|
});
|
|
|
|
//catch uncaught exceptions, trace, then exit normally
|
|
process.on('uncaughtException', function (e) {
|
|
console.log('Uncaught Exception...');
|
|
console.log(e.stack);
|
|
process.exit(99);
|
|
});
|
|
}
|
|
|
|
function getSplitDuration (duration) {
|
|
let dur = duration;
|
|
let retObj = {};
|
|
retObj.milliseconds = dur % 1000;
|
|
dur = Math.floor(dur / 1000);
|
|
retObj.seconds = dur % 60;
|
|
dur = Math.floor(dur / 60);
|
|
retObj.minutes = dur % 60;
|
|
dur = Math.floor(dur / 60);
|
|
retObj.hours = dur % 24;
|
|
dur = Math.floor(dur / 24);
|
|
retObj.days = dur;
|
|
return retObj;
|
|
}
|
|
|
|
/**
|
|
* Resolves a nested promise by resolving it iterative.
|
|
* @param promise
|
|
* @returns {Promise<*>}
|
|
*/
|
|
async function resolveNestedPromise (promise) {
|
|
let result = await promise;
|
|
while (result instanceof Promise)
|
|
result = await result; // eslint-disable-line no-await-in-loop
|
|
return result;
|
|
}
|
|
|
|
/* Classes */
|
|
|
|
class Enum {
|
|
/**
|
|
* Constructor.
|
|
* @param symbols {Array<String>}
|
|
*/
|
|
constructor(symbols) {
|
|
for (let symbol in symbols)
|
|
this[symbol] = symbols;
|
|
Object.freeze(this);
|
|
}
|
|
}
|
|
|
|
class YouTube {
|
|
/**
|
|
* returns if an url is a valid youtube url (without checking for an entity id)
|
|
* @param url
|
|
* @returns {boolean}
|
|
*/
|
|
static isValidUrl(url) {
|
|
return /https?:\/\/(www\.)?youtube\.com\/(watch\?v=|playlist\?list=)/g.test(url) ||
|
|
/https?:\/\/youtu\.be\//g.test(url);
|
|
}
|
|
|
|
/**
|
|
* returns if an url is a valid youtube url for an entity
|
|
* @param url
|
|
* @returns {boolean}
|
|
*/
|
|
static isValidEntityUrl(url) {
|
|
return /https?:\/\/(www\.)?youtube\.com\/(watch\?v=.+?|playlist\?list=.+?)/g.test(url) ||
|
|
/https?:\/\/youtu\.be\/.+?/g.test(url);
|
|
}
|
|
|
|
/**
|
|
* Returns if an url is a valid youtube url for a playlist
|
|
* @param url
|
|
* @returns {boolean}
|
|
*/
|
|
static isValidPlaylistUrl(url) {
|
|
return /https?:\/\/(www\.)?youtube\.com\/playlist\?list=.+?/g.test(url);
|
|
}
|
|
|
|
/**
|
|
* Returns if an url is a valid youtube url for a video
|
|
* @param url
|
|
* @returns {boolean}
|
|
*/
|
|
static isValidVideoUrl(url) {
|
|
return /https?:\/\/(www\.)?youtube\.com\/watch\?v=.+?/g.test(url) || /https?:\/\/youtu\.be\/.+?/g.test(url);
|
|
}
|
|
|
|
/**
|
|
* Returns the id for a youtube video stripped from the url
|
|
* @param url
|
|
* @returns {String}
|
|
*/
|
|
static getPlaylistIdFromUrl(url) {
|
|
if (!exports.YouTube.isValidPlaylistUrl(url))
|
|
return null;
|
|
let matches = url.match(/(?<=\?list=)[\w-]+/);
|
|
if (matches)
|
|
return matches[0];
|
|
else
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Returns the id for a youtube video stripped from the url
|
|
* @param url
|
|
* @return {String}
|
|
*/
|
|
static getVideoIdFromUrl(url) {
|
|
if (!exports.YouTube.isValidVideoUrl(url))
|
|
return null;
|
|
let matches1 = url.match(/(?<=\?v=)[\w-]+/);
|
|
if (matches1) {
|
|
return matches1[0];
|
|
} else {
|
|
let matches2 = url.match(/(?<=youtu\.be\/)[\w-]+/);
|
|
if (matches2)
|
|
return matches2[0];
|
|
else
|
|
return null;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Returns the youtube video url for a video id by string concatenation
|
|
* @param id
|
|
*/
|
|
static getVideoUrlFromId(id) {
|
|
return `https://www.youtube.com/watch?v=${id}`;
|
|
}
|
|
|
|
/**
|
|
* Returns the youtube video thumbnail for a video url
|
|
* @param url
|
|
* @returns {string}
|
|
*/
|
|
static getVideoThumbnailUrlFromUrl(url) {
|
|
let id = exports.YouTube.getVideoIdFromUrl(url);
|
|
return id? `https://i3.ytimg.com/vi/${id}/maxresdefault.jpg` : null;
|
|
}
|
|
}
|
|
|
|
class ConfigVerifyer {
|
|
/**
|
|
* @param confObj
|
|
* @param required {Array} the attributes that are required for the bot to work
|
|
*/
|
|
constructor(confObj, required) {
|
|
this.config = confObj;
|
|
this.requiredAttributes = required;
|
|
}
|
|
|
|
/**
|
|
* @param logger set the logger to log to
|
|
*/
|
|
verifyConfig(logger) {
|
|
let missing = [];
|
|
for (let reqAttr of this.requiredAttributes)
|
|
if (exports.objectDeepFind(this.config, reqAttr) === undefined)
|
|
missing.push(reqAttr);
|
|
|
|
this.missingAttributes = missing;
|
|
this.logMissing(logger);
|
|
return this.missingAttributes.length === 0;
|
|
}
|
|
|
|
/**
|
|
* Promts the user which attributes are missing
|
|
* @param logger
|
|
*/
|
|
logMissing(logger) {
|
|
if (this.missingAttributes.length > 0)
|
|
logger.error(`Missing required Attributes ${this.missingAttributes.join(', ')}`);
|
|
|
|
}
|
|
}
|
|
|
|
exports.sql = {
|
|
tableExistCreate: 'CREATE TABLE IF NOT EXISTS',
|
|
pkIdSerial: 'id INTEGER PRIMARY KEY AUTOINCREMENT UNIQUE NOT NULL'
|
|
};
|
|
|
|
exports.logLevels = {
|
|
'debug': 0,
|
|
'verbose': 1,
|
|
'info': 2,
|
|
'warning': 3,
|
|
'warn': 3,
|
|
'error:': 4
|
|
};
|
|
|
|
Object.assign(exports, {
|
|
resolveNestedPromise: resolveNestedPromise,
|
|
YouTube: YouTube,
|
|
ConfigVerifyer: ConfigVerifyer,
|
|
getSplitDuration: getSplitDuration,
|
|
getExtension: getFileExtension,
|
|
getFileExtension: getFileExtension,
|
|
objectDeepFind: objectDeepFind,
|
|
Cleanup: Cleanup,
|
|
Enum: Enum
|
|
});
|