Skip to content

Instantly share code, notes, and snippets.

@tfausak
Last active August 9, 2018 05:49
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save tfausak/c275143017f8ab4999a4f36ea82cfce6 to your computer and use it in GitHub Desktop.
Save tfausak/c275143017f8ab4999a4f36ea82cfce6 to your computer and use it in GitHub Desktop.
#!/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