Compare commits
4 commits
patcher-re
...
main
Author | SHA1 | Date | |
---|---|---|---|
bd1e45adc4 | |||
|
4f5ebec4bb | ||
|
7b9f0a36ba | ||
|
fc4e95806d |
6 changed files with 231 additions and 21 deletions
|
@ -37,13 +37,9 @@ export default definePlugin({
|
||||||
{
|
{
|
||||||
find: ".handleSendMessage,onResize",
|
find: ".handleSendMessage,onResize",
|
||||||
replacement: {
|
replacement: {
|
||||||
// FIXME: Simplify this change once all branches share the same code
|
// https://regex101.com/r/hBlXpl/1
|
||||||
// props.chatInputType...then((function(isMessageValid)... var parsedMessage = b.c.parse(channel,... var replyOptions = f.g.getSendMessageOptionsForReply(pendingReply);
|
match: /let (\i)=\i\.\i\.parse\((\i),.+?let (\i)=\i\.\i\.getSendMessageOptions\(\{.+?\}\);(?<=\)\(({.+?})\)\.then.+?)/,
|
||||||
// Lookbehind: validateMessage)({openWarningPopout:..., type: i.props.chatInputType, content: t, stickers: r, ...}).then((function(isMessageValid)
|
replace: (m, parsedMessage, channel, replyOptions, extra) => m +
|
||||||
match: /(\{openWarningPopout:.{0,100}type:this.props.chatInputType.+?\.then\((?:async )?)(\i=>\{.+?let (\i)=\i\.\i\.parse\((\i),.+?let (\i)=\i\.\i\.getSendMessageOptions\(\{.+?\}\);)(?<=\)\(({.+?})\)\.then.+?)/,
|
|
||||||
// props.chatInputType...then((async function(isMessageValid)... var replyOptions = f.g.getSendMessageOptionsForReply(pendingReply); if(await Vencord.api...) return { shoudClear:true, shouldRefocus:true };
|
|
||||||
replace: (_, rest1, rest2, parsedMessage, channel, replyOptions, extra) => "" +
|
|
||||||
`${rest1}${rest1.includes("async") ? "" : "async "}${rest2}` +
|
|
||||||
`if(await Vencord.Api.MessageEvents._handlePreSend(${channel}.id,${parsedMessage},${extra},${replyOptions}))` +
|
`if(await Vencord.Api.MessageEvents._handlePreSend(${channel}.id,${parsedMessage},${extra},${replyOptions}))` +
|
||||||
"return{shouldClear:false,shouldRefocus:true};"
|
"return{shouldClear:false,shouldRefocus:true};"
|
||||||
}
|
}
|
||||||
|
@ -53,8 +49,7 @@ export default definePlugin({
|
||||||
replacement: {
|
replacement: {
|
||||||
match: /let\{id:\i}=(\i),{id:\i}=(\i);return \i\.useCallback\((\i)=>\{/,
|
match: /let\{id:\i}=(\i),{id:\i}=(\i);return \i\.useCallback\((\i)=>\{/,
|
||||||
replace: (m, message, channel, event) =>
|
replace: (m, message, channel, event) =>
|
||||||
// the message param is shadowed by the event param, so need to alias them
|
`const vcMsg=${message},vcChan=${channel};${m}Vencord.Api.MessageEvents._handleClick(vcMsg,vcChan,${event});`
|
||||||
`const vcMsg=${message},vcChan=${channel};${m}Vencord.Api.MessageEvents._handleClick(vcMsg, vcChan, ${event});`
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
179
src/plugins/antiTessie/index.ts
Normal file
179
src/plugins/antiTessie/index.ts
Normal file
|
@ -0,0 +1,179 @@
|
||||||
|
/*
|
||||||
|
* Vencord, a Discord client mod
|
||||||
|
* Copyright (c) 2025 Vendicated and contributors
|
||||||
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { MessageExtra, MessageObject } from "@api/MessageEvents";
|
||||||
|
import { Devs } from "@utils/constants";
|
||||||
|
import { Logger } from "@utils/Logger";
|
||||||
|
import definePlugin from "@utils/types";
|
||||||
|
import { findLazy } from "@webpack";
|
||||||
|
|
||||||
|
const TesseractLogger = new Logger("Tesseract", "#ff9e64");
|
||||||
|
|
||||||
|
let worker!: Tesseract.Worker;
|
||||||
|
function reduceBboxGreatest(acc: Tesseract.Bbox, cur: Tesseract.Bbox): Tesseract.Bbox {
|
||||||
|
return {
|
||||||
|
x0: Math.min(acc.x0, cur.x0),
|
||||||
|
x1: Math.max(acc.x1, cur.x1),
|
||||||
|
y0: Math.min(acc.y0, cur.y0),
|
||||||
|
y1: Math.max(acc.y1, cur.y1)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
function findWordLocation(text: Tesseract.Block[], regex: RegExp): Tesseract.Bbox[] {
|
||||||
|
const locs: Tesseract.Bbox[] = [];
|
||||||
|
for (let i = 0; i < text.length; i++) {
|
||||||
|
const block = text[i];
|
||||||
|
if (block.text.match(regex)) {
|
||||||
|
const bl = locs.length;
|
||||||
|
for (let j = 0; j < block.paragraphs.length; j++) {
|
||||||
|
const paragraph = block.paragraphs[j];
|
||||||
|
if (paragraph.text.match(regex)) {
|
||||||
|
const bl = locs.length;
|
||||||
|
for (let k = 0; k < paragraph.lines.length; k++) {
|
||||||
|
const line = paragraph.lines[k];
|
||||||
|
if (line.text.match(regex)) {
|
||||||
|
const bl = locs.length;
|
||||||
|
for (let l = 0; l < line.words.length; l++) {
|
||||||
|
const word = line.words[l];
|
||||||
|
let matches: RegExpExecArray[];
|
||||||
|
if ((matches = [...word.text.matchAll(new RegExp(regex, `${regex.flags.replace("g", "")}g`))]).length) {
|
||||||
|
for (const match of matches) {
|
||||||
|
const syms = word.symbols
|
||||||
|
.slice(match.index, match.index + match[0].length)
|
||||||
|
.map(x => x.bbox)
|
||||||
|
.reduce(reduceBboxGreatest);
|
||||||
|
locs.push(syms);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (locs.length === bl) {
|
||||||
|
locs.push(line.bbox);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (locs.length === bl) {
|
||||||
|
locs.push(paragraph.bbox);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (locs.length === bl) {
|
||||||
|
locs.push(block.bbox);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return locs;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface CloudUpload {
|
||||||
|
new(file: { file: File; isThumbnail: boolean; platform: number; }, channelId: string, showDiaglog: boolean, numCurAttachments: number): CloudUpload;
|
||||||
|
upload(): void;
|
||||||
|
}
|
||||||
|
const CloudUpload: CloudUpload = findLazy(m => m.prototype?.trackUploadFinished);
|
||||||
|
|
||||||
|
function getImage(file: File): Promise<HTMLImageElement> {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const img = new Image();
|
||||||
|
img.src = URL.createObjectURL(file);
|
||||||
|
img.onload = () => resolve(img);
|
||||||
|
img.onerror = reject;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function canvasToBlob(canvas: HTMLCanvasElement): Promise<Blob> {
|
||||||
|
return new Promise<Blob>(resolve => {
|
||||||
|
canvas.toBlob(blob => {
|
||||||
|
if (blob) {
|
||||||
|
resolve(blob);
|
||||||
|
} else {
|
||||||
|
throw new Error("Failed to create Blob");
|
||||||
|
}
|
||||||
|
}, "image/png");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
const badRegex = /nix(?:os)?|This ?content ?is|blocked ?by ?this ?server/i;
|
||||||
|
export default definePlugin({
|
||||||
|
name: "AntiTessie",
|
||||||
|
authors: [Devs.sadan],
|
||||||
|
description: "Scans your messages with ocr for anything that matches the selected regex, and if found, blurs it",
|
||||||
|
|
||||||
|
start() {
|
||||||
|
if (!window?.Tesseract) {
|
||||||
|
fetch(
|
||||||
|
"https://unpkg.com/tesseract.js@6.0.0/dist/tesseract.min.js"
|
||||||
|
)
|
||||||
|
.then(async r => void (0, eval)(await r.text()))
|
||||||
|
.then(async () => {
|
||||||
|
worker = await Tesseract.createWorker("eng", Tesseract.OEM.TESSERACT_LSTM_COMBINED, {
|
||||||
|
corePath: "https://unpkg.com/tesseract.js-core@6.0.0/tesseract-core-simd-lstm.wasm.js",
|
||||||
|
workerPath: "https://unpkg.com/tesseract.js@6.0.0/dist/worker.min.js",
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
worker.setParameters({
|
||||||
|
tessedit_pageseg_mode: Tesseract.PSM.AUTO
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
stop() {
|
||||||
|
worker.terminate();
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
async onBeforeMessageSend(channelId: string, message: MessageObject, extra: MessageExtra): Promise<void | { cancel: boolean; }> {
|
||||||
|
if (extra.channel.guild_id !== "1015060230222131221" && extra.channel.guild_id !== "1041012073603289109") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const uploads = extra?.uploads ?? [];
|
||||||
|
for (let i = 0; i < uploads.length; i++) {
|
||||||
|
async function convertToFile(canvas: HTMLCanvasElement): Promise<File> {
|
||||||
|
const blob = await canvasToBlob(canvas);
|
||||||
|
return new File([blob], `${upload.filename.substring(0, upload.filename.lastIndexOf("."))}.png`, {
|
||||||
|
type: "image/png"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
const upload = uploads[i];
|
||||||
|
|
||||||
|
if (!upload.isImage) continue;
|
||||||
|
|
||||||
|
console.log(upload);
|
||||||
|
|
||||||
|
const ret = await worker.recognize(upload.item.file, {
|
||||||
|
}, {
|
||||||
|
text: true,
|
||||||
|
blocks: true,
|
||||||
|
});
|
||||||
|
if (ret.data.text.match(badRegex)) {
|
||||||
|
const toBlur = findWordLocation(ret.data.blocks!, badRegex);
|
||||||
|
|
||||||
|
const sourceImage = await getImage(upload.item.file);
|
||||||
|
const width = sourceImage.naturalWidth;
|
||||||
|
const height = sourceImage.naturalHeight;
|
||||||
|
|
||||||
|
const canvas = document.createElement("canvas");
|
||||||
|
const ctx = canvas.getContext("2d")!;
|
||||||
|
ctx.canvas.width = width;
|
||||||
|
ctx.canvas.height = height;
|
||||||
|
|
||||||
|
ctx.drawImage(sourceImage, 0, 0, width, height);
|
||||||
|
for (const { x0, x1, y0, y1 } of toBlur) {
|
||||||
|
ctx.fillStyle = "black";
|
||||||
|
ctx.fillRect(x0, y0, x1 - x0, y1 - y0);
|
||||||
|
}
|
||||||
|
const newFile = await convertToFile(canvas);
|
||||||
|
const attachment = new CloudUpload({
|
||||||
|
file: newFile,
|
||||||
|
isThumbnail: false,
|
||||||
|
platform: 1
|
||||||
|
}, channelId, false, uploads.length);
|
||||||
|
attachment.upload();
|
||||||
|
extra.uploads![i] = attachment as any;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
|
@ -8,6 +8,7 @@ import ErrorBoundary from "@components/ErrorBoundary";
|
||||||
import { Devs } from "@utils/constants";
|
import { Devs } from "@utils/constants";
|
||||||
import definePlugin from "@utils/types";
|
import definePlugin from "@utils/types";
|
||||||
import { findComponentByCodeLazy } from "@webpack";
|
import { findComponentByCodeLazy } from "@webpack";
|
||||||
|
import { UserStore, useStateFromStores } from "@webpack/common";
|
||||||
import { ReactNode } from "react";
|
import { ReactNode } from "react";
|
||||||
|
|
||||||
const UserMentionComponent = findComponentByCodeLazy(".USER_MENTION)");
|
const UserMentionComponent = findComponentByCodeLazy(".USER_MENTION)");
|
||||||
|
@ -34,14 +35,19 @@ export default definePlugin({
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|
||||||
UserMentionComponent: ErrorBoundary.wrap((props: UserMentionComponentProps) => (
|
UserMentionComponent: ErrorBoundary.wrap((props: UserMentionComponentProps) => {
|
||||||
<UserMentionComponent
|
const user = useStateFromStores([UserStore], () => UserStore.getUser(props.id));
|
||||||
|
if (user == null) {
|
||||||
|
return props.originalComponent();
|
||||||
|
}
|
||||||
|
|
||||||
|
return <UserMentionComponent
|
||||||
// This seems to be constant
|
// This seems to be constant
|
||||||
className="mention"
|
className="mention"
|
||||||
userId={props.id}
|
userId={props.id}
|
||||||
channelId={props.channelId}
|
channelId={props.channelId}
|
||||||
/>
|
/>;
|
||||||
), {
|
}, {
|
||||||
fallback: ({ wrappedProps: { originalComponent } }) => originalComponent()
|
fallback: ({ wrappedProps: { originalComponent } }) => originalComponent()
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
|
@ -50,9 +50,9 @@ export default definePlugin({
|
||||||
{
|
{
|
||||||
find: "#{intl::FRIENDS_SECTION_ONLINE}",
|
find: "#{intl::FRIENDS_SECTION_ONLINE}",
|
||||||
replacement: {
|
replacement: {
|
||||||
match: /(\(0,\i\.jsx\)\(\i\.\i\.Item,\{id:\i\.\i)\.BLOCKED,className:([^\s]+?)\.item,children:\i\.\i\.string\(\i\.\i#{intl::BLOCKED}\)\}\)/,
|
match: /,{id:(\i\.\i)\.BLOCKED,show:.+?className:(\i\.item)/,
|
||||||
replace: "$1.IMPLICIT,className:$2.item,children:\"Implicit\"}),$&"
|
replace: (rest, relationShipTypes, className) => `,{id:${relationShipTypes}.IMPLICIT,show:true,className:${className},content:"Implicit"}${rest}`
|
||||||
},
|
}
|
||||||
},
|
},
|
||||||
// Sections content
|
// Sections content
|
||||||
{
|
{
|
||||||
|
|
|
@ -41,13 +41,25 @@ const settings = definePluginSettings({
|
||||||
restartNeeded: true,
|
restartNeeded: true,
|
||||||
type: OptionType.BOOLEAN,
|
type: OptionType.BOOLEAN,
|
||||||
default: true
|
default: true
|
||||||
|
},
|
||||||
|
applyColorOnlyToUsersWithoutColor: {
|
||||||
|
description: "Apply colors only to users who don't have a predefined color",
|
||||||
|
restartNeeded: false,
|
||||||
|
type: OptionType.BOOLEAN,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
applyColorOnlyInDms: {
|
||||||
|
description: "Apply colors only in direct messages; do not apply colors in servers.",
|
||||||
|
restartNeeded: false,
|
||||||
|
type: OptionType.BOOLEAN,
|
||||||
|
default: false
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
export default definePlugin({
|
export default definePlugin({
|
||||||
name: "IrcColors",
|
name: "IrcColors",
|
||||||
description: "Makes username colors in chat unique, like in IRC clients",
|
description: "Makes username colors in chat unique, like in IRC clients",
|
||||||
authors: [Devs.Grzesiek11],
|
authors: [Devs.Grzesiek11, Devs.jamesbt365],
|
||||||
settings,
|
settings,
|
||||||
|
|
||||||
patches: [
|
patches: [
|
||||||
|
@ -70,10 +82,28 @@ export default definePlugin({
|
||||||
|
|
||||||
calculateNameColorForMessageContext(context: any) {
|
calculateNameColorForMessageContext(context: any) {
|
||||||
const id = context?.message?.author?.id;
|
const id = context?.message?.author?.id;
|
||||||
return calculateNameColorForUser(id);
|
const colorString = context?.author?.colorString;
|
||||||
|
const color = calculateNameColorForUser(id);
|
||||||
|
|
||||||
|
if (settings.store.applyColorOnlyInDms && !context?.channel?.isPrivate()) {
|
||||||
|
return colorString;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (!settings.store.applyColorOnlyToUsersWithoutColor || !colorString)
|
||||||
|
? color
|
||||||
|
: colorString;
|
||||||
},
|
},
|
||||||
calculateNameColorForListContext(context: any) {
|
calculateNameColorForListContext(context: any) {
|
||||||
const id = context?.user?.id;
|
const id = context?.user?.id;
|
||||||
return calculateNameColorForUser(id);
|
const colorString = context?.colorString;
|
||||||
|
const color = calculateNameColorForUser(id);
|
||||||
|
|
||||||
|
if (settings.store.applyColorOnlyInDms && !context?.channel?.isPrivate()) {
|
||||||
|
return colorString;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (!settings.store.applyColorOnlyToUsersWithoutColor || !colorString)
|
||||||
|
? color
|
||||||
|
: colorString;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -25,9 +25,9 @@ export default definePlugin({
|
||||||
settings,
|
settings,
|
||||||
patches: [
|
patches: [
|
||||||
{
|
{
|
||||||
find: "_ensureAudio(){",
|
find: "ensureAudio(){",
|
||||||
replacement: {
|
replacement: {
|
||||||
match: /(?=Math\.min\(\i\.\i\.getOutputVolume\(\)\/100)/,
|
match: /(?=Math\.min\(\i\.\i\.getOutputVolume\(\)\/100)/g,
|
||||||
replace: "$self.settings.store.notificationVolume/100*"
|
replace: "$self.settings.store.notificationVolume/100*"
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
Loading…
Add table
Reference in a new issue