diff --git a/commands/events/doomsday-clock.js b/commands/events/doomsday-clock.js index 6ef5ae66..3433d85c 100644 --- a/commands/events/doomsday-clock.js +++ b/commands/events/doomsday-clock.js @@ -1,6 +1,7 @@ const Command = require('../../structures/Command'); const request = require('node-superfetch'); const { MessageEmbed } = require('discord.js'); +const { embedURL } = require('../../util/Util'); module.exports = class DoomsdayClockCommand extends Command { constructor(client) { @@ -26,7 +27,7 @@ module.exports = class DoomsdayClockCommand extends Command { const time = text.match(/

(.+)<\/h3>/)[1]; const year = text.match(/

(.+)<\/h5>/)[1]; const description = text.match(/
.+

(.+)<\/p>/)[1] - .replace(/(.+)<\/a>/, '[$2]($1)') + .replace(/(.+)<\/a>/, embedURL('$2', '$1')) .replace(/(.+)<\/em>/i, '_$1_'); const embed = new MessageEmbed() .setTitle(`${year}: ${time}`) diff --git a/commands/events/today-in-history.js b/commands/events/today-in-history.js index b6f31c60..60e2c028 100644 --- a/commands/events/today-in-history.js +++ b/commands/events/today-in-history.js @@ -1,6 +1,7 @@ const Command = require('../../structures/Command'); const { MessageEmbed } = require('discord.js'); const request = require('node-superfetch'); +const { embedURL } = require('../../util/Util'); module.exports = class TodayInHistoryCommand extends Command { constructor(client) { @@ -51,8 +52,7 @@ module.exports = class TodayInHistoryCommand extends Command { .setTitle(`On this day (${body.date})...`) .setTimestamp() .setDescription(`${event.year}: ${event.text}`) - .addField('❯ See More', - event.links.map(link => `[${link.title}](${link.link.replace(/\)/g, '%29')})`).join('\n')); + .addField('❯ See More', event.links.map(link => embedURL(link.title, link.link)).join('\n')); return msg.embed(embed); } catch (err) { if (err.status === 404 || err.status === 500) return msg.say('Invalid date.'); diff --git a/commands/info/avatar.js b/commands/info/avatar.js index 891689bf..c8ffe038 100644 --- a/commands/info/avatar.js +++ b/commands/info/avatar.js @@ -1,5 +1,6 @@ const Command = require('../../structures/Command'); const { MessageEmbed } = require('discord.js'); +const { embedURL } = require('../../util/Util'); module.exports = class AvatarCommand extends Command { constructor(client) { @@ -28,7 +29,7 @@ module.exports = class AvatarCommand extends Command { const embed = new MessageEmbed() .setTitle(user.tag) .setDescription( - formats.map(fmt => `[${fmt.toUpperCase()}](${user.displayAvatarURL({ format: fmt, size: 2048 })})`).join(' | ') + formats.map(fmt => embedURL(fmt.toUpperCase(), user.displayAvatarURL({ format: fmt, size: 2028 }))).join(' | ') ) .setImage(user.displayAvatarURL({ format, size: 2048 })) .setColor(0x00AE86); diff --git a/commands/readme/generate-credit.js b/commands/readme/generate-credit.js index 46fe42c4..74b19403 100644 --- a/commands/readme/generate-credit.js +++ b/commands/readme/generate-credit.js @@ -1,5 +1,5 @@ const Command = require('../../structures/Command'); -const { sortByName } = require('../../util/Util'); +const { sortByName, embedURL } = require('../../util/Util'); module.exports = class GenerateCreditCommand extends Command { constructor(client) { @@ -43,9 +43,9 @@ module.exports = class GenerateCreditCommand extends Command { } credit = sortByName(credit, 'name'); const mapped = credit - .map(c => `- [${c.name}](${c.url})\n${sortByName(c.commands, 'name').map(cmd => { + .map(c => `- ${embedURL(c.name, c.url)}\n${sortByName(c.commands, 'name').map(cmd => { if (!cmd.reasonURL) return ` * ${cmd.name} (${cmd.reason})`; - return ` * ${cmd.name} ([${cmd.reason}](${cmd.reasonURL}))`; + return ` * ${cmd.name} (${embedURL(c.reason, c.reasonURL)})`; }).join('\n')}`); return msg.channel.send({ files: [{ attachment: Buffer.from(mapped.join('\n')), name: 'credit.txt' }] }); } diff --git a/commands/search/anime.js b/commands/search/anime.js index a621c4dc..5ea24de7 100644 --- a/commands/search/anime.js +++ b/commands/search/anime.js @@ -3,7 +3,7 @@ const { MessageEmbed } = require('discord.js'); const request = require('node-superfetch'); const cheerio = require('cheerio'); const { stripIndents } = require('common-tags'); -const { cleanAnilistHTML } = require('../../util/Util'); +const { embedURL, cleanAnilistHTML } = require('../../util/Util'); const ANILIST_USERNAME = process.env.ANILIST_USERNAME || 'dragonfire535'; const searchGraphQL = stripIndents` query ($search: String, $type: MediaType, $isAdult: Boolean) { @@ -125,7 +125,7 @@ module.exports = class AnimeCommand extends Command { .addField('❯ Season', anime.season ? `${seasons[anime.season]} ${anime.startDate.year}` : '???', true) .addField('❯ Average Score', anime.meanScore ? `${anime.meanScore}%` : '???', true) .addField(`❯ ${ANILIST_USERNAME}'s Score`, entry && entry.score ? `${entry.score}/10` : '?/10', true) - .addField(`❯ MAL Score`, malScore ? `[${malScore}](${malURL})` : '???', true); + .addField(`❯ MAL Score`, malScore ? embedURL(malScore, malURL) : '???', true); return msg.embed(embed); } catch (err) { return msg.reply(`Oh no, an error occurred: \`${err.message}\`. Try again later!`); diff --git a/commands/search/character.js b/commands/search/character.js index 78c21122..d50b1535 100644 --- a/commands/search/character.js +++ b/commands/search/character.js @@ -2,7 +2,7 @@ const Command = require('../../structures/Command'); const { MessageEmbed } = require('discord.js'); const request = require('node-superfetch'); const { stripIndents } = require('common-tags'); -const { cleanAnilistHTML } = require('../../util/Util'); +const { embedURL, cleanAnilistHTML } = require('../../util/Util'); const searchGraphQL = stripIndents` query ($search: String) { characters: Page (perPage: 1) { @@ -85,7 +85,7 @@ module.exports = class CharacterCommand extends Command { .setDescription(character.description ? cleanAnilistHTML(character.description) : 'No description.') .addField('❯ Appearances', character.media.edges.map(edge => { const title = edge.node.title.english || edge.node.title.userPreferred; - return `[${title} (${types[edge.node.type]})](${edge.node.siteUrl})`; + return embedURL(`${title} (${types[edge.node.type]})`, edge.node.siteUrl); }).join(', ')); return msg.embed(embed); } catch (err) { diff --git a/commands/search/manga.js b/commands/search/manga.js index b6473837..c4579e04 100644 --- a/commands/search/manga.js +++ b/commands/search/manga.js @@ -3,7 +3,7 @@ const { MessageEmbed } = require('discord.js'); const request = require('node-superfetch'); const cheerio = require('cheerio'); const { stripIndents } = require('common-tags'); -const { cleanAnilistHTML } = require('../../util/Util'); +const { embedURL, cleanAnilistHTML } = require('../../util/Util'); const ANILIST_USERNAME = process.env.ANILIST_USERNAME || 'dragonfire535'; const searchGraphQL = stripIndents` query ($search: String, $type: MediaType, $isAdult: Boolean) { @@ -119,7 +119,7 @@ module.exports = class MangaCommand extends Command { .addField('❯ Year', manga.startDate.year || '???', true) .addField('❯ Average Score', manga.meanScore ? `${manga.meanScore}%` : '???', true) .addField(`❯ ${ANILIST_USERNAME}'s Score`, entry && entry.score ? `${entry.score}/10` : '?/10', true) - .addField(`❯ MAL Score`, malScore ? `[${malScore}](${malURL})` : '???', true); + .addField(`❯ MAL Score`, malScore ? embedURL(malScore, malURL) : '???', true); return msg.embed(embed); } catch (err) { return msg.reply(`Oh no, an error occurred: \`${err.message}\`. Try again later!`); diff --git a/commands/search/nasa.js b/commands/search/nasa.js index cea99a35..bcd5ebf5 100644 --- a/commands/search/nasa.js +++ b/commands/search/nasa.js @@ -1,7 +1,7 @@ const Command = require('../../structures/Command'); const request = require('node-superfetch'); const { MessageEmbed } = require('discord.js'); -const { shorten } = require('../../util/Util'); +const { shorten, embedURL } = require('../../util/Util'); module.exports = class NASACommand extends Command { constructor(client) { @@ -58,6 +58,6 @@ module.exports = class NASACommand extends Command { return text .replace(/<\/?b>/g, '**') .replace(/<\/?i>/g, '*') - .replace(/([^<>]+)<\/a>/g, '[$2]($1)'); + .replace(/([^<>]+)<\/a>/g, embedURL('$2', '$1')); } }; diff --git a/commands/search/soundcloud.js b/commands/search/soundcloud.js index e274ce20..8e718035 100644 --- a/commands/search/soundcloud.js +++ b/commands/search/soundcloud.js @@ -2,7 +2,7 @@ const Command = require('../../structures/Command'); const moment = require('moment'); const { MessageEmbed } = require('discord.js'); const request = require('node-superfetch'); -const { shorten, formatNumber } = require('../../util/Util'); +const { shorten, formatNumber, embedURL } = require('../../util/Util'); const { SOUNDCLOUD_KEY } = process.env; module.exports = class SoundcloudCommand extends Command { @@ -49,7 +49,7 @@ module.exports = class SoundcloudCommand extends Command { .setThumbnail(data.artwork_url) .setTitle(data.title) .setDescription(data.description ? shorten(data.description) : 'No description available.') - .addField('❯ Artist', `[${data.user.username}](${data.user.permalink_url})`, true) + .addField('❯ Artist', embedURL(data.user.username, data.user.permalink_url), true) .addField('❯ Release Date', moment.utc(new Date(data.created_at)).format('MM/DD/YYYY'), true) .addField('❯ Genre', data.genre || '???', true) .addField('❯ Likes', formatNumber(data.likes_count), true); diff --git a/commands/search/stack-overflow.js b/commands/search/stack-overflow.js index 21ef4ced..3589eb4c 100644 --- a/commands/search/stack-overflow.js +++ b/commands/search/stack-overflow.js @@ -2,7 +2,7 @@ const Command = require('../../structures/Command'); const moment = require('moment'); const { MessageEmbed } = require('discord.js'); const request = require('node-superfetch'); -const { formatNumber } = require('../../util/Util'); +const { formatNumber, embedURL } = require('../../util/Util'); const { STACKOVERFLOW_KEY } = process.env; module.exports = class StackOverflowCommand extends Command { @@ -53,7 +53,7 @@ module.exports = class StackOverflowCommand extends Command { .setURL(data.link) .setTitle(data.title) .addField('❯ ID', data.question_id, true) - .addField('❯ Asker', `[${data.owner.display_name}](${data.owner.link})`, true) + .addField('❯ Asker', embedURL(data.owner.display_name, data.owner.link), true) .addField('❯ Views', formatNumber(data.view_count), true) .addField('❯ Score', formatNumber(data.score), true) .addField('❯ Creation Date', moment.utc(data.creation_date * 1000).format('MM/DD/YYYY h:mm A'), true) diff --git a/commands/search/twitter.js b/commands/search/twitter.js index 0a4f62f3..031cea67 100644 --- a/commands/search/twitter.js +++ b/commands/search/twitter.js @@ -2,7 +2,7 @@ const Command = require('../../structures/Command'); const moment = require('moment'); const { MessageEmbed } = require('discord.js'); const request = require('node-superfetch'); -const { formatNumber, base64 } = require('../../util/Util'); +const { formatNumber, base64, embedURL } = require('../../util/Util'); const { TWITTER_KEY, TWITTER_SECRET } = process.env; const retweetRegex = /^RT @([a-zA-Z0-9_]{1,15}):/; @@ -45,7 +45,7 @@ module.exports = class TwitterCommand extends Command { if (latest) { const statusUser = body.status.retweeted_status ? body.status.text.match(retweetRegex)[1] : body.screen_name; const statusID = body.status.retweeted_status ? body.status.retweeted_status.id_str : body.status.id_str; - latest = `[${body.status.text}](https://twitter.com/${statusUser}/status/${statusID})`; + latest = embedURL(body.status.text, `https://twitter.com/${statusUser}/status/${statusID}`); } else { latest = body.protected ? '🔒 Protected' : 'No tweets found.'; } diff --git a/commands/util/changelog.js b/commands/util/changelog.js index aa56f544..6a9cc2b2 100644 --- a/commands/util/changelog.js +++ b/commands/util/changelog.js @@ -1,7 +1,7 @@ const Command = require('../../structures/Command'); const { MessageEmbed } = require('discord.js'); const request = require('node-superfetch'); -const { shorten, base64 } = require('../../util/Util'); +const { shorten, base64, embedURL } = require('../../util/Util'); const { GITHUB_USERNAME, GITHUB_PASSWORD, XIAO_GITHUB_REPO_USERNAME, XIAO_GITHUB_REPO_NAME } = process.env; module.exports = class ChangelogCommand extends Command { @@ -34,7 +34,7 @@ module.exports = class ChangelogCommand extends Command { .setColor(0x7289DA) .setURL(`https://github.com/${XIAO_GITHUB_REPO_USERNAME}/${XIAO_GITHUB_REPO_NAME}/commits/master`) .setDescription(commits.map(commit => { - const hash = `[\`${commit.sha.slice(0, 7)}\`](${commit.html_url})`; + const hash = embedURL(`\`${commit.sha.slice(0, 7)}\``, commit.html_url); return `${hash} ${shorten(commit.commit.message.split('\n')[0], 50)} - ${commit.author.login}`; }).join('\n')); return msg.embed(embed); diff --git a/commands/util/credit.js b/commands/util/credit.js index 96bf28a1..df573a02 100644 --- a/commands/util/credit.js +++ b/commands/util/credit.js @@ -1,5 +1,6 @@ const Command = require('../../structures/Command'); const { MessageEmbed } = require('discord.js'); +const { embedURL } = require('../../util/Util'); module.exports = class CreditCommand extends Command { constructor(client) { @@ -25,8 +26,8 @@ module.exports = class CreditCommand extends Command { .setTitle(command.name) .setColor(0x7289DA) .setDescription(command.credit.map(credit => { - if (!credit.reasonURL) return `[${credit.name}](${credit.url}) (${credit.reason})`; - return `[${credit.name}](${credit.url}) ([${credit.reason}](${credit.reasonURL}))`; + if (!credit.reasonURL) return `${embedURL(credit.name, credit.url)} (${credit.reason})`; + return `${embedURL(credit.name, credit.url)} (${embedURL(credit.reason, credit.reasonURL)})`; }).join('\n')); return msg.embed(embed); } diff --git a/commands/util/info.js b/commands/util/info.js index 822f77a1..7beb4a06 100644 --- a/commands/util/info.js +++ b/commands/util/info.js @@ -3,7 +3,7 @@ const { MessageEmbed, version: djsVersion } = require('discord.js'); const { version: commandoVersion } = require('discord.js-commando'); const moment = require('moment'); require('moment-duration-format'); -const { formatNumber } = require('../../util/Util'); +const { formatNumber, embedURL } = require('../../util/Util'); const { version, dependencies } = require('../../package'); const permissions = require('../../assets/json/permissions'); const { XIAO_GITHUB_REPO_USERNAME, XIAO_GITHUB_REPO_NAME } = process.env; @@ -24,16 +24,17 @@ module.exports = class InfoCommand extends Command { async run(msg) { const invite = await this.client.generateInvite(permissions); + const repoURL = `https://github.com/${XIAO_GITHUB_REPO_USERNAME}/${XIAO_GITHUB_REPO_NAME}`; const embed = new MessageEmbed() .setColor(0x00AE86) .setFooter('©2017-2020 dragonfire535#8081') .addField('❯ Servers', formatNumber(this.client.guilds.cache.size), true) .addField('❯ Commands', formatNumber(this.client.registry.commands.size), true) .addField('❯ Shards', formatNumber(this.client.options.shardCount), true) - .addField('❯ Home Server', this.client.options.invite ? `[Invite](${this.client.options.invite})` : 'None', true) - .addField('❯ Invite', `[Add Me](${invite})`, true) - .addField('❯ Source Code', - source ? `[GitHub](https://github.com/${XIAO_GITHUB_REPO_USERNAME}/${XIAO_GITHUB_REPO_NAME})` : 'N/A', true) + .addField('❯ Home Server', + this.client.options.invite ? embedURL('Invite', this.client.options.invite) : 'None', true) + .addField('❯ Invite', embedURL('Add Me', invite), true) + .addField('❯ Source Code', source ? embedURL('GitHub', repoURL) : 'N/A', true) .addField('❯ Memory Usage', `${Math.round(process.memoryUsage().heapUsed / 1024 / 1024)}MB`, true) .addField('❯ Uptime', moment.duration(this.client.uptime).format('d:hh:mm:ss'), true) .addField('❯ Version', `v${version}`, true) @@ -48,9 +49,9 @@ module.exports = class InfoCommand extends Command { return Object.entries(deps).map(dep => { if (dep[1].startsWith('github:')) { const repo = dep[1].replace('github:', '').split('/'); - return `[${dep[0]}](https://github.com/${repo[0]}/${repo[1].replace(/#(.+)/, '/tree/$1')})`; + return embedURL(dep[0], `https://github.com/${repo[0]}/${repo[1].replace(/#(.+)/, '/tree/$1')}`); } - return `[${dep[0]}](https://npmjs.com/${dep[0]})`; + return embedURL(dep[0], `https://npmjs.com/${dep[0]}`); }).join(', '); } }; diff --git a/package.json b/package.json index 4bb96910..1cfda033 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "xiao", - "version": "112.19.1", + "version": "112.19.2", "description": "Your personal server companion.", "main": "Xiao.js", "scripts": { diff --git a/structures/MemePoster.js b/structures/MemePoster.js index bb07c1d9..20752ebc 100644 --- a/structures/MemePoster.js +++ b/structures/MemePoster.js @@ -1,5 +1,6 @@ const { POSTER_ID, POSTER_TOKEN, POSTER_TIME } = process.env; const request = require('node-superfetch'); +const { embedURL } = require('../util/Util'); const subreddits = require('../assets/json/meme'); const types = ['image', 'rich:video']; @@ -17,11 +18,10 @@ module.exports = class MemePoster { const subreddit = subreddits[Math.floor(Math.random() * subreddits.length)]; const post = await this.fetchMeme(subreddit); if (!post) return; + const url = embedURL(post.title, ``); await request .post(`https://discordapp.com/api/webhooks/${this.id}/${this.token}`) - .send({ - content: `**r/${subreddit}** [${post.title}]()\n${post.url}` - }); + .send({ content: `**r/${subreddit}** ${url}\n${post.url}` }); } catch (err) { this.client.logger.error(err); } diff --git a/util/Util.js b/util/Util.js index 2e83d6ae..1081bf79 100644 --- a/util/Util.js +++ b/util/Util.js @@ -120,6 +120,10 @@ module.exports = class Util { return today; } + static embedURL(title, url, display = url) { + return `[${title}](${url.replace(/\)/g, '%27')}, "${display}")`; + } + static async verify(channel, user, time = 30000) { const filter = res => { const value = res.content.toLowerCase();