196 lines
4.7 KiB
Go
196 lines
4.7 KiB
Go
package scheduler
|
||
|
||
import (
|
||
"fmt"
|
||
"log"
|
||
"time"
|
||
|
||
"github.com/robfig/cron/v3"
|
||
"github.com/daniil/homelab-api/internal/bot"
|
||
"github.com/daniil/homelab-api/internal/model"
|
||
"github.com/daniil/homelab-api/internal/repository"
|
||
)
|
||
|
||
type Scheduler struct {
|
||
cron *cron.Cron
|
||
bot *bot.Bot
|
||
userRepo *repository.UserRepository
|
||
taskRepo *repository.TaskRepository
|
||
habitRepo *repository.HabitRepository
|
||
}
|
||
|
||
func New(b *bot.Bot, userRepo *repository.UserRepository, taskRepo *repository.TaskRepository, habitRepo *repository.HabitRepository) *Scheduler {
|
||
return &Scheduler{
|
||
cron: cron.New(),
|
||
bot: b,
|
||
userRepo: userRepo,
|
||
taskRepo: taskRepo,
|
||
habitRepo: habitRepo,
|
||
}
|
||
}
|
||
|
||
func (s *Scheduler) Start() {
|
||
if s.bot == nil || s.bot.GetAPI() == nil {
|
||
log.Println("Scheduler: bot not configured, skipping")
|
||
return
|
||
}
|
||
|
||
// Run every minute
|
||
s.cron.AddFunc("* * * * *", s.checkNotifications)
|
||
|
||
s.cron.Start()
|
||
log.Println("Scheduler started")
|
||
}
|
||
|
||
func (s *Scheduler) Stop() {
|
||
s.cron.Stop()
|
||
}
|
||
|
||
func (s *Scheduler) checkNotifications() {
|
||
users, err := s.userRepo.GetUsersWithNotifications()
|
||
if err != nil {
|
||
log.Printf("Scheduler: error getting users: %v", err)
|
||
return
|
||
}
|
||
|
||
for _, user := range users {
|
||
go s.checkUserNotifications(user)
|
||
}
|
||
}
|
||
|
||
func (s *Scheduler) checkUserNotifications(user model.User) {
|
||
if !user.TelegramChatID.Valid {
|
||
return
|
||
}
|
||
|
||
chatID := user.TelegramChatID.Int64
|
||
|
||
// Get user timezone
|
||
loc, err := time.LoadLocation(user.Timezone)
|
||
if err != nil {
|
||
loc = time.FixedZone("Europe/Moscow", 3*60*60)
|
||
}
|
||
|
||
now := time.Now().In(loc)
|
||
currentTime := now.Format("15:04")
|
||
today := now.Format("2006-01-02")
|
||
weekday := int(now.Weekday())
|
||
|
||
// 1. Morning briefing at 09:00
|
||
if currentTime == "09:00" {
|
||
s.sendMorningBriefing(user.ID, chatID, loc)
|
||
}
|
||
|
||
// 2. Task reminders
|
||
s.checkTaskReminders(user.ID, chatID, currentTime, today)
|
||
|
||
// 3. Habit reminders
|
||
s.checkHabitReminders(user.ID, chatID, currentTime, weekday)
|
||
}
|
||
|
||
func (s *Scheduler) sendMorningBriefing(userID, chatID int64, loc *time.Location) {
|
||
tasks, err := s.taskRepo.GetTodayTasks(userID)
|
||
if err != nil {
|
||
log.Printf("Scheduler: error getting tasks for user %d: %v", userID, err)
|
||
return
|
||
}
|
||
|
||
habits, err := s.habitRepo.ListByUser(userID, false)
|
||
if err != nil {
|
||
log.Printf("Scheduler: error getting habits for user %d: %v", userID, err)
|
||
return
|
||
}
|
||
|
||
// Filter habits for today
|
||
weekday := int(time.Now().In(loc).Weekday())
|
||
var todayHabits int
|
||
for _, habit := range habits {
|
||
if habit.Frequency == "daily" {
|
||
todayHabits++
|
||
} else {
|
||
for _, day := range habit.TargetDays {
|
||
if day == weekday {
|
||
todayHabits++
|
||
break
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
if len(tasks) == 0 && todayHabits == 0 {
|
||
return // Nothing to report
|
||
}
|
||
|
||
text := "☀️ <b>Доброе утро!</b>\n\n"
|
||
|
||
if len(tasks) > 0 {
|
||
text += fmt.Sprintf("📋 Задач на сегодня: <b>%d</b>\n", len(tasks))
|
||
for i, task := range tasks {
|
||
if i >= 3 {
|
||
text += fmt.Sprintf(" ... и ещё %d\n", len(tasks)-3)
|
||
break
|
||
}
|
||
text += fmt.Sprintf(" • %s %s\n", task.Icon, task.Title)
|
||
}
|
||
text += "\n"
|
||
}
|
||
|
||
if todayHabits > 0 {
|
||
text += fmt.Sprintf("🎯 Привычек: <b>%d</b>\n", todayHabits)
|
||
}
|
||
|
||
text += "\n/tasks — посмотреть задачи\n/habits — посмотреть привычки"
|
||
|
||
s.bot.SendMessage(chatID, text)
|
||
}
|
||
|
||
func (s *Scheduler) checkTaskReminders(userID, chatID int64, currentTime, today string) {
|
||
tasks, err := s.taskRepo.GetTasksWithReminder(currentTime, today)
|
||
if err != nil {
|
||
log.Printf("Scheduler: error getting task reminders: %v", err)
|
||
return
|
||
}
|
||
|
||
for _, task := range tasks {
|
||
if task.UserID != userID {
|
||
continue
|
||
}
|
||
|
||
text := fmt.Sprintf("⏰ <b>Напоминание о задаче:</b>\n\n%s <b>%s</b>", task.Icon, task.Title)
|
||
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)
|
||
}
|
||
}
|
||
|
||
func (s *Scheduler) checkHabitReminders(userID, chatID int64, currentTime string, weekday int) {
|
||
habits, err := s.habitRepo.GetHabitsWithReminder(currentTime, weekday)
|
||
if err != nil {
|
||
log.Printf("Scheduler: error getting habit reminders: %v", err)
|
||
return
|
||
}
|
||
|
||
for _, habit := range habits {
|
||
if habit.UserID != userID {
|
||
continue
|
||
}
|
||
|
||
// Check if already completed today
|
||
completed, _ := s.habitRepo.IsHabitCompletedToday(habit.ID, userID)
|
||
if completed {
|
||
continue
|
||
}
|
||
|
||
text := fmt.Sprintf("⏰ <b>Напоминание о привычке:</b>\n\n%s <b>%s</b>", habit.Icon, habit.Name)
|
||
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)
|
||
}
|
||
}
|