gdritter repos lektor / 802bef8
First pass at Atom feed fetching Getty Ritter 9 years ago
3 changed file(s) with 136 addition(s) and 0 deletion(s). Collapse all Expand all
1 Copyright (c) 2015, Getty Ritter
2
3 All rights reserved.
4
5 Redistribution and use in source and binary forms, with or without
6 modification, are permitted provided that the following conditions are met:
7
8 * Redistributions of source code must retain the above copyright
9 notice, this list of conditions and the following disclaimer.
10
11 * Redistributions in binary form must reproduce the above
12 copyright notice, this list of conditions and the following
13 disclaimer in the documentation and/or other materials provided
14 with the distribution.
15
16 * Neither the name of Getty Ritter nor the names of other
17 contributors may be used to endorse or promote products derived
18 from this software without specific prior written permission.
19
20 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23 A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24 OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26 LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
1 name: lektor-rss
2 version: 0.0.0
3 author: Getty Ritter<gettylefou@gmail.com>
4 maintainer: Getty Ritter<gettylefou@gmail.com>
5 license: BSD3
6 license-file: LICENSE
7 -- synopsis:
8 -- description:
9 cabal-version: >= 1.10
10 build-type: Simple
11
12 executable lektor-rss
13 hs-source-dirs: src
14 main-is: Main.hs
15 default-language: Haskell2010
16 ghc-options: -Wall
17 build-depends: base >= 4 && < 5, feed, HTTP, SHA, xml, bytestring, directory, filepath, hostname, time, unix
18
19 source-repository head
20 type: git
21 -- Location:
1 {-# LANGUAGE ViewPatterns #-}
2 {-# LANGUAGE LambdaCase #-}
3 {-# LANGUAGE RecordWildCards #-}
4
5 module Main where
6
7 import Control.Monad (zipWithM_)
8 import Data.ByteString.Lazy.Char8 (pack)
9 import Data.Digest.Pure.SHA
10 import Data.Monoid ((<>))
11 import Data.Time.Clock.POSIX (getPOSIXTime)
12 import Network.HostName
13 import Network.HTTP
14 import System.Directory
15 import System.Environment
16 import System.Exit (die)
17 import System.FilePath ((</>))
18 import System.Posix.Process (getProcessID)
19 import Text.Atom.Feed.Import (elementFeed)
20 import Text.Atom.Feed
21 import Text.XML.Light.Input (parseXMLDoc)
22
23 usage :: String
24 usage = "Usage: lektor-rss [feed url]"
25
26 lektorSetup :: IO ()
27 lektorSetup = do
28 dir <- getEnv "LEKTORDIR"
29 setCurrentDirectory dir
30 mapM_ (createDirectoryIfMissing True)
31 [ "src", "tmp", "new", "cur" ]
32
33 main :: IO ()
34 main = do
35 lektorSetup
36 getArgs >>= \case
37 [] -> putStrLn usage
38 (url:_) -> do
39 simpleHTTP (getRequest url) >>= \case
40 Left err -> die "Unable to fetch document"
41 Right r -> makeEntries url (rspBody r)
42
43 makeEntries :: String -> String -> IO ()
44 makeEntries url s = case parseXMLDoc s of
45 Nothing -> die "Unale to parse XML document"
46 Just xml -> case elementFeed xml of
47 Just atom -> buildLektorDir url atom
48 Nothing -> die "XML document not an Atom feed"
49
50 contentAsString :: TextContent -> String
51 contentAsString (TextString s) = s
52 contentAsString _ = error "..."
53
54 buildLektorDir :: String -> Feed -> IO ()
55 buildLektorDir url feed = do
56 let hash = showDigest (sha1 (pack url))
57 mapM_ (createDirectoryIfMissing True)
58 [ "src" </> hash
59 , "tmp" </> hash
60 , "new" </> hash
61 , "cur" </> hash
62 ]
63 writeFile ("src" </> hash </> "name")
64 (contentAsString (feedTitle feed))
65 writeFile ("src" </> hash </> "id") url
66 zipWithM_ (buildLektorEntry hash) [0..] (reverse (feedEntries feed))
67
68 buildLektorEntry :: String -> Int -> Entry -> IO ()
69 buildLektorEntry hash n (Entry { .. }) = do
70 t <- fmap (floor . realToFrac) getPOSIXTime
71 p <- getProcessID
72 h <- getHostName
73 let dirId = show t <> ".P" <> show p <> "Q" <> show n <> "." <> h
74 let tmpDir = "tmp" </> hash </> dirId
75 createDirectoryIfMissing True tmpDir
76 writeFile (tmpDir </> "title") (contentAsString entryTitle)
77 writeFile (tmpDir </> "id") entryId
78 writeFile (tmpDir </> "content") $ case entryContent of
79 Just (TextContent s) -> s
80 Just (HTMLContent s) -> s
81 _ -> "[unsupported content]"
82 writeFile (tmpDir </> "type") $ case entryContent of
83 Just (HTMLContent s) -> "text/html"
84 _ -> "text/plain"
85 renameDirectory tmpDir ("new" </> hash </> dirId)