1
1
mirror of https://github.com/leon-ai/leon.git synced 2024-09-11 10:25:40 +03:00

refactor(server): capitalize singleton helpers instance constants

This commit is contained in:
louistiti 2022-09-21 20:25:00 +08:00
parent 285bfa6e78
commit 4f1683dc55
No known key found for this signature in database
GPG Key ID: 0A1C3B043E70C77D
61 changed files with 564 additions and 517 deletions

View File

@ -1,6 +1,6 @@
import { command } from 'execa'
import { log } from '@/helpers/log'
import { LOG } from '@/helpers/log'
/**
* Build web app
@ -12,6 +12,6 @@ export default () =>
stdout: 'inherit'
})
log.success('Web app built')
LOG.success('Web app built')
resolve()
})

View File

@ -1,4 +1,4 @@
import { log } from '@/helpers/log'
import { LOG } from '@/helpers/log'
import buildApp from './build-app'
@ -9,6 +9,6 @@ import buildApp from './build-app'
try {
await buildApp()
} catch (e) {
log.error(`Failed to build: ${e}`)
LOG.error(`Failed to build: ${e}`)
}
})()

View File

@ -1,6 +1,6 @@
import execa from 'execa'
import { log } from '@/helpers/log'
import { LOG } from '@/helpers/log'
import { getOSInformation } from '@/helpers/os'
/**
@ -8,49 +8,49 @@ import { getOSInformation } from '@/helpers/os'
*/
export default () =>
new Promise(async (resolve, reject) => {
log.info('Checking OS environment...')
LOG.info('Checking OS environment...')
const info = getOSInformation()
if (info.type === 'windows') {
log.error('Voice offline mode is not available on Windows')
LOG.error('Voice offline mode is not available on Windows')
reject()
} else if (info.type === 'unknown') {
log.error(
LOG.error(
'This OS is unknown, please open an issue to let us know about it'
)
reject()
} else {
try {
log.success(`You are running ${info.name}`)
log.info('Checking tools...')
LOG.success(`You are running ${info.name}`)
LOG.info('Checking tools...')
await execa('tar', ['--version'])
log.success('"tar" found')
LOG.success('"tar" found')
await execa('make', ['--version'])
log.success('"make" found')
LOG.success('"make" found')
if (info.type === 'macos') {
await execa('brew', ['--version'])
log.success('"brew" found')
LOG.success('"brew" found')
await execa('curl', ['--version'])
log.success('"curl" found')
LOG.success('"curl" found')
} else if (info.type === 'linux') {
await execa('apt-get', ['--version'])
log.success('"apt-get" found')
LOG.success('"apt-get" found')
await execa('wget', ['--version'])
log.success('"wget" found')
LOG.success('"wget" found')
}
resolve()
} catch (e) {
if (e.cmd) {
const cmd = e.cmd.substr(0, e.cmd.indexOf(' '))
log.error(
LOG.error(
`The following command has failed: "${e.cmd}". "${cmd}" is maybe missing. To continue this setup, please install the required tool. More details about the failure: ${e}`
)
} else {
log.error(`Failed to prepare the environment: ${e}`)
LOG.error(`Failed to prepare the environment: ${e}`)
}
reject(e)

View File

@ -5,7 +5,7 @@ import { command } from 'execa'
import semver from 'semver'
import { version } from '@@/package.json'
import { log } from '@/helpers/log'
import { LOG } from '@/helpers/log'
dotenv.config()
@ -76,19 +76,19 @@ export default () =>
}
}
log.title('Checking')
LOG.title('Checking')
/**
* Leon version checking
*/
log.info('Leon version')
log.success(`${version}\n`)
LOG.info('Leon version')
LOG.success(`${version}\n`)
/**
* Environment checking
*/
log.info('OS')
LOG.info('OS')
const osInfo = {
type: os.type(),
@ -97,7 +97,7 @@ export default () =>
cpus: os.cpus().length,
release: os.release()
}
log.success(`${JSON.stringify(osInfo)}\n`)
LOG.success(`${JSON.stringify(osInfo)}\n`)
;(
await Promise.all([
command('node --version', { shell: true }),
@ -105,7 +105,7 @@ export default () =>
command('pipenv --version', { shell: true })
])
).forEach((p) => {
log.info(p.command)
LOG.info(p.command)
if (
p.command.indexOf('node --version') !== -1 &&
@ -117,7 +117,7 @@ export default () =>
Object.keys(report).forEach((item) => {
if (report[item].type === 'error') report[item].v = false
})
log.error(
LOG.error(
`${p.stdout}\nThe Node.js version must be >=${nodeMinRequiredVersion}. Please install it: https://nodejs.org (or use nvm)\n`
)
} else if (
@ -130,11 +130,11 @@ export default () =>
Object.keys(report).forEach((item) => {
if (report[item].type === 'error') report[item].v = false
})
log.error(
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`)
LOG.success(`${p.stdout}\n`)
}
})
;(
@ -143,7 +143,7 @@ export default () =>
command('pipenv run python --version', { shell: true })
])
).forEach((p) => {
log.info(p.command)
LOG.info(p.command)
if (
p.command.indexOf('pipenv run python --version') !== -1 &&
@ -155,11 +155,11 @@ export default () =>
Object.keys(report).forEach((item) => {
if (report[item].type === 'error') report[item].v = false
})
log.error(
LOG.error(
`${p.stdout}\nThe Python version must be >=${pythonMinRequiredVersion}. Please install it: https://www.python.org/downloads\n`
)
} else {
log.success(`${p.stdout}\n`)
LOG.success(`${p.stdout}\n`)
}
})
@ -172,12 +172,12 @@ export default () =>
'pipenv run python bridges/python/main.py scripts/assets/intent-object.json',
{ shell: true }
)
log.info(p.command)
log.success(`${p.stdout}\n`)
LOG.info(p.command)
LOG.success(`${p.stdout}\n`)
} catch (e) {
log.info(e.command)
LOG.info(e.command)
report.can_run_skill.v = false
log.error(`${e}\n`)
LOG.error(`${e}\n`)
}
/**
@ -189,12 +189,12 @@ export default () =>
'pipenv run python -c "import en_core_web_trf"',
{ shell: true }
)
log.info(p.command)
log.success(`spaCy model installed\n`)
LOG.info(p.command)
LOG.success(`spaCy model installed\n`)
} catch (e) {
log.info(e.command)
LOG.info(e.command)
report.has_spacy_model.v = false
log.error(
LOG.error(
'No spaCy model is installed. It is recommended to run the following command: "npm run clean:python-deps"\n'
)
}
@ -203,7 +203,7 @@ export default () =>
* Global resolvers NLP model checking
*/
log.info('Global resolvers NLP model state')
LOG.info('Global resolvers NLP model state')
if (
!fs.existsSync(globalResolversNlpModelPath) ||
@ -214,18 +214,18 @@ export default () =>
if (item.indexOf('stt') !== -1 || item.indexOf('tts') !== -1)
report[item].v = false
})
log.error(
LOG.error(
'Global resolvers NLP model not found or broken. Try to generate a new one: "npm run train"\n'
)
} else {
log.success('Found and valid\n')
LOG.success('Found and valid\n')
}
/**
* Skills resolvers NLP model checking
*/
log.info('Skills resolvers NLP model state')
LOG.info('Skills resolvers NLP model state')
if (
!fs.existsSync(skillsResolversNlpModelPath) ||
@ -236,18 +236,18 @@ export default () =>
if (item.indexOf('stt') !== -1 || item.indexOf('tts') !== -1)
report[item].v = false
})
log.error(
LOG.error(
'Skills resolvers NLP model not found or broken. Try to generate a new one: "npm run train"\n'
)
} else {
log.success('Found and valid\n')
LOG.success('Found and valid\n')
}
/**
* Main NLP model checking
*/
log.info('Main NLP model state')
LOG.info('Main NLP model state')
if (
!fs.existsSync(mainNlpModelPath) ||
@ -258,18 +258,18 @@ export default () =>
if (item.indexOf('stt') !== -1 || item.indexOf('tts') !== -1)
report[item].v = false
})
log.error(
LOG.error(
'Main NLP model not found or broken. Try to generate a new one: "npm run train"\n'
)
} else {
log.success('Found and valid\n')
LOG.success('Found and valid\n')
}
/**
* TTS/STT checking
*/
log.info('Amazon Polly TTS')
LOG.info('Amazon Polly TTS')
try {
const json = JSON.parse(fs.readFileSync(amazonPath))
@ -278,16 +278,16 @@ export default () =>
json.credentials.secretAccessKey === ''
) {
report.can_amazon_polly_tts.v = false
log.warning('Amazon Polly TTS is not yet configured\n')
LOG.warning('Amazon Polly TTS is not yet configured\n')
} else {
log.success('Configured\n')
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.warning(`Amazon Polly TTS is not yet configured: ${e}\n`)
}
log.info('Google Cloud TTS/STT')
LOG.info('Google Cloud TTS/STT')
try {
const json = JSON.parse(fs.readFileSync(googleCloudPath))
@ -298,17 +298,17 @@ export default () =>
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')
LOG.warning('Google Cloud TTS/STT is not yet configured\n')
} else {
log.success('Configured\n')
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.warning(`Google Cloud TTS/STT is not yet configured: ${e}\n`)
}
log.info('Watson TTS')
LOG.info('Watson TTS')
try {
const json = JSON.parse(fs.readFileSync(watsonTtsPath))
@ -318,27 +318,27 @@ export default () =>
})
if (results.includes(false)) {
report.can_watson_tts.v = false
log.warning('Watson TTS is not yet configured\n')
LOG.warning('Watson TTS is not yet configured\n')
} else {
log.success('Configured\n')
LOG.success('Configured\n')
}
} catch (e) {
report.can_watson_tts.v = false
log.warning(`Watson TTS is not yet configured: ${e}\n`)
LOG.warning(`Watson TTS is not yet configured: ${e}\n`)
}
log.info('Offline TTS')
LOG.info('Offline TTS')
if (!fs.existsSync(flitePath)) {
report.can_offline_tts.v = false
log.warning(
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.success(`Found Flite at ${flitePath}\n`)
}
log.info('Watson STT')
LOG.info('Watson STT')
try {
const json = JSON.parse(fs.readFileSync(watsonSttPath))
@ -348,59 +348,59 @@ export default () =>
})
if (results.includes(false)) {
report.can_watson_stt.v = false
log.warning('Watson STT is not yet configured\n')
LOG.warning('Watson STT is not yet configured\n')
} else {
log.success('Configured\n')
LOG.success('Configured\n')
}
} catch (e) {
report.can_watson_stt.v = false
log.warning(`Watson STT is not yet configured: ${e}`)
LOG.warning(`Watson STT is not yet configured: ${e}`)
}
log.info('Offline STT')
LOG.info('Offline STT')
if (!fs.existsSync(coquiLanguageModelPath)) {
report.can_offline_stt.v = false
log.warning(
LOG.warning(
`Cannot find ${coquiLanguageModelPath}. You can setup the offline STT by running: "npm run setup:offline-stt"`
)
} else {
log.success(`Found Coqui language model at ${coquiLanguageModelPath}`)
LOG.success(`Found Coqui language model at ${coquiLanguageModelPath}`)
}
/**
* Report
*/
log.title('Report')
LOG.title('Report')
log.info('Here is the diagnosis about your current setup')
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)
LOG.success(report[item].title)
} else {
log[report[item].type](report[item].title)
LOG[report[item].type](report[item].title)
}
})
log.default('')
LOG.default('')
if (
report.can_run.v &&
report.can_run_skill.v &&
report.can_text.v &&
report.has_spacy_model.v
) {
log.success('Hooray! Leon can run correctly')
log.info(
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')
LOG.error('Please fix the errors above')
}
resolve()
} catch (e) {
log.error(e)
LOG.error(e)
reject()
}
})

View File

@ -1,7 +1,7 @@
import fs from 'fs'
import { join } from 'path'
import { log } from '@/helpers/log'
import { LOG } from '@/helpers/log'
import { getSkillDomains } from '@/helpers/skill-domain'
/**
@ -9,7 +9,7 @@ import { getSkillDomains } from '@/helpers/skill-domain'
*/
export default () =>
new Promise(async (resolve, reject) => {
log.info('Cleaning test DB files...')
LOG.info('Cleaning test DB files...')
const skillDomains = await getSkillDomains()
@ -27,17 +27,17 @@ export default () =>
.filter((entity) => entity.indexOf('.spec.json') !== -1)
if (dbTestFiles.length > 0) {
log.info(`Deleting ${dbTestFiles[0]}...`)
LOG.info(`Deleting ${dbTestFiles[0]}...`)
fs.unlinkSync(join(dbFolder, dbTestFiles[0]))
log.success(`${dbTestFiles[0]} deleted`)
LOG.success(`${dbTestFiles[0]} deleted`)
}
} catch (e) {
log.error(`Failed to clean: "${skillKeys[j]}" test DB file`)
LOG.error(`Failed to clean: "${skillKeys[j]}" test DB file`)
reject(e)
}
}
}
log.success('Cleaning done')
LOG.success('Cleaning done')
resolve()
})

View File

@ -1,12 +1,12 @@
import fs from 'fs'
import { log } from '@/helpers/log'
import { LOG } from '@/helpers/log'
/**
* This script is executed after "git commit" or "git merge" (Git hook https://git-scm.com/docs/githooks#_commit_msg)
* it ensures the authenticity of commit messages
*/
log.info('Checking commit message...')
LOG.info('Checking commit message...')
const commitEditMsgFile = '.git/COMMIT_EDITMSG'
@ -17,13 +17,13 @@ if (fs.existsSync(commitEditMsgFile)) {
'(build|BREAKING|chore|ci|docs|feat|fix|perf|refactor|style|test)(\\((web app|docker|server|hotword|skill\\/([\\w-]+)))?\\)?: .{1,50}' // eslint-disable-line no-useless-escape
if (commitMessage.match(regex) !== null) {
log.success('Commit message validated')
LOG.success('Commit message validated')
} else {
log.error(`Commit message does not match the format: ${regex}`)
LOG.error(`Commit message does not match the format: ${regex}`)
process.exit(1)
}
} catch (e) {
log.error(e.message)
LOG.error(e.message)
process.exit(1)
}
}

View File

@ -4,7 +4,7 @@ import fs from 'fs'
import { prompt } from 'inquirer'
import path from 'path'
import { log } from '@/helpers/log'
import { LOG } from '@/helpers/log'
import { randomString } from '@/helpers/string'
dotenv.config()
@ -15,7 +15,7 @@ dotenv.config()
*/
const generateHttpApiKey = () =>
new Promise(async (resolve, reject) => {
log.info('Generating the HTTP API key...')
LOG.info('Generating the HTTP API key...')
try {
const shasum = crypto.createHash('sha1')
@ -39,11 +39,11 @@ const generateHttpApiKey = () =>
content = lines.join('\n')
fs.writeFileSync(dotEnvPath, content)
log.success('HTTP API key generated')
LOG.success('HTTP API key generated')
resolve()
} catch (e) {
log.error(e.message)
LOG.error(e.message)
reject(e)
}
})

View File

@ -2,7 +2,7 @@ import dotenv from 'dotenv'
import fs from 'fs'
import path from 'path'
import { log } from '@/helpers/log'
import { LOG } from '@/helpers/log'
import { langs } from '@@/core/langs.json'
import { getSkillDomains } from '@/helpers/skill-domain'
@ -65,7 +65,7 @@ export default () =>
}
if (i + 1 === skillDomains.size) {
log.success(`${outputFile} is already up-to-date`)
LOG.success(`${outputFile} is already up-to-date`)
isFileNeedToBeGenerated = false
}
@ -75,7 +75,7 @@ export default () =>
// Force if a language is given
if (isFileNeedToBeGenerated) {
log.info('Parsing skills configuration...')
LOG.info('Parsing skills configuration...')
for (const currentDomain of skillDomains.values()) {
const skillKeys = Object.keys(currentDomain.skills)
@ -143,17 +143,17 @@ export default () =>
}
}
log.info(`Writing ${outputFile} file...`)
LOG.info(`Writing ${outputFile} file...`)
try {
fs.writeFileSync(outputFilePath, JSON.stringify(finalObj, null, 2))
log.success(`${outputFile} file generated`)
LOG.success(`${outputFile} file generated`)
resolve()
} catch (e) {
reject(`Failed to generate ${outputFile} file: ${e.message}`)
}
}
} catch (e) {
log.error(e.message)
LOG.error(e.message)
reject(e)
}
})

View File

@ -1,4 +1,4 @@
import { log } from '@/helpers/log'
import { LOG } from '@/helpers/log'
import generateHttpApiKey from './generate-http-api-key'
@ -9,6 +9,6 @@ import generateHttpApiKey from './generate-http-api-key'
try {
await generateHttpApiKey()
} catch (e) {
log.error(`Failed to generate the HTTP API key: ${e}`)
LOG.error(`Failed to generate the HTTP API key: ${e}`)
}
})()

View File

@ -1,4 +1,4 @@
import { log } from '@/helpers/log'
import { LOG } from '@/helpers/log'
import generateSkillsEndpoints from './generate-skills-endpoints'
@ -9,6 +9,6 @@ import generateSkillsEndpoints from './generate-skills-endpoints'
try {
await generateSkillsEndpoints()
} catch (e) {
log.error(`Failed to generate skills endpoints: ${e}`)
LOG.error(`Failed to generate skills endpoints: ${e}`)
}
})()

View File

@ -1,14 +1,14 @@
import { command } from 'execa'
import { log } from '@/helpers/log'
import { loader } from '@/helpers/loader'
import { LOG } from '@/helpers/log'
import { LOADER } from '@/helpers/loader'
/**
* This script ensures the correct coding syntax of the whole project
*/
;(async () => {
loader.start()
log.info('Linting...')
LOADER.start()
LOG.info('Linting...')
try {
const globs = [
@ -32,11 +32,11 @@ import { loader } from '@/helpers/loader'
{ shell: true }
)
log.success('Looks great')
loader.stop()
LOG.success('Looks great')
LOADER.stop()
} catch (e) {
log.error(`Does not look great: ${e.message}`)
loader.stop()
LOG.error(`Does not look great: ${e.message}`)
LOADER.stop()
process.exit(1)
}
})()

View File

@ -1,7 +1,7 @@
import { command } from 'execa'
import fs from 'fs'
import { log } from '@/helpers/log'
import { LOG } from '@/helpers/log'
/**
* Update version number in files which need version number
@ -11,7 +11,7 @@ export default (version) =>
const changelog = 'CHANGELOG.md'
const tmpChangelog = 'TMP-CHANGELOG.md'
log.info(`Generating ${changelog}...`)
LOG.info(`Generating ${changelog}...`)
try {
await command(
@ -19,13 +19,13 @@ export default (version) =>
{ shell: true }
)
} catch (e) {
log.error(`Error during git-changelog: ${e}`)
LOG.error(`Error during git-changelog: ${e}`)
reject(e)
}
try {
log.info('Getting remote origin URL...')
log.info('Getting previous tag...')
LOG.info('Getting remote origin URL...')
LOG.info('Getting previous tag...')
const sh = await command(
'git config --get remote.origin.url && git tag | tail -n1',
@ -38,23 +38,23 @@ export default (version) =>
const compareUrl = `${repoUrl}/compare/${previousTag}...v${version}`
let tmpData = fs.readFileSync(`scripts/tmp/${tmpChangelog}`, 'utf8')
log.success(`Remote origin URL gotten: ${repoUrl}.git`)
log.success(`Previous tag gotten: ${previousTag}`)
LOG.success(`Remote origin URL gotten: ${repoUrl}.git`)
LOG.success(`Previous tag gotten: ${previousTag}`)
if (previousTag !== '') {
tmpData = tmpData.replace(version, `[${version}](${compareUrl})`)
}
fs.writeFile(changelog, `${tmpData}${changelogData}`, (err) => {
if (err) log.error(`Failed to write into file: ${err}`)
if (err) LOG.error(`Failed to write into file: ${err}`)
else {
fs.unlinkSync(`scripts/tmp/${tmpChangelog}`)
log.success(`${changelog} generated`)
LOG.success(`${changelog} generated`)
resolve()
}
})
} catch (e) {
log.error(`Error during git commands: ${e}`)
LOG.error(`Error during git commands: ${e}`)
reject(e)
}
})

View File

@ -1,5 +1,5 @@
import { log } from '@/helpers/log'
import { loader } from '@/helpers/loader'
import { LOG } from '@/helpers/log'
import { LOADER } from '@/helpers/loader'
import updateVersion from './update-version'
import generateChangelog from './generate-changelog'
@ -8,8 +8,8 @@ import generateChangelog from './generate-changelog'
* Main entry of the release preparation
*/
;(async () => {
loader.start()
log.info('Preparing for release...')
LOADER.start()
LOG.info('Preparing for release...')
const { argv } = process
const version = argv[2].toLowerCase()
@ -21,16 +21,16 @@ import generateChangelog from './generate-changelog'
await updateVersion(version)
await generateChangelog(version)
log.success('Hooray! Leon is ready to be released!')
loader.stop()
LOG.success('Hooray! Leon is ready to be released!')
LOADER.stop()
} catch (e) {
log.error(e)
loader.stop()
LOG.error(e)
LOADER.stop()
}
} else {
log.error(
LOG.error(
'The version number does match the Semantic Versioning rules (https://semver.org)'
)
loader.stop()
LOADER.stop()
}
})()

View File

@ -1,13 +1,13 @@
import { command } from 'execa'
import { log } from '@/helpers/log'
import { LOG } from '@/helpers/log'
/**
* Update version number in files which need version number
*/
export default (version) =>
new Promise(async (resolve, reject) => {
log.info('Updating version...')
LOG.info('Updating version...')
const promises = []
const files = ['package.json', 'package-lock.json']
@ -23,10 +23,10 @@ export default (version) =>
try {
await Promise.all(promises)
log.success(`Version updated to ${version}`)
LOG.success(`Version updated to ${version}`)
resolve()
} catch (e) {
log.error(`Error while updating version: ${e.stderr}`)
LOG.error(`Error while updating version: ${e.stderr}`)
reject(e)
}
})

View File

@ -1,4 +1,4 @@
import { loader } from '@/helpers/loader'
import { LOADER } from '@/helpers/loader'
import check from './check'
@ -7,10 +7,10 @@ import check from './check'
*/
;(async () => {
try {
loader.start()
LOADER.start()
await check()
loader.stop()
LOADER.stop()
} catch (e) {
loader.stop()
LOADER.stop()
}
})()

View File

@ -1,4 +1,4 @@
import { log } from '@/helpers/log'
import { LOG } from '@/helpers/log'
import cleanTestDbs from './clean-test-dbs'
@ -9,6 +9,6 @@ import cleanTestDbs from './clean-test-dbs'
try {
await cleanTestDbs()
} catch (e) {
log.error(`Failed to clean test DBs: ${e}`)
LOG.error(`Failed to clean test DBs: ${e}`)
}
})()

View File

@ -1,4 +1,4 @@
import { log } from '@/helpers/log'
import { LOG } from '@/helpers/log'
import setupHotword from './setup-hotword'
@ -9,6 +9,6 @@ import setupHotword from './setup-hotword'
try {
await setupHotword()
} catch (e) {
log.error(`Failed to setup offline hotword: ${e}`)
LOG.error(`Failed to setup offline hotword: ${e}`)
}
})()

