Добавлена 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",
|
||||
"imap-simple": "^5.1.0",
|
||||
"jsonc-parser": "^3.2.0",
|
||||
"jsonpath-plus": "^8.1.0",
|
||||
"luxon": "^3.1.0",
|
||||
"moo": "^0.5.2",
|
||||
"nano": "^10.0.0",
|
||||
|
|
@ -54,6 +55,7 @@
|
|||
"@types/cron": "^2.0.0",
|
||||
"@types/express": "^4.17.13",
|
||||
"@types/jest": "27.4.0",
|
||||
"@types/jsonpath": "^0.2.4",
|
||||
"@types/luxon": "^3.1.0",
|
||||
"@types/moo": "^0.5.6",
|
||||
"@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 { Dashboards as DashboardsDs } from '@app/event-emitter/couchdb-datasources/dashboards';
|
||||
import { DashboardInitService } from './dashboards/dashboard-init.service';
|
||||
import { TelegramBotController } from './telegram-bot/telegram-bot.controller';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
|
|
@ -76,6 +77,7 @@ import { DashboardInitService } from './dashboards/dashboard-init.service';
|
|||
SimpleKanbanBoardController,
|
||||
SimpleIssuesListController,
|
||||
TagsManagerController,
|
||||
TelegramBotController,
|
||||
],
|
||||
providers: [
|
||||
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 { TelegramBotHandlerInterface } from './telegram.bot-handler.interface';
|
||||
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;
|
||||
|
||||
|
|
@ -18,6 +22,60 @@ export function cutMessage(msg: string): string {
|
|||
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()
|
||||
export class TelegramBotService {
|
||||
private logger = new Logger(TelegramBotService.name);
|
||||
|
|
@ -35,6 +93,7 @@ export class TelegramBotService {
|
|||
private configService: ConfigService,
|
||||
private currentIssuesBotHandlerService: CurrentIssuesEccmBotHandlerService,
|
||||
private setDailyEccmUserCommentBotHandlerService: SetDailyEccmUserCommentBotHandlerService,
|
||||
private issuesService: IssuesService,
|
||||
) {
|
||||
this.telegramBotToken = this.configService.get<string>('telegramBotToken');
|
||||
this.redminePublicUrlPrefix =
|
||||
|
|
@ -142,6 +201,62 @@ export class TelegramBotService {
|
|||
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(
|
||||
msg: TelegramBot.Message,
|
||||
): Promise<{ result: boolean; message: string }> {
|
||||
|
|
@ -202,4 +317,24 @@ export class TelegramBotService {
|
|||
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