Equicord/src/equicordplugins/sedEnhanced/index.ts
Ecorous cb00a5b2c0
Add SedEnhanced plugin (#1188)
* add SedEnchanced

* [SedEnhanced] add README.md

* [SedEnhanced] clarify in readme

* [SedEnhanced] fix typo in readme

* [SedEnhanced] make `sedRegex` const

* [SedEnhanced] Apply strict checking suggestion

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>

* [SedEnhanced] remove `export` from `currentReply`

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>

* [SedEnhanced] fix dodgy patch regex

* [SedEnhanced] improve patch regex further

* [SedEnhanced] add license header

* [SedEnhancer] add reasonable limits to patch `match`

* [SedEnhancer] improve reply logic

* [SedEnhanced] tighten patch further

* [SedEnhanced] use \i for identifier

* [SedEnhanced] add explanation for large pattern

* [SedEnhanced] fix typescript errors

* [SedEnhanced] fix remaining issues (hopefully)

* remove stuff we dont care about because I hate myself

* Update index.ts

* Update index.ts

* revert `[...flags]` back to `flags`

---------

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
Co-authored-by: thororen1234 <78185467+thororen1234@users.noreply.github.com>
2026-06-24 00:11:47 -04:00

75 lines
2.9 KiB
TypeScript

/*
* Vencord, a Discord client mod
* Copyright (c) 2026 Vendicated and contributors
* SPDX-License-Identifier: GPL-3.0-or-later
*/
import { definePluginSettings } from "@api/Settings";
import { EquicordDevs } from "@utils/constants";
import definePlugin, { OptionType } from "@utils/types";
import { MessageActions, MessageStore, PendingReplyStore, UserStore } from "@webpack/common";
const sedRegex = /^s(?<sep>[/|$#@!])(?<match>(?!\1)(?:(?![^\\]\1).)*.|)\1(?<replace>(?!\1)(?:(?![^\\]\1).)*.|)\1?(?<modes>[rgmisudyv]*)$/;
const settings = definePluginSettings({
regexByDefault: {
description: "Inverts the `r` flag, so using the `r` flag enables non-regex mode, and omitting it uses regex mode.",
type: OptionType.BOOLEAN,
default: false
}
});
export default definePlugin({
name: "SedEnhanced",
description: "Expands on Discord's rudimentary `sed` support.",
authors: [EquicordDevs.dawn, EquicordDevs.Willow, EquicordDevs.kat],
patches: [
{
find: ".SLASH_COMMAND_USED,{",
replacement: {
match: /searchReplace:\{match:(\i\(\)\.anyScopeRegex)/,
replace: "searchReplace:{match:$1($self.sedRegex),action:$self.searchReplace},_eq_$&"
}
}
],
settings,
sedRegex,
searchReplace(content, { isEdit, channel }) {
if (isEdit) return;
const pendingReply = PendingReplyStore.getPendingReply(channel.id)?.message;
const toEdit = pendingReply ?? MessageStore.getLastEditableMessage(channel.id);
if (pendingReply && pendingReply.author.id !== UserStore.getCurrentUser()?.id || toEdit?.id == null) return { content: "" };
const groups = content.match(sedRegex)?.groups;
if (groups?.match == null || groups?.replace == null || groups?.modes == null) return;
let { match: pattern, replace, modes } = groups;
const flags = modes.split("");
const isRegex = flags.includes("r") !== settings.store.regexByDefault;
if (!isRegex) {
const escapeChars = /\\([*?+/])/g;
pattern = pattern.replace(escapeChars, (_, x) => x);
replace = replace.replace(escapeChars, (_, x) => x);
}
let find: string | RegExp = pattern;
if (isRegex) {
try {
const regexFlags = flags.filter(f => "gmisudyv".includes(f)).join("");
find = new RegExp(pattern, regexFlags);
} catch {
return { content: "" };
}
}
const replaced = flags.includes("g") ? toEdit.content.replaceAll(find, replace) : toEdit.content.replace(find, replace);
if (!replaced.trim() && toEdit.attachments.length === 0) {
MessageActions.deleteMessage(channel.id, toEdit.id);
} else if (replaced !== toEdit.content) {
MessageActions.editMessage(channel.id, toEdit.id, { content: replaced });
}
return { content: "" };
}
});