module Web.Twitter.Enumerator.Fetch
(
UserParam (..)
, ListParam (..)
, statusesHomeTimeline
, statusesMentions
, statusesPublicTimeline
, statusesRetweetedByMe
, statusesRetweetedToMe
, statusesRetweetsOfMe
, statusesUserTimeline
, statusesRetweetedToUser
, statusesRetweetedByUser
, statusesIdRetweetedBy
, statusesIdRetweetedByIds
, statusesRetweetsId
, statusesShowId
, search
, friendsIds
, followersIds
, usersShow
, listsAll
, listsMembers
, userstream
, statusesFilter
)
where
import Web.Twitter.Enumerator.Types
import Web.Twitter.Enumerator.Monad
import Web.Twitter.Enumerator.Utils
import Web.Twitter.Enumerator.Api
import Data.Aeson hiding (Error)
import qualified Data.Aeson.Types as AE
import qualified Network.HTTP.Types as HT
import Data.Enumerator hiding (map, filter, drop, span, iterate)
import qualified Data.Enumerator.List as EL
import qualified Data.Text as T
import Data.ByteString (ByteString)
import qualified Data.ByteString.Char8 as B8
import Data.Maybe
import Control.Monad.Trans.Class
import Control.Applicative
import qualified Data.Map as M
apiGet :: FromJSON a => String -> HT.Query -> Iteratee a IO b -> TW b
apiGet uri query iter = run_ $ api True "GET" uri query (handleParseError iter')
where iter' = enumJSON =$ EL.map fromJSON' =$ skipNothing =$ iter
statuses :: (FromJSON a, Show a) => String -> HT.Query -> Enumerator a TW b
statuses uri query = apiWithPages True furi query 1
where furi = endpoint ++ "statuses/" ++ uri
apiWithPages :: (FromJSON a, Show a) => Bool -> String -> HT.Query -> Integer -> Enumerator a TW b
apiWithPages isStatuses uri query initPage =
checkContinue1 go initPage
where
go loop page k = do
let query' = insertQuery "page" (toMaybeByteString page) query
res <- lift $ run_ $ api isStatuses "GET" uri query' (handleParseError (enumJSON =$ (if isStatuses then iterPageC else iterPageCSearch)))
case res of
Just [] -> k EOF
Just xs -> k (Chunks xs) >>== loop (page + 1)
Nothing -> k EOF
iterPageC :: (Monad m, FromJSON a) => Iteratee Value m (Maybe [a])
iterPageC = do
ret <- EL.head
case ret of
Just v -> return . fromJSON' $ v
Nothing -> return Nothing
iterPageCSearch :: (Monad m, FromJSON a) => Iteratee Value m (Maybe [a])
iterPageCSearch = do
ret <- EL.head
case ret of
Just v -> return . fromJSONSearch' $ v
Nothing -> return Nothing
insertQuery :: ByteString -> Maybe ByteString -> HT.Query -> HT.Query
insertQuery key value = mk
where mk = M.toList . M.insert key value . M.fromList
statusesHomeTimeline :: HT.Query -> Enumerator Status TW a
statusesHomeTimeline = statuses "home_timeline.json"
statusesMentions :: HT.Query -> Enumerator Status TW a
statusesMentions = statuses "mentions.json"
statusesPublicTimeline :: HT.Query -> Enumerator Status TW a
statusesPublicTimeline = statuses "public_timeline.json"
statusesRetweetedByMe :: HT.Query -> Enumerator Status TW a
statusesRetweetedByMe = statuses "retweeted_by_me.json"
statusesRetweetedToMe :: HT.Query -> Enumerator Status TW a
statusesRetweetedToMe = statuses "retweeted_to_me.json"
statusesRetweetsOfMe :: HT.Query -> Enumerator Status TW a
statusesRetweetsOfMe = statuses "retweeted_of_me.json"
statusesUserTimeline :: HT.Query -> Enumerator Status TW a
statusesUserTimeline = statuses "user_timeline.json"
statusesRetweetedToUser :: HT.Query -> Enumerator Status TW a
statusesRetweetedToUser = statuses "retweeted_to_user.json"
statusesRetweetedByUser :: HT.Query -> Enumerator Status TW a
statusesRetweetedByUser = statuses "retweeted_by_user.json"
statusesIdRetweetedBy :: StatusId -> HT.Query -> Enumerator User TW a
statusesIdRetweetedBy status_id = statuses (show status_id ++ "/retweeted_by.json")
statusesIdRetweetedByIds :: StatusId -> HT.Query -> Enumerator UserId TW a
statusesIdRetweetedByIds status_id = statuses (show status_id ++ "/retweeted_by/ids.json")
statusesRetweetsId :: StatusId -> HT.Query -> TW [RetweetedStatus]
statusesRetweetsId status_id query = apiGet uri query EL.head_
where uri = endpoint ++ "statuses/retweets/" ++ show status_id ++ ".json"
statusesShowId :: StatusId -> HT.Query -> TW Status
statusesShowId status_id query = apiGet (endpoint ++ "statuses/show/" ++ show status_id ++ ".json") query EL.head_
search :: String -> Enumerator SearchStatus TW a
search q = apiWithPages False (endpointSearch ++ "search.json") query 1
where query = [("q", Just . B8.pack $ q)]
friendsIds, followersIds :: UserParam -> Enumerator UserId TW a
friendsIds q = apiCursor (endpoint ++ "friends/ids.json") (mkUserParam q) "ids" (1)
followersIds q = apiCursor (endpoint ++ "followers/ids.json") (mkUserParam q) "ids" (1)
usersShow :: UserParam -> TW User
usersShow q = apiGet (endpoint ++ "users/show.json") (mkUserParam q) EL.head_
listsAll :: UserParam -> Enumerator List TW a
listsAll q = apiCursor (endpoint ++ "lists/all.json") (mkUserParam q) "" (1)
listsMembers :: ListParam -> Enumerator User TW a
listsMembers q = apiCursor (endpoint ++ "lists/members.json") (mkListParam q) "users" (1)
data Cursor a =
Cursor
{ cursorCurrent :: [a]
, cursorNext :: Maybe Integer
} deriving (Show, Eq)
iterCursor' :: (Monad m, FromJSON a) => T.Text -> Iteratee Value m (Maybe (Cursor a))
iterCursor' key = do
ret <- EL.head
case ret of
Just v -> return . AE.parseMaybe (parseCursor key) $ v
Nothing -> return Nothing
iterCursor :: (Monad m, FromJSON a) => T.Text -> Iteratee ByteString m (Maybe (Cursor a))
iterCursor key = enumLine =$ handleParseError (enumJSON =$ iterCursor' key)
parseCursor :: FromJSON a => T.Text -> Value -> AE.Parser (Cursor a)
parseCursor key (Object o) =
checkError o
<|>
Cursor <$> o .: key <*> o .:? "next_cursor"
parseCursor _ v@(Array _) = return $ Cursor (fromMaybe [] $ fromJSON' v) Nothing
parseCursor _ o = fail $ "Error at parseCursor: unknown object " ++ show o
apiCursor
:: (FromJSON a, Show a) =>
String
-> HT.Query
-> T.Text
-> Integer
-> Enumerator a TW b
apiCursor uri query cursorKey initCur =
checkContinue1 go initCur
where
go loop cursor k = do
let query' = insertQuery "cursor" (toMaybeByteString cursor) query
res <- lift $ run_ $ api True "GET" uri query' (iterCursor cursorKey)
case res of
Just r -> do
let nextCur = cursorNext r
chunks = Chunks . cursorCurrent $ r
case nextCur of
Just 0 -> k chunks
Just nc -> k chunks >>== loop nc
Nothing -> k chunks
Nothing -> k EOF
apiIter :: (FromJSON a, Monad m) => Iteratee a m b -> Iteratee ByteString m b
apiIter iter = enumLine =$ handleParseError (enumJSON =$ EL.map fromJSON' =$ skipNothing =$ iter)
userstream :: Iteratee StreamingAPI IO a -> Iteratee ByteString TW a
userstream = api True "GET" "https://userstream.twitter.com/2/user.json" [] . apiIter
statusesFilter :: HT.Query -> Iteratee StreamingAPI IO a -> Iteratee ByteString TW a
statusesFilter query = api True "GET" "https://stream.twitter.com/1/statuses/filter.json" query . apiIter