Simp-O-Matic

Dumb Discord bot in TS.
git clone git://git.knutsen.co/Simp-O-Matic
Log | Files | Refs | README | LICENSE

commit 0a66e10d32af93f66afc7ce32234beba9a920f2d
parent 439e9b8a6361cbfa8fd15133791f24762897b3c9
Author: Demonstrandum <moi@knutsen.co>
Date:   Thu, 19 Mar 2020 04:30:59 +0000

Format some code, and re-add heart image.

Diffstat:
MREADME.md | 60+++++++++++++++++++++++++++++++++++++-----------------------
Aheart.png | 0
Mlib/api/contextual.ts | 2+-
Mlib/api/google.ts | 4++--
Mlib/api/oxford.ts | 2+-
Mlib/api/pastebin.ts | 8++++----
Mlib/api/urban.ts | 2+-
Mlib/api/youtube.ts | 6+++---
Mlib/api/yt_scrape.ts | 2+-
Mlib/commands/8ball.ts | 8+++++---
Mlib/commands/choose.ts | 2+-
Mlib/commands/echo.ts | 2+-
Mlib/commands/emojify.ts | 46++++++++++++++++++++++------------------------
Mlib/commands/help.ts | 24+++++++++++-------------
Mlib/commands/kiss.ts | 12++++++------
Mlib/commands/pp.ts | 2+-
Mlib/commands/reject.ts | 4++--
Mlib/commands/ship.ts | 167+++++++++++++++++++++++++++++++++++++++----------------------------------------
Mlib/commands/weather.ts | 2+-
Mlib/default.ts | 2+-
Mlib/extensions.ts | 88++++++++++++++++++++++++++++++++++++++++----------------------------------------
Mlib/format_oed.ts | 10+++++-----
Mlib/main.ts | 70++++++++++++++++++++++++++++++++++------------------------------------
Mlib/utils.ts | 12++++++------
Atslint.json | 18++++++++++++++++++
25 files changed, 291 insertions(+), 264 deletions(-)

