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 }