Skip to content

New module: Message-Quotes#196

Open
JeanCoding16 wants to merge 3 commits into
ScootKit:with-testingfrom
JeanCoding16:dev
Open

New module: Message-Quotes#196
JeanCoding16 wants to merge 3 commits into
ScootKit:with-testingfrom
JeanCoding16:dev

Conversation

@JeanCoding16

@JeanCoding16 JeanCoding16 commented Jun 4, 2026

Copy link
Copy Markdown

A quotes module that is extensively customizable. Aligned with the following suggestions:

Featuring the following capabilities:

  • Role blacklist
  • Channel blacklist (Supports both specific channels and categories)
  • Include attachments? (Must be disabled for Components v2)
  • Do not quote bots? (Applies to links referencing bot messages; actual messages sent by bots are always ignored. Configurable; enabled by default for a more natural feel)
  • Reply to message? (Ignored if "Delete trigger message" is enabled)
  • Delete trigger message?
  • Message content is configurable (Using the following parameters: %userID%, %userName%, %userAvatar%, %channelID%, %channelName%, %timestamp%, %link%, %image%, %content%)

Notes: The "Include attachments" option forwards images, videos, and GIFs; this may potentially require more granular filtering in the future.

@CLAassistant

CLAassistant commented Jun 4, 2026

Copy link
Copy Markdown

CLA assistant check
All committers have signed the CLA.

@JeanCoding16

Copy link
Copy Markdown
Author

A description and an icon for the dashboard are missing.

@JeanCoding16 JeanCoding16 changed the title New module: Message-Qoutes New module: Message-Quotes Jun 4, 2026
@JeanCoding16 JeanCoding16 changed the base branch from main to with-testing June 8, 2026 20:01

@SCDerox SCDerox left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @JeanCoding16 👋 thanks a lot for this - a quotes module has been one of the most-requested features on the board, and you clearly put real care into it: the config surface is thoughtful, the permission checks on the target channel are a nice touch, and the attachment/reply/delete options cover a lot of real use-cases. Really appreciate the contribution. 🙌

I went through it in depth and left inline comments. Heads up: this was an AI-assisted review, so a human has sanity-checked the important points, but please push back on anything that looks off - a few are judgment calls rather than hard rules.

There's one security issue I'd consider a blocker before merge, plus a handful of smaller must-fixes, some "would be great to have" config ideas, and a few typos. Grouped overview:

🔴 Must-fix before merge

  • Cross-channel info disclosure (security): the bot checks whether it can read the linked channel, but not whether the user who pasted the link can. Right now any member can paste a link to a staff/ticket/mod channel and have its contents reposted into a public channel. This is the big one - details inline.
  • Mention re-pinging (security): quoted content can fire live @role/@user pings (and @everyone on the reply path). Suppress mentions on both send paths.
  • Broken icon: fas fa-chat isn't a valid icon and won't render.
  • Expiring image links: quoted images use raw Discord CDN URLs that 404 after a few hours.
  • Crash if the config arrays aren't set yet (first boot before defaults persist).

🟡 Should-fix / robustness

  • No rate-limit or cooldown - a single user can spam quotes and burn the bot's API budget.
  • Silent failure when "delete trigger" is on but the bot lacks Manage Messages (no log).
  • Attachment forwarding has no count/size cap.
  • Empty >>> blockquote when quoting an image-only message.

🟢 Nice-to-have config ideas (totally your call - the module works without these)

  • A channel allow-list (not just blacklist), a selfQuote toggle, a per-user cooldown, and an optional trigger keyword so quoting is opt-in rather than firing on every link. These mirror what our starboard module offers.
  • Surfacing the original message's timestamp in the default embed, and exposing the author's server nickname + a channel mention as params.

✍️ Typos / copy (these show up in the dashboard, so worth a quick pass) - flagged inline.

Thanks again - this is genuinely close, and the security fix aside it's mostly polish. Happy to clarify any of the comments. 🚀

Comment thread modules/message-quotes/events/messageCreate.js
Comment thread modules/message-quotes/events/messageCreate.js
});
}

