diff --git a/telegram_bot_project/bot/buttons.py b/telegram_bot_project/bot/buttons.py index f307a2b..8f467a9 100644 --- a/telegram_bot_project/bot/buttons.py +++ b/telegram_bot_project/bot/buttons.py @@ -202,4 +202,29 @@ def work_buttons_keyboard(task_id: int) -> InlineKeyboardMarkup: cancel_work_btn: InlineKeyboardButton = InlineKeyboardButton(text=CANCEL_WORK_BTN, callback_data=f"cancel_task:{task_id}") inline_markup.inline_keyboard.append([start_work_btn, cancel_work_btn]) - return inline_markup \ No newline at end of file + return inline_markup + +def get_work_session_keyboard() -> None: + task_finished = KeyboardButton(text=STOP_WORK_SESSION) + task_break = KeyboardButton(text=CANCEL_WORK_BTN) + + keyboard = [ + [task_break, task_finished] + ] + + return ReplyKeyboardMarkup( + keyboard=keyboard, + resize_keyboard=True + ) + +def get_start_day_btn() -> ReplyKeyboardMarkup: + start_day_btn = KeyboardButton(text=START_DAY_BTN) + + keyboard = [ + [start_day_btn] + ] + + return ReplyKeyboardMarkup( + keyboard=keyboard, + resize_keyboard=True + ) \ No newline at end of file diff --git a/telegram_bot_project/bot/callbacks.py b/telegram_bot_project/bot/callbacks.py index c88719c..eb38566 100644 --- a/telegram_bot_project/bot/callbacks.py +++ b/telegram_bot_project/bot/callbacks.py @@ -1,5 +1,6 @@ # bot/callbacks.py from typing import Optional +from datetime import datetime from aiogram import types from aiogram.fsm.context import FSMContext @@ -12,6 +13,7 @@ from service.user import UserService from states import DialogStates from service.focus import FocusService +from states import user_task_start_time async def start_callback_language(callback_query: types.CallbackQuery) -> None: await callback_query.answer() @@ -208,12 +210,18 @@ async def callback_task_menu(callback_query: types.CallbackQuery) -> None: action, task_id_str = data.split(":") task_id = int(task_id_str) + print(f"[DEBUG] - Task id: {task_id}") + task = await TaskService.get_task_by_id(task_id) + task_name = task['task_name'] + match action: case "complete_task": await TaskService.update_started_status(task_id) - await callback_query.message.answer(MESSAGES[language]['REMIND_WORK_CANCEL'], - reply_markup=menu_reply_keyboard()) + user_task_start_time[user_id] = (task_id, datetime.now()) + await callback_query.message.answer(MESSAGES[language]['REMIND_WORK_START'].format(task_name), + reply_markup=get_work_session_keyboard()) case "cancel_task": await TaskService.update_started_status(task_id) + user_task_start_time[user_id] = (task_id, datetime.now()) await callback_query.message.answer(MESSAGES[language]['REMIND_WORK_CANCEL'], reply_markup=menu_reply_keyboard()) \ No newline at end of file diff --git a/telegram_bot_project/bot/commands.py b/telegram_bot_project/bot/commands.py index 729241e..4409ffd 100644 --- a/telegram_bot_project/bot/commands.py +++ b/telegram_bot_project/bot/commands.py @@ -1,6 +1,7 @@ # bot/commands.py from aiogram import types from aiogram.fsm.context import FSMContext +from datetime import datetime from service.focus import FocusService from bot.utills import format_date, calculate_awake_hours @@ -385,7 +386,7 @@ async def show_evening_routines(message: types.Message): print(f"[INFO] - User with id: {user_id} - opened /evening_routines.") evening_routine = await RoutineService.get_user_routines(user_id, routine_type="evening") if not evening_routine: - await message.answer(MESSAGES[language]['NO_MORNING_ROUTINE']) + await message.answer(MESSAGES[language]['NO_EVENING_ROUTINE']) return dividers: str = "\n" + ("-" * int(len(MESSAGES[language]['EVENING_ROUTINE_SHOW']) * 1.65)) @@ -499,3 +500,29 @@ async def delete_focus_session(message: types.Message, state: FSMContext) -> Non await state.update_data(focuses=focuses) await state.set_state(DialogStates.delete_focus) await message.answer(MESSAGES[language]['DELETE_FOCUS_SESSION_MSG']) + +async def start_day_command(message: types.Message) -> None: + user_id: int = message.from_user.id + user_find: Any = await UserService.get_user_by_id(user_id) + language: str = await UserService.get_user_language(user_id) or 'ENGLISH' + + if not user_find: + await message.answer(MESSAGES['ENGLISH']['AUTHORIZATION_PROBLEM']) + return + + morning_routine = await RoutineService.get_user_routines(user_id, routine_type="morning") + print(f"[INFO] - Sending morning routine to user with id {user_id}") + + time_str = datetime.now().strftime("%H:%M") + + await MyDayService.add_wake_up_time(user_id, time_str) + + formatted_routine_items = "\n".join( + f"# {idx}. {routine['routine_name']}" + for idx, routine in enumerate(morning_routine, start=1) + ) + + await message.answer( + MESSAGES[language]['SEND_MORNING_MSG'] + '\n' + formatted_routine_items, + reply_markup=menu_reply_keyboard() + ) \ No newline at end of file diff --git a/telegram_bot_project/bot/scheduler.py b/telegram_bot_project/bot/scheduler.py index bedb6c2..667db7c 100644 --- a/telegram_bot_project/bot/scheduler.py +++ b/telegram_bot_project/bot/scheduler.py @@ -1,17 +1,19 @@ # bot/scheduler.py from datetime import time, datetime, timedelta from aiogram import Bot -from aiogram.fsm.context import FSMContext -from aiogram.fsm.storage.memory import MemoryStorage from apscheduler.schedulers.asyncio import AsyncIOScheduler from apscheduler.triggers.cron import CronTrigger -from messages import send_morning_message, send_evening_message -from bot.buttons import work_buttons_keyboard +from apscheduler.triggers.date import DateTrigger +from bot.buttons import work_buttons_keyboard, get_start_day_btn from service.task import TaskService from service.user import UserService +from collections import defaultdict +from typing import Set +from messages import * scheduler: AsyncIOScheduler = AsyncIOScheduler() notified_task_ids = set() +sent_notifications: defaultdict[int, Set[str]] = defaultdict(set) def initialize_scheduler(): scheduler.start() @@ -41,54 +43,147 @@ async def schedule_all_users_jobs(bot: Bot): if user["sleep_time"]: update_user_job(user["id"], user["sleep_time"], bot, job_type="sleep") -async def check_upcoming_tasks(bot: Bot): +class PreciseTaskNotifier: + def __init__(self, bot: Bot, scheduler: AsyncIOScheduler): + self.bot = bot + self.scheduler = scheduler + self.scheduled_jobs = {} + + async def schedule_task_notifications(self, task_id: int, user_id: int, task_name: str, start_time: datetime, language: str): + now = datetime.now() + + notification_times = [ + (start_time - timedelta(minutes=30), 30, "30min"), + (start_time - timedelta(minutes=15), 15, "15min"), + (start_time - timedelta(minutes=5), 5, "5min"), + (start_time, 0, "now") + ] + + for notification_time, minutes, notification_type in notification_times: + if notification_time > now: + job_id = f"task_{task_id}_{notification_type}" + + try: + if self.scheduler.get_job(job_id): + self.scheduler.remove_job(job_id) + except: + pass + + try: + self.scheduler.add_job( + self._send_notification, + trigger=DateTrigger(run_date=notification_time), + args=[language, user_id, task_name, minutes, task_id], + id=job_id, + replace_existing=True + ) + self.scheduled_jobs[job_id] = True + print(f"[SCHEDULED] - Notification for task {task_id} at {notification_time.strftime('%H:%M:%S')}") + except Exception as e: + print(f"[ERROR] Failed to schedule notification for task {task_id}: {e}") + + async def _send_notification(self, language: str, user_id: int, task_name: str, minutes: int, task_id: int): + try: + await send_task_notification(language, self.bot, user_id, task_name, minutes, task_id) + print(f"[PRECISE NOTIFY] - Sent notification to user {user_id} for task {task_id}") + + notification_key = f"{task_id}_{minutes}min" if minutes > 0 else f"{task_id}_now" + sent_notifications[user_id].add(notification_key) + + except Exception as e: + print(f"[ERROR] Failed to send precise notification: {e}") + + async def schedule_all_task_notifications(self): + try: + tasks = await TaskService.get_all_upcoming_tasks() + print(f"[INFO] Scheduling notifications for {len(tasks)} tasks") + + for task in tasks: + task_time = task.get("start_time") + if not task_time: + continue + + task_id = task["id"] + user_id = task["user_id"] + task_name = task["task_name"] + language = await UserService.get_user_language(user_id) + + await self.schedule_task_notifications(task_id, user_id, task_name, task_time, language) + + except Exception as e: + print(f"[ERROR] Failed to schedule task notifications: {e}") + + async def remove_task_notifications(self, task_id: int): + notification_types = ["30min", "15min", "5min", "now"] + + for notification_type in notification_types: + job_id = f"task_{task_id}_{notification_type}" + try: + if self.scheduler.get_job(job_id): + self.scheduler.remove_job(job_id) + if job_id in self.scheduled_jobs: + del self.scheduled_jobs[job_id] + print(f"[REMOVED] - Notification job {job_id}") + except Exception as e: + print(f"[ERROR] Failed to remove notification job {job_id}: {e}") + +async def check_upcoming_tasks_v2(bot: Bot): now = datetime.now() - print("[TASK CHECK] - Checking for upcoming tasks at {}".format(now.strftime('%Y-%m-%d %H:%M:%S'))) - - tasks = await TaskService.get_all_upcoming_tasks() - - for task in tasks: - task_time = task.get("start_time") - if not task_time: - continue - - task_id = task["id"] - user_id = task["user_id"] - task_name = task["task_name"] - language: str = await UserService.get_user_language(user_id) - - time_diff = (task_time - now).total_seconds() - time_after_start = (now - task_time).total_seconds() - - print(f"[DEBUG] - Task: '{task_name}' | user_id: {user_id} | time_diff: {time_diff:.2f} sec | start_time: {task_time.strftime('%H:%M:%S')}") - - if 1790 <= time_diff <= 1810: - print(f"[NOTIFY] - User {user_id} - Task '{task_name}' starts in 30 min") - await send_task_notification(language, bot, user_id, task_name, 30) - elif 890 <= time_diff <= 910: - print(f"[NOTIFY] - User {user_id} - Task '{task_name}' starts in 15 min") - await send_task_notification(language, bot, user_id, task_name, 15) - elif 290 <= time_diff <= 310: - print(f"[NOTIFY] - User {user_id} - Task '{task_name}' starts in 5 min") - await send_task_notification(language, bot, user_id, task_name, 5) - elif -60 <= time_diff <= 60: - print(f"[NOTIFY] - User {user_id} - Task '{task_name}' starts now") - await send_task_notification(language, bot, user_id, task_name, 0, task_id) - - if time_after_start >= 0: - started = await TaskService.get_started_status(task_id) - print(f"[DEBUG REMINDER] Task {task_id}, started: {started}, time_after_start: {time_after_start:.2f}") - - for target_minute in [5, 10, 15]: - lower = target_minute * 60 - 60 - upper = target_minute * 60 + 60 - if lower <= time_after_start <= upper: - reminder_key = (task_id, f"late_{target_minute}") - if reminder_key not in notified_task_ids: - print(f"[REMIND] - Sending reminder for task {task_id} after {target_minute} min") - await send_task_notification(language, bot, user_id, task_name, target_minute, task_id) - notified_task_ids.add(reminder_key) + print(f"[BACKUP CHECK] - Checking for upcoming tasks at {now.strftime('%Y-%m-%d %H:%M:%S')}") + + try: + tasks = await TaskService.get_all_upcoming_tasks() + + for task in tasks: + task_time = task.get("start_time") + if not task_time: + continue + + task_id = task["id"] + user_id = task["user_id"] + task_name = task["task_name"] + language: str = await UserService.get_user_language(user_id) + + time_diff = (task_time - now).total_seconds() + time_after_start = (now - task_time).total_seconds() + + print(f"[DEBUG] - Task: '{task_name}' | user_id: {user_id} | time_diff: {time_diff:.2f} sec | start_time: {task_time.strftime('%H:%M:%S')}") + + notifications_to_check = [ + (1800, 30, "30min"), + (900, 15, "15min"), + (300, 5, "5min"), + (0, 0, "now") + ] + + for target_seconds, minutes, notification_type in notifications_to_check: + if target_seconds - 60 <= time_diff <= target_seconds + 60: + notification_key = f"{task_id}_{notification_type}" + + if notification_key not in sent_notifications[user_id]: + print(f"[BACKUP NOTIFY] - User {user_id} - Task '{task_name}' notification: {notification_type}") + await send_task_notification(language, bot, user_id, task_name, minutes, task_id) + sent_notifications[user_id].add(notification_key) + + if time_after_start >= 300: + try: + started = await TaskService.get_started_status(task_id) + print(f"[DEBUG REMINDER] Task {task_id}, started: {started}, time_after_start: {time_after_start:.2f}") + + for target_minute in [5, 10, 15]: + lower = target_minute * 60 - 60 + upper = target_minute * 60 + 60 + if lower <= time_after_start <= upper: + reminder_key = f"{task_id}_late_{target_minute}" + if reminder_key not in sent_notifications[user_id]: + print(f"[BACKUP REMIND] - Sending reminder for task {task_id} after {target_minute} min") + await send_task_notification(language, bot, user_id, task_name, target_minute, task_id) + sent_notifications[user_id].add(reminder_key) + except Exception as e: + print(f"[ERROR] Failed to check task status for {task_id}: {e}") + except Exception as e: + print(f"[ERROR] Failed in backup task check: {e}") async def send_task_notification(language: str, bot: Bot, user_id: int, task_name: str, minutes: int, task_id: int): messages = { @@ -115,7 +210,51 @@ async def send_task_notification(language: str, bot: Bot, user_id: int, task_nam text = message_set["soon"] try: - await bot.send_message(user_id, text, reply_markup=work_buttons_keyboard(task_id)) + await bot.send_message(user_id, text, reply_markup=work_buttons_keyboard(task_id), parse_mode="Markdown") print(f"[BOT] Sent message to {user_id}: {text}") except Exception as e: - print(f"[ERROR] Failed to send message to {user_id}: {e}") \ No newline at end of file + print(f"[ERROR] Failed to send message to {user_id}: {e}") + +async def cleanup_old_notifications(): + global sent_notifications + sent_notifications.clear() + print("[CLEANUP] - Cleared old notification cache") + +_notifier_instance = None + +def get_notifier_instance(bot: Bot = None) -> PreciseTaskNotifier: + global _notifier_instance + if _notifier_instance is None and bot: + _notifier_instance = PreciseTaskNotifier(bot, scheduler) + return _notifier_instance + +async def schedule_new_task_notifications(task_id: int, user_id: int, task_name: str, start_time: datetime, language: str): + notifier = get_notifier_instance() + if notifier: + await notifier.schedule_task_notifications(task_id, user_id, task_name, start_time, language) + +async def remove_task_notifications(task_id: int): + notifier = get_notifier_instance() + if notifier: + await notifier.remove_task_notifications(task_id) + + +async def send_morning_message(bot: Bot, user_id: int): + language = await UserService.get_user_language(user_id) or "ENGLISH" + user = await UserService.get_user_by_id(user_id) + + user_name = user.get('user_name', 'User') if user else 'User' + + await bot.send_message( + user_id, + MESSAGES[language]['WAKE_UP_MESSAGE'].format(user_name), + reply_markup=get_start_day_btn() + ) + +async def send_evening_message(bot: Bot, user_id: int): + language = await UserService.get_user_language(user_id) or "ENGLISH" + print(f"[INFO] - Sending evening routine to user with id, {user_id}") + await bot.send_message( + user_id, + MESSAGES[language]['SEND_EVENING_MSG'].format("👤") + ) \ No newline at end of file diff --git a/telegram_bot_project/main.py b/telegram_bot_project/main.py index 0cb9f4c..c413956 100644 --- a/telegram_bot_project/main.py +++ b/telegram_bot_project/main.py @@ -7,18 +7,16 @@ from apscheduler.schedulers.asyncio import AsyncIOScheduler from config import TOKEN -from bot.scheduler import initialize_scheduler, schedule_all_users_jobs, check_upcoming_tasks +from bot.scheduler import * from bot.commands import * from bot.callbacks import * from bot.fallbacks import * from messages import * +from states import * storage: MemoryStorage = MemoryStorage() dp = Dispatcher(storage=storage) -focus_times = {} -FOCUS_ZONE_START_TIME: int = None - # Command Handlers @dp.message(Command("start")) async def start(message: Message): @@ -229,6 +227,62 @@ async def focus_end(message: Message, state: FSMContext): reply_markup=focus_save_question_keyboard() ) +@dp.message(lambda m: m.text == STOP_WORK_SESSION) +async def start_work_btn(message: Message) -> None: + user_id = message.from_user.id + user_find = await UserService.get_user_by_id(user_id) + language = await UserService.get_user_language(user_id) or 'ENGLISH' + + if not user_find: + await message.answer(MESSAGES['ENGLISH']['AUTHORIZATION_PROBLEM']) + return + + task_info = user_task_start_time.get(user_id) + if not task_info: + await message.answer(MESSAGES[language]['AUTHORIZATION_PROBLEM']) + return + + task_id, task_data = task_info + print(f"[DEBUG] - Task id: {task_id} - Start Time: {task_data}") + + stop_time = datetime.now() + total_seconds = int((stop_time - task_data).total_seconds()) + minutes, seconds = divmod(total_seconds, 60) + + user_task_start_time.pop(user_id, None) + + print(f"[DEBUG] - User with id {user_id} add duration {total_seconds}s to task {task_id}.") + await TaskService.add_task_complete_duration(task_id, total_seconds) + + task = await TaskService.get_task_by_id(task_id) + + await TaskService.toggle_task_status(task_id) + await MyDayService.increment_completed_tasks(user_id) + + task_name = task['task_name'] if task else f"Task {task_id}" + + await message.answer( + MESSAGES[language]['FINISH_WORK_SESSION'].format(task_name, minutes, seconds), + reply_markup=menu_reply_keyboard() + ) + +@dp.message(lambda m: m.text == STOP_WORK_CANCEL) +async def cancel_work_btn(message: Message) -> None: + user_id = message.from_user.id + user_find = await UserService.get_user_by_id(user_id) + language = await UserService.get_user_language(user_id) or 'ENGLISH' + + if not user_find: + await message.answer(MESSAGES['ENGLISH']['AUTHORIZATION_PROBLEM']) + return + + user_task_start_time.pop(user_id, None) + await message.answer(MESSAGES[language]['BREAK_WORK_SESSION'], reply_markup=menu_reply_keyboard()) + +@dp.message(lambda m: m.text == START_DAY_BTN) +async def start_day_btn(message: Message) -> None: + await start_day_command(message) + @dp.message(Command("focuses")) @dp.message(lambda m: m.text == ALL_FOCUSES_BTN) async def show_saved_focus(message: Message): @@ -270,30 +324,55 @@ async def callback_task_status(callback_query: CallbackQuery) -> None: async def process_fallback(message: Message, state: FSMContext): await fallback(message, state) -# Main Function async def main(): bot = Bot(token=TOKEN) storage = dp.storage - - scheduler: AsyncIOScheduler = initialize_scheduler() + + scheduler = initialize_scheduler() scheduler.add_job( MyDayService.create_stats_for_all_users, trigger='cron', - hour='0', - minute='0' + hour=0, + minute=0, + id='daily_stats' ) - + + scheduler.add_job( + cleanup_old_notifications, + trigger='cron', + hour=1, + minute=0, + id='cleanup_notifications' + ) + + notifier = PreciseTaskNotifier(bot, scheduler) + get_notifier_instance(bot) + + print("[STARTUP] Planning precise notifications for all tasks...") + await notifier.schedule_all_task_notifications() + scheduler.add_job( - check_upcoming_tasks, + notifier.schedule_all_task_notifications, trigger='interval', - minutes=1, - args={bot} + minutes=10, + id='reschedule_notifications' ) - + + scheduler.add_job( + check_upcoming_tasks_v2, + trigger='interval', + minutes=2, + args=[bot], + id='backup_task_check' + ) + await schedule_all_users_jobs(bot) + + print("[STARTUP] All jobs scheduled successfully") + + # Запускаем бота await dp.start_polling(bot) -# Start point if __name__ == "__main__": - print("-- STARTING ROCKY --\n") + import asyncio asyncio.run(main()) \ No newline at end of file diff --git a/telegram_bot_project/messages.py b/telegram_bot_project/messages.py index 483b6fc..17ff7de 100644 --- a/telegram_bot_project/messages.py +++ b/telegram_bot_project/messages.py @@ -8,7 +8,7 @@ "UKRANIAN": { "START_MSG": ( "🎉 Вітаємо! \n" - "Готові розпочати роботу? Я допоможу організувати ваш день. 📋" + "Готові розпочати роботу? Я допоможу організувати ваш день." ), "START_MSG_AGAIN": ( "👋 Ви повернулися! \n" @@ -16,102 +16,104 @@ ), "HELP_MSG": ( "❓ Потрібна допомога? \n" - "Використовуйте /start, /language або /menu для навігації. 📚" + "Використовуйте /start, /language або /menu для навігації." ), "MENU_MSG": ( "📋 Ось ваше меню. \n" - "Виберіть потрібну опцію для продовження. ⚙️" + "Виберіть потрібну опцію для продовження." ), "AUTHORIZATION_PROBLEM": ( "🚫 Виникла проблема з авторизацією. \n" - "Спробуйте /start для початку. 🔄" + "Спробуйте /start для початку." ), "TEXT_RESPONSE": ( "✉️ Отримано: \"{response}\". \n" - "Дякуємо за ваш ввід! 📝" + "Дякуємо за ваш ввід!" ), "CONTINUE_MSG": ( "➡️ Що далі? \n" - "Відкрийте /menu, щоб обрати наступну дію. 📋" + "Відкрийте /menu, щоб обрати наступну дію." ), - "SETTINGS_RESPONSE": "⚙️ Відкриваємо налаштування. Давайте налаштуємо все під вас. 🔧", - "MYDAY_RESPONSE": "📅 Ваш план на день готовий. Перегляньте його! 🕒", - "IDEA_RESPONSE": "💡 Маєте ідею? Напишіть, я збережу її. 📝", - "IDEA_SAVED": "✅ Ідею збережено. Дякуємо за внесок! 📚", - "ADD_TASK_RESPONSE": "📝 Додаємо завдання. Вкажіть деталі. 🛠️", - "IDEA_ACTION": "📌 Що зробити з цією ідеєю? Виберіть дію. ⚙️", - "IDEA_DELETE": "🗑️ Ідею видалено. Готові до нових? 📝", - "IDEA_PROBLEM": "⚠️ Помилка збереження ідеї. Спробуйте ще раз. 🔄", - "IDEAS_SHOW": "💡 Ваші ідеї готові до перегляду. 📋", - "IDEA_EXISTS": "⚠️ Така ідея вже існує. Спробуйте нову. 💡", - "ERROR_SAVING_IDEA": "⚠️ Не вдалося зберегти ідею. Спробуйте пізніше. ⏳", - "NO_IDEAS": "📝 Ідей поки немає. Додайте першу! 🚀", - "DELETE_IDEA": "ℹ️ Вкажіть номер ідеї для видалення. 🗑️", - "UPDATE_IDEA": "ℹ️ Вкажіть номер ідеї для оновлення. ✏️", - "NOT_VALID_IDEA_NUM": "❌ Неправильний номер ідеї. Спробуйте ще раз. 🔢", - "INVALID_IDEA_NUM": "❌ Номер ідеї некоректний. Перевірте ще раз. 🔍", - "IDEA_DELETED": "🗑️ Ідея №{} '{}' видалена. Готові до нових завдань? 📝", - "ASK_NEW_IDEA_TEXT": "✏️ Введіть новий текст для ідеї №{} '{}'. 📝", - "IDEA_UPDATED": "✅ Ідея №{} оновлена. Відмінна робота! 📚", - "TASK_ADD": "📝 Вкажіть назву завдання для додавання. 🛠️", - "TASK_DEADLINE_ASK": "⏰ Додати дедлайн до завдання? Виберіть опцію. 🕒", - "TASK_DEADLINE_YES": "🕒 Введіть час дедлайну (наприклад, 13:10). 📅", - "TASK_DEADLINE_NO": "✅ Завдання збережено без дедлайну. 📝", - "TASK_DEADLINE_INVALID": "❌ Некоректний формат часу. Спробуйте ще раз. ⏳", - "TASK_SAVED": "✅ Завдання збережено. Продовжуйте! 📋", - "TASK_MENU": "📂 Меню завдань. Усе під контролем. 🛠️", - "NO_TASKS": "❌ Завдань поки немає. Додайте нове через /task. 🚀", - "YOUR_TASKS": "📋 Список ваших завдань. Обирайте! 🛠️", - "TASK_DELETE_MSG": "🗑️ Вкажіть номер завдання для видалення. 📝", - "INVALID_TASK_NUM": "❌ Некоректний номер завдання. Спробуйте ще раз. 🔢", - "TASK_DELETED": "✅ Завдання №{} '{}' видалено. 📝", - "TASK_DELETE_PROBLEM": "⚠️ Помилка видалення завдання. Спробуйте пізніше. ⏳", + "SETTINGS_RESPONSE": "⚙️ Відкриваємо налаштування. Давайте налаштуємо все під вас.", + "MYDAY_RESPONSE": "📅 Ваш план на день готовий. Перегляньте його!", + "IDEA_RESPONSE": "💡 Маєте ідею? Напишіть, я збережу її.", + "IDEA_SAVED": "✅ Ідею збережено. Дякуємо за внесок!", + "ADD_TASK_RESPONSE": "📝 Додаємо завдання. Вкажіть деталі.", + "IDEA_ACTION": "📌 Що зробити з цією ідеєю? Виберіть дію.", + "IDEA_DELETE": "🗑️ Ідею видалено. Готові до нових?", + "IDEA_PROBLEM": "⚠️ Помилка збереження ідеї. Спробуйте ще раз.", + "IDEAS_SHOW": "💡 Ваші ідеї готові до перегляду.", + "IDEA_EXISTS": "⚠️ Така ідея вже існує. Спробуйте нову.", + "ERROR_SAVING_IDEA": "⚠️ Не вдалося зберегти ідею. Спробуйте пізніше.", + "NO_IDEAS": "📝 Ідей поки немає. Додайте першу!", + "DELETE_IDEA": "ℹ️ Вкажіть номер ідеї для видалення.", + "UPDATE_IDEA": "ℹ️ Вкажіть номер ідеї для оновлення.", + "NOT_VALID_IDEA_NUM": "❌ Неправильний номер ідеї. Спробуйте ще раз.", + "INVALID_IDEA_NUM": "❌ Номер ідеї некоректний. Перевірте ще раз.", + "IDEA_DELETED": "🗑️ Ідея №{} '{}' видалена. Готові до нових завдань?", + "ASK_NEW_IDEA_TEXT": "✏️ Введіть новий текст для ідеї №{} '{}'.", + "IDEA_UPDATED": "✅ Ідея №{} оновлена. Відмінна робота!", + "TASK_ADD": "📝 Вкажіть назву завдання для додавання.", + "TASK_DEADLINE_ASK": "⏰ Додати дедлайн до завдання? Виберіть опцію.", + "TASK_DEADLINE_YES": "🕒 Введіть час дедлайну (наприклад, 13:10).", + "TASK_DEADLINE_NO": "✅ Завдання збережено без дедлайну.", + "TASK_DEADLINE_INVALID": "❌ Некоректний формат часу. Спробуйте ще раз.", + "TASK_SAVED": "✅ Завдання збережено. Продовжуйте!", + "TASK_MENU": "📂 Меню завдань. Усе під контролем.", + "NO_TASKS": "❌ Завдань поки немає.\nДодайте нове через /task", + "YOUR_TASKS": "📋 Список ваших завдань.", + "TASK_DELETE_MSG": "🗑️ Вкажіть номер завдання для видалення.", + "INVALID_TASK_NUM": "❌ Некоректний номер завдання.\nСпробуйте ще раз. 🔢", + "TASK_DELETED": "✅ Завдання №{} '{}' видалено.", + "TASK_DELETE_PROBLEM": "⚠️ Помилка видалення завдання.\nСпробуйте пізніше. ⏳", "COMPLETE_TASK_MSG": "✅ Вкажіть номер завершеного завдання. 🏆", - "COMPLETE_TASK_INVALID": "❌ Некоректний номер завдання. Спробуйте ще раз. 🔢", - "COMPLETE_TASK_SUCCESS": "🏆 Завдання №{} '{}' виконано. Відмінно! 📝", - "COMPLETE_TASK_PROBLEM": "⚠️ Помилка оновлення завдання. Спробуйте ще раз. 🔄", + "COMPLETE_TASK_INVALID": "❌ Некоректний номер завдання.\nСпробуйте ще раз. 🔢", + "COMPLETE_TASK_SUCCESS": "🏆 Завдання №{} '{}' виконано.\nВідмінно! 📝", + "COMPLETE_TASK_PROBLEM": "⚠️ Помилка оновлення завдання.\nСпробуйте ще раз. 🔄", "UPDATE_TASK_MSG": "✏️ Вкажіть номер завдання для оновлення. 📝", - "UPDATE_TASK_INVALID": "❌ Некоректний номер завдання. Спробуйте ще раз. 🔢", - "UPDATE_TASK_SUCCESS": "✅ Завдання №{} оновлено. Гарна робота! 📝", - "UPDATE_TASK_PROBLEM": "⚠️ Помилка оновлення завдання. Спробуйте пізніше. ⏳", - "UPDATE_TASK_NAME_MSG": "📝 Введіть нову назву завдання. 🛠️", - "UPDATE_TASK_NAME_INVALID": "❌ Некоректна назва завдання. Спробуйте ще раз. 🔢", - "SETTINGS_MENU": "⚙️ Меню налаштувань. Налаштуйте все за вашим бажанням. 🔧", - "ROUTINE_MENU_DAY": "🌅 Налаштувати ранковий чи вечірній розпорядок? 📅", - "MORNING_ROUTINE": "☀️ Ваш ранковий розпорядок готовий. 📋", - "EVENING_ROUTINE": "🌙 Ваш вечірній розпорядок готовий. 📋", - "ROUTINES_INVALID": "❌ Помилка з розпорядками. Спробуйте ще раз. 🔄", - "ADD_MORNING_ROUTINE": "📝 Введіть назву ранкового розпорядку. 🛠️", - "INVALID_MORNING_ROUTINE": "❌ Некоректна назва. Спробуйте ще раз. 🔢", - "ROUTINE_EXISTS": "⚠️ Розпорядок з такою назвою вже існує. Виберіть іншу. 📝", - "ROUTINE_SAVED": "✅ Розпорядок «{}» збережено. 📝", - "MORNING_ROUTINE_SHOW": "☀️ Ваші ранкові розпорядки. 📋", - "EVENING_ROUTINE_SHOW": "🌙 Ваші вечірні розпорядки. 📋", - "NO_MORNING_ROUTINE": "📝 Ранкових розпорядків немає. Додайте перший! 🚀", - "PROVIDE_ROUTINE_ID": "🔢 Вкажіть номер розпорядку для дії. 📝", - "ROUTINE_DELETED": "🗑️ Розпорядок видалено. Готові до нових? 📝", - "NEW_ROUTINE_NAME": "✏️ Введіть нову назву розпорядку. 📝", - "ROUTINE_NAME_SET": "✅ Назва розпорядку змінена на «{}». 📝", - "SMTP_MESSAGE_TEXT": "📝 Напишіть ваш відгук. Ми цінуємо вашу думку! 💬", - "SMTP_MESSAGE_SENT": "🙏 Відгук отримано. Дякуємо за ваш внесок! 📝", - "INVALID_MESSAGE": "❌ Некоректний текст. Спробуйте ще раз. 🔢", - "SET_TIME_MSG": "⏰ Введіть час для таймера (наприклад, 10:00). 🕒", - "TIMER_SET": "✅ Таймер встановлено на {}. 📅", - "ROUTINE_TIME": "⏰ Прокидання о {}, сон о {}, тривалість дня: {}. 📋", - "TIMER_INVALID": "❌ Некоректний формат часу (потрібно 10:00). Спробуйте ще раз. ⏳", - "IDEA_EXIST": "⚠️ Ідея з такою назвою вже є. Виберіть іншу. 💡", - "SEND_MORNING_MSG": "Доброго ранку, {}! ☀️", - "SEND_EVENING_MSG": "Доброго вечора, {}! 🌙", - "WELCOME_TO_FOCUS": "Вітаємо у зоні фокусу! 🎯", + "UPDATE_TASK_INVALID": "❌ Некоректний номер завдання.\nСпробуйте ще раз. 🔢", + "UPDATE_TASK_SUCCESS": "✅ Завдання №{} оновлено.\nГарна робота! 📝", + "UPDATE_TASK_PROBLEM": "⚠️ Помилка оновлення завдання.\nСпробуйте пізніше. ⏳", + "UPDATE_TASK_NAME_MSG": "📝 Введіть нову назву завдання.\n🛠️", + "UPDATE_TASK_NAME_INVALID": "❌ Некоректна назва завдання.\nСпробуйте ще раз. 🔢", + "SETTINGS_MENU": "⚙️ Меню налаштувань. Налаштуйте все за вашим бажанням.", + "ROUTINE_MENU_DAY": "🌅 Налаштувати ранковий чи вечірній розпорядок?", + "MORNING_ROUTINE": "☀️ Ваш ранковий розпорядок готовий.", + "EVENING_ROUTINE": "🌙 Ваш вечірній розпорядок готовий.", + "ROUTINES_INVALID": "❌ Помилка з розпорядками.\nСпробуйте ще раз. 🔄", + "ADD_MORNING_ROUTINE": "📝 Введіть назву для ранкового розпорядку.", + "INVALID_MORNING_ROUTINE": "❌ Некоректна назва.\nСпробуйте ще раз. 🔢", + "ROUTINE_EXISTS": "⚠️ Розпорядок з такою назвою вже існує.\nВиберіть іншу. 📝", + "ROUTINE_SAVED": "✅ Розпорядок «{}» збережено.", + "MORNING_ROUTINE_SHOW": "☀️ Ваші ранкові розпорядки.", + "EVENING_ROUTINE_SHOW": "🌙 Ваші вечірні розпорядки.", + "NO_MORNING_ROUTINE": "📝 Ранкових розпорядків немає. Додайте перший!", + "NO_EVENING_ROUTINE": "📝 Вечірних розпорядків немає. Додайте перший!", + "ROUTINE_TIME_NOT": "⚠️ Ви ще не маєте налаштованих годин пробудження і сну.", + "PROVIDE_ROUTINE_ID": "🔢 Вкажіть номер розпорядку для дії.", + "ROUTINE_DELETED": "🗑️ Розпорядок видалено.", + "NEW_ROUTINE_NAME": "✏️ Введіть нову назву розпорядку.", + "ROUTINE_NAME_SET": "✅ Назва розпорядку змінена на «{}».", + "SMTP_MESSAGE_TEXT": "📝 Напишіть ваш відгук.\nМи цінуємо вашу думку! 💬", + "SMTP_MESSAGE_SENT": "🙏 Відгук отримано.\nДякуємо за ваш внесок! 📝", + "INVALID_MESSAGE": "❌ Некоректний текст.\nСпробуйте ще раз. 🔢", + "SET_TIME_MSG": "⏰ Введіть час для таймера (наприклад, 10:00).", + "TIMER_SET": "✅ Таймер встановлено на {}.", + "ROUTINE_TIME": "⏰ Прокидання о {}, сон о {}, тривалість дня: {}.", + "TIMER_INVALID": "❌ Некоректний формат часу (потрібно 10:00). Спробуйте ще раз.", + "IDEA_EXIST": "⚠️ Ідея з такою назвою вже існує.", + "SEND_MORNING_MSG": "Відмінний початок дня! ☀️\nТвій ранковий розпорядок:\n", + "SEND_EVENING_MSG": "🌙 Доброго вечора, {}!", + "WELCOME_TO_FOCUS": "🎯 Вітаємо у зоні фокусу!", "START_FOCUS_MSG": "Сесію фокусу розпочато. 🕒", - "STOP_FOCUS_MSG": "Сесію фокусу зупинено.\nТривалість: - {}хв {}с. ⏳", - "SAVE_FOCUS_ZONE": "Зберегти сесію фокусу? 📝", + "STOP_FOCUS_MSG": "Сесію фокусу зупинено.\n⏳ Тривалість: - {}хв {}с.", + "SAVE_FOCUS_ZONE": "❗ Зберегти сесію фокусу?", "SAVED_FOCUS_MSG": "Сесію фокусу збережено. ✅", "NOT_SAVED_FOCUS_MSG": "Сесію фокусу не збережено. ❌", "TITLE_FOCUS_ZONE_MSG": "Бажаєте дати назву цій сесії? 📝", "NOT_FOUND_FOCUS_SESSION": "❗ Не знайдено початку фокус-сесії.", "FOCUS_INVALID": "❗ Неправильний параметр фокусування", - "FOCUS_TITLE_ASK": "Будь ласка, введіть назву фокус-сесії.", + "FOCUS_TITLE_ASK": "✏️ Будь ласка, введіть назву фокус-сесії.", "FOCUS_EXISTS": "❗ Фокус-сесія вже активна.", "FOCUS_LIST_TITLE": "🧠 Список ваших фокус-сесій", "NO_FOCUS_SESSIONS": "😕 У вас ще немає жодної фокус-сесії.", @@ -119,11 +121,15 @@ "🌐 Виберіть мову для роботи. \n" "Оберіть опцію нижче. 📚" ), - "LANGUAGE_OK": "✅ Мову змінено. Готові продовжити? 🚀", - "LANGUAGE_INVALID": "❌ Некоректний вибір мови. Спробуйте ще раз. 🔢", - "DELETE_FOCUS_SESSION_MSG": "Вкажіть номер сесії, яку ви хочете видалити.", + "LANGUAGE_OK": "✅ Мову змінено.\nГотові продовжити? 🚀", + "LANGUAGE_INVALID": "❌ Некоректний вибір мови.\nСпробуйте ще раз. 🔢", + "DELETE_FOCUS_SESSION_MSG": "🔢 Вкажіть номер сесії, яку ви хочете видалити.", "FOCUS_DELETED": "✅ Фокус-сесію №{} з назвою \"{}\" успішно видалено.", - "REMIND_WORK_CANCEL": "Нагадування для цього завдання було вимкнено", + "REMIND_WORK_CANCEL": "✅ Нагадування для цього завдання було вимкнено", + "REMIND_WORK_START": "✅ Починаємо задачу - {}", + "FINISH_WORK_SESSION": "🎉 Вітаємо, ви виконали завдання - {}\n⏳ Час, витрачений на завдання: {}хв {}с\nПродовжуйте в тому ж дусі! 🏆", + "BREAK_WORK_SESSION": "✅ Ви успішно зробили перерву в робочій сесії.\nЗакінчіть її, коли знайдете час :)", + "WAKE_UP_MESSAGE": "☀️ Ти готовий до нового дня - {}?" }, "ENGLISH": { "START_MSG": ( @@ -154,89 +160,94 @@ "➡️ What’s next? \n" "Open /menu to select your next action. 📋" ), - "SETTINGS_RESPONSE": "⚙️ Accessing settings. Let’s customize your experience. 🔧", - "MYDAY_RESPONSE": "📅 Your daily plan is ready. Review it now! 🕒", - "IDEA_RESPONSE": "💡 Have an idea? Write it down, and I’ll save it. 📝", - "IDEA_SAVED": "✅ Idea saved. Thank you for sharing! 📚", - "ADD_TASK_RESPONSE": "📝 Adding a task. Provide the details. 🛠️", - "IDEA_ACTION": "📌 What would you like to do with this idea? Select an action. ⚙️", - "IDEA_DELETE": "🗑️ Idea deleted. Ready for new ones? 📝", - "IDEA_PROBLEM": "⚠️ Error saving the idea. Please try again. 🔄", - "IDEAS_SHOW": "💡 Your ideas are ready to view. 📋", - "IDEA_EXISTS": "⚠️ This idea already exists. Try a new one. 💡", - "ERROR_SAVING_IDEA": "⚠️ Failed to save the idea. Try again later. ⏳", - "NO_IDEAS": "📝 No ideas yet. Add your first one! 🚀", - "DELETE_IDEA": "ℹ️ Specify the idea number to delete. 🗑️", - "UPDATE_IDEA": "ℹ️ Specify the idea number to update. ✏️", - "NOT_VALID_IDEA_NUM": "❌ Invalid idea number. Try again. 🔢", - "INVALID_IDEA_NUM": "❌ Incorrect idea number. Please check again. 🔍", - "IDEA_DELETED": "🗑️ Idea #{} '{}' deleted. Ready for new tasks? 📝", - "ASK_NEW_IDEA_TEXT": "✏️ Enter new text for idea #{} '{}'. 📝", - "IDEA_UPDATED": "✅ Idea #{} updated. Great work! 📚", - "TASK_ADD": "📝 Enter the task name to add. 🛠️", - "TASK_DEADLINE_ASK": "⏰ Add a deadline for this task? Choose an option. 🕒", - "TASK_DEADLINE_YES": "🕒 Enter the deadline time (e.g., 13:10). 📅", - "TASK_DEADLINE_NO": "✅ Task saved without a deadline. 📝", - "TASK_DEADLINE_INVALID": "❌ Invalid time format. Try again. ⏳", - "TASK_SAVED": "✅ Task saved. Keep it up! 📋", - "TASK_MENU": "📂 Task menu. Everything is under control. 🛠️", - "NO_TASKS": "❌ No tasks yet. Add one with /task. 🚀", - "YOUR_TASKS": "📋 List of your tasks. Choose one! 🛠️", - "TASK_DELETE_MSG": "🗑️ Specify the task number to delete. 📝", - "INVALID_TASK_NUM": "❌ Incorrect task number. Try again. 🔢", - "TASK_DELETED": "✅ Task #{} '{}' deleted. 📝", - "TASK_DELETE_PROBLEM": "⚠️ Error deleting task. Try again later. ⏳", - "COMPLETE_TASK_MSG": "✅ Specify the completed task number. 🏆", - "COMPLETE_TASK_INVALID": "❌ Incorrect task number. Try again. 🔢", - "COMPLETE_TASK_SUCCESS": "🏆 Task #{} '{}' completed. Well done! 📝", - "COMPLETE_TASK_PROBLEM": "⚠️ Error updating task. Try again. 🔄", - "UPDATE_TASK_MSG": "✏️ Specify the task number to update. 📝", - "UPDATE_TASK_INVALID": "❌ Incorrect task number. Try again. 🔢", - "UPDATE_TASK_SUCCESS": "✅ Task #{} updated. Nice job! 📝", - "UPDATE_TASK_PROBLEM": "⚠️ Error updating task. Try again later. ⏳", - "UPDATE_TASK_NAME_MSG": "📝 Enter the new task name. 🛠️", - "UPDATE_TASK_NAME_INVALID": "❌ Invalid task name. Try again. 🔢", - "SETTINGS_MENU": "⚙️ Settings menu. Customize as needed. 🔧", - "ROUTINE_MENU_DAY": "🌅 Set up a morning or evening routine? 📅", - "MORNING_ROUTINE": "☀️ Your morning routine is ready. 📋", - "EVENING_ROUTINE": "🌙 Your evening routine is ready. 📋", - "ROUTINES_INVALID": "❌ Error with routines. Try again. 🔄", - "ADD_MORNING_ROUTINE": "📝 Enter a title for your morning routine. 🛠️", - "INVALID_MORNING_ROUTINE": "❌ Invalid title. Try again. 🔢", - "ROUTINE_EXISTS": "⚠️ Routine with this title already exists. Choose another. 📝", - "ROUTINE_SAVED": "✅ Routine «{}» saved. 📝", - "MORNING_ROUTINE_SHOW": "☀️ Your morning routines. 📋", - "EVENING_ROUTINE_SHOW": "🌙 Your evening routines. 📋", - "NO_MORNING_ROUTINE": "📝 No morning routines yet. Add one! 🚀", - "PROVIDE_ROUTINE_ID": "🔢 Specify the routine number for action. 📝", - "ROUTINE_DELETED": "🗑️ Routine deleted. Ready for new plans? 📝", - "NEW_ROUTINE_NAME": "✏️ Enter a new name for the routine. 📝", - "ROUTINE_NAME_SET": "✅ Routine name changed to «{}». 📝", - "SMTP_MESSAGE_TEXT": "📝 Share your feedback. We value your input! 💬", - "SMTP_MESSAGE_SENT": "🙏 Feedback received. Thank you! 📝", - "INVALID_MESSAGE": "❌ Invalid text. Try again. 🔢", - "SET_TIME_MSG": "⏰ Enter the timer time (e.g., 10:00). 🕒", - "TIMER_SET": "✅ Timer set for {}. 📅", - "TIMER_INVALID": "❌ Invalid time format (use 10:00). Try again. ⏳", - "ROUTINE_TIME": "⏰ Wake up at {}, sleep at {}, total day time: {}. 📋", - "IDEA_EXIST": "⚠️ Idea with this name already exists. Choose another. 💡", - "SEND_MORNING_MSG": "Good morning, {}! ☀️", - "SEND_EVENING_MSG": "Good evening, {}! 🌙", - "WELCOME_TO_FOCUS": "Welcome to the focus zone! 🎯", - "START_FOCUS_MSG": "Focus session started. 🕒", - "STOP_FOCUS_MSG": "Focus session stopped.\nDuration - {}m {}s. ⏳", - "SAVE_FOCUS_ZONE": "Save the focus session? 📝", + "SETTINGS_RESPONSE": "⚙️ Accessing settings. Let’s customize your experience.", + "MYDAY_RESPONSE": "📅 Your daily plan is ready. Review it now!", + "IDEA_RESPONSE": "💡 Have an idea? Write it down, and I’ll save it.", + "IDEA_SAVED": "✅ Idea saved. Thank you for sharing!", + "ADD_TASK_RESPONSE": "📝 Adding a task. Provide the details.", + "IDEA_ACTION": "📌 What would you like to do with this idea? Select an action.", + "IDEA_DELETE": "🗑️ Idea deleted. Ready for new ones? ", + "IDEA_PROBLEM": "⚠️ Error saving the idea. Please try again.", + "IDEAS_SHOW": "💡 Your ideas are ready to view.", + "IDEA_EXISTS": "⚠️ This idea already exists. Try a new one.", + "ERROR_SAVING_IDEA": "⚠️ Failed to save the idea. Try again later.", + "NO_IDEAS": "📝 No ideas yet. Add your first one!", + "DELETE_IDEA": "ℹ️ Specify the idea number to delete.", + "UPDATE_IDEA": "ℹ️ Specify the idea number to update.", + "NOT_VALID_IDEA_NUM": "❌ Invalid idea number. Try again.", + "INVALID_IDEA_NUM": "❌ Incorrect idea number. Please check again.", + "IDEA_DELETED": "🗑️ Idea #{} '{}' deleted. Ready for new tasks?", + "ASK_NEW_IDEA_TEXT": "✏️ Enter new text for idea #{} '{}'.", + "IDEA_UPDATED": "✅ Idea #{} updated. Great work!", + "TASK_ADD": "📝 Enter the task name to add.", + "TASK_DEADLINE_ASK": "⏰ Add a deadline for this task? Choose an option.", + "TASK_DEADLINE_YES": "🕒 Enter the deadline time (e.g., 13:10).", + "TASK_DEADLINE_NO": "✅ Task saved without a deadline.", + "TASK_DEADLINE_INVALID": "❌ Invalid time format. Try again.", + "TASK_SAVED": "✅ Task saved. Keep it up!", + "TASK_MENU": "📂 Task menu. Everything is under control.", + "NO_TASKS": "❌ No tasks yet. Add one with /task.", + "YOUR_TASKS": "📋 List of your tasks. Choose one!", + "TASK_DELETE_MSG": "🗑️ Specify the task number to delete.", + "INVALID_TASK_NUM": "❌ Incorrect task number. Try again.", + "TASK_DELETED": "✅ Task #{} '{}' deleted.", + "TASK_DELETE_PROBLEM": "⚠️ Error deleting task. Try again later.", + "COMPLETE_TASK_MSG": "✅ Specify the completed task number.", + "COMPLETE_TASK_INVALID": "❌ Incorrect task number. Try again.", + "COMPLETE_TASK_SUCCESS": "🏆 Task #{} '{}' completed. Well done!", + "COMPLETE_TASK_PROBLEM": "⚠️ Error updating task. Try again.", + "UPDATE_TASK_MSG": "✏️ Specify the task number to update.", + "UPDATE_TASK_INVALID": "❌ Incorrect task number. Try again.", + "UPDATE_TASK_SUCCESS": "✅ Task #{} updated. Nice job!", + "UPDATE_TASK_PROBLEM": "⚠️ Error updating task. Try again later.", + "UPDATE_TASK_NAME_MSG": "📝 Enter the new task name.", + "UPDATE_TASK_NAME_INVALID": "❌ Invalid task name.\nTry again.", + "SETTINGS_MENU": "⚙️ Settings menu. Customize as needed.", + "ROUTINE_MENU_DAY": "🌅 Set up a morning or evening routine?", + "MORNING_ROUTINE": "☀️ Your morning routine is ready.", + "EVENING_ROUTINE": "🌙 Your evening routine is ready.", + "ROUTINES_INVALID": "❌ Error with routines. Try again.", + "ADD_MORNING_ROUTINE": "📝 Enter a title for your morning routine.", + "INVALID_MORNING_ROUTINE": "❌ Invalid title. Try again.", + "ROUTINE_EXISTS": "⚠️ Routine with this title already exists. Choose another.", + "ROUTINE_SAVED": "✅ Routine «{}» saved.", + "MORNING_ROUTINE_SHOW": "☀️ Your morning routines.", + "EVENING_ROUTINE_SHOW": "🌙 Your evening routines.", + "NO_MORNING_ROUTINE": "📝 No morning routines yet. Add one!", + "NO_EVENING_ROUTINE": "📝 No evening routines yet. Add the first one!", + "PROVIDE_ROUTINE_ID": "🔢 Specify the routine number for action.", + "ROUTINE_DELETED": "🗑️ Routine deleted. Ready for new plans?", + "NEW_ROUTINE_NAME": "✏️ Enter a new name for the routine.", + "ROUTINE_NAME_SET": "✅ Routine name changed to «{}».", + "ROUTINE_TIME_NOT": "You don’t have wake-up and sleep times set yet.", + "SMTP_MESSAGE_TEXT": "📝 Share your feedback. We value your input!", + "SMTP_MESSAGE_SENT": "🙏 Feedback received. Thank you!", + "INVALID_MESSAGE": "❌ Invalid text. Try again.", + "SET_TIME_MSG": "⏰ Enter the timer time (e.g., 10:00).", + "TIMER_SET": "✅ Timer set for {}.", + "TIMER_INVALID": "❌ Invalid time format (use 10:00). Try again.", + "ROUTINE_TIME": "⏰ Wake up at {}, sleep at {}, total day time: {}.", + "IDEA_EXIST": "⚠️ Idea with this name already exists. Choose another.", + "SEND_MORNING_MSG": "☀️ Excellent start a day!\nYour morning routine:\n", + "SEND_EVENING_MSG": "🌙 Good evening, {}!", + "WELCOME_TO_FOCUS": "🎯 Welcome to the focus zone!", + "START_FOCUS_MSG": "🕒 Focus session started.", + "STOP_FOCUS_MSG": "Focus session stopped.\n⏳ Duration - {}m {}s.", + "SAVE_FOCUS_ZONE": "📝 Save the focus session? ", "SAVED_FOCUS_MSG": "Focus session saved. ✅", "NOT_SAVED_FOCUS_MSG": "Focus session not saved. ❌", "TITLE_FOCUS_ZONE_MSG": "Would you like to name this session? 📝", "NOT_FOUND_FOCUS_SESSION": "❗ Focus session start not found.", - "FOCUS_INVALID": "Invalid option for focus", - "FOCUS_TITLE_ASK": "Please provide the title for focus session.", + "FOCUS_INVALID": "⚠️ Invalid option for focus", + "FOCUS_TITLE_ASK": "✏️ Please provide the title for focus session.", "FOCUS_EXISTS": "❗ A focus session is already active.", "FOCUS_LIST_TITLE": "🧠 Your Focus Sessions", "NO_FOCUS_SESSIONS": "😕 No focus sessions found.", "DELETE_FOCUS_SESSION_MSG": "Provide a number of session which you want to delete.", - "REMIND_WORK_CANCEL": "Reminders for this task was deactivated", + "REMIND_WORK_CANCEL": "✅ Reminders for this task was deactivated", + "REMIND_WORK_START": "✅ Starting task - {}", + "FINISH_WORK_SESSION": "🎉 Congratulations you done task - '{}'.\n⏳ Time duration for this task was: {}m:{}s\nKeep going 🏆! ", + "BREAK_WORK_SESSION": "✅ You succesfully break the work session.\nComplete it when you will find the time :)", "LANGUAGE_ASK": ( "🌐 Choose your language. \n" "Select an option below. 📚" @@ -244,6 +255,7 @@ "LANGUAGE_OK": "✅ Language updated. Ready to proceed? 🚀", "LANGUAGE_INVALID": "❌ Invalid language choice. Try again. 🔢", "FOCUS_DELETED": "✅ Focus session #{} with the title \"{}\" has been successfully deleted.", + "WAKE_UP_MESSAGE": "☀️ Are you ready for new day - {}?", } } @@ -289,13 +301,18 @@ FOCUS_ZONE_START = "🟢 Start" FOCUS_ZONE_END = "🔴 Stop" -FOCUS_INLINE_YES = "Yes" -FOCUS_INLINE_NO = "No" -ALL_FOCUSES_BTN = "All Focuses" -DELETE_FOCUS = "Delete" +FOCUS_INLINE_YES = "✅ Yes" +FOCUS_INLINE_NO = "❌ No" +ALL_FOCUSES_BTN = "📝 All Focuses" +DELETE_FOCUS = "🗑️ Delete" -START_WORK_BTN = "Start" -CANCEL_WORK_BTN = "Cancel" +START_WORK_BTN = "✅ Start" +CANCEL_WORK_BTN = "❌ Cancel" + +START_DAY_BTN = "🚀 Start Day" + +STOP_WORK_SESSION = "✅ Finished" +STOP_WORK_CANCEL = "❌ Break Work Session" USER_FEEDBACK_MAIL_TEXT = """ 📬 New feedback received! @@ -333,38 +350,4 @@ def generate_daily_stats_message(language: str, created_ideas: int, completed_ta f"📝 Tasks added: {created_tasks}\n\n" "🔄 Updates daily at 00:00.\n\n" "Keep up the good work! 📝" - ) - -async def send_morning_message(bot: Bot, user_id: int): - language = await UserService.get_user_language(user_id) or "ENGLISH" - morning_routine = await RoutineService.get_user_routines(user_id, routine_type="morning") - - print(f"[INFO] - Sending morning routine to user with id, {user_id}") - if not morning_routine: - await bot.send_message( - user_id, - MESSAGES[language]['SEND_MORNING_MSG'].format("👤") + '\n' + MESSAGES[language]['NO_MORNING_ROUTINE'] - ) - return - - dividers: str = "\n" + ("-" * int(len(MESSAGES[language]['MORNING_ROUTINE_SHOW']) * 1.65)) - formatted_routine_items = "\n".join( - f"# {idx}. {routine['routine_name']}" - for idx, routine in enumerate(morning_routine, start=1) - ) - formatted_morning_routine = ( - MESSAGES[language]['MORNING_ROUTINE_SHOW'] + - dividers + - "\n" + - formatted_routine_items - ) - - await bot.send_message(user_id, formatted_morning_routine) - -async def send_evening_message(bot: Bot, user_id: int): - language = await UserService.get_user_language(user_id) or "ENGLISH" - print(f"[INFO] - Sending evening routine to user with id, {user_id}") - await bot.send_message( - user_id, - MESSAGES[language]['SEND_EVENING_MSG'].format("👤") - ) \ No newline at end of file + ) \ No newline at end of file diff --git a/telegram_bot_project/requirements.txt b/telegram_bot_project/requirements.txt new file mode 100644 index 0000000..92c4918 --- /dev/null +++ b/telegram_bot_project/requirements.txt @@ -0,0 +1,28 @@ +aiofiles==24.1.0 +aiogram==3.20.0.post0 +aiohappyeyeballs==2.6.1 +aiohttp==3.11.18 +aiosignal==1.3.2 +aiosmtplib==4.0.1 +annotated-types==0.7.0 +APScheduler==3.11.0 +asyncpg==0.30.0 +async-timeout==5.0.1 +attrs==25.3.0 +certifi==2025.6.15 +dotenv==0.9.9 +frozenlist==1.7.0 +greenlet==3.2.3 +idna==3.10 +magic-filter==1.0.12 +multidict==6.6.3 +propcache==0.3.2 +psycopg2==2.9.10 +pydantic==2.11.7 +pydantic-core==2.33.2 +python-dotenv==1.1.1 +SQLAlchemy==2.0.41 +typing-inspection==0.4.1 +tzdata==2025.2 +tzlocal==5.3.1 +yarl==1.20.1 diff --git a/telegram_bot_project/service/myday.py b/telegram_bot_project/service/myday.py index 2f27ef8..07dce99 100644 --- a/telegram_bot_project/service/myday.py +++ b/telegram_bot_project/service/myday.py @@ -1,5 +1,6 @@ # service/myday.py from sqlalchemy import text +from datetime import datetime from config import get_session from typing import Any, Optional @@ -52,7 +53,7 @@ async def get_today_stats(user_id: int) -> Optional[dict]: result: Any = await session.execute( text( """ - SELECT id, user_id, created_tasks, created_ideas, completed_tasks, stats_day_date + SELECT id, user_id, created_tasks, created_ideas, completed_tasks, wake_up_time, stats_day_date FROM daily_stats WHERE user_id = :user_id AND stats_day_date = CURRENT_DATE """ @@ -67,6 +68,7 @@ async def get_today_stats(user_id: int) -> Optional[dict]: "created_tasks": stat.created_tasks, "created_ideas": stat.created_ideas, "completed_tasks": stat.completed_tasks, + "wake_up_time": stat.wake_up_time, "stats_day_date": stat.stats_day_date, } return None @@ -166,3 +168,21 @@ async def decrement_completed_tasks(user_id: int) -> None: {"user_id": user_id} ) await session.commit() + + @staticmethod + async def add_wake_up_time(user_id: int, wake_time_str: str) -> None: + async with get_session() as session: + try: + wake_time = datetime.strptime(wake_time_str, "%H:%M").time() + except ValueError: + raise ValueError(f"Invalid time format: {wake_time_str}. Expected HH:MM") + + query_stats = text( + """ + UPDATE daily_stats + SET wake_up_time = :time + WHERE user_id = :user_id + """ + ) + await session.execute(query_stats, {"time": wake_time, "user_id": user_id}) + await session.commit() \ No newline at end of file diff --git a/telegram_bot_project/service/task.py b/telegram_bot_project/service/task.py index 7850c7c..4d5677a 100644 --- a/telegram_bot_project/service/task.py +++ b/telegram_bot_project/service/task.py @@ -211,3 +211,16 @@ async def update_started_status(task_id): await session.commit() return result.rowcount == 1 + + @staticmethod + async def add_task_complete_duration(task_id: int, duration: int) -> None: + async with get_session() as session: + query = text( + """ + UPDATE tasks + SET completion_duration = :duration + WHERE id = :task_id + """ + ) + await session.execute(query, {"task_id": task_id, "duration": duration}) + await session.commit() \ No newline at end of file diff --git a/telegram_bot_project/states.py b/telegram_bot_project/states.py index 3dab3c1..22c2fbf 100644 --- a/telegram_bot_project/states.py +++ b/telegram_bot_project/states.py @@ -22,4 +22,9 @@ class DialogStates(StatesGroup): feedback_message = State() provide_title_focusing = State() delete_focus = State() - start_work = State() \ No newline at end of file + start_work = State() + +user_task_start_time = {} +routine_start_time = {} +focus_times = {} +FOCUS_ZONE_START_TIME: int = None \ No newline at end of file