View File

@ -1,4 +1,4 @@
import { log } from '@/helpers/log'
import { LOG } from '@/helpers/log'
import setupStt from './setup-stt'
@ -9,6 +9,6 @@ import setupStt from './setup-stt'
try {
await setupStt()
} catch (e) {
log.error(`Failed to setup offline STT: ${e}`)
LOG.error(`Failed to setup offline STT: ${e}`)
}
})()

View File

@ -1,4 +1,4 @@
import { log } from '@/helpers/log'
import { LOG } from '@/helpers/log'
import setupTts from './setup-tts'
@ -9,6 +9,6 @@ import setupTts from './setup-tts'
try {
await setupTts()
} catch (e) {
log.error(`Failed to setup offline TTS: ${e}`)
LOG.error(`Failed to setup offline TTS: ${e}`)
}
})()

View File

@ -1,6 +1,6 @@
import { command } from 'execa'
import { log } from '@/helpers/log'
import { LOG } from '@/helpers/log'
import { getOSInformation } from '@/helpers/os'
/**
@ -8,7 +8,7 @@ import { getOSInformation } from '@/helpers/os'
*/
export default () =>
new Promise(async (resolve, reject) => {
log.info('Setting up offline hotword detection...')
LOG.info('Setting up offline hotword detection...')
const info = getOSInformation()
let pkgm = 'apt-get install'
@ -17,35 +17,35 @@ export default () =>
}
if (info.type === 'windows') {
log.error('Voice offline mode is not available on Windows')
LOG.error('Voice offline mode is not available on Windows')
reject()
} else {
try {
log.info('Installing dependencies...')
LOG.info('Installing dependencies...')
let cmd = `sudo ${pkgm} sox libsox-fmt-all -y`
if (info.type === 'linux') {
log.info(`Executing the following command: ${cmd}`)
LOG.info(`Executing the following command: ${cmd}`)
await command(cmd, { shell: true })
} else if (info.type === 'macos') {
cmd = `${pkgm} install swig portaudio sox`
log.info(`Executing the following command: ${cmd}`)
LOG.info(`Executing the following command: ${cmd}`)
await command(cmd, { shell: true })
}
log.success('System dependencies downloaded')
log.info('Installing hotword dependencies...')
LOG.success('System dependencies downloaded')
LOG.info('Installing hotword dependencies...')
await command('cd hotword && npm install', { shell: true })
log.success('Offline hotword detection installed')
LOG.success('Offline hotword detection installed')
await command(
'cd hotword/node_modules/@bugsounet/snowboy && CXXFLAGS="--std=c++17" ../../../node_modules/@mapbox/node-pre-gyp/bin/node-pre-gyp clean configure build',
{ shell: true }
)
log.success('Snowboy bindings compiled')
LOG.success('Snowboy bindings compiled')
resolve()
} catch (e) {
log.error(`Failed to install offline hotword detection: ${e}`)
LOG.error(`Failed to install offline hotword detection: ${e}`)
reject(e)
}
}

View File

@ -1,7 +1,7 @@
import dotenv from 'dotenv'
import { loader } from '@/helpers/loader'
import { log } from '@/helpers/log'
import { LOADER } from '@/helpers/loader'
import { LOG } from '@/helpers/log'
import checkOs from '../check-os'
import setupHotword from './setup-hotword'
@ -15,18 +15,18 @@ dotenv.config()
*/
;(async () => {
try {
loader.start()
LOADER.start()
await checkOs()
loader.stop()
LOADER.stop()
await setupHotword()
loader.start()
LOADER.start()
await setupTts()
await setupStt()
loader.stop()
log.success('Hooray! Offline components are installed!')
LOADER.stop()
LOG.success('Hooray! Offline components are installed!')
} catch (e) {
log.error(e)
loader.stop()
LOG.error(e)
LOADER.stop()
}
})()

View File

@ -1,7 +1,7 @@
import { command } from 'execa'
import fs from 'fs'
import { log } from '@/helpers/log'
import { LOG } from '@/helpers/log'
import { getOSInformation } from '@/helpers/os'
/**
@ -9,7 +9,7 @@ import { getOSInformation } from '@/helpers/os'
*/
export default () =>
new Promise(async (resolve, reject) => {
log.info('Setting up offline speech-to-text...')
LOG.info('Setting up offline speech-to-text...')
const destCoquiFolder = 'bin/coqui'
const tmpDir = 'scripts/tmp'
@ -22,7 +22,7 @@ export default () =>
if (!fs.existsSync(`${destCoquiFolder}/model.tflite`)) {
try {
log.info('Downloading pre-trained model...')
LOG.info('Downloading pre-trained model...')
await command(
`cd ${tmpDir} && ${downloader} https://github.com/coqui-ai/STT-models/releases/download/english/coqui/v${coquiModelVersion}-huge-vocab/model.tflite`,
{ shell: true }
@ -31,8 +31,8 @@ export default () =>
`cd ${tmpDir} && ${downloader} https://github.com/coqui-ai/STT-models/releases/download/english/coqui/v${coquiModelVersion}-huge-vocab/huge-vocabulary.scorer`,
{ shell: true }
)
log.success('Pre-trained model download done')
log.info('Moving...')
LOG.success('Pre-trained model download done')
LOG.info('Moving...')
await command(
`mv -f ${tmpDir}/model.tflite ${destCoquiFolder}/model.tflite`,
{ shell: true }
@ -41,16 +41,16 @@ export default () =>
`mv -f ${tmpDir}/huge-vocabulary.scorer ${destCoquiFolder}/huge-vocabulary.scorer`,
{ shell: true }
)
log.success('Move done')
log.success('Offline speech-to-text installed')
LOG.success('Move done')
LOG.success('Offline speech-to-text installed')
resolve()
} catch (e) {
log.error(`Failed to install offline speech-to-text: ${e}`)
LOG.error(`Failed to install offline speech-to-text: ${e}`)
reject(e)
}
} else {
log.success('Offline speech-to-text is already installed')
LOG.success('Offline speech-to-text is already installed')
resolve()
}
})

View File

@ -1,7 +1,7 @@
import { command } from 'execa'
import fs from 'fs'
import { log } from '@/helpers/log'
import { LOG } from '@/helpers/log'
import { getOSInformation, getNumberOfCPUCores } from '@/helpers/os'
/**
@ -9,7 +9,7 @@ import { getOSInformation, getNumberOfCPUCores } from '@/helpers/os'
*/
export default () =>
new Promise(async (resolve, reject) => {
log.info('Setting up offline text-to-speech...')
LOG.info('Setting up offline text-to-speech...')
const destFliteFolder = 'bin/flite'
const tmpDir = 'scripts/tmp'
@ -24,44 +24,44 @@ export default () =>
if (!fs.existsSync(`${destFliteFolder}/flite`)) {
try {
log.info('Downloading run-time synthesis engine...')
LOG.info('Downloading run-time synthesis engine...')
await command(
`cd ${tmpDir} && ${downloader} http://ports.ubuntu.com/pool/universe/f/flite/flite_2.1-release.orig.tar.bz2`,
{ shell: true }
)
log.success('Run-time synthesis engine download done')
log.info('Unpacking...')
LOG.success('Run-time synthesis engine download done')
LOG.info('Unpacking...')
await command(
`cd ${tmpDir} && tar xfvj flite_2.1-release.orig.tar.bz2 && cp ../assets/leon.lv flite-2.1-release/config`,
{ shell: true }
)
log.success('Unpack done')
log.info('Configuring...')
LOG.success('Unpack done')
LOG.info('Configuring...')
await command(
`cd ${tmpDir}/flite-2.1-release && ./configure --with-langvox=leon`,
{ shell: true }
)
log.success('Configure done')
log.info('Building...')
LOG.success('Configure done')
LOG.info('Building...')
await command(`cd ${tmpDir}/flite-2.1-release && make ${makeCores}`, {
shell: true
})
log.success('Build done')
log.info('Cleaning...')
LOG.success('Build done')
LOG.info('Cleaning...')
await command(
`cp -f ${tmpDir}/flite-2.1-release/bin/flite ${destFliteFolder} && rm -rf ${tmpDir}/flite-2.1-release*`,
{ shell: true }
)
log.success('Clean done')
log.success('Offline text-to-speech installed')
LOG.success('Clean done')
LOG.success('Offline text-to-speech installed')
resolve()
} catch (e) {
log.error(`Failed to install offline text-to-speech: ${e}`)
LOG.error(`Failed to install offline text-to-speech: ${e}`)
reject(e)
}
} else {
log.success('Offline text-to-speech is already installed')
LOG.success('Offline text-to-speech is already installed')
resolve()
}
})

View File

@ -1,14 +1,14 @@
import fs from 'fs'
import path from 'path'
import { log } from '@/helpers/log'
import { LOG } from '@/helpers/log'
/**
* Setup Leon's core configuration
*/
export default () =>
new Promise((resolve) => {
log.info('Configuring core...')
LOG.info('Configuring core...')
const dir = 'core/config'
const list = (dir) => {
@ -30,12 +30,12 @@ export default () =>
fs.createWriteStream(`${dir}/${file}`)
)
log.success(`${file} file created`)
LOG.success(`${file} file created`)
} else if (
entities[i].indexOf('.sample.json') !== -1 &&
fs.existsSync(`${dir}/${file}`)
) {
log.success(`${file} already exists`)
LOG.success(`${file} already exists`)
}
}
}

View File

@ -1,19 +1,19 @@
import { prompt } from 'inquirer'
import fs from 'fs'
import { log } from '@/helpers/log'
import { LOG } from '@/helpers/log'
/**
* Duplicate the .env.sample to .env file
*/
export default () =>
new Promise(async (resolve) => {
log.info('.env file creation...')
LOG.info('.env file creation...')
const createDotenv = () => {
fs.createReadStream('.env.sample').pipe(fs.createWriteStream('.env'))
log.success('.env file created')
LOG.success('.env file created')
}
if (!fs.existsSync('.env')) {

View File

@ -2,18 +2,18 @@ import { command } from 'execa'
import fs from 'fs'
import path from 'path'
import { log } from '@/helpers/log'
import { LOG } from '@/helpers/log'
/**
* Download and setup Leon's Python packages dependencies
*/
export default () =>
new Promise(async (resolve, reject) => {
log.info('Checking Python env...')
LOG.info('Checking Python env...')
// Check if the Pipfile exists
if (fs.existsSync('bridges/python/Pipfile')) {
log.success('bridges/python/Pipfile found')
LOG.success('bridges/python/Pipfile found')
try {
// Check if Pipenv is installed
@ -29,9 +29,9 @@ export default () =>
pipenvVersion = `${pipenvVersion} version`
}
log.success(`Pipenv ${pipenvVersion} found`)
LOG.success(`Pipenv ${pipenvVersion} found`)
} catch (e) {
log.error(
LOG.error(
`${e}\nPlease install Pipenv: "pip install pipenv" or read the documentation https://docs.pipenv.org`
)
reject(e)
@ -44,18 +44,18 @@ export default () =>
const isDotVenvExist = fs.existsSync(dotVenvPath)
const installPythonPackages = async () => {
if (isDotVenvExist) {
log.info(`Deleting ${dotVenvPath}...`)
LOG.info(`Deleting ${dotVenvPath}...`)
fs.rmSync(dotVenvPath, { recursive: true, force: true })
log.success(`${dotVenvPath} deleted`)
LOG.success(`${dotVenvPath} deleted`)
}
// Installing Python packages
log.info('Installing Python packages from bridges/python/Pipfile...')
LOG.info('Installing Python packages from bridges/python/Pipfile...')
await command('pipenv install --site-packages', { shell: true })
log.success('Python packages installed')
LOG.success('Python packages installed')
log.info('Installing spaCy models...')
LOG.info('Installing spaCy models...')
// Find new spaCy models: https://github.com/explosion/spacy-models/releases
await Promise.all([
command(
@ -68,7 +68,7 @@ export default () =>
)
])
log.success('spaCy models installed')
LOG.success('spaCy models installed')
}
if (!isDotVenvExist) {
@ -85,7 +85,7 @@ export default () =>
if (pipfileMtime > dotProjectMtime) {
await installPythonPackages()
} else {
log.success('Python packages are up-to-date')
LOG.success('Python packages are up-to-date')
}
} else {
await installPythonPackages()
@ -94,11 +94,11 @@ export default () =>
resolve()
} catch (e) {
log.error(`Failed to install the Python packages: ${e}`)
LOG.error(`Failed to install the Python packages: ${e}`)
reject(e)
}
} else {
log.error(
LOG.error(
'bridges/python/Pipfile does not exist. Try to pull the project (git pull)'
)
reject()

View File

@ -2,7 +2,7 @@ import { commandSync } from 'execa'
import fs from 'fs'
import path from 'path'
import { log } from '@/helpers/log'
import { LOG } from '@/helpers/log'
import { getSkillDomains } from '@/helpers/skill-domain'
/**
@ -10,7 +10,7 @@ import { getSkillDomains } from '@/helpers/skill-domain'
*/
export default () =>
new Promise(async (resolve, reject) => {
log.info('Setting up skills configuration...')
LOG.info('Setting up skills configuration...')
const skillDomains = await getSkillDomains()
@ -46,7 +46,7 @@ export default () =>
for (let j = 0; j < configSampleKeys.length; j += 1) {
// Check if the current config key does not exist
if (configKeys.includes(configSampleKeys[j]) === false) {
log.info(
LOG.info(
`Adding new configuration key "${configSampleKeys[j]}" for the ${skillFriendlyName} skill...`
)
@ -63,11 +63,11 @@ export default () =>
}=${JSON.stringify(configKey[configSampleKeys[j]])}'`,
{ shell: true }
)
log.success(
LOG.success(
`"${configSampleKeys[j]}" configuration key added to ${configFile}`
)
} catch (e) {
log.error(
LOG.error(
`Error while adding "${configSampleKeys[j]}" configuration key to ${configFile}: ${e}`
)
reject()
@ -77,7 +77,7 @@ export default () =>
}
} else if (!fs.existsSync(configSampleFile)) {
// Stop the setup if the config.sample.json of the current skill does not exist
log.error(
LOG.error(
`The "${skillFriendlyName}" skill configuration file does not exist. Try to pull the project (git pull)`
)
reject()
@ -87,7 +87,7 @@ export default () =>
fs.createWriteStream(`${configDir}/config.json`)
)
log.success(
LOG.success(
`"${skillFriendlyName}" skill configuration file created`
)
resolve()
@ -96,6 +96,6 @@ export default () =>
}
}
log.success('Skills configured')
LOG.success('Skills configured')
resolve()
})

View File

@ -1,5 +1,5 @@
import { loader } from '@/helpers/loader'
import { log } from '@/helpers/log'
import { LOADER } from '@/helpers/loader'
import { LOG } from '@/helpers/log'
import train from '../train/train'
import generateHttpApiKey from '../generate/generate-http-api-key'
@ -20,19 +20,19 @@ import setupPythonPackages from './setup-python-packages'
process.env.PIPENV_VENV_IN_PROJECT = 'true'
await setupDotenv()
loader.start()
LOADER.start()
await Promise.all([setupCore(), setupSkillsConfig()])
await setupPythonPackages()
loader.stop()
LOADER.stop()
await generateHttpApiKey()
loader.start()
LOADER.start()
await train()
log.default('')
log.success('Hooray! Leon is installed and ready to go!')
loader.stop()
LOG.default('')
LOG.success('Hooray! Leon is installed and ready to go!')
LOADER.stop()
} catch (e) {
log.error(e)
loader.stop()
LOG.error(e)
LOADER.stop()
}
})()

View File

@ -1,7 +1,7 @@
import { command } from 'execa'
import { log } from '@/helpers/log'
import { loader } from '@/helpers/loader'
import { LOG } from '@/helpers/log'
import { LOADER } from '@/helpers/loader'
/**
* Specific module testing script
@ -15,18 +15,18 @@ import { loader } from '@/helpers/loader'
const [pkg, module] = arr
try {
loader.start()
LOADER.start()
await command('npm run train en', { shell: true })
const cmd = await command(
`cross-env PIPENV_PIPFILE=bridges/python/Pipfile LEON_NODE_ENV=testing jest --silent --config=./test/e2e/modules/e2e.modules.jest.json packages/${pkg}/test/${module}.spec.js && npm run train`,
{ shell: true }
)
log.default(cmd.stdout)
log.default(cmd.stderr)
loader.stop()
LOG.default(cmd.stdout)
LOG.default(cmd.stderr)
LOADER.stop()
} catch (e) {
log.default(e.message)
loader.stop()
LOG.default(e.message)
LOADER.stop()
}
})()

View File

@ -1,4 +1,4 @@
import { log } from '@/helpers/log'
import { LOG } from '@/helpers/log'
import train from './train'
@ -9,6 +9,6 @@ import train from './train'
try {
await train()
} catch (e) {
log.error(`Failed to train: ${e}`)
LOG.error(`Failed to train: ${e}`)
}
})()

View File

@ -1,7 +1,7 @@
import path from 'path'
import fs from 'fs'
import { log } from '@/helpers/log'
import { LOG } from '@/helpers/log'
/**
* Train global entities
@ -9,7 +9,7 @@ import { log } from '@/helpers/log'
*/
export default (lang, nlp) =>
new Promise((resolve) => {
log.title('Global entities training')
LOG.title('Global entities training')
const globalEntitiesPath = path.join(
process.cwd(),
@ -31,7 +31,7 @@ export default (lang, nlp) =>
const optionKeys = Object.keys(options)
const optionsObj = {}
log.info(`[${lang}] Adding "${entityName}" global entity...`)
LOG.info(`[${lang}] Adding "${entityName}" global entity...`)
optionKeys.forEach((optionKey) => {
const { synonyms } = options[optionKey]
@ -40,7 +40,7 @@ export default (lang, nlp) =>
})
newEntitiesObj[entityName] = { options: optionsObj }
log.success(`[${lang}] "${entityName}" global entity added`)
LOG.success(`[${lang}] "${entityName}" global entity added`)
}
nlp.addEntities(newEntitiesObj, lang)

View File

@ -1,6 +1,6 @@
import { composeFromPattern } from '@nlpjs/utils'
import { log } from '@/helpers/log'
import { LOG } from '@/helpers/log'
import { findAndMap } from '@/helpers/string'
import { getSkillDomains, getSkillConfig } from '@/helpers/skill-domain'
@ -9,7 +9,7 @@ import { getSkillDomains, getSkillConfig } from '@/helpers/skill-domain'
*/
export default (lang, nlp) =>
new Promise(async (resolve) => {
log.title('Skills actions training')
LOG.title('Skills actions training')
const supportedActionTypes = ['dialog', 'logic']
const skillDomains = await getSkillDomains()
@ -17,12 +17,12 @@ export default (lang, nlp) =>
for (const [domainName, currentDomain] of skillDomains) {
const skillKeys = Object.keys(currentDomain.skills)
log.info(`[${lang}] Training "${domainName}" domain model...`)
LOG.info(`[${lang}] Training "${domainName}" domain model...`)
for (let j = 0; j < skillKeys.length; j += 1) {
const { name: skillName } = currentDomain.skills[skillKeys[j]]
log.info(`[${lang}] Using "${skillKeys[j]}" skill config data`)
LOG.info(`[${lang}] Using "${skillKeys[j]}" skill config data`)
const skillConfigData = await getSkillConfig({
domain: currentDomain.name,
@ -48,7 +48,7 @@ export default (lang, nlp) =>
!actionObj.type ||
!supportedActionTypes.includes(actionObj.type)
) {
log.error(`This action type isn't supported: ${actionObj.type}`)
LOG.error(`This action type isn't supported: ${actionObj.type}`)
process.exit(1)
}
@ -115,7 +115,7 @@ export default (lang, nlp) =>
}
}
log.success(`[${lang}] "${domainName}" domain trained`)
LOG.success(`[${lang}] "${domainName}" domain trained`)
}
resolve()

View File

@ -2,14 +2,14 @@ import path from 'path'
import fs from 'fs'
import { composeFromPattern } from '@nlpjs/utils'
import { log } from '@/helpers/log'
import { LOG } from '@/helpers/log'
/**
* Train global resolvers
*/
export default (lang, nlp) =>
new Promise((resolve) => {
log.title('Global resolvers training')
LOG.title('Global resolvers training')
const resolversPath = path.join(
process.cwd(),
@ -27,7 +27,7 @@ export default (lang, nlp) =>
)
const intentKeys = Object.keys(resolverIntents)
log.info(`[${lang}] Training "${resolverName}" resolver...`)
LOG.info(`[${lang}] Training "${resolverName}" resolver...`)
for (let j = 0; j < intentKeys.length; j += 1) {
const intentName = intentKeys[j]
@ -47,7 +47,7 @@ export default (lang, nlp) =>
}
}
log.success(`[${lang}] "${resolverName}" resolver trained`)
LOG.success(`[${lang}] "${resolverName}" resolver trained`)
}
resolve()

View File

@ -1,6 +1,6 @@
import { composeFromPattern } from '@nlpjs/utils'
import { log } from '@/helpers/log'
import { LOG } from '@/helpers/log'
import { getSkillDomains, getSkillConfig } from '@/helpers/skill-domain'
/**
@ -8,7 +8,7 @@ import { getSkillDomains, getSkillConfig } from '@/helpers/skill-domain'
*/
export default (lang, nlp) =>
new Promise(async (resolve) => {
log.title('Skills resolvers training')
LOG.title('Skills resolvers training')
const skillDomains = await getSkillDomains()
@ -33,7 +33,7 @@ export default (lang, nlp) =>
const resolver = resolvers[resolverName]
const intentKeys = Object.keys(resolver.intents)
log.info(
LOG.info(
`[${lang}] Training ${skillName} "${resolverName}" resolver...`
)
@ -54,7 +54,7 @@ export default (lang, nlp) =>
})
})
log.success(
LOG.success(
`[${lang}] ${skillName} "${resolverName}" resolver trained`
)
})

View File

@ -3,7 +3,7 @@ import { Nlp } from '@nlpjs/nlp'
import { LangAll } from '@nlpjs/lang-all'
import dotenv from 'dotenv'
import { log } from '@/helpers/log'
import { LOG } from '@/helpers/log'
import { getShortLanguages } from '@/helpers/lang'
import trainGlobalResolvers from './train-resolvers-model/train-global-resolvers'
import trainSkillsResolvers from './train-resolvers-model/train-skills-resolvers'
@ -106,38 +106,38 @@ export default () =>
try {
await globalResolversNlp.train()
log.success(
LOG.success(
`Global resolvers NLP model saved in ${globalResolversModelFileName}`
)
resolve()
} catch (e) {
log.error(`Failed to save global resolvers NLP model: ${e}`)
LOG.error(`Failed to save global resolvers NLP model: ${e}`)
reject()
}
try {
await skillsResolversNlp.train()
log.success(
LOG.success(
`Skills resolvers NLP model saved in ${skillsResolversModelFileName}`
)
resolve()
} catch (e) {
log.error(`Failed to save skills resolvers NLP model: ${e}`)
LOG.error(`Failed to save skills resolvers NLP model: ${e}`)
reject()
}
try {
await mainNlp.train()
log.success(`Main NLP model saved in ${mainModelFileName}`)
LOG.success(`Main NLP model saved in ${mainModelFileName}`)
resolve()
} catch (e) {
log.error(`Failed to save main NLP model: ${e}`)
LOG.error(`Failed to save main NLP model: ${e}`)
reject()
}
} catch (e) {
log.error(e.message)
LOG.error(e.message)
reject(e)
}
})

View File

@ -2,7 +2,7 @@ import { path as ffmpegPath } from '@ffmpeg-installer/ffmpeg'
import Ffmpeg from 'fluent-ffmpeg'
import fs from 'fs'
import { log } from '@/helpers/log'
import { LOG } from '@/helpers/log'
const audios = {
webm: `${__dirname}/../tmp/speech.webm`,
@ -13,8 +13,8 @@ class Asr {
constructor() {
this.blob = {}
log.title('ASR')
log.success('New instance')
LOG.title('ASR')
LOG.success('New instance')
}
static get audios() {
@ -27,7 +27,7 @@ class Asr {
*/
run(blob, stt) {
return new Promise((resolve, reject) => {
log.title('ASR')
LOG.title('ASR')
this.blob = blob
@ -47,10 +47,10 @@ class Asr {
ffmpeg
.addInput(audios.webm)
.on('start', () => {
log.info('Encoding WebM file to WAVE file...')
LOG.info('Encoding WebM file to WAVE file...')
})
.on('end', () => {
log.success('Encoding done')
LOG.success('Encoding done')
if (Object.keys(stt).length === 0) {
reject({

View File

@ -4,7 +4,7 @@ import { spawn } from 'child_process'
import { langs } from '@@/core/langs.json'
import { HAS_TTS } from '@/constants'
import { log } from '@/helpers/log'
import { LOG } from '@/helpers/log'
import { findAndMap, randomString } from '@/helpers/string'
import Synchronizer from '@/core/synchronizer'
import { getLongLanguageCode } from '@/helpers/lang'
@ -30,8 +30,8 @@ class Brain {
this._stt = {}
this._tts = {}
log.title('Brain')
log.success('New instance')
LOG.title('Brain')
LOG.success('New instance')
}
get socket() {
@ -74,8 +74,8 @@ class Brain {
if (HAS_TTS) {
this._tts.init(this._lang, () => {
log.title('Brain')
log.info('Language has changed')
LOG.title('Brain')
LOG.info('Language has changed')
})
}
}
@ -89,7 +89,7 @@ class Brain {
fs.unlinkSync(intentObjectPath)
}
} catch (e) {
log.error(`Failed to delete intent object file: ${e}`)
LOG.error(`Failed to delete intent object file: ${e}`)
}
}
@ -97,8 +97,8 @@ class Brain {
* Make Leon talk
*/
talk(rawSpeech, end = false) {
log.title('Leon')
log.info('Talking...')
LOG.title('Leon')
LOG.info('Talking...')
if (rawSpeech !== '') {
if (HAS_TTS) {
@ -233,7 +233,7 @@ class Brain {
{ shell: true }
)
} catch (e) {
log.error(`Failed to save intent object: ${e}`)
LOG.error(`Failed to save intent object: ${e}`)
}
}
@ -256,8 +256,8 @@ class Brain {
if (typeof obj === 'object') {
if (obj.output.type === 'inter') {
log.title(`${skillFriendlyName} skill`)
log.info(data.toString())
LOG.title(`${skillFriendlyName} skill`)
LOG.info(data.toString())
this.interOutput = obj.output
@ -281,8 +281,8 @@ class Brain {
})
}
} catch (e) {
log.title('Brain')
log.debug(`process.stdout: ${String(data)}`)
LOG.title('Brain')
LOG.debug(`process.stdout: ${String(data)}`)
/* istanbul ignore next */
reject({
@ -310,8 +310,8 @@ class Brain {
Brain.deleteIntentObjFile(intentObjectPath)
log.title(`${skillFriendlyName} skill`)
log.error(data.toString())
LOG.title(`${skillFriendlyName} skill`)
LOG.error(data.toString())
const executionTimeEnd = Date.now()
const executionTime = executionTimeEnd - executionTimeStart
@ -325,8 +325,8 @@ class Brain {
// Catch the end of the skill execution
this.process.stdout.on('end', () => {
log.title(`${skillFriendlyName} skill`)
log.info(output)
LOG.title(`${skillFriendlyName} skill`)
LOG.info(output)
this.finalOutput = output

View File

@ -1,6 +1,6 @@
import fs from 'fs'
import { log } from '@/helpers/log'
import { LOG } from '@/helpers/log'
const maxContextHistory = 5
const defaultActiveContext = {
@ -23,8 +23,8 @@ class Conversation {
this._activeContext = defaultActiveContext
this._previousContexts = {}
log.title('Conversation')
log.success('New instance')
LOG.title('Conversation')
LOG.success('New instance')
}
get id() {
@ -78,8 +78,8 @@ class Conversation {
activatedAt: Date.now()
}
log.title('Conversation')
log.info(`New active context: ${newContextName}`)
LOG.title('Conversation')
LOG.info(`New active context: ${newContextName}`)
}
this.setSlots(lang, entities, slots)
@ -113,8 +113,8 @@ class Conversation {
activatedAt: Date.now()
}
log.title('Conversation')
log.info(`New active context: ${newContextName}`)
LOG.title('Conversation')
LOG.info(`New active context: ${newContextName}`)
} else {
this._activeContext.currentEntities = entities
// Add new entities at the end of the context entities array
@ -184,8 +184,8 @@ class Conversation {
slot.value.resolution.value !== newSlot.value.resolution.value)
) {
if (newSlot?.isFilled) {
log.title('Conversation')
log.success(
LOG.title('Conversation')
LOG.success(
`Slot filled: { name: ${newSlot.name}, value: ${JSON.stringify(
newSlot.value
)} }`
@ -220,8 +220,8 @@ class Conversation {
* Clean up active context
*/
cleanActiveContext() {
log.title('Conversation')
log.info('Clean active context')
LOG.title('Conversation')
LOG.info('Clean active context')
this.pushToPreviousContextsStack()
this._activeContext = defaultActiveContext

View File

@ -2,20 +2,20 @@ import fs from 'fs'
import path from 'path'
import archiver from 'archiver'
import { log } from '@/helpers/log'
import { LOG } from '@/helpers/log'
import { ucFirst } from '@/helpers/string'
const getDownloads = async (fastify, options) => {
fastify.get(`/api/${options.apiVersion}/downloads`, (request, reply) => {
log.title('GET /downloads')
LOG.title('GET /downloads')
const clean = (dir, files) => {
log.info('Cleaning skill download directory...')
LOG.info('Cleaning skill download directory...')
for (let i = 0; i < files.length; i += 1) {
fs.unlinkSync(`${dir}/${files[i]}`)
}
fs.rmdirSync(dir)
log.success('Downloads directory cleaned')
LOG.success('Downloads directory cleaned')
}
let message = ''
@ -27,18 +27,18 @@ const getDownloads = async (fastify, options) => {
)
const skill = path.join(dlDomainDir, `${request.query.skill}.py`)
log.info(
LOG.info(
`Checking existence of the ${ucFirst(request.query.skill)} skill...`
)
if (fs.existsSync(skill)) {
log.success(`${ucFirst(request.query.skill)} skill exists`)
LOG.success(`${ucFirst(request.query.skill)} skill exists`)
const downloadsDir = `${dlDomainDir}/${request.query.skill}`
log.info('Reading downloads directory...')
LOG.info('Reading downloads directory...')
fs.readdir(downloadsDir, (err, files) => {
if (err && err.code === 'ENOENT') {
message = 'There is no content to download for this skill.'
log.error(message)
LOG.error(message)
reply.code(404).send({
success: false,
status: 404,
@ -46,16 +46,16 @@ const getDownloads = async (fastify, options) => {
message
})
} else {
if (err) log.error(err)
if (err) LOG.error(err)
// Download the file if there is only one
if (files.length === 1) {
log.info(`${files[0]} is downloading...`)
LOG.info(`${files[0]} is downloading...`)
reply.download(`${downloadsDir}/${files[0]}`)
log.success(`${files[0]} downloaded`)
LOG.success(`${files[0]} downloaded`)
clean(downloadsDir, files)
} else {
log.info('Deleting previous archives...')
LOG.info('Deleting previous archives...')
const zipSlug = `leon-${request.query.domain}-${request.query.skill}`
const domainsFiles = fs.readdirSync(dlDomainDir)
@ -65,11 +65,11 @@ const getDownloads = async (fastify, options) => {
domainsFiles[i].indexOf(zipSlug) !== -1
) {
fs.unlinkSync(`${dlDomainDir}/${domainsFiles[i]}`)
log.success(`${domainsFiles[i]} archive deleted`)
LOG.success(`${domainsFiles[i]} archive deleted`)
}
}
log.info('Preparing new archive...')
LOG.info('Preparing new archive...')
const zipName = `${zipSlug}-${Date.now()}.zip`
const zipFile = `${dlDomainDir}/${zipName}`
const output = fs.createWriteStream(zipFile)
@ -77,35 +77,35 @@ const getDownloads = async (fastify, options) => {
// When the archive is ready
output.on('close', () => {
log.info(`${zipName} is downloading...`)
LOG.info(`${zipName} is downloading...`)
reply.download(zipFile, (err) => {
if (err) log.error(err)
if (err) LOG.error(err)
log.success(`${zipName} downloaded`)
LOG.success(`${zipName} downloaded`)
clean(downloadsDir, files)
})
})
archive.on('error', (err) => {
log.error(err)
LOG.error(err)
})
// Add the content to the archive
log.info('Adding content...')
LOG.info('Adding content...')
archive.directory(downloadsDir, false)
// Inject stream data to the archive
log.info('Injecting stream data...')
LOG.info('Injecting stream data...')
archive.pipe(output)
log.info('Finalizing...')
LOG.info('Finalizing...')
archive.finalize()
}
}
})
} else {
message = 'This skill does not exist.'
log.error(message)
LOG.error(message)
reply.code(404).send({
success: false,
status: 404,
@ -115,7 +115,7 @@ const getDownloads = async (fastify, options) => {
}
} else {
message = 'Bad request.'
log.error(message)
LOG.error(message)
reply.code(400).send({
success: false,
status: 400,

View File

@ -7,15 +7,15 @@ import {
STT_PROVIDER,
TTS_PROVIDER
} from '@/constants'
import { log } from '@/helpers/log'
import { LOG } from '@/helpers/log'
const getInfo = async (fastify, options) => {
fastify.get(`/api/${options.apiVersion}/info`, (request, reply) => {
log.title('GET /info')
LOG.title('GET /info')
const message = 'Information pulled.'
log.success(message)
LOG.success(message)
reply.send({
success: true,

View File

@ -1,10 +1,10 @@
import { log } from '@/helpers/log'
import { LOG } from '@/helpers/log'
const otherMidd = async (request, reply) => {
// Disable from the header, else it makes hacker's life easier to know more about our system
reply.removeHeader('X-Powered-By')
log.title('Requesting')
log.info(`${request.method} ${request.url}`)
LOG.title('Requesting')
LOG.info(`${request.method} ${request.url}`)
}
export default otherMidd

View File

@ -26,7 +26,7 @@ import otherMidd from '@/core/http-server/plugins/other'
import keyMidd from '@/core/http-server/plugins/key'
import infoPlugin from '@/core/http-server/api/info'
import downloadsPlugin from '@/core/http-server/api/downloads'
import { log } from '@/helpers/log'
import { LOG } from '@/helpers/log'
import { getTimeZone } from '@/helpers/date'
const server = {}
@ -61,7 +61,7 @@ const createProvider = async (id) => {
nlu
}
} catch (e) {
log[e.type](e.obj.message)
LOG[e.type](e.obj.message)
return null
}
@ -171,7 +171,7 @@ server.generateSkillsRoutes = (instance) => {
success: true
})
} catch (e) /* istanbul ignore next */ {
log[e.type](e.obj.message)
LOG[e.type](e.obj.message)
reply.statusCode = 500
reply.send({
...responseData,
@ -204,13 +204,13 @@ server.generateSkillsRoutes = (instance) => {
* Bootstrap socket
*/
server.handleOnConnection = (socket) => {
log.title('Client')
log.success('Connected')
LOG.title('Client')
LOG.success('Connected')
// Init
socket.on('init', async (data) => {
log.info(`Type: ${data}`)
log.info(`Socket id: ${socket.id}`)
LOG.info(`Type: ${data}`)
LOG.info(`Socket id: ${socket.id}`)
const provider = await addProvider(socket.id)
@ -226,8 +226,8 @@ server.handleOnConnection = (socket) => {
if (data === 'hotword-node') {
// Hotword triggered
socket.on('hotword-detected', (data) => {
log.title('Socket')
log.success(`Hotword ${data.hotword} detected`)
LOG.title('Socket')
LOG.success(`Hotword ${data.hotword} detected`)
socket.broadcast.emit('enable-record')
})
@ -252,14 +252,14 @@ server.handleOnConnection = (socket) => {
provider.brain.tts.init('en', () => null)
}
log.title('Initialization')
log.success(`STT ${sttState}`)
log.success(`TTS ${ttsState}`)
LOG.title('Initialization')
LOG.success(`STT ${sttState}`)
LOG.success(`TTS ${ttsState}`)
// Listen for new utterance
socket.on('utterance', async (data) => {
log.title('Socket')
log.info(`${data.client} emitted: ${data.value}`)
LOG.title('Socket')
LOG.info(`${data.client} emitted: ${data.value}`)
socket.emit('is-typing', true)
@ -276,7 +276,7 @@ server.handleOnConnection = (socket) => {
try {
await asr.run(data, provider.brain.stt)
} catch (e) {
log[e.type](e.obj.message)
LOG[e.type](e.obj.message)
}
})
}
@ -305,8 +305,8 @@ server.listen = async (port) => {
host: '0.0.0.0'
},
() => {
log.title('Initialization')
log.success(`Server is available at ${HOST}:${port}`)
LOG.title('Initialization')
LOG.success(`Server is available at ${HOST}:${port}`)
}
)
}
@ -363,7 +363,7 @@ server.bootstrap = async () => {
try {
await server.listen(PORT)
} catch (e) {
log.error(e.message)
LOG.error(e.message)
}
}
@ -374,14 +374,14 @@ server.init = async () => {
server.fastify.addHook('onRequest', corsMidd)
server.fastify.addHook('preValidation', otherMidd)
log.title('Initialization')
log.success(`The current env is ${process.env.LEON_NODE_ENV}`)
log.success(`The current version is ${version}`)
LOG.title('Initialization')
LOG.success(`The current env is ${process.env.LEON_NODE_ENV}`)
LOG.success(`The current version is ${version}`)
log.success(`The current time zone is ${getTimeZone()}`)
LOG.success(`The current time zone is ${getTimeZone()}`)
const sLogger = !HAS_LOGGER ? 'disabled' : 'enabled'
log.success(`Collaborative logger ${sLogger}`)
LOG.success(`Collaborative logger ${sLogger}`)
await addProvider('1')

View File

@ -5,22 +5,22 @@
*/
import fs from 'fs'
import { log } from '@/helpers/log'
import { LOG } from '@/helpers/log'
import { removeEndPunctuation, snakeToPascalCase } from '@/helpers/string'
class Ner {
constructor(ner) {
this.ner = ner
log.title('NER')
log.success('New instance')
LOG.title('NER')
LOG.success('New instance')
}
static logExtraction(entities) {
log.title('NER')
log.success('Entities found:')
LOG.title('NER')
LOG.success('Entities found:')
entities.forEach((ent) =>
log.success(`{ value: ${ent.sourceText}, entity: ${ent.entity} }`)
LOG.success(`{ value: ${ent.sourceText}, entity: ${ent.entity} }`)
)
}
@ -29,8 +29,8 @@ class Ner {
*/
extractEntities(lang, utteranceSamplesFilePath, obj) {
return new Promise(async (resolve) => {
log.title('NER')
log.info('Searching for entities...')
LOG.title('NER')
LOG.info('Searching for entities...')
const { classification } = obj
// Remove end-punctuation and add an end-whitespace
@ -84,8 +84,8 @@ class Ner {
return resolve(entities)
}
log.title('NER')
log.info('No entity found')
LOG.title('NER')
LOG.info('No entity found')
return resolve([])
})
}

View File

@ -17,7 +17,7 @@ import {
TCP_SERVER_PORT
} from '@/constants'
import Ner from '@/core/ner'
import { log } from '@/helpers/log'
import { LOG } from '@/helpers/log'
import { ucFirst } from '@/helpers/string'
import { getShortLanguages, getLongLanguageCode } from '@/helpers/lang'
import TcpClient from '@/core/tcp-client'
@ -51,8 +51,8 @@ class Nlu {
this.conv = new Conversation('conv0')
this.nluResultObj = defaultNluResultObj // TODO
log.title('NLU')
log.success('New instance')
LOG.title('NLU')
LOG.success('New instance')
}
/**
@ -61,7 +61,7 @@ class Nlu {
loadGlobalResolversModel(nlpModel) {
return new Promise(async (resolve, reject) => {
if (!fs.existsSync(nlpModel)) {
log.title('NLU')
LOG.title('NLU')
reject({
type: 'warning',
obj: new Error(
@ -69,7 +69,7 @@ class Nlu {
)
})
} else {
log.title('NLU')
LOG.title('NLU')
try {
const container = await containerBootstrap()
@ -82,7 +82,7 @@ class Nlu {
nluManager.settings.spellCheck = true
await this.globalResolversNlp.load(nlpModel)
log.success('Global resolvers NLP model loaded')
LOG.success('Global resolvers NLP model loaded')
resolve()
} catch (err) {
@ -107,7 +107,7 @@ class Nlu {
loadSkillsResolversModel(nlpModel) {
return new Promise(async (resolve, reject) => {
if (!fs.existsSync(nlpModel)) {
log.title('NLU')
LOG.title('NLU')
reject({
type: 'warning',
obj: new Error(
@ -126,7 +126,7 @@ class Nlu {
nluManager.settings.spellCheck = true
await this.skillsResolversNlp.load(nlpModel)
log.success('Skills resolvers NLP model loaded')
LOG.success('Skills resolvers NLP model loaded')
resolve()
} catch (err) {
@ -151,7 +151,7 @@ class Nlu {
loadMainModel(nlpModel) {
return new Promise(async (resolve, reject) => {
if (!fs.existsSync(nlpModel)) {
log.title('NLU')
LOG.title('NLU')
reject({
type: 'warning',
obj: new Error(
@ -177,7 +177,7 @@ class Nlu {
nluManager.settings.spellCheck = true
await this.mainNlp.load(nlpModel)
log.success('Main NLP model loaded')
LOG.success('Main NLP model loaded')
this.ner = new Ner(this.mainNlp.ner)
@ -362,11 +362,11 @@ class Nlu {
(intent.includes('resolver.global') ||
intent.includes(`resolver.${skillName}`))
) {
log.title('NLU')
log.success('Resolvers resolved:')
LOG.title('NLU')
LOG.success('Resolvers resolved:')
this.nluResultObj.resolvers = resolveResolvers(expectedItemName, intent)
this.nluResultObj.resolvers.forEach((resolver) =>
log.success(`${intent}: ${JSON.stringify(resolver)}`)
LOG.success(`${intent}: ${JSON.stringify(resolver)}`)
)
hasMatchingResolver = this.nluResultObj.resolvers.length > 0
}
@ -462,8 +462,8 @@ class Nlu {
const processingTimeStart = Date.now()
return new Promise(async (resolve, reject) => {
log.title('NLU')
log.info('Processing...')
LOG.title('NLU')
LOG.info('Processing...')
opts = opts || {
mute: false // Close Leon mouth e.g. over HTTP
@ -477,7 +477,7 @@ class Nlu {
const msg =
'The NLP model is missing, please rebuild the project or if you are in dev run: npm run train'
log.error(msg)
LOG.error(msg)
return reject(msg)
}
@ -570,9 +570,9 @@ class Nlu {
this.brain.socket.emit('is-typing', false)
}
log.title('NLU')
LOG.title('NLU')
const msg = 'Intent not found'
log.warning(msg)
LOG.warning(msg)
const processingTimeEnd = Date.now()
const processingTime = processingTimeEnd - processingTimeStart
@ -586,8 +586,8 @@ class Nlu {
this.nluResultObj = fallback
}
log.title('NLU')
log.success(
LOG.title('NLU')
LOG.success(
`Intent found: ${this.nluResultObj.classification.skill}.${this.nluResultObj.classification.action} (domain: ${this.nluResultObj.classification.domain})`
)
@ -607,8 +607,8 @@ class Nlu {
this.nluResultObj
)
} catch (e) /* istanbul ignore next */ {
if (log[e.type]) {
log[e.type](e.obj.message)
if (LOG[e.type]) {
LOG[e.type](e.obj.message)
}
if (!opts.mute) {
@ -684,7 +684,7 @@ class Nlu {
nluProcessingTime: processingTime - processedData?.executionTime // In ms, NLU processing time only
})
} catch (e) /* istanbul ignore next */ {
log[e.type](e.obj.message)
LOG[e.type](e.obj.message)
if (!opts.mute) {
this.brain.socket.emit('is-typing', false)
@ -829,7 +829,7 @@ class Nlu {
const words = this.nluResultObj.utterance.toLowerCase().split(' ')
if (fallbacks.length > 0) {
log.info('Looking for fallbacks...')
LOG.info('Looking for fallbacks...')
const tmpWords = []
for (let i = 0; i < fallbacks.length; i += 1) {
@ -846,7 +846,7 @@ class Nlu {
this.nluResultObj.classification.action = fallbacks[i].action
this.nluResultObj.classification.confidence = 1
log.success('Fallback found')
LOG.success('Fallback found')
return this.nluResultObj
}
}

View File

@ -3,7 +3,7 @@ import fs from 'fs'
import path from 'path'
import { waterfall } from 'async'
import { log } from '@/helpers/log'
import { LOG } from '@/helpers/log'
class Synchronizer {
constructor(brain, classification, sync) {
@ -12,8 +12,8 @@ class Synchronizer {
this.sync = sync
this.downloadDir = `${__dirname}/../../../downloads/${this.classification.domain}/${this.classification.skill}`
log.title('Synchronizer')
log.success('New instance')
LOG.title('Synchronizer')
LOG.success('New instance')
}
/**
@ -92,7 +92,7 @@ class Synchronizer {
(cb) => {
drive.files.list({}, (err, list) => {
if (err) {
log.error(`Error during listing: ${err}`)
LOG.error(`Error during listing: ${err}`)
return reject(err)
}
cb(null, list)
@ -121,7 +121,7 @@ class Synchronizer {
// Delete Drive files
/* setTimeout(() => {
drive.files.delete({ fileId: list.data.files[i].id })
log.title('Synchronizer'); log.success(`"${list.data.files[i].id}" deleted`)
LOG.title('Synchronizer'); LOG.success(`"${list.data.files[i].id}" deleted`)
}, 200 * i) */
}
@ -140,13 +140,13 @@ class Synchronizer {
},
(err, folder) => {
if (err) {
log.error(`Error during the folder creation: ${err}`)
LOG.error(`Error during the folder creation: ${err}`)
return reject(err)
}
folderId = folder.data.id
log.title('Synchronizer')
log.success(
LOG.title('Synchronizer')
LOG.success(
`"${driveFolderName}" folder created on Google Drive`
)
@ -165,12 +165,12 @@ class Synchronizer {
},
(err) => {
if (err) {
log.error(
LOG.error(
`Error during the folder permission creation: ${err}`
)
return reject(err)
}
log.success(`"${driveFolderName}" ownership transferred`)
LOG.success(`"${driveFolderName}" ownership transferred`)
cb(null, folderId)
return true
}
@ -200,14 +200,14 @@ class Synchronizer {
},
(err) => {
if (err) {
log.error(
LOG.error(
`Error during the "${entities[i]}" file creation: ${err}`
)
return reject(err)
}
iEntities += 1
log.title('Synchronizer')
log.success(`"${entities[i]}" file added to Google Drive`)
LOG.title('Synchronizer')
LOG.success(`"${entities[i]}" file added to Google Drive`)
if (iEntities === entities.length) {
cb(null)
}
@ -224,7 +224,7 @@ class Synchronizer {
],
(err) => {
if (err) {
log.error(err)
LOG.error(err)
return reject(err)
}
// Content available on Google Drive

View File

@ -2,7 +2,7 @@ import Net from 'net'
import { EventEmitter } from 'events'
import { IS_PRODUCTION_ENV } from '@/constants'
import { log } from '@/helpers/log'
import { LOG } from '@/helpers/log'
// Time interval between each try (in ms)
const INTERVAL = IS_PRODUCTION_ENV ? 3000 : 300
@ -20,51 +20,51 @@ export default class TcpClient {
this._status = this.tcpSocket.readyState
this._isConnected = false
log.title('TCP Client')
log.success('New instance')
LOG.title('TCP Client')
LOG.success('New instance')
this.tcpSocket.on('connect', () => {
log.title('TCP Client')
log.success(`Connected to the TCP server tcp://${this.host}:${this.port}`)
LOG.title('TCP Client')
LOG.success(`Connected to the TCP server tcp://${this.host}:${this.port}`)
this._isConnected = true
this._ee.emit('connected', null)
})
this.tcpSocket.on('data', (chunk) => {
log.title('TCP Client')
log.info(`Received data: ${chunk.toString()}`)
LOG.title('TCP Client')
LOG.info(`Received data: ${chunk.toString()}`)
const data = JSON.parse(chunk)
this._ee.emit(data.topic, data.data)
})
this.tcpSocket.on('error', (err) => {
log.title('TCP Client')
LOG.title('TCP Client')
if (err.code === 'ECONNREFUSED') {
this.reconnectCounter += 1
if (this.reconnectCounter >= RETRIES_NB) {
log.error('Failed to connect to the TCP server')
LOG.error('Failed to connect to the TCP server')
this.tcpSocket.end()
}
if (this.reconnectCounter >= 1) {
log.info('Trying to connect to the TCP server...')
LOG.info('Trying to connect to the TCP server...')
setTimeout(() => {
this.connect()
}, INTERVAL * this.reconnectCounter)
}
} else {
log.error(`Failed to connect to the TCP server: ${err}`)
LOG.error(`Failed to connect to the TCP server: ${err}`)
}
})
this.tcpSocket.on('end', () => {
log.title('TCP Client')
log.success('Disconnected from the TCP server')
LOG.title('TCP Client')
LOG.success('Disconnected from the TCP server')
})
setTimeout(() => {

View File

@ -3,7 +3,7 @@ import utc from 'dayjs/plugin/utc'
import timezone from 'dayjs/plugin/timezone'
import { TIME_ZONE } from '@/constants'
import { log } from '@/helpers/log'
import { LOG } from '@/helpers/log'
dayjs.extend(utc)
dayjs.extend(timezone)
@ -31,7 +31,7 @@ export function getTimeZone() {
Intl.DateTimeFormat(undefined, { timeZone: TIME_ZONE })
timeZone = TIME_ZONE
} catch (e) {
log.warning(
LOG.warning(
`The time zone "${TIME_ZONE}" is not valid. Falling back to "${timeZone}"`
)
}

View File

@ -13,6 +13,9 @@ export type Language = Languages[LongLanguageCode]
export type ShortLanguageCode = Language['short']
/**
* TODO
*/
export function getShortLanguages(): ShortLanguageCode[] {
const longLanguages = Object.keys(langs) as LongLanguageCode[]
return longLanguages.map((lang) => {
@ -20,19 +23,27 @@ export function getShortLanguages(): ShortLanguageCode[] {
})
}
/**
* TODO
*/
export function getLongLanguageCode(
shortLanguage: ShortLanguageCode
): LongLanguageCode | null {
for (const longLanguage in langs) {
const longLanguageType = longLanguage as LongLanguageCode
const lang = langs[longLanguageType]
if (lang.short === shortLanguage) {
return longLanguageType
}
}
return null
}
/**
* TODO
*/
export function getShortLanguageCode(
longLanguage: LongLanguageCode
): ShortLanguageCode {

View File

@ -1,16 +1,12 @@
import { Spinner } from 'cli-spinner'
import { log } from '@/helpers/log'
import { LOG } from '@/helpers/log'
function randomNumber(min: number, max: number): number {
return Math.floor(Math.random() * (max - min + 1)) + min
}
/**
* Loader Singleton Class.
*/
export class Loader {
private static instance: Loader
class Loader {
static readonly SENTENCES = [
'This process takes time, please go for a coffee (or a fruit juice)',
'This may take a while, grab a drink and come back later',
@ -19,6 +15,8 @@ export class Loader {
'Leon will be ready for you in a moment'
] as const
private static instance: Loader
private spinner = new Spinner('\x1b[95m%s\x1b[0m\r').setSpinnerString(18)
private interval: NodeJS.Timer | undefined = undefined
@ -27,28 +25,41 @@ export class Loader {
// Singleton
}
public static getInstance(): Loader {
/**
* TODO
*/
public static getInstance() {
if (Loader.instance == null) {
Loader.instance = new Loader()
}
return Loader.instance
}
public start(): void {
/**
* TODO
*/
public start() {
this.interval = setInterval(() => {
if (this.spinner.isSpinning()) {
const randomSentenceIndex = randomNumber(0, Loader.SENTENCES.length - 1)
const randomSentence = Loader.SENTENCES[randomSentenceIndex]
log.info(randomSentence ?? 'Loading...')
LOG.info(randomSentence ?? 'Loading...')
}
}, 60_000)
this.spinner.start()
}
public stop(): void {
/**
* TODO
*/
public stop() {
clearInterval(this.interval)
this.spinner.stop()
}
}
export const loader = Loader.getInstance()
export const LOADER = Loader.getInstance()

View File

@ -4,10 +4,7 @@ import path from 'node:path'
import { IS_TESTING_ENV } from '@/constants'
import { getDateTime } from '@/helpers/date'
/**
* Log Singleton Class.
*/
export class Log {
class Log {
static readonly ERRORS_PATH = path.join(
__dirname,
'..',
@ -16,37 +13,58 @@ export class Log {
'logs',
'errors.log'
)
private static instance: Log
private constructor() {
// Singleton
}
public static getInstance(): Log {
/**
* TODO
*/
public static getInstance() {
if (Log.instance == null) {
Log.instance = new Log()
}
return Log.instance
}
public success(value: string): void {
/**
* TODO
*/
public success(value: string) {
console.log('\x1b[32m✅ %s\x1b[0m', value)
}
public info(value: string): void {
/**
* TODO
*/
public info(value: string) {
console.info('\x1b[36m %s\x1b[0m', value)
}
public warning(value: string): void {
/**
* TODO
*/
public warning(value: string) {
console.warn('\x1b[33m⚠ %s\x1b[0m', value)
}
public debug(value: string): void {
/**
* TODO
*/
public debug(value: string) {
console.info('\u001b[35m🐞 [DEBUG] %s\x1b[0m', value)
}
public error(value: string): void {
/**
* TODO
*/
public error(value: string) {
const data = `${getDateTime()} - ${value}`
if (!IS_TESTING_ENV) {
if (fs.existsSync(Log.ERRORS_PATH)) {
fs.appendFileSync(Log.ERRORS_PATH, `\n${data}`)
@ -54,16 +72,23 @@ export class Log {
fs.writeFileSync(Log.ERRORS_PATH, data, { flag: 'wx' })
}
}
console.error('\x1b[31m🚨 %s\x1b[0m', value)
}
public title(value: string): void {
/**
* TODO
*/
public title(value: string) {
console.log('\n\n\x1b[7m.: %s :.\x1b[0m', value.toUpperCase())
}
public default(value: string): void {
/**
* TODO
*/
public default(value: string) {
console.log(value)
}
}
export const log = Log.getInstance()
export const LOG = Log.getInstance()

View File

@ -3,9 +3,9 @@ import fs from 'fs'
import { Model } from 'stt'
import { IS_TESTING_ENV } from '@/constants'
import { log } from '@/helpers/log'
import { LOG } from '@/helpers/log'
log.title('Coqui STT Parser')
LOG.title('Coqui STT Parser')
const parser = {}
let model = {}
@ -23,10 +23,10 @@ parser.conf = {
* Load models
*/
parser.init = (args) => {
log.info(`Loading model from file ${args.model}...`)
LOG.info(`Loading model from file ${args.model}...`)
if (!fs.existsSync(args.model)) {
log.error(
LOG.error(
`Cannot find ${args.model}. You can setup the offline STT by running: "npm run setup:offline-stt"`
)
@ -34,7 +34,7 @@ parser.init = (args) => {
}
if (!fs.existsSync(args.scorer)) {
log.error(
LOG.error(
`Cannot find ${args.scorer}. You can setup the offline STT by running: "npm run setup:offline-stt"`
)
@ -57,7 +57,7 @@ parser.init = (args) => {
}
}
log.success('Model loaded')
LOG.success('Model loaded')
return true
}
@ -69,7 +69,7 @@ parser.parse = (buffer, cb) => {
const wavDecode = wav.decode(buffer)
if (wavDecode.sampleRate < desiredSampleRate) {
log.warning(
LOG.warning(
`Original sample rate (${wavDecode.sampleRate}) is lower than ${desiredSampleRate}Hz. Up-sampling might produce erratic speech recognition`
)
}

View File

@ -2,9 +2,9 @@ import path from 'path'
import stt from '@google-cloud/speech'
import { LANG } from '@/constants'
import { log } from '@/helpers/log'
import { LOG } from '@/helpers/log'
log.title('Google Cloud STT Parser')
LOG.title('Google Cloud STT Parser')
const parser = {}
let client = {}
@ -28,9 +28,9 @@ parser.init = () => {
try {
client = new stt.SpeechClient()
log.success('Parser initialized')
LOG.success('Parser initialized')
} catch (e) {
log.error(`Google Cloud STT: ${e}`)
LOG.error(`Google Cloud STT: ${e}`)
}
}
@ -52,7 +52,7 @@ parser.parse = async (buffer, cb) => {
cb({ string })
} catch (e) {
log.error(`Google Cloud STT: ${e}`)
LOG.error(`Google Cloud STT: ${e}`)
}
}

View File

@ -3,7 +3,7 @@ import path from 'path'
import { IS_TESTING_ENV } from '@/constants'
import Asr from '@/core/asr'
import { log } from '@/helpers/log'
import { LOG } from '@/helpers/log'
class Stt {
constructor(socket, provider) {
@ -12,18 +12,18 @@ class Stt {
this.providers = ['google-cloud-stt', 'watson-stt', 'coqui-stt']
this.parser = {}
log.title('STT')
log.success('New instance')
LOG.title('STT')
LOG.success('New instance')
}
/**
* Initialize the STT provider
*/
init(cb) {
log.info('Initializing STT...')
LOG.info('Initializing STT...')
if (!this.providers.includes(this.provider)) {
log.error(
LOG.error(
`The STT provider "${this.provider}" does not exist or is not yet supported`
)
@ -45,7 +45,7 @@ class Stt {
'google-cloud.json'
) === -1
) {
log.warning(
LOG.warning(
`The "GOOGLE_APPLICATION_CREDENTIALS" env variable is already settled with the following value: "${process.env.GOOGLE_APPLICATION_CREDENTIALS}"`
)
}
@ -57,8 +57,8 @@ class Stt {
this.parser.default.init(this.parser.default.conf)
}
log.title('STT')
log.success('STT initialized')
LOG.title('STT')
LOG.success('STT initialized')
cb(this)
@ -77,17 +77,17 @@ class Stt {
}
})
log.success(`Parsing result: ${string}`)
LOG.success(`Parsing result: ${string}`)
}
/**
* Read the speech file and parse
*/
parse(file) {
log.info('Parsing WAVE file...')
LOG.info('Parsing WAVE file...')
if (!fs.existsSync(file)) {
log.error(`The WAVE file "${file}" does not exist`)
LOG.error(`The WAVE file "${file}" does not exist`)
return false
}

View File

@ -5,9 +5,9 @@ import path from 'path'
import { Duplex } from 'stream'
import { LANG } from '@/constants'
import { log } from '@/helpers/log'
import { LOG } from '@/helpers/log'
log.title('Watson STT Parser')
LOG.title('Watson STT Parser')
const parser = {}
let client = {}
@ -34,9 +34,9 @@ parser.init = () => {
serviceUrl: config.url
})
log.success('Parser initialized')
LOG.success('Parser initialized')
} catch (e) {
log.error(`Watson STT: ${e}`)
LOG.error(`Watson STT: ${e}`)
}
}
@ -59,12 +59,12 @@ parser.parse = async (buffer, cb) => {
cb({ string })
})
.catch((err) => {
log.error(`Watson STT: ${err}`)
LOG.error(`Watson STT: ${err}`)
})
client.recognize(parser.conf, (err, res) => {
if (err) {
log.error(`Watson STT: ${err}`)
LOG.error(`Watson STT: ${err}`)
} else {
const string = res.results
.map((data) => data.alternatives[0].transcript)

View File

@ -5,10 +5,10 @@ import { path as ffprobePath } from '@ffprobe-installer/ffprobe'
import fs from 'fs'
import path from 'path'
import { log } from '@/helpers/log'
import { LOG } from '@/helpers/log'
import { randomString } from '@/helpers/string'
log.title('Amazon Polly Synthesizer')
LOG.title('Amazon Polly Synthesizer')
const synthesizer = {}
const voices = {
@ -41,9 +41,9 @@ synthesizer.init = (lang) => {
try {
client = new Polly(config)
log.success('Synthesizer initialized')
LOG.success('Synthesizer initialized')
} catch (e) {
log.error(`Amazon Polly: ${e}`)
LOG.error(`Amazon Polly: ${e}`)
}
}
@ -69,7 +69,7 @@ synthesizer.save = (speech, em, cb) => {
// Get file duration thanks to ffprobe
ffmpeg.input(file).ffprobe((err, data) => {
if (err) log.error(err)
if (err) LOG.error(err)
else {
const duration = data.streams[0].duration * 1000
em.emit('saved', duration)
@ -79,16 +79,16 @@ synthesizer.save = (speech, em, cb) => {
})
wStream.on('error', (err) => {
log.error(`Amazon Polly: ${err}`)
LOG.error(`Amazon Polly: ${err}`)
})
})
.catch((err) => {
if (err.code === 'UnknownEndpoint') {
log.error(
LOG.error(
`Amazon Polly: the region "${err.region}" does not exist or does not support the Polly service`
)
} else {
log.error(`Amazon Polly: ${err.message}`)
LOG.error(`Amazon Polly: ${err.message}`)
}
})
}

View File

@ -4,10 +4,10 @@ import { path as ffmpegPath } from '@ffmpeg-installer/ffmpeg'
import { path as ffprobePath } from '@ffprobe-installer/ffprobe'
import fs from 'fs'
import { log } from '@/helpers/log'
import { LOG } from '@/helpers/log'
import { randomString } from '@/helpers/string'
log.title('Flite Synthesizer')
LOG.title('Flite Synthesizer')
const synthesizer = {}
@ -26,20 +26,20 @@ synthesizer.init = (lang) => {
/* istanbul ignore if */
if (lang !== 'en-US') {
log.warning(
LOG.warning(
'The Flite synthesizer only accepts the "en-US" language for the moment'
)
}
/* istanbul ignore if */
if (!fs.existsSync(flitePath)) {
log.error(
LOG.error(
`Cannot find ${flitePath} You can setup the offline TTS by running: "npm run setup:offline-tts"`
)
return false
}
log.success('Synthesizer initialized')
LOG.success('Synthesizer initialized')
return true
}
@ -66,7 +66,7 @@ synthesizer.save = (speech, em, cb) => {
/* istanbul ignore next */
// Handle error
process.stderr.on('data', (data) => {
log.error(data.toString())
LOG.error(data.toString())
})
process.stdout.on('end', () => {
@ -77,7 +77,7 @@ synthesizer.save = (speech, em, cb) => {
// Get file duration thanks to ffprobe
ffmpeg.input(file).ffprobe((err, data) => {
/* istanbul ignore if */
if (err) log.error(err)
if (err) LOG.error(err)
else {
const duration = data.streams[0].duration * 1000
em.emit('saved', duration)

View File

@ -5,10 +5,10 @@ import { path as ffprobePath } from '@ffprobe-installer/ffprobe'
import fs from 'fs'
import path from 'path'
import { log } from '@/helpers/log'
import { LOG } from '@/helpers/log'
import { randomString } from '@/helpers/string'
log.title('Google Cloud TTS Synthesizer')
LOG.title('Google Cloud TTS Synthesizer')
const synthesizer = {}
const voices = {
@ -47,9 +47,9 @@ synthesizer.init = (lang) => {
try {
client = new tts.TextToSpeechClient()
log.success('Synthesizer initialized')
LOG.success('Synthesizer initialized')
} catch (e) {
log.error(`Google Cloud TTS: ${e}`)
LOG.error(`Google Cloud TTS: ${e}`)
}
}
@ -63,13 +63,13 @@ synthesizer.save = (speech, em, cb) => {
client.synthesizeSpeech(synthesizer.conf, (err, res) => {
if (err) {
log.error(`Google Cloud TTS: ${err}`)
LOG.error(`Google Cloud TTS: ${err}`)
return
}
fs.writeFile(file, res.audioContent, 'binary', (err) => {
if (err) {
log.error(`Google Cloud TTS: ${err}`)
LOG.error(`Google Cloud TTS: ${err}`)
return
}
@ -79,7 +79,7 @@ synthesizer.save = (speech, em, cb) => {
// Get file duration thanks to ffprobe
ffmpeg.input(file).ffprobe((err, data) => {
if (err) log.error(err)
if (err) LOG.error(err)
else {
const duration = data.streams[0].duration * 1000
em.emit('saved', duration)

View File

@ -2,7 +2,7 @@ import events from 'events'
import fs from 'fs'
import path from 'path'
import { log } from '@/helpers/log'
import { LOG } from '@/helpers/log'
import { getLongLanguageCode } from '@/helpers/lang'
class Tts {
@ -15,20 +15,20 @@ class Tts {
this.speeches = []
this.lang = 'en'
log.title('TTS')
log.success('New instance')
LOG.title('TTS')
LOG.success('New instance')
}
/**
* Initialize the TTS provider
*/
init(newLang, cb) {
log.info('Initializing TTS...')
LOG.info('Initializing TTS...')
this.lang = newLang || this.lang
if (!this.providers.includes(this.provider)) {
log.error(
LOG.error(
`The TTS provider "${this.provider}" does not exist or is not yet supported`
)
@ -50,7 +50,7 @@ class Tts {
'google-cloud.json'
) === -1
) {
log.warning(
LOG.warning(
`The "GOOGLE_APPLICATION_CREDENTIALS" env variable is already settled with the following value: "${process.env.GOOGLE_APPLICATION_CREDENTIALS}"`
)
}
@ -61,8 +61,8 @@ class Tts {
this.onSaved()
log.title('TTS')
log.success('TTS initialized')
LOG.title('TTS')
LOG.success('TTS initialized')
cb(this)

View File

@ -6,10 +6,10 @@ import { path as ffprobePath } from '@ffprobe-installer/ffprobe'
import fs from 'fs'
import path from 'path'
import { log } from '@/helpers/log'
import { LOG } from '@/helpers/log'
import { randomString } from '@/helpers/string'
log.title('Watson TTS Synthesizer')
LOG.title('Watson TTS Synthesizer')
const synthesizer = {}
const voices = {
@ -45,9 +45,9 @@ synthesizer.init = (lang) => {
serviceUrl: config.url
})
log.success('Synthesizer initialized')
LOG.success('Synthesizer initialized')
} catch (e) {
log.error(`Watson TTS: ${e}`)
LOG.error(`Watson TTS: ${e}`)
}
}
@ -73,7 +73,7 @@ synthesizer.save = (speech, em, cb) => {
// Get file duration thanks to ffprobe
ffmpeg.input(file).ffprobe((err, data) => {
if (err) log.error(err)
if (err) LOG.error(err)
else {
const duration = data.streams[0].duration * 1000
em.emit('saved', duration)
@ -83,11 +83,11 @@ synthesizer.save = (speech, em, cb) => {
})
wStream.on('error', (err) => {
log.error(`Watson TTS: ${err}`)
LOG.error(`Watson TTS: ${err}`)
})
})
.catch((err) => {
log.error(`Watson TTS: ${err}`)
LOG.error(`Watson TTS: ${err}`)
})
}

View File

@ -1,4 +1,4 @@
import { loader } from '@/helpers/loader'
import { LOADER } from '@/helpers/loader'
jest.useFakeTimers()
@ -8,14 +8,14 @@ describe('loader helper', () => {
jest.spyOn(global, 'setInterval')
test('starts spinner', () => {
expect(loader.start()).toBeObject()
expect(LOADER.start()).toBeObject()
expect(setInterval).toHaveBeenCalledTimes(1)
})
})
describe('stop()', () => {
test('stops spinner', () => {
expect(loader.stop()).toBeObject()
expect(LOADER.stop()).toBeObject()
})
})
})

View File

@ -1,10 +1,10 @@
import { log } from '@/helpers/log'
import { LOG } from '@/helpers/log'
describe('log helper', () => {
describe('success()', () => {
test('logs success', () => {
console.log = jest.fn()
log.success('This is a success')
LOG.success('This is a success')
expect(console.log.mock.calls[0][1]).toBe('This is a success')
})
})
@ -12,7 +12,7 @@ describe('log helper', () => {
describe('info()', () => {
test('logs info', () => {
console.info = jest.fn()
log.info('This is an info')
LOG.info('This is an info')
expect(console.info.mock.calls[0][1]).toBe('This is an info')
})
})
@ -20,7 +20,7 @@ describe('log helper', () => {
describe('error()', () => {
test('logs error', () => {
console.error = jest.fn()
log.error('This is an error')
LOG.error('This is an error')
expect(console.error.mock.calls[0][1]).toBe('This is an error')
})
})
@ -28,7 +28,7 @@ describe('log helper', () => {
describe('warning()', () => {
test('logs warning', () => {
console.warn = jest.fn()
log.warning('This is a warning')
LOG.warning('This is a warning')
expect(console.warn.mock.calls[0][1]).toBe('This is a warning')
})
})
@ -36,7 +36,7 @@ describe('log helper', () => {
describe('title()', () => {
test('logs title', () => {
console.log = jest.fn()
log.title('This is a title')
LOG.title('This is a title')
expect(console.log.mock.calls[0][1]).toBe('THIS IS A TITLE')
})
})
@ -44,7 +44,7 @@ describe('log helper', () => {
describe('default()', () => {
test('logs default', () => {
console.log = jest.fn()
log.default('This is a default')
LOG.default('This is a default')
expect(console.log.mock.calls[0][1]).toBe('This is a default')
})
})