taffybar-7.2.0: src/System/Taffybar/Widget/WttrIn.hs
{-# LANGUAGE OverloadedStrings #-}
-- | This is a simple weather widget that polls wttr.in to retrieve the weather,
-- instead of relying on noaa data.
--
-- Useful if NOAA data doesn't cover your needs, or if you just like wttr.in
-- better.
--
-- For more information on how to use wttr.in, see <https://wttr.in/:help>.
module System.Taffybar.Widget.WttrIn (textWttrNew) where
import Control.Exception as E (handle)
import Control.Monad.IO.Class (MonadIO)
import Data.ByteString (ByteString)
import Data.ByteString.Lazy (toStrict)
import Data.Maybe (isJust)
import qualified Data.Text as T
import Data.Text.Encoding (decodeUtf8)
import GI.Gtk (Widget)
import Network.HTTP.Client
( HttpException,
Request (requestHeaders),
Response (responseBody, responseStatus),
defaultManagerSettings,
httpLbs,
newManager,
parseRequest,
)
import Network.HTTP.Types.Status (statusIsSuccessful)
import System.Log.Logger (Priority (ERROR), logM)
import System.Taffybar.Widget.Generic.PollingLabel (pollingLabelWithVariableDelayAndRefresh)
import Text.Regex (matchRegex, mkRegex)
-- | Creates a GTK Label widget that polls the requested wttr.in url for weather
-- information.
--
-- Not compatible with image endpoints and binary data, such as the %.png%
-- endpoints.
--
-- > -- Yields a label with the text "London: ⛅️ +72°F". Updates every 60
-- > -- seconds.
-- > textWttrNew "http://wttr.in/London?format=3" 60
textWttrNew ::
(MonadIO m) =>
-- | URL. All non-alphanumeric characters must be properly %-encoded.
String ->
-- | Update Interval (in seconds)
Double ->
m Widget
textWttrNew url interval = pollingLabelWithVariableDelayAndRefresh action True
where
action = do
rsp <- callWttr url
return (rsp, Nothing, interval)
-- | IO Action that calls wttr.in as per the user's request.
callWttr :: String -> IO T.Text
callWttr url =
let unknownLocation rsp =
-- checks for a common wttr.in bug
isJust $ T.stripPrefix "Unknown location; please try" rsp
isImage = isJust . matchRegex (mkRegex ".png")
getResponseData r =
( statusIsSuccessful $ responseStatus r,
toStrict $ responseBody r
)
in do
manager <- newManager defaultManagerSettings
request <- parseRequest url
(isOk, response) <-
handle
logException
( getResponseData
<$> httpLbs
(request {requestHeaders = [("User-Agent", "curl")]})
manager
)
let body = decodeUtf8 response
return $
if not isOk || isImage url || unknownLocation body
then "✨"
else body
-- Logs an Http Exception and returns wttr.in's weather unknown label.
logException :: HttpException -> IO (Bool, ByteString)
logException e = do
let errmsg = show e
logM
"System.Taffybar.Widget.WttrIn"
ERROR
("Warning: Couldn't call wttr.in. \n" ++ errmsg)
return (False, "✨")