Compare commits
14 commits
f33e0dcb73
...
87d3e0e5d8
Author | SHA1 | Date | |
---|---|---|---|
87d3e0e5d8 | |||
e730208cf2 | |||
d8cfc88f02 | |||
2ea7627559 | |||
ce4116ffb9 | |||
f1ad566f10 | |||
70f054c284 | |||
|
48a9aef2eb | ||
|
fc731bc6c8 | ||
|
0a0bd6a713 | ||
|
40a8cf1a85 | ||
|
5f1c5fa370 | ||
|
00c968473e | ||
|
2dc8c2bf76 |
23 changed files with 966 additions and 50 deletions
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"name": "vencord",
|
||||
"private": "true",
|
||||
"version": "1.10.8",
|
||||
"version": "1.10.9",
|
||||
"description": "The cutest Discord client mod",
|
||||
"homepage": "https://github.com/Vendicated/Vencord#readme",
|
||||
"bugs": {
|
||||
|
@ -39,8 +39,10 @@
|
|||
"@sapphi-red/web-noise-suppressor": "0.3.5",
|
||||
"@vap/core": "0.0.12",
|
||||
"@vap/shiki": "0.10.5",
|
||||
"dompurify": "^3.1.7",
|
||||
"fflate": "^0.8.2",
|
||||
"gifenc": "github:mattdesl/gifenc#64842fca317b112a8590f8fef2bf3825da8f6fe3",
|
||||
"katex": "^0.16.11",
|
||||
"monaco-editor": "^0.50.0",
|
||||
"nanoid": "^5.0.7",
|
||||
"virtual-merge": "^1.0.1"
|
||||
|
@ -49,6 +51,8 @@
|
|||
"@stylistic/eslint-plugin": "^2.6.1",
|
||||
"@types/chrome": "^0.0.269",
|
||||
"@types/diff": "^5.2.1",
|
||||
"@types/dompurify": "^3.0.5",
|
||||
"@types/katex": "^0.16.7",
|
||||
"@types/lodash": "^4.17.7",
|
||||
"@types/node": "^22.0.3",
|
||||
"@types/react": "^18.3.3",
|
||||
|
@ -107,6 +111,6 @@
|
|||
},
|
||||
"engines": {
|
||||
"node": ">=18",
|
||||
"pnpm": ">=9"
|
||||
"pnpm": "*"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,12 +28,18 @@ importers:
|
|||
'@vap/shiki':
|
||||
specifier: 0.10.5
|
||||
version: 0.10.5
|
||||
dompurify:
|
||||
specifier: ^3.1.7
|
||||
version: 3.1.7
|
||||
fflate:
|
||||
specifier: ^0.8.2
|
||||
version: 0.8.2
|
||||
gifenc:
|
||||
specifier: github:mattdesl/gifenc#64842fca317b112a8590f8fef2bf3825da8f6fe3
|
||||
version: https://codeload.github.com/mattdesl/gifenc/tar.gz/64842fca317b112a8590f8fef2bf3825da8f6fe3
|
||||
katex:
|
||||
specifier: ^0.16.11
|
||||
version: 0.16.11
|
||||
monaco-editor:
|
||||
specifier: ^0.50.0
|
||||
version: 0.50.0
|
||||
|
@ -53,6 +59,12 @@ importers:
|
|||
'@types/diff':
|
||||
specifier: ^5.2.1
|
||||
version: 5.2.1
|
||||
'@types/dompurify':
|
||||
specifier: ^3.0.5
|
||||
version: 3.0.5
|
||||
'@types/katex':
|
||||
specifier: ^0.16.7
|
||||
version: 0.16.7
|
||||
'@types/lodash':
|
||||
specifier: ^4.17.7
|
||||
version: 4.17.7
|
||||
|
@ -622,6 +634,9 @@ packages:
|
|||
'@types/diff@5.2.1':
|
||||
resolution: {integrity: sha512-uxpcuwWJGhe2AR1g8hD9F5OYGCqjqWnBUQFD8gMZsDbv8oPHzxJF6iMO6n8Tk0AdzlxoaaoQhOYlIg/PukVU8g==}
|
||||
|
||||
'@types/dompurify@3.0.5':
|
||||
resolution: {integrity: sha512-1Wg0g3BtQF7sSb27fJQAKck1HECM6zV1EB66j8JH9i3LCjYabJa0FSdiSgsD5K/RbrsR0SiraKacLB+T8ZVYAg==}
|
||||
|
||||
'@types/eslint@9.6.0':
|
||||
resolution: {integrity: sha512-gi6WQJ7cHRgZxtkQEoyHMppPjq9Kxo5Tjn2prSKDSmZrCz8TZ3jSRCeTJm+WoM+oB0WG37bRqLzaaU3q7JypGg==}
|
||||
|
||||
|
@ -649,6 +664,9 @@ packages:
|
|||
'@types/jsonfile@6.1.4':
|
||||
resolution: {integrity: sha512-D5qGUYwjvnNNextdU59/+fI+spnwtTFmyQP0h+PfIOSkNfpU6AOICUOkm4i0OnSk+NyjdPJrxCDro0sJsWlRpQ==}
|
||||
|
||||
'@types/katex@0.16.7':
|
||||
resolution: {integrity: sha512-HMwFiRujE5PjrgwHQ25+bsLJgowjGjm5Z8FVSf0N6PwgJrwxH0QxzHYDcKsTfV3wva0vzrpqMTJS2jXPr5BMEQ==}
|
||||
|
||||
'@types/lodash@4.14.194':
|
||||
resolution: {integrity: sha512-r22s9tAS7imvBt2lyHC9B8AGwWnXaYb1tY09oyLkXDs4vArpYJzw09nj8MLx5VfciBPGIb+ZwG0ssYnEPJxn/g==}
|
||||
|
||||
|
@ -688,6 +706,9 @@ packages:
|
|||
'@types/scheduler@0.16.3':
|
||||
resolution: {integrity: sha512-5cJ8CB4yAx7BH1oMvdU0Jh9lrEXyPkar6F9G/ERswkCuvP4KQZfZkSjcMbAICCpQTN4OuZn8tz0HiKv9TGZgrQ==}
|
||||
|
||||
'@types/trusted-types@2.0.7':
|
||||
resolution: {integrity: sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==}
|
||||
|
||||
'@types/yauzl@2.10.3':
|
||||
resolution: {integrity: sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==}
|
||||
|
||||
|
@ -995,6 +1016,10 @@ packages:
|
|||
commander@2.20.3:
|
||||
resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==}
|
||||
|
||||
commander@8.3.0:
|
||||
resolution: {integrity: sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==}
|
||||
engines: {node: '>= 12'}
|
||||
|
||||
component-emitter@1.3.0:
|
||||
resolution: {integrity: sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==}
|
||||
|
||||
|
@ -1127,6 +1152,9 @@ packages:
|
|||
resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
||||
dompurify@3.1.7:
|
||||
resolution: {integrity: sha512-VaTstWtsneJY8xzy7DekmYWEOZcmzIe3Qb3zPd4STve1OBTa+e+WmS1ITQec1fZYXI3HCsOZZiSMpG6oxoWMWQ==}
|
||||
|
||||
dot-case@3.0.4:
|
||||
resolution: {integrity: sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==}
|
||||
|
||||
|
@ -1945,6 +1973,10 @@ packages:
|
|||
jszip@2.7.0:
|
||||
resolution: {integrity: sha512-JIsRKRVC3gTRo2vM4Wy9WBC3TRcfnIZU8k65Phi3izkvPH975FowRYtKGT6PxevA0XnJ/yO8b0QwV0ydVyQwfw==}
|
||||
|
||||
katex@0.16.11:
|
||||
resolution: {integrity: sha512-RQrI8rlHY92OLf3rho/Ts8i/XvjgguEjOkO1BEXcU3N8BqPpSzBNwV/G0Ukr+P/l3ivvJUE/Fa/CwbS6HesGNQ==}
|
||||
hasBin: true
|
||||
|
||||
keyv@4.5.4:
|
||||
resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==}
|
||||
|
||||
|
@ -3052,6 +3084,10 @@ snapshots:
|
|||
|
||||
'@types/diff@5.2.1': {}
|
||||
|
||||
'@types/dompurify@3.0.5':
|
||||
dependencies:
|
||||
'@types/trusted-types': 2.0.7
|
||||
|
||||
'@types/eslint@9.6.0':
|
||||
dependencies:
|
||||
'@types/estree': 1.0.5
|
||||
|
@ -3080,6 +3116,8 @@ snapshots:
|
|||
dependencies:
|
||||
'@types/node': 18.16.3
|
||||
|
||||
'@types/katex@0.16.7': {}
|
||||
|
||||
'@types/lodash@4.14.194': {}
|
||||
|
||||
'@types/lodash@4.17.7': {}
|
||||
|
@ -3124,6 +3162,8 @@ snapshots:
|
|||
|
||||
'@types/scheduler@0.16.3': {}
|
||||
|
||||
'@types/trusted-types@2.0.7': {}
|
||||
|
||||
'@types/yauzl@2.10.3':
|
||||
dependencies:
|
||||
'@types/node': 22.0.3
|
||||
|
@ -3494,6 +3534,8 @@ snapshots:
|
|||
|
||||
commander@2.20.3: {}
|
||||
|
||||
commander@8.3.0: {}
|
||||
|
||||
component-emitter@1.3.0: {}
|
||||
|
||||
concat-map@0.0.1: {}
|
||||
|
@ -3612,6 +3654,8 @@ snapshots:
|
|||
dependencies:
|
||||
esutils: 2.0.3
|
||||
|
||||
dompurify@3.1.7: {}
|
||||
|
||||
dot-case@3.0.4:
|
||||
dependencies:
|
||||
no-case: 3.0.4
|
||||
|
@ -4503,6 +4547,10 @@ snapshots:
|
|||
dependencies:
|
||||
pako: 1.0.11
|
||||
|
||||
katex@0.16.11:
|
||||
dependencies:
|
||||
commander: 8.3.0
|
||||
|
||||
keyv@4.5.4:
|
||||
dependencies:
|
||||
json-buffer: 3.0.1
|
||||
|
|
|
@ -59,6 +59,14 @@ export default definePlugin({
|
|||
replace: "$&return;"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
find: ".BetterDiscord||null!=",
|
||||
replacement: {
|
||||
// Make hasClientMods return false
|
||||
match: /(?=let \i=window;)/,
|
||||
replace: "return false;"
|
||||
}
|
||||
}
|
||||
],
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import { canonicalizeMatch } from "@utils/patches";
|
||||
import { execFile } from "child_process";
|
||||
import { promisify } from "util";
|
||||
|
||||
|
@ -26,7 +27,7 @@ interface RemoteData {
|
|||
let cachedRemoteData: { id: string, data: RemoteData; } | { id: string, failures: number; } | null = null;
|
||||
|
||||
const APPLE_MUSIC_BUNDLE_REGEX = /<script type="module" crossorigin src="([a-zA-Z0-9.\-/]+)"><\/script>/;
|
||||
const APPLE_MUSIC_TOKEN_REGEX = /\w+="([A-Za-z0-9-_]*\.[A-Za-z0-9-_]*\.[A-Za-z0-9-_]*)",\w+="x-apple-jingle-correlation-key"/;
|
||||
const APPLE_MUSIC_TOKEN_REGEX = canonicalizeMatch(/Promise.allSettled\(\i\)\}const \i="([A-Za-z0-9-_]*\.[A-Za-z0-9-_]*\.[A-Za-z0-9-_]*)"/);
|
||||
|
||||
let cachedToken: string | undefined = undefined;
|
||||
|
||||
|
|
43
src/plugins/badge.ts
Normal file
43
src/plugins/badge.ts
Normal file
|
@ -0,0 +1,43 @@
|
|||
/* eslint-disable header/header */
|
||||
import { BadgePosition, ProfileBadge } from "@api/Badges";
|
||||
import { Badges } from "@api/index";
|
||||
import { Devs } from "@utils/constants";
|
||||
import definePlugin from "@utils/types";
|
||||
import { UserStore } from "@webpack/common";
|
||||
|
||||
const SHIGGY_BADGE = "https://cdn.discordapp.com/emojis/1101838344146665502.gif?size=240&quality=lossless";
|
||||
const BLOBFOXBOX_BADGE = "https://cdn.discordapp.com/emojis/1036216552736952350.webp?size=240&quality=lossless";
|
||||
|
||||
const ShiggyBadge: ProfileBadge = {
|
||||
description: "true shiggy fan",
|
||||
image: SHIGGY_BADGE,
|
||||
position: BadgePosition.START,
|
||||
props: {
|
||||
style: { transform: "scale(0.9)" }
|
||||
},
|
||||
shouldShow: ({ user }) => user.id === UserStore.getCurrentUser().id,
|
||||
link: "https://ryanccn.dev/"
|
||||
};
|
||||
const BlobfoxBoxBadge: ProfileBadge = {
|
||||
description: "blobfox",
|
||||
image: BLOBFOXBOX_BADGE,
|
||||
position: BadgePosition.START,
|
||||
props: {
|
||||
style: { transform: "scale(0.9)" }
|
||||
},
|
||||
shouldShow: ({ user }) => user.id === UserStore.getCurrentUser().id,
|
||||
link: "https://ryanccn.dev/"
|
||||
};
|
||||
|
||||
export default definePlugin({
|
||||
name: "Ryan's Extra Badges",
|
||||
description: "shiggy",
|
||||
authors: [Devs.RyanCaoDev],
|
||||
dependencies: ["BadgeAPI"],
|
||||
|
||||
|
||||
start() {
|
||||
Badges.addBadge(ShiggyBadge);
|
||||
Badges.addBadge(BlobfoxBoxBadge);
|
||||
},
|
||||
});
|
63
src/plugins/bottom/components/Indicator.tsx
Normal file
63
src/plugins/bottom/components/Indicator.tsx
Normal file
|
@ -0,0 +1,63 @@
|
|||
/*
|
||||
* Vencord, a modification for Discord's desktop app
|
||||
* Copyright (c) 2023 Vendicated and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This plugin was modified from code licensed under the following license:
|
||||
*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2021-present Sebastian Law
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
import { findByPropsLazy } from "@webpack";
|
||||
import { Tooltip } from "@webpack/common";
|
||||
|
||||
export default function Indicator({ layers, bottom }: { layers: number; bottom: boolean; }) {
|
||||
return (
|
||||
<Tooltip color="black" position="top" text={layers <= 1 ? "🥺" : `Decoded from ${layers} nested bottom messages`}>
|
||||
{({ onMouseLeave, onMouseEnter }) => (
|
||||
<span
|
||||
className={`power-bottom-indicator ${findByPropsLazy("edited").edited}`}
|
||||
style={{ color: "var(--text-muted)" }}
|
||||
onMouseEnter={onMouseEnter}
|
||||
onMouseLeave={onMouseLeave}
|
||||
>
|
||||
{bottom ? "(bottom)" : "(original)"}
|
||||
</span>
|
||||
)}
|
||||
</Tooltip>
|
||||
);
|
||||
}
|
73
src/plugins/bottom/encoding.ts
Normal file
73
src/plugins/bottom/encoding.ts
Normal file
|
@ -0,0 +1,73 @@
|
|||
/*
|
||||
* Vencord, a modification for Discord's desktop app
|
||||
* Copyright (c) 2023 Vendicated and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This file was adapted from https://github.com/bottom-software-foundation/bottom-js
|
||||
* Which is, hopefully, licensed under MIT.
|
||||
*/
|
||||
|
||||
const CHARACTER_VALUES: [number, string][] = [
|
||||
[200, "🫂"],
|
||||
[50, "💖"],
|
||||
[10, "✨"],
|
||||
[5, "🥺"],
|
||||
[1, ","],
|
||||
[0, "❤️"],
|
||||
];
|
||||
const SECTION_SEPERATOR = "👉👈";
|
||||
const FINAL_TERMINATOR = new RegExp(`(${SECTION_SEPERATOR})?$`);
|
||||
|
||||
function encodeChar(charValue: number): string {
|
||||
if (charValue === 0) return "";
|
||||
const [val, currentCase]: [number, string] =
|
||||
CHARACTER_VALUES.find(([val]) => charValue >= val) || CHARACTER_VALUES[-1];
|
||||
return `${currentCase}${encodeChar(charValue - val)}`;
|
||||
}
|
||||
|
||||
export function encode(value: string): string {
|
||||
return Array.from(new TextEncoder().encode(value))
|
||||
.map((v: number) => encodeChar(v) + SECTION_SEPERATOR)
|
||||
.join("");
|
||||
}
|
||||
|
||||
export function decode(value: string): string {
|
||||
return new TextDecoder().decode(Uint8Array.from(
|
||||
value
|
||||
.trim()
|
||||
.replace(FINAL_TERMINATOR, "")
|
||||
.split(SECTION_SEPERATOR)
|
||||
.map(letters => {
|
||||
return Array.from(letters)
|
||||
.map(character => {
|
||||
const [value, emoji]: [number, string] = CHARACTER_VALUES.find(
|
||||
([_, em]) => em === character
|
||||
) || [-1, ""];
|
||||
if (!emoji) {
|
||||
throw new TypeError(`Invalid bottom text: '${character}'`);
|
||||
}
|
||||
return value;
|
||||
})
|
||||
.reduce((p, c) => p + c);
|
||||
})
|
||||
));
|
||||
}
|
||||
|
||||
export default {
|
||||
encode: encode,
|
||||
decode: decode
|
||||
};
|
158
src/plugins/bottom/handler.ts
Normal file
158
src/plugins/bottom/handler.ts
Normal file
|
@ -0,0 +1,158 @@
|
|||
/*
|
||||
* Vencord, a modification for Discord's desktop app
|
||||
* Copyright (c) 2023 Vendicated and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This plugin was modified from code licensed under the following license:
|
||||
*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2021-present Sebastian Law
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
import { FluxDispatcher, MessageStore } from "@webpack/common";
|
||||
import type { Message } from "discord-types/general";
|
||||
|
||||
import Bottom from "./encoding";
|
||||
|
||||
class BottomHandler {
|
||||
|
||||
cache: Record<string, Record<string, { originalContent: string; top?: boolean; layers?: number; }>>;
|
||||
re: RegExp;
|
||||
|
||||
constructor() {
|
||||
this.cache = {};
|
||||
this.re = /((?:((?:\uD83E\uDEC2)?(?:💖)*(?:✨)*(?:🥺)*(?:,)*(❤️)?)(?:👉👈|\u200b))+)/gm;
|
||||
}
|
||||
|
||||
isTranslated(message) {
|
||||
if (
|
||||
!this.cache[message.channel_id] ||
|
||||
!this.cache[message.channel_id][message.id]
|
||||
) { return false; }
|
||||
|
||||
return this.cache[message.channel_id][message.id].originalContent !== message.content;
|
||||
}
|
||||
|
||||
translate(text: string, notNested: boolean) {
|
||||
var original = text;
|
||||
var translated = text;
|
||||
var layers = 0;
|
||||
while (original.match(this.re)) {
|
||||
translated = original.replace(this.re, (str, p1, offset, s) => Bottom.decode(p1) || p1);
|
||||
|
||||
// the regex can sometimes pick up invalid bottom in which case we want to return to avoid an infinite loop
|
||||
if (translated === original || notNested) break;
|
||||
else {
|
||||
original = translated;
|
||||
layers++;
|
||||
}
|
||||
}
|
||||
return {
|
||||
translated: translated,
|
||||
layers: layers,
|
||||
};
|
||||
}
|
||||
|
||||
translateMessage(message: Message, decodeLayers: boolean) {
|
||||
if (!message.content || message.content.length === 0) {
|
||||
return "";
|
||||
}
|
||||
// Build cache if it doesn't exist
|
||||
if (!this.cache[message.channel_id]) {
|
||||
this.cache[message.channel_id] = {};
|
||||
}
|
||||
if (!this.cache[message.channel_id][message.id]) {
|
||||
this.cache[message.channel_id][message.id] = {
|
||||
originalContent: message.content,
|
||||
};
|
||||
}
|
||||
|
||||
const cached = this.cache[message.channel_id][message.id];
|
||||
|
||||
if (this.isTranslated(message)) {
|
||||
// if we're reverting back to original, just set the content back to original
|
||||
message.content = cached.originalContent;
|
||||
this.updateMessage(message);
|
||||
} else {
|
||||
// the message hasn't been edited, let's try to decode it
|
||||
const { translated, layers } = this.translate(message.content, !decodeLayers);
|
||||
if (translated === message.content) {
|
||||
// we don't want to do anything if there is no bottom
|
||||
// since the translation fails, mark this message to not show the indicator
|
||||
cached.top = true;
|
||||
throw new Error("No Bottom detected 🥺");
|
||||
} else {
|
||||
// let the indicator show how many layers of decoding we did
|
||||
cached.layers = layers;
|
||||
message.content = translated;
|
||||
this.updateMessage(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
updateMessage(message: Message) {
|
||||
console.log({
|
||||
bottomTranslation: true,
|
||||
type: "MESSAGE_UPDATE",
|
||||
message,
|
||||
});
|
||||
FluxDispatcher.dispatch({
|
||||
bottomTranslation: true,
|
||||
type: "MESSAGE_UPDATE",
|
||||
message,
|
||||
});
|
||||
}
|
||||
|
||||
clearCache() {
|
||||
for (const channelID in this.cache) {
|
||||
for (const messageID in this.cache[channelID]) {
|
||||
this.removeMessage(channelID, messageID);
|
||||
}
|
||||
}
|
||||
this.cache = {};
|
||||
}
|
||||
|
||||
removeMessage(channelID: string, messageID: string, reset = true) {
|
||||
const message = MessageStore.getMessage(channelID, messageID);
|
||||
if (reset) {
|
||||
message.content = this.cache[channelID][messageID].originalContent;
|
||||
this.updateMessage(message);
|
||||
}
|
||||
delete this.cache[channelID][messageID];
|
||||
}
|
||||
}
|
||||
|
||||
export default BottomHandler;
|
262
src/plugins/bottom/index.tsx
Normal file
262
src/plugins/bottom/index.tsx
Normal file
|
@ -0,0 +1,262 @@
|
|||
/*
|
||||
* Vencord, a modification for Discord's desktop app
|
||||
* Copyright (c) 2023 Vendicated and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This plugin was modified from code licensed under the following license:
|
||||
*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2021-present Sebastian Law
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
import { findOption, RequiredMessageOption } from "@api/Commands";
|
||||
import { addAccessory, removeAccessory } from "@api/MessageAccessories";
|
||||
import { addPreSendListener, removePreSendListener } from "@api/MessageEvents";
|
||||
import { addButton, removeButton } from "@api/MessagePopover";
|
||||
import { definePluginSettings } from "@api/settings";
|
||||
import ErrorBoundary from "@components/ErrorBoundary";
|
||||
import definePlugin, { OptionType } from "@utils/types";
|
||||
import { ChannelStore, Toasts } from "@webpack/common";
|
||||
|
||||
import Indicator from "./components/Indicator";
|
||||
import Bottom from "./encoding";
|
||||
import BottomHandler from "./handler";
|
||||
|
||||
const Handler = new BottomHandler();
|
||||
|
||||
const settings = definePluginSettings({
|
||||
"decode-layers": {
|
||||
description: "Decode Layers",
|
||||
type: OptionType.BOOLEAN,
|
||||
default: true,
|
||||
},
|
||||
"auto-encode-send": {
|
||||
description: "Automatically encode outgoing messages",
|
||||
type: OptionType.BOOLEAN,
|
||||
default: false,
|
||||
},
|
||||
"encode-send-type": {
|
||||
description: "Automatic Encode Behavior",
|
||||
type: OptionType.SELECT,
|
||||
options:
|
||||
[
|
||||
{
|
||||
label: "All",
|
||||
default: true,
|
||||
value: 0,
|
||||
},
|
||||
{
|
||||
label: "Inline (Greedy)",
|
||||
value: 1,
|
||||
},
|
||||
{
|
||||
label: "Inline (Parsed)",
|
||||
value: 2,
|
||||
}
|
||||
],
|
||||
},
|
||||
"inline-bottom-prefix": {
|
||||
description: "Inline bottom prefix",
|
||||
type: OptionType.STRING,
|
||||
default: "👉",
|
||||
},
|
||||
"inline-bottom-suffix": {
|
||||
description: "Inline bottom suffix",
|
||||
type: OptionType.STRING,
|
||||
default: "👈",
|
||||
},
|
||||
});
|
||||
|
||||
const escapeRegex: (string: string) => string = string => string.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
||||
|
||||
function count(string: string, subString: string): number {
|
||||
var n = 0;
|
||||
var pos = 0;
|
||||
const step = subString.length;
|
||||
|
||||
while (true) {
|
||||
pos = string.indexOf(subString, pos);
|
||||
if (pos >= 0) {
|
||||
n++;
|
||||
pos += step;
|
||||
} else break;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
function inlineEncode(p: string, s: string, text: string): string {
|
||||
var np = count(text, p);
|
||||
var ns = count(text, s);
|
||||
|
||||
if (np === 0 || ns === 0) return text;
|
||||
|
||||
var pl = p.length;
|
||||
var sl = s.length;
|
||||
const result: string[] = [];
|
||||
let idx = 0;
|
||||
|
||||
while (true) {
|
||||
var startIndex = text.indexOf(p, idx);
|
||||
|
||||
if (startIndex < 0) {
|
||||
result.push(text.slice(idx));
|
||||
break;
|
||||
}
|
||||
|
||||
var endIndex = text.indexOf(s, startIndex + pl);
|
||||
|
||||
if (endIndex < 0) {
|
||||
result.push(text.slice(idx));
|
||||
break;
|
||||
}
|
||||
|
||||
result.push(text.slice(idx, startIndex));
|
||||
startIndex += pl;
|
||||
result.push(Bottom.encode(text.slice(startIndex, endIndex)));
|
||||
endIndex += sl;
|
||||
idx = endIndex;
|
||||
}
|
||||
|
||||
return result.join("");
|
||||
}
|
||||
|
||||
export default definePlugin({
|
||||
name: "Bottom",
|
||||
description: "The Vencord plugin for bottom 🥺",
|
||||
authors: [
|
||||
{
|
||||
id: 1038096782963507210n,
|
||||
name: "skyevg",
|
||||
},
|
||||
],
|
||||
dependencies: ["MessagePopoverAPI", "CommandsAPI", "MessageEventsAPI", "MessageAccessoriesAPI"],
|
||||
|
||||
settings,
|
||||
|
||||
start() {
|
||||
addButton("bottom", msg => {
|
||||
return {
|
||||
label: "Translate Bottom",
|
||||
icon: () => (
|
||||
<svg x="0" y="0" aria-hidden="false" width="22" height="22" viewBox="0 0 36 36" fill="currentColor" className="icon">
|
||||
<circle fill="#FFCC4D" cx="18" cy="18" r="18" />
|
||||
<path fill="#65471B" d="M20.996 27c-.103 0-.206-.016-.309-.049-1.76-.571-3.615-.571-5.375 0-.524.169-1.089-.117-1.26-.642-.171-.525.117-1.089.643-1.26 2.162-.702 4.447-.702 6.609 0 .525.171.813.735.643 1.26-.137.421-.529.691-.951.691z" />
|
||||
<path fill="#FFF" d="M30.335 12.068c-.903 2.745-3.485 4.715-6.494 4.715-.144 0-.289-.005-.435-.014-1.477-.093-2.842-.655-3.95-1.584.036.495.076.997.136 1.54.152 1.388.884 2.482 2.116 3.163.82.454 1.8.688 2.813.752 1.734.109 3.57-.28 4.873-.909 1.377-.665 2.272-1.862 2.456-3.285.183-1.415-.354-2.924-1.515-4.378z" />
|
||||
<path fill="#65471B" d="M21.351 7.583c-1.297.55-1.947 2.301-1.977 5.289l.039.068c.897 1.319 2.373 2.224 4.088 2.332.114.007.228.011.341.011 2.634 0 4.849-1.937 5.253-4.524-.115-.105-.221-.212-.343-.316-3.715-3.17-6.467-3.257-7.401-2.86z" />
|
||||
<path fill="#F4900C" d="M23.841 16.783c3.009 0 5.591-1.97 6.494-4.715-.354-.443-.771-.88-1.241-1.309-.404 2.587-2.619 4.524-5.253 4.524-.113 0-.227-.004-.341-.011-1.715-.108-3.191-1.013-4.088-2.332l-.039-.068c-.007.701.021 1.473.083 2.313 1.108.929 2.473 1.491 3.95 1.584.146.01.291.014.435.014z" />
|
||||
<circle fill="#FFF" cx="21.413" cy="10.705" r="1.107" />
|
||||
<path fill="#FFF" d="M12.159 16.783c-3.009 0-5.591-1.97-6.494-4.715-1.161 1.454-1.697 2.963-1.515 4.377.185 1.423 1.079 2.621 2.456 3.285 1.303.629 3.138 1.018 4.873.909 1.013-.064 1.993-.297 2.813-.752 1.231-.681 1.963-1.775 2.116-3.163.06-.542.1-1.042.136-1.536-1.103.923-2.47 1.487-3.95 1.58-.146.011-.291.015-.435.015z" />
|
||||
<path fill="#65471B" d="M12.159 15.283c.113 0 .227-.004.341-.011 1.715-.108 3.191-1.013 4.088-2.332l.039-.068c-.031-2.988-.68-4.739-1.977-5.289-.934-.397-3.687-.31-7.401 2.859-.122.104-.227.211-.343.316.404 2.588 2.619 4.525 5.253 4.525z" />
|
||||
<path fill="#F4900C" d="M16.626 12.872l-.039.068c-.897 1.319-2.373 2.224-4.088 2.332-.114.007-.228.011-.341.011-2.634 0-4.849-1.937-5.253-4.524-.47.429-.887.866-1.241 1.309.903 2.745 3.485 4.715 6.494 4.715.144 0 .289-.005.435-.014 1.48-.093 2.847-.657 3.95-1.58.062-.841.091-1.614.083-2.317z" />
|
||||
<path fill="#FFF" d="M9.781 11.81c.61-.038 1.074-.564 1.035-1.174-.038-.61-.564-1.074-1.174-1.036-.61.038-1.074.564-1.036 1.174.039.61.565 1.074 1.175 1.036z" />
|
||||
</svg>
|
||||
),
|
||||
message: msg,
|
||||
channel: ChannelStore.getChannel(msg.channel_id),
|
||||
onClick: async () => {
|
||||
try {
|
||||
Handler.translateMessage(msg, settings.store["decode-layers"]);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
Toasts.show(
|
||||
{
|
||||
id: Toasts.genId(),
|
||||
message: e.message,
|
||||
type: Toasts.Type.MESSAGE
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
addAccessory("bottom", props => {
|
||||
try {
|
||||
if (!Handler.cache[props.message.channel_id][props.message.id].top) {
|
||||
try {
|
||||
return (
|
||||
<ErrorBoundary>
|
||||
<Indicator layers={Handler.cache[props.message.channel_id][props.message.id].layers ?? 0} bottom={!Handler.isTranslated(props.message)} />
|
||||
</ErrorBoundary>
|
||||
);
|
||||
} catch { }
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
return null;
|
||||
});
|
||||
|
||||
this.preSend = addPreSendListener((_, msg) => {
|
||||
if (settings.store["auto-encode-send"]) {
|
||||
const sendType = settings.store["encode-send-type"];
|
||||
var { content } = msg;
|
||||
|
||||
switch (sendType) {
|
||||
case 0: // all
|
||||
content = Bottom.encode(content);
|
||||
break;
|
||||
case 1: // inline greedy
|
||||
var prefix = escapeRegex(settings.store["inline-bottom-prefix"]);
|
||||
var suffix = escapeRegex(settings.store["inline-bottom-suffix"]);
|
||||
var reg = new RegExp(`${prefix}(.+)${suffix}`, "gm");
|
||||
content = content.replace(reg, (str, p1, o, s) => Bottom.encode(p1));
|
||||
break;
|
||||
case 2: // inline parsed
|
||||
var prefix = settings.store["inline-bottom-prefix"];
|
||||
var suffix = settings.store["inline-bottom-prefix"];
|
||||
content = inlineEncode(prefix, suffix, content);
|
||||
break;
|
||||
}
|
||||
msg.content = content;
|
||||
}
|
||||
});
|
||||
},
|
||||
stop() {
|
||||
removeButton("bottom");
|
||||
removeAccessory("bottom");
|
||||
removePreSendListener(this.preSend);
|
||||
},
|
||||
|
||||
commands: [
|
||||
{
|
||||
name: "bottom",
|
||||
description: "Translate and send text as bottom 🥺",
|
||||
options: [RequiredMessageOption],
|
||||
execute: opts => ({
|
||||
content: Bottom.encode(findOption(opts, "message", "")),
|
||||
})
|
||||
}
|
||||
]
|
||||
});
|
|
@ -74,9 +74,9 @@ export default definePlugin({
|
|||
}
|
||||
},
|
||||
{
|
||||
find: 'react-spring: The "interpolate" function',
|
||||
find: 'The "interpolate" function is deprecated in v10 (use "to" instead)',
|
||||
replacement: {
|
||||
match: /,console.warn\('react-spring: The "interpolate" function is deprecated in v10 \(use "to" instead\)'\)/,
|
||||
match: /,console.warn\(\i\+'The "interpolate" function is deprecated in v10 \(use "to" instead\)'\)/,
|
||||
replace: ""
|
||||
}
|
||||
},
|
||||
|
@ -133,7 +133,7 @@ export default definePlugin({
|
|||
{
|
||||
find: "Slow dispatch on",
|
||||
replacement: {
|
||||
match: /\i\.totalTime>100&&\i\.verbose\("Slow dispatch on ".+?\)\);/,
|
||||
match: /\i\.totalTime>\i&&\i\.verbose\("Slow dispatch on ".+?\)\);/,
|
||||
replace: ""
|
||||
}
|
||||
},
|
||||
|
|
|
@ -207,8 +207,8 @@ function makeBypassPatches(): Omit<Patch, "plugin"> {
|
|||
return {
|
||||
find: "canUseCustomStickersEverywhere:",
|
||||
replacement: mapping.map(({ func, predicate }) => ({
|
||||
match: new RegExp(String.raw`(?<=${func}:function\(\i(?:,\i)?\){)`),
|
||||
replace: "return true;",
|
||||
match: new RegExp(String.raw`(?<=${func}:)\i`),
|
||||
replace: "() => true",
|
||||
predicate
|
||||
}))
|
||||
};
|
||||
|
@ -297,8 +297,8 @@ export default definePlugin({
|
|||
replacement: [
|
||||
{
|
||||
// Overwrite incoming connection settings proto with our local settings
|
||||
match: /CONNECTION_OPEN:function\((\i)\){/,
|
||||
replace: (m, props) => `${m}$self.handleProtoChange(${props}.userSettingsProto,${props}.user);`
|
||||
match: /function (\i)\((\i)\){(?=.*CONNECTION_OPEN:\1)/,
|
||||
replace: (m, funcName, props) => `${m}$self.handleProtoChange(${props}.userSettingsProto,${props}.user);`
|
||||
},
|
||||
{
|
||||
// Overwrite non local proto changes with our local settings
|
||||
|
|
|
@ -57,7 +57,7 @@ export default definePlugin({
|
|||
{
|
||||
// https://regex101.com/r/x2mobQ/1
|
||||
// searchEmojis(...,maxCount: stuff) ... endEmojis = emojis.slice(0, maxCount - gifResults.length)
|
||||
match: /,maxCount:(\i)(.{1,500}\i)=(\i)\.slice\(0,(\i-\i\.length)\)/,
|
||||
match: /,maxCount:(\i)(.{1,500}\i)=(\i)\.slice\(0,(Math\.max\(\i,\i(?:-\i\.length){2}\))\)/,
|
||||
// ,maxCount:Infinity ... endEmojis = (emojis.sliceTo = n, emojis)
|
||||
replace: ",maxCount:Infinity$2=($3.sliceTo = $4, $3)"
|
||||
}
|
||||
|
|
63
src/plugins/katex/index.tsx
Normal file
63
src/plugins/katex/index.tsx
Normal file
|
@ -0,0 +1,63 @@
|
|||
import "./katex.css";
|
||||
|
||||
import { Devs } from "@utils/constants";
|
||||
import definePlugin, { ReporterTestable } from "@utils/types";
|
||||
import { React, useMemo } from "@webpack/common";
|
||||
import katex from "katex";
|
||||
import domPurify from "dompurify";
|
||||
|
||||
export interface HighlighterProps {
|
||||
lang?: string;
|
||||
content: string;
|
||||
isPreview: boolean;
|
||||
tempSettings?: Record<string, any>;
|
||||
}
|
||||
|
||||
export default definePlugin({
|
||||
name: "KaTeX",
|
||||
description: "TeX typesetting in discord",
|
||||
authors: [Devs.skyevg],
|
||||
reporterTestable: ReporterTestable.Patches,
|
||||
|
||||
patches: [
|
||||
{
|
||||
find: "codeBlock:{react(",
|
||||
replacement: {
|
||||
match: /codeBlock:\{react\((\i),(\i),(\i)\)\{/,
|
||||
replace: "$&if($1.lang == 'latex' || $1.lang == 'tex') return $self.createBlock($1,$2,$3);"
|
||||
}
|
||||
},
|
||||
{
|
||||
find: "inlineCode:{react:(",
|
||||
replacement: {
|
||||
match: /inlineCode:\{react:\((\i),(\i),(\i)\)=>/,
|
||||
replace: "$&($1.content.startsWith('$$') && $1.content.endsWith('$$'))?$self.createInline($1,$2,$3):"
|
||||
}
|
||||
}
|
||||
],
|
||||
start: async () => {
|
||||
},
|
||||
stop: () => {
|
||||
},
|
||||
|
||||
createBlock: (props: HighlighterProps) => (
|
||||
<Latex displayMode formula={props.content} />
|
||||
),
|
||||
createInline: (props: HighlighterProps) => (
|
||||
<Latex formula={props.content.substring(1, props.content.length - 1)} />
|
||||
),
|
||||
});
|
||||
|
||||
function Latex({ formula, displayMode }: { formula: string, displayMode?: boolean; }) {
|
||||
const result = useMemo(() => {
|
||||
const html = katex.renderToString(formula, {
|
||||
displayMode,
|
||||
throwOnError: false
|
||||
});
|
||||
return domPurify.sanitize(html);
|
||||
}, [formula, displayMode]);
|
||||
|
||||
return displayMode
|
||||
? <div className="tex" dangerouslySetInnerHTML={{ __html: result }} />
|
||||
: <span className="tex" dangerouslySetInnerHTML={{ __html: result }} />;
|
||||
}
|
6
src/plugins/katex/katex.css
Normal file
6
src/plugins/katex/katex.css
Normal file
|
@ -0,0 +1,6 @@
|
|||
@import url("https://unpkg.com/katex@0.16.9/dist/katex.min.css");
|
||||
|
||||
.katex-display {
|
||||
width: min-content;
|
||||
margin: 1em;
|
||||
}
|
|
@ -346,35 +346,35 @@ export default definePlugin({
|
|||
replacement: [
|
||||
{
|
||||
// Add deleted=true to all target messages in the MESSAGE_DELETE event
|
||||
match: /MESSAGE_DELETE:function\((\i)\){let.+?((?:\i\.){2})getOrCreate.+?},/,
|
||||
match: /function (?=.+?MESSAGE_DELETE:(\i))\1\((\i)\){let.+?((?:\i\.){2})getOrCreate.+?}(?=function)/,
|
||||
replace:
|
||||
"MESSAGE_DELETE:function($1){" +
|
||||
" var cache = $2getOrCreate($1.channelId);" +
|
||||
" cache = $self.handleDelete(cache, $1, false);" +
|
||||
" $2commit(cache);" +
|
||||
"},"
|
||||
"function $1($2){" +
|
||||
" var cache = $3getOrCreate($2.channelId);" +
|
||||
" cache = $self.handleDelete(cache, $2, false);" +
|
||||
" $3commit(cache);" +
|
||||
"}"
|
||||
},
|
||||
{
|
||||
// Add deleted=true to all target messages in the MESSAGE_DELETE_BULK event
|
||||
match: /MESSAGE_DELETE_BULK:function\((\i)\){let.+?((?:\i\.){2})getOrCreate.+?},/,
|
||||
match: /function (?=.+?MESSAGE_DELETE_BULK:(\i))\1\((\i)\){let.+?((?:\i\.){2})getOrCreate.+?}(?=function)/,
|
||||
replace:
|
||||
"MESSAGE_DELETE_BULK:function($1){" +
|
||||
" var cache = $2getOrCreate($1.channelId);" +
|
||||
" cache = $self.handleDelete(cache, $1, true);" +
|
||||
" $2commit(cache);" +
|
||||
"},"
|
||||
"function $1($2){" +
|
||||
" var cache = $3getOrCreate($2.channelId);" +
|
||||
" cache = $self.handleDelete(cache, $2, true);" +
|
||||
" $3commit(cache);" +
|
||||
"}"
|
||||
},
|
||||
{
|
||||
// Add current cached content + new edit time to cached message's editHistory
|
||||
match: /(MESSAGE_UPDATE:function\((\i)\).+?)\.update\((\i)/,
|
||||
match: /(function (\i)\((\i)\).+?)\.update\((\i)(?=.*MESSAGE_UPDATE:\2)/,
|
||||
replace: "$1" +
|
||||
".update($3,m =>" +
|
||||
" (($2.message.flags & 64) === 64 || $self.shouldIgnore($2.message, true)) ? m :" +
|
||||
" $2.message.edited_timestamp && $2.message.content !== m.content ?" +
|
||||
" m.set('editHistory',[...(m.editHistory || []), $self.makeEdit($2.message, m)]) :" +
|
||||
".update($4,m =>" +
|
||||
" (($3.message.flags & 64) === 64 || $self.shouldIgnore($3.message, true)) ? m :" +
|
||||
" $3.message.edited_timestamp && $3.message.content !== m.content ?" +
|
||||
" m.set('editHistory',[...(m.editHistory || []), $self.makeEdit($3.message, m)]) :" +
|
||||
" m" +
|
||||
")" +
|
||||
".update($3"
|
||||
".update($4"
|
||||
},
|
||||
{
|
||||
// fix up key (edit last message) attempting to edit a deleted message
|
||||
|
@ -488,12 +488,12 @@ export default definePlugin({
|
|||
find: '"ReferencedMessageStore"',
|
||||
replacement: [
|
||||
{
|
||||
match: /MESSAGE_DELETE:function\((\i)\).+?},/,
|
||||
replace: "MESSAGE_DELETE:function($1){},"
|
||||
match: /MESSAGE_DELETE:\i,/,
|
||||
replace: "MESSAGE_DELETE:()=>{},"
|
||||
},
|
||||
{
|
||||
match: /MESSAGE_DELETE_BULK:function\((\i)\).+?},/,
|
||||
replace: "MESSAGE_DELETE_BULK:function($1){},"
|
||||
match: /MESSAGE_DELETE_BULK:\i,/,
|
||||
replace: "MESSAGE_DELETE_BULK:()=>{},"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
|
|
@ -183,8 +183,8 @@ export default definePlugin({
|
|||
{
|
||||
find: ".ORIGINAL_POSTER=",
|
||||
replacement: {
|
||||
match: /\((\i)=\{\}\)\)\[(\i)\.BOT/,
|
||||
replace: "($1=$self.getTagTypes()))[$2.BOT"
|
||||
match: /(?=(\i)\[\i\.BOT)/,
|
||||
replace: "$self.genTagTypes($1);"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -280,8 +280,7 @@ export default definePlugin({
|
|||
.filter(Boolean);
|
||||
},
|
||||
|
||||
getTagTypes() {
|
||||
const obj = {};
|
||||
genTagTypes(obj) {
|
||||
let i = 100;
|
||||
tags.forEach(({ name }) => {
|
||||
obj[name] = ++i;
|
||||
|
@ -291,7 +290,6 @@ export default definePlugin({
|
|||
obj[`${name}-OP`] = ++i;
|
||||
obj[i] = `${name}-OP`;
|
||||
});
|
||||
return obj;
|
||||
},
|
||||
|
||||
isOPTag: (tag: number) => tag === Tag.Types.ORIGINAL_POSTER || tags.some(t => tag === Tag.Types[`${t.name}-OP`]),
|
||||
|
|
|
@ -54,8 +54,8 @@ export default definePlugin({
|
|||
predicate: () => Settings.plugins.NoBlockedMessages.ignoreBlockedMessages === true,
|
||||
replacement: [
|
||||
{
|
||||
match: /(?<=MESSAGE_CREATE:function\((\i)\){)/,
|
||||
replace: (_, props) => `if($self.isBlocked(${props}.message))return;`
|
||||
match: /(?<=function (\i)\((\i)\){)(?=.*MESSAGE_CREATE:\1)/,
|
||||
replace: (_, _funcName, props) => `if($self.isBlocked(${props}.message))return;`
|
||||
}
|
||||
]
|
||||
}))
|
||||
|
|
|
@ -74,10 +74,10 @@ export default definePlugin({
|
|||
// This prevents the Message Requests tab from always hiding due to the previous patch (and is compatible with spam requests)
|
||||
// In short, only the red badge is hidden. Button visibility behavior isn't changed.
|
||||
{
|
||||
find: ".getSpamChannelsCount()",
|
||||
find: ".getSpamChannelsCount();return",
|
||||
predicate: () => settings.store.hideMessageRequestsCount,
|
||||
replacement: {
|
||||
match: /(?<=getSpamChannelsCount\(\),\i=)\i\.getMessageRequestsCount\(\)/,
|
||||
match: /(?<=getSpamChannelsCount\(\);return )\i\.getMessageRequestsCount\(\)/,
|
||||
replace: "$self.getRealMessageRequestCount()"
|
||||
}
|
||||
},
|
||||
|
|
|
@ -71,7 +71,7 @@ export default definePlugin({
|
|||
replacement: [
|
||||
{
|
||||
// Filter out pinned channels from the private channel list
|
||||
match: /(?<=\i,{channels:\i,)privateChannelIds:(\i)/,
|
||||
match: /(?<=channels:\i,)privateChannelIds:(\i)(?=,listRef:)/,
|
||||
replace: "privateChannelIds:$1.filter(c=>!$self.isPinned(c))"
|
||||
},
|
||||
{
|
||||
|
@ -96,8 +96,8 @@ export default definePlugin({
|
|||
|
||||
// Fix Row Height
|
||||
{
|
||||
match: /(?<="getRowHeight",.{1,100}return 1===)\i/,
|
||||
replace: "($&-$self.categoryLen())"
|
||||
match: /(\.startsWith\("section-divider"\).+?return 1===)(\i)/,
|
||||
replace: "$1($2-$self.categoryLen())"
|
||||
},
|
||||
{
|
||||
match: /"getRowHeight",\((\i),(\i)\)=>{/,
|
||||
|
@ -124,7 +124,7 @@ export default definePlugin({
|
|||
{
|
||||
find: ".FRIENDS},\"friends\"",
|
||||
replacement: {
|
||||
match: /(?<=\i=\i=>{).{1,100}premiumTabSelected.{1,800}showDMHeader:.+?,/,
|
||||
match: /(?<=\i=\i=>{).{1,100}premiumTabSelected.{0,950}showDMHeader:.+?,/,
|
||||
replace: "let forceUpdate = Vencord.Util.useForceUpdater();$&_forceUpdate:forceUpdate,"
|
||||
}
|
||||
},
|
||||
|
|
46
src/plugins/useAltSearch.ts
Normal file
46
src/plugins/useAltSearch.ts
Normal file
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
* Vencord, a modification for Discord's desktop app
|
||||
* Copyright (c) 2023 Vendicated and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import definePlugin, { OptionType } from "@utils/types";
|
||||
|
||||
export default definePlugin({
|
||||
name: "UseAlternativeSearch",
|
||||
description: "Use alternative search engine in right click menu",
|
||||
authors: [
|
||||
{
|
||||
id: 1038096782963507210n,
|
||||
name: "skyevg",
|
||||
},
|
||||
],
|
||||
patches: [
|
||||
{
|
||||
find: "https://www.google.com/search?q=",
|
||||
replacement: {
|
||||
match: /"https:\/\/www.google.com\/search\?q=".concat\(encodeURIComponent\(e\)\)/,
|
||||
replace: "Vencord.Settings.plugins.UseAlternativeSearch.source.replace(\"!QUERY!\", encodeURIComponent(e))"
|
||||
}
|
||||
}
|
||||
],
|
||||
options: {
|
||||
source: {
|
||||
description: "Search engine's url (use !QUERY! as replacement for the search term)",
|
||||
type: OptionType.STRING,
|
||||
default: "https://duckduckgo.com/?q=!QUERY!",
|
||||
}
|
||||
}
|
||||
});
|
143
src/plugins/uwuifier.ts
Normal file
143
src/plugins/uwuifier.ts
Normal file
|
@ -0,0 +1,143 @@
|
|||
/*
|
||||
* Vencord, a modification for Discord's desktop app
|
||||
* Copyright (c) 2022 Vendicated and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { findOption, RequiredMessageOption } from "@api/Commands";
|
||||
import { addPreEditListener, addPreSendListener, MessageObject, removePreEditListener, removePreSendListener } from "@api/MessageEvents";
|
||||
import { definePluginSettings } from "@api/Settings";
|
||||
import { Devs } from "@utils/constants";
|
||||
import definePlugin, { OptionType } from "@utils/types";
|
||||
|
||||
const endings = [
|
||||
"rawr x3",
|
||||
"OwO",
|
||||
"UwU",
|
||||
"o.O",
|
||||
"-.-",
|
||||
">w<",
|
||||
"(⑅˘꒳˘)",
|
||||
"(ꈍᴗꈍ)",
|
||||
"(˘ω˘)",
|
||||
"(U ᵕ U❁)",
|
||||
"σωσ",
|
||||
"òωó",
|
||||
"(///ˬ///✿)",
|
||||
"(U ﹏ U)",
|
||||
"( ͡o ω ͡o )",
|
||||
"ʘwʘ",
|
||||
":3",
|
||||
":3", // important enough to have twice
|
||||
"XD",
|
||||
"nyaa~~",
|
||||
"mya",
|
||||
">_<",
|
||||
"😳",
|
||||
"🥺",
|
||||
"😳😳😳",
|
||||
"rawr",
|
||||
"^^",
|
||||
"^^;;",
|
||||
"(ˆ ﻌ ˆ)♡",
|
||||
"^•ﻌ•^",
|
||||
"/(^•ω•^)",
|
||||
"(✿oωo)"
|
||||
];
|
||||
|
||||
const replacements = [
|
||||
["small", "smol"],
|
||||
["cute", "kawaii~"],
|
||||
["fluff", "floof"],
|
||||
["love", "luv"],
|
||||
["stupid", "baka"],
|
||||
["what", "nani"],
|
||||
["meow", "nya~"],
|
||||
["hello", "hewwo"],
|
||||
];
|
||||
|
||||
const settings = definePluginSettings({
|
||||
uwuEveryMessage: {
|
||||
description: "Make every single message uwuified",
|
||||
type: OptionType.BOOLEAN,
|
||||
default: false,
|
||||
restartNeeded: false
|
||||
}
|
||||
});
|
||||
|
||||
function selectRandomElement(arr) {
|
||||
// generate a random index based on the length of the array
|
||||
const randomIndex = Math.floor(Math.random() * arr.length);
|
||||
|
||||
// return the element at the randomly generated index
|
||||
return arr[randomIndex];
|
||||
}
|
||||
|
||||
|
||||
function uwuify(message: string): string {
|
||||
message = message.toLowerCase();
|
||||
// words
|
||||
for (const pair of replacements) {
|
||||
message = message.replaceAll(pair[0], pair[1]);
|
||||
}
|
||||
message = message
|
||||
.replaceAll(/([ \t\n])n/g, "$1ny") // nyaify
|
||||
.replaceAll(/[lr]/g, "w") // [lr] > w
|
||||
.replaceAll(/([ \t\n])([a-z])/g, (_, p1, p2) => Math.random() < .5 ? `${p1}${p2}-${p2}` : `${p1}${p2}`) // stutter
|
||||
.replaceAll(/([^.,!][.,!])([ \t\n])/g, (_, p1, p2) => `${p1} ${selectRandomElement(endings)}${p2}`); // endings
|
||||
return message;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// actual command declaration
|
||||
export default definePlugin({
|
||||
name: "UwUifier",
|
||||
description: "Simply uwuify commands",
|
||||
authors: [Devs.echo, Devs.skyevg, Devs.PandaNinjas],
|
||||
dependencies: ["CommandsAPI", "MessageEventsAPI"],
|
||||
settings,
|
||||
|
||||
commands: [
|
||||
{
|
||||
name: "uwuify",
|
||||
description: "uwuifies your messages",
|
||||
options: [RequiredMessageOption],
|
||||
|
||||
execute: opts => ({
|
||||
content: uwuify(findOption(opts, "message", "")),
|
||||
}),
|
||||
},
|
||||
],
|
||||
|
||||
onSend(msg: MessageObject) {
|
||||
// Only run when it's enabled
|
||||
if (settings.store.uwuEveryMessage) {
|
||||
msg.content = uwuify(msg.content);
|
||||
}
|
||||
},
|
||||
|
||||
start() {
|
||||
this.preSend = addPreSendListener((_, msg) => this.onSend(msg));
|
||||
this.preEdit = addPreEditListener((_cid, _mid, msg) =>
|
||||
this.onSend(msg)
|
||||
);
|
||||
},
|
||||
|
||||
stop() {
|
||||
removePreSendListener(this.preSend);
|
||||
removePreEditListener(this.preEdit);
|
||||
},
|
||||
});
|
|
@ -94,7 +94,7 @@ export default definePlugin({
|
|||
find: "AudioContextSettingsMigrated",
|
||||
replacement: [
|
||||
{
|
||||
match: /(?<=isLocalMute\(\i,\i\),volume:.+?volume:)\i(?=})/,
|
||||
match: /(?<=isLocalMute\(\i,\i\),volume:(\i).+?\i\(\i,\i,)\1(?=\))/,
|
||||
replace: "$&>200?200:$&"
|
||||
},
|
||||
{
|
||||
|
@ -109,7 +109,7 @@ export default definePlugin({
|
|||
},
|
||||
// Prevent the MediaEngineStore from overwriting our LocalVolumes above 200 with the ones the Discord Audio Context Settings sync sends
|
||||
{
|
||||
find: '"MediaEngineStore"',
|
||||
find: '="MediaEngineStore",',
|
||||
replacement: [
|
||||
{
|
||||
match: /(\.settings\.audioContextSettings.+?)(\i\[\i\])=(\i\.volume)(.+?setLocalVolume\(\i,).+?\)/,
|
||||
|
|
|
@ -113,8 +113,8 @@ export default definePlugin({
|
|||
{
|
||||
find: '"MessageReactionsStore"',
|
||||
replacement: {
|
||||
match: /(?<=CONNECTION_OPEN:function\(\){)(\i)={}/,
|
||||
replace: "$&;$self.reactions=$1"
|
||||
match: /function (\i)\(\){(\i)={}(?=.*CONNECTION_OPEN:\1)/,
|
||||
replace: "$&;$self.reactions=$2;"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
|
Loading…
Reference in a new issue