Skip to content

Commit

Permalink
Merge branch 'refactor' into develop
Browse files Browse the repository at this point in the history
  • Loading branch information
slotheth committed Jun 6, 2024
2 parents 883d740 + e2d2f78 commit e9b47da
Show file tree
Hide file tree
Showing 2 changed files with 154 additions and 85 deletions.
215 changes: 142 additions & 73 deletions monitor/monitor.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,16 @@ func NewHandler(services *services.NewServices) *Handler {
}
}

type ProcessProposalContext struct {
Cfg *config.Configurations
Chain config.ChainConfig
ChainName string
GlobalDiscordNotifier *notifiers.DiscordNotifier
LastChecked map[string]int
AlertedProposals map[string]map[string]bool
VotingEndAlertedProposals map[string]map[string]bool
}

// Define constants for alert types and file names
const (
AlertTypeNewProposal = "📝 New proposal on"
Expand All @@ -35,15 +45,22 @@ const (
)

func (h *Handler) Run(cfg *config.Configurations, useMock bool) error {
ctx := context.Background()
lastChecked, alertedProposals, votingEndAlertedProposals, err := h.Services.FirestoreHandler.InitState()
if err != nil {
log.Println(err)
log.Printf("error init state: %v", err)
return fmt.Errorf("error init state: %v", err)
}

globalDiscordNotifier := &notifiers.DiscordNotifier{WebhookURL: cfg.Discord.Webhook}

proposalCtx := &ProcessProposalContext{
Cfg: cfg,
GlobalDiscordNotifier: globalDiscordNotifier,
LastChecked: lastChecked,
AlertedProposals: alertedProposals,
VotingEndAlertedProposals: votingEndAlertedProposals,
}

log.Printf("Checking for new proposals...")

for chainName, chain := range cfg.Chains {
Expand All @@ -52,85 +69,137 @@ func (h *Handler) Run(cfg *config.Configurations, useMock bool) error {
continue
}

for _, proposal := range propList {
if shouldSkipProposal(proposal) {
continue
}
proposalCtx.Chain = chain
proposalCtx.ChainName = chainName

err = h.processProposals(propList, proposalCtx)
if err != nil {
log.Printf("Error processing proposals for chain %s: %v", chainName, err)
}
}

return nil
}

func (h *Handler) processProposals(propList []proposals.Proposal, pctx *ProcessProposalContext) error {
ctx := context.Background()
for _, proposal := range propList {
if shouldSkipProposal(proposal) {
continue
}

proposalID, err := strconv.Atoi(proposal.ProposalID)
if err != nil {
log.Printf("Invalid proposal ID: %v", err)
continue
}

// Check if the proposal is new and alert if it hasn't been alerted yet
err = h.checkAndSendNewProposalAlert(ctx, pctx, proposal, proposalID)
if err != nil {
log.Printf("Error checking new proposal alert: %v", err)
continue
}

// Check if the proposal is nearing its voting end time and if it has not been alerted yet
err = h.checkAndSendVotingNearingAlert(ctx, pctx, proposal)
if err != nil {
log.Printf("Error checking voting nearing alert: %v", err)
continue
}
}
return nil
}

func (h *Handler) checkAndSendNewProposalAlert(ctx context.Context, pctx *ProcessProposalContext, proposal proposals.Proposal, proposalID int) error {
if proposalID > pctx.LastChecked[pctx.ChainName] {
err := SendDiscordAlert(pctx.Cfg, pctx.Chain, pctx.ChainName, proposal, pctx.GlobalDiscordNotifier, AlertTypeNewProposal)
if err != nil {
return fmt.Errorf("error sending alert for new proposal: %v", err)
}
pctx.LastChecked[pctx.ChainName] = proposalID
if pctx.AlertedProposals[pctx.ChainName] == nil {
pctx.AlertedProposals[pctx.ChainName] = make(map[string]bool)
}
pctx.AlertedProposals[pctx.ChainName][proposal.ProposalID] = true

err = h.saveState(ctx, pctx)
if err != nil {
return fmt.Errorf("error saving state: %v", err)
}
}
return nil
}

func (h *Handler) checkAndSendVotingNearingAlert(ctx context.Context, pctx *ProcessProposalContext, proposal proposals.Proposal) error {
if proposal.Status != proposals.ProposalStatusName[1] {
return nil
}

votingEndTime, err := time.Parse(time.RFC3339, proposal.VotingEndTime)
if err != nil {
return fmt.Errorf("error parsing voting end time: %v", err)
}

proposalID, err := strconv.Atoi(proposal.ProposalID)
currentTime := time.Now()
if !pctx.VotingEndAlertedProposals[pctx.ChainName][proposal.ProposalID] && votingEndTime.Sub(currentTime) <= 24*time.Hour {
shouldSendAlert, err := shouldSendVotingNearingAlert(pctx.Cfg, pctx.Chain, proposal)
if err != nil {
return err
}

if shouldSendAlert {
err = sendVotingNearingAlert(ctx, h, pctx, proposal)
if err != nil {
log.Printf("Invalid proposal ID: %v", err)
continue
return fmt.Errorf("error sending alert for voting nearing end: %v", err)
}
}
}

// Check if the proposal is new and alert if it hasn't been alerted yet
if proposalID > lastChecked[chainName] {
err = SendDiscordAlert(cfg, chain, chainName, proposal, globalDiscordNotifier, AlertTypeNewProposal)
if err != nil {
log.Printf("Error sending alert for new proposal: %v", err)
continue
}
lastChecked[chainName] = proposalID
if alertedProposals[chainName] == nil {
alertedProposals[chainName] = make(map[string]bool)
}
alertedProposals[chainName][proposal.ProposalID] = true
err = h.Services.FirestoreHandler.SaveLastCheckedProposalIDs(ctx, proposals.CollectionNameLastChecked, lastChecked)
if err != nil {
log.Printf("Error saving last checked proposal ID: %v", err)
}

err = h.Services.FirestoreHandler.SaveAlertedProposals(ctx, proposals.CollectionNameAlertedProposals, alertedProposals)
if err != nil {
log.Printf("Error saving alerted proposals: %v", err)
}
}
return nil
}

// Check if the proposal is nearing its voting end time and if it has not been alerted yet
if proposal.Status == proposals.ProposalStatusName[1] {
votingEndTime, err := time.Parse(time.RFC3339, proposal.VotingEndTime)
if err != nil {
log.Printf("Error parsing voting end time: %v", err)
continue
}
currentTime := time.Now()

if !votingEndAlertedProposals[chainName][proposal.ProposalID] && votingEndTime.Sub(currentTime) <= 24*time.Hour {
shouldSendAlert := true

if cfg.VotingAlertBehaviorNearing == VotingAlertBehaviorOnlyIfNotVoted {
voted, err := proposals.CheckValidatorVoted(chain, proposal.ProposalID, chain.ValidatorAddress, chain.APIVersion)
if err != nil {
log.Printf("%v", err)
continue
}

if voted {
shouldSendAlert = false
}
}

if shouldSendAlert {
err = SendDiscordAlert(cfg, chain, chainName, proposal, globalDiscordNotifier, AlertTypeVotingNearing)
if err != nil {
log.Printf("Error sending alert for voting nearing end: %v", err)
continue
}
if votingEndAlertedProposals[chainName] == nil {
votingEndAlertedProposals[chainName] = make(map[string]bool)
}
votingEndAlertedProposals[chainName][proposal.ProposalID] = true

err = h.Services.FirestoreHandler.SaveAlertedProposals(ctx, proposals.CollectionNameVotingEndAlerted, votingEndAlertedProposals)
if err != nil {
log.Printf("Error saving voting end alerted proposals: %v", err)
}
}
}
}
func (h *Handler) saveState(ctx context.Context, pctx *ProcessProposalContext) error {
err := h.Services.FirestoreHandler.SaveLastCheckedProposalIDs(ctx, proposals.CollectionNameLastChecked, pctx.LastChecked)
if err != nil {
return fmt.Errorf("error saving last checked proposal ID: %v", err)
}

err = h.Services.FirestoreHandler.SaveAlertedProposals(ctx, proposals.CollectionNameAlertedProposals, pctx.AlertedProposals)
if err != nil {
return fmt.Errorf("error saving alerted proposals: %v", err)
}

return nil
}

func shouldSendVotingNearingAlert(cfg *config.Configurations, chain config.ChainConfig, proposal proposals.Proposal) (bool, error) {
if cfg.VotingAlertBehaviorNearing == VotingAlertBehaviorOnlyIfNotVoted {
voted, err := proposals.CheckValidatorVoted(chain, proposal.ProposalID, chain.ValidatorAddress, chain.APIVersion)
if err != nil {
return false, err
}
if voted {
return false, nil
}
}
return true, nil
}

func sendVotingNearingAlert(ctx context.Context, h *Handler, pctx *ProcessProposalContext, proposal proposals.Proposal) error {
err := SendDiscordAlert(pctx.Cfg, pctx.Chain, pctx.ChainName, proposal, pctx.GlobalDiscordNotifier, AlertTypeVotingNearing)
if err != nil {
return err
}
if pctx.VotingEndAlertedProposals[pctx.ChainName] == nil {
pctx.VotingEndAlertedProposals[pctx.ChainName] = make(map[string]bool)
}
pctx.VotingEndAlertedProposals[pctx.ChainName][proposal.ProposalID] = true

err = h.Services.FirestoreHandler.SaveAlertedProposals(ctx, proposals.CollectionNameVotingEndAlerted, pctx.VotingEndAlertedProposals)
if err != nil {
log.Printf("Error saving voting end alerted proposals: %v", err)
}
return nil
}

Expand Down
24 changes: 12 additions & 12 deletions proposals/state.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,10 @@ const (

type FirestoreHandler struct {
FirestoreClient *firestore.Client
credentialsFile string
projectID string
databaseID string
collectionName string
CredentialsFile string
ProjectID string
DatabaseID string
CollectionName string
}

func New(cfg *config.Configurations) (*FirestoreHandler, error) {
Expand All @@ -43,10 +43,10 @@ func New(cfg *config.Configurations) (*FirestoreHandler, error) {

return &FirestoreHandler{
FirestoreClient: client,
credentialsFile: cfg.Storage.CredentialsPath,
projectID: cfg.Storage.ProjectID,
databaseID: cfg.Storage.DatabaseID,
collectionName: cfg.Storage.CollectionName,
CredentialsFile: cfg.Storage.CredentialsPath,
ProjectID: cfg.Storage.ProjectID,
DatabaseID: cfg.Storage.DatabaseID,
CollectionName: cfg.Storage.CollectionName,
}, nil
}

Expand All @@ -57,7 +57,7 @@ func (c *FirestoreHandler) getFirestoreClient() *firestore.Client {
func (c *FirestoreHandler) GetLastCheckedProposalIDs(ctx context.Context) (map[string]int, error) {
client := c.getFirestoreClient()

doc := client.Collection(c.collectionName).Doc(CollectionNameLastChecked)
doc := client.Collection(c.CollectionName).Doc(CollectionNameLastChecked)
var lastChecked LastCheckedProposals
dsnap, err := doc.Get(ctx)
if err != nil {
Expand All @@ -78,7 +78,7 @@ func (c *FirestoreHandler) GetLastCheckedProposalIDs(ctx context.Context) (map[s
func (c *FirestoreHandler) SaveLastCheckedProposalIDs(ctx context.Context, docID string, lastChecked map[string]int) error {
client := c.getFirestoreClient()

doc := client.Collection(c.collectionName).Doc(docID)
doc := client.Collection(c.CollectionName).Doc(docID)
entity := LastCheckedProposals{Proposals: utils.MapToSlice(lastChecked)}
_, err := doc.Set(ctx, entity)
return err
Expand All @@ -87,7 +87,7 @@ func (c *FirestoreHandler) SaveLastCheckedProposalIDs(ctx context.Context, docID
func (c *FirestoreHandler) GetAlertedProposals(ctx context.Context, docID string) (map[string]map[string]bool, error) {
client := c.getFirestoreClient()

doc := client.Collection(c.collectionName).Doc(docID)
doc := client.Collection(c.CollectionName).Doc(docID)
var entity AlertedProposals
dsnap, err := doc.Get(ctx)
if err != nil {
Expand All @@ -108,7 +108,7 @@ func (c *FirestoreHandler) GetAlertedProposals(ctx context.Context, docID string
func (c *FirestoreHandler) SaveAlertedProposals(ctx context.Context, docID string, alertedProposals map[string]map[string]bool) error {
client := c.getFirestoreClient()

doc := client.Collection(c.collectionName).Doc(docID)
doc := client.Collection(c.CollectionName).Doc(docID)
entity := AlertedProposals{Proposals: utils.MapToNestedSlice(alertedProposals)}
_, err := doc.Set(ctx, entity)
return err
Expand Down

0 comments on commit e9b47da

Please sign in to comment.