Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
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',
'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('vada', response);
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, '');
@ugurayan

This comment has been minimized.

Copy link

@ugurayan ugurayan commented Jul 18, 2017

Nice code

@horstepipe

This comment has been minimized.

Copy link

@horstepipe horstepipe commented Oct 6, 2017

would it be possible to remove DMs, too?

@woodyb23

This comment has been minimized.

Copy link

@woodyb23 woodyb23 commented Oct 7, 2017

Code deletes the messages but when I come back they are there again. Any ideas?

@gummi-io

This comment has been minimized.

Copy link

@gummi-io gummi-io commented Nov 30, 2017

Thank you so much!! Exactly what i needed.

I modified the code a bit to delete direct messages and continue deleting after 1000 limit. If anyone is interested:
https://gist.github.com/gummi-io/f3dbfebfcd5fd1fc4e42da1c0e2b41c8 ;)

@mtruban

This comment has been minimized.

Copy link

@mtruban mtruban commented Jan 3, 2018

thanks man. works like a charm

@niiongi

This comment has been minimized.

Copy link

@niiongi niiongi commented Jan 26, 2018

Hi there! I'm a bit of a noob when it comes to APIs and in need of a bit of help..

Do I understand correctly that in order to wipe my message history in my #general, I: 1) modify the raw code; 2) DL it as a .zip file; 3) import it to my Slack?

If so, what are the fields I need to modify in the raw code and how can I find my #general channel ID?

TY

@aissaghouti

This comment has been minimized.

@djdenv

This comment has been minimized.

Copy link

@djdenv djdenv commented Mar 27, 2018

@woodyb23 (and others) Slack imposes an API request limit of 1 per second (https://api.slack.com/docs/rate-limits#web). Short bursts are allowed, but once you hit 20 messages or so, requests will start getting rejected. Modify the settings of this script to use a delay of say 1250ms so you won't hit the rate limit and messages will be deleted properly.

@yunabsd

This comment has been minimized.

Copy link

@yunabsd yunabsd commented Mar 28, 2018

Thank you very much~!

I modified the code a bit, also added promise methods, delete all messages, and delete direct message. :)
https://github.com/yunabsd/SlackRemoveMe

@CraigReville

This comment has been minimized.

Copy link

@CraigReville CraigReville commented May 24, 2018

Having issues when using it for 1-on-1 Chat, can't get around this:

/Users/name/Scripts/delete-channel-messages.js:66
        for (var i = 0; i < response.messages.length; i++) {
                                              ^

TypeError: Cannot read property 'length' of undefined
    at IncomingMessage.<anonymous> (/Users/name/Scripts/delete-channel-messages.js:66:47)
    at IncomingMessage.emit (events.js:187:15)
    at endReadableNT (_stream_readable.js:1091:14)
    at process._tickCallback (internal/process/next_tick.js:174:19)

BUMP**

Can't figure out why but it seems cannot delete chats, just general group chats.

EDIT:
Just read that the feature doesn't exist to delete private messages via api anymore, they allow you to delete view archive and that's only available on the paid plan.

Basically, they are forcing you to pay.

@firatkucuk

This comment has been minimized.

Copy link
Owner Author

@firatkucuk firatkucuk commented Jul 20, 2018

Now, It supports chat messages as well. And automatically proceed to the next 1000 messages.

@gijo-varghese

This comment has been minimized.

Copy link

@gijo-varghese gijo-varghese commented Aug 1, 2018

Not working

@nedzen

This comment has been minimized.

Copy link

@nedzen nedzen commented Aug 8, 2018

super neat ! thank you !

@roelvdboom

This comment has been minimized.

Copy link

@roelvdboom roelvdboom commented Sep 6, 2018

Works perfectly!

@iLoreto

This comment has been minimized.

Copy link

@iLoreto iLoreto commented Oct 27, 2018

Thank you for sharing. I had a dream where hubot had this functionality in coffee script!
/hubot cleanup --start=2017-1-1 --end=2018-1-1 (admins permissions only)

KUDOS!

@hamid-ne

This comment has been minimized.

Copy link

@hamid-ne hamid-ne commented Dec 14, 2018

Very Tanks :)

@hasanteymoori

This comment has been minimized.

Copy link

@hasanteymoori hasanteymoori commented Dec 15, 2018

👍

@faizzed

This comment has been minimized.

Copy link

@faizzed faizzed commented May 30, 2019

Pretty cool. I would suggest to keep a count, help know where things at
1558967986.008200 deleted! -- COUNT: 82

@waghcwb

This comment has been minimized.

Copy link

@waghcwb waghcwb commented Jun 3, 2019

The script is not warning about errors.

I've made a little modification to it in line 93 so it will throw errors if there's any:

