diff --git a/frontend/src/App.vue b/frontend/src/App.vue
index f12cd5f0..f4d11fe1 100644
--- a/frontend/src/App.vue
+++ b/frontend/src/App.vue
@@ -61,7 +61,7 @@ const navLinks = [
title: 'Account',
component: 'account',
label: '',
- icon: 'lucide:circle-user-round',
+ icon: 'lucide:settings',
},
]
const userStore = useUserStore()
diff --git a/frontend/src/assets/styles/main.scss b/frontend/src/assets/styles/main.scss
index 4aae2c0f..e3447c9a 100644
--- a/frontend/src/assets/styles/main.scss
+++ b/frontend/src/assets/styles/main.scss
@@ -9,7 +9,7 @@
}
.tab-container-default {
- padding: 15px 15px;
+ padding: 2rem 2rem;
}
.bg-success {
diff --git a/frontend/src/components/account/Account.vue b/frontend/src/components/account/Account.vue
new file mode 100644
index 00000000..632ec336
--- /dev/null
+++ b/frontend/src/components/account/Account.vue
@@ -0,0 +1,46 @@
+
+
+
+
+
+
+
+
+
+ Profile settings
+
+
+ Manage your account settings and set e-mail preferences.
+
+
+
+
+
+
diff --git a/frontend/src/components/account/Profile.vue b/frontend/src/components/account/Profile.vue
new file mode 100644
index 00000000..e69de29b
diff --git a/frontend/src/components/account/SidebarNav.vue b/frontend/src/components/account/SidebarNav.vue
new file mode 100644
index 00000000..b3e1387f
--- /dev/null
+++ b/frontend/src/components/account/SidebarNav.vue
@@ -0,0 +1,51 @@
+
+
+
+
+
diff --git a/frontend/src/components/conversationlist/ConversationList.vue b/frontend/src/components/conversationlist/ConversationList.vue
index 6097db63..0e6eceae 100644
--- a/frontend/src/components/conversationlist/ConversationList.vue
+++ b/frontend/src/components/conversationlist/ConversationList.vue
@@ -5,33 +5,6 @@
Conversations
-
-
-
@@ -58,16 +31,33 @@
-
-
-
-
-
- Work in progress.
-
-
+
+
+
@@ -88,13 +78,16 @@
-
-
-
+
+
+
+ All conversations loaded 😎
+
@@ -108,18 +101,11 @@ import { CONVERSATION_LIST_TYPE, CONVERSATION_PRE_DEFINED_FILTERS } from '@/cons
import { Error } from '@/components/ui/error'
import { Skeleton } from '@/components/ui/skeleton'
-import { Input } from '@/components/ui/input'
-import { Search, ListFilter } from 'lucide-vue-next'
import {
Tabs,
TabsList,
TabsTrigger,
} from '@/components/ui/tabs'
-import {
- Popover,
- PopoverContent,
- PopoverTrigger,
-} from '@/components/ui/popover'
import { Button } from '@/components/ui/button'
import {
Select,
@@ -179,6 +165,11 @@ const hasErrored = computed(() => {
return conversationStore.conversations.errorMessage ? true : false
})
+const everythingLoaded = computed(() => {
+ return !conversationStore.conversations.errorMessage && !emptyConversations.value
+})
+
+
const conversationsLoading = computed(() => {
return conversationStore.conversations.loading
})
diff --git a/frontend/src/router/index.js b/frontend/src/router/index.js
index 30c4497e..aec87c8a 100644
--- a/frontend/src/router/index.js
+++ b/frontend/src/router/index.js
@@ -26,10 +26,11 @@ const routes = [
component: UserLoginView
},
{
- path: '/account',
- name: "account",
- component: AccountView
- },
+ path: '/account/:page?',
+ name: 'account',
+ component: AccountView,
+ props: true,
+ }
]
const router = createRouter({
diff --git a/frontend/src/utils/datetime.js b/frontend/src/utils/datetime.js
index cedb87ea..f8cc6359 100644
--- a/frontend/src/utils/datetime.js
+++ b/frontend/src/utils/datetime.js
@@ -1,10 +1,23 @@
-import { format } from 'date-fns'
+import { format, differenceInMinutes, differenceInHours, differenceInDays } from 'date-fns';
-export function formatTime (t) {
+export function formatTime(t) {
try {
- return format(t, 'h:mm a')
+ const now = new Date();
+ const minutesDifference = differenceInMinutes(now, t);
+ const hoursDifference = differenceInHours(now, t);
+ const daysDifference = differenceInDays(now, t);
+
+ if (minutesDifference < 60) {
+ return `${minutesDifference} minutes ago`;
+ } else if (hoursDifference < 24) {
+ return `${hoursDifference} hours ago`;
+ } else if (daysDifference < 7) {
+ return `${daysDifference} days ago`;
+ } else {
+ return format(t, 'MMMM d, yyyy h:mm a');
+ }
} catch (error) {
- console.error("error parsing time", error)
+ console.error("error parsing time", error, "time", t)
return ''
}
-}
\ No newline at end of file
+}
diff --git a/frontend/src/views/AccountView.vue b/frontend/src/views/AccountView.vue
index 097fb208..bf54be14 100644
--- a/frontend/src/views/AccountView.vue
+++ b/frontend/src/views/AccountView.vue
@@ -1,8 +1,9 @@
-
-
-
Work in progress.
-
-
-
\ No newline at end of file
+import Account from '@/components/account/Account.vue'
+
+
+
+
+ yo
+
+
diff --git a/frontend/src/views/ConversationView.vue b/frontend/src/views/ConversationView.vue
index e4ebd0e9..f403df83 100644
--- a/frontend/src/views/ConversationView.vue
+++ b/frontend/src/views/ConversationView.vue
@@ -1,7 +1,7 @@
-
+
diff --git a/internal/automation/automation.go b/internal/automation/automation.go
index 3529ac8f..64959df9 100644
--- a/internal/automation/automation.go
+++ b/internal/automation/automation.go
@@ -4,6 +4,8 @@ import (
"context"
"embed"
"encoding/json"
+ "fmt"
+ "time"
"github.com/abhinavxd/artemis/internal/automation/models"
cmodels "github.com/abhinavxd/artemis/internal/conversation/models"
@@ -18,12 +20,13 @@ var (
)
type Engine struct {
- q queries
- lo *logf.Logger
- conversationStore ConversationStore
- messageStore MessageStore
- rules []models.Rule
- conversationQ chan cmodels.Conversation
+ q queries
+ lo *logf.Logger
+ conversationStore ConversationStore
+ messageStore MessageStore
+ rules []models.Rule
+ newConversationQ chan string
+ updateConversationQ chan string
}
type Opts struct {
@@ -32,8 +35,12 @@ type Opts struct {
}
type ConversationStore interface {
+ Get(uuid string) (cmodels.Conversation, error)
+ GetRecentConversations(t time.Time) ([]cmodels.Conversation, error)
UpdateTeamAssignee(uuid string, assigneeUUID []byte) error
+ UpdateUserAssignee(uuid string, assigneeUUID []byte) error
UpdateStatus(uuid string, status []byte) error
+ UpdatePriority(uuid string, priority []byte) error
}
type MessageStore interface {
@@ -49,20 +56,21 @@ func New(opt Opts) (*Engine, error) {
var (
q queries
e = &Engine{
- lo: opt.Lo,
- conversationQ: make(chan cmodels.Conversation, 10000),
+ lo: opt.Lo,
+ newConversationQ: make(chan string, 5000),
+ updateConversationQ: make(chan string, 5000),
}
)
if err := dbutil.ScanSQLFile("queries.sql", &q, opt.DB, efs); err != nil {
return nil, err
}
e.q = q
- e.rules = e.getRules()
+ e.rules = e.queryRules()
return e, nil
}
func (e *Engine) ReloadRules() {
- e.rules = e.getRules()
+ e.rules = e.queryRules()
}
func (e *Engine) SetMessageStore(messageStore MessageStore) {
@@ -74,34 +82,98 @@ func (e *Engine) SetConversationStore(conversationStore ConversationStore) {
}
func (e *Engine) Serve(ctx context.Context) {
+ ticker := time.NewTicker(30 * time.Second)
+ defer ticker.Stop()
+
+ // Create separate semaphores for each channel
+ maxWorkers := 10
+ newConversationSemaphore := make(chan struct{}, maxWorkers)
+ updateConversationSemaphore := make(chan struct{}, maxWorkers)
+ timeTriggerSemaphore := make(chan struct{}, maxWorkers)
+
for {
select {
case <-ctx.Done():
return
- case conversation := <-e.conversationQ:
- e.processConversations(conversation)
+ case conversationUUID := <-e.newConversationQ:
+ newConversationSemaphore <- struct{}{}
+ go e.handleNewConversation(conversationUUID, newConversationSemaphore)
+ case conversationUUID := <-e.updateConversationQ:
+ updateConversationSemaphore <- struct{}{}
+ go e.handleUpdateConversation(conversationUUID, updateConversationSemaphore)
+ case <-ticker.C:
+ e.lo.Info("evaluating time triggers")
+ timeTriggerSemaphore <- struct{}{}
+ go e.handleTimeTrigger(timeTriggerSemaphore)
}
}
}
-func (e *Engine) EvaluateRules(c cmodels.Conversation) {
- select {
- case e.conversationQ <- c:
- default:
- // Queue is full.
- e.lo.Warn("EvaluateRules: conversationQ is full, unable to enqueue conversation")
+func (e *Engine) handleNewConversation(conversationUUID string, semaphore chan struct{}) {
+ defer func() { <-semaphore }()
+ conversation, err := e.conversationStore.Get(conversationUUID)
+ if err != nil {
+ e.lo.Error("error could not fetch conversations to evaluate new conversation rules", "conversation_uuid", conversationUUID)
+ return
+ }
+ rules := e.filterRulesByType(models.RuleTypeNewConversation)
+ e.evalConversationRules(rules, conversation)
+}
+
+func (e *Engine) handleUpdateConversation(conversationUUID string, semaphore chan struct{}) {
+ defer func() { <-semaphore }()
+ conversation, err := e.conversationStore.Get(conversationUUID)
+ if err != nil {
+ e.lo.Error("error could not fetch conversations to evaluate update conversation rules", "conversation_uuid", conversationUUID)
+ return
+ }
+ rules := e.filterRulesByType(models.RuleTypeConversationUpdate)
+ e.evalConversationRules(rules, conversation)
+}
+
+func (e *Engine) handleTimeTrigger(semaphore chan struct{}) {
+ defer func() { <-semaphore }()
+ thirtyDaysAgo := time.Now().Add(-30 * 24 * time.Hour)
+ conversations, err := e.conversationStore.GetRecentConversations(thirtyDaysAgo)
+ if err != nil {
+ e.lo.Error("error could not fetch conversations to evaluate time triggers")
+ return
+ }
+ rules := e.filterRulesByType(models.RuleTypeTimeTrigger)
+ for _, conversation := range conversations {
+ e.evalConversationRules(rules, conversation)
}
}
-func (e *Engine) getRules() []models.Rule {
- var rulesJSON []string
+func (e *Engine) EvaluateNewConversationRules(conversationUUID string) {
+ select {
+ case e.newConversationQ <- conversationUUID:
+ default:
+ // Queue is full.
+ e.lo.Warn("EvaluateNewConversationRules: newConversationQ is full, unable to enqueue conversation")
+ }
+}
+
+func (e *Engine) EvaluateConversationUpdateRules(conversationUUID string) {
+ select {
+ case e.updateConversationQ <- conversationUUID:
+ default:
+ // Queue is full.
+ e.lo.Warn("EvaluateConversationUpdateRules: updateConversationQ is full, unable to enqueue conversation")
+ }
+}
+
+func (e *Engine) queryRules() []models.Rule {
+ var (
+ rulesJSON []string
+ rules []models.Rule
+ )
err := e.q.GetRules.Select(&rulesJSON)
if err != nil {
e.lo.Error("error fetching automation rules", "error", err)
return nil
}
- var rules []models.Rule
for _, ruleJSON := range rulesJSON {
var rulesBatch []models.Rule
if err := json.Unmarshal([]byte(ruleJSON), &rulesBatch); err != nil {
@@ -111,6 +183,16 @@ func (e *Engine) getRules() []models.Rule {
rules = append(rules, rulesBatch...)
}
- e.lo.Debug("fetched rules", "num", len(rules), "rules", rules)
+ e.lo.Debug("fetched rules", "num", len(rules), "rules", fmt.Sprintf("%+v", rules))
return rules
}
+
+func (e *Engine) filterRulesByType(ruleType string) []models.Rule {
+ var filteredRules []models.Rule
+ for _, rule := range e.rules {
+ if rule.Type == ruleType {
+ filteredRules = append(filteredRules, rule)
+ }
+ }
+ return filteredRules
+}
diff --git a/internal/automation/processor.go b/internal/automation/evaluator.go
similarity index 61%
rename from internal/automation/processor.go
rename to internal/automation/evaluator.go
index befeccb1..fc64c361 100644
--- a/internal/automation/processor.go
+++ b/internal/automation/evaluator.go
@@ -2,6 +2,7 @@ package automation
import (
"fmt"
+ "strconv"
"strings"
"github.com/abhinavxd/artemis/internal/automation/models"
@@ -9,10 +10,11 @@ import (
"github.com/abhinavxd/artemis/internal/systeminfo"
)
-func (e *Engine) processConversations(conversation cmodels.Conversation) {
- e.lo.Debug("num rules", "rules", len(e.rules))
- for _, rule := range e.rules {
+func (e *Engine) evalConversationRules(rules []models.Rule, conversation cmodels.Conversation) {
+ e.lo.Debug("num rules", "rules", len(rules))
+ for _, rule := range rules {
e.lo.Debug("eval rule", "groups", len(rule.Groups), "rule", rule)
+ // At max there can be only 2 groups.
if len(rule.Groups) > 2 {
continue
}
@@ -29,6 +31,8 @@ func (e *Engine) processConversations(conversation cmodels.Conversation) {
for _, action := range rule.Actions {
e.executeActions(conversation, action)
}
+ } else {
+ e.lo.Debug("rule evaluation failed, NOT executing actions")
}
}
}
@@ -81,45 +85,57 @@ func (e *Engine) evaluateGroup(rules []models.RuleDetail, operator string, conve
func (e *Engine) evaluateRule(rule models.RuleDetail, conversation cmodels.Conversation) bool {
var (
- conversationValue string
- conditionMet bool
+ valueToCompare string
+ conditionMet bool
)
// Extract the value from the conversation based on the rule's field
switch rule.Field {
- case "subject":
- conversationValue = conversation.Subject
- case "content":
- conversationValue = conversation.FirstMessage
- case "status":
- conversationValue = conversation.Status.String
- case "priority":
- conversationValue = conversation.Priority.String
+ 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.ConversationFieldAssignedTeamID:
+ if conversation.AssignedTeamID.Valid {
+ valueToCompare = strconv.Itoa(conversation.AssignedTeamID.Int)
+ }
+ case models.ConversationFieldAssignedUserID:
+ if conversation.AssignedUserID.Valid {
+ valueToCompare = strconv.Itoa(conversation.AssignedUserID.Int)
+ }
default:
e.lo.Error("rule field not recognized", "field", rule.Field)
return false
}
- // Lower case the value.
- conversationValue = strings.ToLower(conversationValue)
-
- // Compare the conversation value with the rule's value based on the operator
- switch rule.Operator {
- case "equals":
- conditionMet = conversationValue == rule.Value
- case "not equal":
- conditionMet = conversationValue != rule.Value
- case "contains":
- conditionMet = strings.Contains(conversationValue, rule.Value)
- case "startsWith":
- conditionMet = strings.HasPrefix(conversationValue, rule.Value)
- case "endsWith":
- conditionMet = strings.HasSuffix(conversationValue, rule.Value)
- default:
- e.lo.Error("logical operator not recognized for evaluating rules", "operator", rule.Operator)
- return false
+ if !rule.CaseSensitiveMatch {
+ valueToCompare = strings.ToLower(valueToCompare)
+ rule.Value = strings.ToLower(rule.Value)
}
+ e.lo.Debug("comparing values", "conversation_value", valueToCompare, "rule_value", rule.Value)
+
+ 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 = bool(len(valueToCompare) > 0)
+ case models.RuleNotSet:
+ conditionMet = !bool(len(valueToCompare) > 0)
+ default:
+ e.lo.Error("rule logical operator not recognized", "operator", rule.Operator)
+ return false
+ }
return conditionMet
}
@@ -140,12 +156,20 @@ func (e *Engine) applyAction(action models.RuleAction, conversation cmodels.Conv
return err
}
case models.ActionAssignAgent:
- if err := e.conversationStore.UpdateStatus(conversation.UUID, []byte(action.Action)); err != nil {
+ if err := e.conversationStore.UpdateUserAssignee(conversation.UUID, []byte(action.Action)); err != nil {
return err
}
if err := e.messageStore.RecordStatusChange(action.Action, conversation.UUID, systeminfo.SystemUserUUID); err != nil {
return err
}
+ case models.ActionSetPriority:
+ if err := e.conversationStore.UpdatePriority(conversation.UUID, []byte(action.Action)); err != nil {
+ return err
+ }
+ case models.ActionSetStatus:
+ if err := e.conversationStore.UpdateStatus(conversation.UUID, []byte(action.Action)); err != nil {
+ return err
+ }
default:
return fmt.Errorf("unrecognized rule action: %s", action.Type)
}
diff --git a/internal/automation/models/models.go b/internal/automation/models/models.go
index 48cddfb4..51750d98 100644
--- a/internal/automation/models/models.go
+++ b/internal/automation/models/models.go
@@ -3,11 +3,33 @@ package models
const (
ActionAssignTeam = "assign_team"
ActionAssignAgent = "assign_agent"
- OperatorAnd = "AND"
- OperatorOR = "OR"
+ ActionSetStatus = "set_status"
+ ActionSetPriority = "set_priority"
+
+ OperatorAnd = "AND"
+ OperatorOR = "OR"
+
+ RuleContains = "contains"
+ RuleNotContains = "not contains"
+ RuleEquals = "equals"
+ RuleNotEqual = "not equal"
+ RuleSet = "set"
+ RuleNotSet = "not set"
+
+ RuleTypeNewConversation = "new_conversation"
+ RuleTypeConversationUpdate = "conversation_update"
+ RuleTypeTimeTrigger = "time_trigger"
+
+ ConversationFieldSubject = "subject"
+ ConversationFieldContent = "content"
+ ConversationFieldStatus = "status"
+ ConversationFieldPriority = "priority"
+ ConversationFieldAssignedUserID = "assigned_user_id"
+ ConversationFieldAssignedTeamID = "assigned_team_id"
)
type Rule struct {
+ Type string `json:"type" db:"type"`
GroupOperator string `json:"group_operator" db:"group_operator"`
Groups []RuleGroup `json:"groups" db:"groups"`
Actions []RuleAction `json:"actions" db:"actions"`
@@ -19,9 +41,10 @@ type RuleGroup struct {
}
type RuleDetail struct {
- Field string `json:"field" db:"field"`
- Operator string `json:"operator" db:"operator"`
- Value string `json:"value" db:"value"`
+ Field string `json:"field" db:"field"`
+ Operator string `json:"operator" db:"operator"`
+ Value string `json:"value" db:"value"`
+ CaseSensitiveMatch bool `json:"case_sensitive_match" db:"case_sensitive_match"`
}
type RuleAction struct {
diff --git a/internal/automation/queries.sql b/internal/automation/queries.sql
index b509615f..e05dc799 100644
--- a/internal/automation/queries.sql
+++ b/internal/automation/queries.sql
@@ -1,3 +1,4 @@
-- name: get-rules
-select rules
+select
+ rules
from automation_rules;
\ No newline at end of file
diff --git a/internal/conversation/conversations.go b/internal/conversation/conversations.go
index 0c2d9ad1..964649e3 100644
--- a/internal/conversation/conversations.go
+++ b/internal/conversation/conversations.go
@@ -76,6 +76,7 @@ type queries struct {
GetUUID *sqlx.Stmt `query:"get-uuid"`
GetInboxID *sqlx.Stmt `query:"get-inbox-id"`
GetConversation *sqlx.Stmt `query:"get-conversation"`
+ GetRecentConversations *sqlx.Stmt `query:"get-recent-conversations"`
GetUnassigned *sqlx.Stmt `query:"get-unassigned"`
GetConversationParticipants *sqlx.Stmt `query:"get-conversation-participants"`
GetConversations string `query:"get-conversations"`
@@ -124,15 +125,29 @@ func (c *Manager) Create(contactID int, inboxID int, meta []byte) (int, string,
}
func (c *Manager) Get(uuid string) (models.Conversation, error) {
- var conv models.Conversation
- if err := c.q.GetConversation.Get(&conv, uuid); err != nil {
+ var conversation models.Conversation
+ if err := c.q.GetConversation.Get(&conversation, uuid); err != nil {
if err == sql.ErrNoRows {
- return conv, fmt.Errorf("conversation not found")
+ c.lo.Error("conversation not found", "uuid", uuid)
+ return conversation, fmt.Errorf("conversation not found")
}
c.lo.Error("fetching conversation from DB", "error", err)
- return conv, fmt.Errorf("error fetching conversation")
+ return conversation, fmt.Errorf("error fetching conversation")
}
- return conv, nil
+ return conversation, nil
+}
+
+func (c *Manager) GetRecentConversations(time time.Time) ([]models.Conversation, error) {
+ var conversations []models.Conversation
+ if err := c.q.GetRecentConversations.Select(&conversations, time); err != nil {
+ if err == sql.ErrNoRows {
+ c.lo.Error("conversations not found", "created_after", time)
+ return conversations, fmt.Errorf("conversation not found")
+ }
+ c.lo.Error("fetching conversation from DB", "error", err)
+ return conversations, fmt.Errorf("error fetching conversation")
+ }
+ return conversations, nil
}
func (c *Manager) UpdateAssigneeLastSeen(uuid string) error {
diff --git a/internal/conversation/queries.sql b/internal/conversation/queries.sql
index 21e6fe27..55e9e769 100644
--- a/internal/conversation/queries.sql
+++ b/internal/conversation/queries.sql
@@ -69,6 +69,40 @@ LEFT JOIN users u ON u.id = c.assigned_user_id
LEFT JOIN teams at ON at.id = c.assigned_team_id
WHERE c.uuid = $1;
+
+-- name: get-recent-conversations
+SELECT
+ c.created_at,
+ c.updated_at,
+ c.closed_at,
+ c.resolved_at,
+ c.priority,
+ c.status,
+ c.uuid,
+ c.reference_number,
+ c.first_reply_at,
+ ct.uuid AS contact_uuid,
+ ct.first_name as first_name,
+ ct.last_name as last_name,
+ ct.email as email,
+ ct.phone_number as phone_number,
+ ct.avatar_url as avatar_url,
+ u.uuid AS assigned_user_uuid,
+ at.uuid AS assigned_team_uuid,
+ (SELECT COALESCE(
+ (SELECT json_agg(t.name)
+ FROM tags t
+ INNER JOIN conversation_tags ct ON ct.tag_id = t.id
+ WHERE ct.conversation_id = c.id),
+ '[]'::json
+ )) AS tags
+FROM conversations c
+JOIN contacts ct ON c.contact_id = ct.id
+LEFT JOIN users u ON u.id = c.assigned_user_id
+LEFT JOIN teams at ON at.id = c.assigned_team_id
+WHERE c.created_at > $1 and c.uuid = 'e2f69c9f-17f5-4d09-9aae-12c2a79046a2';
+
+
-- name: get-id
SELECT id from conversations where uuid = $1;
diff --git a/internal/message/messages.go b/internal/message/messages.go
index 8c50e9a2..0ef87fc2 100644
--- a/internal/message/messages.go
+++ b/internal/message/messages.go
@@ -16,7 +16,6 @@ import (
"github.com/abhinavxd/artemis/internal/automation"
"github.com/abhinavxd/artemis/internal/contact"
"github.com/abhinavxd/artemis/internal/conversation"
- cmodels "github.com/abhinavxd/artemis/internal/conversation/models"
"github.com/abhinavxd/artemis/internal/dbutil"
"github.com/abhinavxd/artemis/internal/inbox"
"github.com/abhinavxd/artemis/internal/message/models"
@@ -457,13 +456,11 @@ func (m *Manager) processIncomingMessage(in models.IncomingMessage) error {
m.conversationMgr.UpdateLastMessage(in.Message.ConversationID, in.Message.ConversationUUID, content, in.Message.CreatedAt)
}
- // Evaluate automation rules for this new conversation.
+ // Evaluate automation rules for this conversation.
if isNewConversation {
- m.automationEngine.EvaluateRules(cmodels.Conversation{
- UUID: in.Message.ConversationUUID,
- FirstMessage: in.Message.Content,
- Subject: in.Message.Subject,
- })
+ m.automationEngine.EvaluateNewConversationRules(in.Message.ConversationUUID)
+ } else {
+ m.automationEngine.EvaluateConversationUpdateRules(in.Message.ConversationUUID)
}
return nil