const firstAttachment = targetMsg.attachments.first()?.url || '';

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 attachments.first()?.url is a raw Discord CDN URL with an expiry token - the image will 404 within a few hours, so quoted-image embeds break. Our starboard module handles this with archiveDiscordAttachment() (persistent storage) plus a regex fallback that scans message content for image URLs. Worth mirroring that here.

Comment thread modules/message-quotes/events/messageCreate.js Outdated
Comment thread modules/message-quotes/events/messageCreate.js
Comment thread modules/message-quotes/configs/config.json Outdated
Comment thread modules/message-quotes/configs/config.json Outdated
Comment thread modules/message-quotes/configs/config.json Outdated
Comment thread modules/message-quotes/module.json Outdated
Comment thread modules/message-quotes/module.json Outdated
@SCDerox

SCDerox commented Jun 8, 2026

Copy link
Copy Markdown
Member

One more pointer @JeanCoding16 - while you're tweaking config.json (the copy fixes and any of the new config ideas), our config-file docs are handy for previewing how each option renders in the dashboard: https://scnx.app/developers/configuration 👍

@JeanCoding16

Copy link
Copy Markdown
Author

I've changed the points mentioned. I can't view this page; I'm missing the flag: https://scnx.app/developers/configuration. And regarding archiveDiscordAttachment(): that would need to be added separately. I've taken a few steps for it, but I can't implement it in a way that allows me to guarantee the functionality.

@SCDerox

SCDerox commented Jun 9, 2026

Copy link
Copy Markdown
Member

Thanks for the quick turnaround @JeanCoding16. The two security fixes (the member permission check and allowedMentions on both send paths) look correct, and the attachment cap, cooldown, and content fallbacks all work. A few follow-ups below. As before, this was an AI-assisted review, so feel free to push back on anything.

On the persistent images - this is simpler than it looked, so I don't think you need to leave it out. archiveDiscordAttachment(client, url, meta) in src/functions/helpers.js always returns a usable URL: when archival is available it returns a permanent storage URL, and on any failure it returns the original Discord URL unchanged. That covers OSS builds where the storage integration isn't shipped, archival being disabled, rate limits, quota, and upstream errors. So it's a safe drop-in: no worse than what you have now, and persistent automatically when the bot runs with storage enabled. (There's also tryArchiveDiscordAttachment if you ever need to know whether it actually persisted, since it returns null instead of the original URL, but here you want the always-returns-a-URL one.)

starboard already does this if you want a reference: modules/starboard/handleStarboard.js, the archiveDiscordAttachment(...) call plus the msg.content.match(...) image fallback just below it. Applied here it would be roughly:

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 {
    // covers messages that are just a pasted image link
    const imgMatch = targetMsg.content.match(/https?:\/\/\S+\.(?:png|jpe?g|gif|webp)/i);
    if (imgMatch) finalImage = imgMatch[0];
}

That also covers messages that are just a pasted image link with no attachment.

On the cooldown, one thing to check: cooldowns.set(...) currently runs before the link regex, so every message a user sends starts their 5s timer. If someone sends a normal message and then pastes a quote link within 5s, the quote won't fire. Moving the cooldown check and set to after if (!match) return;, so it only counts actual quote triggers, should fix it.

Smaller stuff:

  • module.json description has a typo: "when a user has pastes a message link" should be "when a user pastes a message link".
  • %displayName% is now passed to the embed but isn't listed in the params array in config.json, so admins won't see it in the dashboard. Worth adding an entry.
  • Optional: %timestamp% still isn't used in the default embed, so the original send time isn't shown by default. Adding it to the footer would be a nice touch, but isn't required.

Also, you should have access to the config preview docs now (https://scnx.app/developers/configuration), so previewing the new param and copy changes should work. Sorry about the flag gap earlier.

The %channelMention% reasoning and the [Attachment]/[Sticker]/[None] fallbacks both make sense. Once the cooldown ordering and the image helper are in, I think this is about there.

…ixed and parameter %displayName% added to configuration
@JeanCoding16

Copy link
Copy Markdown
Author

Fixed. I haven't found anything yet on how to replace the footer timestamp with %timestamp%, so it's still not included.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants