diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..4c22a05 --- /dev/null +++ b/.env.example @@ -0,0 +1,18 @@ +# Put MongoDB database URL here +MONGO= + +# Put Discord token here +TOKEN= +# Put Cat API token here +CAT= +# Put Mashape/RapidAPI key here +MASHAPE= +# Put Google API key here +GOOGLE= +# Put Google Custom Search ID here +CSE= +# Put DBL/top.gg token here +DBL= + +# Put snowflake ID of bot owner here +OWNER= \ No newline at end of file diff --git a/.gitignore b/.gitignore index ea6f188..4f61abb 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,7 @@ data/ appold.js migrate.js config.json +.env logs *.log npm-debug.log* diff --git a/app.js b/app.js index eccf9e9..b74fec4 100644 --- a/app.js +++ b/app.js @@ -1,6 +1,9 @@ // check if using node 10 or higher if (process.version.slice(1).split(".")[0] < 10) throw new Error("Node 10.0.0 or higher is required. Update Node on your system."); +// load config from .env file +require("dotenv").config(); + // turn fs.readdir into a promise const { promisify } = require("util"); const fs = require("fs"); diff --git a/assets/images/leak.png b/assets/images/leak.png new file mode 100644 index 0000000..d1f8b91 Binary files /dev/null and b/assets/images/leak.png differ diff --git a/commands/cat.js b/commands/cat.js index 594db3a..b3489dc 100644 --- a/commands/cat.js +++ b/commands/cat.js @@ -1,11 +1,10 @@ const fetch = require("node-fetch"); -const config = require("../config.json"); exports.run = async (message) => { message.channel.sendTyping(); const data = await fetch("https://api.thecatapi.com/v1/images/search?format=json", { headers: { - "x-api-key": config.catToken + "x-api-key": process.env.CAT } }); const json = await data.json(); diff --git a/commands/exec.js b/commands/exec.js new file mode 100644 index 0000000..d380799 --- /dev/null +++ b/commands/exec.js @@ -0,0 +1,18 @@ +const { clean } = require("../utils/misc.js"); +const util = require("util"); +const exec = util.promisify(require("child_process").exec); + +exports.run = async (message, args) => { + if (message.author.id !== "198198681982205953") return `${message.author.mention}, only the bot owner can use exec!`; + const code = args.join(" "); + try { + const execed = await exec(code); + if (execed.stderr) return `\`ERROR\` \`\`\`xl\n${await clean(execed.stderr)}\n\`\`\``; + const cleaned = await clean(execed.stdout); + return `\`\`\`bash\n${cleaned}\n\`\`\``; + } catch (err) { + return `\`ERROR\` \`\`\`xl\n${await clean(err)}\n\`\`\``; + } +}; + +exports.aliases = ["runcmd"]; diff --git a/commands/image.js b/commands/image.js index 2fe2697..4437e86 100644 --- a/commands/image.js +++ b/commands/image.js @@ -1,7 +1,6 @@ const { google } = require("googleapis"); const client = require("../utils/client.js"); const paginator = require("../utils/pagination/pagination"); -const config = require("../config.json"); const search = google.customsearch("v1"); exports.run = async (message, args) => { @@ -9,7 +8,7 @@ exports.run = async (message, args) => { if (!message.channel.guild.members.get(client.user.id).permission.has("embedLinks") && !message.channel.permissionsOf(client.user.id).has("embedLinks")) return `${message.author.mention}, I don't have the \`Embed Links\` permission!`; if (args.length === 0) return `${message.author.mention}, you need to provide something to search for!`; const embeds = []; - const images = await search.cse.list({ searchType: "image", safe: "active", cx: config.cseID, q: args.join(" "), auth: config.googleKey }); + const images = await search.cse.list({ searchType: "image", safe: "active", cx: process.env.CSE, q: args.join(" "), auth: process.env.GOOGLE }); for (const [i, value] of images.data.items.entries()) { embeds.push({ "embed": { diff --git a/commands/info.js b/commands/info.js index 83f05fa..516af83 100644 --- a/commands/info.js +++ b/commands/info.js @@ -1,8 +1,7 @@ const client = require("../utils/client.js"); -const config = require("../config.json"); exports.run = async (message) => { - const dev = client.users.get(config.botOwner); + const dev = client.users.get(process.env.OWNER); const artist = client.users.get("401980971517214723"); const infoEmbed = { "embed": { diff --git a/commands/leak.js b/commands/leak.js new file mode 100644 index 0000000..b078c9e --- /dev/null +++ b/commands/leak.js @@ -0,0 +1,22 @@ +const gm = require("gm").subClass({ + imageMagick: true +}); +const gmToBuffer = require("../utils/gmbuffer.js"); +const fs = require("fs"); + +exports.run = async (message) => { + const image = await require("../utils/imagedetect.js")(message); + if (image === undefined) return `${message.author.mention}, you need to provide an image to make a Super Smash Bros. leak meme!`; + message.channel.sendTyping(); + const template = "./assets/images/leak.png"; + const path = `/tmp/${Math.random().toString(36).substring(2, 15)}.${image.type}`; + require("util").promisify(fs.writeFile)(path, image.data); + const command = gm(template).out("-background").out("white").out("-gravity").out("Center").out("(").out("-clone").out("0").out("(").out(path).out("-virtual-pixel").out("white").out("-resize").out("640x360!").rotate("white", 15).out(")").out("-geometry").out("+450-200").out("-composite").out(")").out("+swap").out("-composite").out("-alpha").out("remove").out("-alpha").out("off"); + const resultBuffer = await gmToBuffer(command, "png"); + return message.channel.createMessage("", { + file: resultBuffer, + name: "leak.png" + }); +}; + +exports.aliases = ["smash", "laxchris", "ssbu", "smashleak"]; \ No newline at end of file diff --git a/commands/reload.js b/commands/reload.js index 92997c8..af16070 100644 --- a/commands/reload.js +++ b/commands/reload.js @@ -1,7 +1,7 @@ const handler = require("../utils/handler.js"); exports.run = async (message, args) => { - if (message.author.id !== require("../config.json").botOwner) return `${message.author.mention}, only the bot owner can reload commands!`; + if (message.author.id !== process.env.OWNER) return `${message.author.mention}, only the bot owner can reload commands!`; if (args.length === 0) return `${message.author.mention}, you need to provide a command to reload!`; try { await handler.unload(args[0]); diff --git a/commands/restart.js b/commands/restart.js index 3a7c090..f94a8e7 100644 --- a/commands/restart.js +++ b/commands/restart.js @@ -2,7 +2,7 @@ const handler = require("../utils/handler.js"); const collections = require("../utils/collections.js"); exports.run = async (message) => { - if (message.author.id !== require("../config.json").botOwner) return `${message.author.mention}, only the bot owner can restart me!`; + if (message.author.id !== process.env.OWNER) return `${message.author.mention}, only the bot owner can restart me!`; await message.channel.createMessage(`${message.author.mention}, esmBot is restarting.`); collections.commands.forEach(async (command) => { await handler.unload(command); diff --git a/commands/tags.js b/commands/tags.js index 8ef9c9e..cfb127f 100644 --- a/commands/tags.js +++ b/commands/tags.js @@ -1,5 +1,4 @@ const database = require("../utils/database.js"); -const config = require("../config.json"); const client = require("../utils/client.js"); const paginator = require("../utils/pagination/pagination.js"); const { random } = require("../utils/misc.js"); @@ -19,14 +18,14 @@ exports.run = async (message, args) => { case "remove": if (args[1] === undefined) return `${message.author.mention}, you need to provide the name of the tag you want to delete!`; if (!tags.has(args[1].toLowerCase())) return `${message.author.mention}, this tag doesn't exist!`; - if (tags.get(args[1].toLowerCase()).author !== message.author.id && tags.get(args[1].toLowerCase()).author !== config.botOwner) return `${message.author.mention}, you don't own this tag!`; + if (tags.get(args[1].toLowerCase()).author !== message.author.id && tags.get(args[1].toLowerCase()).author !== process.env.OWNER) return `${message.author.mention}, you don't own this tag!`; tags.set(args[1].toLowerCase(), undefined); await guild.save(); return `${message.author.mention}, the tag \`${args[1].toLowerCase()}\` has been deleted!`; case "edit": if (args[1] === undefined) return `${message.author.mention}, you need to provide the name of the tag you want to edit!`; if (!tags.has(args[1].toLowerCase())) return `${message.author.mention}, this tag doesn't exist!`; - if (tags.get(args[1].toLowerCase()).author !== message.author.id && tags.get(args[1].toLowerCase()).author !== config.botOwner) return `${message.author.mention}, you don't own this tag!`; + if (tags.get(args[1].toLowerCase()).author !== message.author.id && tags.get(args[1].toLowerCase()).author !== process.env.OWNER) return `${message.author.mention}, you don't own this tag!`; await setTag(args.slice(2).join(" "), args[1].toLowerCase(), message, guild); return `${message.author.mention}, the tag \`${args[1].toLowerCase()}\` has been edited!`; case "list": diff --git a/commands/wikihow.js b/commands/wikihow.js index 2c85cc7..7812b5b 100644 --- a/commands/wikihow.js +++ b/commands/wikihow.js @@ -1,11 +1,10 @@ const fetch = require("node-fetch"); -const config = require("../config.json"); exports.run = async (message) => { message.channel.sendTyping(); const request = await fetch("https://hargrimm-wikihow-v1.p.mashape.com/images?count=1", { headers: { - "X-Mashape-Key": config.mashapeKey, + "X-Mashape-Key": process.env.MASHAPE, "Accept": "application/json" } }); diff --git a/commands/youtube.js b/commands/youtube.js index 87cc506..bd4e1ee 100644 --- a/commands/youtube.js +++ b/commands/youtube.js @@ -1,8 +1,7 @@ const { google } = require("googleapis"); -const config = require("../config.json"); const youtube = google.youtube({ version: "v3", - auth: config.googleKey, + auth: process.env.GOOGLE, }); exports.run = async (message, args) => { diff --git a/config-example.json b/config-example.json deleted file mode 100644 index f333d5d..0000000 --- a/config-example.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "token": "", - "mashapeKey": "", - "catToken": "", - "googleKey": "", - "cseID": "", - "dblToken": "", - "botOwner": "", - "mongoURL": "", - "activityMessages": [ - "", - "" - ] -} diff --git a/events/ready.js b/events/ready.js index 1a17b6c..4087d87 100644 --- a/events/ready.js +++ b/events/ready.js @@ -1,7 +1,7 @@ const client = require("../utils/client.js"); const database = require("../utils/database.js"); const logger = require("../utils/logger.js"); -const config = require("../config.json"); +const messages = require("../messages.json"); const misc = require("../utils/misc.js"); // run when ready @@ -43,7 +43,7 @@ module.exports = async () => { // set activity (a.k.a. the gamer code) (async function activityChanger() { - client.editStatus("dnd", { name: `${misc.random(config.activityMessages)} | @esmBot help`, url: "https://essem.space/esmBot/commands.html?dev=true" }); + client.editStatus("dnd", { name: `${misc.random(messages)} | @esmBot help`, url: "https://essem.space/esmBot/commands.html?dev=true" }); setTimeout(activityChanger, 900000); })(); diff --git a/messages.json b/messages.json new file mode 100644 index 0000000..2ee8db0 --- /dev/null +++ b/messages.json @@ -0,0 +1,91 @@ +[ + "with your sanity", + "h", + "Club Penguin", + "Skype", + "with yo mama", + "with a bootleg plug-and-play", + "FL Studio: SoundCloud Rapper Edition", + "Funny Fortain", + "Fake Download Button Simulator", + "quick i need playing lines for my discord bot", + "a game", + "anime", + "absolutely nothing", + "Mozilla Firefox", + "Google Chrome", + "music bot soon I guess", + "Fortnut", + "epic mashups bro", + "Netscape Navigator", + "Ubuntu", + "Linux", + "Hello Kitty Island Adventure", + "with the Infinity Gauntlet", + "BLJ Simulator", + "Jake Paul videos on repeat", + "gniyalP", + "HaaH WaaW", + "Shrek Extra Large", + "dQw4w9WgXcQ", + "Hong Kong 97", + "Twitter", + "Reddit", + "Super Smash Bros. Ultimate", + "Yuzu", + "Battletoads for Wii", + "MS Paint", + "Roblox", + "Minecraft", + "Desert Bus", + "Mega Man Legends 3", + "Sonic's Schoolhouse", + "Action 52", + "the funny memes epic", + "Nintendoâ„¢", + "Wario World", + "the Cat Piano", + "Pac-Man Championship Edition DX+", + "Pac-Man Championship Edition 2", + "Bottom Text", + "The Elder Scrolls 6", + "Skyrim", + "Game Boy Advance Video", + "Nintendo Switch Online", + "lol 7", + "Meme Run", + "Yoshi for the NES", + "Family Guy", + "Deltarune", + "subscribe to obama", + "notsobot sucks", + "yeah", + "semicolon", + "Super Mario Maker 2", + "ezio is dumb", + "Rofa Cat", + "jeff", + "TikTok", + "woo yeah", + "Mario", + "with Edgar", + "Microsoft Internet Explorer", + "joe mama", + "Sonic 06", + "Wii Speak Channel", + "Metal Gear Solid 4", + "iPod Music", + "Liquid Sun", + "with your Discord server", + "Scatman's World", + "with a stone, Luigi.", + "Doin' Your Mom by FAttY Spins", + "with a broken god", + "games with the mortals", + "#BringBackNationalSex", + "chiptune", + "foobar2000", + "XMPlay", + "OpenMPT", + "follow @esmBot_ on Twitter" +] \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 1ae2483..86a441b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -626,6 +626,11 @@ "domelementtype": "1" } }, + "dotenv": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-8.2.0.tgz", + "integrity": "sha512-8sJ78ElpbDJBHNeBzUbUVLsqKdccaa/BXF1uPTw3GrvQTBgrQrtObr2mUrE38vzYd8cEv+m/JBfDLioYcfXoaw==" + }, "ecdsa-sig-formatter": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", diff --git a/package.json b/package.json index 366aa35..18877e2 100644 --- a/package.json +++ b/package.json @@ -19,6 +19,7 @@ "bufferutil": "^4.0.1", "cowsay": "^1.4.0", "dblapi.js": "^2.3.0", + "dotenv": "^8.2.0", "eris": "github:abalabahaha/eris#dev", "erlpack": "github:discordapp/erlpack", "eventemitter3": "^3.1.2", diff --git a/utils/client.js b/utils/client.js index 07446b9..6dd6237 100644 --- a/utils/client.js +++ b/utils/client.js @@ -1,7 +1,6 @@ // separate the client from app.js so we can call it later const { Client } = require("eris"); -const config = require("../config.json"); -const client = new Client(config.token, { +const client = new Client(process.env.TOKEN, { defaultImageSize: 1024 }); module.exports = client; diff --git a/utils/database.js b/utils/database.js index 905b8af..7700ea0 100644 --- a/utils/database.js +++ b/utils/database.js @@ -1,21 +1,19 @@ // database stuff const mongoose = require("mongoose"); -const config = require("../config.json"); -mongoose.connect(config.mongoURL); +mongoose.connect(process.env.MONGO); const guildSchema = new mongoose.Schema({ id: String, tags: Map, prefix: String }); const Guild = mongoose.model("Guild", guildSchema); -/*const membersSchema = new mongoose.Schema({ -});*/ const xpSchema = new mongoose.Schema({ id: String, members: Map, enabled: Boolean }); const XP = mongoose.model("XP", xpSchema); + exports.guilds = Guild; exports.xp = XP; \ No newline at end of file diff --git a/utils/dbl.js b/utils/dbl.js index 6b064d0..f19a07a 100644 --- a/utils/dbl.js +++ b/utils/dbl.js @@ -1,9 +1,8 @@ // dbl api client const DBL = require("dblapi.js"); const logger = require("./logger.js"); -const config = require("../config.json"); const client = require("./client.js"); -const dbl = new DBL(config.dblToken, client); +const dbl = new DBL(process.env.DBL, client); dbl.on("error", e => { logger.error(e); }); diff --git a/utils/gmbuffer.js b/utils/gmbuffer.js index 1511665..be17a24 100644 --- a/utils/gmbuffer.js +++ b/utils/gmbuffer.js @@ -1,15 +1,39 @@ // workaround for a gm bug where it doesn't output buffers properly // https://github.com/aheckmann/gm/issues/572#issuecomment-293768810 -module.exports = (data) => { +module.exports = (data, format) => { return new Promise((resolve, reject) => { - data.stream((err, stdout, stderr) => { - if (err) return reject(err); - const chunks = []; - stdout.on("data", (chunk) => { chunks.push(chunk); }); - // these are 'once' because they can and do fire multiple times for multiple errors, - // but this is a promise so you'll have to deal with them one at a time - stdout.once("end", () => { resolve(Buffer.concat(chunks)); }); - stderr.once("data", (data) => { reject(String(data)); }); - }); + if (format) { + data.stream(format, (err, stdout, stderr) => { + if (err) return reject(err); + const chunks = []; + stdout.on("data", (chunk) => { + chunks.push(chunk); + }); + // these are 'once' because they can and do fire multiple times for multiple errors, + // but this is a promise so you'll have to deal with them one at a time + stdout.once("end", () => { + resolve(Buffer.concat(chunks)); + }); + stderr.once("data", (data) => { + reject(String(data)); + }); + }); + } else { + data.stream((err, stdout, stderr) => { + if (err) return reject(err); + const chunks = []; + stdout.on("data", (chunk) => { + chunks.push(chunk); + }); + // these are 'once' because they can and do fire multiple times for multiple errors, + // but this is a promise so you'll have to deal with them one at a time + stdout.once("end", () => { + resolve(Buffer.concat(chunks)); + }); + stderr.once("data", (data) => { + reject(String(data)); + }); + }); + } }); }; diff --git a/utils/misc.js b/utils/misc.js index 97feb92..69dc699 100644 --- a/utils/misc.js +++ b/utils/misc.js @@ -5,22 +5,21 @@ exports.random = (array) => { // clean(text) to clean message of any private info or mentions exports.clean = async (text) => { - const config = require("../config.json"); if (text && text.constructor.name == "Promise") text = await text; - if (typeof evaled !== "string") + if (typeof text !== "string") text = require("util").inspect(text, { depth: 1 }); text = text .replace(/`/g, `\`${String.fromCharCode(8203)}`) .replace(/@/g, `@${String.fromCharCode(8203)}`) - .replace(config.token, "") - .replace(config.mashapeKey, "") - .replace(config.catToken, "") - .replace(config.googleKey, "") - .replace(config.cseID, "") - .replace(config.dblToken, "") - .replace(config.mongoURL, ""); + .replace(process.env.TOKEN, "") + .replace(process.env.MASHAPE, "") + .replace(process.env.CAT, "") + .replace(process.env.GOOGLE, "") + .replace(process.env.CSE, "") + .replace(process.env.DBL, "") + .replace(process.env.MONGO, ""); return text; };