package bot import ( "bytes" "context" "fmt" "strings" "github.com/keel-hq/keel/approvals" "github.com/keel-hq/keel/bot/formatter" "github.com/keel-hq/keel/types" log "github.com/sirupsen/logrus" ) type BotRequestApproval func(req *types.Approval) error type BotReplyApproval func(approval *types.Approval) error func (bm *BotManager) SubscribeForApprovals(ctx context.Context, approval BotRequestApproval) error { approvalsCh, err := bm.approvalsManager.Subscribe(ctx) if err != nil { log.Errorf("bot.subscribeForApprovals(): %s", err.Error()) return err } for { select { case <-ctx.Done(): return nil case a := <-approvalsCh: err = approval(a) if err != nil { log.WithFields(log.Fields{ "error": err, "approval": a.Identifier, }).Error("bot.subscribeForApprovals: approval request failed") } } } } func (bm *BotManager) ProcessApprovalResponses(ctx context.Context, reply BotReplyApproval) error { for { select { case <-ctx.Done(): return nil case resp := <-bm.approvalsRespCh: switch resp.Status { case types.ApprovalStatusApproved: err := bm.processApprovedResponse(resp, reply) if err != nil { log.WithFields(log.Fields{ "error": err, }).Error("bot.processApprovalResponses: failed to process approval response message") } case types.ApprovalStatusRejected: err := bm.processRejectedResponse(resp, reply) if err != nil { log.WithFields(log.Fields{ "error": err, }).Error("bot.processApprovalResponses: failed to process approval reject response message") } } } } } func (bm *BotManager) processApprovedResponse(approvalResponse *ApprovalResponse, reply BotReplyApproval) error { trimmed := strings.TrimPrefix(approvalResponse.Text, ApprovalResponseKeyword) identifiers := strings.Split(trimmed, " ") if len(identifiers) == 0 { return nil } for _, identifier := range identifiers { if identifier == "" { continue } approval, err := bm.approvalsManager.Approve(identifier, approvalResponse.User) if err != nil { log.WithFields(log.Fields{ "error": err, "identifier": identifier, }).Error("bot.processApprovedResponse: failed to approve") continue } err = reply(approval) if err != nil { log.WithFields(log.Fields{ "error": err, "identifier": identifier, }).Error("bot.processApprovedResponse: got error while replying after processing approved approval") } } return nil } func (bm *BotManager) processRejectedResponse(approvalResponse *ApprovalResponse, reply BotReplyApproval) error { trimmed := strings.TrimPrefix(approvalResponse.Text, RejectResponseKeyword) identifiers := strings.Split(trimmed, " ") if len(identifiers) == 0 { return nil } for _, identifier := range identifiers { approval, err := bm.approvalsManager.Reject(identifier) if err != nil { log.WithFields(log.Fields{ "error": err, "identifier": identifier, }).Error("bot.processApprovedResponse: failed to reject") continue } err = reply(approval) if err != nil { log.WithFields(log.Fields{ "error": err, "identifier": identifier, }).Error("bot.processApprovedResponse: got error while replying after processing rejected approval") } } return nil } func ApprovalsResponse(approvalsManager approvals.Manager) string { approvals, err := approvalsManager.List() if err != nil { return fmt.Sprintf("got error while fetching approvals: %s", err) } if len(approvals) == 0 { return fmt.Sprintf("there are currently no request waiting to be approved.") } buf := &bytes.Buffer{} approvalCtx := formatter.Context{ Output: buf, Format: formatter.NewApprovalsFormat(formatter.TableFormatKey, false), } err = formatter.ApprovalWrite(approvalCtx, approvals) if err != nil { return fmt.Sprintf("got error while formatting approvals: %s", err) } return buf.String() } func IsApproval(eventUser string, eventText string) (resp *ApprovalResponse, ok bool) { if strings.HasPrefix(strings.ToLower(eventText), ApprovalResponseKeyword) { return &ApprovalResponse{ User: eventUser, Status: types.ApprovalStatusApproved, Text: eventText, }, true } if strings.HasPrefix(strings.ToLower(eventText), RejectResponseKeyword) { return &ApprovalResponse{ User: eventUser, Status: types.ApprovalStatusRejected, Text: eventText, }, true } return nil, false } func RemoveApprovalHandler(identifier string, approvalsManager approvals.Manager) string { approval, err := approvalsManager.Get(identifier) if err != nil { return fmt.Sprintf("approval with identifier '%s' was not found", identifier) } err = approvalsManager.Delete(approval) if err != nil { return fmt.Sprintf("failed to remove '%s' approval: %s.", identifier, err) } return fmt.Sprintf("approval '%s' removed.", identifier) }