diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..7182f91 --- /dev/null +++ b/.gitignore @@ -0,0 +1,119 @@ +config.json +package-lock.json + +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +lerna-debug.log* + +# Diagnostic reports (https://nodejs.org/api/report.html) +report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage +*.lcov + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# Bower dependency directory (https://bower.io/) +bower_components + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (https://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules/ +jspm_packages/ + +# Snowpack dependency directory (https://snowpack.dev/) +web_modules/ + +# TypeScript cache +*.tsbuildinfo + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Microbundle cache +.rpt2_cache/ +.rts2_cache_cjs/ +.rts2_cache_es/ +.rts2_cache_umd/ + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variables file +.env +.env.test + +# parcel-bundler cache (https://parceljs.org/) +.cache +.parcel-cache + +# Next.js build output +.next +out + +# Nuxt.js build / generate output +.nuxt +dist + +# Gatsby files +.cache/ +# Comment in the public line in if your project uses Gatsby and not Next.js +# https://nextjs.org/blog/next-9-1#public-directory-support +# public + +# vuepress build output +.vuepress/dist + +# Serverless directories +.serverless/ + +# FuseBox cache +.fusebox/ + +# DynamoDB Local files +.dynamodb/ + +# TernJS port file +.tern-port + +# Stores VSCode versions used for testing VSCode extensions +.vscode-test + +# yarn v2 +.yarn/cache +.yarn/unplugged +.yarn/build-state.yml +.yarn/install-state.gz +.pnp.* diff --git a/Main.js b/Main.js new file mode 100644 index 0000000..f715dbf --- /dev/null +++ b/Main.js @@ -0,0 +1,75 @@ +'use strict'; + +const Commando = require('@iceprod/discord.js-commando'); +const client = new Commando.Client({ + owner: '750335181285490760', + partials: ["CHANNEL", "GUILD_MEMBER", "MESSAGE", "REACTION", "USER"] +}); +const sqlite = require('sqlite'); +let configFile = require('./config.json'); +const { errLog, trySend } = require('./resources/functions'); +const { join } = require('path'); +const { chatAnswer } = require("./resources/shaChat"); +require("./database/mongo"); + +client.registry +.registerGroups([ + 'utility', + 'moderation', + 'experiment', + 'image', + 'fun' +]) +.registerDefaults() +.registerCommandsIn(join(__dirname, 'cmds')); + +client.setProvider( + sqlite.open({ + filename:join(__dirname, 'settings.sqlite3'), + driver:require("sqlite3").Database + }).then(db => new Commando.SQLiteProvider(db)) +).catch(e => errLog(e)); + +const guildLog = "840154722434154496"; + +let shaGuild; + +client.registry.findCommands + +client.on('ready', async () => { + //shaGuild = client.guilds.cache.map(g => g); + //console.log(`Member in ${shaGuild.length} guilds.`); + //console.log(client.user.tag+' logged in!'); +}); + +client.on("message", async msg => { + if (msg.channel.id === "837178237322919966" && !msg.author.bot && !msg.content.toLowerCase().startsWith(client.commandPrefix+"chat")) { + chatAnswer(client, msg); + } + + if (!msg.guild) { + //console.log(`(${msg.channel.recipient.id}) ${msg.channel.recipient.tag}: (${msg.author.id}) ${msg.author.tag}: ${msg.content}`); + } +}); + +client.on("guildMemberRemove", memberLeave => { + //console.log(`User ${memberLeave.displayName} (${memberLeave.user.tag}) (${memberLeave.id}) left ${memberLeave.guild.name} (${memberLeave.guild.id}). Now it has ${memberLeave.guild.memberCount} total members count.`); +}); + +client.on("guildCreate", newShaGuild => { + shaGuild = client.guilds.cache.map(g => g); + trySend(client, guildLog, `Joined **${newShaGuild.name}** (${newShaGuild.id}) <:awamazedLife:795227334339985418> I'm in ${shaGuild.length} servers now.`); +}); + +client.on("guildDelete", leaveShaGuild => { + shaGuild = client.guilds.cache.map(g => g); + trySend(client, guildLog, `Left **${leaveShaGuild.name}** (${leaveShaGuild.id}) <:WhenLife:773061840351657984> I'm in ${shaGuild.length} servers now.`); +}); + +client.on("guildMemberAdd", newMember => { + //console.log(`New member ${newMember.displayName} (${newMember.user.tag}) (${newMember.id}) joined ${newMember.guild.name} (${newMember.guild.id})! Now it has ${newMember.guild.memberCount} total members count.`); +}); + +// client.on("debug", (...args) => console.log(...args)); + +client.login(configFile.token); \ No newline at end of file diff --git a/cmds/experiment/mydatabase.js b/cmds/experiment/mydatabase.js new file mode 100644 index 0000000..f87e599 --- /dev/null +++ b/cmds/experiment/mydatabase.js @@ -0,0 +1,29 @@ +'use strict'; + +const commando = require("@iceprod/discord.js-commando"); +const { errLog, trySend, ranLog } = require("../../resources/functions"); +const { database } = require("../../database/mongo"); + +module.exports = class mydatabase extends commando.Command { + constructor(client) { + super(client, { + name: "mydatabase", + memberName: "mydatabase", + group: "experiment", + description: "Show all document collection." + }); + } + run(msg) { + const data = msg.guild ? "Guild" : "User"; + const doc = msg.guild?.id ?? msg.author.id; + database.collection(data).find({document: doc}).toArray(async (e, fetched) => { + if (e) { + return errLog(e, msg, this.client); + } + let mes = `Fetched documents for ${msg.guild ? `server **${msg.guild.name}**` : `**${msg.author.tag}**`}`; + mes = `${mes}\`\`\`js\n${JSON.stringify(fetched).split(',"').join(',\n"').split(',{').join(',\n{').split("{\"").join("{\n\"").split("\"}").join("\"\n}")}\`\`\``; + trySend(this.client, msg, mes); + return ranLog(msg, "mydatabase", fetched); + }); + } +}; \ No newline at end of file diff --git a/cmds/experiment/resetdatabase.js b/cmds/experiment/resetdatabase.js new file mode 100644 index 0000000..d3df9fa --- /dev/null +++ b/cmds/experiment/resetdatabase.js @@ -0,0 +1,36 @@ +'use strict'; + +const commando = require("@iceprod/discord.js-commando"); +const { ranLog, trySend, errLog } = require("../../resources/functions"); +const { database } = require("../../database/mongo"); + +module.exports = class resetdatabase extends commando.Command { + constructor(client) { + super(client, { + name: "resetdatabase", + memberName: "resetdatabase", + group: "experiment", + description: "Reset your server/private database." + }); + } + async run(msg) { + const doc = msg.guild?.id ?? msg.author.id; + const col = database.collection(msg.guild ? "Guild" : "User"); + trySend(this.client, msg, "Are you sure? You will lose every saved settings. This process can't be undone. Type `yes` in 30 seconds to confirm."); + const confirm = msg.channel.createMessageCollector(() => true, {time:30000}); + confirm.on("collect", h => { + if (h.author === msg.author) { + if (h.content.trim() === "yes") { + col.findOneAndDelete({document: doc}) + .then( + trySend(this.client, msg, "Wiped!")) + .catch(e => errLog(e, msg, this.client)); + } else { + trySend(this.client, msg, "Request aborted."); + } + confirm.stop(); + return ranLog(msg, "resetdatabase", h.content); + } + }); + } +}; \ No newline at end of file diff --git a/cmds/fun/chat.js b/cmds/fun/chat.js new file mode 100644 index 0000000..15c8a4b --- /dev/null +++ b/cmds/fun/chat.js @@ -0,0 +1,18 @@ +'use strict'; + +const commando = require("@iceprod/discord.js-commando"); +const { chatAnswer } = require("../../resources/shaChat"); + +module.exports = class chat extends commando.Command { + constructor(client) { + super(client, { + name: "chat", + memberName: "chat", + group: "fun", + description: "Lets chat!" + }); + } + async run(msg) { + chatAnswer(this.client, msg); + } +}; \ No newline at end of file diff --git a/cmds/image/neko.js b/cmds/image/neko.js new file mode 100644 index 0000000..578cb76 --- /dev/null +++ b/cmds/image/neko.js @@ -0,0 +1,23 @@ +'use strict'; + +const commando = require("@iceprod/discord.js-commando"); +const { trySend, ranLog, defaultImageEmbed } = require("../../resources/functions"); + +module.exports = class neko extends commando.Command { + constructor(client) { + super(client, { + name: "neko", + memberName: "neko", + group: "image", + description: "Neko." + }); + } + async run(msg) { + const aut = msg.guild ? msg.guild.member(msg.author) : msg.author; + const title = `${msg.guild ? aut.displayName : aut.username}! ~Nyann~ (UwU) <3`; + const image = `https://nekos.best/nekos/${String(Math.floor(Math.random() * 314)).padStart(4, '0')}.png`; + const emb = await defaultImageEmbed(this.client, msg, image, aut, title); + trySend(this.client, msg, emb); + return ranLog(msg, "neko"); + } +}; \ No newline at end of file diff --git a/cmds/moderation/mute.js b/cmds/moderation/mute.js new file mode 100644 index 0000000..901c92f --- /dev/null +++ b/cmds/moderation/mute.js @@ -0,0 +1,97 @@ +'use strict'; + +const commando = require("@iceprod/discord.js-commando"); +const { getUser, trySend } = require("../../resources/functions"); +const { database } = require("../../database/mongo"); +const { muteDurationMultiplier } = require("../../resources/date"); +const col = database.collection("Guild"); +const dbExp = database.collection("Experiment"); + +module.exports = class mute extends commando.Command { + constructor(client) { + super(client, { + name: "mute", + memberName: "mute", + group: "moderation", + description: "Mute.", + guildOnly: true, + userPermissions:['MANAGE_ROLES'] + }); + } + /** + * + * @param {commando.CommandoMessage} msg + * @param {*} arg + * @returns + */ + async run(msg, arg) { + const doc = await col.findOne({document: msg.guild.id}); + const muteConf = doc?.["moderation"]?.mute; + const args = arg.trim().split(/ +/); + const setArgs = arg.trim().split(/(\-\-)+/); + /* if (config.mute.role.length === 0) { + return msg.channel.send(`Mute role isn't set! Run \`${this.client.commandPrefix}mute --role \`. If you insist i will just give them admin perms <:purifyLife:774102054046007298>`) + } + if (setArgs) { + for(let set of setArgs) { + set = set.toLowerCase(); + switch(set) { + case startsWith('role'): { + let role = set.slice('role'.length).trim(); + if (role.startsWith('<&')) { + role = role.slice(2,-1); + } + //const foundRole = + } + } + } + }*/ + if (args[0]) { + const targetUser = await getUser(this.client, msg, args[0]); + if (targetUser) { + let duration = muteConf?.defaultDuration; + if (/^\d+(?![^ymwdhs])[ymwdhs]?o?/i.test(args[1])) { + duration = 0; + const durationRegExp = /\d+(?![^ymwdhs])[ymwdhs]?o?/gi; + const durationArg = args[1].match(durationRegExp); + for (const value of durationArg) { + console.log(value); + if (value.endsWith("h") || value.endsWith("ho")) { + duration = muteDurationMultiplier(duration, value, 60 * 60 * 1000); + } + if (value.endsWith("y")) { + duration = muteDurationMultiplier(duration, value, 365 * 24 * 60 * 60 * 1000); + } + if (value.endsWith("mo")) { + duration = muteDurationMultiplier(duration, value, 30 * 24 * 60 * 60 * 1000) + } + if (value.endsWith("w")) { + duration = muteDurationMultiplier(duration, value, 7 * 24 * 60 * 60 * 1000) + } + if (value.endsWith("d")) { + duration = muteDurationMultiplier(duration, value, 24 * 60 * 60 * 1000) + } + if (value.endsWith("m")) { + duration = muteDurationMultiplier(duration, value, 60 * 1000) + } + if (value.endsWith("s")) { + duration = muteDurationMultiplier(duration, value, 1000) + } + if (!/\D/.test(value)) { + duration = muteDurationMultiplier(duration, value, 1000) + } + } + const dateDur = new Date(msg.createdAt.valueOf() + duration).toUTCString(); + /*const yearDate = dateDur.getFullYear(); + const monthDate = dateDur.getMonth(); + const dayDate = dateDur.getDay(); + const hourDate = dateDur.getHours(); + const minuteDate = dateDur.getMinutes(); + const secondDate = dateDur.getSeconds();*/ + return trySend(this.client, msg, `Result:\`\`\`js\n${duration} ms\n${dateDur}\`\`\``); + } + } else + return trySend(this.client, msg, "No user with that ID."); + } + } +}; \ No newline at end of file diff --git a/cmds/utility/avatar.js b/cmds/utility/avatar.js new file mode 100644 index 0000000..d25215e --- /dev/null +++ b/cmds/utility/avatar.js @@ -0,0 +1,201 @@ +'use strict'; + +const commando = require("@iceprod/discord.js-commando"); +const { MessageEmbed } = require("discord.js"); +const { ranLog, errLog, trySend, findMemberRegEx, multipleMembersFound } = require("../../resources/functions"); +const { database } = require("../../database/mongo"); +const { randomColors } = require("../../config.json"); + +module.exports = class avatar extends commando.Command { + constructor(client) { + super(client, { + name: "avatar", + aliases:["av","avat"], + memberName: "avatar", + group: "utility", + description: "Avatar showcase." + }); + } + /** + * + * @param {commando.CommandoMessage} msg + * @param {*} arg + */ + async run(msg, arg) { + const doc = msg.guild?.id ?? msg.author.id; + const config = database.collection(msg.guild ? "Guild" : "User"); + config.findOne({document: doc}, async (docErr, r) => { + if (docErr) { + errLog(docErr, msg, this.client); + } + const footerQuote = r?.["settings"]?.defaultEmbed?.footerQuote; + const withPerm = arg.trim().split(/,/); + const option = arg.trim().split(/(\-\-)+/); + let user, avatar, member, show; + let [allEmb, multipleMemMes, dupliCheck] = [[], [], []]; + if (!arg) { + user = msg.guild ? msg.guild.member(msg.author) : msg.author; + avatar = msg.author.displayAvatarURL({size:4096,dynamic:true}); + } + let args; + if (!msg.guild || msg.guild.member(msg.author).hasPermission("MANAGE_MESSAGES")) { + args = withPerm; + } else { + args = withPerm[0]; + if (withPerm.length > 1) { + trySend(this.client, msg, "Manage messages permission required to show two or more avatar at once!"); + } + } + for (const ops of option) { + if (ops.toLowerCase().startsWith("show")) { + const val = ops.trim().split(/ +/); + const theVal = val[1]?.trim().replace(",", ""); + if (theVal && !/\D/.test(theVal)) { + show = parseInt(val[1].trim(), 10); + } + } + } + if (arg) { + for(const theAvThis of args) { + const avThis = theAvThis.replace(/\-\-show *\d*/i, ""); + let uID = avThis.trim(); + if (uID.startsWith('<@')) { + uID = uID.slice(2); + } + if (uID.startsWith('!')) { + uID = uID.slice(1); + } + if (uID.endsWith('>')) { + uID = uID.slice(0,-1) + } + if (uID.startsWith("@")) { + uID = uID.slice(1); + } + if (uID.length === 1) { + return trySend(this.client, msg, "One character for searching member isn't allowed <:catstareLife:794930503076675584>"); + } else { + if (uID) { + let ree = []; + async function nonDigit(client) { + const theree = await findMemberRegEx(msg, client, uID); + if (theree.length === 0) { + user = undefined; + trySend(client, msg, `Can't find user: **${avThis.trim()}**`); + } + for (const reeRes of theree) { + ree.push(reeRes); + } + } + if (/\D/.test(uID)) { + await nonDigit(this.client); + } else { + if (msg.guild.member(uID)) { + ree.push(msg.guild.member(uID)); + } else { + await this.client.users.fetch(uID).then(r => ree.push(r)).catch(async e => await nonDigit(this.client)); + } + } + if (ree.length > 0) { + const duplicateRes = dupliCheck.findIndex(yes => yes === ree[0].id); + if (duplicateRes !== -1) { + allEmb[duplicateRes].setDescription(`Duplicate result for: **${avThis.trim()}**`); + user = undefined; + } else { + dupliCheck.push(ree[0].id); + user = ree[0].user ?? ree[0]; + multipleMemMes.push(await multipleMembersFound(this.client, msg, ree, uID, show)); + } + } + if (user) { + avatar = user.displayAvatarURL({size:4096,dynamic:true}); + let emb = new MessageEmbed() + .setImage(avatar) + .setFooter(footerQuote ?? ""); + member = msg.guild ? msg.guild.member(user) : undefined; + if (member) { + emb.setTitle(member.displayName); + if (member.displayColor) { + emb.setColor(member.displayColor) + } + } else { + emb.setTitle(user.username); + } + if (!msg.guild) { + emb.setColor(randomColors[Math.floor(Math.random() * randomColors.length)]); + } + if (emb.color === 16777215) { + emb.setColor(16777214); + } + allEmb.push(emb); + } + } + } + } + } else { + let emb = new MessageEmbed() + .setTitle(user.displayName ?? user.username) + .setImage(avatar) + .setFooter(footerQuote ?? ""); + if (user.displayColor) { + emb.setColor(user.displayColor); + } + if (!msg.guild) { + emb.setColor(randomColors[Math.floor(Math.random() * randomColors.length)]); + } + if (emb.color === 16777215) { + emb.setColor(16777214); + } + allEmb.push(emb); + } + for (let index = 0; index < allEmb.length; index++) { + const embelement = allEmb[index]; + const contelement = multipleMemMes[index]; + trySend(this.client, msg, { embed: embelement, content: contelement, split:{maxLength:2000,char: ", " || ",\n" || ". " || ".\n" || "," || ".",append:',```',prepend:'```md\n# ' }}); + } + return ranLog(msg,'avatar',arg); + }); + } +}; + +// Old codes + /*args = args.split(/ +/); + try { + let member; + let avUrl; + let avatar = new MessageEmbed(); + if (args[0]) { + member = await getUser(this.client, args[0]); + } + if (!args[0]) { + avUrl = msg.author.displayAvatarURL({size:4096,dynamic:true}); + avatar + .setColor(msg.member.displayColor) + .setTitle(msg.member.displayName); + } else + if (member) { + avUrl = member.displayAvatarURL({size:4096,dynamic:true}); + try { + avatar.setColor(msg.guild.member(member).displayColor); + } catch (e) {errLog(e)} + try { + avatar + .setTitle(msg.guild.member(member).displayName); + } catch (e) { + errLog(e); + avatar + .setTitle(member.username); + } + } + if (!avUrl) { + return msg.channel.send('Who is that? I dunno them!'); + } + avatar + //.setAuthor(msg.author.username, msg.author.displayAvatarURL({size:4096, dynamic:true})) + .setImage(avUrl) + .setFooter(footerQuote); + msg.channel.send(avatar); + return ranLog(msg,'avatar', `${member ? `Avatar of ${member.tag} (${member.id}): ` : `Self avatar: `} ${avUrl}`); + } catch (e) { + await msg.channel.send('Who is that? I dunno them!'); + return errLog(e); + }*/ \ No newline at end of file diff --git a/cmds/utility/clonemb.js b/cmds/utility/clonemb.js new file mode 100644 index 0000000..f568989 --- /dev/null +++ b/cmds/utility/clonemb.js @@ -0,0 +1,50 @@ +'use strict'; + +const commando = require("@iceprod/discord.js-commando"); +const { MessageEmbed } = require("discord.js"); +const { getChannelMessage, errLog, ranLog, trySend, tryReact } = require("../../resources/functions"); + +module.exports = class clonemb extends commando.Command { + constructor(client) { + super(client, { + name: "clonemb", + aliases: ["cloneemb","cloneembed", "clonembed"], + memberName: "clonemb", + group: "utility", + description: "Clone an Embed." + }); + } + async run(msg, cargs) { + const args = cargs.trim().split(/ +/); + const {defaultErrorLogChannel} = require("../../config.json"); + try { + const theMes = await getChannelMessage(this.client,msg,args[0],args[1]); + let content; + if (theMes.content) { + content = theMes.content; + } + if (!theMes.embeds || (theMes.embeds).length === 0) { + return trySend(this.client, msg, 'No embed found.'); + } + if (!args[0]) { + return trySend(this.client, msg, 'Which embed??'); + } + trySend(this.client, msg, {content:content,embed:theMes.embeds[0]}); + const moreEmb = theMes.embeds.slice(1); + for(const emb of moreEmb) { + trySend(this.client, msg, new MessageEmbed(emb)); + } + tryReact(msg, "a:yesLife:794788847996370945"); + return ranLog(msg,'clonemb',`Embed ${theMes.url} (${theMes.id}) in ${theMes.channel.name} (${theMes.channel.id}) of ${theMes.guild.name} cloned.`); + } catch (e) { + return errLog( + e, + msg, + this.client, + false, + "No embed found. Use ` ` if it's in another channel.", + true + ); + } + } +}; \ No newline at end of file diff --git a/cmds/utility/embmaker.js b/cmds/utility/embmaker.js new file mode 100644 index 0000000..dcd6fb1 --- /dev/null +++ b/cmds/utility/embmaker.js @@ -0,0 +1,322 @@ +'use strict'; + +const commando = require("@iceprod/discord.js-commando"); +const { MessageEmbed } = require("discord.js"); +const { ranLog, errLog, getChannelMessage, noPerm, tryReact } = require("../../resources/functions"); +const getColor = require("../../resources/getColor"); + +module.exports = class embmaker extends commando.Command { + constructor(client) { + super(client, { + name: "embmaker", + memberName: "embmaker", + aliases: ["embedmaker","createmb","creatembed"], + group: "utility", + description: "Embed creator.", + details:`Embed creator: You can just copy this template and remove unneeded argument. Every argument is optional.\`\`\`\n--title [text]\n--description [text]\n--author:\n -name [text]\n -icon [url]\n -url [url]\n--color [hex, number, name of color]\n--image [url]\n--thumbnail [url]\n--url [url]\n--newfield:\n -name [text]\n -desc [text]\n -inline (true if provided)\n--footer:\n -text [text]\n -icon [url]\n--content [text]\n--channel [channel_[mention, ID]]\n--timestamp [ISO 8601, UNIX Timestamp (Milliseconds)] - Use https://time.lol \n--attachments [url] - You can put [-copy] when editing to copy all the message attachments (Cannot remove existing attachment unless [--channel] provided) \`\`\`Embed editor: You can put \`\`\`--edit <[message_ID, channel_[mention, ID] message_ID]>\`\`\` as first argument to edit the embed in a message. All existing property will be replaced with provided argument. Put \`\`\`--remove [author, fields, footer]\`\`\` to remove all existing property of the provided argument in the embed.\n\nOther arguments:\`\`\`\n--quote <[message_ID, channel_[mention, ID] message_ID]> - Quote a message\`\`\``, + ownerOnly:false, + hidden:false + }); + } + async run(msg, arg) { + const args = arg.trim().split(/(\-\-)+/); + let embed = new MessageEmbed(); + let autName, footertext, autIcon, autUrl, footericon, content, channel, editSrc, newAttach = []; + try { + for(const value of args) { + if (value.toLowerCase().startsWith('edit')) { + const editArg = value.slice('edit'.length).trim().split(/ +/); + if (editArg) { + editSrc = await getChannelMessage(this.client, msg, editArg[0], editArg[1]); + if (editSrc) { + const editEmb = editSrc.embeds[0]; + if (editSrc.content) { + content = editSrc.content; + } + if (editEmb) { + embed = new MessageEmbed(editEmb); + if (editEmb.author) { + if (editEmb.author.name) { + autName = editEmb.author.name; + } + if (editEmb.author.url) { + autUrl = editEmb.author.url; + } + if (editEmb.author.iconURL) { + autIcon = editEmb.author.iconURL; + } + } + if (editEmb.footer) { + if (editEmb.footer.text) { + footertext = editEmb.footer.text; + } + if (editEmb.footer.iconURL) { + footericon = editEmb.footer.iconURL; + } + } + } + } + } + } + if (value.toLowerCase().startsWith('quote')) { + const quoteargs = value.slice('quote'.length).toLowerCase().trim().split(/ +/); + if (quoteargs) { + await getChannelMessage(this.client, msg, quoteargs[0], quoteargs[1]) + .then(quoteThis => { + if (quoteThis) { + const author = quoteThis.guild.member(quoteThis.author); + autName = author ? author.displayName : quoteThis.author.username; + autIcon = quoteThis.author.displayAvatarURL({size:4096,dynamic:true}); + autUrl = quoteThis.url; + embed + .setAuthor(author ? author.displayName : quoteThis.author.username,quoteThis.author.displayAvatarURL({size:4096,dynamic:true}),quoteThis.url) + .setDescription(quoteThis.content) + .setTimestamp(quoteThis.createdAt); + if (author && author.displayColor) { + embed.setColor(author.displayColor); + } + if (quoteThis.attachments) { + for(const attach of quoteThis.attachments) { + attach.map(g => { + if (/^http/.test(g.proxyURL)) { + newAttach.push(g.proxyURL); + } + }); + } + } + } + }); + } + } + if (value.toLowerCase().startsWith('remove')) { + const remove = value.slice('remove'.length).toLowerCase().trim().split(/ +/); + for(const remThis of remove) { + if (remThis === 'fields') { + embed.fields = []; + } + if (remThis === 'author') { + autName = null; + autIcon = null; + autUrl = null; + embed.author = null; + } + if (remThis === 'footer') { + footertext = null; + footericon = null; + embed.footer = null; + } + } + } + if (value.toLowerCase().startsWith('title')) { + embed.setTitle(value.slice('title'.length).trim().replace(/\\(?!\\)/g,'')); + } + if (value.toLowerCase().startsWith('desc')) { + embed.setDescription(value.slice('desc'.length).trim().replace(/\\(?!\\)/g,'')); + } + if (value.toLowerCase().startsWith('description')) { + embed.setDescription(value.slice('description'.length).trim().replace(/\\(?!\\)/g,'')); + } + if (value.toLowerCase().startsWith("author")) { + const autData = value.trim().split(/( \-)+/); + for(const autVal of autData) { + if (autVal.toLowerCase().startsWith('name')) { + autName = autVal.slice('name'.length).trim().replace(/\\(?!\\)/g,''); + } + if (autVal.toLowerCase().startsWith('icon')) { + if (/^http/.test(autVal.slice('icon'.length).trim())) { + autIcon = autVal.slice('icon'.length).trim(); + } else { + autIcon = null; + } + } + if (autVal.toLowerCase().startsWith('url')) { + if (/^http/.test(autVal.slice('url'.length).trim())) { + autUrl = autVal.slice('url'.length).trim(); + } else { + autUrl = null; + } + } + } + } + if (value.toLowerCase().startsWith("color")) { + const colorName = value.slice("color".length).trim(); + const color = getColor(colorName); + if (color) { + embed.setColor(color); + } + } + if (value.toLowerCase().startsWith("image")) { + if (/^http/.test(value.slice("image".length).trim())) { + embed.setImage(value.slice("image".length).trim()); + } else { + embed.setImage(null); + } + } + if (value.toLowerCase().startsWith("thumbnail")) { + if (/^http/.test(value.slice("thumbnail".length).trim())) { + embed.setThumbnail(value.slice("thumbnail".length).trim()); + } else { + embed.setThumbnail(null); + } + } + if (value.toLowerCase().startsWith('url')) { + if (/^http/.test(value.slice("url".length).trim())) { + embed.setURL(value.slice("url".length).trim()); + } else { + embed.setURL(null); + } + } + if (value.toLowerCase().startsWith('attachment')) { + const attach = value.slice("attachments".length).trim().split(/ +/); + for(const theFile of attach) { + if (/^http/.test(theFile)) { + newAttach.push(theFile); + } + if (theFile === '-copy' && editSrc) { + if (editSrc.attachments) { + for(const attach of editSrc.attachments) { + attach.map(g => { + if (/^http/.test(g.proxyURL)) { + newAttach.push(g.proxyURL); + } + }); + } + } + } + } + } + if (value.toLowerCase().startsWith("timestamp")) { + if(!/[a-zA-Z]/.test(value.slice("timestamp".length).trim())) { + embed.setTimestamp(parseInt(value.slice("timestamp".length).trim(), 10)); + } else { + if (value.slice("timestamp".length).trim().toLowerCase() === 'now') { + embed.setTimestamp(msg.createdAt); + } else { + embed.setTimestamp(value.slice("timestamp".length).trim()); + } + } + } + if (value.toLowerCase().startsWith('footer')) { + const footerData = value.trim().split(/( \-)+/); + for(const footval of footerData) { + if (footval.toLowerCase().startsWith('text')) { + footertext = footval.slice("text".length).trim().replace(/\\(?!\\)/g,''); + } + if (footval.toLowerCase().startsWith('icon')) { + if (/^http/.test(footval.slice('icon'.length).trim())) { + footericon = footval.slice('icon'.length).trim(); + } else { + footericon = null; + } + } + } + } + if (value.toLowerCase().startsWith('newfield')) { + const fieldData = value.trim().split(/( \-)+/); + let fieldName,fieldValue, inline = false; + for(const data of fieldData) { + if (data.toLowerCase().startsWith('name')) { + fieldName = data.slice('name'.length).trim().replace(/\\(?!\\)/g,''); + } + if (data.toLowerCase().startsWith('desc')) { + fieldValue = data.slice('desc'.length).trim().replace(/\\(?!\\)/g,''); + } + if (data.toLowerCase().startsWith('description')) { + fieldValue = data.slice('description'.length).trim().replace(/\\(?!\\)/g,''); + } + if (data.toLowerCase().startsWith('inline')) { + inline = true; + } + } + if (!fieldName) { + fieldName = '​'; + } + if (!fieldValue) { + fieldValue = '​'; + } + embed.addField(fieldName,fieldValue,inline); + } + if (value.toLowerCase().startsWith('content')) { + content = value.slice('content'.length).trim().replace(/\\(?!\\)/g,''); + } + if (value.toLowerCase().startsWith('channel')) { + let ID = value.slice('channel'.length).trim(); + if (ID.toLowerCase() === 'here') { + channel = msg.channel; + } + if (ID.startsWith('<#') && ID.endsWith('>')) { + ID = ID.slice(2, -1); + } + if (!/\D/.test(ID)) { + channel = this.client.channels.cache.get(ID); + } + } + } + if(autIcon === false) { + if (embed.author.name) { + delete embed.author.name; + } + } + if (!autName && autIcon) { + autName = '​'; + } + if (autName || autIcon && embed.author !== null) { + embed.setAuthor(autName,autIcon,autUrl); + } + if (!footertext && footericon) { + footertext = '​'; + } + if (footertext || footericon && embed.footer !== null) { + embed.setFooter(footertext,footericon); + } + if (embed.length === 0 && (embed.thumbnail === null || embed.thumbnail.url === null) && embed.author === null && (embed.image === null || embed.image.url === null)) { + embed.setDescription('​'); + } + if (embed.color === 16777215) { + embed.setColor(16777214); + } + if (embed.description === '​' && (content || newAttach.length > 0)) { + embed = null; + } + if (newAttach.length > 0) { + console.log("Uploading attachments..."); + } + console.log(embed); + if (editSrc) { + if (channel) { + channel.send({content:content,embed:embed,files:newAttach}).catch(e => noPerm(msg)); + } else { + channel = msg.channel; + if (editSrc.author === this.client.user) { + try { + editSrc.edit({content:content,embed:embed,files:newAttach}).catch(e => errLog(e, msg, this.client)); + } catch (e) { + try { + channel.send('Something\'s wrong, i can\'t edit that so here <:WhenLife:773061840351657984>'); + channel.send({content:content,embed:embed,files:newAttach}); + } catch (e) { + noPerm(msg); + } + } + } else { + try { + channel.send('I can\'t edit that, so here <:catstareLife:794930503076675584>'); + channel.send({content:content,embed:embed,files:newAttach}); + } catch (e) { + noPerm(msg); + } + } + } + } else { + if (!channel) { + channel = msg.channel; + } + channel.send({content:content,embed:embed,files:newAttach}).catch(e => noPerm(msg)); + } + tryReact(msg, "a:yesLife:794788847996370945"); + return ranLog(msg,'embmaker',`${arg}\nContent: ${content}\nAttachments: ${newAttach}`); + } catch (e) { + return errLog(e, msg, this.client, true, "", true); + } + } +}; \ No newline at end of file diff --git a/cmds/utility/invite.js b/cmds/utility/invite.js new file mode 100644 index 0000000..6c1b874 --- /dev/null +++ b/cmds/utility/invite.js @@ -0,0 +1,20 @@ +'use strict'; + +const commando = require("@iceprod/discord.js-commando"); +const { ranLog, trySend } = require("../../resources/functions"); +const conf = require('../../config.json'); + +module.exports = class invite extends commando.Command { + constructor(client) { + super(client, { + name: "invite", + memberName: "invite", + group: "utility", + description: "Give you spam.", + }); + } + run(msg) { + trySend(this.client, msg, "Mute me after cuz it's spam\n"+conf.invite); + return ranLog(msg, "invite"); + } +}; \ No newline at end of file diff --git a/cmds/utility/mesemb.js b/cmds/utility/mesemb.js new file mode 100644 index 0000000..a2b4ea2 --- /dev/null +++ b/cmds/utility/mesemb.js @@ -0,0 +1,28 @@ +'use strict'; + +const commando = require("@iceprod/discord.js-commando"); +const { getChannelMessage, ranLog, errLog, noPerm } = require("../../resources/functions"); + +module.exports = class mesemb extends commando.Command { + constructor(client) { + super(client, { + name: "mesemb", + memberName: "mesemb", + group: "utility", + description: "Fetch embed info in a message." + }); + } + async run(msg, arg) { + const args = arg.trim().split(/ +/); + try { + const message = await getChannelMessage(this.client,msg,args[0],args[1]); + console.log(message.embeds); + const mesemb = '```js\n'+JSON.stringify(message.embeds).split(',"').join(',\n"').split(',{').join(',\n{').replace(/`/g,"\\`")+'```'; + const result = msg.channel.send({content:'Collected:'+mesemb,split:{maxLength:2000,char: ", " || ",\n" || ". " || ".\n" || "," || ".",append:',```',prepend:'```js\n'}}); + return ranLog(msg,'mesemb',await result.content); + } catch (e) { + noPerm(msg); + return errLog(e, msg, this.client); + } + } +}; \ No newline at end of file diff --git a/cmds/utility/mesinfo.js b/cmds/utility/mesinfo.js new file mode 100644 index 0000000..0200beb --- /dev/null +++ b/cmds/utility/mesinfo.js @@ -0,0 +1,37 @@ +'use strict'; + +const commando = require("@iceprod/discord.js-commando"); +const { errLog, getChannelMessage, ranLog, noPerm, trySend } = require("../../resources/functions"); + +module.exports = class mesinfo extends commando.Command { + constructor(client) { + super(client, { + name: "mesinfo", + memberName: "mesinfo", + group: "utility", + description: "Fetch message info." + }); + } + async run(msg, arg) { + const {defaultErrorLogChannel} = require("../../config.json"); + const args = arg.trim().split(/ +/); + const message = await getChannelMessage(this.client, msg, args[0], args[1]); + console.log(message); + if (!message) { + return trySend(this.client, msg, "No message with that ID <:catstareLife:794930503076675584>") + } else { + try { + const mesinfo = 'Collected:```js\n'+JSON.stringify(message).split(',"').join(',\n"').split(',{').join(',\n{').replace(/`/g,"\\`")+'```'; + const mentionJSON = message.mentions.toJSON(); + const sendMentionInfo = 'Mentions:```js\n'+JSON.stringify(mentionJSON).split(',"').join(',\n"').split(',{').join(',\n{')+'```'; + const Attachments = 'Attachments:```js\n'+JSON.stringify(message.attachments).split(',"').join(',\n"').split(',{').join(',\n{')+'```'; + const sendmesinfo = mesinfo+sendMentionInfo+Attachments; + const result1 = msg.channel.send({content:sendmesinfo,split:{maxLength:2000,char: ", " || ",\n" || ". " || ".\n" || "," || ".",append:',```',prepend:'```js\n'}}); + return ranLog(msg,'mesinfo',`${await result1}`); + } catch (e) { + noPerm(msg); + return errLog(e, msg, this.client, false, "", false, defaultErrorLogChannel); + } + } + } +}; \ No newline at end of file diff --git a/cmds/utility/newquoteotd.js b/cmds/utility/newquoteotd.js new file mode 100644 index 0000000..a3d86fc --- /dev/null +++ b/cmds/utility/newquoteotd.js @@ -0,0 +1,63 @@ +'use strict'; + +const commando = require("@iceprod/discord.js-commando"); +const { MessageEmbed } = require("discord.js"); +const { getChannelMessage, ranLog, errLog, noPerm, tryReact, trySend } = require("../../resources/functions"); +const { database } = require("../../database/mongo"); +const col = database.collection("Guild"); + +module.exports = class newquoteotd extends commando.Command { + constructor(client) { + super(client, { + name: "newquoteotd", + memberName: "newquoteotd", + group: "utility", + description: "The Life exclusive command for Quote of the day.", + guildOnly: true + }); + } + async run(msg, arg) { + const args = arg.trim().split(/ +/); + const colorConf = require(`../../config.json`); + const findDoc = await col.findOne({document: msg.guild.id}); + const quoteOTD = findDoc?.["settings"]?.quoteOTD; + const color = colorConf.randomColors; + if (!quoteOTD || !quoteOTD.channel) { + return msg.channel.send(`Quote OTD channel not set! Run \`${this.client.commandPrefix}quoteotd\` to set one.`); + } + if (!args[0]) { + return msg.channel.send('Provide ``!'); + } + try { + let emb = new MessageEmbed(); + const mes = await getChannelMessage(this.client,msg,args[0],args[1]); + if (mes) { + const author = mes.guild.member(mes.author); + let description = mes.content; + if (!description.endsWith('.')) { + description = description+'.'; + } + const thumbnail = mes.author.displayAvatarURL({size:4096,dynamic:true}); + let name; + if (author.displayName) { + name = author.displayName; + } else { + name = author.username; + } + emb + .setTitle(name) + .setDescription(description) + .setThumbnail(thumbnail) + .setFooter(quoteOTD.footerText, quoteOTD.footerIcon) + .setColor(color[Math.floor(Math.random()*color.length)]); + await trySend(this.client, quoteOTD.channel, emb); + tryReact(msg, "a:yesLife:794788847996370945"); + return ranLog(msg,'newqotd',`${msg.author.tag} (${msg.author.id}) made new QOTD \`${description}\` by ${author.tag} (${author.id})`); + } + return msg.channel.send('No message with that ID from this channel. Use `[ , message_link]` if it\'s in another channel.'); + } catch (e) { + noPerm(msg); + return errLog(e, msg, this.client, true, "", true); + } + } +}; \ No newline at end of file diff --git a/cmds/utility/profile.js b/cmds/utility/profile.js new file mode 100644 index 0000000..acd7221 --- /dev/null +++ b/cmds/utility/profile.js @@ -0,0 +1,46 @@ +'use strict'; + +const commando = require("@iceprod/discord.js-commando"); +const { MessageEmbed } = require("discord.js"); +const { errLog, trySend } = require("../../resources/functions"); + +module.exports = class profile extends commando.Command { + constructor(client) { + super(client, { + name: "profile", + memberName: "profile", + group: "utility", + description: "Show Users/Member profile" + }); + } + async run(msg, arg) { + const args = arg.trim().split(/ +/); + let Users = []; + let emb = new MessageEmbed(); + if (!arg) { + Users.push(msg.author); + } else { + for(const userArr of args) { + let theUser = userArr; + if (theUser.startsWith("<")) { + theUser.slice(2); + } + if (theUser.endsWith(">")) { + theUser.slice(0,-1); + } + if (theUser.startsWith("!")) { + theUser.slice(1); + } + if (!/\D/.test(theUser)) { + try { + Users.push(await this.client.users.fetch(theUser)); + } catch (e) { + errLog(e, msg, this.client, false, `Can't find user \`${userArr}\``); + } + } else { + trySend(this.client, msg, `Invalid user provided \`${userArr}\``); + } + } + } + } +}; \ No newline at end of file diff --git a/cmds/utility/quoteotd.js b/cmds/utility/quoteotd.js new file mode 100644 index 0000000..ea355c0 --- /dev/null +++ b/cmds/utility/quoteotd.js @@ -0,0 +1,64 @@ +'use strict'; + +const commando = require("@iceprod/discord.js-commando"); +const { writeJSONSync } = require("fs-extra"); +const { join } = require("path"); +const { ranLog, trySend } = require("../../resources/functions"); +const { database } = require("../../database/mongo"); +const col = database.collection("Guild"); + +module.exports = class quoteotd extends commando.Command { + constructor(client) { + super(client, { + name: "quoteotd", + memberName: "quoteotd", + group: "utility", + description: "Set Quote of the day channel and settings.", + details:"```\n--channel\n--text\n--icon```", + guildOnly: true + }); + } + async run(msg, arg) { + const args = arg.trim().split(/(\-\-)+/); + if (args.length < 2) { + return trySend(this.client, msg, `Provide argument: \`--channel [mention, ID], --text [footer text], --icon [url footer icon]\``); + } + let result = ''; + for(const arr of args) { + const startW = arr.toLowerCase(); + let data; + if (startW.startsWith('channel')) { + data = arr.slice('channel'.length).trim(); + if (data.startsWith('<')) { + data = data.slice(2,-1); + } + if (!this.client.channels.cache.get(data)) { + return trySend(this.client, msg, 'Invalid/unknown channel provided! Try mentioning a channel or use `ChannelID`'); + } else { + col.updateOne({document: msg.guild.id}, {$set: {"settings.quoteOTD.channel": data}}, { upsert: true }); + result = result+`Channel set to \`${this.client.channels.cache.get(data).name}\`\n`; + } + } + if (startW.startsWith('text')) { + data = arr.slice('text'.length).trim(); + col.updateOne({document: msg.guild.id}, {$set: {"settings.quoteOTD.footerText": data}}, { upsert: true }); + result = result+`Footer text set to \`${data}\`\n`; + } + if (startW.startsWith('icon')) { + data = arr.slice('icon'.length).trim(); + if (!/^http/.test(data)) { + return trySend(this.client, msg, 'Invalid icon url provided!'); + } else { + col.updateOne({document: msg.guild.id}, {$set: {"settings.quoteOTD.footerIcon": data}}, { upsert: true }); + result = result+`Footer icon set!\n`; + } + } + } + if (result.length > 0) { + trySend(this.client, msg, result); + } else { + return trySend(this.client, msg, `Provide argument: \`--channel [mention, ID], --text [footer text], --icon [url footer icon]\``); + } + return ranLog(msg,'qotd',result); + } +}; \ No newline at end of file diff --git a/cmds/utility/say.js b/cmds/utility/say.js new file mode 100644 index 0000000..38e5e36 --- /dev/null +++ b/cmds/utility/say.js @@ -0,0 +1,26 @@ +'use strict'; + +const commando = require("@iceprod/discord.js-commando"); +const { errLog, ranLog, trySend, tryDelete } = require("../../resources/functions"); + +module.exports = class say extends commando.Command { + constructor(client) { + super(client, { + name: "say", + memberName: "say", + group: "utility", + description: "Say." + }); + } + run(msg, args) { + let noArgs = `<@!${msg.author.id}> what to say?`; + if (!args) { + args = noArgs; + } + trySend(this.client, msg, args); + if (args !== noArgs && msg.channel.guild) { + tryDelete(msg); + } + return ranLog(msg,'say',`Content: ${args}`); + } +}; \ No newline at end of file diff --git a/cmds/utility/send.js b/cmds/utility/send.js new file mode 100644 index 0000000..54df3d4 --- /dev/null +++ b/cmds/utility/send.js @@ -0,0 +1,49 @@ +'use strict'; +const commando = require("@iceprod/discord.js-commando"); +const { ranLog, errLog, trySend, sentAdCheck, tryReact } = require("../../resources/functions"); + +module.exports = class send extends commando.Command { + constructor(client) { + super(client, { + name: "send", + memberName: "send", + group: "utility", + description: "Send message to designated channel." + }); + } + async run(msg, args ) { + const comarg = args.trim().split(/ +/); + const bot = this.client; + let at = comarg[0]; + if (!comarg[0]) { + return trySend(this.client, msg, 'Where?!?'); + } + if (comarg[0].startsWith('<#') && comarg[0].endsWith('>')) { + at = comarg[0].slice(2, -1); + } + const channel = bot.channels.cache.get(at); + const sendTheMes = args.slice(comarg[0].length).trim(); + if (!channel) { + return trySend(this.client, msg, "Give me the right `channel_[mention, ID]` bruh"); + } + try { + if (sendTheMes.length === 0) { + return trySend(this.client, at, `<@!${msg.author.id}> If you wanna send nothin then why you even typed that <:bruhLife:798789686242967554>`); + } + const send = await channel.send(sendTheMes); + sentAdCheck(send); + const filter = () => true; + const collector = send.createReactionCollector(filter, {time: 15*6*1000, dispose:true}); + collector.on('collect', r => { + try { + msg.react(r.emoji); + } catch (e) {} + }); + collector.on('remove', async r => await msg.reactions.resolve(r).id.remove(r.id)); + tryReact(msg, 'yeLife:796401669188354090'); + return ranLog(msg,'send',`ID: ${send.id} url: ${send.url}\nSent to channel: ${channel.name} (${channel.id}) of ${send.guild.name}\nContent: ${args.slice(at.length)}`); + } catch (e) { + return errLog(e, msg, this.client); + } + } +}; \ No newline at end of file diff --git a/cmds/utility/servav.js b/cmds/utility/servav.js new file mode 100644 index 0000000..88d9059 --- /dev/null +++ b/cmds/utility/servav.js @@ -0,0 +1,57 @@ +'use strict'; + +const commando = require("@iceprod/discord.js-commando"); +const { MessageEmbed } = require("discord.js"); +const { database } = require("../../database/mongo"); +const { errLog, trySend, ranLog } = require("../../resources/functions"); +const getColor = require("../../resources/getColor"); + +module.exports = class servav extends commando.Command { + constructor(client) { + super(client, { + name: "servav", + memberName: "servav", + aliases: ["serveravatar", "servavatar", "serverav"], + group: "utility", + description: "Show server avatar." + }); + } + run(msg, arg) { + const server_ID = arg.split(/ +/)[0]; + const doc = msg.guild?.id ?? msg.author.id; + const col = database.collection(msg.guild ? "Guild" : "User"); + col.findOne({document: doc}, async (err, res) => { + if (err) { + errLog(err, msg, this.client); + } + const footerQuote = res?.["settings"]?.defaultEmbed?.footerQuote; + let icon, target; + if (server_ID) { + if (!/\D/.test(server_ID)) { + target = this.client.guilds.cache.get(server_ID); + } else { + return trySend(this.client, msg, "Invalid `server_ID` provided!"); + } + } else { + target = msg.guild; + } + if (target) { + icon = target.iconURL({size:4096, dynamic:true}); + } else { + return trySend(this.client, msg, "I'm not in that server..."); + } + if (icon) { + let embed = new MessageEmbed() + .setImage(icon) + .setTitle(target.name) + .setFooter(footerQuote ?? ""); + if (target.owner.displayColor) { + const color = getColor(target.owner.displayColor) + embed.setColor(color); + } + trySend(this.client, msg, embed); + return ranLog(msg, "servav", `**${target.name}** (${target.id})`); + } + }); + } +}; \ No newline at end of file diff --git a/cmds/utility/setfootq.js b/cmds/utility/setfootq.js new file mode 100644 index 0000000..ea4161b --- /dev/null +++ b/cmds/utility/setfootq.js @@ -0,0 +1,37 @@ +'use strict'; + +const commando = require("@iceprod/discord.js-commando"); +const { ranLog, errLog, trySend } = require("../../resources/functions"); +const { database } = require("../../database/mongo"); + +module.exports = class setfootq extends commando.Command { + constructor(client) { + super(client, { + name: "setfootq", + aliases:["setfooterquote"], + memberName: "setfootq", + group: "utility", + description: "Set server embed footer text." + }); + } + async run(msg, args) { + try { + if (!msg.guild?.member(msg.author).hasPermission("MANAGE_GUILD" && !this.client.owners.includes(msg.author))) { + return trySend(this.client, msg, 'No lol'); + } + const data = msg.guild ? "Guild" : "User"; + const col = database.collection(data); + const doc = msg.guild?.id ?? msg.author.id; + const oldQ = await col.findOne({document: doc}); + col.updateOne({document: doc}, {$set: {"settings.defaultEmbed.footerQuote": args.trim()}}, { upsert: true }, async (e) => { + if (e) { + return errLog(e, msg, this.client); + } + const result = await trySend(this.client, msg, `Changed from \`${oldQ?.["settings"]?.defaultEmbed?.footerQuote}\` to \`${args.trim()}\``); + return ranLog(msg, "setfooterquote", result.content); + }); + } catch (e) { + return errLog(e, msg, this.client); + } + } +}; \ No newline at end of file diff --git a/cmds/utility/uinfo.js b/cmds/utility/uinfo.js new file mode 100644 index 0000000..c45ba5f --- /dev/null +++ b/cmds/utility/uinfo.js @@ -0,0 +1,44 @@ +'use strict'; + +const commando = require("@iceprod/discord.js-commando"); +const { getUser, errLog, ranLog, trySend } = require("../../resources/functions"); + +module.exports = class uinfo extends commando.Command { + constructor(client) { + super(client, { + name: "uinfo", + memberName: "uinfo", + group: "utility", + description: "\"Detailed\" Profile." + }); + } + async run(msg, arg ) { + const args = arg.trim().split(/ +/); + try { + let profile; + if (args[0]) { + profile = await getUser(this.client, msg, args[0]); + } else { + profile = msg.author; + } + const member = await msg.guild.member(profile); + let result = 'User: '+profile.tag+'```js\n'; + if (profile) { + console.log(profile); + result = result+JSON.stringify(profile).split(',"').join(',\n"').split(',{').join(',\n{')+'```'; + } + if (member) { + console.log(member); + result = result+'As member: '+member.displayName+'```js\n'+JSON.stringify(member).split(',"').join(',\n"').split(',{').join(',\n{')+'```'; + if ((member.displayColor)) { + console.log(member.displayColor); + result = result+'Display color:```js\n'+member.displayColor+'```'; + } + } + trySend(this.client, msg, result,{split:{maxLength:2000,char: ", " || ",\n" || ". " || ".\n" || "," || ".",append:',```',prepend:'```js\n'}}); + return ranLog(msg,'profile',msg.content); + } catch (e) { + return errLog(e, msg, this.client, false, 'Gimme the right ID!', true); + } + } +}; \ No newline at end of file diff --git a/config copy.json b/config copy.json new file mode 100644 index 0000000..be86175 --- /dev/null +++ b/config copy.json @@ -0,0 +1,26 @@ +{ + "invite": "https://discord.com/oauth2/authorize?client_id=788006279837909032&scope=bot&permissions=8", + "token": "", + "errLogChannel": "822877910138224660", + "randomColors": [ + 12357519, + 16711935, + 128, + 32896, + 15277667, + "00ff00", + "ff0000", + "ff94f2", + "f1e40f", + "ff8c00", + "a0522d", + 3447003, + "0fffff", + "803c9d", + "faa775", + "000000", + 16777214 + ], + "defaultErrorLogChannel":"822877910138224660", + "mongoServer":"mongodb://localhost:27017" +} diff --git a/database/mongo.js b/database/mongo.js new file mode 100644 index 0000000..a0d97fe --- /dev/null +++ b/database/mongo.js @@ -0,0 +1,22 @@ +'use strict'; + +const { mongoServer } = require("../config.json"); +const { MongoClient } = require("mongodb"); +const dbClient = new MongoClient(mongoServer,{ + useUnifiedTopology: true +}); + +dbClient.connect(e => { + if (e) { + console.error(e); + return process.exit(); + } + console.log("Database connected!"); +}); + +const database = dbClient.db("Shasha"); + +/** + * @type {dbClient: MongoClient, database: Db} + */ +module.exports = { dbClient, database } \ No newline at end of file diff --git a/package.json b/package.json new file mode 100644 index 0000000..d73dc77 --- /dev/null +++ b/package.json @@ -0,0 +1,24 @@ +{ + "dependencies": { + "@iceprod/discord.js-commando": "^0.14.3", + "bree": "^6.2.0", + "bufferutil": "^4.0.3", + "cabin": "^9.0.4", + "cleverbot-node": "^0.3.11", + "discord.js": "^12.5.2", + "discord.js-commando": "^0.12.3", + "erlpack": "github:discord/erlpack", + "fs-extra": "^9.1.0", + "lodash": "^4.17.21", + "mongodb": "^3.6.6", + "nightmare": "^3.0.2", + "node": "^15.12.0", + "node-fetch": "^2.6.1", + "pup": "^0.0.2", + "puppeteer": "^9.0.0", + "sqlite": "^4.0.21", + "sqlite3": "^5.0.2", + "utf-8-validate": "^5.0.4", + "zlib-sync": "^0.1.7" + } +} diff --git a/resources/date.js b/resources/date.js new file mode 100644 index 0000000..8d63650 --- /dev/null +++ b/resources/date.js @@ -0,0 +1,16 @@ +'use strict'; + +/** + * @param {Number} duration - Original duration + * @param {String} value (24h) + * @param {Number} multi - Multiplier + * @returns {Number} + * @example + * const duration = muteDurationMultiplier(5000, 23h, 1 * 60 * 60 * 1000); + */ +function muteDurationMultiplier(duration, value, multi = 0) { + const digit = parseInt(value.match(/\d+/), 10); + return duration + digit * multi; +} + +module.exports = { muteDurationMultiplier } \ No newline at end of file diff --git a/resources/functions.js b/resources/functions.js new file mode 100644 index 0000000..1cf109e --- /dev/null +++ b/resources/functions.js @@ -0,0 +1,332 @@ +'use strict'; + +const fs = require('fs-extra'); +const { MessageEmbed, Message, GuildMember, User, Channel, Client } = require('discord.js'); +const { defaultErrorLogChannel } = require("../config.json"); +const { database } = require("../database/mongo"); + +/** + * Log an error. If second argument, third argument is required + * @param {Error} theError - Catched error (error) + * @param {Message} msg - Message object (msg) + * @param {Client} client - This client (this.client) + * @param {Boolean} sendTheError - Add error content to notify message (true | false) + * @param {String} errorMessage - Error message ("You don't have enough permission to use that command!") + * @param {Boolean} notify - Send error to user who ran the command + */ +async function errLog(theError, msg, client, sendTheError, errorMessage, notify) { + let errLogPath, [logThis, inLogChannel, sendErr] = ['', '', '']; + if (msg) { + const comErr = msg.content.trim().split(/ +/)[0]; + logThis = `\`${comErr}\` (${msg.id}) ${msg.url} in ${msg.guild ? `**${msg.channel.name}** (${msg.channel.id}) of **${msg.guild.name}** (${msg.guild.id})` : `**DM**`} ran by **${msg.author.tag}** (${msg.author.id}) \n\n`; + msg.guild ? errLogPath = `../Guilds/${msg.guild.id}/Log/` : errLogPath = '../Log/'; + if (errorMessage) { + if (errorMessage.length > 0) { + sendErr = sendErr + errorMessage+'\n\n'; + inLogChannel = errorMessage+'\n'; + } + } + if (sendTheError) { + sendErr = sendErr+'```js\n'+theError.stack+'```'; + } + if (notify) { + try { + msg.channel.send(sendErr.trim(),{split:true}); + } catch (e) { + errLog(e,msg); + } + } + } else { + errLogPath = '../Log/'; + } + if (client) { + try { + inLogChannel = inLogChannel+'```js\n'+theError.stack+'```'; + if (msg && msg.guild && msg.guild.id === "823815890285756447") { + logThis = ""; + } + const sendAt = await client.channels.cache.get(defaultErrorLogChannel); + sendAt.send(logThis + inLogChannel.trim(),{split:true}); + } catch (errmes) { + errLog(errmes, msg); + } + } + const f = new Date().toUTCString(); + logThis = logThis+theError.stack+"\nat: "+f; + return //console.log(logThis); +} + /* + fs.appendFile(join(__dirname, errLogPath + 'Error.txt'), logThis+"\n", (err) => { + if (err) { + console.error(err); + fs.mkdir(join(__dirname, errLogPath), { recursive: true }, (err) => { + if (err) { + return console.error('Unable to create directory. Error: ' + err.stack); + } + console.log(errLogPath + ' created.'); + fs.writeFile(join(__dirname, errLogPath + 'Error.txt'), logThis+"\n", (err2) => { + if (err2) { + return console.error(err2); + } + console.error(logThis); + }); + }); + } + console.error(logThis); + }); */ + +/** + * Get message object from the message channel or provided channel + * @param {Client} client - This client (this.client) + * @param {Message} msg - Message object (msg) + * @param {String} MainID - Message ID | Channel ID | Channel Mention + * @param {String} SecondID - Message ID + * @returns {Promise} Message object + */ +async function getChannelMessage(client, msg, MainID, SecondID) { + if (!MainID) { + return + } + if (/\//.test(MainID)) { + const splitURL = MainID.split(/\/+/); + SecondID = splitURL[splitURL.length-1]; + MainID = splitURL[splitURL.length-2]; + } + if (MainID.startsWith('<') && MainID.endsWith('>')) { + MainID = MainID.slice(2, -1); + } + if (SecondID && (!/\D/.test(SecondID))) { + try { + const meschannel = client.channels.cache.get(MainID); + return await meschannel.messages.fetch(SecondID); + } catch (theError) { + return errLog(theError, msg, client); + } + } + return await msg.channel.messages.fetch(MainID).catch(e => {return errLog(e, msg, client)}); +} + +/** + * Get user object + * @param {Client} client - This client (this.client) + * @param {String} MainID - User ID | User Mention + * @returns {Promise} User object + * @example const user = getUser(this.client, args[0]); + */ +async function getUser(client, msg, MainID) { + if (MainID.startsWith('<') && MainID.endsWith('>')) { + MainID = MainID.slice(2, -1); + } + if (MainID.startsWith('!')) { + MainID = (MainID.slice(1)); + } + try { + return await client.users.fetch(MainID); + } catch (theError) { + return errLog(theError, msg, client); + } +} + +function execCB(error, stdout, stderr) { + if (error) { + console.error(error); + return errLog(error); + } + console.log('stdout:\n'+stdout); + console.log('stderr:\n'+stderr); +} + +async function ranLog(msg, cmd, addition) { + let errLogPath; + if (msg.guild) { + errLogPath = `../Guilds/${msg.guild.id}/Log/`; + } else { + errLogPath = '../Log/'; + } + let add = '\n'+addition; + const b = new Date().toUTCString(); + const inLog = `NEW USAGE! Message ID: ${msg.id} url: ${msg.url}\nCommand \`${cmd}\` ran by **${msg.author.tag}** (${msg.author.id}) in ${msg.guild ? `**${msg.channel.name}**` : 'DM'} (${msg.channel.id}) ${msg.guild ? `of **${msg.guild.name}** (${msg.guild.id})` : ``}${add}\nAt: ${b}`; + return //console.log(inLog); +} + /* fs.appendFile(join(__dirname, `${errLogPath}CmdUsage.txt`),`${inLog}\n`, e => { + if (e) { + fs.mkdir(join(errLogPath),{recursive:true}, e2 => { + if (e2) {} + fs.writeFile(join(__dirname, `${errLogPath}CmdUsage.txt`),`${inLog}\n`, e3 => { + if (e3) { + errLog(e3, msg); + } else { + console.log(`${errLogPath}CmdUsage.txt created.`); + } + }); + }); + } + }); + */ + +/** + * Notify when more than one member found when looking in the member list + * @param {Client} client - (this.client) + * @param {Message} msg - Message object + * @param {GuildMember[]} arr - Test array + * @param {String} key - Keyword + * @param {Number} max - Max length + * @returns {Promise} + */ +async function multipleMembersFound(client, msg, arr, key, max) { + if (!max) { + max = 5; + } + if (arr.length > 1) { + try { + let multipleFound = []; + for(const one of arr) { + multipleFound.push((await (client.users.fetch(one.id))).tag); + } + let multi = []; + for(const mu of multipleFound) { + if (multipleFound.indexOf(mu) < max) { + multi.push(mu); + } + } + let mes = multi.join(", "); + if (multipleFound.length > max) { + mes = mes+` and ${multipleFound.length - max} more...`; + } + return `Multiple members found for: **${key}**\`\`\`md\n# ${mes}\`\`\``; + } catch (e) { + errLog(e, msg, client); + } + } else { + return ''; + } +} + +/** + * Get member object with RegExp + * @param {Message} msg + * @param {String} name + * @returns {Promise} Member object found + */ +async function findMemberRegEx(msg, client, name) { + let found = []; + const re = new RegExp(name, "i"); + const list = msg.guild.members.cache.map(g => g); + for(const mem of list) { + if (re.test(mem.displayName) || re.test((await client.users.fetch(mem.id)).tag)) { + found.push(mem); + } + } + return found; +} + +/** + * React when it try something but fail because has not enough perms + * @param {Message} msg + */ +function noPerm(msg) { + if (msg) { + msg.react("sadduLife:797107817001386025").catch(e => {}); + } +} + +/** + * Send message + * @param {Client} client - (this.client) + * @param {Message | String} msg Message object | channel_ID + * @param {...any} content - ({content:content,optionblabla}) + * @returns {Promise} Sent message object + */ +async function trySend(client, msg, ...content) { + //console.log(...content); + let msgOf; + if (msg?.channel) { + msgOf = msg.channel; + } else { + msgOf = client.channels.cache.get(msg); + } + const sentMes = await msgOf.send(...content) + .catch(e => { + if (msg?.channel) { + noPerm(msg); + } + return + }); + sentAdCheck(sentMes); + return sentMes; +} + +/** + * Delete message + * @param {Message} msg - Message to delete (msg) + */ +function tryDelete(msg) { + if (msg) { + msg.delete().catch(noPerm(msg)); + } +} + + /** + * React message + * @param {Message} msg - Message to react (msg) + * @param {String} reaction - Emote ("name:ID") + */ +function tryReact(msg, reaction) { + if (msg) { + msg.react(reaction).catch(() => {}); + } +} + +/** + * Check a message sent by client for ads + * @param {Message} sent - Sent message object (await msg.channel.send("discord.gg/banana")) + */ +function sentAdCheck(sent) { + if (sent) { + if (/(https:\/\/)?(www\.)?discord\.gg\/(?:\w{2,15}(?!\w)(?= *))/.test(sent.content)) { + let newCont = sent.content.replace(/(https:\/\/)?(www\.)?discord\.gg\/(?:\w{2,15}(?!\w)(?= *))/, '`Some invite link goes here`'); + sent.edit(newCont, `Command abuse: Contain server invite link.`); + } + } +} + +/** + * Make default image embed + * @param {Client} client + * @param {Message} msg + * @param {URL} image + * @param {GuildMember | User} author + * @param {String} title + * @param {String} footerText + * @returns {Promise} + */ +async function defaultImageEmbed(client, msg, image, author, title, footerText) { + const { randomColors } = require("../config.json"); + let footerQuote = footerText; + if (!footerQuote) { + const doc = await database.collection(msg.guild ? "Guild" : "User").findOne({document: msg.guild?.id ?? msg.author.id}); + footerQuote = doc?.["settings"]?.defaultEmbed?.footerQuote || ""; + } + let emb = new MessageEmbed(); + try { + emb + .setTitle(title) + .setImage(image) + .setColor(msg.guild ? author?.displayColor : randomColors[Math.floor(Math.random() * randomColors.length)]) + .setFooter(footerQuote); + if (author?.displayColor === 16777215) { + emb.setColor(16777214); + } + } catch (e) { + return errLog(e, msg, client, false, "", false); + } + return emb; +} + +module.exports = { + multipleMembersFound, + findMemberRegEx, getUser, + getChannelMessage, errLog, + execCB, ranLog, noPerm, + trySend, tryDelete, tryReact, + sentAdCheck, defaultImageEmbed } \ No newline at end of file diff --git a/resources/getColor.js b/resources/getColor.js new file mode 100644 index 0000000..8714a79 --- /dev/null +++ b/resources/getColor.js @@ -0,0 +1,67 @@ +'use strict'; + +/** + * Get color by name + * @param {String | Number} name - Name of color | Hex | Number + * @returns {String | Number} Color hex | Color number + */ +module.exports = function getColor(name) { + if (typeof name === "string") { + name = name.toLowerCase(); + } + if (typeof name === 'number') { + if ( name === 16777215) { + return 16777214; + } else { + return name; + } + } + switch(name) { + case 'rosy brown': + return 12357519; + case 'magenta': + return 16711935; + case 'navy blue': + return 128; + case 'teal': + return 32896; + case 'ruby': + return 15277667; + case 'green': + return '00ff00'; + case 'red': + return 'ff0000'; + case 'pink': + return 'ff94f2'; + case 'yellow': + return 'f1e40f'; + case 'orange': + return 'ff8c00'; + case 'brown': + return 'a0522d'; + case 'blue': + return 3447003; + case 'light blue': + return '0fffff'; + case 'cyan': + return '0fffff'; + case 'purple': + return '803c9d'; + case 'peach': + return 'faa775'; + case 'black': + return '000000'; + case 'white': + return 16777214; + default: { + if (/\D/.test(name)) { + if (name.startsWith("#")) { + name = name.slice(1); + } + return name; + } else { + return parseInt(name, 10); + } + } + } +} \ No newline at end of file diff --git a/resources/scheduler.js b/resources/scheduler.js new file mode 100644 index 0000000..513d64a --- /dev/null +++ b/resources/scheduler.js @@ -0,0 +1,10 @@ +'use strict'; + +const bree = require("bree"); +const cabin = require("cabin"); + +module.exports.schedule = new bree({ + // logger: new cabin(), + root: false, + workerMessageHandler: () => console.log +}); \ No newline at end of file diff --git a/resources/shaChat.js b/resources/shaChat.js new file mode 100644 index 0000000..a610a43 --- /dev/null +++ b/resources/shaChat.js @@ -0,0 +1,114 @@ +'use strict'; + + +const puppeteer = require('puppeteer'); +const { trySend, errLog, ranLog, noPerm } = require('./functions'); +const Commando = require("@iceprod/discord.js-commando"); +require("discord.js"); + +//'4, 15, 10, 11, 14, 17, 18' + +const URL = [ + 'https://rebot.me/simsimi', 'https://rebot.me/ryuko-matoi',//1 + 'https://rebot.me/xmonikax', 'https://rebot.me/futa-nun',//3 + 'https://rebot.me/shinku-rozen', 'https://rebot.me/alessandro-magrini',//5 + 'https://rebot.me/cassie-87', 'https://rebot.me/agatha-14',//7 + 'https://rebot.me/himiko-toga-1', 'https://rebot.me/your-girlfriend-sister-slut',//9 + 'https://rebot.me/muffin-6', 'https://rebot.me/paris-1',//11 + 'https://rebot.me/song-answers', 'https://rebot.me/loretta-martin',//13 + 'https://rebot.me/zozo', 'https://rebot.me/sn0w',//15 + 'https://rebot.me/cinnamonwolf', 'https://rebot.me/saori-8',//17 + 'https://rebot.me/zacharie-1', 'https://rebot.me/natsuki-41',//19 + 'https://rebot.me/lea-7062078', 'https://rebot.me/bunny-exe',//21 + 'https://rebot.me/just-monika-56' +]; +const browser = puppeteer.launch({headless:false}); +const page1 = browser.then(r => r.newPage()); +page1.then(r => r.goto(URL[10]).catch(console.error)); + +/** + * Chat with Shasha + * @param {Commando.Client} client - (this.client) + * @param {Number} index - Index of answer + * @param {Commando.Message} question - Message object + * @returns {Promise} Reply + */ +async function shaChat(client, index, question) { + if (page1) { + let query = question.content.trim(); + if (query.toLowerCase().startsWith(client.commandPrefix+"chat")) { + query = query.slice((client.commandPrefix+"chat").length).trim(); + } + try { + const page = await page1; + console.log("New chat query: "+query); + await page.waitForSelector("input[id=\"question\"]"); + await page.type("input[id=\"question\"]", query); + await page.keyboard.press("Enter"); + await page.waitForSelector(`#answer > div:nth-child(${index})`, {timeout:5000}).catch(() => {}); + return getAnswerIndex(page, index); + } catch (error) { + throw error; + } + } +} + +/** + * @param {puppeteer.Page} page + * @param {Number} index + * @returns {String} + */ +async function getAnswerIndex(page, index) { + try { + const result = await page.evaluate((index) => { + const res = document.querySelector(`#answer > div:nth-child(${index})`).childNodes[4].textContent; + return res; + }, index); + return result; + } catch (e) { + throw e; + } +} + + +let chatIndex = 3; + + + /** + * @param {Commando.Client} client + * @param {Commando.CommandoMessage} message + * @returns + */ + async function chatAnswer(client, message) { + console.log(message.content); + console.log(chatIndex); + if (message.content.trim().length === 0) { + return + } else { + try { + message.channel.startTyping(); + await shaChat(client, chatIndex, message).then(async answer => { + chatIndex += 2; + if (message.channel.lastMessage.author === client.user && answer?.trim() === message.channel.lastMessage.content.trim()) { + return trySend(client, message, "Please speak one by one, I'm overwhelmed <:catstareLife:794930503076675584>"); + } else { + trySend(client, message, answer.trim()).then(() => { + message.channel.stopTyping(); + }).catch(e => { + noPerm(message); + message.channel.stopTyping(); + }); + } + return ranLog(message, message.content.trim(), answer); + }).catch(e => { + noPerm(message); + message.channel.stopTyping(); + }); + } catch (e) { + noPerm(message); + message.channel.stopTyping(); + } + } + } + + module.exports = { shaChat, chatAnswer } \ No newline at end of file diff --git a/settings.sqlite3 b/settings.sqlite3 new file mode 100644 index 0000000..313b603 Binary files /dev/null and b/settings.sqlite3 differ