Advertisement
Guest User

Untitled

a guest
Feb 27th, 2020
274
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 18.71 KB | None | 0 0
  1. // Paste this code inside discordapp.com console
  2.  
  3. (function () {
  4. let stop;
  5. let popup;
  6. popup = window.open('', '', `top=0,left=${screen.width-800},width=800,height=${screen.height}`);
  7. if (!popup) return console.error('Popup blocked! Please allow popups and try again.');
  8. popup.document.write(/*html*/`<!DOCTYPE html>
  9. <html><head><meta charset='utf-8'><title>Delete Discord Messages</title><base target="_blank">
  10. <style>body{background-color:#36393f;color:#dcddde;font-family:sans-serif;} a{color:#00b0f4;}
  11. body.redact .priv{display:none;} body:not(.redact) .mask{display:none;} body.redact [priv]{-webkit-text-security:disc;}
  12. .toolbar span{margin-right:8px;}
  13. button{color:#fff;background:#7289da;border:0;border-radius:4px;font-size:14px;} button:disabled{display:none;}
  14. input[type="text"],input[type="password"]{background-color:#202225;color:#b9bbbe;border-radius:4px;border:0;padding:0 .5em;height:24px;width:144px;margin:2px;}
  15. </style></head><body>
  16. <div class="toolbar" style="position:fixed;top:0;left:0;right:0;padding:8px;background:#36393f;box-shadow: 0 1px 0 rgba(0,0,0,.2), 0 1.5px 0 rgba(0,0,0,.05), 0 2px 0 rgba(0,0,0,.05);">
  17. <div style="display:flex;flex-wrap:wrap;">
  18. <span>Authorization <a href="https://github.com/victornpb/deleteDiscordMessages/blob/master/help/authToken.md" title="Help">?</a>
  19. <button id="getToken">Get</button><br>
  20. <input type="password" id="authToken" placeholder="Auth Token" autofocus>*<br>
  21. <span>Author <a href="https://github.com/victornpb/deleteDiscordMessages/blob/master/help/authorId.md" title="Help">?</a></span>
  22. <button id="getAuthor">Me</button><br><input id="authorId" type="text" placeholder="Author ID" priv></span>
  23. <span>Guild/Channel <a href="https://github.com/victornpb/deleteDiscordMessages/blob/master/help/channelId.md" title="Help">?</a>
  24. <button id="getGuildAndChannel">Get</button><br>
  25. <input id="guildId" type="text" placeholder="Guild ID" priv><br>
  26. <input id="channelId" type="text" placeholder="Channel ID" priv></span><br>
  27. <span>Range <a href="https://github.com/victornpb/deleteDiscordMessages/blob/master/help/messageId.md" title="Help">?</a><br>
  28. <input id="afterMessageId" type="text" placeholder="After messageId" priv><br>
  29. <input id="beforeMessageId" type="text" placeholder="Before messageId" priv>
  30. </span>
  31. <span>Filter <a href="https://github.com/victornpb/deleteDiscordMessages/blob/master/help/filters.md" title="Help">?</a><br>
  32. <input id="content" type="text" placeholder="Containing text" priv><br>
  33. <label><input id="hasLink" type="checkbox">has: link</label><br>
  34. <label><input id="hasFile" type="checkbox">has: file</label><br>
  35. <label><input id="includeNsfw" type="checkbox">Include NSFW</label>
  36. </span>
  37. </div>
  38. <button id="start" style="background:#43b581;width:80px;">Start</button>
  39. <button id="stop" style="background:#f04747;width:80px;" disabled>Stop</button>
  40. <button id="clear" style="width:80px;">Clear log</button>
  41. <label><input id="redact" type="checkbox"><small>Hide sensitive information</small></label> <span></span>
  42. <label><input id="autoScroll" type="checkbox" checked><small>Auto scroll</small></label> <span></span>
  43. </div>
  44. <pre style="margin-top:150px;font-size:0.75rem;font-family:Consolas,Liberation Mono,Menlo,Courier,monospace;">
  45. <center>Star this project on <a href="https://github.com/victornpb/deleteDiscordMessages" target="_blank">github.com/victornpb/deleteDiscordMessages</a>!\n\n
  46. <a href="https://github.com/victornpb/deleteDiscordMessages/issues" target="_blank">Issues or help</a></center>
  47. </pre></body></html>`);
  48.  
  49. const logArea = popup.document.querySelector('pre');
  50. const startBtn = popup.document.querySelector('button#start');
  51. const stopBtn = popup.document.querySelector('button#stop');
  52. const autoScroll = popup.document.querySelector('#autoScroll');
  53. startBtn.onclick = e => {
  54. const authToken = popup.document.querySelector('input#authToken').value.trim();
  55. const authorId = popup.document.querySelector('input#authorId').value.trim();
  56. const guildId = popup.document.querySelector('input#guildId').value.trim();
  57. const channelId = popup.document.querySelector('input#channelId').value.trim();
  58. const afterMessageId = popup.document.querySelector('input#afterMessageId').value.trim();
  59. const beforeMessageId = popup.document.querySelector('input#beforeMessageId').value.trim();
  60. const content = popup.document.querySelector('input#content').value.trim();
  61. const hasLink = popup.document.querySelector('input#hasLink').checked;
  62. const hasFile = popup.document.querySelector('input#hasFile').checked;
  63. const includeNsfw = popup.document.querySelector('input#includeNsfw').checked;
  64. stop = stopBtn.disabled = !(startBtn.disabled = true);
  65. deleteMessages(authToken, authorId, guildId, channelId, afterMessageId, beforeMessageId, content, hasLink, hasFile, includeNsfw, logger, () => !(stop === true || popup.closed)).then(() => {
  66. stop = stopBtn.disabled = !(startBtn.disabled = false);
  67. });
  68. };
  69. stopBtn.onclick = e => stop = stopBtn.disabled = !(startBtn.disabled = false);
  70. popup.document.querySelector('button#clear').onclick = e => { logArea.innerHTML = ''; };
  71. popup.document.querySelector('button#getToken').onclick = e => {
  72. window.dispatchEvent(new Event('beforeunload'));
  73. popup.document.querySelector('input#authToken').value = JSON.parse(popup.localStorage.token);
  74. };
  75. popup.document.querySelector('button#getAuthor').onclick = e => {
  76. popup.document.querySelector('input#authorId').value = JSON.parse(popup.localStorage.user_id_cache);
  77. };
  78. popup.document.querySelector('button#getGuildAndChannel').onclick = e => {
  79. const m = location.href.match(/channels\/([\w@]+)\/(\d+)/);
  80. popup.document.querySelector('input#guildId').value = m[1];
  81. popup.document.querySelector('input#channelId').value = m[2];
  82. };
  83. popup.document.querySelector('#redact').onchange = e => {
  84. popup.document.body.classList.toggle('redact') &&
  85. popup.alert('This will attempt to hide personal information, but make sure to double check before sharing screenshots.');
  86. };
  87.  
  88. const logger = (type='', args) => {
  89. const style = { '': '', info: 'color:#00b0f4;', verb: 'color:#72767d;', warn: 'color:#faa61a;', error: 'color:#f04747;', success: 'color:#43b581;' }[type];
  90. logArea.insertAdjacentHTML('beforeend', `<div style="${style}">${Array.from(args).map(o => typeof o === 'object' ? JSON.stringify(o, o instanceof Error && Object.getOwnPropertyNames(o)) : o).join('\t')}</div>`);
  91. if (autoScroll.checked) logArea.querySelector('div:last-child').scrollIntoView(false);
  92. };
  93.  
  94. return 'Looking good!';
  95. /**
  96. * Delete all messages in a Discord channel or DM
  97. * @param {string} authToken Your authorization token
  98. * @param {string} authorId Author of the messages you want to delete
  99. * @param {string} guildId Server were the messages are located
  100. * @param {string} channelId Channel were the messages are located
  101. * @param {string} afterMessageId Only delete messages after this, leave blank do delete all
  102. * @param {string} beforeMessageId Only delete messages before this, leave blank do delete all
  103. * @param {string} content Filter messages that contains this text content
  104. * @param {boolean} hasLink Filter messages that contains link
  105. * @param {boolean} hasFile Filter messages that contains file
  106. * @param {boolean} includeNsfw Search in NSFW channels
  107. * @param {function(string, Array)} extLogger Function for logging
  108. * @param {function} stopHndl stopHndl used for stopping
  109. * @author Victornpb <https://www.github.com/victornpb>
  110. * @see https://github.com/victornpb/deleteDiscordMessages
  111. */
  112. async function deleteMessages(authToken, authorId, guildId, channelId, afterMessageId, beforeMessageId, content,hasLink, hasFile, includeNsfw, extLogger, stopHndl) {
  113. const start = new Date();
  114. let deleteDelay = 50;
  115. let searchDelay = 50;
  116. let delCount = 0;
  117. let failCount = 0;
  118. let avgPing;
  119. let lastPing;
  120. let grandTotal;
  121. let throttledCount = 0;
  122. let throttledTotalTime = 0;
  123. let offset = 0;
  124. let iterations = -1;
  125.  
  126. const wait = async ms => new Promise(done => setTimeout(done, ms));
  127. const msToHMS = s => `${s / 3.6e6 | 0}h ${(s % 3.6e6) / 6e4 | 0}m ${(s % 6e4) / 1000 | 0}s`;
  128. const escapeHTML = html => html.replace(/[&<"']/g, m => ({ '&': '&amp;', '<': '&lt;', '"': '&quot;', '\'': '&#039;' })[m]);
  129. const redact = str => `<span class="priv">${escapeHTML(str)}</span><span class="mask">REDACTED</span>`;
  130. const queryString = params => params.filter(p => p[1] !== undefined).map(p => p[0] + '=' + encodeURIComponent(p[1])).join('&');
  131. const ask = async msg => new Promise(resolve => setTimeout(() => resolve(popup.confirm(msg)), 10));
  132. const printDelayStats = () => log.verb(`Delete delay: ${deleteDelay}ms, Search delay: ${searchDelay}ms`, `Last Ping: ${lastPing}ms, Average Ping: ${avgPing|0}ms`);
  133.  
  134. const log = {
  135. debug() { extLogger ? extLogger('debug', arguments) : console.debug.apply(console, arguments); },
  136. info() { extLogger ? extLogger('info', arguments) : console.info.apply(console, arguments); },
  137. verb() { extLogger ? extLogger('verb', arguments) : console.log.apply(console, arguments); },
  138. warn() { extLogger ? extLogger('warn', arguments) : console.warn.apply(console, arguments); },
  139. error() { extLogger ? extLogger('error', arguments) : console.error.apply(console, arguments); },
  140. success() { extLogger ? extLogger('success', arguments) : console.info.apply(console, arguments); },
  141. };
  142.  
  143. async function recurse() {
  144. iterations++;
  145.  
  146. let API_SEARCH_URL;
  147. if (guildId === '@me') {
  148. API_SEARCH_URL = `https://discordapp.com/api/v6/channels/${channelId}/messages/`; // DMs
  149. }
  150. else {
  151. API_SEARCH_URL = `https://discordapp.com/api/v6/guilds/${guildId}/messages/`; // Server
  152. }
  153.  
  154. const headers = {
  155. 'Authorization': authToken
  156. };
  157.  
  158. let resp;
  159. try {
  160. const s = Date.now();
  161. resp = await fetch(API_SEARCH_URL + 'search?' + queryString([
  162. [ 'author_id', authorId || undefined ],
  163. [ 'channel_id', (guildId !== '@me' ? channelId : undefined) || undefined ],
  164. [ 'min_id', afterMessageId || undefined ],
  165. [ 'max_id', beforeMessageId || undefined ],
  166. [ 'sort_by', 'timestamp' ],
  167. [ 'sort_order', 'desc' ],
  168. [ 'offset', offset ],
  169. [ 'has', hasLink ? 'link' : undefined ],
  170. [ 'has', hasFile ? 'file' : undefined ],
  171. [ 'content', content || undefined ],
  172. [ 'include_nsfw', includeNsfw ? true : undefined ],
  173. ]), { headers });
  174. lastPing = (Date.now() - s);
  175. avgPing = avgPing>0 ? (avgPing*0.9) + (lastPing*0.1):lastPing;
  176. } catch (err) {
  177. return log.error('Search request throwed an error:', err);
  178. }
  179.  
  180. // not indexed yet
  181. if (resp.status === 202) {
  182. const w = (await resp.json()).retry_after;
  183. throttledCount++;
  184. throttledTotalTime += w;
  185. log.warn(`This channel wasn't indexed, waiting ${w}ms for discord to index it...`);
  186. await wait(w);
  187. return await recurse();
  188. }
  189.  
  190. if (!resp.ok) {
  191. // searching messages too fast
  192. if (resp.status === 429) {
  193. const w = (await resp.json()).retry_after;
  194. throttledCount++;
  195. throttledTotalTime += w;
  196. searchDelay += w; // increase delay
  197. log.warn(`Being rate limited by the API for ${w}ms! Increasing search delay...`);
  198. printDelayStats();
  199. log.verb(`Cooling down for ${w * 2}ms before retrying...`);
  200.  
  201. await wait(w*2);
  202. return await recurse();
  203. } else {
  204. return log.error(`Error searching messages, API responded with status ${resp.status}!\n`, await resp.json());
  205. }
  206. }
  207.  
  208. const data = await resp.json();
  209. const total = data.total_results;
  210. if (!grandTotal) grandTotal = total;
  211. const myMessages = data.messages.map(convo => convo.find(message => message.hit===true));
  212. const systemMessages = myMessages.filter(msg => msg.type !== 0); // https://discordapp.com/developers/docs/resources/channel#message-object-message-types
  213. const deletableMessages = myMessages.filter(msg => msg.type === 0);
  214. const end = () => {
  215. log.success(`Ended at ${new Date().toLocaleString()}! Total time: ${msToHMS(Date.now() - start.getTime())}`);
  216. printDelayStats();
  217. log.verb(`Rate Limited: ${throttledCount} times. Total time throttled: ${msToHMS(throttledTotalTime)}.`);
  218. log.debug(`Deleted ${delCount} messages, ${failCount} failed.\n`);
  219. }
  220.  
  221. const etr = msToHMS((searchDelay * Math.round(total / 25)) + ((deleteDelay + avgPing) * total));
  222. log.info(`Total messages found: ${data.total_results}`, `(Messages in current page: ${data.messages.length}, Author: ${deletableMessages.length}, System: ${systemMessages.length})`, `offset: ${offset}`);
  223. printDelayStats();
  224. log.verb(`Estimated time remaining: ${etr}`)
  225.  
  226.  
  227. if (myMessages.length > 0) {
  228.  
  229. if (iterations < 1) {
  230. log.verb(`Waiting for your confirmation...`);
  231. if (!await ask(`Do you want to delete ~${total} messages?\nEstimated time: ${etr}\n\n---- Preview ----\n` +
  232. myMessages.map(m => `${m.author.username}#${m.author.discriminator}: ${m.attachments.length ? '[ATTACHMENTS]' : m.content}`).join('\n')))
  233. return end(log.error('Aborted by you!'));
  234. log.verb(`OK`);
  235. }
  236.  
  237. for (let i = 0; i < deletableMessages.length; i++) {
  238. const message = deletableMessages[i];
  239. if (stopHndl && stopHndl()===false) return end(log.error('Stopped by you!'));
  240.  
  241. log.debug(`${((delCount + 1) / grandTotal * 100).toFixed(2)}% (${delCount + 1}/${grandTotal})`,
  242. `Deleting ID:${redact(message.id)} <b>${redact(message.author.username+'#'+message.author.discriminator)} <small>(${redact(new Date(message.timestamp).toLocaleString())})</small>:</b> <i>${redact(message.content).replace(/\n/g,'↵')}</i>`,
  243. message.attachments.length ? redact(JSON.stringify(message.attachments)) : '');
  244.  
  245. let resp;
  246. try {
  247. const s = Date.now();
  248. const API_DELETE_URL = `https://discordapp.com/api/v6/channels/${channelId}/messages/`;
  249. resp = await fetch(API_DELETE_URL + message.id, {
  250. headers,
  251. method: 'DELETE'
  252. });
  253. lastPing = (Date.now() - s);
  254. avgPing = (avgPing*0.9) + (lastPing*0.1);
  255. delCount++;
  256. } catch (err) {
  257. log.error('Delete request throwed an error:', err);
  258. log.verb('Related object:', redact(JSON.stringify(message)));
  259. failCount++;
  260. }
  261.  
  262. if (!resp.ok) {
  263. // deleting messages too fast
  264. if (resp.status === 429) {
  265. const w = (await resp.json()).retry_after;
  266. throttledCount++;
  267. throttledTotalTime += w;
  268. deleteDelay += w; // increase delay
  269. log.warn(`Being rate limited by the API for ${w}ms! Adjusted delete delay to ${deleteDelay}ms.`);
  270. printDelayStats();
  271. log.verb(`Cooling down for ${w*2}ms before retrying...`);
  272. await wait(w*2);
  273. i--; // retry
  274. } else {
  275. log.error(`Error deleting message, API responded with status ${resp.status}!`, await resp.json());
  276. log.verb('Related object:', redact(JSON.stringify(message)));
  277. failCount++;
  278. }
  279. }
  280.  
  281. await wait(deleteDelay);
  282. }
  283.  
  284. if (systemMessages.length > 0) {
  285. grandTotal -= systemMessages.length;
  286. offset += systemMessages.length;
  287. log.verb(`Found ${systemMessages.length} system messages! Decreasing grandTotal to ${grandTotal} and increasing offset to ${offset}.`);
  288. }
  289.  
  290. log.verb(`Searching next messages in ${searchDelay}ms...`, (offset ? `(offset: ${offset})` : '') );
  291. await wait(searchDelay);
  292.  
  293. if (stopHndl && stopHndl()===false) return end(log.error('Stopped by you!'));
  294.  
  295. return await recurse();
  296. } else {
  297. if (total - offset > 0) log.warn('Ended because API returned an empty page.');
  298. return end();
  299. }
  300. }
  301.  
  302. log.success(`\nStarted at ${start.toLocaleString()}`);
  303. log.debug(`authorId="${redact(authorId)}" guildId="${redact(guildId)}" channelId="${redact(channelId)}" afterMessageId="${redact(afterMessageId)}" beforeMessageId="${redact(beforeMessageId)}" hasLink=${!!hasLink} hasFile=${!!hasFile}`);
  304. return await recurse();
  305. }
  306. })();
  307.  
  308. //END.
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement