twitter-enumerator 0.0.2 → 0.0.3
raw patch · 7 files changed
+295/−110 lines, 7 filesdep +tls-extradep ~http-enumeratorsetup-changed
Dependencies added: tls-extra
Dependency ranges changed: http-enumerator
Files
- Setup.hs +1/−0
- Web/Twitter/Enumerator/Api.hs +38/−7
- Web/Twitter/Enumerator/Fetch.hs +36/−54
- Web/Twitter/Enumerator/Post.hs +56/−17
- Web/Twitter/Enumerator/Types.hs +138/−29
- Web/Twitter/Enumerator/Utils.hs +22/−0
- twitter-enumerator.cabal +4/−3
Setup.hs view
@@ -1,2 +1,3 @@+#!/usr/bin/env runhaskell import Distribution.Simple main = defaultMain
Web/Twitter/Enumerator/Api.hs view
@@ -1,16 +1,25 @@+{-# LANGUAGE OverloadedStrings #-}+ module Web.Twitter.Enumerator.Api ( api , endpoint+ , endpointSearch+ , UserParam(..)+ , ListParam(..)+ , mkUserParam+ , mkListParam ) where import Web.Twitter.Enumerator.Types import Web.Twitter.Enumerator.Monad+import Web.Twitter.Enumerator.Utils import Network.HTTP.Enumerator import qualified Network.HTTP.Types as HT import Data.Enumerator (Iteratee, throwError, liftTrans) import Data.ByteString (ByteString)+import qualified Data.ByteString.Char8 as B8 import Control.Monad.Trans.Class import Control.Monad.IO.Class (MonadIO (liftIO))@@ -18,13 +27,17 @@ endpoint :: String endpoint = "https://api.twitter.com/1/" -api :: ByteString -- ^ HTTP request method (GET or POST)+endpointSearch :: String+endpointSearch = "http://search.twitter.com/"++api :: Bool -- ^ OAuth required?+ -> ByteString -- ^ HTTP request method (GET or POST) -> String -- ^ API Resource URL -> HT.Query -- ^ Query -> Iteratee ByteString IO a -> Iteratee ByteString TW a-api m url query iter = do- req <- lift $ apiRequest m url query+api toOAuth m url query iter = do+ req <- lift $ apiRequest toOAuth m url query httpMgr req (handleError iter) where handleError iter' st@(HT.Status sc _) _ =@@ -41,10 +54,28 @@ mgr <- lift getManager liftTrans $ http req iterf mgr --apiRequest :: ByteString -> String -> HT.Query -> TW (Request IO)-apiRequest m uri query = do+apiRequest :: Bool -> ByteString -> String -> HT.Query -> TW (Request IO)+apiRequest toOAuth m uri query = do p <- getProxy req <- liftIO $ parseUrl uri >>= \r -> return $ r { method = m, queryString = query, proxy = p }- signOAuthTW req+ if toOAuth then signOAuthTW req+ else return req++data UserParam = UserIdParam UserId | ScreenNameParam String+ deriving (Show, Eq)+data ListParam = ListIdParam Integer | ListNameParam String+ deriving (Show, Eq)++mkUserParam :: UserParam -> HT.Query+mkUserParam (UserIdParam uid) = [("user_id", toMaybeByteString uid)]+mkUserParam (ScreenNameParam sn) = [("screen_name", Just . B8.pack $ sn)]++mkListParam :: ListParam -> HT.Query+mkListParam (ListIdParam lid) = [("list_id", toMaybeByteString lid)]+mkListParam (ListNameParam listname) =+ [("slug", Just . B8.pack $ lstName),+ ("owner_screen_name", Just . B8.pack $ screenName)]+ where+ (screenName, ln) = span (/= '/') listname+ lstName = drop 1 ln
Web/Twitter/Enumerator/Fetch.hs view
@@ -1,9 +1,9 @@ {-# LANGUAGE OverloadedStrings #-} module Web.Twitter.Enumerator.Fetch- ( QueryUser(..)- , QueryList(..)-+ (+ UserParam (..)+ , ListParam (..) -- * Timelines , statusesHomeTimeline , statusesMentions@@ -22,7 +22,7 @@ , statusesShowId -- * Search- -- , search+ , search -- * Direct Messages -- , directMessages@@ -88,33 +88,29 @@ 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.Monad.IO.Class (MonadIO (liftIO)) import Control.Applicative import qualified Data.Map as M -data QueryUser = QUserId UserId | QScreenName String- deriving (Show, Eq)-data QueryList = QListId Integer | QListName String- deriving (Show, Eq)-- apiGet :: FromJSON a => String -> HT.Query -> Iteratee a IO b -> TW b-apiGet uri query iter = run_ $ api "GET" uri query (handleParseError iter')+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 furi query 1+statuses uri query = apiWithPages True furi query 1 where furi = endpoint ++ "statuses/" ++ uri -apiWithPages :: (FromJSON a, Show a) => String -> HT.Query -> Integer -> Enumerator a TW b-apiWithPages uri query initPage =+apiWithPages :: (FromJSON a, Show a) => Bool -> String -> HT.Query -> Integer -> Enumerator a TW b+apiWithPages isStatuses uri query initPage =+-- isStatuses = True: normal GET/statuses APIs with OAuth+-- isStatuses = False: search API checkContinue1 go initPage where go loop page k = do let query' = insertQuery "page" (toMaybeByteString page) query- res <- lift $ run_ $ api "GET" uri query' (handleParseError (enumJSON =$ iterPageC))+ 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)@@ -127,6 +123,13 @@ 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@@ -171,36 +174,26 @@ statusesShowId :: StatusId -> HT.Query -> TW Status statusesShowId status_id query = apiGet (endpoint ++ "statuses/show/" ++ show status_id ++ ".json") query EL.head_ -mkQueryUser :: QueryUser -> HT.Query-mkQueryUser (QUserId uid) = [("user_id", toMaybeByteString uid)]-mkQueryUser (QScreenName sn) = [("screen_name", Just . B8.pack $ sn)]--mkQueryList :: QueryList -> HT.Query-mkQueryList (QListId lid) = [("list_id", toMaybeByteString lid)]-mkQueryList (QListName listname) =- [("slug", Just . B8.pack $ lstName),- ("owner_screen_name", Just . B8.pack $ screenName)]- where- (screenName, ln) = span (/= '/') listname- lstName = drop 1 ln+search :: String -> Enumerator SearchStatus TW a+search q = apiWithPages False (endpointSearch ++ "search.json") query 1+ where query = [("q", Just . B8.pack $ q)] -friendsIds, followersIds :: QueryUser -> Enumerator UserId TW a-friendsIds q = apiCursor (endpoint ++ "friends/ids.json") (mkQueryUser q) "ids" (-1)-followersIds q = apiCursor (endpoint ++ "followers/ids.json") (mkQueryUser q) "ids" (-1)+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 :: QueryUser -> TW User-usersShow q = apiGet (endpoint ++ "users/show.json") (mkQueryUser q) EL.head_+usersShow :: UserParam -> TW User+usersShow q = apiGet (endpoint ++ "users/show.json") (mkUserParam q) EL.head_ -listsAll :: QueryUser -> Enumerator List TW a-listsAll q = apiCursor (endpoint ++ "lists/all.json") (mkQueryUser q) "" (-1)+listsAll :: UserParam -> Enumerator List TW a+listsAll q = apiCursor (endpoint ++ "lists/all.json") (mkUserParam q) "" (-1) -listsMembers :: QueryList -> Enumerator User TW a-listsMembers q = apiCursor (endpoint ++ "lists/members.json") (mkQueryList q) "users" (-1)+listsMembers :: ListParam -> Enumerator User TW a+listsMembers q = apiCursor (endpoint ++ "lists/members.json") (mkListParam q) "users" (-1) data Cursor a = Cursor { cursorCurrent :: [a]- , cursorPrev :: Maybe Integer , cursorNext :: Maybe Integer } deriving (Show, Eq) @@ -214,20 +207,12 @@ iterCursor :: (Monad m, FromJSON a) => T.Text -> Iteratee ByteString m (Maybe (Cursor a)) iterCursor key = enumLine =$ handleParseError (enumJSON =$ iterCursor' key) -handleParseError :: Monad m => Iteratee ByteString m b -> Iteratee ByteString m b-handleParseError iter = iter `catchError` hndl- where- getChunk = continue return- hndl e = getChunk >>= \x -> case x of- Chunks xs -> throwError $ PerserException e xs- _ -> throwError $ PerserException e []- parseCursor :: FromJSON a => T.Text -> Value -> AE.Parser (Cursor a) parseCursor key (Object o) = checkError o <|>- Cursor <$> o .: key <*> o .:? "previous_cursor" <*> o .:? "next_cursor"-parseCursor _ v@(Array _) = return $ Cursor (maybe [] id $ fromJSON' v) Nothing Nothing+ 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@@ -242,7 +227,7 @@ where go loop cursor k = do let query' = insertQuery "cursor" (toMaybeByteString cursor) query- res <- lift $ run_ $ api "GET" uri query' (iterCursor cursorKey)+ res <- lift $ run_ $ api True "GET" uri query' (iterCursor cursorKey) case res of Just r -> do let nextCur = cursorNext r@@ -259,10 +244,7 @@ apiIter iter = enumLine =$ handleParseError (enumJSON =$ EL.map fromJSON' =$ skipNothing =$ iter) userstream :: Iteratee StreamingAPI IO a -> Iteratee ByteString TW a-userstream = api "GET" "https://userstream.twitter.com/2/user.json" [] . apiIter+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 "GET" "https://stream.twitter.com/1/statuses/filter.json" query . apiIter--toMaybeByteString :: Show a => a -> Maybe ByteString-toMaybeByteString = Just . B8.pack . show+statusesFilter query = api True "GET" "https://stream.twitter.com/1/statuses/filter.json" query . apiIter
Web/Twitter/Enumerator/Post.hs view
@@ -2,30 +2,69 @@ module Web.Twitter.Enumerator.Post ( statusesUpdate+ , statusesRetweetId - -- * Friendship- -- , friendshipCreate- -- , friendshipDestroy+ -- * Friends & Followers+ , friendshipsCreate+ -- , friendshipDestroy - -- * Favorites- -- , favoritesCreate- -- , favoritesDestroy+ -- * Favorites+ , favoritesCreate+ , favoritesDestroy - -- * Lists- -- , listsCreate- -- , listsDestroy- -- , listsUpdate- -- , listsMembersCreate- -- , listsMembersDestroy- )- where+ -- * Lists+ -- , listsCreate+ -- , listsDestroy+ -- , listsUpdate+ -- , listsMembersCreate+ -- , listsMembersDestroy + -- * Deprecated+ , retweet+ , friendshipCreate++ ) where++import Data.Aeson hiding (Error)++import Web.Twitter.Enumerator.Types import Web.Twitter.Enumerator.Monad+import Web.Twitter.Enumerator.Utils import Web.Twitter.Enumerator.Api +import Data.Text (Text)+import qualified Data.Text.Encoding as T import Data.ByteString (ByteString) -import Data.Enumerator (Iteratee)+import qualified Network.HTTP.Types as HT+import Data.Enumerator (Iteratee, (=$), run_)+import qualified Data.Enumerator.List as EL -statusesUpdate :: ByteString -> Iteratee ByteString IO a -> Iteratee ByteString TW a-statusesUpdate tweet iter = api "POST" (endpoint ++ "statuses/update.json") [("status", Just tweet)] iter+apiPost :: FromJSON a => String -> HT.Query -> Iteratee a IO b -> TW b+apiPost uri query iter = run_ $ api True "POST" uri query (handleParseError iter')+ where iter' = enumJSON =$ EL.map fromJSON' =$ skipNothing =$ iter++statusesUpdate :: Text -> HT.Query -> TW Status+statusesUpdate tweet query = apiPost (endpoint ++ "statuses/update.json") q (debugEE =$ EL.head_)+ where q = ("status", Just . T.encodeUtf8 $ tweet):query++favoritesCreate :: StatusId -> HT.Query -> TW Status+favoritesCreate sid query = apiPost (endpoint ++ "favorites/create/" ++ show sid ++ ".json") query EL.head_++favoritesDestroy :: StatusId -> HT.Query -> TW Status+favoritesDestroy sid query = apiPost (endpoint ++ "favorites/destroy/" ++ show sid ++ ".json") query EL.head_++statusesRetweetId :: Integer -> HT.Query -> TW RetweetedStatus+statusesRetweetId tweetId query = apiPost (endpoint ++ "statuses/retweet/" ++ show tweetId ++ ".json") query EL.head_++friendshipsCreate :: UserParam -> HT.Query -> TW User+friendshipsCreate user query = apiPost (endpoint ++ "friendships/create.json") q EL.head_+ where q = mkUserParam user ++ query++{-# DEPRECATED retweet "'retweet' will be removed in future releases. Use 'statusesRetweetId' instead" #-}+retweet :: Integer -> Iteratee ByteString IO a -> Iteratee ByteString TW a+retweet tweetId = api True "POST" (endpoint ++ "statuses/retweet/" ++ show tweetId ++ ".json") []++{-# DEPRECATED friendshipCreate "'friendshipCreate' will be removed in future releases. Use 'friendshipsCreate' instead" #-}+friendshipCreate :: UserId -> Iteratee ByteString IO a -> Iteratee ByteString TW a+friendshipCreate uid = api True "POST" (endpoint ++ "friendships/create.json") [("user_id", toMaybeByteString uid)]
Web/Twitter/Enumerator/Types.hs view
@@ -4,17 +4,25 @@ ( TwitterException(..) , DateString , UserId+ , Friends , URLString , UserName , StatusId+ , LanguageCode , StreamingAPI(..) , Status(..)+ , SearchStatus(..) , RetweetedStatus(..) , EventTarget(..) , Event(..) , Delete(..) , User(..) , List(..)+ , Entities(..)+ , Entity(..)+ , HashTagEntity(..)+ , UserEntity(..)+ , URLEntity(..) , checkError ) where@@ -30,16 +38,18 @@ import Control.Exception data TwitterException = HTTPStatusCodeException HT.Status- | PerserException SomeException [ByteString]+ | ParserException SomeException [ByteString] | TwitterErrorMessage T.Text Value deriving (Show, Typeable) instance Exception TwitterException -type DateString = String-type UserId = Integer-type URLString = String-type UserName = T.Text-type StatusId = Integer+type DateString = String+type UserId = Integer+type Friends = [UserId]+type URLString = String+type UserName = T.Text+type StatusId = Integer+type LanguageCode = String data StreamingAPI = SStatus Status | SRetweetedStatus RetweetedStatus@@ -64,7 +74,7 @@ SEvent <$> js <|> SDelete <$> js <|> SFriends <$> js <|>- (return $ SUnknown v)+ return (SUnknown v) where js :: FromJSON a => Parser a js = parseJSON v@@ -77,9 +87,11 @@ , statusText :: T.Text , statusSource :: String , statusTruncated :: Bool+ , statusEntities :: Maybe Entities , statusInReplyTo :: Maybe StatusId , statusInReplyToUser :: Maybe UserId , statusFavorite :: Maybe Bool+ , statusRetweetCount :: Maybe Integer , statusUser :: User } deriving (Show, Eq) @@ -90,12 +102,34 @@ <*> o .: "text" <*> o .: "source" <*> o .: "truncated"+ <*> o .:? "entities" <*> o .:? "in_reply_to_status_id" <*> o .:? "in_reply_to_user_id"- <*> o .:? "favorite"+ <*> o .:? "favorited"+ <*> o .:? "retweet_count" <*> o .: "user" parseJSON _ = mzero +data SearchStatus =+ SearchStatus+ { searchStatusCreatedAt :: DateString+ , searchStatusId :: StatusId+ , searchStatusText :: T.Text+ , searchStatusSource :: String+ , searchStatusUserId :: UserId+ , searchStatusUserName :: UserName+ } deriving (Show, Eq)++instance FromJSON SearchStatus where+ parseJSON (Object o) = checkError o <|>+ SearchStatus <$> o .: "created_at"+ <*> o .: "id"+ <*> o .: "text"+ <*> o .: "source"+ <*> o .: "from_user_id"+ <*> o .: "from_user"+ parseJSON _ = mzero+ data RetweetedStatus = RetweetedStatus { rsCreatedAt :: DateString@@ -103,19 +137,21 @@ , rsText :: T.Text , rsSource :: String , rsTruncated :: Bool+ , rsEntities :: Maybe Entities , rsUser :: User , rsRetweetedStatus :: Status } deriving (Show, Eq) instance FromJSON RetweetedStatus where parseJSON (Object o) = checkError o <|>- RetweetedStatus <$> o .: "created_at"- <*> o .: "id"- <*> o .: "text"- <*> o .: "source"- <*> o .: "truncated"- <*> o .: "user"- <*> o .: "retweeted_status"+ RetweetedStatus <$> o .: "created_at"+ <*> o .: "id"+ <*> o .: "text"+ <*> o .: "source"+ <*> o .: "truncated"+ <*> o .:? "entities"+ <*> o .: "user"+ <*> o .: "retweeted_status" parseJSON _ = mzero data EventType = Favorite | Unfavorite@@ -131,7 +167,7 @@ ETUser <$> parseJSON v <|> ETStatus <$> parseJSON v <|> ETList <$> parseJSON v <|>- (return $ ETUnknown v)+ return (ETUnknown v) parseJSON _ = mzero data Event =@@ -165,19 +201,21 @@ <*> s .: "user_id" parseJSON _ = mzero -type Friends = [UserId]- data User = User { userId :: UserId , userName :: UserName , userScreenName :: String- , userDescription :: T.Text- , userLocation :: T.Text+ , userDescription :: Maybe T.Text+ , userLocation :: Maybe T.Text , userProfileImageURL :: Maybe URLString , userURL :: Maybe URLString , userProtected :: Maybe Bool , userFollowers :: Maybe Int+ , userFriends :: Maybe Int+ , userTweets :: Maybe Int+ , userLangCode :: Maybe LanguageCode+ , userCreatedAt :: Maybe DateString } deriving (Show, Eq) instance FromJSON User where@@ -185,12 +223,16 @@ User <$> o .: "id" <*> o .: "name" <*> o .: "screen_name"- <*> o .: "description"- <*> o .: "location"+ <*> o .:? "description"+ <*> o .:? "location" <*> o .:? "profile_image_url" <*> o .:? "url" <*> o .:? "protected" <*> o .:? "followers_count"+ <*> o .:? "friends_count"+ <*> o .:? "statuses_count"+ <*> o .:? "lang"+ <*> o .:? "created_at" parseJSON _ = mzero data List =@@ -206,11 +248,78 @@ instance FromJSON List where parseJSON (Object o) = checkError o <|>- List <$> o .: "id"- <*> o .: "name"- <*> o .: "full_name"- <*> o .: "member_count"- <*> o .: "subscriber_count"- <*> o .: "mode"- <*> o .: "user"+ List <$> o .: "id"+ <*> o .: "name"+ <*> o .: "full_name"+ <*> o .: "member_count"+ <*> o .: "subscriber_count"+ <*> o .: "mode"+ <*> o .: "user" parseJSON _ = mzero++data HashTagEntity =+ HashTagEntity+ { hashTagText :: T.Text -- ^ The Hashtag text+ } deriving (Show, Eq)++instance FromJSON HashTagEntity where+ parseJSON (Object o) =+ HashTagEntity <$> o .: "text"+ parseJSON _ = mzero++-- | The 'UserEntity' is just a wrapper around 'User' which is+-- a bit wasteful, and should probably be replaced by just+-- storing the id, name and screen name here.+data UserEntity = UserEntity User+ deriving (Show, Eq)++instance FromJSON UserEntity where+ parseJSON = (UserEntity <$>) . parseJSON++data URLEntity =+ URLEntity+ { ueURL :: URLString -- ^ The URL that was extracted+ , ueExpanded :: URLString -- ^ The fully resolved URL (only for t.co links)+ , ueDisplay :: T.Text -- ^ Not a URL but a string to display instead of the URL (only for t.co links)+ } deriving (Show, Eq)++instance FromJSON URLEntity where+ parseJSON (Object o) =+ URLEntity <$> o .: "url"+ <*> o .: "expanded_url"+ <*> o .: "display_url"+ parseJSON _ = mzero++-- | Entity handling.+data Entities =+ Entities+ { enHashTags :: [Entity HashTagEntity]+ , enUserMentions :: [Entity UserEntity]+ , enURLs :: [Entity URLEntity]+ } deriving (Show, Eq)++instance FromJSON Entities where+ parseJSON (Object o) =+ Entities <$> o .: "hashtags"+ <*> o .: "user_mentions"+ <*> o .: "urls"+ parseJSON _ = mzero++-- | The character positions the Entity was extracted from+--+-- This is experimental implementation.+-- This may be replaced by more definite types.+type EntityIndices = [Int]++data Entity a =+ Entity+ { entityBody :: a -- ^ The detail information of the specific entity types (HashTag, URL, User)+ , entityIndices :: EntityIndices -- ^ The character positions the Entity was extracted from+ } deriving (Show, Eq)++instance FromJSON a => FromJSON (Entity a) where+ parseJSON v@(Object o) =+ Entity <$> parseJSON v+ <*> o .: "indices"+ parseJSON _ = mzero+
Web/Twitter/Enumerator/Utils.hs view
@@ -1,12 +1,19 @@+{-# LANGUAGE OverloadedStrings #-}+ module Web.Twitter.Enumerator.Utils ( enumLine , enumJSON , skipNothing , debugEE , fromJSON'+ , fromJSONSearch'+ , toMaybeByteString+ , handleParseError ) where +import Web.Twitter.Enumerator.Types+ import Data.Aeson hiding (Error) import Data.Aeson.Types (parseMaybe) import Data.Attoparsec.Enumerator (iterParser)@@ -14,6 +21,7 @@ import qualified Data.Enumerator.List as EL import qualified Data.Enumerator.Binary as EB import Data.ByteString (ByteString)+import qualified Data.ByteString.Char8 as B8 import Data.Maybe import Control.Monad.IO.Class (MonadIO (liftIO)) @@ -30,6 +38,20 @@ fromJSON' :: FromJSON a => Value -> Maybe a fromJSON' = parseMaybe parseJSON +fromJSONSearch' :: FromJSON a => Value -> Maybe a+fromJSONSearch' (Object o) = parseMaybe (.: "results") o+fromJSONSearch' _ = Nothing+ enumJSON :: Monad m => E.Enumeratee ByteString Value m a enumJSON = E.sequence $ iterParser json +toMaybeByteString :: Show a => a -> Maybe ByteString+toMaybeByteString = Just . B8.pack . show++handleParseError :: Monad m => Iteratee ByteString m b -> Iteratee ByteString m b+handleParseError iter = iter `catchError` hndl+ where+ getChunk = continue return+ hndl e = getChunk >>= \x -> case x of+ Chunks xs -> throwError $ ParserException e xs+ _ -> throwError $ ParserException e []
twitter-enumerator.cabal view
@@ -1,5 +1,5 @@ name: twitter-enumerator-version: 0.0.2+version: 0.0.3 license: BSD3 license-file: LICENSE author: Takahiro HIMURA@@ -19,7 +19,7 @@ source-repository this type: git location: git://github.com/himura/twitter-enumerator.git- tag: 0.0.2+ tag: 0.0.3 library ghc-options: -Wall@@ -28,9 +28,10 @@ base >= 4 && < 5 , transformers >= 0.2 && < 0.3 , authenticate >= 0.9 && < 0.11+ , tls-extra >= 0.4 && < 0.4.3 , enumerator >= 0.4.14 && < 0.5 , http-types- , http-enumerator >= 0.6.6+ , http-enumerator >= 0.6.6 && < 0.7.3 , aeson >= 0.3.2.2 , attoparsec >= 0.8.6.1 , attoparsec-enumerator