Last active
September 17, 2018 00:33
-
-
Save StevenXL/8605c206196db6475bfe4d356e8441fa to your computer and use it in GitHub Desktop.
Twilio Grab
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
-- stack --resolver lts-12.5 script --package mtl --package aeson --package http-conduit --package bytestring --package cassava --package MissingH | |
{-# LANGUAGE OverloadedStrings #-} | |
{-# LANGUAGE DeriveGeneric #-} | |
import Control.Monad.IO.Class (liftIO) | |
import Control.Monad.Reader (ReaderT, ask, runReaderT) | |
import Control.Monad.State (StateT, evalStateT, get, put) | |
import Data.Aeson (FromJSON) | |
import qualified Data.ByteString as B | |
import qualified Data.ByteString.Char8 as C | |
import qualified Data.ByteString.Lazy as BL | |
import Data.Csv (DefaultOrdered, ToNamedRecord) | |
import qualified Data.Csv as Csv | |
import Data.String.Utils (replace) | |
import GHC.Generics | |
import Network.HTTP.Simple (Response, getResponseBody, httpJSON, | |
parseRequest_, setRequestBasicAuth) | |
import System.Environment (getEnv) | |
data Env = Env { envUserName :: B.ByteString , envPassword :: B.ByteString } | |
envSid :: Env -> B.ByteString | |
envSid = envUserName | |
data AppState = AppState { nextUrl :: String } | |
data Message = Message { sid :: String | |
, date_created :: String | |
, date_updated :: String | |
, date_sent :: String | |
, account_sid :: String | |
, to :: String | |
, from :: String | |
, message_service_sid :: Maybe String | |
, body :: String | |
, status :: String | |
, num_segments :: String | |
, num_media :: String | |
, direction :: String | |
, api_version :: String | |
, price :: String | |
, price_unit :: String | |
, error_code :: Maybe Integer | |
, error_message :: Maybe String | |
} deriving (Generic, Show) | |
instance FromJSON Message | |
instance ToNamedRecord Message | |
instance DefaultOrdered Message | |
data TwilioBody = TwilioBody { next_page_uri :: Maybe String | |
, messages :: [Message] | |
} deriving Generic | |
instance FromJSON TwilioBody | |
baseUrl :: String | |
baseUrl = "https://api.twilio.com" | |
createFirstUrl :: String -> String | |
createFirstUrl sid = baseUrl <> "/2010-04-01/Accounts" <> "/" <> sid <> "/Messages.json" | |
main :: IO () | |
main = do | |
sid <- getEnv "TWILIO_SID" | |
password <- getEnv "TWILIO_PASSWORD" | |
let initialUrl = createFirstUrl sid | |
let env = Env (C.pack sid) (C.pack password) | |
messages <- evalStateT (runReaderT getMessages env) (AppState initialUrl) | |
saveMessages messages | |
getMessages :: ReaderT Env (StateT AppState IO) [Message] | |
getMessages = do | |
sid <- fmap envSid ask | |
url <- fmap nextUrl get | |
_ <- liftIO $ putStrLn $ "Getting MSGs for " <> scramble sid url | |
userName <- fmap envUserName ask | |
password <- fmap envPassword ask | |
response <- httpJSON $ setRequestBasicAuth userName password (parseRequest_ url) | |
case parse response of | |
(Nothing, msgs) -> pure msgs | |
(Just nextPageUri, msgs) -> do | |
_ <- put $ AppState { nextUrl = constructUrl nextPageUri} | |
newMsgs <- getMessages | |
return $ msgs ++ newMsgs | |
parse :: Response TwilioBody -> (Maybe String, [Message]) | |
parse response = let twilioBody = getResponseBody response | |
in (next_page_uri twilioBody, messages twilioBody) | |
saveMessages :: [Message] -> IO () | |
saveMessages = BL.writeFile "sms.csv" . Csv.encodeDefaultOrderedByName | |
constructUrl :: String -> String | |
constructUrl = (baseUrl <>) | |
scramble :: B.ByteString -> String -> String | |
scramble sid url = replace (C.unpack sid) "<REDACTED>" url |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment