add more options and better safety checks
BiRabittoh andronacomarco@gmail.com
Thu, 30 May 2024 11:01:46 +0200
4 files changed,
121 insertions(+),
48 deletions(-)
M
.env.example
→
.env.example
@@ -1,3 +1,7 @@
TELEGRAM_BOT_TOKEN=your:bot-token TELEGRAM_CHAT_ID=your_chat_id TELEGRAM_THREAD_ID=your_thread_id + +PORT=3000 +HOURS_BETWEEN_CHECKS=1 +DAYS_BEFORE_NOTIFICATION=3
M
handlers.go
→
handlers.go
@@ -35,37 +35,49 @@ c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return } + var isDateValid = false // Validate date - if err := validateDate(input.Month, input.Day); err != nil { - c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) - return + if input.Day != 0 || input.Month != 0 { + if err := validateDate(input.Month, input.Day); err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + isDateValid = true } var occurrence Occurrence if input.ID != 0 { - if err := db.First(&occurrence, input.ID).Error; err == nil { - // Update existing record with new values - if input.Month != 0 { - occurrence.Month = input.Month - } - if input.Day != 0 { - occurrence.Day = input.Day - } - if input.Name != "" { - occurrence.Name = input.Name - } - if input.Description != "" { - occurrence.Description = input.Description - } - occurrence.Notify = input.Notify - db.Save(&occurrence) - c.JSON(http.StatusOK, occurrence) + if err := db.First(&occurrence, input.ID).Error; err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } + + // Update existing record with new values + if isDateValid { + occurrence.Month = input.Month + occurrence.Day = input.Day + } + if input.Name != "" { + occurrence.Name = input.Name + } + if input.Description != "" { + occurrence.Description = input.Description + } + occurrence.Notify = input.Notify + occurrence.Notified = input.Notified + db.Save(&occurrence) + c.JSON(http.StatusOK, occurrence) + return } // Create a new record if no existing record is found + if !isDateValid { + c.JSON(http.StatusBadRequest, gin.H{"error": "invalid date"}) + return + } + occurrence = input + occurrence.Notified = false db.Create(&occurrence) c.JSON(http.StatusOK, occurrence) }
M
main.go
→
main.go
@@ -1,9 +1,11 @@
package main import ( + "fmt" "log" "os" "path" + "strconv" "time" "github.com/gin-gonic/gin"@@ -25,11 +27,17 @@ CreatedAt time.Time `json:"-"`
UpdatedAt time.Time `json:"-"` } -var db *gorm.DB +const ( + dataDir = "data" + dbFile = "occurrences.db" + defaultNotificationWindow = 3 + defaultSleepDuration = 1 + defaultPort = 3000 +) -const ( - dataDir = "data" - dbFile = "occurrences.db" +var ( + db *gorm.DB + port int ) func initDB() {@@ -50,9 +58,30 @@ db.AutoMigrate(&Occurrence{})
} func loadEnv() { - if err := godotenv.Load(); err != nil { + err := godotenv.Load() + if err != nil { log.Println("Error loading .env file") } + + NotificationWindow, err = strconv.Atoi(os.Getenv("DAYS_BEFORE_NOTIFICATION")) + if err != nil { + NotificationWindow = defaultNotificationWindow + } + log.Println("Notification window (days):", NotificationWindow) + + loadedSleepDuration, err := strconv.Atoi(os.Getenv("HOURS_BETWEEN_CHECKS")) + if err != nil { + SleepDuration = defaultSleepDuration * time.Hour + } else { + SleepDuration = time.Duration(loadedSleepDuration) * time.Hour + } + log.Println("Sleep duration:", SleepDuration) + + port, err = strconv.Atoi(os.Getenv("PORT")) + if err != nil { + port = defaultPort + } + log.Println("Port:", port) } func main() {@@ -68,5 +97,5 @@ router.GET("/occurrences", getOccurrences)
router.DELETE("/occurrences/:id", deleteOccurrence) router.GET("/", ShowIndexPage) - router.Run(":3000") + router.Run(fmt.Sprintf(":%d", port)) }
M
notify.go
→
notify.go
@@ -1,6 +1,7 @@
package main import ( + "errors" "fmt" "log" "os"@@ -9,14 +10,18 @@
"github.com/go-resty/resty/v2" ) -var notificationWindow = 3 +var ( + NotificationWindow int + SleepDuration time.Duration -func notifyTelegram(occurrence Occurrence) { - client := resty.New() - telegramToken := os.Getenv("TELEGRAM_BOT_TOKEN") - chatID := os.Getenv("TELEGRAM_CHAT_ID") - threadID := os.Getenv("TELEGRAM_THREAD_ID") + notificationClient *resty.Client + telegramToken string + chatID string + threadID string +) +func notifyTelegram(occurrence Occurrence) error { + log.Println("Sending notification for occurrence", occurrence.ID) message := fmt.Sprintf("*Giorno %02d/%02d*:\n\n_%s_\n%s", occurrence.Day, occurrence.Month, occurrence.Name, occurrence.Description)@@ -24,23 +29,24 @@ url := fmt.Sprintf("https://api.telegram.org/bot%s/sendMessage", telegramToken)
// Create the payload payload := map[string]interface{}{ - "message_thread_id": threadID, "chat_id": chatID, "text": message, "parse_mode": "markdown", + "message_thread_id": threadID, } // Send the POST request - resp, err := client.R(). + resp, err := notificationClient.R(). SetHeader("Content-Type", "application/json"). SetBody(payload). Post(url) if err != nil { log.Printf("Failed to send notification: %v", err) - return + return err } log.Printf("Notification sent: %s, Response: %s", message, resp) + return nil } func resetNotifications() {@@ -51,36 +57,58 @@ log.Println("Notifications have been reset for the new year.")
} } +func initNotifications() error { + notificationClient = resty.New() + + telegramToken = os.Getenv("TELEGRAM_BOT_TOKEN") + chatID = os.Getenv("TELEGRAM_CHAT_ID") + threadID = os.Getenv("TELEGRAM_THREAD_ID") + + if telegramToken == "" || chatID == "" { + log.Println("Warning: you should set your Telegram Bot token and chat id in .env, otherwise you won't get notifications!") + return errors.New("empty telegramToken or chatId") + } + return nil +} + func CheckOccurrences() { - const sleepDuration = 12 * time.Hour + err := initNotifications() + if err != nil { + log.Println(err.Error()) + return + } for { + log.Println("Checking for new occurrences.") now := time.Now() + today := time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, time.Local) + endWindow := today.AddDate(0, 0, NotificationWindow) + var occurrences []Occurrence - endWindow := now.AddDate(0, 0, notificationWindow) - db.Where("notified = ? AND ((month = ? AND day >= ?) OR (month = ? AND day <= ?))", - false, now.Month(), now.Day(), endWindow.Month(), endWindow.Day()).Find(&occurrences) + false, today.Month(), today.Day(), endWindow.Month(), endWindow.Day()).Find(&occurrences) for _, occurrence := range occurrences { - occurrenceDate := time.Date(now.Year(), time.Month(occurrence.Month), occurrence.Day, 0, 0, 0, 0, time.Local) - if occurrenceDate.Before(now) || occurrenceDate.After(endWindow) { + occurrenceDate := time.Date(today.Year(), time.Month(occurrence.Month), occurrence.Day, 0, 0, 0, 0, time.Local) + if occurrenceDate.Before(today) || occurrenceDate.After(endWindow) || !occurrence.Notify || occurrence.Notified { continue } - if occurrence.Notify { - notifyTelegram(occurrence) - occurrence.Notified = true - db.Save(&occurrence) + err := notifyTelegram(occurrence) + if err != nil { + log.Println(err.Error()) + return } + occurrence.Notified = true + db.Save(&occurrence) } // Check if New Year's Eve is within the next sleep cycle - nextCheck := now.Add(sleepDuration) - if now.Month() == 12 && now.Day() == 31 || (nextCheck.Month() == 1 && nextCheck.Day() == 1) { + nextCheck := now.Add(SleepDuration) + if (now.Month() == 12 && now.Day() == 31) && (nextCheck.Month() == 1 && nextCheck.Day() == 1) { resetNotifications() } - time.Sleep(sleepDuration) + time.Sleep(SleepDuration) } }