fs#createWriteStream TypeScript Examples

The following examples show how to use fs#createWriteStream. You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: utils.ts    From cloudmusic-vscode with MIT License 7 votes vote down vote up
export function downloadMusic(
  url: string,
  path: string,
  cache?: { id: string; name: string; path: string; md5?: string }
) {
  if (cache) cache.path = path;
  const download = getMusic(url, cache);
  if (!download) return;
  const file = createWriteStream(path);
  download.pipe(file);
}
Example #2
Source File: replay.tracker.ts    From diablo2 with MIT License 6 votes vote down vote up
write(obj: unknown): void {
    if (this._streamClose != null) clearTimeout(this._streamClose);

    if (this._stream == null) {
      this._stream = createWriteStream(this.fileName, { flags: 'a' });
    }
    this._streamClose = setTimeout(this.closeStream, 5 * 1000);
    this._stream.write(JSON.stringify(obj) + '\n');
  }
Example #3
Source File: fileGenerator.ts    From tx2uml with MIT License 6 votes vote down vote up
generateFile = async (
    pumlStream: Readable,
    options: OutputOptions = {}
) => {
    const filename = constructFilename(options.filename, options.format)
    try {
        const outputStream = createWriteStream(filename)
        if (options.format === "puml") {
            pumlStream.pipe(outputStream)
            debug(`Plant UML file written to ${filename} in raw puml format`)
        } else if (outputFormats.includes(options.format)) {
            await streamPlantUml(pumlStream, outputStream, {
                format: options.format,
                limitSize: 60000,
            })
            debug(
                `Plant UML file written to ${filename} in ${options.format} format.`
            )
        } else {
            throw new Error(
                `Output format ${options.format} is not supported. Only the following formats are supported: ${outputFormats}.`
            )
        }
    } catch (err) {
        throw new VError(err, `Failed to write to file ${filename}.`)
    }
}
Example #4
Source File: file-manager.ts    From open-design-sdk with Apache License 2.0 6 votes vote down vote up
async saveFileStream(
    filePath: string,
    fileStream: NodeJS.ReadableStream,
    options: {
      cancelToken?: CancelToken | null
    } = {}
  ): Promise<void> {
    const cancelToken = createCancelToken.race([
      options.cancelToken,
      this._destroyTokenController.token,
    ])

    const filename = this._resolvePath(filePath)
    const writeStream = createWriteStream(filename)

    return new Promise((resolve, reject) => {
      const unregisterCanceller = cancelToken?.onCancelled(
        (reason: unknown) => {
          writeStream.close()
          reject(reason)
        }
      )
      const handleError = (err: Error) => {
        unregisterCanceller?.()
        reject(err)
      }
      const handleClose = () => {
        unregisterCanceller?.()
        resolve()
      }

      fileStream.once('error', handleError)
      writeStream.once('close', handleClose)
      writeStream.once('error', handleError)

      fileStream.pipe(writeStream)
    })
  }
Example #5
Source File: fs.ts    From radiopanel with GNU General Public License v3.0 6 votes vote down vote up
public async put(path: string, input: NodeJS.ReadableStream | Buffer): Promise<void> {
		if (input instanceof Buffer) {
			return fs.writeFile(join(__dirname, '../../../../../', 'uploads', path), input)
		}

		const destination = createWriteStream(join(__dirname, '../../../../../', 'uploads', path));
		input.pipe(destination);
		return;
	}
Example #6
Source File: typeorm-uml.class.ts    From typeorm-uml with MIT License 6 votes vote down vote up
/**
	 * Downloads image into a file.
	 *
	 * @private
	 * @param {string} url The URL to download.
	 * @param {string} filename The output filename.
	 * @returns {Promise} A promise object.
	 */
	private download( url: string, filename: string ): Promise<void> {
		return new Promise( ( resolve ) => {
			get( url, ( response ) => {
				response.pipe( createWriteStream( this.getPath( filename ) ) );
				response.on( 'end', resolve );
			} );
		} );
	}
Example #7
Source File: subscribe.ts    From swarm-cli with BSD 3-Clause "New" or "Revised" License 6 votes vote down vote up
public async run(): Promise<void> {
    await super.init()

    this.console.log('Subscribing for PSS messages on topic ' + this.topic)

    const stream = this.outFile ? createWriteStream(this.outFile) : null

    this.bee.pssSubscribe(this.topic, {
      onMessage: data => {
        if (stream) {
          stream.write(data)
        } else {
          this.console.log(data.text())
          this.console.quiet(data.text())
        }
      },
      onError: (error: BeeError) => {
        this.console.error(error.message)
      },
    })
  }
Example #8
Source File: receive.ts    From swarm-cli with BSD 3-Clause "New" or "Revised" License 6 votes vote down vote up
public async run(): Promise<void> {
    await super.init()

    this.console.log('Waiting for one PSS message on topic ' + this.topic)

    const stream = this.outFile ? createWriteStream(this.outFile, { encoding: 'binary' }) : null

    try {
      const data = await this.bee.pssReceive(this.topic, this.timeout)

      this.receivedMessage = data.text()

      if (stream) {
        stream.write(data)
      } else {
        // TODO: utf-8 decoding may fail, text is probably not the best choice here
        this.console.log(this.receivedMessage)
        this.console.quiet(this.receivedMessage)
      }
    } catch (error) {
      if (getFieldOrNull(error, 'message') === 'pssReceive timeout') {
        this.console.error('Receive timed out')
      } else {
        this.console.printBeeError(error)
      }
    }
  }
Example #9
Source File: stream-example.ts    From node-duckdb with MIT License 6 votes vote down vote up
async function outputToFileAsCsv() {
    // create new database in memory
    const db = new DuckDB();
    // create a new connection to the database
    // note you can execute only one streaming query at a time per one connection   
    const connection = new Connection(db);

    // by default a query is a streaming one, as opposed to being materialized
    await connection.execute("CREATE TABLE people(id INTEGER, name VARCHAR);");
    await connection.execute("INSERT INTO people VALUES (1, 'Mark'), (2, 'Hannes'), (3, 'Bob');");

    // result is a stream of arrays
    const resultStream = await connection.execute("SELECT * FROM people;", {rowResultFormat: RowResultFormat.Array});

    const transformToCsvStream = new ArrayToCsvTransform();
    const writeStream = createWriteStream("my-people-output");
    // objects -> csv strings -> file
    resultStream.pipe(transformToCsvStream).pipe(writeStream);
}
Example #10
Source File: calibrate.ts    From Assistive-Webdriver with MIT License 6 votes vote down vote up
/**
   * Saves the {@link CalibrationError.screenshot | screenshot} as a file.
   * @param fileName - Full path (or path relative to the current directory) where to store the screenshot.
   * The screenshot will be stored in the PNG format.
   */
  saveScreenshot(fileName: string): Promise<void> {
    return new Promise((resolve, reject) =>
      pipeline(this.screenshot.pack(), createWriteStream(fileName), err =>
        err ? reject(err) : resolve()
      )
    );
  }
Example #11
Source File: project.ts    From cli with Apache License 2.0 6 votes vote down vote up
public async compressResources() {
    let cachedManifest;
    try {
      this.ensureProjectCompliance();
      cachedManifest = readJsonSync(this.resourceManifestPath, {
        throws: false,
      });
      rmSync(this.resourceManifestPath, {force: true});
      await new Promise<void>((resolve, reject) => {
        const outputStream = createWriteStream(this.temporaryZipPath);
        const archive = archiver('zip');

        outputStream.on('close', () => resolve());
        archive.on('error', (err) => reject(err));

        archive.pipe(outputStream);
        archive.directory(this.resourcePath, false);
        archive.finalize();
      });
      if (cachedManifest) {
        writeJsonSync(this.resourceManifestPath, cachedManifest);
      }
      return this.temporaryZipPath;
    } catch (error) {
      if (cachedManifest) {
        writeJsonSync(this.resourceManifestPath, cachedManifest);
      }
      CliUx.ux.error(error as string | Error);
    }
  }
Example #12
Source File: install.ts    From blake3 with MIT License 6 votes vote down vote up
async function download(url: string): Promise<boolean> {
  return new Promise<boolean>(resolve => {
    const onError = (err: Error) => {
      console.error(`Could not download binding from ${url}: ${err.stack || err.message}`);
      resolve(false);
    };

    const req = get(url, res => {
      if (res.headers.location) {
        resolve(download(res.headers.location));
        return;
      }

      if (!res.statusCode || res.statusCode >= 300) {
        console.error(`Unexpected ${res.statusCode} from ${url}`);
        resolve(false);
        return;
      }

      pipeline(res, createWriteStream(bindingPath), err => (err ? onError(err) : resolve(true)));
    });

    req.on('error', onError);
  });
}
Example #13
Source File: images.ts    From blog with GNU General Public License v3.0 6 votes vote down vote up
copyRemoteImage = async (imageUrl: string, postId: string, postPath: string): Promise<string> => {
    const newPath = join('assets', postId);
    mkdirSync(join(publicPath, newPath), {
        recursive: true
    });
    const url = new URL(imageUrl);
    const imageName = basename(url.pathname);
    console.log('Copying remote image ' + imageUrl);
    // GET request for remote image in node.js
    const response = await axios({
        method: 'get',
        url: imageUrl,
        responseType: 'stream'
    });
    response.data.pipe(createWriteStream(join(publicPath, newPath, imageName)));
    console.log(`Copied to ${join(newPath, imageName)}`);
    return join(newPath, imageName);
}
Example #14
Source File: fs.ts    From devoirs with MIT License 6 votes vote down vote up
export function copyFile(source: PathLike, dest: PathLike): Promise<void> {
  return new Promise<void>((resolve) => {
    const input = createReadStream(source);
    const output = createWriteStream(dest);

    output.once('close', resolve);
    input.pipe(output);
  });
}
Example #15
Source File: download.ts    From devoirs with MIT License 6 votes vote down vote up
download = (from: string, to: string) =>
  new Promise<void>((resolve) => {
    const stream = createWriteStream(to);

    get(from, (response) => {
      response.pipe(stream);
      response.on('end', () => {
        stream.end();
        resolve();
      });
    });
  })
Example #16
Source File: starterTemplates.ts    From OpenVolunteerPlatform with MIT License 6 votes vote down vote up
/**
 * download tar file from repository
 * @param tarInfo repository info
 */
async function downloadRepository(
  tarInfo: TemplateRepositoryTarInformation,
): Promise<string> {
  const spinner = ora(`Downloading starter from ${chalk.cyan(tarInfo.uri)}`).start()
  const tmpPath = tmp.fileSync({
    postfix: '.tar.gz',
  })

  // tslint:disable-next-line: typedef
  await new Promise((resolve) => {
    request(tarInfo.uri, {
      headers: {
        'User-Agent': 'aerogear/openvp',
      },
    })
      .pipe(createWriteStream(tmpPath.name))
      .on('close', resolve)
  })

  spinner.succeed()

  return tmpPath.name
}
Example #17
Source File: utils.ts    From cloudmusic-vscode with MIT License 6 votes vote down vote up
export async function getMusicPath(
  id: number,
  name: string,
  wasm?: boolean
): Promise<string> {
  const idS = `${id}`;
  const cachaUrl = MusicCache.get(idS);
  if (cachaUrl) return cachaUrl;

  const { url, md5 } = await NeteaseAPI.songUrl(idS);
  if (!url) return Promise.reject();
  const tmpUri = resolve(TMP_DIR, idS);

  let cache;
  if (!State.fm) cache = { id: idS, name: `${name}-${idS}`, path: tmpUri, md5 };
  const download = getMusic(url, cache);
  if (!download) return Promise.reject();

  return new Promise((resolve, reject) => {
    const timer = setTimeout(() => {
      download.destroy();
      reject();
    }, 30000);

    const ret = () => {
      clearTimeout(timer);
      resolve(tmpUri);
    };

    let len = 0;
    const onData = ({ length }: { length: number }) => {
      len += length;
      if (len > State.minSize) {
        download.removeListener("data", onData);
        ret();
      }
    };

    const file = createWriteStream(tmpUri);
    if (wasm) file.once("finish", () => ret());
    else download.on("data", onData);
    download.once("error", reject).pipe(file);
  });
}
Example #18
Source File: images.ts    From next-cms-ghost with MIT License 6 votes vote down vote up
normalizedImageUrl = async (url: string) => {
  const localhostRegExp = /^http:\/\/\w+(\.\w+)*(:[0-9]+)?\/?(\/.*)*\/(.*)$/
  const filename = url.match(localhostRegExp)?.reverse()[0]

  if (processEnv.nextImages.source && filename) {
    const filePath = join(imageRoot, filename)

    if (!existsSync(filePath)) {
      const response = await fetch(url)
      if (!response.ok) throw new Error(`images.ts: unexpected response ${response.statusText}`)
      await streamPipeline(response.body, createWriteStream(filePath))
    }
    return `${processEnv.siteUrl}/images/${filename}`
  }
  return url.startsWith('//') ? `https:${url}` : url
}
Example #19
Source File: FileDownloader.ts    From Bridge with GNU General Public License v3.0 6 votes vote down vote up
/**
   * Pipes the data from a download response to `this.fullPath`.
   * @param req The download request.
   */
  private handleDownloadResponse() {
    this.callbacks.downloadProgress(0)
    let downloadedSize = 0
    this.req.pipe(createWriteStream(this.fullPath))
    this.req.on('data', this.cancelable((data) => {
      downloadedSize += data.length
      this.callbacks.downloadProgress(downloadedSize)
    }))

    this.req.on('err', this.cancelable((err: Error) => {
      this.failDownload(downloadErrors.connectionError(err))
    }))

    this.req.on('end', this.cancelable(() => {
      this.callbacks.complete()
    }))
  }
