@@ -75,6 +75,7 @@ const developerIDs = getConfiguration("developerIDs");
7575const developmentGuildIDs = getConfiguration ( "developmentGuildIDs" ) ;
7676const token = getConfiguration ( "token" ) ;
7777const developmentEnvironment = getConfiguration ( "developmentEnvironment" ) ;
78+ const cliProcessTimeout = getConfiguration ( "cliProcessTimeout" ) ;
7879const remindmeMaxTimeStringLength = 100 ;
7980// 100 is the limit for ms library, see https://github.com/zeit/ms/blob/2.1.1/index.js#L50
8081const licenseInfoCooldown = 1000 * 60 * 30 ;
@@ -97,7 +98,6 @@ const inviteString = "Invite ChillBot to other servers!\nhttps://discordapp.com/
9798const botUnavailableString = "Sorry, ChillBot is currently unavailable, most likely due to a new and not yet deployed update. Please check back later." ;
9899const githubAPIBaseURL = "https://api.github.com/" ;
99100const changelogBaseURL = `${ githubAPIBaseURL } repos/LightWayUp/chill/releases/` ;
100- const nodeLicenseURL = `${ githubAPIBaseURL } repos/nodejs/node/license`
101101const libraryList = getLibraries ( ) ;
102102
103103const response = {
@@ -141,20 +141,7 @@ const response = {
141141} ;
142142
143143if ( process . env . RESTARTED !== undefined ) {
144- console . log ( "This process was started by the previous process." ) ;
145- if ( restartTimeout >= 1000 ) {
146- const timeoutInSeconds = Math . floor ( restartTimeout / 1000 ) ;
147- let waitString = "." . repeat ( timeoutInSeconds ) ;
148- console . log ( waitString ) ;
149- let i = 1 ;
150- const timerID = setInterval ( ( ) => {
151- waitString = "|" + waitString . substring ( 0 , timeoutInSeconds - 1 ) ;
152- console . log ( waitString ) ;
153- if ( i ++ === timeoutInSeconds ) {
154- clearInterval ( timerID ) ;
155- }
156- } , 1000 ) ;
157- }
144+ console . log ( `This process was started by the previous process. Logging in after ${ restartTimeout } ms...` ) ;
158145 setTimeout ( ( ) => login ( ) , restartTimeout ) ;
159146} else {
160147 login ( ) ;
@@ -409,7 +396,9 @@ client.on("ready", () => {
409396 }
410397
411398 case "osslicenses" :
412- case "opensourcelicenses" : {
399+ case "osslicences" :
400+ case "opensourcelicenses" :
401+ case "opensourcelicences" : {
413402 if ( ! canSendMessages || shouldReject ) {
414403 return ;
415404 }
@@ -425,16 +414,23 @@ client.on("ready", () => {
425414 licenseInfoSentGuildList . delete ( firstItemKey ) ;
426415 }
427416 licenseInfoSentGuildList . set ( guildID , [ message . url , setTimeout ( ( ) => licenseInfoSentGuildList . delete ( guildID ) , licenseInfoCooldown ) ] ) ;
428- for ( const library of libraryList ) {
417+ for ( const library of await libraryList ) {
429418 const libraryName = library . name
430419 const libraryVersion = library . version ;
431- let messageToSend = `${ bold ( libraryName ) } \nVersion: ${ libraryVersion } \nLicense:` ;
420+ const libraryLicense = library . license ;
421+ let messageToSend = bold ( libraryName ) ;
422+ if ( libraryVersion !== undefined ) {
423+ messageToSend += `\nVersion: ${ libraryVersion } ` ;
424+ }
425+ if ( libraryLicense !== undefined ) {
426+ messageToSend += "\nLicense:" ;
427+ }
432428 await channel . send ( messageToSend , sendOptionsForLongMessage )
433429 . then ( async message => {
434- messageToSend = library . license ;
435- if ( messageToSend === undefined ) {
436- messageToSend = "Unavailable" ;
430+ if ( libraryLicense === undefined ) {
431+ return ;
437432 }
433+ messageToSend = libraryLicense ;
438434 while ( messageToSend . length > maxSafeMessageLength ) {
439435 let partialMessage = messageToSend . substring ( 0 , maxSafeMessageLength ) ;
440436 const splitableIndex = partialMessage . lastIndexOf ( "\n" ) ;
@@ -477,7 +473,7 @@ client.on("ready", () => {
477473 messageToSend = "Fetching changelog..." ;
478474 await channel . send ( messageToSend )
479475 . catch ( error => console . error ( `An error occured while sending message "${ messageToSend } "!\n\nFull details:\n${ error } ` ) ) ;
480- https . get ( `${ changelogBaseURL } ${ tagName !== undefined ? `tags/${ tagName } ` : "latest" } ` , {
476+ const request = https . get ( `${ changelogBaseURL } ${ tagName !== undefined ? `tags/${ tagName } ` : "latest" } ` , {
481477 headers : {
482478 "User-Agent" : chillPackageJson . name
483479 } ,
@@ -540,6 +536,11 @@ client.on("ready", () => {
540536 . catch ( error => console . error ( `An error occured while sending message "${ errorFetchingChangelogString } "!\n\nFull details:\n${ error } ` ) ) ;
541537 }
542538 } ) ;
539+ } ) . on ( "timeout" , ( ) => {
540+ request . abort ( ) ;
541+ console . error ( "Unable to get changelog, request timed out!" ) ;
542+ channel . send ( errorFetchingChangelogString )
543+ . catch ( error => console . error ( `An error occured while sending message "${ errorFetchingChangelogString } "!\n\nFull details:\n${ error } ` ) ) ;
543544 } ) ;
544545 break ;
545546 }
@@ -774,6 +775,19 @@ function getConfiguration(configurationType) {
774775 break ;
775776 }
776777
778+ case "cliProcessTimeout" : {
779+ if ( result === useProcessEnvIdentifier ) {
780+ result = parseInt ( process . env . BOT_CLI_PROCESS_TIMEOUT , 10 ) ;
781+ }
782+ if ( ! ( typeof result === "number" && isInteger ( result . toString ( ) ) ) ) {
783+ throw new TypeError ( "Invalid cliProcessTimeout value!" ) ;
784+ }
785+ if ( result <= 0 ) {
786+ throw new Error ( "Invalid cliProcessTimeout value, cliProcessTimeout must not be less than or equal to 0!" ) ;
787+ }
788+ break ;
789+ }
790+
777791 default : {
778792 throw new Error ( "Invalid configuration to fetch!" ) ;
779793 }
@@ -790,6 +804,10 @@ function NodeModule(name, version, license) {
790804 this . license = license ;
791805}
792806
807+ NodeModule . prototype . toString = function ( ) {
808+ return `${ this . name } @${ this . version } ` ;
809+ }
810+
793811function getModuleFromPath ( directoryPath ) {
794812 if ( ! ( typeof directoryPath === "string" || directoryPath instanceof String || directoryPath instanceof url . URL ) ) {
795813 throw new TypeError ( "Incorrect type for getModuleFromPath argument!" ) ;
@@ -806,10 +824,11 @@ function getModuleFromPath(directoryPath) {
806824 }
807825 const packageJson = require ( packageJsonPath ) ;
808826 const nodeModule = new NodeModule ( packageJson . name , packageJson . version ) ;
827+ console . log ( `Found package "${ nodeModule } "` ) ;
809828 const foundLicensesPath = [ ] ;
810829 const foundLicenses = fs . readdirSync ( directoryPath ) . find ( file => {
811830 const filePath = path . resolve ( directoryPath , `./${ file } ` ) ;
812- if ( fs . statSync ( filePath ) . isFile ( ) && / ( ( l i c e n ( s | c ) e ( s ) ? ) | ( c o p y i n g ) ) ( \. ( ( m d ) | ( t x t ) ) ? ) ? / gi. test ( file ) ) {
831+ if ( fs . statSync ( filePath ) . isFile ( ) && / ^ ( ( l i c e n ( s | c ) e ( s ) ? ) | ( c o p y i n g ) ) ( \. ( ( m d ) | ( t x t ) ) ? ) ? $ / gi. test ( file ) ) {
813832 foundLicensesPath . push ( filePath ) ;
814833 return true ;
815834 }
@@ -821,41 +840,83 @@ function getModuleFromPath(directoryPath) {
821840 return nodeModule ;
822841}
823842
824- function getLibraries ( ) {
825- const libraries = [ ] ;
826- https . get ( nodeLicenseURL , {
827- headers : {
828- "User-Agent" : chillPackageJson . name ,
829- "Accept" : "application/vnd.github.v3.raw"
830- } ,
831- timeout : apiFetchTimeout
832- } , response => {
833- const statusCode = response . statusCode ;
834- const contentType = "content-type" ;
835- if ( statusCode !== 200 ) {
836- response . resume ( ) ;
837- return console . error ( `Unable to get LICENSE for nodejs/node, server responded with status code ${ statusCode } !` ) ;
838- }
839- const receivedType = response . headers [ contentType ] ;
840- if ( ! ( / ^ a p p l i c a t i o n \/ .* r a w / gi. test ( receivedType ) ) ) {
841- response . resume ( ) ;
842- return console . error ( `Unable to get LICENSE for nodejs/node, response content type "${ receivedType } " does not match "application/vnd.github.v3.raw"!` ) ;
843- }
844- let raw = "" ;
845- response . on ( "data" , chunk => raw += chunk )
846- . on ( "error" , error => {
847- console . error ( `An error occured while attempting to fetch LICENSE for nodejs/node!\n\nFull details:\n${ error } ` ) ;
848- } ) . on ( "end" , ( ) => {
849- if ( ! response . complete ) {
850- return console . error ( "Unable to get LICENSE for nodejs/node, connection was terminated while response was still not fully received!" ) ;
851- }
852- libraries . unshift ( new NodeModule ( "node" , process . version , raw . replace ( / \r / gi, "" ) ) ) ;
843+ function getLicenseByRepository ( repository ) {
844+ if ( ! ( typeof repository === "string" || repository instanceof String ) ) {
845+ throw new TypeError ( "Incorrect type for getLicenseByRepository argument!" ) ;
846+ }
847+ if ( ! / ^ [ ^ ( / | \s ) ] + \/ [ ^ ( / | \s ) ] + $ / gi. test ( repository ) ) {
848+ throw new Error ( "Invalid repository name!" ) ;
849+ }
850+ return new Promise ( ( resolve , reject ) => {
851+ const request = https . get ( `${ githubAPIBaseURL } repos/${ repository } /license` , {
852+ headers : {
853+ "User-Agent" : chillPackageJson . name ,
854+ "Accept" : "application/vnd.github.v3.raw"
855+ } ,
856+ timeout : apiFetchTimeout
857+ } , response => {
858+ const statusCode = response . statusCode ;
859+ const contentType = "content-type" ;
860+ if ( statusCode !== 200 ) {
861+ response . resume ( ) ;
862+ return reject ( new Error ( `Unable to get license for ${ repository } , server responded with status code ${ statusCode } !` ) ) ;
863+ }
864+ const receivedType = response . headers [ contentType ] ;
865+ if ( ! ( / ^ a p p l i c a t i o n \/ .* r a w / gi. test ( receivedType ) ) ) {
866+ response . resume ( ) ;
867+ return reject ( new Error ( `Unable to get license for ${ repository } , response content type "${ receivedType } " does not match "application/vnd.github.v3.raw"!` ) ) ;
868+ }
869+ let raw = "" ;
870+ response . on ( "data" , chunk => raw += chunk )
871+ . on ( "error" , error => {
872+ console . error ( `An error occured while attempting to fetch license for ${ repository } !\n\nFull details:\n${ error } ` ) ;
873+ } ) . on ( "end" , ( ) => {
874+ if ( ! response . complete ) {
875+ return reject ( new Error ( `Unable to get license for ${ repository } , connection was terminated while response was still not fully received!` ) ) ;
876+ }
877+ resolve ( raw . replace ( / \r / gi, "" ) ) ;
878+ } ) ;
879+ } ) . on ( "timeout" , ( ) => {
880+ request . abort ( ) ;
881+ reject ( new Error ( `Unable to get license for ${ repository } , request timed out!` ) ) ;
853882 } ) ;
854883 } ) ;
855- const npm = getModuleFromPath ( path . resolve ( path . dirname ( process . argv [ 0 ] ) , "./node_modules/npm" ) ) ;
856- if ( npm !== undefined ) {
857- libraries . push ( npm ) ;
884+ }
885+
886+ async function getLibraries ( ) {
887+ const libraries = [ ] ;
888+ let nodeLicense ;
889+ await getLicenseByRepository ( "nodejs/node" )
890+ . then ( license => nodeLicense = license ,
891+ error => console . error ( error ) ) ;
892+ libraries . push ( new NodeModule ( "node" , process . version , nodeLicense ) ) ;
893+ let npm = getModuleFromPath ( path . resolve ( path . dirname ( process . argv [ 0 ] ) , "./node_modules/npm" ) ) ;
894+ if ( npm === undefined ) {
895+ let npmPath = path . resolve ( path . dirname ( process . argv [ 0 ] ) , ( process . platform === "win32" ? "./npm.cmd" : "./npm" ) ) ;
896+ if ( npmPath . includes ( " " ) ) {
897+ npmPath = `"${ npmPath } "` ;
898+ }
899+ const npmProcess = childProcess . spawnSync ( npmPath , [ "-v" ] , {
900+ timeout : cliProcessTimeout ,
901+ shell : true ,
902+ windowsHide : true
903+ } ) ;
904+ let npmVersion ;
905+ if ( npmProcess . error !== undefined && npmProcess . error !== null ) {
906+ console . error ( `An error occured while attempting to read stdout of NPM process!\n\nFull details:\n${ npmProcess . error } ` ) ;
907+ } else {
908+ const bufferString = npmProcess . stdout . toString ( ) ;
909+ if ( / ^ \s * ( \d + \. ) { 2 } \d + \s * $ / gi. test ( bufferString ) ) {
910+ npmVersion = bufferString . trim ( ) ;
911+ }
912+ }
913+ let npmLicense ;
914+ await getLicenseByRepository ( "npm/cli" )
915+ . then ( license => npmLicense = license ,
916+ error => console . error ( error ) ) ;
917+ npm = new NodeModule ( "npm" , npmVersion , npmLicense ) ;
858918 }
919+ libraries . push ( npm ) ;
859920 const mainModule = require . main ;
860921 if ( mainModule === undefined ) {
861922 return libraries ;
0 commit comments