// check if response is ok
if (response.ok) {
  if (response.messages && response.messages.length > 0) {

    if (response.has_more) {
      nextCursor = response.response_metadata.next_cursor;
    }

    for (let i = 0; i < response.messages.length; i++) {
      messages.push(response.messages[i].ts);
    }

    deleteMessage();
  }
} else {
  throw new Error(response.error)
}
@benkorichard

This comment has been minimized.

Copy link

@benkorichard benkorichard commented Jun 19, 2019

Good job. Thanks!

@AbdealiJK

This comment has been minimized.

Copy link

@AbdealiJK AbdealiJK commented Jul 25, 2019

When not using legacy tokens, need to create a Slack App with the scopes:

  • chat:write:user
  • channels:history
@aevaldas

This comment has been minimized.

Copy link

@aevaldas aevaldas commented Aug 16, 2019

I would suggest to use unshift instead of push in 64 line, so it would not have messages, keeping at the bottom.

@firatkucuk

This comment has been minimized.

Copy link
Owner Author

@firatkucuk firatkucuk commented Aug 20, 2019

I would suggest to use unshift instead of push in 64 line, so it would not have messages, keeping at the bottom.

Thanks for the suggestion. But seems it's a basic queue mechanism. First in First out. That's why we use shift-push. shift-unshift is more like a stack.

@firatkucuk

This comment has been minimized.

Copy link
Owner Author

@firatkucuk firatkucuk commented Aug 20, 2019

I would suggest to use unshift instead of push in 64 line, so it would not have messages, keeping at the bottom.

Sorry my bad, You're right. In case of failure, it should be the first one to be tried.

@Ausaes

This comment has been minimized.

Copy link

@Ausaes Ausaes commented Aug 22, 2019

It works like a charm!

But, for example, it will be more efficient if the code only tries to delete the own messages.
I change a bit your code and I add this. You can find your user id in any conversation, looking for the user link.

https://app.slack.com/team/USERID

if (userId == response.messages[i].user) { messages.push(response.messages[i].ts); }

Thank you for code! :D

@firatkucuk

This comment has been minimized.

Copy link
Owner Author

@firatkucuk firatkucuk commented Aug 22, 2019

It works like a charm!

But, for example, it will be more efficient if the code only tries to delete the own messages.
I change a bit your code and I add this. You can find your user id in any conversation, looking for the user link.

https://app.slack.com/team/USERID

if (userId == response.messages[i].user) { messages.push(response.messages[i].ts); }

Thank you for code! :D

Thanks, It might be optional. I guess. It's better to convert this to a complete project instead of simple gist script.

@Toanzzz

This comment has been minimized.

Copy link

@Toanzzz Toanzzz commented Sep 5, 2019

Thank for the code @firatkucuk

I just updated the code with ES6, and it also accept token as parameter. Hope you have time to check it out 😃

https://gist.github.com/Toanzzz/0bc9f903207a6cd4d51ada76edd45513

@firatkucuk

This comment has been minimized.

Copy link
Owner Author

@firatkucuk firatkucuk commented Sep 5, 2019

Thank for the code @firatkucuk

I just updated the code with ES6, and it also accept token as parameter. Hope you have time to check it out

https://gist.github.com/Toanzzz/0bc9f903207a6cd4d51ada76edd45513

Thanks, seems way clearer.
Gonna check it.

@michelleBSquared

This comment has been minimized.

Copy link

@michelleBSquared michelleBSquared commented Jan 5, 2020

This works like a charm! Thanks for doing this and sharing with the public!

@createdbytilt

This comment has been minimized.

Copy link

@createdbytilt createdbytilt commented Jan 19, 2020

This is a great app so far - thank you so much!

Is there a way to delete threaded messages and DM conversations too?

(I can currently only delete DM's that I've sent myself, I would like to flush everyone's messages throughout the entire slack workspace regardless if I was in the conversation or not)

@firatkucuk

This comment has been minimized.

Copy link
Owner Author

@firatkucuk firatkucuk commented Jan 19, 2020

This is a great app so far - thank you so much!

Is there a way to delete threaded messages and DM conversations too?

(I can currently only delete DM's that I've sent myself, I would like to flush everyone's messages throughout the entire slack workspace regardless if I was in the conversation or not)

Thanks for your good wishes,
AFAIK it's not allowed on slack API's. For DM's, only allowed to delete your own messages.

@JoanComasFdz

This comment has been minimized.

Copy link

@JoanComasFdz JoanComasFdz commented Jan 24, 2020

Worked like a charm! A bit more tracing would be great tho :)

@gummi-io

This comment has been minimized.

Copy link

@gummi-io gummi-io commented Feb 15, 2020

I've updated my fork version too, it is fully interactive, no need to update file content if desired.

Here's the link if anyone needs:
https://gist.github.com/gummi-io/f3dbfebfcd5fd1fc4e42da1c0e2b41c8

:)