Example #20
Source File: FileDownloader.ts    From Bridge with GNU General Public License v3.0 6 votes vote down vote up
/**
   * Pipes the data from a download response to `this.fullPath`.
   * @param req The download request.
   */
  private handleDownloadResponse() {
    this.callbacks.downloadProgress(0)
    let downloadedSize = 0
    const writeStream = createWriteStream(this.fullPath)

    try {
      this.downloadStream.pipe(writeStream)
    } catch (err) {
      this.failDownload(downloadErrors.connectionError(err))
    }

    this.downloadStream.on('data', this.cancelable((chunk: Buffer) => {
      downloadedSize += chunk.length
    }))

    const progressUpdater = setInterval(() => {
      this.callbacks.downloadProgress(downloadedSize)
    }, 100)

    this.downloadStream.on('error', this.cancelable((err: Error) => {
      clearInterval(progressUpdater)
      this.failDownload(downloadErrors.connectionError(err))
    }))

    this.downloadStream.on('end', this.cancelable(() => {
      clearInterval(progressUpdater)
      writeStream.end()
      this.downloadStream.destroy()
      this.downloadStream = null

      this.callbacks.complete()
    }))
  }
Example #21
Source File: filelogger.ts    From cli with Apache License 2.0 5 votes vote down vote up
public constructor(name: string) {
    const dir = join(LOGS_PATH, name);
    mkdirSync(dir, {recursive: true});
    this.stdout = createWriteStream(join(dir, 'stdout'));
    this.stderr = createWriteStream(join(dir, 'stderr'));
  }
Example #22
Source File: pdf.ts    From SwissQRBill with MIT License 5 votes vote down vote up
constructor(data: Data, outputPathOrWritableStream: string | Writable, optionsOrCallback?: PDFOptions | Function, callbackOrUndefined?: Function | undefined) {

    let callback: Function | undefined = undefined;
    let options: PDFOptions | undefined = undefined;

    if(typeof optionsOrCallback === "object"){

      options = optionsOrCallback;

      if(typeof callbackOrUndefined === "function"){
        callback = callbackOrUndefined;
      }

    } else if(typeof optionsOrCallback === "function"){
      callback = optionsOrCallback;
    }

    super(data, options);

    let stream: Writable | undefined;

    if(typeof outputPathOrWritableStream === "string"){
      stream = createWriteStream(outputPathOrWritableStream);
    } else {
      stream = outputPathOrWritableStream;
    }

    super.pipe(stream);

    stream.on("finish", ev => {

      if(typeof callback === "function"){
        callback(this);
      }

      this.emit("finish", ev);

    });

  }
