more plugins ported over

This commit is contained in:
Evert Prants 2020-12-05 12:00:00 +02:00
parent 303c9e9828
commit 0621386e55
Signed by: evert
GPG Key ID: 1688DA83D222D0B5
18 changed files with 1770 additions and 107 deletions

9
debug/plugin.json Normal file
View File

@ -0,0 +1,9 @@
{
"main": "plugin.js",
"name": "debug",
"description": "In-chat debugging tools",
"version": "1.0.0",
"tags": ["commands", "tools"],
"dependencies": ["simplecommands"],
"npmDependencies": []
}

118
debug/plugin.ts Normal file
View File

@ -0,0 +1,118 @@
import util from 'util';
import cprog from 'child_process';
import {
Plugin,
Configurable,
EventListener,
DependencyLoad
} from '@squeebot/core/lib/plugin';
import { IMessage } from '@squeebot/core/lib/types';
import { ISqueebotCore, logger } from '@squeebot/core/lib/core';
function addCommands(plugin: UtilityPlugin, commands: any): void {
const cmds = [{
plugin: plugin.manifest.name,
name: 'evaljs',
execute: async (msg: IMessage, spec: any, prefix: string, ...simplified: any[]): Promise<boolean> => {
if (!simplified[0]) {
return true;
}
const script = msg.data.split(' ').slice(1).join(' ');
// Disallow child_process when shell is disallowed
if ((script.indexOf('child_process') !== -1 ||
script.indexOf('cprog') !== -1 ||
script.indexOf('fork') !== -1) &&
!plugin.config.config.allowShell) {
msg.resolve('Error: child_process is not allowed in evaljs due to security reasons.');
return true;
}
try {
// tslint:disable-next-line: no-eval
const mesh = eval(script);
if (mesh === undefined) {
return true;
}
msg.resolve(util.format(mesh));
} catch (e) {
msg.resolve('Error: ' + e.message);
}
return true;
},
usage: '<javascript>',
description: 'Execute JavaScript in a command context',
permissions: ['system_execute'],
hidden: true
}];
if (plugin.config.config.allowShell) {
logger.warn('WARNING! Shell command execution is enabled! Make absolutely sure that there is proper authentication!');
if (process.getuid && process.getuid() === 0) {
logger.warn('NEVER run Squeebot as root! Run `useradd squeebot`! We are not responsible for possible security leaks!');
}
cmds.push({
plugin: plugin.manifest.name,
name: 'sh',
execute: async (msg: IMessage, spec: any, prefix: string, ...simplified: any[]): Promise<boolean> => {
const stripnl = (simplified[0] !== '-n');
const cmd = simplified.slice(stripnl ? 0 : 1).join(' ');
if (!cmd) {
msg.resolve('Nothing to execute!');
return true;
}
cprog.exec(cmd, {shell: '/bin/bash'}, (error, stdout, stderr) => {
if (stdout) {
if (stripnl) { stdout = stdout.replace(/\n/g, ' ;; '); }
return msg.resolve(stdout);
}
msg.resolve('Error executing command.');
logger.error(stderr || error);
});
return true;
},
description: 'Run raw shell command',
usage: '<shell>',
hidden: true,
permissions: ['system_shell'],
});
}
commands.registerCommand(cmds);
}
@Configurable({
allowShell: false,
})
class UtilityPlugin extends Plugin {
public core: ISqueebotCore | null = null;
@DependencyLoad('simplecommands')
addCommands(cmd: any): void {
addCommands(this, cmd);
}
@EventListener('pluginUnload')
public unloadEventHandler(plugin: string | Plugin): void {
if (plugin === this.name || plugin === this) {
this.config.save().then(() =>
this.emit('pluginUnloaded', this));
}
}
initialize(): void {
this.on('core', (c: ISqueebotCore) => this.core = c);
this.emitTo('core', 'request-core', this.name);
}
}
module.exports = UtilityPlugin;

9
diction/plugin.json Normal file
View File

@ -0,0 +1,9 @@
{
"main": "plugin.js",
"name": "diction",
"description": "Find definitions for words",
"version": "1.0.0",
"tags": ["commands", "utility", "dictionary"],
"dependencies": ["simplecommands"],
"npmDependencies": []
}

121
diction/plugin.ts Normal file
View File

@ -0,0 +1,121 @@
import { httpGET } from '@squeebot/core/lib/common';
import { logger } from '@squeebot/core/lib/core';
import {
Plugin,
Configurable,
EventListener,
DependencyLoad
} from '@squeebot/core/lib/plugin';
import { IMessage } from '@squeebot/core/lib/types';
const poslist = [
'noun',
'verb',
'adjective',
'adverb',
'participle',
'article',
'pronoun',
'preposition',
'conjunction',
'interjection',
'determiner',
];
let lastQuery = 0;
@Configurable({
lexicala: {
username: '',
password: '',
},
cooldown: 5,
})
class DictionPlugin extends Plugin {
@EventListener('pluginUnload')
public unloadEventHandler(plugin: string | Plugin): void {
if (plugin === this.name || plugin === this) {
this.emit('pluginUnloaded', this);
}
}
@DependencyLoad('simplecommands')
addCommands(cmd: any): void {
const user = this.config.get('lexicala.username');
const passwd = this.config.get('lexicala.password');
const rate = this.config.get('cooldown', 5);
const rauth = 'Basic ' + Buffer.from(`${user}:${passwd}`).toString('base64');
if (!user || !passwd) {
logger.warn('Lexicala define command is disabled due to no credentials.');
return;
}
cmd.registerCommand({
name: 'define',
plugin: this.name,
execute: async (msg: IMessage, spec: any, prefix: string, ...simplified: any[]): Promise<boolean> => {
if (lastQuery > Date.now() - rate * 1000) {
msg.resolve('You\'re doing that too fast!');
return true;
}
let pos: string | null = simplified[0];
if (pos && poslist.indexOf(pos.toLowerCase()) !== -1 && simplified.length > 1) {
pos = '&pos=' + pos;
} else {
pos = '';
}
const word = encodeURIComponent(msg.text.split(' ').slice(pos ? 2 : 1).join(' '));
if (!word) {
msg.resolve('Please specifiy a word or term!');
return true;
}
lastQuery = Date.now();
let response;
try {
const s = `https://dictapi.lexicala.com/search?source=global&language=en${pos}&text=${word}`;
response = await httpGET(s, { Authorization: rauth });
response = JSON.parse(response);
} catch (e) {
msg.resolve('Server did not respond.');
return true;
}
if (!response.n_results || response.n_results === 0 || !response.results) {
msg.resolve('Nothing found.');
return true;
}
const resolve = [];
for (const def of response.results) {
const wd = def.headword.text;
const wdp = def.headword.pos;
const results = def.senses.map((rep: any) => rep.definition).slice(0, 4);
resolve.push({
word: wd,
type: wdp,
results,
});
}
const str = resolve.slice(0, 4).map((rep: any) => {
return `${rep.word} (${rep.type}):\n` + rep.results.join('\n');
}).join('\n');
msg.resolve(str);
return true;
},
aliases: ['df'],
description: 'Find definitions for words',
usage: '[<pos>] <word>'
});
}
}
module.exports = DictionPlugin;

9
fun/plugin.json Normal file
View File

@ -0,0 +1,9 @@
{
"main": "plugin.js",
"name": "fun",
"description": "Fun commands",
"version": "1.0.0",
"tags": ["commands", "fun"],
"dependencies": ["simplecommands"],
"npmDependencies": []
}

85
fun/plugin.ts Normal file
View File

