all repos — cameraman @ e9af61f938b31b2bb9589e5bb079370941294021

notify.go (view raw)

  1package main
  2
  3import (
  4	"bytes"
  5	"encoding/json"
  6	"errors"
  7	"fmt"
  8	"io"
  9	"log"
 10	"net/http"
 11	"os"
 12	"time"
 13)
 14
 15const msgFormat = "*Giorno %02d/%02d*\n\n_%s_\n%s"
 16const msgFormatYear = "*Giorno %02d/%02d/%04d*\nšŸŽ‚ %d anni\n\n_%s_\n%s"
 17const baseUrl = "https://api.telegram.org/bot"
 18
 19type Response struct {
 20	Ok     bool   `json:"ok"`
 21	Result Result `json:"result"`
 22}
 23
 24type Result struct {
 25	MessageID int `json:"message_id"`
 26}
 27
 28var (
 29	NotificationWindow int
 30	SleepDuration      time.Duration
 31	telegramToken      string
 32	chatID             string
 33	threadID           string
 34)
 35
 36func sendPostRequest(url string, payload map[string]interface{}) (*http.Response, error) {
 37	jsonData, err := json.Marshal(payload)
 38	if err != nil {
 39		return nil, fmt.Errorf("failed to marshal payload: %v", err)
 40	}
 41
 42	req, err := http.NewRequest("POST", url, bytes.NewBuffer(jsonData))
 43	if err != nil {
 44		return nil, fmt.Errorf("failed to create new request: %v", err)
 45	}
 46	req.Header.Set("Content-Type", "application/json")
 47
 48	client := &http.Client{}
 49	resp, err := client.Do(req)
 50	if err != nil {
 51		return nil, fmt.Errorf("failed to send request: %v", err)
 52	}
 53
 54	return resp, nil
 55}
 56
 57func notifyTelegram(occurrence Occurrence) error {
 58	log.Println("Sending notification for occurrence", occurrence.ID)
 59	var message string
 60	if occurrence.Year != nil {
 61		years := time.Now().Year() - int(*occurrence.Year)
 62		message = fmt.Sprintf(msgFormatYear, occurrence.Day, occurrence.Month, *occurrence.Year, years, occurrence.Name, occurrence.Description)
 63	} else {
 64		message = fmt.Sprintf(msgFormat, occurrence.Day, occurrence.Month, occurrence.Name, occurrence.Description)
 65	}
 66
 67	url := fmt.Sprintf("%s%s/sendMessage", baseUrl, telegramToken)
 68
 69	// Create the payload
 70	payload := map[string]interface{}{
 71		"chat_id":              chatID,
 72		"text":                 message,
 73		"parse_mode":           "markdown",
 74		"message_thread_id":    threadID,
 75		"disable_notification": true,
 76	}
 77
 78	// Send the POST request
 79	resp, err := sendPostRequest(url, payload)
 80	if err != nil {
 81		log.Printf("Failed to send notification: %v", err)
 82		return err
 83	}
 84	defer resp.Body.Close()
 85
 86	bodyBytes, err := io.ReadAll(resp.Body)
 87	if err != nil {
 88		log.Printf("Failed to read response body: %v", err)
 89		return err
 90	}
 91
 92	log.Printf("Notification sent: %s, Response: %s", message, string(bodyBytes))
 93
 94	// Decode the JSON response
 95	var r Response
 96	if err := json.Unmarshal(bodyBytes, &r); err != nil {
 97		log.Printf("Failed to decode response: %v", err)
 98		return err
 99	}
100
101	if !r.Ok {
102		log.Printf("Telegram API returned an error: %v", r)
103		return fmt.Errorf("telegram API error: %v", r)
104	}
105
106	msgId := r.Result.MessageID
107
108	// Prepare the request to pin the message
109	url = fmt.Sprintf("%s%s/pinChatMessage", baseUrl, telegramToken)
110	payload = map[string]interface{}{
111		"chat_id":              chatID,
112		"message_id":           msgId,
113		"disable_notification": false,
114	}
115
116	// Send the POST request to pin the message
117	resp, err = sendPostRequest(url, payload)
118	if err != nil {
119		log.Printf("Failed to pin message: %v", err)
120		return err
121	}
122	defer resp.Body.Close()
123
124	bodyBytes, err = io.ReadAll(resp.Body)
125	if err != nil {
126		log.Printf("Failed to read response body: %v", err)
127		return err
128	}
129
130	log.Printf("Message pinned: %s, Response: %s", message, string(bodyBytes))
131	return nil
132}
133
134func resetNotifications() {
135	if err := db.Model(&Occurrence{}).Where("notified = ?", true).Update("notified", false).Error; err != nil {
136		log.Printf("Failed to reset notifications: %v", err)
137	} else {
138		log.Println("Notifications have been reset for the new year.")
139	}
140}
141
142func initNotifications() error {
143	telegramToken = os.Getenv("TELEGRAM_BOT_TOKEN")
144	chatID = os.Getenv("TELEGRAM_CHAT_ID")
145	threadID = os.Getenv("TELEGRAM_THREAD_ID")
146
147	if telegramToken == "" || chatID == "" {
148		log.Println("Warning: you should set your Telegram Bot token and chat id in .env, otherwise you won't get notifications!")
149		return errors.New("empty telegramToken or chatId")
150	}
151	return nil
152}
153
154func CheckOccurrences() {
155	err := initNotifications()
156	if err != nil {
157		log.Println(err.Error())
158		return
159	}
160
161	for {
162		log.Println("Checking for new occurrences.")
163		now := time.Now()
164		today := time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, time.Local)
165		endWindow := today.AddDate(0, 0, NotificationWindow)
166
167		var occurrences []Occurrence
168		db.Where("notified = ? AND ((month = ? AND day >= ?) OR (month = ? AND day <= ?))",
169			false, today.Month(), today.Day(), endWindow.Month(), endWindow.Day()).Find(&occurrences)
170
171		for _, occurrence := range occurrences {
172			occurrenceDate := time.Date(today.Year(), time.Month(occurrence.Month), int(occurrence.Day), 0, 0, 0, 0, time.Local)
173			if occurrenceDate.Before(today) || occurrenceDate.After(endWindow) || !occurrence.Notify || occurrence.Notified {
174				continue
175			}
176
177			err := notifyTelegram(occurrence)
178			if err != nil {
179				log.Println(err.Error())
180				return
181			}
182			occurrence.Notified = true
183			db.Save(&occurrence)
184		}
185
186		// Check if New Year's Eve is within the next sleep cycle
187		nextCheck := now.Add(SleepDuration)
188		if (now.Month() == 12 && now.Day() == 31) && (nextCheck.Month() == 1 && nextCheck.Day() == 1) {
189			resetNotifications()
190		}
191
192		time.Sleep(SleepDuration)
193	}
194}