remove legacy intl compatibility

This commit is contained in:
Vendicated 2025-12-14 15:22:44 +01:00
parent 40b0976c35
commit 5b369dbed1
No known key found for this signature in database
GPG key ID: D66986BAF75ECF18
8 changed files with 26 additions and 95 deletions

View file

@ -317,14 +317,6 @@ export type Slider = ComponentClass<PropsWithChildren<{
"aria-describedby"?: string;
}>>;
// TODO - type maybe idk probably not that useful other than the constants
export type Flex = ComponentType<PropsWithChildren<any>> & {
Align: Record<"START" | "END" | "CENTER" | "STRETCH" | "BASELINE", string>;
Direction: Record<"VERTICAL" | "HORIZONTAL" | "HORIZONTAL_REVERSE", string>;
Justify: Record<"START" | "END" | "CENTER" | "BETWEEN" | "AROUND", string>;
Wrap: Record<"NO_WRAP" | "WRAP" | "WRAP_REVERSE", string>;
};
declare enum PopoutAnimation {
NONE = "1",
TRANSLATE = "2",

View file

@ -23,7 +23,7 @@ import { Devs } from "@utils/constants";
import { getCurrentGuild, getEmojiURL } from "@utils/discord";
import { Logger } from "@utils/Logger";
import definePlugin, { OptionType, Patch } from "@utils/types";
import type { Emoji, Message } from "@vencord/discord-types";
import type { Emoji, Message, Sticker } from "@vencord/discord-types";
import { StickerFormatType } from "@vencord/discord-types/enums";
import { findByCodeLazy, findByPropsLazy, findStoreLazy, proxyLazyWebpack } from "@webpack";
import { Alerts, ChannelStore, DraftType, EmojiStore, FluxDispatcher, Forms, GuildMemberStore, lodash, Parser, PermissionsBits, PermissionStore, StickersStore, UploadHandler, UserSettingsActionCreators, UserStore } from "@webpack/common";
@ -702,8 +702,9 @@ export default definePlugin({
}
},
getStickerLink(stickerId: string) {
return `https://media.discordapp.net/stickers/${stickerId}.png?size=${settings.store.stickerSize}`;
getStickerLink({ format_type, id }: Sticker) {
const ext = format_type === StickerFormatType.GIF;
return `https://media.discordapp.net/stickers/${id}.${ext}?size=${settings.store.stickerSize}`;
},
async sendAnimatedSticker(stickerLink: string, stickerId: string, channelId: string) {
@ -840,14 +841,7 @@ export default definePlugin({
if (sticker.available !== false && (canUseStickers || sticker.guild_id === guildId))
break stickerBypass;
// [12/12/2023]
// Work around an annoying bug where getStickerLink will return StickerType.GIF,
// but will give us a normal non animated png for no reason
// TODO: Remove this workaround when it's not needed anymore
let link = this.getStickerLink(sticker.id);
if (sticker.format_type === StickerFormatType.GIF && link.includes(".png")) {
link = link.replace(".png", ".gif");
}
const link = this.getStickerLink(sticker);
if (sticker.format_type === StickerFormatType.APNG) {
if (!hasAttachmentPerms(channelId)) {

View file

@ -18,7 +18,7 @@
import { definePluginSettings, migratePluginSetting } from "@api/Settings";
import { Devs } from "@utils/constants";
import { runtimeHashMessageKey, runtimeHashMessageKeyLegacy } from "@utils/intlHash";
import { runtimeHashMessageKey } from "@utils/intlHash";
import { Logger } from "@utils/Logger";
import definePlugin, { OptionType } from "@utils/types";
import { Message } from "@vencord/discord-types";
@ -92,7 +92,7 @@ export default definePlugin({
shouldHide(props: MessageDeleteProps): boolean {
try {
const collapsedReason = props.collapsedReason();
const is = (key: string) => collapsedReason === i18n.t[runtimeHashMessageKey(key)]() || collapsedReason === i18n.t[runtimeHashMessageKeyLegacy(key)]();
const is = (key: string) => collapsedReason === i18n.t[runtimeHashMessageKey(key)]();
return is("BLOCKED_MESSAGE_COUNT") || (settings.store.applyToIgnoredUsers && is("IGNORED_MESSAGE_COUNT"));
} catch (e) {

View file

@ -22,21 +22,19 @@ import { ChannelActionCreators, ChannelStore, ComponentDispatch, Constants, Flux
import { Except } from "type-fest";
import { copyToClipboard } from "./clipboard";
import { runtimeHashMessageKey, runtimeHashMessageKeyLegacy } from "./intlHash";
import { runtimeHashMessageKey } from "./intlHash";
import { Logger } from "./Logger";
import { MediaModalItem, MediaModalProps, openMediaModal } from "./modal";
const IntlManagerLogger = new Logger("IntlManager");
// TODO: remove legacy hashing function once Discord ships new one everywhere for a while
/**
* Get an internationalized message from a non hashed key
* @param key The plain message key
* @param values The values to interpolate, if it's a rich message
*/
export function getIntlMessage(key: string, values?: Record<PropertyKey, any>): any {
return getIntlMessageFromHash(runtimeHashMessageKey(key), values, key) || getIntlMessageFromHash(runtimeHashMessageKeyLegacy(key), values, key);
return getIntlMessageFromHash(runtimeHashMessageKey(key), values, key);
}
/**

View file

@ -50,18 +50,3 @@ export function runtimeHashMessageKey(key: string): string {
BASE64_TABLE[((bytes[3] & 0x03) << 4) | (bytes[4] >> 4)],
].join("");
}
// Old variant for compatibility
// TODO: remove once Discord shipped new one everywhere for a while
export function runtimeHashMessageKeyLegacy(key: string): string {
const hash = h64(key, 0);
const bytes = numberToBytes(hash);
return [
BASE64_TABLE[bytes[0] >> 2],
BASE64_TABLE[((bytes[0] & 0x03) << 4) | (bytes[1] >> 4)],
BASE64_TABLE[((bytes[1] & 0x0f) << 2) | (bytes[2] >> 6)],
BASE64_TABLE[bytes[2] & 0x3f],
BASE64_TABLE[bytes[3] >> 2],
BASE64_TABLE[((bytes[3] & 0x03) << 4) | (bytes[3] >> 4)],
].join("");
}

View file

@ -16,66 +16,35 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import { runtimeHashMessageKey, runtimeHashMessageKeyLegacy } from "./intlHash";
import { runtimeHashMessageKey } from "./intlHash";
import { Patch, PatchReplacement, ReplaceFn } from "./types";
// TODO: remove legacy hashing function once Discord ships new one everywhere for a while
// @ts-expect-error "RegExp.escape" is very new and not yet in DOM types
const escapeRegex = RegExp.escape || ((s: string) => s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"));
function getReplacement(isString: boolean, hashed: string) {
const hasSpecialChars = !Number.isNaN(Number(hashed[0])) || hashed.includes("+") || hashed.includes("/");
if (hasSpecialChars) {
return isString
? `["${hashed}"]`
: String.raw`(?:\["${hashed}"\])`.replaceAll("+", "\\+");
}
return isString ? `.${hashed}` : String.raw`(?:\.${hashed})`;
}
function getCompatReplacement(key: string) {
const hashed = getReplacement(false, runtimeHashMessageKey(key));
const legacyHashed = getReplacement(false, runtimeHashMessageKeyLegacy(key));
return String.raw`(?:${hashed}|${legacyHashed})`;
}
function canonicalizeMatchCompatString(str: string) {
let result = "";
let lastIndex = 0;
const re = /#{intl::([\w$+/]*)(?:::(\w+))?}/g;
for (const match of str.matchAll(re)) {
result += escapeRegex(str.slice(lastIndex, match.index));
result += match[2] === "raw" ? getReplacement(false, match[1]) : getCompatReplacement(match[1]);
lastIndex = (match.index ?? 0) + match[0].length;
}
result += escapeRegex(str.slice(lastIndex));
return new RegExp(result);
}
export function canonicalizeMatch<T extends RegExp | string>(match: T): T extends RegExp ? RegExp : string | RegExp {
if (typeof match === "string") {
return canonicalizeMatchCompatString(match) as any;
}
export function canonicalizeMatch<T extends RegExp | string>(match: T): T {
let partialCanon = typeof match === "string" ? match : match.source;
partialCanon = partialCanon.replaceAll(/#{intl::([\w$+/]*)(?:::(\w+))?}/g, (_, key, modifier) => {
if (modifier === "raw") return getReplacement(false, key);
return getCompatReplacement(key);
const hashed = modifier === "raw" ? key : runtimeHashMessageKey(key);
const isString = typeof match === "string";
const hasSpecialChars = !Number.isNaN(Number(hashed[0])) || hashed.includes("+") || hashed.includes("/");
if (hasSpecialChars) {
return isString
? `["${hashed}"]`
: String.raw`(?:\["${hashed}"\])`.replaceAll("+", "\\+");
}
return isString ? `.${hashed}` : String.raw`(?:\.${hashed})`;
});
if (typeof match === "string") {
return partialCanon as any;
return partialCanon as T;
}
const canonSource = partialCanon.replaceAll("\\i", String.raw`(?:[A-Za-z_$][\w$]*)`);
const canonRegex = new RegExp(canonSource, match.flags);
canonRegex.toString = match.toString.bind(match);
return canonRegex as any;
return canonRegex as T;
}
export function canonicalizeReplace<T extends string | ReplaceFn>(replace: T, pluginPath: string): T {

View file

@ -205,8 +205,7 @@ export const DisplayProfileUtils: t.DisplayProfileUtils = mapMangledModuleLazy(/
export const DateUtils: t.DateUtils = mapMangledModuleLazy("millisecondsInUnit:", {
calendarFormat: filters.byCode("sameElse"),
dateFormat: filters.byCode('":'),
// TODO: the +? are for compat with the old version - Remove them once no longer needed
isSameDay: filters.byCode(/Math\.abs\(\+?\i-\+?\i\)/),
isSameDay: filters.byCode(/Math\.abs\(\i-\i\)/),
diffAsUnits: filters.byCode("days:0", "millisecondsInUnit")
});

View file

@ -8,7 +8,6 @@ import { Settings } from "@api/Settings";
import { makeLazy } from "@utils/lazy";
import { Logger } from "@utils/Logger";
import { interpolateIfDefined } from "@utils/misc";
import { canonicalizeReplacement } from "@utils/patches";
import { Patch, PatchReplacement } from "@utils/types";
import { WebpackRequire } from "@vencord/discord-types/webpack";
@ -548,11 +547,6 @@ function patchFactory(moduleId: PropertyKey, originalFactory: AnyModuleFactory):
continue;
}
// TODO: remove once Vesktop has been updated to use addPatch
if (patch.plugin === "Vesktop") {
canonicalizeReplacement(replacement, "VCDP");
}
const lastCode = code;
const lastFactory = originalFactory;