您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Discord Recent Deletions Logger - view recent edits and deletions in console.
// ==UserScript== // @name DRDL // @description Discord Recent Deletions Logger - view recent edits and deletions in console. // @author 0vC4 // @version 1.0 // @namespace https://greasyfork.org/users/670183-exnonull // @match *://discordapp.com/* // @match *://discord.com/* // @icon https://www.google.com/s2/favicons?sz=64&domain=discord.com // @run-at document-start // @grant none // @license MIT // @require https://greasyfork.org/scripts/438620-workertimer/code/WorkerTimer.js // @require https://cdnjs.cloudflare.com/ajax/libs/pako/1.0.10/pako.js // ==/UserScript== const log = console.log; const logError = console.error; (() => { window.console.log = ()=>0; window.console.info = ()=>0; window.console.warn = ()=>0; window.console.warning = ()=>0; window.setTimeout = window.WorkerTimer.setTimeout; window.setInterval = window.WorkerTimer.setInterval; window.clearTimeout = window.WorkerTimer.clearTimeout; window.clearInterval = window.WorkerTimer.clearInterval; })(); (() => { class Client { static ws = null; static guilds = []; static channels = []; static users = []; static undefinedChannel = { path: 'undefined', name: 'undefined', guild: null, parent: null, messages: [], addMessage(msg) { const minutes = 60; Client.undefinedChannel.messages = Client.undefinedChannel.messages.filter((m, i) => { if ((+new Date() - +new Date(m.timestamp))/1e3 < 5*minutes) return true; return i < 200; }); Client.undefinedChannel.messages.push(msg); } }; static guild(id) { return Client.guilds.find(g=>g.id == id); } static channel(id) { return Client.channels.find(c=>c.id == id) ?? Client.undefinedChannel; } static user(id) { return Client.users.find(u=>u.id == id); } static newChannel(channel) { if (!channel) return; if (channel.recipient_ids?.length == 1) channel.name ??= '@' + Client.user(u => u.id == channel.recipient_ids[0])?.username; Object.defineProperty(channel, 'guild', { get(){ return Client.guild(channel.guild_id); }, }); Object.defineProperty(channel, 'parent', { get(){ return Client.channel(channel.parent_id); }, }); Object.defineProperty(channel, 'path', { get(){ const ch = Client.channel(channel.parent_id); let name = this.name; if (ch?.parent_id) return ch.path + '/' + name; return channel.guild?.properties?.name + '/' + name; }, }); channel.messages = []; channel.addMessage = (msg) => { const minutes = 60; channel.messages = channel.messages.filter((m, i) => { if ((+new Date() - +new Date(m.timestamp))/1e3 < 5*minutes) return true; return i < 200; }); channel.messages.push(msg); } Client.channels.push(channel); } static newMessage(message) { if (!message) return {}; message.history = []; message.getHistory = () => [message, ...message.history]; message.getLastEdit = () => [(message.history[message.history.length-2] ?? message), message.history[message.history.length-1] ?? message]; message.updated = false; message.deleted = false; if (message.edited_timestamp) { message.updated = true; } message.update = function(msg) { if (!msg.edited_timestamp) return; message.history.push(msg); message.updated = true; Client.updates.push(message); log('<EDIT>'); log( message.channel.path, '@'+message.author.username+':', message.getLastEdit()[0].content ?? '<???>', ' |-> ', msg.content, ); if (message.embeds.map(a=>a.url).join(' ') != '') log('embeds:', message.embeds.map(a=>a.url).join(' ')); if (message.attachments.map(a=>a.url).join(' ') != '') log('attachments:', message.attachments.map(a=>a.url).join(' ')); }; message.delete = function(msg) { message.history.push(msg); message.deleted = true; Client.deletions.push(message); log('<DELETE>'); log( message.channel.path, '@'+message.author.username+':', message.getLastEdit()[0].content, ); if (message.embeds.map(a=>a.url).join(' ') != '') log('embeds:', message.embeds.map(a=>a.url).join(' ')); if (message.attachments.map(a=>a.url).join(' ') != '') log('attachments:', message.attachments.map(a=>a.url).join(' ')); if (message.updated) log(message.getHistory().map(m => !m ? '' : m.content).filter(a=>!!a).join(' |-> ')); }; Object.defineProperty(message, 'channel', { get(){ return Client.channel(message.channel_id); }, }); return message; } static message(id) { for (let c of Client.channels) for (let m of c.messages) if (m.id == id) return m; return null; } static forEachMessage(cb) { Client.channels.forEach(c => c.messages.forEach(m => cb(m))); } static updates = []; static deletions = []; static actions = []; static onMessage(msg) { Client.actions.push(msg); let data = msg.d; if (msg.t == 'READY') { Client.guilds = msg.d.guilds; Client.users = msg.d.users; [ ...msg.d.private_channels, ...msg.d.guilds.map(g => [ ...g.channels.map(c => { c.guild_id ??= g.id; return c; }), ...g.threads.map(t => { t.guild_id ??= g.id; return t; }) ]).flat(), ].forEach(Client.newChannel); } if (msg.t == 'MESSAGE_CREATE') { if (!Client.message(data.id)) Client.channel(data.channel_id).addMessage(Client.newMessage(data)); } if (msg.t == 'MESSAGE_UPDATE') { const msg = Client.newMessage(data); if (!Client.message(msg.id)) Client.channel(data.channel_id).addMessage(msg); else Client.message(msg.id)?.update(msg); } if (msg.t == 'MESSAGE_DELETE') { Client.message(data.id)?.delete(data); } } }; window.client = Client; // [init decoder] // took from discord source code, compress=zlib-stream (pako.js 1.0.10) let decoder; function initDecoder() { if (decoder) log('reinit decoder'); decoder = new window.pako.Inflate({chunkSize:65536, to: "string"}); decoder.onEnd = decodeOutput; } function decodeOutput(status) { let msg; if (status !== window.pako.Z_OK) throw Error("zlib error, ".concat(status, ", ").concat(decoder.strm.msg)); let {chunks: a} = decoder , s = a.length; if (true) // wants string msg = s > 1 ? a.join("") : a[0]; else if (s > 1) { let e = 0; for (let t = 0; t < s; t++) e += a[t].length; let n = new Uint8Array(e) , i = 0; for (let e = 0; e < s; e++) { let t = a[e]; n.set(t, i); i += t.length; } msg = n } else msg = a[0]; a.length = 0; try { Client.onMessage(JSON.parse(msg)); } catch (e) { logError(e); } } function decodeInput(event) { if (event instanceof ArrayBuffer) { let buffer = new DataView(event); let fin = buffer.byteLength >= 4 && 65535 === buffer.getUint32(buffer.byteLength - 4, !1); decoder.push(event, !!fin && window.pako.Z_SYNC_FLUSH) } else throw Error("Expected array buffer, but got " + typeof event) } // [hook actions] // cancel fast connecct Object.defineProperty(window, '_ws', {set(){}}); // hook socket actions const hook = (obj, key, fn) => { const scheme = Object.getOwnPropertyDescriptor(obj, key); Object.defineProperty(obj, key, { set(value) { fn.call(this, value, () => Object.defineProperty(this, 'onmessage', { ...scheme })); } }); }; hook(window.WebSocket.prototype, 'onmessage', function(callback, restore) { restore(); this.onmessage = function(event) { if (!this.url.match(/^wss\:\/\/[a-zA-Z0-9-]+\.discord\.gg/)) return callback.call(this, event); if (!Client.ws || Client.ws.readyState == WebSocket.CLOSED) { Client.ws = this; initDecoder(); } decodeInput(event.data); callback.call(this, event); }; }); // hook http message history (() => { const proto = window.XMLHttpRequest.prototype; if (!proto._open) proto._open = proto.open; proto.open = function () { const [method, url] = arguments; Object.assign(this, {method, url}); return this._open.apply(this, arguments); }; if (!proto._send) proto._send = proto.send; proto.send = function (body) { if (this.url.match(/discord\.com\/api\/v9\/channels\/[0-9]+\/messages\?/)) { this._lnd = this.onload; this.onload = function (e) { try { const list = JSON.parse(this.response); list.map(Client.newMessage).forEach(m => Client.channel(m.channel_id).addMessage(m)); } catch (e) {logError(e);} this._lnd?.call(this, e); }; } return this._send.apply(this, arguments) }; })(); })(); // 0vC4#7152