From b39013176eaa12df3a539cd74a2db06ebedea17c Mon Sep 17 00:00:00 2001 From: Yuri Romanowski Date: Wed, 22 Feb 2023 13:33:02 +0500 Subject: [PATCH] interm interm2 --- src/TzBot/BotMain.hs | 6 +- src/TzBot/Parser.hs | 245 ++++++++++++++++++----------- src/TzBot/ProcessEvents/Common.hs | 28 +++- src/TzBot/ProcessEvents/Message.hs | 56 +++++-- src/TzBot/RunMonad.hs | 8 +- src/TzBot/Slack/API.hs | 2 +- src/TzBot/TimeContext.hs | 23 +++ tzbot.cabal | 3 +- 8 files changed, 256 insertions(+), 115 deletions(-) create mode 100644 src/TzBot/TimeContext.hs diff --git a/src/TzBot/BotMain.hs b/src/TzBot/BotMain.hs index 623dc19..d865344 100644 --- a/src/TzBot/BotMain.hs +++ b/src/TzBot/BotMain.hs @@ -92,8 +92,12 @@ run opts = do managed $ withTzCacheDefault defaultMessageInfoCachingTime bsReportEntries <- managed $ withTzCacheDefault cCacheReportDialog - -- auto-acknowledge received messages + + let defaultConversationStateCachingTime = hour 12 + bsConversationStateCache <- + managed $ withTzCacheDefault defaultConversationStateCachingTime (bsLogNamespace, bsLogContext, bsLogEnv) <- managed $ withLogger cLogLevel + -- auto-acknowledge received messages liftIO $ runSocketMode sCfg $ handler gracefulShutdownContainer BotState {..} withFeedbackConfig :: BotConfig -> (FeedbackConfig -> IO a) -> IO a diff --git a/src/TzBot/Parser.hs b/src/TzBot/Parser.hs index 18b30ce..541f709 100644 --- a/src/TzBot/Parser.hs +++ b/src/TzBot/Parser.hs @@ -4,10 +4,12 @@ module TzBot.Parser ( parseTimeRefs + , parseWithEmptyContext ) where import Universum hiding (many, toList, try) +import Control.Lens.Operators import Data.Char (isUpper) import Data.List qualified as L import Data.Map qualified as M @@ -19,266 +21,332 @@ import Data.Time.Calendar.Compat (DayOfMonth, MonthOfYear, Year) import Data.Time.LocalTime (TimeOfDay(..)) import Data.Time.Zones.All (TZLabel, tzNameLabelMap) import Glider.NLP.Tokenizer (Token(..), tokenize) -import Text.Megaparsec hiding (Token) +import Text.Megaparsec hiding (State, Token) import TzBot.Instances () +import TzBot.TimeContext (TimeContext(..), emptyTimeContext, tcCurrentDateRefL, tcCurrentLocRefL) import TzBot.TimeReference import TzBot.Util import TzBot.Util qualified as CI type TzParser = Parsec Void [Token] -{- | Parses time references from an input string. +{- | Parses time references from an input string using provided time context + - and produces a new time context. ->>> parseTimeRefs "let's meet tuesday at 10am" +>>> parseWithEmptyContext "let's meet tuesday at 10am" [TimeReference {trText = "tuesday at 10am", trTimeOfDay = 10:00:00, trDateRef = Just (DayOfWeekRef Tuesday), trLocationRef = Nothing}] ->>> parseTimeRefs "i can do it at 3pm MDT" +>>> parseWithEmptyContext "i can do it at 3pm MDT" [TimeReference {trText = "at 3pm MDT", trTimeOfDay = 15:00:00, trDateRef = Nothing, trLocationRef = Just (TimeZoneAbbreviationRef (TimeZoneAbbreviationInfo {tzaiAbbreviation = "MDT", tzaiOffsetMinutes = -360, tzaiFullName = "Mountain Daylight Time (North America)"}))}] ->>> parseTimeRefs "how about between 2pm and 3pm?" +>>> parseWithEmptyContext "how about between 2pm and 3pm?" [TimeReference {trText = "2pm", trTimeOfDay = 14:00:00, trDateRef = Nothing, trLocationRef = Nothing},TimeReference {trText = "3pm", trTimeOfDay = 15:00:00, trDateRef = Nothing, trLocationRef = Nothing}] ->>> parseTimeRefs "Does 10am work for you?" +>>> parseWithEmptyContext "Does 10am work for you?" [TimeReference {trText = "10am", trTimeOfDay = 10:00:00, trDateRef = Nothing, trLocationRef = Nothing}] ->>> parseTimeRefs "That doesn't work for me, what about 10:30 AM?" +>>> parseWithEmptyContext "That doesn't work for me, what about 10:30 AM?" [TimeReference {trText = "10:30 AM", trTimeOfDay = 10:30:00, trDateRef = Nothing, trLocationRef = Nothing}] ->>> parseTimeRefs "I can only be there at 16:00" +>>> parseWithEmptyContext "I can only be there at 16:00" [TimeReference {trText = "at 16:00", trTimeOfDay = 16:00:00, trDateRef = Nothing, trLocationRef = Nothing}] ->>> parseTimeRefs "10am tomorrow" +>>> parseWithEmptyContext "10am tomorrow" [TimeReference {trText = "10am tomorrow", trTimeOfDay = 10:00:00, trDateRef = Just (DaysFromToday 1), trLocationRef = Nothing}] ->>> parseTimeRefs "today at 3pm" +>>> parseWithEmptyContext "today at 3pm" [TimeReference {trText = "today at 3pm", trTimeOfDay = 15:00:00, trDateRef = Just (DaysFromToday 0), trLocationRef = Nothing}] ->>> parseTimeRefs "10am in 2 days" +>>> parseWithEmptyContext "10am in 2 days" [TimeReference {trText = "10am in 2 days", trTimeOfDay = 10:00:00, trDateRef = Just (DaysFromToday 2), trLocationRef = Nothing}] ->>> parseTimeRefs "tuesday at 3pm" +>>> parseWithEmptyContext "tuesday at 3pm" [TimeReference {trText = "tuesday at 3pm", trTimeOfDay = 15:00:00, trDateRef = Just (DayOfWeekRef Tuesday), trLocationRef = Nothing}] ->>> parseTimeRefs "at 3pm on tuesday" +>>> parseWithEmptyContext "at 3pm on tuesday" [TimeReference {trText = "at 3pm on tuesday", trTimeOfDay = 15:00:00, trDateRef = Just (DayOfWeekRef Tuesday), trLocationRef = Nothing}] ->>> parseTimeRefs "at 11am on the 4th " +>>> parseWithEmptyContext "at 11am on the 4th " [TimeReference {trText = "at 11am on the 4th", trTimeOfDay = 11:00:00, trDateRef = Just (DayOfMonthRef 4 Nothing), trLocationRef = Nothing}] ->>> parseTimeRefs "at 11am on the 4th of April" +>>> parseWithEmptyContext "at 11am on the 4th of April" [TimeReference {trText = "at 11am on the 4th of April", trTimeOfDay = 11:00:00, trDateRef = Just (DayOfMonthRef 4 (Just (4,Nothing))), trLocationRef = Nothing}] ->>> parseTimeRefs "at 11am on April 4" +>>> parseWithEmptyContext "at 11am on April 4" [TimeReference {trText = "at 11am on April 4", trTimeOfDay = 11:00:00, trDateRef = Just (DayOfMonthRef 4 (Just (4,Nothing))), trLocationRef = Nothing}] ->>> parseTimeRefs "at 11am on 4 April" +>>> parseWithEmptyContext "at 11am on 4 April" [TimeReference {trText = "at 11am on 4 April", trTimeOfDay = 11:00:00, trDateRef = Just (DayOfMonthRef 4 (Just (4,Nothing))), trLocationRef = Nothing}] ->>> parseTimeRefs "9am in europe/london" +>>> parseWithEmptyContext "9am in europe/london" [TimeReference {trText = "9am in europe/london", trTimeOfDay = 09:00:00, trDateRef = Nothing, trLocationRef = Just (TimeZoneRef Europe__London)}] ->>> parseTimeRefs "2pm CST" +>>> parseWithEmptyContext "2pm CST" [TimeReference {trText = "2pm CST", trTimeOfDay = 14:00:00, trDateRef = Nothing, trLocationRef = Just (TimeZoneAbbreviationRef (TimeZoneAbbreviationInfo {tzaiAbbreviation = "CST", tzaiOffsetMinutes = -360, tzaiFullName = "Central Standard Time (North America)"}))}] ->>> parseTimeRefs "10am UTC+03:00" +>>> parseWithEmptyContext "10am UTC+03:00" [TimeReference {trText = "10am UTC+03:00", trTimeOfDay = 10:00:00, trDateRef = Nothing, trLocationRef = Just (OffsetRef 180)}] ->>> parseTimeRefs "10am UTC+3" +>>> parseWithEmptyContext "10am UTC+3" [TimeReference {trText = "10am UTC+3", trTimeOfDay = 10:00:00, trDateRef = Nothing, trLocationRef = Just (OffsetRef 180)}] ->>> parseTimeRefs "10am UTC +3" +>>> parseWithEmptyContext "10am UTC +3" [TimeReference {trText = "10am UTC +3", trTimeOfDay = 10:00:00, trDateRef = Nothing, trLocationRef = Just (OffsetRef 180)}] ->>> parseTimeRefs "10am UTC-3" +>>> parseWithEmptyContext "10am UTC-3" [TimeReference {trText = "10am UTC-3", trTimeOfDay = 10:00:00, trDateRef = Nothing, trLocationRef = Just (OffsetRef (-180))}] ->>> parseTimeRefs "10am UTC -3" +>>> parseWithEmptyContext "10am UTC -3" [TimeReference {trText = "10am UTC -3", trTimeOfDay = 10:00:00, trDateRef = Nothing, trLocationRef = Just (OffsetRef (-180))}] ->>> parseTimeRefs "10am UTC-blabla" +>>> parseWithEmptyContext "10am UTC-blabla" [TimeReference {trText = "10am", trTimeOfDay = 10:00:00, trDateRef = Nothing, trLocationRef = Nothing}] ->>> parseTimeRefs "Let's meet between 10am and 11:30am" +>>> parseWithEmptyContext "Let's meet between 10am and 11:30am" [TimeReference {trText = "10am", trTimeOfDay = 10:00:00, trDateRef = Nothing, trLocationRef = Nothing},TimeReference {trText = "11:30am", trTimeOfDay = 11:30:00, trDateRef = Nothing, trLocationRef = Nothing}] ->>> parseTimeRefs "35pm" +>>> parseWithEmptyContext "35pm" [] ->>> parseTimeRefs "35pmkek" +>>> parseWithEmptyContext "35pmkek" [] ->>> parseTimeRefs "15:00pm" +>>> parseWithEmptyContext "15:00pm" [TimeReference {trText = "15:00pm", trTimeOfDay = 15:00:00, trDateRef = Nothing, trLocationRef = Nothing}] ->>> parseTimeRefs "13:00 Nov 06" +>>> parseWithEmptyContext "13:00 Nov 06" [TimeReference {trText = "13:00 Nov 06", trTimeOfDay = 13:00:00, trDateRef = Just (DayOfMonthRef 6 (Just (11,Nothing))), trLocationRef = Nothing}] ->>> parseTimeRefs "11:12:13 nOv 1" +>>> parseWithEmptyContext "11:12:13 nOv 1" [TimeReference {trText = "11:12:13 nOv 1", trTimeOfDay = 11:12:00, trDateRef = Just (DayOfMonthRef 1 (Just (11,Nothing))), trLocationRef = Nothing}] ->>> parseTimeRefs "13:00 06 Nov" +>>> parseWithEmptyContext "13:00 06 Nov" [TimeReference {trText = "13:00 06 Nov", trTimeOfDay = 13:00:00, trDateRef = Just (DayOfMonthRef 6 (Just (11,Nothing))), trLocationRef = Nothing}] ->>> parseTimeRefs "10am 11am" +>>> parseWithEmptyContext "10am 11am" [TimeReference {trText = "10am", trTimeOfDay = 10:00:00, trDateRef = Nothing, trLocationRef = Nothing},TimeReference {trText = "11am", trTimeOfDay = 11:00:00, trDateRef = Nothing, trLocationRef = Nothing}] ->>> parseTimeRefs "1:12:23pm" +>>> parseWithEmptyContext "1:12:23pm" [TimeReference {trText = "1:12:23pm", trTimeOfDay = 13:12:00, trDateRef = Nothing, trLocationRef = Nothing}] ->>> parseTimeRefs "12am the day after tomorrow" +>>> parseWithEmptyContext "12am the day after tomorrow" [TimeReference {trText = "12am the day after tomorrow", trTimeOfDay = 12:00:00, trDateRef = Just (DaysFromToday 2), trLocationRef = Nothing}] ->>> parseTimeRefs "12am day after tomorrow" +>>> parseWithEmptyContext "12am day after tomorrow" [TimeReference {trText = "12am day after tomorrow", trTimeOfDay = 12:00:00, trDateRef = Just (DaysFromToday 2), trLocationRef = Nothing}] ->>> parseTimeRefs "12am 3 days ahead" +>>> parseWithEmptyContext "12am 3 days ahead" [TimeReference {trText = "12am 3 days ahead", trTimeOfDay = 12:00:00, trDateRef = Just (DaysFromToday 3), trLocationRef = Nothing}] ->>> parseTimeRefs "9:3am" +>>> parseWithEmptyContext "9:3am" [] ->>> parseTimeRefs "13:00 06 nov" +>>> parseWithEmptyContext "13:00 06 nov" [TimeReference {trText = "13:00 06 nov", trTimeOfDay = 13:00:00, trDateRef = Just (DayOfMonthRef 6 (Just (11,Nothing))), trLocationRef = Nothing}] ->>> parseTimeRefs "13:00 nov 06" +>>> parseWithEmptyContext "13:00 nov 06" [TimeReference {trText = "13:00 nov 06", trTimeOfDay = 13:00:00, trDateRef = Just (DayOfMonthRef 6 (Just (11,Nothing))), trLocationRef = Nothing}] ->>> parseTimeRefs "today,10am" +>>> parseWithEmptyContext "today,10am" [TimeReference {trText = "today,10am", trTimeOfDay = 10:00:00, trDateRef = Just (DaysFromToday 0), trLocationRef = Nothing}] ->>> parseTimeRefs "today , 10am" +>>> parseWithEmptyContext "today , 10am" [TimeReference {trText = "today , 10am", trTimeOfDay = 10:00:00, trDateRef = Just (DaysFromToday 0), trLocationRef = Nothing}] ->>> parseTimeRefs "today, 10am" +>>> parseWithEmptyContext "today, 10am" [TimeReference {trText = "today, 10am", trTimeOfDay = 10:00:00, trDateRef = Just (DaysFromToday 0), trLocationRef = Nothing}] ->>> parseTimeRefs "today ,10am" +>>> parseWithEmptyContext "today ,10am" [TimeReference {trText = "today ,10am", trTimeOfDay = 10:00:00, trDateRef = Just (DaysFromToday 0), trLocationRef = Nothing}] ->>> parseTimeRefs "10am aMeRiCa/Argentina/Buenos_Aires" +>>> parseWithEmptyContext "10am aMeRiCa/Argentina/Buenos_Aires" [TimeReference {trText = "10am aMeRiCa/Argentina/Buenos_Aires", trTimeOfDay = 10:00:00, trDateRef = Nothing, trLocationRef = Just (TimeZoneRef America__Argentina__Buenos_Aires)}] ->>> parseTimeRefs "10am America/North_Dakota/New_Salem" +>>> parseWithEmptyContext "10am America/North_Dakota/New_Salem" [TimeReference {trText = "10am America/North_Dakota/New_Salem", trTimeOfDay = 10:00:00, trDateRef = Nothing, trLocationRef = Just (TimeZoneRef America__North_Dakota__New_Salem)}] ->>> parseTimeRefs "10am America/port-au-Prince" +>>> parseWithEmptyContext "10am America/port-au-Prince" [TimeReference {trText = "10am America/port-au-Prince", trTimeOfDay = 10:00:00, trDateRef = Nothing, trLocationRef = Just (TimeZoneRef America__Port_au_Prince)}] ->>> parseTimeRefs "10am MSKC" +>>> parseWithEmptyContext "10am MSKC" [TimeReference {trText = "10am MSKC", trTimeOfDay = 10:00:00, trDateRef = Nothing, trLocationRef = Just (UnknownTimeZoneAbbreviationRef (UnknownTimeZoneAbbrev {utzaAbbrev = "MSKC", utzaCandidates = ["MSK"]}))}] ->>> parseTimeRefs "10am KSMC" +>>> parseWithEmptyContext "10am KSMC" [TimeReference {trText = "10am KSMC", trTimeOfDay = 10:00:00, trDateRef = Nothing, trLocationRef = Just (UnknownTimeZoneAbbreviationRef (UnknownTimeZoneAbbrev {utzaAbbrev = "KSMC", utzaCandidates = []}))}] ->>> parseTimeRefs "10am KSMc" +>>> parseWithEmptyContext "10am KSMc" [TimeReference {trText = "10am", trTimeOfDay = 10:00:00, trDateRef = Nothing, trLocationRef = Nothing}] ->>> parseTimeRefs "10am K" +>>> parseWithEmptyContext "10am K" [TimeReference {trText = "10am", trTimeOfDay = 10:00:00, trDateRef = Nothing, trLocationRef = Nothing}] ->>> parseTimeRefs "10am KAMAZN" +>>> parseWithEmptyContext "10am KAMAZN" [TimeReference {trText = "10am", trTimeOfDay = 10:00:00, trDateRef = Nothing, trLocationRef = Nothing}] ->>> parseTimeRefs "01:03 6 November America/Winnipeg" +>>> parseWithEmptyContext "01:03 6 November America/Winnipeg" [TimeReference {trText = "01:03 6 November America/Winnipeg", trTimeOfDay = 01:03:00, trDateRef = Just (DayOfMonthRef 6 (Just (11,Nothing))), trLocationRef = Just (TimeZoneRef America__Winnipeg)}] ->>> parseTimeRefs "01:03 6 November 2022 America/Winnipeg" +>>> parseWithEmptyContext "01:03 6 November 2022 America/Winnipeg" [TimeReference {trText = "01:03 6 November 2022 America/Winnipeg", trTimeOfDay = 01:03:00, trDateRef = Just (DayOfMonthRef 6 (Just (11,Just 2022))), trLocationRef = Just (TimeZoneRef America__Winnipeg)}] ->>> parseTimeRefs "01:03 2022 6 November America/Winnipeg" +>>> parseWithEmptyContext "01:03 2022 6 November America/Winnipeg" [TimeReference {trText = "01:03 2022 6 November America/Winnipeg", trTimeOfDay = 01:03:00, trDateRef = Just (DayOfMonthRef 6 (Just (11,Just 2022))), trLocationRef = Just (TimeZoneRef America__Winnipeg)}] ->>> parseTimeRefs "7.30 pm " +>>> parseWithEmptyContext "7.30 pm " [TimeReference {trText = "7.30 pm", trTimeOfDay = 19:30:00, trDateRef = Nothing, trLocationRef = Nothing}] ->>> parseTimeRefs "7.30" +>>> parseWithEmptyContext "7.30" [] ->>> parseTimeRefs "19h " +>>> parseWithEmptyContext "19h " [TimeReference {trText = "19h", trTimeOfDay = 19:00:00, trDateRef = Nothing, trLocationRef = Nothing}] ->>> parseTimeRefs "19h01 " +>>> parseWithEmptyContext "19h01 " [TimeReference {trText = "19h01", trTimeOfDay = 19:01:00, trDateRef = Nothing, trLocationRef = Nothing}] ->>> parseTimeRefs "7:30pm 03/08/2022" +>>> parseWithEmptyContext "7:30pm 03/08/2022" [TimeReference {trText = "7:30pm 03/08/2022", trTimeOfDay = 19:30:00, trDateRef = Just (DayOfMonthRef 3 (Just (8,Just 2022))), trLocationRef = Nothing}] ->>> parseTimeRefs "7:30pm 3-08-2022" +>>> parseWithEmptyContext "7:30pm 3-08-2022" [TimeReference {trText = "7:30pm 3-08-2022", trTimeOfDay = 19:30:00, trDateRef = Just (DayOfMonthRef 3 (Just (8,Just 2022))), trLocationRef = Nothing}] ->>> parseTimeRefs "7:30pm 3.08.2022" +>>> parseWithEmptyContext "7:30pm 3.08.2022" [TimeReference {trText = "7:30pm 3.08.2022", trTimeOfDay = 19:30:00, trDateRef = Just (DayOfMonthRef 3 (Just (8,Just 2022))), trLocationRef = Nothing}] ->>> parseTimeRefs "7:30pm 2022/08/3" +>>> parseWithEmptyContext "7:30pm 2022/08/3" [TimeReference {trText = "7:30pm 2022/08/3", trTimeOfDay = 19:30:00, trDateRef = Just (DayOfMonthRef 3 (Just (8,Just 2022))), trLocationRef = Nothing}] ->>> parseTimeRefs "2022.8.03 7:30 pm " +>>> parseWithEmptyContext "2022.8.03 7:30 pm " [TimeReference {trText = "2022.8.03 7:30 pm", trTimeOfDay = 19:30:00, trDateRef = Just (DayOfMonthRef 3 (Just (8,Just 2022))), trLocationRef = Nothing}] ->>> parseTimeRefs "7:30pm 2022.8.03 America/Havana" +>>> parseWithEmptyContext "7:30pm 2022.8.03 America/Havana" [TimeReference {trText = "7:30pm 2022.8.03 America/Havana", trTimeOfDay = 19:30:00, trDateRef = Just (DayOfMonthRef 3 (Just (8,Just 2022))), trLocationRef = Just (TimeZoneRef America__Havana)}] ->>> parseTimeRefs "tomorrow 10am -11 am" +>>> parseWithEmptyContext "tomorrow 10am -11 am" [TimeReference {trText = "tomorrow 10am", trTimeOfDay = 10:00:00, trDateRef = Just (DaysFromToday 1), trLocationRef = Nothing},TimeReference {trText = "tomorrow 11 am", trTimeOfDay = 11:00:00, trDateRef = Just (DaysFromToday 1), trLocationRef = Nothing}] ->>> parseTimeRefs "tomorrow 10am / 11 am" +>>> parseWithEmptyContext "tomorrow 10am / 11 am" [TimeReference {trText = "tomorrow 10am", trTimeOfDay = 10:00:00, trDateRef = Just (DaysFromToday 1), trLocationRef = Nothing},TimeReference {trText = "tomorrow 11 am", trTimeOfDay = 11:00:00, trDateRef = Just (DaysFromToday 1), trLocationRef = Nothing}] ->>> parseTimeRefs "Hi guys! Let’s have a sync call tomorrow? Almost every time from 7am-2pm UTC works (except 10:30am - 12pm UTC)" -[TimeReference {trText = "7am UTC", trTimeOfDay = 07:00:00, trDateRef = Nothing, trLocationRef = Just (TimeZoneAbbreviationRef (TimeZoneAbbreviationInfo {tzaiAbbreviation = "UTC", tzaiOffsetMinutes = 0, tzaiFullName = "UTC"}))},TimeReference {trText = "2pm UTC", trTimeOfDay = 14:00:00, trDateRef = Nothing, trLocationRef = Just (TimeZoneAbbreviationRef (TimeZoneAbbreviationInfo {tzaiAbbreviation = "UTC", tzaiOffsetMinutes = 0, tzaiFullName = "UTC"}))},TimeReference {trText = "10:30am UTC", trTimeOfDay = 10:30:00, trDateRef = Nothing, trLocationRef = Just (TimeZoneAbbreviationRef (TimeZoneAbbreviationInfo {tzaiAbbreviation = "UTC", tzaiOffsetMinutes = 0, tzaiFullName = "UTC"}))},TimeReference {trText = "12pm UTC", trTimeOfDay = 12:00:00, trDateRef = Nothing, trLocationRef = Just (TimeZoneAbbreviationRef (TimeZoneAbbreviationInfo {tzaiAbbreviation = "UTC", tzaiOffsetMinutes = 0, tzaiFullName = "UTC"}))}] - ->>> parseTimeRefs "between 10am and 11:30am UTC" +>>> parseWithEmptyContext "between 10am and 11:30am UTC" [TimeReference {trText = "10am UTC", trTimeOfDay = 10:00:00, trDateRef = Nothing, trLocationRef = Just (TimeZoneAbbreviationRef (TimeZoneAbbreviationInfo {tzaiAbbreviation = "UTC", tzaiOffsetMinutes = 0, tzaiFullName = "UTC"}))},TimeReference {trText = "11:30am UTC", trTimeOfDay = 11:30:00, trDateRef = Nothing, trLocationRef = Just (TimeZoneAbbreviationRef (TimeZoneAbbreviationInfo {tzaiAbbreviation = "UTC", tzaiOffsetMinutes = 0, tzaiFullName = "UTC"}))}] ->>> parseTimeRefs "Let's go on Wednesday at 10:00 or 11:00." +>>> parseWithEmptyContext "Let's go on Wednesday at 10:00 or 11:00." [TimeReference {trText = "on Wednesday at 10:00", trTimeOfDay = 10:00:00, trDateRef = Just (DayOfWeekRef Wednesday), trLocationRef = Nothing},TimeReference {trText = "on Wednesday 11:00", trTimeOfDay = 11:00:00, trDateRef = Just (DayOfWeekRef Wednesday), trLocationRef = Nothing}] ->>> parseTimeRefs "How about Wednesday at 10:00 / 11:00 or 14:00 / 15:00" +>>> parseWithEmptyContext "How about Wednesday at 10:00 / 11:00 or 14:00 / 15:00" [TimeReference {trText = "Wednesday at 10:00", trTimeOfDay = 10:00:00, trDateRef = Just (DayOfWeekRef Wednesday), trLocationRef = Nothing},TimeReference {trText = "Wednesday 11:00", trTimeOfDay = 11:00:00, trDateRef = Just (DayOfWeekRef Wednesday), trLocationRef = Nothing},TimeReference {trText = "14:00", trTimeOfDay = 14:00:00, trDateRef = Just (DayOfWeekRef Wednesday), trLocationRef = Nothing},TimeReference {trText = "15:00", trTimeOfDay = 15:00:00, trDateRef = Just (DayOfWeekRef Wednesday), trLocationRef = Nothing}] ->>> parseTimeRefs "How about Wednesday at 10:00 / 11:00 OR 14:00 / 15:00 at Thursday UTC" -[TimeReference {trText = "Wednesday at 10:00 OR", trTimeOfDay = 10:00:00, trDateRef = Just (DayOfWeekRef Wednesday), trLocationRef = Just (UnknownTimeZoneAbbreviationRef (UnknownTimeZoneAbbrev {utzaAbbrev = "OR", utzaCandidates = []}))},TimeReference {trText = "Wednesday 11:00 OR", trTimeOfDay = 11:00:00, trDateRef = Just (DayOfWeekRef Wednesday), trLocationRef = Just (UnknownTimeZoneAbbreviationRef (UnknownTimeZoneAbbrev {utzaAbbrev = "OR", utzaCandidates = []}))},TimeReference {trText = "14:00 at Thursday UTC", trTimeOfDay = 14:00:00, trDateRef = Just (DayOfWeekRef Thursday), trLocationRef = Just (TimeZoneAbbreviationRef (TimeZoneAbbreviationInfo {tzaiAbbreviation = "UTC", tzaiOffsetMinutes = 0, tzaiFullName = "UTC"}))},TimeReference {trText = "15:00 at Thursday UTC", trTimeOfDay = 15:00:00, trDateRef = Just (DayOfWeekRef Thursday), trLocationRef = Just (TimeZoneAbbreviationRef (TimeZoneAbbreviationInfo {tzaiAbbreviation = "UTC", tzaiOffsetMinutes = 0, tzaiFullName = "UTC"}))}] +>>> parseWithEmptyContext "How about Wednesday at 10:00 / 11:00 OR 14:00 / 15:00 at Thursday UTC" +[TimeReference {trText = "Wednesday at 10:00", trTimeOfDay = 10:00:00, trDateRef = Just (DayOfWeekRef Wednesday), trLocationRef = Just (TimeZoneAbbreviationRef (TimeZoneAbbreviationInfo {tzaiAbbreviation = "UTC", tzaiOffsetMinutes = 0, tzaiFullName = "UTC"}))},TimeReference {trText = "Wednesday 11:00", trTimeOfDay = 11:00:00, trDateRef = Just (DayOfWeekRef Wednesday), trLocationRef = Just (TimeZoneAbbreviationRef (TimeZoneAbbreviationInfo {tzaiAbbreviation = "UTC", tzaiOffsetMinutes = 0, tzaiFullName = "UTC"}))},TimeReference {trText = "14:00 at Thursday UTC", trTimeOfDay = 14:00:00, trDateRef = Just (DayOfWeekRef Thursday), trLocationRef = Just (TimeZoneAbbreviationRef (TimeZoneAbbreviationInfo {tzaiAbbreviation = "UTC", tzaiOffsetMinutes = 0, tzaiFullName = "UTC"}))},TimeReference {trText = "15:00 at Thursday UTC", trTimeOfDay = 15:00:00, trDateRef = Just (DayOfWeekRef Thursday), trLocationRef = Just (TimeZoneAbbreviationRef (TimeZoneAbbreviationInfo {tzaiAbbreviation = "UTC", tzaiOffsetMinutes = 0, tzaiFullName = "UTC"}))}] ->>> parseTimeRefs "10-11pm tomorrow works for me" +>>> parseWithEmptyContext "10-11pm tomorrow works for me" [TimeReference {trText = "10pm tomorrow", trTimeOfDay = 22:00:00, trDateRef = Just (DaysFromToday 1), trLocationRef = Nothing},TimeReference {trText = "11pm tomorrow", trTimeOfDay = 23:00:00, trDateRef = Just (DaysFromToday 1), trLocationRef = Nothing}] ->>> parseTimeRefs "How about 10:00 or 11:00 pm tomorrow?" +>>> parseWithEmptyContext "How about 10:00 or 11:00 pm tomorrow?" [TimeReference {trText = "10:00 pm tomorrow", trTimeOfDay = 22:00:00, trDateRef = Just (DaysFromToday 1), trLocationRef = Nothing},TimeReference {trText = "11:00 pm tomorrow", trTimeOfDay = 23:00:00, trDateRef = Just (DaysFromToday 1), trLocationRef = Nothing}] ->>> parseTimeRefs "7.30-8.30pm" +>>> parseWithEmptyContext "7.30-8.30pm" [TimeReference {trText = "7.30pm", trTimeOfDay = 19:30:00, trDateRef = Nothing, trLocationRef = Nothing},TimeReference {trText = "8.30pm", trTimeOfDay = 20:30:00, trDateRef = Nothing, trLocationRef = Nothing}] ->>> parseTimeRefs "7.30am-8.30pm tomorrow" +>>> parseWithEmptyContext "7.30am-8.30pm tomorrow" [TimeReference {trText = "7.30am tomorrow", trTimeOfDay = 07:30:00, trDateRef = Just (DaysFromToday 1), trLocationRef = Nothing},TimeReference {trText = "8.30pm tomorrow", trTimeOfDay = 20:30:00, trDateRef = Just (DaysFromToday 1), trLocationRef = Nothing}] ->>> parseTimeRefs "7.30-8.30" +>>> parseWithEmptyContext "7.30-8.30" [] +>>> parseWithEmptyContext "Hi guys! Let’s have a sync call tomorrow? Almost every time from 7am-2pm UTC works (except 10:30am - 12pm UTC)" +[TimeReference {trText = "7am UTC", trTimeOfDay = 07:00:00, trDateRef = Just (DaysFromToday 1), trLocationRef = Just (TimeZoneAbbreviationRef (TimeZoneAbbreviationInfo {tzaiAbbreviation = "UTC", tzaiOffsetMinutes = 0, tzaiFullName = "UTC"}))},TimeReference {trText = "2pm UTC", trTimeOfDay = 14:00:00, trDateRef = Just (DaysFromToday 1), trLocationRef = Just (TimeZoneAbbreviationRef (TimeZoneAbbreviationInfo {tzaiAbbreviation = "UTC", tzaiOffsetMinutes = 0, tzaiFullName = "UTC"}))},TimeReference {trText = "10:30am UTC", trTimeOfDay = 10:30:00, trDateRef = Just (DaysFromToday 1), trLocationRef = Just (TimeZoneAbbreviationRef (TimeZoneAbbreviationInfo {tzaiAbbreviation = "UTC", tzaiOffsetMinutes = 0, tzaiFullName = "UTC"}))},TimeReference {trText = "12pm UTC", trTimeOfDay = 12:00:00, trDateRef = Just (DaysFromToday 1), trLocationRef = Just (TimeZoneAbbreviationRef (TimeZoneAbbreviationInfo {tzaiAbbreviation = "UTC", tzaiOffsetMinutes = 0, tzaiFullName = "UTC"}))}] + +>>> parseWithEmptyContext "How about tomorrow? Let's try something between 10am and 11am" +[TimeReference {trText = "10am", trTimeOfDay = 10:00:00, trDateRef = Just (DaysFromToday 1), trLocationRef = Nothing},TimeReference {trText = "11am", trTimeOfDay = 11:00:00, trDateRef = Just (DaysFromToday 1), trLocationRef = Nothing}] + +>>> parseWithEmptyContext "I wanted to meet tomorrow, but seems it's not possible... maybe 3rd march? Let's try something between 10am and 11am UTC" +[TimeReference {trText = "10am UTC", trTimeOfDay = 10:00:00, trDateRef = Just (DayOfMonthRef 3 (Just (3,Nothing))), trLocationRef = Just (TimeZoneAbbreviationRef (TimeZoneAbbreviationInfo {tzaiAbbreviation = "UTC", tzaiOffsetMinutes = 0, tzaiFullName = "UTC"}))},TimeReference {trText = "11am UTC", trTimeOfDay = 11:00:00, trDateRef = Just (DayOfMonthRef 3 (Just (3,Nothing))), trLocationRef = Just (TimeZoneAbbreviationRef (TimeZoneAbbreviationInfo {tzaiAbbreviation = "UTC", tzaiOffsetMinutes = 0, tzaiFullName = "UTC"}))}] + +>>> parseWithEmptyContext "How about tomorrow 8am-12am UTC? For me 9am is the most perfect time" +[TimeReference {trText = "tomorrow 8am UTC", trTimeOfDay = 08:00:00, trDateRef = Just (DaysFromToday 1), trLocationRef = Just (TimeZoneAbbreviationRef (TimeZoneAbbreviationInfo {tzaiAbbreviation = "UTC", tzaiOffsetMinutes = 0, tzaiFullName = "UTC"}))},TimeReference {trText = "tomorrow 12am UTC", trTimeOfDay = 12:00:00, trDateRef = Just (DaysFromToday 1), trLocationRef = Just (TimeZoneAbbreviationRef (TimeZoneAbbreviationInfo {tzaiAbbreviation = "UTC", tzaiOffsetMinutes = 0, tzaiFullName = "UTC"}))},TimeReference {trText = "9am", trTimeOfDay = 09:00:00, trDateRef = Just (DaysFromToday 1), trLocationRef = Just (TimeZoneAbbreviationRef (TimeZoneAbbreviationInfo {tzaiAbbreviation = "UTC", tzaiOffsetMinutes = 0, tzaiFullName = "UTC"}))}] -} -parseTimeRefs :: Text -> [TimeReference] +parseTimeRefs :: Text -> State TimeContext [TimeReference] parseTimeRefs = -- TODO use better error handling - fromMaybe [] + applyContexts + . fromMaybe [] . parseMaybe timeRefsParser -- time reference can be either at the beginning or after a space . (Whitespace :) . tokenize +-- | Parse given text using provided time context and produce new time context. +parseWithEmptyContext :: Text -> [TimeReference] +parseWithEmptyContext = flip evalState emptyTimeContext . parseTimeRefs + +applyContexts :: [ApplyContextToken] -> State TimeContext [TimeReference] +applyContexts lst = mapMaybe getTimeRef <$> mapM processACTToken lst + where + getTimeRef (ACTTimeReference t) = Just t + getTimeRef _ = Nothing + + processACTToken :: ApplyContextToken -> State TimeContext ApplyContextToken + processACTToken = \case + d@(ACTDateReference ma) -> do + setCurrentDate $ mtValue ma + pure d + ACTTimeReference tr -> do + tr' <- useCurrentDate tr >>= useCurrentLoc + pure $ ACTTimeReference tr' + where + setCurrentDate :: DateReference -> State TimeContext () + setCurrentDate ref = tcCurrentDateRefL .= Just ref + + useCurrentDate :: TimeReference -> State TimeContext TimeReference + useCurrentDate tr = case trDateRef tr of + Nothing -> do + defaultCurDate <- tcCurrentDateRef <$> get + pure $ tr {trDateRef = defaultCurDate} + Just dr -> do + setCurrentDate dr + pure tr + + setCurrentLoc :: LocationReference -> State TimeContext () + setCurrentLoc ref = tcCurrentLocRefL .= Just ref + + useCurrentLoc :: TimeReference -> State TimeContext TimeReference + useCurrentLoc tr = case trLocationRef tr of + Nothing -> do + defaultCurLoc <- tcCurrentLocRef <$> get + pure $ tr {trLocationRef = defaultCurLoc} + Just lr -> do + setCurrentLoc lr + pure tr + -- | Parser for multiple 'TimeReference' s. -- -- This looks for all of them in the input and ignores everything surrounding. -timeRefsParser :: TzParser [TimeReference] +timeRefsParser :: TzParser [ApplyContextToken] timeRefsParser = choice' [ do - tr <- try timeRefConjugParser + tr <- try applyContextTokenParser trs <- timeRefsParser return $ tr <> trs , anySingle >> timeRefsParser , takeRest >> pure [] ] +data ApplyContextToken + = ACTTimeReference TimeReference + | ACTDateReference (Matched DateReference) + deriving stock (Show, Eq, Generic) + +-- | Either time reference or date reference +applyContextTokenParser :: TzParser [ApplyContextToken] +applyContextTokenParser = choice' + [ map ACTTimeReference <$> timeRefConjugParser + , L.singleton . ACTDateReference . uncurry Matched <$> match dateRefParser + ] + -- | Parses entries like @between 10am and 11am@ or -- @10am-11am on thursday or 1pm-2pm on wednesday@ timeRefConjugParser :: TzParser [TimeReference] @@ -408,8 +476,10 @@ timeEntryParser = do applyIsAm isAm ref = do let shouldAppend = isNothing $ getIsAm ref whenFunc shouldAppend (modifyText (<> mtText isAm)) $ fmap (($ mtValue isAm) . snd) ref + extractDefaultResult :: Matched (Maybe (TimeOfDay, a), b) -> Maybe (Matched TimeOfDay) extractDefaultResult ref = traverse (fmap fst . fst) ref + case isAmOptions of [isAm] -> pure $ (TEPair `on` applyIsAm isAm) firstRef secondRef _ -> maybe empty pure $ TEPair <$> extractDefaultResult firstRef <*> extractDefaultResult secondRef @@ -528,7 +598,7 @@ dateParserFormat = do y <- yearParser m <- delim >> monthOfYearNumberParser (d, _) <- delim >> dayOfMonthParser - pure $ DayOfMonthRef d $ Just (m, (Just y)) + pure $ DayOfMonthRef d $ Just (m, Just y) format2 :: TzParser a -> TzParser DateReference format2 delim = do @@ -543,7 +613,7 @@ dateParserVerbose :: TzParser DateReference dateParserVerbose = do precYear <- optional' ( do y <- yearParser; spacedComma; pure y ) _ <- optional' (relationPreposition >> space) - (dayOfMonth, monthOfYear) <- choice' $ + (dayOfMonth, monthOfYear) <- choice' [ do _ <- optional' (word' "the" >> space) (dayOfMonth, hasSuffix) <- dayOfMonthParser @@ -650,8 +720,7 @@ isPossibleTimezoneAbbrev w = T.all isUpper w && T.length w >= 2 && T.length w <= 5 - && w /= "AM" - && w /= "PM" + && notElem w ["AM", "PM", "OR", "AND"] -------------------------------------------------------------------------------- -- Storages diff --git a/src/TzBot/ProcessEvents/Common.hs b/src/TzBot/ProcessEvents/Common.hs index a93eee1..8c25c18 100644 --- a/src/TzBot/ProcessEvents/Common.hs +++ b/src/TzBot/ProcessEvents/Common.hs @@ -4,7 +4,7 @@ module TzBot.ProcessEvents.Common ( openModalCommon - , getTimeReferencesFromMessage + , getTimeReferencesAndNewStateFromMessage -- * exported for tests , ignoreCodeBlocksManually @@ -18,15 +18,17 @@ import Data.Text qualified as T import Fmt (listF) import Text.Interpolation.Nyan (int, rmode') +import TzBot.Cache qualified as Cache import TzBot.Feedback.Dialog (insertDialogEntry) import TzBot.Feedback.Dialog.Types import TzBot.Logger import TzBot.Parser (parseTimeRefs) import TzBot.Render (TranslationPairs, asForModalM, renderAllTP, renderTemplate) -import TzBot.Slack (BotM, getUserCached, startModal) +import TzBot.Slack (BotM, BotState(bsMessageCache), getUserCached, startModal) import TzBot.Slack.API import TzBot.Slack.API.MessageBlock (UnknownBlockElementLevel2Error(ubeType), extractPieces, splitExtractErrors) +import TzBot.TimeContext import TzBot.TimeReference (TimeReference) import TzBot.Util (WithUnknown(unUnknown)) @@ -45,7 +47,9 @@ openModalCommon openModalCommon message channelId whoTriggeredId triggerId mkModalFunc = do let msgText = mText message msgTimestamp = mTs message - mbTimeRefs <- nonEmpty <$> getTimeReferencesFromMessage message + mbTimeRefs <- fmap (nonEmpty . fst) $ + asks bsMessageCache >>= Cache.fetchWithCache msgId \_ -> + getTimeReferencesAndNewStateFromMessage emptyTimeContext message sender <- getUserCached $ mUser message translationPairs <- fmap join $ forM mbTimeRefs $ \neTimeRefs -> do whoTriggered <- getUserCached whoTriggeredId @@ -66,14 +70,22 @@ openModalCommon message channelId whoTriggeredId triggerId mkModalFunc = do insertDialogEntry guid metadata let modal = mkModalFunc msgText translationPairs guid startModal $ OpenViewReq modal triggerId + where + msgId = mMessageId message -- | Extract separate text pieces from the Slack message that can contain -- the whole time reference and try to find time references inside them. -getTimeReferencesFromMessage - :: Message - -> BotM [TimeReference] -getTimeReferencesFromMessage message = - concatMap parseTimeRefs <$> getTextPiecesFromMessage message +-- Old context (date, timezone, offset, etc.) is used for processing +-- and new one is produced. +getTimeReferencesAndNewStateFromMessage + :: TimeContext + -> Message + -> BotM ([TimeReference], TimeContext) +getTimeReferencesAndNewStateFromMessage oldState message = do + pieces <- getTextPiecesFromMessage message + pure $ + first concat $ + runState (mapM parseTimeRefs pieces) oldState -- | Extract separate text pieces from the Slack message that can contain -- the whole time reference. The main way is analyzing the message's block diff --git a/src/TzBot/ProcessEvents/Message.hs b/src/TzBot/ProcessEvents/Message.hs index 43257ef..2aec041 100644 --- a/src/TzBot/ProcessEvents/Message.hs +++ b/src/TzBot/ProcessEvents/Message.hs @@ -19,12 +19,13 @@ import UnliftIO qualified import TzBot.Cache qualified as Cache import TzBot.Config (Config(..)) import TzBot.Logger -import TzBot.ProcessEvents.Common (getTimeReferencesFromMessage) +import TzBot.ProcessEvents.Common import TzBot.Render import TzBot.Slack import TzBot.Slack.API import TzBot.Slack.Events import TzBot.Slack.Fixtures qualified as Fixtures +import TzBot.TimeContext import TzBot.TimeReference (TimeReference(..)) import TzBot.Util (whenT, withMaybe) @@ -80,19 +81,16 @@ processMessageEvent evt = katipAddContext (MessageContext msgId) $ whenJustM (filterMessageTypeWithLog evt) $ \mEventType -> whenJustM (withSenderNotBot evt) $ \sender -> do - timeRefs <- getTimeReferencesFromMessage msg - processMessageEvent' evt mEventType sender timeRefs + processMessageEvent' evt mEventType sender where - msg = meMessage evt msgId = mMessageId $ meMessage evt processMessageEvent' :: MessageEvent -> MessageEventType -> User - -> [TimeReference] -> BotM () -processMessageEvent' evt mEventType sender timeRefs = +processMessageEvent' evt mEventType sender = case meChannelType evt of Just CTDirectChannel -> handleDirectMessage _ -> case mEventType of @@ -155,25 +153,52 @@ processMessageEvent' evt mEventType sender timeRefs = } sendEphemeralMessage req + -- threadId is the same as its parent's messageId, + -- so use messageId if there's no thread yet + getMessageThreadId :: ThreadId + getMessageThreadId = fromMaybe (ThreadId $ unMessageId msgId) mbThreadId + handleMessageChanged :: BotM () handleMessageChanged = katipAddNamespaceText "edit" do messageRefsCache <- asks bsMessageCache - mbMessageRefs <- Cache.lookup msgId messageRefsCache + convStateCache <- asks bsConversationStateCache + mbMessageRefsAndState <- Cache.lookup msgId messageRefsCache -- if not found or expired, just ignore this message -- it's too old or just didn't contain any time refs - whenJust mbMessageRefs $ \oldRefs -> do - let newRefsFound = not $ all (`elem` oldRefs) timeRefs + whenJust mbMessageRefsAndState $ \(oldRefs, stateBefore) -> do + (newRefs, stateAfter) <- + getTimeReferencesAndNewStateFromMessage stateBefore msg + mbConversationState <- Cache.lookup getMessageThreadId convStateCache + -- If the conversation state was defined after processing this + -- message, we should update it. + whenJust mbConversationState \(lastMsgId, _conversationState) -> + when (lastMsgId == msgId) $ + Cache.insert getMessageThreadId (msgId, stateAfter) convStateCache + + let newRefsFound = not $ all (`elem` oldRefs) newRefs -- no new references found, ignoring - when newRefsFound $ withNonEmptyTimeRefs timeRefs \neTimeRefs -> do - Cache.insert msgId timeRefs messageRefsCache + when newRefsFound $ withNonEmptyTimeRefs newRefs \neTimeRefs -> do + -- This cache always keeps only "before" state in order to correctly + -- translate further edits. + Cache.insert msgId (newRefs, stateBefore) messageRefsCache permalink <- getMessagePermalinkCached channelId msgId handleChannelMessageCommon (Just permalink) neTimeRefs handleNewMessage :: BotM () handleNewMessage = do - withNonEmptyTimeRefs timeRefs $ \neTimeRefs -> do + convStateCache <- asks bsConversationStateCache + conversationState <- + fmap (fromMaybe emptyTimeContext . fmap snd . join) $ + traverse (\t -> Cache.lookup t convStateCache) mbThreadId + (timeRefs, newState) <- + getTimeReferencesAndNewStateFromMessage conversationState msg + when (not $ null timeRefs) $ -- save message only if time references are present - asks bsMessageCache >>= Cache.insert msgId timeRefs + asks bsMessageCache >>= Cache.insert msgId (timeRefs, newState) + Cache.insert getMessageThreadId (msgId, newState) convStateCache + asks bsMessageCache >>= Cache.insert msgId (timeRefs, conversationState) + + withNonEmptyTimeRefs timeRefs $ \neTimeRefs -> do handleChannelMessageCommon Nothing neTimeRefs handleChannelMessageCommon :: Maybe Text -> NonEmpty TimeReference -> BotM () @@ -195,8 +220,9 @@ processMessageEvent' evt mEventType sender timeRefs = ephemeralsMailing channelId sendActionLocal handleDirectMessage :: BotM () - handleDirectMessage = - when (mEventType /= METMessageEdited) $ + handleDirectMessage = when (mEventType /= METMessageEdited) $ do + (timeRefs, _stateAfter) <- + getTimeReferencesAndNewStateFromMessage emptyTimeContext msg withNonEmptyTimeRefs timeRefs $ \neTimeRefs -> do -- According to -- https://forums.slackcommunity.com/s/question/0D53a00008vsItQCAU diff --git a/src/TzBot/RunMonad.hs b/src/TzBot/RunMonad.hs index 061ab89..e234397 100644 --- a/src/TzBot/RunMonad.hs +++ b/src/TzBot/RunMonad.hs @@ -17,6 +17,7 @@ import TzBot.Cache (TzCache) import TzBot.Config.Types (BotConfig) import TzBot.Feedback.Dialog.Types (ReportDialogEntry, ReportDialogId) import TzBot.Slack.API +import TzBot.TimeContext (TimeContext) import TzBot.TimeReference import TzBot.Util (postfixFields) @@ -32,8 +33,13 @@ data BotState = BotState , bsUserInfoCache :: TzCache UserId User , bsConversationMembersCache :: TzCache ChannelId (S.Set UserId) , bsReportEntries :: TzCache ReportDialogId ReportDialogEntry - , bsMessageCache :: TzCache MessageId [TimeReference] + , bsMessageCache :: TzCache MessageId ([TimeReference], TimeContext) + -- ^ Used for keeping relevant time references and conversation state + -- that was _before_ this message, i.e. applied to time refs of this message. , bsMessageLinkCache :: TzCache MessageId Text + , bsConversationStateCache :: TzCache ThreadId (MessageId, TimeContext) + -- ^ State of a thread: current state and ID of a message which is origin + -- of that state , bsLogNamespace :: K.Namespace , bsLogContext :: K.LogContexts , bsLogEnv :: K.LogEnv diff --git a/src/TzBot/Slack/API.hs b/src/TzBot/Slack/API.hs index 229f48c..4de577c 100644 --- a/src/TzBot/Slack/API.hs +++ b/src/TzBot/Slack/API.hs @@ -194,7 +194,7 @@ instance FromJSON ChannelType where newtype ThreadId = ThreadId { unThreadId :: Text } deriving stock (Eq, Show) - deriving newtype (ToHttpApiData, FromJSON, ToJSON, Buildable) + deriving newtype (ToHttpApiData, FromJSON, ToJSON, Buildable, Hashable) newtype MessageId = MessageId { unMessageId :: Text } deriving stock (Eq, Show, Ord) diff --git a/src/TzBot/TimeContext.hs b/src/TzBot/TimeContext.hs new file mode 100644 index 0000000..2d850a7 --- /dev/null +++ b/src/TzBot/TimeContext.hs @@ -0,0 +1,23 @@ + +-- SPDX-FileCopyrightText: 2022 Serokell +-- +-- SPDX-License-Identifier: MPL-2.0 + +module TzBot.TimeContext where + +import Universum hiding (many, toList, try) + +import Control.Lens.TH (makeLensesWith) +import TzBot.Instances () +import TzBot.TimeReference +import TzBot.Util + +data TimeContext = TimeContext + { tcCurrentDateRef :: Maybe DateReference + , tcCurrentLocRef :: Maybe LocationReference + } deriving stock (Show, Eq, Generic) + +emptyTimeContext :: TimeContext +emptyTimeContext = TimeContext Nothing Nothing + +makeLensesWith postfixFields ''TimeContext diff --git a/tzbot.cabal b/tzbot.cabal index f31b5c0..4dba63b 100644 --- a/tzbot.cabal +++ b/tzbot.cabal @@ -1,6 +1,6 @@ cabal-version: 1.12 --- This file has been generated from package.yaml by hpack version 0.34.7. +-- This file has been generated from package.yaml by hpack version 0.35.1. -- -- see: https://github.com/sol/hpack @@ -54,6 +54,7 @@ library TzBot.Slack.Events.ViewPayload TzBot.Slack.Fixtures TzBot.Slack.Modal + TzBot.TimeContext TzBot.TimeReference TzBot.Util other-modules: