packages feed

twitter-conduit 0.6.0 → 0.6.1

raw patch · 12 files changed

+551/−527 lines, 12 filesdep +deepseqdep ~base

Dependencies added: deepseq

Dependency ranges changed: base

Files

ChangeLog.md view
@@ -1,3 +1,7 @@+## 0.6.1++*  Export TweetMode (Extended) [#92](https://github.com/himura/twitter-conduit/pull/92)+ ## 0.6.0  * Support extended tweets [#83](https://github.com/himura/twitter-conduit/pull/83)
README.md view
@@ -10,11 +10,6 @@  Documentation is available in [hackage](http://hackage.haskell.org/package/twitter-conduit). -## Usage ##--    $ cabal update-    $ cabal install twitter-conduit- ## Quick Start ##  For a runnable example, see [sample/simple.hs](https://github.com/himura/twitter-conduit/blob/master/sample/simple.hs).@@ -24,19 +19,32 @@  ### Build ### -If you would like to use cabal sandbox, prepare sandbox as below:+#### Building with Cabal ####  ~~~~-$ cabal sandbox init+$ cabal v2-build all ~~~~ -and then,+#### Building with Stack #### -~~~~-$ cabal install --only-dependencies -fbuild-samples-$ cabal configure -fbuild-samples-$ cabal build-~~~~+To build with Stack, you should create top-level `stack.yaml` file first.+An example of `stack.yaml` is like below:++```.yaml+resolver: lts-18.12+packages:+- .+- sample+extra-deps:+- twitter-types-0.11.0+- twitter-types-lens-0.11.0+```++then, run stack.++```+$ stack build+```  ### Run ### 
Web/Twitter/Conduit.hs view
@@ -13,6 +13,18 @@     -- * How to use this library     -- $howto +    -- ** Authentication+    -- $auth++    -- ** How to call API+    -- $call++    -- *** How to specify API parameters+    -- $parameter++    -- *** Conduit API: Recursive API call with changing cursor parameter+    -- $conduit+     -- * Re-exports     module Web.Twitter.Conduit.Api,     module Web.Twitter.Conduit.Cursor,@@ -38,6 +50,7 @@     MediaData (..),     UserListParam (..),     UserParam (..),+    TweetMode (..),      -- * re-exports     OAuth (..),@@ -80,17 +93,22 @@ -- and <http://hackage.haskell.org/package/conduit conduit>. -- All of following examples import modules as below: ----- > {\-# LANGUAGE OverloadedStrings #-\}--- >--- > import Web.Twitter.Conduit--- > import Web.Twitter.Types.Lens--- > import Data.Conduit--- > import qualified Data.Conduit.List as CL--- > import qualified Data.Text as T--- > import qualified Data.Text.IO as T--- > import Control.Monad.IO.Class--- > import Control.Lens+-- @+-- {\-# LANGUAGE OverloadedLabels #-\}+-- {\-# LANGUAGE OverloadedStrings #-\} --+-- import Web.Twitter.Conduit+-- import Web.Twitter.Types.Lens+-- import Data.Conduit+-- import qualified Data.Conduit.List as CL+-- import qualified Data.Text as T+-- import qualified Data.Text.IO as T+-- import Control.Monad.IO.Class+-- import Control.Lens+-- @++-- $auth+-- -- First, you should obtain consumer token and secret from <https://apps.twitter.com/ Twitter>, -- and prepare 'OAuth' variables as follows: --@@ -132,7 +150,11 @@ -- -- Or, simply as follows: ----- > twInfo = setCredential tokens credential def+-- @+-- twInfo = 'setCredential' tokens credential 'def'+-- @++-- $call -- -- Twitter API requests are performed by 'call' function. -- For example, <https://dev.twitter.com/docs/api/1.1/get/statuses/home_timeline GET statuses/home_timeline>@@ -140,7 +162,7 @@ -- -- @ -- mgr \<- 'newManager' 'tlsManagerSettings'--- timeline \<- 'call' twInfo mgr 'homeTimeline'+-- timeline \<- 'call' twInfo mgr 'statusesHomeTimeline' -- @ -- -- The response of 'call' function is wrapped by the suitable type of@@ -153,9 +175,48 @@ -- includes 20 tweets, and you can change the number of tweets by the /count/ parameter. -- -- @--- timeline \<- 'call' twInfo mgr '$' 'homeTimeline' '&' #count '?~' 200+-- timeline \<- 'call' twInfo mgr '$' 'statusesHomeTimeline' '&' #count '?~' 200 -- @++-- $parameter --+-- The parameters which can be specified for this API, is able to be obtained from type parameters of APIRequest.+-- For example,+--+-- @+-- 'statusesHomeTimeline' ::+--     'APIRequest' 'StatusesHomeTimeline' ['Web.Twitter.Types.Status']+-- @+--+-- - The 2nd type parameter of 'APIRequest' represents acceptable parameters in this API request.+-- - The 3nd type parameter of 'APIRequest' denotes a return type of this API request.+--+-- The 2nd type parameter of 'statusesHomeTimeline' is 'StatusesHomeTimeline' defined as below:+--+-- @+-- type StatusesHomeTimeline =+--     '[ "count" ':= Integer+--      , "since_id" ':= Integer+--      , "max_id" ':= Integer+--      , "trim_user" ':= Bool+--      , "exclude_replies" ':= Bool+--      , "contributor_details" ':= Bool+--      , "include_entities" ':= Bool+--      , "tweet_mode" ':= TweetMode+--      ]+-- @+--+-- Each element of list represents the name and type of API parameters.+-- You can specify those parameter with OverloadedLabels extension.+-- In the above example, it shows that 'statusesHomeTimeline' API supports "tweet_mode" parameter with 'TweetMode' type, so+-- you can specify "tweet_mode" parameter as:+--+-- @+-- 'statusesHomeTimeline' & #tweet_mode ?~ 'Extended'+-- @++-- $conduit+-- -- If you need more statuses, you can obtain those with multiple API requests. -- This library provides the wrapper for multiple requests with conduit interfaces. -- For example, if you intend to fetch the all friends information,@@ -163,24 +224,31 @@ -- or use the conduit wrapper 'sourceWithCursor' as below: -- -- @--- friends \<- 'sourceWithCursor' twInfo mgr ('friendsList' ('ScreenNameParam' \"thimura\") '&' #count '?~' 200) '$$' 'CL.consume'+-- friends \<-+--     'runConduit' $+--         'sourceWithMaxId' twInfo mgr ('friendsList' ('ScreenNameParam' \"thimura\") '&' #count '?~' 200)+--             '.|' 'CL.consume' -- @ -- -- Statuses APIs, for instance, 'homeTimeline', are also wrapped by 'sourceWithMaxId'. ----- For example, you can print 1000 tweets from your home timeline, as below:+-- For example, you can print 60 tweets from your home timeline, as below: -- -- @ -- main :: IO () -- main = do --     mgr \<- 'newManager' 'tlsManagerSettings'---     'sourceWithMaxId' twInfo mgr 'homeTimeline'---         $= CL.isolate 60---         $$ CL.mapM_ $ \\status -> liftIO $ do---             T.putStrLn $ T.concat [ T.pack . show $ status ^. statusId---                                   , \": \"---                                   , status ^. statusUser . userScreenName---                                   , \": \"---                                   , status ^. statusText---                                   ]+--     'runConduit' $ 'sourceWithMaxId' twInfo mgr 'homeTimeline'+--         '.|' CL.isolate 60+--         '.|' CL.mapM_+--             (\\status -> do+--                  T.putStrLn $+--                      T.concat+--                          [ T.pack . show $ status ^. statusId+--                          , \": \"+--                          , status ^. statusUser . userScreenName+--                          , \": \"+--                          , status ^. statusText+--                          ]+--             ) -- @
Web/Twitter/Conduit/Api.hs view
@@ -6,16 +6,27 @@  module Web.Twitter.Conduit.Api (     -- * Status+    StatusesMentionsTimeline,     statusesMentionsTimeline,+    StatusesUserTimeline,     statusesUserTimeline,+    StatusesHomeTimeline,     statusesHomeTimeline,+    StatusesRetweetsOfMe,     statusesRetweetsOfMe,+    StatusesRetweetsId,     statusesRetweetsId,+    StatusesShowId,     statusesShowId,+    StatusesDestroyId,     statusesDestroyId,+    StatusesUpdate,     statusesUpdate,+    StatusesRetweetId,     statusesRetweetId,+    StatusesUpdateWithMedia,     statusesUpdateWithMedia,+    StatusesLookup,     statusesLookup,      -- * Search@@ -165,7 +176,6 @@ import Web.Twitter.Conduit.Parameters import Web.Twitter.Conduit.Request import Web.Twitter.Conduit.Request.Internal-import qualified Web.Twitter.Conduit.Status as Status import Web.Twitter.Types  import Data.Aeson@@ -178,6 +188,280 @@ -- >>> :set -XOverloadedStrings -XOverloadedLabels -- >>> import Control.Lens +-- * Timelines++-- | Returns query data asks the most recent mentions for the authenticating user.+--+-- You can perform a query using 'call':+--+-- @+-- res <- 'call' 'mentionsTimeline'+-- @+--+-- >>> statusesMentionsTimeline+-- APIRequest "GET" "https://api.twitter.com/1.1/statuses/mentions_timeline.json" []+statusesMentionsTimeline :: APIRequest StatusesMentionsTimeline [Status]+statusesMentionsTimeline = APIRequest "GET" (endpoint ++ "statuses/mentions_timeline.json") def++type StatusesMentionsTimeline =+    '[ "count" ':= Integer+     , "since_id" ':= Integer+     , "max_id" ':= Integer+     , "trim_user" ':= Bool+     , "contributor_details" ':= Bool+     , "include_entities" ':= Bool+     , "tweet_mode" ':= TweetMode+     ]++-- | Returns query data asks a collection of the most recent Tweets posted by the user indicated by the screen_name or user_id parameters.+--+-- You can perform a search query using 'call':+--+-- @+-- res <- 'call' $ 'userTimeline' ('ScreenNameParam' \"thimura\")+-- @+--+-- >>> statusesUserTimeline (ScreenNameParam "thimura")+-- APIRequest "GET" "https://api.twitter.com/1.1/statuses/user_timeline.json" [("screen_name","thimura")]+-- >>> statusesUserTimeline (ScreenNameParam "thimura") & #include_rts ?~ True & #count ?~ 200+-- APIRequest "GET" "https://api.twitter.com/1.1/statuses/user_timeline.json" [("count","200"),("include_rts","true"),("screen_name","thimura")]+statusesUserTimeline :: UserParam -> APIRequest StatusesUserTimeline [Status]+statusesUserTimeline q = APIRequest "GET" (endpoint ++ "statuses/user_timeline.json") (mkUserParam q)++type StatusesUserTimeline =+    '[ "count" ':= Integer+     , "since_id" ':= Integer+     , "max_id" ':= Integer+     , "trim_user" ':= Bool+     , "exclude_replies" ':= Bool+     , "contributor_details" ':= Bool+     , "include_rts" ':= Bool+     , "tweet_mode" ':= TweetMode+     ]++-- | Returns query data asks a collection of the most recentTweets and retweets posted by the authenticating user and the users they follow.+--+-- You can perform a search query using 'call':+--+-- @+-- res <- 'call' 'homeTimeline'+-- @+--+-- >>> statusesHomeTimeline+-- APIRequest "GET" "https://api.twitter.com/1.1/statuses/home_timeline.json" []+-- >>> statusesHomeTimeline & #count ?~ 200+-- APIRequest "GET" "https://api.twitter.com/1.1/statuses/home_timeline.json" [("count","200")]+statusesHomeTimeline :: APIRequest StatusesHomeTimeline [Status]+statusesHomeTimeline = APIRequest "GET" (endpoint ++ "statuses/home_timeline.json") def++type StatusesHomeTimeline =+    '[ "count" ':= Integer+     , "since_id" ':= Integer+     , "max_id" ':= Integer+     , "trim_user" ':= Bool+     , "exclude_replies" ':= Bool+     , "contributor_details" ':= Bool+     , "include_entities" ':= Bool+     , "tweet_mode" ':= TweetMode+     ]++-- | Returns query data asks the most recent tweets authored by the authenticating user that have been retweeted by others.+--+-- You can perform a search query using 'call':+--+-- @+-- res <- 'call' 'retweetsOfMe'+-- @+--+-- >>> statusesRetweetsOfMe+-- APIRequest "GET" "https://api.twitter.com/1.1/statuses/retweets_of_me.json" []+-- >>> statusesRetweetsOfMe & #count ?~ 100+-- APIRequest "GET" "https://api.twitter.com/1.1/statuses/retweets_of_me.json" [("count","100")]+statusesRetweetsOfMe :: APIRequest StatusesRetweetsOfMe [Status]+statusesRetweetsOfMe = APIRequest "GET" (endpoint ++ "statuses/retweets_of_me.json") def++type StatusesRetweetsOfMe =+    '[ "count" ':= Integer+     , "since_id" ':= Integer+     , "max_id" ':= Integer+     , "trim_user" ':= Bool+     , "include_entities" ':= Bool+     , "include_user_entities" ':= Bool+     , "tweet_mode" ':= TweetMode+     ]++-- * Tweets++-- | Returns query data that asks for the most recent retweets of the specified tweet+--+-- You can perform a search query using 'call':+--+-- @+-- res <- 'call' twInfo mgr '$' 'retweetsId' 1234567890+-- @+--+-- >>> statusesRetweetsId 1234567890+-- APIRequest "GET" "https://api.twitter.com/1.1/statuses/retweets/1234567890.json" []+-- >>> statusesRetweetsId 1234567890 & #count ?~ 100+-- APIRequest "GET" "https://api.twitter.com/1.1/statuses/retweets/1234567890.json" [("count","100")]+statusesRetweetsId :: StatusId -> APIRequest StatusesRetweetsId [RetweetedStatus]+statusesRetweetsId status_id = APIRequest "GET" uri def+  where+    uri = endpoint ++ "statuses/retweets/" ++ show status_id ++ ".json"++type StatusesRetweetsId =+    '[ "count" ':= Integer+     , "trim_user" ':= Bool+     , "tweet_mode" ':= TweetMode+     ]++-- | Returns query data asks a single Tweet, specified by the id parameter.+--+-- You can perform a search query using 'call':+--+-- @+-- res <- 'call' twInfo mgr '$' 'showId' 1234567890+-- @+--+-- >>> statusesShowId 1234567890+-- APIRequest "GET" "https://api.twitter.com/1.1/statuses/show/1234567890.json" []+-- >>> statusesShowId 1234567890 & #include_my_retweet ?~ True+-- APIRequest "GET" "https://api.twitter.com/1.1/statuses/show/1234567890.json" [("include_my_retweet","true")]+statusesShowId :: StatusId -> APIRequest StatusesShowId Status+statusesShowId status_id = APIRequest "GET" uri def+  where+    uri = endpoint ++ "statuses/show/" ++ show status_id ++ ".json"++type StatusesShowId =+    '[ "trim_user" ':= Bool+     , "include_my_retweet" ':= Bool+     , "include_entities" ':= Bool+     , "include_ext_alt_text" ':= Bool+     , "tweet_mode" ':= TweetMode+     ]++-- | Returns post data which destroys the status specified by the require ID parameter.+--+-- You can perform a search query using 'call':+--+-- @+-- res <- 'call' twInfo mgr '$' 'destroyId' 1234567890+-- @+--+-- >>> statusesDestroyId 1234567890+-- APIRequest "POST" "https://api.twitter.com/1.1/statuses/destroy/1234567890.json" []+statusesDestroyId :: StatusId -> APIRequest StatusesDestroyId Status+statusesDestroyId status_id = APIRequest "POST" uri def+  where+    uri = endpoint ++ "statuses/destroy/" ++ show status_id ++ ".json"++type StatusesDestroyId =+    '[ "trim_user" ':= Bool+     , "tweet_mode" ':= TweetMode+     ]++-- | Returns post data which updates the authenticating user's current status.+-- To upload an image to accompany the tweet, use 'updateWithMedia'.+--+-- You can perform a search query using 'call':+--+-- @+-- res <- 'call' twInfo mgr '$' 'update' \"Hello World\"+-- @+--+-- >>> statusesUpdate "Hello World"+-- APIRequest "POST" "https://api.twitter.com/1.1/statuses/update.json" [("status","Hello World")]+-- >>> statusesUpdate "Hello World" & #in_reply_to_status_id ?~ 1234567890+-- APIRequest "POST" "https://api.twitter.com/1.1/statuses/update.json" [("in_reply_to_status_id","1234567890"),("status","Hello World")]+statusesUpdate :: T.Text -> APIRequest StatusesUpdate Status+statusesUpdate status = APIRequest "POST" uri [("status", PVString status)]+  where+    uri = endpoint ++ "statuses/update.json"++type StatusesUpdate =+    '[ "in_reply_to_status_id" ':= Integer+     , -- , "lat_long"+       -- , "place_id"+       "display_coordinates" ':= Bool+     , "trim_user" ':= Bool+     , "media_ids" ':= [Integer]+     , "tweet_mode" ':= TweetMode+     ]++-- | Returns post data which retweets a tweet, specified by ID.+--+-- You can perform a search query using 'call':+--+-- @+-- res <- 'call' twInfo mgr '$' 'retweetId' 1234567890+-- @+--+-- >>> statusesRetweetId 1234567890+-- APIRequest "POST" "https://api.twitter.com/1.1/statuses/retweet/1234567890.json" []+statusesRetweetId :: StatusId -> APIRequest StatusesRetweetId RetweetedStatus+statusesRetweetId status_id = APIRequest "POST" uri def+  where+    uri = endpoint ++ "statuses/retweet/" ++ show status_id ++ ".json"++type StatusesRetweetId =+    '[ "trim_user" ':= Bool+     , "tweet_mode" ':= TweetMode+     ]++-- | Returns post data which updates the authenticating user's current status and attaches media for upload.+--+-- You can perform a search query using 'call':+--+-- @+-- res <- 'call' twInfo mgr '$' 'updateWithMedia' \"Hello World\" ('MediaFromFile' \"/home/thimura/test.jpeg\")+-- @+--+-- >>> statusesUpdateWithMedia "Hello World" (MediaFromFile "/home/fuga/test.jpeg")+-- APIRequestMultipart "POST" "https://api.twitter.com/1.1/statuses/update_with_media.json" [("status","Hello World")]+statusesUpdateWithMedia ::+    T.Text ->+    MediaData ->+    APIRequest StatusesUpdateWithMedia Status+statusesUpdateWithMedia tweet mediaData =+    APIRequestMultipart "POST" uri [("status", PVString tweet)] [mediaBody mediaData]+  where+    uri = endpoint ++ "statuses/update_with_media.json"+    mediaBody (MediaFromFile fp) = partFileSource "media[]" fp+    mediaBody (MediaRequestBody filename filebody) = partFileRequestBody "media[]" filename filebody++type StatusesUpdateWithMedia =+    '[ "possibly_sensitive" ':= Bool+     , "in_reply_to_status_id" ':= Integer+     , -- , "lat_long"+       -- , "place_id"+       "display_coordinates" ':= Bool+     , "tweet_mode" ':= TweetMode+     ]++-- | Returns fully-hydrated tweet objects for up to 100 tweets per request, as specified by comma-separated values passed to the id parameter.+--+-- You can perform a request using 'call':+--+-- @+-- res <- 'call' twInfo mgr '$' 'lookup' [20, 432656548536401920]+-- @+--+-- >>> statusesLookup [10]+-- APIRequest "GET" "https://api.twitter.com/1.1/statuses/lookup.json" [("id","10")]+-- >>> statusesLookup [10, 432656548536401920]+-- APIRequest "GET" "https://api.twitter.com/1.1/statuses/lookup.json" [("id","10,432656548536401920")]+-- >>> statusesLookup [10, 432656548536401920] & #include_entities ?~ True+-- APIRequest "GET" "https://api.twitter.com/1.1/statuses/lookup.json" [("include_entities","true"),("id","10,432656548536401920")]+statusesLookup :: [StatusId] -> APIRequest StatusesLookup [Status]+statusesLookup ids = APIRequest "GET" (endpoint ++ "statuses/lookup.json") [("id", PVIntegerArray ids)]++type StatusesLookup =+    '[ "include_entities" ':= Bool+     , "trim_user" ':= Bool+     , "map" ':= Bool+     , "tweet_mode" ':= TweetMode+     ]+ -- | Returns search query. -- -- You can perform a search query using 'call':@@ -1011,26 +1295,3 @@     mediaBody (MediaRequestBody filename filebody) = partFileRequestBody "media" filename filebody  type MediaUpload = EmptyParams--statusesMentionsTimeline :: APIRequest Status.StatusesMentionsTimeline [Status]-statusesMentionsTimeline = Status.mentionsTimeline-statusesUserTimeline :: UserParam -> APIRequest Status.StatusesUserTimeline [Status]-statusesUserTimeline = Status.userTimeline-statusesHomeTimeline :: APIRequest Status.StatusesHomeTimeline [Status]-statusesHomeTimeline = Status.homeTimeline-statusesRetweetsOfMe :: APIRequest Status.StatusesRetweetsOfMe [Status]-statusesRetweetsOfMe = Status.retweetsOfMe-statusesRetweetsId :: StatusId -> APIRequest Status.StatusesRetweetsId [RetweetedStatus]-statusesRetweetsId = Status.retweetsId-statusesShowId :: StatusId -> APIRequest Status.StatusesShowId Status-statusesShowId = Status.showId-statusesDestroyId :: StatusId -> APIRequest Status.StatusesDestroyId Status-statusesDestroyId = Status.destroyId-statusesUpdate :: T.Text -> APIRequest Status.StatusesUpdate Status-statusesUpdate = Status.update-statusesRetweetId :: StatusId -> APIRequest Status.StatusesRetweetId RetweetedStatus-statusesRetweetId = Status.retweetId-statusesUpdateWithMedia :: T.Text -> MediaData -> APIRequest Status.StatusesUpdateWithMedia Status-statusesUpdateWithMedia = Status.updateWithMedia-statusesLookup :: [StatusId] -> APIRequest Status.StatusesLookup [Status]-statusesLookup = Status.lookup
Web/Twitter/Conduit/Base.hs view
@@ -1,4 +1,3 @@-{-# LANGUAGE CPP #-} {-# LANGUAGE DataKinds #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-}@@ -51,15 +50,12 @@ import qualified Data.Conduit.List as CL import qualified Data.Map as M import qualified Data.Text.Encoding as T+import GHC.TypeLits (KnownSymbol) import Network.HTTP.Client.MultipartFormData import qualified Network.HTTP.Conduit as HTTP import qualified Network.HTTP.Types as HT import Web.Authenticate.OAuth (signOAuth) -#if __GLASGOW_HASKELL__ < 804-import Data.Monoid-#endif- makeRequest ::     APIRequest apiName responseType ->     IO HTTP.Request@@ -298,7 +294,7 @@ sourceWithCursor ::     ( MonadIO m     , FromJSON responseType-    , CursorKey ck+    , KnownSymbol ck     , HasParam "cursor" Integer supports     ) =>     -- | Twitter Setting@@ -322,7 +318,7 @@ -- This function cooperate with instances of 'HasCursorParam'. sourceWithCursor' ::     ( MonadIO m-    , CursorKey ck+    , KnownSymbol ck     , HasParam "cursor" Integer supports     ) =>     -- | Twitter Setting
Web/Twitter/Conduit/Cursor.hs view
@@ -1,10 +1,13 @@-{-# LANGUAGE CPP #-}-{-# LANGUAGE EmptyDataDecls #-}+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE DeriveFoldable #-}+{-# LANGUAGE DeriveFunctor #-}+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE DeriveTraversable #-}+{-# LANGUAGE KindSignatures #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE ScopedTypeVariables #-}  module Web.Twitter.Conduit.Cursor (-    CursorKey (..),     IdsCursorKey,     UsersCursorKey,     ListsCursorKey,@@ -12,79 +15,60 @@     WithCursor (..), ) where +import Control.DeepSeq (NFData) import Data.Aeson-import Data.Text (Text)-import Web.Twitter.Types (checkError)+import Data.Proxy (Proxy (..))+import Data.String+import GHC.Generics+import GHC.TypeLits (KnownSymbol, Symbol, symbolVal)  -- $setup+-- >>> import Data.Text -- >>> type UserId = Integer -{- ORMOLU_DISABLE -}-class CursorKey a where-#if MIN_VERSION_aeson(2, 0, 0)-    cursorKey :: a -> Key-#else-    cursorKey :: a -> Text-#endif-{- ORMOLU_ENABLE -}---- | Phantom type to specify the key which point out the content in the response.-data IdsCursorKey--instance CursorKey IdsCursorKey where-    cursorKey = const "ids"---- | Phantom type to specify the key which point out the content in the response.-data UsersCursorKey--instance CursorKey UsersCursorKey where-    cursorKey = const "users"---- | Phantom type to specify the key which point out the content in the response.-data ListsCursorKey--instance CursorKey ListsCursorKey where-    cursorKey = const "lists"--data EventsCursorKey-instance CursorKey EventsCursorKey where-    cursorKey = const "events"+type IdsCursorKey = "ids"+type UsersCursorKey = "users"+type ListsCursorKey = "lists"+type EventsCursorKey = "events"  -- | A wrapper for API responses which have "next_cursor" field. -- -- The first type parameter of 'WithCursor' specifies the field name of contents. ----- >>> let Just res = decode "{\"previous_cursor\": 0, \"next_cursor\": 1234567890, \"ids\": [1111111111]}" :: Maybe (WithCursor Integer IdsCursorKey UserId)+-- >>> let Just res = decode "{\"previous_cursor\": 0, \"next_cursor\": 1234567890, \"ids\": [1111111111]}" :: Maybe (WithCursor Integer "ids" UserId) -- >>> nextCursor res -- Just 1234567890 -- >>> contents res -- [1111111111] ----- >>> let Just res = decode "{\"previous_cursor\": 0, \"next_cursor\": 0, \"users\": [1000]}" :: Maybe (WithCursor Integer UsersCursorKey UserId)+-- >>> let Just res = decode "{\"previous_cursor\": 0, \"next_cursor\": 0, \"users\": [1000]}" :: Maybe (WithCursor Integer "users" UserId) -- >>> nextCursor res -- Just 0 -- >>> contents res -- [1000] ----- >>> let Just res = decode "{\"next_cursor\": \"hogehoge\", \"events\": [1000]}" :: Maybe (WithCursor Text EventsCursorKey UserId)+-- >>> let Just res = decode "{\"next_cursor\": \"hogehoge\", \"events\": [1000]}" :: Maybe (WithCursor Text "events" UserId) -- >>> nextCursor res -- Just "hogehoge" -- >>> contents res -- [1000]-data WithCursor cursorType cursorKey wrapped = WithCursor+data WithCursor cursorType (cursorKey :: Symbol) wrapped = WithCursor     { previousCursor :: Maybe cursorType     , nextCursor :: Maybe cursorType     , contents :: [wrapped]     }-    deriving (Show)+    deriving (Show, Eq, Generic, Generic1, Functor, Foldable, Traversable) -instance-    (FromJSON wrapped, FromJSON ct, CursorKey c) =>-    FromJSON (WithCursor ct c wrapped)-    where-    parseJSON (Object o) =-        checkError o-            >> WithCursor <$> o .:? "previous_cursor"-            <*> o .:? "next_cursor"-            <*> o .: cursorKey (undefined :: c)-    parseJSON _ = mempty+instance (KnownSymbol cursorKey, FromJSON cursorType) => FromJSON1 (WithCursor cursorType cursorKey) where+    liftParseJSON _ lp =+        withObject ("WithCursor \"" ++ cursorKeyStr ++ "\"") $ \obj ->+            WithCursor <$> obj .:? "previous_cursor"+                <*> obj .:? "next_cursor"+                <*> (obj .: fromString cursorKeyStr >>= lp)+      where+        cursorKeyStr = symbolVal (Proxy :: Proxy cursorKey)++instance (KnownSymbol cursorKey, FromJSON cursorType, FromJSON wrapped) => FromJSON (WithCursor cursorType cursorKey wrapped) where+    parseJSON = parseJSON1++instance (NFData cursorType, NFData wrapped) => NFData (WithCursor cursorType cursorKey wrapped)
Web/Twitter/Conduit/Lens.hs view
@@ -20,10 +20,6 @@      -- * Re-exports     TT.TwitterError (..),-    TT.CursorKey (..),-    TT.IdsCursorKey,-    TT.UsersCursorKey,-    TT.ListsCursorKey, ) where  import Control.Lens
Web/Twitter/Conduit/Request/Internal.hs view
@@ -1,4 +1,3 @@-{-# LANGUAGE CPP #-} {-# LANGUAGE ConstraintKinds #-} {-# LANGUAGE DataKinds #-} {-# LANGUAGE FlexibleInstances #-}@@ -96,7 +95,6 @@     replace k Nothing = dropAssoc k     dropAssoc k = filter ((/= k) . fst) -{- ORMOLU_DISABLE -} instance     ( Parameters req     , ParameterValue a@@ -105,13 +103,8 @@     , Functor f     , lens ~ ((Maybe a -> f (Maybe a)) -> req -> f req)     ) =>-    IsLabel label lens where--#if MIN_VERSION_base(4, 10, 0)+    IsLabel label lens+    where     fromLabel = rawParam key-#else-    fromLabel _ = rawParam key-#endif       where         key = S8.pack (symbolVal (Proxy :: Proxy label))-{- ORMOLU_ENABLE -}
Web/Twitter/Conduit/Status.hs view
@@ -1,323 +1,75 @@-{-# LANGUAGE DataKinds #-}-{-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE TypeOperators #-}- module Web.Twitter.Conduit.Status (+    -- * Notice+    -- $notice+     -- * Timelines-    StatusesMentionsTimeline,     mentionsTimeline,-    StatusesUserTimeline,     userTimeline,-    StatusesHomeTimeline,     homeTimeline,-    StatusesRetweetsOfMe,     retweetsOfMe,      -- * Tweets-    StatusesRetweetsId,     retweetsId,-    StatusesShowId,     showId,-    StatusesDestroyId,     destroyId,-    StatusesUpdate,     update,-    StatusesRetweetId,     retweetId,-    MediaData (..),-    StatusesUpdateWithMedia,     updateWithMedia,-    -- , oembed-    -- , retweetersIds-    StatusesLookup,     lookup, ) where -import Web.Twitter.Conduit.Base+import Data.Text (Text)+import Web.Twitter.Conduit.Api import Web.Twitter.Conduit.Parameters import Web.Twitter.Conduit.Request-import Web.Twitter.Conduit.Request.Internal import Web.Twitter.Types-import Prelude hiding (lookup) -import Data.Default-import qualified Data.Text as T-import Network.HTTP.Client.MultipartFormData---- $setup--- >>> :set -XOverloadedStrings -XOverloadedLabels--- >>> import Control.Lens---- * Timelines+import Prelude hiding (lookup) --- | Returns query data asks the most recent mentions for the authenticating user.------ You can perform a query using 'call':------ @--- res <- 'call' 'mentionsTimeline'--- @+-- $notice ----- >>> mentionsTimeline--- APIRequest "GET" "https://api.twitter.com/1.1/statuses/mentions_timeline.json" []-mentionsTimeline :: APIRequest StatusesMentionsTimeline [Status]-mentionsTimeline = APIRequest "GET" (endpoint ++ "statuses/mentions_timeline.json") def+-- This module provides aliases of statuses API, for backward compatibility. -type StatusesMentionsTimeline =-    '[ "count" ':= Integer-     , "since_id" ':= Integer-     , "max_id" ':= Integer-     , "trim_user" ':= Bool-     , "contributor_details" ':= Bool-     , "include_entities" ':= Bool-     , "tweet_mode" ':= TweetMode-     ]+mentionsTimeline :: APIRequest StatusesMentionsTimeline [Status]+mentionsTimeline = statusesMentionsTimeline+{-# DEPRECATED mentionsTimeline "Please use Web.Twitter.Conduit.API.statusesMentionsTimeline" #-} --- | Returns query data asks a collection of the most recent Tweets posted by the user indicated by the screen_name or user_id parameters.------ You can perform a search query using 'call':------ @--- res <- 'call' $ 'userTimeline' ('ScreenNameParam' \"thimura\")--- @------ >>> userTimeline (ScreenNameParam "thimura")--- APIRequest "GET" "https://api.twitter.com/1.1/statuses/user_timeline.json" [("screen_name","thimura")]--- >>> userTimeline (ScreenNameParam "thimura") & #include_rts ?~ True & #count ?~ 200--- APIRequest "GET" "https://api.twitter.com/1.1/statuses/user_timeline.json" [("count","200"),("include_rts","true"),("screen_name","thimura")] userTimeline :: UserParam -> APIRequest StatusesUserTimeline [Status]-userTimeline q = APIRequest "GET" (endpoint ++ "statuses/user_timeline.json") (mkUserParam q)--type StatusesUserTimeline =-    '[ "count" ':= Integer-     , "since_id" ':= Integer-     , "max_id" ':= Integer-     , "trim_user" ':= Bool-     , "exclude_replies" ':= Bool-     , "contributor_details" ':= Bool-     , "include_rts" ':= Bool-     , "tweet_mode" ':= TweetMode-     ]+userTimeline = statusesUserTimeline+{-# DEPRECATED userTimeline "Please use Web.Twitter.Conduit.API.statusesUserTimeline" #-} --- | Returns query data asks a collection of the most recentTweets and retweets posted by the authenticating user and the users they follow.------ You can perform a search query using 'call':------ @--- res <- 'call' 'homeTimeline'--- @------ >>> homeTimeline--- APIRequest "GET" "https://api.twitter.com/1.1/statuses/home_timeline.json" []--- >>> homeTimeline & #count ?~ 200--- APIRequest "GET" "https://api.twitter.com/1.1/statuses/home_timeline.json" [("count","200")] homeTimeline :: APIRequest StatusesHomeTimeline [Status]-homeTimeline = APIRequest "GET" (endpoint ++ "statuses/home_timeline.json") def--type StatusesHomeTimeline =-    '[ "count" ':= Integer-     , "since_id" ':= Integer-     , "max_id" ':= Integer-     , "trim_user" ':= Bool-     , "exclude_replies" ':= Bool-     , "contributor_details" ':= Bool-     , "include_entities" ':= Bool-     , "tweet_mode" ':= TweetMode-     ]+homeTimeline = statusesHomeTimeline+{-# DEPRECATED homeTimeline "Please use Web.Twitter.Conduit.API.statusesHomeTimeline" #-} --- | Returns query data asks the most recent tweets authored by the authenticating user that have been retweeted by others.------ You can perform a search query using 'call':------ @--- res <- 'call' 'retweetsOfMe'--- @------ >>> retweetsOfMe--- APIRequest "GET" "https://api.twitter.com/1.1/statuses/retweets_of_me.json" []--- >>> retweetsOfMe & #count ?~ 100--- APIRequest "GET" "https://api.twitter.com/1.1/statuses/retweets_of_me.json" [("count","100")] retweetsOfMe :: APIRequest StatusesRetweetsOfMe [Status]-retweetsOfMe = APIRequest "GET" (endpoint ++ "statuses/retweets_of_me.json") def--type StatusesRetweetsOfMe =-    '[ "count" ':= Integer-     , "since_id" ':= Integer-     , "max_id" ':= Integer-     , "trim_user" ':= Bool-     , "include_entities" ':= Bool-     , "include_user_entities" ':= Bool-     , "tweet_mode" ':= TweetMode-     ]---- * Tweets+retweetsOfMe = statusesRetweetsOfMe+{-# DEPRECATED retweetsOfMe "Please use Web.Twitter.Conduit.API.statusesRetweetsOfMe" #-} --- | Returns query data that asks for the most recent retweets of the specified tweet------ You can perform a search query using 'call':------ @--- res <- 'call' twInfo mgr '$' 'retweetsId' 1234567890--- @------ >>> retweetsId 1234567890--- APIRequest "GET" "https://api.twitter.com/1.1/statuses/retweets/1234567890.json" []--- >>> retweetsId 1234567890 & #count ?~ 100--- APIRequest "GET" "https://api.twitter.com/1.1/statuses/retweets/1234567890.json" [("count","100")] retweetsId :: StatusId -> APIRequest StatusesRetweetsId [RetweetedStatus]-retweetsId status_id = APIRequest "GET" uri def-  where-    uri = endpoint ++ "statuses/retweets/" ++ show status_id ++ ".json"--type StatusesRetweetsId =-    '[ "count" ':= Integer-     , "trim_user" ':= Bool-     , "tweet_mode" ':= TweetMode-     ]+retweetsId = statusesRetweetsId+{-# DEPRECATED retweetsId "Please use Web.Twitter.Conduit.API.statusesRetweetsId" #-} --- | Returns query data asks a single Tweet, specified by the id parameter.------ You can perform a search query using 'call':------ @--- res <- 'call' twInfo mgr '$' 'showId' 1234567890--- @------ >>> showId 1234567890--- APIRequest "GET" "https://api.twitter.com/1.1/statuses/show/1234567890.json" []--- >>> showId 1234567890 & #include_my_retweet ?~ True--- APIRequest "GET" "https://api.twitter.com/1.1/statuses/show/1234567890.json" [("include_my_retweet","true")] showId :: StatusId -> APIRequest StatusesShowId Status-showId status_id = APIRequest "GET" uri def-  where-    uri = endpoint ++ "statuses/show/" ++ show status_id ++ ".json"--type StatusesShowId =-    '[ "trim_user" ':= Bool-     , "include_my_retweet" ':= Bool-     , "include_entities" ':= Bool-     , "include_ext_alt_text" ':= Bool-     , "tweet_mode" ':= TweetMode-     ]+showId = statusesShowId+{-# DEPRECATED showId "Please use Web.Twitter.Conduit.API.statusesShowId" #-} --- | Returns post data which destroys the status specified by the require ID parameter.------ You can perform a search query using 'call':------ @--- res <- 'call' twInfo mgr '$' 'destroyId' 1234567890--- @------ >>> destroyId 1234567890--- APIRequest "POST" "https://api.twitter.com/1.1/statuses/destroy/1234567890.json" [] destroyId :: StatusId -> APIRequest StatusesDestroyId Status-destroyId status_id = APIRequest "POST" uri def-  where-    uri = endpoint ++ "statuses/destroy/" ++ show status_id ++ ".json"--type StatusesDestroyId =-    '[ "trim_user" ':= Bool-     , "tweet_mode" ':= TweetMode-     ]---- | Returns post data which updates the authenticating user's current status.--- To upload an image to accompany the tweet, use 'updateWithMedia'.------ You can perform a search query using 'call':------ @--- res <- 'call' twInfo mgr '$' 'update' \"Hello World\"--- @------ >>> update "Hello World"--- APIRequest "POST" "https://api.twitter.com/1.1/statuses/update.json" [("status","Hello World")]--- >>> update "Hello World" & #in_reply_to_status_id ?~ 1234567890--- APIRequest "POST" "https://api.twitter.com/1.1/statuses/update.json" [("in_reply_to_status_id","1234567890"),("status","Hello World")]-update :: T.Text -> APIRequest StatusesUpdate Status-update status = APIRequest "POST" uri [("status", PVString status)]-  where-    uri = endpoint ++ "statuses/update.json"+destroyId = statusesDestroyId+{-# DEPRECATED destroyId "Please use Web.Twitter.Conduit.API.statusesDestroyId" #-} -type StatusesUpdate =-    '[ "in_reply_to_status_id" ':= Integer-     , -- , "lat_long"-       -- , "place_id"-       "display_coordinates" ':= Bool-     , "trim_user" ':= Bool-     , "media_ids" ':= [Integer]-     , "tweet_mode" ':= TweetMode-     ]+update :: Text -> APIRequest StatusesUpdate Status+update = statusesUpdate+{-# DEPRECATED update "Please use Web.Twitter.Conduit.API.statusesUpdate" #-} --- | Returns post data which retweets a tweet, specified by ID.------ You can perform a search query using 'call':------ @--- res <- 'call' twInfo mgr '$' 'retweetId' 1234567890--- @------ >>> retweetId 1234567890--- APIRequest "POST" "https://api.twitter.com/1.1/statuses/retweet/1234567890.json" [] retweetId :: StatusId -> APIRequest StatusesRetweetId RetweetedStatus-retweetId status_id = APIRequest "POST" uri def-  where-    uri = endpoint ++ "statuses/retweet/" ++ show status_id ++ ".json"--type StatusesRetweetId =-    '[ "trim_user" ':= Bool-     , "tweet_mode" ':= TweetMode-     ]---- | Returns post data which updates the authenticating user's current status and attaches media for upload.------ You can perform a search query using 'call':------ @--- res <- 'call' twInfo mgr '$' 'updateWithMedia' \"Hello World\" ('MediaFromFile' \"/home/thimura/test.jpeg\")--- @------ >>> updateWithMedia "Hello World" (MediaFromFile "/home/fuga/test.jpeg")--- APIRequestMultipart "POST" "https://api.twitter.com/1.1/statuses/update_with_media.json" [("status","Hello World")]-updateWithMedia ::-    T.Text ->-    MediaData ->-    APIRequest StatusesUpdateWithMedia Status-updateWithMedia tweet mediaData =-    APIRequestMultipart "POST" uri [("status", PVString tweet)] [mediaBody mediaData]-  where-    uri = endpoint ++ "statuses/update_with_media.json"-    mediaBody (MediaFromFile fp) = partFileSource "media[]" fp-    mediaBody (MediaRequestBody filename filebody) = partFileRequestBody "media[]" filename filebody+retweetId = statusesRetweetId+{-# DEPRECATED retweetId "Please use Web.Twitter.Conduit.API.statusesRetweetId" #-} -type StatusesUpdateWithMedia =-    '[ "possibly_sensitive" ':= Bool-     , "in_reply_to_status_id" ':= Integer-     , -- , "lat_long"-       -- , "place_id"-       "display_coordinates" ':= Bool-     , "tweet_mode" ':= TweetMode-     ]+updateWithMedia :: Text -> MediaData -> APIRequest StatusesUpdateWithMedia Status+updateWithMedia = statusesUpdateWithMedia+{-# DEPRECATED updateWithMedia "Please use Web.Twitter.Conduit.API.statusesUpdateWithMedia" #-} --- | Returns fully-hydrated tweet objects for up to 100 tweets per request, as specified by comma-separated values passed to the id parameter.------ You can perform a request using 'call':------ @--- res <- 'call' twInfo mgr '$' 'lookup' [20, 432656548536401920]--- @------ >>> lookup [10]--- APIRequest "GET" "https://api.twitter.com/1.1/statuses/lookup.json" [("id","10")]--- >>> lookup [10, 432656548536401920]--- APIRequest "GET" "https://api.twitter.com/1.1/statuses/lookup.json" [("id","10,432656548536401920")]--- >>> lookup [10, 432656548536401920] & #include_entities ?~ True--- APIRequest "GET" "https://api.twitter.com/1.1/statuses/lookup.json" [("include_entities","true"),("id","10,432656548536401920")] lookup :: [StatusId] -> APIRequest StatusesLookup [Status]-lookup ids = APIRequest "GET" (endpoint ++ "statuses/lookup.json") [("id", PVIntegerArray ids)]--type StatusesLookup =-    '[ "include_entities" ':= Bool-     , "trim_user" ':= Bool-     , "map" ':= Bool-     , "tweet_mode" ':= TweetMode-     ]+lookup = statusesLookup+{-# DEPRECATED lookup "Please use Web.Twitter.Conduit.API.statusesLookup" #-}
tests/ApiSpec.hs view
@@ -1,4 +1,5 @@ {-# LANGUAGE CPP #-}+{-# LANGUAGE OverloadedLabels #-} {-# LANGUAGE OverloadedStrings #-}  module ApiSpec where@@ -6,17 +7,17 @@ import Control.Lens import Data.Conduit import qualified Data.Conduit.List as CL+import Data.Time import Network.HTTP.Conduit import System.IO.Unsafe+import Test.Hspec import TestUtils-import Web.Twitter.Conduit (TWInfo, call, sourceWithCursor)+import Web.Twitter.Conduit (TWInfo, accountVerifyCredentials, call, sourceWithCursor, sourceWithMaxId) import Web.Twitter.Conduit.Api import Web.Twitter.Conduit.Lens import qualified Web.Twitter.Conduit.Parameters as Param import Web.Twitter.Types.Lens -import Test.Hspec- twInfo :: TWInfo twInfo = unsafePerformIO getTWInfo @@ -24,6 +25,10 @@ mgr = unsafePerformIO $ newManager tlsManagerSettings {-# NOINLINE mgr #-} +self :: User+self = unsafePerformIO $ call twInfo mgr $ accountVerifyCredentials+{-# NOINLINE self #-}+ spec :: Spec spec = do     unit@@ -37,6 +42,70 @@  integrated :: Spec integrated = do+    describe "mentionsTimeline" $ do+        it "returns the 20 most recent mentions for user" $ do+            res <- call twInfo mgr statusesMentionsTimeline+            length res `shouldSatisfy` (> 0)+            let mentionsScreenName = res ^.. traversed . statusEntities . _Just . enUserMentions . traversed . entityBody . userEntityUserScreenName+            mentionsScreenName `shouldSatisfy` allOf folded (== (self ^. userScreenName))+            length mentionsScreenName `shouldSatisfy` (== length res)++    describe "userTimeline" $ do+        it "returns the 20 most recent tweets posted by the user indicated by ScreenNameParam" $ do+            res <- call twInfo mgr $ statusesUserTimeline (Param.ScreenNameParam "thimura")+            length res `shouldSatisfy` (== 20)+            res `shouldSatisfy` (allOf folded (^. statusUser . userScreenName . to (== "thimura")))+        it "returns the recent tweets which include RTs when specified include_rts option" $ do+            res <-+                call twInfo mgr $+                    statusesUserTimeline (Param.ScreenNameParam "thimura")+                        & #count ?~ 100+                        & #include_rts ?~ True+            res `shouldSatisfy` (anyOf (folded . statusRetweetedStatus . _Just . statusUser . userScreenName) (/= "thimura"))+        it "iterate with sourceWithMaxId" $ do+            let src =+                    sourceWithMaxId twInfo mgr $+                        statusesUserTimeline (Param.ScreenNameParam "thimura") & #count ?~ 200+            tl <- runConduit $ src .| CL.isolate 600 .| CL.consume+            length tl `shouldSatisfy` (== 600)++            let ids = tl ^.. traversed . statusId+            zip ids (tail ids) `shouldSatisfy` all (\(a, b) -> a > b)++    describe "homeTimeline" $ do+        it "returns the most recent tweets in home timeline" $ do+            res <- call twInfo mgr statusesHomeTimeline+            length res `shouldSatisfy` (> 0)++    describe "showId" $ do+        it "works for the known tweets" $ do+            res <- call twInfo mgr $ statusesShowId 477833886768959488+            res ^. statusId `shouldBe` 477833886768959488+            res ^. statusText `shouldBe` "真紅かわいいはアレセイア"+            res ^. statusCreatedAt `shouldBe` UTCTime (fromGregorian 2014 6 14) (secondsToDiffTime 55450)+            res ^. statusUser . userScreenName `shouldBe` "thimura"++    describe "update & destroyId" $ do+        it "posts new tweet and destroy it" $ do+            res1 <- call twInfo mgr $ statusesUpdate "おまえの明日が、今日よりもずっと、楽しい事で溢れているようにと、祈っているよ"+            res1 ^. statusUser . userScreenName `shouldBe` self ^. userScreenName++            res2 <- call twInfo mgr $ statusesDestroyId (res1 ^. statusId)+            res2 ^. statusId `shouldBe` res1 ^. statusId++    describe "lookup" $ do+        it "works for the known tweets" $ do+            res <- call twInfo mgr $ statusesLookup [438691466345340928, 477757405942411265]+            length res `shouldSatisfy` (== 2)+            (res !! 0) ^. statusId `shouldBe` 438691466345340928+            (res !! 1) ^. statusId `shouldBe` 477757405942411265++        it "handles extended tweets" $ do+            res <- call twInfo mgr $ statusesLookup [1128358947772145672]+            (res !! 0) ^. statusText `shouldBe` "Through the Twitter Developer Labs program, we'll soon preview new versions of GET /tweets and GET /users, followed\8230 https://t.co/9i4c5bUUCu"+            res <- call twInfo mgr $ statusesLookup [1128358947772145672] & #tweet_mode ?~ Param.Extended+            (res !! 0) ^. statusText `shouldBe` "Through the Twitter Developer Labs program, we'll soon preview new versions of GET /tweets and GET /users, followed by Tweet streaming, search &amp; metrics. More to come! \128073 https://t.co/rDE48yNiSw https://t.co/oFsvkpnDhS"+     describe "friendsIds" $ do         it "returns a cursored collection of users IDs" $ do             res <- call twInfo mgr $ friendsIds (Param.ScreenNameParam "thimura")@@ -44,7 +113,7 @@          it "iterate with sourceWithCursor" $ do             let src = sourceWithCursor twInfo mgr $ friendsIds (Param.ScreenNameParam "thimura")-            friends <- src $$ CL.consume+            friends <- runConduit $ src .| CL.consume             length friends `shouldSatisfy` (>= 0)      describe "listsMembers" $ do@@ -58,5 +127,5 @@          it "iterate with sourceWithCursor" $ do             let src = sourceWithCursor twInfo mgr $ listsMembers (Param.ListNameParam "thimura/haskell")-            members <- src $$ CL.consume+            members <- runConduit $ src .| CL.consume             members ^.. traversed . userScreenName `shouldContain` ["Hackage"]
− tests/StatusSpec.hs
@@ -1,107 +0,0 @@-{-# LANGUAGE CPP #-}-{-# LANGUAGE OverloadedLabels #-}-{-# LANGUAGE OverloadedStrings #-}--module StatusSpec where--import Control.Lens-import Data.Conduit-import qualified Data.Conduit.List as CL-import Data.Time-import Network.HTTP.Conduit-import System.IO.Unsafe-import Web.Twitter.Conduit (TWInfo, accountVerifyCredentials, call, sourceWithMaxId)-import qualified Web.Twitter.Conduit.Parameters as Param-import Web.Twitter.Conduit.Status as Status-import Web.Twitter.Types.Lens--import Test.Hspec-import TestUtils--twInfo :: TWInfo-twInfo = unsafePerformIO getTWInfo--mgr :: Manager-mgr = unsafePerformIO $ newManager tlsManagerSettings-{-# NOINLINE mgr #-}--self :: User-self = unsafePerformIO $ call twInfo mgr $ accountVerifyCredentials-{-# NOINLINE self #-}--spec :: Spec-spec = do-    unit--#ifdef RUN_INTEGRATED_TEST-    integrated-#endif--unit :: Spec-unit = return ()--integrated :: Spec-integrated = do-    describe "mentionsTimeline" $ do-        it "returns the 20 most recent mentions for user" $ do-            res <- call twInfo mgr mentionsTimeline-            length res `shouldSatisfy` (> 0)-            let mentionsScreenName = res ^.. traversed . statusEntities . _Just . enUserMentions . traversed . entityBody . userEntityUserScreenName-            mentionsScreenName `shouldSatisfy` allOf folded (== (self ^. userScreenName))-            length mentionsScreenName `shouldSatisfy` (== length res)--    describe "userTimeline" $ do-        it "returns the 20 most recent tweets posted by the user indicated by ScreenNameParam" $ do-            res <- call twInfo mgr $ userTimeline (Param.ScreenNameParam "thimura")-            length res `shouldSatisfy` (== 20)-            res `shouldSatisfy` (allOf folded (^. statusUser . userScreenName . to (== "thimura")))-        it "returns the recent tweets which include RTs when specified include_rts option" $ do-            res <--                call twInfo mgr $-                    userTimeline (Param.ScreenNameParam "thimura")-                        & #count ?~ 100-                        & #include_rts ?~ True-            res `shouldSatisfy` (anyOf (folded . statusRetweetedStatus . _Just . statusUser . userScreenName) (/= "thimura"))-        it "iterate with sourceWithMaxId" $ do-            let src =-                    sourceWithMaxId twInfo mgr $-                        userTimeline (Param.ScreenNameParam "thimura") & #count ?~ 200-            tl <- src $$ CL.isolate 600 =$ CL.consume-            length tl `shouldSatisfy` (== 600)--            let ids = tl ^.. traversed . statusId-            zip ids (tail ids) `shouldSatisfy` all (\(a, b) -> a > b)--    describe "homeTimeline" $ do-        it "returns the most recent tweets in home timeline" $ do-            res <- call twInfo mgr homeTimeline-            length res `shouldSatisfy` (> 0)--    describe "showId" $ do-        it "works for the known tweets" $ do-            res <- call twInfo mgr $ showId 477833886768959488-            res ^. statusId `shouldBe` 477833886768959488-            res ^. statusText `shouldBe` "真紅かわいいはアレセイア"-            res ^. statusCreatedAt `shouldBe` UTCTime (fromGregorian 2014 6 14) (secondsToDiffTime 55450)-            res ^. statusUser . userScreenName `shouldBe` "thimura"--    describe "update & destroyId" $ do-        it "posts new tweet and destroy it" $ do-            res1 <- call twInfo mgr $ update "おまえの明日が、今日よりもずっと、楽しい事で溢れているようにと、祈っているよ"-            res1 ^. statusUser . userScreenName `shouldBe` self ^. userScreenName--            res2 <- call twInfo mgr $ destroyId (res1 ^. statusId)-            res2 ^. statusId `shouldBe` res1 ^. statusId--    describe "lookup" $ do-        it "works for the known tweets" $ do-            res <- call twInfo mgr $ Status.lookup [438691466345340928, 477757405942411265]-            length res `shouldSatisfy` (== 2)-            (res !! 0) ^. statusId `shouldBe` 438691466345340928-            (res !! 1) ^. statusId `shouldBe` 477757405942411265--        it "handles extended tweets" $ do-            res <- call twInfo mgr $ Status.lookup [1128358947772145672]-            (res !! 0) ^. statusText `shouldBe` "Through the Twitter Developer Labs program, we'll soon preview new versions of GET /tweets and GET /users, followed\8230 https://t.co/9i4c5bUUCu"-            res <- call twInfo mgr $ Status.lookup [1128358947772145672] & #tweet_mode ?~ Param.Extended-            (res !! 0) ^. statusText `shouldBe` "Through the Twitter Developer Labs program, we'll soon preview new versions of GET /tweets and GET /users, followed by Tweet streaming, search &amp; metrics. More to come! \128073 https://t.co/rDE48yNiSw https://t.co/oFsvkpnDhS"
twitter-conduit.cabal view
@@ -1,6 +1,6 @@ cabal-version:      1.24 name:               twitter-conduit-version:            0.6.0+version:            0.6.1 license:            BSD3 license-file:       LICENSE maintainer:         Takahiro HIMURA <taka@himura.jp>@@ -66,7 +66,7 @@     default-language: Haskell2010     ghc-options:      -Wall     build-depends:-        base >=4.9 && <5,+        base >=4.12 && <5,         aeson >=0.7.0.5,         attoparsec >=0.10,         authenticate-oauth >=1.3,@@ -75,6 +75,7 @@         conduit-extra >=1.3,         containers,         data-default >=0.3,+        deepseq,         exceptions >=0.5,         ghc-prim,         http-client >=0.5.0,@@ -107,7 +108,6 @@         Spec         ApiSpec         BaseSpec-        StatusSpec         TestUtils      default-language:   Haskell2010