commit 1888e2c038a74ac96b197b3f6c08cb74aea9aab9
parent bafdffa5708a37f1b11f4e8563c771008b6f7582
Author: Demonstrandum <moi@knutsen.co>
Date: Wed, 18 Mar 2020 15:29:21 +0000
Act on rules, but no way to add new ones.
Diffstat:
13 files changed, 515 insertions(+), 501 deletions(-)
diff --git a/HELP.md b/HELP.md
@@ -74,6 +74,9 @@
- `!wikipedia` **〈not impl.〉** — Search through Wikipedia, returning the most relevant wiki-link.
- `!translate <language> [phrase]` **〈not impl.〉** — Translate a phrase from a language (if none specified, it will auto-detect).
- `!wolfram` **〈not impl.〉** — Query Wolfram|Alpha.
+- `!weather` — Check the weather:
+ - `!weather set [location]` — sets your weather location.
+ - `!weather <location>` — gives you the weather in a certain location, if location is left blank, it will either give you the weather in the default location, or in the area you `set` previously.
- `!say [phrase]` — Repeats what you told it to say.
- `!milkies` — In case you're feeling thirsty...
- `!cowsay <options> [phrase]` **〈not impl.〉** — Make a cow say something, using Unix-like command line arguments.
diff --git a/lib/api/contextual.ts b/lib/api/contextual.ts
@@ -1,36 +1,36 @@
import unirest from 'unirest';
type Options = {
- query : string,
- type : 'image' | 'web' | 'news',
- key : string
+ 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);
-
- const api = `${options.type.capitalize()}SearchAPI`;
- const url = 'https://contextualwebsearch-websearch-v1.p.rapidapi.com/api/Search';
-
- const req = unirest('GET', `${url}/${api}`);
-
- req.query({
- "autoCorrect": "false",
- "pageNumber": "1",
- "pageSize": "10",
- "q": options.query,
- "safeSearch": "false"
- });
-
- req.headers({
- "x-rapidapi-host": "contextualwebsearch-websearch-v1.p.rapidapi.com",
- "x-rapidapi-key": options.key
- });
-
- req.end(res => {
- if (res.error) return reject(res.error);
- return resolve(res.body);
- });
+ console.log('Searching the web, with options: ', options);
+
+ const api = `${options.type.capitalize()}SearchAPI`;
+ const url = 'https://contextualwebsearch-websearch-v1.p.rapidapi.com/api/Search';
+
+ const req = unirest('GET', `${url}/${api}`);
+
+ req.query({
+ "autoCorrect": "false",
+ "pageNumber": "1",
+ "pageSize": "10",
+ "q": options.query,
+ "safeSearch": "false"
+ });
+
+ req.headers({
+ "x-rapidapi-host": "contextualwebsearch-websearch-v1.p.rapidapi.com",
+ "x-rapidapi-key": options.key
+ });
+
+ req.end(res => {
+ if (res.error) return reject(res.error);
+ return resolve(res.body);
+ });
});
export default web_search;
diff --git a/lib/api/google.ts b/lib/api/google.ts
@@ -1,22 +1,22 @@
import { google } from 'googleapis';
type CSE = {
- kind: 'image' | 'web',
- key: string,
- id: string,
- query: string
+ kind: 'image' | 'web',
+ key: string,
+ id: string,
+ query: string
};
// Cache grows from the bottom, and deletes from the top.
// i.e. old cached items get deleted, since they're unpopular.
const CACHE_SIZE = 40;
const CACHE = {
- 'aaron obese': "https://assets3.thrillist.com/v1/image/2765017/size/tmg-article_tall;jpeg_quality=20.jpg\n>>> Aaron obese xD, shut up instgen...",
- 'druggie': "https://vignette.wikia.nocookie.net/sausage-party-recipe-book/images/e/e7/Druggie.png/revision/latest/top-crop/width/360/height/450?cb=20170212165040\n>>> Hurr durr, arron's a shroomer lmao.",
- 'aaron fish': "https://i.pinimg.com/280x280_RS/fa/b5/96/fab5962b97d464781f65952b6b63e4a0.jpg\n>>> arron fish? arron fish. Blub blub.",
- 'sammy obese': "https://s3fs.bestfriends.org/s3fs-public/news/15/09/04/SIJenPettingSammy6919.jpg\n>>> sammy is a cute skinny twink, stfu.",
- 'james obese': "https://i.dailymail.co.uk/i/pix/2014/02/18/article-2562421-1B9E557700000578-9_634x468.jpg\n>>> james very obese, yes, very original...",
- 'aaron penis': "http://m.quickmeme.com/img/ea/eaf8c78ae55815cc7786b1596f9a9767fc3c42b60efff8a2455150e4d0eb37b0.jpg\n>>> aaron's schlong is actually huge. shroomer schlong."
+ 'aaron obese': "https://assets3.thrillist.com/v1/image/2765017/size/tmg-article_tall;jpeg_quality=20.jpg\n>>> Aaron obese xD, shut up instgen...",
+ 'druggie': "https://vignette.wikia.nocookie.net/sausage-party-recipe-book/images/e/e7/Druggie.png/revision/latest/top-crop/width/360/height/450?cb=20170212165040\n>>> Hurr durr, arron's a shroomer lmao.",
+ 'aaron fish': "https://i.pinimg.com/280x280_RS/fa/b5/96/fab5962b97d464781f65952b6b63e4a0.jpg\n>>> arron fish? arron fish. Blub blub.",
+ 'sammy obese': "https://s3fs.bestfriends.org/s3fs-public/news/15/09/04/SIJenPettingSammy6919.jpg\n>>> sammy is a cute skinny twink, stfu.",
+ 'james obese': "https://i.dailymail.co.uk/i/pix/2014/02/18/article-2562421-1B9E557700000578-9_634x468.jpg\n>>> james very obese, yes, very original...",
+ 'aaron penis': "http://m.quickmeme.com/img/ea/eaf8c78ae55815cc7786b1596f9a9767fc3c42b60efff8a2455150e4d0eb37b0.jpg\n>>> aaron's schlong is actually huge. shroomer schlong."
};
// TODO: Reject results if they're from:
@@ -25,37 +25,37 @@ const CACHE = {
// These web-places already have commands given to them.
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)`)
- } else if (cache_keys.length > CACHE_SIZE) {
- // Delete a few, so we can delete less frequently.
- delete CACHE[cache_keys[0]];
- delete CACHE[cache_keys[1]];
- delete CACHE[cache_keys[2]];
- }
-
- const cs = google.customsearch('v1');
-
- cs.cse.list({
- auth: param.key,
- cx: param.id,
- q: param.query,
- searchType: (param.kind === 'web') ? undefined : param.kind,
- start: 0,
- num: 1
- }).then(res => {
- if (!res.data || !res.data.items || res.data.items.length === 0)
- return reject('No such results found.')
-
- const item = res.data.items[0];
- const answer = `${item.link}\n>>> ${item.title}`;
- // Cache this query
- CACHE[param.query] = answer;
- return resolve(answer);
- }).catch(e =>
- reject(`No results, or API capped...\n\`\`\`\n${e}\n\`\`\``));
+ const cache_keys = Object.keys(CACHE);
+ // Retrieve cached query.
+ if (param.query in CACHE) {
+ 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]];
+ delete CACHE[cache_keys[1]];
+ delete CACHE[cache_keys[2]];
+ }
+
+ const cs = google.customsearch('v1');
+
+ cs.cse.list({
+ auth: param.key,
+ cx: param.id,
+ q: param.query,
+ searchType: (param.kind === 'web') ? undefined : param.kind,
+ start: 0,
+ num: 1
+ }).then(res => {
+ if (!res.data || !res.data.items || res.data.items.length === 0)
+ return reject('No such results found.')
+
+ const item = res.data.items[0];
+ const answer = `${item.link}\n>>> ${item.title}`;
+ // Cache this query
+ CACHE[param.query] = answer;
+ return resolve(answer);
+ }).catch(e =>
+ reject(`No results, or API capped...\n\`\`\`\n${e}\n\`\`\``));
});
export default web_search;
diff --git a/lib/api/oxford.ts b/lib/api/oxford.ts
@@ -1,29 +1,29 @@
import unirest from 'unirest';
type Options = {
- word : string,
- lang : string,
- id : string,
- key : string
+ word : string,
+ 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);
+ console.log('Searching Oxford English Dictionary, with options: ', options);
- const url = 'https://od-api.oxforddictionaries.com:443/api/v2/entries';
+ const url = 'https://od-api.oxforddictionaries.com:443/api/v2/entries';
- const word = options.word.toLowerCase();
- const req = unirest('GET', `${url}/${options.lang}/${word}`);
+ const word = options.word.toLowerCase();
+ const req = unirest('GET', `${url}/${options.lang}/${word}`);
- req.headers({
- "app_id": options.id,
- "app_key": options.key
- });
+ req.headers({
+ "app_id": options.id,
+ "app_key": options.key
+ });
- req.end(res => {
- if (res.error) return reject(res.error);
- return resolve(res.body);
- });
+ req.end(res => {
+ if (res.error) return reject(res.error);
+ return resolve(res.body);
+ });
});
export default oed_lookup;
diff --git a/lib/api/pastebin.ts b/lib/api/pastebin.ts
@@ -8,23 +8,23 @@ export const pastebin_url = `https://pastebin.com/${PASTE_ID}`;
paste.setDevKey(process.env['PASTEBIN_KEY']);
export const pastebin_latest = () => new Promise((resolve, reject) => {
- paste.get(PASTE_ID, (succ, res) => {
- if (!succ)
- return reject('Error getting paste.');
- resolve(JSON.parse(res));
- });
+ paste.get(PASTE_ID, (succ, res) => {
+ if (!succ)
+ return reject('Error getting paste.');
+ resolve(JSON.parse(res));
+ });
});
export const pastebin_update = async function (stringified : string) {
- await paste.login(PASTE_USER, PASTE_PASS, async function (succ, res) {
- if (!succ)
- return Promise.reject(console.log('Could not log in.'));
+ await paste.login(PASTE_USER, PASTE_PASS, async function (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)
- return console.log('Error updating paste...');
- });
- });
+ return await paste.edit(PASTE_ID, {
+ contents: stringified
+ }, async function (succ, _res) {
+ if (!succ)
+ return console.log('Error updating paste...');
+ });
+ });
};
diff --git a/lib/api/urban.ts b/lib/api/urban.ts
@@ -1,30 +1,30 @@
import unirest from 'unirest';
type Options = {
- query : string,
- key : string
+ query : string,
+ key : string
}
export const urban_search = (options : Options) => new Promise((resolve, reject) => {
- console.log('Searching Urban Dictionary, with options: ', options);
+ console.log('Searching Urban Dictionary, with options: ', options);
- const url = 'https://mashape-community-urban-dictionary.p.rapidapi.com/define';
+ const url = 'https://mashape-community-urban-dictionary.p.rapidapi.com/define';
- const req = unirest('GET', url);
+ const req = unirest('GET', url);
- req.query({
- "term": options.query
- });
+ req.query({
+ "term": options.query
+ });
- req.headers({
- "x-rapidapi-host": "mashape-community-urban-dictionary.p.rapidapi.com",
- "x-rapidapi-key": options.key
- });
+ req.headers({
+ "x-rapidapi-host": "mashape-community-urban-dictionary.p.rapidapi.com",
+ "x-rapidapi-key": options.key
+ });
- req.end(res => {
- if (res.error) return reject(res.error);
- return resolve(res.body);
- });
+ req.end(res => {
+ if (res.error) return reject(res.error);
+ return resolve(res.body);
+ });
});
export default urban_search;
diff --git a/lib/api/youtube.ts b/lib/api/youtube.ts
@@ -11,66 +11,66 @@ import '../extensions';
const SCOPES = ['https://www.googleapis.com/auth/youtube.readonly'];
type YTSearch = {
- key: string,
- query: string
+ key: string,
+ query: string
};
const web_search = (param: YTSearch) => new Promise((resolve, reject) => {
- const yt = google.youtube('v3');
- const auth = new OAuth2({
- clientId: process.env['GOOGLE_OAUTH_ID'],
- clientSecret: process.env['GOOGLE_OAUTH_SECRET'],
- redirectUri: 'https://google.com/'
- });
- // const auth_url = auth.generateAuthUrl({
- // access_type: 'offline',
- // scope: SCOPES
- // });
- // console.log('Authorize this app by visiting this url: ', auth_url);
- auth.getToken(process.env['GOOGLE_PERSONAL_CODE']).then(code => {
- auth.setCredentials(code.tokens);
- yt.search.list({
- q: param.query,
- maxResults: 1,
- auth: auth,
- part: 'snippet'
- }).then(res => {
- if (!res.data || !res.data.items || res.data.items.length === 0)
- return reject('No such results found.')
+ const yt = google.youtube('v3');
+ const auth = new OAuth2({
+ clientId: process.env['GOOGLE_OAUTH_ID'],
+ clientSecret: process.env['GOOGLE_OAUTH_SECRET'],
+ redirectUri: 'https://google.com/'
+ });
+ // const auth_url = auth.generateAuthUrl({
+ // access_type: 'offline',
+ // scope: SCOPES
+ // });
+ // console.log('Authorize this app by visiting this url: ', auth_url);
+ auth.getToken(process.env['GOOGLE_PERSONAL_CODE']).then(code => {
+ auth.setCredentials(code.tokens);
+ yt.search.list({
+ q: param.query,
+ maxResults: 1,
+ auth: auth,
+ part: 'snippet'
+ }).then(res => {
+ if (!res.data || !res.data.items || res.data.items.length === 0)
+ return reject('No such results found.')
- const video = res.data.items[0];
- const id = video.id.videoId;
+ const video = res.data.items[0];
+ const id = video.id.videoId;
- yt.videos.list({
- part: 'statistics', id
- }).then(vid_res => {
- const title = video.snippet.title;
- const { viewCount: views,
- likeCount: likes,
- dislikeCount: dislikes } = vid_res.data.items[0].statistics;
+ yt.videos.list({
+ part: 'statistics', id
+ }).then(vid_res => {
+ const title = video.snippet.title;
+ const { viewCount: views,
+ likeCount: likes,
+ dislikeCount: dislikes } = vid_res.data.items[0].statistics;
- const url = `https://youtu.be/${id}/`;
- const by = video.snippet.channelTitle;
- console.log(video);
+ const url = `https://youtu.be/${id}/`;
+ const by = video.snippet.channelTitle;
+ console.log(video);
- return resolve(`${url}\n> ${title} | ${views} views | \
- :+1: ${likes} — :-1: ${dislikes} \nby: ${by}.`.squeeze());
- }).catch(e =>
- reject(`No results, or API capped...\n\`\`\`\n${e}\n\`\`\``));
- }).catch(e =>
- reject(`No results, or API capped...\n\`\`\`\n${e}\n\`\`\``));
- }).catch(err => {
- console.log('Error with code:', err);
- reject('Token probably expired, i.e. logged out.\n'
- + '```\n' + err.toString() + '\n```');
- });
+ return resolve(`${url}\n> ${title} | ${views} views | \
+ :+1: ${likes} — :-1: ${dislikes} \nby: ${by}.`.squeeze());
+ }).catch(e =>
+ reject(`No results, or API capped...\n\`\`\`\n${e}\n\`\`\``));
+ }).catch(e =>
+ reject(`No results, or API capped...\n\`\`\`\n${e}\n\`\`\``));
+ }).catch(err => {
+ console.log('Error with code:', err);
+ reject('Token probably expired, i.e. logged out.\n'
+ + '```\n' + err.toString() + '\n```');
+ });
});
export default web_search;
web_search({
- key: process.env['GOOGLE_API_KEY'],
- query: 'cat videos'
+ key: process.env['GOOGLE_API_KEY'],
+ query: 'cat videos'
}).then(res => {
- console.log(res);
+ console.log(res);
}).catch(console.log);
diff --git a/lib/api/yt_scrape.ts b/lib/api/yt_scrape.ts
@@ -2,24 +2,24 @@ const search = require('scrape-youtube');
import '../extensions';
type YTSearch = {
- query: string
+ query: string
};
const yt_search = (params: YTSearch) => new Promise((resolve, reject) => {
- search(params.query, {
- limit: 2,
- type: 'video'
- }).then(res => {
- if (!res || res.length === 0) {
- return reject('No YouTube results found.');
- }
+ search(params.query, {
+ limit: 2,
+ type: 'video'
+ }).then(res => {
+ if (!res || res.length === 0) {
+ return reject('No YouTube results found.');
+ }
- const { channel: by, title, views,
- upload_date, link: url } = res[0];
+ const { channel: by, title, views,
+ upload_date, link: url } = res[0];
- return resolve(`${url}\n> ${title} | ${views.to_metric()} views | \
- uploaded ${upload_date} | by: ${by}.`.squeeze());
- });
+ return resolve(`${url}\n> ${title} | ${views.to_metric()} views | \
+ uploaded ${upload_date} | by: ${by}.`.squeeze());
+ });
});
export default yt_search;
diff --git a/lib/default.ts b/lib/default.ts
@@ -3,125 +3,129 @@
/// laid out. All fields are accounted for here.
export default {
- name: "Simp'O'Matic",
- tag: "#1634",
- permissions: 8,
- lang: 'en',
+ name: "Simp'O'Matic",
+ tag: "#1634",
+ permissions: 8,
+ lang: 'en',
- weather_locations: {
- '541761315887120399': 'Moscow'
- },
- commands: {
- prefix: '!',
- max_history: 40,
- not_understood: "Command not understood",
- aliases: {
- 'img': 'image',
- 'i': 'image',
- 'h': 'help',
- 's': 'search',
- 'web': 'search',
- 'g': 'search',
- 'google': 'search',
- 'bing': 'search',
- 'yt': 'youtube',
- 'y': 'youtube',
- 'd': 'define',
- 'def': 'define',
- 'oed': 'define',
- 'oxford': 'define',
- 'ud': 'urban',
- 'u': 'urban',
- 'blacklist': 'ignore',
- 'whitelist': 'ignore whitelist',
- 'w': 'weather',
- 'reply': 'respond',
- 'reject': 'delete',
- 'wa': 'wolfram',
- 'wolf': 'wolfram',
- 'toilet': 'figlet',
- 'wiki': 'wikipedia',
- 'aliases': 'alias',
- 'boomerfy': 'boomer',
- 'mocking': 'mock',
- 'pull': 'fork',
- 'git': 'github',
- 'bug': 'issue',
- 'source': 'github',
- 'save': 'export'
- },
- },
+ weather_locations: {
+ '541761315887120399': 'Moscow'
+ },
+ commands: {
+ prefix: '!',
+ max_history: 40,
+ not_understood: "Command not understood",
+ aliases: {
+ 'img': 'image',
+ 'i': 'image',
+ 'h': 'help',
+ 's': 'search',
+ 'web': 'search',
+ 'g': 'search',
+ 'google': 'search',
+ 'bing': 'search',
+ 'yt': 'youtube',
+ 'y': 'youtube',
+ 'd': 'define',
+ 'def': 'define',
+ 'oed': 'define',
+ 'oxford': 'define',
+ 'ud': 'urban',
+ 'u': 'urban',
+ 'blacklist': 'ignore',
+ 'whitelist': 'ignore whitelist',
+ 'w': 'weather',
+ 'reply': 'respond',
+ 'reject': 'delete',
+ 'wa': 'wolfram',
+ 'wolf': 'wolfram',
+ 'toilet': 'figlet',
+ 'wiki': 'wikipedia',
+ 'aliases': 'alias',
+ 'boomerfy': 'boomer',
+ 'mocking': 'mock',
+ 'pull': 'fork',
+ 'git': 'github',
+ 'bug': 'issue',
+ 'source': 'github',
+ 'save': 'export'
+ },
+ },
- rules: {
- // Below are the different kinds of _rules_.
- respond: [
- {
- match: "/^\\s*thanks.*\\s*$/i",
- response: 'Obama.'
- },
- ],
- reject: [
- {
- match: "/\\.{4,}/",
- response: null
- },
- ],
- replace: [ // Message editing functionality not a thing yet...
- {
- match: "/tbh/i",
- response: 'desu'
- },
- ],
- // Blacklist (initially everyone can do everything,
- // except for those listed specifically on this list).
- blacklist: {
- channels: [
- 'music', 'news'
- ],
- users: {
- // Should all be numbers/hashes, this one is bogus:
- "instcel": {
- commands: true,
- commands_elevated: false,
- speech: true,
- },
- // For real this time:
- // -> `accelarion#0764`
- // a.k.a. instcel,
- // a.k.a. instgen,
- // a.k.a. installgentoo.
- "409461942495871016": {
- commands: true,
- commands_elevated: false,
- speech: true
- }
- },
- groups: {
- // Should all be numbers/hashes, these are bogus.
- "obese": {
- commands_elevated: false,
- },
- "bpd": {
- commands: false,
- }
- },
- },
+ rules: {
+ // Below are the different kinds of _rules_.
+ respond: [
+ {
+ match: "/^\\s*thanks.*\\s*$/i",
+ response: 'Obama.'
+ },
+ {
+ match: "/[^\p{L}\p{N}]bot[^\p{L}\p{N}]/i",
+ respond: "The hell you sayn' about bots?"
+ }
+ ],
+ reject: [
+ {
+ match: "/\\.{4,}/",
+ response: null
+ },
+ ],
+ replace: [ // Message editing functionality not a thing yet...
+ {
+ match: "/tbh/i",
+ response: 'desu'
+ },
+ ],
+ // Blacklist (initially everyone can do everything,
+ // except for those listed specifically on this list).
+ blacklist: {
+ channels: [
+ 'music', 'news'
+ ],
+ users: {
+ // Should all be numbers/hashes, this one is bogus:
+ "instcel": {
+ commands: true,
+ commands_elevated: false,
+ speech: true,
+ },
+ // For real this time:
+ // -> `accelarion#0764`
+ // a.k.a. instcel,
+ // a.k.a. instgen,
+ // a.k.a. installgentoo.
+ "409461942495871016": {
+ commands: true,
+ commands_elevated: false,
+ speech: true
+ }
+ },
+ groups: {
+ // Should all be numbers/hashes, these are bogus.
+ "obese": {
+ commands_elevated: false,
+ },
+ "bpd": {
+ commands: false,
+ }
+ },
+ },
- // In case you blacklist @everyone or something,
- // you can override completely, to obtain all permissions just
- // by putting them on the whitelist.
- whitelist: {
- users: [
- // Dr. Henry Kissinger#5457
- "265958795254038535",
- // Danny#1986
- "541761315887120399"
- ],
- groups: [
- // Will all be numbers/hashes too.
- "bourgeoisie"
- ]
- }
- }
+ // In case you blacklist @everyone or something,
+ // you can override completely, to obtain all permissions just
+ // by putting them on the whitelist.
+ whitelist: {
+ users: [
+ // Dr. Henry Kissinger#5457
+ "265958795254038535",
+ // Danny#1986
+ "541761315887120399"
+ ],
+ groups: [
+ // Will all be numbers/hashes too.
+ "bourgeoisie"
+ ]
+ }
+ }
}
diff --git a/lib/extensions.ts b/lib/extensions.ts
@@ -1,53 +1,53 @@
// 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>
- }
-
- interface String {
- squeeze(): string
- capitalize(): string
- leading_space(): string
- head(): string
- tail(): string
- first(): string
- last(off? : number): string
- }
-
- interface Number {
- round_to(dp: number): number
- to_metric(figures): string
- }
+ 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>
+ }
+
+ interface String {
+ squeeze(): string
+ capitalize(): string
+ leading_space(): string
+ head(): string
+ tail(): string
+ first(): string
+ last(off? : number): string
+ }
+
+ interface Number {
+ round_to(dp: number): number
+ to_metric(figures): string
+ }
}
// Array Extensions:
Array.prototype.head = function () {
- return this[0];
+ return this[0];
};
Array.prototype.first = Array.prototype.head;
Array.prototype.tail = function () {
- return this.slice(1);
+ return this.slice(1);
};
Array.prototype.last = function (off=0) {
- return this[this.length - 1 - off];
+ return this[this.length - 1 - off];
};
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); };
@@ -55,32 +55,32 @@ Array.prototype.squeeze = function () { return this.filter(e => !!e); };
Array.prototype.each = Array.prototype.forEach;
Array.prototype.mut_unique = function () {
- const uniq = this.unique();
- Object.assign(this, uniq);
- this.splice(uniq.length);
- return this;
+ const uniq = this.unique();
+ Object.assign(this, uniq);
+ this.splice(uniq.length);
+ return this;
};
Array.prototype.mut_map = function (f) {
- for (const i in this) {
- this[i] = f(this[i]);
- }
- return this;
+ for (const i in this) {
+ this[i] = f(this[i]);
+ }
+ return this;
};
// String Extensions:
String.prototype.squeeze = function () {
- return this
- .replace(/[\t\s]+/g, ' ')
- .replace(/\n[\t\s]/g, '\n');
+ return this
+ .replace(/[\t\s]+/g, ' ')
+ .replace(/\n[\t\s]/g, '\n');
};
String.prototype.leading_space = function () {
- return this.replace(/\n[ ]([^ ]+)/g, '\n$1');
+ return this.replace(/\n[ ]([^ ]+)/g, '\n$1');
};
String.prototype.capitalize = function () {
- return this.charAt(0).toUpperCase() + this.slice(1);
+ return this.charAt(0).toUpperCase() + this.slice(1);
};
String.prototype.head = Array.prototype.head as any;
@@ -90,42 +90,42 @@ String.prototype.last = Array.prototype.last as any;
// Number Extensions:
Number.prototype.round_to = function (dp : number) {
- const exp = 10 ** dp;
- return Math.round(this.valueOf() * exp) / exp;
+ const exp = 10 ** dp;
+ return Math.round(this.valueOf() * exp) / exp;
};
const SI_EXTENSIONS = [
- { value: 1, symbol: "" },
- { value: 1E3, symbol: "k" },
- { value: 1E6, symbol: "M" },
- { value: 1E9, symbol: "G" },
- { value: 1E12, symbol: "T" },
- { value: 1E15, symbol: "P" },
- { value: 1E18, symbol: "E" }
+ { value: 1, symbol: "" },
+ { value: 1E3, symbol: "k" },
+ { value: 1E6, symbol: "M" },
+ { value: 1E9, symbol: "G" },
+ { value: 1E12, symbol: "T" },
+ { value: 1E15, symbol: "P" },
+ { value: 1E18, symbol: "E" }
];
Number.prototype.to_metric = function (figures) {
- let i = SI_EXTENSIONS.length - 1;
- for (; i > 0; --i)
- if (this >= SI_EXTENSIONS[i].value)
- break;
-
- return (this.valueOf() / SI_EXTENSIONS[i].value)
- .toFixed(figures)
- .replace(/\.0+$|(\.[0-9]*[1-9])0+$/, "$1")
- + SI_EXTENSIONS[i].symbol;
+ let i = SI_EXTENSIONS.length - 1;
+ for (; i > 0; --i)
+ if (this >= SI_EXTENSIONS[i].value)
+ break;
+
+ return (this.valueOf() / SI_EXTENSIONS[i].value)
+ .toFixed(figures)
+ .replace(/\.0+$|(\.[0-9]*[1-9])0+$/, "$1")
+ + SI_EXTENSIONS[i].symbol;
};
// Discord Extensions:
declare module 'discord.js' {
- interface Message {
- answer(...args: any): void
- }
+ interface Message {
+ answer(...args: any): void
+ }
}
import { Message } from 'discord.js';
Message.prototype.answer = function (...args) {
- return this.channel.send(`${this.author}, ${args[0]}`,
- ...(args.slice(1)));
+ return this.channel.send(`${this.author}, ${args[0]}`,
+ ...(args.slice(1)));
};
diff --git a/lib/format_oed.ts b/lib/format_oed.ts
@@ -3,69 +3,69 @@ import { pp } from './utils';
// Mmm... spaghetti...
export default (res, message) => {
- let msg = `Definition for ‘${res.word}’, yielded:\n`;
- let has_sent_audio = false;
+ let msg = `Definition for ‘${res.word}’, yielded:\n`;
+ let has_sent_audio = false;
- const lex_entries = res['results'][0].lexicalEntries;
- let entry_n = 1;
- for (const lex_entry of lex_entries) {
- if (lex_entries.length > 1) {
- msg += `\nLexical Entry №${entry_n}:\n`
- entry_n += 1;
- }
- console.log('Lex entry:', pp(lex_entries))
- for (const entry of Object.values(lex_entry.entries)) {
- const senses = entry['senses'];
+ const lex_entries = res['results'][0].lexicalEntries;
+ let entry_n = 1;
+ for (const lex_entry of lex_entries) {
+ if (lex_entries.length > 1) {
+ msg += `\nLexical Entry №${entry_n}:\n`
+ entry_n += 1;
+ }
+ console.log('Lex entry:', pp(lex_entries))
+ for (const entry of Object.values(lex_entry.entries)) {
+ const senses = entry['senses'];
- for (const sense of Object.values(senses) as any) {
- let sense_msg = "";
- if (!!sense.definitions && sense.definitions.length > 0) {
- for (const definition
- of Object.values(sense.definitions) as any) {
- sense_msg += ` Defined as (${lex_entry.lexicalCategory.text.toLowerCase()}):\n> ${definition.capitalize()}\n`;
- }
- }
- if (!!sense.synonyms && sense.synonyms.length > 0) {
- const synonyms = sense.synonyms
- .map(s => `‘${s.text}’`)
- .join(', ');
- sense_msg += ` Synonyms include: ${synonyms}\n`;
- }
- if (sense_msg.trim().length > 0) {
- msg += "\nIn the sense:\n"
- msg += sense_msg;
- }
- }
- const etys = entry['etymologies'];
- if (!!etys && etys.length > 0) {
- msg += '\nEtymology:\n ';
- msg += etys.join(';\n ');
- msg += '\n';
- }
- }
- if (!!lex_entry.pronunciations && !has_sent_audio) {
- const prons = Object.values(lex_entry.pronunciations) as any;
- if (!!prons && prons.length > 0) {
- msg += "\nPronunciations:\n"
- for (const pron of prons) {
- if (!!pron.dialects) {
- const dialects = Object.values(pron.dialects);
- msg += ` Dialects of ${dialects.join(', ')}:\n`;
- }
- msg += ` ${pron.phoneticNotation}: [${pron.phoneticSpelling}]\n`;
- if (pron.audioFile) {
- msg += ` Audio file: ${pron.audioFile}\n`;
- has_sent_audio = !has_sent_audio;
- const attach = new Attachment(
- pron.audioFile,
- pron.audioFile.split('/').slice(-1)[0]
- );
- message.channel.send('', attach);
- }
- }
- }
- }
- }
- console.log('Became:', msg);
- return msg;
+ for (const sense of Object.values(senses) as any) {
+ let sense_msg = "";
+ if (!!sense.definitions && sense.definitions.length > 0) {
+ for (const definition
+ of Object.values(sense.definitions) as any) {
+ sense_msg += ` Defined as (${lex_entry.lexicalCategory.text.toLowerCase()}):\n> ${definition.capitalize()}\n`;
+ }
+ }
+ if (!!sense.synonyms && sense.synonyms.length > 0) {
+ const synonyms = sense.synonyms
+ .map(s => `‘${s.text}’`)
+ .join(', ');
+ sense_msg += ` Synonyms include: ${synonyms}\n`;
+ }
+ if (sense_msg.trim().length > 0) {
+ msg += "\nIn the sense:\n"
+ msg += sense_msg;
+ }
+ }
+ const etys = entry['etymologies'];
+ if (!!etys && etys.length > 0) {
+ msg += '\nEtymology:\n ';
+ msg += etys.join(';\n ');
+ msg += '\n';
+ }
+ }
+ if (!!lex_entry.pronunciations && !has_sent_audio) {
+ const prons = Object.values(lex_entry.pronunciations) as any;
+ if (!!prons && prons.length > 0) {
+ msg += "\nPronunciations:\n"
+ for (const pron of prons) {
+ if (!!pron.dialects) {
+ const dialects = Object.values(pron.dialects);
+ msg += ` Dialects of ${dialects.join(', ')}:\n`;
+ }
+ msg += ` ${pron.phoneticNotation}: [${pron.phoneticSpelling}]\n`;
+ if (pron.audioFile) {
+ msg += ` Audio file: ${pron.audioFile}\n`;
+ has_sent_audio = !has_sent_audio;
+ const attach = new Attachment(
+ pron.audioFile,
+ pron.audioFile.split('/').slice(-1)[0]
+ );
+ message.channel.send('', attach);
+ }
+ }
+ }
+ }
+ }
+ console.log('Became:', msg);
+ return msg;
}
diff --git a/lib/main.ts b/lib/main.ts
@@ -314,12 +314,6 @@ export class SimpOMatic {
}
message.answer(`Current command prefix is: \`${CONFIG.commands.prefix}\`.`);
break;
- } case 'ignore': {
- // Man alive, someone else do this please.
- break;
- } case 'response': {
- // TODO
- break
} case 'search': {
const query = args.join(' ').toLowerCase();
@@ -503,9 +497,22 @@ export class SimpOMatic {
process_generic(message : Message) {
const { content } = message;
- if (content.includes('bot'))
+ if (content.includes(' bot '))
message.answer("The hell you sayn' about bots?");
- // TODO: Process _rules_ appropriately.
+
+ for (const responder of CONFIG.rules.respond) {
+ const match = content.match(responder.match);
+ const { response } = responder;
+ if (match && response) message.answer(response);
+ }
+ for (const rejecter of CONFIG.rules.reject) {
+ const match = content.match(rejecter.match);
+ const { response } = rejecter;
+ if (match) {
+ if (response) message.answer(response);
+ if (message.deletable) message.delete();
+ }
+ }
}
async last_message(opts) : Promise<string> {
diff --git a/lib/utils.ts b/lib/utils.ts
@@ -5,101 +5,101 @@ import './extensions';
// This assumes no two string-array entries
// would ever be greater than 2000 characters long.
export const glue_strings = arr => {
- let acc = "";
- const new_strings = [];
- for (const msg of arr)
- if (acc.length + msg.length >= 2000) {
- new_strings.push(acc);
- acc = msg;
- } else { acc += msg; }
- new_strings.push(acc);
- return new_strings;
+ let acc = "";
+ const new_strings = [];
+ for (const msg of arr)
+ if (acc.length + msg.length >= 2000) {
+ new_strings.push(acc);
+ acc = msg;
+ } else { acc += msg; }
+ new_strings.push(acc);
+ return new_strings;
};
export const access = (obj: any, shiftable: string[]) =>
- (shiftable.length === 0)
- ? obj
- : access(obj[shiftable.shift()], shiftable);
+ (shiftable.length === 0)
+ ? obj
+ : access(obj[shiftable.shift()], shiftable);
export const type: (obj: any) => string = (global => obj =>
- (obj === global)
- ? 'global'
- : ({})
- .toString.call(obj)
- .match(/\s([a-z|A-Z]+)/)[1]
- .toLowerCase())(this);
+ (obj === global)
+ ? 'global'
+ : ({})
+ .toString.call(obj)
+ .match(/\s([a-z|A-Z]+)/)[1]
+ .toLowerCase())(this);
export const pp = o => inspect(o, {
- colors: true,
- showHidden: false,
- depth: 23
+ colors: true,
+ showHidden: false,
+ depth: 23
});
export const deep_merge_pair = (target, source) => {
- Object.keys(source).each(key => {
- const target_value = target[key];
- const source_value = source[key];
+ Object.keys(source).each(key => {
+ const target_value = target[key];
+ const source_value = source[key];
- if (Array.isArray(target_value)
- && Array.isArray(source_value)) {
- target[key] = target_value.concat(...source_value);
- }
- else if (type(target_value) === 'object'
- && type(source_value) === 'object') {
- target[key] = deep_merge_pair(target_value, source_value);
- }
- else {
- target[key] = source_value;
- }
- });
+ if (Array.isArray(target_value)
+ && Array.isArray(source_value)) {
+ target[key] = target_value.concat(...source_value);
+ }
+ else if (type(target_value) === 'object'
+ && type(source_value) === 'object') {
+ target[key] = deep_merge_pair(target_value, source_value);
+ }
+ else {
+ target[key] = source_value;
+ }
+ });
- return target;
+ return target;
}
export const deep_merge = (...objects) =>
- (objects.length === 2)
- ? deep_merge_pair(objects[0], objects[1])
- : deep_merge_pair(objects[0], deep_merge(objects.slice(1)));
+ (objects.length === 2)
+ ? deep_merge_pair(objects[0], objects[1])
+ : deep_merge_pair(objects[0], deep_merge(objects.slice(1)));
export const parse_regex = (s : string) => {
- let temp = s.split('/');
- const options = temp.pop();
- temp.shift();
- const contents = temp.join('/');
- return new RegExp(contents, options);
+ let temp = s.split('/');
+ const options = temp.pop();
+ temp.shift();
+ const contents = temp.join('/');
+ return new RegExp(contents, options);
};
export const compile_match = obj => {
- const o = deep_clone(obj);
- if (type(o.match) === 'string') {
- o.match = parse_regex(o.match);
- }
- return o;
+ const o = deep_clone(obj);
+ if (type(o.match) === 'string') {
+ o.match = parse_regex(o.match);
+ }
+ return o;
};
const recursive_regex_to_string = o => {
- if (type(o) === 'regexp') {
- return o.toString();
- }
- if (type(o) === 'object' || type(o) === 'array') {
- for (const key in o) {
- o[key] = recursive_regex_to_string(o[key]);
- }
- return o;
- }
- return o;
+ if (type(o) === 'regexp') {
+ return o.toString();
+ }
+ if (type(o) === 'object' || type(o) === 'array') {
+ for (const key in o) {
+ 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));
- // Make sure all rules are unique,
- // i.e. eliminate duplicate rules.
- ['respond', 'reject', 'replace']
- .forEach(name => o.rules[name] = o.rules[name]
- .map(JSON.stringify)
- .unique()
- .map(JSON.parse));
+ const o = recursive_regex_to_string(deep_clone(obj));
+ // Make sure all rules are unique,
+ // i.e. eliminate duplicate rules.
+ ['respond', 'reject', 'replace']
+ .forEach(name => o.rules[name] = o.rules[name]
+ .map(JSON.stringify)
+ .unique()
+ .map(JSON.parse));
- return JSON.stringify(o, null, ugly ? null : 4);
+ return JSON.stringify(o, null, ugly ? null : 4);
};