2020-12-05 10:00:00 +00:00
|
|
|
import { httpGET } from '@squeebot/core/lib/common';
|
|
|
|
import { logger } from '@squeebot/core/lib/core';
|
|
|
|
import {
|
|
|
|
Plugin,
|
|
|
|
Configurable,
|
|
|
|
EventListener,
|
|
|
|
DependencyLoad
|
|
|
|
} from '@squeebot/core/lib/plugin';
|
2020-12-13 10:16:49 +00:00
|
|
|
import { IMessage, MessageResolver } from '@squeebot/core/lib/types';
|
2020-12-05 10:00:00 +00:00
|
|
|
|
2021-09-15 18:50:28 +00:00
|
|
|
interface IDefinition {
|
|
|
|
partOfSpeech: string;
|
|
|
|
text: string;
|
|
|
|
}
|
|
|
|
|
|
|
|
interface IWordCache {
|
|
|
|
lastIndex: {
|
|
|
|
name: string;
|
|
|
|
index: number;
|
|
|
|
lastTime: number;
|
|
|
|
}[];
|
|
|
|
definitions: IDefinition[];
|
|
|
|
inserted: number;
|
|
|
|
}
|
2020-12-05 10:00:00 +00:00
|
|
|
|
|
|
|
let lastQuery = 0;
|
2021-09-15 18:50:28 +00:00
|
|
|
const wordCache: Record<string, IWordCache> = {};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Find and remove words that haven't been looked up for a day
|
|
|
|
*/
|
2021-10-02 09:31:46 +00:00
|
|
|
function flushCache(): void {
|
|
|
|
const oldestWords: string[] = [];
|
2021-09-15 18:50:28 +00:00
|
|
|
Object.keys(wordCache).forEach((word) => {
|
|
|
|
const cached = wordCache[word];
|
|
|
|
let notOld = false;
|
|
|
|
|
|
|
|
for (const person of cached.lastIndex) {
|
|
|
|
if (person.lastTime > Date.now() - 24 * 60 * 60 * 1000) {
|
|
|
|
notOld = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!notOld) {
|
|
|
|
oldestWords.push(word);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
if (!oldestWords.length) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
oldestWords.forEach((item) => {
|
|
|
|
delete wordCache[item];
|
|
|
|
});
|
|
|
|
|
|
|
|
logger.log(`[diction] Dictionary cleared of the previous words ${oldestWords.join(', ')}.`);
|
|
|
|
}
|
2020-12-05 10:00:00 +00:00
|
|
|
|
|
|
|
@Configurable({
|
2021-09-15 18:50:28 +00:00
|
|
|
wordnik: '',
|
2020-12-05 10:00:00 +00:00
|
|
|
cooldown: 5,
|
2021-09-15 18:50:28 +00:00
|
|
|
limit: 5,
|
2020-12-05 10:00:00 +00:00
|
|
|
})
|
|
|
|
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 {
|
2021-09-15 18:50:28 +00:00
|
|
|
const key = this.config.get('wordnik');
|
2020-12-05 10:00:00 +00:00
|
|
|
const rate = this.config.get('cooldown', 5);
|
2021-09-15 18:50:28 +00:00
|
|
|
const limit = this.config.get('limit', 5);
|
2020-12-05 10:00:00 +00:00
|
|
|
|
2021-09-15 18:50:28 +00:00
|
|
|
if (!key) {
|
|
|
|
logger.warn('Wordnik define command is disabled due to no credentials.');
|
2020-12-05 10:00:00 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
cmd.registerCommand({
|
|
|
|
name: 'define',
|
|
|
|
plugin: this.name,
|
2020-12-13 10:16:49 +00:00
|
|
|
execute: async (msg: IMessage, msr: MessageResolver, spec: any, prefix: string, ...simplified: any[]): Promise<boolean> => {
|
2021-09-15 18:50:28 +00:00
|
|
|
const word = simplified.join(' ');
|
2020-12-05 10:00:00 +00:00
|
|
|
|
|
|
|
if (!word) {
|
|
|
|
msg.resolve('Please specifiy a word or term!');
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2021-09-15 18:50:28 +00:00
|
|
|
if (lastQuery > Date.now() - rate * 1000 && !wordCache[word]) {
|
|
|
|
msg.resolve('You\'re doing that too fast!');
|
2020-12-05 10:00:00 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2021-09-15 18:50:28 +00:00
|
|
|
let chosenDefinition: IDefinition;
|
|
|
|
let definitionCount = 0;
|
|
|
|
let definitionIndex = 0;
|
|
|
|
|
|
|
|
// Check cached definitions
|
|
|
|
const userTarget = msg.fullSenderID as string;
|
|
|
|
if (wordCache[word]) {
|
|
|
|
const cached = wordCache[word];
|
|
|
|
const alreadyAsked = cached.lastIndex.find((item) => item.name === userTarget);
|
|
|
|
definitionCount = cached.definitions.length;
|
|
|
|
|
|
|
|
if (alreadyAsked) {
|
|
|
|
let nextI = alreadyAsked.index + 1;
|
|
|
|
|
|
|
|
if (nextI >= cached.definitions.length) {
|
|
|
|
nextI = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (alreadyAsked.lastTime < Date.now() - 5 * 60 * 1000) {
|
|
|
|
nextI = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
alreadyAsked.lastTime = Date.now();
|
|
|
|
alreadyAsked.index = nextI;
|
|
|
|
definitionIndex = nextI;
|
|
|
|
chosenDefinition = cached.definitions[nextI];
|
|
|
|
} else {
|
|
|
|
chosenDefinition = cached.definitions[0];
|
|
|
|
cached.lastIndex.push({
|
|
|
|
name: userTarget,
|
|
|
|
index: 0,
|
|
|
|
lastTime: Date.now(),
|
|
|
|
});
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// Request definition
|
|
|
|
const encodedWord = encodeURIComponent(word);
|
|
|
|
lastQuery = Date.now();
|
|
|
|
|
|
|
|
let response;
|
|
|
|
try {
|
|
|
|
const s = `https://api.wordnik.com/v4/word.json/${encodedWord}/definitions?limit=${limit}&api_key=${key}`;
|
|
|
|
response = await httpGET(s);
|
|
|
|
response = JSON.parse(response);
|
|
|
|
} catch (e) {
|
|
|
|
msg.resolve('Server did not respond.');
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!response || !response.length) {
|
|
|
|
msg.resolve('No definitions found.');
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
const cached: IWordCache = {
|
|
|
|
lastIndex: [
|
|
|
|
{
|
|
|
|
name: userTarget,
|
|
|
|
index: 0,
|
|
|
|
lastTime: Date.now(),
|
|
|
|
}
|
|
|
|
],
|
2021-09-19 08:39:34 +00:00
|
|
|
definitions: (response as Record<string, string>[])
|
|
|
|
.filter(entry => entry.text)
|
2021-10-02 09:31:46 +00:00
|
|
|
.map((data) => ({
|
|
|
|
partOfSpeech: data.partOfSpeech,
|
|
|
|
text: data.text.replace(/(<([^>]+)>)/ig, ''),
|
2021-09-19 08:39:34 +00:00
|
|
|
})),
|
2021-09-15 18:50:28 +00:00
|
|
|
inserted: Date.now(),
|
2021-10-02 09:31:46 +00:00
|
|
|
};
|
2021-09-15 18:50:28 +00:00
|
|
|
|
|
|
|
wordCache[word] = cached;
|
|
|
|
chosenDefinition = cached.definitions[0];
|
|
|
|
definitionCount = cached.definitions.length;
|
|
|
|
logger.log(`[diction] Dictionary cached the word "${word}"`);
|
2020-12-05 10:00:00 +00:00
|
|
|
}
|
|
|
|
|
2021-09-15 18:50:28 +00:00
|
|
|
const { partOfSpeech, text } = chosenDefinition;
|
2020-12-05 10:00:00 +00:00
|
|
|
|
2021-09-15 18:50:28 +00:00
|
|
|
msg.resolve(`(${definitionIndex + 1}/${definitionCount}) ${word}${
|
|
|
|
partOfSpeech ? ` - ${partOfSpeech}` : ''
|
|
|
|
} - ${text}`);
|
2020-12-05 10:00:00 +00:00
|
|
|
|
|
|
|
return true;
|
|
|
|
},
|
2020-12-05 16:14:35 +00:00
|
|
|
match: /define (\w*)/,
|
2021-09-19 08:39:34 +00:00
|
|
|
aliases: ['df', 'word'],
|
2021-09-15 18:50:28 +00:00
|
|
|
description: 'Find definitions for words. Call again to advance to next definition',
|
|
|
|
usage: '<word>'
|
2020-12-05 10:00:00 +00:00
|
|
|
});
|
|
|
|
}
|
2021-10-02 09:31:46 +00:00
|
|
|
|
|
|
|
@DependencyLoad('cron')
|
|
|
|
public cronLoaded(cron: any): void {
|
|
|
|
cron.registerTimer(
|
|
|
|
this,
|
|
|
|
'0 0 * * *',
|
|
|
|
() => flushCache(),
|
|
|
|
);
|
|
|
|
}
|
2020-12-05 10:00:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
module.exports = DictionPlugin;
|