content: {-# LANGUAGE OverloadedStrings #-}
  {-# LANGUAGE ScopedTypeVariables #-}
  
  import Control.Monad (when, void, forever)
  import UnliftIO (try, IOException) -- liftIO
  import UnliftIO.MVar
  import UnliftIO.Chan
  import UnliftIO.Concurrent (forkIO, killThread)
  import qualified Data.Text as T
  import qualified Data.Text.IO as TIO
  
  import Discord
  import Discord.Types
  import qualified Discord.Requests as R
  
  import ExampleUtils (getToken)
  
  main :: IO ()
  main = stateExample
  
  data State = State { pingCount :: Integer }
    deriving (Show, Read, Eq, Ord)
  
  -- | Counts how many pings we've seen across sessions
  stateExample :: IO ()
  stateExample = do
    tok <- getToken
  
    -- eventHandler is called concurrently, need to sync stdout
    printQueue <- newChan :: IO (Chan T.Text)
    threadId <- forkIO $ forever $ readChan printQueue >>= TIO.putStrLn
  
    -- try to read previous state, otherwise use 0
    state :: MVar (State) <- do
          mfile <- try $ read . T.unpack <$> TIO.readFile "./cachedState"
          s <- case mfile of
              Right file -> do
                      writeChan printQueue "loaded state from file"
                      pure file
              Left (_ :: IOException) -> do
                      writeChan printQueue "created new state"
                      pure $ State { pingCount = 0 }
          newMVar s
  
    t <- runDiscord $ def { discordToken = tok
                          , discordOnStart = writeChan printQueue "starting ping loop"
                          , discordOnEvent = eventHandler state printQueue
                          , discordOnEnd = do killThread threadId
                                              --
                                              s <- readMVar state
                                              TIO.writeFile "./cachedState" (T.pack (show s))
                          }
    TIO.putStrLn t
  
  
  eventHandler :: MVar State -> Chan T.Text -> Event -> DiscordHandler ()
  eventHandler state printQueue event = case event of
    -- respond to message, and modify state
    MessageCreate m -> when (not (fromBot m) && isPing m) $ do
      writeChan printQueue "got a ping!"
  
      s <- takeMVar state
  
      void $ restCall (R.CreateMessage (messageChannelId m) (T.pack ("Pong #" <> show (pingCount s))))
  
      putMVar state $ State { pingCount = pingCount s + 1 }
  
    _ -> pure ()
  
  
  fromBot :: Message -> Bool
  fromBot = userIsBot . messageAuthor
  
  isPing :: Message -> Bool
  isPing = ("ping" `T.isPrefixOf`) . T.toLower . messageContent
  