plugin: SplitLargeMessages (#249)

* add(SplitLargeMessages)

* Fixes

* Update README.md

---------

Co-authored-by: thororen1234 <78185467+thororen1234@users.noreply.github.com>
This commit is contained in:
Reycko 2025-05-01 18:29:47 +02:00 committed by GitHub
parent 6f422545eb
commit 0d09c083c6
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 163 additions and 2 deletions

View file

@ -11,7 +11,7 @@ You can join our [discord server](https://discord.gg/5Xh2W87egW) for commits, ch
### Extra included plugins ### Extra included plugins
<details> <details>
<summary>169 additional plugins</summary> <summary>170 additional plugins</summary>
### All Platforms ### All Platforms
@ -96,6 +96,7 @@ You can join our [discord server](https://discord.gg/5Xh2W87egW) for commits, ch
- JumpToStart by Samwich - JumpToStart by Samwich
- KeyboardSounds by HypedDomi - KeyboardSounds by HypedDomi
- KeywordNotify by camila314 & x3rt - KeywordNotify by camila314 & x3rt
- - LastActive by Crxa
- LimitMiddleClickPaste by no dev listed - LimitMiddleClickPaste by no dev listed
- LoginWithQR by nexpid - LoginWithQR by nexpid
- MediaPlaybackSpeed by D3SOX - MediaPlaybackSpeed by D3SOX
@ -142,6 +143,7 @@ You can join our [discord server](https://discord.gg/5Xh2W87egW) for commits, ch
- Signature by Ven, Rini, ImBanana, KrystalSkull - Signature by Ven, Rini, ImBanana, KrystalSkull
- Slap by Korbo - Slap by Korbo
- SoundBoardLogger by Moxxie, fres, echo, maintained by thororen - SoundBoardLogger by Moxxie, fres, echo, maintained by thororen
- - SplitLargeMessages by Reycko
- SpotifyLyrics by Joona - SpotifyLyrics by Joona
- StatsfmPresence by Crxa - StatsfmPresence by Crxa
- StatusPresets by iamme - StatusPresets by iamme
@ -177,7 +179,6 @@ You can join our [discord server](https://discord.gg/5Xh2W87egW) for commits, ch
- Woof by Samwich - Woof by Samwich
- WriteUpperCase by Samwich & KrystalSkull - WriteUpperCase by Samwich & KrystalSkull
- YoutubeDescription by arHSM - YoutubeDescription by arHSM
- LastActive by Crxa
### Web Only ### Web Only

View file

@ -0,0 +1,156 @@
/*
* Vencord, a Discord client mod
* Copyright (c) 2025 Vendicated and contributors
* SPDX-License-Identifier: GPL-3.0-or-later
*/
import { addMessagePreSendListener, MessageSendListener, removeMessagePreSendListener } from "@api/MessageEvents";
import { definePluginSettings } from "@api/Settings";
import { EquicordDevs } from "@utils/constants";
import { getCurrentChannel, sendMessage } from "@utils/discord";
import definePlugin, { OptionType } from "@utils/types";
import { ChannelStore, ComponentDispatch, PermissionsBits, UserStore } from "@webpack/common";
let maxLength: number = 0;
const canSplit: () => boolean = () => {
const slowmode = getCurrentChannel()?.rateLimitPerUser ?? 0;
return (settings.store.splitInSlowmode ? slowmode < settings.store.slowmodeMax : slowmode <= 0) && settings.store.disableFileConversion;
};
const autoMaxLength = () => {
const hasNitro = UserStore.getCurrentUser().premiumType === 2;
return hasNitro ? 4000 : 2000;
};
const split = async (channelId: string, chunks: string[], delayInMs: number) => {
const sendChunk = async (chunk: string) => {
await sendMessage(channelId, { content: chunk }, true);
};
// Send the chunks
for (let i = 0; i < chunks.length; i++) {
await sendChunk(chunks[i]);
if (i < chunks.length - 1) // Not the last chunk
await new Promise(resolve => setTimeout(resolve, delayInMs)); // Wait for `delayInMs`
}
};
const listener: MessageSendListener = async (channelId, msg) => {
if (msg.content.trim().length < maxLength || !canSplit()) return; // Nothing to split
const channel = ChannelStore.getChannel(channelId);
// Check for slowmode
let isSlowmode = channel.rateLimitPerUser > 0;
if ((channel.accessPermissions & PermissionsBits.MANAGE_MESSAGES) === PermissionsBits.MANAGE_MESSAGES
|| (channel.accessPermissions & PermissionsBits.MANAGE_CHANNELS) === PermissionsBits.MANAGE_CHANNELS)
isSlowmode = false;
// Not slowmode or splitInSlowmode is on and less than slowmodeMax
if (!isSlowmode || (settings.store.splitInSlowmode && channel.rateLimitPerUser < settings.store.slowmodeMax)) {
const chunks: string[] = [];
const { hardSplit } = settings.store;
while (msg.content.length > maxLength) {
msg.content = msg.content.trim();
// Get last space or newline
const splitIndex = Math.max(msg.content.lastIndexOf(" ", maxLength), msg.content.lastIndexOf("\n", maxLength));
// If hard split is on or neither newline or space found, split at maxLength
if (hardSplit || splitIndex === -1) {
chunks.push(msg.content.slice(0, maxLength));
msg.content = msg.content.slice(maxLength);
}
else {
chunks.push(msg.content.slice(0, splitIndex));
msg.content = msg.content.slice(splitIndex);
}
}
ComponentDispatch.dispatchToLastSubscribed("CLEAR_TEXT");
await split(channelId, [...chunks, msg.content], settings.store.sendDelay * 1000);
}
return { cancel: true };
};
const settings = definePluginSettings({
maxLength: {
type: OptionType.NUMBER,
description: "Maximum length of a message before it is split. Set to 0 to automatically detect.",
default: 0,
max: 4000,
onChange(newValue) {
if (newValue === 0)
maxLength = autoMaxLength();
},
},
disableFileConversion: {
type: OptionType.BOOLEAN,
description: "If true, disables file conversion for large messages.",
default: true,
},
sendDelay: {
type: OptionType.SLIDER,
description: "Delay between each chunk in seconds.",
default: 1,
markers: [1, 2, 3, 5, 10],
},
hardSplit: {
type: OptionType.BOOLEAN,
description: "If true, splits on the last character instead of the last space/newline.",
default: false,
},
splitInSlowmode: {
type: OptionType.BOOLEAN,
description: "Should messages be split if the channel has slowmode enabled?",
},
slowmodeMax: {
type: OptionType.NUMBER,
description: "Maximum slowmode time if splitting in slowmode.",
default: 5,
min: 1,
max: 30,
}
});
export default definePlugin({
name: "SplitLargeMessages",
description: "Splits large messages into multiple to fit Discord's message limit.",
authors: [EquicordDevs.Reycko],
dependencies: ["MessageEventsAPI"],
settings,
start() {
if (settings.store.maxLength === 0)
maxLength = autoMaxLength();
addMessagePreSendListener(listener);
},
stop() {
removeMessagePreSendListener(listener);
},
patches: [
{
find: 'type:"MESSAGE_LENGTH_UPSELL"', // bypass message length check
replacement: {
match: /if\(\i.length>\i/,
replace: "if(false",
}
},
{
find: '(this,"hideAutocomplete"', // disable file conversion
replacement: {
match: /if\(\i.length>\i\)/,
replace: "if(false)",
},
}
]
});

View file

@ -1058,6 +1058,10 @@ export const EquicordDevs = Object.freeze({
name: "Buzzy", name: "Buzzy",
id: 1273353654644117585n id: 1273353654644117585n
}, },
Reycko: {
name: "Reycko",
id: 1123725368004726794n,
}
} satisfies Record<string, Dev>); } satisfies Record<string, Dev>);
// iife so #__PURE__ works correctly // iife so #__PURE__ works correctly