diff options
author | Jacques Comeaux <jacquesrcomeaux@gmail.com> | 2022-05-29 16:07:59 -0500 |
---|---|---|
committer | Jacques Comeaux <jacquesrcomeaux@gmail.com> | 2022-05-29 16:07:59 -0500 |
commit | 8a06c083fab9c66e754c7ed5a75dd4d89131c43e (patch) | |
tree | 28098c165bce0d8ebb5dda2d5390e63ddca1ab1e /src | |
parent | 399ab3ee1653bbc0da69e2cc299aa2a313e765b4 (diff) |
Improve error reporting
Diffstat (limited to 'src')
-rw-r--r-- | src/Tomato/App.hs | 32 | ||||
-rw-r--r-- | src/Tomato/Bot.hs | 19 | ||||
-rw-r--r-- | src/Tomato/Data/Except.hs | 9 | ||||
-rw-r--r-- | src/Tomato/Data/Message.hs (renamed from src/Data/Message.hs) | 2 | ||||
-rw-r--r-- | src/Tomato/Data/Tomato.hs (renamed from src/Data/Tomato.hs) | 2 | ||||
-rw-r--r-- | src/Tomato/Post.hs | 36 | ||||
-rw-r--r-- | src/Tomato/Retrieve.hs | 34 | ||||
-rw-r--r-- | src/Tomato/Validate.hs | 3 |
8 files changed, 103 insertions, 34 deletions
diff --git a/src/Tomato/App.hs b/src/Tomato/App.hs new file mode 100644 index 0000000..14bf61a --- /dev/null +++ b/src/Tomato/App.hs @@ -0,0 +1,32 @@ +module Tomato.App + ( App (..) + , runApp + ) where + +import RIO + + +data App = App + { appLogFunc :: !LogFunc + , appToken :: !ByteString + , appBotId :: !Text + , appFile :: !FilePath + , appClientId :: !Text + } + +instance HasLogFunc App where + logFuncL = lens appLogFunc (\x y -> x { appLogFunc = y }) + +runApp :: RIO App a -> IO a +runApp inner = do + logOptions' <- logOptionsHandle stderr False + let logOptions = setLogUseTime True $ setLogUseLoc True logOptions' + withLogFunc logOptions $ \logFunc -> do + let app = App + { appLogFunc = logFunc + , appToken = "t2YhlxxwZmn2cWfkAomjMc6BgVPMaC5NRkqHzGQl" + , appBotId = "713bcb4a4604006c944804552c" + , appFile = "tomato.png" + , appClientId = "FbzqI-oR7277JwL1ZGsyUw7yG1F5U0U3WhQ3kOW71Do" + } + runRIO app inner diff --git a/src/Tomato/Bot.hs b/src/Tomato/Bot.hs new file mode 100644 index 0000000..8d4cf6d --- /dev/null +++ b/src/Tomato/Bot.hs @@ -0,0 +1,19 @@ +module Tomato.Bot + ( tomatoBot + ) where + +import RIO + +import Tomato.App (App) +import Tomato.Post (postTomato) +import Tomato.Retrieve (randomTomato) + + +-- | Fetch and post a tomato +tomatoBot :: RIO App () +tomatoBot = do + logInfo $ "Fetching tomato" + randomTomato + logInfo $ "Posting tomato" + postTomato + logInfo $ "Done" diff --git a/src/Tomato/Data/Except.hs b/src/Tomato/Data/Except.hs new file mode 100644 index 0000000..30212cf --- /dev/null +++ b/src/Tomato/Data/Except.hs @@ -0,0 +1,9 @@ +module Tomato.Data.Except + ( DecodeException (..) + ) where + +import RIO + +data DecodeException = DecodeException !Text + deriving (Show, Typeable) +instance Exception DecodeException diff --git a/src/Data/Message.hs b/src/Tomato/Data/Message.hs index c218cdc..0f650db 100644 --- a/src/Data/Message.hs +++ b/src/Tomato/Data/Message.hs @@ -1,5 +1,5 @@ {-# LANGUAGE DeriveGeneric #-} -module Data.Message +module Tomato.Data.Message ( InMessage (..) , GMIUrl (..) , OutMessage (..) diff --git a/src/Data/Tomato.hs b/src/Tomato/Data/Tomato.hs index fc9b8b5..4b75adf 100644 --- a/src/Data/Tomato.hs +++ b/src/Tomato/Data/Tomato.hs @@ -1,4 +1,4 @@ -module Data.Tomato +module Tomato.Data.Tomato ( Links (..) , Tomato (..) , Urls (..) diff --git a/src/Tomato/Post.hs b/src/Tomato/Post.hs index c3bd847..4adcdbb 100644 --- a/src/Tomato/Post.hs +++ b/src/Tomato/Post.hs @@ -10,6 +10,9 @@ import Network.HTTP.Req ( jsonResponse , ignoreResponse , req + , Req + , defaultHttpConfig + , runReq , ReqBodyFile (..) , ReqBodyJson (..) , (/:) @@ -20,46 +23,47 @@ import Network.HTTP.Req , responseBody ) -import Data.Message (GMIUrl (..), OutMessage (..)) +import Tomato.Data.Except (DecodeException (..)) +import Tomato.Data.Message (GMIUrl (..), OutMessage (..)) +import Tomato.App (App (..)) import qualified Data.Aeson as Ae import qualified Data.Aeson.KeyMap as Ae +import qualified RIO.Text as T --- TODO global config reader monad -tomatoFile :: FilePath -tomatoFile = "tomato.png" - -accessToken :: ByteString -accessToken = "t2YhlxxwZmn2cWfkAomjMc6BgVPMaC5NRkqHzGQl" - -botId :: Text -botId = "713bcb4a4604006c944804552c" -- | Post the current tomato -postTomato :: Req () +postTomato :: RIO App () postTomato = do url <- uploadTomato postMessage $ OutMessage url -- | Upload the current tomato to the GroupMe image server -uploadTomato :: Req GMIUrl +uploadTomato :: RIO App GMIUrl uploadTomato = do + tomatoFile <- asks appFile + accessToken <- asks appToken let url = https "image.groupme.com" /: "pictures" - js <- req POST url (ReqBodyFile tomatoFile) jsonResponse $ + js <- rr $ req POST url (ReqBodyFile tomatoFile) jsonResponse $ header "X-Access-Token" accessToken <> header "Content-Type" "image/png" gmURL <- case Ae.fromJSON (responseBody js) of Ae.Success r -> pure r - Ae.Error _s -> error "deal with this later" -- TODO error monad + Ae.Error s -> throwM $ DecodeException $ T.pack s return gmURL -- | Post a message to the group with image as attachment -postMessage :: OutMessage -> Req () +postMessage :: OutMessage -> RIO App () postMessage outMes = do + botId <- asks appBotId + accessToken <- asks appToken let url = https "api.groupme.com" /: "v3" /: "bots" /: "post" outMes' = case Ae.toJSON outMes of Ae.Object o -> Ae.Object $ Ae.insert "bot_id" (Ae.toJSON botId) o _ -> error "exceptional" - req POST url (ReqBodyJson outMes') ignoreResponse $ + rr $ req POST url (ReqBodyJson outMes') ignoreResponse $ header "X-Access-Token" accessToken return () + +rr :: Req a -> RIO App a +rr = runReq defaultHttpConfig diff --git a/src/Tomato/Retrieve.hs b/src/Tomato/Retrieve.hs index 80bdac6..2f7f20b 100644 --- a/src/Tomato/Retrieve.hs +++ b/src/Tomato/Retrieve.hs @@ -10,49 +10,53 @@ import Network.HTTP.Req ( jsonResponse , bsResponse , req + , Req + , runReq + , defaultHttpConfig , NoReqBody (..) , (/:) , (=:) - , Req , GET (..) , https , responseBody ) -import Data.Tomato (Tomato (..), Links (..)) +import Tomato.App (App (..)) +import Tomato.Data.Except (DecodeException (..)) +import Tomato.Data.Tomato (Tomato (..), Links (..)) import qualified Data.Aeson as Ae import qualified RIO.ByteString as B +import qualified RIO.Text as T --- TODO global config reader monad -tomatoFile :: FilePath -tomatoFile = "tomato.png" - -clientId :: Text -clientId = "FbzqI-oR7277JwL1ZGsyUw7yG1F5U0U3WhQ3kOW71Do" - -- | Get a random tomato -randomTomato :: Req () +randomTomato :: RIO App () randomTomato = queryTomato >>= downloadTomato -- | Ask for a tomato -queryTomato :: Req Tomato +queryTomato :: RIO App Tomato queryTomato = do + clientId <- asks appClientId let url = https "api.unsplash.com" /: "photos" /: "random" - js <- req GET url NoReqBody jsonResponse $ + js <- rr $ req GET url NoReqBody jsonResponse $ "query" =: ("tomato" :: Text) <> "client_id" =: clientId tomato <- case Ae.fromJSON (responseBody js) of Ae.Success r -> pure r - Ae.Error _s -> error "deal with this later" + Ae.Error s -> throwM $ DecodeException $ T.pack s return tomato -- | Download a specific tomato -downloadTomato :: Tomato -> Req () +downloadTomato :: Tomato -> RIO App () downloadTomato tom = do + tomatoFile <- asks appFile + clientId <- asks appClientId let url = download (links tom) - bs <- req GET url NoReqBody bsResponse $ + bs <- rr $ req GET url NoReqBody bsResponse $ "query" =: ("tomato" :: Text) <> "client_id" =: clientId B.writeFile tomatoFile (responseBody bs) + +rr :: Req a -> RIO App a +rr = runReq defaultHttpConfig diff --git a/src/Tomato/Validate.hs b/src/Tomato/Validate.hs index 0afc4b5..fe0d82a 100644 --- a/src/Tomato/Validate.hs +++ b/src/Tomato/Validate.hs @@ -4,9 +4,10 @@ module Tomato.Validate import RIO -import Data.Message (InMessage (..)) +import Tomato.Data.Message (InMessage (..)) +-- | Check if a message is a tomato request isTomato :: InMessage -> Bool isTomato m = not (hasAttach m) && |