github.com/sharovik/devbot@v1.0.1-0.20240308094637-4a0387c40516/internal/service/message/conversation/helper.go (about)

     1  package conversation
     2  
     3  import (
     4  	"fmt"
     5  	"regexp"
     6  	"strings"
     7  	"time"
     8  
     9  	_time "github.com/sharovik/devbot/internal/service/time"
    10  
    11  	"github.com/sharovik/devbot/internal/database"
    12  	"github.com/sharovik/devbot/internal/dto"
    13  )
    14  
    15  // Conversation the conversation object which contains the information about the scenario selected for the conversation and the last question asked by the customer.
    16  type Conversation struct {
    17  	ScenarioID             int64
    18  	EventID                int64
    19  	ScenarioQuestionID     int64
    20  	Question               string
    21  	Channel                string
    22  	EventReadyToBeExecuted bool
    23  	LastQuestion           dto.BaseChatMessage
    24  	ReactionType           string
    25  	Scenario               database.EventScenario
    26  }
    27  
    28  // currentConversations contains the list of current open conversations, where each key is a channel ID and the value is the last channel question
    29  var currentConversations = map[string]Conversation{}
    30  
    31  const openConversationTimeout = time.Second * 600
    32  
    33  // GetCurrentConversations returns the list of current open conversations
    34  func GetCurrentConversations() map[string]Conversation {
    35  	return currentConversations
    36  }
    37  
    38  // AddConversation add the new conversation to the list of open conversations. This will be used for scenarios build
    39  func AddConversation(scenario database.EventScenario, message dto.BaseChatMessage) {
    40  	conversation := Conversation{
    41  		ScenarioID:         message.DictionaryMessage.ScenarioID,
    42  		EventID:            message.DictionaryMessage.EventID,
    43  		Question:           message.DictionaryMessage.Question,
    44  		ScenarioQuestionID: message.DictionaryMessage.QuestionID,
    45  		LastQuestion:       message,
    46  		ReactionType:       message.DictionaryMessage.ReactionType,
    47  		Scenario:           scenario,
    48  	}
    49  
    50  	currentConversations[message.Channel] = conversation
    51  }
    52  
    53  // SetLastQuestion sets the last question to the current conversation
    54  func SetLastQuestion(message dto.BaseChatMessage) {
    55  	conv := GetConversation(message.Channel)
    56  	conv.LastQuestion = message
    57  
    58  	currentConversations[message.Channel] = conv
    59  }
    60  
    61  // GetConversation method retrieve the conversation for selected channel
    62  func GetConversation(channel string) Conversation {
    63  	if conversation, ok := currentConversations[channel]; ok {
    64  		return conversation
    65  	}
    66  
    67  	return Conversation{}
    68  }
    69  
    70  // MarkAsReadyEventToBeExecuted method set the conversation event ready to be executed
    71  func MarkAsReadyEventToBeExecuted(channel string) {
    72  	conversation := currentConversations[channel]
    73  	conversation.EventReadyToBeExecuted = true
    74  
    75  	currentConversations[channel] = conversation
    76  }
    77  
    78  // CleanUpExpiredMessages removes the messages from the CleanUpExpiredMessages map object, which are expired
    79  func CleanUpExpiredMessages() {
    80  	currentTime := _time.Service.Now()
    81  
    82  	for channel, conversation := range GetCurrentConversations() {
    83  		elapsed := time.Duration(currentTime.Sub(conversation.LastQuestion.Ts).Nanoseconds())
    84  		if elapsed >= openConversationTimeout {
    85  			FinaliseConversation(channel)
    86  		}
    87  	}
    88  }
    89  
    90  // FinaliseConversation method delete the conversation for selected channel
    91  func FinaliseConversation(channel string) {
    92  	if _, ok := currentConversations[channel]; !ok {
    93  		return
    94  	}
    95  
    96  	delete(currentConversations, channel)
    97  }
    98  
    99  // getStopScenarioWords method returns the stop words, which will be used for identification if we need to stop the scenario.
   100  func getStopScenarioWords() []string {
   101  	var stopPhrases []string
   102  
   103  	for _, text := range []string{
   104  		"stop!",
   105  		"stop scenario!",
   106  		"exit",
   107  		"stop",
   108  		"cancel",
   109  	} {
   110  		modifiedText := fmt.Sprintf("(%s)", text)
   111  		stopPhrases = append(stopPhrases, modifiedText)
   112  	}
   113  
   114  	return stopPhrases
   115  }
   116  
   117  // IsScenarioStopTriggered method checks if the scenario stop action was triggered
   118  func IsScenarioStopTriggered(text string) bool {
   119  	regexStr := fmt.Sprintf("(?i)%s", strings.Join(getStopScenarioWords(), "|"))
   120  	regex, err := regexp.Compile(regexStr)
   121  	if err != nil {
   122  		return false
   123  	}
   124  
   125  	matches := regex.FindStringSubmatch(text)
   126  
   127  	return len(matches) != 0
   128  }