global fix and update bot Api
This commit is contained in:
@@ -13,6 +13,7 @@ import {
|
||||
ForwardMessagesDto,
|
||||
MarkRoomReadDto,
|
||||
SendChatMessageDto,
|
||||
SetMessagePinnedDto,
|
||||
SetRoomNotificationsMutedDto,
|
||||
ToggleMessageReactionDto,
|
||||
UpdateChatRoomDto,
|
||||
@@ -198,6 +199,17 @@ export class ChatController {
|
||||
return firstValueFrom(this.core.chat.DeleteMessage({ userId, messageId }));
|
||||
}
|
||||
|
||||
@Post('messages/:messageId/pin')
|
||||
@ApiOperation({ summary: 'Закрепить или открепить сообщение' })
|
||||
async setMessagePinned(
|
||||
@Headers('authorization') authorization: string | undefined,
|
||||
@Param('messageId') messageId: string,
|
||||
@Body() dto: SetMessagePinnedDto
|
||||
) {
|
||||
const userId = await this.auth(authorization);
|
||||
return firstValueFrom(this.core.chat.SetMessagePinned({ userId, messageId, pinned: dto.pinned }));
|
||||
}
|
||||
|
||||
@Post('messages/:messageId/reactions')
|
||||
@ApiOperation({ summary: 'Поставить или снять реакцию' })
|
||||
async toggleReaction(
|
||||
|
||||
@@ -5,7 +5,7 @@ import { firstValueFrom } from 'rxjs';
|
||||
import { map } from 'rxjs/operators';
|
||||
import { CoreGrpcService } from '../core-grpc.service';
|
||||
import { getAuthorizedUserId } from '../document-access';
|
||||
import { AddFamilyMemberDto, AddBotToFamilyDto, CreateFamilyGroupDto, RespondFamilyInviteDto, SendFamilyInviteDto, UpdateFamilyGroupDto } from '../dto/identity.dto';
|
||||
import { AddFamilyMemberDto, CreateFamilyGroupDto, RespondFamilyInviteDto, SendFamilyInviteDto, UpdateFamilyGroupDto } from '../dto/identity.dto';
|
||||
|
||||
@ApiTags('Семья')
|
||||
@ApiBearerAuth()
|
||||
@@ -152,23 +152,5 @@ export class FamilyController {
|
||||
return firstValueFrom(this.core.family.GetFamilyPresence({ requesterId, groupId }));
|
||||
}
|
||||
|
||||
@Post('groups/:groupId/botfather')
|
||||
@ApiOperation({ summary: 'Добавить BotFather в семью', description: 'Добавляет системного бота BotFather для создания Telegram-ботов через чат.' })
|
||||
async addBotFather(@Headers('authorization') authorization: string | undefined, @Param('groupId') groupId: string) {
|
||||
const requesterId = await this.auth(authorization);
|
||||
return firstValueFrom(this.core.family.AddBotFatherToFamily({ requesterId, groupId }));
|
||||
}
|
||||
|
||||
@Post('groups/:groupId/bots')
|
||||
@ApiOperation({ summary: 'Добавить бота в семью', description: 'Добавляет Telegram-бота (своего или другого пользователя) в семейную группу.' })
|
||||
@ApiBody({ type: AddBotToFamilyDto })
|
||||
async addBot(
|
||||
@Headers('authorization') authorization: string | undefined,
|
||||
@Param('groupId') groupId: string,
|
||||
@Body() dto: AddBotToFamilyDto
|
||||
) {
|
||||
const requesterId = await this.auth(authorization);
|
||||
return firstValueFrom(this.core.family.AddBotToFamily({ requesterId, groupId, botId: dto.botId }));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user