fastTimeParser :: Data.Attoparsec.Text.Parser UTCTime fastTimeParser = do y <- decimal; char '-'; m <- decimal; char '-'; d <- decimal; char ' ' h <- decimal; char ':'; n <- decimal; char ':'; s <- decimal pure $ UTCTime (fromGregorian y m d) (fromIntegral (h * 3600 + n * 60 + s)) fastParseTime :: T.Text -> Maybe UTCTime fastParseTime t = either (const Nothing) Just $ parseOnly fastTimeParser t where parseWeechatLog :: T.Text -> T.Text -> (M.HashMap T.Text T.Text, V.Vector (UTCTime, WeechatLogEntry)) parseWeechatLog chan log = either (const $ (M.empty, V.empty)) id . (flip parseOnly $ log) $ execute M.empty V.empty where opGlyphs = S.fromList "+%@&~" execute hostmasks acc = do done <- atEnd if done then return (hostmasks, acc) else do ts <- weechatTimestampParser (hostmasks', result) <- choice [ Data.Attoparsec.Text.string "--\t" >> takeTill isEndOfLine >> char '\n' >> return (hostmasks, Nothing) , Data.Attoparsec.Text.string "<--\t" >> weechatPartMsgParser chan hostmasks , Data.Attoparsec.Text.string "<--\t" >> weechatKickMsgParser chan hostmasks , Data.Attoparsec.Text.string "<--\t" >> weechatQuitMsgParser hostmasks , Data.Attoparsec.Text.string "Welcome\t" >> takeTill isEndOfLine >> char '\n' >> return (hostmasks, Nothing) , Data.Attoparsec.Text.string "-->\t" >> weechatJoinMsgParser chan hostmasks , parseWeechatPrivMsg chan hostmasks ] end <- atEnd case result of Just entry -> if end then return $ (hostmasks', V.snoc acc $ (ts, entry)) else execute hostmasks' . V.snoc acc $ (ts, entry) Nothing -> execute hostmasks acc weechatTimestampParser :: Data.Attoparsec.Text.Parser UTCTime weechatTimestampParser = do t <- fastTimeParser Data.Attoparsec.Text.take 1 return t weechatJoinMsgParser :: T.Text -> M.HashMap T.Text T.Text -> Data.Attoparsec.Text.Parser (M.HashMap T.Text T.Text, Maybe WeechatLogEntry) weechatJoinMsgParser chan hostmasks = do n <- takeTill isHorizontalSpace Data.Attoparsec.Text.take 2 h <- takeTill (== ')') takeTill isEndOfLine (char '\n' >> return ()) <|> pure () let hostmasks' = M.insert n h hostmasks return (hostmasks', Just (WeechatJoinMsg n h chan)) weechatPartMsgParser :: T.Text -> M.HashMap T.Text T.Text -> Data.Attoparsec.Text.Parser (M.HashMap T.Text T.Text, Maybe WeechatLogEntry) weechatPartMsgParser chan hostmasks = do n <- takeTill isHorizontalSpace Data.Attoparsec.Text.take 2 h <- takeTill (== ')') Data.Attoparsec.Text.string ") has left " takeTill (== '(') Data.Attoparsec.Text.take 1 m <- takeTill isEndOfLine (char '\n' >> return ()) <|> pure () let hostmasks' = M.insert n h hostmasks return (hostmasks', Just (WeechatPartMsg n h chan (T.dropEnd 1 m))) weechatKickMsgParser :: T.Text -> M.HashMap T.Text T.Text -> Data.Attoparsec.Text.Parser (M.HashMap T.Text T.Text, Maybe WeechatLogEntry) weechatKickMsgParser chan hostmasks = do n <- takeTill isHorizontalSpace Data.Attoparsec.Text.take 1 string "has kicked " p <- takeTill isHorizontalSpace Data.Attoparsec.Text.take 2 m <- takeTill isEndOfLine (char '\n' >> return ()) <|> pure () let host = M.lookup n hostmasks return (hostmasks, Just (WeechatKickMsg n host p chan (T.dropEnd 1 m))) weechatQuitMsgParser :: M.HashMap T.Text T.Text -> Data.Attoparsec.Text.Parser (M.HashMap T.Text T.Text, Maybe WeechatLogEntry) weechatQuitMsgParser hostmasks = do n <- takeTill isHorizontalSpace Data.Attoparsec.Text.take 2 h <- takeTill (== ')') takeTill (== '(') Data.Attoparsec.Text.take 1 m <- takeTill isEndOfLine (char '\n' >> return ()) <|> pure () let hostmasks' = M.insert n h hostmasks return (hostmasks', Just (WeechatQuitMsg n h (T.dropEnd 1 m))) parseWeechatPrivMsg :: T.Text -> M.HashMap T.Text T.Text -> Data.Attoparsec.Text.Parser (M.HashMap T.Text T.Text, Maybe WeechatLogEntry) parseWeechatPrivMsg chan hostmasks = do n <- takeTill (== '\t') Data.Attoparsec.Text.take 1 m <- takeTill isEndOfLine (char '\n' >> return ()) <|> pure () let host = M.lookup n hostmasks return (hostmasks, Just (WeechatPrivMsg (T.filter (not . (`elem` opGlyphs)) n) host chan m))