-
-
Save jackcarter/d86808449f0d95060a40 to your computer and use it in GitHub Desktop.
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) |
And the PHP version
Refactored the code to make it easier to use (fork)
Usage: python slack_delete.py --token <token here>
This doesn't work on python 3.5 does it? I keep getting module/library issues.
@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)
Does this delete all file types? if so is there a way to only have it delete images?
@something915 This delete all file types. So far there is not filter but it can be implemented.
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
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.
Is it appropriate to ask how this code works? If so, how does it work? If not, thank you for the amazing code!
Good job @jackcarter
does this work on files that might be in Private channels?
Can I put tokens from multiple users?
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.
Thanks! @jackcarter
awesome work. thanks 👍
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.
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.
@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
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.
@maltose1117 I got the same error. I keep executing the code (python3 slack_delete.py) until it's all deleted.
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.
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.
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.
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()
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?
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.
As of today, legacy tester tokens may no longer be created.
However, you can still use this script following this steps:
-
Create a Slack app on https://api.slack.com/apps?new_app=1 and assign that to your workspace.
-
Then within this new app, go to OAuth & Permissions and in the section Scopes > User Token Scopes add the
files:read
andfiles:write
Oauth scopes. -
Finally, you can now go to the top of the page and install the app to the workspace. After that you'll get the User OAuth Token that you can use on the script.
* Bare in mind that if you forget to add the scopes, or you need to modify them after you install the app to the workspace, you would need to reinstall it after changing the scopes.
I have also adapted the code to work with the Oauth authentication.
https://gist.github.com/IlanVivanco/d2a96abb364ccb3b59e198f1c5fdf673
@IlanVivanco good job! saved my time.
None of the scripts listed here works anymore for me. Something changed @slack?
I've modified the script a bit to avoid external dependencies: