diff options
author | Jacques Comeaux <jacquesrcomeaux@gmail.com> | 2022-05-27 21:07:43 -0500 |
---|---|---|
committer | Jacques Comeaux <jacquesrcomeaux@gmail.com> | 2022-05-27 21:07:43 -0500 |
commit | 3d3163a1028e99e5073fd39adc9aee5d76a5e5a2 (patch) | |
tree | d1cff9ed00418183d3ed8de412d0fdccd7666eaf /src |
Initial Commit
Diffstat (limited to 'src')
-rw-r--r-- | src/Data/Message.hs | 78 | ||||
-rw-r--r-- | src/Data/Tomato.hs | 70 | ||||
-rw-r--r-- | src/Tomato/.Post.hs.swp | bin | 0 -> 12288 bytes | |||
-rw-r--r-- | src/Tomato/Post.hs | 65 | ||||
-rw-r--r-- | src/Tomato/Validate.hs | 16 |
5 files changed, 229 insertions, 0 deletions
diff --git a/src/Data/Message.hs b/src/Data/Message.hs new file mode 100644 index 0000000..dfbc20b --- /dev/null +++ b/src/Data/Message.hs @@ -0,0 +1,78 @@ +module Data.Message + ( InMessage (..) + , GMIUrl (..) + , OutMessage (..) + ) where + +import RIO + +import Data.Aeson ((.:), (.=)) +import Network.HTTP.Req + ( Url + , Scheme (Https) + , useHttpsURI + , renderUrl + ) + +import qualified Data.Aeson as Ae +import qualified Data.Aeson.Types as Ae +import qualified RIO.Vector as V +import qualified Text.URI as URI + +data InMessage = InMessage + { hasAttach :: !Bool + , groupId :: !Word + , isUser :: !Bool + , text :: !Text + } deriving Show + +instance Ae.FromJSON InMessage where + parseJSON = Ae.withObject "InMessage" $ \o -> InMessage + <$> (o .: "attachments" >>= Ae.withArray "attachments" (pure . not . null)) + <*> o .: "group-id" + <*> ((==) ("user" :: Text) <$> o .: "sender_type") + <*> o .: "text" + + +newtype GMIUrl = GMIUrl + { unGMIUrl :: Url Https + } deriving Show + +instance Ae.FromJSON GMIUrl where + parseJSON = Ae.withObject "payload" $ \o -> do + p <- o .: "payload" + u <- p .: "url" + GMIUrl <$> toUrl u + where + toUrl :: Text -> Ae.Parser (Url Https) + toUrl t = maybe mzero pure $ fmap fst $ useHttpsURI =<< URI.mkURI t + +data OutMessage = OutMessage + { image :: !GMIUrl + } deriving Show + +instance Ae.ToJSON OutMessage where + toJSON om = Ae.object + [ "attachments" .= + ( Ae.Array $ V.singleton attach ) + -- , "bot_id" .= botId + ] + where + attach :: Ae.Value + attach = Ae.object + [ "type" .= ("image" :: Text) + , "url" .= url + ] + url :: Text + url = renderUrl $ unGMIUrl $ image om + + -- { + -- "bot_id" : "j5abcdefg", + -- "text" : "Hello world", + -- "attachments" : [ + -- { + -- "type" : "image", + -- "url" : "https://i.groupme.com/somethingsomething.large" + -- } + -- ] +-- } diff --git a/src/Data/Tomato.hs b/src/Data/Tomato.hs new file mode 100644 index 0000000..fc9b8b5 --- /dev/null +++ b/src/Data/Tomato.hs @@ -0,0 +1,70 @@ +module Data.Tomato + ( Links (..) + , Tomato (..) + , Urls (..) + ) where + +import RIO + +import Data.Aeson ((.:)) +import Network.HTTP.Req (Url, Scheme (Https), useHttpsURI) + +import qualified Data.Aeson as Ae +import qualified Data.Aeson.Types as Ae +import qualified Text.URI as URI + + +data Tomato = Tomato + { id :: !Text + , width :: !Word + , height :: !Word + , color :: !Text + , blur_hash :: !Text + , description :: !Text + , urls :: !Urls + , links :: !Links + } deriving Show + +instance Ae.FromJSON Tomato where + parseJSON = Ae.withObject "Tomato" $ \o -> Tomato + <$> o .: "id" + <*> o .: "width" + <*> o .: "height" + <*> o .: "color" + <*> o .: "blur_hash" + <*> o .: "description" + <*> o .: "urls" + <*> o .: "links" + +data Links = Links + { self :: !(Url Https) + , html :: !(Url Https) + , download :: !(Url Https) + , download_location :: !(Url Https) + } deriving Show + +instance Ae.FromJSON Links where + parseJSON = Ae.withObject "Links" $ \o -> Links + <$> (o .: "self" >>= toUrl) + <*> (o .: "html" >>= toUrl) + <*> (o .: "download" >>= toUrl) + <*> (o .: "download_location" >>= toUrl) + +data Urls = Urls + { raw :: !(Url Https) + , full :: !(Url Https) + , regular :: !(Url Https) + , small :: !(Url Https) + , thumb :: !(Url Https) + } deriving Show + +instance Ae.FromJSON Urls where + parseJSON = Ae.withObject "Urls" $ \o -> Urls + <$> (o .: "raw" >>= toUrl) + <*> (o .: "full" >>= toUrl) + <*> (o .: "regular" >>= toUrl) + <*> (o .: "small" >>= toUrl) + <*> (o .: "thumb" >>= toUrl) + +toUrl :: Text -> Ae.Parser (Url Https) +toUrl t = maybe mzero pure $ fmap fst $ useHttpsURI =<< URI.mkURI t diff --git a/src/Tomato/.Post.hs.swp b/src/Tomato/.Post.hs.swp Binary files differnew file mode 100644 index 0000000..f823f90 --- /dev/null +++ b/src/Tomato/.Post.hs.swp diff --git a/src/Tomato/Post.hs b/src/Tomato/Post.hs new file mode 100644 index 0000000..1e97782 --- /dev/null +++ b/src/Tomato/Post.hs @@ -0,0 +1,65 @@ +module Tomato.Post + ( postTomato + , uploadTomato + , postMessage + ) where + +import RIO + +import Network.HTTP.Req + ( jsonResponse + , ignoreResponse + , req + , ReqBodyFile (..) + , ReqBodyJson (..) + , (/:) + , POST (..) + , Req + , https + , header + , responseBody + ) + +import Data.Message (GMIUrl (..), OutMessage (..)) + +import qualified Data.Aeson as Ae +import qualified Data.Aeson.KeyMap as Ae + +-- TODO global config reader monad +tomatoFile :: FilePath +tomatoFile = "tomato.png" + +accessToken :: ByteString +accessToken = "nj8X8tB3TC0MoZdMLhIUA4G89r9IlQfVej97Mhg3" + +botId :: Text +botId = "713bcb4a4604006c944804552c" + +-- | Post the current tomato +postTomato :: Req () +postTomato = do + url <- uploadTomato + postMessage $ OutMessage url + +-- | Upload the current tomato to the GroupMe image server +uploadTomato :: Req GMIUrl +uploadTomato = do + let url = https "image.groupme.com" /: "pictures" + js <- 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 + return gmURL + +-- | Post a message to the group with image as attachment +postMessage :: OutMessage -> Req () +postMessage outMes = do + 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 $ + header "X-Access-Token" accessToken + return () diff --git a/src/Tomato/Validate.hs b/src/Tomato/Validate.hs new file mode 100644 index 0000000..a3dd328 --- /dev/null +++ b/src/Tomato/Validate.hs @@ -0,0 +1,16 @@ +module Tomato.Validate + ( isTomato + ) where + +import RIO + +import Data.Message (InMessage (..)) + + +isTomato :: InMessage -> Bool +isTomato m = + not (hasAttach m) && + groupId m == 87220147 && + isUser m && + text m == "tomato" + |