-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
render ugly likes with templates/post.html add github action to automatically sync webmentios every 24hr save webmentions to webmentions.json
- Loading branch information
Showing
9 changed files
with
337 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
name: Webmentions Sync and Redeploy | ||
|
||
on: | ||
schedule: | ||
- cron: "0 */24 * * *" | ||
workflow_dispatch: | ||
|
||
jobs: | ||
webmentions-sync-rebuild-redeploy: | ||
runs-on: ubuntu-latest | ||
steps: | ||
- uses: actions/checkout@v3 | ||
- uses: cachix/install-nix-action@v20 | ||
with: | ||
nix_path: nixpkgs=channel:nixos-unstable | ||
- uses: DeterminateSystems/magic-nix-cache-action@v2 | ||
- run: nix profile install nixpkgs#python312Packages.pygments | ||
- run: nix build | ||
- name: Rebuild and Sync webmentions | ||
env: | ||
WMTOKEN: ${{ secrets.WMTOKEN }} | ||
run: ./result/bin/site rebuild -v | ||
|
||
- name: Commit webmentions to repository | ||
env: | ||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | ||
- name: Check for changes and commit | ||
run: | | ||
git config user.name "github-actions[bot]" | ||
git config user.email "github-actions[bot]@users.noreply.github.com" | ||
set +e | ||
git add webmentions.json | ||
git diff --quiet && git diff --staged --quiet || (git commit -m "Sync webmentions [skip actions]"; git push origin test) | ||
set -e | ||
- name: Deploy | ||
uses: JamesIves/github-pages-deploy-action@v4 | ||
with: | ||
folder: _site |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,166 @@ | ||
{-# LANGUAGE BlockArguments #-} | ||
{-# LANGUAGE ImportQualifiedPost #-} | ||
{-# LANGUAGE LambdaCase #-} | ||
{-# LANGUAGE OverloadedStrings #-} | ||
{-# LANGUAGE ViewPatterns #-} | ||
{-# LANGUAGE RecordWildCards #-} | ||
{-# LANGUAGE DeriveGeneric #-} | ||
module WMentions where | ||
|
||
import Data.Maybe | ||
import GHC.Generics(Generic) | ||
import System.Environment.Blank (getEnv) | ||
import System.Posix.Files (fileExist) | ||
import Text.Blaze.Html.Renderer.String (renderHtml) | ||
import Data.Map (Map) | ||
import Data.Map qualified as Map | ||
import Text.Blaze.Html5 ((!)) | ||
import Text.Blaze.Html5 qualified as H | ||
import Text.Blaze.Html5.Attributes qualified as H | ||
import Data.String (fromString) | ||
import Network.Wreq qualified as W | ||
import Data.Aeson | ||
import Data.Aeson (Value) | ||
import Data.Aeson.KeyMap qualified as JK | ||
import Control.Lens ((^.)) | ||
import Data.Vector (Vector, fromList) | ||
import Data.Vector qualified as V | ||
import Data.Vector.Algorithms qualified as VAlg | ||
import Data.Text (Text) | ||
import Data.Text qualified as T | ||
import Data.ByteString (ByteString) | ||
import Data.ByteString qualified as B | ||
import Data.Ord qualified as Ord | ||
import Control.Applicative ((<|>)) | ||
|
||
type WMPost = String | ||
|
||
data StoredMentions = SM { | ||
likes :: Map WMPost (Vector Value), | ||
replies :: Map WMPost (Vector Value), | ||
reposts :: Map WMPost (Vector Value) | ||
} deriving (Show, Generic) | ||
|
||
instance ToJSON StoredMentions where | ||
toEncoding = genericToEncoding defaultOptions | ||
|
||
instance FromJSON StoredMentions | ||
|
||
emptySM :: StoredMentions | ||
emptySM = SM mempty mempty mempty | ||
|
||
newtype GetWM = GetWM { unGetWM :: StoredMentions } deriving (Show) | ||
|
||
instance FromJSON GetWM where | ||
parseJSON = withObject "webmention" $ \v -> do | ||
cs <- v .: "children" | ||
case cs of | ||
Array vec -> pure . GetWM $ SM (foldToMap likes) (foldToMap replies) (foldToMap reposts) where | ||
|
||
foldToMap objs = Map.fromList $ foldl go mempty (V.groupBy gb objs) where | ||
-- go :: [(WMPost, Vector Value)] -> Vector Value -> [(WMPost, Vector Value)] | ||
go wmmap ls | ||
| V.null ls = wmmap | ||
| otherwise = case V.head ls of | ||
Object o -> case JK.lookup "wm-target" o of | ||
Just (String t) -> (drop 8 $ T.unpack t, ls) : wmmap | ||
_ -> wmmap | ||
_ -> wmmap | ||
|
||
gb (Object kmap1) (Object kmap2) = fromMaybe False $ (==) | ||
<$> JK.lookup "wm-target" kmap1 | ||
<*> JK.lookup "wm-target" kmap2 | ||
gb _ _ = False | ||
|
||
(likes, rest) = V.partition (isType "like-of") vec | ||
(replies, reposts) = V.partition (isType "in-reply-to") rest | ||
|
||
isType type_ (Object kmap) = maybe False (type_ ==) (JK.lookup "wm-property" kmap) | ||
isType _ _ = False | ||
|
||
_ -> fail "failed to parse chidren as an array" | ||
|
||
|
||
renderRepost (Object kmap) = do | ||
Object author <- JK.lookup "author" kmap | ||
String authorPhotoUrl <- JK.lookup "photo" author | ||
String authorUrl <- JK.lookup "url" author | ||
pure . renderHtml $ H.img ! H.src (fromString $ T.unpack $ authorPhotoUrl) ! H.alt "like image" | ||
|
||
renderReply (Object kmap) = do | ||
String (T.unpack -> url) <- JK.lookup "url" kmap | ||
String (T.unpack -> published) <- JK.lookup "published" kmap | ||
Object author <- JK.lookup "author" kmap | ||
Object content <- JK.lookup "content" kmap | ||
String (T.unpack -> chtml) <- JK.lookup "html" content <|> JK.lookup "text" content | ||
String (T.unpack -> authorPhotoUrl) <- JK.lookup "photo" author | ||
String (T.unpack -> authorName) <- JK.lookup "name" author | ||
String (T.unpack -> authorUrl) <- JK.lookup "url" author | ||
pure . renderHtml $ | ||
H.div ! H.class_ "mention" $ do | ||
H.a ! H.class_ "mention__authorImageLink" ! H.href (fromString url) $ | ||
H.img ! H.class_ "mention_authorLink" ! H.src (fromString authorPhotoUrl) ! H.alt (fromString authorName) | ||
H.div ! H.class_ "mention__authorLink" $ do | ||
H.strong (H.a ! H.href (fromString authorUrl) $ (fromString authorName)) | ||
H.div ! H.class_ "mention__content" $ fromString chtml | ||
H.small $ H.a ! H.href (fromString url) $ fromString published | ||
|
||
renderLike (Object kmap) = do | ||
String (T.unpack -> url) <- JK.lookup "url" kmap | ||
Object author <- JK.lookup "author" kmap | ||
String authorPhotoUrl <- JK.lookup "photo" author | ||
String (T.unpack -> authorPhotoUrl) <- JK.lookup "photo" author | ||
String (T.unpack -> authorName) <- JK.lookup "name" author | ||
pure . renderHtml $ H.a ! H.class_ "like" ! H.href (fromString url) $ | ||
H.img ! H.class_ "like__image" ! H.src (fromString authorPhotoUrl) ! H.alt (fromString authorName) | ||
|
||
getFromFileOrWebmentionIO :: IO StoredMentions | ||
getFromFileOrWebmentionIO = do | ||
webementionIoToken <- getEnv "WMTOKEN" | ||
newMentions <- case webementionIoToken of | ||
Nothing -> do | ||
putStrLn $ "Warning: no webmention.io token found" | ||
putStrLn $ "Warning: please set WMToken environmental variable" | ||
pure emptySM | ||
Just token -> do | ||
|
||
response <- W.get $ "https://webmention.io/api/mentions.jf2?domain=jezenthomas.com&token=" <> token | ||
|
||
case response ^. W.responseStatus . W.statusCode of | ||
200 -> case fmap unGetWM . eitherDecode $ response ^. W.responseBody of | ||
Right sm@(SM _ _ _) -> pure sm | ||
Left err -> do | ||
putStrLn $ "Error: decoding webmentions. aeson error: " | ||
<> show err | ||
pure $ SM mempty mempty mempty | ||
|
||
bad -> do | ||
putStrLn $ "Error: fetching webmentions. status code: " <> show bad | ||
pure emptySM | ||
|
||
fileExist "webmentions.json" >>= \case | ||
True -> do | ||
SM oldLikes oldReplies oldReposts <- fromMaybe emptySM . decodeStrict <$> B.readFile "webmentions.json" | ||
|
||
let SM newLikes newReplies newReposts = newMentions | ||
result = SM (merge newLikes oldLikes) (merge newReplies oldReplies) | ||
(merge oldReposts oldReposts) | ||
|
||
merge = Map.unionWith checkIds | ||
|
||
checkIds :: Vector Value -> Vector Value -> Vector Value | ||
checkIds vec1 vec2 = VAlg.nubBy comparison (vec1 <> vec2) | ||
|
||
comparison v1@(Object o1) v2@(Object o2) = | ||
fromMaybe (Ord.compare (encode v1) (encode v2)) $ | ||
Ord.compare <$> JK.lookup "wm-id" o1 <*> JK.lookup "wm-id" o2 | ||
|
||
comparison o1 o2 = (Ord.compare (encode o1) (encode o2)) | ||
|
||
encodeFile "webmentions.json" result | ||
pure result | ||
|
||
False -> do | ||
encodeFile "webmentions.json" newMentions | ||
pure newMentions | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,5 @@ | ||
<footer> | ||
<p>© 2025 Jezen Thomas</p> | ||
<a href="https://github.com/jezen" rel="me">github.com/jezen</a> | ||
$partial("templates/feed.html")$ | ||
</footer> |
Oops, something went wrong.