package main import ( "encoding/json" "fmt" "log" "net/http" "os" "strings" "time" tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api/v5" ) type Task struct { ID string `json:"id"` Content string `json:"content"` Due *struct { Date string `json:"date"` } `json:"due"` } var ( todoistToken = os.Getenv("TODOIST_TOKEN") telegramToken = os.Getenv("TELEGRAM_TOKEN") chatID = os.Getenv("CHAT_ID") bot *tgbotapi.BotAPI overdueTasks = map[string]bool{} ) func getOverdueTasks() []Task { log.Println("[INFO] Запрашиваю задачи из Todoist...") req, _ := http.NewRequest("GET", "https://api.todoist.com/rest/v2/tasks", nil) req.Header.Set("Authorization", "Bearer "+todoistToken) resp, err := http.DefaultClient.Do(req) if err != nil { log.Println("Ошибка запроса Todoist:", err) return nil } defer resp.Body.Close() var tasks []Task if err := json.NewDecoder(resp.Body).Decode(&tasks); err != nil { log.Println("Ошибка парсинга задач:", err) return nil } now := time.Now().Format("2006-01-02T15:04") var overdue []Task for _, task := range tasks { if task.Due != nil && strings.Compare(task.Due.Date, now) < 0 { overdue = append(overdue, task) } } return overdue } func closeTask(taskID string) { url := fmt.Sprintf("https://api.todoist.com/rest/v2/tasks/%s/close", taskID) req, _ := http.NewRequest("POST", url, nil) req.Header.Set("Authorization", "Bearer "+todoistToken) http.DefaultClient.Do(req) } func getRandomReminderMessage(task Task) string { reminderMessages := []string{ "⏰ Ну что, сделал уже?! Задача: %s", "⏰ Эй, не забыл про задачу? %s", "⏰ Напоминаю о задаче: %s", "⏰ Задача все еще ждет выполнения: %s", "⏰ Пора бы уже сделать: %s", } randomIndex := time.Now().UnixNano() % int64(len(reminderMessages)) return fmt.Sprintf(reminderMessages[randomIndex], task.Content) } func isQuietHours() bool { // Get current time in CET timezone cet, err := time.LoadLocation("Europe/Paris") // Paris is in CET if err != nil { log.Println("Ошибка загрузки таймзоны:", err) return false // If we can't determine the time, assume it's not quiet hours } now := time.Now().In(cet) hour := now.Hour() // Quiet hours are from 23:00 to 08:00 return hour >= 23 || hour < 8 } func spamCheck() { // Don't send messages during quiet hours if isQuietHours() { log.Println("[INFO] Тихий час (23:00-08:00 CET). Уведомления отключены.") return } tasks := getOverdueTasks() for _, task := range tasks { if !overdueTasks[task.ID] { msg := tgbotapi.NewMessage(toInt64(chatID), fmt.Sprintf("🔥 Просрочено: %s", task.Content)) button := tgbotapi.NewInlineKeyboardMarkup( tgbotapi.NewInlineKeyboardRow( tgbotapi.NewInlineKeyboardButtonData("✅ Сделал", task.ID), ), ) msg.ReplyMarkup = button bot.Send(msg) overdueTasks[task.ID] = true } else { reminderMessage := getRandomReminderMessage(task) bot.Send(tgbotapi.NewMessage(toInt64(chatID), reminderMessage)) } } } func toInt64(s string) int64 { var n int64 fmt.Sscan(s, &n) return n } func main() { var err error bot, err = tgbotapi.NewBotAPI(telegramToken) if err != nil { log.Fatal("Ошибка запуска бота:", err) } log.Println("[INFO] Бот запущен") u := tgbotapi.NewUpdate(0) u.Timeout = 60 updates := bot.GetUpdatesChan(u) ticker := time.NewTicker(1 * time.Hour) go func() { spamCheck() for range ticker.C { spamCheck() } }() for update := range updates { if update.CallbackQuery != nil { taskID := update.CallbackQuery.Data closeTask(taskID) bot.Send(tgbotapi.NewMessage(update.CallbackQuery.Message.Chat.ID, "✅ Задача закрыта!")) delete(overdueTasks, taskID) } } }