Skip to content

Instantly share code, notes, and snippets.

@firatkucuk
Last active May 1, 2024 04:17
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, '');
@firatkucuk
Copy link
Author

Hey guys, which "token" exactly am I supposed to use here?
I created "App-Level Tokens" and tried to use it but it returns "invalid_auth".

image

I use User OAuth Token

and the scopes are:

image

@kadiiskiFFW
Copy link

kadiiskiFFW commented Feb 26, 2021

Hey firatkucuk, i'm using the same thing.
I added it in the token. Added the APP to the slack workspace, I'm admin there, added all of the listed scopes and added the channel ID from the URL.

Still I get "invalid_auth". :(

image

And these are my scopes as well:

image

@firatkucuk
Copy link
Author

Hey firatkucuk, i'm using the same thing.
I added it in the token. Added the APP to the slack workspace, I'm admin there, added all of the listed scopes and added the channel ID from the URL.

Still I get "invalid_auth". :(

image

And these are my scopes as well:

image

TBH, it should work with those settings could you please add those bot scopes as well:

image

@kadiiskiFFW
Copy link

Hey thanks for the help.

I have set the BOT permissions same as yours:
image

I have just tested the script both with User OAuth Token and Bot User OAuth Token.

I have also reinstalled the app - still not working. I'm so sorry for losing your time.

@firatkucuk
Copy link
Author

@kadiiskiFFW No problem. I'll try to create an app from scratch in my spare time. It was old-style tokens and was really easy to use then slack changed to tokens with scopes. Now it's really hard to adjust things.

@kadiiskiFFW
Copy link

@firatkucuk thank you very much for the effort!

@lorvent
Copy link

lorvent commented Feb 26, 2021

FWIW, this was not working for me too but i gave up because it is not that important for me.
my understanding is that, if a message is created by bot using webhook, then it is unable to delete such messages but i need it for exact same purpose.

thanks.

@firatkucuk
Copy link
Author

I have created a clone app that is identical to the working one and seems there's an issue on the slack side. I have contacted slack support.

@firatkucuk
Copy link
Author

@kadiiskiFFW and @lorvent for the new application they are no longer support tokens from query string so I've updated the code using as Authorization Bearer header.

@akintt
Copy link

akintt commented Mar 16, 2021

@firatkucuk teşekkürler hocam sorunsuz hallettik.

@firatkucuk
Copy link
Author

@akintt super.

@ensargunesdogdu
Copy link

Thanks, Fırat :) Very useful snippet.

@harrywebster
Copy link

How crazy that this work around is needed... surely it would be easier for Slack to offer this rather than getting hundreds of users spamming them with delete API calls.

Thanks for this script, it made the reset far, far, easier... even if i did need to leave it running over the weekend.

@shadowmodder
Copy link

+1 much-needed feature. Thank you for the script!.

@Bumpyshot
Copy link

Bumpyshot commented Jul 13, 2021

Hey, thanks for the script but i have a little problem...
I have a channel_not_found when i run the code, yet i have the right id of the channel
Any idea ?

@firatkucuk
Copy link
Author

@Bumpyshot
channel id is at the end of the URL: https://COMPANY.slack.com/archives/CHANNEL_ID
if you're sure about that in that case maybe your token has no adequate access for that operation.

@JonathanNobrega
Copy link

How to use this script?

First: Get your Slack Api Token

  1. Login to Your Apps
  2. Create a new app and select the workplace you would like to connect to
  3. From the sidebar, go to “OAuth & Permissions” page
  4. Under “Scopes” section -> “User Token Scopes” select the following scopes:
    channels:history
    groups:history
    im:history
    mpim:history
    chat:write
  5. Under “OAuth Tokens for Your Workspace” section, click “Install App to Workspace” and follow the instructions
  6. Now, under “OAuth Tokens for Your Workspace” section, copy the “User OAuth Token”: this is your token

How to get the Channel ID which I want to delete the messages?

  1. Go to your Slack from the web, and navigate to the desired channel
  2. You can see the channel ID from the url (https://app.slack.com/client/{TEAM_ID}/{CHANNEL_ID})
  3. Copy the last part of the URL, this is your CHANNEL_ID

Now download the script in this page, and insert your token inside the file (under SLACK TOKEN), then save

Now, run the script: node ./delete-slack-messages.js CHANNEL_ID

(Instructions adapted from: https://gist.github.com/gummi-io/f3dbfebfcd5fd1fc4e42da1c0e2b41c8)

@im-syk
Copy link

im-syk commented Nov 5, 2021

@JonathanNobrega
Thanks, your discription helped me!

@im-syk
Copy link

im-syk commented Nov 5, 2021

Please tell me.
Is it possible to specify the period to be deleted by this script?

@doschkinow
Copy link

thanks to @firatkucuk and @JonathanNobrega , this script worked fine for me!

@JonathanNobrega
Copy link

Glad to help @im-syk and @doschkinow!

@erickacevedor
Copy link

Hey! When deleting messages from a DM, I got the message:

vada {
  ok: false,
  error: 'cant_delete_message',
  warning: 'missing_charset',
  response_metadata: { warnings: [ 'missing_charset' ] }
}
1650841222.763739 could not be deleted! (cant_delete_message)

Any ideas?

@razikallayi
Copy link

@JonathanNobrega

Thanks, I was using the bot token. Changed to user token and reinstalled the app. It worked.

@alevaldiviezo
Copy link

Is there any chance to run this script just with 'node ./slack-delete-messages', without the channel id in the terminal?

@firatkucuk
Copy link
Author

@alevaldiviezo what do you want to achieve? Deleting all messages in all channels or just running without channel_id.?

let channel = 'put your channel id here';

// ------------------- delete following section from here
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);
}
// ------------------- to here

@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.

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