twitch-clone/backend/src/module/libs/telegram/telegram.service.ts
2025-07-29 05:30:29 +03:00

178 lines
5.5 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { Injectable, NotFoundException } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import {
Action,
Command, Ctx, Start, Update,
} from 'nestjs-telegraf';
import { Context, Telegraf } from 'telegraf';
import { PrismaService } from '@/src/core/prisma/prisma.service';
import { BUTTONS } from '@/src/module/libs/telegram/telegram.button';
import { MESSAGES } from '@/src/module/libs/telegram/telegram.message';
import { SessionInfo } from '@/src/shared/types/session-metadata.types';
import { SponsorshipPlan, TokenType, User } from '@prisma/generated';
import { ProcessEnv } from '../../../shared/types/env';
@Update()
@Injectable()
export class TelegramService extends Telegraf {
private readonly _token: string;
constructor(
private readonly prismaService: PrismaService,
private readonly configService: ConfigService<ProcessEnv>,
) {
super(configService.getOrThrow('TELEGRAM_BOT_TOKEN'));
this._token = configService.getOrThrow('TELEGRAM_BOT_TOKEN');
}
@Start()
public async onStart(@Ctx() ctx: Context) {
const chatId = ctx.chat?.id.toString();
// @ts-ignore
const token = ctx.message.text.split(' ')[1] as string;
if (token) {
const authToken = await this.prismaService.token.findUnique({
where: {
token,
type: TokenType.TELEGRAM_AUTH,
},
});
if (!authToken?.userId) {
await ctx.reply('Токен не найден');
return;
}
const hasExpired = new Date(authToken.expiresIn) < new Date();
if (hasExpired) {
await ctx.reply(MESSAGES.invalidToken);
return;
}
await this.connectTelegram(authToken.userId, chatId!);
await this.prismaService.token.delete({
where: { id: authToken.id },
});
await ctx.replyWithHTML(MESSAGES.authSuccess, BUTTONS.authSuccess);
return;
}
const user = await this.findUserByChatId(chatId!);
if (user) {
await this.onMe(ctx);
return;
}
await ctx.replyWithHTML(MESSAGES.welcome, BUTTONS.profile);
}
@Command('me')
@Action('me')
public async onMe(@Ctx() ctx: Context) {
const chatId = ctx.chat?.id.toString();
if (typeof chatId === 'undefined') {
throw new NotFoundException('Пользователь не найден');
}
const user = await this.findUserByChatId(chatId);
if (!user) {
throw new NotFoundException('Пользователь не найден');
}
const followersCount = await this.prismaService.follow.count({
where: { followingId: user.id },
});
await ctx.replyWithHTML(MESSAGES.profile(user, followersCount), BUTTONS.profile);
}
@Command('follows')
@Action('follows')
public async onFollow(@Ctx() ctx: Context) {
const chatId = ctx.chat?.id;
if (typeof chatId === 'undefined') {
throw new NotFoundException('Пользователь не найден');
}
const user = await this.findUserByChatId(chatId.toString());
if (!user) {
throw new NotFoundException('Пользователь не найден');
}
const follows = await this.prismaService.follow.findMany({
where: { followerId: user.id },
include: { following: true },
});
if (follows.length > 0) {
const followList = follows.map((follow) => MESSAGES.follows(follow.following)).join('\n');
const message = `<b>Каналы, на которые Вы подписаны</b>\n\n${followList}`;
await ctx.replyWithHTML(message);
return;
}
await ctx.replyWithHTML('❌ <b>У Вас нет подписок</b>');
}
public async sendPasswordResetToken(chatId: string, token: string, metadata: SessionInfo) {
await this.telegram.sendMessage(chatId, MESSAGES.resetPassword(token, metadata), { parse_mode: 'HTML' });
}
public async sendDeactivateToken(chatId: string, token: string, metadata: SessionInfo) {
await this.telegram.sendMessage(chatId, MESSAGES.deactivate(token, metadata), { parse_mode: 'HTML' });
}
public async sendAccountDeletionToken(chatId: string) {
await this.telegram.sendMessage(chatId, MESSAGES.accountDeleted, { parse_mode: 'HTML' });
}
public async sendStreamStart(chatId: string, channel: User) {
await this.telegram.sendMessage(chatId, MESSAGES.streamStart(channel), { parse_mode: 'HTML' });
}
public async sendNewSponsorship(chatId: string, plan: SponsorshipPlan, sponsor: User) {
await this.telegram.sendMessage(chatId, MESSAGES.newSponsorship(plan, sponsor), { parse_mode: 'HTML' });
}
public async sendNewFollowing(chatId: string, follower: User) {
const user = await this.findUserByChatId(chatId);
if (!user) {
throw new NotFoundException('Пользователь не найден');
}
await this.telegram.sendMessage(chatId, MESSAGES.newFollowing(follower, user.followings.length), { parse_mode: 'HTML' });
}
public async sendEnableTwoFactor(chatId: string) {
return this.telegram.sendMessage(chatId, MESSAGES.enableTwoFactor, { parse_mode: 'HTML' });
}
public async sendVerifyChannel(chatId: string) {
return this.telegram.sendMessage(chatId, MESSAGES.verifyChannel, { parse_mode: 'HTML' });
}
private async connectTelegram(userId: string, chatId: string) {
return this.prismaService.user.update({
where: { id: userId },
data: { telegramId: chatId },
});
}
private async findUserByChatId(chatId: string) {
return this.prismaService.user.findUnique({
where: { telegramId: chatId },
include: {
followings: true,
followers: true,
},
});
}
}