Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
119 changes: 119 additions & 0 deletions modules/message-quotes/configs/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
{
"description": "Configure the message quoting system",
"humanName": "Configuration",
"filename": "config.json",
"content": [
{
"name": "roles",
"humanName": "Blacklist roles",
"description": "Roles that are excluded from quoting",
"default": [],
"type": "array",
"content": "roleID"
},
{
"name": "channels",
"humanName": "Blacklist channels",
"description": "Channels that are excluded from quoting (Channels and categories are supported; a category excludes all its channels)",
"default": [],
"type": "array",
"content": "channelID"
},
{
"name": "withAttachments",
"humanName": "Attach files?",
"default": false,
"description": "Should all attachments be quoted? (Deactivation recommended if \"Message\" components v2 are used)",
"type": "boolean"
},
{
"name": "noBots",
"humanName": "Ignore bot messages?",
"default": true,
"description": "Bot messages are not included in the quote when activated",
"type": "boolean"
},
{
"name": "selfQuote",
"humanName": "Allow Self-quotes?",
"default": true,
"description": "Can users quote their own messages?",
"type": "boolean"
},
{
"name": "asReply",
"humanName": "Reply to messages?",
"default": true,
"description": "Reply to the message that triggered the quote (Ignored when \"Delete trigger\" is enabled)",
"type": "boolean"
},
{
"name": "deleteOrigin",
"humanName": "Delete trigger?",
"default": false,
"description": "When enabled, the trigger message will be deleted",
"type": "boolean"
},
{
"name": "message",
"humanName": "Message",
"description": "Message in which the quote is returned",
"default": {
"title": "Quote from #%channelName%",
"url": "%link%",
"description": ">>> %content%",
Comment thread
SCDerox marked this conversation as resolved.
"image": "%image%",
"color": "#2ECC71",
"author": {
"name": "%userName%",
"img": "%userAvatar%"
}
},
"type": "string",
"allowEmbed": true,
"allowGeneratedImage": true,
"params": [
{
"name": "%userID%",
"description": "Id of the user"
},
{
"name": "%userName%",
"description": "Username of the user"
},
{
"name": "%displayName%",
"description": "Displays the user's nickname"
},
{
"name": "%userAvatar%",
"description": "Avatar of the user"
},
{
"name": "%channelID%",
"description": "Id of the channel from which the quote originates"
},
{
"name": "%channelName%",
"description": "Name of the channel from which the quote originates"
},
{
"name": "%timestamp%",
"description": "Shows when the original message was sent (Used discord timestamp)"
},
{
"name": "%link%",
"description": "Message-link of the original message"
},
{
"name": "%image%",
"description": "First image of the message, if available"
},
{
"name": "%content%",
"description": "Message content of the quote"
}
]
}
]
}
135 changes: 135 additions & 0 deletions modules/message-quotes/events/messageCreate.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
const {
embedType,
embedTypeV2,
formatDiscordUserName,
archiveDiscordAttachment
} = require('../../../src/functions/helpers');
const cooldowns = new Map();

