@@ -49,6 +49,7 @@ type createConversationRequest struct {
4949 Subject string `json:"subject"`
5050 Content string `json:"content"`
5151 Attachments []int `json:"attachments"`
52+ Initiator string `json:"initiator"` // "contact" | "agent"
5253}
5354
5455// handleGetAllConversations retrieves all conversations.
@@ -273,8 +274,8 @@ func handleGetConversation(r *fastglue.Request) error {
273274 return sendErrorEnvelope (r , err )
274275 }
275276
276- prev , _ := app .conversation .GetContactConversations (conv .ContactID )
277- conv .PreviousConversations = filterCurrentConv (prev , conv .UUID )
277+ prev , _ := app .conversation .GetContactPreviousConversations (conv .ContactID , 10 )
278+ conv .PreviousConversations = filterCurrentPreviousConv (prev , conv .UUID )
278279 return r .SendEnvelope (conv )
279280}
280281
@@ -649,14 +650,14 @@ func handleRemoveTeamAssignee(r *fastglue.Request) error {
649650 return r .SendEnvelope (true )
650651}
651652
652- // filterCurrentConv removes the current conversation from the list of conversations.
653- func filterCurrentConv (convs []cmodels.Conversation , uuid string ) []cmodels.Conversation {
653+ // filterCurrentPreviousConv removes the current conversation from the list of previous conversations.
654+ func filterCurrentPreviousConv (convs []cmodels.PreviousConversation , uuid string ) []cmodels.PreviousConversation {
654655 for i , c := range convs {
655656 if c .UUID == uuid {
656657 return append (convs [:i ], convs [i + 1 :]... )
657658 }
658659 }
659- return []cmodels.Conversation {}
660+ return []cmodels.PreviousConversation {}
660661}
661662
662663// handleCreateConversation creates a new conversation and sends a message to it.
@@ -672,38 +673,16 @@ func handleCreateConversation(r *fastglue.Request) error {
672673 return r .SendErrorEnvelope (fasthttp .StatusBadRequest , app .i18n .Ts ("globals.messages.errorParsing" , "name" , "{globals.terms.request}" ), nil , envelope .InputError )
673674 }
674675
675- to := []string {req .Email }
676-
677- // Validate required fields
678- if req .InboxID <= 0 {
679- return r .SendErrorEnvelope (fasthttp .StatusBadRequest , app .i18n .Ts ("globals.messages.required" , "name" , "`inbox_id`" ), nil , envelope .InputError )
680- }
681- if req .Content == "" {
682- return r .SendErrorEnvelope (fasthttp .StatusBadRequest , app .i18n .Ts ("globals.messages.required" , "name" , "`content`" ), nil , envelope .InputError )
683- }
684- if req .Email == "" {
685- return r .SendErrorEnvelope (fasthttp .StatusBadRequest , app .i18n .Ts ("globals.messages.required" , "name" , "`contact_email`" ), nil , envelope .InputError )
686- }
687- if req .FirstName == "" {
688- return r .SendErrorEnvelope (fasthttp .StatusBadRequest , app .i18n .Ts ("globals.messages.required" , "name" , "`first_name`" ), nil , envelope .InputError )
689- }
690- if ! stringutil .ValidEmail (req .Email ) {
691- return r .SendErrorEnvelope (fasthttp .StatusBadRequest , app .i18n .Ts ("globals.messages.invalid" , "name" , "`contact_email`" ), nil , envelope .InputError )
692- }
693-
694- user , err := app .user .GetAgent (auser .ID , "" )
695- if err != nil {
676+ // Validate the request
677+ if err := validateCreateConversationRequest (req , app ); err != nil {
696678 return sendErrorEnvelope (r , err )
697679 }
698680
699- // Check if inbox exists and is enabled.
700- inbox , err := app .inbox . GetDBRecord ( req . InboxID )
681+ to := [] string { req . Email }
682+ user , err := app .user . GetAgent ( auser . ID , "" )
701683 if err != nil {
702684 return sendErrorEnvelope (r , err )
703685 }
704- if ! inbox .Enabled {
705- return r .SendErrorEnvelope (fasthttp .StatusBadRequest , app .i18n .Ts ("globals.messages.disabled" , "name" , "inbox" ), nil , envelope .InputError )
706- }
707686
708687 // Find or create contact.
709688 contact := umodels.User {
@@ -717,22 +696,22 @@ func handleCreateConversation(r *fastglue.Request) error {
717696 return sendErrorEnvelope (r , envelope .NewError (envelope .GeneralError , app .i18n .Ts ("globals.messages.errorCreating" , "name" , "{globals.terms.contact}" ), nil ))
718697 }
719698
720- // Create conversation
699+ // Create conversation first.
721700 conversationID , conversationUUID , err := app .conversation .CreateConversation (
722701 contact .ID ,
723702 contact .ContactChannelID ,
724703 req .InboxID ,
725704 "" , /** last_message **/
726705 time .Now (), /** last_message_at **/
727706 req .Subject ,
728- true , /** append reference number to subject **/
707+ true , /** append reference number to subject? **/
729708 )
730709 if err != nil {
731710 app .lo .Error ("error creating conversation" , "error" , err )
732711 return sendErrorEnvelope (r , envelope .NewError (envelope .GeneralError , app .i18n .Ts ("globals.messages.errorCreating" , "name" , "{globals.terms.conversation}" ), nil ))
733712 }
734713
735- // Prepare attachments .
714+ // Get media for the attachment ids .
736715 var media = make ([]medModels.Media , 0 , len (req .Attachments ))
737716 for _ , id := range req .Attachments {
738717 m , err := app .media .Get (id , "" )
@@ -743,13 +722,29 @@ func handleCreateConversation(r *fastglue.Request) error {
743722 media = append (media , m )
744723 }
745724
746- // Send reply to the created conversation.
747- if _ , err := app .conversation .SendReply (media , req .InboxID , auser .ID /**sender_id**/ , conversationUUID , req .Content , to , nil /**cc**/ , nil /**bcc**/ , map [string ]any {} /**meta**/ ); err != nil {
748- // Delete the conversation if reply fails.
749- if err := app .conversation .DeleteConversation (conversationUUID ); err != nil {
750- app .lo .Error ("error deleting conversation" , "error" , err )
725+ // Send initial message based on the initiator of conversation.
726+ switch req .Initiator {
727+ case umodels .UserTypeAgent :
728+ // Queue reply.
729+ if _ , err := app .conversation .QueueReply (media , req .InboxID , auser .ID /**sender_id**/ , conversationUUID , req .Content , to , nil /**cc**/ , nil /**bcc**/ , map [string ]any {} /**meta**/ ); err != nil {
730+ // Delete the conversation if msg queue fails.
731+ if err := app .conversation .DeleteConversation (conversationUUID ); err != nil {
732+ app .lo .Error ("error deleting conversation" , "error" , err )
733+ }
734+ return sendErrorEnvelope (r , envelope .NewError (envelope .GeneralError , app .i18n .Ts ("globals.messages.errorSending" , "name" , "{globals.terms.message}" ), nil ))
751735 }
752- return sendErrorEnvelope (r , envelope .NewError (envelope .GeneralError , app .i18n .Ts ("globals.messages.errorSending" , "name" , "{globals.terms.message}" ), nil ))
736+ case umodels .UserTypeContact :
737+ // Create message on behalf of contact.
738+ if _ , err := app .conversation .CreateContactMessage (media , contact .ID , conversationUUID , req .Content , cmodels .ContentTypeHTML ); err != nil {
739+ // Delete the conversation if message creation fails.
740+ if err := app .conversation .DeleteConversation (conversationUUID ); err != nil {
741+ app .lo .Error ("error deleting conversation" , "error" , err )
742+ }
743+ return sendErrorEnvelope (r , envelope .NewError (envelope .GeneralError , app .i18n .Ts ("globals.messages.errorSending" , "name" , "{globals.terms.message}" ), nil ))
744+ }
745+ default :
746+ // Guard anyway.
747+ return r .SendErrorEnvelope (fasthttp .StatusBadRequest , app .i18n .Ts ("globals.messages.invalid" , "name" , "`initiator`" ), nil , envelope .InputError )
753748 }
754749
755750 // Assign the conversation to the agent or team.
@@ -768,3 +763,36 @@ func handleCreateConversation(r *fastglue.Request) error {
768763
769764 return r .SendEnvelope (conversation )
770765}
766+
767+ // validateCreateConversationRequest validates the create conversation request fields.
768+ func validateCreateConversationRequest (req createConversationRequest , app * App ) error {
769+ if req .InboxID <= 0 {
770+ return envelope .NewError (envelope .InputError , app .i18n .Ts ("globals.messages.required" , "name" , "`inbox_id`" ), nil )
771+ }
772+ if req .Content == "" {
773+ return envelope .NewError (envelope .InputError , app .i18n .Ts ("globals.messages.required" , "name" , "`content`" ), nil )
774+ }
775+ if req .Email == "" {
776+ return envelope .NewError (envelope .InputError , app .i18n .Ts ("globals.messages.required" , "name" , "`contact_email`" ), nil )
777+ }
778+ if req .FirstName == "" {
779+ return envelope .NewError (envelope .InputError , app .i18n .Ts ("globals.messages.required" , "name" , "`first_name`" ), nil )
780+ }
781+ if ! stringutil .ValidEmail (req .Email ) {
782+ return envelope .NewError (envelope .InputError , app .i18n .Ts ("globals.messages.invalid" , "name" , "`contact_email`" ), nil )
783+ }
784+ if req .Initiator != umodels .UserTypeContact && req .Initiator != umodels .UserTypeAgent {
785+ return envelope .NewError (envelope .InputError , app .i18n .Ts ("globals.messages.invalid" , "name" , "`initiator`" ), nil )
786+ }
787+
788+ // Check if inbox exists and is enabled.
789+ inbox , err := app .inbox .GetDBRecord (req .InboxID )
790+ if err != nil {
791+ return err
792+ }
793+ if ! inbox .Enabled {
794+ return envelope .NewError (envelope .InputError , app .i18n .Ts ("globals.messages.disabled" , "name" , "inbox" ), nil )
795+ }
796+
797+ return nil
798+ }
0 commit comments