Добавлена локализация, настройка частоты цитат и админ-панель
This commit is contained in:
@@ -1,9 +1,3 @@
|
|||||||
// This is your Prisma schema file,
|
|
||||||
// learn more about it in the docs: https://pris.ly/d/prisma-schema
|
|
||||||
|
|
||||||
// Looking for ways to speed up your queries, or scale easily with your serverless or edge functions?
|
|
||||||
// Try Prisma Accelerate: https://pris.ly/cli/accelerate-init
|
|
||||||
|
|
||||||
generator client {
|
generator client {
|
||||||
provider = "prisma-client"
|
provider = "prisma-client"
|
||||||
output = "../generated/prisma"
|
output = "../generated/prisma"
|
||||||
@@ -15,11 +9,13 @@ datasource db {
|
|||||||
}
|
}
|
||||||
|
|
||||||
model User {
|
model User {
|
||||||
id Int @id @default(autoincrement()) // Âíóòðåííèé ID áàçû
|
id Int @id @default(autoincrement()) // Внутренний ID базы
|
||||||
telegramId BigInt @unique // ID èç Òåëåãðàìà (BigInt îáÿçàòåëåí)
|
telegramId BigInt @unique // ID из Телеграма (BigInt обязателен)
|
||||||
username String? // Íèêíåéì (ìîæåò íå áûòü)
|
username String? // Никнейм (может не быть)
|
||||||
fio String? // ÔÈÎ (Èìÿ + Ôàìèëèÿ)
|
fio String? // ФИО (Имя + Фамилия)
|
||||||
createdAt DateTime @default(now()) // Äàòà ïåðâîãî îáðàùåíèÿ ê áîòó
|
createdAt DateTime @default(now()) // Дата первого обращения к боту
|
||||||
|
frequency Int @default(1) // Частота отправки цитат в часах (1, 3, 5, 7, 9, 12)
|
||||||
|
lastQuoteSentAt DateTime @default(now()) // Время последней отправки цитаты
|
||||||
|
|
||||||
@@map("users") // Íåîáÿçàòåëüíî: äåëàåò èìÿ òàáëèöû â ÁÄ "users" (ìíîæåñòâåííîå ÷èñëî)
|
@@map("users") // Необязательно: делает имя таблицы в БД "users" (множественное число)
|
||||||
}
|
}
|
||||||
27
src/admin/admin.controller.ts
Normal file
27
src/admin/admin.controller.ts
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
import { Controller, Post, Body, UnauthorizedException } from '@nestjs/common';
|
||||||
|
import { BotService } from '../bot/bot.service';
|
||||||
|
|
||||||
|
@Controller('admin')
|
||||||
|
export class AdminController {
|
||||||
|
constructor(private readonly botService: BotService) { }
|
||||||
|
|
||||||
|
@Post('send-message')
|
||||||
|
async sendMessage(@Body() body: any) {
|
||||||
|
// body: { userId: number, password: string, message: string }
|
||||||
|
const { userId, password, message } = body;
|
||||||
|
|
||||||
|
const adminPassword = process.env.ADMIN_PASSWORD;
|
||||||
|
if (!adminPassword || password !== adminPassword) {
|
||||||
|
throw new UnauthorizedException('Invalid or missing password');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!userId || !message) {
|
||||||
|
// Maybe Throw BadRequestException?
|
||||||
|
// For now just return failure or throw
|
||||||
|
throw new UnauthorizedException('Missing userId or message');
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.botService.sendAdminMessage(Number(userId), message);
|
||||||
|
return { success: true };
|
||||||
|
}
|
||||||
|
}
|
||||||
9
src/admin/admin.module.ts
Normal file
9
src/admin/admin.module.ts
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
import { Module } from '@nestjs/common';
|
||||||
|
import { AdminController } from './admin.controller';
|
||||||
|
import { BotModule } from '../bot/bot.module';
|
||||||
|
|
||||||
|
@Module({
|
||||||
|
imports: [BotModule],
|
||||||
|
controllers: [AdminController],
|
||||||
|
})
|
||||||
|
export class AdminModule { }
|
||||||
@@ -5,6 +5,7 @@ import { UsersModule } from './users/users.module';
|
|||||||
import { QuotesModule } from './quotes/quotes.module';
|
import { QuotesModule } from './quotes/quotes.module';
|
||||||
import { BotModule } from './bot/bot.module';
|
import { BotModule } from './bot/bot.module';
|
||||||
import { SchedulerModule } from './scheduler/scheduler.module';
|
import { SchedulerModule } from './scheduler/scheduler.module';
|
||||||
|
import { AdminModule } from './admin/admin.module';
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [
|
imports: [
|
||||||
@@ -16,6 +17,7 @@ import { SchedulerModule } from './scheduler/scheduler.module';
|
|||||||
QuotesModule,
|
QuotesModule,
|
||||||
BotModule,
|
BotModule,
|
||||||
SchedulerModule,
|
SchedulerModule,
|
||||||
|
AdminModule,
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
export class AppModule { }
|
export class AppModule { }
|
||||||
|
|||||||
@@ -5,5 +5,6 @@ import { UsersModule } from '../users/users.module';
|
|||||||
@Module({
|
@Module({
|
||||||
imports: [UsersModule],
|
imports: [UsersModule],
|
||||||
providers: [BotService],
|
providers: [BotService],
|
||||||
|
exports: [BotService],
|
||||||
})
|
})
|
||||||
export class BotModule { }
|
export class BotModule { }
|
||||||
|
|||||||
@@ -1,10 +1,13 @@
|
|||||||
import { Update, Ctx, Start, Help, On, Message } from 'nestjs-telegraf';
|
import { Update, Ctx, Start, Help, On, Message, Command, Action, InjectBot } from 'nestjs-telegraf';
|
||||||
import { Context } from 'telegraf';
|
import { Context, Telegraf, Markup } from 'telegraf';
|
||||||
import { UsersService } from '../users/users.service';
|
import { UsersService } from '../users/users.service';
|
||||||
|
|
||||||
@Update()
|
@Update()
|
||||||
export class BotService {
|
export class BotService {
|
||||||
constructor(private readonly usersService: UsersService) { }
|
constructor(
|
||||||
|
private readonly usersService: UsersService,
|
||||||
|
@InjectBot() private readonly bot: Telegraf<Context>
|
||||||
|
) { }
|
||||||
|
|
||||||
@Start()
|
@Start()
|
||||||
async start(@Ctx() ctx: Context) {
|
async start(@Ctx() ctx: Context) {
|
||||||
@@ -13,14 +16,38 @@ export class BotService {
|
|||||||
this.usersService.create({
|
this.usersService.create({
|
||||||
id: user.id,
|
id: user.id,
|
||||||
fullName: `${user.first_name} ${user.last_name || ''}`.trim(),
|
fullName: `${user.first_name} ${user.last_name || ''}`.trim(),
|
||||||
createdAt: new Date().toISOString(),
|
|
||||||
});
|
});
|
||||||
await ctx.reply(`Welcome, ${user.first_name}! You have been registered for motivational quotes.`);
|
await ctx.reply('Приветствую тебя, мой дорогой друг. Я бот, который будет писать тебе мотивирующие цитаты. Сейчас цитаты буду приходит один раз в час, в настройках можно изменить это время.');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Help()
|
@Help()
|
||||||
async help(@Ctx() ctx: Context) {
|
async help(@Ctx() ctx: Context) {
|
||||||
await ctx.reply('I will send you a motivational quote every 3 hours.');
|
await ctx.reply('Я буду присылать тебе мотивирующие цитаты. Используй /settings чтобы изменить частоту.');
|
||||||
|
}
|
||||||
|
|
||||||
|
@Command('settings')
|
||||||
|
async settings(@Ctx() ctx: Context) {
|
||||||
|
await ctx.reply('Выберите частоту получения цитат:', Markup.inlineKeyboard([
|
||||||
|
[Markup.button.callback('1 час', 'frequency_1'), Markup.button.callback('3 часа', 'frequency_3')],
|
||||||
|
[Markup.button.callback('5 часов', 'frequency_5'), Markup.button.callback('7 часов', 'frequency_7')],
|
||||||
|
[Markup.button.callback('9 часов', 'frequency_9'), Markup.button.callback('12 часов', 'frequency_12')],
|
||||||
|
]));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Action(/^frequency_(\d+)$/)
|
||||||
|
async onFrequencySelect(@Ctx() ctx: Context & { match: RegExpExecArray }) {
|
||||||
|
const user = ctx.from;
|
||||||
|
if (!user) return;
|
||||||
|
|
||||||
|
const hours = parseInt(ctx.match[1]);
|
||||||
|
this.usersService.update(user.id, { frequency: hours });
|
||||||
|
|
||||||
|
await ctx.answerCbQuery();
|
||||||
|
await ctx.editMessageText(`Отлично! Теперь я буду присылать цитаты каждые ${hours} ч.`);
|
||||||
|
}
|
||||||
|
|
||||||
|
async sendAdminMessage(userId: number, message: string) {
|
||||||
|
await this.bot.telegram.sendMessage(userId, message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
610
src/quotes.json
610
src/quotes.json
@@ -1,612 +1,33 @@
|
|||||||
[
|
[
|
||||||
{
|
{
|
||||||
"text": "Единственный способ делать великие дела — любить то, что вы делаете.",
|
"text": "Сила не в том, чтобы не падать, а в том, чтобы подниматься."
|
||||||
"author": "Стив Джобс"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"text": "Верь, что ты можешь, и ты уже прошел половину пути.",
|
|
||||||
"author": "Теодор Рузвельт"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"text": "Не важно, как медленно ты идешь, главное — не останавливаться.",
|
|
||||||
"author": "Конфуций"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"text": "Твое время ограничено, не трать его, живя чужой жизнью.",
|
|
||||||
"author": "Стив Джобс"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"text": "Лучший способ предсказать будущее — создать его.",
|
|
||||||
"author": "Питер Друкер"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"text": "Успех — это не финал, неудача — это не фатально: главное — это мужество продолжать.",
|
|
||||||
"author": "Уинстон Черчилль"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"text": "Трудности часто готовят обычных людей к необычной судьбе.",
|
|
||||||
"author": "К.С. Льюис"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"text": "Верь в себя и во все, что ты есть. Знайте, что внутри вас есть что-то большее, чем любое препятствие.",
|
|
||||||
"author": "Кристиан Д. Ларсон"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"text": "Не смотри на часы; делай то, что делают они. Продолжай идти.",
|
|
||||||
"author": "Сэм Левенсон"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"text": "Никогда не поздно поставить новую цель или обрести новую мечту.",
|
|
||||||
"author": "К.С. Льюис"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"text": "Ты сильнее, чем ты думаешь."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"text": "Все будет хорошо. Просто дыши."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"text": "Ты справляешься лучше, чем тебе кажется."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"text": "Не забывай заботиться о себе."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"text": "Твои чувства важны."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"text": "Это всего лишь плохой день, а не плохая жизнь."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"text": "Ты заслуживаешь счастья и покоя."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"text": "Маленькие шаги тоже ведут к цели."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"text": "Позволь себе отдохнуть."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"text": "Ты не одинок в своих переживаниях."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"text": "Счастье можно найти даже в самые темные времена, если не забывать обращаться к свету.",
|
|
||||||
"author": "Альбус Дамблдор"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"text": "Неудача — это просто возможность начать снова, но уже более мудро.",
|
|
||||||
"author": "Генри Форд"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"text": "Наша величайшая слава не в том, чтобы никогда не падать, а в том, чтобы подниматься каждый раз, когда мы падаем.",
|
|
||||||
"author": "Конфуций"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"text": "Ты — автор своей жизни. Пиши так, как хочешь ты."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"text": "Даже самая темная ночь закончится и взойдет солнце.",
|
|
||||||
"author": "Виктор Гюго"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"text": "Сделай сегодня что-то, за что твое будущее «я» скажет тебе спасибо."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"text": "Ошибки — это доказательство того, что ты пытаешься."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"text": "Будь собой, все остальные роли уже заняты.",
|
|
||||||
"author": "Оскар Уайльд"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"text": "Секрет того, чтобы добиться чего-то — начать.",
|
|
||||||
"author": "Марк Твен"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"text": "Ты важен. Твоя жизнь имеет значение."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"text": "Не позволяй вчерашнему дню занимать слишком много сегодняшнего.",
|
|
||||||
"author": "Уилл Роджерс"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"text": "Жизнь — это то, что с тобой происходит, пока ты строишь другие планы.",
|
|
||||||
"author": "Джон Леннон"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"text": "Через год ты пожалеешь, что не начал сегодня.",
|
|
||||||
"author": "Карен Лэмб"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"text": "Не бойся отказаться от хорошего, чтобы пойти за великим.",
|
|
||||||
"author": "Джон Д. Рокфеллер"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"text": "Я не потерпел неудачу. Я просто нашел 10 000 способов, которые не работают.",
|
|
||||||
"author": "Томас Эдисон"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"text": "Если ты проходишь через ад, продолжай идти.",
|
|
||||||
"author": "Уинстон Черчилль"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"text": "Никто не может заставить вас почувствовать себя неполноценным без вашего согласия.",
|
|
||||||
"author": "Элеонора Рузвельт"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"text": "Лучшая месть — это огромный успех.",
|
|
||||||
"author": "Фрэнк Синатра"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"text": "Ты можешь все, если поверишь в это."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"text": "Помни, зачем ты начал."
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"text": "Каждый день — это новый шанс."
|
"text": "Каждый день — это новый шанс."
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"text": "Будь добр к себе."
|
"text": "Ты сильнее, чем ты думаешь."
|
||||||
},
|
|
||||||
{
|
|
||||||
"text": "Твой потенциал безграничен."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"text": "Сфокусируйся на прогрессе, а не на совершенстве."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"text": "Ты делаешь мир лучше просто тем, что ты в нем есть."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"text": "Не сравнивай свое начало с чьей-то серединой."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"text": "Вдох-выдох. Ты справишься."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"text": "Иногда нужно просто остановиться и посмотреть, как далеко ты уже зашел."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"text": "Твоя улыбка может изменить чей-то день."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"text": "Не бойся просить о помощи. Это признак силы, а не слабости."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"text": "Жизнь на 10% состоит из того, что с нами происходит, и на 90% из того, как мы на это реагируем.",
|
|
||||||
"author": "Чарльз Свиндолл"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"text": "Ограничения живут только в нашей голове. Но если мы используем наше воображение, наши возможности становятся безграничными.",
|
|
||||||
"author": "Джейми Паолинетти"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"text": "Логика приведет вас из пункта А в пункт Б. Воображение приведет вас куда угодно.",
|
|
||||||
"author": "Альберт Эйнштейн"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"text": "Не ждите. Время никогда не будет «самым подходящим».",
|
|
||||||
"author": "Наполеон Хилл"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"text": "Все, что вы можете представить — реально.",
|
|
||||||
"author": "Пабло Пикассо"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"text": "Стремитесь не к успеху, а к ценностям, которые он дает.",
|
|
||||||
"author": "Альберт Эйнштейн"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"text": "Два самых важных дня в твоей жизни: день, когда ты появился на свет, и день, когда понял зачем.",
|
|
||||||
"author": "Марк Твен"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"text": "Начинай там, где ты есть. Используй то, что у тебя есть. Делай то, что можешь.",
|
|
||||||
"author": "Артур Эш"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"text": "Падать — часть жизни, подниматься — ее суть."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"text": "Ты — это не твои ошибки."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"text": "Сегодня — отличный день, чтобы начать."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"text": "Твоя скорость не имеет значения, вперед — есть вперед."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"text": "Поверь в магию новых начал."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"text": "Ты достоин любви и уважения."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"text": "Слушай свое сердце."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"text": "Ты — уникален, и это твоя суперсила."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"text": "Не позволяй страху решать за тебя."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"text": "Каждая проблема — это замаскированная возможность.",
|
|
||||||
"author": "Джон Адамс"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"text": "Счастье — это не что-то готовое. Оно происходит от ваших собственных действий.",
|
|
||||||
"author": "Далай Лама"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"text": "Чтобы дойти до цели, надо прежде всего идти.",
|
|
||||||
"author": "Оноре де Бальзак"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"text": "Великие дела нужно совершать, а не обдумывать их бесконечно.",
|
|
||||||
"author": "Юлий Цезарь"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"text": "Победа над самим собой — единственная торжественная победа.",
|
|
||||||
"author": "Платон"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"text": "Кто хочет — ищет возможности, кто не хочет — ищет причины.",
|
|
||||||
"author": "Сократ"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"text": "Жизнь прекрасна, когда ты создаешь ее сам.",
|
|
||||||
"author": "Софи Марсо"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"text": "Не бойтесь расти медленно, бойтесь оставаться неизменными.",
|
|
||||||
"author": "Китайская пословица"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"text": "Лучшее время, чтобы посадить дерево, было 20 лет назад. Следующее лучшее время — сегодня.",
|
|
||||||
"author": "Китайская пословица"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"text": "Ты можешь изменить свой мир, изменив свои мысли."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"text": "Твоя энергия — это твоя валюта. Трать ее мудро."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"text": "Будь причиной, по которой кто-то сегодня улыбнется."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"text": "Не забывай хвалить себя за маленькие победы."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"text": "Ты — достаточно хорош."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"text": "Мир нуждается в том, что можешь дать только ты."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"text": "Свет есть даже в самой темной комнате, если зажечь свечу."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"text": "Твоя жизнь — это твое искусство."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"text": "Не позволяй никому тушить твой свет."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"text": "Ты сильнее любой бури."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"text": "Вселенная любит тебя."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"text": "Доверяй процессу."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"text": "Ты находишься именно там, где должен быть."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"text": "Отпусти то, что ты не можешь контролировать."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"text": "Каждый закат — это доказательство того, что финалы тоже могут быть красивыми."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"text": "Ты — чудо."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"text": "Люби себя в первую очередь."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"text": "Ты заслуживаешь всего самого лучшего."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"text": "Не сдавайся. Чудеса случаются каждый день."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"text": "Твоя история важна."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"text": "Будь смелым."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"text": "Мечтай по-крупному."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"text": "Ты — вдохновение."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"text": "Делай то, что можешь, с тем, что имеешь, там, где ты есть.",
|
|
||||||
"author": "Теодор Рузвельт"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"text": "Гений — это 1% таланта и 99% труда.",
|
|
||||||
"author": "Томас Эдисон"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"text": "Неудача — это приправа, которая придает успеху его вкус.",
|
|
||||||
"author": "Трумен Капоте"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"text": "Если вы думаете, что вы способны на что-то, вы правы; если вы думаете, что у вас ничего не получится, вы тоже правы.",
|
|
||||||
"author": "Генри Форд"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"text": "Лучший способ взяться за что-то — перестать говорить и начать делать.",
|
|
||||||
"author": "Уолт Дисней"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"text": "Пессимист видит трудность в каждой возможности; оптимист видит возможность в каждой трудности.",
|
|
||||||
"author": "Уинстон Черчилль"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"text": "Не позволяйте шуму чужих мнений заглушить ваш внутренний голос.",
|
|
||||||
"author": "Стив Джобс"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"text": "То, что нас не убивает, делает нас сильнее.",
|
|
||||||
"author": "Фридрих Ницше"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"text": "Будьте переменой, которую вы хотите видеть в мире.",
|
|
||||||
"author": "Махатма Ганди"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"text": "Счастье не зависит от того, кто вы есть или что у вас есть; оно зависит исключительно от того, что вы думаете.",
|
|
||||||
"author": "Дейл Карнеги"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"text": "Единственный человек, которым вы должны стараться быть, — это тот, кем вы были вчера.",
|
|
||||||
"author": "Зигмунд Фрейд"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"text": "Жизнь — это 10% того, что с нами происходит, и 90% того, как мы на это реагируем.",
|
|
||||||
"author": "Чарльз Свиндолл"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"text": "Ваше время ограничено, не тратьте его, живя чужой жизнью.",
|
|
||||||
"author": "Стив Джобс"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"text": "Сложнее всего начать действовать, все остальное зависит только от упорства.",
|
|
||||||
"author": "Амелия Эрхарт"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"text": "Жизнь — это либо дерзкое приключение, либо ничего.",
|
|
||||||
"author": "Хелен Келлер"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"text": "Вы никогда не пересечете океан, если не наберетесь смелости потерять из виду берег.",
|
|
||||||
"author": "Христофор Колумб"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"text": "Два самых важных дня в твоей жизни: день, когда ты родился, и день, когда понял, зачем.",
|
|
||||||
"author": "Марк Твен"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"text": "Человек, который никогда не совершал ошибок, никогда не пробовал ничего нового.",
|
|
||||||
"author": "Альберт Эйнштейн"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"text": "Образование — это самое мощное оружие, которое вы можете использовать, чтобы изменить мир.",
|
|
||||||
"author": "Нельсон Мандела"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"text": "Секрет успеха — это знать то, что никто другой не знает.",
|
|
||||||
"author": "Аристотель Онассис"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"text": "Не бойтесь совершенства. Вам его никогда не достичь.",
|
|
||||||
"author": "Сальвадор Дали"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"text": "Успех — это способность идти от неудачи к неудаче, не теряя энтузиазма.",
|
|
||||||
"author": "Уинстон Черчилль"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"text": "Мечты не работают, пока не работаешь ты."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"text": "Ты способен на большее, чем можешь себе представить."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"text": "Каждый шаг вперед приближает тебя к цели."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"text": "Не останавливайся, пока не будешь гордиться собой."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"text": "Твоя настойчивость окупится."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"text": "Верь в свою интуицию."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"text": "Ты — кузнец своего счастья."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"text": "Не бойся быть новичком."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"text": "Все великое начинается с малого."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"text": "Твоя жизнь — это твой холст."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"text": "Рискуй, или упустишь шанс."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"text": "Слушай себя."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"text": "Ты — свет."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"text": "Не позволяй никому говорить, что ты не можешь."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"text": "Твоя сила внутри тебя."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"text": "Будь благодарен за каждый день."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"text": "Ищи радость в мелочах."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"text": "Ты — подарок этому миру."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"text": "Не бойся перемен."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"text": "Все происходит вовремя."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"text": "Ты — хозяин своей судьбы."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"text": "Не оглядывайся назад, ты идешь не туда."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"text": "Твоя улыбка — твое оружие."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"text": "Будь добрым, это всегда модно."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"text": "Ты — вдохновение для других."
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"text": "Не бойся мечтать."
|
"text": "Не бойся мечтать."
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"text": "Твои мечты важны."
|
"text": "Маленькие шаги ведут к большим целям."
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"text": "Ты — уникален."
|
"text": "Все в твоих руках."
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"text": "Люби жизнь."
|
"text": "Верь в процесс."
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"text": "Живи моментом."
|
"text": "Ты уникален."
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"text": "Ты — достаточно сильный."
|
"text": "Никогда не сдавайся."
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"text": "Не сдавайся перед трудностями."
|
"text": "Будь лучшей версией себя."
|
||||||
},
|
|
||||||
{
|
|
||||||
"text": "Ты — победитель."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"text": "Верь в чудеса."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"text": "Ты — любовь."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"text": "Свети ярко."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"text": "Ты — надежда."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"text": "Будь собой."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"text": "Ты — радость."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"text": "Не бойся быть счастливым."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"text": "Ты — мир."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"text": "Все будет хорошо."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"text": "Ты — гармония."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"text": "Люби себя."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"text": "Ты — красота."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"text": "Не бойся жить."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"text": "Ты — свобода."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"text": "Ты — мудрость."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"text": "Ты — сила."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"text": "Ты — добро."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"text": "Ты — свет."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"text": "Ты — любовь."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"text": "Ты — жизнь."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"text": "Ты — вселенная."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"text": "Ты — бесконечность."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"text": "Ты — вечность."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"text": "Ты — сейчас."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"text": "Ты — здесь."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"text": "Ты — есть."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"text": "Просто будь."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"text": "Дыши."
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"text": "Живи."
|
"text": "Живи."
|
||||||
@@ -673,5 +94,20 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"text": "Будь."
|
"text": "Будь."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"text": "Дорогу осилит идущий."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"text": "Секрет успеха — просто начать."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"text": "Ошибки — это доказательство того, что ты пытаешься."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"text": "Самое темное время — перед рассветом."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"text": "Действуй сегодня."
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
import { Injectable, Logger } from '@nestjs/common';
|
import { Injectable, Logger } from '@nestjs/common';
|
||||||
import { Cron, CronExpression } from '@nestjs/schedule';
|
import { Cron } from '@nestjs/schedule';
|
||||||
import { InjectBot } from 'nestjs-telegraf';
|
import { InjectBot } from 'nestjs-telegraf';
|
||||||
import { Context, Telegraf } from 'telegraf';
|
import { Context, Telegraf } from 'telegraf';
|
||||||
import { UsersService } from '../users/users.service';
|
import { UsersService, User } from '../users/users.service';
|
||||||
import { QuotesService } from '../quotes/quotes.service';
|
import { QuotesService } from '../quotes/quotes.service';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
@@ -15,39 +15,56 @@ export class SchedulerService {
|
|||||||
@InjectBot() private readonly bot: Telegraf<Context>,
|
@InjectBot() private readonly bot: Telegraf<Context>,
|
||||||
) { }
|
) { }
|
||||||
|
|
||||||
// Run every 3 hours
|
// Run every hour
|
||||||
@Cron('0 */3 * * *')
|
@Cron('0 * * * *')
|
||||||
handleCron() {
|
async handleCron() {
|
||||||
this.logger.debug('Cron job triggered. Scheduling random quote...');
|
this.logger.debug('Hourly cron triggered. Checking users...');
|
||||||
|
const users = this.usersService.findAll();
|
||||||
|
const now = new Date();
|
||||||
|
|
||||||
// Random delay between 0 and 3 hours (in milliseconds)
|
for (const user of users) {
|
||||||
// 3 hours = 3 * 60 * 60 * 1000 = 10800000 ms
|
await this.checkAndSendQuote(user, now);
|
||||||
// We'll use slightly less than 3 hours to avoid overlap, e.g., 2.5 hours
|
}
|
||||||
const maxDelay = 2.5 * 60 * 60 * 1000;
|
|
||||||
const delay = Math.floor(Math.random() * maxDelay);
|
|
||||||
|
|
||||||
this.logger.debug(`Quote scheduled in ${delay / 1000 / 60} minutes.`);
|
|
||||||
|
|
||||||
setTimeout(() => {
|
|
||||||
this.sendQuoteToAllUsers();
|
|
||||||
}, delay);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async sendQuoteToAllUsers() {
|
private async checkAndSendQuote(user: User, now: Date) {
|
||||||
this.logger.debug('Sending quotes to all users...');
|
const lastSent = user.lastQuoteSentAt ? new Date(user.lastQuoteSentAt) : new Date(0);
|
||||||
const users = this.usersService.findAll();
|
|
||||||
|
const diffMs = now.getTime() - lastSent.getTime();
|
||||||
|
const diffHours = diffMs / (1000 * 60 * 60);
|
||||||
|
|
||||||
|
// Allow a small buffer for timing execution, e.g. if diff is 0.99 hours but frequency is 1?
|
||||||
|
// Cron is exact hour. If last sent was 10:00:05 and now is 11:00:00. diff is slightly less than 1.
|
||||||
|
// But we update `lastQuoteSentAt` to `now` (execution time).
|
||||||
|
// If we update to `now`, then next time it will be exact hour difference.
|
||||||
|
// Let's use strict >= logic but maybe `frequency - 0.1`?
|
||||||
|
// Actually, if we update `lastQuoteSentAt` to `now.toISOString()` which is the cron time approx.
|
||||||
|
// It should be fine. If we missed by a second, it waits another hour.
|
||||||
|
// User won't mind waiting 1 more hour initially or we can be generous.
|
||||||
|
// Let's stick to `>= user.frequency`.
|
||||||
|
// Initial user `lastQuoteSentAt` is registration time.
|
||||||
|
// If reg at 10:30. Cron at 11:00. Diff 0.5. No quote.
|
||||||
|
// Cron at 12:00. Diff 1.5. Quote! (if freq 1).
|
||||||
|
// Seems correct usage.
|
||||||
|
|
||||||
|
if (diffHours >= user.frequency) {
|
||||||
|
await this.sendQuote(user);
|
||||||
|
// Updating user lastQuoteSentAt to now
|
||||||
|
this.usersService.update(user.id, { lastQuoteSentAt: now.toISOString() });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async sendQuote(user: User) {
|
||||||
|
try {
|
||||||
const quote = this.quotesService.getRandomQuote();
|
const quote = this.quotesService.getRandomQuote();
|
||||||
let message = `"${quote.text}"`;
|
let message = `"${quote.text}"`;
|
||||||
if (quote.author) {
|
if (quote.author) {
|
||||||
message += `\n\n- ${quote.author}`;
|
message += `\n\n- ${quote.author}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const user of users) {
|
|
||||||
try {
|
|
||||||
await this.bot.telegram.sendMessage(user.id, message);
|
await this.bot.telegram.sendMessage(user.id, message);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.logger.error(`Failed to send quote to user ${user.id}: ${error.message}`);
|
this.logger.error(`Failed to send quote to user ${user.id}: ${error.message}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ export interface User {
|
|||||||
id: number;
|
id: number;
|
||||||
fullName: string;
|
fullName: string;
|
||||||
createdAt: string;
|
createdAt: string;
|
||||||
|
frequency: number; // in hours, default 1
|
||||||
|
lastQuoteSentAt: string; // ISO string
|
||||||
}
|
}
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
@@ -17,10 +19,19 @@ export class UsersService {
|
|||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
const data = fs.readFileSync(this.filePath, 'utf8');
|
const data = fs.readFileSync(this.filePath, 'utf8');
|
||||||
|
try {
|
||||||
return JSON.parse(data);
|
return JSON.parse(data);
|
||||||
|
} catch (e) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private writeUsers(users: User[]): void {
|
private writeUsers(users: User[]): void {
|
||||||
|
// Ensure directory exists
|
||||||
|
const dir = path.dirname(this.filePath);
|
||||||
|
if (!fs.existsSync(dir)) {
|
||||||
|
fs.mkdirSync(dir, { recursive: true });
|
||||||
|
}
|
||||||
fs.writeFileSync(this.filePath, JSON.stringify(users, null, 2), 'utf8');
|
fs.writeFileSync(this.filePath, JSON.stringify(users, null, 2), 'utf8');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -28,11 +39,34 @@ export class UsersService {
|
|||||||
return this.readUsers();
|
return this.readUsers();
|
||||||
}
|
}
|
||||||
|
|
||||||
create(user: User): void {
|
findOne(id: number): User | undefined {
|
||||||
|
return this.readUsers().find((u) => u.id === id);
|
||||||
|
}
|
||||||
|
|
||||||
|
create(user: Partial<User> & { id: number; fullName: string }): void {
|
||||||
const users = this.readUsers();
|
const users = this.readUsers();
|
||||||
const existingUser = users.find((u) => u.id === user.id);
|
const existingUser = users.find((u) => u.id === user.id);
|
||||||
|
const newUser: User = {
|
||||||
|
...user,
|
||||||
|
createdAt: user.createdAt || new Date().toISOString(),
|
||||||
|
frequency: user.frequency || 1,
|
||||||
|
lastQuoteSentAt: user.lastQuoteSentAt || new Date().toISOString(),
|
||||||
|
};
|
||||||
|
|
||||||
if (!existingUser) {
|
if (!existingUser) {
|
||||||
users.push(user);
|
users.push(newUser);
|
||||||
|
this.writeUsers(users);
|
||||||
|
} else {
|
||||||
|
// Option: update existing user if re-registering?
|
||||||
|
// For now, assume id persistence.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
update(id: number, updateData: Partial<User>): void {
|
||||||
|
const users = this.readUsers();
|
||||||
|
const index = users.findIndex(u => u.id === id);
|
||||||
|
if (index !== -1) {
|
||||||
|
users[index] = { ...users[index], ...updateData };
|
||||||
this.writeUsers(users);
|
this.writeUsers(users);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user