Skip to content

Commit

Permalink
Merge pull request #129 from Galaxyzd/master
Browse files Browse the repository at this point in the history
Add option to set initial search and delete delay values
  • Loading branch information
victornpb committed Oct 28, 2020
2 parents 5b92837 + 7162b08 commit b1cfdf6
Show file tree
Hide file tree
Showing 2 changed files with 57 additions and 44 deletions.
98 changes: 54 additions & 44 deletions deleteDiscordMessages.user.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,8 @@
* @author Victornpb <https://www.github.com/victornpb>
* @see https://github.com/victornpb/deleteDiscordMessages
*/
async function deleteMessages(authToken, authorId, guildId, channelId, minId, maxId, content,hasLink, hasFile, includeNsfw, includePinned, extLogger, stopHndl, onProgress) {
async function deleteMessages(authToken, authorId, guildId, channelId, minId, maxId, content, hasLink, hasFile, includeNsfw, includePinned, searchDelay, deleteDelay, extLogger, stopHndl, onProgress) {
const start = new Date();
let deleteDelay = 100;
let searchDelay = 100;
let delCount = 0;
let failCount = 0;
let avgPing;
Expand All @@ -42,16 +40,16 @@ async function deleteMessages(authToken, authorId, guildId, channelId, minId, ma
let throttledTotalTime = 0;
let offset = 0;
let iterations = -1;

const wait = async ms => new Promise(done => setTimeout(done, ms));
const msToHMS = s => `${s / 3.6e6 | 0}h ${(s % 3.6e6) / 6e4 | 0}m ${(s % 6e4) / 1000 | 0}s`;
const escapeHTML = html => html.replace(/[&<"']/g, m => ({ '&': '&amp;', '<': '&lt;', '"': '&quot;', '\'': '&#039;' })[m]);
const redact = str => `<span class="priv">${escapeHTML(str)}</span><span class="mask">REDACTED</span>`;
const queryString = params => params.filter(p => p[1] !== undefined).map(p => p[0] + '=' + encodeURIComponent(p[1])).join('&');
const ask = async msg => new Promise(resolve => setTimeout(() => resolve(window.confirm(msg)), 10));
const printDelayStats = () => log.verb(`Delete delay: ${deleteDelay}ms, Search delay: ${searchDelay}ms`, `Last Ping: ${lastPing}ms, Average Ping: ${avgPing|0}ms`);
const printDelayStats = () => log.verb(`Delete delay: ${deleteDelay}ms, Search delay: ${searchDelay}ms`, `Last Ping: ${lastPing}ms, Average Ping: ${avgPing | 0}ms`);
const toSnowflake = (date) => /:/.test(date) ? ((new Date(date).getTime() - 1420070400000) * Math.pow(2, 22)) : date;

const log = {
debug() { extLogger ? extLogger('debug', arguments) : console.debug.apply(console, arguments); },
info() { extLogger ? extLogger('info', arguments) : console.info.apply(console, arguments); },
Expand All @@ -73,25 +71,25 @@ async function deleteMessages(authToken, authorId, guildId, channelId, minId, ma
const headers = {
'Authorization': authToken
};

let resp;
try {
const s = Date.now();
resp = await fetch(API_SEARCH_URL + 'search?' + queryString([
[ 'author_id', authorId || undefined ],
[ 'channel_id', (guildId !== '@me' ? channelId : undefined) || undefined ],
[ 'min_id', minId ? toSnowflake(minId) : undefined ],
[ 'max_id', maxId ? toSnowflake(maxId) : undefined ],
[ 'sort_by', 'timestamp' ],
[ 'sort_order', 'desc' ],
[ 'offset', offset ],
[ 'has', hasLink ? 'link' : undefined ],
[ 'has', hasFile ? 'file' : undefined ],
[ 'content', content || undefined ],
[ 'include_nsfw', includeNsfw ? true : undefined ],
['author_id', authorId || undefined],
['channel_id', (guildId !== '@me' ? channelId : undefined) || undefined],
['min_id', minId ? toSnowflake(minId) : undefined],
['max_id', maxId ? toSnowflake(maxId) : undefined],
['sort_by', 'timestamp'],
['sort_order', 'desc'],
['offset', offset],
['has', hasLink ? 'link' : undefined],
['has', hasFile ? 'file' : undefined],
['content', content || undefined],
['include_nsfw', includeNsfw ? true : undefined],
]), { headers });
lastPing = (Date.now() - s);
avgPing = avgPing>0 ? (avgPing*0.9) + (lastPing*0.1) : lastPing;
avgPing = avgPing > 0 ? (avgPing * 0.9) + (lastPing * 0.1) : lastPing;
} catch (err) {
return log.error('Search request threw an error:', err);
}
Expand All @@ -116,8 +114,8 @@ async function deleteMessages(authToken, authorId, guildId, channelId, minId, ma
log.warn(`Being rate limited by the API for ${w}ms! Increasing search delay...`);
printDelayStats();
log.verb(`Cooling down for ${w * 2}ms before retrying...`);
await wait(w*2);

await wait(w * 2);
return await recurse();
} else {
return log.error(`Error searching messages, API responded with status ${resp.status}!\n`, await resp.json());
Expand All @@ -127,11 +125,11 @@ async function deleteMessages(authToken, authorId, guildId, channelId, minId, ma
const data = await resp.json();
const total = data.total_results;
if (!grandTotal) grandTotal = total;
const discoveredMessages = data.messages.map(convo => convo.find(message => message.hit===true));
const discoveredMessages = data.messages.map(convo => convo.find(message => message.hit === true));
const messagesToDelete = discoveredMessages.filter(msg => {
return msg.type === 0 || msg.type === 6 || (msg.pinned && includePinned);
});
const skippedMessages = discoveredMessages.filter(msg=>!messagesToDelete.find(m=> m.id===msg.id));
const skippedMessages = discoveredMessages.filter(msg => !messagesToDelete.find(m => m.id === msg.id));

const end = () => {
log.success(`Ended at ${new Date().toLocaleString()}! Total time: ${msToHMS(Date.now() - start.getTime())}`);
Expand All @@ -144,27 +142,27 @@ async function deleteMessages(authToken, authorId, guildId, channelId, minId, ma
log.info(`Total messages found: ${data.total_results}`, `(Messages in current page: ${data.messages.length}, To be deleted: ${messagesToDelete.length}, System: ${skippedMessages.length})`, `offset: ${offset}`);
printDelayStats();
log.verb(`Estimated time remaining: ${etr}`)


if (messagesToDelete.length > 0) {

if (++iterations < 1) {
log.verb(`Waiting for your confirmation...`);
if (!await ask(`Do you want to delete ~${total} messages?\nEstimated time: ${etr}\n\n---- Preview ----\n` +
messagesToDelete.map(m => `${m.author.username}#${m.author.discriminator}: ${m.attachments.length ? '[ATTACHMENTS]' : m.content}`).join('\n')))
return end(log.error('Aborted by you!'));
return end(log.error('Aborted by you!'));
log.verb(`OK`);
}

for (let i = 0; i < messagesToDelete.length; i++) {
const message = messagesToDelete[i];
if (stopHndl && stopHndl()===false) return end(log.error('Stopped by you!'));
if (stopHndl && stopHndl() === false) return end(log.error('Stopped by you!'));

log.debug(`${((delCount + 1) / grandTotal * 100).toFixed(2)}% (${delCount + 1}/${grandTotal})`,
`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>`,
`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>`,
message.attachments.length ? redact(JSON.stringify(message.attachments)) : '');
if (onProgress) onProgress(delCount + 1, grandTotal);

let resp;
try {
const s = Date.now();
Expand All @@ -174,7 +172,7 @@ async function deleteMessages(authToken, authorId, guildId, channelId, minId, ma
method: 'DELETE'
});
lastPing = (Date.now() - s);
avgPing = (avgPing*0.9) + (lastPing*0.1);
avgPing = (avgPing * 0.9) + (lastPing * 0.1);
delCount++;
} catch (err) {
log.error('Delete request throwed an error:', err);
Expand All @@ -191,16 +189,16 @@ async function deleteMessages(authToken, authorId, guildId, channelId, minId, ma
deleteDelay = w; // increase delay
log.warn(`Being rate limited by the API for ${w}ms! Adjusted delete delay to ${deleteDelay}ms.`);
printDelayStats();
log.verb(`Cooling down for ${w*2}ms before retrying...`);
await wait(w*2);
log.verb(`Cooling down for ${w * 2}ms before retrying...`);
await wait(w * 2);
i--; // retry
} else {
log.error(`Error deleting message, API responded with status ${resp.status}!`, await resp.json());
log.verb('Related object:', redact(JSON.stringify(message)));
failCount++;
}
}

await wait(deleteDelay);
}

Expand All @@ -209,11 +207,11 @@ async function deleteMessages(authToken, authorId, guildId, channelId, minId, ma
offset += skippedMessages.length;
log.verb(`Found ${skippedMessages.length} system messages! Decreasing grandTotal to ${grandTotal} and increasing offset to ${offset}.`);
}
log.verb(`Searching next messages in ${searchDelay}ms...`, (offset ? `(offset: ${offset})` : '') );

log.verb(`Searching next messages in ${searchDelay}ms...`, (offset ? `(offset: ${offset})` : ''));
await wait(searchDelay);

if (stopHndl && stopHndl()===false) return end(log.error('Stopped by you!'));
if (stopHndl && stopHndl() === false) return end(log.error('Stopped by you!'));

return await recurse();
} else {
Expand Down Expand Up @@ -259,7 +257,7 @@ function initUI() {
#undiscord .toolbar span{margin-right:8px}
#undiscord button,#undiscord .btn{color:#fff;background:#7289da;border:0;border-radius:4px;font-size:14px}
#undiscord button:disabled{display:none}
#undiscord input[type="text"],#undiscord input[type="search"],#undiscord input[type="password"],#undiscord input[type="datetime-local"]{background-color:#202225;color:#b9bbbe;border-radius:4px;border:0;padding:0 .5em;height:24px;width:144px;margin:2px}
#undiscord input[type="text"],#undiscord input[type="search"],#undiscord input[type="password"],#undiscord input[type="datetime-local"],#undiscord input[type="number"]{background-color:#202225;color:#b9bbbe;border-radius:4px;border:0;padding:0 .5em;height:24px;width:144px;margin:2px}
#undiscord input#file{display:none}
#undiscord hr{border-color:rgba(255,255,255,0.1)}
#undiscord .header{padding:12px 16px;background-color:var(--background-tertiary);color:var(--text-muted)}
Expand Down Expand Up @@ -305,6 +303,16 @@ function initUI() {
<label><input id="hasLink" type="checkbox">has: link</label><br>
<label><input id="hasFile" type="checkbox">has: file</label><br>
<label><input id="includePinned" type="checkbox">Include pinned</label>
</span><br>
<span>Search Delay <a
href="https://github.com/victornpb/deleteDiscordMessages/blob/master/help/delay.md" title="Help"
target="_blank">?</a><br>
<input id="searchDelay" type="number" value="100" step="100"><br>
</span>
<span>Delete Delay <a
href="https://github.com/victornpb/deleteDiscordMessages/blob/master/help/delay.md" title="Help"
target="_blank">?</a><br>
<input id="deleteDelay" type="number" value="1000" step="100">
</span>
</div>
<hr>
Expand Down Expand Up @@ -347,7 +355,7 @@ function initUI() {

function mountBtn() {
const toolbar = document.querySelector('[class^=toolbar]');
if (toolbar) toolbar.appendChild(btn);
if (toolbar) toolbar.appendChild(btn);
}

const observer = new MutationObserver(function (_mutationsList, _observer) {
Expand All @@ -362,7 +370,7 @@ function initUI() {
const startBtn = $('button#start');
const stopBtn = $('button#stop');
const autoScroll = $('#autoScroll');

startBtn.onclick = async e => {
const authToken = $('input#authToken').value.trim();
const authorId = $('input#authorId').value.trim();
Expand All @@ -377,6 +385,8 @@ function initUI() {
const hasFile = $('input#hasFile').checked;
const includeNsfw = $('input#includeNsfw').checked;
const includePinned = $('input#includePinned').checked;
const searchDelay = parseInt($('input#searchDelay').value.trim());
const deleteDelay = parseInt($('input#deleteDelay').value.trim());
const progress = $('#progress');
const progress2 = btn.querySelector('progress');
const percent = $('.percent');
Expand Down Expand Up @@ -411,7 +421,7 @@ function initUI() {

stop = stopBtn.disabled = !(startBtn.disabled = true);
for (let i = 0; i < channelIds.length; i++) {
await deleteMessages(authToken, authorId, guildId, channelIds[i], minId || minDate, maxId || maxDate, content, hasLink, hasFile, includeNsfw, includePinned, logger, stopHndl, onProg);
await deleteMessages(authToken, authorId, guildId, channelIds[i], minId || minDate, maxId || maxDate, content, hasLink, hasFile, includeNsfw, includePinned, searchDelay, deleteDelay, logger, stopHndl, onProg);
stop = stopBtn.disabled = !(startBtn.disabled = false);
}
};
Expand All @@ -432,12 +442,12 @@ function initUI() {
};
$('#redact').onchange = e => {
popover.classList.toggle('redact') &&
window.alert('This will attempt to hide personal information, but make sure to double check before sharing screenshots.');
window.alert('This will attempt to hide personal information, but make sure to double check before sharing screenshots.');
};

const logger = (type='', args) => {
const logger = (type = '', args) => {
const style = { '': '', info: 'color:#00b0f4;', verb: 'color:#72767d;', warn: 'color:#faa61a;', error: 'color:#f04747;', success: 'color:#43b581;' }[type];
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>`);
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>`);
if (autoScroll.checked) logArea.querySelector('div:last-child').scrollIntoView(false);
};

Expand Down
3 changes: 3 additions & 0 deletions help/delay.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Search and Delete Delay

This setting controls the initial delay for for searching messages. Sometimes the Discord API will quickly rate limit you, in this case setting the delays to something higher might help. This is especially true for the **Delete Delay**.

0 comments on commit b1cfdf6

Please sign in to comment.