Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Delete Slack files older than 30 days. Rewrite of https://gist.github.com/jamescmartinez/909401b19c0f779fc9c1
import requests
import time
import json
token = ''
#Delete files older than this:
ts_to = int(time.time()) - 30 * 24 * 60 * 60
def list_files():
params = {
'token': token
,'ts_to': ts_to
,'count': 1000
}
uri = 'https://slack.com/api/files.list'
response = requests.get(uri, params=params)
return json.loads(response.text)['files']
def delete_files(file_ids):
count = 0
num_files = len(file_ids)
for file_id in file_ids:
count = count + 1
params = {
'token': token
,'file': file_id
}
uri = 'https://slack.com/api/files.delete'
response = requests.get(uri, params=params)
print count, "of", num_files, "-", file_id, json.loads(response.text)['ok']
files = list_files()
file_ids = [f['id'] for f in files]
delete_files(file_ids)
@edwintyr

This comment has been minimized.

Copy link

edwintyr commented Mar 9, 2016

Hi Jack, sorry noob here. How do I run this?

@jackcarter

This comment has been minimized.

Copy link
Owner Author

jackcarter commented Mar 14, 2016

Here's a guide to running python code on your PC: http://stackoverflow.com/a/1527012/662761

You need to use your own Slack token by editing this line: token = ''. Put your slack token in between the 's. You can find your token here: https://api.slack.com/docs/oauth-test-tokens

@peterramsing

This comment has been minimized.

Copy link

peterramsing commented Mar 25, 2016

Thanks @jackcarter

@DrOverdose

This comment has been minimized.

Copy link

DrOverdose commented Apr 12, 2016

getting an error trying to run this after adding my slack api code.

$ python3 slack_delete.py
File "slack_delete.py", line 31
print count, "of", num_files, "-", file_id, json.loads(response.text)['ok']
^
SyntaxError: invalid syntax

most likely missing something silly any thoughts?

@jackcarter

This comment has been minimized.

Copy link
Owner Author

jackcarter commented Apr 14, 2016

@DrOverdose You're probably running Python 3 instead of Python 2. Try changing that line to print(count, "of", num_files, "-", file_id, json.loads(response.text)['ok'])

@katspaugh

This comment has been minimized.

Copy link

katspaugh commented Apr 18, 2016

I've modified the script a bit to avoid external dependencies:

import urllib
import urllib2
import time
import json

token = ''

#Delete files older than this:
days = 30
ts_to = int(time.time()) - days * 24 * 60 * 60

def list_files():
  params = {
    'token': token,
    'ts_to': ts_to,
    'count': 1000,
  }
  uri = 'https://slack.com/api/files.list'
  response = urllib2.urlopen(uri + '?' + urllib.urlencode(params)).read()
  return json.loads(response)['files']

def delete_files(file_ids):
  count = 0
  num_files = len(file_ids)
  for file_id in file_ids:
    count = count + 1
    params = {
      'token': token
      ,'file': file_id
      }
    uri = 'https://slack.com/api/files.delete'
    response = urllib2.urlopen(uri + '?' + urllib.urlencode(params)).read()
    print count, "of", num_files, "-", file_id, json.loads(response)['ok']

files = list_files()
file_ids = [f['id'] for f in files]
delete_files(file_ids)
@lyrixx

This comment has been minimized.

Copy link

lyrixx commented Aug 2, 2016

And the PHP version

@Paradoxis

This comment has been minimized.

Copy link

Paradoxis commented Aug 3, 2016

Refactored the code to make it easier to use (fork)
Usage: python slack_delete.py --token <token here>

@kkreezy

This comment has been minimized.

Copy link

kkreezy commented Nov 5, 2016

This doesn't work on python 3.5 does it? I keep getting module/library issues.

@cosmonauta

This comment has been minimized.

Copy link

cosmonauta commented Dec 26, 2016

@kkreezy @DrOverdose I've modified the script to python3

from urllib.parse import urlencode
from urllib.request import urlopen
import time
import json
import codecs

reader = codecs.getreader("utf-8")

token = ''

#Delete files older than this:
days = 30
ts_to = int(time.time()) - days * 24 * 60 * 60

def list_files():
  params = {
    'token': token,
    'ts_to': ts_to,
    'count': 1000,
  }
  uri = 'https://slack.com/api/files.list'
  response = reader(urlopen(uri + '?' + urlencode(params)))
  return json.load(response)['files']

