From b7c81fe3aae5c7225b31388ad65bb96004a5955a Mon Sep 17 00:00:00 2001 From: Fabian Haas <29468630+hfxbse@users.noreply.github.com> Date: Thu, 2 May 2024 18:23:50 +0200 Subject: [PATCH] replace placeholders with real data after graph generation --- README.md | 2 +- package.json | 3 ++- src/index.ts | 67 +++++++++++++++++++++++++++++++++++++++------------- 3 files changed, 53 insertions(+), 19 deletions(-) diff --git a/README.md b/README.md index af01c5a..40eef51 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ npm install To compile and run the program use ``` -npm run run +npm run cli ``` Tests are written with [Jest](https://jestjs.io/). Run them via diff --git a/package.json b/package.json index e8916e8..2a403ca 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,8 @@ "description": "My webpack project", "private": true, "scripts": { - "run": "node --import tsx ./src/index.ts", + "precli": "webpack --mode=production --node-env=production", + "cli": "node --import tsx ./src/index.ts", "test": "node --experimental-vm-modules node_modules/.bin/jest", "build": "webpack --mode=production --node-env=production", "build:dev": "webpack --mode=development", diff --git a/src/index.ts b/src/index.ts index 585d1b6..11ae5da 100644 --- a/src/index.ts +++ b/src/index.ts @@ -2,24 +2,22 @@ import * as prompt from '@inquirer/prompts'; import {ExitPromptError} from '@inquirer/prompts'; import {FollowerFetcherEvent, FollowerFetcherEventTypes, getFollowerGraph, printGraph} from "./instagram/follower"; import SessionData from "./instagram/session-data"; -import {UnsettledUser, UnsettledUserGraph} from "./instagram/user"; -import {writeFileSync} from "node:fs"; +import {UnsettledUserGraph, UserGraph} from "./instagram/user"; +import {PathOrFileDescriptor, writeFileSync} from "node:fs"; import {ReadableStream} from "node:stream/web"; import {authenticate, readExistingSessionId, rootUser, wholeNumberPrompt} from "./cli/promps"; import {settleGraph} from "./cli/graph"; +import {readFileSync} from "fs"; -const writeGraphToFile = async (root: UnsettledUser, graph: UnsettledUserGraph) => { - const filename = `${root.id}:${root.profile.username}:${new Date().toISOString()}.json` - const data = await settleGraph(graph) - +async function writeGraphToFile(filename: string, graph: UserGraph) { try { - writeFileSync(filename, JSON.stringify(data, null, 2)) - console.log(`Wrote graph into ${filename}.`) + writeFileSync(filename, JSON.stringify(graph, null, 2)) + console.log(`Wrote graph into ${filename}.json.`) } catch (error) { - console.error({message: `Cannot write graph into ${filename}. Using stdout instead.`, error}) + console.error({message: `Cannot write graph into ${filename}.json. Using stdout instead.`, error}) await new Promise(resolve => setTimeout(() => { - console.log(JSON.stringify(data)); + console.log(JSON.stringify(graph)); resolve(undefined); }, 500)) } @@ -27,6 +25,25 @@ const writeGraphToFile = async (root: UnsettledUser, graph: UnsettledUserGraph) return filename } +async function generateVisualization({template, output, graph, title}: { + template: PathOrFileDescriptor, + output: string, + title: string + graph: UserGraph | string +}) { + if (typeof graph !== 'string') { + graph = JSON.stringify(graph); + } + + const result = readFileSync(template, {encoding: 'utf-8'}) + .replace('REPLACE-ME-WITH-TITLE', title) + .replace('REPLACE-ME-WITH-USER-GRAPH', graph.replace(/"/g, '\\\"')); + + writeFileSync(output, result) + console.log(`Created visualization for graph in ${output}.html.`) +} + + async function streamGraph(stream: ReadableStream) { let graph: UnsettledUserGraph = {} let cancellation: Promise @@ -92,6 +109,7 @@ try { } const root = await rootUser({session}) + const filename = `${root.id}:${root.profile.username}:${new Date().toISOString()}` const generations = await wholeNumberPrompt({ message: "Generations to include: ", defaultValue: 1 @@ -133,13 +151,28 @@ try { } }) - const {graph, cancellation} = await streamGraph(stream) - await Promise.all([writeGraphToFile(root, graph).then(() => { - console.info( - "The may process still needs to wait on the rate limiting timeouts to exit cleanly. " + - "Killing it should not cause any data lose." - ) - }), cancellation]) + const {graph: unsettledGraph, cancellation} = await streamGraph(stream) + const graph = await settleGraph(unsettledGraph) + + const fileWriters = Promise.allSettled([ + writeGraphToFile(`${filename}.json`, graph), + generateVisualization({ + template: 'dist/index.html', + graph, + title: `${root.profile.name} @${root.profile.username} - ${new Date().toISOString()}`, + output: `${filename}.html` + }) + ]) + + await Promise.all([ + fileWriters.then(() => { + console.info( + "The may process still needs to wait on the rate limiting timeouts to exit cleanly. " + + "Killing it should not cause any data lose." + ) + }), + cancellation + ]) } catch (e) { if (!(e instanceof ExitPromptError)) { console.error(e)