packages feed

advent-of-code-api 0.2.7.1 → 0.2.8.0

raw patch · 5 files changed

+86/−27 lines, 5 filesPVP: major bump suggested

API removals or changes: PVP suggests a major version bump

API changes (from Hackage documentation)

+ Advent: NextDayTime :: Day -> Int -> NextDayTime
+ Advent: NoNextDayTime :: NextDayTime
+ Advent: [AoCNextDayTime] :: AoC NextDayTime
+ Advent: data NextDayTime
+ Advent.API: instance Advent.API.FromTags "script" Advent.Types.NextDayTime
+ Advent.API: type Scripts = HTMLTags "script"
+ Advent.Types: NextDayTime :: Day -> Int -> NextDayTime
+ Advent.Types: NoNextDayTime :: NextDayTime
+ Advent.Types: data NextDayTime
+ Advent.Types: instance GHC.Classes.Eq Advent.Types.NextDayTime
+ Advent.Types: instance GHC.Classes.Ord Advent.Types.NextDayTime
+ Advent.Types: instance GHC.Generics.Generic Advent.Types.NextDayTime
+ Advent.Types: instance GHC.Show.Show Advent.Types.NextDayTime
- Advent.API: adventAPIClient :: Integer -> (Day -> ClientM (Map Part Text) :<|> (ClientM Text :<|> (SubmitInfo -> ClientM (Text :<|> SubmitRes)))) :<|> (ClientM GlobalLeaderboard :<|> ((Day -> ClientM DailyLeaderboard) :<|> (PublicCode -> ClientM Leaderboard)))
+ Advent.API: adventAPIClient :: Integer -> ClientM NextDayTime :<|> ((Day -> ClientM (Map Part Text) :<|> (ClientM Text :<|> (SubmitInfo -> ClientM (Text :<|> SubmitRes)))) :<|> (ClientM GlobalLeaderboard :<|> ((Day -> ClientM DailyLeaderboard) :<|> (PublicCode -> ClientM Leaderboard))))
- Advent.API: type AdventAPI = Capture "year" Integer :> ("day" :> Capture "day" Day :> (Get '[Articles] (Map Part Text) :<|> "input" :> Get '[RawText] Text :<|> "answer" :> ReqBody '[FormUrlEncoded] SubmitInfo :> Post '[Articles] (Text :<|> SubmitRes)) :<|> "leaderboard" :> (Get '[Divs] GlobalLeaderboard :<|> "day" :> Capture "day" Day :> Get '[Divs] DailyLeaderboard :<|> "private" :> "view" :> Capture "code" PublicCode :> Get '[JSON] Leaderboard))
+ Advent.API: type AdventAPI = Capture "year" Integer :> (Get '[Scripts] NextDayTime :<|> "day" :> Capture "day" Day :> (Get '[Articles] (Map Part Text) :<|> "input" :> Get '[RawText] Text :<|> "answer" :> ReqBody '[FormUrlEncoded] SubmitInfo :> Post '[Articles] (Text :<|> SubmitRes)) :<|> ("leaderboard" :> (Get '[Divs] GlobalLeaderboard :<|> "day" :> Capture "day" Day :> Get '[Divs] DailyLeaderboard :<|> "private" :> "view" :> Capture "code" PublicCode :> Get '[JSON] Leaderboard)))

Files