def delete_files(file_ids):
  count = 0
  num_files = len(file_ids)
  for file_id in file_ids:
    count = count + 1
    params = {
      'token': token
      ,'file': file_id
      }
    uri = 'https://slack.com/api/files.delete'
    response = reader(urlopen(uri + '?' + urlencode(params)))
    print(count, "of", num_files, "-", file_id, json.load(response)['ok'])

files = list_files()
file_ids = [f['id'] for f in files]
delete_files(file_ids)
@something915

This comment has been minimized.

Copy link

something915 commented Feb 28, 2017

Does this delete all file types? if so is there a way to only have it delete images?

@darieldejesus

This comment has been minimized.

Copy link

darieldejesus commented Mar 24, 2017

@something915 This delete all file types. So far there is not filter but it can be implemented.

@mcgoughp0870

This comment has been minimized.

Copy link

mcgoughp0870 commented Mar 27, 2017

So I am not a computer person at all - is there an easy way that i can implement this into slack? I have no idea what i am doing

@DanielMT57

This comment has been minimized.

Copy link

DanielMT57 commented Mar 30, 2017

Hey!, we had some problem at work with our files, so we were told to delete them, i had a lot so i had to find something, and google took me here. Thank you very much, it worked like a charm.

@Siludorf

This comment has been minimized.

Copy link

Siludorf commented Apr 28, 2017

Is it appropriate to ask how this code works? If so, how does it work? If not, thank you for the amazing code!

@thamaraiselvam

This comment has been minimized.

Copy link

thamaraiselvam commented May 10, 2017

Good job @jackcarter

@savage4618

This comment has been minimized.

Copy link

savage4618 commented Jun 15, 2017

does this work on files that might be in Private channels?

@henryngo

This comment has been minimized.

Copy link

henryngo commented Aug 16, 2017

Can I put tokens from multiple users?

@henry-p

This comment has been minimized.

Copy link

henry-p commented Nov 30, 2017

@cosmonauta

I extended the script to be filterable by file size and filetype. I also added a info method that returns the most relevant info and refactored the code a little bit :)

from urllib.parse import urlencode
from urllib.request import urlopen
import time
import json
import codecs
import datetime
from collections import OrderedDict

reader = codecs.getreader("utf-8")

# Obtain here: https://api.slack.com/custom-integrations/legacy-tokens
token = ''

# Params for file listing. More info here: https://api.slack.com/methods/files.list

# Delete files older than this:
days = 30
ts_to = int(time.time()) - days * 24 * 60 * 60

# How many? (Maximum is 1000, otherwise it defaults to 100)
count = 1000

# Types?
types = 'all'
# types = 'spaces,snippets,images,gdocs,zips,pdfs'
# types = 'zips'


def list_files():
    params = {
        'token': token,
        'ts_to': ts_to,
        'count': count,
        'types': types
    }
    uri = 'https://slack.com/api/files.list'
    response = reader(urlopen(uri + '?' + urlencode(params)))
    return json.load(response)['files']


def filter_by_size(files, mb, greater_or_smaller):
    if greater_or_smaller == 'greater':
        return [file for file in files if (file['size'] / 1000000) > mb]
    elif greater_or_smaller == 'smaller':
        return [file for file in files if (file['size'] / 1000000) < mb]
    else:
        return None


def info(file):
    order = ['Title', 'Name', 'Created', 'Size', 'Filetype',
             'Comment', 'Permalink', 'Download', 'User', 'Channels']
    info = {
        'Title': file['title'],
        'Name': file['name'],
        'Created': datetime.datetime.utcfromtimestamp(file['created']).strftime('%B %d, %Y %H:%M:%S'),
        'Size': str(file['size'] / 1000000) + ' MB',
        'Filetype': file['filetype'],
        'Comment': file['initial_comment'] if 'initial_comment' in file else '',
        'Permalink': file['permalink'],
        'Download': file['url_private'],
        'User': file['user'],
        'Channels': file['channels']
    }
    return OrderedDict((key, info[key]) for key in order)


def file_ids(files):
    return [f['id'] for f in files]


def delete_files(file_ids):
    num_files = len(file_ids)
    for index, file_id in enumerate(file_ids):
        params = {
            'token': token,
            'file': file_id
        }
        uri = 'https://slack.com/api/files.delete'
        response = reader(urlopen(uri + '?' + urlencode(params)))
        print((index + 1, "of", num_files, "-",
               file_id, json.load(response)['ok']))

files = list_files()
files_by_size = filter_by_size(files, 50, 'greater')
print(len(files_by_size))
[info(file) for file in files_by_size]
file_ids = file_ids(files_by_size)
# delete_files(file_ids) # Commented out, so you don't accidentally run this.
@MattHofmann

This comment has been minimized.

