Отправка отформатированных по шаблону персональных сообщений

This commit is contained in:
Pavel Gnedov 2022-10-03 08:59:54 +07:00
parent b5a90b357b
commit 4b2a070851
5 changed files with 91 additions and 10 deletions

View file

@ -21,6 +21,7 @@ import { Users } from '@app/event-emitter/couchdb-datasources/users';
import { TelegramBotService } from './telegram-bot/telegram-bot.service'; import { TelegramBotService } from './telegram-bot/telegram-bot.service';
import { UserMetaInfoService } from './user-meta-info/user-meta-info.service'; import { UserMetaInfoService } from './user-meta-info/user-meta-info.service';
import { UserMetaInfo } from './couchdb-datasources/user-meta-info'; import { UserMetaInfo } from './couchdb-datasources/user-meta-info';
import { PersonalNotificationAdapterService } from './notifications/adapters/personal-notification.adapter/personal-notification.adapter.service';
@Module({ @Module({
imports: [ imports: [
@ -45,6 +46,7 @@ import { UserMetaInfo } from './couchdb-datasources/user-meta-info';
TelegramBotService, TelegramBotService,
UserMetaInfoService, UserMetaInfoService,
UserMetaInfo, UserMetaInfo,
PersonalNotificationAdapterService,
], ],
}) })
export class AppModule implements OnModuleInit { export class AppModule implements OnModuleInit {
@ -60,6 +62,7 @@ export class AppModule implements OnModuleInit {
private statusChangeNotificationsService: StatusChangeNotificationsService, private statusChangeNotificationsService: StatusChangeNotificationsService,
private changesCacheWriterService: ChangesCacheWriterService, private changesCacheWriterService: ChangesCacheWriterService,
private telegramBotService: TelegramBotService, private telegramBotService: TelegramBotService,
private personalNotificationAdapterService: PersonalNotificationAdapterService,
) {} ) {}
onModuleInit() { onModuleInit() {
@ -74,13 +77,14 @@ export class AppModule implements OnModuleInit {
this.currentUserEnhancer, this.currentUserEnhancer,
]); ]);
this.personalNotificationsService.$messages.subscribe((message) => { this.personalNotificationsService.$messages.subscribe((resp) => {
this.logger.log( this.logger.log(
`Get personal message ` + `Get personal message ` +
JSON.stringify(message.message) + JSON.stringify(resp.personalParsedMessage.message) +
` for recipients ` + ` for recipients ` +
JSON.stringify(message.recipients), JSON.stringify(resp.personalParsedMessage.recipients),
); );
this.personalNotificationAdapterService.send(resp);
}); });
this.statusChangeNotificationsService.$changes.subscribe((change) => { this.statusChangeNotificationsService.$changes.subscribe((change) => {
this.logger.log( this.logger.log(
@ -125,6 +129,10 @@ export class AppModule implements OnModuleInit {
this.logger.debug('Save changes in couchdb successed'); this.logger.debug('Save changes in couchdb successed');
return args; return args;
}), }),
switchMap(async (args) => {
this.logger.debug(``);
return args;
}),
) )
.subscribe(async (args) => { .subscribe(async (args) => {
this.logger.debug( this.logger.debug(

View file

@ -0,0 +1,7 @@
import { RedmineTypes } from '@app/event-emitter/models/redmine-types';
import { PersonalParsedMessage } from './personal-parsed-message.model';
export type IssueAndPersonalParsedMessageModel = {
issue: RedmineTypes.Issue;
personalParsedMessage: PersonalParsedMessage;
};

View file

@ -0,0 +1,55 @@
import { UsersService } from '@app/event-emitter/users/users.service';
import { Injectable } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { IssueAndPersonalParsedMessageModel } from 'src/models/issue-and-personal-parsed-message.model';
import { TelegramBotService } from 'src/telegram-bot/telegram-bot.service';
import Handlebars from 'handlebars';
@Injectable()
export class PersonalNotificationAdapterService {
private personalMessageTemplate: HandlebarsTemplateDelegate;
private redminePublicUrlPrefix: string;
constructor(
private telegramBotService: TelegramBotService,
private configService: ConfigService,
private usersService: UsersService,
) {
const template = this.configService.get<string>('personalMessageTemplate');
this.personalMessageTemplate = Handlebars.compile(template);
this.redminePublicUrlPrefix =
this.configService.get<string>('redmineUrlPublic');
}
async send(
issueAndMessages: IssueAndPersonalParsedMessageModel,
): Promise<void> {
const promises = issueAndMessages.personalParsedMessage.recipients.map(
async (recipient) => {
const redmineId = recipient;
const issueUrl = `${this.redminePublicUrlPrefix}/issues/${issueAndMessages.issue.id}`;
const issue = issueAndMessages.issue;
const issueUrlHtml = `<a href="${issueUrl}">${issue.tracker.name} #${issue.id}</a>`;
const sender = await this.usersService.getUser(
issueAndMessages.personalParsedMessage.sender,
);
const data = {
data: {
issue: issueAndMessages.issue,
messages: issueAndMessages.personalParsedMessage,
},
issue_url: issueUrlHtml,
sender_name: sender.name,
message: issueAndMessages.personalParsedMessage.message,
};
const message = this.personalMessageTemplate(data);
return await this.telegramBotService.sendMessageByRedmineId(
redmineId,
message,
{ parse_mode: 'HTML' },
);
},
);
await Promise.all(promises);
}
}

View file

@ -3,6 +3,7 @@ import { SaveResponse } from '@app/event-emitter/models/save-response';
import { UsersService } from '@app/event-emitter/users/users.service'; import { UsersService } from '@app/event-emitter/users/users.service';
import { Injectable, Logger } from '@nestjs/common'; import { Injectable, Logger } from '@nestjs/common';
import { Subject } from 'rxjs'; import { Subject } from 'rxjs';
import { IssueAndPersonalParsedMessageModel } from 'src/models/issue-and-personal-parsed-message.model';
import { PersonalParsedMessage } from 'src/models/personal-parsed-message.model'; import { PersonalParsedMessage } from 'src/models/personal-parsed-message.model';
@Injectable() @Injectable()
@ -10,11 +11,13 @@ export class PersonalNotificationsService {
private userNameRe = /@([\wА-Яа-яЁё]+) ([\wА-Яа-яЁё]+)@/g; private userNameRe = /@([\wА-Яа-яЁё]+) ([\wА-Яа-яЁё]+)@/g;
private logger = new Logger(PersonalNotificationsService.name); private logger = new Logger(PersonalNotificationsService.name);
$messages = new Subject<PersonalParsedMessage>(); $messages = new Subject<IssueAndPersonalParsedMessageModel>();
constructor(private usersService: UsersService) {} constructor(private usersService: UsersService) {}
async analize(data: SaveResponse): Promise<PersonalParsedMessage[]> { async analize(
data: SaveResponse,
): Promise<IssueAndPersonalParsedMessageModel[]> {
this.logger.debug( this.logger.debug(
`Analize personal messages for issue ` + `Analize personal messages for issue ` +
`#${data.current.id} (${data.current.subject}) start`, `#${data.current.id} (${data.current.subject}) start`,
@ -29,11 +32,16 @@ export class PersonalNotificationsService {
`from sender ${message.sender} ` + `from sender ${message.sender} ` +
`for recipients ${JSON.stringify(message.recipients)}`, `for recipients ${JSON.stringify(message.recipients)}`,
); );
this.$messages.next(message); this.$messages.next({
issue: data.current,
personalParsedMessage: message,
});
} }
return message; return message;
}); });
const res = (await Promise.all(pMessages)).filter((m) => Boolean(m)); const res = (await Promise.all(pMessages))
.filter((m) => Boolean(m))
.map((m) => ({ issue: data.current, personalParsedMessage: m }));
this.logger.debug( this.logger.debug(
`Analize personal messages for issue ` + `Analize personal messages for issue ` +
`#${data.current.id} (${data.current.subject}) finished`, `#${data.current.id} (${data.current.subject}) finished`,

View file

@ -74,17 +74,19 @@ export class TelegramBotService {
async sendMessageByRedmineId( async sendMessageByRedmineId(
redmineId: number, redmineId: number,
msg: string, msg: string,
options?: TelegramBot.SendMessageOptions,
): Promise<boolean> { ): Promise<boolean> {
const userMetaInfo = await this.userMetaInfoService.findByRedmineId( const userMetaInfo = await this.userMetaInfoService.findByRedmineId(
redmineId, redmineId,
); );
if (!userMetaInfo) return false; if (!userMetaInfo) return false;
const chatId = userMetaInfo.telegram_chat_id; const chatId = userMetaInfo.telegram_chat_id;
await this.bot.sendMessage(chatId, msg); await this.bot.sendMessage(chatId, msg, options);
this.logger.debug( this.logger.debug(
`Sent message for redmineUserId = ${redmineId}, ` + `Sent message for redmineUserId = ${redmineId}, ` +
`telegramChatId = ${chatId}, ` + `telegramChatId = ${chatId}, ` +
`message = ${msg}`, `message = ${msg}, ` +
`options = ${JSON.stringify(options)}`,
); );
return true; return true;
} }
@ -93,10 +95,11 @@ export class TelegramBotService {
firstname: string, firstname: string,
lastname: string, lastname: string,
msg: string, msg: string,
options?: TelegramBot.SendMessageOptions,
): Promise<boolean> { ): Promise<boolean> {
const user = await this.usersService.findUserByName(firstname, lastname); const user = await this.usersService.findUserByName(firstname, lastname);
if (!user) return false; if (!user) return false;
return await this.sendMessageByRedmineId(user.id, msg); return await this.sendMessageByRedmineId(user.id, msg, options);
} }
private async register( private async register(