Example #23
Source File: fileNode.ts    From vscode-ssh with MIT License 5 votes vote down vote up
download(): any {

        const extName = extname(this.file.filename)?.replace(".", "");
        vscode.window.showSaveDialog({ defaultUri: vscode.Uri.file(this.file.filename), filters: { "Type": [extName] }, saveLabel: "Select Download Path" })
            .then(async uri => {
                if (uri) {
                    const { sftp } = await ClientManager.getSSH(this.sshConfig)
                    vscode.window.withProgress({
                        location: vscode.ProgressLocation.Notification,
                        title: `Start downloading ${this.fullPath}`,
                        cancellable:true
                    }, (progress, token) => {
                        return new Promise((resolve) => {
                            const fileReadStream = sftp.createReadStream(this.fullPath)
                            var str = progressStream({
                                length: this.file.attrs.size,
                                time: 100
                            });
                            let before=0;
                            str.on("progress", (progressData: any) => {
                                if (progressData.percentage == 100) {
                                    resolve(null)
                                    vscode.window.showInformationMessage(`Download ${this.fullPath} success, cost time: ${progressData.runtime}s`)
                                    return;
                                }
                                progress.report({ increment: progressData.percentage-before,message:`remaining : ${prettyBytes(progressData.remaining)}` });
                                before=progressData.percentage
                            })
                            str.on("error",err=>{
                                vscode.window.showErrorMessage(err.message)
                            })
                            const outStream = createWriteStream(uri.fsPath);
                            fileReadStream.pipe(str).pipe(outStream);
                            token.onCancellationRequested(() => {
                                fileReadStream.destroy()
                                outStream.destroy()
                            });
                        })
                    })
                }
            })
    }
Example #24
Source File: parentNode.ts    From vscode-ssh with MIT License 5 votes vote down vote up
upload(): any {
        vscode.window.showOpenDialog({ canSelectFiles: true, canSelectMany: false, canSelectFolders: false, openLabel: "Select Upload Path" })
            .then(async uri => {
                if (uri) {
                    const { sftp } = await ClientManager.getSSH(this.sshConfig)
                    const targetPath = uri[0].fsPath;

                    vscode.window.withProgress({
                        location: vscode.ProgressLocation.Notification,
                        title: `Start uploading ${targetPath}`,
                        cancellable:true
                    }, (progress, token) => {
                        return new Promise((resolve) => {
                            const fileReadStream = createReadStream(targetPath)
                            var str = progressStream({
                                length: statSync(targetPath).size,
                                time: 100
                            });
                            let before=0;
                            str.on("progress", (progressData: any) => {
                                if (progressData.percentage == 100) {
                                    resolve(null)
                                    vscode.window.showInformationMessage(`Upload ${targetPath} success, cost time: ${progressData.runtime}s`)
                                    return;
                                }
                                progress.report({ increment: progressData.percentage-before,message:`remaining : ${prettyBytes(progressData.remaining)}` });
                                before=progressData.percentage
                            })
                            str.on("error",err=>{
                                vscode.window.showErrorMessage(err.message)
                            })
                            const outStream = sftp.createWriteStream(this.fullPath + "/" + path.basename(targetPath));
                            fileReadStream.pipe(str).pipe(outStream);
                            token.onCancellationRequested(() => {
                                fileReadStream.destroy()
                                outStream.destroy()
                            });
                        })
                    })

                    // const start = new Date()
                    // vscode.window.showInformationMessage(`Start uploading ${targetPath}.`)
                    // sftp.fastPut(targetPath, this.fullPath + "/" + path.basename(targetPath), err => {
                    //     if (err) {
                    //         vscode.window.showErrorMessage(err.message)
                    //     } else {
                    //         vscode.window.showInformationMessage(`Upload ${this.fullPath} success, cost time: ${new Date().getTime() - start.getTime()}`)
                    //         vscode.commands.executeCommand(Command.REFRESH)
                    //     }
                    // })
                }
            })
    }
Example #25
Source File: generateMinecraftAssets.ts    From NekoMaid with MIT License 5 votes vote down vote up
processFn = (body: any) => {
  try {
    const data = readFileSync('client.jar')
    const hash = createHash('sha1').update(data).digest('hex')
    if (hash !== body.downloads.client.sha1) exit('Hash is not equal:', body.downloads.client.sha1, hash)
    fromBuffer(data, { lazyEntries: true }, (err, file) => {
      if (err) exit(err)
      const map: Record<string, 0> = { }
      file.on('entry', entry => {
        if (!itemOnly && entry.fileName === 'assets/minecraft/lang/en_us.json') {
          file.openReadStream(entry, (err, readStream) => {
            if (err) exit(err)
            readStream.on('end', () => {
              console.log('Saved: en_us.json')
              file.readEntry()
            })
            readStream.pipe(createWriteStream('languages/minecraft/en_us.json'))
          })
          return
        }
        if (entry.fileName !== 'assets/minecraft/textures/misc/enchanted_item_glint.png' &&
          (entry.fileName.endsWith('/') || entry.fileName.endsWith('.mcmeta') ||
            !entry.fileName.startsWith(`assets/minecraft/textures/${itemOnly ? 'item' : 'block'}/`))) {
          file.readEntry()
          return
        }
        const name = basename(entry.fileName).replace(regexp, '').replace(/\.png$/, '')
        if (name in map) {
          file.readEntry()
          return
        }
        file.openReadStream(entry, (err, readStream) => {
          if (err) exit(err)
          readStream.on('end', () => {
            map[name] = 0
            console.log('Saved:', name)
            file.readEntry()
          })
          readStream.pipe(createWriteStream(`icons/minecraft/${name}.png`))
        })
      }).readEntry()
    })
  } catch (e) { exit(e) }
}
Example #26
Source File: plantuml.test.ts    From tx2uml with MIT License 5 votes vote down vote up
describe("Test Plant UML", () => {
    beforeEach(() => {
        outputPng = createWriteStream("output.png")
    })
    test("No options", async () => {
        const plantUmlStream = Readable.from(`
  @startuml

  participant "0x0000..1111" as 00001111
  participant "0x1111..2222" as 11112222
  participant "0x2222..3333" as 22223333
  participant "0x3333..4444" as 33334444
  participant "0x3333..4444" as 44445555

  00001111 -> 11112222: first call
  activate 11112222
  11112222 -> 22223333: second call
  activate 22223333
  return
  return

  @endumls`)
        const exitCode = await streamPlantUml(plantUmlStream, outputPng)
        expect(exitCode).toEqual(0)
    })

    test("Invalid Plant UML", async () => {
        const plantUmlStream = Readable.from("invalid")
        expect.assertions(2)
        try {
            await streamPlantUml(plantUmlStream, outputPng)
        } catch (err) {
            expect(err).toBeInstanceOf(Error)
            expect(VError.info(err).code).toEqual(200)
        }
    })

    test("Syntax error", async () => {
        const plantUmlStream = Readable.from(`
@startuml
XXXparticipant "0x0000..1111" as 00001111
participant "0x1111..2222" as 11112222
00001111 -> 11112222: first call
@endumls`)
        expect.assertions(2)
        try {
            await streamPlantUml(plantUmlStream, outputPng)
        } catch (err) {
            expect(err).toBeInstanceOf(Error)
            expect(VError.info(err).code).toEqual(200)
        }
    })
})
Example #27
Source File: project.spec.ts    From cli with Apache License 2.0 5 votes vote down vote up
mockedCreateWriteStream = jest.mocked(createWriteStream)
Example #28
Source File: Generator.ts    From graphql-ts-client with MIT License 5 votes vote down vote up
export function createStreamAndLog(path: string): WriteStream {
    console.log(`Write code into file: ${path}`);
    return createWriteStream(path);
}
Example #29
Source File: saufen.ts    From CSZ-Bot with MIT License 5 votes vote down vote up
async handleInteraction(command: CommandInteraction<CacheType>, client: Client<boolean>, context: BotContext): Promise<void> {
        const subCommand = command.options.getSubcommand() as SubCommand;
        const reply = () => command.reply("WOCHENENDE!! SAUFEN!! GEIL");

        switch (subCommand) {
            case "los": {
                await Promise.all([
                    connectAndPlaySaufen(context),
                    reply()
                ]);
                return;
            }
            case "select": {
                const toPlay = command.options.getString("sound", true);
                await Promise.all([
                    connectAndPlaySaufen(context, toPlay),
                    reply()
                ]);
                return;
            }
            case "add": {
                const soundUrl = new URL(command.options.getString("sound", true));
                const targetPath = path.resolve(soundDir, path.basename(soundUrl.pathname));
                const fileStream = createWriteStream(targetPath);
                const res = await fetch(soundUrl, {
                    method: "GET"
                });
                const savePromise = new Promise((resolve, reject) => {
                    res.body.pipe(fileStream);
                    res.body.on("error", reject);
                    fileStream.on("finish", resolve);
                });
                await Promise.all([
                    savePromise,
                    command.reply("Jo, habs eingefĆ¼gt")
                ]);
                return;
            }
            case "list": {
                const files = await readdir(soundDir, { withFileTypes: true});
                await command.reply(files.map(f => f.name).join("\n- "));
                return;
            }
            default:
                return assertNever(subCommand);
        }
    }