@aldidoanta

This comment has been minimized.

Copy link

@aldidoanta aldidoanta commented Mar 3, 2020

Worked like a charm, thank you 😁

I also created a fork to delete thread replies
https://gist.github.com/aldidoanta/240e4d71e42c83ce60f07248009c91ab

@liberty914

This comment has been minimized.

Copy link

@liberty914 liberty914 commented Mar 12, 2020

It's amazing. Thank you so much. :-)

@daruj

This comment has been minimized.

Copy link

@daruj daruj commented Apr 10, 2020

@aldidoanta I'm using your script but thread replies don't work.. is there anything I need to know/do to make it work?

@firatkucuk

This comment has been minimized.

Copy link
Owner Author

@firatkucuk firatkucuk commented Apr 10, 2020

@aldidoanta I'm using your script but thread replies don't work.. is there anything I need to know/do to make it work?

Gonna update my script for thread replies soon.

@aldidoanta

This comment has been minimized.

Copy link

@aldidoanta aldidoanta commented Apr 11, 2020

@aldidoanta I'm using your script but thread replies don't work.. is there anything I need to know/do to make it work?

Any error messages? Or it just didn't work?

EDIT It looks like the API response has changed. I'll take a look at my gist later.

@firatkucuk

This comment has been minimized.

Copy link
Owner Author

@firatkucuk firatkucuk commented Apr 26, 2020

I updated the script so you can delete thread replies as well. Thanks @Toanzzz for ES6 conversion.

@z33ma

This comment has been minimized.

Copy link

@z33ma z33ma commented May 29, 2020

this works perfectly, is there a way to stop the script once all messages have been deleted withing the script as apposed to manually stopping it

@firatkucuk

This comment has been minimized.

Copy link
Owner Author

@firatkucuk firatkucuk commented May 29, 2020

this works perfectly, is there a way to stop the script once all messages have been deleted withing the script as apposed to manually stopping it

@z33ma there should be something wrong. 'cause it automatically stops after execution.

@z33ma

This comment has been minimized.

Copy link

@z33ma z33ma commented Jun 1, 2020

this works perfectly, is there a way to stop the script once all messages have been deleted withing the script as apposed to manually stopping it

@z33ma there should be something wrong. 'cause it automatically stops after execution.

Hi yes sorry I didn't give it enough time, working correctly thank you :) quick question, is there a way to delete X amount of messages by changing (messages.length == 0) ?

@firatkucuk

This comment has been minimized.

Copy link
Owner Author

@firatkucuk firatkucuk commented Jun 1, 2020

@z33ma no it checks the queue size, it's better to use a script-wide counter for this purpose.

@misterpyrrhuloxia

This comment has been minimized.

Copy link

@misterpyrrhuloxia misterpyrrhuloxia commented Jun 9, 2020

It works like a charm!

But, for example, it will be more efficient if the code only tries to delete the own messages.
I change a bit your code and I add this. You can find your user id in any conversation, looking for the user link.

https://app.slack.com/team/USERID

if (userId == response.messages[i].user) { messages.push(response.messages[i].ts); }

Thank you for code! :D

@Ausaes, where did you put that line of code within the script? It sure is wasting a ton of time trying to delete messages that I don't have permission to delete. And I tried putting your line of code in multiple different places within the script but it threw some kind of error or another each time I ran it.

@lorvent

This comment has been minimized.

Copy link

@lorvent lorvent commented Oct 7, 2020

Hello,

after bit of struggle i am able to run the node command but it says 1602053703.000400 could not be deleted! (cant_delete_message) for each message.

@firatkucuk

This comment has been minimized.

Copy link
Owner Author

@firatkucuk firatkucuk commented Oct 7, 2020

slack recently changed url from company.slack.com to app.slack.com/xxx/ and i can't find a way to get channelId,

is there any word around?

You can right click the channel on slack and copy the link it's in the channel url.

@lorvent

This comment has been minimized.

Copy link

@lorvent lorvent commented Oct 7, 2020

Hello, yes i sorted it out,

pls check updated msg.... it is not able to delete any msg... :'(

@firatkucuk

This comment has been minimized.

Copy link
Owner Author

@firatkucuk firatkucuk commented Oct 7, 2020

Hello, yes i sorted it out,

pls check updated msg.... it is not able to delete any msg... :'(

That means you don't have permission to delete that message. If you're not "owne"r you can only delete your own messages.

@lorvent

This comment has been minimized.

Copy link

@lorvent lorvent commented Oct 7, 2020

Hello, thanks for the quick response.

  1. i have setup a incoming webhooks with gitlab notifications and i am admin of the slack group... they are not deleted.
  2. i have sent some test messages to group, which are not deleted too.

and after few hours, it ended with some thread error.

@firatkucuk

This comment has been minimized.

Copy link
Owner Author

@firatkucuk firatkucuk commented Oct 7, 2020

Hello, thanks for the quick response.

1. i have setup a incoming webhooks with gitlab notifications and i am admin of the slack group... they are not deleted.

2. i have sent some test messages to group, which are not deleted too.

and after few hours, it ended with some thread error.

so no succeeded deletion? That's weird. I just try it works fine.
Generally, error messages are definitive and one of these: https://api.slack.com/methods/chat.delete#errors

@EgoPingvina

This comment has been minimized.

Copy link

@EgoPingvina EgoPingvina commented Oct 27, 2020

image
It doesn`t work, can anyone help?

If delete shebang it give an error too:
image

@firatkucuk

This comment has been minimized.

Copy link
Owner Author

@firatkucuk firatkucuk commented Oct 27, 2020

image
It doesn`t work, can anyone help?

If delete shebang it give an error too:
image

You need to install nodejs and then node SlackClean.js ....

@EgoPingvina

This comment has been minimized.

Copy link

@EgoPingvina EgoPingvina commented Oct 27, 2020

@firatkucuk thanks, now it is working)
But it give me message 'not_in_channel' and not delete messages.

As parameter I use last part(second_code) of url: https://app.slack.com/client/first_code/second_code

@firatkucuk

This comment has been minimized.

Copy link
Owner Author

@firatkucuk firatkucuk commented Oct 27, 2020

@firatkucuk thanks, now it is working)
But it give me message 'not_in_channel' and not delete messages.

As parameter I use last part(second_code) of url: https://app.slack.com/client/first_code/second_code/

it should be channel id https://mycompany.slack.com/archives/some-code
the code at the end of url. If you have a slack destop app you can right click and copy the link if you're using browser it's obvious when you enter a channel. it's in the url

@EgoPingvina

This comment has been minimized.

Copy link

@EgoPingvina EgoPingvina commented Oct 27, 2020

I took url by right click, but it is the same code I used before. Script return me:
image

@firatkucuk

This comment has been minimized.

Copy link
Owner Author

@firatkucuk firatkucuk commented Oct 27, 2020

I took url by right click, but it is the same code I used before. Script return me:
image

seems a token issue then:
https://stackoverflow.com/a/60213585/159837

@EgoPingvina

This comment has been minimized.

Copy link

@EgoPingvina EgoPingvina commented Oct 27, 2020

Done, thanks) sorry for my stupid questions.
But now it loops on the error output
image

@EgoPingvina

This comment has been minimized.

Copy link

@EgoPingvina EgoPingvina commented Oct 27, 2020

Scopes from comment was added
image

@firatkucuk

This comment has been minimized.

Copy link
Owner Author

@firatkucuk firatkucuk commented Oct 27, 2020

Scopes from comment was added
image

If you're eligible to delete those messages manually then you should be able to delete with proper scopes in your token. That means you don't have the access. I suggest you to add some random messages to yourself and try to delete them.

@EgoPingvina

This comment has been minimized.

Copy link

@EgoPingvina EgoPingvina commented Oct 27, 2020

I originally created a new test team with three channels and two users. And yes I can delete messages manually.

@EgoPingvina

This comment has been minimized.

Copy link

@EgoPingvina EgoPingvina commented Oct 27, 2020

I found the solution!
Scopes has two groups: Bot Token Scopes and User Token Scopes.
It is needed to add this scopes to the second (User Token Scopes) group.

@kadiiskiFFW

This comment has been minimized.

Copy link

@kadiiskiFFW kadiiskiFFW commented Feb 26, 2021

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

@firatkucuk

This comment has been minimized.

Copy link
Owner Author

@firatkucuk firatkucuk commented Feb 26, 2021

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

This comment has been minimized.

Copy link

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

This comment has been minimized.

Copy link
Owner Author

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

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

image

@kadiiskiFFW

This comment has been minimized.

Copy link

@kadiiskiFFW kadiiskiFFW commented Feb 26, 2021

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

This comment has been minimized.

Copy link
Owner Author

@firatkucuk firatkucuk commented Feb 26, 2021

@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

This comment has been minimized.

Copy link

@kadiiskiFFW kadiiskiFFW commented Feb 26, 2021

@firatkucuk thank you very much for the effort!

@lorvent

This comment has been minimized.

Copy link

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

This comment has been minimized.

Copy link
Owner Author

@firatkucuk firatkucuk commented Feb 26, 2021

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

This comment has been minimized.

Copy link
Owner Author

@firatkucuk firatkucuk commented Feb 26, 2021

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

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