Skip to content

Instantly share code, notes, and snippets.

@firatkucuk
Last active November 5, 2024 13:15
Show Gist options
  • Save firatkucuk/ee898bc919021da621689f5e47e7abac to your computer and use it in GitHub Desktop.
Save firatkucuk/ee898bc919021da621689f5e47e7abac to your computer and use it in GitHub Desktop.
Deletes 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.');
}
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 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();
if (message.thread_ts !== threadTs) {
await fetchAndDeleteMessages(message.thread_ts, ''); // Fetching replies, it will delete main message as well.
} else {
const response = await request(deleteApiUrl, {channel: channel, ts: message.ts});
if (response.ok === true) {
console.log(message.ts + (threadTs ? ' reply' : '') + ' deleted!');
} 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, '');
@alevaldiviezo
Copy link

Hi, thanks for your reply, I got it, the idea is to run the script as a second process with the fork command, many thanks I got it now.

@firatkucuk
Copy link
Author

firatkucuk commented Feb 5, 2024 via email

@firatkucuk
Copy link
Author

firatkucuk commented Feb 5, 2024 via email

@firatkucuk
Copy link
Author

firatkucuk commented Feb 5, 2024 via email

@mrjones-plip
Copy link

mrjones-plip commented May 1, 2024

Excellent script - thanks @firatkucuk !

@im-syk et al. - if anyone is interested in a version of this that deletes last 30 days, I made a version that does that. To change how long it keeps things for, update the keepTime variable as needed.

@vdiazh
Copy link

vdiazh commented Jun 18, 2024

Thanks a lot, all work fine.

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