Skip to content

Instantly share code, notes, and snippets.

@heptal
Last active March 14, 2019 06:27
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save heptal/622859d8aea612132ad8d2029c65df8e to your computer and use it in GitHub Desktop.
Save heptal/622859d8aea612132ad8d2029c65df8e to your computer and use it in GitHub Desktop.
Pattern (regex) based mass deletion of discord messages, with UI button!
// enable developer tools (aka chrome inspector) in discord app
// in dev tools get user token from Application > Local Storage (or google how)
// then go to Sources, open Snippets, add and save this code (replacing token with your own)
// use play button in the bottom right to enable, creating a new 'Purge' widget
// discord search in a server/dm (example - from: you, before: jan 2019) and get a postid
// paste in the appropriate input box to delete older/newer messages
const token = "sUp3R-s3kR1t"; //replace with your own
const api = async (uri, opts = {}) => await fetch(GLOBAL_ENV.API_ENDPOINT + uri, {headers: {authorization: token}, method: "GET", ...opts});
const channel = async (id) => await api(`/channels/${id}`).then(r => r.json());
const user = async (id) => await api(`/users/${id}`).then(r => r.json());
user("@me").then(o => window.author_id = o.id);
const snowflake_from_date = (date) => String(BigInt(new Date(date).valueOf() - 1420070400000) << 22n);
const date_from_snowflake = (snowflake) => new Date(Number((BigInt(snowflake) >> 22n) + 1420070400000n));
const sleep = (time) => new Promise((resolve) => setTimeout(resolve, time));
const focused = () => location.href.split("/").pop();
const filter_messages = (msgs, content) => {
const filtered = msgs.filter(msg => msg && msg.type == 0 && msg.author.id == author_id);
const regex = RegExp(`\\b${content}\\b`, "i");
return content ? filtered.filter(msg => regex.test(msg.content)) : filtered;
}
const channel_messages = async (chan_id = focused()) => {
const url = `/channels/${chan_id}/messages?limit=100`;
const msgs = await api(url).then(r => r.json());
while (true) {
const prior = await api(`${url}&before=${msgs[msgs.length - 1].id}`).then(r => r.json());
if (prior.length == 0) break;
msgs.push(...prior);
}
return msgs;
}
const search_messages = async (content, max_id, min_id, chan_id = focused()) => {
const chan = await channel(chan_id);
let url = `/v6` + (chan.type ? `/channels/${chan.id}` : `/guilds/${chan.guild_id}`) + `/messages/search?`;
url += String(new URLSearchParams(Object.entries({include_nsfw: true, author_id, content, max_id, min_id}).filter(i => i[1])));
let res = await api(url).then(o => o.json());
if (res.code && res.code == 110000) { await sleep(res.retry_after); res = await api(url).then(o => o.json()); }
const flatten = (r) => r.messages.map(grp => grp.filter(msg => msg.hit).pop());
const msgs = flatten(res);
for (let p = 1; p < Math.ceil(res.total_results / 25); p++) {
let next = await api(url + `&offset=${p * 25}`).then(o => o.json());
msgs.push(...flatten(next));
}
return filter_messages(msgs, content);
}
const delete_messages = async (msgs) => {
const total = msgs.length;
while (msgs.length) {
const msg = msgs.shift();
const resp = await api(`/channels/${msg.channel_id}/messages/${msg.id}`, {method: "DELETE"});
if (resp.status == 429) msgs.unshift(msg);
console.log(`${((total - msgs.length) / total * 100).toFixed(2)}%, ${(msgs.length / 120).toFixed(2)}mins left`);
await sleep(500);
}
}
document.body.insertAdjacentHTML("afterbegin", `<style>form>div{display: flex} input{width: 100%} input[type=submit]{width:30%}</style>`)
$(`[class^="chan"]`).insertAdjacentHTML("afterbegin",`<style>input{width: 100%}</style><form id="purge">
<div style="display:flex"><input type="text" placeholder="regex filter: either|or"><input type="submit" value="Purge"></div>
<input type="text" placeholder="before id: 526593342650449240"><input type="text" placeholder="after id: 426593342650449240"></form>`);
$("#purge").onsubmit = async (e) => { e.preventDefault(); delete_messages(await search_messages(e.target[0].value, e.target[2].value, e.target[3].value)) };
$(`[class^="chan"]`).insertAdjacentHTML("afterbegin",`<form id="user"><div><input type="text" placeholder="userid (ex: 585387035561951133)">
<input type="submit" value="Lookup User"></div><input type="text" placeholder="username#discriminator"><img></form>`);
$("#user").onsubmit = async (e) => {
e.preventDefault();
const u = await user(e.target[0].value); console.log(u);
e.target[2].value = `${u.username}#${u.discriminator}`;
e.target.lastChild.src = `https://cdn.discordapp.com/avatars/${u.id}/${u.avatar}?size=64`;
}
const repeater = async (content, chan_id = focused(), interval = 1000) => {
while (true) {
await api(`/channels/${chan_id}/messages`, {method: "POST", headers: {authorization: token, "Content-Type": "application/json"}, body: JSON.stringify({content})});
await sleep(interval);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment