Добавлен парсер персональных сообщений по упоминанию в комментариях
This commit is contained in:
parent
0d7b684244
commit
8c77117703
14 changed files with 172 additions and 65 deletions
|
|
@ -1,8 +1,6 @@
|
||||||
import { DynamicModule, Logger, Module, OnModuleInit } from '@nestjs/common';
|
import { DynamicModule, Logger, Module, OnModuleInit } from '@nestjs/common';
|
||||||
import { EventEmitterService } from './event-emitter.service';
|
import { EventEmitterService } from './event-emitter.service';
|
||||||
import { RedmineEventsGateway } from './events/redmine-events.gateway';
|
import { RedmineEventsGateway } from './events/redmine-events.gateway';
|
||||||
import { ServeStaticModule } from '@nestjs/serve-static';
|
|
||||||
import { join } from 'path';
|
|
||||||
import MainConfig from './configs/main-config';
|
import MainConfig from './configs/main-config';
|
||||||
import { ConfigModule } from '@nestjs/config';
|
import { ConfigModule } from '@nestjs/config';
|
||||||
import { RedmineDataLoader } from './redmine-data-loader/redmine-data-loader';
|
import { RedmineDataLoader } from './redmine-data-loader/redmine-data-loader';
|
||||||
|
|
@ -41,10 +39,6 @@ export class EventEmitterModule implements OnModuleInit {
|
||||||
UsersService,
|
UsersService,
|
||||||
IssuesService,
|
IssuesService,
|
||||||
TimestampEnhancer,
|
TimestampEnhancer,
|
||||||
{
|
|
||||||
provide: 'ENHANCERS',
|
|
||||||
useValue: params?.enhancers || null,
|
|
||||||
},
|
|
||||||
EnhancerService,
|
EnhancerService,
|
||||||
],
|
],
|
||||||
exports: [
|
exports: [
|
||||||
|
|
@ -75,23 +69,25 @@ export class EventEmitterModule implements OnModuleInit {
|
||||||
onModuleInit() {
|
onModuleInit() {
|
||||||
const queue = this.redmineEventsGateway.getIssuesChangesQueue();
|
const queue = this.redmineEventsGateway.getIssuesChangesQueue();
|
||||||
const subj = queue.queue;
|
const subj = queue.queue;
|
||||||
subj.subscribe(async (issues: any) => {
|
subj.subscribe(async (issues: RedmineTypes.Issue[]) => {
|
||||||
this.logger.debug(`Changed issues = ${JSON.stringify(issues)}`);
|
this.logger.debug(
|
||||||
|
`Changed issues - ` +
|
||||||
|
issues.map((i) => `#${i.id} (${i.subject})`).join(', '),
|
||||||
|
);
|
||||||
|
|
||||||
for (let i = 0; i < issues.length; i++) {
|
for (let i = 0; i < issues.length; i++) {
|
||||||
const issue: RedmineTypes.Issue = issues[i];
|
const issue: RedmineTypes.Issue = issues[i];
|
||||||
|
|
||||||
try {
|
try {
|
||||||
this.logger.debug(
|
this.logger.debug(`Save issue #${issue.id} (${issue.subject})`);
|
||||||
`Save issue #${issue.id} - ${JSON.stringify(issue)}`,
|
|
||||||
);
|
|
||||||
|
|
||||||
const response = await this.redmineIssuesCacheWriterService.saveIssue(
|
const response = await this.redmineIssuesCacheWriterService.saveIssue(
|
||||||
issue,
|
issue,
|
||||||
);
|
);
|
||||||
|
|
||||||
this.logger.debug(
|
this.logger.debug(
|
||||||
`Save issue #${issue.id} response = ${JSON.stringify(response)}`,
|
// eslint-disable-next-line prettier/prettier
|
||||||
|
`Save ${(response.prev) ? 'exists' : 'new'} issue #${issue.id} - count of changes = ${response.journalsDiff.length}`,
|
||||||
);
|
);
|
||||||
} catch (ex) {
|
} catch (ex) {
|
||||||
this.logger.error(`Saving issue error - ${ex}`, null, {
|
this.logger.error(`Saving issue error - ${ex}`, null, {
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@ import { WebhookConfigItemModel } from '../models/webhook-config-item-model';
|
||||||
import { RssListener } from '../rsslistener/rsslistener';
|
import { RssListener } from '../rsslistener/rsslistener';
|
||||||
import { EventsListener } from './events-listener';
|
import { EventsListener } from './events-listener';
|
||||||
import { Logger } from '@nestjs/common';
|
import { Logger } from '@nestjs/common';
|
||||||
|
import { RedmineTypes } from '../models/redmine-types';
|
||||||
|
|
||||||
type IssuesChangesQueueParams = {
|
type IssuesChangesQueueParams = {
|
||||||
updateInterval: number;
|
updateInterval: number;
|
||||||
|
|
@ -48,14 +49,14 @@ export class RedmineEventsGateway {
|
||||||
issuesChanges(
|
issuesChanges(
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
@MessageBody() data: any,
|
@MessageBody() data: any,
|
||||||
): Observable<WsResponse<RedmineIssueData[]>> {
|
): Observable<WsResponse<RedmineTypes.Issue[]>> {
|
||||||
return this.issuesChangesObservable;
|
return this.issuesChangesObservable;
|
||||||
}
|
}
|
||||||
|
|
||||||
private issuesChangesQueue: Queue<number, RedmineIssueData>;
|
private issuesChangesQueue: Queue<number, RedmineTypes.Issue>;
|
||||||
getIssuesChangesQueue(): Queue<number, RedmineIssueData> {
|
getIssuesChangesQueue(): Queue<number, RedmineTypes.Issue> {
|
||||||
if (!this.issuesChangesQueue) {
|
if (!this.issuesChangesQueue) {
|
||||||
this.issuesChangesQueue = new Queue<number, RedmineIssueData>(
|
this.issuesChangesQueue = new Queue<number, RedmineTypes.Issue>(
|
||||||
this.issuesChangesQueueParams.updateInterval,
|
this.issuesChangesQueueParams.updateInterval,
|
||||||
this.issuesChangesQueueParams.itemsLimit,
|
this.issuesChangesQueueParams.itemsLimit,
|
||||||
async (issueNumbers) => {
|
async (issueNumbers) => {
|
||||||
|
|
|
||||||
|
|
@ -17,13 +17,11 @@ export class RedmineIssuesCacheWriterService {
|
||||||
|
|
||||||
async saveIssue(issue: RedmineTypes.Issue): Promise<SaveResponse> {
|
async saveIssue(issue: RedmineTypes.Issue): Promise<SaveResponse> {
|
||||||
this.logger.debug(
|
this.logger.debug(
|
||||||
`Saving issue ${issue?.id || '-'} - ${
|
`Saving issue ${issue?.id || '-'} (${issue?.subject || '-'})`,
|
||||||
issue?.subject || '-'
|
|
||||||
}, issue data = ${JSON.stringify(issue)}`,
|
|
||||||
);
|
);
|
||||||
const id = Number(issue['id']);
|
const id = Number(issue['id']);
|
||||||
let prevIssue: (nano.DocumentGetResponse & RedmineTypes.Issue) | null;
|
let prevIssue: (nano.DocumentGetResponse & RedmineTypes.Issue) | null;
|
||||||
const issueDb = await Issues.getDatasource();
|
const issueDb = await this.issues.getDatasource();
|
||||||
if (!issueDb) {
|
if (!issueDb) {
|
||||||
throw `CouchDb datasource must defined`;
|
throw `CouchDb datasource must defined`;
|
||||||
}
|
}
|
||||||
|
|
@ -46,7 +44,7 @@ export class RedmineIssuesCacheWriterService {
|
||||||
journalsDiff: this.getJournalsDiff(prevIssue, newIssue),
|
journalsDiff: this.getJournalsDiff(prevIssue, newIssue),
|
||||||
};
|
};
|
||||||
this.logger.debug(
|
this.logger.debug(
|
||||||
`Saving issue success ${issue?.id || '-'} - ${issue?.subject || '-'}`,
|
`Saving issue success #${issue?.id || '-'} - ${issue?.subject || '-'}`,
|
||||||
);
|
);
|
||||||
this.subject.next(res);
|
this.subject.next(res);
|
||||||
return res;
|
return res;
|
||||||
|
|
|
||||||
|
|
@ -1,31 +1,19 @@
|
||||||
import { Inject, Injectable, Logger } from '@nestjs/common';
|
import { Injectable, Logger } from '@nestjs/common';
|
||||||
import { ModuleRef } from '@nestjs/core';
|
|
||||||
import { RedmineTypes } from '../models/redmine-types';
|
import { RedmineTypes } from '../models/redmine-types';
|
||||||
import { IssueEnhancerInterface } from './issue-enhancer-interface';
|
import { IssueEnhancerInterface } from './issue-enhancer-interface';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class EnhancerService {
|
export class EnhancerService {
|
||||||
private logger = new Logger(EnhancerService.name);
|
private logger = new Logger(EnhancerService.name);
|
||||||
private initilialized = false;
|
private enhancers: IssueEnhancerInterface[] = [];
|
||||||
|
|
||||||
constructor(
|
addEnhancer(enh: IssueEnhancerInterface[]): void {
|
||||||
private moduleRef: ModuleRef,
|
this.enhancers.push(...enh);
|
||||||
@Inject('ENHANCERS')
|
|
||||||
private enhancers: IssueEnhancerInterface[],
|
|
||||||
) {}
|
|
||||||
|
|
||||||
private init(): void {
|
|
||||||
if (!this.initilialized) {
|
|
||||||
this.logger.log(`Initialize EnhancerService start`);
|
|
||||||
this.enhancers.forEach((e) => e.init(this.moduleRef));
|
|
||||||
this.logger.log(`Initialize EnhancerService finished`);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async enhanceIssue(
|
async enhanceIssue(
|
||||||
issue: RedmineTypes.Issue & Record<string, any>,
|
issue: RedmineTypes.Issue & Record<string, any>,
|
||||||
): Promise<RedmineTypes.Issue & Record<string, any>> {
|
): Promise<RedmineTypes.Issue & Record<string, any>> {
|
||||||
this.init();
|
|
||||||
for (let i = 0; i < this.enhancers.length; i++) {
|
for (let i = 0; i < this.enhancers.length; i++) {
|
||||||
const enhancer = this.enhancers[i];
|
const enhancer = this.enhancers[i];
|
||||||
// eslint-disable-next-line prettier/prettier
|
// eslint-disable-next-line prettier/prettier
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,7 @@
|
||||||
import { ModuleRef } from '@nestjs/core';
|
|
||||||
import { RedmineTypes } from '../models/redmine-types';
|
import { RedmineTypes } from '../models/redmine-types';
|
||||||
|
|
||||||
export interface IssueEnhancerInterface {
|
export interface IssueEnhancerInterface {
|
||||||
name: string;
|
name: string;
|
||||||
init(moduleRef: ModuleRef);
|
|
||||||
enhance(
|
enhance(
|
||||||
issue: RedmineTypes.Issue,
|
issue: RedmineTypes.Issue,
|
||||||
): Promise<RedmineTypes.Issue & Record<string, any>>;
|
): Promise<RedmineTypes.Issue & Record<string, any>>;
|
||||||
|
|
|
||||||
|
|
@ -6,10 +6,6 @@ import { IssueEnhancerInterface } from './issue-enhancer-interface';
|
||||||
export class TimestampEnhancer implements IssueEnhancerInterface {
|
export class TimestampEnhancer implements IssueEnhancerInterface {
|
||||||
name = 'timestamp';
|
name = 'timestamp';
|
||||||
|
|
||||||
init() {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
async enhance(
|
async enhance(
|
||||||
issue: RedmineTypes.Issue,
|
issue: RedmineTypes.Issue,
|
||||||
): Promise<RedmineTypes.Issue & Record<string, any>> {
|
): Promise<RedmineTypes.Issue & Record<string, any>> {
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,5 @@
|
||||||
import { IssueEnhancerInterface } from '../issue-enhancers/issue-enhancer-interface';
|
|
||||||
import { MainConfigModel } from './main-config-model';
|
import { MainConfigModel } from './main-config-model';
|
||||||
|
|
||||||
export type ModuleParams = {
|
export type ModuleParams = {
|
||||||
config?: MainConfigModel;
|
config?: MainConfigModel;
|
||||||
enhancers?: IssueEnhancerInterface[];
|
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -46,6 +46,32 @@ export class UsersService {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async findUserByName(
|
||||||
|
firstname: string,
|
||||||
|
lastname: string,
|
||||||
|
): Promise<RedmineTypes.User | null> {
|
||||||
|
const userFromMemoryCache = this.memoryCache.find((item) => {
|
||||||
|
return item.firstname === firstname && item.lastname === lastname;
|
||||||
|
});
|
||||||
|
if (userFromMemoryCache) {
|
||||||
|
return RedmineTypes.CreateUser(userFromMemoryCache);
|
||||||
|
}
|
||||||
|
const usersDb = await this.users.getDatasource();
|
||||||
|
const res = await usersDb.find({
|
||||||
|
selector: {
|
||||||
|
firstname: firstname,
|
||||||
|
lastname: lastname,
|
||||||
|
},
|
||||||
|
limit: 1,
|
||||||
|
});
|
||||||
|
if (!res && !res.docs && !res.docs[0]) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const userFromDb = res.docs[0];
|
||||||
|
this.memoryCache.set(userFromDb.id, userFromDb);
|
||||||
|
return RedmineTypes.CreateUser(userFromDb);
|
||||||
|
}
|
||||||
|
|
||||||
private async getUserFromRedmine(
|
private async getUserFromRedmine(
|
||||||
userId: number,
|
userId: number,
|
||||||
): Promise<RedmineTypes.User | null> {
|
): Promise<RedmineTypes.User | null> {
|
||||||
|
|
|
||||||
|
|
@ -39,6 +39,22 @@ export class MemoryCache<K, T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
find(fn: (item: T, key: K) => boolean): (T & Timestamped) | null {
|
||||||
|
for (const key in this.memoryCache) {
|
||||||
|
if (Object.prototype.hasOwnProperty.call(this.memoryCache, key)) {
|
||||||
|
const item = this.memoryCache[key];
|
||||||
|
if (TimestampIsTimeouted(item, this.timeout)) {
|
||||||
|
delete this.memoryCache[key];
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (fn(item, key as any)) {
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
private startAutoclean() {
|
private startAutoclean() {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
this.cleanTimeouted();
|
this.cleanTimeouted();
|
||||||
|
|
|
||||||
|
|
@ -1,38 +1,58 @@
|
||||||
import { EventEmitterModule } from '@app/event-emitter';
|
import { EventEmitterModule } from '@app/event-emitter';
|
||||||
|
import { RedmineIssuesCacheWriterService } from '@app/event-emitter/issue-cache-writer/redmine-issues-cache-writer.service';
|
||||||
|
import { EnhancerService } from '@app/event-emitter/issue-enhancers/enhancer.service';
|
||||||
import { TimestampEnhancer } from '@app/event-emitter/issue-enhancers/timestamps-enhancer';
|
import { TimestampEnhancer } from '@app/event-emitter/issue-enhancers/timestamps-enhancer';
|
||||||
import { MainController } from '@app/event-emitter/main/main.controller';
|
import { MainController } from '@app/event-emitter/main/main.controller';
|
||||||
import { UsersService } from '@app/event-emitter/users/users.service';
|
import { Logger, Module, OnModuleInit } from '@nestjs/common';
|
||||||
import { Module } from '@nestjs/common';
|
|
||||||
import { ConfigModule } from '@nestjs/config';
|
import { ConfigModule } from '@nestjs/config';
|
||||||
import { AppController } from './app.controller';
|
import { AppController } from './app.controller';
|
||||||
import { AppService } from './app.service';
|
import { AppService } from './app.service';
|
||||||
import configuration from './configs/app';
|
import configuration from './configs/app';
|
||||||
import { CurrentUserEnhancer } from './issue-enhancers/current-user-enhancer';
|
import { CurrentUserEnhancer } from './issue-enhancers/current-user-enhancer';
|
||||||
import { CustomFieldsEnhancer } from './issue-enhancers/custom-fields-enhancer';
|
import { CustomFieldsEnhancer } from './issue-enhancers/custom-fields-enhancer';
|
||||||
|
import { PersonalNotificationsService } from './notifications/personal-notifications.service';
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [
|
imports: [
|
||||||
EventEmitterModule.register({
|
EventEmitterModule.register({
|
||||||
config: configuration().redmineIssueEventEmitterConfig,
|
config: configuration().redmineIssueEventEmitterConfig,
|
||||||
enhancers: [
|
|
||||||
new TimestampEnhancer(),
|
|
||||||
new CustomFieldsEnhancer(),
|
|
||||||
new CurrentUserEnhancer(),
|
|
||||||
],
|
|
||||||
}),
|
}),
|
||||||
ConfigModule.forRoot({ load: [configuration] }),
|
ConfigModule.forRoot({ load: [configuration] }),
|
||||||
],
|
],
|
||||||
controllers: [AppController, MainController],
|
controllers: [AppController, MainController],
|
||||||
providers: [
|
providers: [
|
||||||
AppService,
|
AppService,
|
||||||
UsersService,
|
|
||||||
CustomFieldsEnhancer,
|
CustomFieldsEnhancer,
|
||||||
CurrentUserEnhancer,
|
CurrentUserEnhancer,
|
||||||
|
PersonalNotificationsService,
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
export class AppModule {
|
export class AppModule implements OnModuleInit {
|
||||||
|
private logger = new Logger(AppModule.name);
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
|
private personalNotificationsService: PersonalNotificationsService,
|
||||||
|
private redmineIssuesCacheWriterService: RedmineIssuesCacheWriterService,
|
||||||
|
private enhancerService: EnhancerService,
|
||||||
private timestampEnhancer: TimestampEnhancer,
|
private timestampEnhancer: TimestampEnhancer,
|
||||||
private customFieldsEnhancer: CustomFieldsEnhancer,
|
private customFieldsEnhancer: CustomFieldsEnhancer,
|
||||||
|
private currentUserEnhancer: CurrentUserEnhancer,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
|
onModuleInit() {
|
||||||
|
this.enhancerService.addEnhancer([
|
||||||
|
this.timestampEnhancer,
|
||||||
|
this.customFieldsEnhancer,
|
||||||
|
this.currentUserEnhancer,
|
||||||
|
]);
|
||||||
|
this.personalNotificationsService.$messages.subscribe((message) => {
|
||||||
|
// eslint-disable-next-line prettier/prettier
|
||||||
|
this.logger.log(`Get personal message ${JSON.stringify(message.message)} for recipients ${JSON.stringify(message.recipients)}`);
|
||||||
|
});
|
||||||
|
this.redmineIssuesCacheWriterService.subject.subscribe(
|
||||||
|
async (saveResult) => {
|
||||||
|
await this.personalNotificationsService.analize(saveResult);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -20,10 +20,6 @@ export class CurrentUserEnhancer implements IssueEnhancerInterface {
|
||||||
Rejected: 'dev',
|
Rejected: 'dev',
|
||||||
};
|
};
|
||||||
|
|
||||||
init() {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
async enhance(
|
async enhance(
|
||||||
issue: RedmineTypes.Issue,
|
issue: RedmineTypes.Issue,
|
||||||
): Promise<RedmineTypes.Issue & Record<string, any>> {
|
): Promise<RedmineTypes.Issue & Record<string, any>> {
|
||||||
|
|
|
||||||
|
|
@ -2,17 +2,11 @@ import { IssueEnhancerInterface } from '@app/event-emitter/issue-enhancers/issue
|
||||||
import { RedmineTypes } from '@app/event-emitter/models/redmine-types';
|
import { RedmineTypes } from '@app/event-emitter/models/redmine-types';
|
||||||
import { UsersService } from '@app/event-emitter/users/users.service';
|
import { UsersService } from '@app/event-emitter/users/users.service';
|
||||||
import { Injectable } from '@nestjs/common';
|
import { Injectable } from '@nestjs/common';
|
||||||
import { ModuleRef } from '@nestjs/core';
|
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class CustomFieldsEnhancer implements IssueEnhancerInterface {
|
export class CustomFieldsEnhancer implements IssueEnhancerInterface {
|
||||||
name = 'custom-fields';
|
name = 'custom-fields';
|
||||||
|
|
||||||
private usersService: UsersService;
|
constructor(private usersService: UsersService) {}
|
||||||
|
|
||||||
init(moduleRef: ModuleRef): void {
|
|
||||||
this.usersService = moduleRef.get(UsersService);
|
|
||||||
}
|
|
||||||
|
|
||||||
async enhance(
|
async enhance(
|
||||||
issue: RedmineTypes.Issue,
|
issue: RedmineTypes.Issue,
|
||||||
|
|
|
||||||
5
src/models/personal-parsed-message.model.ts
Normal file
5
src/models/personal-parsed-message.model.ts
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
export class PersonalParsedMessage {
|
||||||
|
sender: number;
|
||||||
|
message: string;
|
||||||
|
recipients: number[];
|
||||||
|
}
|
||||||
75
src/notifications/personal-notifications.service.ts
Normal file
75
src/notifications/personal-notifications.service.ts
Normal file
|
|
@ -0,0 +1,75 @@
|
||||||
|
import { RedmineTypes } from '@app/event-emitter/models/redmine-types';
|
||||||
|
import { SaveResponse } from '@app/event-emitter/models/save-response';
|
||||||
|
import { UsersService } from '@app/event-emitter/users/users.service';
|
||||||
|
import { Injectable, Logger } from '@nestjs/common';
|
||||||
|
import { Subject } from 'rxjs';
|
||||||
|
import { PersonalParsedMessage } from 'src/models/personal-parsed-message.model';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class PersonalNotificationsService {
|
||||||
|
private userNameRe = /@([\wА-Яа-яЁё]+) ([\wА-Яа-яЁё]+)@/g;
|
||||||
|
private logger = new Logger(PersonalNotificationsService.name);
|
||||||
|
|
||||||
|
$messages = new Subject<PersonalParsedMessage>();
|
||||||
|
|
||||||
|
constructor(private usersService: UsersService) {}
|
||||||
|
|
||||||
|
async analize(data: SaveResponse): Promise<PersonalParsedMessage[]> {
|
||||||
|
this.logger.debug(
|
||||||
|
`Analize personal messages for issue ` +
|
||||||
|
`#${data.current.id} (${data.current.subject}) start`,
|
||||||
|
);
|
||||||
|
const pMessages = data.journalsDiff.map(async (journal) => {
|
||||||
|
const message = await this.getMessage(journal);
|
||||||
|
if (message) {
|
||||||
|
this.logger.log(
|
||||||
|
`Found personal message in issue #${data.current.id} ` +
|
||||||
|
`(${data.current.subject}) - ` +
|
||||||
|
`message = ${JSON.stringify(message.message)} ` +
|
||||||
|
`from sender ${message.sender} ` +
|
||||||
|
`for recipients ${JSON.stringify(message.recipients)}`,
|
||||||
|
);
|
||||||
|
this.$messages.next(message);
|
||||||
|
}
|
||||||
|
return message;
|
||||||
|
});
|
||||||
|
const res = (await Promise.all(pMessages)).filter((m) => Boolean(m));
|
||||||
|
this.logger.debug(
|
||||||
|
`Analize personal messages for issue ` +
|
||||||
|
`#${data.current.id} (${data.current.subject}) finished`,
|
||||||
|
);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async getMessage(
|
||||||
|
journal: RedmineTypes.Journal,
|
||||||
|
): Promise<PersonalParsedMessage | null> {
|
||||||
|
if (!journal.notes) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const notes = journal.notes;
|
||||||
|
const results = notes.matchAll(this.userNameRe);
|
||||||
|
const recipients: number[] = [];
|
||||||
|
let result = results.next();
|
||||||
|
while (!result.done) {
|
||||||
|
if (result.value && result.value[1] && result.value[2]) {
|
||||||
|
const firstname = result.value[1];
|
||||||
|
const lastname = result.value[2];
|
||||||
|
const user = await this.usersService.findUserByName(
|
||||||
|
firstname,
|
||||||
|
lastname,
|
||||||
|
);
|
||||||
|
if (user) recipients.push(user.id);
|
||||||
|
}
|
||||||
|
result = results.next();
|
||||||
|
}
|
||||||
|
if (recipients.length > 0) {
|
||||||
|
return {
|
||||||
|
message: notes,
|
||||||
|
sender: journal.user.id,
|
||||||
|
recipients: recipients,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue