Initial commit (moving refactored code out of seal-blog proper)
parent
10bce52555
commit
d9b6aed2b9
|
@ -0,0 +1,13 @@
|
||||||
|
# Haskell stuff
|
||||||
|
*.dyn_hi
|
||||||
|
*.dyn_o
|
||||||
|
*.hi
|
||||||
|
*.o
|
||||||
|
dist
|
||||||
|
generateSealPosts
|
||||||
|
|
||||||
|
# Direnv lorri stuff
|
||||||
|
.direnv
|
||||||
|
|
||||||
|
# Too many posts, hiding for now
|
||||||
|
posts
|
|
@ -0,0 +1,11 @@
|
||||||
|
{
|
||||||
|
"cSpell.words": [
|
||||||
|
"Birthdayingly",
|
||||||
|
"Dhall",
|
||||||
|
"NOINLINE",
|
||||||
|
"runghc",
|
||||||
|
"uncurry",
|
||||||
|
"unlines",
|
||||||
|
"utct"
|
||||||
|
]
|
||||||
|
}
|
|
@ -0,0 +1,101 @@
|
||||||
|
{ sealImagesPath = "./images"
|
||||||
|
, postsOutputPath = "./posts"
|
||||||
|
, startDate = 2022-01-01
|
||||||
|
, adjectives1 =
|
||||||
|
[ "absorbing"
|
||||||
|
, "adorable"
|
||||||
|
, "alluring"
|
||||||
|
, "ambrosial"
|
||||||
|
, "amiable"
|
||||||
|
, "appealing"
|
||||||
|
, "attractive"
|
||||||
|
, "beautiful"
|
||||||
|
, "bewitching"
|
||||||
|
, "captivating"
|
||||||
|
, "charismatic"
|
||||||
|
, "charming"
|
||||||
|
, "choice"
|
||||||
|
, "cute"
|
||||||
|
, "dainty"
|
||||||
|
, "darling"
|
||||||
|
, "dear"
|
||||||
|
, "delectable"
|
||||||
|
, "delicate"
|
||||||
|
, "delicious"
|
||||||
|
, "delightful"
|
||||||
|
, "desirable"
|
||||||
|
, "dishy"
|
||||||
|
, "dreamy"
|
||||||
|
, "electrifying"
|
||||||
|
, "elegant"
|
||||||
|
, "enamoring"
|
||||||
|
, "engaging"
|
||||||
|
, "engrossing"
|
||||||
|
, "enthralling"
|
||||||
|
, "entrancing"
|
||||||
|
, "eye-catching"
|
||||||
|
, "fascinating"
|
||||||
|
, "fetching"
|
||||||
|
, "glamorous"
|
||||||
|
, "graceful"
|
||||||
|
, "heavenly"
|
||||||
|
, "infatuating"
|
||||||
|
, "inviting"
|
||||||
|
, "irresistible"
|
||||||
|
, "likable"
|
||||||
|
, "lovable"
|
||||||
|
, "lovely"
|
||||||
|
, "magnetizing"
|
||||||
|
, "nice"
|
||||||
|
, "pleasant"
|
||||||
|
, "precious"
|
||||||
|
, "pretty"
|
||||||
|
, "provocative"
|
||||||
|
, "rapturous"
|
||||||
|
, "ravishing"
|
||||||
|
, "seducing"
|
||||||
|
, "seductive"
|
||||||
|
, "suave"
|
||||||
|
, "sweet"
|
||||||
|
, "tantalizing"
|
||||||
|
, "tempting"
|
||||||
|
, "titillating"
|
||||||
|
, "winning"
|
||||||
|
, "winsome"
|
||||||
|
]
|
||||||
|
, adjectives2 =
|
||||||
|
[ "ample"
|
||||||
|
, "bearish"
|
||||||
|
, "big"
|
||||||
|
, "butterball"
|
||||||
|
, "buxom"
|
||||||
|
, "chunky"
|
||||||
|
, "fatty"
|
||||||
|
, "flabby"
|
||||||
|
, "fleshy"
|
||||||
|
, "full-figured"
|
||||||
|
, "hefty"
|
||||||
|
, "husky"
|
||||||
|
, "pleasingly plump"
|
||||||
|
, "plump"
|
||||||
|
, "plumpish"
|
||||||
|
, "podgy"
|
||||||
|
, "portly"
|
||||||
|
, "pudgy"
|
||||||
|
, "roly-poly"
|
||||||
|
, "rotund"
|
||||||
|
, "round"
|
||||||
|
, "stout"
|
||||||
|
, "tubby"
|
||||||
|
, "zaftig"
|
||||||
|
]
|
||||||
|
, looks =
|
||||||
|
[ "Look at"
|
||||||
|
, "Gaze upon"
|
||||||
|
, "Check out"
|
||||||
|
, "Witness!"
|
||||||
|
, "Look upon and tremble at"
|
||||||
|
, "Lookie here at"
|
||||||
|
, "Whoa! See"
|
||||||
|
]
|
||||||
|
}
|
|
@ -0,0 +1,61 @@
|
||||||
|
{
|
||||||
|
"nodes": {
|
||||||
|
"nixpkgs": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1681482634,
|
||||||
|
"narHash": "sha256-cT/nr3L8khEYZSGp8qqwxFH+/q4/547MfyOdSj6MhBk=",
|
||||||
|
"owner": "NixOS",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"rev": "fda0d99c2cbbb5c89d8855d258cb0821bd9113ad",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "NixOS",
|
||||||
|
"ref": "nixos-22.11",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": {
|
||||||
|
"inputs": {
|
||||||
|
"nixpkgs": "nixpkgs",
|
||||||
|
"utils": "utils"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"systems": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1681028828,
|
||||||
|
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
||||||
|
"owner": "nix-systems",
|
||||||
|
"repo": "default",
|
||||||
|
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "nix-systems",
|
||||||
|
"repo": "default",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"utils": {
|
||||||
|
"inputs": {
|
||||||
|
"systems": "systems"
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1681202837,
|
||||||
|
"narHash": "sha256-H+Rh19JDwRtpVPAWp64F+rlEtxUWBAQW28eAi3SRSzg=",
|
||||||
|
"owner": "numtide",
|
||||||
|
"repo": "flake-utils",
|
||||||
|
"rev": "cfacdce06f30d2b68473a46042957675eebb3401",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "numtide",
|
||||||
|
"repo": "flake-utils",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": "root",
|
||||||
|
"version": 7
|
||||||
|
}
|
|
@ -0,0 +1,30 @@
|
||||||
|
{
|
||||||
|
description = "A Nix flake for a blog-post generating script, written in Haskell, with a seal bent.";
|
||||||
|
|
||||||
|
inputs = {
|
||||||
|
nixpkgs.url = "github:NixOS/nixpkgs/nixos-22.11";
|
||||||
|
utils.url = "github:numtide/flake-utils";
|
||||||
|
};
|
||||||
|
|
||||||
|
outputs = { self, nixpkgs, utils }:
|
||||||
|
utils.lib.eachDefaultSystem (system:
|
||||||
|
let
|
||||||
|
pkgs = nixpkgs.legacyPackages.${system};
|
||||||
|
|
||||||
|
ghc' = pkgs.haskellPackages.ghcWithHoogle (self: with self; [
|
||||||
|
dhall
|
||||||
|
neat-interpolation
|
||||||
|
random
|
||||||
|
]);
|
||||||
|
in
|
||||||
|
{
|
||||||
|
devShells.default = pkgs.mkShell {
|
||||||
|
buildInputs = with pkgs.haskellPackages;
|
||||||
|
[
|
||||||
|
ghc'
|
||||||
|
hlint
|
||||||
|
haskell-language-server
|
||||||
|
];
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
|
@ -0,0 +1,174 @@
|
||||||
|
#! /usr/bin/env nix-shell
|
||||||
|
#! nix-shell -p "haskellPackages.ghcWithPackages (ps: with ps; [dhall neat-interpolation random])"
|
||||||
|
#! nix-shell -i runghc
|
||||||
|
{-
|
||||||
|
Must be run in folder containing the source code
|
||||||
|
-}
|
||||||
|
{-# LANGUAGE DeriveGeneric #-}
|
||||||
|
{-# LANGUAGE OverloadedStrings #-}
|
||||||
|
{-# LANGUAGE QuasiQuotes #-}
|
||||||
|
-- enter in repl:
|
||||||
|
-- :set -XOverloadedStrings
|
||||||
|
|
||||||
|
import Basement.Block (create)
|
||||||
|
import Control.Monad (forM, forM_, replicateM, unless, when)
|
||||||
|
import Data.List (sort)
|
||||||
|
import Data.Text (Text)
|
||||||
|
import qualified Data.Text as T
|
||||||
|
import qualified Data.Text.IO as TIO
|
||||||
|
import Data.Time.Calendar (Day, addDays, diffDays, fromGregorian,
|
||||||
|
toGregorian)
|
||||||
|
import Data.Time.Clock (UTCTime (utctDay), getCurrentTime)
|
||||||
|
import Dhall (FromDhall, Generic, auto, input)
|
||||||
|
import NeatInterpolation (text)
|
||||||
|
import System.Directory (createDirectoryIfMissing, doesFileExist,
|
||||||
|
listDirectory)
|
||||||
|
import System.IO.Unsafe (unsafePerformIO)
|
||||||
|
import System.Random (Random, randomRIO)
|
||||||
|
-- https://hackage.haskell.org/package/neat-interpolation-0.3.2.1/docs/NeatInterpolation.html
|
||||||
|
|
||||||
|
{-
|
||||||
|
Gives a random number between from and to
|
||||||
|
Uses unsafeIO to get the number out of IO
|
||||||
|
It's safe because we're only shuffling
|
||||||
|
-}
|
||||||
|
randomNum :: System.Random.Random a => a -> a -> a
|
||||||
|
randomNum from to =
|
||||||
|
unsafePerformIO $
|
||||||
|
randomRIO (from, to)
|
||||||
|
|
||||||
|
{-
|
||||||
|
Given a list, returns a random element
|
||||||
|
-}
|
||||||
|
randomPull :: [a] -> a
|
||||||
|
randomPull lst = lst !! r'
|
||||||
|
where r' = randomNum 0 l
|
||||||
|
l = length lst - 1
|
||||||
|
|
||||||
|
blogPost
|
||||||
|
:: Text
|
||||||
|
-> Text
|
||||||
|
-> Text
|
||||||
|
-> Text
|
||||||
|
-> Text
|
||||||
|
-> Day
|
||||||
|
-> Text
|
||||||
|
blogPost title see adj1 adj2 seal date =
|
||||||
|
[text|
|
||||||
|
---
|
||||||
|
title: $title
|
||||||
|
---
|
||||||
|
|
||||||
|
$see this $adj1, $adj2 seal!
|
||||||
|
<img
|
||||||
|
src="/images/$seal"
|
||||||
|
alt="A picture of a $adj1, $adj2 seal! <3"
|
||||||
|
width="400"
|
||||||
|
/>
|
||||||
|
|]
|
||||||
|
|
||||||
|
-- Returns a filePath, and a corresponding random blog post
|
||||||
|
sealText :: Config -> Integer -> Day -> (FilePath, Text)
|
||||||
|
sealText config n date = ( fileName', bp)
|
||||||
|
where
|
||||||
|
fileName' =
|
||||||
|
show date <> "-"
|
||||||
|
<> "seal-post-"
|
||||||
|
<> show n
|
||||||
|
<> ".markdown"
|
||||||
|
date' = T.pack . show $ date
|
||||||
|
title = T.pack $ "Seal Post Number " <> show n
|
||||||
|
title' = T.replace " " "-" title
|
||||||
|
bp = blogPost' (toGregorian date)
|
||||||
|
|
||||||
|
blogPost' :: (Integer, Int, Int) -> Text
|
||||||
|
blogPost' (_, 09, 26) =
|
||||||
|
blogPost
|
||||||
|
title
|
||||||
|
"Birthdayingly gaze at"
|
||||||
|
"spoiled"
|
||||||
|
"older"
|
||||||
|
"birthdaySeal.jpg"
|
||||||
|
date
|
||||||
|
|
||||||
|
blogPost' (_, 04, 01) =
|
||||||
|
blogPost
|
||||||
|
title
|
||||||
|
"Foolishly look at"
|
||||||
|
"beautiful"
|
||||||
|
"supermodel, singing"
|
||||||
|
"singerSeal.jpg"
|
||||||
|
date
|
||||||
|
|
||||||
|
blogPost' (_, _, _) =
|
||||||
|
blogPost
|
||||||
|
title
|
||||||
|
(randomPull $ looks config)
|
||||||
|
(randomPull $ adjectives1 config)
|
||||||
|
(randomPull $ adjectives2 config)
|
||||||
|
(randomPull $ unsafeListDirContents $ sealImagesPath config)
|
||||||
|
date
|
||||||
|
|
||||||
|
-- Generating all the previous blog posts
|
||||||
|
-- Only need to do this once
|
||||||
|
allDatesSince :: Day -> [Day]
|
||||||
|
allDatesSince startDate = map (`addDays` startDate) [0..daysSinceStart]
|
||||||
|
where
|
||||||
|
daysSinceStart = diffDays today startDate
|
||||||
|
{-# NOINLINE today #-}
|
||||||
|
today = unsafePerformIO $ utctDay <$> getCurrentTime
|
||||||
|
|
||||||
|
allBlogPosts :: Config -> [(FilePath, Text)]
|
||||||
|
allBlogPosts config = map f zippedDates
|
||||||
|
where
|
||||||
|
f = uncurry $ sealText config
|
||||||
|
zippedDates = zip [1..] (allDatesSince (startDate config))
|
||||||
|
|
||||||
|
writeToFile :: Config -> (FilePath, Text) -> IO()
|
||||||
|
writeToFile config (fp, txt) = do
|
||||||
|
-- fileExists <- doesFileExist fp'
|
||||||
|
-- unless fileExists (write fp' txt)
|
||||||
|
write fp' txt
|
||||||
|
where
|
||||||
|
write = TIO.writeFile
|
||||||
|
fp' = postsOutputPath config <> "/" <> fp
|
||||||
|
|
||||||
|
-- For all the blog posts
|
||||||
|
-- Write them to file
|
||||||
|
unsafeGenerateAllBlogs :: Config -> IO ()
|
||||||
|
unsafeGenerateAllBlogs config =
|
||||||
|
forM_ (allBlogPosts config) (writeToFile config)
|
||||||
|
|
||||||
|
main :: IO ()
|
||||||
|
main = do
|
||||||
|
putStrLn "Starting blog post generation script"
|
||||||
|
|
||||||
|
config <- input auto "./config.dhall"
|
||||||
|
let postsOutputPath' = postsOutputPath config
|
||||||
|
putStrLn "Validated config successfully"
|
||||||
|
|
||||||
|
createDirectoryIfMissing True postsOutputPath'
|
||||||
|
unsafeGenerateAllBlogs config
|
||||||
|
putStrLn $ "Generated blog posts successfully to " <> postsOutputPath'
|
||||||
|
|
||||||
|
prettyPrint :: Show a => [a] -> IO ()
|
||||||
|
prettyPrint = putStr . unlines . map show
|
||||||
|
|
||||||
|
{-
|
||||||
|
Config
|
||||||
|
-}
|
||||||
|
data Config = Config
|
||||||
|
{ adjectives1 :: [Text]
|
||||||
|
, adjectives2 :: [Text]
|
||||||
|
, looks :: [Text]
|
||||||
|
, sealImagesPath :: FilePath
|
||||||
|
, postsOutputPath :: FilePath
|
||||||
|
, startDate :: Day
|
||||||
|
} deriving (Generic, Show)
|
||||||
|
instance FromDhall Config
|
||||||
|
|
||||||
|
{-
|
||||||
|
Utils
|
||||||
|
-}
|
||||||
|
unsafeListDirContents :: FilePath -> [Text]
|
||||||
|
unsafeListDirContents = map T.pack . sort . unsafePerformIO . listDirectory
|
Loading…
Reference in New Issue