Main.hs 2.6 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485
  1. {-# LANGUAGE ViewPatterns #-}
  2. {-# LANGUAGE LambdaCase #-}
  3. {-# LANGUAGE RecordWildCards #-}
  4. module Main where
  5. import Control.Monad (zipWithM_)
  6. import Data.ByteString.Lazy.Char8 (pack)
  7. import Data.Digest.Pure.SHA
  8. import Data.Monoid ((<>))
  9. import Data.Time.Clock.POSIX (getPOSIXTime)
  10. import Network.HostName
  11. import Network.HTTP
  12. import System.Directory
  13. import System.Environment
  14. import System.Exit (die)
  15. import System.FilePath ((</>))
  16. import System.Posix.Process (getProcessID)
  17. import Text.Atom.Feed.Import (elementFeed)
  18. import Text.Atom.Feed
  19. import Text.XML.Light.Input (parseXMLDoc)
  20. usage :: String
  21. usage = "Usage: lektor-rss [feed url]"
  22. lektorSetup :: IO ()
  23. lektorSetup = do
  24. dir <- getEnv "LEKTORDIR"
  25. setCurrentDirectory dir
  26. mapM_ (createDirectoryIfMissing True)
  27. [ "src", "tmp", "new", "cur" ]
  28. main :: IO ()
  29. main = do
  30. lektorSetup
  31. getArgs >>= \case
  32. [] -> putStrLn usage
  33. (url:_) -> do
  34. simpleHTTP (getRequest url) >>= \case
  35. Left err -> die "Unable to fetch document"
  36. Right r -> makeEntries url (rspBody r)
  37. makeEntries :: String -> String -> IO ()
  38. makeEntries url s = case parseXMLDoc s of
  39. Nothing -> die "Unale to parse XML document"
  40. Just xml -> case elementFeed xml of
  41. Just atom -> buildLektorDir url atom
  42. Nothing -> die "XML document not an Atom feed"
  43. contentAsString :: TextContent -> String
  44. contentAsString (TextString s) = s
  45. contentAsString _ = error "..."
  46. buildLektorDir :: String -> Feed -> IO ()
  47. buildLektorDir url feed = do
  48. let hash = showDigest (sha1 (pack url))
  49. mapM_ (createDirectoryIfMissing True)
  50. [ "src" </> hash
  51. , "tmp" </> hash
  52. , "new" </> hash
  53. , "cur" </> hash
  54. ]
  55. writeFile ("src" </> hash </> "name")
  56. (contentAsString (feedTitle feed))
  57. writeFile ("src" </> hash </> "id") url
  58. zipWithM_ (buildLektorEntry hash) [0..] (reverse (feedEntries feed))
  59. buildLektorEntry :: String -> Int -> Entry -> IO ()
  60. buildLektorEntry hash n (Entry { .. }) = do
  61. t <- fmap (floor . realToFrac) getPOSIXTime
  62. p <- getProcessID
  63. h <- getHostName
  64. let dirId = show t <> ".P" <> show p <> "Q" <> show n <> "." <> h
  65. let tmpDir = "tmp" </> hash </> dirId
  66. createDirectoryIfMissing True tmpDir
  67. writeFile (tmpDir </> "title") (contentAsString entryTitle)
  68. writeFile (tmpDir </> "id") entryId
  69. writeFile (tmpDir </> "content") $ case entryContent of
  70. Just (TextContent s) -> s
  71. Just (HTMLContent s) -> s
  72. _ -> "[unsupported content]"
  73. writeFile (tmpDir </> "type") $ case entryContent of
  74. Just (HTMLContent s) -> "text/html"
  75. _ -> "text/plain"
  76. renameDirectory tmpDir ("new" </> hash </> dirId)