Unfinished, do not pull.

This commit is contained in:
Kwafuri
2024-03-21 07:12:04 +00:00
parent 98c7f46085
commit 44a55219d9
16 changed files with 1315 additions and 194 deletions
+219
View File
@@ -0,0 +1,219 @@
const { EmbedBuilder, ApplicationCommandType, AttachmentBuilder } = require('discord.js');
const { dangercordcheckVerification } = require('../../functions/dangerCordUtils.js');
const { createCanvas } = require('canvas');
const cfg = require('../../config/config.js');
module.exports = {
name: 'verify',
description: 'To verify yourself.',
type: ApplicationCommandType.ChatInput,
run: async (client, interaction) => {
await interaction.deferReply({ ephemeral: true });
const guildId = interaction.guild.id;
async function generateCaptcha(count) {
// Setting up the variables for generation
var upchars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
var lowchars = "abcdefghijklmnopqrstuvwxyz";
var nums = "0123456789";
var captcha = "";
for (var i = 0; i < count; i++) {
var c = Math.floor(Math.random() * 3);
if (c === 0) {
captcha += upchars.charAt(Math.floor(Math.random() * upchars.length));
} else if (c === 1) {
captcha += lowchars.charAt(Math.floor(Math.random() * lowchars.length));
} else {
captcha += nums.charAt(Math.floor(Math.random() * nums.length));
}
}
return captcha;
}
async function createCaptcha(captcha) {
const canvas = createCanvas(200, 75);
const ctx = canvas.getContext('2d');
// Background color
ctx.fillStyle = '#000000';
ctx.fillRect(0, 0, canvas.width, canvas.height);
// Text
ctx.font = '30px Arial';
ctx.textBaseline = 'middle';
ctx.textAlign = 'center';
ctx.fillStyle = '#ffffff';
// Calculate spacing between characters to fit within canvas width
const charWidth = 30;
const totalWidth = captcha.length * charWidth;
const startX = (canvas.width - totalWidth) / 2;
// Draw each character with random y-offset
for (let i = 0; i < captcha.length; i++) {
const x = startX + i * charWidth + charWidth / 2; // Adjust x-position for each character
const y = 37.5 + Math.random() * 30 - 15; // Random y-offset
ctx.fillText(captcha[i], x, y);
}
// Adding noise
for (let i = 0; i < 50; i++) {
ctx.fillStyle = `rgba(255,255,255,${Math.random() * 0.9})`;
ctx.fillRect(Math.random() * canvas.width, Math.random() * canvas.height, 1, 1);
}
// Adding white lines
for (let i = 0; i < 30; i++) {
ctx.strokeStyle = `rgba(255,255,255,${Math.random() * 1.8})`;
ctx.beginPath();
ctx.moveTo(Math.random() * canvas.width, Math.random() * canvas.height);
ctx.lineTo(Math.random() * canvas.width, Math.random() * canvas.height);
ctx.stroke();
}
// Adding blue lines
for (let i = 0; i < 30; i++) {
ctx.strokeStyle = `rgba(0, 0, 255, ${Math.random() * 0.9})`;
ctx.beginPath();
ctx.moveTo(Math.random() * canvas.width, Math.random() * canvas.height);
ctx.lineTo(Math.random() * canvas.width, Math.random() * canvas.height);
ctx.stroke();
}
// Rotation
const rotation = Math.random() * 0.2 - 0.1; // Random rotation between -0.1 and 0.1 radians
ctx.translate(canvas.width / 2, canvas.height / 2);
ctx.rotate(rotation);
ctx.translate(-canvas.width / 2, -canvas.height / 2);
const buffer = canvas.toBuffer('image/png');
return buffer;
}
if (interaction.channel.id !== cfg.guilds[guildId].channelId) {
return interaction.editReply({ content: cfg.messages.wrongChannel, ephemeral: true })
}
if (await client.db.get(`settings-${guildId}.verification`) === false) {
return interaction.editReply({ content: cfg.messages.verifyDisabled, ephemeral: true })
}
if (interaction.member.roles.cache.has(cfg.guilds[guildId].roleToAdd)) {
return interaction.editReply({ content: cfg.messages.alreadyVerified, ephemeral: true })
}
let attempts = 0;
let blacklisted = false;
const maxAttempts = cfg.maxAttempts;
let validated = false; // Flag to track if user is already validated
async function verifyUser(captcha) {
attempts++;
if (attempts > maxAttempts) {
await interaction.user.send(cfg.messages.maxAttemptsExceeded);
return;
}
if (attempts == 1) {
// Check the Dangercord API using our custom library function
await dangercordcheckVerification(interaction.user.id)
.then(result => {
console.log('Verification result:', result);
if (result === 1) {
console.log(`${interaction.user.id} is not blacklisted at dangercord, lets continue with the verification process`)
// Proceed with further actions
} else {
console.log(`${interaction.user.id} is blacklisted at dangercord, we will not continue with the verification process`);
blacklisted = true; // Set blacklisted to true
// Handle the case when user is blacklisted
interaction.editReply({
content: cfg.messages.cannotContinue,
ephemeral: true
}).then(() => {
// Stop execution here
return;
}).catch(error => {
console.error('Error:', error);
});
}
})
.catch(error => {
console.error('Error:', error);
});
}
if (blacklisted == true) return
const buff = await createCaptcha(captcha);
const attachment = new AttachmentBuilder(buff, { name: 'captcha.png' });
const embed = new EmbedBuilder()
.setTitle("Captcha Verification")
.setDescription(`Please type the captcha below. You have ${cfg.captchaTimeout / 1000} seconds to respond. Attempt ${attempts}/${maxAttempts}`)
.setColor('Green')
.setImage('attachment://captcha.png')
.setTimestamp();
await interaction.user.send({
embeds: [embed], files: [attachment]
}).then(async (msg) => {
await interaction.editReply({ content: cfg.messages.toVerify, ephemeral: true });
const filter = m => !m.author.bot && m.author.id === interaction.user.id && m.channel.id === msg.channel.id;
const collector = msg.channel.createMessageCollector({
filter,
time: cfg.captchaTimeout
});
collector.on('collect', async (m) => {
//if member left the server before the verification is done
if (!interaction.guild.members.cache.has(interaction.user.id)) {
await interaction.user.send(cfg.messages.userLeft);
collector.stop();
} else {
if (m.content === captcha) {
validated = true;
await interaction.member.roles.add(cfg.guilds[guildId].roleToAdd);
await interaction.member.roles.remove(cfg.guilds[guildId].firstRole);
await interaction.user.send(cfg.messages.success);
return
} else {
// If the content doesn't match captcha
collector.stop();
}
}
});
collector.on('end', async (collected, reason) => {
if (reason === 'time' && !validated) { // Send timeout message only if not already validated
await interaction.user.send(cfg.messages.tookTooLong);
} else if (!validated) {
// Only generate a new captcha and attempt verification if attempts < maxAttempts
const newCaptcha = await generateCaptcha(cfg.captchaCount);
await verifyUser(newCaptcha);
}
});
}).catch(async (err) => {
await interaction.editReply({
content: cfg.messages.unableToDM,
ephemeral: true
});
});
}
const captcha = await generateCaptcha(cfg.captchaCount);
verifyUser(captcha);
}
}
/*
Note from Naoki:
This looks simple enough but damn.
Canvas is driving me nuts due to how hard to work with it.
Had to StackOverFlow-ed it to get it to work.
I'm not sure if this is the best way to do it but it works.
Big shoutout to StackOverFlow and the people I got the code from.
*/
+15
View File
@@ -0,0 +1,15 @@
const cron = require('node-cron');
async function cronjob() {
cron.schedule('* * * * * *', async () => {
//run every .js file in the crons folder
const fs = require('fs');
const path = require('path');
const normalizedPath = path.join(__dirname, './crons');
fs.readdirSync(normalizedPath).forEach((file) => {
require(`./crons/${file}`);
});
});
}
module.exports = cronjob;
@@ -0,0 +1,34 @@
const cron = require('node-cron');
const client = require('..');
const ModerationAction = require('../database/models/ModerationAction');
const ModerationUtils = require('../database/models/ModerationUtils');
cron.schedule('* * * * * *', async () => {
const guilds = await ModerationAction.find({});
for (const guild of guilds) {
const warns = await ModerationUtils.find({ guildId: guild.guildId });
//check if user is in the guild
const guild = client.guilds.cache.get(guild.guildId);
if (!guild) return;
for (const warn of warns) {
const member = guild.members.cache.get(warn.userId);
if (!member) return;
switch (warn.warns) {
case guild.firstThreshold:
member.send(guild.firstAction);
break;
case guild.secondThreshold:
member.send(guild.secondAction);
break;
case guild.thirdThreshold:
member.send(guild.thirdAction);
break;
}
}
}
});
+17
View File
@@ -0,0 +1,17 @@
const mongoose = require('mongoose');
const { mongouri } = require('../config/config.js');
async function connect() {
mongoose.connect(mongouri.replace('<password>', process.env.MONGOPASS), {
useNewUrlParser: true,
useUnifiedTopology: true,
autoIndex: true,
});
mongoose.connection.once('open', () => {
console.log('Connection to the database has been established.');
});
return;
}
module.exports = connect;
+21
View File
@@ -0,0 +1,21 @@
const mongoose = require('mongoose');
const GuildSettings = new mongoose.Schema(
{
guildId: {
type: mongoose.SchemaTypes.String,
required: true,
unique: true
},
verifyEnabled: {
type: mongoose.SchemaTypes.String,
required: false
},
antiNewUserEnabled: {
type: mongoose.SchemaTypes.String,
required: false,
},
}
);
module.exports = mongoose.model('GuildSettings', GuildSettings);
+37
View File
@@ -0,0 +1,37 @@
const mongoose = require('mongoose');
const ModerationAction = new mongoose.Schema(
{
guildId: {
type: mongoose.SchemaTypes.String,
required: true,
unique: true
},
firstAction: {
type: mongoose.SchemaTypes.String,
required: true
},
secondAction: {
type: mongoose.SchemaTypes.String,
required: true
},
thirdAction: {
type: mongoose.SchemaTypes.String,
required: true
},
firstThreshold: {
type: mongoose.SchemaTypes.Number,
required: true
},
secondThreshold: {
type: mongoose.SchemaTypes.Number,
required: true
},
thirdThreshold: {
type: mongoose.SchemaTypes.Number,
required: true
}
}
);
module.exports = mongoose.model('ModerationAction', ModerationAction);
+48
View File
@@ -0,0 +1,48 @@
const mongoose = require('mongoose');
const ModerationUtils = new mongoose.Schema(
{
guildId: {
type: mongoose.SchemaTypes.String,
required: true,
unique: true
},
caseId: {
type: mongoose.SchemaTypes.Number,
required: true
},
target: {
type: mongoose.SchemaTypes.String,
required: true
},
moderator: {
type: mongoose.SchemaTypes.String,
required: true
},
reason: {
type: mongoose.SchemaTypes.String,
required: true
},
duration: {
type: mongoose.SchemaTypes.String,
required: true
},
action: {
type: mongoose.SchemaTypes.String,
required: true
},
proof: {
type: mongoose.SchemaTypes.String,
required: false
},
timestamp: {
type: mongoose.SchemaTypes.String,
required: true
},
alreadyPunished: {
type: mongoose.SchemaTypes.Boolean,
required: true
}
});
module.exports = mongoose.model('ModerationUtils', ModerationUtils);
+29
View File
@@ -0,0 +1,29 @@
const mongoose = require('mongoose');
const NewUserAction = new mongoose.Schema(
{
guildId: {
type: mongoose.SchemaTypes.String,
required: true,
unique: true
},
action: {
type: mongoose.SchemaTypes.String,
required: true,
},
reason: {
type: mongoose.SchemaTypes.String,
required: true,
},
threshold: {
type: mongoose.SchemaTypes.Number,
required: true,
},
duration: {
type: mongoose.SchemaTypes.String,
required: true,
},
}
);
module.exports = mongoose.model('NewUserAction', NewUserAction);
+29
View File
@@ -0,0 +1,29 @@
const mongoose = require('mongoose');
const VerifyFailAction = new mongoose.Schema(
{
guildId: {
type: mongoose.SchemaTypes.String,
required: true,
unique: true
},
action: {
type: mongoose.SchemaTypes.String,
required: true,
},
reason: {
type: mongoose.SchemaTypes.String,
required: true,
},
threshold: {
type: mongoose.SchemaTypes.Number,
required: true,
},
duration: {
type: mongoose.SchemaTypes.String,
required: true,
},
}
);
module.exports = mongoose.model('VerifyFailAction', VerifyFailAction);
+20 -8
View File
@@ -6,17 +6,29 @@ client.on('ready', () => {
console.log(chalk.green(`Logged in as ${client.user.tag}!`)); console.log(chalk.green(`Logged in as ${client.user.tag}!`));
setInterval(() => { setInterval(() => {
let activities = [ let activities = [
{ type: "Playing", name: "/verify" }, { type: "Playing", name: "with /verify command." },
{ type: "Playing", name: "Securing our servers" }, { type: "Playing", name: "whilst securing our servers." },
{ type: "Watching", name: `Watching bans...` }, { type: "Watching", name: `for the shooting stars.` },
{ type: "Watching", name: `over everyone.` },
{ type: "Watching", name: `for the storm that is approaching.` },
{ type: "Listening", name: "the sound of silence." },
{ type: "Listening", name: "the sound of the rain." },
{ type: "Listening", name: "the sound of the wind." }
]; ];
const status = activities[Math.floor(Math.random() * activities.length)]; const status = activities[Math.floor(Math.random() * activities.length)];
if (status.type === "Watching") { switch (status.type) {
client.user.setPresence({ activities: [{ name: `${status.name}`, type: ActivityType.Watching }] }); case "Playing":
} else { client.user.setPresence({ activities: [{ name: `${status.name}`, type: ActivityType.Playing }] });
client.user.setPresence({ activities: [{ name: `${status.name}`, type: ActivityType.Playing }] }); break;
case "Watching":
client.user.setPresence({ activities: [{ name: `${status.name}`, type: ActivityType.Watching }] });
break;
case "Listening":
client.user.setPresence({ activities: [{ name: `${status.name}`, type: ActivityType.Listening }] });
break;
} }
}, 300000); // update every 5 mins
}, 30000); // update every 5 mins
}); });
+115 -9
View File
@@ -1,19 +1,125 @@
const client = require('..'); const client = require('..');
const moment = require('moment');
const GuildSettings = require('../database/models/GuildSettings');
const NewUserAction = require('../database/models/NewUserAction');
const { convertActionDurationFromTimeToMs, getActionDuration, moderationUtilities } = require('./utils.js');
async function newUserKicker(member) { async function newUserKicker(member) {
const guildId = member.guild.id; const guildId = member.guild.id;
const db = await client.db.get(`settings-${guildId}`) || {}; const newUserAction = await NewUserAction.findOne({ guildId: guildId });
const threshold = db.newUserKickerThreshold; const guildSettings = await GuildSettings.findOne({ guildId: guildId });
const threshold = newUserAction.threshold;
const accountAge = (Date.now() - member.user.createdAt) / (1000 * 60 * 60 * 24); const accountAge = (Date.now() - member.user.createdAt) / (1000 * 60 * 60 * 24);
if (accountAge <= threshold) {
member.kick('Account age is less than the threshold.'); switch (guildSettings?.antiNewUserEnabled) {
console.log(`Account age of ${member.user.tag} is ${accountAge.toFixed(0)} day(s) which is lower than the threshold. Kicked.`); case 'true': {
return 0; // Not allowed to continue switch (accountAge) {
} else { case accountAge <= threshold: {
console.log(`Account age of ${member.user.tag} is ${accountAge.toFixed(0)} day(s) which is higher than the threshold. Not kicked.`); switch (newUserAction?.action) {
return 1; // Allowed to continue case 'kick': {
await moderationUtilities(
guildId,
member.user.id,
client.user.id,
'Account age is less than the threshold.',
'kick',
null
);
member.kick('Account age is less than the threshold.');
console.log(`Account age of ${member.user.tag} is ${accountAge.toFixed(0)} day(s) which is lower than the threshold. Kicked.`);
return 0; // Not allowed to continue
}
case 'ban': {
switch (newUserAction?.duration) {
case 0: {
await moderationUtilities(
guildId,
member.user.id,
client.user.id,
'Account age is less than the threshold.',
'ban',
'Permanent'
);
member.ban({ reason: 'Account age is less than the threshold.' });
console.log(`Account age of ${member.user.tag} is ${accountAge.toFixed(0)} day(s) which is lower than the threshold. Banned.`);
return 0; // Not allowed to continue
}
default: {
await moderationUtilities(
guildId,
member.user.id,
client.user.id,
'Account age is less than the threshold.',
'ban',
await getActionDuration(newUserAction?.duration)
);
member.ban({ days: await convertActionDurationFromTimeToMs(newUserAction?.duration), reason: 'Account age is less than the threshold.' });
console.log(`Account age of ${member.user.tag} is ${accountAge.toFixed(0)} day(s) which is lower than the threshold. Banned.`);
return 0; // Not allowed to continue
}
}
}
case 'timeout': {
switch (newUserAction?.duration) {
case 0: {
await moderationUtilities(
guildId,
member.user.id,
client.user.id,
'Account age is less than the threshold.',
'timeout',
await getActionDuration('5m')
);
member.timeout({ timeout: await convertActionDurationFromTimeToMs('5m'), reason: 'Account age is less than the threshold.' });
console.log(`Account age of ${member.user.tag} is ${accountAge.toFixed(0)} day(s) which is lower than the threshold. Timed out.`);
return 0; // Not allowed to continue
}
default: {
await moderationUtilities(
guildId,
member.user.id,
client.user.id,
'Account age is less than the threshold.',
'timeout',
await getActionDuration(newUserAction?.duration)
);
member.timeout({ timeout: await convertActionDurationFromTimeToMs(newUserAction?.duration), reason: 'Account age is less than the threshold.' });
console.log(`Account age of ${member.user.tag} is ${accountAge.toFixed(0)} day(s) which is lower than the threshold. Timed out.`);
return 0; // Not allowed to continue
}
break;
}
}
case 'warn': {
await moderationUtilities(
guildId,
member.user.id,
client.user.id,
newUserAction?.reason || 'Account age is less than the threshold.',
'warn',
null
);
console.log(`Account age of ${member.user.tag} is ${accountAge.toFixed(0)} day(s) which is lower than the threshold. Warned.`);
return 0; // Not allowed to continue
}
}
}
}
}
} }
/*
if (accountAge <= threshold) {
member.kick('Account age is less than the threshold.');
console.log(`Account age of ${member.user.tag} is ${accountAge.toFixed(0)} day(s) which is lower than the threshold. Kicked.`);
return 0; // Not allowed to continue
} else {
console.log(`Account age of ${member.user.tag} is ${accountAge.toFixed(0)} day(s) which is higher than the threshold. Not kicked.`);
return 1; // Allowed to continue
}
*/
} }
module.exports = { module.exports = {
+122
View File
@@ -0,0 +1,122 @@
const GuildSettings = require('../database/models/GuildSettings');
const NewUserAction = require('../database/models/NewUserAction');
const ModerationUtils = require('../database/models/ModerationUtils');
async function convertActionDurationFromTimeToMs(duration) {
if (!duration) return;
const matches = duration?.match(/^(\d+)([smhdwMy])$/);
const ms = [];
if (matches) {
const value = parseInt(matches[1]);
const unit = matches[2];
let milliseconds;
switch (unit) {
case 'ms':
milliseconds = value;
ms.push(milliseconds);
break;
case 's':
milliseconds = value * 1000;
ms.push(milliseconds);
break;
case 'm':
milliseconds = value * 60 * 1000;
ms.push(milliseconds);
break;
case 'h':
milliseconds = value * 60 * 60 * 1000;
ms.push(milliseconds);
break;
case 'd':
milliseconds = value * 24 * 60 * 60 * 1000;
ms.push(milliseconds);
break;
case 'w':
milliseconds = value * 7 * 24 * 60 * 60 * 1000;
ms.push(milliseconds);
break;
case 'M':
milliseconds = value * 30 * 24 * 60 * 60 * 1000;
ms.push(milliseconds);
break;
case 'y':
milliseconds = value * 365 * 24 * 60 * 60 * 1000;
ms.push(milliseconds);
break;
default:
return 'Invalid duration';
}
}
const sum = ms.reduce((a, b) => a + b, 0);
return sum;
}
async function getActionDuration(duration) {
if (!duration) return;
const matches = duration?.matchAll(/(\d+)([smhdwMy])/g);
const humanizedDuration = [];
for (const match of matches) {
const value = parseInt(match[1]);
const unit = match[2];
switch (unit) {
case 'ms':
humanizedDuration.push(value + ' milliseconds');
break;
case 's':
humanizedDuration.push(value + ' seconds');
break;
case 'm':
humanizedDuration.push(value + ' minutes');
break;
case 'h':
humanizedDuration.push(value + ' hours');
break;
case 'd':
humanizedDuration.push(value + ' days');
break;
case 'w':
humanizedDuration.push(value + ' weeks');
break;
case 'M':
humanizedDuration.push(value + ' months');
break;
case 'y':
humanizedDuration.push(value + ' years');
break;
default:
return 'Invalid duration';
}
}
return humanizedDuration.join(', and ');
}
async function moderationUtilities(guildId, target, moderator, reason, action, duration) {
const moderationUtils = await ModerationUtils.findOne({ guildId: guildId });
await moderationUtils.create({
guildId: guildId,
caseId: moderationUtils.caseId + 1 || 1,
target: target,
moderator: moderator,
reason: reason,
duration: duration,
action: action,
timestamp: Date.now()
});
await moderationUtils.save();
}
module.exports = {
convertActionDurationFromTimeToMs,
getActionDuration,
moderationUtilities
};
+5 -4
View File
@@ -1,5 +1,4 @@
const { Client, Collection, Permissions, GatewayIntentBits, Partials } = require('discord.js'); const { Client, Collection, Permissions, GatewayIntentBits, Partials } = require('discord.js');
const { QuickDB } = require('quick.db');
require('dotenv').config(); require('dotenv').config();
const client = new Client({ const client = new Client({
@@ -17,11 +16,13 @@ const client = new Client({
Partials.User, Partials.User,
] ]
}); });
const db = new QuickDB();
module.exports = client; module.exports = client;
client.db = db; (async () => {
require('./database/connect')();
require('./cron/cronjob')();
})();
client.commands = new Collection(); client.commands = new Collection();
client.aliases = new Collection(); client.aliases = new Collection();
client.slashCommands = new Collection(); client.slashCommands = new Collection();
+3
View File
@@ -21,6 +21,9 @@
"discord.js": "^14.14.1", "discord.js": "^14.14.1",
"dotenv": "^16.4.1", "dotenv": "^16.4.1",
"fs": "^0.0.1-security", "fs": "^0.0.1-security",
"moment": "^2.30.1",
"mongoose": "^6.7.0",
"node-cron": "^3.0.3",
"quick.db": "^9.1.7" "quick.db": "^9.1.7"
} }
} }
+437 -92
View File
@@ -1,5 +1,6 @@
const { ApplicationCommandType, ApplicationCommandOptionType, EmbedBuilder } = require('discord.js'); const { ApplicationCommandType, ApplicationCommandOptionType, EmbedBuilder } = require('discord.js');
const cfg = require('../../config/config.js'); const cfg = require('../../config/config.js');
const { getActionDuration, convertActionDurationFromMsToTime, convertActionDurationFromTimeToMs } = require('../../functions/utils.js');
module.exports = { module.exports = {
name: 'settings', name: 'settings',
@@ -7,14 +8,31 @@ module.exports = {
type: ApplicationCommandType.ChatInput, type: ApplicationCommandType.ChatInput,
options: [ options: [
{ {
name: 'verification', name: 'change',
description: 'Enable or disable, or change the settings of verification.', description: 'Set the settings of the bot.',
type: ApplicationCommandOptionType.Subcommand, type: ApplicationCommandOptionType.Subcommand,
options: [ options: [
{ {
name: 'enable-or-disable', name: 'set',
description: 'Enable or disable verification.', description: 'The settings to change.',
type: ApplicationCommandOptionType.String, type: ApplicationCommandOptionType.String,
required: true,
choices: [
{
name: 'Verification',
value: 'verification'
},
{
name: 'Anti New User',
value: 'anti-new-user'
}
]
},
{
name: 'enable-or-disable',
description: 'Enable or disable the setting.',
type: ApplicationCommandOptionType.String,
required: false,
choices: [ choices: [
{ {
name: 'Enable', name: 'Enable',
@@ -24,122 +42,449 @@ module.exports = {
name: 'Disable', name: 'Disable',
value: 'false' value: 'false'
} }
], ]
required: true
}, },
]
},
{
name: 'new-user-kicker',
description: 'Enable or disable, or change the settings of new user kicker.',
type: ApplicationCommandOptionType.Subcommand,
options: [
{ {
name: 'enable-or-disable', name: 'automod-action',
description: 'Enable or disable new user kicker.', description: 'The action to take when the threshold is reached.',
type: ApplicationCommandOptionType.String, type: ApplicationCommandOptionType.String,
required: false,
choices: [ choices: [
{ {
name: 'Enable', name: 'Timeout',
value: 'true' value: 'Timeout'
}, },
{ {
name: 'Disable', name: 'Warn',
value: 'false' value: 'warn'
},
{
name: 'Kick',
value: 'kick'
},
{
name: 'Ban',
value: 'ban'
},
{
name: 'None',
value: 'none'
} }
], ]
required: true
}, },
{ {
name: 'threshold', name: 'automod-action-reason',
description: 'Set the amount of days user account age to kick.', description: 'The reason for the action.',
type: ApplicationCommandOptionType.String,
required: false
},
{
name: 'automod-action-threshold',
description: 'The threshold for the setting.',
type: ApplicationCommandOptionType.Integer, type: ApplicationCommandOptionType.Integer,
required: false required: false
},
{
name: 'automod-action-duration',
description: 'The duration for the action.',
type: ApplicationCommandOptionType.String,
required: false
} }
] ]
}, },
{ {
name: 'check', name: 'check',
description: 'Check the current settings of the bot.', description: 'Check the current settings of the bot.',
type: ApplicationCommandOptionType.Subcommand type: ApplicationCommandOptionType.Subcommand,
options: [
{
name: 'settings',
description: 'The settings to check.',
type: ApplicationCommandOptionType.String,
required: true,
choices: [
{
name: 'General',
value: 'general'
},
{
name: 'Actions',
value: 'actions'
},
{
name: 'All',
value: 'all'
}
]
}
]
}, },
{ {
name: 'reset', name: 'reset',
description: 'Reset the settings of the bot.', description: 'Reset the settings of the bot.',
type: ApplicationCommandOptionType.Subcommand type: ApplicationCommandOptionType.Subcommand
},
{
name: 'rawcheck',
description: 'Check the raw settings of the bot.',
type: ApplicationCommandOptionType.Subcommand
} }
], ],
default_member_permissions: ["Administrator"], default_member_permissions: ["Administrator"],
run: async (client, interaction) => { run: async (client, interaction) => {
await interaction.deferReply({ ephemeral: true }); await interaction.deferReply({ ephemeral: true });
const subcommand = interaction.options.getSubcommand(); const filter = {
const guildId = interaction.guild.id; guildId: interaction.guild.id
const settings = await client.db.get(`settings-${guildId}`) || {};
if (subcommand === 'verification') {
const value = interaction.options.getString('enable-or-disable');
if (value === 'true') {
await client.db.set(
`settings-${guildId}.verification`,
true
)
return interaction.editReply({ content: 'Verification has been enabled.' });
} else {
await client.db.set(
`settings-${guildId}.verification`,
false
)
return interaction.editReply({ content: 'Verification has been disabled.' });
}
} else if (subcommand === 'new-user-kicker') {
const value = interaction.options.getString('enable-or-disable');
if (value === 'true') {
const threshold = interaction.options.getInteger('threshold') || 7;
await client.db.set(
`settings-${guildId}.newUserKicker`,
true
)
await client.db.set(
`settings-${guildId}.newUserKickerThreshold`,
threshold
)
return interaction.editReply({ content: `New user kicker has been enabled with a threshold of ${threshold} days.` });
} else {
await client.db.set(
`settings-${guildId}.newUserKicker`,
false
)
await client.db.delete(`settings-${guildId}.newUserKickerThreshold`);
return interaction.editReply({ content: `New user kicker has been disabled.` });
}
} else if (subcommand === 'check') {
const embed = new EmbedBuilder()
.setTitle('Bot Settings')
.setColor('Green')
.setTimestamp();
if (settings.verification === true) {
embed.addFields({ name: 'Verification', value: 'Enabled' })
} else {
embed.addFields({ name: 'Verification', value: 'Disabled' })
}
if (settings.newUserKicker === true) {
embed.addFields(
{
name: 'New User Kicker',
value: `Enabled with a threshold of ${settings.newUserKickerThreshold} days.`
}
)
} else {
embed.addFields({ name: 'New User Kicker', value: 'Disabled' })
}
return interaction.editReply({ embeds: [embed] });
} else if (subcommand === 'reset') {
await client.db.delete(`settings-${guildId}`);
return interaction.editReply({ content: 'Settings have been reset.' });
} }
const subcommand = interaction.options.getSubcommand();
const settings = interaction.options.get('set')?.value;
const enableOrDisable = interaction.options.get('enable-or-disable')?.value;
const automodAction = interaction.options.get('automod-action')?.value;
const automodActionReason = interaction.options.get('automod-action-reason')?.value;
const automodActionThreshold = interaction.options.get('automod-action-threshold')?.value;
const automodActionDuration = interaction.options.get('automod-action-duration')?.value;
const checksettings = interaction.options.get('settings')?.value;
const GuildSettings = require('../../database/models/GuildSettings');
const NewUserAction = require('../../database/models/NewUserAction.js');
const VerifyFailAction = require('../../database/models/VerifyFailAction.js');
const guildSettings = await GuildSettings.findOne(filter);
const newUserAction = await NewUserAction.findOne(filter);
const verifyFailAction = await VerifyFailAction.findOne(filter);
switch (subcommand) {
case 'change':
switch (settings) {
case 'verification':
switch (automodAction) {
case undefined:
console.log('a')
switch (enableOrDisable) {
case 'true': case 'false':
console.log(enableOrDisable)
switch (guildSettings) {
case null:
const newGuildSettings = new GuildSettings({
guildId: interaction.guild.id,
verifyEnabled: enableOrDisable
});
await newGuildSettings.save();
break;
default:
guildSettings.verifyEnabled = enableOrDisable;
await guildSettings.save();
break;
}
break;
}
break;
case 'none':
console.log('b')
await VerifyFailAction.find(filter).deleteMany();
break;
default:
console.log('c')
switch (enableOrDisable) {
case 'true': case 'false':
switch (guildSettings) {
case null:
const newGuildSettings = new GuildSettings({
guildId: interaction.guild.id,
verifyEnabled: enableOrDisable
});
await newGuildSettings.save();
break;
default:
guildSettings.verifyEnabled = enableOrDisable;
await guildSettings.save();
break;
}
break;
}
switch (verifyFailAction) {
case null:
const newVerifyFailAction = new VerifyFailAction({
guildId: interaction.guild.id,
action: automodAction,
reason: automodActionReason || 'User failed to verify.',
threshold: automodActionThreshold || 3,
duration: automodActionDuration || '7d'
});
await newVerifyFailAction.save();
break;
default:
verifyFailAction.action = automodAction || verifyFailAction?.action;
verifyFailAction.reason = `${automodActionReason?.toString()}` || verifyFailAction?.reason || 'User failed to verify.';
verifyFailAction.threshold = automodActionThreshold || verifyFailAction?.threshold || 3;
verifyFailAction.duration = automodActionDuration || verifyFailAction?.duration || '7d';
await verifyFailAction.save();
break;
}
break;
}
break;
case 'anti-new-user':
switch (automodAction) {
case undefined:
switch (enableOrDisable) {
case 'true': case 'false':
switch (guildSettings) {
case null:
const newGuildSettings = new GuildSettings({
guildId: interaction.guild.id,
antiNewUserEnabled: enableOrDisable
});
await newGuildSettings.save();
break;
default:
guildSettings.antiNewUserEnabled = enableOrDisable;
await guildSettings.save();
break;
}
break;
}
break;
case 'none':
await NewUserAction.find(filter).deleteMany();
break;
default:
switch (enableOrDisable) {
case 'true': case 'false':
switch (guildSettings) {
case null:
const newGuildSettings = new GuildSettings({
guildId: interaction.guild.id,
antiNewUserEnabled: enableOrDisable
});
await newGuildSettings.save();
break;
default:
guildSettings.antiNewUserEnabled = enableOrDisable;
await guildSettings.save();
break;
}
break;
}
switch (newUserAction) {
case null:
const newNewUserAction = new NewUserAction({
guildId: interaction.guild.id,
action: automodAction,
reason: automodActionReason || 'User account age is way too low.',
threshold: automodActionThreshold || 3,
duration: automodActionDuration || '7d'
});
await newNewUserAction.save();
break;
default:
newUserAction.action = automodAction || newUserAction?.action;
newUserAction.reason = `${automodActionReason?.toString()}` || newUserAction?.reason || 'User account age is way too low.';
newUserAction.threshold = automodActionThreshold || newUserAction?.threshold || 3;
newUserAction.duration = automodActionDuration || newUserAction?.duration || '7d';
await newUserAction.save();
break;
}
break;
}
break;
}
await interaction.editReply({ content: 'Settings have been updated.' });
break;
case 'check':
function verification() {
if (guildSettings?.verifyEnabled === 'true') {
return 'Enabled';
} else if (guildSettings?.verifyEnabled === 'false') {
return 'Disabled';
} else {
return 'Disabled';
}
}
function antiNewUser() {
if (guildSettings?.antiNewUserEnabled === 'true') {
return 'Enabled';
} else if (guildSettings?.antiNewUserEnabled === 'false') {
return 'Disabled';
} else {
return 'Disabled';
}
}
const embed = new EmbedBuilder()
.setTitle('Settings')
.addFields({
name: 'Verification',
value: `${verification()}`,
},
{
name: 'Anti New User',
value: `${antiNewUser()}`,
})
.setColor("Gold");
const embedd = new EmbedBuilder()
.setTitle('Settings')
.addFields({
name: 'Verification Auto Action',
value: `${verifyFailAction ? `Action: ${verifyFailAction?.action.toUpperCase()}\nReason: ${verifyFailAction?.reason}\nThreshold: ${verifyFailAction?.threshold}\nDuration: ${await getActionDuration(verifyFailAction?.duration)}` : "None"}`,
},
{
name: 'Anti New User Auto Action',
value: `${newUserAction ? `Action: ${newUserAction?.action.toUpperCase()}\nReason: ${newUserAction?.reason}\nThreshold: ${newUserAction?.threshold}\nDuration: ${await getActionDuration(newUserAction?.duration)}` : "None"}`,
})
.setColor("Gold");
console.log(verifyFailAction?.duration, await getActionDuration(verifyFailAction?.duration), await convertActionDurationFromTimeToMs(verifyFailAction?.duration));
console.log(newUserAction?.duration, await getActionDuration(newUserAction?.duration), await convertActionDurationFromTimeToMs(newUserAction?.duration));
switch (checksettings) {
case 'general':
await interaction.editReply({ embeds: [embed] });
break;
case 'actions':
await interaction.editReply({ embeds: [embedd] });
break;
case 'all':
await interaction.editReply({ embeds: [embed, embedd] });
break;
}
break;
case 'reset':
await GuildSettings.find(filter).deleteMany();
await NewUserAction.find(filter).deleteMany();
await VerifyFailAction.find(filter).deleteMany();
await interaction.editReply({ content: 'Settings have been reset.' });
break;
case 'rawcheck':
console.log(guildSettings);
console.log(verifyFailAction);
console.log(newUserAction);
await interaction.editReply({ content: 'Check the console.' });
break;
}
/*
if (settings === 'verification') {
console.log('a')
if (automodAction === undefined) {
console.log('i')
if (enableOrDisable !== undefined) {
if (guildSettings === null) {
const newGuildSettings = new GuildSettings({
guildId: interaction.guild.id,
verifyEnabled: enableOrDisable
});
await newGuildSettings.save();
} else {
guildSettings.verifyEnabled = enableOrDisable;
await guildSettings.save();
}
} else if (enableOrDisable === undefined) {
if (verifyFailAction === null) {
const newVerifyFailAction = new VerifyFailAction({
guildId: interaction.guild.id,
action: automodAction,
reason: `${automodActionReason.toString()}` || 'No reason provided.',
threshold: automodActionThreshold || 3,
duration: automodActionDuration || 7
});
await newVerifyFailAction.save();
} else if (verifyFailAction !== null) {
verifyFailAction.action = automodAction;
verifyFailAction.reason = `${automodActionReason.toString()}` || 'No reason provided.';
verifyFailAction.threshold = automodActionThreshold || 3;
verifyFailAction.duration = automodActionDuration || 7;
await verifyFailAction.save();
} else if (verifyFailAction?.action !== 'none' && automodAction === 'none') {
await VerifyFailAction.find(filter).deleteMany();
}
}
}
} else if (settings === 'anti-new-user') {
if (enableOrDisable !== undefined && automodAction === undefined) {
if (guildSettings === null) {
const newGuildSettings = new GuildSettings({
guildId: interaction.guild.id,
antiNewUserEnabled: enableOrDisable
});
await newGuildSettings.save();
} else {
guildSettings.antiNewUserEnabled = enableOrDisable;
await guildSettings.save();
}
} else if (enableOrDisable !== undefined && automodAction !== 'none') {
if (guildSettings === null) {
const newGuildSettings = new GuildSettings({
guildId: interaction.guild.id,
antiNewUserEnabled: enableOrDisable
});
await newGuildSettings.save();
} else {
guildSettings.antiNewUserEnabled = enableOrDisable;
await guildSettings.save();
}
if (newUserAction === null) {
const newNewUserAction = new NewUserAction({
guildId: interaction.guild.id,
action: automodAction,
reason: `${automodActionReason}` || 'No reason provided.',
threshold: automodActionThreshold || 3,
duration: automodActionDuration || 0
});
await newNewUserAction.save();
} else {
newUserAction.action = automodAction;
newUserAction.reason = `${automodActionReason}` || 'No reason provided.';
newUserAction.threshold = automodActionThreshold || 3;
newUserAction.duration = automodActionDuration || 0;
await newUserAction.save();
}
} else if (enableOrDisable !== undefined && automodAction === 'none') {
if (guildSettings === null) {
const newGuildSettings = new GuildSettings({
guildId: interaction.guild.id,
antiNewUserEnabled: enableOrDisable
});
await newGuildSettings.save();
} else {
guildSettings.antiNewUserEnabled = enableOrDisable;
await guildSettings.save();
}
if (newUserAction?.action !== 'none') {
await NewUserAction.find(filter).deleteMany();
}
}
}
await interaction.editReply({ content: 'Settings have been updated.' });
} else if (subcommand === 'check') {
const embed = new EmbedBuilder()
.setTitle('Settings')
.addFields({
name: 'Verification',
value: `${guildSettings?.verifyEnabled ? 'Enabled' : 'Disabled'}`,
},
{
name: 'Verification Auto Action',
value: `${verifyFailAction ? `Action: ${verifyFailAction?.action.toUpperCase()}\nReason: ${verifyFailAction?.reason}\nThreshold: ${verifyFailAction?.threshold}\nDuration: ${verifyFailAction?.duration}` : "None"}`,
},
{
name: 'Anti New User',
value: `${guildSettings?.antiNewUserEnabled ? 'Enabled' : 'Disabled'}`,
},
{
name: 'Anti New User Auto Action',
value: `${newUserAction ? `Action: ${newUserAction?.action.toUpperCase()}\nReason: ${newUserAction?.reason}\nThreshold: ${newUserAction?.threshold}\nDuration: ${newUserAction?.duration}` : "None"}`,
})
.setColor("Gold");
await interaction.editReply({ embeds: [embed] });
} else if (subcommand === 'reset') {
await GuildSettings.find(filter).deleteMany();
await NewUserAction.find(filter).deleteMany();
await VerifyFailAction.find(filter).deleteMany();
await interaction.editReply({ content: 'Settings have been reset.' });
}
*/
} }
} }
+161 -78
View File
@@ -1,13 +1,16 @@
const { EmbedBuilder, ApplicationCommandType, AttachmentBuilder } = require('discord.js'); const { EmbedBuilder, ApplicationCommandType, AttachmentBuilder } = require('discord.js');
const { dangercordcheckVerification } = require('../../functions/dangerCordUtils.js'); const { dangercordcheckVerification } = require('../../functions/dangerCordUtils.js');
const { createCanvas } = require('canvas'); const { createCanvas } = require('canvas');
const GuildSettings = require('../../database/models/GuildSettings');
const cfg = require('../../config/config.js'); const cfg = require('../../config/config.js');
const { moderationUtilities } = require('../../functions/utils.js');
module.exports = { module.exports = {
name: 'verify', name: 'verify',
description: 'To verify yourself.', description: 'To verify yourself.',
type: ApplicationCommandType.ChatInput, type: ApplicationCommandType.ChatInput,
run: async (client, interaction) => { run: async (client, interaction) => {
const guildSettings = await GuildSettings.findOne({ guildId: interaction.guild.id });
await interaction.deferReply({ ephemeral: true }); await interaction.deferReply({ ephemeral: true });
const guildId = interaction.guild.id; const guildId = interaction.guild.id;
@@ -98,7 +101,7 @@ module.exports = {
return interaction.editReply({ content: cfg.messages.wrongChannel, ephemeral: true }) return interaction.editReply({ content: cfg.messages.wrongChannel, ephemeral: true })
} }
if (await client.db.get(`settings-${guildId}.verification`) === false) { if (guildSettings?.verifyEnabled === false || !guildSettings?.verifyEnabled) {
return interaction.editReply({ content: cfg.messages.verifyDisabled, ephemeral: true }) return interaction.editReply({ content: cfg.messages.verifyDisabled, ephemeral: true })
} }
@@ -113,98 +116,178 @@ module.exports = {
async function verifyUser(captcha) { async function verifyUser(captcha) {
attempts++; attempts++;
if (attempts > maxAttempts) { switch (attempts) {
await interaction.user.send(cfg.messages.maxAttemptsExceeded); case attempts > maxAttempts:
return; await interaction.user.send(cfg.messages.maxAttemptsExceeded);
return;
} }
}
if (attempts == 1) { /*
// Check the Dangercord API using our custom library function if (attempts > maxAttempts) {
await interaction.user.send(cfg.messages.maxAttemptsExceeded);
return;
}
*/
const captcha = await generateCaptcha(cfg.captchaCount);
switch (attempts) {
case 1:
await dangercordcheckVerification(interaction.user.id) await dangercordcheckVerification(interaction.user.id)
.then(result => { .then(result => {
console.log('Verification result:', result); console.log('Verification result:', result);
if (result === 1) { switch (result) {
console.log(`${interaction.user.id} is not blacklisted at dangercord, lets continue with the verification process`) case 1: {
// Proceed with further actions console.log(`${interaction.user.id} is not blacklisted at dangercord, lets continue with the verification process`)
} else { // Proceed with further actions
console.log(`${interaction.user.id} is blacklisted at dangercord, we will not continue with the verification process`); break;
blacklisted = true; // Set blacklisted to true }
// Handle the case when user is blacklisted default: {
interaction.editReply({ console.log(`${interaction.user.id} is blacklisted at dangercord, we will not continue with the verification process`);
content: cfg.messages.cannotContinue, blacklisted = true; // Set blacklisted to true
ephemeral: true // Handle the case when user is blacklisted
}).then(() => { interaction.editReply({
// Stop execution here content: cfg.messages.cannotContinue,
return; ephemeral: true
}).catch(error => { }).then(() => {
console.error('Error:', error); // Stop execution here
}); return;
}).catch(error => {
console.error('Error:', error);
});
break;
}
} }
}) })
.catch(error => { .catch(error => {
console.error('Error:', error); console.error('Error:', error);
}); });
} }
/*
if (blacklisted == true) return if (attempts == 1) {
// Check the Dangercord API using our custom library function
const buff = await createCaptcha(captcha); await dangercordcheckVerification(interaction.user.id)
const attachment = new AttachmentBuilder(buff, { name: 'captcha.png' }); .then(result => {
console.log('Verification result:', result);
const embed = new EmbedBuilder() if (result === 1) {
.setTitle("Captcha Verification") console.log(`${interaction.user.id} is not blacklisted at dangercord, lets continue with the verification process`)
.setDescription(`Please type the captcha below. You have ${cfg.captchaTimeout / 1000} seconds to respond. Attempt ${attempts}/${maxAttempts}`) // Proceed with further actions
.setColor('Green')
.setImage('attachment://captcha.png')
.setTimestamp();
await interaction.user.send({
embeds: [embed], files: [attachment]
}).then(async (msg) => {
await interaction.editReply({ content: cfg.messages.toVerify, ephemeral: true });
const filter = m => !m.author.bot && m.author.id === interaction.user.id && m.channel.id === msg.channel.id;
const collector = msg.channel.createMessageCollector({
filter,
time: cfg.captchaTimeout
});
collector.on('collect', async (m) => {
//if member left the server before the verification is done
if (!interaction.guild.members.cache.has(interaction.user.id)) {
await interaction.user.send(cfg.messages.userLeft);
collector.stop();
} else { } else {
if (m.content === captcha) { console.log(`${interaction.user.id} is blacklisted at dangercord, we will not continue with the verification process`);
validated = true; blacklisted = true; // Set blacklisted to true
await interaction.member.roles.add(cfg.guilds[guildId].roleToAdd); // Handle the case when user is blacklisted
await interaction.member.roles.remove(cfg.guilds[guildId].firstRole); interaction.editReply({
await interaction.user.send(cfg.messages.success); content: cfg.messages.cannotContinue,
return ephemeral: true
} else { }).then(() => {
// If the content doesn't match captcha // Stop execution here
collector.stop(); return;
} }).catch(error => {
console.error('Error:', error);
});
} }
}); })
.catch(error => {
collector.on('end', async (collected, reason) => { console.error('Error:', error);
if (reason === 'time' && !validated) { // Send timeout message only if not already validated
await interaction.user.send(cfg.messages.tookTooLong);
} else if (!validated) {
// Only generate a new captcha and attempt verification if attempts < maxAttempts
const newCaptcha = await generateCaptcha(cfg.captchaCount);
await verifyUser(newCaptcha);
}
});
}).catch(async (err) => {
await interaction.editReply({
content: cfg.messages.unableToDM,
ephemeral: true
});
}); });
} }
*/
if (blacklisted == true) return
const buff = await createCaptcha(captcha);
const attachment = new AttachmentBuilder(buff, { name: 'captcha.png' });
const embed = new EmbedBuilder()
.setTitle("Captcha Verification")
.setDescription(`Please type the captcha below. You have ${cfg.captchaTimeout / 1000} seconds to respond. Attempt ${attempts}/${maxAttempts}`)
.setColor('Green')
.setImage('attachment://captcha.png')
.setTimestamp();
await interaction.user.send({
embeds: [embed], files: [attachment]
}).then(async (msg) => {
await interaction.editReply({ content: cfg.messages.toVerify, ephemeral: true });
const filter = m => !m.author.bot && m.author.id === interaction.user.id && m.channel.id === msg.channel.id;
const collector = msg.channel.createMessageCollector({
filter,
time: cfg.captchaTimeout
});
collector.on('collect', async (m) => {
//if member left the server before the verification is done
switch (!interaction.guild.members.cache.has(interaction.user.id)) {
case true: {
await interaction.user.send(cfg.messages.userLeft);
collector.stop();
break;
}
default: {
switch (m.content === captcha) {
case true: {
validated = true;
await interaction.member.roles.add(cfg.guilds[guildId].roleToAdd);
await interaction.member.roles.remove(cfg.guilds[guildId].firstRole);
await interaction.user.send(cfg.messages.success);
return;
}
default: {
// If the content doesn't match captcha
collector.stop();
break;
}
}
}
}
});
/*
if (!interaction.guild.members.cache.has(interaction.user.id)) {
await interaction.user.send(cfg.messages.userLeft);
collector.stop();
} else {
if (m.content === captcha) {
validated = true;
await interaction.member.roles.add(cfg.guilds[guildId].roleToAdd);
await interaction.member.roles.remove(cfg.guilds[guildId].firstRole);
await interaction.user.send(cfg.messages.success);
return
} else {
// If the content doesn't match captcha
collector.stop();
}
}
*/
collector.on('end', async (collected, reason) => {
switch (reason) {
case 'time':
switch (validated) {
case false: case null:
await interaction.user.send(cfg.messages.tookTooLong);
break;
}
break;
case
}
/*
if (reason === 'time' && !validated) { // Send timeout message only if not already validated
await interaction.user.send(cfg.messages.tookTooLong);
} else if (!validated) {
// Only generate a new captcha and attempt verification if attempts < maxAttempts
const newCaptcha = await generateCaptcha(cfg.captchaCount);
await verifyUser(newCaptcha);
}
*/
});
}).catch(async (err) => {
await interaction.editReply({
content: cfg.messages.unableToDM,
ephemeral: true
});
});
const captcha = await generateCaptcha(cfg.captchaCount);
verifyUser(captcha); verifyUser(captcha);
} }
} }