Background.purs 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129
  1. module JobSearchExtension.Background where
  2. import Prelude
  3. import Browser.WebExt.BrowserAction (setBadgeBackgroundColor, setBadgeText)
  4. import Browser.WebExt.Port (Port)
  5. import Browser.WebExt.Runtime (MessageSender(..))
  6. import Browser.WebExt.Tabs (TabId)
  7. import Data.Argonaut.Decode (printJsonDecodeError)
  8. import Data.Either (Either(..))
  9. import Data.Foldable (for_)
  10. import Data.Int64 as I64
  11. import Data.Maybe (Maybe(..))
  12. import Effect (Effect)
  13. import Effect.Aff (launchAff_)
  14. import Effect.Class (liftEffect)
  15. import Effect.Class.Console (debug, error, log, logShow)
  16. import JobSearchExtension.NativeMessage (ApplicationProcess(..), NativeMessage(..), connectToNativeApplication, onNativeDisconnectAddListener, onNativeMessageAddListener, sendMessageToNative)
  17. import JobSearchExtension.RuntimeMessage (RuntimeMessage(..), onRuntimeMessageAddListener)
  18. import JobSearchExtension.Storage (clearAllJobs, getJobsPath, storeJob)
  19. import LinkedIn.Jobs.JobOffer (JobOffer(..))
  20. import LinkedIn.Loadable (sleep)
  21. import LinkedIn.Output.Types (Output(..))
  22. import LinkedIn.PageUrl (PageUrl(..))
  23. import LinkedIn.UI.Basic.Types (JobOfferId(..))
  24. import Web.URL as URL
  25. main :: Effect Unit
  26. main = do
  27. log "[bg] starting up"
  28. port <- connectToNativeApplication "job_search_background"
  29. onNativeMessageAddListener port nativeMessageHandler
  30. onNativeDisconnectAddListener port \p -> log $ "disconnected from native port " <> p.name <> " (" <> p.error <> ")"
  31. onRuntimeMessageAddListener $ contentScriptMessageHandler port
  32. sendConfigurationToNative port
  33. contentScriptMessageHandler ∷ Port -> RuntimeMessage -> MessageSender → Effect Unit
  34. contentScriptMessageHandler
  35. port
  36. (RuntimeMessagePageContent (UrlJobOffer (JobOfferId jobId)) (OutJobOffer offer))
  37. (MessageSender {tab: Just {id, url}}) =
  38. case maybeMsg offer, maybeCompany offer of
  39. Just msgJob, Just msgCompany -> do
  40. sendMessageToNative port msgJob
  41. sendMessageToNative port msgCompany
  42. setBadgeOk id
  43. _, _ -> do
  44. error "Job offer sent by content script could not be sent"
  45. setBadgeKo id
  46. where
  47. maybeMsg (JobOffer jo) = ado
  48. url <- cleanUpUrl url
  49. in NativeMessageAddJob {
  50. id: "linked_in_" <> I64.toString jobId,
  51. origin: "linked_in",
  52. title: jo.title,
  53. url,
  54. company: jo.companyName,
  55. location: jo.location,
  56. flexibility: jo.flexibility,
  57. application_process: Just $ if jo.hasSimplifiedApplicationProcess then ApplicationProcessLinkedInSimplified else ApplicationProcessRegular
  58. }
  59. maybeCompany (JobOffer jo) = Just $ NativeMessageAddCompany {
  60. name: jo.companyName,
  61. domain: jo.companyDomain,
  62. url: Just jo.companyLink
  63. }
  64. contentScriptMessageHandler
  65. _
  66. (RuntimeMessageError err)
  67. (MessageSender {tab: Just {id, url}}) = do
  68. setBadgeKo id
  69. error $ "tab " <> show url <> " sent an error : " <> show err
  70. contentScriptMessageHandler _ m (MessageSender {tab, id}) = do
  71. let
  72. senderMsg = case tab of
  73. Just {url} -> "tab " <> url
  74. Nothing -> "unknown " <> id
  75. msg = "[bg] received " <> show m <> " from " <> senderMsg
  76. debug msg
  77. setBadgeKo ∷ TabId → Effect Unit
  78. setBadgeKo id = setBadge id "KO" "red"
  79. setBadgeOk ∷ TabId → Effect Unit
  80. setBadgeOk id = setBadge id "OK" "green"
  81. setBadge ∷ TabId → String → String → Effect Unit
  82. setBadge tabId text color = do
  83. setBadgeText text tabId
  84. setBadgeBackgroundColor color tabId
  85. cleanUpUrl :: String -> Maybe String
  86. cleanUpUrl u = do
  87. url <- URL.fromAbsolute u
  88. pure $ URL.toString $ URL.setSearch "" url
  89. nativeMessageHandler ∷ Port -> NativeMessage → Effect Unit
  90. nativeMessageHandler _ (NativeMessageJobOfferList job_offers) = do
  91. clearAllJobs
  92. for_ job_offers \jo -> do
  93. storeJob jo
  94. nativeMessageHandler _ NativeMessageStorageNotReady = log "[bg] waiting for storage"
  95. nativeMessageHandler port NativeMessageStorageReady = sendMessageToNative port $ NativeMessageListJobsRequest
  96. nativeMessageHandler port NativeMessageStorageUpdated = sendMessageToNative port $ NativeMessageListJobsRequest
  97. nativeMessageHandler port (NativeMessageJobAdded _) = sendMessageToNative port $ NativeMessageListJobsRequest
  98. -- TODO : The actual solution is to make the sender stateful
  99. nativeMessageHandler port (NativeMessageMessageNotProcessed msg) = launchAff_ do
  100. sleep 500
  101. liftEffect $ sendMessageToNative port msg
  102. nativeMessageHandler _ m = logShow m
  103. sendConfigurationToNative ∷ Port → Effect Unit
  104. sendConfigurationToNative port = launchAff_ do
  105. path <- getJobsPath
  106. case path of
  107. Left l' -> log $ "Could not read value of jobsPath : " <> printJsonDecodeError l'
  108. Right path' -> liftEffect $ sendMessageToNative port $ NativeMessageInitialConfiguration {jobsPath: path'}