summaryrefslogtreecommitdiff
path: root/fig-monitor-twitch/src/Fig/Monitor/Twitch/Utils.hs
blob: 94d7b6a318f975c20463757195ec89c4e5364627 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
{-# Language RecordWildCards #-}
{-# Language ApplicativeDo #-}

module Fig.Monitor.Twitch.Utils
  ( FigMonitorTwitchException(..)
  , loadConfig
  , RequestConfig(..)
  , Config(..)
  , authedRequest
  , authedRequestJSON
  , Authed
  , runAuthed
  , userIsLiveScrape
  ) where

import Fig.Prelude

import Control.Monad.Reader (ReaderT, runReaderT)

import qualified Data.ByteString as BS
import qualified Data.ByteString.Lazy as BS.Lazy

import qualified Toml

import qualified Data.Aeson as Aeson

import Network.HTTP.Client as HTTP
import Network.HTTP.Client.TLS as HTTP

newtype FigMonitorTwitchException = FigMonitorTwitchException Text
  deriving (Show, Eq, Ord)
instance Exception FigMonitorTwitchException

data Config = Config
  { clientId :: Text
  , userToken :: Text
  , userLogin :: Text
  , monitor :: [Text]
  } deriving (Show, Eq, Ord)

configCodec :: Toml.TomlCodec Config
configCodec = do
  clientId <- Toml.text "client_id" Toml..= (\a -> a.clientId)
  userToken <- Toml.text "user_token" Toml..= (\a -> a.userToken)
  -- userIds <- Toml.arrayOf Toml._Text "user_ids" Toml..= (\a -> a.userIds)
  userLogin <- Toml.text "user_login" Toml..= (\a -> a.userLogin)
  monitor <- Toml.arrayOf Toml._Text "monitor" Toml..= (\a -> a.monitor)
  pure $ Config{..}

loadConfig :: FilePath -> IO Config
loadConfig path = Toml.decodeFileEither configCodec path >>= \case
  Left err -> throwM . FigMonitorTwitchException $ tshow err
  Right config -> pure config

data RequestConfig = RequestConfig
  { config :: Config
  , manager :: HTTP.Manager
  }

newtype Authed a = Authed { unAuthed :: ReaderT RequestConfig IO a }
  deriving (Functor, Applicative, Monad, MonadReader RequestConfig, MonadIO, MonadThrow)

authedRequest :: Text -> Text -> BS.Lazy.ByteString -> Authed BS.Lazy.ByteString
authedRequest method url body = do
  rc <- ask
  initialRequest <- liftIO . HTTP.parseRequest $ unpack url
  let request = initialRequest
        { method = encodeUtf8 method
        , requestBody = RequestBodyLBS body
        , requestHeaders = 
            [ ("Authorization", encodeUtf8 $ "Bearer " <> rc.config.userToken)
            , ("Client-Id", encodeUtf8 rc.config.clientId)
            , ("Content-Type", "application/json")
            ]
        }
  response <- liftIO $ HTTP.httpLbs request rc.manager
  pure $ HTTP.responseBody response

authedRequestJSON :: (Aeson.ToJSON a, Aeson.FromJSON b) => Text -> Text -> Maybe a -> Authed b
authedRequestJSON method url val = do
  resp <- authedRequest method url $ maybe "" Aeson.encode val
  case Aeson.eitherDecode resp of
    Left err -> do
      throwM . FigMonitorTwitchException $ tshow err
    Right res -> pure res

runAuthed :: Config -> Authed a -> IO a
runAuthed config body = do
  manager <- HTTP.newManager HTTP.tlsManagerSettings
  runReaderT body.unAuthed RequestConfig{..}

userIsLiveScrape :: Text -> Authed Bool
userIsLiveScrape user = do
  rc <- ask
  request <- liftIO . HTTP.parseRequest $ mconcat
    [ "https://twitch.tv/"
    , unpack user
    ]
  response <- liftIO $ HTTP.httpLbs request rc.manager
  let res = BS.isInfixOf "\"isLiveBroadcast\":true" . BS.Lazy.toStrict $ HTTP.responseBody response
  log $ mconcat
    [ user
    , " is "
    , if res then "" else "not "
    , "live"
    ]
  pure res