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}