diff --git a/README.md b/README.md @@ -43,31 +43,45 @@ When starting the bot, you'll need your secrets set up (API keys etc.). Make sure locally, you have the following secrets exported as environment variables: ```sh -export BOT_API_TOKEN="exampleExampleExampleExample" -export RAPID_API_KEY="exampleExampleExampleExample" -export CLIENT_KEY="exampleExampleExampleExample" -export CLIENT_ID="exampleExampleExampleExample" -export OXFORD_ID="exampleExampleExampleExample" -export OXFORD_KEY="exampleExampleExampleExample" - -export GOOGLE_API_KEY="exampleExampleExampleExample" -export GOOGLE_SEARCH_ID="exampleExampleExampleExample" -export GOOGLE_OAUTH_ID="exampleExampleExampleExample" -export GOOGLE_OAUTH_SECRET="exampleExampleExampleExample" -export GOOGLE_PERSONAL_CODE="exampleExampleExampleExample" -export GOOGLE_TYPE="exampleExampleExampleExample" -export GOOGLE_PROJECT_ID="exampleExampleExampleExample" -export GOOGLE_PRIVATE_KEY_ID="exampleExampleExampleExample" -export GOOGLE_PRIVATE_KEY="exampleExampleExampleExample" -export GOOGLE_CLIENT_EMAIL="exampleExampleExampleExample" -export GOOGLE_CLIENT_ID="exampleExampleExampleExample" -export GOOGLE_AUTH_URI="exampleExampleExampleExample" -export GOOGLE_TOKEN_URI="exampleExampleExampleExample" -export GOOGLE_AUTH_PROVIDER_X509_CERT_URL="exampleExampleExampleExample" -export GOOGLE_CLIENT_X509_CERT_URL="exampleExampleExampleExample" +# Discord +export BOT_API_TOKEN="exampleExampleExample" +export CLIENT_KEY="exampleExampleExample" +export CLIENT_ID="exampleExampleExample" + +# RapidAPI for Urban Dictionary and ContextualWeb +export RAPID_API_KEY="exampleExampleExample" + +# Oxford English Dictionary +export OXFORD_ID="exampleExampleExample" +export OXFORD_KEY="exampleExampleExample" + +# Google APIs +export GOOGLE_API_KEY="exampleExampleExample" +export GOOGLE_SEARCH_ID="exampleExampleExample" +export GOOGLE_OAUTH_ID="exampleExampleExample" +export GOOGLE_OAUTH_SECRET="exampleExampleExample" +export GOOGLE_PERSONAL_CODE="exampleExampleExample" +export GOOGLE_TYPE="exampleExampleExample" +export GOOGLE_PROJECT_ID="exampleExampleExample" +export GOOGLE_PRIVATE_KEY_ID="exampleExampleExample" +export GOOGLE_PRIVATE_KEY="exampleExampleExample" +export GOOGLE_CLIENT_EMAIL="exampleExampleExample" +export GOOGLE_CLIENT_ID="exampleExampleExample" +export GOOGLE_AUTH_URI="exampleExampleExample" +export GOOGLE_TOKEN_URI="exampleExampleExample" +export GOOGLE_AUTH_PROVIDER_X509_CERT_URL="exampleExampleExample" +export GOOGLE_CLIENT_X509_CERT_URL="exampleExampleExample" + +# OpenWeather +export OPENWEATHER_KEY="exampleExampleExample" + +# Pastebin +export PASTEBIN_KEY="exampleExampleExample" +export PASTEBIN_PASSWORD="exampleExampleExample" + ``` -I can send you the tokens file, if I _trust_ you. +I can send you the secrets file, if I _really_ trust you. ### Low on Space? ```sh diff --git a/heart.png b/heart.png Binary files differ. diff --git a/lib/api/contextual.ts b/lib/api/contextual.ts @@ -4,7 +4,7 @@ type Options = { query : string, type : 'image' | 'web' | 'news', key : string -} +}; export const web_search = (options : Options) => new Promise((resolve, reject) => { console.log('Searching the web, with options: ', options); diff --git a/lib/api/google.ts b/lib/api/google.ts @@ -29,7 +29,7 @@ const web_search = (param : CSE) => new Promise((resolve, reject) => { const cache_keys = Object.keys(CACHE); // Retrieve cached query. if (param.query in CACHE) { - return resolve(`${CACHE[param.query]} (cached response)`) + return resolve(`${CACHE[param.query]} (cached response)`); } else if (cache_keys.length > CACHE_SIZE) { // Delete a few, so we can delete less frequently. delete CACHE[cache_keys[0]]; @@ -49,7 +49,7 @@ const web_search = (param : CSE) => new Promise((resolve, reject) => { safe: param.nsfw ? 'off' : 'active' }).then(res => { if (!res.data || !res.data.items || res.data.items.length === 0) - return reject('No such results found.') + return reject('No such results found.'); const item = res.data.items[0]; const answer = `Search for ‘${param.query}’: ${item.link}\n>>> ${item.title}`; diff --git a/lib/api/oxford.ts b/lib/api/oxford.ts @@ -5,7 +5,7 @@ type Options = { lang : string, id : string, key : string -} +}; export const oed_lookup = (options : Options) => new Promise((resolve, reject) => { console.log('Searching Oxford English Dictionary, with options: ', options); diff --git a/lib/api/pastebin.ts b/lib/api/pastebin.ts @@ -15,15 +15,15 @@ export const pastebin_latest = () => new Promise((resolve, reject) => { }); }); -export const pastebin_update = async function (stringified : string) { - await paste.login(PASTE_USER, PASTE_PASS, async function (succ, res) { +export const pastebin_update = async (stringified : string) => { + await paste.login(PASTE_USER, PASTE_PASS, async (succ, res) => { if (!succ) return Promise.reject(console.log('Could not log in.')); return await paste.edit(PASTE_ID, { contents: stringified - }, async function (succ, _res) { - if (!succ) + }, async (worked, _) => { + if (!worked) return console.log('Error updating paste...'); }); }); diff --git a/lib/api/urban.ts b/lib/api/urban.ts @@ -3,7 +3,7 @@ import unirest from 'unirest'; type Options = { query : string, key : string -} +}; export const urban_search = (options : Options) => new Promise((resolve, reject) => { console.log('Searching Urban Dictionary, with options: ', options); diff --git a/lib/api/youtube.ts b/lib/api/youtube.ts @@ -32,11 +32,11 @@ const web_search = (param: YTSearch) => new Promise((resolve, reject) => { yt.search.list({ q: param.query, maxResults: 1, - auth: auth, - part: 'snippet' + part: 'snippet', + auth }).then(res => { if (!res.data || !res.data.items || res.data.items.length === 0) - return reject('No such results found.') + return reject('No such results found.'); const video = res.data.items[0]; const id = video.id.videoId; diff --git a/lib/api/yt_scrape.ts b/lib/api/yt_scrape.ts @@ -1,4 +1,4 @@ -const search = require('scrape-youtube'); +import * as search from 'scrape-youtube'; import '../extensions'; type YTSearch = { diff --git a/lib/commands/8ball.ts b/lib/commands/8ball.ts @@ -1,8 +1,9 @@ export default home_scope => { const { message, args, HELP_SECTIONS, KNOWN_COMMANDS } = home_scope; - if (args.length === 0 || args[0] == 'help') { - message.channel.send(HELP_SECTIONS[KNOWN_COMMANDS.indexOf('8ball')].trim()); + if (args.length === 0 || args[0] === 'help') { + message.channel.send( + HELP_SECTIONS[KNOWN_COMMANDS.indexOf('8ball')].trim()); return; } @@ -34,5 +35,6 @@ export default home_scope => { "You may rely on it.", ]; - message.answer(":8ball: " + responses[Math.floor(Math.random() * responses.length)]); + message.answer(":8ball: " + + responses[Math.floor(Math.random() * responses.length)]); }; diff --git a/lib/commands/choose.ts b/lib/commands/choose.ts @@ -2,4 +2,4 @@ export default home_scope => { const { message, args } = home_scope; const a = args.join(' ').split(/\s*(?:,|or)\s*/); message.answer(a[Math.floor(Math.random() * a.length)]); -} +}; diff --git a/lib/commands/echo.ts b/lib/commands/echo.ts @@ -1,4 +1,4 @@ export default home_scope => { const { message, args } = home_scope; message.channel.send(args.join(' ')); -} +}; diff --git a/lib/commands/emojify.ts b/lib/commands/emojify.ts @@ -1,32 +1,30 @@ import { Message } from 'discord.js'; -export default home_scope => { - const { message, args } - : { message: Message, args: string[] } = home_scope; - - let input: string; +const ALPHABET = 'abcdefghijklmnopqrstuvwxyz'; +const NUMBER_NAMES = [ + 'one', 'two', 'three', 'four', 'five', + 'six', 'seven', 'eight', 'nine', 'keycap_ten' +]; - if (args.length === 0) - input = "nibba"; - else - input = args.join(' ').toLowerCase(); +export default home_scope => { + const { message, args } + : { message: Message, + args: string[] } = home_scope; - const alphabet = 'abcdefghijklmnopqrstuvwxyz'; - const numerics = [ - 'one', 'two', 'three', 'four', 'five', - 'six', 'seven', 'eight', 'nine', 'keycap_ten' - ]; + const input = args.length === 0 + ? "nibba" + : args.join(' ').toLowerCase(); - let letters = [...input].map((chr: any) => { - if (isNaN(chr) && alphabet.includes(chr)) - return chr === 'b' ? chr.emojify() : `regional_indicator_${chr}`.emojify(); - else if (chr === ' ') - return chr; - else if (!isNaN(Number(chr))) - return numerics[Number(chr)].emojify(); - else - return chr; - }) + const letters = [...input].map((chr: any) => { + if (chr === ' ') return chr; + if (isNaN(chr) && ALPHABET.includes(chr)) + return chr === 'b' + ? chr.emojify() + : `regional_indicator_${chr}`.emojify(); + if (!isNaN(Number(chr))) + return NUMBER_NAMES[Number(chr)].emojify(); + return chr; + }); message.channel.send(letters.join(' ')); }; diff --git a/lib/commands/help.ts b/lib/commands/help.ts @@ -3,18 +3,16 @@ export default home_scope => { KNOWN_COMMANDS, CONFIG, ALL_HELP, HELP_KEY, HELP_SOURCE } = home_scope; - if (args.length === 0 || args[0] == 'help') { + if (args.length === 0 || args[0] === 'help') { message.channel.send(HELP_SECTIONS[0]); return; } - if (args[0] === 'key') { - message.channel.send(HELP_KEY); - return; - } else if (args[0] === 'source') { - message.channel.send(HELP_SOURCE); - return; - } else if (args[0] === 'all') { + if (args[0] === 'key') + return message.channel.send(HELP_KEY); + if (args[0] === 'source') + return message.channel.send(HELP_SOURCE); + if (args[0] === 'all') { for (const msg of ALL_HELP) message.channel.send(msg); return; @@ -32,8 +30,8 @@ export default home_scope => { const help_index = KNOWN_COMMANDS.indexOf(command); if (help_index === -1) - message.answer(`No such command/help-page (\`${command}\`).`); - else - message.answer(`**Help (\`${command}\`):**\n` - + HELP_SECTIONS[help_index].trim()); -} + return message.answer(`No such command/help-page (\`${command}\`).`); + + message.answer(`**Help (\`${command}\`):**\n` + + HELP_SECTIONS[help_index].trim()); +}; diff --git a/lib/commands/kiss.ts b/lib/commands/kiss.ts @@ -5,10 +5,9 @@ export default home_scope => { const { message, args } : { message: Message, args: string[] } = home_scope; - if (args.length === 0 || message.mentions.users.size === 0) { - message.channel.send("You kissed your own hand. :face_with_hand_over_mouth:"); - return; - } + if (args.length === 0 || message.mentions.users.size === 0) + return message.channel.send( + "You kissed your own hand. :face_with_hand_over_mouth:"); const author = message.author.username; const to = message.mentions.users.first().username; @@ -16,8 +15,9 @@ export default home_scope => { const embed = new RichEmbed() .setColor('#ba3d8a') .setTitle("Uh-oh... You're getting a kiss!") - .setDescription(`${to.format(FORMATS.bold)}, you got a kissu from ${author.format(FORMATS.bold)}! :flushed:`) - .setImage('https://i.imgur.com/lz1BY2x.gif') + .setDescription(`${to.format(FORMATS.bold)}, you got a kissu from \ + ${author.format(FORMATS.bold)}! :flushed:`.squeeze()) + .setImage('https://i.imgur.com/lz1BY2x.gif'); message.channel.send(embed); }; diff --git a/lib/commands/pp.ts b/lib/commands/pp.ts @@ -12,4 +12,4 @@ export default home_scope => { message.answer(`8${shaft}>`); } -} +}; diff --git a/lib/commands/reject.ts b/lib/commands/reject.ts @@ -20,7 +20,7 @@ export default home_scope => { const match = args[1].match(/#?(\d+)/); if (!match || !match[1]) return message.answer('Please provide a numerical index' - + ' as to which rule to remove.') + + ' as to which rule to remove.'); const index = Number(match[1]) - 1; if (index >= reject.length) @@ -80,4 +80,4 @@ export default home_scope => { message.reply(`Here's how you use the command:\n` + HELP_SECTIONS[KNOWN_COMMANDS.indexOf('reject')]); } -} +}; diff --git a/lib/commands/ship.ts b/lib/commands/ship.ts @@ -1,97 +1,96 @@ import { FORMATS } from '../extensions'; import { Message } from 'discord.js'; -import fs from 'fs'; -//const fs = require('fs'); -const cp = require('child_process'); +import { createWriteStream as write_stream, + WriteStream } from 'fs'; +import { execSync as shell } from 'child_process'; import fetch from 'node-fetch'; -function ps(stream) { - return new Promise((resolve, reject) => { +const RESPONSES = [ + { range: [24, 49], message: "Not good. :confounded: :broken_heart:" }, + { range: [49, 59], message: "Nothing is impossible, but... :slight_frown:" }, + { range: [59, 64], message: "Friends, but maybe... :smirk:" }, + { range: [64, 69], message: "We have some chemistry going on here... :heart_eyes:" }, + { range: [69, 74], message: "A match for sure! :relaxed:" }, + { range: [74, 79], message: "I approve this couple! :kissing_heart:" }, + { range: [79, 84], message: "They like each other very much!! :blush:" }, + { range: [84, 89], message: "If both aren't already dating I'd be surprised! :kissing_closed_eyes:" }, + { range: [89, 93], message: "Both are made for each other! :relaxed:" }, + { range: [93, 97], message: "They deeply love each other! :blush:" }, + { range: [97, 100], message: "Lovey-dovey couple!! :kissing_heart: :heart: :two_hearts:" }, +]; + +const ps = (stream : WriteStream) => + new Promise((resolve, reject) => { stream.once('error', reject); stream.once('finish', resolve); }); -} -async function make_img(u1, u2) { - let res1 = await fetch(u1); - let res2 = await fetch(u2); - let ps1 = await ps(res1.body.pipe(fs.createWriteStream('./u1.png'))); - let ps2 = await ps(res2.body.pipe(fs.createWriteStream('./u2.png'))); +const make_img = async (u1: string, u2: string) => { + const res1 = await fetch(u1); + const res2 = await fetch(u2); + await ps(res1.body.pipe(write_stream('./u1.png'))); + await ps(res2.body.pipe(write_stream('./u2.png'))); + + shell('montage ./u1.png ./heart.png ./u2.png ./out.png', + { cwd: process.cwd() }); - cp.execSync( - 'montage ./u1.png ./❤️.png ./u2.png ./out.png', - { cwd: process.cwd() }); return './out.png'; -} +}; export default home_scope => { - const { message, args, - HELP_SECTIONS, - KNOWN_COMMANDS } - : { message: Message, args: string[], - HELP_SECTIONS: string[], - KNOWN_COMMANDS: string[] } = home_scope; - - if (args.length === 0 || args[0] == 'help' || - message.mentions.users.size === 0 || - message.mentions.users.size > 2) - { - message.channel.send(HELP_SECTIONS[KNOWN_COMMANDS.indexOf('ship')].trim()); - return; - } - - let userAvatars = { - first: message.mentions.users.size === 1 - ? message.author.avatarURL - : message.mentions.users.first().avatarURL, - second: message.mentions.users.last().avatarURL - }; - - const responses = [ - { range: [24, 49], message: "Not good. :confounded: :broken_heart:" }, - { range: [49, 59], message: "Nothing is impossible, but... :slight_frown:" }, - { range: [59, 64], message: "Friends, but maybe... :smirk:" }, - { range: [64, 69], message: "We have some chemistry going on here... :heart_eyes:" }, - { range: [69, 74], message: "A match for sure! :relaxed:" }, - { range: [74, 79], message: "I approve this couple! :kissing_heart:" }, - { range: [79, 84], message: "They like each other very much!! :blush:" }, - { range: [84, 89], message: "If both aren't already dating I'd be surprised! :kissing_closed_eyes:" }, - { range: [89, 93], message: "Both are made for each other! :relaxed:" }, - { range: [93, 97], message: "They deeply love each other! :blush:" }, - { range: [97, 100], message: "Lovey-dovey couple!! :kissing_heart: :heart: :two_hearts:" }, - ]; - - const inRange = ([min, max]: number[], num: number): boolean => - num >= min && num < max; - - const getResponse = (num: number): string => - responses.filter(res => inRange(res.range, num))[0]?.message || responses[0].message; - - const getPercentage = function (die: number) { - const bar = { - size: 10, - knob: '█', - empty: '.', - get filling(): number { - return Math.round(die / this.size); - }, - get space(): number { - return Math.abs(this.filling - this.size); - } - }!; - - let percentage = `${die.toString().format(FORMATS.bold)}%`; - let progressbar = `[${ bar.knob.repeat(bar.filling) }${ bar.empty.repeat(bar.space) }]`.format(FORMATS.block); - - return `${percentage} ${progressbar}`; - }; - - let die: number = Math.floor(Math.random() * 100); - - let response: string = - `${getPercentage(die)} ${getResponse(die)}`; - - make_img(userAvatars.first, userAvatars.second).then(str => - message.channel.send(response, {files: [str]})) -} + const { message, args, + HELP_SECTIONS, + KNOWN_COMMANDS } + : { message: Message, args: string[], + HELP_SECTIONS: string[], + KNOWN_COMMANDS: string[] } = home_scope; + + if (args.length === 0 || args[0] === 'help' + || message.mentions.users.size === 0 + || message.mentions.users.size > 2) { + message.channel.send( + HELP_SECTIONS[KNOWN_COMMANDS.indexOf('ship')].trim()); + return; + } + + const user_avatars = { + first: message.mentions.users.size === 1 + ? message.author.avatarURL + : message.mentions.users.first().avatarURL, + second: message.mentions.users.last().avatarURL + }; + + const in_range = ([min, max]: number[], num: number) => + num >= min && num < max; + + const get_response = (num: number) => RESPONSES.filter(res => + in_range(res.range, num))[0]?.message + || RESPONSES[0].message; + + const get_percentage = (n: number) => { + const bar = { + size: 10, + knob: '█', + empty: '.', + get filling(): number { + return Math.round(n / this.size); + }, + get space(): number { + return Math.abs(this.filling - this.size); + } + }!; + + const percentage = `${n.toString().format(FORMATS.bold)}%`; + const progress_bar = (`[${ bar.knob.repeat(bar.filling) }` + + `${ bar.empty.repeat(bar.space) }]`).format(FORMATS.block); + + return `${percentage} ${progress_bar}`; + }; + + const die = Math.floor(Math.random() * 100); + const response = `${get_percentage(die)} ${get_response(die)}`; + + make_img(user_avatars.first, user_avatars.second).then(str => + message.channel.send(response, {files: [str]})); +}; diff --git a/lib/commands/weather.ts b/lib/commands/weather.ts @@ -1,4 +1,4 @@ -const fetch = require('node-fetch'); +import fetch from 'node-fetch'; const WEATHER_URL = 'http://api.openweathermap.org/data/2.5/weather'; diff --git a/lib/default.ts b/lib/default.ts @@ -132,4 +132,4 @@ export default { } } -} +}; diff --git a/lib/extensions.ts b/lib/extensions.ts @@ -1,49 +1,49 @@ // Global Extensions: declare global { interface Array<T> { - head(): T - tail(): Array<T> - first(): T - last(off? : number | undefined): T - get(i : number): T - unique(): Array<T> - squeeze(): Array<T> - each(callbackfn : (value : T, index : number, array : T[]) => void, thisArg? : T): void - mut_unique(): Array<T> - mut_map(f: (T) => any): Array<any> + head(): T; + tail(): T[]; + first(): T; + last(off? : number | undefined): T; + get(i : number): T; + unique(): T[]; + squeeze(): T[]; + each(callbackfn : (value : T, index : number, array : T[]) => void, thisArg? : T): void; + mut_unique(): T[]; + mut_map(f: (T) => any): any[]; } - interface TextFormat { - italics: string, - bold: string, - bold_italics: string, - underline: string, - underline_italics: string, - underline_bold: string, - underline_bold_italics: string, - strikethrough: string, - block: string, - code_block: string, - block_quote: string, - multiline_block_quote: string, - hidden: string, - } + interface TextFormat { + italics: string; + bold: string; + bold_italics: string; + underline: string; + underline_italics: string; + underline_bold: string; + underline_bold_italics: string; + strikethrough: string; + block: string; + code_block: string; + block_quote: string; + multiline_block_quote: string; + hidden: string; + } interface String { - squeeze(): string - capitalize(): string - leading_space(): string - head(): string - tail(): string - first(): string - last(off? : number): string - format(fmt: string): string - emojify(): string + squeeze(): string; + capitalize(): string; + leading_space(): string; + head(): string; + tail(): string; + first(): string; + last(off? : number): string; + format(fmt: string): string; + emojify(): string; } interface Number { - round_to(dp: number): number - to_metric(figures): string + round_to(dp: number): number; + to_metric(figures): string; } } @@ -62,10 +62,10 @@ Array.prototype.last = function (off=0) { return this[this.length - 1 - off]; }; -Array.prototype.get = function (i) { return this[i] }; +Array.prototype.get = function (i) { return this[i]; }; Array.prototype.unique = function () { - return this.filter((e, i) => this.indexOf(e) === i) + return this.filter((e, i) => this.indexOf(e) === i); }; Array.prototype.squeeze = function () { return this.filter(e => !!e); }; @@ -80,9 +80,9 @@ Array.prototype.mut_unique = function () { }; Array.prototype.mut_map = function (f) { - for (const i in this) { - this[i] = f(this[i]); - } + for (const i in this) + if (this.hasOwnProperty(i) && i !== 'length') + this[i] = f(this[i]); return this; }; @@ -101,7 +101,7 @@ String.prototype.capitalize = function () { return this.charAt(0).toUpperCase() + this.slice(1); }; -String.prototype.emojify = function () { return `:${this}:` }; +String.prototype.emojify = function () { return `:${this}:`; }; String.prototype.head = Array.prototype.head as any; String.prototype.tail = Array.prototype.tail as any; @@ -126,7 +126,7 @@ export const FORMATS: TextFormat = { String.prototype.format = function (fmt: string) { return `${fmt}${this}${fmt}`; -} +}; // Number Extensions: Number.prototype.round_to = function (dp : number) { @@ -160,7 +160,7 @@ Number.prototype.to_metric = function (figures) { declare module 'discord.js' { interface Message { - answer(...args: any): void + answer(...args: any): void; } } import { Message } from 'discord.js'; diff --git a/lib/format_oed.ts b/lib/format_oed.ts @@ -10,10 +10,10 @@ export default (res, message) => { let entry_n = 1; for (const lex_entry of lex_entries) { if (lex_entries.length > 1) { - msg += `\nLexical Entry №${entry_n}:\n` + msg += `\nLexical Entry №${entry_n}:\n`; entry_n += 1; } - console.log('Lex entry:', pp(lex_entries)) + console.log('Lex entry:', pp(lex_entries)); for (const entry of Object.values(lex_entry.entries)) { const senses = entry['senses']; @@ -32,7 +32,7 @@ export default (res, message) => { sense_msg += ` Synonyms include: ${synonyms}\n`; } if (sense_msg.trim().length > 0) { - msg += "\nIn the sense:\n" + msg += "\nIn the sense:\n"; msg += sense_msg; } } @@ -46,7 +46,7 @@ export default (res, message) => { if (!!lex_entry.pronunciations && !has_sent_audio) { const prons = Object.values(lex_entry.pronunciations) as any; if (!!prons && prons.length > 0) { - msg += "\nPronunciations:\n" + msg += "\nPronunciations:\n"; for (const pron of prons) { if (!!pron.dialects) { const dialects = Object.values(pron.dialects); @@ -68,4 +68,4 @@ export default (res, message) => { } console.log('Became:', msg); return msg; -} +}; diff --git a/lib/main.ts b/lib/main.ts @@ -40,11 +40,10 @@ const CONFIG = deep_merge( // CONFIG will eventually update to the online version. pastebin_latest().then(res => { deep_merge(CONFIG, res); - // Precompile all regular-expressions in known places. ['respond', 'reject', 'replace'] - .each(name => CONFIG.rules[name].mut_map(compile_match)) -}).catch(console.log); + .each(name => CONFIG.rules[name].mut_map(compile_match)); +}).catch(console.warn); // Store secrets in an object, retrieved from shell's // environment variables. @@ -90,7 +89,7 @@ console.log('File/Execution locations:', { @Discord export class SimpOMatic { - private static _client : Client; + private static _CLIENT : Client; private _COMMAND_HISTORY : Message[] = []; constructor() { @@ -100,8 +99,8 @@ export class SimpOMatic { } static start() { - this._client = new Client(); - this._client.login( + this._CLIENT = new Client(); + this._CLIENT.login( SECRETS.api.token, `${__dirname}/*Discord.ts` ); @@ -109,17 +108,17 @@ export class SimpOMatic { expand_alias(operator, args) { const expander = unexpanded => { - let expanded = unexpanded; + let expansion = unexpanded; if (CONFIG.commands.aliases.hasOwnProperty(unexpanded)) - expanded = CONFIG.commands.aliases[unexpanded].trim().squeeze(); + expansion = CONFIG.commands.aliases[unexpanded].trim().squeeze(); - const expanded_command_words = expanded.split(' '); + const expanded_command_words = expansion.split(' '); if (expanded_command_words.length > 1) { // This means the alias has expanded to more than just one word. - expanded = expanded_command_words.shift(); + expansion = expanded_command_words.shift(); expanded_command_words.each(e => args.push(e)); } - return expanded + return expansion; }; // Continue expanding until we have no more change. let i = 0; @@ -155,7 +154,7 @@ export class SimpOMatic { if (delta <= 400) return; return message.answer(`I can't help but notice you're running \ the same commands over in rather rapid succession. - Would you like to slow down a little?`.squeeze()) + Would you like to slow down a little?`.squeeze()); } if (delta <= 900) { if (delta <= 300) return; @@ -174,7 +173,7 @@ export class SimpOMatic { if (operator === 'CYCLIC_ALIAS') { message.reply('The command you just used has aliases that go' + ' 300 levels deep, or the alias is cyclically dependant.' - + '\n**Fix this immediately.**') + + '\n**Fix this immediately.**'); } operator = operator.toLowerCase(); console.log('Received command:', [operator, args]); @@ -195,13 +194,13 @@ export class SimpOMatic { const joined_commands = KNOWN_COMMANDS.slice(0, -1) .map(c => `\`${p}${c}\``) .join(', '); - const last_command = `\`${p}${KNOWN_COMMANDS.last()}\``; + const final_command = `\`${p}${KNOWN_COMMANDS.last()}\``; message.reply(`All known commands (excluding aliases): \ - ${joined_commands} and ${last_command}`.squeeze()); + ${joined_commands} and ${final_command}`.squeeze()); break; } case 'id': { if (args[0]) { - const matches = args[0].match(/<@!?(\d+)>/) + const matches = args[0].match(/<@!?(\d+)>/); if (!matches) { message.answer(`Please tag a user, or \ provide no argument(s) at all. See \`!help id\`` @@ -256,7 +255,7 @@ export class SimpOMatic { if (args.length === 0 || args[0] === 'ls') { const lines = Object.keys(CONFIG.commands.aliases) .map((e, i) => `${i + 1}. \`${p}${e}\` ↦ \`${p}${CONFIG.commands.aliases[e]}\``); - message.answer('List of **Aliases**:\n') + message.answer('List of **Aliases**:\n'); message.channel.send('**KEY: `Alias` ↦ `Command it maps to`**\n\n' + lines.join('\n')); break; @@ -325,7 +324,7 @@ export class SimpOMatic { } break; } case 'prefix': { - if (args.length == 1) { + if (args.length === 1) { if (args[0].length !== 1) { message.answer(`You may only use a prefix that is exactly one character/symbol/grapheme/rune long.` @@ -390,11 +389,11 @@ export class SimpOMatic { }).then(res => { console.log('Dictionary response:', pp(res)); if (!res['results'] - || res['results'].length == 0 + || res['results'].length === 0 || !res['results'][0].lexicalEntries - || res['results'][0].lexicalEntries.length == 0 - || res['results'][0].lexicalEntries[0].entries.length == 0 - || res['results'][0].lexicalEntries[0].entries[0].senses.length == 0) { + || res['results'][0].lexicalEntries.length === 0 + || res['results'][0].lexicalEntries[0].entries.length === 0 + || res['results'][0].lexicalEntries[0].entries[0].senses.length === 0) { message.answer(nasty_reply); return; } @@ -409,7 +408,7 @@ export class SimpOMatic { if (part_msg.length + line.length >= 2000) { message.channel.send(part_msg); part_msg = line + '\n'; - } else { part_msg += line + '\n' } + } else { part_msg += line + '\n'; } // Send what's left over, and not >2000 characters. message.channel.send(part_msg); @@ -417,7 +416,7 @@ export class SimpOMatic { } message.channel.send(msg); }).catch(e => { - if (e.status == 404) { + if (e.status === 404) { message.channel.send(`That 404'd. ${nasty_reply}`); } else { message.channel.send(`Error getting definition:\n${e}`); @@ -473,7 +472,7 @@ export class SimpOMatic { .reverse() .join('_'); - const file_name = `export-${today}.json` + const file_name = `export-${today}.json`; const file_dest = `${process.cwd()}/${file_name}`; write_file(file_dest, export_config(CONFIG, {})); pastebin_update(export_config(CONFIG, {})); @@ -499,10 +498,10 @@ export class SimpOMatic { message.answer(`${GIT_URL}/`); break; } case 'fork': { - message.answer(`${GIT_URL}/fork`) + message.answer(`${GIT_URL}/fork`); break; } case 'issue': { - message.answer(`${GIT_URL}/issues`) + message.answer(`${GIT_URL}/issues`); break; } case '': { message.answer("That's an empty command..."); @@ -555,7 +554,7 @@ export class SimpOMatic { if (opts.command) { let commands = this._COMMAND_HISTORY - .filter(m => m.channel.id === channel.id) + .filter(m => m.channel.id === channel.id); if (opts.mention) commands = commands.filter(m => m.author.toString() === opts.mentioning); @@ -610,8 +609,7 @@ export class SimpOMatic { expansions[i] = await this.last_message({ command: !opts.includes('^'), - mention: mention, - mentioning: mentioning, + mention, mentioning, offset: offset || 1, channel: message.channel }); @@ -629,7 +627,7 @@ export class SimpOMatic { if (!message.content) return; console.log('Message acknowledged.'); - if (SimpOMatic._client.user.id === message.author.id) { + if (SimpOMatic._CLIENT.user.id === message.author.id) { return; } console.log('Message received:', message.content); @@ -647,10 +645,10 @@ export class SimpOMatic { console.log('Expanded message:', message.content); if (message.content[0] === CONFIG.commands.prefix) { - console.log('Message type: command.') + console.log('Message type: command.'); this.process_command(message); } else { - console.log('Message type: generic.') + console.log('Message type: generic.'); this.process_generic(message); } }); @@ -663,10 +661,10 @@ const on_termination = () => { write_file(`${process.cwd()}/export-exit.json`, export_config(CONFIG, {})); pastebin_update(export_config(CONFIG, {})); // Make sure we saved ok. - new Promise(res => setTimeout(() => { - res(null) + return new Promise(res => setTimeout(() => { + res(null); console.log('Clean finished.'); - process.exit(0) + process.exit(0); }, 6000)); }; diff --git a/lib/utils.ts b/lib/utils.ts @@ -54,7 +54,7 @@ export const deep_merge_pair = (target, source) => { }); return target; -} +}; export const deep_merge = (...objects) => (objects.length === 2) @@ -63,7 +63,7 @@ export const deep_merge = (...objects) => export const parse_regex = (s : string) => { - let temp = s.split('/'); + const temp = s.split('/'); const options = temp.pop(); temp.shift(); const contents = temp.join('/'); @@ -83,13 +83,13 @@ const recursive_regex_to_string = o => { return o.toString(); } if (type(o) === 'object' || type(o) === 'array') { - for (const key in o) { - o[key] = recursive_regex_to_string(o[key]); - } + for (const key in o) + if (o.hasOwnProperty(key)) + o[key] = recursive_regex_to_string(o[key]); return o; } return o; -} +}; export const export_config = (obj, { ugly = false }) => { const o = recursive_regex_to_string(deep_clone(obj)); diff --git a/tslint.json b/tslint.json @@ -0,0 +1,18 @@ +{ + "defaultSeverity": "error", + "extends": [ + "tslint:recommended" + ], + "jsRules": {}, + "rules": { + "semicolon": [true, "always"], + "variable-name": { + "options": "allow-snake-case" + }, + "no-console": false, + "no-string-literal": false, + "no-conditional-assignment": false, + "one-variable-per-declaration": false + }, + "rulesDirectory": [] +}