@ -0,0 +1,85 @@
import {
Plugin,
Configurable,
EventListener,
DependencyLoad
} from '@squeebot/core/lib/plugin';
import { IMessage } from '@squeebot/core/lib/types';
const alpaca = ['a3d53439', '870b9e10', 'c40c5aad', '77e87cd4', '5815410d', '1855876d', 'd134d47f', '6e0db005', 'b2e471da', 'd083213f', '9ac93967', '1f53ea27', '00ab6411', 'bfdc0079', 'ce29fc6f', '552665d0', '8be91f16', 'b7499ec2', 'b9d52259', '6e505095', '4bc30df8', 'c1854975', 'fe87532c', '9e6cb311', '9e6d6e69', 'f79992bf', '11aa0714', 'c27e712f', '8ee7d28c', 'd79b158f', 'a495bad4', 'df93dc7e', '57ed2f42', '9e2c0722', '3a6c0bab', '4268b737', '490f09bf', 'f6d8fca6', '3d3d96b1', 'f1d82aaa', 'fb1a6e0e', '31a96aee', 'a0cc346f', '9f88d146', 'a55a91a4', '83ee2889', '94c60ee3', '30e66d58', '27870c50', '7adf19c6', '2920ceac', 'ddd62b77', '86a6efe7', '02d2d4f4', 'bebd657c', '1c5a3508', 'c3c6fc4b', '1761ebbc', '7eeaab38', 'd9a12e72', '42356945', 'dff46a1a', '413c741a', 'a7761c60', 'f3316806', '782b8b6c', '6547d625', 'd17bea20', 'fdc5c329', '56bb5991', '9a9ebe09', '8bb0818e', '4692f354', '9d91ce89', 'b3fb3715', '0d03a684', '115af660', '845031e2', '945d26bd', 'd3b1dd25', 'c2cb872f', '0c202666', '357e5195', 'fef6732c', '8a88104e', '1fdbc9ec', 'f6ad84dc', '473eb851', 'd078e723', '5079e902', '8a81a94f', '285c6ef8', '443b7f25', '0ba9ecac', '36d044a9', '7fa1366e', '88dedd53', '7839d770', '50ee9fd7', 'f0a4f672', '74526d28', '639106bf', '8aa61988', 'f6615095', '42385e98', 'ba94be4d', '8c81f5ee', '7bfb19f9', '5cc54d76', '962d2cfb', 'eaee222d', '994575be', '114927a6', 'd3119ed2', 'ce62f835', '0dc3fbb7', 'dc7f629f', '103d03cb', '96e54eb1', 'be395291', 'c469785e', '0c3d13cf', 'f61a1b43', 'bb64b44e', 'c8cdf1e7', '9cd9589a', '92f745d3', 'c9a168e4', 'b5a59c3e', 'ae5e3337', 'ff2a8f2d', 'eef41a7e', 'ef4fad05', '4a2f3a69', '7f9af8df', '9731dcba', 'f4b64638', 'f19b7a28', '87b5f9bb', '8e261176', '6353444c', '68cf22f1', 'e8c05fa5', '0d080496', 'f15c48a1', 'd0dcb52d', '1774d947', 'ead69e2f', '16a9d0d0', 'bae38658', '1b802c4b', '4502f826', '0a4bbb94', '957892e3', '54b50c3b', '6d0e742e', 'e3231c9d', '4e88f997', 'de8a6111', '9d9ff907', 'dbecd1de', 'c11dd818', '6ce07218', 'e73aec0b', 'e5a030b1', '52c22730', '6648a887', 'dabec190', '079c58e1', '94b7882c', 'ac905c5f', '11845f34', '87a871cd', 'bacb3cb9', '9f8584fd', 'f6788d62', 'fae5c010', '8d73c238', '1fe8c363', 'c7408956', '313bd1fb', '0a2de87a', '0fec45a8', '5a23fe9f', '9496c280', 'dc04d424', '1dee8f8b', '44f222a4', 'a0d96fd5', '70563299', 'b3e7bfd2', '49f47c49', '20d103e1', '7ebe873a', '7b13e329', '85cc1c96', 'b569d2d5', '61ccd52f', '8e99ecad', '63b87b93', '3b2e9d58', 'c07a484a', '3e414e22', 'ea030f21', 'd94348a6', '87b486cf', 'fbd5f38e', '81d7c9fc', '4a8aec26', '0d5cb865', 'f840c174', '8762c66d', '5a99399c', 'c00c8cf1', '122ae6de', 'adb4efc5', '84e09e92', 'b21658fd', 'c25cfcaf', '0ac96278', 'a8e49500', '05a639ef', 'bb10bed8', '6a844be3', 'ab06cede', 'b9a8c57e', '6e6b1745', 'a8bf0e1d', 'ae9c19bf', '0392b422', '9dc049e4', '61efd0d2', 'f0759c3d', '29482eb7', 'aa059afe', '7a03c62f', 'a8a8b79c', '59629470', 'c1949ac1', '721db006', '486a31f9', '793cf9cd', '81eb3518', '319c6398', 'd35b269d', '1d1a5014', 'a10506a9', 'ed61196c', '5fd4c99e', '1d92b5ab', 'c34a7a0e', 'ad19db59', '25346668', '0b17393e', '466bd057', 'f9860642', '7d74f7d0', '6719ce53', '95864f2a', 'e810e07c', '9a0a127d', '428f9e7c', '98d645ba', '444a27d5', '999f6fe9'];
@Configurable({
simpleReplies: {
ping: 'pong'
}
})
class FunPlugin extends Plugin {
@EventListener('pluginUnload')
public unloadEventHandler(plugin: string | Plugin): void {
if (plugin === this.name || plugin === this) {
this.emit('pluginUnloaded', this);
}
}
initialize(): void {
// Hug action
this.on('message', (data: IMessage) => {
const messageRefined = data.text.toLowerCase().trim().replace(/@/g, '');
const myName = data.source.me.name.toLowerCase();
if (messageRefined.indexOf('hugs ' + myName) !== -1) {
// Add a little bit of delay (personal preference)
setTimeout(() => {
if (data.sender) {
data.resolve(data.source.format.format('action', 'hugs ' +
data.mention(data.sender)));
}
}, 1000);
}
});
}
@DependencyLoad('simplecommands')
addCommands(cmd: any): void {
const cmds: any = [];
if (this.config.config.simpleReplies) {
const simpleReply = this.config.config.simpleReplies;
for (const name in simpleReply) {
cmds.push({
name,
plugin: this.name,
execute: async (msg: IMessage): Promise<boolean> => {
msg.resolve(simpleReply[name]);
return true;
},
hidden: true
});
}
}
cmds.push({
name: 'hug',
plugin: this.name,
execute: async (msg: IMessage): Promise<boolean> => {
msg.resolve(msg.source.format.format('action', 'hugs %s'), msg.sender?.name);
return true;
},
hidden: true
});
cmds.push({
name: 'alpaca',
plugin: this.name,
execute: async (msg: IMessage): Promise<boolean> => {
const rand = Math.floor(Math.random() * alpaca.length);
msg.resolve('http://jocketf.se/c/' + alpaca[rand]);
return true;
},
hidden: true
});
cmd.registerCommand(cmds);
}
}
module.exports = FunPlugin;

9
gamedig/plugin.json Normal file
View File

@ -0,0 +1,9 @@
{
"main": "plugin.js",
"name": "gamedig",
"description": "Game server status string",
"version": "1.0.0",
"tags": ["commands", "games"],
"dependencies": ["simplecommands"],
"npmDependencies": ["gamedig@^2.0.23"]
}

116
gamedig/plugin.ts Normal file
View File

@ -0,0 +1,116 @@
import {
Plugin,
Configurable,
EventListener,
DependencyLoad
} from '@squeebot/core/lib/plugin';
import { IMessage } from '@squeebot/core/lib/types';
import { query } from 'gamedig';
interface IMinecraftType {
vanilla: {
raw: {
description: {
extra: [
{
text: string;
}
];
},
players: {
max: number,
online: number;
},
version: {
name: string;
}
}
};
}
@Configurable({
games: []
})
class GamePlugin extends Plugin {
@EventListener('pluginUnload')
public unloadEventHandler(plugin: string | Plugin): void {
if (plugin === this.name || plugin === this) {
this.emit('pluginUnloaded', this);
}
}
@DependencyLoad('simplecommands')
addCommands(cmd: any): void {
for (const i in this.config.get('games', [])) {
const game = this.config.get('games', [])[i];
if (game.game === 'minecraft' && game.host) {
const port = game.port || 25565;
const command: any = {
plugin: this.name,
name: 'minecraft',
execute: async (msg: IMessage, spec: any, prefix: string, ...simplified: any[]): Promise<boolean> => {
const keys: any[] = [];
let inclPlayers = false;
if (simplified[0] === 'players' || simplified[0] === 'online') {
inclPlayers = true;
}
keys.push(['field', 'Minecraft', { type: 'title' }]);
let state;
try {
state = await query({
type: 'minecraftping',
host: game.host,
port,
});
if (!state) {
throw new Error();
}
} catch (e) {
msg.resolve([['field', 'Server is offline.', { type: 'title' }]]);
return true;
}
if (inclPlayers) {
const players = [];
if (state.players.length > 0) {
for (const j in state.players) {
players.push(state.players[j].name);
}
}
keys.push(['field', players.length ? players.join(', ') : 'No players', { label: 'Players online' }]);
msg.resolve(keys);
return true;
}
keys.push(['field', state.connect, { label: 'Address' }]);
const raw = state.raw as IMinecraftType;
if (raw && raw.vanilla && raw.vanilla.raw) {
keys.push(['field', raw.vanilla.raw.description.extra[0].text
.replace(/§./g, '').replace(/\n/g, ' '), { label: 'MOTD', type: 'description' }]);
keys.push(['field', state.players.length + '/' + state.maxplayers, { label: 'Players' }]);
keys.push(['field', raw.vanilla.raw.version.name, { label: 'Version' }]);
}
msg.resolve(keys);
return true;
},
description: 'Minecraft server',
usage: '[players]',
aliases: ['mc']
};
if (game.rooms) {
command.source = game.rooms;
}
cmd.registerCommand(command);
}
}
}
}
module.exports = GamePlugin;

575
package-lock.json generated
View File

