Skip to content

Instantly share code, notes, and snippets.

@karlwilcox
Created March 18, 2021 10:56
Show Gist options
  • Save karlwilcox/f7c08f30b83000cc49f41358c47810f6 to your computer and use it in GitHub Desktop.
Save karlwilcox/f7c08f30b83000cc49f41358c47810f6 to your computer and use it in GitHub Desktop.
Drawshield Discord Bot
const Discord = require('discord.js');
const fetch = require('node-fetch');
const querystring = require('querystring');
const crypto = require('crypto');
const fs = require('fs');
const { exec } = require('child_process');
const limit = 500;
const client = new Discord.Client();
const prefix = '!';
const helpInfo = {
define: "*!define <term>*\nProvide a brief definition of a heraldic term (Searches Parker's heraldic dictionary, and Elvins' Heraldic dictionary) and provides a link to the full entry.",
challenge: `*!challenge*\n
Shows you a randomly selected image of a coat of arms from somewhere on the internet. Can you reproduce it using DrawShield? Sometimes you will have to guess charges and colours, and sometimes the referenced resource will have disappeared, if you don't see anything just ask for another challenge\n.
You can also provide the name of source for the challenges, the currently supported sources are:\n
coadb - coadb.com catalog of European surnames (32,000 entries, monochrome)\n
public - The Book of Public Arms (1,200 entries, monochrome)\n
wikimedia - Wikimedia commons (243, genuine DrawShield, colour)`,
catalog: "*!catalog <item>*\nSearch the visual catalog for <item>. If there is an exact match then the relevant image from the catalog is returned. You can also do a wildcard search by putting an asterisk at the end, in which case the names of up to 10 charge names that contain the string <item> will be listed.",
gallery: `*!gallery <number> | <search-term>*
Display image of gallery entry <number>, including comments, or
Display list of 10 matching gallery numbers that include the search term
With no arguments, Display a random gallery entry`,
link: "*!link*\nReturns an invite link to add the bot to another server.",
draw: "*!draw <blazon>*\nInvoke drawshield to draw the specified blazon. *!draw random* to get a random shield.",
test: "*!test <blazon>*\nInvoke drawshield test site to draw the specified blazon.",
help: "*!help <command>*\nShow this information"
};
const trim = (str, max) => (str.length > max ? `${str.slice(0, max - 3)}...` : str);
client.once('ready', () => {
console.log('Ready!');
});
client.on('message', async message => {
if (!message.content.startsWith(prefix) || message.author.bot) return;
const args = message.content.slice(prefix.length).trim().split(/ +/);
const command = args.shift().toLowerCase();
if (command === 'define') {
if (!args.length) {
return message.channel.send('You need to supply a search term!');
}
const term = args.join(' ');
const query = querystring.escape(term);
// The default search does a fuzzy match, which is sometimes brilliant at resolving spelling
// errors and sometimes returns nonsense. If you don't want this behaviour add:
// &match=exact
// to the query string. For full details of the API see
// https://github.com/drawshield/drawshield-website/wiki/DrawShield-API
const definition = await fetch(`https://drawshield.net/api/define/${query}`).then(response => response.json());
if (definition.hasOwnProperty('error')) {
return message.channel.send(`Sorry, no results found for **${term}**.`);
}
message.channel.send(trim(definition.content, 1024) + " See more: " + definition.URL);
} else if (command === 'draw' || command === 'test') {
if (!args.length) {
return message.channel.send('You need to supply a blazon!');
}
var host = 'https://drawshield.net';
if (command === 'test') {
host = 'http://test.drawshield.net';
}
var term = args.join(' ');
if (term.startsWith('-')) {
term = term.slice(1);
}
if (term === 'random') {
const random = await fetch('https://drawshield.net/include/randomblazon.php').then(response => response.text());
term = random;
message.channel.send(term);
}
exec("ls -1tr /var/www/html/cache", (error, stdout, stderr) => {
var i = 0;
if (!error && !stderr) {
const fileList = stdout.split("\n");
var listSize = fileList.length;
while (listSize-- > limit) {
fs.unlink("/var/www/html/cache/" + fileList[i++], (err => {
if (err) console.log(err);
}));
}
}
});
const blazon = querystring.escape(term);
const fileId = 'cache/' + crypto.randomBytes(16).toString('hex') + '.png';
fetch(`${host}/include/drawshield.php?blazon=${blazon}&outputformat=png&dummy=shield.png`)
.then(res => {
const dest = fs.createWriteStream('/var/www/html/' + fileId);
res.body.pipe(dest);
return message.channel.send(`https://drawshield.net/${fileId}`);
});
} else if (command === 'challenge') {
var source = 'all';
if (args.length) {
source = args[0];
}
const image = await fetch(`https://drawshield.net/api/challenge/${source}`).then(response => response.json());
if (image.hasOwnProperty('error')) {
return message.channel.send(`Sorry, no challenge found.`);
}
message.channel.send("Can you recreate, as closely as possible, the following image\n" + image);
} else if (command === 'gallery') {
var term = args.join(' ');
const query = querystring.escape(term);
const entry = await fetch(`https://drawshield.net/api/gallery/${query}`).then(response => response.json());
return message.channel.send(entry);
} else if (command === 'catalog') {
if (!args.length) {
return message.channel.send('You need to supply a search term!');
}
var term = args.join(' ');
const query = querystring.escape(term);
const catalogResponse = await fetch(`https://drawshield.net/api/catalog/${query}`).then(response => response.json());
return message.channel.send(catalogResponse);
} else if (command === 'link') {
return message.channel.send('https://discord.com/oauth2/authorize?client_id=775740113804197888&scope=bot');
} else if (command === 'help') {
helpText = "Commands are define, gallery, challenge, catalog, link, draw. Use !help <command> for details.";
helpItem = args.join(' ');
if (helpItem.startsWith('def')) {
helpText = helpInfo.define;
} else if (helpItem.startsWith('cha')) {
helpText = helpInfo.challenge;
} else if (helpItem.startsWith('lin')) {
helpText = helpInfo.link;
} else if (helpItem.startsWith('cat')) {
helpText = helpInfo.catalog;
} else if (helpItem.startsWith('gal')) {
helpText = helpInfo.gallery;
} else if (helpItem.startsWith('dra')) {
helpText = helpInfo.draw;
} else if (helpItem.length) { // don't respond to unknown item (might be other bots)
helpText = null;
}
if (helpText !== null) {
return message.channel.send(helpText);
}
}
});
client.login('<YOUR_TOKEN_HERE>');
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment