/** * @name MentionCacheFix * @author Ahlawat * @authorId 1025214794766221384 * @version 1.3.1 * @invite SgKSKyh9gY * @description Fix uncached user mentions, including in embeds. * @website https://tharki-god.github.io/ * @source https://github.com/Tharki-God/BetterDiscordPlugins * @updateUrl https://tharki-god.github.io/BetterDiscordPlugins/MentionCacheFix.plugin.js */ /*@cc_on @if (@_jscript) var shell = WScript.CreateObject("WScript.Shell"); var fs = new ActiveXObject("Scripting.FileSystemObject"); var pathPlugins = shell.ExpandEnvironmentStrings("%APPDATA%\\BetterDiscord\\plugins"); var pathSelf = WScript.ScriptFullName; shell.Popup("It looks like you've mistakenly tried to run me directly. \n(Don't do that!)", 0, "I'm a plugin for BetterDiscord", 0x30); if (fs.GetParentFolderName(pathSelf) === fs.GetAbsolutePathName(pathPlugins)) { shell.Popup("I'm in the correct folder already.", 0, "I'm already installed", 0x40); } else if (!fs.FolderExists(pathPlugins)) { shell.Popup("I can't find the BetterDiscord plugins folder.\nAre you sure it's even installed?", 0, "Can't install myself", 0x10); } else if (shell.Popup("Should I move myself to BetterDiscord's plugins folder for you?", 0, "Do you need some help?", 0x34) === 6) { fs.MoveFile(pathSelf, fs.BuildPath(pathPlugins, fs.GetFileName(pathSelf))); shell.Exec("explorer " + pathPlugins); shell.Popup("I'm installed!", 0, "Successfully installed", 0x40); } WScript.Quit(); @else@*/ module.exports = (() => { const config = { info: { name: "MentionCacheFix", authors: [ { name: "Ahlawat", discord_id: "1025214794766221384", github_username: "Tharki-God", }, ], version: "1.3.1", description: "Fix uncached user mentions, including in embeds.", github: "https://github.com/Tharki-God/BetterDiscordPlugins", github_raw: "https://tharki-god.github.io/BetterDiscordPlugins/MentionCacheFix.plugin.js", }, changelog: [ { title: "v0.0.1", items: ["Idea in mind"], }, { title: "v0.0.5", items: ["Base Model"], }, { title: "Initial Release v1.0.0", items: [ "This is the initial release of the plugin :)", "Why, WHY... WHY(⊙o⊙)", ], }, { title: "v1.1.1", items: [ "Plugin Working again", ], }, ], main: "MentionCacheFix.plugin.js", }; const RequiredLibs = [{ window: "ZeresPluginLibrary", filename: "0PluginLibrary.plugin.js", external: "https://rauenzi.github.io/BDPluginLibrary/release/0PluginLibrary.plugin.js", downloadUrl: "https://rauenzi.github.io/BDPluginLibrary/release/0PluginLibrary.plugin.js" }, { window: "BunnyLib", filename: "1BunnyLib.plugin.js", external: "https://github.com/Tharki-God/BetterDiscordPlugins", downloadUrl: "https://tharki-god.github.io/BetterDiscordPlugins/1BunnyLib.plugin.js" }, ]; class handleMissingLibrarys { load() { for (const Lib of RequiredLibs.filter(lib => !window.hasOwnProperty(lib.window))) BdApi.showConfirmationModal( "Library Missing", `The library plugin (${Lib.window}) needed for ${config.info.name} is missing. Please click Download Now to install it.`, { confirmText: "Download Now", cancelText: "Cancel", onConfirm: () => this.downloadLib(Lib), } ); } async downloadLib(Lib) { const fs = require("fs"); const path = require("path"); const { Plugins } = BdApi; const LibFetch = await fetch( Lib.downloadUrl ); if (!LibFetch.ok) return this.errorDownloadLib(Lib); const LibContent = await LibFetch.text(); try { await fs.writeFile( path.join(Plugins.folder, Lib.filename), LibContent, (err) => { if (err) return this.errorDownloadLib(Lib); } ); } catch (err) { return this.errorDownloadLib(Lib); } } errorDownloadZLib(Lib) { const { shell } = require("electron"); BdApi.showConfirmationModal( "Error Downloading", [ `${Lib.window} download failed. Manually install plugin library from the link below.`, ], { confirmText: "Download", cancelText: "Cancel", onConfirm: () => { shell.openExternal( Lib.external ); }, } ); } start() { } stop() { } } return RequiredLibs.some(m => !window.hasOwnProperty(m.window)) ? handleMissingLibrarys : (([Plugin, ZLibrary]) => { const { PluginUpdater, Logger, Patcher, ReactTools, DiscordModules: { GuildMemberStore, SelectedGuildStore }, } = ZLibrary; const { LibraryModules: { Slate, Praser, Message, ProfileStore } } = BunnyLib.build(config); return class MentionCacheFix extends Plugin { constructor() { super(); this.checkingMessages = new Set(); this.cachedMembers = new Set(); } checkForUpdates() { try { PluginUpdater.checkForUpdate( config.info.name, config.info.version, config.info.github_raw ); } catch (err) { Logger.err("Plugin Updater could not be reached.", err); } } start() { this.checkForUpdates(); this.patchUserMentions(); this.patchMessage(); this.patchTopic(); } isCached(id) { const guildId = SelectedGuildStore.getGuildId(); return ( this.cachedMembers.has(`${id}-${guildId}`) || !!GuildMemberStore.getMember(guildId, id) ); } fetchUser(id, retry = false) { if (this.isCached(id)) return; const guildId = SelectedGuildStore.getGuildId(); const fn = retry ? ProfileStore.getUser(id) : ProfileStore.fetchProfile(id, { guildId, withMutualGuilds: false, }); return fn .then((x) => { if (retry || (!retry && !x.guild_member)) this.cachedMembers.add(`${id}-${guildId}`); return false; }) .catch((e) => { if (e && e.status === 429) return true; // Abort if ratelimited else if (e?.status === 403 && !retry) return this.fetchUser(id, true); else this.cachedMembers.add(`${id}-${guildId}`); return; }); } async processMatches(matches, updateInfo, channelId) { for (const id of matches) { const abort = await this.fetchUser(id); if (abort) break; this.update(updateInfo, channelId); } } update(updateInfo, channelId) { switch (updateInfo) { case "topic": const channelTopic = document.querySelector(".topic-11NuQZ"); if (channelTopic) ReactTools.getOwnerInstance( channelTopic )?.forceUpdate(); break; default: // Message const messageContent = document.querySelector( `#chat-messages-${channelId}-${updateInfo} > div > div.contents-2MsGLg` ) if (messageContent) ReactTools.getOwnerInstance( messageContent )?.forceUpdate(); const messageAccessories = document.querySelector( `#message-accessories-${updateInfo} > article` ); if (messageAccessories) ReactTools.getOwnerInstance( messageAccessories )?.forceUpdate(); } } getIDsFromText(text) { return [...text.matchAll(/<@!?(\d+)>/g)] .map((m) => m[1]) .filter((id, i, arr) => arr.indexOf(id) === i) .filter((id) => !this.isCached(id)); } getMatches(message) { const content = [message.content]; message.embeds.forEach((embed) => { content.push(embed.rawDescription || ""); if (embed.fields) embed.fields.forEach((field) => content.push(field.rawValue)); }); return this.getIDsFromText(content.join(" ")); } patchUserMentions() { Patcher.before(Slate, "render", (_, [{ textValue }]) => { const mentions = this.getIDsFromText(textValue); for (const id of mentions) { this.fetchUser(id); } }); } patchMessage() { Patcher.after( Message, "ZP", (_, [{ message, isInteracting }], res) => { if (!isInteracting) { if (!this.checkingMessages.has(message.id)) return; this.checkingMessages.delete(message.id); this.update(message.id, message.channel_id); } if (isInteracting) { if (this.checkingMessages.has(message.id)) return; this.checkingMessages.add(message.id); this.update(message.id, message.channel_id); const matches = this.getMatches(message); this.processMatches(matches, message.id, message.channel_id); } } ); } patchTopic() { Patcher.before(Praser, "parseTopic", (_, [content]) => { const matches = this.getIDsFromText(content); this.processMatches(matches, "topic"); }); } onStop() { Patcher.unpatchAll(); } }; })(ZLibrary.buildPlugin(config)); })(); /*@end@*/