aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorJacques Comeaux <jacquesrcomeaux@gmail.com>2022-05-29 16:07:59 -0500
committerJacques Comeaux <jacquesrcomeaux@gmail.com>2022-05-29 16:07:59 -0500
commit8a06c083fab9c66e754c7ed5a75dd4d89131c43e (patch)
tree28098c165bce0d8ebb5dda2d5390e63ddca1ab1e /src
parent399ab3ee1653bbc0da69e2cc299aa2a313e765b4 (diff)
Improve error reporting
Diffstat (limited to 'src')
-rw-r--r--src/Tomato/App.hs32
-rw-r--r--src/Tomato/Bot.hs19
-rw-r--r--src/Tomato/Data/Except.hs9
-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.hs36
-rw-r--r--src/Tomato/Retrieve.hs34
-rw-r--r--src/Tomato/Validate.hs3
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) &&