packages feed

nekos-best-0.2.0.0: src/NekosBest/API.hs

{-# LANGUAGE OverloadedStrings #-}

module NekosBest.API (
    getNbImage,
    getNbImages,
    randomNbImage,
    randomNbImages,
    downloadNbImage
) where

import Network.HTTP.Client
import Network.HTTP.Types.Status (statusCode, requestHeaderFieldsTooLarge431)
import Network.HTTP.Client.TLS (tlsManagerSettings)
import Data.Aeson ( decode', FromJSON, withObject, (.:?), parseJSON)
import Data.ByteString.Lazy as L ( ByteString, writeFile )
import Data.Map ( Map, findWithDefault )
import Data.Maybe ( fromMaybe )
import Data.Char (toLower)
import System.Random (RandomGen, randomR)

import NekosBest.Category ( NbCategory, allCategories )
import NekosBest.Error (NbError(..))

data NbResult = NbResult {
    artistHref :: Maybe String,
    artistName :: Maybe String,
    sourceUrl :: Maybe String,
    animeName :: Maybe String,
    url :: Maybe String
} deriving (Show)

instance FromJSON NbResult where
    parseJSON = withObject "NbResult" $ \v -> NbResult
        <$> v .:? "artist_href"
        <*> v .:? "artist_name"
        <*> v .:? "source_url"
        <*> v .:? "anime_name"
        <*> v .:? "url"

makeHttpRequest :: String -> IO (Response L.ByteString)
makeHttpRequest url = do
    manager <- newManager tlsManagerSettings
    request <- parseRequest url
    httpLbs request manager

getNbImages :: (Show i, Integral i) => NbCategory -> i -> IO (Either NbError [NbResult])
getNbImages c i = do
    let endpoint = toLower <$> show c
    response <- makeHttpRequest $ "https://nekos.best/api/v2/" ++ endpoint ++ "?amount=" ++ show i
    let status = statusCode $ responseStatus response
    if status == 200 then do
        let json = responseBody response
        let result = getResultsFromJson json

        return $ Right result
    else do
        return $ Left (NbError "Received invalid HTTP status code")

getNbImage :: NbCategory -> IO (Either NbError NbResult)
getNbImage c = do
    result <- getNbImages c 1
    return $ fmap (\(x:_) -> x) result
getResultsFromJson :: L.ByteString -> [NbResult]
getResultsFromJson json = fromMaybe [] results
    where json' = decode' json :: Maybe (Map String [NbResult])
          results = findWithDefault [] "results" <$> json'


randomNbImage :: (RandomGen g) => g -> IO (Either NbError NbResult, g)
randomNbImage gen = do
    let (c, gen') = randomCategory gen
    res <- getNbImage c
    return (res, gen')
    where randomIndex = randomR (0, length allCategories)
          randomCategory gen = let (c, gen') = randomIndex gen
                               in (allCategories !! c, gen')

randomNbImages :: (RandomGen g, Integral i) => g -> i -> IO ([Either NbError NbResult], g)
randomNbImages gen 0 = return ([], gen)
randomNbImages gen n = do
    (res, gen') <- randomNbImage gen
    (xs, gen'') <- randomNbImages gen' (n-1)
    return $ case res of Right x -> (Right x:xs, gen'')
                         Left e -> (Left e:xs, gen'')

downloadNbImage :: NbResult -> FilePath -> IO (Maybe NbError)
downloadNbImage res path = case url res of
    Just x -> do
              response <- makeHttpRequest x
              let status = statusCode $ responseStatus response
              if status == 200 then do
                let content = responseBody response
                L.writeFile path content
                return Nothing
              else do
                return $ Just $ NbError "Received invalid HTTP status code"
    Nothing -> return $ Just $ NbError "No image url in result"