Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
#!/usr/bin/env stack
-- stack --resolver lts-12.0 script
module Main
( main
)
where
import qualified Control.Arrow
import qualified Data.Aeson
import qualified Data.Aeson.Types
import qualified Data.List
import qualified Data.Maybe
import qualified Data.Text
import qualified Data.Text.Encoding
import qualified Data.Time
import qualified Network.HTTP.Client
import qualified Network.HTTP.Client.TLS
import qualified Network.HTTP.Types
import qualified System.Environment
main :: IO ()
main = do
key <- System.Environment.getEnv "MEETUP_API_KEY"
today <- Data.Time.utctDay <$> Data.Time.getCurrentTime
url <- makeUrl
"https://api.meetup.com/find/upcoming_events"
[ ("end_date_range", formatDay $ Data.Time.addDays 8 today)
, ("key", key)
, ("oder", "time")
, ("radius", "global")
, ("start_date_range", formatDay $ Data.Time.addDays (-1) today)
, ("text", "haskell")
]
request <- Network.HTTP.Client.parseUrlThrow url
manager <- Network.HTTP.Client.TLS.newTlsManager
response <- Network.HTTP.Client.httpLbs request manager
payload <-
either fail pure
. Data.Aeson.eitherDecode
$ Network.HTTP.Client.responseBody response
mapM_ printEvent
. Data.List.sortOn (eventLocalDate Control.Arrow.&&& eventYesRsvpCount)
$ payloadEvents payload
printEvent :: Event -> IO ()
printEvent event = putStrLn $ concat
[ "- "
, formatDate $ eventLocalDate event
, ": ["
, eventName event
, "]("
, eventLink event
, ") hosted by ["
, groupName $ eventGroup event
, "](https://www.meetup.com/"
, groupUrlname $ eventGroup event
, "/) in ["
, eventLocation event
, "](https://www.openstreetmap.org/?mlat="
, show $ eventLatitude event
, "&mlon="
, show $ eventLongitude event
, ")"
]
makeUrl :: String -> [(String, String)] -> IO String
makeUrl url query =
either (fail . show) (pure . Data.Text.unpack)
. Data.Text.Encoding.decodeUtf8'
$ Data.Text.Encoding.encodeUtf8 (Data.Text.pack url)
<> Network.HTTP.Types.renderQuery True (Network.HTTP.Types.toQuery query)
formatDay :: Data.Time.Day -> String
formatDay = Data.Time.formatTime Data.Time.defaultTimeLocale "%FT00:00:00"
formatDate :: Date -> String
formatDate (Date day) =
Data.Time.formatTime Data.Time.defaultTimeLocale "%A, %B %-d" day
newtype Payload = Payload
{ payloadEvents :: [Event]
}
instance Data.Aeson.FromJSON Payload where
parseJSON = Data.Aeson.withObject "Payload"$ \object -> Payload
<$> required object "events"
data Event = Event
{ eventGroup :: Group
, eventLink :: String
, eventLocalDate :: Date
, eventName :: String
, eventVenue :: Maybe Venue
, eventYesRsvpCount :: Integer
}
instance Data.Aeson.FromJSON Event where
parseJSON = Data.Aeson.withObject "Event" $ \object -> Event
<$> required object "group"
<*> required object "link"
<*> required object "local_date"
<*> required object "name"
<*> optional object "venue"
<*> required object "yes_rsvp_count"
eventLocation :: Event -> String
eventLocation event =
maybe
(groupLocalizedLocation $ eventGroup event)
(\venue -> Data.List.intercalate ", " $ Data.Maybe.catMaybes
[ Just $ venueCity venue
, venueState venue
, Just $ venueLocalizedCountryName venue
]
)
$ eventVenue event
eventLatitude :: Event -> Double
eventLatitude event =
maybe (groupLat $ eventGroup event) venueLat $ eventVenue event
eventLongitude :: Event -> Double
eventLongitude event =
maybe (groupLon $ eventGroup event) venueLon $ eventVenue event
data Group = Group
{ groupLat :: Double
, groupLon :: Double
, groupName :: String
, groupLocalizedLocation :: String
, groupUrlname :: String
}
instance Data.Aeson.FromJSON Group where
parseJSON = Data.Aeson.withObject "Group" $ \object -> Group
<$> required object "lat"
<*> required object "lon"
<*> required object "name"
<*> required object "localized_location"
<*> required object "urlname"
data Venue = Venue
{ venueCity :: String
, venueLat :: Double
, venueLocalizedCountryName :: String
, venueLon :: Double
, venueState :: Maybe String
}
instance Data.Aeson.FromJSON Venue where
parseJSON = Data.Aeson.withObject "Venue" $ \object -> Venue
<$> required object "city"
<*> required object "lat"
<*> required object "localized_country_name"
<*> required object "lon"
<*> optional object "state"
required
:: Data.Aeson.FromJSON a
=> Data.Aeson.Object
-> String
-> Data.Aeson.Types.Parser a
required object key = object Data.Aeson..: Data.Text.pack key
optional
:: Data.Aeson.FromJSON a
=> Data.Aeson.Object
-> String
-> Data.Aeson.Types.Parser (Maybe a)
optional object key = object Data.Aeson..:? Data.Text.pack key
newtype Date
= Date Data.Time.Day
deriving (Eq, Ord)
instance Data.Aeson.FromJSON Date where
parseJSON value = do
string <- Data.Aeson.parseJSON value
Date <$> Data.Time.parseTimeM False Data.Time.defaultTimeLocale "%F" string
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.