@ -4,6 +4,11 @@
"lockfileVersion": 1,
"requires": true,
"dependencies": {
"@sindresorhus/is": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.0.0.tgz",
"integrity": "sha512-FyD2meJpDPjyNQejSjvnhpgI/azsQkA4lGbuu5BQZfjvJ9cbRZXzeWL2HceCekW4lixO9JPesIIQkSoLjeJHNQ=="
},
"@squeebot/core": {
"version": "file:../core",
"requires": {
@ -451,12 +456,59 @@
}
}
},
"@szmarczak/http-timer": {
"version": "4.0.5",
"resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.5.tgz",
"integrity": "sha512-PyRA9sm1Yayuj5OIoJ1hGt2YISX45w9WcFbh6ddT0Z/0yaFxOtGLInr4jUfU1EAFVs0Yfyfev4RNwBlUaHdlDQ==",
"requires": {
"defer-to-connect": "^2.0.0"
}
},
"@types/cacheable-request": {
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.1.tgz",
"integrity": "sha512-ykFq2zmBGOCbpIXtoVbz4SKY5QriWPh3AjyU4G74RYbtt5yOc5OfaY75ftjg7mikMOla1CTGpX3lLbuJh8DTrQ==",
"requires": {
"@types/http-cache-semantics": "*",
"@types/keyv": "*",
"@types/node": "*",
"@types/responselike": "*"
}
},
"@types/cheerio": {
"version": "0.22.22",
"resolved": "https://registry.npmjs.org/@types/cheerio/-/cheerio-0.22.22.tgz",
"integrity": "sha512-05DYX4zU96IBfZFY+t3Mh88nlwSMtmmzSYaQkKN48T495VV1dkHSah6qYyDTN5ngaS0i0VonH37m+RuzSM0YiA==",
"dev": true,
"requires": {
"@types/node": "*"
}
},
"@types/convert-units": {
"version": "2.3.3",
"resolved": "https://registry.npmjs.org/@types/convert-units/-/convert-units-2.3.3.tgz",
"integrity": "sha512-oo4ZyjU0nZMDQFP0AKYk4KvFjydlSpbgXTCdF+TC3d/TL4b85/zi0W5pC97UrzXXpwsbcVEfS01nEgUqKgjSww==",
"dev": true
},
"@types/gamedig": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/@types/gamedig/-/gamedig-2.0.1.tgz",
"integrity": "sha512-lMMFmfcVfX4auHXaxyPGswwQn5e/SwoQ/OmdRvpGxf/TpoGgQAyr9UXMxeTfqbaisi1JfhdlyM5mNNcQ3fQI3Q==",
"dev": true
},
"@types/http-cache-semantics": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.0.tgz",
"integrity": "sha512-c3Xy026kOF7QOTn00hbIllV1dLR9hG9NkSrLQgCVs8NF6sBU+VGWjD3wLPhmh1TYAc7ugCFsvHYMN4VcBN1U1A=="
},
"@types/keyv": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.1.tgz",
"integrity": "sha512-MPtoySlAZQ37VoLaPcTHCu1RWJ4llDkULYZIzOYxlhxBqYPB0RsRlmMU0R6tahtFe27mIdkHV+551ZWV4PLmVw==",
"requires": {
"@types/node": "*"
}
},
"@types/mathjs": {
"version": "6.0.8",
"resolved": "https://registry.npmjs.org/@types/mathjs/-/mathjs-6.0.8.tgz",
@ -469,14 +521,119 @@
"@types/node": {
"version": "14.14.10",
"resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.10.tgz",
"integrity": "sha512-J32dgx2hw8vXrSbu4ZlVhn1Nm3GbeCFNw2FWL8S5QKucHGY0cyNwjdQdO+KMBZ4wpmC7KhLCiNsdk1RFRIYUQQ==",
"dev": true
"integrity": "sha512-J32dgx2hw8vXrSbu4ZlVhn1Nm3GbeCFNw2FWL8S5QKucHGY0cyNwjdQdO+KMBZ4wpmC7KhLCiNsdk1RFRIYUQQ=="
},
"@types/responselike": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.0.tgz",
"integrity": "sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA==",
"requires": {
"@types/node": "*"
}
},
"amdefine": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz",
"integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU="
},
"any-promise": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz",
"integrity": "sha1-q8av7tzqUugJzcA3au0845Y10X8="
},
"barse": {
"version": "0.4.3",
"resolved": "https://registry.npmjs.org/barse/-/barse-0.4.3.tgz",
"integrity": "sha1-KJhk15XQECu7sYHmbs0IxUobwMs=",
"requires": {
"readable-stream": "~1.0.2"
},
"dependencies": {
"readable-stream": {
"version": "1.0.34",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz",
"integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=",
"requires": {
"core-util-is": "~1.0.0",
"inherits": "~2.0.1",
"isarray": "0.0.1",
"string_decoder": "~0.10.x"
}
},
"string_decoder": {
"version": "0.10.31",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz",
"integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ="
}
}
},
"boolbase": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz",
"integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24="
},
"cacheable-lookup": {
"version": "5.0.3",
"resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-5.0.3.tgz",
"integrity": "sha512-W+JBqF9SWe18A72XFzN/V/CULFzPm7sBXzzR6ekkE+3tLG72wFZrBiBZhrZuDoYexop4PHJVdFAKb/Nj9+tm9w=="
},
"cacheable-request": {
"version": "7.0.1",
"resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-7.0.1.tgz",
"integrity": "sha512-lt0mJ6YAnsrBErpTMWeu5kl/tg9xMAWjavYTN6VQXM1A/teBITuNcccXsCxF0tDQQJf9DfAaX5O4e0zp0KlfZw==",
"requires": {
"clone-response": "^1.0.2",
"get-stream": "^5.1.0",
"http-cache-semantics": "^4.0.0",
"keyv": "^4.0.0",
"lowercase-keys": "^2.0.0",
"normalize-url": "^4.1.0",
"responselike": "^2.0.0"
}
},
"cheerio": {
"version": "1.0.0-rc.3",
"resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0-rc.3.tgz",
"integrity": "sha512-0td5ijfUPuubwLUu0OBoe98gZj8C/AA+RW3v67GPlGOrvxWjZmBXiBCRU+I8VEiNyJzjth40POfHiz2RB3gImA==",
"requires": {
"css-select": "~1.2.0",
"dom-serializer": "~0.1.1",
"entities": "~1.1.1",
"htmlparser2": "^3.9.1",
"lodash": "^4.15.0",
"parse5": "^3.0.1"
}
},
"clone-response": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.2.tgz",
"integrity": "sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws=",
"requires": {
"mimic-response": "^1.0.0"
}
},
"commander": {
"version": "2.8.1",
"resolved": "https://registry.npmjs.org/commander/-/commander-2.8.1.tgz",
"integrity": "sha1-Br42f+v9oMMwqh4qBy09yXYkJdQ=",
"requires": {
"graceful-readlink": ">= 1.0.0"
}
},
"complex.js": {
"version": "2.0.11",
"resolved": "https://registry.npmjs.org/complex.js/-/complex.js-2.0.11.tgz",
"integrity": "sha512-6IArJLApNtdg1P1dFtn3dnyzoZBEF0MwMnrfF1exSBRpZYoy4yieMkpZhQDC0uwctw48vii0CFVyHfpgZ/DfGw=="
},
"compressjs": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/compressjs/-/compressjs-1.0.3.tgz",
"integrity": "sha1-ldt03VuQOM+AvKMhqw7eJxtJWbY=",
"requires": {
"amdefine": "~1.0.0",
"commander": "~2.8.1"
}
},
"convert-units": {
"version": "2.3.4",
"resolved": "https://registry.npmjs.org/convert-units/-/convert-units-2.3.4.tgz",
@ -486,26 +643,239 @@
"lodash.keys": "2.3.x"
}
},
"core-util-is": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
"integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac="
},
"css-select": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/css-select/-/css-select-1.2.0.tgz",
"integrity": "sha1-KzoRBTnFNV8c2NMUYj6HCxIeyFg=",
"requires": {
"boolbase": "~1.0.0",
"css-what": "2.1",
"domutils": "1.5.1",
"nth-check": "~1.0.1"
}
},
"css-what": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/css-what/-/css-what-2.1.3.tgz",
"integrity": "sha512-a+EPoD+uZiNfh+5fxw2nO9QwFa6nJe2Or35fGY6Ipw1R3R4AGz1d1TEZrCegvw2YTmZ0jXirGYlzxxpYSHwpEg=="
},
"decimal.js": {
"version": "10.2.1",
"resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.2.1.tgz",
"integrity": "sha512-KaL7+6Fw6i5A2XSnsbhm/6B+NuEA7TZ4vqxnd5tXz9sbKtrN9Srj8ab4vKVdK8YAqZO9P1kg45Y6YLoduPf+kw=="
},
"decompress-response": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz",
"integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==",
"requires": {
"mimic-response": "^3.1.0"
},
"dependencies": {
"mimic-response": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz",
"integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ=="
}
}
},
"defer-to-connect": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.0.tgz",
"integrity": "sha512-bYL2d05vOSf1JEZNx5vSAtPuBMkX8K9EUutg7zlKvTqKXHt7RhWJFbmd7qakVuf13i+IkGmp6FwSsONOf6VYIg=="
},
"dom-serializer": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.1.1.tgz",
"integrity": "sha512-l0IU0pPzLWSHBcieZbpOKgkIn3ts3vAh7ZuFyXNwJxJXk/c4Gwj9xaTJwIDVQCXawWD0qb3IzMGH5rglQaO0XA==",
"requires": {
"domelementtype": "^1.3.0",
"entities": "^1.1.1"
}
},
"domelementtype": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz",
"integrity": "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w=="
},
"domhandler": {
"version": "2.4.2",
"resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.4.2.tgz",
"integrity": "sha512-JiK04h0Ht5u/80fdLMCEmV4zkNh2BcoMFBmZ/91WtYZ8qVXSKjiw7fXMgFPnHcSZgOo3XdinHvmnDUeMf5R4wA==",
"requires": {
"domelementtype": "1"
}
},
"domutils": {
"version": "1.5.1",
"resolved": "https://registry.npmjs.org/domutils/-/domutils-1.5.1.tgz",
"integrity": "sha1-3NhIiib1Y9YQeeSMn3t+Mjc2gs8=",
"requires": {
"dom-serializer": "0",
"domelementtype": "1"
}
},
"end-of-stream": {
"version": "1.4.4",
"resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz",
"integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==",
"requires": {
"once": "^1.4.0"
}
},
"entities": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/entities/-/entities-1.1.2.tgz",
"integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w=="
},
"escape-latex": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/escape-latex/-/escape-latex-1.2.0.tgz",
"integrity": "sha512-nV5aVWW1K0wEiUIEdZ4erkGGH8mDxGyxSeqPzRNtWP7ataw+/olFObw7hujFWlVjNsaDFw5VZ5NzVSIqRgfTiw=="
},
"event-to-promise": {
"version": "0.7.0",
"resolved": "https://registry.npmjs.org/event-to-promise/-/event-to-promise-0.7.0.tgz",
"integrity": "sha1-ywffzUGNoiIdkPd+q3E7wjXiCQ8="
},
"fraction.js": {
"version": "4.0.12",
"resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.0.12.tgz",
"integrity": "sha512-8Z1K0VTG4hzYY7kA/1sj4/r1/RWLBD3xwReT/RCrUCbzPszjNQCCsy3ktkU/eaEqX3MYa4pY37a52eiBlPMlhA=="
},
"gamedig": {
"version": "2.0.23",
"resolved": "https://registry.npmjs.org/gamedig/-/gamedig-2.0.23.tgz",
"integrity": "sha512-RNVRjdnPK1PaeB0ypqkqZQZnCIkINPs5d5ynQFUsptuNwL1rXjKwFOhYhM3euFYq3zOiaGd6yJfnBkizgDWlQg==",
"requires": {
"cheerio": "^1.0.0-rc.3",
"compressjs": "^1.0.2",
"gbxremote": "^0.2.1",
"got": "^11.5.1",
"iconv-lite": "^0.6.2",
"long": "^4.0.0",
"minimist": "^1.2.5",
"moment": "^2.27.0",
"punycode": "^2.1.1",
"varint": "^5.0.0"
}
},
"gbxremote": {
"version": "0.2.1",
"resolved": "https://registry.npmjs.org/gbxremote/-/gbxremote-0.2.1.tgz",
"integrity": "sha1-hN9PvXgXgNxdaS0krASi1/Bd23w=",
"requires": {
"any-promise": "^1.1.0",
"barse": "~0.4.2",
"event-to-promise": "^0.7.0",
"string-to-stream": "^1.0.1",
"xmlrpc": "^1.3.1"
}
},
"get-stream": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz",
"integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==",
"requires": {
"pump": "^3.0.0"
}
},
"got": {
"version": "11.8.0",
"resolved": "https://registry.npmjs.org/got/-/got-11.8.0.tgz",
"integrity": "sha512-k9noyoIIY9EejuhaBNLyZ31D5328LeqnyPNXJQb2XlJZcKakLqN5m6O/ikhq/0lw56kUYS54fVm+D1x57YC9oQ==",
"requires": {
"@sindresorhus/is": "^4.0.0",
"@szmarczak/http-timer": "^4.0.5",
"@types/cacheable-request": "^6.0.1",
"@types/responselike": "^1.0.0",
"cacheable-lookup": "^5.0.3",
"cacheable-request": "^7.0.1",
"decompress-response": "^6.0.0",
"http2-wrapper": "^1.0.0-beta.5.2",
"lowercase-keys": "^2.0.0",
"p-cancelable": "^2.0.0",
"responselike": "^2.0.0"
}
},
"graceful-readlink": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/graceful-readlink/-/graceful-readlink-1.0.1.tgz",
"integrity": "sha1-TK+tdrxi8C+gObL5Tpo906ORpyU="
},
"htmlparser2": {
"version": "3.10.1",
"resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.10.1.tgz",
"integrity": "sha512-IgieNijUMbkDovyoKObU1DUhm1iwNYE/fuifEoEHfd1oZKZDaONBSkal7Y01shxsM49R4XaMdGez3WnF9UfiCQ==",
"requires": {
"domelementtype": "^1.3.1",
"domhandler": "^2.3.0",
"domutils": "^1.5.1",
"entities": "^1.1.1",
"inherits": "^2.0.1",
"readable-stream": "^3.1.1"
}
},
"http-cache-semantics": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz",
"integrity": "sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ=="
},
"http2-wrapper": {
"version": "1.0.0-beta.5.2",
"resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-1.0.0-beta.5.2.tgz",
"integrity": "sha512-xYz9goEyBnC8XwXDTuC/MZ6t+MrKVQZOk4s7+PaDkwIsQd8IwqvM+0M6bA/2lvG8GHXcPdf+MejTUeO2LCPCeQ==",
"requires": {
"quick-lru": "^5.1.1",
"resolve-alpn": "^1.0.0"
}
},
"iconv-lite": {
"version": "0.6.2",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.2.tgz",
"integrity": "sha512-2y91h5OpQlolefMPmUlivelittSWy0rP+oYVpn6A7GwVHNE8AWzoYOBNmlwks3LobaJxgHCYZAnyNo2GgpNRNQ==",
"requires": {
"safer-buffer": ">= 2.1.2 < 3.0.0"
}
},
"inherits": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
},
"isarray": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
"integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8="
},
"javascript-natural-sort": {
"version": "0.7.1",
"resolved": "https://registry.npmjs.org/javascript-natural-sort/-/javascript-natural-sort-0.7.1.tgz",
"integrity": "sha1-+eIwPUUH9tdDVac2ZNFED7Wg71k="
},
"json-buffer": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz",
"integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ=="
},
"keyv": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/keyv/-/keyv-4.0.3.tgz",
"integrity": "sha512-zdGa2TOpSZPq5mU6iowDARnMBZgtCqJ11dJROFi6tg6kTn4nuUdU09lFyLFSaHrWqpIJ+EBq4E8/Dc0Vx5vLdA==",
"requires": {
"json-buffer": "3.0.1"
}
},
"lodash": {
"version": "4.17.20",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz",
"integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA=="
},
"lodash._basebind": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/lodash._basebind/-/lodash._basebind-2.3.0.tgz",
@ -660,6 +1030,16 @@
"lodash._renative": "~2.3.0"
}
},
"long": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz",
"integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA=="
},
"lowercase-keys": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz",
"integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA=="
},
"mathjs": {
"version": "8.0.1",
"resolved": "https://registry.npmjs.org/mathjs/-/mathjs-8.0.1.tgz",
@ -675,11 +1055,173 @@
"typed-function": "^2.0.0"
}
},
"mimic-response": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz",
"integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ=="
},
"minimist": {
"version": "1.2.5",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw=="
},
"moment": {
"version": "2.29.1",
"resolved": "https://registry.npmjs.org/moment/-/moment-2.29.1.tgz",
"integrity": "sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ=="
},
"normalize-url": {
"version": "4.5.0",
"resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.0.tgz",
"integrity": "sha512-2s47yzUxdexf1OhyRi4Em83iQk0aPvwTddtFz4hnSSw9dCEsLEGf6SwIO8ss/19S9iBb5sJaOuTvTGDeZI00BQ=="
},
"nth-check": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/nth-check/-/nth-check-1.0.2.tgz",
"integrity": "sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg==",
"requires": {
"boolbase": "~1.0.0"
}
},
"once": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
"integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
"requires": {
"wrappy": "1"
}
},
"p-cancelable": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.0.0.tgz",
"integrity": "sha512-wvPXDmbMmu2ksjkB4Z3nZWTSkJEb9lqVdMaCKpZUGJG9TMiNp9XcbG3fn9fPKjem04fJMJnXoyFPk2FmgiaiNg=="
},
"parse5": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/parse5/-/parse5-3.0.3.tgz",
"integrity": "sha512-rgO9Zg5LLLkfJF9E6CCmXlSE4UVceloys8JrFqCcHloC3usd/kJCyPDwH2SOlzix2j3xaP9sUX3e8+kvkuleAA==",
"requires": {
"@types/node": "*"
}
},
"process-nextick-args": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
"integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag=="
},
"pump": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz",
"integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==",
"requires": {
"end-of-stream": "^1.1.0",
"once": "^1.3.1"
}
},
"punycode": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
"integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A=="
},
"quick-lru": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz",
"integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA=="
},
"readable-stream": {
"version": "3.6.0",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
"integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
"requires": {
"inherits": "^2.0.3",
"string_decoder": "^1.1.1",
"util-deprecate": "^1.0.1"
}
},
"resolve-alpn": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.0.0.tgz",
"integrity": "sha512-rTuiIEqFmGxne4IovivKSDzld2lWW9QCjqv80SYjPgf+gS35eaCAjaP54CCwGAwBtnCsvNLYtqxe1Nw+i6JEmA=="
},
"responselike": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/responselike/-/responselike-2.0.0.tgz",
"integrity": "sha512-xH48u3FTB9VsZw7R+vvgaKeLKzT6jOogbQhEe/jewwnZgzPcnyWui2Av6JpoYZF/91uueC+lqhWqeURw5/qhCw==",
"requires": {
"lowercase-keys": "^2.0.0"
}
},
"safe-buffer": {
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
"integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="
},
"safer-buffer": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
},
"sax": {
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz",
"integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw=="
},
"seedrandom": {
"version": "3.0.5",
"resolved": "https://registry.npmjs.org/seedrandom/-/seedrandom-3.0.5.tgz",
"integrity": "sha512-8OwmbklUNzwezjGInmZ+2clQmExQPvomqjL7LFqOYqtmuxRgQYqOD3mHaU+MvZn5FLUeVxVfQjwLZW/n/JFuqg=="
},
"string-to-stream": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/string-to-stream/-/string-to-stream-1.1.1.tgz",
"integrity": "sha512-QySF2+3Rwq0SdO3s7BAp4x+c3qsClpPQ6abAmb0DGViiSBAkT5kL6JT2iyzEVP+T1SmzHrQD1TwlP9QAHCc+Sw==",
"requires": {
"inherits": "^2.0.1",
"readable-stream": "^2.1.0"
},
"dependencies": {
"isarray": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
"integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE="
},
"readable-stream": {
"version": "2.3.7",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",
"integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==",
"requires": {
"core-util-is": "~1.0.0",
"inherits": "~2.0.3",
"isarray": "~1.0.0",
"process-nextick-args": "~2.0.0",
"safe-buffer": "~5.1.1",
"string_decoder": "~1.1.1",
"util-deprecate": "~1.0.1"
}
},
"safe-buffer": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
},
"string_decoder": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
"requires": {
"safe-buffer": "~5.1.0"
}
}
}
},
"string_decoder": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
"integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
"requires": {
"safe-buffer": "~5.2.0"
}
},
"tiny-emitter": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/tiny-emitter/-/tiny-emitter-2.1.0.tgz",
@ -694,6 +1236,35 @@
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.1.2.tgz",
"integrity": "sha512-thGloWsGH3SOxv1SoY7QojKi0tc+8FnOmiarEGMbd/lar7QOEd3hvlx3Fp5y6FlDUGl9L+pd4n2e+oToGMmhRQ=="
},
"util-deprecate": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
"integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8="
},
"varint": {
"version": "5.0.2",
"resolved": "https://registry.npmjs.org/varint/-/varint-5.0.2.tgz",
"integrity": "sha512-lKxKYG6H03yCZUpAGOPOsMcGxd1RHCu1iKvEHYDPmTyq2HueGhD73ssNBqqQWfvYs04G9iUFRvmAVLW20Jw6ow=="
},
"wrappy": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
},
"xmlbuilder": {
"version": "8.2.2",
"resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-8.2.2.tgz",
"integrity": "sha1-aSSGc0ELS6QuGmE2VR0pIjNap3M="
},
"xmlrpc": {
"version": "1.3.2",
"resolved": "https://registry.npmjs.org/xmlrpc/-/xmlrpc-1.3.2.tgz",
"integrity": "sha1-JrLqNHhI0Ciqx+dRS1NRl23j6D0=",
"requires": {
"sax": "1.2.x",
"xmlbuilder": "8.2.x"
}
}
}
}

