Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save mrjones-plip/3c01098ec1f7cdd333d544768c33067e to your computer and use it in GitHub Desktop.
Save mrjones-plip/3c01098ec1f7cdd333d544768c33067e to your computer and use it in GitHub Desktop.
Deletes last 30 days of slack public/private channel messages, private chat messages and channel thread replies.
#!/usr/bin/env node
// Channel ID is on the the browser URL.: https://mycompany.slack.com/messages/MYCHANNELID/
// Pass it as a parameter: node ./delete-slack-messages.js CHANNEL_ID
// CONFIGURATION #######################################################################################################
const token = 'SLACK TOKEN';
// Legacy tokens are no more supported.
// Please create an app or use an existing Slack App
// Add following scopes in your app from "OAuth & Permissions"
// - channels:history
// - groups:history
// - im:history
// - mpim:history
// - chat:write
// VALIDATION ##########################################################################################################
if (token === 'SLACK TOKEN') {
console.error('Token seems incorrect. Please open the file with an editor and modify the token variable.');
process.exit(1);
}
let channel = '';
if (process.argv[0].indexOf('node') !== -1 && process.argv.length > 2) {
channel = process.argv[2];
} else if (process.argv.length > 1) {
channel = process.argv[1];
} else {
console.log('Usage: node ./delete-slack-messages.js CHANNEL_ID');
process.exit(1);
}
// GLOBALS #############################################################################################################
const https = require('https')
const historyApiUrl = `/api/conversations.history?channel=${channel}&count=1000&cursor=`;
const deleteApiUrl = '/api/chat.delete';
const repliesApiUrl = `/api/conversations.replies?channel=${channel}&ts=`
let delay = 300; // Delay between delete operations in milliseconds
const epochTime = Math.floor(new Date().getTime() / 1000);
const keepTime = (epochTime - 2678400); // one month ago
const now = new Date();
const nowEpoch = Math.floor(now.getTime() / 1000);
const prettyPrintDate = new Date(0); // The 0 there is the key, which sets the date to the epoch
prettyPrintDate.setUTCSeconds(keepTime);
console.log('');
console.log('Delting messages for channel',channel);
console.log('');
console.log("Currently it is", now.toLocaleDateString() , now.toLocaleTimeString(), "(", nowEpoch, ")");
console.log("Keeping items older than", prettyPrintDate.toLocaleDateString(keepTime) , prettyPrintDate.toLocaleTimeString(), "(", keepTime,")");
console.log('');
// ---------------------------------------------------------------------------------------------------------------------
const sleep = delay => new Promise(r => setTimeout(r, delay));
const request = (path, data) => new Promise((resolve, reject) => {
const options = {
hostname: 'slack.com',
port : 443,
path : path,
method : data ? 'POST' : 'GET',
headers : {
'Authorization': `Bearer ${token}`,
'Content-Type' : 'application/json; charset=utf-8',
'Accept' : 'application/json'
}
};
const req = https.request(options, res => {
let body = '';
res.on('data', chunk => (body += chunk));
res.on('end', () => resolve(JSON.parse(body)));
});
req.on('error', reject);
if (data) {
req.write(JSON.stringify(data));
}
req.end();
});
// ---------------------------------------------------------------------------------------------------------------------
async function deleteMessages(threadTs, messages) {
if (messages.length == 0) {
return;
}
const message = messages.shift();
const messageTs = Math.ceil(message.ts);
if (message.thread_ts !== threadTs) {
await fetchAndDeleteMessages(message.thread_ts, ''); // Fetching replies, it will delete main message as well.
} else if(messageTs < keepTime) {
const response = await request(deleteApiUrl, {channel: channel, ts: message.ts});
if (response.ok === true) {
const d = new Date(0); // The 0 there is the key, which sets the date to the epoch
d.setUTCSeconds(messageTs);
console.log(message.ts + (threadTs ? ' reply' : '') + ' Deleting message from', d.toLocaleDateString() , d.toLocaleTimeString(), "(", keepTime,")");
} else if (response.ok === false) {
console.log(message.ts + ' could not be deleted! (' + response.error + ')');
if (response.error === 'ratelimited') {
await sleep(1000);
delay += 100; // If rate limited error caught then we need to increase delay.
messages.unshift(message);
}
}
}
await sleep(delay);
await deleteMessages(threadTs, messages);
}
// ---------------------------------------------------------------------------------------------------------------------
async function fetchAndDeleteMessages(threadTs, cursor) {
const response = await request((threadTs ? repliesApiUrl + threadTs + '&cursor=' : historyApiUrl) + cursor);
if (!response.ok) {
console.error(response.error);
return;
}
if (!response.messages || response.messages.length === 0) {
return;
}
await deleteMessages(threadTs, response.messages);
if (response.has_more) {
await fetchAndDeleteMessages(threadTs, response.response_metadata.next_cursor);
}
}
// ---------------------------------------------------------------------------------------------------------------------
fetchAndDeleteMessages(null, '');
console.log('');
@mrjones-plip
Copy link
Author

In case you missed it at the top of the page, this is forked off this fine script!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment