/**
 * Copyright 2017 Google Inc. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */ import * as _ from 'lodash' ; import * as speechAPI from '@google-cloud/speech' ; import * as translateAPI from '@google-cloud/translate' ; import * as db from './db' ; import * as ProfanityFilter from 'bad-words' ; const speech = speechAPI ( ) ; const translate = translateAPI ( ) ; const profanityFilter = new ProfanityFilter ( { replaceRegex : /[A-Za-z0-9가-힣_]/g } ) ; export function setDefaults ( userId ) { return db . set ( `/users/${ userId }` , { lang : { name : 'English' , code : 'en-US' } } ) ; } const speechFilenameRegex = /(\w*).([a-zA-Z\-]*).(\d*).raw/ ; export async function analyzeSpeech ( url , filename ) { const components = filename . match ( speechFilenameRegex ) ; if ( components == null ) { console . error ( 'Failed to parse filename ' + filename ) ; return ; } const userId = components [ 1 ] ; const languageCode = components [ 2 ] ; const timestamp = components [ 3 ] ; const request = { encoding : 'LINEAR16' , languageCode : languageCode , profanityFilter : true , } ; const results = await speech . recognize ( url , request ) ; let transcription = results [ 0 ] ; const scene = await db . get ( '/admin/current_scene' ) ; if ( transcription == '' ) { console . log ( 'Empty transcription, not written.' ) ; await markProcessCompleted ( userId , scene , timestamp ) ; return ; } let nouns = transcription . split ( ' ' ) ; await writeNounsAsGuesses ( nouns , userId , scene ) ; await markProcessCompleted ( userId , scene , timestamp ) ; } function writeNounsAsGuesses ( nouns , userId , scene ) { let operations = [ ] ; for ( let index in nouns ) { let noun = nouns [ index ] . toLowerCase ( ) ; operations . push ( db . set ( `/users/${ userId }/scenes/${ scene }/nouns/${ noun }` , 'maybe...' ) ) ; } return Promise . all ( operations ) ; } function markProcessCompleted ( userId , scene , timestamp ) { return db . remove ( `/users/${ userId }/scenes/${ scene }/in_progress/${ timestamp }` ) ; } export async function judgeGuessedNoun ( userId , scene , noun , guessed_before ) { noun = profanityFilter . clean ( noun ) . toLowerCase ( ) ; let lang = await getUserLanguage ( userId ) ; let english = await getOriginalNoun ( noun , scene , lang ) ; let correct = english !== null ? 'true' : 'false' ; let score_diff = correct === 'true' && ! guessed_before ? 1 : 0 ; return Promise . all ( [ updateAllGuesses ( scene , noun , correct , lang , english ) , updateCorrectness ( userId , scene , noun , correct ) , updateScore ( userId , scene , score_diff ) , updateSummary ( scene , english , lang , score_diff ) , ] ) ; } function getUserLanguage ( userId ) { return db . get ( `/users/${ userId }/lang/code` ) ; } async function getOriginalNoun ( noun , scene , lang ) { let nouns = await db . get ( `/admin/scenes/${ scene }/nouns/${ lang }` ) ; if ( ! _ . has ( nouns , noun ) ) { return null ; } return nouns [ noun ] ; } function updateAllGuesses ( scene , noun , correct , lang , english ) { return db . push ( `/all_guesses/${ scene }` , { original : noun , correctness : correct , lang : lang , translated : english , } ) ; } function updateCorrectness ( userId , scene , noun , correct ) { return db . set ( `/users/${ userId }/scenes/${ scene }/nouns/${ noun }` , correct ) ; } function updateScore ( userId , scene , diff ) { if ( diff === 0 ) return ; return db . transaction ( `/users/${ userId }/scenes/${ scene }/score` , val => val ? val + diff : diff ) ; } function updateSummary ( scene , english_noun , lang , score_diff ) { if ( score_diff <= 0 ) return ; return db . transaction ( `/summary/${ scene }/${ english_noun }` , val => { if ( val === null ) { val = { } ; } if ( val . langs === undefined || val . langs === null || val . langs === 0 ) { val . langs = { } ; } if ( ! _ . has ( val . langs , lang ) ) { val . langs [ lang ] = score_diff ; } else { val . langs [ lang ] += score_diff ; } if ( val . langs [ lang ] === 0 ) { delete val . langs [ lang ] ; } val . num_langs = _ . size ( val . langs ) ; if ( val . score === undefined || val . score === null ) { val . score = 0 ; } val . score += score_diff ; return val ; } ) ; } export async function updateCollectiveScores ( userId , scene , diff ) { let userLang = await getUserLanguage ( userId ) ; let operations = [ ] ; operations . push ( db . transaction ( `/total_scores/${ scene }` , val => val + diff ) ) ; operations . push ( db . transaction ( `/total_langs/${ scene }` , val => { if ( val === null ) { val = { } ; } if ( ! _ . has ( val , 'numLanguages' ) ) { val [ 'numLanguages' ] = 0 ; } if ( ! _ . has ( val , userLang ) || val [ userLang ] == 0 ) { val [ 'numLanguages' ] += 1 ; val [ userLang ] = 0 ; } val [ userLang ] += diff ; if ( val [ userLang ] <= 0 ) { val [ 'numLanguages' ] -= 1 ; val [ userLang ] = 0 ; } return val ; } ) ) ; await Promise . all ( operations ) ; } import bcp47toISO639 from './bcp47iso639' ; export function nounAdded ( scene , noun ) { let operations = [ ] ; _ . forEach ( bcp47toISO639 , ( iso639code , bcp47code ) => { if ( bcp47code == 'en-US' ) { return ; } let options = { from : 'en' , to : iso639code , } ; operations . push ( translate . translate ( noun , options ) . then ( results => { let translations = results [ 0 ] ; let translation = Array . isArray ( translations ) ? translations [ 0 ] : translations ; translation = translation . toLowerCase ( ) ; return db . set ( `/admin/scenes/${ scene }/nouns/${ bcp47code }/${ translation }` , noun ) ; } ) ) ; operations . push ( db . set ( `summary/${ scene }/${ noun }` , { num_langs : 0 , score : 0 } ) ) ; } ) ; return Promise . all ( operations ) ; }