View File

@ -12,12 +12,16 @@
"license": "ISC",
"dependencies": {
"@squeebot/core": "file:../core",
"cheerio": "^1.0.0-rc.3",
"convert-units": "^2.3.4",
"gamedig": "^2.0.23",
"mathjs": "^8.0.1",
"typescript": "^4.1.2"
},
"devDependencies": {
"@types/cheerio": "^0.22.22",
"@types/convert-units": "^2.3.3",
"@types/gamedig": "^2.0.1",
"@types/mathjs": "^6.0.8",
"@types/node": "^14.14.10"
}

View File

@ -1,6 +1,34 @@
{
"name": "plugins-evert",
"plugins": [
{
"name": "debug",
"version": "1.0.0"
},
{
"name": "diction",
"version": "1.0.0"
},
{
"name": "fun",
"version": "1.0.0"
},
{
"name": "gamedig",
"version": "1.0.0"
},
{
"name": "timezone",
"version": "1.0.0"
},
{
"name": "url-fediverse",
"version": "1.0.0"
},
{
"name": "urlreply",
"version": "1.0.0"
},
{
"name": "utility",
"version": "3.0.0"

9
timezone/plugin.json Normal file
View File

@ -0,0 +1,9 @@
{
"main": "plugin.js",
"name": "timezone",
"description": "Show time from different time zones",
"version": "1.0.0",
"tags": ["commands", "utility", "time"],
"dependencies": ["simplecommands"],
"npmDependencies": []
}

64
timezone/plugin.ts Normal file
View File

@ -0,0 +1,64 @@
import {
Plugin,
Configurable,
EventListener,
DependencyLoad
} from '@squeebot/core/lib/plugin';
import { IMessage } from '@squeebot/core/lib/types';
const tz: {[key: string]: number} = {'UTC-12': -12, 'UTC-11': -11, 'UTC-10': -10, 'UTC-9': -9, 'UTC-9:30': -9.5, 'UTC-8': -8, 'UTC-7': -7, 'UTC-6': -6, 'UTC-5': -5, 'UTC-4': -4, 'UTC-3': -3, 'UTC-3:30': -3.5, 'UTC-2': -2, 'UTC-1': -1, UTC: 0, 'UTC+1': 1, 'UTC+2': 2, 'UTC+3': 3, 'UTC+4': 4, 'UTC+4:30': 4.5, 'UTC+5': 5, 'UTC+5:30': 5.5, 'UTC+5:45': 5.75, 'UTC+6': 6, 'UTC+6:30': 6.5, 'UTC+7': 7, 'UTC+8': 8, 'UTC+8:30': 8.5, 'UTC+8:45': 8.75, 'UTC+9': 9, 'UTC+9:30': 9.5, 'UTC+10': 10, 'UTC+10:30': 10.5, 'UTC+11': 11, 'UTC+12': 12, 'UTC+12:45': 12.75, 'UTC+13': 13, 'UTC+13:45': 13.75, 'UTC+14': 14, BST: 1, CET: 1, DFT: 1, IST: 1, MET: 1, WAT: 1, WEST: 1, CAT: 2, CEST: 2, EET: 2, HAEC: 2, MEST: 2, SAST: 2, USZ1: 2, WAST: 2, AST: 3, EAT: 3, EEST: 3, FET: 3, IDT: 3, IOT: 3, MSK: 3, SYOT: 3, TRT: 3, IRST: 3.5, AMT: 4, AZT: 4, GET: 4, GST: 4, MUT: 4, RET: 4, SAMT: 4, SCT: 4, VOLT: 4, AFT: 4.5, IRDT: 4.5, MAWT: 5, MVT: 5, ORAT: 5, PKT: 5, TFT: 5, TJT: 5, TMT: 5, UZT: 5, YEKT: 5, IndianST: 5.5, SLST: 5.5, NPT: 5.75, BIOT: 6, BTT: 6, KGT: 6, OMST: 6, VOST: 6, CCT: 6.5, MMT: 6.5, ACT: 6.5, CXT: 7, DAVT: 7, HOVT: 7, ICT: 7, KRAT: 7, THA: 7, WIT: 7, AWST: 8, BDT: 8, CHOT: 8, CIT: 8, CT: 8, HKT: 8, HOVST: 8, IRKT: 8, MST: 8, MYT: 8, PHT: 8, SGT: 8, SST: 8, ULAT: 8, WST: 8, CWST: 8.5, CHOST: 9, EIT: 9, JST: 9, KST: 9, TLT: 9, ULAST: 9, YAKT: 9, ACST: 9.5, AEST: 10, CHST: 10, CHUT: 10, DDUT: 10, PGT: 10, VLAT: 10, ACDT: 10.5, LHST: 10.5, AEDT: 11, KOST: 11, MIST: 11, NCT: 11, NFT: 11, PONT: 11, SAKT: 11, SBT: 11, SRET: 11, VUT: 11, FJT: 12, GILT: 12, MAGT: 12, MHT: 12, NZST: 12, PETT: 12, TVT: 12, WAKT: 12, CHAST: 12.75, NZDT: 13, PHOT: 13, TKT: 13, TOT: 13, CHADT: 13.75, LINT: 14, AZOT: -1, CVT: -1, EGT: -1, BRST: -2, FNT: -2, PMDT: -2, UYST: -2, NDT: -2.5, ADT: -3, AMST: -3, ART: -3, BRT: -3, CLST: -3, FKST: -3, GFT: -3, PMST: -3, PYST: -3, ROTT: -3, UYT: -3, NST: -3.5, NT: -3.5, AtST: -4, BOT: -4, CLT: -4, COST: -4, ECT: -4, EDT: -4, FKT: -4, GYT: -4, PYT: -4, VET: -4, CDT: -5, COT: -5, EASST: -5, EST: -5, PET: -5, CST: -6, EAST: -6, GALT: -6, MDT: -6, PDT: -7, AKDT: -8, CIST: -8, PST: -8, AKST: -9, GAMT: -9, GIT: -9, HADT: -9, MART: -9.5, MIT: -9.5, CKT: -10, HAST: -10, TAHT: -10, NUT: -11, BIT: -12, GMT: 0, WET: 0};
// Offset timezone from UTC
function offsetTZ(offset: number): number {
const utc = new Date(new Date().toUTCString()).getTime();
return utc + 3600000 * offset;
}
// Offset timezone from UTC (readable string)
function offsetTZStr(offset: number, tzn: string | null): string {
const real = offset >= 0 ? '+' + offset : offset;
return new Date(offsetTZ(offset)).toUTCString().replace('GMT', 'UTC') + real + (tzn ? ' (' + tzn + ')' : '');
}
function timeNow(short: string): string | null {
short = short.toUpperCase();
const offset = tz[short];
if (offset == null) { return null; }
return offsetTZStr(offset, short.indexOf('UTC') !== 0 ? short : null);
}
@Configurable({
timezone: 'UTC'
})
class TZPlugin extends Plugin {
@EventListener('pluginUnload')
public unloadEventHandler(plugin: string | Plugin): void {
if (plugin === this.name || plugin === this) {
this.emit('pluginUnloaded', this);
}
}
@DependencyLoad('simplecommands')
addCommands(cmd: any): void {
cmd.registerCommand({
name: 'timezone',
plugin: this.name,
execute: async (msg: IMessage, spec: any, prefix: string, ...simplified: any[]): Promise<boolean> => {
const cfg = this.config.get('timezone', 'UTC');
const tn = timeNow(simplified[0] || cfg);
if (!tn) {
msg.resolve('Unknown or unlisted timezone abbreviation');
return true;
}
msg.resolve(tn);
return true;
},
aliases: ['tz'],
description: 'Date and Time in a certain time zone',
usage: '[abbrv]|UTC<+|-><offset>'
});
}
}
module.exports = TZPlugin;

View File

@ -0,0 +1,9 @@
{
"main": "plugin.js",
"name": "url-fediverse",
"description": "URLReply Fediverse",
"version": "1.0.0",
"tags": ["urlreply"],
"dependencies": ["urlreply"],
"npmDependencies": []
}

99
url-fediverse/plugin.ts Normal file
View File

@ -0,0 +1,99 @@
import { httpGET, sanitizeEscapedText } from '@squeebot/core/lib/common';
import {
Plugin,
EventListener,
DependencyLoad
} from '@squeebot/core/lib/plugin';
import { IMessage } from '@squeebot/core/lib/types';
async function mastodonResponse(msg: IMessage, url: string, type = 'Mastodon'): Promise<boolean> {
let murl;
if (type === 'Mastodon') {
murl = url.match(/^(https?):\/\/((?:[\w\d-]+\.)*[\w\d-]+\.\w{2,16})\/(?:users\/)?@?([\w-_]+)\/(?:statuses\/)?(\d+[^&#?\s/])/i);
} else if (type === 'Pleroma') {
murl = url.match(/^(https?):\/\/((?:[\w\d-]+\.)*[\w\d-]+\.\w{2,16})\/(notice)\/([^&#?\s/]+)/i);
}
if (!murl) {
return false;
}
const url2go = `${murl[1]}://${murl[2]}/api/v1/statuses/${murl[4]}`;
let data;
try {
data = await httpGET(url2go);
if (!data) {
throw new Error('No API response, probably not ' + type + ' after all.')
}
data = JSON.parse(data);
} catch (e) {
return false;
}
let content = data.content;
if (!content) {
return false;
}
if (data.spoiler_text) {
content = '[CW] ' + data.spoiler_text;
}
const keys = [];
let end = sanitizeEscapedText(content.replace(/<\/p>/g, '\n') // Add newlines to paragraph endings
.replace(/<br \/>/g, '\n') // Add newlines instead of <br />
.replace(/(<([^>]+)>)/ig, '')); // Strip the rest of the HTML out
if (end.length > 220) {
end = end.substring(0, 220) + '…';
}
keys.push(['field', type, { color: 'cyan', type: 'title' }]);
keys.push(['field', data.favourites_count.toString(), { color: 'red', label: ['★', 'Favourites'], type: 'metric' }]);
keys.push(['field', data.reblogs_count.toString(), { color: 'green', label: ['↱↲', 'Reblogs'], type: 'metric' }]);
keys.push(['bold', '@' + data.account.acct + ':']);
keys.push(['field', end, { type: 'content' }]);
msg.resolve(keys);
return true;
}
class FediResponsePlugin extends Plugin {
@EventListener('pluginUnload')
public unloadEventHandler(plugin: string | Plugin): void {
if (plugin === this.name || plugin === this) {
this.emit('pluginUnloaded', this);
}
}
@DependencyLoad('urlreply')
addUrlReply(urlreply: any): void {
urlreply.registerHandler(this.name, 'html/mastodon',
async (url: string, msg: IMessage, title: string, body: any): Promise<boolean> => {
const type = title === 'Pleroma' ? 'Pleroma' : 'Mastodon';
let pass = false;
if (type === 'Pleroma') {
pass = true;
} else {
const tag = body('a[href="https://joinmastodon.org/"]');
if (tag && tag.text() && tag.text().indexOf('Mastodon') !== -1) {
pass = true;
}
}
if (pass) {
const mastodonTest = await mastodonResponse(msg, url, type);
if (mastodonTest) {
return true;
}
}
return false;
});
}
}
module.exports = FediResponsePlugin;

9
urlreply/plugin.json Normal file
View File

@ -0,0 +1,9 @@
{
"main": "plugin.js",
"name": "urlreply",
"description": "Fetch titles from web pages, specifically made for IRC",
"version": "1.0.0",
"tags": ["irc"],
"dependencies": [],
"npmDependencies": ["cheerio@^1.0.0-rc.3"]
}

384
urlreply/plugin.ts Normal file
View File

@ -0,0 +1,384 @@
import { httpGET, parseTimeToSeconds, toHHMMSS } from '@squeebot/core/lib/common';
import { logger } from '@squeebot/core/lib/core';
import {
Plugin,
Configurable,
EventListener,
} from '@squeebot/core/lib/plugin';
import { IMessage } from '@squeebot/core/lib/types';
import cheerio from 'cheerio';
import * as urllib from 'url';
interface URLHandler {
plugin: string;
action: Function;
}
let urlHandlers: {[key: string]: URLHandler} = {};
let htmlHandlers: any[] = [];
const urlRegex = /(((ftp|https?):\/\/)[-\w@:%_+.~#?,&//=]+)/g;
function findUrls(text: string): string[] {
const source = (text || '').toString();
const urlArray = [];
let matchArray = urlRegex.exec(source);
while (matchArray !== null) {
urlArray.push(matchArray[0]);
matchArray = urlRegex.exec(source);
}
return urlArray;
}
// http://stackoverflow.com/a/22149575
function ytDuration(source: string): string {
const a = source.match(/\d+/g);
let res;
if (!a) {
return '';
}
if (source.indexOf('M') >= 0 && source.indexOf('H') === -1 && source.indexOf('S') === -1) {
res = [0, a[0], 0];
}
if (source.indexOf('H') >= 0 && source.indexOf('M') === -1) {
res = [a[0], 0, a[1]];
}
if (source.indexOf('H') >= 0 && source.indexOf('M') === -1 && source.indexOf('S') === -1) {
res = [a[0], 0, 0];
}
let duration = 0;
if (a.length === 3) {
duration = duration + parseInt(a[0], 10) * 3600;
duration = duration + parseInt(a[1], 10) * 60;
duration = duration + parseInt(a[2], 10);
}
if (a.length === 2) {
duration = duration + parseInt(a[0], 10) * 60;
duration = duration + parseInt(a[1], 10);
}
if (a.length === 1) {
duration = duration + parseInt(a[0], 10);
}
return toHHMMSS(duration.toString());
}
async function dailymotionFromId(id: string, msg: IMessage): Promise<boolean> {
const url = 'https://api.dailymotion.com/video/' + id + '?fields=id,title,owner,owner.screenname,duration,views_total';
let data = await httpGET(url);
try {
data = JSON.parse(data);
} catch (e) {
return false;
}
const keys = [];
keys.push(['field', 'Dailymotion', { type: 'title' }]);
keys.push(['field', data.title, { type: 'description' }]);
keys.push(['field', data.views_total.toString(), { label: 'Views', type: 'metric' }]);
keys.push(['field', data.duration.toString(), { label: 'Duration', type: 'duration' }]);
keys.push(['field', data['owner.screenname'], { label: ['By'] }]);
msg.resolve(keys);
return true;
}
async function getSoundcloudFromUrl(plugin: URLReplyPlugin, url: string, msg: IMessage): Promise<boolean> {
const token = plugin.config.get('tokens.soundcloud');
if (!token) {
return false;
}
const sAPI = 'https://api.soundcloud.com/resolve?url=' + encodeURIComponent(url) + '&client_id=' + token;
let data = await httpGET(sAPI);
try {
data = JSON.parse(data);
} catch (e) {
return false;
}
if (!data) {
return false;
}
const keys = [];
keys.push(['field', 'SoundCloud' + ((data.kind === 'playlist') ? ' Playlist' : ''), { type: 'title', color: 'gold' }]);
keys.push(['field', data.title, { type: 'description' }]);
if (data.kind === 'track') {
keys.push(['field', data.playback_count.toString(), { color: 'green', label: ['▶', 'Plays'], type: 'metric' }]);
keys.push(['field', data.favoritings_count.toString(), { color: 'red', label: ['♥', 'Favourites'], type: 'metric' }]);
} else {
keys.push(['field', data.track_count.toString(), { label: 'Tracks', type: 'metric' }]);
}
keys.push(['field', Math.floor(data.duration / 1000).toString(), { label: 'Duration', type: 'duration' }]);
keys.push(['field', data.user.username, { label: ['By'] }]);
msg.resolve(keys);
return true;
}
async function getYoutubeFromVideo(
plugin: URLReplyPlugin,
id: string,
full: urllib.URL,
msg: IMessage): Promise<boolean> {
const gtoken = plugin.config.get('tokens.google');
if (!gtoken) {
return false;
}
const gAPI = 'https://www.googleapis.com/youtube/v3/videos?id=' + id + '&key=' +
gtoken + '&part=snippet,contentDetails,statistics';
let data = await httpGET(gAPI);
try {
data = JSON.parse(data);
} catch (e) {
return false;
}
if (!data || !data.items || !data.items.length) {
msg.resolve('Video does not exist or is private.');
return false;
}
const vid = data.items[0];
const time = full.searchParams.get('t');
let live = false;
if (!vid.snippet) {
return false;
}
const keys = [];
if (vid.snippet.liveBroadcastContent === 'live') {
live = true;
keys.push(['field', '[LIVE]', {color: 'red'}]);
}
if (time) {
let tag = parseInt(time, 10);
if (tag != null && !isNaN(tag)) {
if (time.indexOf('s') !== -1 || time.indexOf('m') !== -1 || time.indexOf('h') !== -1) {
tag = parseTimeToSeconds(time);
}
keys.push(['field', `[${toHHMMSS(tag)}]`, {color: 'red'}]);
}
}
keys.push(['field', 'You' + msg.source.format.color('brown', 'Tube'), { color: 'white', type: 'title' }]);
keys.push(['field', vid.snippet.title, {type: 'description'}]);
keys.push(['field', vid.statistics.viewCount.toString(), { type: 'metric', label: 'Views' }]);
if (!live) {
keys.push(['field', ytDuration(vid.contentDetails.duration.toString()), { label: 'Duration' }]);
}
if (vid.statistics && vid.statistics.likeCount != null) {
const likeCount = vid.statistics.likeCount.toString();
const dislikeCount = vid.statistics.dislikeCount.toString();
keys.push(['field', likeCount, { color: 'limegreen', label: ['▲', 'Likes'], type: 'metric' }]);
keys.push(['field', dislikeCount, { color: 'red', label: ['▼', 'Dislikes'], type: 'metric' }]);
}
keys.push(['field', vid.snippet.channelTitle, { label: ['By'] }]);
msg.resolve(keys);
return true;
}
@Configurable({
tokens: {
google: null,
soundcloud: null
}
})
class URLReplyPlugin extends Plugin {
registerHandler(plugin: string, match: string, handler: Function): void {
if (!handler || typeof handler !== 'function') {
throw new Error('Expected handler function as third argument.');
}
if (plugin !== 'urlreply') {
logger.log('[urlreply] %s has added an handler for "%s"', plugin, match);
}
if (match.indexOf('html') === 0) {
htmlHandlers.push([plugin, handler]);
} else {
urlHandlers[match] = {
plugin, action: handler
};
}
}
resetHandlers(): void {
urlHandlers = {};
htmlHandlers = [];
// YouTube
this.registerHandler(this.name, 'youtube.com/', async (url: urllib.URL, msg: IMessage, data: any) => {
const det = url.searchParams.get('v');
if (!det) {
return false;
}
return getYoutubeFromVideo.apply(this, [this, det, url, msg]);
});
this.registerHandler(this.name, 'youtu.be/', async (url: urllib.URL, msg: IMessage, data: any) => {
const det = url.pathname.substring(1);
if (!det) {
return false;
}
return getYoutubeFromVideo.apply(this, [this, det, url, msg]);
});
// Dailymotion
this.registerHandler(this.name, 'dailymotion.com/video/', async (url: urllib.URL, msg: IMessage, data: any) => {
const det = url.href.match('/video/([^?&#]*)');
if (!det) {
return false;
}
return dailymotionFromId.apply(this, [det[1], msg]);
});
// SoundCloud
this.registerHandler(this.name, 'soundcloud.com/', async (url: urllib.URL, msg: IMessage, data: any) => {
return getSoundcloudFromUrl.apply(this, [this, url.href, msg]);
});
}
@EventListener('pluginUnload')
public unloadEventHandler(plugin: string | Plugin): void {
if (plugin === this.name || plugin === this) {
this.emit('pluginUnloaded', this);
}
}
initialize(): void {
this.resetHandlers();
this.on('message', async (msg: IMessage) => {
// Find URLS in the message
const urls = findUrls(msg.text);
if (!urls.length) {
return;
}
const url = urls[0];
let matched = false;
// Find handlers matching this URL
for (const handler in urlHandlers) {
const obj = urlHandlers[handler];
if (url.indexOf(handler) !== -1 || !obj.action) {
try {
const urlParsed = new urllib.URL(url);
matched = await obj.action.apply(this, [urlParsed, msg]);
} catch (e) {
logger.error(e.stack);
matched = false;
}
break;
}
}
// If there were no matches, pull the title of the website
if (!matched) {
try {
const data = await httpGET(url, {}, true);
if (!data) { return; }
const full = cheerio.load(data);
const head = full('head');
if (!head) { return; }
const titleEl = head.find('title');
if (!titleEl || !titleEl.length) { return; }
let title = titleEl.eq(0).text();
title = title.replace(/\n/g, ' ').trim();
title = msg.source.format.strip(title);
// Try HTML handlers
let htmlHandle = false;
for (const k in htmlHandlers) {
const handler = htmlHandlers[k];
if (htmlHandle) { break; }
// Ensure handler is a function
if (!handler[1] || typeof handler[1] !== 'function') { continue; }
try {
htmlHandle = await handler[1].apply(this, [url, msg, title, full]);
} catch (e) {
logger.error(e);
htmlHandle = false;
}
}
if (htmlHandle) { return; }
if (title.length > 140) { title = title.substring(0, 140) + '…'; }
msg.resolve(msg.source.format.color('purple', '[ ') + title + msg.source.format.color('purple', ' ]'));
} catch (e) {
logger.error(e);
}
}
});
}
@EventListener('pluginUnloaded')
unloadedPlugin(plugin: string | Plugin): void {
const id = typeof plugin === 'string' ? plugin : plugin.manifest.name;
// Delete URL handlers for plugin
for (const i in urlHandlers) {
if (urlHandlers[i].plugin === id) {
delete urlHandlers[i];
}
}
// Delete HTML handlers for plugin
const resolve = [];
for (const i in htmlHandlers) {
const k = htmlHandlers[i];
if (k[0] !== id) {
resolve.push(k);
}
}
htmlHandlers = resolve;
}
}
module.exports = URLReplyPlugin;

View File

@ -1,6 +1,5 @@
import path from 'path';
import net from 'net';
import util from 'util';
import cprog from 'child_process';
import convert from 'convert-units';
@ -114,6 +113,61 @@ function rgbToHex(r: number, g: number, b: number): string {
return '#' + ((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1);
}
function RGBToHSL(r: number, g: number, b: number): {[key: string]: number} | null {
// Make r, g, and b fractions of 1
r /= 255;
g /= 255;
b /= 255;
// Find greatest and smallest channel values
const cmin = Math.min(r, g, b);
const cmax = Math.max(r, g, b);
const delta = cmax - cmin;
let h = 0;
let s = 0;
let l = 0;
// Calculate hue
// No difference
if (delta === 0) {
h = 0;
}
// Red is max
else if (cmax === r) {
h = ((g - b) / delta) % 6;
}
// Green is max
else if (cmax === g) {
h = (b - r) / delta + 2;
}
// Blue is max
else {
h = (r - g) / delta + 4;
}
h = Math.round(h * 60);
// Make negative hues positive behind 360°
if (h < 0) {
h += 360;
}
// Calculate lightness
l = (cmax + cmin) / 2;
// Calculate saturation
s = delta === 0 ? 0 : delta / (1 - Math.abs(2 * l - 1));
// Multiply l and s by 100
s = +(s * 100).toFixed(1);
l = +(l * 100).toFixed(1);
return { h, s, l };
}
function hexToRgb(hex: string): {[key: string]: number} | null {
// Expand shorthand form (e.g. "03F") to full form (e.g. "0033FF")
const shorthandRegex = /^#?([a-f\d])([a-f\d])([a-f\d])$/i;
@ -183,7 +237,7 @@ function addCommands(plugin: UtilityPlugin, commands: any): void {
let response = '';
let strArr;
let i;
let text = msg.data.split(' ').slice(2).join(' ');
let text = msg.text.split(' ').slice(2).join(' ');
try {
switch (simplified[0] ? simplified[0].toUpperCase() : null) {
@ -226,7 +280,7 @@ function addCommands(plugin: UtilityPlugin, commands: any): void {
execute: async (msg: IMessage, spec: any, prefix: string, ...simplified: any[]): Promise<boolean> => {
let response = '';
let i;
let text = msg.data.split(' ').slice(2).join(' ');
let text = msg.text.split(' ').slice(2).join(' ');
try {
switch (simplified[0] ? simplified[0].toUpperCase() : null) {
@ -261,7 +315,7 @@ function addCommands(plugin: UtilityPlugin, commands: any): void {
name: 'base64',
execute: async (msg: IMessage, spec: any, prefix: string, ...simplified: any[]): Promise<boolean> => {
let response = '';
const text = msg.data.split(' ').slice(2).join(' ');
const text = msg.text.split(' ').slice(2).join(' ');
try {
switch (simplified[0] ? simplified[0].toUpperCase() : null) {
@ -324,7 +378,7 @@ function addCommands(plugin: UtilityPlugin, commands: any): void {
cmds.push({
name: 'converttime',
execute: async (msg: IMessage, spec: any, prefix: string, ...simplified: any[]): Promise<boolean> => {
const str = msg.data.split(' ').slice(1).join(' ');
const str = msg.text.split(' ').slice(1).join(' ');
if (!str) {
msg.resolve('Invalid input');
@ -342,7 +396,7 @@ function addCommands(plugin: UtilityPlugin, commands: any): void {
cmds.push({
name: 'reconverttime',
execute: async (msg: IMessage, spec: any, prefix: string, ...simplified: any[]): Promise<boolean> => {
const str = msg.data.split(' ').slice(1).join(' ');
const str = msg.text.split(' ').slice(1).join(' ');
if (!str) {
msg.resolve('Invalid input');
@ -364,13 +418,14 @@ function addCommands(plugin: UtilityPlugin, commands: any): void {
return true;
}
const wholeRow = msg.data.split(' ').slice(1).join(' ');
const wholeRow = msg.text.split(' ').slice(1).join(' ');
opMath(wholeRow).then((repl) => {
try {
const repl = await opMath(wholeRow);
msg.resolve(repl);
}, (e) => {
} catch (e) {
msg.resolve('Could not evaluate expression:', e.message);
});
}
return true;
},
@ -386,13 +441,14 @@ function addCommands(plugin: UtilityPlugin, commands: any): void {
return true;
}
const wholeRow = msg.data.split(' ').slice(1).join(' ');
const wholeRow = msg.text.split(' ').slice(1).join(' ');
opMath(wholeRow, 'simplify').then((repl) => {
try {
const repl = await opMath(wholeRow, 'simplify');
msg.resolve(repl);
}, () => {
msg.resolve('Could not evaluate expression!');
});
} catch (e) {
msg.resolve('Could not evaluate expression:', e.message);
}
return true;
},
@ -401,40 +457,6 @@ function addCommands(plugin: UtilityPlugin, commands: any): void {
description: 'Simplify a math expression'
});
cmds.push({
name: 'evaljs',
execute: async (msg: IMessage, spec: any, prefix: string, ...simplified: any[]): Promise<boolean> => {
if (!simplified[0]) {
return true;
}
const script = msg.data.split(' ').slice(1).join(' ');
// Disallow child_process when shell is disallowed
if ((script.indexOf('child_process') !== -1 ||
script.indexOf('cprog') !== -1 ||
script.indexOf('fork') !== -1) &&
!plugin.config.config.allowShell) {
msg.resolve('Error: child_process is not allowed in evaljs due to security reasons.');
return true;
}
try {
const mesh = eval(script); /* eslint no-eval: off */
if (mesh === undefined) {
return true;
}
msg.resolve(util.format(mesh));
} catch (e) {
msg.resolve('Error: ' + e.message);
}
return true;
},
description: 'Execute JavaScript in a command context',
permissions: ['system_execute'],
hidden: true
});
cmds.push({
name: 'userid',
execute: async (msg: IMessage, spec: any, prefix: string, ...simplified: any[]): Promise<boolean> => {
@ -479,7 +501,7 @@ function addCommands(plugin: UtilityPlugin, commands: any): void {
return true;
}
const fullmsg = msg.data.split(' ').slice(1).join(' ');
const fullmsg = msg.text.split(' ').slice(1).join(' ');
const channels = fullmsg.match(/(rgb)?\(?(\d{1,3}),?\s(\d{1,3}),?\s(\d{1,3})\)?/i);
if (!channels || channels[2] == null) {
msg.resolve('Invalid parameter');
@ -503,6 +525,37 @@ function addCommands(plugin: UtilityPlugin, commands: any): void {
usage: '[rgb](<r>, <g>, <b>)|<r> <g> <b>'
});
cmds.push({
name: 'rgb2hsl',
execute: async (msg: IMessage, spec: any, prefix: string, ...simplified: any[]): Promise<boolean> => {
if (!simplified[0]) {
return true;
}
const fullmsg = msg.text.split(' ').slice(1).join(' ');
const channels = fullmsg.match(/(rgb)?\(?(\d{1,3}),?\s(\d{1,3}),?\s(\d{1,3})\)?/i);
if (!channels || channels[2] == null) {
msg.resolve('Invalid parameter');
return true;
}
const r = parseInt(channels[2], 10);
const g = parseInt(channels[3], 10);
const b = parseInt(channels[4], 10);
if (r > 255 || g > 255 || b > 255) {
msg.resolve('Invalid colors');
return true;
}
const hsl = RGBToHSL(r, g, b);
msg.resolve('hsl(%d, %d%, %d%)', hsl?.h, hsl?.s, hsl?.l);
return true;
},
description: 'Convert RGB to HSL colors',
usage: '[rgb](<r>, <g>, <b>)|<r> <g> <b>'
});
cmds.push({
name: 'hex2rgb',
execute: async (msg: IMessage, spec: any, prefix: string, ...simplified: any[]): Promise<boolean> => {
@ -743,42 +796,6 @@ function addCommands(plugin: UtilityPlugin, commands: any): void {
aliases: ['rnum', 'rand']
});
if (plugin.config.config.allowShell) {
logger.warn('WARNING! Shell command execution is enabled! Make absolutely sure that there is proper authentication!');
if (process.getuid && process.getuid() === 0) {
logger.warn('NEVER run Squeebot as root! Run `useradd squeebot`! We are not responsible for possible security leaks!');
}
cmds.push({
name: 'sh',
execute: async (msg: IMessage, spec: any, prefix: string, ...simplified: any[]): Promise<boolean> => {
const stripnl = (simplified[0] !== '-n');
const cmd = simplified.slice(stripnl ? 0 : 1).join(' ');
if (!cmd) {
msg.resolve('Nothing to execute!');
return true;
}
cprog.exec(cmd, {shell: '/bin/bash'}, (error, stdout, stderr) => {
if (stdout) {
if (stripnl) { stdout = stdout.replace(/\n/g, ' ;; '); }
return msg.resolve(stdout);
}
msg.resolve('Error executing command.');
logger.error(stderr || error);
});
return true;
},
description: 'Run raw shell command.',
usage: '<query>',
hidden: true,
permissions: ['system_execute'],
});
}
commands.registerCommand(cmds.map((x: any) => {
x.plugin = plugin.manifest.name;
return x;
@ -786,27 +803,10 @@ function addCommands(plugin: UtilityPlugin, commands: any): void {
}
@Configurable({
allowShell: false,
googleapikey: null,
ipfsGateway: 'https://ipfs.io',
randomMax: 64
})
class UtilityPlugin extends Plugin {
bindEvents(): void {
this.on('message', (msg: IMessage) => {
// Pre-regex check
if (msg.data.indexOf('ipfs://') === -1 && msg.data.indexOf('Qm') === -1) {
return;
}
// IPFS urlify
const mmatch = msg.data.match(/(?:ipfs:\/\/|\s|^)(Qm[\w\d]{44})(?:\s|$)/);
if (mmatch && mmatch[1]) {
msg.resolve(this.config.config.ipfsGateway + '/ipfs/' + mmatch[1]);
}
});
}
@DependencyLoad('simplecommands')
addCommands(cmd: any): void {
addCommands(this, cmd);
@ -821,8 +821,18 @@ class UtilityPlugin extends Plugin {
}
initialize(): void {
this.bindEvents();
this.emit('pluginLoaded', this);
this.on('message', (msg: IMessage) => {
// Pre-regex check
if (msg.text.indexOf('ipfs://') === -1 && msg.text.indexOf('Qm') === -1) {
return;
}
// IPFS urlify
const mmatch = msg.text.match(/(?:ipfs:\/\/|\s|^)(Qm[\w\d]{44})(?:\s|$)/);
if (mmatch && mmatch[1]) {
msg.resolve(this.config.config.ipfsGateway + '/ipfs/' + mmatch[1]);
}
});
}
}