mirror of
https://github.com/danbulant/Shasha
synced 2026-05-24 20:31:47 +00:00
501 lines
No EOL
16 KiB
JavaScript
501 lines
No EOL
16 KiB
JavaScript
'use strict';
|
||
|
||
const { MessageEmbed, Message, GuildMember, User, Client, GuildChannel, Role, MessageOptions, TextChannel, DMChannel, Guild, Channel } = require('discord.js');
|
||
const { defaultErrorLogChannel, ranLogger } = require("../config.json");
|
||
const { database } = require("../database/mongo");
|
||
const { timestampAt } = require('./debug');
|
||
const getColor = require('./getColor');
|
||
const { randomColors } = require("../config.json");
|
||
const { CommandoMessage, CommandoClient } = require('@iceprod/discord.js-commando');
|
||
|
||
/**
|
||
* Log an error. Second or third argument is required
|
||
* @param {Error} theError - Catched error (error)
|
||
* @param {CommandoMessage} 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) {
|
||
if (!(theError instanceof Error) || !(msg ?? client)) return console.error("[ERRLOG] Not error instance or no required param.\n", theError);
|
||
let [logThis, inLogChannel, sendErr] = ['', '', ''];
|
||
if (msg) {
|
||
logThis = `\`${msg.command?.name}\` (${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`;
|
||
if (errorMessage) {
|
||
if (errorMessage.length > 0) {
|
||
sendErr = sendErr + errorMessage+'\n';
|
||
inLogChannel = errorMessage+'\n';
|
||
}
|
||
}
|
||
if (sendTheError) {
|
||
sendErr = sendErr+'```js\n'+theError.stack+'```';
|
||
}
|
||
if (notify || !client) {
|
||
msg.channel.send(sendErr.trim(),{split:true}).catch(noPerm(msg));
|
||
}
|
||
}
|
||
if (client) {
|
||
try {
|
||
inLogChannel = inLogChannel+'```js\n'+theError.stack+'```';
|
||
const sendAt = client.channels.cache.get(defaultErrorLogChannel);
|
||
sendAt.send(logThis + inLogChannel.trim() + timestampAt(client),{split:{maxLength:4000,char: "\n",append:'```',prepend:'```js\n'}});
|
||
} catch (errmes) {
|
||
console.error(errmes);
|
||
}
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Get message object from the message channel or provided channel
|
||
* @param {Message} msg - Message object (msg)
|
||
* @param {String} MainID - Message ID | Channel ID | Channel Mention
|
||
* @param {String} SecondID - Message ID
|
||
* @returns {Promise<Message>} Message object | undefined
|
||
*/
|
||
async function getChannelMessage(msg, MainID, SecondID) {
|
||
if (!MainID || !msg) {
|
||
return;
|
||
}
|
||
if (/\//.test(MainID)) {
|
||
const splitURL = MainID.split(/\/+/);
|
||
SecondID = splitURL[splitURL.length-1];
|
||
MainID = splitURL[splitURL.length-2];
|
||
}
|
||
MainID = cleanMentionID(MainID);
|
||
if (SecondID && !/\D/.test(SecondID)) {
|
||
try {
|
||
const meschannel = msg.client.channels.cache.get(MainID);
|
||
return meschannel.messages.fetch(SecondID);
|
||
} catch (theError) {
|
||
return
|
||
}
|
||
} else {
|
||
return msg.channel.messages.fetch(MainID).catch(() => {});
|
||
}
|
||
}
|
||
|
||
function execCB(error, stdout, stderr) {
|
||
if (error) {
|
||
console.error(error);
|
||
return errLog(error);
|
||
}
|
||
console.log('stdout:\n'+stdout);
|
||
console.log('stderr:\n'+stderr);
|
||
}
|
||
|
||
/**
|
||
* Command usage logger
|
||
* @param {CommandoMessage} msg
|
||
* @param {String} addition
|
||
*/
|
||
async function ranLog(msg, addition) {
|
||
const channel = msg.client.channels.cache.get(ranLogger),
|
||
ifCode = addition.startsWith("```") && addition.endsWith("```");
|
||
const addSplit = splitOnLength((addition.substr(ifCode ? 2045 : 2049)).split(","), 1010, ",");
|
||
const embed = await defaultImageEmbed(msg, null, msg.command.name.toLocaleUpperCase() + ` (${msg.id})`);
|
||
embed.setAuthor(msg.author.tag + ` (${msg.author.id})`, msg.author.displayAvatarURL({format: "png", size: 4096, dynamic: true}))
|
||
.setURL(msg.url)
|
||
.setDescription(addition.slice(0, ifCode && addSplit[0]?.[0].length > 0 ? 2044 : 2048) + (ifCode && addSplit[0]?.[0].length > 0 ? "```" : ""));
|
||
if (addSplit[0]?.[0].length > 0) for (const add of addSplit) embed.addField("", "```js\n" + add.join(",") + (embed.fields.length < (addSplit.length - 1) ? ",```" : ""));
|
||
embed.setFooter(timestampAt(msg.client), msg.guild?.iconURL({"size": 4096, "dynamic": true}));
|
||
if (msg.guild) embed.addField("Guild", `\`${msg.guild?.name}\`\n(${msg.guild?.id})`, true);
|
||
embed.addField("Channel", (msg.guild ? `<#${msg.channel.id}>\n\`${msg.channel?.name}\`` : `**DM**\n\`${msg.channel.recipient.tag}\``) + `\n(${msg.channel.id})`, true)
|
||
.addField("User", `<@!${msg.author.id}>`, true);
|
||
trySend(msg.client, channel, {embed: embed});
|
||
}
|
||
|
||
/**
|
||
* Notify when more than one member found when looking in the member list
|
||
* @param {Message} msg - Message object
|
||
* @param {GuildMember[]} arr - Test array
|
||
* @param {String} key - Keyword
|
||
* @param {Number} max - Max length
|
||
* @param {Boolean} withID - Include user_ID
|
||
* @returns {String}
|
||
*/
|
||
function multipleMembersFound(msg, arr, key, max = 4, withID) {
|
||
if (msg && arr.length > 0) {
|
||
try {
|
||
let multipleFound = [];
|
||
for(const one of arr) {
|
||
const user = one.user ?? one;
|
||
let mes = user.tag;
|
||
if (withID) {
|
||
mes = mes + ` (${user.id})`;
|
||
}
|
||
multipleFound.push(mes);
|
||
}
|
||
let multi = [];
|
||
for(const mu of multipleFound) {
|
||
if (multipleFound.indexOf(mu) < max) {
|
||
multi.push(mu);
|
||
}
|
||
}
|
||
let mes = multi.join(",\n' ");
|
||
if (multipleFound.length > max) {
|
||
mes = mes+`,\n' ${multipleFound.length - max} more...`;
|
||
}
|
||
return `Multiple members found for: **${key}**\`\`\`js\n' ${mes}\`\`\``;
|
||
} catch (e) {
|
||
errLog(e, msg, msg.client);
|
||
}
|
||
} else {
|
||
return '';
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Get member object with RegExp
|
||
* @param {Message | GuildMember} msg Object of the guild being searched
|
||
* @param {String} name Keyword
|
||
* @returns {GuildMember[]} Member object found
|
||
*/
|
||
function findMemberRegEx(msg, name) {
|
||
if (!(msg ?? name)) {
|
||
return;
|
||
}
|
||
const re = new RegExp(name, "i");
|
||
return msg.guild?.members.cache.array().filter(r => re.test(r.displayName) || re.test(r.user.tag));
|
||
}
|
||
|
||
/**
|
||
* React when it try something but fail because has not enough perms
|
||
* @param {Message} msg
|
||
*/
|
||
function noPerm(msg) {
|
||
if (!msg) {
|
||
return;
|
||
}
|
||
msg.react("sadduLife:797107817001386025").catch(() => {});
|
||
}
|
||
|
||
/**
|
||
* Send message
|
||
* @param {CommandoClient} client - (this.client)
|
||
* @param {Message | String | TextChannel | DMChannel} msgOrChannel Message object | channel_ID
|
||
* @param {MessageOptions} content - ({content:content,optionblabla})
|
||
* @param {Boolean} adCheck - Check source for Discord invite link (true)
|
||
* @returns {Promise<Message>} Sent message object
|
||
*/
|
||
async function trySend(client, msgOrChannel, content, adCheck = true) {
|
||
/*if (content instanceof MessageEmbed) {
|
||
let fLength = [];
|
||
for (const f of content.fields) {
|
||
fLength.push(f.value.length);
|
||
}
|
||
console.log("Embed", content.title, timestampAt(client), "\n", content.description, content.description?.length, "\n", content.fields, fLength)
|
||
}*/
|
||
if (!client || !msgOrChannel) {
|
||
return;
|
||
}
|
||
if (typeof msgOrChannel === "string") {
|
||
msgOrChannel = client.channels.cache.get(msgOrChannel);
|
||
};
|
||
msgOrChannel.channel?.startTyping() || msgOrChannel.startTyping();
|
||
if (client.owners.includes(msgOrChannel.author)) {
|
||
adCheck = false;
|
||
if (content.disableMentions) {
|
||
content.disableMentions = "none";
|
||
}
|
||
}
|
||
if (adCheck) {
|
||
if (content.content) {
|
||
content.content = sentAdCheck(content.content);
|
||
} else {
|
||
if (typeof content === "string") {
|
||
content = sentAdCheck(content);
|
||
}
|
||
}
|
||
}
|
||
if (msgOrChannel instanceof Message) {
|
||
const ret = await msgOrChannel.channel.send(content).catch(() => {
|
||
noPerm(msgOrChannel);
|
||
msgOrChannel.channel.stopTyping();
|
||
});
|
||
msgOrChannel.channel.stopTyping();
|
||
return ret;
|
||
} else {
|
||
if ((msgOrChannel instanceof TextChannel) || (msgOrChannel instanceof DMChannel)) {
|
||
const ret = await msgOrChannel.send(content).catch((e) => {
|
||
errLog(e, null, client);
|
||
msgOrChannel.stopTyping();
|
||
});
|
||
msgOrChannel.stopTyping();
|
||
return ret;
|
||
} else {
|
||
errLog(e, null, client, false, "[TRYSEND] Invalid {msgOrChannel} type.```js\n" + JSON.stringify(msgOrChannel, (k, v) => v ?? undefined, 2) + "```");
|
||
}
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Delete message
|
||
* @param {Message} msg - Message to delete (msg)
|
||
*/
|
||
function tryDelete(msg) {
|
||
if (!msg) {
|
||
return;
|
||
}
|
||
msg.delete().catch(e => {throw e});
|
||
}
|
||
|
||
/**
|
||
* React message
|
||
* @param {Message} msg - Message to react (msg)
|
||
* @param {String} reaction - Emote ("name:ID")
|
||
*/
|
||
function tryReact(msg, reaction) {
|
||
if (!msg || reaction.length === 0) {
|
||
return;
|
||
}
|
||
msg.react(reaction).catch(e => {throw e});
|
||
}
|
||
|
||
/**
|
||
* Check message's content for ads
|
||
* @param {String} content - Content to check
|
||
*/
|
||
function sentAdCheck(content) {
|
||
if (content.length > 5) {
|
||
if (/(https:\/\/)?(www\.)?discord\.gg\/(?:\w{2,15}(?!\w)(?= *))/.test(content)) {
|
||
content = content.replace(/(https:\/\/)?(www\.)?discord\.gg\/(?:\w{2,15}(?!\w)(?= *))/, '`Some invite link goes here`');
|
||
}
|
||
}
|
||
return content;
|
||
}
|
||
|
||
/**
|
||
* Make default image embed
|
||
* @param {Message | GuildMember} msg
|
||
* @param {String} image
|
||
* @param {GuildMember | User} author
|
||
* @param {String} title
|
||
* @param {String} footerQuote
|
||
* @returns {Promise<MessageEmbed>}
|
||
*/
|
||
async function defaultImageEmbed(msg, image, title, footerQuote) {
|
||
if (!footerQuote) {
|
||
const r = await database.collection(msg.guild ? "Guild" : "User").findOne({document: msg.guild?.id ?? msg.author?.id}).catch(() => {});
|
||
footerQuote = r?.["settings"]?.defaultEmbed?.footerQuote || "";
|
||
}
|
||
return new MessageEmbed()
|
||
.setTitle(title)
|
||
.setImage(image)
|
||
.setColor(msg.guild ? getColor(msg.member?.displayColor) : randomColors[Math.floor(Math.random() * randomColors.length)])
|
||
.setFooter(footerQuote);
|
||
}
|
||
|
||
/**
|
||
* Return clean ID of provided key
|
||
* @param {String} key - Mention | Channel Name | Username | Rolename
|
||
* @returns {String} Clean ID
|
||
*/
|
||
function cleanMentionID(key) {
|
||
if (!key) {
|
||
return;
|
||
}
|
||
let uID = key.trim();
|
||
if (!/\D/.test(uID)) {
|
||
return uID;
|
||
}
|
||
if (uID.startsWith('<@') || uID.startsWith('<#')) {
|
||
uID = uID.slice(2);
|
||
}
|
||
if (uID.endsWith('>')) {
|
||
uID = uID.slice(0,-1)
|
||
}
|
||
if (uID.startsWith('!') || uID.startsWith("@") || uID.startsWith("#") || uID.startsWith('&')) {
|
||
uID = uID.slice(1);
|
||
}
|
||
return uID;
|
||
}
|
||
|
||
/**
|
||
* Get channel object wit RegExp
|
||
* @param {Message | GuildMember} msg Object of the guild being searched
|
||
* @param {String} name Keyword
|
||
* @param {ChannelType[]} exclude Exclude channel type
|
||
* @returns {GuildChannel[]} Channels object found
|
||
*/
|
||
function findChannelRegEx(msg, name, exclude) {
|
||
const re = new RegExp(name, "i");
|
||
return msg.guild?.channels.cache.array().filter(r => {
|
||
if (exclude?.includes(r.type)) {
|
||
return false;
|
||
} else {
|
||
return re.test(r.name);
|
||
}
|
||
});
|
||
}
|
||
|
||
/**
|
||
* Get role object with RegExp
|
||
* @param {Message | GuildMember} msg Object of the guild being searched
|
||
* @param {String} name Keyword
|
||
* @returns {Role[]} Roles object found
|
||
*/
|
||
function findRoleRegEx(msg, name) {
|
||
const re = new RegExp(name, "i");
|
||
return msg.guild?.roles.cache.array().filter(r => re.test(r.name));
|
||
}
|
||
|
||
/**
|
||
* Notify when more than one channel found when looking in the channel list
|
||
* @param {Message} msg - Message object
|
||
* @param {GuildChannel[]} arr - Test array
|
||
* @param {String} key - Keyword
|
||
* @param {Number} max - Max length
|
||
* @param {Boolean} withID - Include channel_ID
|
||
* @returns {String}
|
||
*/
|
||
function multipleChannelsFound(msg, arr, key, max = 4, withID) {
|
||
if (arr.length > 0) {
|
||
try {
|
||
let multipleFound = [];
|
||
for(const one of arr) {
|
||
let mes = one.name;
|
||
if (withID) {
|
||
mes = mes + ` (${one.id})`;
|
||
}
|
||
multipleFound.push(mes);
|
||
}
|
||
let multi = [];
|
||
for(const mu of multipleFound) {
|
||
if (multipleFound.indexOf(mu) < max) {
|
||
multi.push(mu);
|
||
}
|
||
}
|
||
let mes = multi.join(",\n' ");
|
||
if (multipleFound.length > max) {
|
||
mes = mes+`,\n' ${multipleFound.length - max} more...`;
|
||
}
|
||
return `Multiple channels found for: **${key}**\`\`\`js\n' ${mes}\`\`\``;
|
||
} catch (e) {
|
||
errLog(e, msg, msg.client);
|
||
}
|
||
} else {
|
||
return '';
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Notify when more than one role found when looking in the role list
|
||
* @param {Message} msg - Message object
|
||
* @param {Role[]} arr - Test array
|
||
* @param {String} key - Keyword
|
||
* @param {Number} max - Max length
|
||
* @param {Boolean} withID - Include role_ID
|
||
* @returns {String}
|
||
*/
|
||
function multipleRolesFound(msg, arr, key, max = 4, withID) {
|
||
if (arr.length > 0) {
|
||
try {
|
||
let multipleFound = [];
|
||
for(const one of arr) {
|
||
let mes = one.name;
|
||
if (withID) {
|
||
mes = mes + ` (${one.id})`;
|
||
}
|
||
multipleFound.push(mes);
|
||
}
|
||
let multi = [];
|
||
for(const mu of multipleFound) {
|
||
if (multipleFound.indexOf(mu) < max) {
|
||
multi.push(mu);
|
||
}
|
||
}
|
||
let mes = multi.join(",\n' ");
|
||
if (multipleFound.length > max) {
|
||
mes = mes+`,\n' ${multipleFound.length - max} more...`;
|
||
}
|
||
return `Multiple roles found for: **${key}**\`\`\`js\n' ${mes}\`\`\``;
|
||
} catch (e) {
|
||
errLog(e, msg, msg.client);
|
||
}
|
||
} else {
|
||
return '';
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Standard
|
||
* @param {Message} msg - Message object
|
||
* @param {String} key - Channel ID | Mention | Name
|
||
* @returns {GuildChannel | Channel} Channel object
|
||
*/
|
||
function getChannelProchedure(msg, key) {
|
||
if (key.length === 0) {
|
||
return;
|
||
}
|
||
const search = cleanMentionID(key);
|
||
if (search.length === 0) {
|
||
return;
|
||
}
|
||
let channel;
|
||
if (/^\d{17,19}$/.test(search)) {
|
||
channel = msg.guild?.channels.cache.get(search);
|
||
if (!channel && msg.client.owners.includes(msg.author)) {
|
||
channel = msg.client.channels.cache.get(search);
|
||
}
|
||
}
|
||
if (!channel) {
|
||
channel = findChannelRegEx(msg, search, ["category", "voice"])?.[0];
|
||
}
|
||
return channel;
|
||
}
|
||
/**
|
||
* Compare 2 different timestamp
|
||
* @param {Number} compare - Number to compare
|
||
* @returns {Number} Result
|
||
*/
|
||
function getUTCComparison(compare) {
|
||
return compare - new Date().valueOf();
|
||
}
|
||
/**
|
||
* Make guild's event log embed with author and footer
|
||
* @param {Guild} guild
|
||
* @returns {MessageEmbed}
|
||
*/
|
||
function defaultEventLogEmbed(guild) {
|
||
return new MessageEmbed()
|
||
.setAuthor(guild.name, guild.iconURL({size:4096, dynamic: true}))
|
||
.setFooter((guild.defaultEmbed?.footerQuote ? guild.defaultEmbed.footerQuote + "・" : "") + new Date(new Date().valueOf() + (guild.client.matchTimestamp ?? 0)).toUTCString().slice(0, -4));
|
||
}
|
||
|
||
/**
|
||
* Split on Length
|
||
* @param {Array<String>} arr
|
||
* @param {Number} maxLength - Max character length per split
|
||
* @param {String} joiner
|
||
* @returns {Array<String[]>}
|
||
*/
|
||
function splitOnLength(arr, maxLength, joiner = "\n") {
|
||
let toField = [], i = 0, pushed = 0;
|
||
for (const res of arr) {
|
||
if (arr[pushed] && ((toField[i] ? toField[i].join(joiner) : "") + joiner + arr[pushed]).length > maxLength) {
|
||
i++;
|
||
} else {
|
||
pushed++;
|
||
}
|
||
if (!toField[i] || toField[i].length === 0) {
|
||
toField[i] = [];
|
||
}
|
||
toField[i].push(res);
|
||
if (!arr[pushed]) {
|
||
break;
|
||
}
|
||
}
|
||
return toField;
|
||
}
|
||
|
||
module.exports = {
|
||
cleanMentionID, defaultEventLogEmbed,
|
||
multipleMembersFound, multipleRolesFound, multipleChannelsFound,
|
||
findMemberRegEx, findChannelRegEx, findRoleRegEx,
|
||
getChannelMessage, errLog,
|
||
execCB, ranLog, noPerm, getUTCComparison,
|
||
trySend, tryDelete, tryReact,
|
||
sentAdCheck, defaultImageEmbed, getChannelProchedure,
|
||
splitOnLength
|
||
} |