2021-01-08 03:11:49 +08:00
#!/usr/bin/env node
2021-01-06 03:13:05 +08:00
const clc = require ( 'cli-color' ) ;
const notifier = require ( 'node-notifier' ) ;
2021-01-08 03:11:49 +08:00
const Diff = require ( 'diff' ) ;
2021-01-06 03:13:05 +08:00
const path = require ( 'path' ) ;
2021-04-23 23:50:18 +08:00
const isWsl = require ( 'is-wsl' ) ;
2021-01-06 03:13:05 +08:00
const arguments = require ( './tools/process-args' ) . arguments ;
2021-01-08 03:11:49 +08:00
const cli _elem = require ( './tools/cli-elements' ) ; //Idk js package structure/conventions...
2021-01-06 03:13:05 +08:00
const date _tools = require ( './tools/date-tools' ) ;
const net _tools = require ( './tools/net-tools' ) ;
const format _tools = require ( './tools/format-tools' ) ;
const constants = require ( './constants' ) ;
const COLOR = constants . GETCOLORS ( clc ) ;
const STRING = constants . GETSTRING ( COLOR ) ;
const CONSTANT _VALUES = constants . CONSTANT _VALUES ;
var screen = cli _elem . screen ;
var table _element = cli _elem . table _element ;
2021-01-08 03:11:49 +08:00
var diff _table _element = cli _elem . diff _table _element ;
var diff _json _element = cli _elem . diff _json _element ;
2021-01-06 03:13:05 +08:00
var status _element = cli _elem . status _element ;
var countdown _element = cli _elem . countdown _element ;
var controls _element = cli _elem . controls _element ;
var information _element = cli _elem . information _element ;
const secondsHumanReadable = date _tools . secondsHumanReadable ;
const getData = net _tools . getData ;
const formatErr = net _tools . formatErr ;
const prettyPrintData = format _tools . prettyPrintData ;
// =====================================
//TODO: better viewer (than json and the crappy one I made using only string manipulation.)
process . env . FORCE _COLOR = true ;
// const SCREEN_REFRESH = 1000; //ms
// const API_REFRESH = 10*60*1000; //ms
const SCREEN _REFRESH = arguments . screen _refresh ; //ms
const API _REFRESH = arguments . api _refresh ; //ms
const API _REFRESH _CYCLES = API _REFRESH / SCREEN _REFRESH ;
const NONINTERACTIVE = arguments . dump ;
2021-01-08 03:11:49 +08:00
const BLINK = arguments . blink ;
const ARCHIVE = arguments . archive ;
2021-01-06 03:13:05 +08:00
//
var notified _1h = [ ] ;
var notifying _1h = [ ] ;
2021-04-23 23:50:18 +08:00
var ICON _PATH = path . join ( _ _dirname , 'spacex.ico' ) ;
if ( isWsl && ICON _PATH . startsWith ( "/mnt/" , 0 ) ) {
ICON _PATH = ICON _PATH . substr ( 5 ) . replace ( "/" , ":/" ) ;
}
2021-01-08 03:11:49 +08:00
function selectData ( arr ) {
2021-01-06 03:13:05 +08:00
const LUNCHPADS _RESP = arr [ 0 ] ;
const LUNCHES _RESP = arr [ 1 ] ;
const ROCKETS _RESP = arr [ 2 ] ;
const PAYLOADS _RESP = arr [ 3 ] ;
const CORES _RESP = arr [ 4 ] ;
var ROCKETS = { } ;
ROCKETS _RESP . forEach ( rocket => {
ROCKETS [ rocket . id ] = rocket . name
} ) ;
var LUNCHPADS = { } ;
LUNCHPADS _RESP . forEach ( lunchpad => {
LUNCHPADS [ lunchpad . id ] = {
name : lunchpad . name ,
region : lunchpad . region
}
} ) ;
var PAYLOADS = { }
PAYLOADS _RESP . forEach ( payload => {
PAYLOADS [ payload . id ] = {
name : payload . name ,
customers : payload . customers . join ( ', ' )
}
} ) ;
var CORES = { } ;
CORES _RESP . forEach ( core => {
CORES [ core . id ] = {
serial : core . serial ,
reuse _count : core . reuse _count
}
} ) ;
var LUNCHES = LUNCHES _RESP . map ( lunch => {
const lunchpad = LUNCHPADS [ lunch . launchpad ] ;
var precision = lunch . date _precision ;
const t _s = lunch . date _unix ;
const date _arr = new Date ( t _s * 1000 ) . toIsoArr ( precision ) ;
var payload _names _str = '' ;
var payload _customers _str = '' ;
lunch . payloads . forEach ( id => {
payload _names _str += ( PAYLOADS [ id ] . name + ' | ' )
payload _customers _str += ( PAYLOADS [ id ] . customers + ' | ' )
} ) ;
payload _names _str = payload _names _str . substr ( 0 , payload _names _str . length - 3 )
payload _customers _str = payload _customers _str . substr ( 0 , payload _customers _str . length - 3 )
var cores _str = "" ;
2021-01-08 03:11:49 +08:00
var cores _str _ = "" ;
2021-01-06 03:13:05 +08:00
lunch . cores . forEach ( core _ => {
cores _str += CORES [ core _ [ "core" ] ] ? ( CORES [ core _ [ "core" ] ] [ "serial" ] + '(' + CORES [ core _ [ "core" ] ] [ "reuse_count" ] + ') ' ) : STRING . CORE _UNASSIGNED + ' ' ;
2021-01-08 03:11:49 +08:00
cores _str _ += CORES [ core _ [ "core" ] ] ? ( CORES [ core _ [ "core" ] ] [ "serial" ] + '(' + CORES [ core _ [ "core" ] ] [ "reuse_count" ] + ') ' ) : STRING . CORE _UNASSIGNED _ + ' ' ;
2021-01-06 03:13:05 +08:00
} ) ;
2021-01-08 03:11:49 +08:00
cores _str = cores _str . substr ( 0 , cores _str . length - 1 ) ;
cores _str _ = cores _str _ . substr ( 0 , cores _str _ . length - 1 ) ;
2021-01-06 03:13:05 +08:00
var time _style = {
hour : { p : "minutes" , c : COLOR . TIME . LT1 } ,
day : { p : "hours" , c : COLOR . TIME . LTD } ,
month : { p : "hours" , c : COLOR . TIME . LTM } ,
quarter : { p : "days" , c : COLOR . TIME . LTQ } , // use this symbol? ◔ ◑
half : { p : "days" , c : COLOR . TIME . LTH }
} [ precision ]
const dt _s = t _s - new Date ( ) . getTime ( ) / 1000 ;
2021-01-08 03:11:49 +08:00
const dt _str _ = secondsHumanReadable ( dt _s , time _style . p ) ;
var dt _str ;
if ( dt _s <= CONSTANT _VALUES . TIME _NOTIFY && precision === "hour" && ! lunch . tbd && ! lunch . net && ! notified _1h . includes ( lunch . flight _number ) ) { //Notify user.
2021-01-06 03:13:05 +08:00
notified _1h . push ( lunch . flight _number )
notifying _1h . push ( lunch . name )
}
2021-01-08 03:11:49 +08:00
if ( dt _s <= CONSTANT _VALUES . TIME _BLINK && precision === "hour" && ! lunch . tbd && ! lunch . net ) {
dt _str = COLOR . HUGE _SUCCESS ( "! " + ( BLINK ? COLOR . BLINK ( dt _str _ ) : dt _str _ ) ) + COLOR . HUGE _SUCCESS ( " !" ) ;
2021-01-06 03:13:05 +08:00
date _h = COLOR . HUGE _SUCCESS ( date _arr . bw . join ( '' ) ) ;
} else if ( dt _s < 0 ) {
2021-01-08 03:11:49 +08:00
dt _str = COLOR . INVALID ( dt _str _ ) ;
2021-01-06 03:13:05 +08:00
date _h = COLOR . INVALID ( date _arr . bw . join ( '' ) ) + '{/}' ;
} else {
2021-01-08 03:11:49 +08:00
dt _str = time _style . c ( dt _str _ )
2021-01-06 03:13:05 +08:00
date _h = date _arr . col . join ( '' ) ;
}
2021-01-08 03:11:49 +08:00
const precision _ = precision ;
2021-01-06 03:13:05 +08:00
precision = time _style . c ( precision )
return {
name : lunch . name ,
2021-01-08 03:11:49 +08:00
flight _number : String ( lunch . flight _number ) ,
2021-01-06 03:13:05 +08:00
date _precision : precision ,
2021-01-08 03:11:49 +08:00
date _precision _ : precision _ ,
2021-01-06 03:13:05 +08:00
tbd : lunch . tbd ,
net : lunch . net ,
date _h : date _h ,
2021-01-08 03:11:49 +08:00
date _h _ : date _arr . bw . join ( '' ) ,
2021-01-06 03:13:05 +08:00
rocket : ROCKETS [ lunch . rocket ] ,
cores : cores _str ,
2021-01-08 03:11:49 +08:00
cores _ : cores _str _ ,
2021-01-06 03:13:05 +08:00
launchpad : lunchpad . name ,
launchpad _reg : lunchpad . region ,
dt : dt _str ,
2021-01-08 03:11:49 +08:00
dt _ : dt _str _ ,
2021-01-06 03:13:05 +08:00
payloads : {
names _str : payload _names _str ,
customers _str : payload _customers _str
}
}
} ) ;
2021-01-08 03:11:49 +08:00
return LUNCHES ;
} ;
2021-01-06 03:13:05 +08:00
2021-01-08 03:11:49 +08:00
async function formatData ( arr ) {
return format _tools . tabularizeData ( selectData ( arr ) ) ;
} ;
2021-01-06 03:13:05 +08:00
2021-01-08 03:11:49 +08:00
var diff _table _content ;
var diff _json _content ;
2021-01-06 03:13:05 +08:00
2021-01-08 03:11:49 +08:00
var newData = false ;
2021-01-06 03:13:05 +08:00
var json _view = false ;
//Keep these keybind listeners in main // Quit on Escape, q, or Control-C.
screen . key ( [ 'escape' , 'q' , '' , 'C-c' , 'left' ] , ( ch , key ) => {
2021-01-08 03:11:49 +08:00
const name = screen . focused . name ;
if ( name === "table" && key . name !== 'left' ) {
2021-01-06 03:13:05 +08:00
console . log ( '\033[?25h' ) ;
return process . exit ( 0 ) ;
2021-01-08 03:11:49 +08:00
} else if ( name === "detailed_information" || name === "diff_table" || name === "diff_json" ) {
if ( name === "diff_table" || name === "diff_json" ) {
newData = false ;
} ;
2021-01-06 03:13:05 +08:00
table _element . setFront ( ) ;
table _element . focus ( ) ;
controls _element . setContent ( STRING . CONTROLS . TABLE ) ;
screen . render ( ) ;
} ;
} ) ;
screen . key ( [ 'j' ] , ( ch , key ) => {
2021-01-08 03:11:49 +08:00
const name = screen . focused . name ;
if ( name === "detailed_information" ) {
json _view = ( json _view ? false : true ) ;
information _element . setData ( prettyPrintData ( data _cache , idx , json _view ) ) ;
} else if ( name === "diff_table" ) {
json _view = true ;
diff _json _element . setFront ( ) ;
diff _json _element . focus ( ) ;
diff _json _element . enableInput ( ) ;
} else if ( name === "diff_json" ) {
json _view = false ;
diff _table _element . setFront ( ) ;
diff _table _element . focus ( ) ;
diff _table _element . enableInput ( ) ;
} ;
screen . render ( ) ;
} ) ;
screen . key ( [ 'd' ] , ( ch , key ) => {
const name = screen . focused . name ;
if ( name !== "diff_table" && name !== "diff_json" ) {
// console.log(diff_table_content);
// process.kill(process.pid)
controls _element . setContent ( STRING . CONTROLS . INFORMATION ) ;
if ( json _view ) {
diff _json _element . setFront ( ) ;
diff _json _element . focus ( ) ;
diff _json _element . enableInput ( ) ;
} else {
diff _table _element . setFront ( ) ;
diff _table _element . focus ( ) ;
diff _table _element . enableInput ( ) ;
} ;
} else {
table _element . setFront ( ) ;
table _element . focus ( ) ;
controls _element . setContent ( STRING . CONTROLS . TABLE ) ;
} ;
2021-01-06 03:13:05 +08:00
screen . render ( ) ;
} ) ;
screen . key ( [ 'enter' , 'right' ] , ( ch , key ) => {
2021-01-08 03:11:49 +08:00
const name = screen . focused . name ;
if ( name === "table" ) {
2021-01-06 03:13:05 +08:00
idx = table _element . getScroll ( ) - 2 ;
if ( idx >= 0 ) {
status _element . setContent ( String ( data _cache [ 1 ] [ idx ] . name ) ) ;
information _element . setFront ( ) ;
information _element . focus ( ) ;
information _element . enableInput ( ) ;
information _element . setData ( prettyPrintData ( data _cache , idx , json _view ) ) ;
controls _element . setContent ( STRING . CONTROLS . INFORMATION ) ;
} ;
2021-01-08 03:11:49 +08:00
} else if ( key . name === 'enter' && ( name === "detailed_information" || name === "diff_table" || name === "diff_json" ) ) {
if ( name === "diff_table" || name === "diff_json" ) {
newData = false ;
} ;
2021-01-06 03:13:05 +08:00
table _element . setFront ( ) ;
table _element . focus ( ) ;
controls _element . setContent ( STRING . CONTROLS . TABLE ) ;
} ;
screen . render ( ) ;
} ) ;
screen . key ( [ 'r' ] , ( ch , key ) => {
api _counter = 1 ;
} ) ;
var data _cache ;
2021-01-08 03:11:49 +08:00
var checkDiff = true ;
var firstRun = true ;
var lastDelta = '' ;
2021-01-06 03:13:05 +08:00
var api _counter = 1 ;
( async function ( ) {
// data_cache = await getData()
// const TABLE = await formatData(await data_cache);
// table_element.setData(TABLE);
table _element . select ( 1 ) ;
status _element . setContent ( STRING . DOWNLOADING ) ;
countdown _element . setContent ( STRING . DOWNLOADING _STAR ) ;
2021-01-08 03:11:49 +08:00
diff _table _element . setData ( diff _table _content ? diff _table _content : [ [ 'diff_table_content' ] ] ) ;
2021-01-06 03:13:05 +08:00
screen . render ( ) ;
} ) ( )
async function main ( ) {
api _counter -- ;
2021-01-08 03:11:49 +08:00
if ( api _counter === 0 ) {
checkDiff = true ;
2021-01-06 03:13:05 +08:00
status _element . setContent ( STRING . DOWNLOADING ) ;
countdown _element . setContent ( STRING . DOWNLOADING _STAR ) ;
screen . render ( ) ;
api _counter = API _REFRESH _CYCLES ;
data _cache = await getData ( ) ;
}
if ( data _cache ) {
var err _message = await formatErr ( await data _cache ) ;
2021-01-08 03:11:49 +08:00
if ( err _message === true ) {
2021-01-06 03:13:05 +08:00
const TABLE = await formatData ( await data _cache ) ;
const SCROLL = table _element . getScroll ( ) ;
table _element . setData ( TABLE ) ;
table _element . select ( SCROLL ) ;
2021-01-08 03:11:49 +08:00
err _message = [ ( ( data _cache [ 1 ] . length - ( table _element . height - 4 ) ) > 0 ? STRING . OK _SCROLL : STRING . OK _GENERIC ) + ( newData ? ' | ' + COLOR . WARNING ( STRING . NEW _DATA ) : '' ) + ' ' + lastDelta ] ;
} ;
if ( notifying _1h . length > 0 ) {
notifier . notify ( {
title : STRING . H _WARNING ,
message : notifying _1h . join ( ' │ ' ) ,
2021-04-23 23:50:18 +08:00
icon : ICON _PATH ,
2021-01-08 03:11:49 +08:00
appID : STRING . APPID
} ) ;
notifying _1h = [ ] ;
} ;
2021-01-06 03:13:05 +08:00
if ( NONINTERACTIVE ) {
screen . remove ( status _element ) ;
screen . remove ( countdown _element ) ;
screen . remove ( controls _element ) ;
const snip = 2 ; //Add offset incase you want to add in a line in a bash script (Press any key to continue thing.)
table _element . height = screen . height - snip ;
screen . render ( ) ;
const screenshot = screen . screenshot ( ) . split ( '\n' ) ;
screen . destroy ( ) ;
console . log ( '\x1b[H\x1b[J' + screenshot . slice ( 0 , screenshot . length - snip - 1 ) . join ( '\n' ) ) ;
process . exit ( 0 ) ;
}
status _element . setContent ( err _message [ api _counter % err _message . length ] ) ;
countdown _element . setContent ( "Next update in " + secondsHumanReadable ( api _counter * SCREEN _REFRESH / 1000 ) ) ;
2021-01-08 03:11:49 +08:00
2021-01-06 03:13:05 +08:00
2021-01-08 03:11:49 +08:00
if ( checkDiff ) {
checkDiff = false ;
const path _prev _launches = path . join ( CONSTANT _VALUES . DATA _PATH , 'previous_data.json' ) ;
const path _prev _launches _table = path . join ( CONSTANT _VALUES . DATA _PATH , 'previous_data_table.json' ) ;
2021-01-06 03:13:05 +08:00
2021-01-08 03:11:49 +08:00
const curr _launches = data _cache [ 1 ] ;
var prev _launches = net _tools . readFile ( path _prev _launches ) ;
prev _launches = prev _launches !== '' ? JSON . parse ( prev _launches ) : [ ] ;
diff _json _content = [ [ STRING . HEADERS . JSON ] , [ '' ] ] ;
// jsonDiff.diffString(prev_launches, curr_launches)
// .split('\n')
// .forEach(u => {diff_json_content.push([clc.cyan(u)])});
const json _diff _data = Diff . diffJson ( prev _launches , curr _launches )
const changed = json _diff _data . some ( chunk => {
return chunk . added || chunk . removed ;
} ) ;
2021-01-06 03:13:05 +08:00
2021-01-08 03:11:49 +08:00
if ( changed || firstRun ) {
firstRun = false ;
json _diff _data . forEach ( chunk => {
if ( chunk . added && chunk . removed ) { //Not sure if this case is possible.
chunk . value . split ( '\\n' ) . forEach ( line => { [ diff _json _content . push ( [ COLOR . WARNING ( STRING . DIFF . BOTH _SYMBOL + '│ ' + line ) ] ) ] } ) ;
} else if ( chunk . added ) {
chunk . value . split ( '\\n' ) . forEach ( line => { [ diff _json _content . push ( [ COLOR . SUCCESS ( STRING . DIFF . CURRENT _SYMBOL + '│ ' + line ) ] ) ] } ) ;
} else if ( chunk . removed ) {
chunk . value . split ( '\\n' ) . forEach ( line => { [ diff _json _content . push ( [ COLOR . DANGER ( STRING . DIFF . PREVIOUS _SYMBOL + '│ ' + line ) ] ) ] } ) ;
} else {
chunk . value . replace ( /\n/gm , "_NEWLINE_" ) //Why the frick doesn't my regexp work when it works in a tester???? Using this workaround.
. replace ( /_NEWLINE_\s\s\{(.*?)_NEWLINE_\s\s\},/gm , '_NEWLINE_ [...]' )
. replace ( /_NEWLINE_\s\s\{(.*?)_NEWLINE_]/m , '_NEWLINE_ [...]_NEWLINE_]' )
. split ( '_NEWLINE_' )
. forEach ( line => { line !== '' ? [ diff _json _content . push ( [ COLOR . GENERIC ( STRING . DIFF . UNCHANGED _SYMBOL _ + '│ ' + line ) ] ) ] : null } ) ; //Trailing newline results in a empty line - need to filter it out.
} ;
} ) ;
diff _json _content . push ( [ '' ] )
2021-01-06 03:13:05 +08:00
2021-01-08 03:11:49 +08:00
// console.log(json_diff_data);
// process.kill(process.pid)
// JSON.stringify((json_diff_data ? json_diff_data : ''),null,2)
// .split('\n')
// .forEach(u => {diff_json_content.push([clc.cyan(u)])});
var prev _launches _table = net _tools . readFile ( path _prev _launches _table ) ;
const curr _launches _table = format _tools . tabularizeDiffData ( await selectData ( await data _cache ) ) ;
prev _launches _table = prev _launches _table !== '' ? JSON . parse ( prev _launches _table ) : Array ( curr _launches _table . length ) . fill ( Array ( curr _launches _table [ 0 ] . length ) . fill ( '' ) ) ;
diff _table _content = format _tools . diffTable ( prev _launches _table , curr _launches _table )
diff _table _element . setData ( diff _table _content ? diff _table _content : [ [ 'diff_table_content' ] ] ) ;
diff _json _element . setData ( diff _json _content ? diff _json _content : [ [ 'diff_json_content' ] ] ) ;
if ( changed ) {
const dateStr = new Date ( ) . toIsoArr ( ) . bw . join ( '' ) ;
lastDelta = '| ' + STRING . LAST _DELTA + COLOR . GENERIC ( dateStr ) ;
newData = true ;
notifier . notify ( {
title : STRING . NEW _DATA ,
message : 'Press d to see table diff. Press j to see JSON diff.' ,
2021-04-23 23:50:18 +08:00
icon : ICON _PATH ,
2021-01-08 03:11:49 +08:00
appID : STRING . APPID
} ) ;
net _tools . writeFile ( path _prev _launches , JSON . stringify ( curr _launches ) ) ;
net _tools . writeFile ( path _prev _launches _table , JSON . stringify ( curr _launches _table ) ) ;
if ( ARCHIVE ) {
net _tools . writeFile (
path . join ( CONSTANT _VALUES . DATA _PATH , '/archive/' + dateStr . replace ( /:/g , '_' ) . replace ( ' ' , '_' ) + '.json' ) ,
JSON . stringify ( curr _launches )
) ;
net _tools . writeFile (
path . join ( CONSTANT _VALUES . DATA _PATH , '/archive/' + dateStr . replace ( /:/g , '_' ) . replace ( ' ' , '_' ) + '_table.json' ) ,
JSON . stringify ( curr _launches _table )
) ;
} ;
} ;
} ;
} ;
} ;
screen . render ( ) ;
} ;
console . log ( '\033[?25l' ) ; //ANSI code - hide cursor
setInterval ( main , SCREEN _REFRESH ) ;