global fix and update bot Api

This commit is contained in:
lendry
2026-06-26 13:01:52 +03:00
parent d3ea470d02
commit aa228d84eb
29 changed files with 980 additions and 221 deletions

View File

@@ -1,5 +1,5 @@
import { All, Body, Controller, HttpCode, Param, Req, Res } from '@nestjs/common';
import { ApiExcludeController } from '@nestjs/swagger';
import { All, Body, Controller, Get, HttpCode, Param, Post, Query, Req, Res } from '@nestjs/common';
import { ApiBody, ApiOperation, ApiParam, ApiTags } from '@nestjs/swagger';
import { firstValueFrom, timeout } from 'rxjs';
import { CoreGrpcService } from '../core-grpc.service';
@@ -13,19 +13,205 @@ type HttpResponse = {
setHeader(name: string, value: string): void;
};
@ApiExcludeController()
const botPathParam = {
name: 'botPath',
description: 'Путь токена в формате bot<token>, совместимо с Telegram Bot API.',
example: 'bot123456:ABC-DEF'
};
const chatIdProperty = {
oneOf: [{ type: 'string' }, { type: 'number' }],
description: 'ID пользователя/чата в приложении или numeric chat_id из ответа Bot API.'
};
@ApiTags('Telegram Bot API')
@Controller()
export class TelegramBotApiController {
constructor(private readonly core: CoreGrpcService) {}
@Get(':botPath/getMe')
@ApiOperation({
summary: 'Bot API: getMe',
description: 'Возвращает профиль бота в формате Telegram Bot API.'
})
@ApiParam(botPathParam)
getMe(@Res({ passthrough: true }) response: HttpResponse, @Param('botPath') botPath: string) {
return this.executeTelegramMethod(response, botPath, 'getMe', {});
}
@Post(':botPath/getChat')
@HttpCode(200)
@ApiOperation({
summary: 'Bot API: getChat',
description: 'Возвращает приватный чат приложения и pinned_message, если сообщение закреплено.'
})
@ApiParam(botPathParam)
@ApiBody({
schema: {
type: 'object',
required: ['chat_id'],
properties: {
chat_id: chatIdProperty
}
}
})
getChatPost(
@Res({ passthrough: true }) response: HttpResponse,
@Param('botPath') botPath: string,
@Body() body: Record<string, unknown>
) {
return this.executeTelegramMethod(response, botPath, 'getChat', body ?? {});
}
@Get(':botPath/getChat')
@ApiOperation({
summary: 'Bot API: getChat (GET)',
description: 'GET-вариант метода getChat для клиентов, которые передают параметры в query.'
})
@ApiParam(botPathParam)
getChatGet(
@Res({ passthrough: true }) response: HttpResponse,
@Param('botPath') botPath: string,
@Query() query: Record<string, unknown>
) {
return this.executeTelegramMethod(response, botPath, 'getChat', query ?? {});
}
@Post(':botPath/sendMessage')
@HttpCode(200)
@ApiOperation({
summary: 'Bot API: sendMessage',
description: 'Отправляет сообщение пользователю внутри приложения. Поддерживаются text, parse_mode и reply_markup.'
})
@ApiParam(botPathParam)
@ApiBody({
schema: {
type: 'object',
required: ['chat_id', 'text'],
properties: {
chat_id: chatIdProperty,
text: { type: 'string' },
parse_mode: { type: 'string', example: 'HTML' },
reply_markup: { type: 'object', additionalProperties: true }
}
}
})
sendMessage(
@Res({ passthrough: true }) response: HttpResponse,
@Param('botPath') botPath: string,
@Body() body: Record<string, unknown>
) {
return this.executeTelegramMethod(response, botPath, 'sendMessage', body ?? {});
}
@Post(':botPath/editMessageText')
@HttpCode(200)
@ApiOperation({
summary: 'Bot API: editMessageText',
description: 'Редактирует текст ранее отправленного ботом сообщения.'
})
@ApiParam(botPathParam)
@ApiBody({
schema: {
type: 'object',
required: ['chat_id', 'message_id', 'text'],
properties: {
chat_id: chatIdProperty,
message_id: { type: 'integer' },
text: { type: 'string' },
parse_mode: { type: 'string', example: 'HTML' },
reply_markup: { type: 'object', additionalProperties: true }
}
}
})
editMessageText(
@Res({ passthrough: true }) response: HttpResponse,
@Param('botPath') botPath: string,
@Body() body: Record<string, unknown>
) {
return this.executeTelegramMethod(response, botPath, 'editMessageText', body ?? {});
}
@Post(':botPath/pinChatMessage')
@HttpCode(200)
@ApiOperation({
summary: 'Bot API: pinChatMessage',
description: 'Закрепляет сообщение бота в чате приложения.'
})
@ApiParam(botPathParam)
@ApiBody({
schema: {
type: 'object',
required: ['chat_id', 'message_id'],
properties: {
chat_id: chatIdProperty,
message_id: { type: 'integer' },
disable_notification: { type: 'boolean', default: false }
}
}
})
pinChatMessage(
@Res({ passthrough: true }) response: HttpResponse,
@Param('botPath') botPath: string,
@Body() body: Record<string, unknown>
) {
return this.executeTelegramMethod(response, botPath, 'pinChatMessage', body ?? {});
}
@Post(':botPath/unpinChatMessage')
@HttpCode(200)
@ApiOperation({
summary: 'Bot API: unpinChatMessage',
description: 'Открепляет указанное сообщение. Если message_id не передан, открепляет последнее закреплённое сообщение.'
})
@ApiParam(botPathParam)
@ApiBody({
schema: {
type: 'object',
required: ['chat_id'],
properties: {
chat_id: chatIdProperty,
message_id: { type: 'integer' }
}
}
})
unpinChatMessage(
@Res({ passthrough: true }) response: HttpResponse,
@Param('botPath') botPath: string,
@Body() body: Record<string, unknown>
) {
return this.executeTelegramMethod(response, botPath, 'unpinChatMessage', body ?? {});
}
@All(':botPath/:method')
@HttpCode(200)
@ApiOperation({
summary: 'Bot API: универсальный метод',
description:
'Совместимый endpoint /bot<TOKEN>/<method>. Поддерживаются getMe, getChat, sendMessage, editMessageText, editMessageReplyMarkup, sendPhoto, sendDocument, pinChatMessage, unpinChatMessage, answerCallbackQuery, setWebhook, deleteWebhook, getWebhookInfo, getUpdates и setChatMenuButton.'
})
@ApiParam(botPathParam)
@ApiParam({
name: 'method',
description: 'Название метода Telegram Bot API.',
example: 'sendMessage'
})
async handleTelegramMethod(
@Req() request: HttpRequest,
@Res({ passthrough: true }) response: HttpResponse,
@Param('botPath') botPath: string,
@Param('method') method: string,
@Body() body: Record<string, unknown>
) {
const payload = request.method === 'GET' ? { ...request.query } : body ?? {};
return this.executeTelegramMethod(response, botPath, method, payload);
}
private async executeTelegramMethod(
response: HttpResponse,
botPath: string,
method: string,
payload: Record<string, unknown>
) {
if (!botPath.startsWith('bot')) {
response.status(404);
@@ -38,7 +224,6 @@ export class TelegramBotApiController {
return { ok: false, error_code: 401, description: 'Unauthorized' };
}
const payload = request.method === 'GET' ? { ...request.query } : body ?? {};
const grpcCall = this.core.bot.ExecuteBotMethod({
token,
method,