`);
- this.channelLogButton.addEventListener('click', () => {
- this.openWindow();
- });
- this.channelLogButton.addEventListener('contextmenu', () => {
- if (!this.selectedChannel) return;
- this.menu.filter = `channel:${this.selectedChannel.id}`;
- this.openWindow();
- });
- new ZeresPluginLibrary.Tooltip(this.channelLogButton, 'Open Logs', { side: 'bottom' });
-
- if (this.settings.showOpenLogsButton) this.addOpenLogsButton();
-
- this.unpatches.push(
- this.Patcher.instead(ZeresPluginLibrary.DiscordModules.MessageActions, 'deleteMessage', (_, args, original) => {
- const messageId = args[1];
- if (this.messageRecord[messageId] && this.messageRecord[messageId].delete_data) return;
- this.localDeletes.push(messageId);
- if (this.localDeletes.length > 10) this.localDeletes.shift();
- return original(...args);
- })
- );
-
- this.unpatches.push(
- this.Patcher.instead(this.messageStore, 'getLastEditableMessage', (_this, [channelId]) => {
- const me = XenoLib.DiscordAPI.userId;
- return _this
- .getMessages(channelId)
- .toArray()
- .reverse()
- .find(iMessage => iMessage.author.id === me && iMessage.state === 'SENT' && (!this.messageRecord[iMessage.id] || !this.messageRecord[iMessage.id].delete_data));
- })
- );
- this.patchContextMenus();
-
- if (!(this.settings.flags & Flags.STARTUP_HELP)) {
- this.settings.flags |= Flags.STARTUP_HELP;
- this.showLoggerHelpModal(true);
- this.saveSettings();
- }
-
- this.selfTestInterval = setInterval(() => {
- this.selfTestTimeout = setTimeout(() => {
- if (this.selfTestFailures > 4) {
- clearInterval(this.selfTestInterval);
- this.selfTestInterval = 0;
- return BdApi.alert(`${this.getName()}: internal error.`, `Self test failure: Failed to hook dispatch. Recommended to reload your discord (CTRL + R) as the plugin may be in a broken state! If you still see this error, open up the devtools console (CTRL + SHIFT + I, click console tab) and report the errors to ${this.getAuthor()} for further assistance.`);
- }
- ZeresPluginLibrary.Logger.warn(this.getName(), 'Dispatch is not hooked, all our hooks may be invalid, attempting to reload self');
- this.selfTestFailures++;
- this.stop();
- this.start();
- }, 3000);
- this.dispatcher.dispatch({
- type: 'MESSAGE_LOGGER_V2_SELF_TEST'
- });
- }, 10000);
-
- if (this.selfTestInited) return;
- this.selfTestFailures = 0;
- this.selfTestInited = true;
- }
- shutdown() {
- if (!global.ZeresPluginLibrary) return;
- this.__started = false;
- const tryUnpatch = fn => {
- if (typeof fn !== 'function') return;
- try {
- // things can bug out, best to reload tbh, should maybe warn the user?
- fn();
- } catch (e) {
- ZeresPluginLibrary.Logger.stacktrace(this.getName(), 'Error unpatching', e);
- }
- };
- if (Array.isArray(this.unpatches)) for (let unpatch of this.unpatches) tryUnpatch(unpatch);
- ZeresPluginLibrary.Patcher.unpatchAll(this.getName());
- if (this.MessageContextMenuPatch) tryUnpatch(this.MessageContextMenuPatch);
- if (this.ChannelContextMenuPatch) tryUnpatch(this.ChannelContextMenuPatch);
- if (this.GuildContextMenuPatch) tryUnpatch(this.GuildContextMenuPatch);
- try {
- this.Patcher.unpatchAll();
- } catch (e) { }
- this.forceReloadMessages();
- // if (this.keybindListener) this.keybindListener.destroy();
- if (this.style && this.style.css) ZeresPluginLibrary.PluginUtilities.removeStyle(this.style.css);
- if (this.dataManagerInterval) clearInterval(this.dataManagerInterval);
- // if (this.keybindListenerInterval) clearInterval(this.keybindListenerInterval);
- if (this.selfTestInterval) clearInterval(this.selfTestInterval);
- if (this.selfTestTimeout) clearTimeout(this.selfTestTimeout);
- if (this._autoUpdateInterval) clearInterval(this._autoUpdateInterval);
- if (this.keydownListener) document.removeEventListener('keydown', this.keydownListener);
- if (this.keyupListener) document.removeEventListener('keyup', this.keyupListener);
- // if (this.powerMonitor) this.powerMonitor.removeListener('resume', this.powerMonitorResumeListener);
- if (this.channelLogButton) this.channelLogButton.remove();
- if (this._imageCacheServer) this._imageCacheServer.stop();
- if (typeof this._modalsApiUnsubcribe === 'function')
- try {
- this._modalsApiUnsubcribe();
- } catch { }
- // console.log('invalidating cache');
- this.invalidateAllChannelCache();
- // if (this.selectedChannel) this.cacheChannelMessages(this.selectedChannel.id); // bad idea?
- }
- automaticallyUpdate(tryProxy) {
- const updateFail = () => XenoLib.Notifications.warning(`[${this.getName()}] Unable to check for updates!`, { timeout: 7500 });
- new Promise(resolve => {
- const https = require('https');
- const req = https.get(tryProxy ? 'https://cors-anywhere.herokuapp.com/https://raw.githubusercontent.com/1Lighty/BetterDiscordPlugins/master/Plugins/MessageLoggerV2/MessageLoggerV2.plugin.js' : 'https://raw.githubusercontent.com/1Lighty/BetterDiscordPlugins/master/Plugins/MessageLoggerV2/MessageLoggerV2.plugin.js', { headers: { 'origin': 'discord.com' } }, res => {
- let body = '';
- res.on('data', chunk => ((body += new TextDecoder("utf-8").decode(chunk)), void 0));
- res.on('end', (rez) => {
- if (rez.statusCode !== 200) {
- if (!tryProxy) return this.automaticallyUpdate(true);
- updateFail();
- return;
- }
- if (!XenoLib.versionComparator(this.getVersion(), XenoLib.extractVersion(body))) return;
- const fs = require('fs');
- /*
- * why are we letting Zere, the braindead American let control BD when he can't even
- * fucking read clearly documented and well known standards, such as __filename being
- * the files full fucking path and not just the filename itself, IS IT REALLY SO HARD
- * TO FUCKING READ?! https://nodejs.org/api/modules.html#modules_filename
- */
- const _zerecantcode_path = require('path');
- const theActualFileNameZere = _zerecantcode_path.join(__dirname, _zerecantcode_path.basename(__filename));
- fs.writeFileSync(theActualFileNameZere, body);
- XenoLib.Notifications.success(`[${this.getName()}] Successfully updated!`, { timeout: 0 });
- if (BdApi.isSettingEnabled('fork-ps-5') && !this.__isPowerCord) return;
- BdApi.Plugins.reload(this.getName());
- });
- });
- req.on('error', _ => {
- if (!tryProxy) return this.automaticallyUpdate(true);
- updateFail();
- });
- //req.end();
- });
- }
- // title-3qD0b- da-title container-1r6BKw da-container themed-ANHk51 da-themed
- // chatContent-a9vAAp da-chatContent
- observer({ addedNodes }) {
- let isChat = false;
- let isTitle = false;
- for (const change of addedNodes) {
- if ((isTitle = isChat = typeof change.className === 'string' && change.className.indexOf(this.observer.chatClass) !== -1) || (isChat = typeof change.className === 'string' && change.className.indexOf(this.observer.chatContentClass) !== -1) || (isTitle = typeof change.className === 'string' && change.className.indexOf(this.observer.titleClass) !== -1) || (change.style && change.style.cssText === 'border-radius: 2px; background-color: rgba(114, 137, 218, 0);') || (typeof change.className === 'string' && change.className.indexOf(this.observer.containerCozyClass) !== -1)) {
- try {
- if (isChat) {
- this.selectedChannel = this.getSelectedTextChannel();
- this.noTintIds = [];
- this.editModifiers = {};
- }
- if (!this.selectedChannel) return ZeresPluginLibrary.Logger.warn(this.getName(), 'Chat was loaded but no text channel is selected');
- if (isTitle && this.settings.showOpenLogsButton) {
- let srch = change.querySelector('div[class*="search-"]');
- if (!srch) return ZeresPluginLibrary.Logger.warn(this.getName(), 'Observer caught title loading, but no search bar was found! Open Logs button will not show!');
- if (this.channelLogButton && srch.parentElement) {
- srch.parentElement.insertBefore(this.channelLogButton, srch); // memory leak..?
- }
- srch = null;
- if (!isChat) return;
- }
- const showStuff = (map, name) => {
- if (map[this.selectedChannel.id] && map[this.selectedChannel.id]) {
- if (this.settings.useNotificationsInstead) {
- XenoLib.Notifications.info(`There are ${map[this.selectedChannel.id]} new ${name} messages in ${this.selectedChannel.name && this.selectedChannel.type !== 3 ? '<#' + this.selectedChannel.id + '>' : 'DMs'}`, { timeout: 3000 });
- } else {
- this.showToast(`There are ${map[this.selectedChannel.id]} new ${name} messages in ${this.selectedChannel.name ? '#' + this.selectedChannel.name : 'DMs'}`, {
- type: 'info',
- onClick: () => this.openWindow(name),
- timeout: 3000
- });
- }
- map[this.selectedChannel.id] = 0;
- }
- };
- if (this.settings.showDeletedCount) showStuff(this.deletedChatMessagesCount, 'deleted');
- if (this.settings.showEditedCount) showStuff(this.editedChatMessagesCount, 'edited');
- } catch (e) {
- ZeresPluginLibrary.Logger.stacktrace(this.getName(), 'Error in observer', e);
- }
- break;
- }
- }
- }
- buildSetting(data) {
- const { id } = data;
- const setting = XenoLib.buildSetting(data);
- if (id) setting.getElement().id = this.obfuscatedClass(id);
- return setting;
- }
- createSetting(data) {
- const current = Object.assign({}, data);
- if (!current.onChange) {
- current.onChange = value => {
- this.settings[current.id] = value;
- if (current.callback) current.callback(value);
- };
- }
- if (typeof current.value === 'undefined') current.value = this.settings[current.id];
- return this.buildSetting(current);
- }
- createGroup(group) {
- const { name, id, collapsible, shown, settings } = group;
-
- const list = [];
- for (let s = 0; s < settings.length; s++) list.push(this.createSetting(settings[s]));
-
- const settingGroup = new ZeresPluginLibrary.Settings.SettingGroup(name, { shown, collapsible }).append(...list);
- settingGroup.group.id = id; // should generate the id in here instead?
- return settingGroup;
- }
- getSettingsPanel() {
- // todo, sort out the menu
- const list = [];
- // list.push(
- // this.createGroup({
- // name: 'Keybinds',
- // id: this.obfuscatedClass('ml2-settings-keybinds'),
- // collapsible: true,
- // shown: false,
- // settings: [
- // {
- // name: 'Open menu keybind',
- // id: 'openLogKeybind',
- // type: 'keybind'
- // },
- // {
- // name: 'Open log filtered by selected channel',
- // id: 'openLogFilteredKeybind',
- // type: 'keybind'
- // },
- // {
- // name: 'Disable keybinds',
- // id: 'disableKeybind',
- // type: 'switch'
- // }
- // ]
- // })
- // );
- list.push(
- this.createGroup({
- name: 'Ignores and overrides',
- id: this.obfuscatedClass('ml2-settings-ignores-overrides'),
- collapsible: true,
- shown: false,
- settings: [
- {
- name: 'Ignore muted servers',
- id: 'ignoreMutedGuilds',
- type: 'switch'
- },
- {
- name: 'Ignore muted channels',
- id: 'ignoreMutedChannels',
- type: 'switch'
- },
- {
- name: 'Ignore bots',
- id: 'ignoreBots',
- type: 'switch'
- },
- {
- name: 'Ignore messages posted by you',
- id: 'ignoreSelf',
- type: 'switch'
- },
- {
- name: 'Ignore message edits from you',
- id: 'ignoreLocalEdits',
- type: 'switch'
- },
- {
- name: 'Ignore message deletes from you',
- note: 'Only ignores if you delete your own message.',
- id: 'ignoreLocalDeletes',
- type: 'switch'
- },
- {
- name: 'Ignore blocked users',
- id: 'ignoreBlockedUsers',
- type: 'switch'
- },
- {
- name: 'Ignore NSFW channels',
- id: 'ignoreNSFW',
- type: 'switch'
- },
- {
- name: 'Only log whitelist',
- id: 'onlyLogWhitelist',
- type: 'switch'
- },
- {
- name: 'Always log selected channel, regardless of whitelist/blacklist',
- id: 'alwaysLogSelected',
- type: 'switch'
- },
- {
- name: 'Always log DMs, regardless of whitelist/blacklist',
- id: 'alwaysLogDM',
- type: 'switch'
- },
- {
- name: 'Always log ghost pings, regardless of whitelist/blacklist',
- note: 'Messages sent in ignored/muted/blacklisted servers and channels will be logged and shown in sent, but only gets saved if a ghost ping occurs.',
- id: 'alwaysLogGhostPings',
- type: 'switch'
- }
- ]
- })
- );
- list.push(
- this.createGroup({
- name: 'Display settings',
- id: this.obfuscatedClass('ml2-settings-display'),
- collapsible: true,
- shown: false,
- settings: [
- {
- name: 'Display dates with timestamps',
- id: 'displayDates',
- type: 'switch',
- callback: () => {
- if (this.selectedChannel) {
- // change NOW
- this.invalidateAllChannelCache();
- this.cacheChannelMessages(this.selectedChannel.id);
- }
- }
- },
- {
- name: 'Display deleted messages in chat',
- id: 'showDeletedMessages',
- type: 'switch',
- callback: () => {
- this.invalidateAllChannelCache();
- if (this.selectedChannel) this.cacheChannelMessages(this.selectedChannel.id);
- }
- },
- {
- name: 'Display edited messages in chat',
- id: 'showEditedMessages',
- type: 'switch',
- callback: () => this.dispatcher.dispatch({ type: 'MLV2_FORCE_UPDATE_MESSAGE_CONTENT' })
- },
- {
- name: 'Max number of shown edits',
- id: 'maxShownEdits',
- type: 'textbox',
- onChange: val => {
- if (isNaN(val)) return this.showToast('Value must be a number!', { type: 'error' });
- this.settings.maxShownEdits = parseInt(val);
- this.dispatcher.dispatch({ type: 'MLV2_FORCE_UPDATE_MESSAGE_CONTENT' });
- }
- },
- {
- name: 'Show oldest edit instead of newest if over the shown edits limit',
- id: 'hideNewerEditsFirst',
- type: 'switch',
- callback: () => this.dispatcher.dispatch({ type: 'MLV2_FORCE_UPDATE_MESSAGE_CONTENT' })
- },
- {
- name: 'Use red background instead of red text for deleted messages',
- id: 'useAlternativeDeletedStyle',
- type: 'switch',
- callback: () => this.dispatcher.dispatch({ type: 'MLV2_FORCE_UPDATE_MESSAGE' })
- },
- {
- name: 'Display purged messages in chat',
- id: 'showPurgedMessages',
- type: 'switch',
- callback: () => {
- this.invalidateAllChannelCache();
- if (this.selectedChannel) this.cacheChannelMessages(this.selectedChannel.id);
- }
- },
- {
- name: 'Restore deleted messages after reload',
- id: 'restoreDeletedMessages',
- type: 'switch',
- callback: val => {
- if (val) {
- this.invalidateAllChannelCache();
- if (this.selectedChannel) this.cacheChannelMessages(this.selectedChannel.id);
- }
- }
- },
- {
- name: 'Show amount of new deleted messages when entering a channel',
- id: 'showDeletedCount',
- type: 'switch'
- },
- {
- name: 'Show amount of new edited messages when entering a channel',
- id: 'showEditedCount',
- type: 'switch'
- },
- {
- name: 'Display update notes',
- id: 'displayUpdateNotes',
- type: 'switch'
- },
- {
- name: 'Menu sort direction',
- id: 'reverseOrder',
- type: 'radio',
- options: [
- {
- name: 'New - old',
- value: false
- },
- {
- name: 'Old - new',
- value: true
- }
- ]
- },
- {
- name: 'Use XenoLib notifications instead of toasts',
- note: "This works for edit, send, delete and purge toasts, as well as delete and edit count toasts. Toggle it if you don't know what this does.",
- id: 'useNotificationsInstead',
- type: 'switch',
- callback: e => (e ? XenoLib.Notifications.success('Using Xenolib notifications', { timeout: 5000 }) : this.showToast('Using toasts', { type: 'success', timeout: 5000 }))
- }
- ]
- })
- );
- list.push(
- this.createGroup({
- name: 'Misc settings',
- id: this.obfuscatedClass('ml2-settings-misc'),
- collapsible: true,
- shown: false,
- settings: [
- {
- name: 'Disable saving data. Logged messages are erased after reload/restart. Disables auto backup.',
- id: 'dontSaveData',
- type: 'switch',
- callback: val => {
- if (!val) this.saveData();
- if (!val && this.settings.autoBackup) this.saveBackup();
- }
- },
- {
- name: "Auto backup data (won't fully prevent losing data, just prevent total data loss)",
- id: 'autoBackup',
- type: 'switch',
- callback: val => {
- if (val && !this.settings.dontSaveData) this.saveBackup();
- }
- } /*
- {
- // no time, TODO!
- name: 'Deleted messages color',
- id: 'deletedMessageColor',
- type: 'color'
- }, */,
- {
- name: 'Aggresive message caching (makes sure we have the data of any deleted or edited messages)',
- id: 'aggresiveMessageCaching',
- type: 'switch'
- },
- {
- name: 'Cache all images by storing them locally in the MLV2_IMAGE_CACHE folder inside the plugins folder',
- id: 'cacheAllImages',
- type: 'switch'
- },
- {
- name: "Don't delete cached images",
- note: "If the message the image is from is erased from data, the cached image will be kept. You'll have to monitor disk usage on your own!",
- id: 'dontDeleteCachedImages',
- type: 'switch'
- },
- {
- name: 'Display open logs button next to the search box top right in channels',
- id: 'showOpenLogsButton',
- type: 'switch',
- callback: val => {
- if (val) return this.addOpenLogsButton();
- this.removeOpenLogsButton();
- }
- },
- {
- name: 'Block spam edit notifications (if enabled)',
- id: 'blockSpamEdit',
- type: 'switch'
- }
- ]
- })
- );
- list.push(
- this.createGroup({
- name: 'Toast notifications for guilds',
- id: this.obfuscatedClass('ml2-settings-toast-guilds'),
- collapsible: true,
- shown: false,
- settings: [
- {
- name: 'Message sent',
- id: 'sent',
- type: 'switch',
- value: this.settings.toastToggles.sent,
- onChange: val => {
- this.settings.toastToggles.sent = val;
- }
- },
- {
- name: 'Message edited',
- id: 'edited',
- type: 'switch',
- value: this.settings.toastToggles.edited,
- onChange: val => {
- this.settings.toastToggles.edited = val;
- }
- },
- {
- name: 'Message deleted',
- id: 'deleted',
- type: 'switch',
- value: this.settings.toastToggles.deleted,
- onChange: val => {
- this.settings.toastToggles.deleted = val;
- }
- },
- {
- name: 'Ghost pings',
- id: 'ghostPings',
- type: 'switch',
- value: this.settings.toastToggles.ghostPings,
- onChange: val => {
- this.settings.toastToggles.ghostPings = val;
- }
- },
- {
- name: 'Disable toasts for local user (yourself)',
- id: 'disableToastsForLocal',
- type: 'switch',
- value: this.settings.toastToggles.disableToastsForLocal,
- onChange: val => {
- this.settings.toastToggles.disableToastsForLocal = val;
- }
- }
- ]
- })
- );
-
- list.push(
- this.createGroup({
- name: 'Toast notifications for DMs',
- id: this.obfuscatedClass('ml2-settings-toast-dms'),
- collapsible: true,
- shown: false,
- settings: [
- {
- name: 'Message sent',
- id: 'sent',
- type: 'switch',
- value: this.settings.toastTogglesDMs.sent,
- onChange: val => {
- this.settings.toastTogglesDMs.sent = val;
- }
- },
- {
- name: 'Message edited',
- id: 'edited',
- type: 'switch',
- value: this.settings.toastTogglesDMs.edited,
- onChange: val => {
- this.settings.toastTogglesDMs.edited = val;
- }
- },
- {
- name: 'Message deleted',
- id: 'deleted',
- type: 'switch',
- value: this.settings.toastTogglesDMs.deleted,
- onChange: val => {
- this.settings.toastTogglesDMs.deleted = val;
- }
- },
- {
- name: 'Ghost pings',
- id: 'ghostPings',
- type: 'switch',
- value: this.settings.toastTogglesDMs.ghostPings,
- onChange: val => {
- this.settings.toastTogglesDMs.ghostPings = val;
- }
- }
- ]
- })
- );
-
- list.push(
- this.createGroup({
- name: 'Message caps',
- id: this.obfuscatedClass('ml2-settings-caps'),
- collapsible: true,
- shown: false,
- settings: [
- {
- name: 'Cached messages cap',
- note: 'Max number of sent messages logger should keep track of',
- id: 'messageCacheCap',
- type: 'textbox',
- onChange: val => {
- if (isNaN(val)) return this.showToast('Value must be a number!', { type: 'error' });
- this.settings.messageCacheCap = parseInt(val);
- clearInterval(this.dataManagerInterval);
- this.dataManagerInterval = setInterval(() => {
- this.handleMessagesCap();
- }, 60 * 1000 * 5);
- }
- },
- {
- name: 'Saved messages cap',
- note: "Max number of messages saved to disk, this limit is for deleted, edited and purged INDIVIDUALLY. So if you have it set to 1000, it'll be 1000 edits, 1000 deletes and 1000 purged messages max",
- id: 'savedMessagesCap',
- type: 'textbox',
- onChange: val => {
- if (isNaN(val)) return this.showToast('Value must be a number!', { type: 'error' });
- this.settings.savedMessagesCap = parseInt(val);
- clearInterval(this.dataManagerInterval);
- this.dataManagerInterval = setInterval(() => {
- this.handleMessagesCap();
- }, 60 * 1000 * 5);
- }
- },
- {
- name: 'Menu message render cap',
- note: 'How many messages will show before the LOAD MORE button will show',
- id: 'renderCap',
- type: 'textbox',
- onChange: val => {
- if (isNaN(val)) return this.showToast('Value must be a number!', { type: 'error' });
- this.settings.renderCap = parseInt(val);
- clearInterval(this.dataManagerInterval);
- }
- }
- ]
- })
- );
-
- list.push(
- this.createGroup({
- name: 'Advanced',
- id: this.obfuscatedClass('ml2-settings-advanced'),
- collapsible: true,
- shown: false,
- settings: [
- {
- name: 'Obfuscate CSS classes',
- note: 'Enable this if some plugin, library or theme is blocking you from using the plugin',
- id: 'obfuscateCSSClasses',
- type: 'switch'
- },
- {
- name: 'Automatic updates',
- note: "Do NOT disable unless you really don't want automatic updates",
- id: 'autoUpdate',
- type: 'switch',
- callback: val => {
- if (val) {
- this._autoUpdateInterval = setInterval(_ => this.automaticallyUpdate(), 1000 * 60 * 15); // 15 minutes
- this.automaticallyUpdate();
- } else {
- clearInterval(this._autoUpdateInterval);
- try {
- ZeresPluginLibrary.PluginUpdater.checkForUpdate(this.getName(), this.getVersion(), 'https://raw.githubusercontent.com/1Lighty/BetterDiscordPlugins/master/Plugins/MessageLoggerV2/MessageLoggerV2.plugin.js');
- } catch (err) {}
- }
- }
- },
- {
- name: 'Contextmenu submenu name',
- note: "Instead of saying Message Logger, make it say something else, so it's screenshot friendly",
- id: 'contextmenuSubmenuName',
- type: 'textbox'
- } /* ,
- {
- name: 'Image cache directory',
- note: 'Press enter to save the path',
- id: 'imageCacheDir',
- type: 'path',
- onChange: val => {
- console.log(this.settings.imageCacheDir, val, 'what?');
- if (this.settings.imageCacheDir === val) return;
- const savedImages = this.nodeModules.fs.readdirSync(this.settings.imageCacheDir);
- console.log(savedImages);
- if (!savedImages.length) return;
- https://stackoverflow.com/questions/10420352/
- function humanFileSize(bytes, si) {
- const thresh = si ? 1000 : 1024;
- if (Math.abs(bytes) < thresh) return `${bytes} B`;
- const units = si ? ['kB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'] : ['KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB'];
- let u = -1;
- do {
- bytes /= thresh;
- ++u;
- } while (Math.abs(bytes) >= thresh && u < units.length - 1);
- return `${bytes.toFixed(1)}${units[u]}`;
- }
- let sz = 0;
- for (let image of savedImages) ;
- const size = humanFileSize(this.nodeModules.fs.statSync(this.settings.imageCacheDir).size);
- ZeresPluginLibrary.Modals.showModal('Move images', ZeresPluginLibrary.DiscordModules.React.createElement(ZeresPluginLibrary.DiscordModules.TextElement.default, { color: ZeresPluginLibrary.DiscordModules.TextElement.Colors.PRIMARY, children: [`Would you like to move ${savedImages.length} images from the old folder to the new? Size of all images is ${size}.`] }), {
- confirmText: 'Yes',
- onConfirm: () => {}
- });
- //this.settings.imageCacheDir = val;
- }
- } */
- ]
- })
- );
-
- const div = document.createElement('div');
- div.id = this.obfuscatedClass('ml2-settings-buttonbox');
- div.style.display = 'inline-flex';
- div.appendChild(this.createButton('Changelog', () => XenoLib.showChangelog(`${this.getName()} has been updated!`, this.getVersion(), this.getChanges())));
- div.appendChild(this.createButton('Stats', () => this.showStatsModal()));
- div.appendChild(this.createButton('Donate', () => this.nodeModules.electron.shell.openExternal('https://paypal.me/lighty13')));
- div.appendChild(
- this.createButton('Support server', () => {
- ZeresPluginLibrary.DiscordModules.LayerManager.popLayer();
- if (this.tools.getServer('389049952732446731')) {
- ZeresPluginLibrary.DiscordModules.GuildActions.transitionToGuildSync('389049952732446731');
- } else {
- ZeresPluginLibrary.DiscordModules.InviteActions.openNativeAppModal('NYvWdN5');
- }
- })
- );
- div.appendChild(this.createButton('Help', () => this.showLoggerHelpModal()));
- let button = div.firstElementChild;
- while (button) {
- button.style.marginRight = button.style.marginLeft = `5px`;
- button = button.nextElementSibling;
- }
-
- list.push(div);
-
- return ZeresPluginLibrary.Settings.SettingPanel.build(_ => this.saveSettings(), ...list);
- }
- /* ==================================================-|| START HELPERS ||-================================================== */
- saveSettings() {
- ZeresPluginLibrary.PluginUtilities.saveSettings(this.getName(), this.settings);
- }
- handleDataSaving() {
- // saveData/setPluginData is synchronous, can get slow with bigger files
- if (!this.handleDataSaving.errorPageClass) this.handleDataSaving.errorPageClass = '.' + XenoLib.getClass('errorPage');
- /* refuse saving on error page */
- if (!this.messageRecord || document.querySelector(this.handleDataSaving.errorPageClass)) return; /* did we crash? */
- if (!Object.keys(this.messageRecord).length) return BdApi.deleteData(this.getName() + 'Data', 'data');
- const callback = err => {
- if (err) {
- XenoLib.Notifications.error('There has been an error saving the data file');
- ZeresPluginLibrary.Logger.stacktrace(this.getName(), 'There has been an error saving the data file', err);
- }
- if (this.settings.autoBackup) {
- if (this.saveBackupTimeout) this.autoBackupSaveInterupts++;
- if (this.autoBackupSaveInterupts < 4) {
- if (this.saveBackupTimeout) clearTimeout(this.saveBackupTimeout);
- // 20 seconds after, in case shits going down y'know, better not to spam save and corrupt it, don't become the thing you're trying to eliminate
- this.saveBackupTimeout = setTimeout(() => this.saveBackup(), 20 * 1000);
- }
- }
- this.requestedDataSave = 0;
- };
- const useEfficient = !window.ED;
- if (useEfficient) {
- this.efficientlySaveData(
- this.getName() + 'Data',
- 'data',
- {
- messageRecord: this.messageRecord,
- deletedMessageRecord: this.deletedMessageRecord,
- editedMessageRecord: this.editedMessageRecord,
- purgedMessageRecord: this.purgedMessageRecord
- },
- callback
- );
- } else {
- ZeresPluginLibrary.PluginUtilities.saveData(this.getName() + 'Data', 'data', {
- messageRecord: this.messageRecord,
- deletedMessageRecord: this.deletedMessageRecord,
- editedMessageRecord: this.editedMessageRecord,
- purgedMessageRecord: this.purgedMessageRecord
- });
- callback();
- }
- }
- saveData() {
- if (!this.settings.dontSaveData && !this.requestedDataSave) this.requestedDataSave = setTimeout(() => this.handleDataSaving(), 1000); // needs to be async
- }
- efficientlySaveData(name, key, data, callback) {
- try {
- let loadedData;
- try {
- /* bd gay bruh */
- loadedData = BdApi.loadData(name, key);
- } catch (err) { }
- if (loadedData) for (const key in data) loadedData[key] = data[key];
- this.nodeModules.fs.writeFile(this.__isPowerCord ? BdApi.__getPluginConfigPath(name) : this.nodeModules.path.join(this.pluginDir, `${name}.config.json`), JSON.stringify({ [key]: data }), callback);
- } catch (e) {
- XenoLib.Notifications.error('There has been an error saving the data file');
- ZeresPluginLibrary.Logger.stacktrace(this.getName(), 'There has been an error saving the data file', e);
- }
- }
- saveBackup() {
- const callback = err => {
- if (err) {
- XenoLib.Notifications.error('There has been an error saving the data file');
- ZeresPluginLibrary.Logger.stacktrace(this.getName(), 'There has been an error saving the data file', err);
- }
- this.saveBackupTimeout = 0;
- this.autoBackupSaveInterupts = 0;
- if (!XenoLib.loadData(this.getName() + 'DataBackup', 'data').messageRecord) this.saveBackupTimeout = setTimeout(() => this.saveBackup, 300); // don't be taxing
- };
- const useEfficient = !window.ED;
- if (useEfficient) {
- this.efficientlySaveData(
- this.getName() + 'DataBackup',
- 'data',
- {
- messageRecord: this.messageRecord,
- deletedMessageRecord: this.deletedMessageRecord,
- editedMessageRecord: this.editedMessageRecord,
- purgedMessageRecord: this.purgedMessageRecord
- },
- callback
- );
- } else {
- ZeresPluginLibrary.PluginUtilities.saveData(this.getName() + 'DataBackup', 'data', {
- messageRecord: this.messageRecord,
- deletedMessageRecord: this.deletedMessageRecord,
- editedMessageRecord: this.editedMessageRecord,
- purgedMessageRecord: this.purgedMessageRecord
- });
- callback();
- }
- }
- parseHTML(html) {
- // TODO: drop this func, it's 75% slower than just making the elements manually
- var template = document.createElement('template');
- html = html.trim(); // Never return a text node of whitespace as the result
- template.innerHTML = html;
- return template.content.firstChild;
- }
- randomString() {
- let start = rand();
- while (start[0].toUpperCase() == start[0].toLowerCase()) start = rand();
- return start + '-' + rand();
- function rand() {
- return Math.random().toString(36).substr(2, 7);
- }
- }
- obfuscatedClass(selector) {
- if (!this.obfuscatedClass.obfuscations) this.obfuscatedClass.obfuscations = {};
- if (this.settings.obfuscateCSSClasses) {
- const { obfuscations } = this.obfuscatedClass;
- return obfuscations[selector] || (obfuscations[selector] = this.randomString());
- }
- return selector;
- }
- createTimeStamp(from = undefined, forcedDate = false) {
- // todo: timestamp for edited tooltip
- let date;
- if (from) date = new Date(from);
- else date = new Date();
- return (this.settings.displayDates || forcedDate) && forcedDate !== -1 ? `${date.toLocaleTimeString()}, ${date.toLocaleDateString()}` : forcedDate !== -1 ? date.toLocaleTimeString() : date.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' });
- }
- getCachedMessage(id, channelId = 0) {
- let cached = this.cachedMessageRecord.find(m => m.id == id);
- if (cached) return cached;
- if (channelId) return this.tools.getMessage(channelId, id); // if the message isn't cached, it returns undefined
- return null;
- }
- getEditedMessage(messageId, channelId) {
- if (this.editedMessageRecord[channelId] && this.editedMessageRecord[channelId].findIndex(m => m === messageId) != -1) {
- return this.messageRecord[messageId];
- }
- return null;
- }
- getSavedMessage(id) {
- /* DEPRECATED */
- return this.messageRecord[id];
- }
- cleanupUserObject(user) {
- /* backported from MLV2 rewrite */
- return {
- discriminator: user.discriminator,
- username: user.username,
- avatar: user.avatar,
- id: user.id,
- bot: user.bot,
- public_flags: typeof user.publicFlags !== 'undefined' ? user.publicFlags : user.public_flags
- };
- }
- cleanupMessageObject(message) {
- const ret = {
- mention_everyone: typeof message.mention_everyone !== 'boolean' ? typeof message.mentionEveryone !== 'boolean' ? false : message.mentionEveryone : message.mention_everyone,
- edited_timestamp: message.edited_timestamp || message.editedTimestamp && new Date(message.editedTimestamp).getTime() || null,
- attachments: message.attachments || [],
- channel_id: message.channel_id,
- reactions: (message.reactions || []).map(e => (!e.emoji.animated && delete e.emoji.animated, !e.me && delete e.me, e)),
- guild_id: message.guild_id || (this.ChannelStore.getChannel(message.channel_id) ? this.ChannelStore.getChannel(message.channel_id).guild_id : undefined),
- content: message.content,
- type: message.type,
- embeds: message.embeds || [],
- author: this.cleanupUserObject(message.author),
- mentions: (message.mentions || []).map(e => (typeof e === 'string' ? this.UserStore.getUser(e) ? this.cleanupUserObject(this.UserStore.getUser(e)) : e : this.cleanupUserObject(e))),
- mention_roles: message.mention_roles || message.mentionRoles || [],
- id: message.id,
- flags: message.flags,
- timestamp: new Date(message.timestamp).getTime(),
- referenced_message: null
- };
- if (ret.type === 19) {
- ret.message_reference = message.message_reference || message.messageReference;
- if (ret.message_reference) {
- if (message.referenced_message) {
- ret.referenced_message = this.cleanupMessageObject(message.referenced_message);
- } else if (this.messageStore.getMessage(ret.message_reference.channel_id, ret.message_reference.message_id)) {
- ret.referenced_message = this.cleanupMessageObject(this.messageStore.getMessage(ret.message_reference.channel_id, ret.message_reference.message_id));
- }
- }
- }
- this.fixEmbeds(ret);
- return ret;
- }
- createMiniFormattedData(message) {
- message = XenoLib.DiscordUtils.cloneDeep(message);
- const obj = {
- message: this.cleanupMessageObject(message), // works!
- local_mentioned: this.tools.isMentioned(message, this.localUser.id),
- /* ghost_pinged: false, */
- delete_data: null /* {
- time: integer,
- hidden: bool
- } */,
- edit_history: null /* [
- {
- content: string,
- timestamp: string
- }
- ],
- edits_hidden: bool */
- };
- return obj;
- }
- getSelectedTextChannel() {
- return this.ChannelStore.getChannel(ZeresPluginLibrary.DiscordModules.SelectedChannelStore.getChannelId());
- }
- invalidateAllChannelCache() {
- for (let channelId in this.channelMessages) this.invalidateChannelCache(channelId);
- }
- invalidateChannelCache(channelId) {
- if (!this.channelMessages[channelId]) return;
- this.channelMessages[channelId].ready = false;
- }
- cacheChannelMessages(id, relative) {
- // TODO figure out if I can use this to get messages at a certain point
- this.tools.fetchMessages({ channelId: id, limit: 50, jump: (relative && { messageId: relative, ML2: true }) || undefined });
- }
- /* UNUSED */
- cachenChannelMessagesRelative(channelId, messageId) {
- ZeresPluginLibrary.DiscordModules.APIModule.get({
- url: ZeresPluginLibrary.DiscordModules.DiscordConstants.Endpoints.MESSAGES(channelId),
- query: {
- before: null,
- after: null,
- limit: 50,
- around: messageId
- }
- })
- .then(res => {
- if (res.status != 200) return;
- const results = res.body;
- const final = results.filter(x => this.cachedMessageRecord.findIndex(m => x.id === m.id) == -1);
- this.cachedMessageRecord.push(...final);
- })
- .catch(err => {
- ZeresPluginLibrary.Logger.stacktrace(this.getName(), `Error caching messages from ${channelId} around ${messageId}`, err);
- });
- }
- formatMarkup(content, channelId) {
- const markup = document.createElement('div');
-
- const parsed = this.tools.parse(content, true, channelId ? { channelId: channelId } : {});
- // console.log(parsed);
- // error, this render doesn't work with tags
- // TODO: this parser and renderer sucks
- // this may be causing a severe memory leak over the course of a few hours
- ZeresPluginLibrary.DiscordModules.ReactDOM.render(ZeresPluginLibrary.DiscordModules.React.createElement('div', { className: '' }, parsed), markup);
-
- const hiddenClass = this.classes.hidden;
-
- const hidden = markup.getElementsByClassName(hiddenClass);
-
- for (let i = 0; i < hidden.length; i++) {
- hidden[i].classList.remove(hiddenClass);
- }
- const child = markup.firstChild;
- let previousTab = this.menu.selectedTab;
- let previousOpen = this.menu.open;
- const callback = () => {
- if (this.menu.open === previousOpen && this.menu.selectedTab === previousTab) return; /* lol ez */
- try {
- markup.appendChild(child);
- ZeresPluginLibrary.DiscordModules.ReactDOM.unmountComponentAtNode(markup);
- } catch (e) { }
- ZeresPluginLibrary.DOMTools.observer.unsubscribe(callback);
- };
- ZeresPluginLibrary.DOMTools.observer.subscribe(callback, mutation => {
- const nodes = Array.from(mutation.removedNodes);
- const directMatch = nodes.indexOf(child) > -1;
- const parentMatch = nodes.some(parent => parent.contains(child));
- return directMatch || parentMatch;
- });
- return child;
- }
- async showToast(content, options = {}) {
- // credits to Zere, copied from Zeres Plugin Library
- const { type = '', icon = '', timeout = 3000, onClick = () => { }, onContext = () => { } } = options;
- ZeresPluginLibrary.Toasts.ensureContainer();
- const toast = ZeresPluginLibrary['DOMTools'].parseHTML(ZeresPluginLibrary.Toasts.buildToast(content.replace(/&/g, '&').replace(/ {
- toast.classList.add('closing');
- sto2 = setTimeout(() => {
- toast.remove();
- if (!document.querySelectorAll('.toasts .toast').length) document.querySelector('.toasts').remove();
- }, 300);
- };
- const sto = setTimeout(wait, timeout);
- const toastClicked = () => {
- clearTimeout(sto);
- clearTimeout(sto2);
- wait();
- };
- toast.addEventListener('auxclick', toastClicked);
- toast.addEventListener('click', () => {
- toastClicked();
- onClick();
- });
- toast.addEventListener('contextmenu', () => {
- toastClicked();
- onContext();
- });
- }
- clamp(val, min, max) {
- // this is so sad, can we hit Metalloriff?
- // his message logger added the func to Math obj and I didn't realize
- return Math.max(min, Math.min(val, max));
- }
- deleteEditedMessageFromRecord(id, editNum) {
- const record = this.messageRecord[id];
- if (!record) return;
-
- record.edit_history.splice(editNum, 1);
- if (!record.edit_history.length) record.edit_history = null;
- else return this.saveData();
-
- const channelId = record.message.channel_id;
- const channelMessages = this.editedMessageRecord[channelId];
- channelMessages.splice(
- channelMessages.findIndex(m => m === id),
- 1
- );
- if (this.deletedMessageRecord[channelId] && this.deletedMessageRecord[channelId].findIndex(m => m === id) != -1) return this.saveData();
- if (this.purgedMessageRecord[channelId] && this.purgedMessageRecord[channelId].findIndex(m => m === id) != -1) return this.saveData();
- delete this.messageRecord[id];
- this.saveData();
- }
- jumpToMessage(channelId, messageId, guildId) {
- if (this.menu.open) this.ModalStack.closeModal(this.style.menu);
- ZeresPluginLibrary.DiscordModules.NavigationUtils.transitionTo(`/channels/${guildId || '@me'}/${channelId}${messageId ? '/' + messageId : ''}`);
- }
- isImage(url) {
- return /\.(jpe?g|png|gif|bmp)$/i.test(url);
- }
- cleanupEmbed(embed) {
- /* backported code from MLV2 rewrite */
- if (!embed.id) return embed; /* already cleaned */
- const retEmbed = {};
- if (typeof embed.rawTitle === 'string') retEmbed.title = embed.rawTitle;
- if (typeof embed.rawDescription === 'string') retEmbed.description = embed.rawDescription;
- if (typeof embed.referenceId !== 'undefined') retEmbed.reference_id = embed.referenceId;
- if (typeof embed.color === 'string') retEmbed.color = ZeresPluginLibrary.ColorConverter.hex2int(embed.color);
- if (typeof embed.type !== 'undefined') retEmbed.type = embed.type;
- if (typeof embed.url !== 'undefined') retEmbed.url = embed.url;
- if (typeof embed.provider === 'object') retEmbed.provider = { name: embed.provider.name, url: embed.provider.url };
- if (typeof embed.footer === 'object') retEmbed.footer = { text: embed.footer.text, icon_url: embed.footer.iconURL, proxy_icon_url: embed.footer.iconProxyURL };
- if (typeof embed.author === 'object') retEmbed.author = { name: embed.author.name, url: embed.author.url, icon_url: embed.author.iconURL, proxy_icon_url: embed.author.iconProxyURL };
- if (typeof embed.timestamp === 'object' && embed.timestamp._isAMomentObject) retEmbed.timestamp = embed.timestamp.milliseconds();
- if (typeof embed.thumbnail === 'object') {
- if (typeof embed.thumbnail.proxyURL === 'string' || (typeof embed.thumbnail.url === 'string' && !embed.thumbnail.url.endsWith('?format=jpeg'))) {
- retEmbed.thumbnail = {
- url: embed.thumbnail.url,
- proxy_url: typeof embed.thumbnail.proxyURL === 'string' ? embed.thumbnail.proxyURL.split('?format')[0] : undefined,
- width: embed.thumbnail.width,
- height: embed.thumbnail.height
- };
- }
- }
- if (typeof embed.image === 'object') {
- retEmbed.image = {
- url: embed.image.url,
- proxy_url: embed.image.proxyURL,
- width: embed.image.width,
- height: embed.image.height
- };
- }
- if (typeof embed.video === 'object') {
- retEmbed.video = {
- url: embed.video.url,
- proxy_url: embed.video.proxyURL,
- width: embed.video.width,
- height: embed.video.height
- };
- }
- if (Array.isArray(embed.fields) && embed.fields.length) {
- retEmbed.fields = embed.fields.map(e => ({ name: e.rawName, value: e.rawValue, inline: e.inline }));
- }
- return retEmbed;
- }
- fixEmbeds(message) {
- message.embeds = message.embeds.map(this.cleanupEmbed);
- }
- isCompact() {
- return false; // fix if someone complains, no one has so far so who cares
- }
- /* ==================================================-|| END HELPERS ||-================================================== */
- /* ==================================================-|| START MISC ||-================================================== */
- addOpenLogsButton() {
- if (!this.selectedChannel) return;
- const parent = document.querySelector('div[class*="chat-"] div[class*="toolbar-"]');
- if (!parent) return;
- const srch = parent.querySelector('div[class*="search-"]'); // you know who you are that think this is my issue
- if (!srch) return;
- parent.insertBefore(this.channelLogButton, srch);
- }
- removeOpenLogsButton() {
- this.channelLogButton.remove();
- }
- showLoggerHelpModal(initial = false) {
- return;
- this.createModal({
- confirmText: 'OK',
- header: 'Logger help',
- size: this.createModal.confirmationModal.Sizes.LARGE,
- children: [
- ZeresPluginLibrary.ReactTools.createWrappedElement([
- this.parseHTML(
- `
- ${initial ? 'As you are a first time user, you must know in order to have a server be logged, you must RIGHT CLICK a server or channel and add it to the whitelist.Alternatively if this behavior is unwanted, you can always log all unmuted servers and channels by disabling Only log whitelist in logger settings under IGNORES AND OVERRIDES.' : ''}
- Hello! This is the ${this.getName()} help modal! You may at any time open this in plugin settings by clicking the help button, or in the menu by pressing the question mark button and then then Logger help button.
- Menu:
-
- DELETE + LEFT-CLICK:
-
- Clicking on a message, deletes the message
- Clicking on an edit deletes that specific edit
- Clicking on the timestamp deletes all messages in that message group
-
- RIGHT-CLICK:
-
- Right-clicking the timestamp opens up options for the entire message group
-
-
- Toasts:
-
- Note: Little "notifications" in discord that tell you if a message was edited, deleted, purged etc are called Toasts!
- LEFT-CLICK:
-
- Opens menu with the relevant tab
-
- RIGHT-CLICK:
-
- Jumps to relevant message in the relevant channel
-
- MIDDLE-CLICK/SCROLLWHEEL-CLICK:
-
- Only dismisses/closes the Toast.
-
-
- Notifications:
-
- Note: They show in the top right corner and are called XenoLib notifications. Can be enabled in Settings > Display Settings, all the way at the bottom.
- LEFT-CLICK:
-
- Opens menu with the relevant tab
-
- RIGHT-CLICK:
-
- Jumps to relevant message in the relevant channel
-
-
- Open Logs button (top right next to search):
-
- LEFT-CLICK:
-
- Opens menu
-
- RIGHT-CLICK:
-
- Opens filtered menu that only shows messages from selected channel
-
-
- Whitelist/blacklist, ignores and overrides:
-
- WHITELIST-ONLY:
-
- All servers are ignored unless whitelisted
- Muted channels in whitelisted servers are ignored unless whitelisted or "Ignore muted channels" is disabled
- All channels in whitelisted servers are logged unless blacklisted, or muted and "Ignore muted channels" is enabled
-
- DEFAULT:
-
- All servers are logged unless blacklisted or muted and "Ignore muted servers" is enabled
- Muted channels are ignored unless whitelisted or "Ignore muted channels" is disabled
- Muted servers are ignored unless whitelisted or "Ignore muted servers" is disabled
- Whitelisted channels in muted or blacklisted servers are logged
-
- ALL:
-
- Whitelisted channels in blacklisted servers are logged
- Blacklisted channels in whitelisted servers are ignored
- "Always log selected channel" overrides blacklist, whitelist-only mode, NSFW channel ignore, mute
- "Always log DMs" overrides blacklist as well as whitelist-only mode
- Channels marked NSFW and not whitelisted are ignored unless "Ignore NSFW channels" is disabled
-
-
- Chat:
-
- RIGHT-CLICK:
-
- Right-clicking an edit (darkened text) allows you to delete that edit, or hide edits
- Right-clicking on a edited or deleted message gives you the option to hide the deleted message or hide or unhide edits, remove the edited or deleted message from log and remove deleted tint which makes the message look like it isn't deleted.
-
-
-
`
- )
- ])
- ],
- red: false
- });
- }
- showStatsModal() {
- const elements = [];
- let totalMessages = Object.keys(this.messageRecord).length;
- let messageCounts = [];
- let spaceUsageMB = 0;
- let cachedImageCount = 0;
- let cachedImagesUsageMB = 0;
-
- let mostDeletesChannel = { num: 0, id: '' };
- let mostEditsChannel = { num: 0, id: '' };
- let deleteDataTemp = {};
- let editDataTemp = {};
-
- for (const map of [this.deletedMessageRecord, this.editedMessageRecord, this.cachedMessageRecord]) {
- let messageCount = 0;
- if (!Array.isArray(map)) {
- for (const channelId in map) {
- if (!deleteDataTemp[channelId]) deleteDataTemp[channelId] = [];
- if (!editDataTemp[channelId]) editDataTemp[channelId] = [];
- for (const messageId of map[channelId]) {
- messageCount++;
- const record = this.messageRecord[messageId];
- if (!record) continue; // wtf?
- if (record.delete_data && deleteDataTemp[channelId].findIndex(m => m === messageId)) deleteDataTemp[channelId].push(messageId);
- if (record.edit_history && editDataTemp[channelId].findIndex(m => m === messageId)) editDataTemp[channelId].push(messageId);
- }
- }
- }
- for (const channelId in deleteDataTemp) if (deleteDataTemp[channelId].length > mostDeletesChannel.num) mostDeletesChannel = { num: deleteDataTemp[channelId].length, id: channelId };
- for (const channelId in editDataTemp) if (editDataTemp[channelId].length > mostEditsChannel.num) mostEditsChannel = { num: editDataTemp[channelId].length, id: channelId };
-
- messageCounts.push(messageCount);
- }
- const addLine = (name, value) => {
- elements.push(this.parseHTML(`
${name}: ${value}
`));
- };
- addLine('Total messages', totalMessages);
- addLine('Deleted message count', messageCounts[0]);
- addLine('Edited message count', messageCounts[1]);
- addLine('Sent message count', this.cachedMessageRecord.length);
-
- let channel = this.tools.getChannel(mostDeletesChannel.id);
- if (channel) addLine('Most deletes', mostDeletesChannel.num + ' ' + this.getLiteralName(channel.guild_id, channel.id));
- if (channel) addLine('Most edits', mostEditsChannel.num + ' ' + this.getLiteralName(channel.guild_id, channel.id));
-
- // addLine('Data file size', (this.nodeModules.fs.statSync(this.pluginDir + '/MessageLoggerV2Data.config.json').size / 1024 / 1024).toFixed(2) + 'MB');
- // addLine('Data file size severity', this.slowSaveModeStep == 0 ? 'OK' : this.slowSaveModeStep == 1 ? 'MILD' : this.slowSaveModeStep == 2 ? 'BAD' : 'EXTREME');
- this.createModal({
- confirmText: 'OK',
- header: 'Data stats',
- size: ZeresPluginLibrary.Modals.ModalSizes.SMALL,
- children: [ZeresPluginLibrary.ReactTools.createWrappedElement(elements)],
- red: false
- });
- }
- _findLastIndex(array, predicate) {
- let l = array.length;
- while (l--) {
- if (predicate(array[l], l, array))
- return l;
- }
- return -1;
- }
- /*
- how it works:
- messages, stripped into IDs and times into var IDs:
- [1, 2, 3, 4, 5, 6, 7]
- ^ ^
- lowestTime highestTime
- deletedMessages, stripped into IDs and times into var savedIDs:
- sorted by time, newest to oldest
- lowest IDX that is higher than lowestTime, unless channelEnd, then it's 0
- highest IDX that is lower than highestTime, unless channelStart, then it's savedIDs.length - 1
-
- savedIDs sliced start lowest IDX, end highest IDX + 1
- appended IDs
- sorted by time, oldest to newest
- iterated, checked if ID is in messages, if not, fetch from this.messageRecord and splice it in at
- specified index
- */
- reAddDeletedMessages(messages, deletedMessages, channelStart, channelEnd) {
- if (!messages.length || !deletedMessages.length) return;
- const DISCORD_EPOCH = 14200704e5;
- const IDs = [];
- const savedIDs = [];
- for (let i = 0, len = messages.length; i < len; i++) {
- const { id } = messages[i];
- IDs.push({ id: id, time: (id / 4194304) + DISCORD_EPOCH });
- }
- for (let i = 0, len = deletedMessages.length; i < len; i++) {
- const id = deletedMessages[i];
- const record = this.messageRecord[id];
- if (!record) continue;
- if (!record.delete_data) {
- /* SOME WIZARD BROKE THE LOGGER LIKE THIS, WTFFFF */
- this.deleteMessageFromRecords(id);
- continue;
- }
- if (record.delete_data.hidden) continue;
- savedIDs.push({ id: id, time: (id / 4194304) + DISCORD_EPOCH });
- }
- savedIDs.sort((a, b) => a.time - b.time);
- if (!savedIDs.length) return;
- const { time: lowestTime } = IDs[IDs.length - 1];
- const [{ time: highestTime }] = IDs;
- const lowestIDX = channelEnd ? 0 : savedIDs.findIndex(e => e.time > lowestTime);
- if (lowestIDX === -1) return;
- const highestIDX = channelStart ? savedIDs.length - 1 : this._findLastIndex(savedIDs, e => e.time < highestTime);
- if (highestIDX === -1) return;
- const reAddIDs = savedIDs.slice(lowestIDX, highestIDX + 1);
- reAddIDs.push(...IDs);
- reAddIDs.sort((a, b) => b.time - a.time);
- for (let i = 0, len = reAddIDs.length; i < len; i++) {
- const { id } = reAddIDs[i];
- if (messages.findIndex((e) => e.id === id) !== -1) continue;
- const { message } = this.messageRecord[id];
- messages.splice(i, 0, message);
- }
- }
- getLiteralName(guildId, channelId, useTags = false) {
- // TODO, custom channel server failure text
- const guild = this.tools.getServer(guildId);
- const channel = this.tools.getChannel(channelId); // todo
- /* if (typeof guildNameBackup !== 'number' && guild && guildNameBackup) */ if (guildId) {
- const channelName = (channel ? channel.name : 'unknown-channel');
- const guildName = (guild ? guild.name : 'unknown-server');
- if (useTags && channel) return `${guildName}, <#${channel.id}>`;
- return `${guildName}, #${channelName}`;
- } else if (channel && channel.name.length) {
- return `group ${channel.name}`;
- } else if (channel && channel.type == 3) {
- let finalGroupName = '';
- for (let i of channel.recipients) {
- const user = this.tools.getUser(i);
- if (!user) continue;
- if (useTags) finalGroupName += ', <@' + user.id + '>';
- else finalGroupName += ',' + user.username;
- }
- if (!finalGroupName.length) {
- return 'unknown group';
- } else {
- finalGroupName = finalGroupName.substr(1);
- if (useTags) return `group ${finalGroupName}`;
- finalGroupName = finalGroupName.length > 10 ? finalGroupName.substr(0, 10 - 1) + '...' : finalGroupName;
- return `group ${finalGroupName}`;
- }
- } else if (channel && channel.recipients) {
- const user = this.tools.getUser(channel.recipients[0]);
- if (!user) return 'DMs';
- if (useTags) return `<@${user.id}> DMs`;
- return `${user.username} DMs`;
- } else {
- return 'DMs';
- }
- }
- saveDeletedMessage(message, targetMessageRecord) {
- let result = this.createMiniFormattedData(message);
- result.delete_data = {};
- const id = message.id;
- const channelId = message.channel_id;
- result.delete_data.time = new Date().getTime();
- result.ghost_pinged = result.local_mentioned; // it's simple bruh
- if (!Array.isArray(targetMessageRecord[channelId])) targetMessageRecord[channelId] = [];
- if (this.messageRecord[id]) {
- const record = this.messageRecord[id];
- record.delete_data = result.delete_data;
- record.ghost_pinged = result.ghost_pinged;
- } else {
- this.messageRecord[id] = result;
- }
- if (this.messageRecord[id].message.attachments) {
- const attachments = this.messageRecord[id].message.attachments;
- for (let i = 0; i < attachments.length; i++) {
- attachments[i].url = attachments[i].proxy_url; // proxy url lasts longer
- }
- }
- if (this.settings.cacheAllImages) this.cacheMessageImages(this.messageRecord[id].message);
- targetMessageRecord[channelId].push(id);
- }
- createButton(label, callback) {
- const classes = this.createButton.classes;
- const ret = this.parseHTML(``);
- if (callback) ret.addEventListener('click', callback);
- return ret;
- }
- createModal(options, image, name) {
- const modal = image ? this.createModal.imageModal : this.createModal.confirmationModal;
- this.ModalStack.openModal(props => ZeresPluginLibrary.DiscordModules.React.createElement(modal, Object.assign({}, options, props, options.onClose ? { onClose: options.onClose } : {})), { modalKey: name });
- }
- getMessageAny(id) {
- const record = this.messageRecord[id];
- if (!record) return this.cachedMessageRecord.find(m => m.id == id);
- return record.message;
- }
- cacheImage(url, attachmentIdx, attachmentId, messageId, channelId, attempts = 0) {
- this.nodeModules.request({ url: url, encoding: null, headers: { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) discord/1.0.9002 Chrome/83.0.4103.122 Electron/9.3.5 Safari/537.36' } }, (err, res, buffer) => {
- try {
- if (err || res.statusCode != 200) {
- if (res.statusCode == 404 || res.statusCode == 403) return;
- attempts++;
- if (attempts > 3) return ZeresPluginLibrary.Logger.warn(this.getName(), `Failed to get image ${attachmentId} for caching, error code ${res.statusCode}`);
- return setTimeout(() => this.cacheImage(url, attachmentIdx, attachmentId, messageId, channelId, attempts), 1000);
- }
- const fileExtension = url.match(/\.[0-9a-z]+$/i)[0];
- this.nodeModules.fs.writeFileSync(this.settings.imageCacheDir + `/${attachmentId}${fileExtension}`, buffer, { encoding: null });
- } catch (err) {
- console.error('Failed to save image cache', err.message);
- }
- });
- }
- cacheMessageImages(message) {
- // don't block it, ugly but works, might rework later
- setTimeout(() => {
- for (let i = 0; i < message.attachments.length; i++) {
- const attachment = message.attachments[i];
- if (!this.isImage(attachment.url)) continue;
- this.cacheImage(attachment.url, i, attachment.id, message.id, message.channel_id);
- }
- }, 0);
- }
- /* ==================================================-|| END MISC ||-================================================== */
- /* ==================================================-|| START MESSAGE MANAGMENT ||-================================================== */
- deleteMessageFromRecords(id) {
- const record = this.messageRecord[id];
- if (!record) {
- for (let map of [this.deletedMessageRecord, this.editedMessageRecord, this.purgedMessageRecord]) {
- for (let channelId in map) {
- const index = map[channelId].findIndex(m => m === id);
- if (index == -1) continue;
- map[channelId].splice(index, 1);
- if (!map[channelId].length) delete map[channelId];
- }
- }
- return;
- }
- // console.log('Deleting', record);
- const channelId = record.message.channel_id;
- for (let map of [this.deletedMessageRecord, this.editedMessageRecord, this.purgedMessageRecord]) {
- if (!map[channelId]) continue;
- const index = map[channelId].findIndex(m => m === id);
- if (index == -1) continue;
- map[channelId].splice(index, 1);
- if (!map[channelId].length) delete map[channelId];
- }
- delete this.messageRecord[id];
- }
- handleMessagesCap() {
- try {
- // TODO: add empty record and infinite loop checking for speed improvements
- const extractAllMessageIds = map => {
- let ret = [];
- for (let channelId in map) {
- for (let messageId of map[channelId]) {
- ret.push(messageId);
- }
- }
- return ret;
- };
- if (this.cachedMessageRecord.length > this.settings.messageCacheCap) this.cachedMessageRecord.splice(0, this.cachedMessageRecord.length - this.settings.messageCacheCap);
- let changed = false;
- const deleteMessages = map => {
- this.sortMessagesByAge(map);
- const toDelete = map.length - this.settings.savedMessagesCap;
- for (let i = map.length - 1, deleted = 0; i >= 0 && deleted != toDelete; i--, deleted++) {
- this.deleteMessageFromRecords(map[i]);
- }
- changed = true;
- };
- const handleInvalidEntries = map => {
- for (let channelId in map) {
- for (let messageIdIdx = map[channelId].length - 1; messageIdIdx >= 0; messageIdIdx--) {
- if (!Array.isArray(map[channelId])) {
- delete map[channelId];
- changed = true;
- continue;
- }
- if (!this.messageRecord[map[channelId][messageIdIdx]]) {
- map[channelId].splice(messageIdIdx, 1);
- changed = true;
- }
- }
- if (!map[channelId].length) {
- delete map[channelId];
- changed = true;
- }
- }
- };
- for (let map of [this.deletedMessageRecord, this.editedMessageRecord, this.purgedMessageRecord]) handleInvalidEntries(map);
- // I have no idea how to optimize this, HELP!
- //const checkIsInRecords = (channelId, messageId) => {
- // // for (let map of [this.deletedMessageRecord, this.editedMessageRecord, this.purgedMessageRecord]) if (map[channelId] && map[channelId].indexOf(messageId) !== -1) return true;
- // let map = this.deletedMessageRecord[channelId];
- // if (map && map.indexOf(messageId) !== -1) return true;
- // map = this.editedMessageRecord[channelId];
- // if (map && map.indexOf(messageId) !== -1) return true;
- // map = this.purgedMessageRecord[channelId];
- // if (map && map.indexOf(messageId) !== -1) return true;
- // return false;
- //};
-
- //for (const messageId in this.messageRecord) {
- // if (!checkIsInRecords(this.messageRecord[messageId].message.channel_id, messageId)) {/* delete this.messageRecord[messageId]; */ }
- //}
- let deletedMessages = extractAllMessageIds(this.deletedMessageRecord);
- let editedMessages = extractAllMessageIds(this.editedMessageRecord);
- let purgedMessages = extractAllMessageIds(this.purgedMessageRecord);
- for (let map of [deletedMessages, editedMessages, purgedMessages]) if (map.length > this.settings.savedMessagesCap) deleteMessages(map);
- if (changed) this.saveData();
- if (!this.settings.cacheAllImages) return;
- if (!this.settings.dontDeleteCachedImages) {
- const savedImages = this.nodeModules.fs.readdirSync(this.settings.imageCacheDir);
- const msgs = Object.values(this.messageRecord)
- .filter(e => e.delete_data)
- .map(({ message: { attachments } }) => attachments)
- .filter(e => e.length);
- for (let img of savedImages) {
- const [attId] = img.split('.');
- if (isNaN(attId)) continue;
- let found = false;
- for (let i = 0, len = msgs.length; i < len; i++) {
- if (msgs[i].findIndex(({ id }) => id === attId) !== -1) {
- found = true;
- break;
- }
- }
- if (found) continue;
- this.nodeModules.fs.unlink(`${this.settings.imageCacheDir}/${img}`, e => e && ZeresPluginLibrary.Logger.err(this.getName(), 'Error deleting unreferenced image, what the shit', e.message));
- }
- }
- // 10 minutes
- for (let id in this.editHistoryAntiSpam) if (new Date().getTime() - this.editHistoryAntiSpam[id].times[0] < 10 * 60 * 1000) delete this.editHistoryAntiSpam[id];
- } catch (e) {
- ZeresPluginLibrary.Logger.stacktrace(this.getName(), 'Error clearing out data', e);
- }
- }
- /* ==================================================-|| END MESSAGE MANAGMENT ||-================================================== */
- onDispatchEvent(args, callDefault) {
- const dispatch = args[0];
- let ret = Promise.resolve();
-
- if (!dispatch) return callDefault(...args);
-
- try {
- if (dispatch.type === 'MESSAGE_LOGGER_V2_SELF_TEST') {
- clearTimeout(this.selfTestTimeout);
- //console.log('Self test OK');
- this.selfTestFailures = 0;
- return ret;
- }
- // if (dispatch.type == 'EXPERIMENT_TRIGGER') return callDefault(...args);
- // console.log('INFO: onDispatchEvent -> dispatch', dispatch);
- if (dispatch.type === 'CHANNEL_SELECT') {
- ret = callDefault(...args);
- this.selectedChannel = this.getSelectedTextChannel();
- return ret;
- }
-
- if (dispatch.ML2 && dispatch.type === 'MESSAGE_DELETE') return callDefault(...args);
-
- if (dispatch.type !== 'MESSAGE_CREATE' && dispatch.type !== 'MESSAGE_DELETE' && dispatch.type !== 'MESSAGE_DELETE_BULK' && dispatch.type !== 'MESSAGE_UPDATE' && dispatch.type !== 'LOAD_MESSAGES_SUCCESS') return callDefault(...args);
-
- // console.log('INFO: onDispatchEvent -> dispatch', dispatch);
-
- if (dispatch.message && (dispatch.message.type !== 0 && dispatch.message.type !== 19 && (dispatch.message.type !== 20 || (dispatch.message.flags & 64) === 64))) return callDefault(...args); // anti other shit 1
-
- const channel = this.tools.getChannel(dispatch.message ? dispatch.message.channel_id : dispatch.channelId);
- if (!channel) return callDefault(...args);
- const guild = channel.guild_id ? this.tools.getServer(channel.guild_id) : false;
-
- let author = dispatch.message && dispatch.message.author ? this.tools.getUser(dispatch.message.author.id) : false;
- if (!author) author = ((this.channelMessages[channel.id] || { _map: {} })._map[dispatch.message ? dispatch.message.id : dispatch.id] || {}).author;
- if (!author) {
- // last ditch attempt
- let message = this.getCachedMessage(dispatch.id);
- if (message) author = this.tools.getUser(message.author.id);
- }
-
- if (!author && !(dispatch.type == 'LOAD_MESSAGES_SUCCESS' || dispatch.type == 'MESSAGE_DELETE_BULK')) return callDefault(...args);
-
- const isLocalUser = author && author.id === this.localUser.id;
-
- if (author && author.bot && this.settings.ignoreBots) return callDefault(...args);
- if (author && isLocalUser && this.settings.ignoreSelf) return callDefault(...args);
- if (author && this.settings.ignoreBlockedUsers && this.tools.isBlocked(author.id) && !isLocalUser) return callDefault(...args);
- if (author && author.avatar === 'clyde') return callDefault(...args);
-
- if (this.settings.ignoreLocalEdits && dispatch.type === 'MESSAGE_UPDATE' && isLocalUser) return callDefault(...args);
- if (this.settings.ignoreLocalDeletes && dispatch.type === 'MESSAGE_DELETE' && isLocalUser && this.localDeletes.findIndex(m => m === dispatch.id) !== -1) return callDefault(...args);
-
- let guildIsMutedReturn = false;
- let channelIgnoreReturn = false;
-
- const isInWhitelist = id => this.settings.whitelist.findIndex(m => m === id) != -1;
- const isInBlacklist = id => this.settings.blacklist.findIndex(m => m === id) != -1;
- const guildWhitelisted = guild && isInWhitelist(guild.id);
- const channelWhitelisted = isInWhitelist(channel.id);
-
- const guildBlacklisted = guild && isInBlacklist(guild.id);
- const channelBlacklisted = isInBlacklist(channel.id);
-
- let doReturn = false;
-
- if (guild) {
- guildIsMutedReturn = this.settings.ignoreMutedGuilds && this.muteModule.isMuted(guild.id);
- channelIgnoreReturn = (this.settings.ignoreNSFW && channel.nsfw && !channelWhitelisted) || (this.settings.ignoreMutedChannels && (this.muteModule.isChannelMuted(guild.id, channel.id) || (channel.parent_id && this.muteModule.isChannelMuted(guild.id, channel.parent_id))));
- }
-
- if (!((this.settings.alwaysLogSelected && this.selectedChannel && this.selectedChannel.id == channel.id) || (this.settings.alwaysLogDM && !guild))) {
- if (guildBlacklisted) {
- if (!channelWhitelisted) doReturn = true; // not whitelisted
- } else if (guildWhitelisted) {
- if (channelBlacklisted) doReturn = true; // channel blacklisted
- if (channelIgnoreReturn && !channelWhitelisted) doReturn = true;
- } else {
- if (this.settings.onlyLogWhitelist) {
- if (!channelWhitelisted) doReturn = true; // guild not in either list, channel not whitelisted
- } else {
- if (channelBlacklisted) doReturn = true; // channel blacklisted
- if (channelIgnoreReturn || guildIsMutedReturn) {
- if (!channelWhitelisted) doReturn = true;
- }
- }
- }
- }
-
- if (doReturn && this.settings.alwaysLogGhostPings) {
- if (dispatch.type === 'MESSAGE_DELETE') {
- const deleted = (this.tempEditedMessageRecord[dispatch.id] && this.tempEditedMessageRecord[dispatch.id].message) || this.getCachedMessage(dispatch.id, dispatch.channelId);
- if (!deleted || (deleted.type !== 0 && deleted.type !== 19 && deleted.type !== 20)) return callDefault(...args); // nothing we can do past this point..
- if (!this.tools.isMentioned(deleted, this.localUser.id)) return callDefault(...args);
- const record = this.messageRecord[dispatch.id];
- if ((!this.selectedChannel || this.selectedChannel.id != channel.id) && (guild ? this.settings.toastToggles.ghostPings : this.settings.toastTogglesDMs.ghostPings) && (!record || !record.ghost_pinged)) {
- XenoLib.Notifications.warning(`You got ghost pinged in ${this.getLiteralName(channel.guild_id, channel.id, true)}`, { timeout: 0, onClick: () => this.openWindow('ghostpings'), onContext: () => this.jumpToMessage(dispatch.channelId, dispatch.id, guild && guild.id), channelId: channel.id });
- if (!this.settings.useNotificationsInstead) {
- this.showToast(`You got ghost pinged in ${this.getLiteralName(channel.guild_id, channel.id)}`, {
- type: 'warning',
- onClick: () => this.openWindow('ghostpings'),
- onContext: () => this.jumpToMessage(dispatch.channelId, dispatch.id, guild && guild.id),
- timeout: 4500
- });
- }
- }
- this.saveDeletedMessage(deleted, this.deletedMessageRecord);
- this.saveData();
- if (XenoLib.DiscordAPI.channelId.id === dispatch.channelId) this.dispatcher.dispatch({ type: 'MLV2_FORCE_UPDATE_MESSAGE', id: dispatch.id });
- } else if (dispatch.type === 'MESSAGE_UPDATE') {
- if (!dispatch.message.edited_timestamp) {
- if (dispatch.message.embeds) {
- let last = this.getCachedMessage(dispatch.message.id);
- if (last) last.embeds = dispatch.message.embeds.map(this.cleanupEmbed);
- }
- return callDefault(...args);
- }
- let isSaved = this.getEditedMessage(dispatch.message.id, channel.id);
- const last = this.getCachedMessage(dispatch.message.id, channel.id);
- const lastEditedSaved = isSaved || this.tempEditedMessageRecord[dispatch.message.id];
- // if we have lastEdited then we can still continue as we have all the data we need to process it.
- if (!last && !lastEditedSaved) return callDefault(...args); // nothing we can do past this point..
-
- if (isSaved && !lastEditedSaved.local_mentioned) {
- lastEditedSaved.message.content = dispatch.message.content; // don't save history, just the value so we don't confuse the user
- return callDefault(...args);
- }
-
- let ghostPinged = false;
- if (lastEditedSaved) {
- // last is not needed, we have all the data already saved
- if (lastEditedSaved.message.content === dispatch.message.content) return callDefault(...args); // we don't care about that
- lastEditedSaved.edit_history.push({
- content: lastEditedSaved.message.content,
- time: new Date().getTime()
- });
- lastEditedSaved.message.content = dispatch.message.content;
- ghostPinged = !lastEditedSaved.ghost_pinged && lastEditedSaved.local_mentioned && !this.tools.isMentioned(dispatch.message, this.localUser.id);
- } else {
- if (last.content === dispatch.message.content) return callDefault(...args); // we don't care about that
- let data = this.createMiniFormattedData(last);
- data.edit_history = [
- {
- content: last.content,
- time: new Date().getTime()
- }
- ];
- data.message.content = dispatch.message.content;
- this.tempEditedMessageRecord[data.message.id] = data;
- ghostPinged = this.tools.isMentioned(last, this.localUser.id) && !this.tools.isMentioned(dispatch.message, this.localUser.id);
- }
-
- if (isSaved) this.saveData();
-
- if (!ghostPinged) return callDefault(...args);
-
- if (!isSaved) {
- const data = this.tempEditedMessageRecord[dispatch.message.id];
- data.ghost_pinged = true;
- this.messageRecord[dispatch.message.id] = data;
- if (!this.editedMessageRecord[channel.id]) this.editedMessageRecord[channel.id] = [];
- this.editedMessageRecord[channel.id].push(dispatch.message.id);
- this.saveData();
- } else {
- const lastEdited = this.getEditedMessage(dispatch.message.id, channel.id);
- if (!lastEdited) return callDefault(...args);
- lastEdited.ghost_pinged = true;
- this.saveData();
- }
-
- if ((!this.selectedChannel || this.selectedChannel.id != channel.id) && (guild ? this.settings.toastToggles.ghostPings : this.settings.toastTogglesDMs.ghostPings)) {
- XenoLib.Notifications.warning(`You got ghost pinged in ${this.getLiteralName(channel.guild_id, channel.id, true)}`, { timeout: 0, onClick: () => this.openWindow('ghostpings'), onContext: () => this.jumpToMessage(dispatch.channelId, dispatch.id, guild && guild.id), channelId: channel.id });
- if (!this.settings.useNotificationsInstead) {
- this.showToast(`You got ghost pinged in ${this.getLiteralName(channel.guild_id, channel.id)}`, {
- type: 'warning',
- onClick: () => this.openWindow('ghostpings'),
- onContext: () => this.jumpToMessage(dispatch.channelId, dispatch.id, guild && guild.id),
- timeout: 4500
- });
- }
- }
- } else if (dispatch.type == 'MESSAGE_CREATE' && dispatch.message && (dispatch.message.content.length || (dispatch.attachments && dispatch.attachments.length) || (dispatch.embeds && dispatch.embeds.length)) && dispatch.message.state != 'SENDING' && !dispatch.optimistic && (dispatch.message.type === 0 || dispatch.message.type === 19 || dispatch.message.type === 20) && this.tools.isMentioned(dispatch.message, this.localUser.id)) {
- if (this.cachedMessageRecord.findIndex(m => m.id === dispatch.message.id) != -1) return callDefault(...args);
- this.cachedMessageRecord.push(dispatch.message);
- }
- }
- if (doReturn) return callDefault(...args);
-
- if (dispatch.type == 'LOAD_MESSAGES_SUCCESS') {
- if (!this.settings.restoreDeletedMessages) return callDefault(...args);
- if (dispatch.jump && dispatch.jump.ML2) delete dispatch.jump;
- const deletedMessages = this.deletedMessageRecord[channel.id];
- const purgedMessages = this.purgedMessageRecord[channel.id];
- try {
- const recordIDs = [...(deletedMessages || []), ...(purgedMessages || [])];
- const fetchUser = id => this.tools.getUser(id) || dispatch.messages.find(e => e.author.id === id)
- for (let i = 0, len = recordIDs.length; i < len; i++) {
- const id = recordIDs[i];
- if (!this.messageRecord[id]) continue;
- const { message } = this.messageRecord[id];
- for (let j = 0, len2 = message.mentions.length; j < len2; j++) {
- const user = message.mentions[j];
- const cachedUser = fetchUser(user.id || user);
- if (cachedUser) message.mentions[j] = this.cleanupUserObject(cachedUser);
- }
- const author = fetchUser(message.author.id);
- if (!author) continue;
- message.author = this.cleanupUserObject(author);
- }
- } catch { }
- if ((!deletedMessages && !purgedMessages) || (!this.settings.showPurgedMessages && !this.settings.showDeletedMessages)) return callDefault(...args);
- if (this.settings.showDeletedMessages && deletedMessages) this.reAddDeletedMessages(dispatch.messages, deletedMessages, !dispatch.hasMoreAfter && !dispatch.isBefore, !dispatch.hasMoreBefore && !dispatch.isAfter);
- if (this.settings.showPurgedMessages && purgedMessages) this.reAddDeletedMessages(dispatch.messages, purgedMessages, !dispatch.hasMoreAfter && !dispatch.isBefore, !dispatch.hasMoreBefore && !dispatch.isAfter);
- return callDefault(...args);
- }
-
- const notificationsBlacklisted = this.settings.notificationBlacklist.indexOf(channel.id) !== -1 || (guild && this.settings.notificationBlacklist.indexOf(guild.id) !== -1);
-
- if (dispatch.type == 'MESSAGE_DELETE') {
- const deleted = this.getCachedMessage(dispatch.id, dispatch.channelId);
-
- if (this.settings.aggresiveMessageCaching) {
- const channelMessages = this.channelMessages[channel.id];
- if (!channelMessages || !channelMessages.ready) this.cacheChannelMessages(channel.id);
- }
-
- if (!deleted) return callDefault(...args); // nothing we can do past this point..
-
- if (this.deletedMessageRecord[channel.id] && this.deletedMessageRecord[channel.id].findIndex(m => m === deleted.id) != -1) {
- if (!this.settings.showDeletedMessages) ret = callDefault(...args);
- return ret;
- }
-
- if (deleted.type !== 0 && deleted.type !== 19 && (deleted.type !== 20 || (deleted.flags & 64) === 64)) return callDefault(...args);
-
- if (this.settings.showDeletedCount) {
- if (!this.deletedChatMessagesCount[channel.id]) this.deletedChatMessagesCount[channel.id] = 0;
- if (!this.selectedChannel || this.selectedChannel.id != channel.id) this.deletedChatMessagesCount[channel.id]++;
- }
- if (!notificationsBlacklisted) {
- if (guild ? this.settings.toastToggles.deleted && ((isLocalUser && !this.settings.toastToggles.disableToastsForLocal) || !isLocalUser) : this.settings.toastTogglesDMs.deleted && !isLocalUser) {
- if (this.settings.useNotificationsInstead) {
- XenoLib.Notifications.danger(`Message deleted from ${this.getLiteralName(channel.guild_id, channel.id, true)}`, {
- onClick: () => this.openWindow('deleted'),
- onContext: () => this.jumpToMessage(dispatch.channelId, dispatch.id, guild && guild.id),
- timeout: 4500
- });
- } else {
- this.showToast(`Message deleted from ${this.getLiteralName(channel.guild_id, channel.id)}`, {
- type: 'error',
- onClick: () => this.openWindow('deleted'),
- onContext: () => this.jumpToMessage(dispatch.channelId, dispatch.id, guild && guild.id),
- timeout: 4500
- });
- }
- }
- }
-
- const record = this.messageRecord[dispatch.id];
-
- if ((!this.selectedChannel || this.selectedChannel.id != channel.id) && (guild ? this.settings.toastToggles.ghostPings : this.settings.toastTogglesDMs.ghostPings) && (!record || !record.ghost_pinged) && this.tools.isMentioned(deleted, this.localUser.id)) {
- XenoLib.Notifications.warning(`You got ghost pinged in ${this.getLiteralName(channel.guild_id, channel.id, true)}`, { timeout: 0, onClick: () => this.openWindow('ghostpings'), onContext: () => this.jumpToMessage(dispatch.channelId, dispatch.id, guild && guild.id), channelId: dispatch.channelId });
- if (!this.settings.useNotificationsInstead) {
- this.showToast(`You got ghost pinged in ${this.getLiteralName(channel.guild_id, channel.id)}`, {
- type: 'warning',
- onClick: () => this.openWindow('ghostpings'),
- onContext: () => this.jumpToMessage(dispatch.channelId, dispatch.id, guild && guild.id),
- timeout: 4500
- });
- }
- }
-
- this.saveDeletedMessage(deleted, this.deletedMessageRecord);
- // if (this.settings.cacheAllImages) this.cacheImages(deleted);
- if (!this.settings.showDeletedMessages) ret = callDefault(...args);
- else if (XenoLib.DiscordAPI.channelId === dispatch.channelId) this.dispatcher.dispatch({ type: 'MLV2_FORCE_UPDATE_MESSAGE', id: dispatch.id });
- this.saveData();
- } else if (dispatch.type == 'MESSAGE_DELETE_BULK') {
- if (this.settings.showDeletedCount) {
- if (!this.deletedChatMessagesCount[channel.id]) this.deletedChatMessagesCount[channel.id] = 0;
- if (!this.selectedChannel || this.selectedChannel.id != channel.id) this.deletedChatMessagesCount[channel.id] += dispatch.ids.length;
- }
-
- let failedMessage = false;
-
- for (let i = 0; i < dispatch.ids.length; i++) {
- const purged = this.getCachedMessage(dispatch.ids[i], channel.id);
- if (!purged) {
- failedMessage = true;
- continue;
- }
- this.saveDeletedMessage(purged, this.purgedMessageRecord);
- if (XenoLib.DiscordAPI.channelId === dispatch.channelId) this.dispatcher.dispatch({ type: 'MLV2_FORCE_UPDATE_MESSAGE', id: purged.id });
- }
-
- if (failedMessage && this.aggresiveMessageCaching)
- // forcefully cache the channel in case there are active convos there
- this.cacheChannelMessages(channel.id);
- else if (this.settings.aggresiveMessageCaching) {
- const channelMessages = this.channelMessages[channel.id];
- if (!channelMessages || !channelMessages.ready) this.cacheChannelMessages(channel.id);
- }
- if (!notificationsBlacklisted) {
- if (guild ? this.settings.toastToggles.deleted : this.settings.toastTogglesDMs.deleted) {
- if (this.settings.useNotificationsInstead) {
- XenoLib.Notifications.danger(`${dispatch.ids.length} messages bulk deleted from ${this.getLiteralName(channel.guild_id, channel.id, true)}`, {
- onClick: () => this.openWindow('purged'),
- onContext: () => this.jumpToMessage(channel.id, undefined, guild && guild.id),
- timeout: 4500
- });
- } else {
- this.showToast(`${dispatch.ids.length} messages bulk deleted from ${this.getLiteralName(channel.guild_id, channel.id)}`, {
- type: 'error',
- onClick: () => this.openWindow('purged'),
- onContext: () => this.jumpToMessage(channel.id, undefined, guild && guild.id),
- timeout: 4500
- });
- }
- }
- }
- if (!this.settings.showPurgedMessages) ret = callDefault(...args);
- this.saveData();
- } else if (dispatch.type == 'MESSAGE_UPDATE') {
- if (!dispatch.message.edited_timestamp) {
- if (dispatch.message.embeds) {
- let last = this.getCachedMessage(dispatch.message.id);
- if (last) last.embeds = dispatch.message.embeds.map(this.cleanupEmbed);
- }
- return callDefault(...args);
- }
-
- if (this.settings.showEditedCount) {
- if (!this.editedChatMessagesCount[channel.id]) this.editedChatMessagesCount[channel.id] = 0;
- if (!this.selectedChannel || this.selectedChannel.id != channel.id) this.editedChatMessagesCount[channel.id]++;
- }
-
- if (this.settings.aggresiveMessageCaching) {
- const channelMessages = this.channelMessages[channel.id];
- if (!channelMessages || !channelMessages.ready) this.cacheChannelMessages(channel.id);
- }
-
- const last = this.getCachedMessage(dispatch.message.id, channel.id);
- const lastEditedSaved = this.getEditedMessage(dispatch.message.id, channel.id);
-
- // if we have lastEdited then we can still continue as we have all the data we need to process it.
- if (!last && !lastEditedSaved) return callDefault(...args); // nothing we can do past this point..
- let ghostPinged = false;
- if (lastEditedSaved) {
- // last is not needed, we have all the data already saved
- // console.log(lastEditedSaved.message);
- // console.log(dispatch.message);
- if (lastEditedSaved.message.content === dispatch.message.content) {
- return callDefault(...args); // we don't care about that
- }
- lastEditedSaved.edit_history.push({
- content: lastEditedSaved.message.content,
- time: new Date().getTime()
- });
- lastEditedSaved.message.content = dispatch.message.content;
- ghostPinged = !lastEditedSaved.ghost_pinged && lastEditedSaved.local_mentioned && !this.tools.isMentioned(dispatch.message, this.localUser.id);
- if (ghostPinged) lastEditedSaved.ghost_pinged = true;
- } else {
- if (last.content === dispatch.message.content) {
- return callDefault(...args); // we don't care about that
- }
- let data = this.createMiniFormattedData(last);
- data.edit_history = [
- {
- content: last.content,
- time: new Date().getTime()
- }
- ];
- ghostPinged = this.tools.isMentioned(last, this.localUser.id) && !this.tools.isMentioned(dispatch.message, this.localUser.id);
- data.message.content = dispatch.message.content;
- if (ghostPinged) data.ghost_pinged = true;
- this.messageRecord[data.message.id] = data;
- if (!this.editedMessageRecord[channel.id]) this.editedMessageRecord[channel.id] = [];
- this.editedMessageRecord[channel.id].push(data.message.id);
- }
- if (!notificationsBlacklisted) {
- if (guild ? this.settings.toastToggles.edited && ((isLocalUser && !this.settings.toastToggles.disableToastsForLocal) || !isLocalUser) : this.settings.toastTogglesDMs.edited && !isLocalUser) {
- if (!this.settings.blockSpamEdit) {
- if (!this.editHistoryAntiSpam[author.id]) {
- this.editHistoryAntiSpam[author.id] = {
- blocked: false,
- times: [new Date().getTime()]
- };
- } else {
- this.editHistoryAntiSpam[author.id].times.push(new Date().getTime());
- }
- if (this.editHistoryAntiSpam[author.id].times.length > 10) this.editHistoryAntiSpam[author.id].times.shift();
- if (this.editHistoryAntiSpam[author.id].times.length === 10 && new Date().getTime() - this.editHistoryAntiSpam[author.id].times[0] < 60 * 1000) {
- if (!this.editHistoryAntiSpam[author.id].blocked) {
- if (this.settings.useNotificationsInstead) {
- XenoLib.Notifications.warning(`Edit notifications from <@${author.id}> have been temporarily blocked for 1 minute.`, {
- timeout: 7500,
- channelId: channel.id
- });
- } else {
- this.showToast(`Edit notifications from ${author.username} have been temporarily blocked for 1 minute.`, {
- type: 'warning',
- timeout: 7500
- });
- }
- this.editHistoryAntiSpam[author.id].blocked = true;
- }
- } else if (this.editHistoryAntiSpam[author.id].blocked) {
- this.editHistoryAntiSpam[author.id].blocked = false;
- this.editHistoryAntiSpam[author.id].times = [];
- }
- }
- if (this.settings.blockSpamEdit || !this.editHistoryAntiSpam[author.id].blocked) {
- if (this.settings.useNotificationsInstead) {
- XenoLib.Notifications.info(`Message edited in ${this.getLiteralName(channel.guild_id, channel.id, true)}`, {
- onClick: () => this.openWindow('edited'),
- onContext: () => this.jumpToMessage(channel.id, dispatch.message.id, guild && guild.id),
- timeout: 4500
- });
- } else {
- this.showToast(`Message edited in ${this.getLiteralName(channel.guild_id, channel.id)}`, {
- type: 'info',
- onClick: () => this.openWindow('edited'),
- onContext: () => this.jumpToMessage(channel.id, dispatch.message.id, guild && guild.id),
- timeout: 4500
- });
- }
- }
- }
- }
- if ((!this.selectedChannel || this.selectedChannel.id != channel.id) && (guild ? this.settings.toastToggles.ghostPings : this.settings.toastTogglesDMs.ghostPings) && ghostPinged) {
- XenoLib.Notifications.warning(`You got ghost pinged in ${this.getLiteralName(channel.guild_id, channel.id, true)}`, { timeout: 0, onClick: () => this.openWindow('ghostpings'), onContext: () => this.jumpToMessage(dispatch.channelId, dispatch.id, guild && guild.id), channelId: dispatch.channelId });
- if (!this.settings.useNotificationsInstead) {
- this.showToast(`You got ghost pinged in ${this.getLiteralName(channel.guild_id, channel.id)}`, {
- type: 'warning',
- onClick: () => this.openWindow('ghostpings'),
- onContext: () => this.jumpToMessage(dispatch.channelId, dispatch.id, guild && guild.id),
- timeout: 4500
- });
- }
- }
- this.saveData();
- return callDefault(...args);
- } else if (dispatch.type == 'MESSAGE_CREATE' && dispatch.message && (dispatch.message.content.length || (dispatch.attachments && dispatch.attachments.length) || (dispatch.embeds && dispatch.embeds.length)) && dispatch.message.state != 'SENDING' && !dispatch.optimistic && (dispatch.message.type === 0 || dispatch.message.type === 19 || dispatch.message.type === 20)) {
- if (this.cachedMessageRecord.findIndex(m => m.id === dispatch.message.id) != -1) return callDefault(...args);
- this.cachedMessageRecord.push(dispatch.message);
-
- /* if (this.menu.open && this.menu.selectedTab == 'sent') this.refilterMessages(); */
-
- if (this.settings.aggresiveMessageCaching) {
- const channelMessages = this.channelMessages[channel.id];
- if (!channelMessages || !channelMessages.ready) this.cacheChannelMessages(channel.id);
- }
- if (!notificationsBlacklisted) {
- if ((guild ? this.settings.toastToggles.sent : this.settings.toastTogglesDMs.sent) && (!this.selectedChannel || this.selectedChannel.id != channel.id)) {
- if (this.settings.useNotificationsInstead) {
- XenoLib.Notifications.info(`Message sent in ${this.getLiteralName(channel.guild_id, channel.id, true)}`, { onClick: () => this.openWindow('sent'), onContext: () => this.jumpToMessage(channel.id, dispatch.message.id, guild && guild.id), timeout: 4500 });
- } else {
- this.showToast(`Message sent in ${this.getLiteralName(channel.guild_id, channel.id)}`, { type: 'info', onClick: () => this.openWindow('sent'), onContext: () => this.jumpToMessage(channel.id, dispatch.message.id, guild && guild.id), timeout: 4500 });
- }
- }
- }
- return callDefault(...args);
- } else return callDefault(...args);
- } catch (err) {
- ZeresPluginLibrary.Logger.stacktrace(this.getName(), 'Error in onDispatchEvent', err);
- }
- return ret;
- }
- /* ==================================================-|| START MENU ||-================================================== */
- processUserRequestQueue() {
- return;
- if (!this.processUserRequestQueue.queueIntervalTime) this.processUserRequestQueue.queueIntervalTime = 500;
- if (this.menu.queueInterval) return;
- const messageDataManager = () => {
- return;
- if (!this.menu.userRequestQueue.length) {
- clearInterval(this.menu.queueInterval);
- this.menu.queueInterval = 0;
- return;
- }
- const data = this.menu.userRequestQueue.shift();
- this.tools
- .getUserAsync(data.id)
- .then(res => {
- for (let ss of data.success) ss(res);
- })
- .catch(reason => {
- if (reason.status == 429 && typeof reason.body.retry_after === 'number') {
- clearInterval(this.menu.queueInterval);
- this.menu.queueInterval = 0;
- this.processUserRequestQueue.queueIntervalTime += 50;
- setTimeout(messageDataManager, reason.body.retry_after);
- ZeresPluginLibrary.Logger.warn(this.getName(), 'Rate limited, retrying in', reason.body.retry_after, 'ms');
- this.menu.userRequestQueue.push(data);
- return;
- }
- ZeresPluginLibrary.Logger.warn(this.getName(), `Failed to get info for ${data.username}, reason:`, reason);
- for (let ff of data.fail) ff();
- });
- };
- this.menu.queueInterval = setInterval(messageDataManager, this.processUserRequestQueue.queueIntervalTime);
- }
- async patchMessages() {
- const Tooltip = ZeresPluginLibrary.WebpackModules.getByString('shouldShowTooltip', 'handleMouseEnter');
- const dateFormat = ZeresPluginLibrary.WebpackModules.getModule(e => typeof e === 'function' && e?.toString()?.includes('sameDay'), { searchExports: true });
- const i18n = ZeresPluginLibrary.WebpackModules.find(e => e.Messages && e.Messages.HOME);
- /* suck it you retarded asshole devilfuck */
- const SuffixEdited = ZeresPluginLibrary.DiscordModules.React.memo(e => ZeresPluginLibrary.DiscordModules.React.createElement(Tooltip, { text: e.timestamp ? dateFormat(e.timestamp, 'LLLL') : null }, tt => ZeresPluginLibrary.DiscordModules.React.createElement('time', Object.assign({ dateTime: e.timestamp.toISOString(), className: this.multiClasses.edited, role: 'note' }, tt), `(${i18n.Messages.MESSAGE_EDITED})`)));
- SuffixEdited.displayName = 'SuffixEdited';
- const parseContent = (() => {
- const parse = ZeresPluginLibrary.WebpackModules.getModule(e => typeof e === 'function' && e?.toString()?.includes('customRenderedContent') && e?.toString()?.includes('renderMediaEmbeds'), { searchExports: true });
- if (parse) {
- return function parseContent() {
- const ReactDispatcher = ZeresPluginLibrary.DiscordModules.React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.ReactCurrentDispatcher.current;
- const oUseMemo = ReactDispatcher.useMemo;
- ReactDispatcher.useMemo = memo => memo();
- try {
- return parse(...arguments);
- } finally {
- ReactDispatcher.useMemo = oUseMemo;
- }
- return {};
- }
- }
- return null;
- })();
- const MessageContent = ZeresPluginLibrary.WebpackModules.getModule(e => e?.type?.toString()?.includes('Messages.MESSAGE_EDITED'));
- const MemoMessage = await (async () => {
- var el = document.querySelector('.messageListItem-ZZ7v6g') || (await new Promise(res => {
- var sub = ZeresPluginLibrary.DOMTools.observer.subscribeToQuerySelector(() => {
- ZeresPluginLibrary.DOMTools.observer.unsubscribe(sub);
- res(document.querySelector('.messageListItem-ZZ7v6g'));
- }, '.messageListItem-ZZ7v6g', null, true)
- }));
- return ZeresPluginLibrary.Utilities.findInTree(ZeresPluginLibrary.ReactTools.getReactInstance(el), e => ((typeof e?.memoizedProps?.isHighlight) === 'boolean'), { walkable: ['return'] })?.elementType
- })()
- if (!MessageContent || !MemoMessage) return XenoLib.Notifications.error('Failed to patch message components, edit history and deleted tint will not show!', { timeout: 0 });
- this.unpatches.push(
- this.Patcher.after(MessageContent, 'type', (_, [props], ret) => {
- const forceUpdate = ZeresPluginLibrary.DiscordModules.React.useState()[1];
- ZeresPluginLibrary.DiscordModules.React.useEffect(
- () => {
- function callback(e) {
- if (!e || !e.id || e.id === props.message.id) {
- forceUpdate({});
- }
- }
- this.dispatcher.subscribe('MLV2_FORCE_UPDATE_MESSAGE_CONTENT', callback);
- return () => {
- this.dispatcher.unsubscribe('MLV2_FORCE_UPDATE_MESSAGE_CONTENT', callback);
- };
- },
- [props.message.id, forceUpdate]
- );
- if (!this.settings.showEditedMessages || (typeof props.className === 'string' && ~props.className.indexOf('repliedTextContent'))) return;
- if (!this.editedMessageRecord[props.message.channel_id] || this.editedMessageRecord[props.message.channel_id].indexOf(props.message.id) === -1) return;
- const record = this.messageRecord[props.message.id];
- if (!record || record.edits_hidden || !Array.isArray(ret.props.children)) return;
- const createEditedMessage = (edit, editNum, isSingular, noSuffix) =>
- ZeresPluginLibrary.DiscordModules.React.createElement(
- XenoLib.ReactComponents.ErrorBoundary,
- { label: 'Edit history' },
- ZeresPluginLibrary.DiscordModules.React.createElement(
- Tooltip,
- {
- text: !!record.delete_data ? null : 'Edited: ' + this.createTimeStamp(edit.time),
- position: 'left',
- hideOnClick: true
- },
- _ =>
- ZeresPluginLibrary.DiscordModules.React.createElement(
- 'div',
- {
- ..._,
- className: XenoLib.joinClassNames({ [this.style.editedCompact]: props.compact && !isSingular, [this.style.edited]: !isSingular }),
- editNum
- },
- parseContent({ channel_id: props.message.channel_id, mentionChannels: props.message.mentionChannels, content: edit.content, embeds: [], isCommandType: () => false, hasFlag: () => false }, {}).content,
- noSuffix
- ? null
- : ZeresPluginLibrary.DiscordModules.React.createElement(SuffixEdited, {
- timestamp: this.tools.createMomentObject(edit.time)
- })
- )
- )
- );
- ret.props.className = XenoLib.joinClassNames(ret.props.className, this.style.edited);
- const modifier = this.editModifiers[props.message.id];
- if (modifier) {
- ret.props.children = [createEditedMessage(record.edit_history[modifier.editNum], modifier.editNum, true, modifier.noSuffix)];
- return;
- }
- const oContent = Array.isArray(ret.props.children[0]) ? ret.props.children[0] : ret.props.children[1];
- const edits = [];
- let i = 0;
- let max = record.edit_history.length;
- if (this.settings.maxShownEdits) {
- if (record.edit_history.length > this.settings.maxShownEdits) {
- if (this.settings.hideNewerEditsFirst) {
- max = this.settings.maxShownEdits;
- } else {
- i = record.edit_history.length - this.settings.maxShownEdits;
- }
- }
- }
- for (; i < max; i++) {
- const edit = record.edit_history[i];
- if (!edit) continue;
- let editNum = i;
- edits.push(createEditedMessage(edit, editNum));
- }
- ret.props.children = [edits, oContent];
- })
- );
-
- const messageClass = XenoLib.getSingleClass('ephemeral message');
- const _self = this;
- function Message(props, ...whatever) {
- try {
- const ret = props.__MLV2_type(props, ...whatever);
- if (!props.__MLV2_deleteTime) return ret;
- const oRef = ret.props.children.ref;
- ret.props.children.ref = e => {
- if (e && !e.__tooltip) {
- // later
- new ZeresPluginLibrary.Tooltip(e, 'Deleted: ' + _self.tools.createMomentObject(props.__MLV2_deleteTime).format('LLLL'), { side: 'left' });
- e.__tooltip = true;
- }
- if (typeof oRef === 'function') return oRef(e);
- else if (XenoLib._.isObject(oRef)) oRef.current = e;
- };
- return ret;
- } catch (err) {}
- return null;
- }
- this.unpatches.push(
- this.Patcher.after(MemoMessage, 'type', (_, [props], ret) => {
- const forceUpdate = ZeresPluginLibrary.DiscordModules.React.useState()[1];
- ZeresPluginLibrary.DiscordModules.React.useEffect(
- () => {
- function callback(e) {
- if (!e || !e.id || e.id === props.message.id) forceUpdate({});
- }
- this.dispatcher.subscribe('MLV2_FORCE_UPDATE_MESSAGE', callback);
- return () => {
- this.dispatcher.unsubscribe('MLV2_FORCE_UPDATE_MESSAGE', callback);
- };
- },
- [props.message.id, forceUpdate]
- );
- const record = this.messageRecord[props.message.id];
- if (!record || !record.delete_data) return;
- if (this.noTintIds.indexOf(props.message.id) !== -1) return;
- const message = ZeresPluginLibrary.Utilities.findInReactTree(ret, e => e && typeof e?.props?.className === 'string' && ~e?.props?.className?.indexOf(messageClass));
- if (!message) return;
- message.props.className += ' ' + (this.settings.useAlternativeDeletedStyle ? this.style.deletedAlt : this.style.deleted);
- message.props.__MLV2_deleteTime = record.delete_data.time;
- message.props.__MLV2_type = message.type;
- message.type = Message;
- })
- );
- this.forceReloadMessages();
- }
- forceReloadMessages() {
- const instance = ZeresPluginLibrary.Utilities.findInTree(ZeresPluginLibrary.ReactTools.getReactInstance(document.querySelector('.chatContent-3KubbW')), e => ((typeof e?.memoizedProps?.showQuarantinedUserBanner) === 'boolean'), { walkable: ['return'] })?.stateNode;
- if (!instance) return;
- const unpatch = this.Patcher.after(instance, 'render', (_this, _, ret) => {
- unpatch();
- if (!ret) return;
- ret.key = Math.random().toString(36).substring(2, 10).toUpperCase();
- ret.ref = () => _this.forceUpdate();
- });
- instance.forceUpdate();
- }
- patchModal() {
- return;
- // REQUIRED not anymore I guess lol
- try {
- const confirmModal = ZeresPluginLibrary.WebpackModules.getByDisplayName('ConfirmModal');
- this.createModal.confirmationModal = props => {
- try {
- const ret = confirmModal(props);
- if (props.size) ret.props.size = props.size;
-
- if (props.onCancel) {
- const cancelButton = ZeresPluginLibrary.Utilities.findInReactTree(ret, e => e && e.type === XenoLib.ReactComponents.Button && e.props && e.props.look);
- if (cancelButton) cancelButton.props.onClick = props.onCancel;
- }
- return ret;
- } catch (err) {
- if (props.onCancel) props.onCancel();
- else props.onClose();
- return null;
- }
- };
- this.createModal.confirmationModal.Sizes = ZeresPluginLibrary.WebpackModules.getByProps('ModalSize').ModalSize;
- } catch { }
- this.ModalStack = ZeresPluginLibrary.WebpackModules.getByProps('openModal', 'hasModalOpen');
- this._modalsApiUnsubcribe = (this.ModalStack.modalsApi || this.ModalStack.useModalsStore).subscribe(_ => {
- if (this.menu.open && !this.ModalStack.hasModalOpen(this.style.menu)) {
- this.menu.filter = '';
- this.menu.open = false;
- this.menu.shownMessages = -1;
- if (this.menu.messages) this.menu.messages.length = 0;
- }
- });
- /*
- this.createModal.confirmationModal = class ConfirmationModal extends ZeresPluginLibrary.DiscordModules.ConfirmationModal {
- constructor(props) {
- super(props);
- this._handleSubmit = this.handleSubmit.bind(this);
- this._handleClose = this.handleClose.bind(this);
- this.handleSubmit = this.handleSubmitEx.bind(this);
- this.handleClose = this.handleCloseEx.bind(this);
- }
- handleSubmitEx(e) {
- if (this.props.ml2Data) onClearLog(e);
- else return this._handleSubmit(e);
- }
- handleCloseEx(e) {
- if (this.props.ml2Data) onChangeOrder(e);
- else return this._handleClose(e);
- }
- render() {
- const ret = super.render();
- if (!ret) return ret;
- delete ret.props['aria-label'];
- return ret;
- }
- };
- this.unpatches.push(
- ZeresPluginLibrary.Patcher.instead(this.getName(), ZeresPluginLibrary.DiscordModules.ConfirmationModal.prototype, 'componentDidMount', (thisObj, args, original) => {
- if (thisObj.props.ml2Data) {
- if (this.menu.refilterOnMount) {
- this.refilterMessages();
- this.menu.refilterOnMount = false;
- }
- document.getElementById(this.style.menuMessages).parentElement.parentElement.parentElement.scrollTop = this.scrollPosition;
- }
- return original(...args);
- })
- );
-*/
- }
- buildMenu(setup) {
- const ret = ZeresPluginLibrary.DCM.buildMenu(setup);
- return props => ret({ ...props, onClose: _ => { } });
- }
- // >>-|| POPULATION ||-<<
- createMessageGroup(message, isStart) {
- let deleted = false;
- let edited = false;
- let details = 'Sent in';
- let channel = this.tools.getChannel(message.channel_id);
- let timestamp = message.timestamp;
- let author = this.tools.getUser(message.author.id);
- let noUserInfo = false;
- let userInfoBeingRequested = true;
- const isBot = message.author.bot;
- const record = this.messageRecord[message.id];
- if (record) {
- deleted = !!record.delete_data;
- edited = !!record.edit_history;
-
- if (deleted && edited) {
- details = 'Edited and deleted from';
- timestamp = record.delete_data.time;
- } else if (deleted) {
- details = 'Deleted from';
- timestamp = record.delete_data.time;
- } else if (edited) {
- details = 'Last edit in'; // todo: purged?
- if (typeof record.edit_history[record.edit_history.length - 1].time !== 'string') timestamp = record.edit_history[record.edit_history.length - 1].time;
- }
- }
-
- details += ` ${this.getLiteralName(message.guild_id || (channel && channel.guild_id), message.channel_id)} `;
-
- details += `at ${this.createTimeStamp(timestamp, true)}`;
-
- details = details.replace(/[<>"&]/g, c => ({ "<": "<", ">": ">", "\"": """, "&": "&" })[c]);
- const classes = this.createMessageGroup.classes;
- const getAvatarOf = user => {
- if (!user.avatar) return '/assets/322c936a8c8be1b803cd94861bdfa868.png';
- return `https://cdn.discordapp.com/avatars/${user.id}/${user.avatar}.png?size=128`;
- };
- if (!classes.extra)
- classes.extra = [
- /* 0 */ XenoLib.joinClassNames(XenoLib.getClass('groupStart message'), XenoLib.getClass('groupStart cozyMessage'), XenoLib.getClass('systemMessage groupStart'), XenoLib.getClass('zalgo wrapper'), XenoLib.getClass('zalgo cozy'), XenoLib.getClass('cozy zalgo')),
- /* 1 */ XenoLib.joinClassNames(XenoLib.getClass('groupStart message'), XenoLib.getClass('groupStart cozyMessage'), XenoLib.getClass('zalgo wrapper'), XenoLib.getClass('zalgo cozy'), XenoLib.getClass('cozy zalgo')),
- /* 2 */ XenoLib.getClass('username header'),
- /* 3 */ XenoLib.joinClassNames(XenoLib.getClass('edited avatar'), XenoLib.getClass('edited avatar clickable')),
- /* 4 */ XenoLib.joinClassNames(XenoLib.getClass('timestampTooltip username'), XenoLib.getClass('edited avatar clickable')),
- /* 5 */ XenoLib.joinClassNames(XenoLib.getClass('separator timestamp'), XenoLib.getClass('separator timestampInline')),
- /* 6 */ XenoLib.joinClassNames(this.multiClasses.markup, XenoLib.getClass('buttonContainer markupRtl')),
- /* 7 */ XenoLib.getClass('embedWrapper container'),
- /* 8 */ XenoLib.joinClassNames(XenoLib.getClass('zalgo latin24CompactTimeStamp'), XenoLib.getClass('separator timestamp'), XenoLib.getClass('alt timestampVisibleOnHover'), XenoLib.getClass('timestampVisibleOnHover alt')),
- /* 9 */ XenoLib.getClass('latin24CompactTimeStamp separator'),
- /* 10 */ XenoLib.getSingleClass('timestampTooltip username'),
- /* 11 */ XenoLib.getSingleClass('separator timestamp'),
- /* 12 */ XenoLib.getClass('zalgo contents')
- ];
-
- const element = isStart
- ? this.parseHTML(`
`
- );
- const inputEl = textBox.getElementsByTagName('input')[0];
- inputEl.addEventListener('focusout', e => {
- DOMTokenList.prototype.remove.apply(e.target.parentElement.parentElement.classList, classes.focused);
- });
- inputEl.addEventListener('focusin', e => {
- DOMTokenList.prototype.add.apply(e.target.parentElement.parentElement.classList, classes.focused);
- });
- const onUpdate = e => {
- if (this.menu.filterSetTimeout) clearTimeout(this.menu.filterSetTimeout);
- this.menu.filter = inputEl.value;
- const filters = this.menu.filter.split(',');
- // console.log(filters);
- if (!filters[0].length) return this.refilterMessages();
- this.menu.filterSetTimeout = setTimeout(() => {
- if (filters[0].length) {
- for (let i = 0; i < filters.length; i++) {
- const split = filters[i].split(':');
- if (split.length < 2) return;
- }
- }
- this.refilterMessages();
- }, 200);
- };
- inputEl.addEventListener('keyup', onUpdate); // maybe I can actually use keydown but it didn't work for me
- inputEl.addEventListener('paste', onUpdate);
- const helpButton = textBox.getElementsByClassName(classes.questionMarkSingle)[0];
- helpButton.addEventListener('click', () => {
- const extraHelp = this.createButton('Logger help', () => this.showLoggerHelpModal());
- this.createModal({
- confirmText: 'OK',
- header: 'Filter help',
- size: this.createModal.confirmationModal.Sizes.LARGE,
- children: [
- ZeresPluginLibrary.ReactTools.createWrappedElement([
- this.parseHTML(
- `
"server: " - Filter results with the specified server name or id.
- "channel: " - Filter results with the specified channel name or id.
- "user: " - Filter results with the specified username, nickname or userid.
- "message: " or "content: " - Filter results with the specified message content.
- "has: - Filter results to only images or links
-
- Separate the search tags with commas.
- Example: server: tom's bd stuff, message: heck
-
-
- Shortcut help:
-
- "Ctrl + M" (default) - Open message log.
- "Ctrl + N" (default) - Open message log with selected channel filtered.\n\n