diff --git a/src/Consts/FeatureFlags.consts.ts b/src/Consts/FeatureFlags.consts.ts index 495e440..31b1dda 100644 --- a/src/Consts/FeatureFlags.consts.ts +++ b/src/Consts/FeatureFlags.consts.ts @@ -1,3 +1,5 @@ export class FeatureFlagsConsts { static EnableEndgamePoints = 'EnableEndgamePoints'; + static DontMarkQuestionsAsCompleted = 'DontMarkQuestionsAsCompleted'; + static DisableVoice = 'DisableVoice'; } \ No newline at end of file diff --git a/src/app.module.ts b/src/app.module.ts index 5b0d4e9..6422730 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -45,6 +45,6 @@ import { FeatureflagService } from './featureflag/featureflag.service'; ], controllers: [AppController, FeatureflagController], providers: [AppService, SocketGateway, SchedulerService, FeatureflagService], - exports: [AppService, SocketGateway], + exports: [AppService, SocketGateway, FeatureflagService], }) export class AppModule {} diff --git a/src/featureflag/featureflag.service.ts b/src/featureflag/featureflag.service.ts index 3ab3e5b..a968439 100644 --- a/src/featureflag/featureflag.service.ts +++ b/src/featureflag/featureflag.service.ts @@ -1,5 +1,6 @@ import {Injectable, Logger} from '@nestjs/common'; import {SharedService} from "../shared/shared.service"; +import {SocketEvents} from "../shared/events.consts"; export interface IFeatureFlagStatus { name: string; @@ -13,17 +14,25 @@ export class FeatureflagService { } async getFeatureFlag(id: string): Promise { - this.logger.verbose(`Getting feature flag status for ${id}`); - const state = await this.sharedService.getConfig(`featureflag/${id}`); + this.logger.verbose(`[getFeatureFlag] Getting feature flag status for ${id}`); + const configRecord = await this.sharedService.getConfig(`featureflag/${id}`); + let ffState; + if(!configRecord) { + ffState = false; + } else { + ffState = configRecord.value !== 'false' + } + this.logger.verbose(`[getFeatureFlag] Feature flag status for ${id} is ${ffState}`); return { name: id, - state: state.value !== 'false', + state: ffState } } async setFeatureFlag(id: string, status: boolean) : Promise { this.logger.verbose(`Setting feature flag status for ${id} to ${status} `); const result = await this.sharedService.setConfig(`featureflag/${id}`, status.toString()); + this.sharedService.sendSocketNotificationToAllClients(SocketEvents.FEATURE_FLAG_CHANGED, {}); return { name: id, state: result.value !== 'false', diff --git a/src/quiz/quiz.service.ts b/src/quiz/quiz.service.ts index 42fb91a..208221f 100644 --- a/src/quiz/quiz.service.ts +++ b/src/quiz/quiz.service.ts @@ -16,6 +16,8 @@ import {CreateNewQueueItemCommand} from "../game/commands/create-new-queue-item. import {GameQueueTypes} from "../schemas/game-queue.schema"; import {IncreasePlayerWinningRateCommand} from "../game/commands/increase-player-winning-rate.command"; import {IncreasePlayerScoreCommand} from "../guests/command/increase-player-score.command"; +import {FeatureflagService} from "../featureflag/featureflag.service"; +import {FeatureFlagsConsts} from "../Consts/FeatureFlags.consts"; @Injectable({ scope: Scope.TRANSIENT }) export class QuizService { @@ -30,6 +32,7 @@ export class QuizService { private sharedService: SharedService, private eventBus: EventBus, private commandBus: CommandBus, + private featureFlagService: FeatureflagService, ) { } @@ -54,7 +57,6 @@ export class QuizService { async validateAnswer(answer: string, id: number) { this.logger.verbose(`enter validate answer ${answer} ${id}`); const question = await this.get(); - //question.answered = true; await question.save(); const regexp = new RegExp( Object.keys(this.answerNumbers) @@ -82,7 +84,6 @@ export class QuizService { await question.save(); this.logger.verbose("question saved with user details") if (question.valid === filtered) { - //question.answered = true; question.answeredBy = id; this.logger.verbose(`extra ${question.note}`); this.eventBus.publish( @@ -126,6 +127,11 @@ export class QuizService { private async calculateScore() { const question = await this.get(); + if(!await this.featureFlagService.getFeatureFlag(FeatureFlagsConsts.DontMarkQuestionsAsCompleted)) { + this.logger.verbose(`[proceedWithGame]: DontMarkQuestionsAsCompleted disabled, marking as complete`); + question.answered = true; + await question.save(); + } this.logger.verbose(`[calculateScore] enter `); const playerAnswers = question.userAnswers.map((answer) => { return { diff --git a/src/shared/events.consts.ts b/src/shared/events.consts.ts index b07930e..4806418 100644 --- a/src/shared/events.consts.ts +++ b/src/shared/events.consts.ts @@ -12,4 +12,5 @@ export enum SocketEvents { GAME_PAUSED = 'game_paused', GAME_RESUMED = 'game_resumed', NOTIFICATION = 'notification', + FEATURE_FLAG_CHANGED = 'feature_flag_changed', } diff --git a/src/shared/shared.module.ts b/src/shared/shared.module.ts index 4367356..7149884 100644 --- a/src/shared/shared.module.ts +++ b/src/shared/shared.module.ts @@ -8,6 +8,7 @@ import { ClientProxyFactory, Transport } from '@nestjs/microservices'; import * as process from "process"; import {ConfigModule} from "@nestjs/config"; import {CqrsModule} from "@nestjs/cqrs"; +import {FeatureflagService} from "../featureflag/featureflag.service"; @Global() @Module({ imports: [ @@ -17,7 +18,7 @@ import {CqrsModule} from "@nestjs/cqrs"; GameModule, MongooseModule.forFeature([{ name: Config.name, schema: ConfigSchema }]), ], - providers: [SharedService, { + providers: [SharedService,FeatureflagService, { provide: 'Telegram', useFactory: () => ClientProxyFactory.create({ @@ -31,7 +32,7 @@ import {CqrsModule} from "@nestjs/cqrs"; }, }), }], - exports: [SharedService, 'Telegram'], + exports: [SharedService, 'Telegram',FeatureflagService], }) export class SharedModule { constructor() { diff --git a/src/shared/shared.service.ts b/src/shared/shared.service.ts index 7cc7793..aec82bf 100644 --- a/src/shared/shared.service.ts +++ b/src/shared/shared.service.ts @@ -20,6 +20,9 @@ export class SharedService { key, }) .exec(); + if(!res) { + return null; + } return { key: res.key, value: res.value, diff --git a/src/voice/voice.controller.ts b/src/voice/voice.controller.ts index c28648a..c1fe6c7 100644 --- a/src/voice/voice.controller.ts +++ b/src/voice/voice.controller.ts @@ -1,24 +1,39 @@ -import { Controller, Get, Header, NotFoundException, Query, StreamableFile } from '@nestjs/common'; +import {Controller, Get, Header, Logger, NotFoundException, Query, StreamableFile} from '@nestjs/common'; import { VoiceService } from './voice.service'; import { TtsRequestDto, TtsRequestWithVars } from './models/TtsRequestDto'; import { invalidPrefixDict } from './dicts/invalid-prefix.dict'; import { validPrefixDict } from './dicts/valid-prefix.dict'; import * as translit from 'latin-to-cyrillic'; import {ConfigService} from "@nestjs/config"; +import {FeatureflagService} from "../featureflag/featureflag.service"; +import {FeatureFlagsConsts} from "../Consts/FeatureFlags.consts"; @Controller('voice') export class VoiceController { - constructor(private voiceService: VoiceService, private configService: ConfigService) {} + private voiceEnabled = false; + private logger = new Logger(VoiceController.name) + constructor(private voiceService: VoiceService, private configService: ConfigService,private featureFlagService: FeatureflagService) { + setInterval(() => { + this.featureFlagService.getFeatureFlag(FeatureFlagsConsts.DisableVoice).then(r => this.voiceEnabled = !r.state); + }, 30000); + this.featureFlagService.getFeatureFlag(FeatureFlagsConsts.DisableVoice).then(r => { + this.voiceEnabled = !r.state; + console.log(`Voice enabled: ${this.voiceEnabled}`); + }); + + } @Get('ssml') @Header('content-type', 'audio/opus') @Header('content-disposition', 'inline') async textToSpeechSSML(@Query() dto: TtsRequestDto) { - if (Boolean(this.configService.get('ENABLE_VOICE')) === true) { - //return new StreamableFile(await this.voiceService.textToFile(dto, true)); + this.logger.verbose(`[textToSpeechSSML] enter, FF state is: ${this.voiceEnabled}`); + if (this.voiceEnabled) { + return new StreamableFile(await this.voiceService.textToFile(dto, true)); } else { return new NotFoundException('Voice disabled'); } + } @Get('tts') @@ -26,8 +41,8 @@ export class VoiceController { @Header('content-disposition', 'inline') async getText(@Query() dto: TtsRequestDto) { dto.text = translit(dto.text); - if (Boolean(this.configService.get('ENABLE_VOICE')) === true) { - //return new StreamableFile(await this.voiceService.textToFile(dto)); + if (this.voiceEnabled) { + return new StreamableFile(await this.voiceService.textToFile(dto)); } else { return new NotFoundException('Voice disabled'); } @@ -36,8 +51,7 @@ export class VoiceController { @Header('content-type', 'audio/opus') @Header('content-disposition', 'inline') async announceValid(@Query() dto: TtsRequestWithVars) { - console.log(this.configService.get('ENABLE_VOICE')); - if (Boolean(this.configService.get('ENABLE_VOICE')) === true) { + if (this.voiceEnabled) { const vars = JSON.parse(dto.vars); dto.text = this.voiceService.buildTemplate(dto, vars, validPrefixDict); return new StreamableFile(await this.voiceService.textToFile(dto)); @@ -50,7 +64,7 @@ export class VoiceController { @Header('content-type', 'audio/opus') @Header('content-disposition', 'inline') async announceInvalid(@Query() dto: TtsRequestWithVars) { - if (Boolean(this.configService.get('ENABLE_VOICE')) === true) { + if (this.voiceEnabled) { const vars = JSON.parse(dto.vars); dto.text = this.voiceService.buildTemplate(dto, vars, invalidPrefixDict); return new StreamableFile(await this.voiceService.textToFile(dto)); diff --git a/src/voice/voice.service.ts b/src/voice/voice.service.ts index 78cc2dd..b8d05cb 100644 --- a/src/voice/voice.service.ts +++ b/src/voice/voice.service.ts @@ -7,12 +7,13 @@ import { AxiosRequestConfig } from 'axios'; import * as translit from 'latin-to-cyrillic'; import { TGD_Config } from 'app.config'; import {ConfigService} from "@nestjs/config"; +import {FeatureflagService} from "../featureflag/featureflag.service"; @Injectable() export class VoiceService { private apiUrl = 'https://tts.api.cloud.yandex.net/speech/v1/tts:synthesize'; private apiKey: string; private readonly logger = new Logger(VoiceService.name); - constructor(private httpService: HttpService, private configService: ConfigService) { + constructor(private httpService: HttpService, private configService: ConfigService, private featureFlagService: FeatureflagService) { this.apiKey = this.configService.get("VOICE_APIKEY"); }