Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion sql/schema.sql
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@ CREATE TABLE users (
id BIGSERIAL PRIMARY KEY,
user_name VARCHAR(255) NOT NULL UNIQUE,
language lan NOT NULL DEFAULT 'ENGLISH',
register_date TIMESTAMP NOT NULL DEFAULT NOW()
register_date TIMESTAMP NOT NULL DEFAULT NOW(),
wake_time TIME DEFAULT NULL,
sleep_time TIME DEFAULT NULL
);

-- TABLE tasks
Expand Down
15 changes: 14 additions & 1 deletion telegram_bot_project/bot/buttons.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,4 +90,17 @@ def settings_menu_keyboard() -> ReplyKeyboardMarkup:
settings_menu_keyboard.keyboard.append([language_button, feedback_button])
settings_menu_keyboard.keyboard.append([main_menu_button])

return settings_menu_keyboard
return settings_menu_keyboard

def routine_time_keyboard() -> ReplyKeyboardMarkup:
routine_time_keyboard = ReplyKeyboardMarkup(keyboard=[], resize_keyboard=True, row_width=2)

wake_time_button = KeyboardButton(text=ROUTINE_SET_WAKE_BUTTON)
sleep_time_button = KeyboardButton(text=ROUTINE_SET_SLEEP_BUTTON)
my_routine_button = KeyboardButton(text=ROUTINE_MY_TIME)
settings_menu_button = KeyboardButton(text=BUTTON_SETTINGS)

routine_time_keyboard.keyboard.append([wake_time_button, sleep_time_button])
routine_time_keyboard.keyboard.append([my_routine_button, settings_menu_button])

return routine_time_keyboard
46 changes: 45 additions & 1 deletion telegram_bot_project/bot/commands.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
from time import sleep
from typing import Any, List
from aiogram import types
from aiogram.fsm.context import FSMContext

from bot.utills import format_date
from bot.utills import format_date, calculate_awake_hours
from messages import MESSAGES
from service.idea import IdeaService
from service.task import TaskService
Expand Down Expand Up @@ -235,3 +236,46 @@ async def setting_menu_command(message: types.Message):
await message.answer(MESSAGES['ENGLISH']['AUTHORIZATION_PROBLEM'])
else:
await message.answer(MESSAGES[language]['SETTINGS_MENU'], reply_markup=settings_menu_keyboard())

#Routine Time Command Handler
async def routine_time_command(message: types.Message):
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)

if not user_find:
await message.answer(MESSAGES['ENGLISH']['AUTHORIZATION_PROBLEM'])
else:
wake_time, sleep_time = await UserService.get_wake_and_sleep_times(user_id)
if not wake_time and not sleep_time:
await message.answer(MESSAGES[language]['ROUTINE_TIME_NOT'], reply_markup=routine_time_keyboard())
return
wake_time = None if not wake_time else wake_time.strftime("%H:%M")
sleep_time = None if not sleep_time else sleep_time.strftime("%H:%M")
awake_time = calculate_awake_hours(wake_time, sleep_time)
await message.answer(MESSAGES[language]['ROUTINE_TIME'].format(wake_time, sleep_time, awake_time), reply_markup=routine_time_keyboard())

#Set Wake Time Command Handler
async def set_wake_time_command(message: types.Message, state: FSMContext):
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)

if not user_find:
await message.answer(MESSAGES['ENGLISH']['AUTHORIZATION_PROBLEM'])
else:
await message.answer(MESSAGES[language]['SET_WAKE_TIME_MSG'])
await state.set_state(DialogStates.set_wake_time)


# Set Sleep Time Command Handler
async def set_sleep_time_command(message: types.Message, state: FSMContext):
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'])
else:
await message.answer(MESSAGES[language]['SET_SLEEP_TIME_MSG'])
await state.set_state(DialogStates.set_sleep_time)
74 changes: 72 additions & 2 deletions telegram_bot_project/bot/handlers.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
from aiogram.fsm.context import FSMContext
from aiogram.types import Message

from bot.buttons import get_idea_conf_keyboard, menu_reply_keyboard, idea_reply_keyboard, task_reply_keyboard, task_menu_keyboard
from bot.buttons import *
from messages import MESSAGES
from service.idea import IdeaService
from service.task import TaskService
from service.user import UserService
from states import DialogStates
from bot.utills import check_valid_time

async def process_idea_save(message: Message, state: FSMContext) -> None:
user_id = message.from_user.id
Expand Down Expand Up @@ -297,4 +298,73 @@ async def process_save_updated_task_name(message: Message, state: FSMContext):
print(f"--[INFO] User with id: {message.from_user.id} updated task name: {new_task_name}")
await TaskService.update_task(task_id=task_id, task_name=new_task_name)
await message.answer(MESSAGES["ENGLISH"]['UPDATE_TASK_SUCCESS'].format(user_number), reply_markup=task_menu_keyboard())
await state.clear()
await state.clear()

async def process_set_wake_time(message: Message, state: FSMContext):
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

new_wake_time = message.text.strip()
if not new_wake_time or not check_valid_time(new_wake_time):
await message.answer(
MESSAGES["ENGLISH"]['WAKE_TIME_INVALID'],
reply_markup=routine_time_keyboard()
)
return

try:
time_obj = datetime.strptime(new_wake_time, "%H:%M").time()
except ValueError:
await message.answer(
MESSAGES["ENGLISH"]['WAKE_TIME_INVALID'],
reply_markup=routine_time_keyboard()
)
return

print(f"--User with id: {user_id} set wake time to: {new_wake_time}")
await UserService.update_wake_time(user_id, time_obj)
await message.answer(
MESSAGES[language]['WAKE_TIME_SET'].format(new_wake_time),
reply_markup=routine_time_keyboard()
)
await state.clear()


async def process_set_sleep_time(message: Message, state: FSMContext):
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

new_sleep_time = message.text.strip()
if not new_sleep_time or not check_valid_time(new_sleep_time):
await message.answer(
MESSAGES["ENGLISH"]['SLEEP_TIME_INVALID'],
reply_markup=routine_time_keyboard()
)
return

try:
time_obj = datetime.strptime(new_sleep_time, "%H:%M").time()
except ValueError:
await message.answer(
MESSAGES["ENGLISH"]['SLEEP_TIME_INVALID'],
reply_markup=routine_time_keyboard()
)
return

print(f"--User with id: {user_id} set sleep time to: {new_sleep_time}")
await UserService.update_sleep_time(user_id, time_obj)
await message.answer(
MESSAGES[language]['SLEEP_TIME_SET'].format(new_sleep_time),
reply_markup=routine_time_keyboard()
)
await state.clear()
33 changes: 31 additions & 2 deletions telegram_bot_project/bot/utills.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,33 @@
from datetime import datetime
from datetime import datetime, timedelta, time

def format_date(dt: datetime) -> str:
return dt.strftime("%d.%m.%Y %H:%M")
return dt.strftime("%d.%m.%Y %H:%M")

def check_valid_time(time_str: str) -> bool:
try:
datetime.strptime(time_str, "%H:%M")
return True
except ValueError:
return False

def calculate_awake_hours(wake_time, sleep_time) -> str:
if isinstance(wake_time, str):
wake_time = datetime.strptime(wake_time, "%H:%M").time()
if isinstance(sleep_time, str):
sleep_time = datetime.strptime(sleep_time, "%H:%M").time()

if not wake_time or not sleep_time:
return "N/A"

today = datetime.today().date()
wake_dt = datetime.combine(today, wake_time)
sleep_dt = datetime.combine(today, sleep_time)

if sleep_dt <= wake_dt:
sleep_dt += timedelta(days=1)

awake_duration = sleep_dt - wake_dt
total_hours = awake_duration.seconds // 3600
total_minutes = (awake_duration.seconds % 3600) // 60

return f"{total_hours}h {total_minutes}m"
37 changes: 37 additions & 0 deletions telegram_bot_project/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from aiogram.types import CallbackQuery, Message
from aiogram.fsm.context import FSMContext
from sqlalchemy import lambda_stmt
from sqlalchemy.util import await_fallback

from bot.callbacks import (
start_callback_language,
Expand Down Expand Up @@ -112,6 +113,38 @@ async def settings(message: Message):
async def settings(message: Message):
await setting_menu_command(message)

@dp.message(Command("routinetime"))
async def waketime(message: Message):
await routine_time_command(message)

@dp.message(lambda m: m.text == SETTINGS_BUTTON_ROUTINE_TIME)
async def waketime(message: Message):
await routine_time_command(message)

@dp.message(lambda m: m.text == ROUTINE_MY_TIME)
async def my_routine_time(message: Message):
await routine_time_command(message)

@dp.message(Command("setwake"))
async def set_waketime(message: Message, state: FSMContext):
await set_wake_time_command(message, state)

@dp.message(Command("time"))
async def my_routine_time(message: Message):
await routine_time_command(message)

@dp.message(Command("setsleep"))
async def set_sleep_time(message: Message, state: FSMContext):
await set_sleep_time_command(message, state)

@dp.message(lambda m: m.text == ROUTINE_SET_SLEEP_BUTTON)
async def set_sleep_time(message: Message, state: FSMContext):
await set_sleep_time_command(message, state)

@dp.message(lambda m: m.text == ROUTINE_SET_WAKE_BUTTON)
async def set_waketime(message: Message, state: FSMContext):
await set_wake_time_command(message, state)

@dp.callback_query(F.data.in_({"lang_ua", "lang_en"}))
async def callback_language(callback_query: CallbackQuery):
await start_callback_language(callback_query)
Expand Down Expand Up @@ -148,6 +181,10 @@ async def process_fallback(message: Message, state: FSMContext):
await process_task_update(message, state)
elif current_state == DialogStates.update_task_name:
await process_save_updated_task_name(message, state)
elif current_state == DialogStates.set_wake_time:
await process_set_wake_time(message, state)
elif current_state == DialogStates.set_sleep_time:
await process_set_sleep_time(message, state)

# Main Function
async def main():
Expand Down
28 changes: 28 additions & 0 deletions telegram_bot_project/messages.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,18 @@
"UPDATE_TASK_NAME_MSG": "📝 Введіть нову назву завдання:",
"UPDATE_TASK_NAME_INVALID": "❌ Неправильна назва! 🔄",
"SETTINGS_MENU": "⚙️ Ласкаво просимо в Налаштування",
"ROUTINE_TIME": (
"⏰ Ваш розпорядок:\n"
"• Час підйому: {}\n"
"• Час сну: {}\n"
"• Загальна кількість годин неспання: {}"
),
"ROUTINE_TIME_NOT": "⚠️ Ви ще не встановили час підйому та сну. Налаштуйте їх, щоб тримати розпорядок під контролем!",
"ROUTINE_MENU": "🛠 Ласкаво просимо до налаштувань розпорядку! Тут ви можете встановити час підйому та сну.",
"SET_WAKE_TIME_MSG": "🌅 Введіть час підйому у 24-годинному форматі (ГГ:ХХ):",
"WAKE_TIME_SET": "✅ Час підйому успішно встановлено на {}.",
"SET_SLEEP_TIME_MSG": "🌙 Введіть час сну у 24-годинному форматі (ГГ:ХХ):",
"SLEEP_TIME_SET": "✅ Час сну успішно встановлено на {}.",
"LANGUAGE_ASK": (
"🌐 **Оберіть мову інтерфейсу:**\n"
"Натисніть кнопку нижче ⬇️"
Expand Down Expand Up @@ -115,6 +127,18 @@
"UPDATE_TASK_NAME_MSG": "📝 Enter the new task name:",
"UPDATE_TASK_NAME_INVALID": "❌ Invalid task name! 🔄",
"SETTINGS_MENU": "⚙️ Welcome to Settings",
"ROUTINE_TIME": (
"⏰ Your routine:\n"
"• Wake-up time: {}\n"
"• Sleep time: {}\n"
"• Total hours awake: {}"
),
"ROUTINE_TIME_NOT": "⚠️ You haven’t set your wake-up and sleep times yet. Set them to keep your routine on track!",
"ROUTINE_MENU": "🛠 Welcome to your Routine Settings! Customize your wake-up and sleep times here.",
"SET_WAKE_TIME_MSG": "🌅 Please enter your wake-up time in 24-hour format (HH:MM):",
"WAKE_TIME_SET": "✅ Wake-up time successfully set to {}.",
"SET_SLEEP_TIME_MSG": "🌙 Please enter your sleep time in 24-hour format (HH:MM):",
"SLEEP_TIME_SET": "✅ Sleep time successfully set to {}.",
"LANGUAGE_ASK": (
"🌐 **Choose your language:**\n"
"Tap a button below ⬇️"
Expand Down Expand Up @@ -152,3 +176,7 @@
SETTINGS_BUTTON_FEEDBACK = "💬 Feedback"
SETTINGS_BUTTON_ROUTINE = "✅ Routine"
SETTINGS_BUTTON_ROUTINE_TIME = "⏰ Routine Time"
ROUTINE_SET_WAKE_BUTTON = "🌅 Set Wake-Up Time"
ROUTINE_SET_SLEEP_BUTTON = "🌙 Set Sleep Time"
ROUTINE_MY_TIME = "🕒 My Routine"

50 changes: 48 additions & 2 deletions telegram_bot_project/service/user.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
from sqlalchemy import text
from config import get_session
from typing import Optional
from datetime import datetime

class UserService:
@staticmethod
Expand All @@ -14,8 +16,7 @@ async def create_user(user_id: int, user_name: str, language: str = "ENGLISH") -
{"id": user_id, "user_name": user_name, "language": language}
)
await session.commit()
inserted_id = result.scalar_one()
return inserted_id
return result.scalar_one()

@staticmethod
async def get_user_by_id(user_id: int):
Expand Down Expand Up @@ -49,3 +50,48 @@ async def get_user_language(user_id: int) -> str:
{"id": user_id}
)
return result.scalar_one_or_none()

@staticmethod
async def update_wake_time(user_id: int, wake_time: datetime) -> bool:
async with get_session() as session:
result = await session.execute(
text("""
UPDATE users
SET wake_time = :wake_time
WHERE id = :id
"""),
{"wake_time": wake_time, "id": user_id}
)
await session.commit()
return result.rowcount == 1

@staticmethod
async def get_wake_and_sleep_times(user_id: int) -> tuple[datetime, Optional[datetime]]:
async with get_session() as session:
result = await session.execute(
text("""
SELECT wake_time, sleep_time
FROM users
WHERE id = :id
"""),
{"id": user_id}
)

row = result.first()
if row:
return row.wake_time, row.sleep_time
return None, None

@staticmethod
async def update_sleep_time(user_id: int, sleep_time: datetime) -> bool:
async with get_session() as session:
result = await session.execute(
text("""
UPDATE users
SET sleep_time = :sleep_time
WHERE id = :id
"""),
{"sleep_time": sleep_time, "id": user_id}
)
await session.commit()
return result.rowcount == 1
Loading