diff --git a/src/app.module.ts b/src/app.module.ts index 35b18cf..46f35c8 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -104,20 +104,8 @@ export class AppModule implements OnModuleInit { ); this.personalNotificationAdapterService.send(resp); }); - this.statusChangeNotificationsService.$changes.subscribe((change) => { - const messages = change.messages - .map((m) => m.change_message) - .filter((m) => !!m); - const notifications = change.messages - .map((m) => m.notification_message) - .filter((m) => !!m); - this.logger.log( - `Get status changes messages for ` + - `issue_id = ${change.issue_id}, ` + - `messages = ${JSON.stringify(messages)}, ` + - `notifications = ${JSON.stringify(notifications)}`, - ); - this.statusChangeAdapterService.send(change); + this.statusChangeNotificationsService.$batchChanges.subscribe((changes) => { + this.statusChangeAdapterService.batchSend(changes); }); this.redmineIssuesCacheWriterService.subject diff --git a/src/notifications/adapters/status-change.adapter.service.ts b/src/notifications/adapters/status-change.adapter.service.ts index 06ccba6..dcdba57 100644 --- a/src/notifications/adapters/status-change.adapter.service.ts +++ b/src/notifications/adapters/status-change.adapter.service.ts @@ -1,7 +1,20 @@ import { Injectable } from '@nestjs/common'; +import TelegramBot from 'node-telegram-bot-api'; import { Change } from 'src/models/change.model'; import { TelegramBotService } from 'src/telegram-bot/telegram-bot.service'; +// eslint-disable-next-line @typescript-eslint/no-namespace +namespace StatusChangeAdapter { + export type MsgFromBatch = { + initiatorId: number; + recipientId: number; + issueId: number; + createdAt: number; + msg: string; + options?: TelegramBot.SendMessageOptions; + }; +} + @Injectable() export class StatusChangeAdapterService { constructor(private telegramBotService: TelegramBotService) {} @@ -19,4 +32,57 @@ export class StatusChangeAdapterService { }); return await Promise.all(promises); } + + async batchSend(changes: Change[]): Promise { + const messages = this.getMessages(changes).map((item) => { + item.options = { parse_mode: 'HTML' }; + return item; + }); + for (let i = 0; i < messages.length; i++) { + const message = messages[i]; + await this.telegramBotService.sendMessageByRedmineId( + message.recipientId, + message.msg, + message.options, + ); + } + } + + private getMessages(changes: Change[]): StatusChangeAdapter.MsgFromBatch[] { + const res: StatusChangeAdapter.MsgFromBatch[] = []; + const store: Record = {}; + for (let i = 0; i < changes.length; i++) { + const change = changes[i]; + for (let j = 0; j < change.messages.length; j++) { + const message = change.messages[j]; + if (!message.change_message) continue; + if (change.initiator.id == message.recipient.id) continue; + const item: StatusChangeAdapter.MsgFromBatch = { + initiatorId: change.initiator.id, + recipientId: message.recipient.id, + createdAt: change.created_on_timestamp, + issueId: change.issue_id, + msg: message.change_message, + }; + const key = this.keyForMsgFromBatch(item); + if ( + !store[key] || + (store[key] && store[key].createdAt < item.createdAt) + ) { + store[key] = item; + } + } + } + for (const key in store) { + if (Object.prototype.hasOwnProperty.call(store, key)) { + const item = store[key]; + res.push(item); + } + } + return res; + } + + private keyForMsgFromBatch(item: StatusChangeAdapter.MsgFromBatch): string { + return `${item.issueId}-${item.recipientId}`; + } } diff --git a/src/notifications/status-change-notifications.service.ts b/src/notifications/status-change-notifications.service.ts index 23063ea..7c9e525 100644 --- a/src/notifications/status-change-notifications.service.ts +++ b/src/notifications/status-change-notifications.service.ts @@ -19,6 +19,7 @@ export class StatusChangeNotificationsService { private statusChanges: StatusChangesConfig.Config; $changes = new Subject(); + $batchChanges = new Subject(); constructor( private usersService: UsersService, @@ -68,6 +69,7 @@ export class StatusChangeNotificationsService { ); changes.forEach((c) => this.$changes.next(c)); + this.$batchChanges.next(changes); return changes; }