diff --git a/src/plugins/validUser.tsx b/src/plugins/validUser.tsx new file mode 100644 index 000000000..d92269ce3 --- /dev/null +++ b/src/plugins/validUser.tsx @@ -0,0 +1,143 @@ +/* + * 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 . +*/ + +import ErrorBoundary from "@components/ErrorBoundary"; +import { Devs } from "@utils/constants"; +import { sleep } from "@utils/misc"; +import { Queue } from "@utils/Queue"; +import definePlugin from "@utils/types"; +import { findByCodeLazy } from "@webpack"; +import { UserStore, useState } from "@webpack/common"; +import type { User } from "discord-types/general"; +import type { ComponentType } from "react"; + +const fetching = new Set(); +const queue = new Queue(5); +const fetchUser = findByCodeLazy("USER(") as (id: string) => Promise; + +interface MentionProps { + data: { + userId?: string; + channelId?: string; + content: any; + }; + parse: (content: any, props: MentionProps["props"]) => string[]; + props: { + key: string; + formatInline: boolean; + noStyleAndInteraction: boolean; + }; + RoleMention: ComponentType; + UserMention: ComponentType; +} + +function MentionWrapper({ data, UserMention, RoleMention, parse, props }: MentionProps) { + const [userId, setUserId] = useState(data.userId); + + // if userId is set it means the user is cached. Uncached users have userId set to undefined + if (userId) + return ( + + ); + + // Parses the raw text node array data.content into a ReactNode[]: ["<@userid>"] + const children = parse(data.content, props); + + return ( + // Discord is deranged and renders unknown user mentions as role mentions + + { + const mention = children?.[0]; + if (typeof mention !== "string") return; + + const id = mention.match(/<@(\d+)>/)?.[1]; + if (!id) return; + + if (fetching.has(id)) + return; + + if (UserStore.getUser(id)) + return setUserId(id); + + const fetch = () => { + fetching.add(id); + + queue.unshift(() => + fetchUser(id) + .then(() => { + setUserId(id); + fetching.delete(id); + }) + .catch(e => { + if (e?.status === 429) { + queue.unshift(() => sleep(1000).then(fetch)); + fetching.delete(id); + } + }) + .finally(() => sleep(300)) + ); + }; + + fetch(); + }} + > + {children} + + + ); +} + +export default definePlugin({ + name: "ValidUser", + description: "Fix mentions for unknown users showing up as '<@343383572805058560>' (hover over a mention to fix it)", + authors: [Devs.Ven], + + patches: [{ + find: 'className:"mention"', + replacement: { + // mention = { react: function (data, parse, props) { if (data.userId == null) return RoleMention() else return UserMention() + match: /react:(?=function\(\i,\i,\i\).{0,50}return null==\i\?\(0,\i\.jsx\)\((\i),.+?jsx\)\((\i),\{className:"mention")/, + // react: (...args) => OurWrapper(RoleMention, UserMention, ...args), originalReact: theirFunc + replace: "react:(...args)=>$self.renderMention($1,$2,...args),originalReact:" + } + }], + + renderMention(RoleMention, UserMention, data, parse, props) { + return ( + + + + ); + }, +});