package metrics import ( "encoding/json" "net/http" "os" "regexp" "strings" log "github.com/sirupsen/logrus" ) type MetricConfig struct { Url string BotUserAgents *[]regexp.Regexp Enabled bool } // Checks if we should send a metric ping to page-metrics based on the served path. func (c *MetricConfig) ShouldSendMetrics(path, userAgent, dnt, gpc string) bool { if !strings.HasSuffix(path, ".html") || !c.Enabled { return false } // Ignore requests where the user have set "Do-Not-Track" or "Do-Not-Sell-My-Data", even though // there is no user data and we're not selling it. if dnt == "1" || gpc == "1" { return false } // Filter out bots for _, pattern := range *c.BotUserAgents { if pattern.MatchString(userAgent) { return false } } return true } func (c *MetricConfig) SendMetricPing(domain, path, referrer string) { data := map[string]string{ "domain": domain, "path": path, "referer": referrer, } jsonData, err := json.Marshal(data) if err != nil { log.Errorf("Failed to send metric ping: %v", err) return } log.Debugf("Sending payload %s", string(jsonData)) // Send the ping to the server go func() { res, err := http.Post( c.Url, "application/json", strings.NewReader(string(jsonData)), ) if err != nil { log.Errorf("Failed to send payload to: %v", err) return } defer res.Body.Close() if res.StatusCode != 200 { log.Errorf("Server returned non-200 status code %d", res.StatusCode) } }() } // Reads a JSON array of bot user agents from disk and parses them // into regular expressions. func ReadBotPatterns(file string) ([]regexp.Regexp, error) { content, err := os.ReadFile(file) if err != nil { log.Warnf("Failed to read bot metrics file: %v", err) return []regexp.Regexp{}, err } var payload []string err = json.Unmarshal(content, &payload) if err != nil { log.Warnf("Failed to unmarshal file: %v", err) return []regexp.Regexp{}, err } patterns := make([]regexp.Regexp, 0) for _, v := range payload { patterns = append( patterns, *regexp.MustCompile(v), ) } return patterns, nil }