From c24520dd245b0ecc8a61ab670f4b9155b00ffa9a Mon Sep 17 00:00:00 2001
From: Supositware <sup@libtar.de>
Date: Mon, 29 Aug 2022 20:56:26 +0200
Subject: [PATCH] Optout of quotation feature

---
 commands/utility/optout.js                 | 46 +++++++++++++++
 events/client/messageCreate.js             | 67 +++++++++++-----------
 migrations/20220829184747-create-optout.js | 27 +++++++++
 models/optout.js                           | 23 ++++++++
 4 files changed, 131 insertions(+), 32 deletions(-)
 create mode 100644 commands/utility/optout.js
 create mode 100644 migrations/20220829184747-create-optout.js
 create mode 100644 models/optout.js

diff --git a/commands/utility/optout.js b/commands/utility/optout.js
new file mode 100644
index 0000000..319524c
--- /dev/null
+++ b/commands/utility/optout.js
@@ -0,0 +1,46 @@
+import { SlashCommandBuilder, ActionRowBuilder, ButtonBuilder, ButtonStyle } from 'discord.js';
+import db from '../../models/index.js';
+
+export default {
+	data: new SlashCommandBuilder()
+		.setName('optout')
+		.setDescription('Opt out of the quotation command.'),
+	category: 'utility',
+	async execute(interaction, args, client) {
+		const isOptOut = await db.optout.findOne({ where: { userID: interaction.user.id } });
+
+		if (!isOptOut) {
+			const body = { userID: interaction.user.id };
+			await db.optout.create(body);
+			return await interaction.reply({ content: 'You have successfully been opt out.' });
+		}
+
+		const row = new ActionRowBuilder()
+			.addComponents(
+				new ButtonBuilder()
+					.setCustomId('yes')
+					.setLabel('Yes')
+					.setStyle(ButtonStyle.Primary),
+			)
+			.addComponents(
+				new ButtonBuilder()
+					.setCustomId('no')
+					.setLabel('No')
+					.setStyle(ButtonStyle.Danger),
+			);
+
+		await interaction.reply({ content: 'You are already opt out, do you wish to opt in?', components: [row] });
+
+		client.once('interactionCreate', async (interactionMenu) => {
+			if (!interactionMenu.isButton) return;
+			interactionMenu.update({ components: [] });
+			if (interactionMenu.customId === 'yes') {
+				await db.optout.destroy({ where: { userID: interaction.user.id } });
+				return interaction.editReply('You have successfully been opt in');
+			}
+			else {
+				return interaction.editReply('Nothing has been changed.');
+			}
+		});
+	},
+};
diff --git a/events/client/messageCreate.js b/events/client/messageCreate.js
index ab6a7fa..a7c0e50 100644
--- a/events/client/messageCreate.js
+++ b/events/client/messageCreate.js
@@ -205,46 +205,49 @@ export default {
 				*	This section will contain the code for the quotation feature, it will detect link for it and send it as embed
 				*
 				*/
-			const quotationstat = await db.quotationStat.findOne({ where: { serverID: message.guild.id, stat: 'enable' } });
+			const isOptOut = await db.optout.findOne({ where: { userID: message.author.id } });
+			if (!isOptOut) {
+				const quotationstat = await db.quotationStat.findOne({ where: { serverID: message.guild.id, stat: 'enable' } });
 
-			if (quotationstat && (message.content.includes('discordapp.com/channels/') || message.content.includes('discord.com/channels/'))) {
-				const url = message.content.split('/');
-				const guildID = url[4];
-				const channelID = url[5];
-				const messageID = url[6].split(' ')[0];
+				if (quotationstat && (message.content.includes('discordapp.com/channels/') || message.content.includes('discord.com/channels/'))) {
+					const url = message.content.split('/');
+					const guildID = url[4];
+					const channelID = url[5];
+					const messageID = url[6].split(' ')[0];
 
 
-				// Verify if the guild, channel and message exist
-				const guild = client.guilds.resolve(guildID);
-				if (!guild) return;
-				const channel = client.channels.resolve(channelID);
-				if (!channel) return;
-				const quote = await channel.messages.fetch(messageID)
-					.catch(() => {
-						return;
-					});
-				if (!quote) return;
+					// Verify if the guild, channel and message exist
+					const guild = client.guilds.resolve(guildID);
+					if (!guild) return;
+					const channel = client.channels.resolve(channelID);
+					if (!channel) return;
+					const quote = await channel.messages.fetch(messageID)
+						.catch(() => {
+							return;
+						});
+					if (!quote) return;
 
-				const Embed = new EmbedBuilder()
-					.setAuthor({ name: quote.author.username, iconURL: quote.author.displayAvatarURL() })
-					.setColor(message.member ? message.member.displayHexColor : 'NAVY')
-					.addFields(
-						{ name: 'Jump to', value: `[message](https://discordapp.com/channels/${message.guild.id}/${channelID}/${messageID})`, inline: true },
-						{ name: 'In channel', value: quote.channel.name.toString(), inline: true },
-						{ name: 'Quoted by', value: message.author.toString(), inline: true },
-					)
-					.setDescription(quote.content)
-					.setTimestamp(quote.createdTimestamp);
+					const Embed = new EmbedBuilder()
+						.setAuthor({ name: quote.author.username, iconURL: quote.author.displayAvatarURL() })
+						.setColor(message.member ? message.member.displayHexColor : 'NAVY')
+						.addFields(
+							{ name: 'Jump to', value: `[message](https://discordapp.com/channels/${message.guild.id}/${channelID}/${messageID})`, inline: true },
+							{ name: 'In channel', value: quote.channel.name.toString(), inline: true },
+							{ name: 'Quoted by', value: message.author.toString(), inline: true },
+						)
+						.setDescription(quote.content)
+						.setTimestamp(quote.createdTimestamp);
 
-				if (quote.member) Embed.setAuthor({ name: `${quote.author.username}#${quote.author.discriminator}`, iconURL: quote.author.displayAvatarURL() });
+					if (quote.member) Embed.setAuthor({ name: `${quote.author.username}#${quote.author.discriminator}`, iconURL: quote.author.displayAvatarURL() });
 
-				if (quote.author.bot) Embed.setAuthor({ name: `${quote.author.username}#${quote.author.discriminator} (BOT)`, iconURL: quote.author.displayAvatarURL() });
+					if (quote.author.bot) Embed.setAuthor({ name: `${quote.author.username}#${quote.author.discriminator} (BOT)`, iconURL: quote.author.displayAvatarURL() });
 
-				if (guild.id != message.guild.id) Embed.addFields({ name: 'In guild', value: guild.name, inline: true });
-				const Attachment = Array.from(message.attachments.values());
-				if (Attachment[0]) Embed.setImage(Attachment[0].url);
+					if (guild.id != message.guild.id) Embed.addFields({ name: 'In guild', value: guild.name, inline: true });
+					const Attachment = Array.from(message.attachments.values());
+					if (Attachment[0]) Embed.setImage(Attachment[0].url);
 
-				return message.channel.send({ embeds: [Embed] });
+					return message.channel.send({ embeds: [Embed] });
+				}
 			}
 		}
 
diff --git a/migrations/20220829184747-create-optout.js b/migrations/20220829184747-create-optout.js
new file mode 100644
index 0000000..1c89ec4
--- /dev/null
+++ b/migrations/20220829184747-create-optout.js
@@ -0,0 +1,27 @@
+'use strict';
+module.exports = {
+  async up(queryInterface, Sequelize) {
+    await queryInterface.createTable('optouts', {
+      id: {
+        allowNull: false,
+        autoIncrement: true,
+        primaryKey: true,
+        type: Sequelize.INTEGER
+      },
+      userID: {
+        type: Sequelize.BIGINT
+      },
+      createdAt: {
+        allowNull: false,
+        type: Sequelize.DATE
+      },
+      updatedAt: {
+        allowNull: false,
+        type: Sequelize.DATE
+      }
+    });
+  },
+  async down(queryInterface, Sequelize) {
+    await queryInterface.dropTable('optouts');
+  }
+};
\ No newline at end of file
diff --git a/models/optout.js b/models/optout.js
new file mode 100644
index 0000000..c9afc92
--- /dev/null
+++ b/models/optout.js
@@ -0,0 +1,23 @@
+'use strict';
+const {
+  Model
+} = require('sequelize');
+module.exports = (sequelize, DataTypes) => {
+  class optout extends Model {
+    /**
+     * Helper method for defining associations.
+     * This method is not a part of Sequelize lifecycle.
+     * The `models/index` file will call this method automatically.
+     */
+    static associate(models) {
+      // define association here
+    }
+  }
+  optout.init({
+    userID: DataTypes.BIGINT
+  }, {
+    sequelize,
+    modelName: 'optout',
+  });
+  return optout;
+};
\ No newline at end of file