module.exports.run = async (client, msg) => {
if (!client.botReadyAt) return;
if (!msg.content || msg.author.bot || msg.system) return;
if (!msg.guild || !msg.member) return;
if (msg.guild.id !== client.guildID) return;

const now = Date.now();
const cooldownAmount = 5 * 1000;
if (cooldowns.has(msg.author.id)) {
const expirationTime = cooldowns.get(msg.author.id) + cooldownAmount;
if (now < expirationTime) return;
}

const moduleConfig = client.configurations['message-quotes']['config'] || {};

const blacklistedChannels = moduleConfig.channels || [];
const blacklistedRoles = moduleConfig.roles || [];

if (blacklistedChannels.includes(msg.channel.id) ||
blacklistedChannels.includes(msg.channel.parentId) ||
(msg.channel.parent?.parentId && blacklistedChannels.includes(msg.channel.parent.parentId))) {
return;
};
if (msg.member.roles.cache.some(r => blacklistedRoles.some(br => String(br) === r.id))) return;

const discordLinkRegex = /https:\/\/discord\.com\/channels\/(\d+)\/(\d+)\/(\d+)/i;
const match = msg.content.match(discordLinkRegex);
Comment thread
JeanCoding16 marked this conversation as resolved.
if (!match) return;

cooldowns.set(msg.author.id, now);

const [_, guildId, channelId, messageId] = match;
if (guildId !== msg.guild.id) return;

try {
const targetChannel = await msg.guild.channels.fetch(channelId).catch(() => null);
if (!targetChannel || !targetChannel.isTextBased()) return;

const userPerms = targetChannel.permissionsFor(msg.member);
if (!userPerms || !userPerms.has('ViewChannel') || !userPerms.has('ReadMessageHistory')) return;

const botPerms = targetChannel.permissionsFor(msg.guild.members.me);
if (!botPerms || !botPerms.has('ViewChannel') || !botPerms.has('ReadMessageHistory')) return;
Comment thread
JeanCoding16 marked this conversation as resolved.

const targetMsg = await targetChannel.messages.fetch(messageId).catch(() => null);
if (!targetMsg) return;

if (moduleConfig.noBots === true && targetMsg.author.bot) return;
if (moduleConfig.selfQuote === false && targetMsg.author.id === msg.author.id) return;

let files = [];
const withAttachments = moduleConfig.withAttachments;
if (withAttachments && targetMsg.attachments.size > 0) {
let count = 0;
for (const [_, att] of targetMsg.attachments) {
if (count >= 3) break;
if (att.size > 8 * 1024 * 1024) continue;

files.push({
attachment: att.url,
name: att.name ?? 'attachment'
});
Comment thread
JeanCoding16 marked this conversation as resolved.
count++;
}
}

let finalImage = '';
const firstAttachment = targetMsg.attachments.first();
if (firstAttachment) {
finalImage = await archiveDiscordAttachment(client, firstAttachment.url, {
displayName: `Quote by ${formatDiscordUserName(targetMsg.author)} in #${targetChannel.name}`.slice(0, 100),
tags: ['message-quotes'],
uploaderDiscordID: targetMsg.author.id
});
} else {
const imgMatch = targetMsg.content.match(/https?:\/\/\S+\.(?:png|jpe?g|gif|webp)/i);
if (imgMatch) finalImage = imgMatch[0];
}

const userAvatar = targetMsg.author.displayAvatarURL();
const unixSeconds = Math.floor(targetMsg.createdTimestamp / 1000);
const displayContent = targetMsg.content ||
(targetMsg.attachments.size > 0 ? '*[Attachment]*' : '') ||
(targetMsg.stickers?.size > 0 ? '*[Sticker]*' : '*[None]*');

const quoteMsg = await embedTypeV2(moduleConfig.message, {
'%userID%': targetMsg.author.id,
'%userName%': formatDiscordUserName(targetMsg.author),
'%displayName%': targetMsg.member?.displayName || targetMsg.author.username,
'%userAvatar%': userAvatar,
'%channelID%': targetChannel.id,
'%channelName%': targetChannel.name,
'%link%': match[0],
'%image%': finalImage,
'%timestamp%': `<t:${unixSeconds}:R>`,
'%content%': displayContent
});

let finalFiles = quoteMsg.files && Array.isArray(quoteMsg.files) ? [...quoteMsg.files] : [];
if (files.length > 0) {
finalFiles = finalFiles.concat(files);
}

const sendOptions = {
...quoteMsg,
files: finalFiles.length > 0 ? finalFiles : undefined,
allowedMentions: { parse: [], repliedUser: false }
};

if (moduleConfig.asReply === true && moduleConfig.deleteOrigin !== true) {
await msg.reply(sendOptions);
} else {
await msg.channel.send(sendOptions);
Comment thread
JeanCoding16 marked this conversation as resolved.
}

if (moduleConfig.deleteOrigin === true) {
Comment thread
JeanCoding16 marked this conversation as resolved.
const currentChannelPerms = msg.channel.permissionsFor(msg.guild.members.me);
if (currentChannelPerms && currentChannelPerms.has('ManageMessages')) {
await msg.delete().catch(() => null);
} else {
client.logger.warn(`[Message-Quotes] Messages cannot deleted, missing Permission: ManageMessages`);
}
}
} catch(error) {
client.logger.error('[Message-Quotes]' + error);
}
};
18 changes: 18 additions & 0 deletions modules/message-quotes/module.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"name": "message-quotes",
"humanReadableName": "Message quotes",
"description": "Quotes a Discord message when a user pastes a message link.",
"fa-icon": "fas fa-quote-left",
"author": {
"name": "Jean S.",
"link": "https://github.com/JeanCoding16"
},
"openSourceURL": "https://github.com/ScootKit/CustomDCBot/tree/main/modules/message-quotes",
"tags": [
"community"
],
"events-dir": "/events",
"config-example-files": [
"configs/config.json"
]
}
Loading