CHANGELOG.md view
@@ -1,6 +1,17 @@ Changelog ========= +Version 0.2.8.0+---------------++*December 14, 2020*++<https://github.com/mstksg/advent-of-code-api/releases/tag/v0.2.8.0>++*   Add servant endpoint to query calendar page for a year to infer next puzzle+    and time to puzzle in seconds, backed by the `NextDayTime` data type.+*   Add `AoCNextDayTime` to `AoC` to support the above operation.+ Version 0.2.7.1 --------------- 
advent-of-code-api.cabal view
@@ -4,10 +4,10 @@ -- -- see: https://github.com/sol/hpack ----- hash: 42fe072e1e23c3a691ec787cf5e1118785d56470c1ec117048fc826c4e47609c+-- hash: 6cf4283a8e57b7716c98e2f2914b0396dc18f396a3ff0bb7f5df28750eefbb92  name:           advent-of-code-api-version:        0.2.7.1+version:        0.2.8.0 synopsis:       Advent of Code REST API bindings and servant API description:    Haskell bindings for Advent of Code REST API and a servant API.  Please use                 responsibly! See README.md or "Advent" module for an introduction and
src/Advent.hs view
@@ -50,6 +50,7 @@     AoC(..)   , Part(..)   , Day(..)+  , NextDayTime(..)   , AoCOpts(..)   , SubmitRes(..), showSubmitRes   , runAoC@@ -224,6 +225,18 @@     AoCGlobalLeaderboard         :: AoC GlobalLeaderboard +    -- | From the calendar, fetch the next release's day and the+    -- number of seconds util its release, if there is any at all.+    --+    -- This does an actual request to the AoC servers, and is only accurate+    -- to the second; to infer this information (to the millisecond level)+    -- from the system clock, you should probably use 'timeToRelease' and+    -- 'aocServerTime' instead, which requires no network requests.+    --+    -- @since 0.2.8.0+    AoCNextDayTime+        :: AoC NextDayTime+ deriving instance Show (AoC a) deriving instance Typeable (AoC a) @@ -235,6 +248,7 @@ aocDay (AoCLeaderboard _) = Nothing aocDay (AoCDailyLeaderboard d) = Just d aocDay AoCGlobalLeaderboard = Nothing+aocDay AoCNextDayTime       = Nothing  -- | A possible (syncronous, logical, pure) error returnable from 'runAoC'. -- Does not cover any asynchronous or IO errors.@@ -313,12 +327,14 @@     AoCInput  i       -> let _ :<|> r :<|> _ = adventAPIPuzzleClient yr i in r     AoCSubmit i p ans -> let _ :<|> _ :<|> r = adventAPIPuzzleClient yr i                          in  r (SubmitInfo p ans) <&> \(x :<|> y) -> (x, y)-    AoCLeaderboard c  -> let _ :<|> _ :<|> _ :<|> r = adventAPIClient yr+    AoCLeaderboard c  -> let _ :<|> _ :<|> _ :<|> _ :<|> r = adventAPIClient yr                          in  r (PublicCode c)-    AoCDailyLeaderboard d -> let _ :<|> _ :<|> r :<|> _ = adventAPIClient yr+    AoCDailyLeaderboard d -> let _ :<|> _ :<|> _ :<|> r :<|> _ = adventAPIClient yr                              in  r d-    AoCGlobalLeaderboard  -> let _ :<|> r :<|> _ :<|> _ = adventAPIClient yr+    AoCGlobalLeaderboard  -> let _ :<|> _ :<|> r :<|> _ = adventAPIClient yr                              in  r+    AoCNextDayTime        -> let r :<|> _ = adventAPIClient yr+                             in  r   -- | Cache file for a given 'AoC' command@@ -332,8 +348,9 @@     AoCInput  d      -> Just $ printf "input/%s%04d/%02d.txt" keyDir yr (dayInt d)     AoCSubmit{}      -> Nothing     AoCLeaderboard{} -> Nothing-    AoCDailyLeaderboard d -> Just $ printf "daily/%04d/%02d.json" yr (dayInt d)+    AoCDailyLeaderboard d  -> Just $ printf "daily/%04d/%02d.json" yr (dayInt d)     AoCGlobalLeaderboard{} -> Just $ printf "global/%04d.json" yr+    AoCNextDayTime         -> Nothing   where     keyDir = case sess of       Nothing -> ""@@ -443,6 +460,7 @@             guard $ not evt'        -- only load cache if evt' is false: it was saved in a non-evt time             pure $ Right lb         }+    AoCNextDayTime{} -> noCache   where     expectedParts :: Set Part     expectedParts
src/Advent/API.hs view
@@ -42,38 +42,40 @@   , FromTags(..)   , Articles   , Divs+  , Scripts   , RawText   -- * Internal   , processHTML   ) where  import           Advent.Types+import           Control.Applicative import           Control.Monad import           Control.Monad.State import           Data.Bifunctor import           Data.Char import           Data.Finite import           Data.Foldable-import           Data.List.NonEmpty         (NonEmpty(..))-import           Data.Map                   (Map)+import           Data.List.NonEmpty     (NonEmpty(..))+import           Data.Map               (Map) import           Data.Maybe import           Data.Ord import           Data.Proxy-import           Data.Text                  (Text)-import           Data.Time hiding           (Day)+import           Data.Text              (Text)+import           Data.Time hiding       (Day) import           GHC.TypeLits import           Servant.API import           Servant.Client-import           Text.HTML.TagSoup.Tree     (TagTree(..))-import           Text.Read                  (readMaybe)-import qualified Data.ByteString.Lazy       as BSL-import qualified Data.List.NonEmpty         as NE-import qualified Data.Map                   as M-import qualified Data.Text                  as T-import qualified Data.Text.Encoding         as T-import qualified Network.HTTP.Media         as M-import qualified Text.HTML.TagSoup          as H-import qualified Text.HTML.TagSoup.Tree     as H+import           Text.HTML.TagSoup.Tree (TagTree(..))+import           Text.Read              (readMaybe)+import qualified Data.ByteString.Lazy   as BSL+import qualified Data.List.NonEmpty     as NE+import qualified Data.Map               as M+import qualified Data.Text              as T+import qualified Data.Text.Encoding     as T+import qualified Network.HTTP.Media     as M+import qualified Text.HTML.TagSoup      as H+import qualified Text.HTML.TagSoup.Tree as H  #if !MIN_VERSION_base(4,11,0) import           Data.Semigroup ((<>))@@ -106,6 +108,9 @@ -- @since 0.2.3.0 type Divs     = HTMLTags "div" +-- | Interpret a response as a list of HTML 'T.Text' found in @<script>@ tags.+type Scripts = HTMLTags "script"+ -- | Class for interpreting a list of 'T.Text' in tags to some desired -- output. --@@ -205,30 +210,46 @@               xs' <- forM xs $ \x -> x { glbmRank = Rank currScore } <$ modify succ               pure (Rank currScore, xs') -+instance FromTags "script" NextDayTime where+    fromTags _ = (<|> Just NoNextDayTime) . listToMaybe . mapMaybe findNDT+      where+        -- var server_eta = 25112;+        -- var key = "2020-15-"+server_eta;+        findNDT body = do+          eta    <- T.unpack <$> grabKey "server_eta" body+          yd     <- grabKey "key" body+          sec    <- readMaybe eta+          dayStr <- listToMaybe . drop 1 . T.splitOn "-" $ yd+          dy     <- mkDay =<< readMaybe (T.unpack dayStr)+          pure $ NextDayTime dy sec+        grabKey t str =+            fst . T.breakOn ";\n" <$> T.stripPrefix t' (snd (T.breakOn t' str))+          where+            t' = "var " <> t <> " = "  -- | REST API of Advent of Code. -- -- Note that most of these requests assume a "session=" cookie. type AdventAPI =       Capture "year" Integer-   :> ("day" :> Capture "day" Day+   :> (Get '[Scripts] NextDayTime+  :<|> "day" :> Capture "day" Day              :> (Get '[Articles] (Map Part Text)             :<|> "input" :> Get '[RawText] Text             :<|> "answer"                      :> ReqBody '[FormUrlEncoded] SubmitInfo                      :> Post    '[Articles] (Text :<|> SubmitRes)                 )-  :<|> "leaderboard"+  :<|> ("leaderboard"     :> (Get '[Divs] GlobalLeaderboard    :<|> "day"     :> Capture "day" Day :> Get '[Divs] DailyLeaderboard    :<|> "private" :> "view"                   :> Capture "code" PublicCode                   :> Get '[JSON] Leaderboard--       )+       ))       ) + -- | 'Proxy' used for /servant/ functions. adventAPI :: Proxy AdventAPI adventAPI = Proxy@@ -236,7 +257,8 @@ -- | 'ClientM' requests based on 'AdventAPI', generated by servant. adventAPIClient     :: Integer-    -> (Day -> ClientM (Map Part Text) :<|> ClientM Text :<|> (SubmitInfo -> ClientM (Text :<|> SubmitRes)) )+    -> ClientM NextDayTime+  :<|> (Day -> ClientM (Map Part Text) :<|> ClientM Text :<|> (SubmitInfo -> ClientM (Text :<|> SubmitRes)) )   :<|> ClientM GlobalLeaderboard   :<|> (Day -> ClientM DailyLeaderboard)   :<|> (PublicCode -> ClientM Leaderboard)@@ -250,7 +272,7 @@     -> ClientM (Map Part Text) :<|> ClientM Text :<|> (SubmitInfo -> ClientM (Text :<|> SubmitRes)) adventAPIPuzzleClient y = pis   where-    pis :<|> _ = adventAPIClient y+    _ :<|> pis :<|> _ = adventAPIClient y  userNameNaked :: [TagTree Text] -> Maybe Text userNameNaked = (listToMaybe .) . mapMaybe $ \x -> do
src/Advent/Types.hs view
@@ -41,6 +41,7 @@   , DailyLeaderboardMember(..)   , GlobalLeaderboard(..)   , GlobalLeaderboardMember(..)+  , NextDayTime(..)   -- * Util   , mkDay, mkDay_, dayInt   , _DayInt, pattern DayInt@@ -246,6 +247,13 @@   }   deriving (Show, Eq, Ord, Typeable, Generic) +-- | The next day for a challenge in a given year, and also the number of+-- seconds until the challenge is released.+--+-- @since 0.2.8.0+data NextDayTime = NextDayTime Day Int+                 | NoNextDayTime+  deriving (Show, Eq, Ord, Typeable, Generic)  instance ToHttpApiData Part where     toUrlPiece = T.pack . show . partInt