mirror of
				https://github.com/abhinavxd/libredesk.git
				synced 2025-11-03 21:43:35 +00:00 
			
		
		
		
	- feat: adds csrf token check - feat: adds conversation sub and unsub for WS updates. - Clean up and remove unncessary code - refactor and simplify auth middlewares - fix: automation rules - Update schema.sql
		
			
				
	
	
		
			189 lines
		
	
	
		
			6.4 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			189 lines
		
	
	
		
			6.4 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
package automation
 | 
						|
 | 
						|
import (
 | 
						|
	"fmt"
 | 
						|
	"strconv"
 | 
						|
	"strings"
 | 
						|
 | 
						|
	"github.com/abhinavxd/artemis/internal/automation/models"
 | 
						|
	cmodels "github.com/abhinavxd/artemis/internal/conversation/models"
 | 
						|
)
 | 
						|
 | 
						|
// evalConversationRules evaluates a list of rules against a given conversation.
 | 
						|
// If all the groups of a rule pass their evaluations based on the defined logical operations,
 | 
						|
// the corresponding actions are executed.
 | 
						|
func (e *Engine) evalConversationRules(rules []models.Rule, conversation cmodels.Conversation) {
 | 
						|
	for _, rule := range rules {
 | 
						|
		e.lo.Debug("evaluating rule for conversation", "rule", rule, "conversation_uuid", conversation.UUID)
 | 
						|
 | 
						|
		// At max there can be only 2 groups.
 | 
						|
		if len(rule.Groups) > 2 {
 | 
						|
			e.lo.Warn("WARNING: more than 2 groups found for rules skipping evaluation")
 | 
						|
			continue
 | 
						|
		}
 | 
						|
 | 
						|
		var results []bool
 | 
						|
		for _, group := range rule.Groups {
 | 
						|
			result := e.evaluateGroup(group.Rules, group.LogicalOp, conversation)
 | 
						|
			e.lo.Debug("evaluating group rules", "logical_op", group.LogicalOp, "result", result, "conversation_uuid", conversation.UUID)
 | 
						|
			results = append(results, result)
 | 
						|
		}
 | 
						|
 | 
						|
		if evaluateFinalResult(results, rule.GroupOperator) {
 | 
						|
			e.lo.Debug("rule evaluation successfull executing actions", "conversation_uuid", conversation.UUID)
 | 
						|
			// All group rule evaluations successful, execute the actions.
 | 
						|
			for _, action := range rule.Actions {
 | 
						|
				e.applyAction(action, conversation)
 | 
						|
			}
 | 
						|
		} else {
 | 
						|
			e.lo.Debug("rule evaluation failed", "conversation_uuid", conversation.UUID)
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// evaluateFinalResult computes the final result of multiple group evaluations
 | 
						|
// based on the specified logical operator (AND/OR).
 | 
						|
func evaluateFinalResult(results []bool, operator string) bool {
 | 
						|
	if operator == models.OperatorAnd {
 | 
						|
		for _, result := range results {
 | 
						|
			if !result {
 | 
						|
				return false
 | 
						|
			}
 | 
						|
		}
 | 
						|
		return true
 | 
						|
	}
 | 
						|
	if operator == models.OperatorOR {
 | 
						|
		for _, result := range results {
 | 
						|
			if result {
 | 
						|
				return true
 | 
						|
			}
 | 
						|
		}
 | 
						|
		return false
 | 
						|
	}
 | 
						|
	return false
 | 
						|
}
 | 
						|
 | 
						|
// evaluateGroup evaluates a set of rules within a group against a given conversation
 | 
						|
// based on the specified logical operator (AND/OR).
 | 
						|
func (e *Engine) evaluateGroup(rules []models.RuleDetail, operator string, conversation cmodels.Conversation) bool {
 | 
						|
	switch operator {
 | 
						|
	case models.OperatorAnd:
 | 
						|
		// All conditions within the group must be true
 | 
						|
		for _, rule := range rules {
 | 
						|
			if !e.evaluateRule(rule, conversation) {
 | 
						|
				return false
 | 
						|
			}
 | 
						|
		}
 | 
						|
		return true
 | 
						|
	case models.OperatorOR:
 | 
						|
		// At least one condition within the group must be true
 | 
						|
		for _, rule := range rules {
 | 
						|
			if e.evaluateRule(rule, conversation) {
 | 
						|
				return true
 | 
						|
			}
 | 
						|
		}
 | 
						|
		return false
 | 
						|
	default:
 | 
						|
		e.lo.Error("invalid group operator", "operator", operator)
 | 
						|
	}
 | 
						|
	return false
 | 
						|
}
 | 
						|
 | 
						|
// evaluateRule evaluates a single rule against a given conversation.
 | 
						|
func (e *Engine) evaluateRule(rule models.RuleDetail, conversation cmodels.Conversation) bool {
 | 
						|
	var (
 | 
						|
		valueToCompare string
 | 
						|
		conditionMet   bool
 | 
						|
	)
 | 
						|
 | 
						|
	// Extract the value from the conversation based on the rule's field
 | 
						|
	switch rule.Field {
 | 
						|
	case models.ConversationFieldSubject:
 | 
						|
		valueToCompare = conversation.Subject
 | 
						|
	case models.ConversationFieldContent:
 | 
						|
		valueToCompare = conversation.FirstMessage
 | 
						|
	case models.ConversationFieldStatus:
 | 
						|
		valueToCompare = conversation.Status.String
 | 
						|
	case models.ConversationFieldPriority:
 | 
						|
		valueToCompare = conversation.Priority.String
 | 
						|
	case models.ConversationFieldAssignedTeam:
 | 
						|
		if conversation.AssignedTeamID.Valid {
 | 
						|
			valueToCompare = strconv.Itoa(conversation.AssignedTeamID.Int)
 | 
						|
		}
 | 
						|
	case models.ConversationFieldAssignedUser:
 | 
						|
		if conversation.AssignedUserID.Valid {
 | 
						|
			valueToCompare = strconv.Itoa(conversation.AssignedUserID.Int)
 | 
						|
		}
 | 
						|
	default:
 | 
						|
		e.lo.Error("unrecognized rule field", "field", rule.Field)
 | 
						|
		return false
 | 
						|
	}
 | 
						|
 | 
						|
	if !rule.CaseSensitiveMatch {
 | 
						|
		valueToCompare = strings.ToLower(valueToCompare)
 | 
						|
		rule.Value = strings.ToLower(rule.Value)
 | 
						|
	}
 | 
						|
 | 
						|
	e.lo.Debug("evaluating rule", "rule_field", rule.Field, "rule_operator", rule.Operator, "rule_value", rule.Value, "compared_with", valueToCompare, "coversation_uuid", conversation.UUID)
 | 
						|
 | 
						|
	// Compare with set operator.
 | 
						|
	switch rule.Operator {
 | 
						|
	case models.RuleEquals:
 | 
						|
		conditionMet = valueToCompare == rule.Value
 | 
						|
	case models.RuleNotEqual:
 | 
						|
		conditionMet = valueToCompare != rule.Value
 | 
						|
	case models.RuleContains:
 | 
						|
		conditionMet = strings.Contains(valueToCompare, rule.Value)
 | 
						|
	case models.RuleNotContains:
 | 
						|
		conditionMet = !strings.Contains(valueToCompare, rule.Value)
 | 
						|
	case models.RuleSet:
 | 
						|
		conditionMet = len(valueToCompare) > 0
 | 
						|
	case models.RuleNotSet:
 | 
						|
		conditionMet = len(valueToCompare) == 0
 | 
						|
	default:
 | 
						|
		e.lo.Error("unrecognized rule logical operator", "operator", rule.Operator)
 | 
						|
		return false
 | 
						|
	}
 | 
						|
	e.lo.Debug("rule conditions met", "coversation_uuid", conversation.UUID)
 | 
						|
	return conditionMet
 | 
						|
}
 | 
						|
 | 
						|
// applyAction applies a specific action to the given conversation.
 | 
						|
func (e *Engine) applyAction(action models.RuleAction, conversation cmodels.Conversation) error {
 | 
						|
	switch action.Type {
 | 
						|
	case models.ActionAssignTeam:
 | 
						|
		e.lo.Debug("executing assign team action", "value", action.Action, "conversation_uuid", conversation.UUID)
 | 
						|
		teamID, err := strconv.Atoi(action.Action)
 | 
						|
		if err != nil {
 | 
						|
			e.lo.Error("error converting string to int", "string", action.Action, "error", err)
 | 
						|
			return err
 | 
						|
		}
 | 
						|
		if err := e.conversationStore.UpdateConversationTeamAssignee(conversation.UUID, teamID, e.systemUser); err != nil {
 | 
						|
			return err
 | 
						|
		}
 | 
						|
	case models.ActionAssignUser:
 | 
						|
		e.lo.Debug("executing assign user action", "value", action.Action, "conversation_uuid", conversation.UUID)
 | 
						|
		agentID, err := strconv.Atoi(action.Action)
 | 
						|
		if err != nil {
 | 
						|
			e.lo.Error("error converting string to int", "string", action.Action, "error", err)
 | 
						|
			return err
 | 
						|
		}
 | 
						|
		if err := e.conversationStore.UpdateConversationUserAssignee(conversation.UUID, agentID, e.systemUser); err != nil {
 | 
						|
			return err
 | 
						|
		}
 | 
						|
	case models.ActionSetPriority:
 | 
						|
		e.lo.Debug("executing set priority action", "value", action.Action, "conversation_uuid", conversation.UUID)
 | 
						|
		if err := e.conversationStore.UpdateConversationPriority(conversation.UUID, []byte(action.Action), e.systemUser); err != nil {
 | 
						|
			return err
 | 
						|
		}
 | 
						|
	case models.ActionSetStatus:
 | 
						|
		e.lo.Debug("executing set status action", "value", action.Action, "conversation_uuid", conversation.UUID)
 | 
						|
		if err := e.conversationStore.UpdateConversationStatus(conversation.UUID, []byte(action.Action), e.systemUser); err != nil {
 | 
						|
			return err
 | 
						|
		}
 | 
						|
	default:
 | 
						|
		return fmt.Errorf("unrecognized rule action: %s", action.Type)
 | 
						|
	}
 | 
						|
	return nil
 | 
						|
}
 |