Добавлена api функция для отправки сообщений в telegram bot
/api/telegram-bot/send-message (POST)
This commit is contained in:
parent
5fb996a12e
commit
b2ba939323
5 changed files with 1431 additions and 580 deletions
1854
package-lock.json
generated
1854
package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
|
@ -36,6 +36,7 @@
|
||||||
"hbs": "^4.2.0",
|
"hbs": "^4.2.0",
|
||||||
"imap-simple": "^5.1.0",
|
"imap-simple": "^5.1.0",
|
||||||
"jsonc-parser": "^3.2.0",
|
"jsonc-parser": "^3.2.0",
|
||||||
|
"jsonpath-plus": "^8.1.0",
|
||||||
"luxon": "^3.1.0",
|
"luxon": "^3.1.0",
|
||||||
"moo": "^0.5.2",
|
"moo": "^0.5.2",
|
||||||
"nano": "^10.0.0",
|
"nano": "^10.0.0",
|
||||||
|
|
@ -54,6 +55,7 @@
|
||||||
"@types/cron": "^2.0.0",
|
"@types/cron": "^2.0.0",
|
||||||
"@types/express": "^4.17.13",
|
"@types/express": "^4.17.13",
|
||||||
"@types/jest": "27.4.0",
|
"@types/jest": "27.4.0",
|
||||||
|
"@types/jsonpath": "^0.2.4",
|
||||||
"@types/luxon": "^3.1.0",
|
"@types/luxon": "^3.1.0",
|
||||||
"@types/moo": "^0.5.6",
|
"@types/moo": "^0.5.6",
|
||||||
"@types/node": "^16.0.0",
|
"@types/node": "^16.0.0",
|
||||||
|
|
|
||||||
|
|
@ -53,6 +53,7 @@ import { CreateTagManagerServiceProvider } from './tags-manager/tags-manager.ser
|
||||||
import { CalendarEnhancer } from '@app/event-emitter/issue-enhancers/calendar-enhancer';
|
import { CalendarEnhancer } from '@app/event-emitter/issue-enhancers/calendar-enhancer';
|
||||||
import { Dashboards as DashboardsDs } from '@app/event-emitter/couchdb-datasources/dashboards';
|
import { Dashboards as DashboardsDs } from '@app/event-emitter/couchdb-datasources/dashboards';
|
||||||
import { DashboardInitService } from './dashboards/dashboard-init.service';
|
import { DashboardInitService } from './dashboards/dashboard-init.service';
|
||||||
|
import { TelegramBotController } from './telegram-bot/telegram-bot.controller';
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [
|
imports: [
|
||||||
|
|
@ -76,6 +77,7 @@ import { DashboardInitService } from './dashboards/dashboard-init.service';
|
||||||
SimpleKanbanBoardController,
|
SimpleKanbanBoardController,
|
||||||
SimpleIssuesListController,
|
SimpleIssuesListController,
|
||||||
TagsManagerController,
|
TagsManagerController,
|
||||||
|
TelegramBotController,
|
||||||
],
|
],
|
||||||
providers: [
|
providers: [
|
||||||
AppService,
|
AppService,
|
||||||
|
|
|
||||||
18
src/telegram-bot/telegram-bot.controller.ts
Normal file
18
src/telegram-bot/telegram-bot.controller.ts
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
import { Body, Controller, Post } from '@nestjs/common';
|
||||||
|
import { SendMessageParams, TelegramBotService } from './telegram-bot.service';
|
||||||
|
import {
|
||||||
|
BadRequestErrorHandler,
|
||||||
|
getOrAppErrorOrThrow,
|
||||||
|
} from '@app/event-emitter/utils/result';
|
||||||
|
|
||||||
|
@Controller('/api/telegram-bot')
|
||||||
|
export class TelegramBotController {
|
||||||
|
constructor(private telegramBotService: TelegramBotService) {}
|
||||||
|
|
||||||
|
@Post('send-message')
|
||||||
|
async sendMessage(@Body() params: SendMessageParams): Promise<void> {
|
||||||
|
await getOrAppErrorOrThrow(async () => {
|
||||||
|
await this.telegramBotService.sendMessageByParams(params);
|
||||||
|
}, BadRequestErrorHandler);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -8,6 +8,10 @@ import { UserMetaInfoModel } from 'src/models/user-meta-info.model';
|
||||||
import { CurrentIssuesEccmBotHandlerService } from './handlers/current-issues-eccm.bot-handler.service';
|
import { CurrentIssuesEccmBotHandlerService } from './handlers/current-issues-eccm.bot-handler.service';
|
||||||
import { TelegramBotHandlerInterface } from './telegram.bot-handler.interface';
|
import { TelegramBotHandlerInterface } from './telegram.bot-handler.interface';
|
||||||
import { SetDailyEccmUserCommentBotHandlerService } from './handlers/set-daily-eccm-user-comment.bot-handler.service';
|
import { SetDailyEccmUserCommentBotHandlerService } from './handlers/set-daily-eccm-user-comment.bot-handler.service';
|
||||||
|
import { IssuesService } from '@app/event-emitter/issues/issues.service';
|
||||||
|
import { JSONPath } from 'jsonpath-plus';
|
||||||
|
import { RedmineTypes } from '@app/event-emitter/models/redmine-types';
|
||||||
|
import { createAppError } from '@app/event-emitter/utils/result';
|
||||||
|
|
||||||
const MAX_TELEGRAM_MESSAGE_LENGTH = 4000;
|
const MAX_TELEGRAM_MESSAGE_LENGTH = 4000;
|
||||||
|
|
||||||
|
|
@ -18,6 +22,60 @@ export function cutMessage(msg: string): string {
|
||||||
return msg;
|
return msg;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type OptionsParams = {
|
||||||
|
parse_mode?: TelegramBot.ParseMode | undefined;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type RecipientByName = {
|
||||||
|
firstname: string;
|
||||||
|
lastname: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type RecipientByIssueField = {
|
||||||
|
issueId: number;
|
||||||
|
field: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type SendMessageParams = {
|
||||||
|
recipients: (RecipientByName | RecipientByIssueField)[];
|
||||||
|
msg: string;
|
||||||
|
options?: OptionsParams;
|
||||||
|
};
|
||||||
|
|
||||||
|
export function validateSendMessageParams(params: SendMessageParams): string[] {
|
||||||
|
const res: string[] = [];
|
||||||
|
if (typeof params.msg !== 'string') {
|
||||||
|
res.push('Wrong msg field value, must be string');
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
typeof params.recipients !== 'object' ||
|
||||||
|
typeof params.recipients.length !== 'number'
|
||||||
|
) {
|
||||||
|
res.push('Wrong recipients field value, must be array');
|
||||||
|
}
|
||||||
|
for (let i = 0; i < params.recipients?.length; i++) {
|
||||||
|
const r: any = params.recipients[i];
|
||||||
|
const checkRecipient = Boolean(
|
||||||
|
(typeof r.firstname === 'string' && typeof r.lastname === 'string') ||
|
||||||
|
(typeof r.issueId === 'number' && typeof r.field === 'string'),
|
||||||
|
);
|
||||||
|
if (!checkRecipient) {
|
||||||
|
res.push(
|
||||||
|
'Wrong recipient item value, must be object with firstname+lastname or issueId+field',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
params?.options?.parse_mode &&
|
||||||
|
['Markdown', 'MarkdownV2', 'HTML'].indexOf(params.options.parse_mode) < 0
|
||||||
|
) {
|
||||||
|
res.push(
|
||||||
|
`Wrong options.parse_mode value, must be one of 'Markdown', 'MarkdownV2', 'HTML'`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class TelegramBotService {
|
export class TelegramBotService {
|
||||||
private logger = new Logger(TelegramBotService.name);
|
private logger = new Logger(TelegramBotService.name);
|
||||||
|
|
@ -35,6 +93,7 @@ export class TelegramBotService {
|
||||||
private configService: ConfigService,
|
private configService: ConfigService,
|
||||||
private currentIssuesBotHandlerService: CurrentIssuesEccmBotHandlerService,
|
private currentIssuesBotHandlerService: CurrentIssuesEccmBotHandlerService,
|
||||||
private setDailyEccmUserCommentBotHandlerService: SetDailyEccmUserCommentBotHandlerService,
|
private setDailyEccmUserCommentBotHandlerService: SetDailyEccmUserCommentBotHandlerService,
|
||||||
|
private issuesService: IssuesService,
|
||||||
) {
|
) {
|
||||||
this.telegramBotToken = this.configService.get<string>('telegramBotToken');
|
this.telegramBotToken = this.configService.get<string>('telegramBotToken');
|
||||||
this.redminePublicUrlPrefix =
|
this.redminePublicUrlPrefix =
|
||||||
|
|
@ -142,6 +201,62 @@ export class TelegramBotService {
|
||||||
return await this.sendMessageByRedmineId(user.id, msg, options);
|
return await this.sendMessageByRedmineId(user.id, msg, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async sendMessageByParams(params: SendMessageParams): Promise<void> {
|
||||||
|
const paramsErrors = validateSendMessageParams(params);
|
||||||
|
if (paramsErrors && paramsErrors.length > 0) {
|
||||||
|
throw createAppError(`Params errors - ${JSON.stringify(paramsErrors)}`);
|
||||||
|
}
|
||||||
|
const issuesStore = await this.getIssuesStore(params);
|
||||||
|
for (let i = 0; i < params.recipients.length; i++) {
|
||||||
|
const recipient: any = params.recipients[i];
|
||||||
|
if (recipient.firstname && recipient.lastname) {
|
||||||
|
await this.sendMessageByName(
|
||||||
|
recipient.firstname,
|
||||||
|
recipient.lastname,
|
||||||
|
params.msg,
|
||||||
|
params.options,
|
||||||
|
);
|
||||||
|
} else if (recipient.issueId && recipient.field) {
|
||||||
|
const issue = issuesStore[recipient.issueId];
|
||||||
|
if (!issue) continue;
|
||||||
|
let fieldValue: any;
|
||||||
|
try {
|
||||||
|
fieldValue = JSONPath({ json: issue, path: recipient.field });
|
||||||
|
} catch (ex) {
|
||||||
|
const warnMsg =
|
||||||
|
`Error at get value from issueId = ${recipient.issueId}; ` +
|
||||||
|
`field = ${JSON.stringify(recipient.field)} ` +
|
||||||
|
`- ${ex.message}`;
|
||||||
|
this.logger.warn(warnMsg);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (fieldValue && fieldValue.length > 0) {
|
||||||
|
fieldValue = fieldValue[0];
|
||||||
|
} else {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
typeof fieldValue === 'object' &&
|
||||||
|
fieldValue.firstname &&
|
||||||
|
fieldValue.lastname
|
||||||
|
) {
|
||||||
|
await this.sendMessageByName(
|
||||||
|
fieldValue.firstname,
|
||||||
|
fieldValue.lastname,
|
||||||
|
params.msg,
|
||||||
|
params.options,
|
||||||
|
);
|
||||||
|
} else if (typeof fieldValue === 'number') {
|
||||||
|
await this.sendMessageByRedmineId(
|
||||||
|
fieldValue,
|
||||||
|
params.msg,
|
||||||
|
params.options,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private async register(
|
private async register(
|
||||||
msg: TelegramBot.Message,
|
msg: TelegramBot.Message,
|
||||||
): Promise<{ result: boolean; message: string }> {
|
): Promise<{ result: boolean; message: string }> {
|
||||||
|
|
@ -202,4 +317,24 @@ export class TelegramBotService {
|
||||||
await this.userMetaInfoService.delete(userMetaInfo);
|
await this.userMetaInfoService.delete(userMetaInfo);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async getIssuesStore(
|
||||||
|
params: SendMessageParams,
|
||||||
|
): Promise<Record<number, RedmineTypes.Issue>> {
|
||||||
|
const issueIds: number[] = [];
|
||||||
|
for (let i = 0; i < params.recipients.length; i++) {
|
||||||
|
const recipient: any = params.recipients[i];
|
||||||
|
if (typeof recipient.issueId === 'number' && recipient.issueId > 0) {
|
||||||
|
issueIds.push(recipient.issueId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (issueIds.length <= 0) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
const issues = await this.issuesService.getIssues(issueIds);
|
||||||
|
return issues.reduce((acc, issue) => {
|
||||||
|
acc[issue.id] = issue;
|
||||||
|
return acc;
|
||||||
|
}, {});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue