feat: Telegram bot, notifications, profile settings, 365-day refresh tokens
This commit is contained in:
@@ -6,6 +6,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/robfig/cron/v3"
|
||||
tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api/v5"
|
||||
"github.com/daniil/homelab-api/internal/bot"
|
||||
"github.com/daniil/homelab-api/internal/model"
|
||||
"github.com/daniil/homelab-api/internal/repository"
|
||||
@@ -76,15 +77,30 @@ func (s *Scheduler) checkUserNotifications(user model.User) {
|
||||
today := now.Format("2006-01-02")
|
||||
weekday := int(now.Weekday())
|
||||
|
||||
// 1. Morning briefing at 09:00
|
||||
if currentTime == "09:00" {
|
||||
// Get user's reminder times
|
||||
morningTime := "09:00"
|
||||
if user.MorningReminderTime.Valid && len(user.MorningReminderTime.String) >= 5 {
|
||||
morningTime = user.MorningReminderTime.String[:5]
|
||||
}
|
||||
eveningTime := "21:00"
|
||||
if user.EveningReminderTime.Valid && len(user.EveningReminderTime.String) >= 5 {
|
||||
eveningTime = user.EveningReminderTime.String[:5]
|
||||
}
|
||||
|
||||
// 1. Morning briefing
|
||||
if currentTime == morningTime {
|
||||
s.sendMorningBriefing(user.ID, chatID, loc)
|
||||
}
|
||||
|
||||
// 2. Task reminders
|
||||
// 2. Evening summary
|
||||
if currentTime == eveningTime {
|
||||
s.sendEveningSummary(user.ID, chatID, loc)
|
||||
}
|
||||
|
||||
// 3. Task reminders
|
||||
s.checkTaskReminders(user.ID, chatID, currentTime, today)
|
||||
|
||||
// 3. Habit reminders
|
||||
// 4. Habit reminders
|
||||
s.checkHabitReminders(user.ID, chatID, currentTime, weekday)
|
||||
}
|
||||
|
||||
@@ -139,7 +155,125 @@ func (s *Scheduler) sendMorningBriefing(userID, chatID int64, loc *time.Location
|
||||
text += fmt.Sprintf("🎯 Привычек: <b>%d</b>\n", todayHabits)
|
||||
}
|
||||
|
||||
text += "\n/tasks — посмотреть задачи\n/habits — посмотреть привычки"
|
||||
text += "\nИспользуй /tasks и /habits для просмотра"
|
||||
|
||||
s.bot.SendMessage(chatID, text)
|
||||
}
|
||||
|
||||
func (s *Scheduler) sendEveningSummary(userID, chatID int64, loc *time.Location) {
|
||||
// Get tasks
|
||||
tasks, err := s.taskRepo.GetTodayTasks(userID)
|
||||
if err != nil {
|
||||
log.Printf("Scheduler: error getting tasks for user %d: %v", userID, err)
|
||||
return
|
||||
}
|
||||
|
||||
// Count completed/incomplete tasks
|
||||
var completedTasks, incompleteTasks int
|
||||
for _, task := range tasks {
|
||||
if task.CompletedAt.Valid {
|
||||
completedTasks++
|
||||
} else {
|
||||
incompleteTasks++
|
||||
}
|
||||
}
|
||||
|
||||
// Get habits
|
||||
habits, err := s.habitRepo.ListByUser(userID, false)
|
||||
if err != nil {
|
||||
log.Printf("Scheduler: error getting habits for user %d: %v", userID, err)
|
||||
return
|
||||
}
|
||||
|
||||
// Filter and count today's habits
|
||||
weekday := int(time.Now().In(loc).Weekday())
|
||||
var completedHabits, incompleteHabits int
|
||||
var incompleteHabitNames []string
|
||||
|
||||
for _, habit := range habits {
|
||||
isToday := false
|
||||
if habit.Frequency == "daily" {
|
||||
isToday = true
|
||||
} else {
|
||||
for _, day := range habit.TargetDays {
|
||||
if day == weekday {
|
||||
isToday = true
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if isToday {
|
||||
completed, _ := s.habitRepo.IsHabitCompletedToday(habit.ID, userID)
|
||||
if completed {
|
||||
completedHabits++
|
||||
} else {
|
||||
incompleteHabits++
|
||||
incompleteHabitNames = append(incompleteHabitNames, habit.Icon+" "+habit.Name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Don't send if nothing to report
|
||||
totalTasks := completedTasks + incompleteTasks
|
||||
totalHabits := completedHabits + incompleteHabits
|
||||
if totalTasks == 0 && totalHabits == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
text := "🌙 <b>Итоги дня</b>\n\n"
|
||||
|
||||
// Tasks summary
|
||||
if totalTasks > 0 {
|
||||
text += "📋 <b>Задачи:</b>\n"
|
||||
text += fmt.Sprintf(" ✅ Выполнено: %d\n", completedTasks)
|
||||
text += fmt.Sprintf(" ⬜ Осталось: %d\n", incompleteTasks)
|
||||
text += "\n"
|
||||
}
|
||||
|
||||
// Habits summary
|
||||
if totalHabits > 0 {
|
||||
text += "🎯 <b>Привычки:</b>\n"
|
||||
text += fmt.Sprintf(" ✅ Выполнено: %d\n", completedHabits)
|
||||
text += fmt.Sprintf(" ⬜ Осталось: %d\n", incompleteHabits)
|
||||
|
||||
// Show incomplete habits
|
||||
if len(incompleteHabitNames) > 0 && len(incompleteHabitNames) <= 5 {
|
||||
text += "\n Не выполнены:\n"
|
||||
for _, name := range incompleteHabitNames {
|
||||
text += fmt.Sprintf(" • %s\n", name)
|
||||
}
|
||||
}
|
||||
text += "\n"
|
||||
}
|
||||
|
||||
// Motivational message
|
||||
taskPercent := 0
|
||||
if totalTasks > 0 {
|
||||
taskPercent = completedTasks * 100 / totalTasks
|
||||
}
|
||||
habitPercent := 0
|
||||
if totalHabits > 0 {
|
||||
habitPercent = completedHabits * 100 / totalHabits
|
||||
}
|
||||
|
||||
avgPercent := (taskPercent + habitPercent) / 2
|
||||
if totalTasks == 0 {
|
||||
avgPercent = habitPercent
|
||||
}
|
||||
if totalHabits == 0 {
|
||||
avgPercent = taskPercent
|
||||
}
|
||||
|
||||
if avgPercent == 100 {
|
||||
text += "🎉 Отличный день! Всё выполнено!"
|
||||
} else if avgPercent >= 75 {
|
||||
text += "👍 Хороший день! Почти всё сделано."
|
||||
} else if avgPercent >= 50 {
|
||||
text += "💪 Неплохо! Завтра будет лучше."
|
||||
} else {
|
||||
text += "🌱 Бывает. Завтра новый день!"
|
||||
}
|
||||
|
||||
s.bot.SendMessage(chatID, text)
|
||||
}
|
||||
@@ -160,9 +294,15 @@ func (s *Scheduler) checkTaskReminders(userID, chatID int64, currentTime, today
|
||||
if task.Description != "" {
|
||||
text += fmt.Sprintf("\n<i>%s</i>", task.Description)
|
||||
}
|
||||
text += fmt.Sprintf("\n\n/done_%d — отметить выполненной", task.ID)
|
||||
|
||||
s.bot.SendMessage(chatID, text)
|
||||
keyboard := tgbotapi.NewInlineKeyboardMarkup(
|
||||
tgbotapi.NewInlineKeyboardRow(
|
||||
tgbotapi.NewInlineKeyboardButtonData("✅ Выполнено", fmt.Sprintf("donetask_%d", task.ID)),
|
||||
tgbotapi.NewInlineKeyboardButtonData("⏰ +30 мин", fmt.Sprintf("snoozetask_%d", task.ID)),
|
||||
),
|
||||
)
|
||||
|
||||
s.bot.SendMessageWithKeyboard(chatID, text, &keyboard)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -188,8 +328,14 @@ func (s *Scheduler) checkHabitReminders(userID, chatID int64, currentTime string
|
||||
if habit.Description != "" {
|
||||
text += fmt.Sprintf("\n<i>%s</i>", habit.Description)
|
||||
}
|
||||
text += fmt.Sprintf("\n\n/check_%d — отметить выполненной", habit.ID)
|
||||
|
||||
s.bot.SendMessage(chatID, text)
|
||||
keyboard := tgbotapi.NewInlineKeyboardMarkup(
|
||||
tgbotapi.NewInlineKeyboardRow(
|
||||
tgbotapi.NewInlineKeyboardButtonData("✅ Выполнено", fmt.Sprintf("checkhabit_%d", habit.ID)),
|
||||
tgbotapi.NewInlineKeyboardButtonData("⏰ +30 мин", fmt.Sprintf("snoozehabit_%d", habit.ID)),
|
||||
),
|
||||
)
|
||||
|
||||
s.bot.SendMessageWithKeyboard(chatID, text, &keyboard)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user