From 6b8d78abfd7ce3ba31ee78828a86a016d1569fbc Mon Sep 17 00:00:00 2001 From: Cyrille Derche Date: Tue, 17 Sep 2024 12:21:15 +0200 Subject: [PATCH] reply with question or statement --- apps/bot/src/bot.controller.ts | 25 +++++++ apps/bot/src/bot.module.ts | 2 + apps/bot/src/bot.service.ts | 19 +++++- .../event-store/src/event-store.controller.ts | 4 +- .../src/chat_member/chat_member.controller.ts | 6 +- .../edited_message.controller.ts | 6 +- apps/graph-store/src/graph-store.module.ts | 2 +- .../src/interceptors/rmq.interceptor.spec.ts | 65 ------------------- .../src/interceptors/rmq.interceptor.ts | 24 ------- .../src/message/message.controller.ts | 6 +- .../message_reaction.controller.ts | 6 +- .../src/question-service.controller.ts | 43 +++++++++--- .../src/question-service.module.ts | 9 ++- docker-compose.yml | 6 +- libs/common/src/constants/events.ts | 4 ++ .../interceptors/events.interceptor.spec.ts | 0 .../rmq}/interceptors/events.interceptor.ts | 0 package-lock.json | 16 ++--- package.json | 2 +- 19 files changed, 119 insertions(+), 126 deletions(-) create mode 100644 apps/bot/src/bot.controller.ts delete mode 100644 apps/graph-store/src/interceptors/rmq.interceptor.spec.ts delete mode 100644 apps/graph-store/src/interceptors/rmq.interceptor.ts rename {apps/event-store/src => libs/common/src/rmq}/interceptors/events.interceptor.spec.ts (100%) rename {apps/event-store/src => libs/common/src/rmq}/interceptors/events.interceptor.ts (100%) diff --git a/apps/bot/src/bot.controller.ts b/apps/bot/src/bot.controller.ts new file mode 100644 index 0000000..d8f7a80 --- /dev/null +++ b/apps/bot/src/bot.controller.ts @@ -0,0 +1,25 @@ +import { Controller, Logger, UseInterceptors } from '@nestjs/common'; +import { MessagePattern, Payload } from '@nestjs/microservices'; +import { BotService } from './bot.service'; +import { TelegramAction } from '@app/common'; +import { EventsInterceptor } from '@app/common/rmq/interceptors/events.interceptor'; + +interface MessagePayload { + chat_id: number | string; + text: string; + other: any; +} + +@Controller() +@UseInterceptors(EventsInterceptor) +export class BotController { + private readonly logger = new Logger(BotController.name); + constructor(private readonly botService: BotService) { } + + @MessagePattern(TelegramAction.SEND_MESSAGE) + async sendMessage(@Payload() payload: MessagePayload): Promise { + const { chat_id, text, other } = payload; + await this.botService.sendMessage(chat_id, text, other); + return; + } +} diff --git a/apps/bot/src/bot.module.ts b/apps/bot/src/bot.module.ts index 234919c..9ae101a 100644 --- a/apps/bot/src/bot.module.ts +++ b/apps/bot/src/bot.module.ts @@ -9,6 +9,7 @@ import { RmqModule, } from '@app/common'; import { Services } from '@app/common'; +import { BotController } from './bot.controller'; @Module({ imports: [ @@ -21,6 +22,7 @@ import { Services } from '@app/common'; RmqModule.register(Services.GraphStore), RmqModule.register(Services.TGQuestionService), ], + controllers: [BotController], providers: [BotService], }) export class BotModule { } diff --git a/apps/bot/src/bot.service.ts b/apps/bot/src/bot.service.ts index 2695a17..a03a2c3 100644 --- a/apps/bot/src/bot.service.ts +++ b/apps/bot/src/bot.service.ts @@ -2,7 +2,9 @@ import { IgnoreEvent, Services, UpdateEvent } from '@app/common'; import { Inject, Injectable, Logger, OnModuleInit } from '@nestjs/common'; import { ConfigService } from '@nestjs/config'; import { ClientProxy } from '@nestjs/microservices'; -import { API_CONSTANTS, Bot } from 'grammy'; +import { API_CONSTANTS, Bot, RawApi } from 'grammy'; +import { Other } from 'grammy/out/core/api'; +import { Message, ReplyParameters } from 'grammy/types'; @Injectable() export class BotService implements OnModuleInit { @@ -29,7 +31,10 @@ export class BotService implements OnModuleInit { this.logger.log(`Received ${event} from ${ctx.chat.id}`); this.eventClient.emit(event, ctx); this.graphClient.emit(event, ctx); - this.tgQuestionClient.emit(event, ctx); + if (!ctx.update.message.from.is_bot) { + this.tgQuestionClient.emit(event, ctx); + } + return; }); }); } @@ -39,4 +44,14 @@ export class BotService implements OnModuleInit { allowed_updates: API_CONSTANTS.ALL_UPDATE_TYPES, }); } + + async sendMessage( + chat_id: number | string, + text: string, + other?: Other, + ): Promise { + const receipt = await this.bot.api.sendMessage(chat_id, text, other); + this.logger.log(`Message ${receipt.message_id} sent to ${receipt.chat.id}`); + return receipt; + } } diff --git a/apps/event-store/src/event-store.controller.ts b/apps/event-store/src/event-store.controller.ts index 3d007ad..71cbdbc 100644 --- a/apps/event-store/src/event-store.controller.ts +++ b/apps/event-store/src/event-store.controller.ts @@ -2,7 +2,7 @@ import { Controller, Logger, UseInterceptors } from '@nestjs/common'; import { EventStoreService } from './event-store.service'; import { MessagePattern, Payload } from '@nestjs/microservices'; import { UpdateEvent } from '@app/common'; -import { EventsInterceptor } from './interceptors/events.interceptor'; +import { EventsInterceptor } from '@app/common/rmq/interceptors/events.interceptor'; import { API_CONSTANTS } from 'grammy'; @Controller() @@ -10,7 +10,7 @@ import { API_CONSTANTS } from 'grammy'; export class EventStoreController { private readonly logger = new Logger(EventStoreController.name); - constructor(private readonly eventsService: EventStoreService) {} + constructor(private readonly eventsService: EventStoreService) { } a = typeof API_CONSTANTS.ALL_UPDATE_TYPES; diff --git a/apps/graph-store/src/chat_member/chat_member.controller.ts b/apps/graph-store/src/chat_member/chat_member.controller.ts index eec9a82..ecf5849 100644 --- a/apps/graph-store/src/chat_member/chat_member.controller.ts +++ b/apps/graph-store/src/chat_member/chat_member.controller.ts @@ -2,7 +2,7 @@ import { UpdateEvent } from '@app/common'; import { Controller, Logger, UseInterceptors } from '@nestjs/common'; import { MessagePattern } from '@nestjs/microservices'; import { DChatMemberUpdated } from '../decorators/chatMemberUpdated.decorator'; -import { RmqInterceptor } from '../interceptors/rmq.interceptor'; +import { EventsInterceptor } from '@app/common/rmq/interceptors/events.interceptor'; import { Chat, ChatMemberUpdated } from 'grammy/types'; import { ChatMemberService } from './chat_member.service'; import { DChat } from '../decorators/chat.decorator'; @@ -25,11 +25,11 @@ type Action = | 'DEMOTED'; @Controller('chat-member') -@UseInterceptors(RmqInterceptor) +@UseInterceptors(EventsInterceptor) export class ChatMemberController { private readonly logger = new Logger(ChatMemberController.name); - constructor(private readonly chatMemberService: ChatMemberService) {} + constructor(private readonly chatMemberService: ChatMemberService) { } @MessagePattern(UpdateEvent.CHAT_MEMBER) async chat_member( diff --git a/apps/graph-store/src/edited_message/edited_message.controller.ts b/apps/graph-store/src/edited_message/edited_message.controller.ts index a2c860a..9166eea 100644 --- a/apps/graph-store/src/edited_message/edited_message.controller.ts +++ b/apps/graph-store/src/edited_message/edited_message.controller.ts @@ -13,10 +13,10 @@ import { MentionedService } from '../mentioned/mentioned.service'; import { MessageService } from '../message/message.service'; import { RepliedService } from '../replied/replied.service'; import { UserService } from '../user/user.service'; -import { RmqInterceptor } from '../interceptors/rmq.interceptor'; +import { EventsInterceptor } from '@app/common/rmq/interceptors/events.interceptor'; @Controller('edited-message') -@UseInterceptors(RmqInterceptor) +@UseInterceptors(EventsInterceptor) export class EditedMessageController { private readonly logger = new Logger(EditedMessageController.name); @@ -28,7 +28,7 @@ export class EditedMessageController { private readonly joinedService: JoinedService, private readonly repliedService: RepliedService, private readonly mentionedService: MentionedService, - ) {} + ) { } @MessagePattern(UpdateEvent.EDITED_MESSAGE) async chat_member( diff --git a/apps/graph-store/src/graph-store.module.ts b/apps/graph-store/src/graph-store.module.ts index 8c86c4b..1abf0b0 100644 --- a/apps/graph-store/src/graph-store.module.ts +++ b/apps/graph-store/src/graph-store.module.ts @@ -36,4 +36,4 @@ import { MessageReactionModule } from './message_reaction/message_reaction.modul MessageReactionModule, ], }) -export class GraphStoreModule {} +export class GraphStoreModule { } diff --git a/apps/graph-store/src/interceptors/rmq.interceptor.spec.ts b/apps/graph-store/src/interceptors/rmq.interceptor.spec.ts deleted file mode 100644 index 658a65c..0000000 --- a/apps/graph-store/src/interceptors/rmq.interceptor.spec.ts +++ /dev/null @@ -1,65 +0,0 @@ -import { Test, TestingModule } from '@nestjs/testing'; -import { RmqInterceptor } from './rmq.interceptor'; -import { of } from 'rxjs'; -import { ExecutionContext, CallHandler } from '@nestjs/common'; -import { RmqService } from '@app/common'; - -// Mock RmqService -class RmqServiceMock { - ack = jest.fn(); -} - -describe('RmqInterceptor', () => { - let interceptor: RmqInterceptor; - let rmqService: RmqServiceMock; - - beforeEach(async () => { - const module: TestingModule = await Test.createTestingModule({ - providers: [ - RmqInterceptor, - // Provide mock RmqService - { provide: RmqService, useClass: RmqServiceMock }, - ], - }).compile(); - - interceptor = module.get(RmqInterceptor); - rmqService = module.get(RmqService); - }); - - it('should be defined', () => { - expect(interceptor).toBeDefined(); - }); - - it('should call ack method of RmqService when intercepting', () => { - const mockContext = { - switchToRpc: jest.fn().mockReturnThis(), - getContext: jest.fn().mockReturnValue({ - getChannelRef: jest.fn(), - getMessage: jest.fn(), - }), - } as unknown as ExecutionContext; - const mockCallHandler = { - handle: jest.fn().mockReturnValue(of(null)), // Mock returning an observable - } as CallHandler; - - // Intercept the call - interceptor.intercept(mockContext, mockCallHandler); - - // You can assert that the ack method is called synchronously - - // If you want to assert behavior from the observable, you can subscribe to it - // and add your assertions inside the subscribe block - mockCallHandler.handle().subscribe({ - next: () => { - // Assert behavior here - }, - error: () => { - // Handle error if needed - }, - complete: () => { - // Handle completion if needed - expect(rmqService.ack).toHaveBeenCalled(); - }, - }); - }); -}); diff --git a/apps/graph-store/src/interceptors/rmq.interceptor.ts b/apps/graph-store/src/interceptors/rmq.interceptor.ts deleted file mode 100644 index 40bc352..0000000 --- a/apps/graph-store/src/interceptors/rmq.interceptor.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { - Injectable, - NestInterceptor, - ExecutionContext, - CallHandler, -} from '@nestjs/common'; -import { Observable } from 'rxjs'; -import { tap } from 'rxjs/operators'; -import { RmqContext } from '@nestjs/microservices'; -import { RmqService } from '@app/common'; - -@Injectable() -export class RmqInterceptor implements NestInterceptor { - constructor(private readonly rmqService: RmqService) {} - - intercept(context: ExecutionContext, next: CallHandler): Observable { - const ctx = context.switchToRpc().getContext(); - return next.handle().pipe( - tap(() => { - this.rmqService.ack(ctx); - }), - ); - } -} diff --git a/apps/graph-store/src/message/message.controller.ts b/apps/graph-store/src/message/message.controller.ts index 14e0443..4e3caa1 100644 --- a/apps/graph-store/src/message/message.controller.ts +++ b/apps/graph-store/src/message/message.controller.ts @@ -2,7 +2,6 @@ import { UpdateEvent } from '@app/common'; import { Controller, Logger, UseInterceptors } from '@nestjs/common'; import { MessagePattern } from '@nestjs/microservices'; import { DChat } from '../decorators/chat.decorator'; -import { RmqInterceptor } from '../interceptors/rmq.interceptor'; import { MessageService } from './message.service'; import { DMessage } from '../decorators/message.decorator'; import { Chat, Message, MessageEntity, User } from 'grammy/types'; @@ -14,9 +13,10 @@ import { MentionedService } from '../mentioned/mentioned.service'; import { RepliedService } from '../replied/replied.service'; import { UserService } from '../user/user.service'; import { Neo4jService } from 'nest-neo4j/dist'; +import { EventsInterceptor } from '@app/common/rmq/interceptors/events.interceptor'; @Controller('message') -@UseInterceptors(RmqInterceptor) +@UseInterceptors(EventsInterceptor) export class MessageController { private readonly logger = new Logger(MessageController.name); @@ -28,7 +28,7 @@ export class MessageController { private readonly joinedService: JoinedService, private readonly repliedService: RepliedService, private readonly mentionedService: MentionedService, - ) {} + ) { } @MessagePattern(UpdateEvent.MESSAGE) async message( diff --git a/apps/graph-store/src/message_reaction/message_reaction.controller.ts b/apps/graph-store/src/message_reaction/message_reaction.controller.ts index f3d1958..2560686 100644 --- a/apps/graph-store/src/message_reaction/message_reaction.controller.ts +++ b/apps/graph-store/src/message_reaction/message_reaction.controller.ts @@ -10,10 +10,10 @@ import { DUser } from '../decorators/user.decorator'; import { JoinedService } from '../joined/joined.service'; import { UserService } from '../user/user.service'; import { MessageReactionService } from './message_reaction.service'; -import { RmqInterceptor } from '../interceptors/rmq.interceptor'; +import { EventsInterceptor } from '@app/common/rmq/interceptors/events.interceptor'; @Controller('message-reaction') -@UseInterceptors(RmqInterceptor) +@UseInterceptors(EventsInterceptor) export class MessageReactionController { private readonly logger = new Logger(MessageReactionController.name); @@ -23,7 +23,7 @@ export class MessageReactionController { private readonly userService: UserService, private readonly joinedService: JoinedService, private readonly messageReactionService: MessageReactionService, - ) {} + ) { } @MessagePattern(UpdateEvent.MESSAGE_REACTION) async message( diff --git a/apps/question-service/src/question-service.controller.ts b/apps/question-service/src/question-service.controller.ts index 09a5827..3d440b9 100644 --- a/apps/question-service/src/question-service.controller.ts +++ b/apps/question-service/src/question-service.controller.ts @@ -1,20 +1,47 @@ -import { Controller, Logger } from '@nestjs/common'; +import { Controller, Inject, Logger } from '@nestjs/common'; import { QuestionServiceService } from './question-service.service'; -import { MessagePattern, Payload } from '@nestjs/microservices'; -import { UpdateEvent } from '@app/common'; +import { ClientProxy, MessagePattern, Payload } from '@nestjs/microservices'; +import { Services, TelegramAction, UpdateEvent } from '@app/common'; +import { Context } from 'grammy'; +import { ReplyParameters } from 'grammy/types'; +import { UseInterceptors } from '@nestjs/common'; +import { EventsInterceptor } from '@app/common/rmq/interceptors/events.interceptor'; @Controller() +@UseInterceptors(EventsInterceptor) export class QuestionServiceController { private readonly logger = new Logger(QuestionServiceController.name); constructor( private readonly questionServiceService: QuestionServiceService, + @Inject(Services.TelegramBot.name) + private readonly telegramClient: ClientProxy, ) { } @MessagePattern(UpdateEvent.MESSAGE) - async message(@Payload() payload): Promise { - const { update } = payload; - const { message } = update; - const { data } = await this.questionServiceService.test(message.text); - this.logger.log('Result', data); + async message(@Payload() ctx: Context): Promise { + const { + message_id, + text: msg, + chat: { id: chat_id }, + } = ctx.update?.message; + const { + data: { label, score }, + } = await this.questionServiceService.test(msg); + + const text = `${label}: ${score}`; + + const reply_parameters: ReplyParameters = { + message_id, + }; + + const data = { + chat_id, + text, + other: { + reply_parameters, + }, + }; + + this.telegramClient.emit(TelegramAction.SEND_MESSAGE, data); } } diff --git a/apps/question-service/src/question-service.module.ts b/apps/question-service/src/question-service.module.ts index 8a40d93..3acc38a 100644 --- a/apps/question-service/src/question-service.module.ts +++ b/apps/question-service/src/question-service.module.ts @@ -1,7 +1,13 @@ import { Module } from '@nestjs/common'; import { QuestionServiceController } from './question-service.controller'; import { QuestionServiceService } from './question-service.service'; -import { schemaConfig, rmqConfig, mongoConfig, RmqModule } from '@app/common'; +import { + schemaConfig, + rmqConfig, + mongoConfig, + RmqModule, + Services, +} from '@app/common'; import { ConfigModule } from '@nestjs/config'; import { HttpModule } from '@nestjs/axios'; @@ -14,6 +20,7 @@ import { HttpModule } from '@nestjs/axios'; }), RmqModule, HttpModule, + RmqModule.register(Services.TelegramBot), ], controllers: [QuestionServiceController], providers: [QuestionServiceService], diff --git a/docker-compose.yml b/docker-compose.yml index da890ca..12df082 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -13,6 +13,8 @@ x-telegram-common: &telegram-common condition: service_healthy mongodb: condition: service_healthy + volumes: + - ./:/usr/src/app/ services: mongodb: @@ -75,7 +77,7 @@ services: telegram-bot: <<: [ *telegram-common ] container_name: telegram-bot - command: npm run start bot + command: npm run start:dev bot telegram-events: <<: [ *telegram-common ] container_name: telegram-events @@ -87,7 +89,7 @@ services: telegram-question: <<: [ *telegram-common ] container_name: telegram-question - command: npm run start question-service + command: npm run start:dev question-service question-service-ext: image: ghcr.io/togethercrew/question-service:main diff --git a/libs/common/src/constants/events.ts b/libs/common/src/constants/events.ts index 784d2a9..b18b12b 100644 --- a/libs/common/src/constants/events.ts +++ b/libs/common/src/constants/events.ts @@ -15,3 +15,7 @@ export const IgnoreEvent: FilterQueryDictionary = { MESSAGE_NEW_MEMBERS: 'message:new_chat_members', MESSAGE_LEFT_MEMBER: 'message:left_chat_member', }; + +export const TelegramAction = { + SEND_MESSAGE: 'send_message', +}; diff --git a/apps/event-store/src/interceptors/events.interceptor.spec.ts b/libs/common/src/rmq/interceptors/events.interceptor.spec.ts similarity index 100% rename from apps/event-store/src/interceptors/events.interceptor.spec.ts rename to libs/common/src/rmq/interceptors/events.interceptor.spec.ts diff --git a/apps/event-store/src/interceptors/events.interceptor.ts b/libs/common/src/rmq/interceptors/events.interceptor.ts similarity index 100% rename from apps/event-store/src/interceptors/events.interceptor.ts rename to libs/common/src/rmq/interceptors/events.interceptor.ts diff --git a/package-lock.json b/package-lock.json index cfd48dd..3c519b0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -19,7 +19,7 @@ "amqp-connection-manager": "^4.1.14", "amqplib": "^0.10.3", "axios": "^1.7.7", - "grammy": "^1.22.4", + "grammy": "^1.30.0", "joi": "^17.12.2", "mongoose": "^8.2.0", "nest-neo4j": "^0.3.1", @@ -970,9 +970,9 @@ } }, "node_modules/@grammyjs/types": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/@grammyjs/types/-/types-3.6.2.tgz", - "integrity": "sha512-7OswNRPN72qFUWhysNrY96+5LKBQXgxqw0iNbleYV7G8GB6ZPVdkwFMIZn1Fda2iRKMYT6S6Sxuel0465VGtHQ==" + "version": "3.14.0", + "resolved": "https://registry.npmjs.org/@grammyjs/types/-/types-3.14.0.tgz", + "integrity": "sha512-uUVikYAiHNU8CvkeQ6YA/QPbMLgTTpFVLp83VkXCs423w6cDsvfdA6bcrQJGUnc/Q6zHQXXZUftXDYl2b/6L4A==" }, "node_modules/@hapi/hoek": { "version": "9.3.0", @@ -5210,11 +5210,11 @@ "dev": true }, "node_modules/grammy": { - "version": "1.22.4", - "resolved": "https://registry.npmjs.org/grammy/-/grammy-1.22.4.tgz", - "integrity": "sha512-7EIdixo4kV/lJWyKswyi5n3AJ2U/2731rPf8a3SKoXQ0tiHdCTvdgc2atdN2b10g8o6wJ2SXTf0xQX3Dqm1buA==", + "version": "1.30.0", + "resolved": "https://registry.npmjs.org/grammy/-/grammy-1.30.0.tgz", + "integrity": "sha512-uD1kMbgR04qIed4z+eHc68t41ZyzPC3mbHSJD+hQtoTA6RkBSgVxL2HJLmgP7cUjEirlkxHjV6F7Hl7a4Ozvbg==", "dependencies": { - "@grammyjs/types": "3.6.2", + "@grammyjs/types": "3.14.0", "abort-controller": "^3.0.0", "debug": "^4.3.4", "node-fetch": "^2.7.0" diff --git a/package.json b/package.json index aee47c8..71d9627 100644 --- a/package.json +++ b/package.json @@ -30,7 +30,7 @@ "amqp-connection-manager": "^4.1.14", "amqplib": "^0.10.3", "axios": "^1.7.7", - "grammy": "^1.22.4", + "grammy": "^1.30.0", "joi": "^17.12.2", "mongoose": "^8.2.0", "nest-neo4j": "^0.3.1",