Skip to content

Instantly share code, notes, and snippets.

@0b5vr
Last active March 3, 2023 15:08
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save 0b5vr/070a33c7971300a3f194ed1b3af7de0a to your computer and use it in GitHub Desktop.
Save 0b5vr/070a33c7971300a3f194ed1b3af7de0a to your computer and use it in GitHub Desktop.
A deno script that downloads all files in the Slack export archive
/**
* A deno script that downloads all files in the Slack export archive
* 2023-03-03
*
* Copyright (c) 2023 0b5vr
* SPDX-License-Identifier: MIT
*
* Usage:
* - Install deno - https://deno.land/
* - `deno run --allow-read --allow-write --allow-net download.ts <export>`
*
* Ref: https://gist.github.com/marnitto/f9970c2bbf2798782c06bc2b9d58c0cc
*/
import * as fs from "https://deno.land/std@0.178.0/fs/mod.ts";
import * as path from "https://deno.land/std@0.178.0/path/mod.ts";
async function downloadFile(
fileEntity: Record<string, unknown>,
downloadDir: string,
) {
const name = fileEntity["name"] as string | undefined;
if (name == null) {
console.log("🤔 Skipping a file - The file entry does not have a name");
return;
}
const id = fileEntity["id"] as string | undefined;
if (id == null) {
console.log(`🤔 Skipping ${name} - The file entry does not have an id`);
return;
}
const url = fileEntity["url_private_download"] as string | undefined;
if (url == null) {
console.log(`🤔 Skipping ${name} - The file entry does not have an URL`);
return;
}
console.log(`📥 Downloading ${name}`);
fs.ensureDir(downloadDir);
const filepath = path.join(downloadDir, `${id}_${name}`);
const res = await fetch(url);
const dst = await Deno.open(filepath, { write: true, create: true });
await res.body?.pipeTo(dst.writable);
}
const ignoreJsonList = new Set([
"channels.json",
"integration_logs.json",
"users.json",
]);
async function main(): Promise<void> {
const exportDir = Deno.args[0];
if (exportDir == null) {
throw new Error("Please specify an exported directory!");
}
const exportDirStat = await Deno.stat(exportDir);
if (!exportDirStat.isDirectory) {
throw new Error("The given path is not a directory");
}
const diaryEntryIter = fs.expandGlob(path.join(exportDir, "**/*.json"));
for await (const diaryEntry of diaryEntryIter) {
if (ignoreJsonList.has(diaryEntry.name)) {
console.log(`⏩ Skipping ${diaryEntry.name}`);
continue;
}
const diary = JSON.parse(await Deno.readTextFile(diaryEntry.path));
if (!Array.isArray(diary)) {
console.log(`🤔 Skipping ${diaryEntry.name} - The JSON is not an array`);
continue;
}
const diaryPathSplit = diaryEntry.path.split("\\");
const date = diaryPathSplit[diaryPathSplit.length - 1].split(".")[0];
const channel = diaryPathSplit[diaryPathSplit.length - 2];
const downloadDir = path.join(exportDir, channel, date);
console.log(`👀 Processing ${downloadDir}`);
for (const messages of diary as Record<string, unknown>[]) {
const file = messages["file"] as Record<string, unknown> | undefined;
if (file != null) {
await downloadFile(file, downloadDir);
}
const files = messages["files"] as Record<string, unknown>[] | undefined;
if (files != null) {
for (const file of files) {
await downloadFile(file, downloadDir);
}
}
}
}
console.log("✅ Completed");
}
main();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment