Skip to content

Instantly share code, notes, and snippets.

@wojtek1150
Last active March 13, 2024 13:26
Show Gist options
  • Save wojtek1150/fd26be751ed1eefdf94dcc92a1e81fd7 to your computer and use it in GitHub Desktop.
Save wojtek1150/fd26be751ed1eefdf94dcc92a1e81fd7 to your computer and use it in GitHub Desktop.
Delete all slack messages
#!/usr/bin/env node
// Channel ID is on the the browser URL.: https://app.slack.com/client/COMPANY_ID/CHANNEL_ID
// User id in on the browser URL.: https://app.slack.com/client/COMPANY_ID/CHANNEL_ID/user_profile/USER_ID
// Token can get from: https://api.slack.com/custom-integrations/legacy-tokens
// Pass it as a parameter: node ./bulk-delete-slack.js TOKEN CHANNEL_ID USER_ID DELAY
// Or use it without download the script: curl -Ls RAW_GIST_URL | node - TOKEN CHANNEL_ID
// GLOBALS #############################################################################################################
const [, , token, userId, channel, delay = 400] = process.argv
if (!token || !channel) {
console.log('Usage: node ./bulk-delete-slack.js TOKEN CHANNEL_ID')
console.log('Usage: curl -Ls RAW_GIST_URL | node - TOKEN CHANNEL_ID')
process.exit(1)
}
const https = require('https')
const baseApiUrl = 'https://slack.com/api'
const historyApiUrl = `${baseApiUrl}/conversations.history?token=${token}&count=1000&channel=${channel}&cursor=`
const deleteApiUrl = `${baseApiUrl}/chat.delete?token=${token}&channel=${channel}&ts=`
// ---------------------------------------------------------------------------------------------------------------------
const sleep = ms => new Promise(r => setTimeout(r, +ms))
const getJsonAsync = url =>
new Promise((resolve, reject) =>
https
.get(url, res => {
let body = ''
res.on('data', chunk => (body += chunk))
res.on('end', () => resolve(JSON.parse(body)))
})
.on('error', reject)
)
async function deleteMessage(messages) {
console.log(`Deleting ${messages.length} messages`)
while (messages.length > 0) {
const ts = messages.shift()
const response = await getJsonAsync(deleteApiUrl + ts)
if (response.ok === true) {
console.log(ts + ' deleted!')
} else if (response.ok === false) {
console.log(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(ts)
}
}
await sleep(delay)
}
}
// ---------------------------------------------------------------------------------------------------------------------
async function processHistory() {
let nextCursor = ''
while (true) {
const {ok, error, messages, has_more, response_metadata} = await getJsonAsync(historyApiUrl + nextCursor)
if (!ok) throw new Error(error)
if (Array.isArray(messages)) {
if (userId) {
console.log('Deleting only for: ', userId)
await deleteMessage(messages.filter(z => z.user === userId).map(z => z.ts))
} else {
console.log('Deleting all ')
await deleteMessage(messages.map(z => z.ts))
}
} else {
console.log('No message found')
}
if (!has_more) break
nextCursor = response_metadata.next_cursor
}
}
// ---------------------------------------------------------------------------------------------------------------------
processHistory().catch(console.log)
@baj84
Copy link

baj84 commented Aug 9, 2022

I couldn't get this working :/

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