reddit-pub/src/MyLib.hs

90 lines
3.1 KiB
Haskell
Raw Normal View History

2021-10-27 23:30:48 +03:00
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE DeriveAnyClass #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE DerivingVia #-}
2021-10-25 19:04:24 +03:00
{-# LANGUAGE OverloadedStrings #-}
2021-10-27 23:30:48 +03:00
{-# LANGUAGE TypeOperators #-}
2021-10-27 21:10:22 +03:00
module MyLib (defaultMain) where
2021-10-25 19:04:24 +03:00
import Control.Exception (bracket)
import Control.Lens
2021-10-27 21:10:22 +03:00
import Control.Monad (void)
2021-10-27 23:30:48 +03:00
import Data.Aeson (FromJSON, ToJSON, Value)
2021-10-27 21:10:22 +03:00
import qualified Data.Aeson as A
2021-10-25 19:04:24 +03:00
import Data.Config
2021-10-27 23:30:48 +03:00
import Data.Deriving.Aeson
2021-10-27 21:10:22 +03:00
import Data.Foldable (for_)
import Data.Functor.Contravariant ((>$<))
import Data.Text (Text)
2021-10-25 19:04:24 +03:00
import qualified Data.Text.Strict.Lens as T
2021-10-27 23:30:48 +03:00
import qualified Database.SQLite.Simple as SQL
import GHC.Generics (Generic)
import qualified Membership
2021-10-25 19:04:24 +03:00
import Network.AMQP
2021-10-27 23:30:48 +03:00
( Channel
, DeliveryMode(Persistent)
, exchangeName
, exchangeType
, msgBody
, msgDeliveryMode
, newMsg
, publishMsg, openConnection, closeConnection, openChannel, newExchange, declareExchange
)
import Network.Reddit (RedditId (RedditId), publishEntries)
2021-10-25 19:04:24 +03:00
import Network.Wreq.Session (newSession)
import Publish (Publish(..))
2021-10-27 23:30:48 +03:00
import Data.Aeson.Lens (key, _String)
2021-10-27 23:41:05 +03:00
import Data.Bool (bool)
2021-10-27 23:30:48 +03:00
data MessageType = Create | Update
deriving stock (Show, Eq, Generic)
deriving anyclass (ToJSON, FromJSON)
data Message = Message
{ messageType :: MessageType
, messageIdentifier :: RedditId
, messageContent :: Value
}
deriving stock (Show, Eq, Generic)
deriving (ToJSON, FromJSON)
via AesonCodec (Field (CamelCase <<< DropPrefix "message")) Message
toMessage :: Value -> Message
toMessage entry = Message Create (RedditId (entry ^. key "id" . _String)) entry
2021-10-25 19:04:24 +03:00
2021-10-27 23:21:09 +03:00
sqlRecorder :: SQL.Connection -> Publish IO RedditId
sqlRecorder conn = Publish $ Membership.recordSeen conn
2021-10-25 19:04:24 +03:00
2021-10-27 23:41:05 +03:00
amqpPublisher :: SQL.Connection -> Channel -> Text -> Publish IO Message
amqpPublisher sqlConn channel exchange = Publish $ \msg -> do
seen <- Membership.isSeen sqlConn (messageIdentifier msg)
let msg' = msg{messageType = bool Create Update seen}
void $ publishMsg channel exchange routingKey (message (A.encode msg'))
2021-10-25 19:04:24 +03:00
where
2021-10-27 21:10:22 +03:00
routingKey = "doesn't matter on fanout"
2021-10-25 19:04:24 +03:00
message lbs = newMsg
{ msgBody = lbs
, msgDeliveryMode = Just Persistent
}
2022-02-10 21:47:09 +02:00
defaultMain :: FilePath -> IO ()
defaultMain path = do
conf <- readConfig path
2021-10-27 23:21:09 +03:00
let rabbitConnect = openConnection
2021-10-25 19:04:24 +03:00
(conf ^. amqp . host . T.unpacked)
(conf ^. amqp . vhost)
(conf ^. amqp . username)
(conf ^. amqp . password)
2021-10-27 23:21:09 +03:00
bracket rabbitConnect closeConnection $ \conn -> do
SQL.withConnection (conf ^. sqlite) $ \sqlConn -> do
SQL.execute_ sqlConn "create table if not exists membership (reddit_id primary key)"
chan <- openChannel conn
declareExchange chan newExchange { exchangeName = "reddit_posts", exchangeType = "fanout" }
sess <- newSession
2021-10-27 23:41:05 +03:00
let encoder = amqpPublisher sqlConn chan "reddit_posts"
2021-10-27 23:21:09 +03:00
recorder = sqlRecorder sqlConn
2021-10-27 23:41:05 +03:00
publisher = encoder <> (messageIdentifier >$< recorder)
2021-10-27 23:21:09 +03:00
for_ (conf ^. fetchers) $ \fetcher -> do
print fetcher
2021-10-27 23:30:48 +03:00
publishEntries (toMessage >$< publisher) sess fetcher