Merge remote-tracking branch 'upstream/dev' into dev

This commit is contained in:
thororen1234 2025-10-15 15:50:57 -04:00
commit 72939c1878
No known key found for this signature in database
6 changed files with 71 additions and 26 deletions

View file

@ -1,7 +1,7 @@
{
"name": "equicord",
"private": "true",
"version": "1.13.3",
"version": "1.13.4",
"description": "The other cutest Discord client mod",
"homepage": "https://github.com/Equicord/Equicord#readme",
"bugs": {
@ -121,4 +121,4 @@
"engines": {
"node": ">=18"
}
}
}

View file

@ -16,8 +16,8 @@ export default definePlugin({
{
find: "#{intl::APP_TAG::hash}\":",
replacement: {
match: /(#{intl::APP_TAG::hash}"):".*?"/,
replace: '$1:"BOT"'
match: /(9RNkeF":\[)"APP"/,
replace: '$1"BOT"'
}
}
],

View file

@ -18,8 +18,8 @@ import { findComponentLazy } from "@webpack";
import { ChannelStore, GuildMemberStore, TooltipContainer } from "@webpack/common";
import { ReactNode } from "react";
const countDownFilter = canonicalizeMatch("#{intl::MAX_AGE_NEVER}");
const CountDown = findComponentLazy(m => m.prototype?.render?.toString().includes(countDownFilter));
const countDownFilter = canonicalizeMatch(/#{intl::MAX_AGE_NEVER}/);
const CountDown = findComponentLazy(m => m.prototype?.render && countDownFilter.test(m.prototype.render.toString()));
const enum DisplayStyle {
Tooltip = "tooltip",

View file

@ -21,19 +21,21 @@ import { Channel, CloudUpload, Guild, GuildFeatures, Message, User } from "@venc
import { ChannelActionCreators, ChannelStore, ComponentDispatch, Constants, FluxDispatcher, GuildStore, i18n, IconUtils, InviteActions, MessageActions, RestAPI, SelectedChannelStore, SelectedGuildStore, UserProfileActions, UserProfileStore, UserSettingsActionCreators, UserUtils } from "@webpack/common";
import { Except } from "type-fest";
import { runtimeHashMessageKey } from "./intlHash";
import { runtimeHashMessageKey, runtimeHashMessageKeyLegacy } 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);
return getIntlMessageFromHash(runtimeHashMessageKey(key), values, key) || getIntlMessageFromHash(runtimeHashMessageKeyLegacy(key), values, key);
}
/**

View file

@ -39,6 +39,21 @@ function numberToBytes(number: number | bigint) {
* `@discord/intl-loader-core`, to be able to hash names at runtime.
*/
export function runtimeHashMessageKey(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[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 [

View file

@ -16,37 +16,65 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import { runtimeHashMessageKey } from "./intlHash";
import { runtimeHashMessageKey, runtimeHashMessageKeyLegacy } from "./intlHash";
import { Patch, PatchReplacement, ReplaceFn } from "./types";
export function canonicalizeMatch<T extends RegExp | string>(match: T): T {
// 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, "\\$&"));
export 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 += 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;
}
let partialCanon = typeof match === "string" ? match : match.source;
partialCanon = partialCanon.replaceAll(/#{intl::([\w$+/]*)(?:::(\w+))?}/g, (_, key, modifier) => {
const hashed = modifier === "raw" ? key : runtimeHashMessageKey(key);
const isString = typeof match === "string";
const hasSpecialChars = !Number.isNaN(Number(hashed[0])) || hashed.includes("+") || hashed.includes("/");
if (modifier === "hash") return hashed;
if (hasSpecialChars) {
return isString
? `["${hashed}"]`
: String.raw`(?:\["${hashed}"\])`.replaceAll("+", "\\+");
}
return isString ? `.${hashed}` : String.raw`(?:\.${hashed})`;
if (modifier === "raw") return getReplacement(false, key);
return getCompatReplacement(key);
});
if (typeof match === "string") {
return partialCanon as T;
return partialCanon as any;
}
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 T;
return canonRegex as any;
}
export function canonicalizeReplace<T extends string | ReplaceFn>(replace: T, pluginPath: string): T {