This commit is contained in:
Kirill Ivlev 2024-11-20 14:24:46 +04:00
parent 7030a19b03
commit d18d12a458
25 changed files with 249 additions and 194 deletions

View file

@ -0,0 +1,5 @@
export class GameStateConsts {
static Main = 'main';
static EndgamePoints = 'endgamepoints';
static Finish = 'finish';
}

65
src/Consts/types.d.ts vendored Normal file
View file

@ -0,0 +1,65 @@
import {GameQueueTypes} from "../schemas/game-queue.schema";
export interface IStateInfo {
state:string;
value:string;
}
export interface IValidAnswerReceivedSocketEvent {
telegramId: number;
validAnswer: string;
note: string;
}
export interface IUserInfoMinimal {
telegramId: number;
}
export interface IUserBasicInfo extends IUserInfoMinimal {
name: string;
}
export interface IVersusBeginSocketEvent {
player1: number;
player2: number;
player1name: string;
player2name: string;
}
export interface IVersusEndSocketEvent {
winner: number;
}
export interface IScoreChangedSocketEvent extends IUserInfoMinimal {
newScore: number;
}
export interface IUserCardChangedEvent extends IUserInfoMinimal {
cards: string[];
}
export interface IEmptyNotification {}
export interface ISocketNotificationEvent {
text: string;
timeout: number;
}
export interface IUserPropertyChangedEvent {
user: number;
property: string;
value: string;
}
export interface ICardPlayedSocketEvent extends IUserInfoMinimal{
card: string;
name: string;
timeout: number;
}
export interface IGameQueueSocketEvent {
_id: any;
completed: boolean;
target: number;
type: GameQueueTypes;
text: string;
}

View file

@ -1,6 +1,6 @@
import {Injectable, Logger} from '@nestjs/common'; import {Injectable, Logger} from '@nestjs/common';
import {SharedService} from "../shared/shared.service"; import {SharedService} from "../shared/shared.service";
import {SocketEvents} from "../shared/events.consts"; import {ClientNotificationType} from "../socket/socket.gateway";
export interface IFeatureFlagStatus { export interface IFeatureFlagStatus {
name: string; name: string;
@ -32,11 +32,12 @@ export class FeatureflagService {
async setFeatureFlag(id: string, status: boolean) : Promise<IFeatureFlagStatus> { async setFeatureFlag(id: string, status: boolean) : Promise<IFeatureFlagStatus> {
this.logger.verbose(`Setting feature flag status for ${id} to ${status} `); this.logger.verbose(`Setting feature flag status for ${id} to ${status} `);
const result = await this.sharedService.setConfig(`featureflag/${id}`, status.toString()); const result = await this.sharedService.setConfig(`featureflag/${id}`, status.toString());
this.sharedService.sendSocketNotificationToAllClients(SocketEvents.FEATURE_FLAG_CHANGED, {}); const ffStatus: IFeatureFlagStatus = {
return {
name: id, name: id,
state: result.value !== 'false', state: result.value !== 'false',
} }
this.sharedService.notifyAllClients<IFeatureFlagStatus>(ClientNotificationType.FeatureFlagChanged, ffStatus);
return ffStatus;
} }
} }

View file

@ -1,13 +1,14 @@
import { CommandBus, CommandHandler, EventBus, ICommandHandler } from '@nestjs/cqrs'; import {CommandBus, CommandHandler, EventBus, ICommandHandler} from '@nestjs/cqrs';
import { ProceedGameQueueCommand } from '../commands/proceed-game-queue.command'; import {ProceedGameQueueCommand} from '../commands/proceed-game-queue.command';
import { GameService } from '../game.service'; import {GameService} from '../game.service';
import { NextQuestionCommand } from '../commands/next-question.command'; import {NextQuestionCommand} from '../commands/next-question.command';
import { SharedService } from '../../shared/shared.service'; import {SharedService} from '../../shared/shared.service';
import { SocketEvents } from '../../shared/events.consts'; import {Logger} from '@nestjs/common';
import { Logger } from '@nestjs/common'; import {GameQueueTypes} from '../../schemas/game-queue.schema';
import { GameQueueTypes } from '../../schemas/game-queue.schema'; import {QuizAnswerStateChangedEvent} from '../events/quiz-answer-state-changed.event';
import { QuizAnswerStateChangedEvent } from '../events/quiz-answer-state-changed.event'; import {QuizAnswerStateEnum} from '../entities/quiz-answer-state.enum';
import { QuizAnswerStateEnum } from '../entities/quiz-answer-state.enum'; import {IGameQueueSocketEvent} from "../../Consts/types";
import {ClientNotificationType} from "../../socket/socket.gateway";
@CommandHandler(ProceedGameQueueCommand) @CommandHandler(ProceedGameQueueCommand)
export class GameProceedGameQueueCommandHandler export class GameProceedGameQueueCommandHandler
@ -25,16 +26,13 @@ export class GameProceedGameQueueCommandHandler
if (!item) { if (!item) {
return this.cmdBus.execute(new NextQuestionCommand()); return this.cmdBus.execute(new NextQuestionCommand());
} }
this.sharedService.sendSocketNotificationToAllClients( this.sharedService.notifyAllClients<IGameQueueSocketEvent>(ClientNotificationType.GameQueueItem, {
SocketEvents.GameQueueItem, _id: item.id,
{ completed: item.completed,
_id: item.id, target: item.target,
completed: item.completed, type: item.type,
target: item.target, text: item.text
type: item.type, });
text: item.text
},
);
switch (item.type) { switch (item.type) {
case GameQueueTypes.giveOutAPrize: case GameQueueTypes.giveOutAPrize:
this.eventBus.publish( this.eventBus.publish(

View file

@ -1,19 +1,15 @@
import {Injectable, InternalServerErrorException, Logger, OnApplicationBootstrap} from '@nestjs/common'; import {Injectable, InternalServerErrorException, Logger, OnApplicationBootstrap} from '@nestjs/common';
import {CommandBus, EventBus, QueryBus} from '@nestjs/cqrs'; import {CommandBus, EventBus, QueryBus} from '@nestjs/cqrs';
import { CardSelectionTimeExceedCommand } from './commands/card-selection-time-exceed.command'; import {CardSelectionTimeExceedCommand} from './commands/card-selection-time-exceed.command';
import { InjectModel } from '@nestjs/mongoose'; import {InjectModel} from '@nestjs/mongoose';
import { import {GameQueue, GameQueueDocument, GameQueueTypes,} from '../schemas/game-queue.schema';
GameQueue,
GameQueueDocument,
GameQueueTypes,
} from '../schemas/game-queue.schema';
import {Model, Promise} from 'mongoose'; import {Model, Promise} from 'mongoose';
import { ProceedGameQueueCommand } from './commands/proceed-game-queue.command'; import {ProceedGameQueueCommand} from './commands/proceed-game-queue.command';
import { SharedService } from '../shared/shared.service'; import {SharedService} from '../shared/shared.service';
import { SocketEvents } from '../shared/events.consts';
import {ConfigService} from "@nestjs/config"; import {ConfigService} from "@nestjs/config";
import {gameCards} from "./entities/cards.entities"; import {gameCards} from "./entities/cards.entities";
import {GuestsService} from "../guests/guests.service"; import {IEmptyNotification} from "../Consts/types";
import {ClientNotificationType} from "../socket/socket.gateway";
@Injectable() @Injectable()
export class GameService implements OnApplicationBootstrap{ export class GameService implements OnApplicationBootstrap{
@ -27,7 +23,6 @@ export class GameService implements OnApplicationBootstrap{
private sharedService: SharedService, private sharedService: SharedService,
private commandBus: CommandBus, private commandBus: CommandBus,
private queryBus: QueryBus, private queryBus: QueryBus,
private guestService: GuestsService,
) { ) {
} }
@ -72,29 +67,20 @@ export class GameService implements OnApplicationBootstrap{
} }
qItem.completed = true; qItem.completed = true;
await qItem.save(); await qItem.save();
this.sharedService.sendSocketNotificationToAllClients( this.sharedService.notifyAllClients<IEmptyNotification>(ClientNotificationType.QueueCompleted, {});
SocketEvents.QUEUE_COMPLETED,
{},
);
await this.cmdBus.execute(new ProceedGameQueueCommand()); await this.cmdBus.execute(new ProceedGameQueueCommand());
return qItem; return qItem;
} }
async pauseGame() { async pauseGame() {
await this.sharedService.setConfig('game_state', 'paused'); await this.sharedService.setConfig('game_state', 'paused');
await this.sharedService.sendSocketNotificationToAllClients( this.sharedService.notifyAllClients<IEmptyNotification>(ClientNotificationType.GamePaused, {});
SocketEvents.GAME_PAUSED,
{},
);
return Promise.resolve({ result: true }); return Promise.resolve({ result: true });
} }
async resumeGame() { async resumeGame() {
await this.sharedService.setConfig('game_state', 'running'); await this.sharedService.setConfig('game_state', 'running');
await this.sharedService.sendSocketNotificationToAllClients( this.sharedService.notifyAllClients<IEmptyNotification>(ClientNotificationType.GameResumed,{});
SocketEvents.GAME_RESUMED,
{},
);
return Promise.resolve({ result: true }); return Promise.resolve({ result: true });
} }

View file

@ -1,18 +1,18 @@
import {Injectable, Logger} from '@nestjs/common'; import {Injectable, Logger} from '@nestjs/common';
import {SocketEvents} from "../../shared/events.consts";
import {GuestsService} from "../../guests/guests.service"; import {GuestsService} from "../../guests/guests.service";
import {SharedService} from "../../shared/shared.service"; import {SharedService} from "../../shared/shared.service";
import {InjectModel} from "@nestjs/mongoose"; import {InjectModel} from "@nestjs/mongoose";
import {Versus, VersusDocument} from "../../schemas/versus.schema"; import {Versus, VersusDocument} from "../../schemas/versus.schema";
import {Model, Query} from "mongoose"; import {Model} from "mongoose";
import {VersusDto} from "./versus.types"; import {VersusDto} from "./versus.types";
import {CommandBus, QueryBus} from "@nestjs/cqrs"; import {CommandBus, QueryBus} from "@nestjs/cqrs";
import {IncreasePlayerScoreCommand} from "../../guests/command/increase-player-score.command"; import {IncreasePlayerScoreCommand} from "../../guests/command/increase-player-score.command";
import {IncreasePlayerWinningRateCommand} from "../commands/increase-player-winning-rate.command"; import {IncreasePlayerWinningRateCommand} from "../commands/increase-player-winning-rate.command";
import {GetGuestQuery} from "../../guests/queries/getguest.query";
import {GetGuestPropertyQuery} from "../../guests/command/get-guest-property.handler"; import {GetGuestPropertyQuery} from "../../guests/command/get-guest-property.handler";
import {GuestPropertyNamesConsts} from "../../Consts/guest-property-names.consts"; import {GuestPropertyNamesConsts} from "../../Consts/guest-property-names.consts";
import {SetGuestPropertyCommand} from "../../guests/command/set-guest-property.command"; import {SetGuestPropertyCommand} from "../../guests/command/set-guest-property.command";
import {IVersusBeginSocketEvent, IVersusEndSocketEvent} from "../../Consts/types";
import {ClientNotificationType} from "../../socket/socket.gateway";
@Injectable() @Injectable()
export class VersusService { export class VersusService {
@ -51,10 +51,12 @@ export class VersusService {
player2name: p2data.name, player2name: p2data.name,
} }
})); }));
this.sharedService.sendSocketNotificationToAllClients( this.sharedService.notifyAllClients<IVersusBeginSocketEvent>(ClientNotificationType.BeginVersus, {
SocketEvents.BEGIN_VERSUS, player1,
{ player1, player2, player1name: p1data.name, player2name: p2data.name } player2,
) player1name: p1data.name,
player2name: p2data.name
});
} }
async importVersus(data: VersusDto[]) { async importVersus(data: VersusDto[]) {
@ -111,11 +113,10 @@ export class VersusService {
this.logger.verbose(`Set win count for ${winner} to ${wonCount}`); this.logger.verbose(`Set win count for ${winner} to ${wonCount}`);
tasks.push(await this.cmdBus.execute(new SetGuestPropertyCommand(winner, GuestPropertyNamesConsts.VersusWonCount, wonCount.toString))); tasks.push(await this.cmdBus.execute(new SetGuestPropertyCommand(winner, GuestPropertyNamesConsts.VersusWonCount, wonCount.toString)));
await Promise.all(tasks); await Promise.all(tasks);
this.sharedService.sendSocketNotificationToAllClients( this.sharedService.notifyAllClients<IVersusEndSocketEvent>(ClientNotificationType.EndVersus, {
SocketEvents.END_VERSUS, winner: winner
{ winner: winner } }
) );
return item; return item;
} }

View file

@ -1,15 +1,15 @@
import {Controller, Logger} from "@nestjs/common"; import {Controller, Logger} from "@nestjs/common";
import {Ctx, EventPattern, MessagePattern, Payload, RmqContext} from "@nestjs/microservices"; import {Ctx, MessagePattern, Payload, RmqContext} from "@nestjs/microservices";
import {GetGuestInfoModel} from "./models/get-guest-info.model"; import {GetGuestInfoModel} from "./models/get-guest-info.model";
import {GuestsService} from "../guests/guests.service"; import {GuestsService} from "../guests/guests.service";
import {RegisterUserModel} from "./models/register-user.model"; import {RegisterUserModel} from "./models/register-user.model";
import {SharedService} from "../shared/shared.service"; import {SharedService} from "../shared/shared.service";
import {SocketEvents} from "../shared/events.consts";
import {CommandsConsts} from "../Consts/commands.consts"; import {CommandsConsts} from "../Consts/commands.consts";
import {EventBus} from "@nestjs/cqrs"; import {EventBus} from "@nestjs/cqrs";
import {PlayerCardSelectedEvent} from "../game/events/player-card-selected.event"; import {PlayerCardSelectedEvent} from "../game/events/player-card-selected.event";
import {getCard} from "../helpers/card-parser"; import {getCard} from "../helpers/card-parser";
import {QuizService} from "../quiz/quiz.service"; import {QuizService} from "../quiz/quiz.service";
import {ClientNotificationType} from "../socket/socket.gateway";
@Controller() @Controller()
export class GuestsMessageController { export class GuestsMessageController {
@ -49,7 +49,7 @@ export class GuestsMessageController {
@MessagePattern({ cmd: CommandsConsts.PhotoUpdated }) @MessagePattern({ cmd: CommandsConsts.PhotoUpdated })
async photoUpdated(@Payload() data: { id: number}) { async photoUpdated(@Payload() data: { id: number}) {
this.logger.verbose(`Photo updated event, send notification`); this.logger.verbose(`Photo updated event, send notification`);
this.sharedService.sendSocketNotificationToAllClients(SocketEvents.PHOTOS_UPDATED_EVENT, data); this.sharedService.notifyAllClients<{id: number}>(ClientNotificationType.PhotosUpdated, data)
} }
@MessagePattern({ cmd: CommandsConsts.CardPlayed }) @MessagePattern({ cmd: CommandsConsts.CardPlayed })

View file

@ -1,5 +1,5 @@
export const SharedServiceMock = { export const SharedServiceMock = {
setConfig: jest.fn(), setConfig: jest.fn(),
getConfig: jest.fn(), getConfig: jest.fn(),
sendSocketNotificationToAllClients: jest.fn(), notifyAllClients: jest.fn(),
} }

View file

@ -51,7 +51,7 @@ export class QuizController {
return this.quizService.dealPrize(); return this.quizService.dealPrize();
} }
@Get('endgame-extrapoints') @Post('calculate-endgame-extrapoints')
async endgameExtrapoints() async endgameExtrapoints()
{ {
return await this.quizService.calculateEndgamePoints(); return await this.quizService.calculateEndgamePoints();

View file

@ -19,6 +19,7 @@ import {IncreasePlayerScoreCommand} from "../guests/command/increase-player-scor
import {FeatureflagService} from "../featureflag/featureflag.service"; import {FeatureflagService} from "../featureflag/featureflag.service";
import {FeatureFlagsConsts} from "../Consts/FeatureFlags.consts"; import {FeatureFlagsConsts} from "../Consts/FeatureFlags.consts";
import {QuizEndGameResults} from "./quiz.types"; import {QuizEndGameResults} from "./quiz.types";
import {ClientNotificationType} from "../socket/socket.gateway";
@Injectable({ scope: Scope.TRANSIENT }) @Injectable({ scope: Scope.TRANSIENT })
export class QuizService { export class QuizService {
@ -48,10 +49,7 @@ export class QuizService {
await item.save(); await item.save();
this.logger.verbose(`Question updated`); this.logger.verbose(`Question updated`);
await this.guestService.postQuestion(questionDto, target); await this.guestService.postQuestion(questionDto, target);
this.sharedService.sendSocketNotificationToAllClients( this.sharedService.notifyAllClients<QuestionDto>(ClientNotificationType.QuestionChanged, questionDto);
'question_changed',
questionDto,
);
return item.save(); return item.save();
} }
@ -171,7 +169,7 @@ export class QuizService {
//const { maxRewards, maxInvalidAnswers } = Promise.all([maxRewardsPromise, maxInvalidAnswersPromise]); //const { maxRewards, maxInvalidAnswers } = Promise.all([maxRewardsPromise, maxInvalidAnswersPromise]);
const [maxRewards, maxInvalidAnswers, maxPenaltiesReceived] = await Promise.all([maxRewardsPromise, maxInvalidAnswersPromise, maxPenaltiesPromise]); const [maxRewards, maxInvalidAnswers, maxPenaltiesReceived] = await Promise.all([maxRewardsPromise, maxInvalidAnswersPromise, maxPenaltiesPromise]);
return { const result = {
maxInvalidAnswers: { maxInvalidAnswers: {
id: maxInvalidAnswers[0].id, id: maxInvalidAnswers[0].id,
count: maxInvalidAnswers[0].invalidAnswers, count: maxInvalidAnswers[0].invalidAnswers,
@ -188,6 +186,8 @@ export class QuizService {
name: maxPenaltiesReceived[0].name, name: maxPenaltiesReceived[0].name,
} }
} }
await this.sharedService.setConfig('endgame-points', JSON.stringify(result));
return result;
} }
private async getNextQuestion() { private async getNextQuestion() {

View file

@ -49,7 +49,7 @@ describe('SchedulerService', () => {
it('should finish game if prizes count is 0', async () => { it('should finish game if prizes count is 0', async () => {
const getRemainingPrizeCountFn = jest.spyOn(giftService, 'getRemainingPrizeCount').mockImplementation(() => Promise.resolve(0)); const getRemainingPrizeCountFn = jest.spyOn(giftService, 'getRemainingPrizeCount').mockImplementation(() => Promise.resolve(0));
const notificationFn = jest.spyOn(sharedService,'sendSocketNotificationToAllClients').mockImplementation(); const notificationFn = jest.spyOn(sharedService,'notifyAllClients').mockImplementation();
const setStateFn = jest.spyOn(stateService,'setState').mockImplementation((name,newstate) => Promise.resolve({ state: name, value: newstate})); const setStateFn = jest.spyOn(stateService,'setState').mockImplementation((name,newstate) => Promise.resolve({ state: name, value: newstate}));
await service.gameStatus(); await service.gameStatus();
expect(getRemainingPrizeCountFn).toHaveBeenCalled(); expect(getRemainingPrizeCountFn).toHaveBeenCalled();
@ -59,7 +59,7 @@ describe('SchedulerService', () => {
it('should not finish game if prizes count above 0', async () => { it('should not finish game if prizes count above 0', async () => {
const getRemainingPrizeCountFn = jest.spyOn(giftService, 'getRemainingPrizeCount').mockImplementation(() => Promise.resolve(5)); const getRemainingPrizeCountFn = jest.spyOn(giftService, 'getRemainingPrizeCount').mockImplementation(() => Promise.resolve(5));
const notificationFn = jest.spyOn(sharedService,'sendSocketNotificationToAllClients').mockImplementation() const notificationFn = jest.spyOn(sharedService,'notifyAllClients').mockImplementation()
await service.gameStatus(); await service.gameStatus();
expect(notificationFn).not.toHaveBeenCalled(); expect(notificationFn).not.toHaveBeenCalled();
}); });

View file

@ -7,9 +7,9 @@ import {SharedService} from '../shared/shared.service';
import {FeatureflagService} from "../featureflag/featureflag.service"; import {FeatureflagService} from "../featureflag/featureflag.service";
import {FeatureFlagsConsts} from "../Consts/FeatureFlags.consts"; import {FeatureFlagsConsts} from "../Consts/FeatureFlags.consts";
import {CommandBus} from "@nestjs/cqrs"; import {CommandBus} from "@nestjs/cqrs";
import {EndgameDict} from "../voice/dicts/endgame.dict"; import {GameStateConsts} from "../Consts/game-state.consts";
import {CreateNewQueueItemCommand} from "../game/commands/create-new-queue-item.command"; import {IStateInfo} from "../Consts/types";
import {GameQueueTypes} from "../schemas/game-queue.schema"; import {ClientNotificationType} from "../socket/socket.gateway";
@Injectable() @Injectable()
export class SchedulerService { export class SchedulerService {
@ -37,24 +37,11 @@ export class SchedulerService {
if(await this.featureFlagService.getFeatureFlag(FeatureFlagsConsts.EnableEndgamePoints)) { if(await this.featureFlagService.getFeatureFlag(FeatureFlagsConsts.EnableEndgamePoints)) {
this.logger.verbose(`Feature flag ${FeatureFlagsConsts.EnableEndgamePoints} is enabled`); this.logger.verbose(`Feature flag ${FeatureFlagsConsts.EnableEndgamePoints} is enabled`);
const endgamePoints = await this.quizService.calculateEndgamePoints(); const endgamePoints = await this.quizService.calculateEndgamePoints();
await Promise.all([ const state = await this.stateService.setState(GameStateConsts.Main, GameStateConsts.EndgamePoints);
this.commandBus.execute( this.sharedService.notifyAllClients<IStateInfo>(ClientNotificationType.StateChanged, state);
new CreateNewQueueItemCommand(endgamePoints.maxInvalidAnswers.id,
GameQueueTypes.extra_points,
EndgameDict.maxAmountOfInvalidQuestions)),
new CreateNewQueueItemCommand(endgamePoints.maxPenalties.id,
GameQueueTypes.extra_points,
EndgameDict.maxPenalties),
new CreateNewQueueItemCommand(endgamePoints.maxRewards.id,
GameQueueTypes.extra_points,
EndgameDict.maxAmountOfRewards)
]);
} else { } else {
const state = await this.stateService.setState('main', 'finish'); const state = await this.stateService.setState('main', 'finish');
this.sharedService.sendSocketNotificationToAllClients( this.sharedService.notifyAllClients<IStateInfo>(ClientNotificationType.StateChanged, state);
'state_changed',
state,
);
this.logger.warn(`Gifts is ended, finishing game`); this.logger.warn(`Gifts is ended, finishing game`);
} }
} }

View file

@ -1,18 +0,0 @@
export enum SocketEvents {
PHOTOS_UPDATED_EVENT = 'photos_updated',
VALID_ANSWER_RECEIVED = 'answer_received',
WRONG_ANSWER_RECEIVED = 'wrong_answer_received',
USER_ADDED = 'user_added',
USER_PROPERTY_CHANGED = 'user_property_changed',
CARDS_CHANGED_EVENT = 'cards_changed',
CARD_PLAYED = 'card_played',
SCORE_CHANGED = 'score_changed',
GameQueueItem = 'game_queue',
QUEUE_COMPLETED = 'queue_completed',
GAME_PAUSED = 'game_paused',
GAME_RESUMED = 'game_resumed',
NOTIFICATION = 'notification',
FEATURE_FLAG_CHANGED = 'feature_flag_changed',
BEGIN_VERSUS = 'begin_versus',
END_VERSUS = 'end_versus',
}

View file

@ -1,4 +1,4 @@
import { SocketGateway } from '../socket/socket.gateway'; import {ClientNotificationType, SocketGateway} from '../socket/socket.gateway';
import { Injectable, Logger } from '@nestjs/common'; import { Injectable, Logger } from '@nestjs/common';
import { InjectModel } from '@nestjs/mongoose'; import { InjectModel } from '@nestjs/mongoose';
import { Config, ConfigDocument } from '../schemas/config.schema'; import { Config, ConfigDocument } from '../schemas/config.schema';
@ -55,14 +55,19 @@ export class SharedService {
} }
/** /**
* Notifies all connected socket about changes * Notifies all connected clients via the socket gateway with the given event and payload.
* @deprecated Use specific handlers in this class *
* @param event * @template T - The type of the payload.
* @param payload *
* @param event - The event name to be sent to the clients.
* @param payload - The data to be sent along with the event.
*
* @returns {void} - This function does not return any value.
*/ */
sendSocketNotificationToAllClients(event: string, payload?: any) { notifyAllClients<T>(event: ClientNotificationType, payload: T): void {
this.logger.verbose(`Sending notification to client: ${event}, ${JSON.stringify(payload)}`); this.logger.verbose(`Sending notification to client: ${event}, ${JSON.stringify(payload)}`);
this.socketGateway.notifyAllClients(event, payload); this.socketGateway.notifyAllClients(event, payload);
} }
} }

View file

@ -1,8 +1,9 @@
import { CommandHandler, ICommandHandler } from '@nestjs/cqrs'; import {CommandHandler, ICommandHandler} from '@nestjs/cqrs';
import { NotifyCardOnScreenCommand } from '../../../game/commands/notify-card-on-screen-command'; import {NotifyCardOnScreenCommand} from '../../../game/commands/notify-card-on-screen-command';
import { SharedService } from '../../../shared/shared.service'; import {SharedService} from '../../../shared/shared.service';
import { SocketEvents } from '../../../shared/events.consts'; import {Logger} from '@nestjs/common';
import { Logger } from '@nestjs/common'; import {ICardPlayedSocketEvent} from "../../../Consts/types";
import {ClientNotificationType} from "../../socket.gateway";
@CommandHandler(NotifyCardOnScreenCommand) @CommandHandler(NotifyCardOnScreenCommand)
export class NotifyCardPlayedCommandHandler export class NotifyCardPlayedCommandHandler
@ -12,8 +13,7 @@ export class NotifyCardPlayedCommandHandler
} }
async execute(command: NotifyCardOnScreenCommand): Promise<any> { async execute(command: NotifyCardOnScreenCommand): Promise<any> {
this.logger.log(`Notify about card`); this.logger.log(`Notify about card`);
this.sharedService.sendSocketNotificationToAllClients( this.sharedService.notifyAllClients<ICardPlayedSocketEvent>(ClientNotificationType.CardPlayed, {
SocketEvents.CARD_PLAYED, {
telegramId: command.telegramId, telegramId: command.telegramId,
card: command.card.description, card: command.card.description,
name: command.card.name, name: command.card.name,

View file

@ -1,8 +1,9 @@
import { CommandHandler, ICommandHandler } from '@nestjs/cqrs'; import {CommandHandler, ICommandHandler} from '@nestjs/cqrs';
import { SendToastCommand } from '../../../game/commands/send-toast.command'; import {SendToastCommand} from '../../../game/commands/send-toast.command';
import { SharedService } from '../../../shared/shared.service'; import {SharedService} from '../../../shared/shared.service';
import { SocketEvents } from '../../../shared/events.consts';
import {Logger} from "@nestjs/common"; import {Logger} from "@nestjs/common";
import {ISocketNotificationEvent} from "../../../Consts/types";
import {ClientNotificationType} from "../../socket.gateway";
@CommandHandler(SendToastCommand) @CommandHandler(SendToastCommand)
export class SendToastCommandHandler implements ICommandHandler<SendToastCommand> { export class SendToastCommandHandler implements ICommandHandler<SendToastCommand> {
@ -11,12 +12,9 @@ export class SendToastCommandHandler implements ICommandHandler<SendToastCommand
} }
async execute(command: SendToastCommand) { async execute(command: SendToastCommand) {
this.logger.verbose(`send notification: ${command.text}`); this.logger.verbose(`send notification: ${command.text}`);
await this.sharedService.sendSocketNotificationToAllClients( this.sharedService.notifyAllClients<ISocketNotificationEvent>(ClientNotificationType.Notification, {
SocketEvents.NOTIFICATION, text: command.text,
{ timeout: command.timeout,
text: command.text, });
timeout: command.timeout,
},
);
} }
} }

View file

@ -1,17 +1,16 @@
import { EventsHandler, IEventHandler } from '@nestjs/cqrs'; import {EventsHandler, IEventHandler} from '@nestjs/cqrs';
import { CardsSetChangedEvent } from '../../../game/events/cards-events/cards-set-changed.event'; import {CardsSetChangedEvent} from '../../../game/events/cards-events/cards-set-changed.event';
import { SharedService } from '../../../shared/shared.service'; import {SharedService} from '../../../shared/shared.service';
import { SocketEvents } from '../../../shared/events.consts'; import {IUserInfoMinimal} from "../../../Consts/types";
import {ClientNotificationType} from "../../socket.gateway";
@EventsHandler(CardsSetChangedEvent) @EventsHandler(CardsSetChangedEvent)
export class CardsSetChangedEventHandler export class CardsSetChangedEventHandler
implements IEventHandler<CardsSetChangedEvent> implements IEventHandler<CardsSetChangedEvent>
{ {
constructor(private sharedService: SharedService) {} constructor(private sharedService: SharedService) {}
handle(event: CardsSetChangedEvent): any { handle(event: CardsSetChangedEvent): void {
this.sharedService.sendSocketNotificationToAllClients( this.sharedService
SocketEvents.CARDS_CHANGED_EVENT, .notifyAllClients<IUserInfoMinimal>(ClientNotificationType.CardsChanged, { telegramId: event.telegramId})
{ telegramId: event.telegramId },
);
} }
} }

View file

@ -1,18 +1,17 @@
import { EventsHandler, IEventHandler } from '@nestjs/cqrs'; import {EventsHandler, IEventHandler} from '@nestjs/cqrs';
import { CardsDealedEvent } from '../../../game/events/cards-dealed.event'; import {CardsDealedEvent} from '../../../game/events/cards-dealed.event';
import { SharedService } from '../../../shared/shared.service'; import {SharedService} from '../../../shared/shared.service';
import { SocketEvents } from '../../../shared/events.consts'; import {IUserCardChangedEvent} from "../../../Consts/types";
import {ClientNotificationType} from "../../socket.gateway";
@EventsHandler(CardsDealedEvent) @EventsHandler(CardsDealedEvent)
export class CardsUpdatedEventHandler implements IEventHandler<CardsDealedEvent> { export class CardsUpdatedEventHandler implements IEventHandler<CardsDealedEvent> {
constructor(private sharedService: SharedService) { constructor(private sharedService: SharedService) {
} }
handle(event: CardsDealedEvent): any { handle(event: CardsDealedEvent): void {
this.sharedService.sendSocketNotificationToAllClients( this.sharedService.notifyAllClients<IUserCardChangedEvent>(ClientNotificationType.CardsChanged, {
SocketEvents.CARDS_CHANGED_EVENT, telegramId: event.telegramId,
{ cards: event.cards,
telegramId: event.telegramId,
cards: event.cards,
}); });
} }
} }

View file

@ -1,7 +1,8 @@
import { EventsHandler, IEventHandler } from '@nestjs/cqrs'; import {EventsHandler, IEventHandler} from '@nestjs/cqrs';
import { ScoreChangedEvent } from '../../../game/events/score-changed.event'; import {ScoreChangedEvent} from '../../../game/events/score-changed.event';
import { SharedService } from '../../../shared/shared.service'; import {SharedService} from '../../../shared/shared.service';
import { SocketEvents } from '../../../shared/events.consts'; import {IScoreChangedSocketEvent} from "../../../Consts/types";
import {ClientNotificationType} from "../../socket.gateway";
@EventsHandler(ScoreChangedEvent) @EventsHandler(ScoreChangedEvent)
export class SocketScoreChangedEventHandler export class SocketScoreChangedEventHandler
@ -9,12 +10,9 @@ export class SocketScoreChangedEventHandler
constructor(private sharedService: SharedService) { constructor(private sharedService: SharedService) {
} }
async handle(event: ScoreChangedEvent) { async handle(event: ScoreChangedEvent) {
this.sharedService.sendSocketNotificationToAllClients( this.sharedService.notifyAllClients<IScoreChangedSocketEvent>(ClientNotificationType.ScoreChanged, {
SocketEvents.SCORE_CHANGED, telegramId: event.telegramId,
{ newScore: event.newScore,
telegramId: event.telegramId, });
newScore: event.newScore,
},
);
} }
} }

View file

@ -1,17 +1,19 @@
import {EventsHandler, IEventHandler} from "@nestjs/cqrs"; import {EventsHandler, IEventHandler} from "@nestjs/cqrs";
import {UserPropertyChangedEvent} from "../../../guests/event-handlers/user-property-changed.event"; import {UserPropertyChangedEvent} from "../../../guests/event-handlers/user-property-changed.event";
import {SharedService} from "../../../shared/shared.service"; import {SharedService} from "../../../shared/shared.service";
import {SocketEvents} from "../../../shared/events.consts"; import {IUserPropertyChangedEvent} from "../../../Consts/types";
import {ClientNotificationType} from "../../socket.gateway";
@EventsHandler(UserPropertyChangedEvent) @EventsHandler(UserPropertyChangedEvent)
export class UserPropertyChangedEventHandler implements IEventHandler<UserPropertyChangedEvent> { export class UserPropertyChangedEventHandler implements IEventHandler<UserPropertyChangedEvent> {
constructor(private sharedService: SharedService) { constructor(private sharedService: SharedService) {
} }
handle(event: UserPropertyChangedEvent): any { handle(event: UserPropertyChangedEvent): void {
this.sharedService.sendSocketNotificationToAllClients( this.sharedService.notifyAllClients<IUserPropertyChangedEvent>(ClientNotificationType.UserPropertyChanged, {
SocketEvents.USER_PROPERTY_CHANGED, user: event.user,
{ user: event.user, property: event.property, value: event.propertyValue } property: event.property,
); value: event.propertyValue
});
} }
} }

View file

@ -1,17 +1,17 @@
import { EventsHandler, IEventHandler } from '@nestjs/cqrs'; import {EventsHandler, IEventHandler} from '@nestjs/cqrs';
import { UserRegisteredEvent } from '../../../game/events/user-registered.event'; import {UserRegisteredEvent} from '../../../game/events/user-registered.event';
import { SharedService } from '../../../shared/shared.service'; import {SharedService} from '../../../shared/shared.service';
import { SocketEvents } from '../../../shared/events.consts'; import {IUserBasicInfo} from "../../../Consts/types";
import {ClientNotificationType} from "../../socket.gateway";
@EventsHandler(UserRegisteredEvent) @EventsHandler(UserRegisteredEvent)
export class UserRegisteredEventHandler implements IEventHandler<UserRegisteredEvent> { export class UserRegisteredEventHandler implements IEventHandler<UserRegisteredEvent> {
constructor(private sharedService: SharedService) { constructor(private sharedService: SharedService) {
} }
handle(event: UserRegisteredEvent): any { handle(event: UserRegisteredEvent): void {
this.sharedService.sendSocketNotificationToAllClients( this.sharedService.notifyAllClients<IUserBasicInfo>(ClientNotificationType.UserAdded, {
SocketEvents.USER_ADDED, telegramId: event.telegramId,
{ name: event.name,
telegramId: event.telegramId,
name: event.name,
}); });
} }
} }

View file

@ -1,7 +1,8 @@
import { EventsHandler, IEventHandler } from '@nestjs/cqrs'; import {EventsHandler, IEventHandler} from '@nestjs/cqrs';
import { ValidAnswerReceivedEvent } from '../../../game/events/valid-answer.recieved'; import {ValidAnswerReceivedEvent} from '../../../game/events/valid-answer.recieved';
import { SharedService } from '../../../shared/shared.service'; import {SharedService} from '../../../shared/shared.service';
import { SocketEvents } from '../../../shared/events.consts'; import {ClientNotificationType} from "../../socket.gateway";
import {IValidAnswerReceivedSocketEvent} from "../../../Consts/types";
@EventsHandler(ValidAnswerReceivedEvent) @EventsHandler(ValidAnswerReceivedEvent)
export class SocketValidAnswerReceivedEventHandler export class SocketValidAnswerReceivedEventHandler
@ -11,9 +12,12 @@ export class SocketValidAnswerReceivedEventHandler
} }
handle(event: ValidAnswerReceivedEvent): any { handle(event: ValidAnswerReceivedEvent): any {
this.sharedService.sendSocketNotificationToAllClients( const notification : IValidAnswerReceivedSocketEvent = {
SocketEvents.VALID_ANSWER_RECEIVED, telegramId: event.tId,
{ telegramId: event.tId, validAnswer: event.validAnswer, note: event.extraDetails }, validAnswer: event.validAnswer,
); note: event.extraDetails
};
this.sharedService
.notifyAllClients<IValidAnswerReceivedSocketEvent>(ClientNotificationType.ValidAnswerReceived, notification);
} }
} }

View file

@ -10,6 +10,28 @@ import { Server, Socket } from 'socket.io';
import { Injectable, Logger } from "@nestjs/common"; import { Injectable, Logger } from "@nestjs/common";
import { from, map, Observable } from 'rxjs'; import { from, map, Observable } from 'rxjs';
export const enum ClientNotificationType {
StateChanged = 'state_changed',
PhotosUpdated = 'photos_updated',
ValidAnswerReceived = 'answer_received',
WrongAnswerReceived = 'wrong_answer_received',
UserAdded = 'user_added',
UserPropertyChanged = 'user_property_changed',
CardsChanged = 'cards_changed',
CardPlayed = 'card_played',
ScoreChanged = 'score_changed',
GameQueueItem = 'game_queue',
QueueCompleted = 'queue_completed',
GamePaused = 'game_paused',
GameResumed = 'game_resumed',
Notification = 'notification',
FeatureFlagChanged = 'feature_flag_changed',
BeginVersus = 'begin_versus',
EndVersus = 'end_versus',
QuestionChanged = 'question_changed'
}
@WebSocketGateway({ cors: true, transports: ['websocket']}) @WebSocketGateway({ cors: true, transports: ['websocket']})
@Injectable() @Injectable()
export class SocketGateway implements OnGatewayConnection, OnGatewayDisconnect{ export class SocketGateway implements OnGatewayConnection, OnGatewayDisconnect{
@ -47,7 +69,7 @@ export class SocketGateway implements OnGatewayConnection, OnGatewayDisconnect{
} }
notifyAllClients(event: string, payload: any) { notifyAllClients(event: ClientNotificationType, payload: any) {
this.server.emit("events", { event, data: payload}); this.server.emit("events", { event, data: payload});
// this.logger.warn(`send notification to all clients ${event}`); // this.logger.warn(`send notification to all clients ${event}`);
// this.clients.forEach((c) => { // this.clients.forEach((c) => {

View file

@ -1,11 +1,13 @@
import {Body, Controller, Get, Inject, Logger, Param, Post} from '@nestjs/common'; import {Body, Controller, Get, Inject, Logger, Param, Post} from '@nestjs/common';
import { StateService } from './state.service'; import {StateService} from './state.service';
import { SharedService } from '../shared/shared.service'; import {SharedService} from '../shared/shared.service';
import { EventBus } from '@nestjs/cqrs'; import {EventBus} from '@nestjs/cqrs';
import { GameStartedEvent } from '../game/events/game-started.event'; import {GameStartedEvent} from '../game/events/game-started.event';
import {ClientProxy} from "@nestjs/microservices"; import {ClientProxy} from "@nestjs/microservices";
import {CommandsConsts} from "../Consts/commands.consts"; import {CommandsConsts} from "../Consts/commands.consts";
import {MqtMessageModel} from "../messaging/models/mqt-message.model"; import {MqtMessageModel} from "../messaging/models/mqt-message.model";
import {ClientNotificationType} from "../socket/socket.gateway";
import {IStateInfo} from "../Consts/types";
interface SetStateDTO { interface SetStateDTO {
state: string; state: string;
@ -50,7 +52,7 @@ export class StateController {
this.logger.verbose('reset commands'); this.logger.verbose('reset commands');
this.telegramService.emit({ cmd: CommandsConsts.ResetCommands }, {}); this.telegramService.emit({ cmd: CommandsConsts.ResetCommands }, {});
} }
this.sharedService.sendSocketNotificationToAllClients('state_changed', res); this.sharedService.notifyAllClients<IStateInfo>(ClientNotificationType.StateChanged, res);
return res; return res;
} }
} }

View file

@ -4,6 +4,7 @@ import { State, StateDocument } from '../schemas/state.schema';
import { Model } from 'mongoose'; import { Model } from 'mongoose';
import { EventBus } from '@nestjs/cqrs'; import { EventBus } from '@nestjs/cqrs';
import { PrepareGameEvent } from '../game/events/prepare-game.event'; import { PrepareGameEvent } from '../game/events/prepare-game.event';
import {IStateInfo} from "../Consts/types";
interface StateDTO { interface StateDTO {
name: string; name: string;
@ -30,7 +31,7 @@ export class StateService {
return state; return state;
} }
async setState(name: string, newValue: string) { async setState(name: string, newValue: string): Promise<IStateInfo> {
if (newValue === 'onboarding') { if (newValue === 'onboarding') {
this.eventBus.publish(new PrepareGameEvent()); this.eventBus.publish(new PrepareGameEvent());
} }
@ -40,6 +41,6 @@ export class StateService {
return { return {
state: stateEntity.state, state: stateEntity.state,
value: stateEntity.value, value: stateEntity.value,
} } as IStateInfo
} }
} }