Last active
June 14, 2016 16:23
-
-
Save aaronzirbes/66926e65032009f9b36acdcb137dcea5 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@Grapes([ | |
@Grab(group='com.squareup.retrofit', module='retrofit', version='1.9.0') | |
]) | |
import retrofit.http.* | |
import retrofit.RestAdapter | |
import java.time.ZoneOffset | |
import java.time.LocalDateTime | |
import java.text.NumberFormat | |
class CleanSlack { | |
static final String TEAM = 'zirbes' // your Slack team | |
static final String API_TOKEN = 'YOUR_SLACK_API_TOKEN' | |
static final Integer PAGE_SIZE = 50 | |
static final LocalDateTime DELETE_THRESHOLD = LocalDateTime.now().minusDays(90) | |
SlackApi slackApi = new RestAdapter.Builder().setEndpoint('https://slack.com/api').build().create(SlackApi) | |
// static final Long MAX_SPACE = 1000000000 // 1 GB | |
static final Long MAX_SPACE = 750000000 // 750 MB | |
static void main(String ... args) { | |
CleanSlack cleaner = new CleanSlack() | |
cleaner.clean() | |
} | |
void clean() { | |
println "# Cleaning up Slack file attachments.\n" | |
Integer totalFiles | |
List<SlackFile> files = getFiles(users, channels, totalFiles) | |
Long totalSize = calculateTotalSize(files) | |
Long spaceToClean = calculateSpaceToClean(totalSize) | |
List<SlackFile> filesToRemove = findOldest(files, spaceToClean) | |
List<SlackFile> sortedBySize = files.sort { it.size * -1 } | |
Long cleanTotal = filesToRemove*.size.sum() ?: 0 | |
println "## ${filesToRemove.size()}/${totalFiles} files to remove (${format(cleanTotal)} bytes):\n" | |
filesToRemove.each { SlackFile file -> println " * ${file}" } | |
println "## ${filesToRemove.size()}/${totalFiles} files to remove (${format(cleanTotal)}/${format(totalSize)} bytes)." | |
filesToRemove.each { SlackFile file -> | |
println " ! Deleting ${file.id}, ${file}" | |
slackApi.fileDelete(API_TOKEN, file.id) | |
} | |
println "## Top 20 files remaining\n" | |
sortedBySize[0..19].each { SlackFile file -> println " * ${file}" } | |
} | |
protected Long calculateSpaceToClean(Long totalSize) { | |
Long spaceToClean = 0 | |
if (totalSize > MAX_SPACE) { spaceToClean = totalSize - MAX_SPACE } | |
println "Space to Clean up: ${format(spaceToClean)}" | |
return spaceToClean | |
} | |
protected Map<String, SlackUser> getUsers() { | |
println "## Scanning users\n" | |
UserList userList = slackApi.userList(token: API_TOKEN) | |
return userList.members.collectEntries { [ (it.id): it ] } | |
} | |
protected Map<String, SlackChannel> getChannels() { | |
println "## Scanning channels\n" | |
ChannelList channelList = slackApi.channelList(token: API_TOKEN) | |
return channelList.channels.collectEntries { [ (it.id): it ] } | |
} | |
protected Long calculateTotalSize(Collection<SlackFile> files) { | |
Long totalSize = 0 | |
println "## Files by Size:\n" | |
files.sort { it.size }.each { SlackFile file -> | |
totalSize += file.size | |
println " * ${file}" | |
} | |
println "Total Size: ${format(totalSize)}" | |
return totalSize | |
} | |
protected Collection<SlackFile> findOldest(Collection<SlackFile> files, Long spaceToClean) { | |
List<SlackFile> filesToRemove = [] | |
Long deleted = 0 | |
List<SlackFile> sortedByDate = files.sort { it.created * -1 } | |
sortedByDate.each { SlackFile file -> | |
if (deleted < spaceToClean && file.createdDate < DELETE_THRESHOLD) { | |
filesToRemove << file | |
deleted += file.size | |
} | |
} | |
return filesToRemove.sort { it.created } | |
} | |
protected Collection<SlackFile> getFiles(def users, def channels, Integer totalFiles) { | |
println "## Scanning all files for details\n" | |
Integer page = 0 | |
Integer pages = null | |
List<SlackFile> files = [] | |
Integer fileNo = 0 | |
while (!pages || page < pages) { | |
Map<String, String> params = [ token: API_TOKEN, count: PAGE_SIZE, page: page ] | |
FileList resp = slackApi.fileList(params) | |
pages = resp.paging.pages | |
totalFiles = resp.paging.total | |
resp.files.each { SlackFile file -> | |
fileNo++ | |
file.username = users[file.user].name | |
file.channels = file.channels.collect{ channels[it].name } | |
files << file | |
println " * Scanning [pg${page}/${pages}, ${fileNo}/${totalFiles}] ${file}" | |
} | |
page++ | |
} | |
return files | |
} | |
protected String format(Long number) { | |
NumberFormat.getInstance().format(number) | |
} | |
} | |
class SlackResponse { | |
Boolean ok | |
String warning | |
String error | |
} | |
class FileList extends SlackResponse { | |
Paging paging | |
List<SlackFile> files | |
} | |
class UserList extends SlackResponse { | |
List<SlackUser> members | |
} | |
class ChannelList extends SlackResponse { | |
List<SlackChannel> channels | |
} | |
class Paging { | |
Integer count | |
Integer total | |
Integer page | |
Integer pages | |
} | |
class SlackUser { | |
String id | |
String name | |
} | |
class SlackChannel { | |
String id | |
String name | |
Boolean is_archived | |
} | |
class SlackFile { | |
String id | |
Integer created | |
Integer timestamp | |
LocalDateTime getCreatedDate() { | |
LocalDateTime.ofEpochSecond(created, 0, ZoneOffset.UTC) | |
} | |
LocalDateTime getTimestampDate() { | |
LocalDateTime.ofEpochSecond(timestamp, 0, ZoneOffset.UTC) | |
} | |
String name | |
String title | |
String mimetype | |
String filetype | |
String pretty_type | |
String user | |
String mode | |
Boolean editable | |
Boolean is_external | |
String external_type | |
String username | |
Long size | |
URL url_private | |
URL url_private_download | |
URL permalink | |
URL permalink_public | |
URL edit_link | |
String preview | |
String preview_highlight | |
Integer lines | |
Integer lines_more | |
Boolean is_public | |
Boolean public_url_shared | |
Boolean display_as_bot | |
List<String> channels | |
List<String> groups | |
List<String> ims | |
Integer num_stars | |
Boolean is_starred | |
List<String> pinned_to | |
Integer comments_count | |
String getSizeString() { | |
NumberFormat.getInstance().format(size) | |
} | |
String getChannel() { | |
if (channels) { | |
return "#${channels[0]}" | |
} | |
return null | |
} | |
String toString() { | |
"${name} (${sizeString} bytes) - Uploaded ${createdDate} by @${username} ${channel ? 'in ' : ''}${channel}" | |
} | |
} | |
protected interface SlackApi { | |
@Headers(['Accept: application/json']) | |
@GET('/channels.list') | |
ChannelList channelList(@QueryMap Map<String, String> params) | |
@Headers(['Accept: application/json']) | |
@GET('/files.list') | |
FileList fileList(@QueryMap Map<String, String> params) | |
@Headers(['Accept: application/json']) | |
@GET('/users.list') | |
UserList userList(@QueryMap Map<String, String> params) | |
@Headers(['Accept: application/json']) | |
@DELETE('/files.delete') | |
SlackResponse fileDelete(@Query('token') String token, @Query('file') String id) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment