import dotenv from 'dotenv' import fs from 'fs' import { shell } from 'execa' import semver from 'semver' import log from '@/helpers/log' dotenv.config() /** * Checking script * Help to figure out what is installed or not */ export default () => new Promise(async (resolve, reject) => { try { const nodeMinRequiredVersion = '10' const npmMinRequiredVersion = '5' const pythonRequiredVersion = '3.6' const flitePath = 'bin/flite/flite' const deepSpeechPath = 'bin/deepspeech/lm.binary' const amazonPath = 'server/src/config/voice/amazon.json' const googleCloudPath = 'server/src/config/voice/google-cloud.json' const watsonSttPath = 'server/src/config/voice/watson-stt.json' const watsonTtsPath = 'server/src/config/voice/watson-tts.json' const classifierPath = 'server/src/data/expressions/classifier.json' const report = { can_run: { title: 'Run', type: 'error', v: true }, can_run_module: { title: 'Run modules', type: 'error', v: true }, can_text: { title: 'Reply you by texting', type: 'error', v: true }, can_amazon_polly_tts: { title: 'Amazon Polly text-to-speech', type: 'warning', v: true }, can_google_cloud_tts: { title: 'Google Cloud text-to-speech', type: 'warning', v: true }, can_watson_tts: { title: 'Watson text-to-speech', type: 'warning', v: true }, can_offline_tts: { title: 'Offline text-to-speech', type: 'warning', v: true }, can_google_cloud_stt: { title: 'Google Cloud speech-to-text', type: 'warning', v: true }, can_watson_stt: { title: 'Watson speech-to-text', type: 'warning', v: true }, can_offline_stt: { title: 'Offline speech-to-text', type: 'warning', v: true } } log.title('Checking'); // Environment checking (await Promise.all([ shell('node --version'), shell('npm --version'), shell('pipenv --version') ])).forEach((p) => { log.info(p.cmd) if (p.cmd.indexOf('node --version') !== -1 && !semver.satisfies(semver.clean(p.stdout), `>=${nodeMinRequiredVersion}`)) { Object.keys(report).forEach((item) => { if (report[item].type === 'error') report[item].v = false }) log.error(`${p.stdout}\nThe Node.js version must be >=${nodeMinRequiredVersion}. Please install it: https://nodejs.org (or use nvm)\n`) } else if (p.cmd.indexOf('npm --version') !== -1 && !semver.satisfies(semver.clean(p.stdout), `>=${npmMinRequiredVersion}`)) { Object.keys(report).forEach((item) => { if (report[item].type === 'error') report[item].v = false }) log.error(`${p.stdout}\nThe npm version must be >=${npmMinRequiredVersion}. Please install it: https://www.npmjs.com/get-npm (or use nvm)\n`) } else { log.success(`${p.stdout}\n`) } }); (await Promise.all([ shell('pipenv --where'), shell('pipenv run python --version') ])).forEach((p) => { log.info(p.cmd) if (p.cmd.indexOf('pipenv run python --version') !== -1 && !semver.satisfies(p.stdout.split(' ')[1], pythonRequiredVersion)) { Object.keys(report).forEach((item) => { if (report[item].type === 'error') report[item].v = false }) log.error(`${p.stdout}\nThe Python version must be ${pythonRequiredVersion}.x. Please install it: https://www.python.org/downloads\n`) } else { log.success(`${p.stdout}\n`) } }) // Module execution checking try { const p = await shell('pipenv run python bridges/python/main.py en leon randomnumber "Give me a random number"') log.info(p.cmd) log.success(`${p.stdout}\n`) } catch (e) { log.info(e.cmd) report.can_run_module.v = false log.error(`${e}\n`) } // Classifier checking log.info('Classifier state') if (!fs.existsSync(classifierPath) || !Object.keys(fs.readFileSync(classifierPath)).length) { report.can_text.v = false Object.keys(report).forEach((item) => { if (item.indexOf('stt') !== -1 || item.indexOf('tts') !== -1) report[item].v = false }) log.error('Classifier not found or broken. Try to generate a new one: "npm run train expressions"\n') } else { log.success('Found and valid\n') } // TTS checking log.info('Amazon Polly TTS') try { const json = JSON.parse(fs.readFileSync(amazonPath)) if (json.accessKeyId === '' || json.secretAccessKey === '') { report.can_amazon_polly_tts.v = false log.warning('Amazon Polly TTS is not yet configured\n') } else { log.success('Configured\n') } } catch (e) { report.can_amazon_polly_tts.v = false log.warning(`Amazon Polly TTS is not yet configured: ${e}\n`) } log.info('Google Cloud TTS/STT') try { const json = JSON.parse(fs.readFileSync(googleCloudPath)) const results = [] Object.keys(json).forEach((item) => { if (json[item] === '') results.push(false) }) if (results.includes(false)) { report.can_google_cloud_tts.v = false report.can_google_cloud_stt.v = false log.warning('Google Cloud TTS/STT is not yet configured\n') } else { log.success('Configured\n') } } catch (e) { report.can_google_cloud_tts.v = false report.can_google_cloud_stt.v = false log.warning(`Google Cloud TTS/STT is not yet configured: ${e}\n`) } log.info('Watson TTS') try { const json = JSON.parse(fs.readFileSync(watsonTtsPath)) const results = [] Object.keys(json).forEach((item) => { if (json[item] === '') results.push(false) }) if (results.includes(false)) { report.can_watson_tts.v = false log.warning('Watson TTS is not yet configured\n') } else { log.success('Configured\n') } } catch (e) { report.can_watson_tts.v = false log.warning(`Watson TTS is not yet configured: ${e}\n`) } log.info('Offline TTS') if (!fs.existsSync(flitePath)) { report.can_offline_tts.v = false log.warning(`Cannot find ${flitePath}. You can setup the offline TTS by running: "npm run setup:offline-tts"\n`) } else { log.success(`Found Flite at ${flitePath}\n`) } log.info('Watson STT') try { const json = JSON.parse(fs.readFileSync(watsonSttPath)) const results = [] Object.keys(json).forEach((item) => { if (json[item] === '') results.push(false) }) if (results.includes(false)) { report.can_watson_stt.v = false log.warning('Watson STT is not yet configured\n') } else { log.success('Configured\n') } } catch (e) { report.can_watson_stt.v = false log.warning(`Watson STT is not yet configured: ${e}`) } log.info('Offline STT') if (!fs.existsSync(deepSpeechPath)) { report.can_offline_stt.v = false log.warning(`Cannot find ${deepSpeechPath}. You can setup the offline STT by running: "npm run setup:offline-stt"`) } else { log.success(`Found DeepSpeech language model at ${deepSpeechPath}`) } // Report log.title('Report') log.info('Here is the diagnosis about your current setup') Object.keys(report).forEach((item) => { if (report[item].v === true) { log.success(report[item].title) } else { log[report[item].type](report[item].title) } }) log.default('') if (report.can_run.v && report.can_run_module.v && report.can_text.v) { log.success('Hooray! Leon can run correctly') log.info('If you have some yellow warnings, it is all good. It means some entities are not yet configured') } else { log.error('Please fix the errors above') } resolve() } catch (e) { log.error(e) reject() } })