// Copyright 2018-2019 Campbell Crowley. All rights reserved.
// Author: Campbell Crowley (dev@campbellcrowley.com)
require('./subModule.js').extend(Define); // Extends the SubModule class.
const https = require('https');
const auth = require('../auth.js');
/**
* @classdesc Handles defining word commands.
* @class
* @augments SubModule
* @listens Command#define
*/
function Define() {
const self = this;
/** @inheritdoc */
this.myName = 'Define';
/** @inheritdoc */
this.initialize = function() {
self.command.on(
new self.command.SingleCommand(
['define', 'def', 'definition'], commandDefine));
};
/** @inheritdoc */
this.shutdown = function() {
self.command.deleteEvent('define');
};
/**
* Base url to request from api.
*
* @private
* @default
* @constant
* @type {string}
*/
const reqURL = 'https://wordsapiv1.p.rapidapi.com/words/';
/**
* Handle the user asking for a definition.
*
* @private
* @type {commandHandler}
* @param {Discord~Message} msg Message that triggered command.
* @listens Command#define
*/
function commandDefine(msg) {
const match = (msg.text || '').match(/(\w+)/);
if (!match) {
self.common.reply(msg, 'Please specify a word to define.')
.catch(() => {});
return;
}
const word = match[1];
msg.channel.sendTyping();
const opt = {headers: {'User-Agent': self.common.ua}};
const req = https.request(reqURL + word, opt, (res) => {
let data = '';
res.on('data', (chunk) => {
data += chunk;
});
res.on('end', () => {
let parsed;
try {
parsed = JSON.parse(data);
} catch (err) {
self.common.reply(msg, 'Oops, something is broken.').catch(() => {});
return;
}
if (parsed.success ===false) {
self.common.reply(
msg, 'Unable to find word in the dictionary.', word);
} else {
replyDef(msg, parsed);
}
if (msg.text.indexOf('--raw') > -1) {
msg.channel.send({
content: '```' +
JSON.stringify(parsed, null, 1).substring(0, 1994) + '```',
});
}
});
});
req.on('error', (err) => {
self.error('Error while fetching definition: ' + err.message);
console.error(err);
});
req.setHeader('X-RapidAPI-Key', auth.wordsAPIKey);
req.end();
}
/**
* Format a Discord message reply from the given data.
*
* @private
* @param {Discord~Message} msg Message to reply to.
* @param {object} data Parsed reply from words api.
*/
function replyDef(msg, data) {
const embed = new self.Discord.EmbedBuilder();
embed.setTitle(fUp(data.word));
embed.setColor([255, 0, 255]);
if (data.results) {
const list = data.results.slice(0, 5).map(formatSingle);
embed.addFields([{
name: 'Definition' + (data.results.length > 1 ? 's' : ''),
value: list.join('\n'),
}]);
}
if (data.syllables) {
embed.addFields([{
name: 'Syllable' + (data.syllables.count > 1 ? 's (' : ' (') +
data.syllables.count + ')',
value: data.syllables.list.join(' '),
}]);
}
if (data.pronunciation) {
const p = typeof data.pronunciation === 'string' ?
data.pronunciation :
Object.entries(data.pronunciation)
.map((el) => {
return el[0] + ': ' + el[1];
})
.join('\n');
embed.addFields([{name: 'Pronunciation', value: p}]);
}
msg.channel.send({content: self.common.mention(msg), embeds: [embed]});
}
/**
* Format a single definition to string format.
*
* @private
* @param {{
* definition: string,
* partOfSpeech: string,
* examples: Array.<string>,
* synonyms: Array.<string>
* }} el Definition object to format.
* @returns {string} Formatted string.
*/
function formatSingle(el) {
let res = '';
if (el.definition) {
res += `__${fUp(el.definition)}__\n`;
}
if (el.synonyms) {
res += el.synonyms.join(', ') + '\n';
}
if (el.partOfSpeech) {
res += `*${el.partOfSpeech}*\n`;
}
if (el.examples) {
res += el.examples.map((el, i) => (i + 1) + ') ' + fUp(el)).join('\n') +
'\n';
}
return res;
}
/**
* Capitalize first character of string, and lowercase the rest.
*
* @private
* @param {string} s Input.
* @returns {string} Output.
*/
function fUp(s) {
return s[0].toLocaleUpperCase() + s.slice(1).toLocaleLowerCase();
}
}
module.exports = new Define();