Copy link

MattHofmann commented Jan 16, 2018

Thanks! @jackcarter

@WopKatan

This comment has been minimized.

Copy link

WopKatan commented Jan 17, 2018

awesome work. thanks 👍

@agaltsoff

This comment has been minimized.

Copy link

agaltsoff commented Apr 18, 2018

Added an option to retrieve files by slack member id. Handy 'cause if not admin you retrieve all files but can delete only yours.

Plus some refactoring as well.

from urllib.parse import urlencode
from urllib.request import urlopen
import time
import json
import codecs
import datetime
from collections import OrderedDict

reader = codecs.getreader("utf-8")

# Obtain here: https://api.slack.com/custom-integrations/legacy-tokens
token = '' 

# Set it to delete only this user's files. Handy if you are not admin.
member_id= ''

# Params for file listing. More info here: https://api.slack.com/methods/files.list

# Delete files older than this:
days = 30
ts_to = int(time.time()) - days * 24 * 60 * 60

# How many? (Maximum is 1000, otherwise it defaults to 100)
count = 1000

# Types?
types = 'all'
# types = 'spaces,snippets,images,gdocs,zips,pdfs'
# types = 'zips'

def list_files(user= ''):
    params = {
        'token': token,
        'ts_to': ts_to,
        'count': count,
        'types': types,
        'user': user,
    }
    uri = 'https://slack.com/api/files.list'
    response = reader(urlopen(uri + '?' + urlencode(params)))
    return json.load(response)['files']

def greater_mb(file, mb):
    return file['size'] / 1000000 >= mb

def smaller_mb(file, mb):
    return file['size'] / 1000000 < mb

def filter_by_size(files, greater_or_smaller, mb):
    return [file for file in files if greater_or_smaller(file, mb)]

def info(file):
    order = ['Title', 'Name', 'Created', 'Size', 'Filetype',
             'Comment', 'Permalink', 'Download', 'User', 'Channels']
    info = {
        'Title': file['title'],
        'Name': file['name'],
        'Created': datetime.datetime.utcfromtimestamp(file['created']).strftime('%B %d, %Y %H:%M:%S'),
        'Size': str(file['size'] / 1000000) + ' MB',
        'Filetype': file['filetype'],
        'Comment': file['initial_comment'] if 'initial_comment' in file else '',
        'Permalink': file['permalink'],
        'Download': file['url_private'],
        'User': file['user'],
        'Channels': file['channels']
    }
    return OrderedDict((key, info[key]) for key in order)

def delete_files(files):
    num_files = len(files)
    file_ids = map(lambda f: f['id'], files)
    print('Deleting %i files'%num_files)
    for index, file_id in enumerate(file_ids):
        params = {
            'token': token,
            'file': file_id
        }
        uri = 'https://slack.com/api/files.delete'
        response = reader(urlopen(uri + '?' + urlencode(params)))
        print((index + 1, "of", num_files, "-",
               file_id, json.load(response)['ok']))

print('Retrieving files older than %s days'%(days))			   
			   
files = list_files(member_id)

print('Total %i files'%len(files))

files = filter_by_size(files, greater_mb, 50)

print('Match size %i files'%len(files))

#delete_files(files) # Commented out, so you don't accidentally run this.
@jamiesphinx

This comment has been minimized.

Copy link

jamiesphinx commented Apr 29, 2018

Thank you @jackcarter. This works like a charm! I have been running https://github.com/PalmBeachPost/SlackPruner until for some reason, the script exits after deleting just a couple of files from each user.
Query: how can I delete files of multiple users? I am admin on Slack and have their tokens.

@alpinstang

This comment has been minimized.

Copy link

alpinstang commented May 14, 2018

@jamiesphinx If you are an admin and use a legacy API key from Slack you will be able to delete all users files. https://api.slack.com/custom-integrations/legacy-tokens

@maltose1117

This comment has been minimized.

Copy link

maltose1117 commented Dec 20, 2018

Thanks @jackcarter . However, after execute the code, it shows:
1 of 510 - F1KE8KE9H False
2 of 510 - F1KFF9QFQ False
3 of 510 - F1KFF9QJ2 False
.
.
and so on. After running the code, I checked my Slack files. Many of them as expected are not deleted. May I ask what is going on here? Thank you.

@pravashkarki

This comment has been minimized.

Copy link

pravashkarki commented Jan 22, 2019

@maltose1117 I got the same error. I keep executing the code (python3 slack_delete.py) until it's all deleted.

@ZachBania

This comment has been minimized.

Copy link

ZachBania commented Feb 4, 2019

