162 lines
4.9 KiB
JavaScript
162 lines
4.9 KiB
JavaScript
// eslint-disable-next-line no-control-regex
|
|
const actionMessageRegex = /^\u0001ACTION ([^\u0001]+)\u0001$/;
|
|
const justinFanRegex = /^(justinfan)(\d+$)/;
|
|
const unescapeIRCRegex = /\\([sn:r\\])/g;
|
|
const escapeIRCRegex = /([ \n;\r\\])/g;
|
|
const ircEscapedChars = { s: ' ', n: '', ':': ';', r: '' };
|
|
const ircUnescapedChars = { ' ': 's', '\n': 'n', ';': ':', '\r': 'r' };
|
|
const urlRegex = new RegExp('^(?:(?:https?|ftp)://)(?:\\S+(?::\\S*)?@)?(?:(?!(?:10|127)(?:\\.\\d{1,3}){3})(?!(?:169\\.254|192\\.168)(?:\\.\\d{1,3}){2})(?!172\\.(?:1[6-9]|2\\d|3[0-1])(?:\\.\\d{1,3}){2})(?:[1-9]\\d?|1\\d\\d|2[01]\\d|22[0-3])(?:\\.(?:1?\\d{1,2}|2[0-4]\\d|25[0-5])){2}(?:\\.(?:[1-9]\\d?|1\\d\\d|2[0-4]\\d|25[0-4]))|(?:(?:[a-z\\u00a1-\\uffff0-9]-*)*[a-z\\u00a1-\\uffff0-9]+)(?:\\.(?:[a-z\\u00a1-\\uffff0-9]-*)*[a-z\\u00a1-\\uffff0-9]+)*(?:\\.(?:[a-z\\u00a1-\\uffff]{2,}))\\.?)(?::\\d{2,5})?(?:[/?#]\\S*)?$', 'i');
|
|
const regexEmoteRegex = /[|\\^$*+?:#]/;
|
|
const _ = module.exports = {
|
|
// Return the second value if the first value is undefined..
|
|
get: (a, b) => typeof a === 'undefined' ? b : a,
|
|
|
|
// Indirectly use hasOwnProperty
|
|
hasOwn: (obj, key) => ({}).hasOwnProperty.call(obj, key),
|
|
|
|
// Race a promise against a delay..
|
|
promiseDelay: time => new Promise(resolve => setTimeout(resolve, time)),
|
|
|
|
// Value is a finite number..
|
|
isFinite: int => isFinite(int) && !isNaN(parseFloat(int)),
|
|
|
|
// Parse string to number. Returns NaN if string can't be parsed to number..
|
|
toNumber(num, precision) {
|
|
if(num === null) {
|
|
return 0;
|
|
}
|
|
const factor = Math.pow(10, _.isFinite(precision) ? precision : 0);
|
|
return Math.round(num * factor) / factor;
|
|
},
|
|
|
|
// Value is an integer..
|
|
isInteger: int => !isNaN(_.toNumber(int, 0)),
|
|
|
|
// Value is a regex..
|
|
isRegex: str => regexEmoteRegex.test(str),
|
|
|
|
// Value is a valid url..
|
|
isURL: str => urlRegex.test(str),
|
|
|
|
// Return a random justinfan username..
|
|
justinfan: () => `justinfan${Math.floor((Math.random() * 80000) + 1000)}`,
|
|
|
|
// Username is a justinfan username..
|
|
isJustinfan: username => justinFanRegex.test(username),
|
|
|
|
// Return a valid channel name..
|
|
channel(str) {
|
|
const channel = (str ? str : '').toLowerCase();
|
|
return channel[0] === '#' ? channel : '#' + channel;
|
|
},
|
|
|
|
// Return a valid username..
|
|
username(str) {
|
|
const username = (str ? str : '').toLowerCase();
|
|
return username[0] === '#' ? username.slice(1) : username;
|
|
},
|
|
|
|
// Return a valid token..
|
|
token: str => str ? str.toLowerCase().replace('oauth:', '') : '',
|
|
|
|
// Return a valid password..
|
|
password(str) {
|
|
const token = _.token(str);
|
|
return token ? `oauth:${token}` : '';
|
|
},
|
|
|
|
actionMessage: msg => msg.match(actionMessageRegex),
|
|
|
|
// Replace all occurences of a string using an object..
|
|
replaceAll(str, obj) {
|
|
if(str === null || typeof str === 'undefined') {
|
|
return null;
|
|
}
|
|
for (const x in obj) {
|
|
str = str.replace(new RegExp(x, 'g'), obj[x]);
|
|
}
|
|
return str;
|
|
},
|
|
|
|
unescapeHtml: safe =>
|
|
safe.replace(/\\&\\;/g, '&')
|
|
.replace(/\\<\\;/g, '<')
|
|
.replace(/\\>\\;/g, '>')
|
|
.replace(/\\"\\;/g, '"')
|
|
.replace(/\\'\\;/g, '\''),
|
|
|
|
// Escaping values:
|
|
// http://ircv3.net/specs/core/message-tags-3.2.html#escaping-values
|
|
unescapeIRC(msg) {
|
|
if(!msg || typeof msg !== 'string' || !msg.includes('\\')) {
|
|
return msg;
|
|
}
|
|
return msg.replace(
|
|
unescapeIRCRegex,
|
|
(m, p) => p in ircEscapedChars ? ircEscapedChars[p] : p
|
|
);
|
|
},
|
|
|
|
escapeIRC(msg) {
|
|
if(!msg || typeof msg !== 'string') {
|
|
return msg;
|
|
}
|
|
return msg.replace(
|
|
escapeIRCRegex,
|
|
(m, p) => p in ircUnescapedChars ? `\\${ircUnescapedChars[p]}` : p
|
|
);
|
|
},
|
|
|
|
// Add word to a string..
|
|
addWord: (line, word) => line.length ? line + ' ' + word : line + word,
|
|
|
|
// Split a line but try not to cut a word in half..
|
|
splitLine(input, length) {
|
|
let lastSpace = input.substring(0, length).lastIndexOf(' ');
|
|
// No spaces found, split at the very end to avoid a loop..
|
|
if(lastSpace === -1) {
|
|
lastSpace = length - 1;
|
|
}
|
|
return [ input.substring(0, lastSpace), input.substring(lastSpace + 1) ];
|
|
},
|
|
|
|
// Extract a number from a string..
|
|
extractNumber(str) {
|
|
const parts = str.split(' ');
|
|
for (let i = 0; i < parts.length; i++) {
|
|
if(_.isInteger(parts[i])) {
|
|
return ~~parts[i];
|
|
}
|
|
}
|
|
return 0;
|
|
},
|
|
|
|
// Format the date..
|
|
formatDate(date) {
|
|
let hours = date.getHours();
|
|
let mins = date.getMinutes();
|
|
|
|
hours = (hours < 10 ? '0' : '') + hours;
|
|
mins = (mins < 10 ? '0' : '') + mins;
|
|
return `${hours}:${mins}`;
|
|
},
|
|
|
|
// Inherit the prototype methods from one constructor into another..
|
|
inherits(ctor, superCtor) {
|
|
ctor.super_ = superCtor;
|
|
const TempCtor = function () {};
|
|
TempCtor.prototype = superCtor.prototype;
|
|
ctor.prototype = new TempCtor();
|
|
ctor.prototype.constructor = ctor;
|
|
},
|
|
|
|
// Return whether inside a Node application or not..
|
|
isNode() {
|
|
try {
|
|
return typeof process === 'object' &&
|
|
Object.prototype.toString.call(process) === '[object process]';
|
|
} catch(e) {}
|
|
return false;
|
|
}
|
|
};
|