149 lines
3.1 KiB
Go
149 lines
3.1 KiB
Go
package metrics
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"os"
|
|
"sync"
|
|
"time"
|
|
)
|
|
|
|
type CommandStats struct {
|
|
AllTime int `json:"all_time"`
|
|
Last30 [30]int `json:"last_30"`
|
|
Last30Day int64 `json:"last_30_day"` // Unix day of the first slot
|
|
}
|
|
|
|
type ChatInfo struct {
|
|
IsGroup bool `json:"is_group"`
|
|
LastActive int64 `json:"last_active"`
|
|
Blocked bool `json:"blocked"`
|
|
LastError string `json:"last_error,omitempty"`
|
|
}
|
|
|
|
type Metrics struct {
|
|
Commands map[string]*CommandStats `json:"commands"`
|
|
Chats map[int64]*ChatInfo `json:"chats"`
|
|
}
|
|
|
|
var (
|
|
metricsFile = "metrics/metrics.json"
|
|
metrics *Metrics
|
|
mu sync.Mutex
|
|
)
|
|
|
|
func loadMetrics() {
|
|
if metrics != nil {
|
|
return
|
|
}
|
|
metrics = &Metrics{
|
|
Commands: make(map[string]*CommandStats),
|
|
Chats: make(map[int64]*ChatInfo),
|
|
}
|
|
f, err := os.Open(metricsFile)
|
|
if err != nil {
|
|
return
|
|
}
|
|
defer f.Close()
|
|
json.NewDecoder(f).Decode(metrics)
|
|
}
|
|
|
|
func saveMetrics() {
|
|
// Do not lock here; caller must hold the lock else it will shitty deadlock
|
|
loadMetrics()
|
|
f, err := os.Create(metricsFile)
|
|
if err != nil {
|
|
return
|
|
}
|
|
defer f.Close()
|
|
enc := json.NewEncoder(f)
|
|
enc.SetIndent("", " ")
|
|
enc.Encode(metrics)
|
|
}
|
|
|
|
func IncrementCommandUsage(cmd string) {
|
|
fmt.Printf("[metrics] IncrementCommandUsage called with cmd=%s\n", cmd)
|
|
mu.Lock()
|
|
defer mu.Unlock()
|
|
loadMetrics()
|
|
stat, ok := metrics.Commands[cmd]
|
|
if !ok {
|
|
stat = &CommandStats{Last30Day: time.Now().Unix() / 86400}
|
|
metrics.Commands[cmd] = stat
|
|
}
|
|
today := time.Now().Unix() / 86400
|
|
shift := int(today - stat.Last30Day)
|
|
if shift > 0 && shift < 30 {
|
|
copy(stat.Last30[shift:], stat.Last30[:30-shift])
|
|
for i := 0; i < shift; i++ {
|
|
stat.Last30[i] = 0
|
|
}
|
|
stat.Last30Day = today
|
|
} else if shift >= 30 {
|
|
for i := 0; i < 30; i++ {
|
|
stat.Last30[i] = 0
|
|
}
|
|
stat.Last30Day = today
|
|
}
|
|
stat.AllTime++
|
|
stat.Last30[0]++
|
|
saveMetrics()
|
|
}
|
|
|
|
func UpdateChat(chatID int64, isGroup bool) {
|
|
fmt.Printf("[metrics] UpdateChat called with chatID=%d isGroup=%v\n", chatID, isGroup)
|
|
mu.Lock()
|
|
defer mu.Unlock()
|
|
loadMetrics()
|
|
info, ok := metrics.Chats[chatID]
|
|
if !ok {
|
|
info = &ChatInfo{IsGroup: isGroup}
|
|
metrics.Chats[chatID] = info
|
|
}
|
|
info.LastActive = time.Now().Unix()
|
|
info.Blocked = false
|
|
info.LastError = ""
|
|
saveMetrics()
|
|
}
|
|
|
|
func MarkChatBlocked(chatID int64, errMsg string) {
|
|
fmt.Printf("[metrics] MarkChatBlocked called with chatID=%d errMsg=%s\n", chatID, errMsg)
|
|
mu.Lock()
|
|
defer mu.Unlock()
|
|
loadMetrics()
|
|
info, ok := metrics.Chats[chatID]
|
|
if !ok {
|
|
info = &ChatInfo{}
|
|
metrics.Chats[chatID] = info
|
|
}
|
|
info.Blocked = true
|
|
info.LastError = errMsg
|
|
saveMetrics()
|
|
}
|
|
|
|
func GetStats() *Metrics {
|
|
fmt.Printf("[metrics] GetStats called\n")
|
|
mu.Lock()
|
|
defer mu.Unlock()
|
|
loadMetrics()
|
|
copy := *metrics
|
|
return ©
|
|
}
|
|
|
|
func BroadcastMessage(sendFunc func(chatID int64) error) {
|
|
fmt.Printf("[metrics] BroadcastMessage called\n")
|
|
mu.Lock()
|
|
loadMetrics()
|
|
ids := make([]int64, 0, len(metrics.Chats))
|
|
for id := range metrics.Chats {
|
|
ids = append(ids, id)
|
|
}
|
|
mu.Unlock()
|
|
for _, id := range ids {
|
|
err := sendFunc(id)
|
|
if err != nil {
|
|
MarkChatBlocked(id, err.Error())
|
|
}
|
|
}
|
|
}
|