1
1
package main
2
2
3
3
import (
4
- "bytes"
5
- "encoding/json"
4
+ "context"
6
5
"flag"
7
6
"fmt"
8
7
"log"
9
- "net/http"
10
- "os"
11
- "os/exec"
12
- "strconv"
13
- "strings"
14
- "time"
15
8
)
16
9
17
10
var BuildVersion = "dev"
18
11
19
- const (
20
- highTempThreshold = 50.0
21
- sleepDuration = 10 * time .Minute
22
- )
23
-
24
- func parseJsonNumber (n json.Number ) (float64 , error ) {
25
- if n == "" {
26
- return 0 , nil
27
- }
28
- return strconv .ParseFloat (string (n ), 64 )
29
- }
30
-
31
- func renderMessage () (string , float64 , error ) {
32
- output , err := exec .Command ("sensors" , "-j" ).Output ()
33
- if err != nil {
34
- return "" , 0 , err
35
- }
36
-
37
- var sensorData map [string ]interface {}
38
- if e := json .Unmarshal (output , & sensorData ); e != nil {
39
- return "" , 0 , e
40
- }
41
-
42
- cpuData , ok := sensorData ["coretemp-isa-0000" ].(map [string ]interface {})
43
- if ! ok {
44
- return "" , 0 , fmt .Errorf ("failed to find CPU data" )
45
- }
46
-
47
- packageData , ok := cpuData ["Package id 0" ].(map [string ]interface {})
48
- if ! ok {
49
- return "" , 0 , fmt .Errorf ("failed to find Package id 0 data" )
50
- }
51
-
52
- mainTemp , err := parseJsonNumber (json .Number (fmt .Sprintf ("%v" , packageData ["temp1_input" ])))
53
- if err != nil {
54
- return "" , 0 , err
55
- }
56
-
57
- warning := ""
58
- if mainTemp > highTempThreshold {
59
- warning = "⚠️ High Temperature Warning"
60
- }
61
-
62
- var coreTemps []string
63
- coreTemps = append (coreTemps , fmt .Sprintf ("%.1f" , mainTemp ))
64
-
65
- for key , value := range cpuData {
66
- if strings .HasPrefix (key , "Core" ) {
67
- coreData , exist := value .(map [string ]interface {})
68
- if ! exist {
69
- continue
70
- }
71
- for k , v := range coreData {
72
- if strings .HasSuffix (k , "_input" ) {
73
- coreTemp , e := parseJsonNumber (json .Number (fmt .Sprintf ("%v" , v )))
74
- if e == nil {
75
- coreTemps = append (coreTemps , fmt .Sprintf ("%.1f" , coreTemp ))
76
- }
77
- break
78
- }
79
- }
80
- }
81
- }
82
-
83
- text := fmt .Sprintf ("%s\n CPU: %s°C\n " , warning , strings .Join (coreTemps , "°C | " ))
84
-
85
- if acpiData , exist := sensorData ["acpitz-acpi-0" ].(map [string ]interface {}); exist {
86
- if temp1 , tExist := acpiData ["temp1" ].(map [string ]interface {}); tExist {
87
- if acpiTemp , aExist := temp1 ["temp1_input" ]; aExist {
88
- acpiTempFloat , _ := parseJsonNumber (json .Number (fmt .Sprintf ("%v" , acpiTemp )))
89
- text += fmt .Sprintf ("\n ACPI: %.1f°C" , acpiTempFloat )
90
- }
91
- }
92
- }
93
-
94
- text += "\n "
95
-
96
- if nvmeData , exist := sensorData ["nvme-pci-0400" ].(map [string ]interface {}); exist {
97
- if composite , cExist := nvmeData ["Composite" ].(map [string ]interface {}); cExist {
98
- if nvmeTemp , nExist := composite ["temp1_input" ]; nExist {
99
- nvmeTempFloat , _ := parseJsonNumber (json .Number (fmt .Sprintf ("%v" , nvmeTemp )))
100
- text += fmt .Sprintf ("\n NVME: %.1f°C" , nvmeTempFloat )
101
- }
102
- }
103
- }
104
-
105
- return text , mainTemp , nil
106
- }
107
-
108
- func sendPVEStatusToTelegram (text string , temp float64 , conf * Config ) error {
109
- data := map [string ]interface {}{
110
- "chat_id" : conf .TargetId ,
111
- "text" : text ,
112
- "disable_notification" : temp <= 50 ,
113
- }
114
- jsonData , err := json .Marshal (data )
115
- if err != nil {
116
- return err
117
- }
118
-
119
- req , err := http .NewRequest ("POST" , fmt .Sprintf ("https://api.telegram.org/bot%s/sendMessage" , conf .Token ), bytes .NewBuffer (jsonData ))
120
- if err != nil {
121
- return err
122
- }
123
- req .Header .Set ("Content-Type" , "application/json" )
124
-
125
- resp , err := http .DefaultClient .Do (req )
126
- if err != nil {
127
- return err
128
- }
129
- defer resp .Body .Close ()
130
-
131
- if resp .StatusCode != http .StatusOK {
132
- return fmt .Errorf ("request failed with status code: %s" , resp .Status )
133
- }
134
-
135
- defer log .Printf ("%.1f°C" , temp )
136
-
137
- if ! conf .PinLatest {
138
- return nil
139
- }
140
-
141
- var respData struct {
142
- Result struct {
143
- MessageId int64 `json:"message_id"`
144
- }
145
- }
146
- err = json .NewDecoder (resp .Body ).Decode (& respData )
147
- if err != nil {
148
- return err
149
- }
150
-
151
- err = pinMessageToTelegram (conf , respData .Result .MessageId )
152
- if err != nil {
153
- return err
154
- }
155
-
156
- return nil
157
- }
158
-
159
- func pinMessageToTelegram (conf * Config , messageId int64 ) error {
160
- // unpin all messages
161
- data := map [string ]interface {}{
162
- "chat_id" : conf .TargetId ,
163
- }
164
- jsonData , err := json .Marshal (data )
165
- if err != nil {
166
- return err
167
- }
168
- req , err := http .NewRequest ("POST" , fmt .Sprintf ("https://api.telegram.org/bot%s/unpinAllChatMessages" , conf .Token ), bytes .NewBuffer (jsonData ))
169
- if err != nil {
170
- return err
171
- }
172
- req .Header .Set ("Content-Type" , "application/json" )
173
- resp , err := http .DefaultClient .Do (req )
174
- if err != nil {
175
- return err
176
- }
177
- defer resp .Body .Close ()
178
-
179
- // pin the message
180
- data = map [string ]interface {}{
181
- "chat_id" : conf .TargetId ,
182
- "message_id" : messageId ,
183
- "disable_notification" : true ,
184
- }
185
- jsonData , err = json .Marshal (data )
186
- if err != nil {
187
- return err
188
- }
189
- req , err = http .NewRequest ("POST" , fmt .Sprintf ("https://api.telegram.org/bot%s/pinChatMessage" , conf .Token ), bytes .NewBuffer (jsonData ))
190
- if err != nil {
191
- return err
192
- }
193
- req .Header .Set ("Content-Type" , "application/json" )
194
- resp , err = http .DefaultClient .Do (req )
195
- if err != nil {
196
- return err
197
- }
198
- defer resp .Body .Close ()
199
-
200
- return nil
201
- }
202
-
203
- func loadConfig (path string ) (* Config , error ) {
204
- if strings .HasPrefix (path , "http" ) {
205
- resp , err := http .Get (path )
206
- if err != nil {
207
- return nil , err
208
- }
209
- defer resp .Body .Close ()
210
- config := & Config {}
211
- err = json .NewDecoder (resp .Body ).Decode (config )
212
- if err != nil {
213
- return nil , err
214
- }
215
- return config , nil
216
- } else {
217
- file , err := os .ReadFile (path )
218
- if err != nil {
219
- log .Fatal (err )
220
- }
221
- config := & Config {}
222
- err = json .Unmarshal (file , config )
223
- if err != nil {
224
- return nil , err
225
- }
226
- return config , nil
227
- }
228
- }
229
-
230
- type Config struct {
231
- Token string `json:"token"`
232
- TargetId int64 `json:"target_id"`
233
- PinLatest bool `json:"pin_latest"`
234
- }
235
-
236
12
func main () {
237
- conf := flag .String ("config" , "config.json" , "config file path" )
13
+ config := flag .String ("config" , "config.json" , "config file path" )
238
14
help := flag .Bool ("help" , false , "show help" )
239
15
flag .Parse ()
240
16
@@ -243,20 +19,14 @@ func main() {
243
19
flag .Usage ()
244
20
return
245
21
}
246
-
247
- config , err := loadConfig (* conf )
22
+ conf , err := loadConfig (* config )
248
23
if err != nil {
249
24
log .Fatal (err )
250
25
}
251
- for {
252
- text , temp , e := renderMessage ()
253
- if e != nil {
254
- log .Printf ("Error rendering message: %v" , e )
255
- continue
256
- }
257
- if se := sendPVEStatusToTelegram (text , temp , config ); se != nil {
258
- log .Printf ("Error sending message to Telegram: %v" , se )
259
- }
260
- time .Sleep (sleepDuration )
26
+ app , err := NewApplication (conf )
27
+ if err != nil {
28
+ log .Fatal (err )
261
29
}
30
+ go app .startPolling (context .Background ())
31
+ app .startMonitoring (context .Background ())
262
32
}
0 commit comments