librandomorg-0.0.1.0: src/Network/Randomorg.hs
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE UnicodeSyntax #-}
{-# LANGUAGE ViewPatterns #-}
-- | Random.org API module
{-# OPTIONS_HADDOCK prune #-}
module Network.Randomorg
( RNG(..)
, integers, shuffle, strings, quota
) where
import Control.Applicative ((<$>))
import Data.ByteString.Char8 (ByteString, readInteger, readInt)
import Data.List (intercalate)
import Data.Maybe (fromMaybe)
import Data.Word (Word8)
import Network.Curl
import Prelude hiding (max, min)
import qualified Data.ByteString.Char8 as B
-- | Specify the randomization method
data RNG = New -- ^ Truly random bitstream
| Id String -- ^ Determine randomization from a large pool of pregenerated bits
| Date String -- ^ Determine randomization based on one of the daily pregenerated files. The date must be in YYYY-MM-DD format or one of the two shorthand strings `today' or `yesterday'.
class Get α where
get ∷ α → String
getF ∷ (Functor μ, Get α) ⇒ μ α → μ String
getF = fmap get
instance Get Int where
get = show
instance Get Bool where
get True = "on"
get False = "off"
instance Get (Word8,Word8,Word8,Word8) where
get (α,β,γ,δ) = intercalate "." . map show $ [α,β,γ,δ]
instance Get RNG where
get New = "new"
get (Id s) = "id." ++ s
get (Date s) = "date." ++ s
-- | Generate random integers in configurable intervals
integers ∷ Int -- ^ Number of integers to generate ([1,1e4])
→ Int -- ^ Minimum value (-1e9,1e9)
→ Int -- ^ Maximum value (-1e9,1e9)
→ RNG
→ IO (Maybe [Int])
integers (get → n)
(get → min)
(get → max)
(get → rng) =
let url = "http://www.random.org/integers/?col=1&base=10&format=plain&"
params = zip ["num","min","max","rnd"] [n, min, max, rng]
in mapM (\l → fst <$> readInt l) . B.lines . respBody <$> query url params
-- | Shuffle integers in given interval
--
-- NOTE: Maintains the invariant `max - min + 1 <= 1e4'
--
-- NOTE: Doesn't maintain the invariant `max >= min'
shuffle ∷ Int -- ^ Minimum value (-1e9,1e9)
→ Int -- ^ Maximum value (-1e9,1e9)
→ RNG
→ IO (Maybe [Int])
shuffle min
max
(get → rng)
| max - min + 1 <= 10000 =
let url = "http://www.random.org/sequences/?col=1&format=plain&"
params = zip ["min","max","rnd"] [get min, get max, rng]
in mapM (\l → fst <$> readInt l) . B.lines . respBody <$> query url params
| otherwise = return Nothing
-- | Generate random strings of various length and composition
strings ∷ Int -- ^ Number of strings to generate ([1,1e4])
→ Int -- ^ Length of each string ([1,20])
→ Bool -- ^ Allow digits
→ Bool -- ^ Allow uppercase alphabetic characters
→ Bool -- ^ Allow lowercase alphabetic characters
→ Bool -- ^ Whether all string should be unique
→ RNG
→ IO [String]
strings (get → n)
(get → l)
(get → digits)
(get → upper)
(get → lower)
(get → unique)
(get → rng) =
let url = "http://www.random.org/strings/?format=plain&"
params = zip ["num","len","digits","upperalpha","loweralpha","unique","rnd"] [n, l, digits, upper, lower, unique, rng]
in map B.unpack . B.lines . respBody <$> query url params
-- | Examine quota for given IP address
quota ∷ Maybe (Word8, Word8, Word8, Word8)-- ^ Optional IP address. If not provided use IP of the caller
→ IO (Maybe Integer)
quota (getF → ip) =
let url = "http://www.random.org/quota/?format=plain&"
params = zip ["ip"] [fromMaybe "" ip]
in (fst <$>) . readInteger . respBody <$> query url params
query ∷ String → [(String,String)] → IO (CurlResponse_ [(String, String)] ByteString)
query url (filter (not . null . snd) → params) =
let ua = "Mozilla/5.0 (X11; Linux x86_64; rv:10.0) Gecko/20100101 Firefox/10.0 Iceweasel/10.0"
in withCurlDo $ curlGetResponse_ (url ++ intercalate "&" (map (\(k,v) → k ++ "=" ++ v) params))
[ CurlFailOnError False
, CurlUserAgent ua
]