func (e *Alert) ReportEvent(ctx context.Context, req *alert.ReportEventRequest, rsp *alert.ReportEventResponse) error {
	if req.Event == nil {
		return errors.New("event can't be empty")
	}
	ev := &event{
		ID:       uuid.New().String(),
		Category: req.Event.Category,
		Action:   req.Event.Action,
		Label:    req.Event.Label,
		Value:    req.Event.Value,
		UserID:   req.Event.UserID,
	}
	for _, block := range e.config.BlockList {
		if strings.Contains(ev.Label, block) {
			return nil
		}
	}
	err := e.saveEvent(ev)
	if err != nil {
		log.Warnf("Error saving event: %v", err)
	}
	err = e.sendToGA(ev)
	if err != nil {
		log.Warnf("Error sending event to google analytics: %v", err)
	}
	if e.config.Slack.Enabled && req.Event.Action != "success" {  
		jsond, err := json.MarshalIndent(req.Event, "", "   ")
		if err != nil {
			return err
		}
		msg := fmt.Sprintf("Event received:\n```\n%v\n```", string(jsond))
		_, _, _, err = e.slackClient.SendMessage(e.config.Slack.Channel, slack.MsgOptionUsername(e.config.Slack.Username), slack.MsgOptionText(msg, false))
		if err != nil {
			log.Errorf("Error sending to Slack %s", err)
			return err
		}
	}
	return nil
}
ReportEvent ingests events and sends alerts if needed
package handler
import (
	"context"
	"encoding/json"
	"errors"
	"fmt"
	"net/http"
	"net/url"
	"strings"
	alert "github.com/m3o/services/alert/proto/alert"
	"github.com/micro/micro/v3/service/config"
	log "github.com/micro/micro/v3/service/logger"
	mstore "github.com/micro/micro/v3/service/store"
	"github.com/google/uuid"
	"github.com/slack-go/slack"
)
const (
	storePrefixEvents = "events/"
)
type Alert struct {
	slackClient *slack.Client
	config      conf
}
type event struct {
	ID       string            `json:"id"`
	UserID   string            `json:"userID"`
	Category string            `json:"category"`
	Action   string            `json:"action"`
	Label    string            `json:"label"`
	Value    uint64            `json:"value"`
	Metadata map[string]string `json:"metadata"`
}
type slackConf struct {
	Token    string `json:"token"`
	Enabled  bool   `json:"enabled"`
	Channel  string `json:"channel"`
	Username string `json:"user_name"`
}
type conf struct {
	Slack        slackConf `json:"slack"`
	GaPropertyID string    `json:"ga_property_id"`
	BlockList    []string  `json:"blocklist"`
}
func NewAlert() *Alert {
	c := conf{}
	val, err := config.Get("micro.alert")
	if err != nil {
		log.Warnf("Error getting config: %v", err)
	}
	err = val.Scan(&c)
	if err != nil {
		log.Warnf("Error scanning config: %v", err)
	}
	if c.Slack.Enabled && len(c.Slack.Token) == 0 {
		log.Errorf("Slack token missing")
	}
	if len(c.GaPropertyID) == 0 {
		log.Errorf("Google Analytics key (property ID) is missing")
	}
	log.Infof("Slack enabled: %v", c.Slack.Enabled)
	if len(c.Slack.Channel) == 0 {
		c.Slack.Channel = "alerts"
	}
	if len(c.Slack.Username) == 0 {
		c.Slack.Username = "Alert Service"
	}
	return &Alert{
		slackClient: slack.New(c.Slack.Token),
		config:      c,
	}
}