Hey @alpinstang - @pravashkarki, I had the same problem.
It's because the slack API is requesting for all files within a private group. If your not admin, the request fails due to authorization.
Check out @agaltsoff's script above in the comments, worked for me.

@amcguireweb

This comment has been minimized.

Copy link

amcguireweb commented Feb 6, 2019

I just get a bunch of exceptions when attempting to use @agaltsoff's script above. Admittedly I really have no idea what I'm doing, but the original script runs for me. Just not deleting all the files.

@JeffHanna

This comment has been minimized.

Copy link

JeffHanna commented Feb 20, 2019

For all of this refactoring why does everyone keep the uri = 'https://slack.com/api/files.delete' line under for loop? Its better placed outside of the loop so Python doesn't keep allocating memory for the same static string.

@jindov

This comment has been minimized.

Copy link

jindov commented Mar 4, 2019

I update this script because I want to delete file by id, this will handle case can't not delete because the file is not belong to another user and workaround ratelimit of Slack's API. With some default value as count=1000. You have to get a list of id, token, username to fill in the users list. Run with command python slack_delete.py --days 30. Note python 2.7

import argparse
import requests
import time
import json
requests.packages.urllib3.disable_warnings()

users = [{"name": "myName", "user":"UDPKXJ8JU", "token": "xoxp-xxxxxxxxxx"}]

def main():
    """
    Entry point of the application
    :return: void
    """
    parser = argparse.ArgumentParser()
    parser.add_argument("-d", "--days", type=int, default=None, help="Delete files older than x days (optional)")
    options = parser.parse_args()

    try:
        print "[*] Fetching file list.."
        file_ids = list_file_ids(days=options.days)
    except KeyboardInterrupt:
        print "\b\b[-] Aborted"
        exit(1)

def calculate_days(days):
    """
    Calculate days to unix time
    :param days: int
    :return: int
    """
    return int(time.time()) - days * 24 * 60 * 60

def list_file_ids(days=None):
    for user in users:
        user_name = user['name']
        print "delete files of user: "+user_name
        user_id = user['user']
        user_token = user['token']
        if days:
            params = {'token': user_token, 'count': 1000, 'ts_to': calculate_days(days), 'user': user_id}
            print "ts_to: ", calculate_days(days)
        else:
            params = {'token': user_token, 'count': 1000, 'user': user_id}
        uri = 'https://slack.com/api/files.list'
        response = requests.get(uri, params=params).json()
        files = response['files']
        #return [f['id'] for f in files]
        print [f['id'] for f in files]
        for f in files:
            dresponse = json.loads(requests.get('https://slack.com/api/files.delete', params={'token': user_token, 'file': f['id']}).text)
            time.sleep(2)
            if dresponse["ok"]:
                print "[+] Deleted: ", f['id']
            else:
                print "[!] Unable to delete: ", f['id'] + ", reason:", dresponse["error"]

if __name__ == '__main__':
    main()
@Redsandro

This comment has been minimized.

Copy link

Redsandro commented May 17, 2019

api.slack.com is a maze. I've tried so many tokens. Where exactly do I get the proper token? Or this user and token combination?

@Bears-Beets-BSG

This comment has been minimized.

Copy link

Bears-Beets-BSG commented Aug 27, 2019

I'm not a programmer/coder, but I love tinkering with code to get stuff done more efficiently. Apologies in advance if anything below is wrong/misleading. I hope it helps others running into this problem.

We have long ago reached and passed the storage limit of our free Slack plan (>22GB). Naturally, older files were "tombstoned" or archived automatically once our storage usage passed 5.0GB. I've been trying to figure how to delete the tombstoned files using this script or its variations above (thank you for all the work, by the way!) to no avail. It seems I missed a little section in the Slack API documentation:

In order to gather information on tombstoned files in Free workspaces, so that you can delete or revoke them, pass the show_files_hidden_by_limit parameter. While the yielded files will still be redacted, you'll gain the id of the files so that you can delete or revoke them.

In @jackcarter's original code at the top, which worked fine for me except for the "hidden" files, I added the missing parameter as below. (I also used a workspace owner legacy token.)

def list_files():
   params = {
    'token': token,  
    'ts_to': ts_to,  
    'count': 1000,  
    'show_files_hidden_by_limit': 1,  
}

It seems to have worked. I've deleted thousands more files older than X days. Slack analytics also showed a significant decrease in storage usage. Unfortunately, and as confirmed with Slack Support, (with a Free plan) we are unable to see or delete files shared by users in private channels or direct messages, even through the API.

Anyway, hope this still helps someone.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.