twitter-conduit 0.5.1 → 0.6.0
raw patch · 36 files changed
+1230/−1176 lines, 36 filesdep −hlintdep ~aesondep ~basesetup-changed
Dependencies removed: hlint
Dependency ranges changed: aeson, base
Files
- ChangeLog.md +5/−0
- Setup.hs +4/−1
- Warning.hs +7/−4
- Web/Twitter/Conduit.hs +38/−39
- Web/Twitter/Conduit/Api.hs +348/−289
- Web/Twitter/Conduit/Base.hs +171/−135
- Web/Twitter/Conduit/Cursor.hs +30/−16
- Web/Twitter/Conduit/Lens.hs +32/−34
- Web/Twitter/Conduit/Parameters.hs +33/−20
- Web/Twitter/Conduit/ParametersDeprecated.hs +0/−113
- Web/Twitter/Conduit/Request.hs +19/−18
- Web/Twitter/Conduit/Request/Internal.hs +28/−22
- Web/Twitter/Conduit/Response.hs +16/−14
- Web/Twitter/Conduit/Status.hs +139/−120
- Web/Twitter/Conduit/Stream.hs +55/−52
- Web/Twitter/Conduit/Types.hs +24/−19
- sample/Setup.hs +1/−0
- sample/common/Common.hs +19/−16
- sample/fav.hs +1/−1
- sample/oauth_callback.hs +15/−15
- sample/oauth_pin.hs +12/−10
- sample/oslist.hs +2/−1
- sample/post.hs +2/−1
- sample/postWithMedia.hs +2/−1
- sample/postWithMultipleMedia.hs +4/−4
- sample/search.hs +2/−1
- sample/searchSource.hs +2/−1
- sample/simple.hs +28/−22
- sample/userstream.hs +32/−24
- tests/ApiSpec.hs +3/−2
- tests/BaseSpec.hs +4/−3
- tests/StatusSpec.hs +20/−10
- tests/TestUtils.hs +10/−8
- tests/doctests.hs +1/−1
- tests/hlint.hs +0/−20
- twitter-conduit.cabal +121/−139
ChangeLog.md view
@@ -1,3 +1,8 @@+## 0.6.0++* Support extended tweets [#83](https://github.com/himura/twitter-conduit/pull/83)+* Remove deprecated module `Web.Twitter.Conduit.ParametersDeprecated` [#81](https://github.com/himura/twitter-conduit/pull/81)+ ## 0.5.1 * Add result_type to searchTweets [#78](https://github.com/himura/twitter-conduit/pull/78).
Setup.hs view
@@ -1,5 +1,7 @@ {-# LANGUAGE CPP #-}+ module Main (main) where+ #ifndef MIN_VERSION_cabal_doctest #define MIN_VERSION_cabal_doctest(x,y,z) 0 #endif@@ -17,7 +19,8 @@ import Warning () #endif import Distribution.Simple+ main :: IO () main = defaultMain-#endif +#endif
Warning.hs view
@@ -1,5 +1,8 @@ module Warning- {-# WARNING ["You are configuring this package without cabal-doctest installed.",- "The doctests test-suite will not work as a result.",- "To fix this, install cabal-doctest before configuring."] #-}- () where+ {-# WARNING+ [ "You are configuring this package without cabal-doctest installed."+ , "The doctests test-suite will not work as a result."+ , "To fix this, install cabal-doctest before configuring."+ ]+ #-}+ () where
Web/Twitter/Conduit.hs view
@@ -9,46 +9,44 @@ -- Portability: portable -- -- A client library for Twitter APIs (see <https://dev.twitter.com/>).--module Web.Twitter.Conduit- (- -- * How to use this library- -- $howto+module Web.Twitter.Conduit (+ -- * How to use this library+ -- $howto - -- * Re-exports- module Web.Twitter.Conduit.Api- , module Web.Twitter.Conduit.Cursor- , module Web.Twitter.Conduit.Request- , module Web.Twitter.Conduit.Response- , module Web.Twitter.Conduit.Stream- , module Web.Twitter.Conduit.Types+ -- * Re-exports+ module Web.Twitter.Conduit.Api,+ module Web.Twitter.Conduit.Cursor,+ module Web.Twitter.Conduit.Request,+ module Web.Twitter.Conduit.Response,+ module Web.Twitter.Conduit.Stream,+ module Web.Twitter.Conduit.Types, - -- * 'Web.Twitter.Conduit.Base'- , call- , call'- , callWithResponse- , callWithResponse'- , sourceWithMaxId- , sourceWithMaxId'- , sourceWithCursor- , sourceWithCursor'- , sourceWithSearchResult- , sourceWithSearchResult'+ -- * 'Web.Twitter.Conduit.Base'+ call,+ call',+ callWithResponse,+ callWithResponse',+ sourceWithMaxId,+ sourceWithMaxId',+ sourceWithCursor,+ sourceWithCursor',+ sourceWithSearchResult,+ sourceWithSearchResult', - -- * 'Web.Twitter.Conduit.Parameters'- , ListParam(..)- , MediaData(..)- , UserListParam(..)- , UserParam(..)+ -- * 'Web.Twitter.Conduit.Parameters'+ ListParam (..),+ MediaData (..),+ UserListParam (..),+ UserParam (..), - -- * re-exports- , OAuth (..)- , Credential (..)- , def- , Manager- , newManager- , tlsManagerSettings- ) where+ -- * re-exports+ OAuth (..),+ Credential (..),+ def,+ Manager,+ newManager,+ tlsManagerSettings,+) where import Web.Twitter.Conduit.Api import Web.Twitter.Conduit.Base@@ -64,12 +62,13 @@ import Web.Authenticate.OAuth -- for haddock++import Control.Lens+import Control.Monad.IO.Class 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 {-# ANN module "HLint: ignore Use import/export shortcut" #-} @@ -81,7 +80,7 @@ -- and <http://hackage.haskell.org/package/conduit conduit>. -- All of following examples import modules as below: ----- > {-# LANGUAGE OverloadedStrings #-}+-- > {\-# LANGUAGE OverloadedStrings #-\} -- > -- > import Web.Twitter.Conduit -- > import Web.Twitter.Types.Lens
Web/Twitter/Conduit/Api.hs view
@@ -4,158 +4,162 @@ {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE TypeOperators #-} -module Web.Twitter.Conduit.Api- (- -- * Status- statusesMentionsTimeline- , statusesUserTimeline- , statusesHomeTimeline- , statusesRetweetsOfMe- , statusesRetweetsId- , statusesShowId- , statusesDestroyId- , statusesUpdate- , statusesRetweetId- , statusesUpdateWithMedia- , statusesLookup+module Web.Twitter.Conduit.Api (+ -- * Status+ statusesMentionsTimeline,+ statusesUserTimeline,+ statusesHomeTimeline,+ statusesRetweetsOfMe,+ statusesRetweetsId,+ statusesShowId,+ statusesDestroyId,+ statusesUpdate,+ statusesRetweetId,+ statusesUpdateWithMedia,+ statusesLookup, - -- * Search- , SearchTweets- , searchTweets- , search+ -- * Search+ SearchTweets,+ searchTweets,+ search, - -- * Direct Messages- , DirectMessages- , directMessages- , DirectMessagesSent- , directMessagesSent- , DirectMessagesShow- , directMessagesShow- , DirectMessagesDestroy- , directMessagesDestroy- , DirectMessagesNew- , directMessagesNew+ -- * Direct Messages+ DirectMessages,+ directMessages,+ DirectMessagesSent,+ directMessagesSent,+ DirectMessagesShow,+ directMessagesShow,+ DirectMessagesDestroy,+ directMessagesDestroy,+ DirectMessagesNew,+ directMessagesNew, - -- * Friends & Followers- , FriendshipsNoRetweetsIds- , friendshipsNoRetweetsIds- , FriendsIds- , friendsIds- , FollowersIds- , followersIds- , FriendshipsIncoming- , friendshipsIncoming- , FriendshipsOutgoing- , friendshipsOutgoing- , FriendshipsCreate- , friendshipsCreate- , FriendshipsDestroy- , friendshipsDestroy- -- , friendshipsUpdate- -- , friendshipsShow- , FriendsList- , friendsList- , FollowersList- , followersList- -- , friendshipsLookup+ -- * Friends & Followers+ FriendshipsNoRetweetsIds,+ friendshipsNoRetweetsIds,+ FriendsIds,+ friendsIds,+ FollowersIds,+ followersIds,+ FriendshipsIncoming,+ friendshipsIncoming,+ FriendshipsOutgoing,+ friendshipsOutgoing,+ FriendshipsCreate,+ friendshipsCreate,+ FriendshipsDestroy,+ friendshipsDestroy,+ -- , friendshipsUpdate+ -- , friendshipsShow+ FriendsList,+ friendsList,+ FollowersList,+ followersList,+ -- , friendshipsLookup - -- * Users- -- , accountSettings- , AccountVerifyCredentials- , accountVerifyCredentials- -- , accountSettingsUpdate- -- , accountUpdateDeliveryDevice- , AccountUpdateProfile- , accountUpdateProfile- -- , accountUpdateProfileBackgroundImage- -- , accountUpdateProfileColors- -- , accoutUpdateProfileImage- -- , blocksList- -- , blocksIds- -- , blocksCreate- -- , blocksDestroy+ -- * Users - , UsersLookup- , usersLookup- , UsersShow- , usersShow- -- , usersSearch- -- , usersContributees- -- , usersContributors- -- , accuntRemoveProfileBanner- -- , accuntUpdateProfileBanner- -- , usersProfileBanner- -- , mutesUsersCreate- -- , mutesUsersDestroy- -- , mutesUsersIds- -- , mutesUsersList+ -- , accountSettings+ AccountVerifyCredentials,+ accountVerifyCredentials,+ -- , accountSettingsUpdate+ -- , accountUpdateDeliveryDevice+ AccountUpdateProfile,+ accountUpdateProfile,+ -- , accountUpdateProfileBackgroundImage+ -- , accountUpdateProfileColors+ -- , accoutUpdateProfileImage+ -- , blocksList+ -- , blocksIds+ -- , blocksCreate+ -- , blocksDestroy - -- * Suggested Users- -- , usersSuggestionsSlug- -- , usersSuggestions- -- , usersSuggestionsSlugMembers+ UsersLookup,+ usersLookup,+ UsersShow,+ usersShow,+ -- , usersSearch+ -- , usersContributees+ -- , usersContributors+ -- , accuntRemoveProfileBanner+ -- , accuntUpdateProfileBanner+ -- , usersProfileBanner+ -- , mutesUsersCreate+ -- , mutesUsersDestroy+ -- , mutesUsersIds+ -- , mutesUsersList - -- * Favorites- , FavoritesList- , favoritesList- , FavoritesDestroy- , favoritesDestroy- , FavoritesCreate- , favoritesCreate+ -- * Suggested Users - -- * Lists- -- , listsList- , ListsStatuses- , listsStatuses- , ListsMembersDestroy- , listsMembersDestroy- , ListsMemberships- , listsMemberships- , ListsSubscribers- , listsSubscribers- -- , listsSubscribersCreate- -- , listsSubscribersShow- -- , listsSubscribersDestroy- , ListsMembersCreateAll- , listsMembersCreateAll- -- , listsMembersShow- , ListsMembers- , listsMembers- , ListsMembersCreate- , listsMembersCreate- , ListsDestroy- , listsDestroy- , ListsUpdate- , listsUpdate- , ListsCreate- , listsCreate- , ListsShow- , listsShow- , ListsSubscriptions- , listsSubscriptions- , ListsMembersDestroyAll- , listsMembersDestroyAll- , ListsOwnerships- , listsOwnerships+ -- , usersSuggestionsSlug+ -- , usersSuggestions+ -- , usersSuggestionsSlugMembers - -- * Saved Searches- -- savedSearchesList- -- savedSearchesShowId- -- savedSearchesCreate- -- savedSearchesDestroyId+ -- * Favorites+ FavoritesList,+ favoritesList,+ FavoritesDestroy,+ favoritesDestroy,+ FavoritesCreate,+ favoritesCreate, - -- * Places & Geo- -- geoIdPlaceId- -- geoReverseGeocode- -- geoSearch- -- geoSimilarPlaces- -- geoPlace+ -- * Lists - -- * media- , MediaUpload- , mediaUpload- ) where+ -- , listsList+ ListsStatuses,+ listsStatuses,+ ListsMembersDestroy,+ listsMembersDestroy,+ ListsMemberships,+ listsMemberships,+ ListsSubscribers,+ listsSubscribers,+ -- , listsSubscribersCreate+ -- , listsSubscribersShow+ -- , listsSubscribersDestroy+ ListsMembersCreateAll,+ listsMembersCreateAll,+ -- , listsMembersShow+ ListsMembers,+ listsMembers,+ ListsMembersCreate,+ listsMembersCreate,+ ListsDestroy,+ listsDestroy,+ ListsUpdate,+ listsUpdate,+ ListsCreate,+ listsCreate,+ ListsShow,+ listsShow,+ ListsSubscriptions,+ listsSubscriptions,+ ListsMembersDestroyAll,+ listsMembersDestroyAll,+ ListsOwnerships,+ listsOwnerships, + -- * Saved Searches++ -- savedSearchesList+ -- savedSearchesShowId+ -- savedSearchesCreate+ -- savedSearchesDestroyId++ -- * Places & Geo++ -- geoIdPlaceId+ -- geoReverseGeocode+ -- geoSearch+ -- geoSimilarPlaces+ -- geoPlace++ -- * media+ MediaUpload,+ mediaUpload,+) where+ import Web.Twitter.Conduit.Base import Web.Twitter.Conduit.Cursor import Web.Twitter.Conduit.Parameters@@ -164,11 +168,11 @@ import qualified Web.Twitter.Conduit.Status as Status import Web.Twitter.Types -import Network.HTTP.Client.MultipartFormData-import qualified Data.Text as T+import Data.Aeson import Data.Default+import qualified Data.Text as T import Data.Time.Calendar (Day)-import Data.Aeson+import Network.HTTP.Client.MultipartFormData -- $setup -- >>> :set -XOverloadedStrings -XOverloadedLabels@@ -187,23 +191,29 @@ -- APIRequest "GET" "https://api.twitter.com/1.1/search/tweets.json" [("q","search text")] -- >>> searchTweets "search text" & #lang ?~ "ja" & #count ?~ 100 -- APIRequest "GET" "https://api.twitter.com/1.1/search/tweets.json" [("count","100"),("lang","ja"),("q","search text")]-searchTweets :: T.Text -- ^ search string- -> APIRequest SearchTweets (SearchResult [Status])+searchTweets ::+ -- | search string+ T.Text ->+ APIRequest SearchTweets (SearchResult [Status]) searchTweets q = APIRequest "GET" (endpoint ++ "search/tweets.json") [("q", PVString q)]-type SearchTweets = '[- "lang" ':= T.Text- , "locale" ':= T.Text- , "result_type" ':= T.Text- , "count" ':= Integer- , "until" ':= Day- , "since_id" ':= Integer- , "max_id" ':= Integer- , "include_entities" ':= Bool- ] +type SearchTweets =+ '[ "lang" ':= T.Text+ , "locale" ':= T.Text+ , "result_type" ':= T.Text+ , "count" ':= Integer+ , "until" ':= Day+ , "since_id" ':= Integer+ , "max_id" ':= Integer+ , "include_entities" ':= Bool+ , "tweet_mode" ':= TweetMode+ ]+ -- | Alias of 'searchTweets', for backward compatibility-search :: T.Text -- ^ search string- -> APIRequest SearchTweets (SearchResult [Status])+search ::+ -- | search string+ T.Text ->+ APIRequest SearchTweets (SearchResult [Status]) search = searchTweets {-# DEPRECATED search "Please use Web.Twitter.Conduit.searchTweets" #-} @@ -221,14 +231,15 @@ -- APIRequest "GET" "https://api.twitter.com/1.1/direct_messages/events/list.json" [("count","50")] directMessages :: APIRequest DirectMessages (WithCursor T.Text EventsCursorKey DirectMessage) directMessages = APIRequest "GET" (endpoint ++ "direct_messages/events/list.json") def-type DirectMessages = '[- "count" ':= Integer- , "include_entities" ':= Bool- , "skip_status" ':= Bool- , "full_text" ':= Bool- , "cursor" ':= T.Text- ] +type DirectMessages =+ '[ "count" ':= Integer+ , "include_entities" ':= Bool+ , "skip_status" ':= Bool+ , "full_text" ':= Bool+ , "cursor" ':= T.Text+ ]+ -- | Returns query data which asks recent direct messages sent by the authenticating user. -- -- You can perform a query using 'call':@@ -243,16 +254,17 @@ -- APIRequest "GET" "https://api.twitter.com/1.1/direct_messages/sent.json" [("count","100")] directMessagesSent :: APIRequest DirectMessagesSent [DirectMessage] directMessagesSent = APIRequest "GET" (endpoint ++ "direct_messages/sent.json") def-type DirectMessagesSent = '[- "since_id" ':= Integer- , "max_id" ':= Integer- , "count" ':= Integer- , "include_entities" ':= Bool- , "page" ':= Integer- , "skip_status" ':= Bool- , "full_text" ':= Bool- ] +type DirectMessagesSent =+ '[ "since_id" ':= Integer+ , "max_id" ':= Integer+ , "count" ':= Integer+ , "include_entities" ':= Bool+ , "page" ':= Integer+ , "skip_status" ':= Bool+ , "full_text" ':= Bool+ ]+ -- | Returns query data which asks a single direct message, specified by an id parameter. -- -- You can perform a query using 'call':@@ -265,10 +277,11 @@ -- APIRequest "GET" "https://api.twitter.com/1.1/direct_messages/show.json" [("id","1234567890")] directMessagesShow :: StatusId -> APIRequest DirectMessagesShow DirectMessage directMessagesShow sId = APIRequest "GET" (endpoint ++ "direct_messages/show.json") [("id", PVInteger sId)]-type DirectMessagesShow = '[- "full_text" ':= Bool- ] +type DirectMessagesShow =+ '[ "full_text" ':= Bool+ ]+ -- | Returns post data which destroys the direct message specified in the required ID parameter. -- -- You can perform a query using 'call':@@ -281,11 +294,13 @@ -- APIRequest "DELETE" "https://api.twitter.com/1.1/direct_messages/events/destroy.json" [("id","1234567890")] directMessagesDestroy :: StatusId -> APIRequest DirectMessagesDestroy NoContent directMessagesDestroy sId = APIRequest "DELETE" (endpoint ++ "direct_messages/events/destroy.json") [("id", PVInteger sId)]+ type DirectMessagesDestroy = EmptyParams newtype DirectMessagesNewResponse = DirectMessagesNewResponse { directMessageBody :: DirectMessage- } deriving (Show, Eq)+ }+ deriving (Show, Eq) instance FromJSON DirectMessagesNewResponse where parseJSON = withObject "DirectMessagesNewResponse" $ \o -> DirectMessagesNewResponse <$> o .: "event"@@ -306,16 +321,17 @@ where body = object- [ "event" .=- object- [ "type" .= ("message_create" :: String)- , "message_create" .=- object- [ "target" .= object ["recipient_id" .= up]- , "message_data" .= object ["text" .= msg]- ]- ]+ [ "event"+ .= object+ [ "type" .= ("message_create" :: String)+ , "message_create"+ .= object+ [ "target" .= object ["recipient_id" .= up]+ , "message_data" .= object ["text" .= msg]+ ]+ ] ]+ type DirectMessagesNew = EmptyParams type RecipientId = Integer@@ -332,6 +348,7 @@ -- APIRequest "GET" "https://api.twitter.com/1.1/friendships/no_retweets/ids.json" [] friendshipsNoRetweetsIds :: APIRequest FriendshipsNoRetweetsIds [UserId] friendshipsNoRetweetsIds = APIRequest "GET" (endpoint ++ "friendships/no_retweets/ids.json") []+ type FriendshipsNoRetweetsIds = EmptyParams -- | Returns query data which asks a collection of user IDs for every user the specified user is following.@@ -354,11 +371,12 @@ -- APIRequest "GET" "https://api.twitter.com/1.1/friends/ids.json" [("count","5000"),("screen_name","thimura")] friendsIds :: UserParam -> APIRequest FriendsIds (WithCursor Integer IdsCursorKey UserId) friendsIds q = APIRequest "GET" (endpoint ++ "friends/ids.json") (mkUserParam q)-type FriendsIds = '[- "count" ':= Integer- , "cursor" ':= Integer- ] +type FriendsIds =+ '[ "count" ':= Integer+ , "cursor" ':= Integer+ ]+ -- | Returns query data which asks a collection of user IDs for every user following the specified user. -- -- You can perform a query using 'call':@@ -379,11 +397,12 @@ -- APIRequest "GET" "https://api.twitter.com/1.1/followers/ids.json" [("count","5000"),("screen_name","thimura")] followersIds :: UserParam -> APIRequest FollowersIds (WithCursor Integer IdsCursorKey UserId) followersIds q = APIRequest "GET" (endpoint ++ "followers/ids.json") (mkUserParam q)-type FollowersIds = '[- "count" ':= Integer- , "cursor" ':= Integer- ] +type FollowersIds =+ '[ "count" ':= Integer+ , "cursor" ':= Integer+ ]+ -- | Returns a collection of numeric IDs for every user who has a pending request to follow the authenticating user. -- -- You can perform a request by using 'call':@@ -402,10 +421,11 @@ -- APIRequest "GET" "https://api.twitter.com/1.1/friendships/incoming.json" [] friendshipsIncoming :: APIRequest FriendshipsIncoming (WithCursor Integer IdsCursorKey UserId) friendshipsIncoming = APIRequest "GET" (endpoint ++ "friendships/incoming.json") def-type FriendshipsIncoming = '[- "cursor" ':= Integer- ] +type FriendshipsIncoming =+ '[ "cursor" ':= Integer+ ]+ -- | Returns a collection of numeric IDs for every protected user for whom the authenticating user has a pending follow request. -- -- You can perform a request by using 'call':@@ -424,10 +444,11 @@ -- APIRequest "GET" "https://api.twitter.com/1.1/friendships/outgoing.json" [] friendshipsOutgoing :: APIRequest FriendshipsOutgoing (WithCursor Integer IdsCursorKey UserId) friendshipsOutgoing = APIRequest "GET" (endpoint ++ "friendships/outgoing.json") def-type FriendshipsOutgoing = '[- "cursor" ':= Integer- ] +type FriendshipsOutgoing =+ '[ "cursor" ':= Integer+ ]+ -- | Returns post data which follows the user specified in the ID parameter. -- -- You can perform request by using 'call':@@ -442,10 +463,11 @@ -- APIRequest "POST" "https://api.twitter.com/1.1/friendships/create.json" [("user_id","69179963")] friendshipsCreate :: UserParam -> APIRequest FriendshipsCreate User friendshipsCreate user = APIRequest "POST" (endpoint ++ "friendships/create.json") (mkUserParam user)-type FriendshipsCreate = '[- "follow" ':= Bool- ] +type FriendshipsCreate =+ '[ "follow" ':= Bool+ ]+ -- | Returns post data which unfollows the user specified in the ID parameter. -- -- You can perform request by using 'call':@@ -460,6 +482,7 @@ -- APIRequest "POST" "https://api.twitter.com/1.1/friendships/destroy.json" [("user_id","69179963")] friendshipsDestroy :: UserParam -> APIRequest FriendshipsDestroy User friendshipsDestroy user = APIRequest "POST" (endpoint ++ "friendships/destroy.json") (mkUserParam user)+ type FriendshipsDestroy = EmptyParams -- | Returns query data which asks a cursored collection of user objects for every user the specified users is following.@@ -482,13 +505,14 @@ -- APIRequest "GET" "https://api.twitter.com/1.1/friends/list.json" [("user_id","69179963")] friendsList :: UserParam -> APIRequest FriendsList (WithCursor Integer UsersCursorKey User) friendsList q = APIRequest "GET" (endpoint ++ "friends/list.json") (mkUserParam q)-type FriendsList = '[- "count" ':= Integer- , "cursor" ':= Integer- , "skip_status" ':= Bool- , "include_user_entities" ':= Bool- ] +type FriendsList =+ '[ "count" ':= Integer+ , "cursor" ':= Integer+ , "skip_status" ':= Bool+ , "include_user_entities" ':= Bool+ ]+ -- | Returns query data which asks a cursored collection of user objects for users following the specified user. -- -- You can perform request by using 'call':@@ -509,13 +533,14 @@ -- APIRequest "GET" "https://api.twitter.com/1.1/followers/list.json" [("user_id","69179963")] followersList :: UserParam -> APIRequest FollowersList (WithCursor Integer UsersCursorKey User) followersList q = APIRequest "GET" (endpoint ++ "followers/list.json") (mkUserParam q)-type FollowersList = '[- "count" ':= Integer- , "cursor" ':= Integer- , "skip_status" ':= Bool- , "include_user_entities" ':= Bool- ] +type FollowersList =+ '[ "count" ':= Integer+ , "cursor" ':= Integer+ , "skip_status" ':= Bool+ , "include_user_entities" ':= Bool+ ]+ -- | Returns query data asks that the credential is valid. -- -- You can perform request by using 'call':@@ -528,12 +553,13 @@ -- APIRequest "GET" "https://api.twitter.com/1.1/account/verify_credentials.json" [] accountVerifyCredentials :: APIRequest AccountVerifyCredentials User accountVerifyCredentials = APIRequest "GET" (endpoint ++ "account/verify_credentials.json") []-type AccountVerifyCredentials = '[- "include_entities" ':= Bool- , "skip_status" ':= Bool- , "include_email" ':= Bool- ] +type AccountVerifyCredentials =+ '[ "include_entities" ':= Bool+ , "skip_status" ':= Bool+ , "include_email" ':= Bool+ ]+ -- | Returns user object with updated fields. -- Note that while no specific parameter is required, you need to provide at least one parameter before executing the query. --@@ -547,16 +573,17 @@ -- APIRequest "POST" "https://api.twitter.com/1.1/account/update_profile.json" [("url","http://www.example.com")] accountUpdateProfile :: APIRequest AccountUpdateProfile User accountUpdateProfile = APIRequest "POST" (endpoint ++ "account/update_profile.json") []-type AccountUpdateProfile = '[- "include_entities" ':= Bool- , "skip_status" ':= Bool- , "name" ':= T.Text- , "url" ':= URIString- , "location" ':= T.Text- , "description" ':= T.Text- , "profile_link_color" ':= T.Text- ] +type AccountUpdateProfile =+ '[ "include_entities" ':= Bool+ , "skip_status" ':= Bool+ , "name" ':= T.Text+ , "url" ':= URIString+ , "location" ':= T.Text+ , "description" ':= T.Text+ , "profile_link_color" ':= T.Text+ ]+ -- | Returns query data asks user objects. -- -- You can perform request by using 'call':@@ -569,10 +596,11 @@ -- APIRequest "GET" "https://api.twitter.com/1.1/users/lookup.json" [("screen_name","thimura,twitterapi")] usersLookup :: UserListParam -> APIRequest UsersLookup [User] usersLookup q = APIRequest "GET" (endpoint ++ "users/lookup.json") (mkUserListParam q)-type UsersLookup = '[- "include_entities" ':= Bool- ] +type UsersLookup =+ '[ "include_entities" ':= Bool+ ]+ -- | Returns query data asks the user specified by user id or screen name parameter. -- -- You can perform request by using 'call':@@ -585,10 +613,11 @@ -- APIRequest "GET" "https://api.twitter.com/1.1/users/show.json" [("screen_name","thimura")] usersShow :: UserParam -> APIRequest UsersShow User usersShow q = APIRequest "GET" (endpoint ++ "users/show.json") (mkUserParam q)-type UsersShow = '[- "include_entities" ':= Bool- ] +type UsersShow =+ '[ "include_entities" ':= Bool+ ]+ -- | Returns the 20 most recent Tweets favorited by the specified user. -- -- You can perform request by using 'call':@@ -608,13 +637,15 @@ where mkParam Nothing = [] mkParam (Just usr) = mkUserParam usr-type FavoritesList = '[- "count" ':= Integer- , "since_id" ':= Integer- , "max_id" ':= Integer- , "include_entities" ':= Bool- ] +type FavoritesList =+ '[ "count" ':= Integer+ , "since_id" ':= Integer+ , "max_id" ':= Integer+ , "include_entities" ':= Bool+ , "tweet_mode" ':= TweetMode+ ]+ -- | Returns post data which favorites the status specified in the ID parameter as the authenticating user. -- -- You can perform request by using 'call':@@ -627,11 +658,13 @@ -- APIRequest "POST" "https://api.twitter.com/1.1/favorites/create.json" [("id","1234567890")] favoritesCreate :: StatusId -> APIRequest FavoritesCreate Status favoritesCreate sid = APIRequest "POST" (endpoint ++ "favorites/create.json") [("id", PVInteger sid)]-type FavoritesCreate = '[- "include_entities" ':= Bool- ] --- | Returns post data unfavorites the status specified in the ID paramter as the authenticating user.+type FavoritesCreate =+ '[ "include_entities" ':= Bool+ , "tweet_mode" ':= TweetMode+ ]++-- | Returns post data unfavorites the status specified in the ID parameter as the authenticating user. -- -- You can perform request by using 'call': --@@ -643,10 +676,12 @@ -- APIRequest "POST" "https://api.twitter.com/1.1/favorites/destroy.json" [("id","1234567890")] favoritesDestroy :: StatusId -> APIRequest FavoritesDestroy Status favoritesDestroy sid = APIRequest "POST" (endpoint ++ "favorites/destroy.json") [("id", PVInteger sid)]-type FavoritesDestroy = '[- "include_entities" ':= Bool- ] +type FavoritesDestroy =+ '[ "include_entities" ':= Bool+ , "tweet_mode" ':= TweetMode+ ]+ -- | Returns the query parameter which fetches a timeline of tweets authored by members of the specified list. -- -- You can perform request by using 'call':@@ -666,14 +701,16 @@ -- APIRequest "GET" "https://api.twitter.com/1.1/lists/statuses.json" [("list_id","20849097")] listsStatuses :: ListParam -> APIRequest ListsStatuses [Status] listsStatuses q = APIRequest "GET" (endpoint ++ "lists/statuses.json") (mkListParam q)-type ListsStatuses = '[- "since_id" ':= Integer- , "max_id" ':= Integer- , "count" ':= Integer- , "include_entities" ':= Bool- , "include_rts" ':= Bool- ] +type ListsStatuses =+ '[ "since_id" ':= Integer+ , "max_id" ':= Integer+ , "count" ':= Integer+ , "include_entities" ':= Bool+ , "include_rts" ':= Bool+ , "tweet_mode" ':= TweetMode+ ]+ -- | Returns the post parameter which removes the specified member from the list. -- -- You can perform request by using 'call':@@ -688,6 +725,7 @@ -- APIRequest "POST" "https://api.twitter.com/1.1/lists/members/destroy.json" [("list_id","20849097"),("user_id","69179963")] listsMembersDestroy :: ListParam -> UserParam -> APIRequest ListsMembersDestroy List listsMembersDestroy list user = APIRequest "POST" (endpoint ++ "lists/members/destroy.json") (mkListParam list ++ mkUserParam user)+ type ListsMembersDestroy = EmptyParams -- | Returns the request parameters which asks the lists the specified user has been added to.@@ -707,11 +745,12 @@ -- APIRequest "GET" "https://api.twitter.com/1.1/lists/memberships.json" [("user_id","69179963")] listsMemberships :: Maybe UserParam -> APIRequest ListsMemberships (WithCursor Integer ListsCursorKey List) listsMemberships q = APIRequest "GET" (endpoint ++ "lists/memberships.json") $ maybe [] mkUserParam q-type ListsMemberships = '[- "count" ':= Integer- , "cursor" ':= Integer- ] +type ListsMemberships =+ '[ "count" ':= Integer+ , "cursor" ':= Integer+ ]+ -- | Returns the request parameter which asks the subscribers of the specified list. -- -- You can perform request by using 'call':@@ -726,12 +765,13 @@ -- APIRequest "GET" "https://api.twitter.com/1.1/lists/subscribers.json" [("list_id","20849097")] listsSubscribers :: ListParam -> APIRequest ListsSubscribers (WithCursor Integer UsersCursorKey User) listsSubscribers q = APIRequest "GET" (endpoint ++ "lists/subscribers.json") (mkListParam q)-type ListsSubscribers = '[- "count" ':= Integer- , "cursor" ':= Integer- , "skip_status" ':= Bool- ] +type ListsSubscribers =+ '[ "count" ':= Integer+ , "cursor" ':= Integer+ , "skip_status" ':= Bool+ ]+ -- | Returns the request parameter which obtains a collection of the lists the specified user is subscribed to. -- -- You can perform request by using 'call':@@ -748,11 +788,12 @@ -- APIRequest "GET" "https://api.twitter.com/1.1/lists/subscriptions.json" [("user_id","69179963")] listsSubscriptions :: Maybe UserParam -> APIRequest ListsSubscriptions (WithCursor Integer ListsCursorKey List) listsSubscriptions q = APIRequest "GET" (endpoint ++ "lists/subscriptions.json") $ maybe [] mkUserParam q-type ListsSubscriptions = '[- "count" ':= Integer- , "cursor" ':= Integer- ] +type ListsSubscriptions =+ '[ "count" ':= Integer+ , "cursor" ':= Integer+ ]+ -- | Returns the request parameter which asks the lists owned by the specified Twitter user. -- -- You can perform request by using 'call':@@ -769,11 +810,12 @@ -- APIRequest "GET" "https://api.twitter.com/1.1/lists/ownerships.json" [("user_id","69179963")] listsOwnerships :: Maybe UserParam -> APIRequest ListsOwnerships (WithCursor Integer ListsCursorKey List) listsOwnerships q = APIRequest "GET" (endpoint ++ "lists/ownerships.json") $ maybe [] mkUserParam q-type ListsOwnerships = '[- "count" ':= Integer- , "cursor" ':= Integer- ] +type ListsOwnerships =+ '[ "count" ':= Integer+ , "cursor" ':= Integer+ ]+ -- | Adds multiple members to a list. -- -- You can perform request by using 'call':@@ -788,6 +830,7 @@ -- APIRequest "POST" "https://api.twitter.com/1.1/lists/members/create_all.json" [("list_id","20849097"),("user_id","69179963,6253282")] listsMembersCreateAll :: ListParam -> UserListParam -> APIRequest ListsMembersCreateAll List listsMembersCreateAll list users = APIRequest "POST" (endpoint ++ "lists/members/create_all.json") (mkListParam list ++ mkUserListParam users)+ type ListsMembersCreateAll = EmptyParams -- | Adds multiple members to a list.@@ -804,6 +847,7 @@ -- APIRequest "POST" "https://api.twitter.com/1.1/lists/members/destroy_all.json" [("list_id","20849097"),("user_id","69179963,6253282")] listsMembersDestroyAll :: ListParam -> UserListParam -> APIRequest ListsMembersDestroyAll List listsMembersDestroyAll list users = APIRequest "POST" (endpoint ++ "lists/members/destroy_all.json") (mkListParam list ++ mkUserListParam users)+ type ListsMembersDestroyAll = EmptyParams -- | Returns query data asks the members of the specified list.@@ -820,12 +864,13 @@ -- APIRequest "GET" "https://api.twitter.com/1.1/lists/members.json" [("list_id","20849097")] listsMembers :: ListParam -> APIRequest ListsMembers (WithCursor Integer UsersCursorKey User) listsMembers q = APIRequest "GET" (endpoint ++ "lists/members.json") (mkListParam q)-type ListsMembers = '[- "count" ':= Integer- , "cursor" ':= Integer- , "skip_status" ':= Bool- ] +type ListsMembers =+ '[ "count" ':= Integer+ , "cursor" ':= Integer+ , "skip_status" ':= Bool+ ]+ -- | Returns the post parameter which adds a member to a list. -- -- You can perform request by using 'call':@@ -840,6 +885,7 @@ -- APIRequest "POST" "https://api.twitter.com/1.1/lists/members/create.json" [("list_id","20849097"),("user_id","69179963")] listsMembersCreate :: ListParam -> UserParam -> APIRequest ListsMembersCreate List listsMembersCreate list user = APIRequest "POST" (endpoint ++ "lists/members/create.json") (mkListParam list ++ mkUserParam user)+ type ListsMembersCreate = EmptyParams -- | Returns the post parameter which deletes the specified list.@@ -856,6 +902,7 @@ -- APIRequest "POST" "https://api.twitter.com/1.1/lists/destroy.json" [("list_id","20849097")] listsDestroy :: ListParam -> APIRequest ListsDestroy List listsDestroy list = APIRequest "POST" (endpoint ++ "lists/destroy.json") (mkListParam list)+ type ListsDestroy = EmptyParams -- | Returns the post parameter which updates the specified list.@@ -868,16 +915,20 @@ -- -- >>> listsUpdate (ListNameParam "thimura/haskell") True (Just "Haskellers") -- APIRequest "POST" "https://api.twitter.com/1.1/lists/update.json" [("slug","haskell"),("owner_screen_name","thimura"),("description","Haskellers"),("mode","public")]-listsUpdate :: ListParam- -> Bool -- ^ is public- -> Maybe T.Text -- ^ description- -> APIRequest ListsUpdate List+listsUpdate ::+ ListParam ->+ -- | is public+ Bool ->+ -- | description+ Maybe T.Text ->+ APIRequest ListsUpdate List listsUpdate list isPublic description = APIRequest "POST" (endpoint ++ "lists/update.json") (mkListParam list ++ p') where p = [("mode", PVString . mode $ isPublic)]- p' = maybe id (\d -> (("description", PVString d):)) description p+ p' = maybe id (\d -> (("description", PVString d) :)) description p mode True = "public" mode False = "private"+ type ListsUpdate = EmptyParams -- | Returns the post parameter which creates a new list for the authenticated user.@@ -894,16 +945,21 @@ -- APIRequest "POST" "https://api.twitter.com/1.1/lists/create.json" [("name","haskell"),("mode","private")] -- >>> listsCreate "haskell" True (Just "Haskellers") -- APIRequest "POST" "https://api.twitter.com/1.1/lists/create.json" [("description","Haskellers"),("name","haskell"),("mode","public")]-listsCreate :: T.Text -- ^ list name- -> Bool -- ^ whether public(True) or private(False)- -> Maybe T.Text -- ^ the description to give the list- -> APIRequest ListsCreate List+listsCreate ::+ -- | list name+ T.Text ->+ -- | whether public(True) or private(False)+ Bool ->+ -- | the description to give the list+ Maybe T.Text ->+ APIRequest ListsCreate List listsCreate name isPublic description = APIRequest "POST" (endpoint ++ "lists/create.json") p' where p = [("name", PVString name), ("mode", PVString . mode $ isPublic)]- p' = maybe id (\d -> (("description", PVString d):)) description p+ p' = maybe id (\d -> (("description", PVString d) :)) description p mode True = "public" mode False = "private"+ type ListsCreate = EmptyParams -- | Returns the request parameter which asks the specified list.@@ -920,6 +976,7 @@ -- APIRequest "GET" "https://api.twitter.com/1.1/lists/show.json" [("list_id","20849097")] listsShow :: ListParam -> APIRequest ListsShow List listsShow q = APIRequest "GET" (endpoint ++ "lists/show.json") (mkListParam q)+ type ListsShow = EmptyParams -- | Upload media and returns the media data.@@ -943,14 +1000,16 @@ -- -- >>> mediaUpload (MediaFromFile "/home/test/test.png") -- APIRequestMultipart "POST" "https://upload.twitter.com/1.1/media/upload.json" []-mediaUpload :: MediaData- -> APIRequest MediaUpload UploadedMedia+mediaUpload ::+ MediaData ->+ APIRequest MediaUpload UploadedMedia mediaUpload mediaData = APIRequestMultipart "POST" uri [] [mediaBody mediaData] where uri = "https://upload.twitter.com/1.1/media/upload.json" mediaBody (MediaFromFile fp) = partFileSource "media" fp mediaBody (MediaRequestBody filename filebody) = partFileRequestBody "media" filename filebody+ type MediaUpload = EmptyParams statusesMentionsTimeline :: APIRequest Status.StatusesMentionsTimeline [Status]
Web/Twitter/Conduit/Base.hs view
@@ -9,26 +9,26 @@ {-# LANGUAGE TypeFamilies #-} {-# LANGUAGE UndecidableInstances #-} -module Web.Twitter.Conduit.Base- ( ResponseBodyType (..)- , NoContent- , getResponse- , call- , call'- , callWithResponse- , callWithResponse'- , checkResponse- , sourceWithMaxId- , sourceWithMaxId'- , sourceWithCursor- , sourceWithCursor'- , sourceWithSearchResult- , sourceWithSearchResult'- , endpoint- , makeRequest- , sinkJSON- , sinkFromJSON- ) where+module Web.Twitter.Conduit.Base (+ ResponseBodyType (..),+ NoContent,+ getResponse,+ call,+ call',+ callWithResponse,+ callWithResponse',+ checkResponse,+ sourceWithMaxId,+ sourceWithMaxId',+ sourceWithCursor,+ sourceWithCursor',+ sourceWithSearchResult,+ sourceWithSearchResult',+ endpoint,+ makeRequest,+ sinkJSON,+ sinkFromJSON,+) where import Web.Twitter.Conduit.Cursor import Web.Twitter.Conduit.Request@@ -60,8 +60,9 @@ import Data.Monoid #endif -makeRequest :: APIRequest apiName responseType- -> IO HTTP.Request+makeRequest ::+ APIRequest apiName responseType ->+ IO HTTP.Request makeRequest (APIRequest m u pa) = makeRequest' m u (makeSimpleQuery pa) makeRequest (APIRequestMultipart m u param prt) = formDataBody body =<< makeRequest' m u []@@ -72,26 +73,30 @@ req <- makeRequest' m u (makeSimpleQuery param) return $ req- { HTTP.requestBody = HTTP.RequestBodyLBS $ encode body- , HTTP.requestHeaders = ("Content-Type", "application/json") : HTTP.requestHeaders req- }+ { HTTP.requestBody = HTTP.RequestBodyLBS $ encode body+ , HTTP.requestHeaders = ("Content-Type", "application/json") : HTTP.requestHeaders req+ } -makeRequest' :: HT.Method -- ^ HTTP request method (GET or POST)- -> String -- ^ API Resource URL- -> HT.SimpleQuery -- ^ Query- -> IO HTTP.Request+makeRequest' ::+ -- | HTTP request method (GET or POST)+ HT.Method ->+ -- | API Resource URL+ String ->+ -- | Query+ HT.SimpleQuery ->+ IO HTTP.Request makeRequest' m url query = do req <- HTTP.parseRequest url let addParams = if m == "POST"- then HTTP.urlEncodedBody query- else \r -> r { HTTP.queryString = HT.renderSimpleQuery False query }- return $ addParams $ req { HTTP.method = m }+ then HTTP.urlEncodedBody query+ else \r -> r {HTTP.queryString = HT.renderSimpleQuery False query}+ return $ addParams $ req {HTTP.method = m} class ResponseBodyType a where parseResponseBody ::- Response (C.ConduitM () ByteString (ResourceT IO) ())- -> ResourceT IO (Response a)+ Response (C.ConduitM () ByteString (ResourceT IO) ()) ->+ ResourceT IO (Response a) type NoContent = () instance ResponseBodyType NoContent where@@ -105,34 +110,37 @@ instance {-# OVERLAPPABLE #-} FromJSON a => ResponseBodyType a where parseResponseBody = getValueOrThrow -getResponse :: MonadResource m- => TWInfo- -> HTTP.Manager- -> HTTP.Request- -> m (Response (C.ConduitM () ByteString m ()))-getResponse TWInfo{..} mgr req = do- signedReq <- signOAuth (twOAuth twToken) (twCredential twToken) $ req { HTTP.proxy = twProxy }+getResponse ::+ MonadResource m =>+ TWInfo ->+ HTTP.Manager ->+ HTTP.Request ->+ m (Response (C.ConduitM () ByteString m ()))+getResponse TWInfo {..} mgr req = do+ signedReq <- signOAuth (twOAuth twToken) (twCredential twToken) $ req {HTTP.proxy = twProxy} res <- HTTP.http signedReq mgr return- Response { responseStatus = HTTP.responseStatus res- , responseHeaders = HTTP.responseHeaders res- , responseBody = HTTP.responseBody res- }+ Response+ { responseStatus = HTTP.responseStatus res+ , responseHeaders = HTTP.responseHeaders res+ , responseBody = HTTP.responseBody res+ } endpoint :: String endpoint = "https://api.twitter.com/1.1/" getValue ::- Response (C.ConduitM () ByteString (ResourceT IO) ())- -> ResourceT IO (Response Value)+ Response (C.ConduitM () ByteString (ResourceT IO) ()) ->+ ResourceT IO (Response Value) getValue res = do value <-- C.runConduit $ responseBody res C..| sinkJSON- return $ res { responseBody = value }+ C.runConduit $ responseBody res C..| sinkJSON+ return $ res {responseBody = value} -checkResponse :: Response Value- -> Either TwitterError Value-checkResponse Response{..} =+checkResponse ::+ Response Value ->+ Either TwitterError Value+checkResponse Response {..} = case responseBody ^? key "errors" of Just errs@(Array _) -> case fromJSON errs of@@ -147,16 +155,17 @@ where sci = HT.statusCode responseStatus -getValueOrThrow :: FromJSON a- => Response (C.ConduitM () ByteString (ResourceT IO) ())- -> ResourceT IO (Response a)+getValueOrThrow ::+ FromJSON a =>+ Response (C.ConduitM () ByteString (ResourceT IO) ()) ->+ ResourceT IO (Response a) getValueOrThrow res = do res' <- getValue res case checkResponse res' of Left err -> throwM err Right _ -> return () case fromJSON (responseBody res') of- Success r -> return $ res' { responseBody = r }+ Success r -> return $ res' {responseBody = r} Error err -> throwM $ FromJSONError err -- | Perform an 'APIRequest' and then provide the response which is mapped to a suitable type of@@ -171,21 +180,25 @@ -- -- If you need raw JSON value which is parsed by <http://hackage.haskell.org/package/aeson aeson>, -- use 'call'' to obtain it.-call :: ResponseBodyType responseType- => TWInfo -- ^ Twitter Setting- -> HTTP.Manager- -> APIRequest apiName responseType- -> IO responseType+call ::+ ResponseBodyType responseType =>+ -- | Twitter Setting+ TWInfo ->+ HTTP.Manager ->+ APIRequest apiName responseType ->+ IO responseType call = call' -- | Perform an 'APIRequest' and then provide the response. -- The response of this function is not restrict to @responseType@, -- so you can choose an arbitrarily type of FromJSON instances.-call' :: ResponseBodyType value- => TWInfo -- ^ Twitter Setting- -> HTTP.Manager- -> APIRequest apiName responseType- -> IO value+call' ::+ ResponseBodyType value =>+ -- | Twitter Setting+ TWInfo ->+ HTTP.Manager ->+ APIRequest apiName responseType ->+ IO value call' info mgr req = responseBody `fmap` callWithResponse' info mgr req -- | Perform an 'APIRequest' and then provide the 'Response'.@@ -198,11 +211,13 @@ -- 'print' $ 'responseHeaders' res -- 'print' $ 'responseBody' res -- @-callWithResponse :: ResponseBodyType responseType- => TWInfo -- ^ Twitter Setting- -> HTTP.Manager- -> APIRequest apiName responseType- -> IO (Response responseType)+callWithResponse ::+ ResponseBodyType responseType =>+ -- | Twitter Setting+ TWInfo ->+ HTTP.Manager ->+ APIRequest apiName responseType ->+ IO (Response responseType) callWithResponse = callWithResponse' -- | Perform an 'APIRequest' and then provide the 'Response'.@@ -217,11 +232,12 @@ -- 'print' $ 'responseHeaders' res -- 'print' $ 'responseBody' (res :: Value) -- @-callWithResponse' :: ResponseBodyType value- => TWInfo- -> HTTP.Manager- -> APIRequest apiName responseType- -> IO (Response value)+callWithResponse' ::+ ResponseBodyType value =>+ TWInfo ->+ HTTP.Manager ->+ APIRequest apiName responseType ->+ IO (Response value) callWithResponse' info mgr req = runResourceT $ do res <- getResponse info mgr =<< liftIO (makeRequest req)@@ -230,15 +246,17 @@ -- | A wrapper function to perform multiple API request with changing @max_id@ parameter. -- -- This function cooperate with instances of 'HasMaxIdParam'.-sourceWithMaxId :: ( MonadIO m- , FromJSON responseType- , AsStatus responseType- , HasParam "max_id" Integer supports- )- => TWInfo -- ^ Twitter Setting- -> HTTP.Manager- -> APIRequest supports [responseType]- -> C.ConduitT () responseType m ()+sourceWithMaxId ::+ ( MonadIO m+ , FromJSON responseType+ , AsStatus responseType+ , HasParam "max_id" Integer supports+ ) =>+ -- | Twitter Setting+ TWInfo ->+ HTTP.Manager ->+ APIRequest supports [responseType] ->+ C.ConduitT () responseType m () sourceWithMaxId info mgr = loop where loop req = do@@ -255,13 +273,15 @@ -- so you can choose an arbitrarily type of FromJSON instances. -- -- This function cooperate with instances of 'HasMaxIdParam'.-sourceWithMaxId' :: ( MonadIO m- , HasParam "max_id" Integer supports- )- => TWInfo -- ^ Twitter Setting- -> HTTP.Manager- -> APIRequest supports [responseType]- -> C.ConduitT () Value m ()+sourceWithMaxId' ::+ ( MonadIO m+ , HasParam "max_id" Integer supports+ ) =>+ -- | Twitter Setting+ TWInfo ->+ HTTP.Manager ->+ APIRequest supports [responseType] ->+ C.ConduitT () Value m () sourceWithMaxId' info mgr = loop where loop req = do@@ -275,15 +295,17 @@ -- | A wrapper function to perform multiple API request with changing @cursor@ parameter. -- -- This function cooperate with instances of 'HasCursorParam'.-sourceWithCursor :: ( MonadIO m- , FromJSON responseType- , CursorKey ck- , HasParam "cursor" Integer supports- )- => TWInfo -- ^ Twitter Setting- -> HTTP.Manager- -> APIRequest supports (WithCursor Integer ck responseType)- -> C.ConduitT () responseType m ()+sourceWithCursor ::+ ( MonadIO m+ , FromJSON responseType+ , CursorKey ck+ , HasParam "cursor" Integer supports+ ) =>+ -- | Twitter Setting+ TWInfo ->+ HTTP.Manager ->+ APIRequest supports (WithCursor Integer ck responseType) ->+ C.ConduitT () responseType m () sourceWithCursor info mgr req = loop (Just (-1)) where loop Nothing = CL.sourceNull@@ -298,18 +320,21 @@ -- so you can choose an arbitrarily type of FromJSON instances. -- -- This function cooperate with instances of 'HasCursorParam'.-sourceWithCursor' :: ( MonadIO m- , CursorKey ck- , HasParam "cursor" Integer supports- )- => TWInfo -- ^ Twitter Setting- -> HTTP.Manager- -> APIRequest supports (WithCursor Integer ck responseType)- -> C.ConduitT () Value m ()+sourceWithCursor' ::+ ( MonadIO m+ , CursorKey ck+ , HasParam "cursor" Integer supports+ ) =>+ -- | Twitter Setting+ TWInfo ->+ HTTP.Manager ->+ APIRequest supports (WithCursor Integer ck responseType) ->+ C.ConduitT () Value m () sourceWithCursor' info mgr req = loop (Just (-1)) where- relax :: APIRequest apiName (WithCursor Integer ck responseType)- -> APIRequest apiName (WithCursor Integer ck Value)+ relax ::+ APIRequest apiName (WithCursor Integer ck responseType) ->+ APIRequest apiName (WithCursor Integer ck Value) relax = coerce loop Nothing = CL.sourceNull loop (Just 0) = CL.sourceNull@@ -319,17 +344,20 @@ loop $ nextCursor res -- | A wrapper function to perform multiple API request with @SearchResult@.-sourceWithSearchResult :: ( MonadIO m- , FromJSON responseType- )- => TWInfo -- ^ Twitter Setting- -> HTTP.Manager- -> APIRequest supports (SearchResult [responseType])- -> m (SearchResult (C.ConduitT () responseType m ()))+sourceWithSearchResult ::+ ( MonadIO m+ , FromJSON responseType+ ) =>+ -- | Twitter Setting+ TWInfo ->+ HTTP.Manager ->+ APIRequest supports (SearchResult [responseType]) ->+ m (SearchResult (C.ConduitT () responseType m ())) sourceWithSearchResult info mgr req = do res <- liftIO $ call info mgr req- let body = CL.sourceList (res ^. searchResultStatuses) <>- loop (res ^. searchResultSearchMetadata . searchMetadataNextResults)+ let body =+ CL.sourceList (res ^. searchResultStatuses)+ <> loop (res ^. searchResultSearchMetadata . searchMetadataNextResults) return $ res & searchResultStatuses .~ body where origQueryMap = req ^. params . to M.fromList@@ -342,21 +370,25 @@ loop $ res ^. searchResultSearchMetadata . searchMetadataNextResults -- | A wrapper function to perform multiple API request with @SearchResult@.-sourceWithSearchResult' :: ( MonadIO m- )- => TWInfo -- ^ Twitter Setting- -> HTTP.Manager- -> APIRequest supports (SearchResult [responseType])- -> m (SearchResult (C.ConduitT () Value m ()))+sourceWithSearchResult' ::+ ( MonadIO m+ ) =>+ -- | Twitter Setting+ TWInfo ->+ HTTP.Manager ->+ APIRequest supports (SearchResult [responseType]) ->+ m (SearchResult (C.ConduitT () Value m ())) sourceWithSearchResult' info mgr req = do res <- liftIO $ call info mgr $ relax req- let body = CL.sourceList (res ^. searchResultStatuses) <>- loop (res ^. searchResultSearchMetadata . searchMetadataNextResults)+ let body =+ CL.sourceList (res ^. searchResultStatuses)+ <> loop (res ^. searchResultSearchMetadata . searchMetadataNextResults) return $ res & searchResultStatuses .~ body where origQueryMap = req ^. params . to M.fromList- relax :: APIRequest apiName (SearchResult [responseType])- -> APIRequest apiName (SearchResult [Value])+ relax ::+ APIRequest apiName (SearchResult [responseType]) ->+ APIRequest apiName (SearchResult [Value]) relax = coerce loop Nothing = CL.sourceNull loop (Just nextResultsStr) = do@@ -366,13 +398,17 @@ CL.sourceList (res ^. searchResultStatuses) loop $ res ^. searchResultSearchMetadata . searchMetadataNextResults -sinkJSON :: ( MonadThrow m- ) => C.ConduitT ByteString o m Value+sinkJSON ::+ ( MonadThrow m+ ) =>+ C.ConduitT ByteString o m Value sinkJSON = CA.sinkParser json -sinkFromJSON :: ( FromJSON a- , MonadThrow m- ) => C.ConduitT ByteString o m a+sinkFromJSON ::+ ( FromJSON a+ , MonadThrow m+ ) =>+ C.ConduitT ByteString o m a sinkFromJSON = do v <- sinkJSON case fromJSON v of
Web/Twitter/Conduit/Cursor.hs view
@@ -1,15 +1,16 @@-{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE CPP #-} {-# LANGUAGE EmptyDataDecls #-}+{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE ScopedTypeVariables #-} -module Web.Twitter.Conduit.Cursor- ( CursorKey (..)- , IdsCursorKey- , UsersCursorKey- , ListsCursorKey- , EventsCursorKey- , WithCursor (..)- ) where+module Web.Twitter.Conduit.Cursor (+ CursorKey (..),+ IdsCursorKey,+ UsersCursorKey,+ ListsCursorKey,+ EventsCursorKey,+ WithCursor (..),+) where import Data.Aeson import Data.Text (Text)@@ -18,21 +19,30 @@ -- $setup -- >>> 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" @@ -65,12 +75,16 @@ { previousCursor :: Maybe cursorType , nextCursor :: Maybe cursorType , contents :: [wrapped]- } deriving Show+ }+ deriving (Show) -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)+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
Web/Twitter/Conduit/Lens.hs view
@@ -1,61 +1,59 @@ {-# LANGUAGE RankNTypes #-} -module Web.Twitter.Conduit.Lens- (- -- * 'TT.Response'- TT.Response- , responseStatus- , responseBody- , responseHeaders- -- * 'TT.TwitterErrorMessage'- , TT.TwitterErrorMessage- , twitterErrorMessage- , twitterErrorCode+module Web.Twitter.Conduit.Lens (+ -- * 'TT.Response'+ TT.Response,+ responseStatus,+ responseBody,+ responseHeaders, - -- * 'TT.WithCursor'- , TT.WithCursor- , previousCursor- , nextCursor- , contents+ -- * 'TT.TwitterErrorMessage'+ TT.TwitterErrorMessage,+ twitterErrorMessage,+ twitterErrorCode, - -- * Re-exports- , TT.TwitterError(..)- , TT.CursorKey (..)- , TT.IdsCursorKey- , TT.UsersCursorKey- , TT.ListsCursorKey- ) where+ -- * 'TT.WithCursor'+ TT.WithCursor,+ previousCursor,+ nextCursor,+ contents, + -- * Re-exports+ TT.TwitterError (..),+ TT.CursorKey (..),+ TT.IdsCursorKey,+ TT.UsersCursorKey,+ TT.ListsCursorKey,+) where+ import Control.Lens import Data.Text (Text)-import Network.HTTP.Types (Status, ResponseHeaders)+import Network.HTTP.Types (ResponseHeaders, Status) import qualified Web.Twitter.Conduit.Cursor as TT import qualified Web.Twitter.Conduit.Response as TT -- * Lenses for 'TT.Response' responseStatus :: forall responseType. Lens' (TT.Response responseType) Status-responseStatus afb s = (\b -> s { TT.responseStatus = b }) <$> afb (TT.responseStatus s)+responseStatus afb s = (\b -> s {TT.responseStatus = b}) <$> afb (TT.responseStatus s) responseHeaders :: forall responseType. Lens' (TT.Response responseType) ResponseHeaders-responseHeaders afb s = (\b -> s {TT.responseHeaders = b }) <$> afb (TT.responseHeaders s)+responseHeaders afb s = (\b -> s {TT.responseHeaders = b}) <$> afb (TT.responseHeaders s) responseBody :: forall a b. Lens (TT.Response a) (TT.Response b) a b-responseBody afb s = (\b -> s { TT.responseBody = b }) <$> afb (TT.responseBody s)-+responseBody afb s = (\b -> s {TT.responseBody = b}) <$> afb (TT.responseBody s) -- * Lenses for 'TT.TwitterErrorMessage' twitterErrorCode :: Lens' TT.TwitterErrorMessage Int-twitterErrorCode afb s = (\b -> s { TT.twitterErrorCode = b }) <$> afb (TT.twitterErrorCode s)+twitterErrorCode afb s = (\b -> s {TT.twitterErrorCode = b}) <$> afb (TT.twitterErrorCode s) twitterErrorMessage :: Lens' TT.TwitterErrorMessage Text-twitterErrorMessage afb s = (\b -> s { TT.twitterErrorMessage = b }) <$> afb (TT.twitterErrorMessage s)-+twitterErrorMessage afb s = (\b -> s {TT.twitterErrorMessage = b}) <$> afb (TT.twitterErrorMessage s) -- * Lenses for 'TT.WithCursor' previousCursor :: forall cursorType cursorKey wrapped. Lens' (TT.WithCursor cursorType cursorKey wrapped) (Maybe cursorType)-previousCursor afb s = (\b -> s { TT.previousCursor = b }) <$> afb (TT.previousCursor s)+previousCursor afb s = (\b -> s {TT.previousCursor = b}) <$> afb (TT.previousCursor s) nextCursor :: forall cursorType cursorKey wrapped. Lens' (TT.WithCursor cursorType cursorKey wrapped) (Maybe cursorType)-nextCursor afb s = (\b -> s { TT.nextCursor = b }) <$> afb (TT.nextCursor s)+nextCursor afb s = (\b -> s {TT.nextCursor = b}) <$> afb (TT.nextCursor s) contents :: forall cursorType cursorKey a b. Lens (TT.WithCursor cursorType cursorKey a) (TT.WithCursor cursorType cursorKey b) [a] [b]-contents afb s = (\b -> s { TT.contents = b }) <$> afb (TT.contents s)+contents afb s = (\b -> s {TT.contents = b}) <$> afb (TT.contents s)
Web/Twitter/Conduit/Parameters.hs view
@@ -1,31 +1,35 @@ {-# LANGUAGE OverloadedStrings #-} -module Web.Twitter.Conduit.Parameters- ( UserParam(..)- , UserListParam(..)- , ListParam(..)- , MediaData(..)- , mkUserParam- , mkUserListParam- , mkListParam- ) where+module Web.Twitter.Conduit.Parameters (+ UserParam (..),+ UserListParam (..),+ ListParam (..),+ MediaData (..),+ TweetMode (..),+ mkUserParam,+ mkUserListParam,+ mkListParam,+) where import qualified Data.Text as T import Network.HTTP.Client (RequestBody)-import Web.Twitter.Conduit.Request.Internal (APIQuery, PV(..))+import Web.Twitter.Conduit.Request.Internal (APIQuery, PV (..), ParameterValue (..)) import Web.Twitter.Types -- $setup -- >>> import Web.Twitter.Conduit.Request.Internal +-- Required parameters+ data UserParam = UserIdParam UserId | ScreenNameParam String- deriving (Show, Eq)+ deriving (Show, Eq) data UserListParam = UserIdListParam [UserId] | ScreenNameListParam [String]- deriving (Show, Eq)+ deriving (Show, Eq) data ListParam = ListIdParam Integer | ListNameParam String- deriving (Show, Eq)-data MediaData = MediaFromFile FilePath- | MediaRequestBody FilePath RequestBody+ deriving (Show, Eq)+data MediaData+ = MediaFromFile FilePath+ | MediaRequestBody FilePath RequestBody -- | converts 'UserParam' to 'HT.SimpleQuery'. --@@ -34,7 +38,7 @@ -- >>> makeSimpleQuery . mkUserParam $ ScreenNameParam "thimura" -- [("screen_name","thimura")] mkUserParam :: UserParam -> APIQuery-mkUserParam (UserIdParam uid) = [("user_id", PVInteger uid)]+mkUserParam (UserIdParam uid) = [("user_id", PVInteger uid)] mkUserParam (ScreenNameParam sn) = [("screen_name", PVString . T.pack $ sn)] -- | converts 'UserListParam' to 'HT.SimpleQuery'.@@ -46,7 +50,7 @@ -- >>> makeSimpleQuery . mkUserListParam $ ScreenNameListParam ["thimura", "NikaidouShinku"] -- [("screen_name","thimura,NikaidouShinku")] mkUserListParam :: UserListParam -> APIQuery-mkUserListParam (UserIdListParam uids) = [("user_id", PVIntegerArray uids)]+mkUserListParam (UserIdListParam uids) = [("user_id", PVIntegerArray uids)] mkUserListParam (ScreenNameListParam sns) = [("screen_name", PVStringArray (map T.pack sns))] -- | converts 'ListParam' to 'HT.SimpleQuery'.@@ -56,10 +60,19 @@ -- >>> makeSimpleQuery . mkListParam $ ListNameParam "thimura/haskell" -- [("slug","haskell"),("owner_screen_name","thimura")] mkListParam :: ListParam -> APIQuery-mkListParam (ListIdParam lid) = [("list_id", PVInteger lid)]+mkListParam (ListIdParam lid) = [("list_id", PVInteger lid)] mkListParam (ListNameParam listname) =- [("slug", PVString (T.pack lstName)),- ("owner_screen_name", PVString (T.pack screenName))]+ [ ("slug", PVString (T.pack lstName))+ , ("owner_screen_name", PVString (T.pack screenName))+ ] where (screenName, ln) = span (/= '/') listname lstName = drop 1 ln++-- Optional parameters++data TweetMode = Extended deriving (Show, Eq)++instance ParameterValue TweetMode where+ wrap Extended = PVString "extended"+ unwrap = const Extended
− Web/Twitter/Conduit/ParametersDeprecated.hs
@@ -1,113 +0,0 @@-{-# LANGUAGE DataKinds #-}-{-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE TypeFamilies #-}--module Web.Twitter.Conduit.ParametersDeprecated where--import Data.Text (Text)-import Web.Twitter.Types-import Data.Time.Calendar (Day)-import Web.Twitter.Conduit.Request.Internal-import Control.Lens--count :: (Parameters p, HasParam "count" Integer (SupportParameters p)) => Lens' p (Maybe Integer)-count = rawParam "count"--sinceId :: (Parameters p, HasParam "since_id" Integer (SupportParameters p)) => Lens' p (Maybe Integer)-sinceId = rawParam "since_id"--maxId :: (Parameters p, HasParam "max_id" Integer (SupportParameters p)) => Lens' p (Maybe Integer)-maxId = rawParam "max_id"--page :: (Parameters p, HasParam "page" Integer (SupportParameters p)) => Lens' p (Maybe Integer)-page = rawParam "page"--trimUser :: (Parameters p, HasParam "trim_user" Bool (SupportParameters p)) => Lens' p (Maybe Bool)-trimUser = rawParam "trim_user"--excludeReplies :: (Parameters p, HasParam "exclude_replies" Bool (SupportParameters p)) => Lens' p (Maybe Bool)-excludeReplies = rawParam "exclude_replies"--contributorDetails :: (Parameters p, HasParam "contributor_details" Bool (SupportParameters p)) => Lens' p (Maybe Bool)-contributorDetails = rawParam "contributor_details"--includeEntities :: (Parameters p, HasParam "include_entities" Bool (SupportParameters p)) => Lens' p (Maybe Bool)-includeEntities = rawParam "include_entities"--includeEmail :: (Parameters p, HasParam "include_email" Bool (SupportParameters p)) => Lens' p (Maybe Bool)-includeEmail = rawParam "include_email"--includeUserEntities :: (Parameters p, HasParam "include_user_entities" Bool (SupportParameters p)) => Lens' p (Maybe Bool)-includeUserEntities = rawParam "include_user_entities"--includeRts :: (Parameters p, HasParam "include_rts" Bool (SupportParameters p)) => Lens' p (Maybe Bool)-includeRts = rawParam "include_rts"--includeMyRetweet :: (Parameters p, HasParam "include_my_retweet" Bool (SupportParameters p)) => Lens' p (Maybe Bool)-includeMyRetweet = rawParam "include_my_retweet"--includeExtAltText :: (Parameters p, HasParam "include_ext_alt_text" Bool (SupportParameters p)) => Lens' p (Maybe Bool)-includeExtAltText = rawParam "include_ext_alt_text"--inReplyToStatusId :: (Parameters p, HasParam "in_reply_to_status_id" Integer (SupportParameters p)) => Lens' p (Maybe Integer)-inReplyToStatusId = rawParam "in_reply_to_status_id"--displayCoordinates :: (Parameters p, HasParam "display_coordinates" Bool (SupportParameters p)) => Lens' p (Maybe Bool)-displayCoordinates = rawParam "display_coordinates"--possiblySensitive :: (Parameters p, HasParam "possibly_sensitive" Bool (SupportParameters p)) => Lens' p (Maybe Bool)-possiblySensitive = rawParam "possibly_sensitive"--lang :: (Parameters p, HasParam "lang" Text (SupportParameters p)) => Lens' p (Maybe Text)-lang = rawParam "lang"--language :: (Parameters p, HasParam "language" Text (SupportParameters p)) => Lens' p (Maybe Text)-language = rawParam "language"--locale :: (Parameters p, HasParam "locale" Text (SupportParameters p)) => Lens' p (Maybe Text)-locale = rawParam "locale"--filterLevel :: (Parameters p, HasParam "filter_level" Text (SupportParameters p)) => Lens' p (Maybe Text)-filterLevel = rawParam "filter_level"--stallWarnings :: (Parameters p, HasParam "stall_warnings" Bool (SupportParameters p)) => Lens' p (Maybe Bool)-stallWarnings = rawParam "stall_warnings"--replies :: (Parameters p, HasParam "replies" Text (SupportParameters p)) => Lens' p (Maybe Text)-replies = rawParam "replies"--until :: (Parameters p, HasParam "until" Day (SupportParameters p)) => Lens' p (Maybe Day)-until = rawParam "until"--skipStatus :: (Parameters p, HasParam "skip_status" Bool (SupportParameters p)) => Lens' p (Maybe Bool)-skipStatus = rawParam "skip_status"--follow :: (Parameters p, HasParam "follow" Bool (SupportParameters p)) => Lens' p (Maybe Bool)-follow = rawParam "follow"--map :: (Parameters p, HasParam "map" Bool (SupportParameters p)) => Lens' p (Maybe Bool)-map = rawParam "map"--mediaIds :: (Parameters p, HasParam "media_ids" [Integer] (SupportParameters p)) => Lens' p (Maybe [Integer])-mediaIds = rawParam "media_ids"--description :: (Parameters p, HasParam "description" Text (SupportParameters p)) => Lens' p (Maybe Text)-description = rawParam "description"--name :: (Parameters p, HasParam "name" Text (SupportParameters p)) => Lens' p (Maybe Text)-name = rawParam "name"--profileLinkColor :: (Parameters p, HasParam "profile_link_color" Text (SupportParameters p)) => Lens' p (Maybe Text)-profileLinkColor = rawParam "profile_link_color"--location :: (Parameters p, HasParam "location" Text (SupportParameters p)) => Lens' p (Maybe Text)-location = rawParam "location"--url :: (Parameters p, HasParam "url" URIString (SupportParameters p)) => Lens' p (Maybe URIString)-url = rawParam "url"--fullText :: (Parameters p, HasParam "full_text" Bool (SupportParameters p)) => Lens' p (Maybe Bool)-fullText = rawParam "full_text"--with :: (Parameters p, HasParam "with" Text (SupportParameters p)) => Lens' p (Maybe Text)-with = rawParam "with"
Web/Twitter/Conduit/Request.hs view
@@ -3,10 +3,10 @@ {-# LANGUAGE Rank2Types #-} {-# LANGUAGE TypeFamilies #-} -module Web.Twitter.Conduit.Request- ( HasParam- , APIRequest(..)- ) where+module Web.Twitter.Conduit.Request (+ HasParam,+ APIRequest (..),+) where import Data.Aeson import GHC.TypeLits (Symbol)@@ -51,22 +51,23 @@ -- [("max_id",PVInteger {unPVInteger = 1234567890})] data APIRequest (supports :: [Param Symbol *]) responseType = APIRequest- { _method :: HT.Method- , _url :: String- , _params :: APIQuery- }+ { _method :: HT.Method+ , _url :: String+ , _params :: APIQuery+ } | APIRequestMultipart- { _method :: HT.Method- , _url :: String- , _params :: APIQuery- , _part :: [Part]- }+ { _method :: HT.Method+ , _url :: String+ , _params :: APIQuery+ , _part :: [Part]+ } | APIRequestJSON- { _method :: HT.Method- , _url :: String- , _params :: APIQuery- , _body :: Value- }+ { _method :: HT.Method+ , _url :: String+ , _params :: APIQuery+ , _body :: Value+ }+ instance Parameters (APIRequest supports responseType) where type SupportParameters (APIRequest supports responseType) = supports
Web/Twitter/Conduit/Request/Internal.hs view
@@ -37,12 +37,12 @@ type APIQueryItem = (ByteString, PV) data PV- = PVInteger { unPVInteger :: Integer }- | PVBool { unPVBool :: Bool }- | PVString { unPVString :: Text }- | PVIntegerArray { unPVIntegerArray :: [Integer] }- | PVStringArray { unPVStringArray :: [Text] }- | PVDay { unPVDay :: Day }+ = PVInteger {unPVInteger :: Integer}+ | PVBool {unPVBool :: Bool}+ | PVString {unPVString :: Text}+ | PVIntegerArray {unPVIntegerArray :: [Integer]}+ | PVStringArray {unPVStringArray :: [Text]}+ | PVDay {unPVDay :: Day} deriving (Show, Eq) class Parameters req where@@ -84,24 +84,29 @@ paramValueBS (PVDay day) = S8.pack . show $ day rawParam ::- (Parameters p, ParameterValue a)- => ByteString -- ^ key- -> Lens' p (Maybe a)+ (Parameters p, ParameterValue a) =>+ -- | key+ ByteString ->+ Lens' p (Maybe a) rawParam key = lens getter setter- where- getter = preview $ params . to (lookup key) . _Just . to unwrap- setter = flip (over params . replace key)- replace k (Just v) = ((k, wrap v):) . dropAssoc k- replace k Nothing = dropAssoc k- dropAssoc k = filter ((/= k) . fst)+ where+ getter = preview $ params . to (lookup key) . _Just . to unwrap+ setter = flip (over params . replace key)+ replace k (Just v) = ((k, wrap v) :) . dropAssoc k+ replace k Nothing = dropAssoc k+ dropAssoc k = filter ((/= k) . fst) -instance ( Parameters req- , ParameterValue a- , KnownSymbol label- , HasParam label a (SupportParameters req)- , Functor f- , lens ~ ((Maybe a -> f (Maybe a)) -> req -> f req)) =>- IsLabel label lens where+{- ORMOLU_DISABLE -}+instance+ ( Parameters req+ , ParameterValue a+ , KnownSymbol label+ , HasParam label a (SupportParameters req)+ , Functor f+ , lens ~ ((Maybe a -> f (Maybe a)) -> req -> f req)+ ) =>+ IsLabel label lens where+ #if MIN_VERSION_base(4, 10, 0) fromLabel = rawParam key #else@@ -109,3 +114,4 @@ #endif where key = S8.pack (symbolVal (Proxy :: Proxy label))+{- ORMOLU_ENABLE -}
Web/Twitter/Conduit/Response.hs view
@@ -2,23 +2,24 @@ {-# LANGUAGE DeriveTraversable #-} {-# LANGUAGE OverloadedStrings #-} -module Web.Twitter.Conduit.Response- ( Response (..)- , TwitterError (..)- , TwitterErrorMessage (..)- ) where+module Web.Twitter.Conduit.Response (+ Response (..),+ TwitterError (..),+ TwitterErrorMessage (..),+) where import Control.Exception import Data.Aeson import Data.Data import qualified Data.Text as T-import Network.HTTP.Types (Status, ResponseHeaders, Status, ResponseHeaders)+import Network.HTTP.Types (ResponseHeaders, Status) data Response responseType = Response { responseStatus :: Status , responseHeaders :: ResponseHeaders , responseBody :: responseType- } deriving (Show, Eq, Typeable, Functor, Foldable, Traversable)+ }+ deriving (Show, Eq, Typeable, Functor, Foldable, Traversable) data TwitterError = FromJSONError String@@ -35,15 +36,16 @@ data TwitterErrorMessage = TwitterErrorMessage { twitterErrorCode :: Int , twitterErrorMessage :: T.Text- } deriving (Show, Data, Typeable)+ }+ deriving (Show, Data, Typeable) instance Eq TwitterErrorMessage where- TwitterErrorMessage { twitterErrorCode = a } == TwitterErrorMessage { twitterErrorCode = b }- = a == b+ TwitterErrorMessage {twitterErrorCode = a} == TwitterErrorMessage {twitterErrorCode = b} =+ a == b instance Ord TwitterErrorMessage where- compare TwitterErrorMessage { twitterErrorCode = a } TwitterErrorMessage { twitterErrorCode = b }- = a `compare` b+ compare TwitterErrorMessage {twitterErrorCode = a} TwitterErrorMessage {twitterErrorCode = b} =+ a `compare` b instance Enum TwitterErrorMessage where fromEnum = twitterErrorCode@@ -52,6 +54,6 @@ instance FromJSON TwitterErrorMessage where parseJSON (Object o) = TwitterErrorMessage- <$> o .: "code"- <*> o .: "message"+ <$> o .: "code"+ <*> o .: "message" parseJSON v = fail $ "unexpected: " ++ show v
Web/Twitter/Conduit/Status.hs view
@@ -2,47 +2,47 @@ {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE TypeOperators #-} -module Web.Twitter.Conduit.Status- (- -- * 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+module Web.Twitter.Conduit.Status (+ -- * Timelines+ StatusesMentionsTimeline,+ mentionsTimeline,+ StatusesUserTimeline,+ userTimeline,+ StatusesHomeTimeline,+ homeTimeline,+ StatusesRetweetsOfMe,+ retweetsOfMe, -import Prelude hiding ( lookup )+ -- * 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 Web.Twitter.Conduit.Parameters import Web.Twitter.Conduit.Request import Web.Twitter.Conduit.Request.Internal-import Web.Twitter.Conduit.Parameters import Web.Twitter.Types+import Prelude hiding (lookup) +import Data.Default import qualified Data.Text as T import Network.HTTP.Client.MultipartFormData-import Data.Default -- $setup -- >>> :set -XOverloadedStrings -XOverloadedLabels@@ -62,16 +62,17 @@ -- 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-type StatusesMentionsTimeline = '[- "count" ':= Integer- , "since_id" ':= Integer- , "max_id" ':= Integer- , "trim_user" ':= Bool- , "contributor_details" ':= Bool- , "include_entities" ':= Bool- , "tweet_mode" ':= T.Text- ] +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':@@ -86,17 +87,18 @@ -- 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" ':= T.Text- ] +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':@@ -111,17 +113,18 @@ -- 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" ':= T.Text- ] +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':@@ -136,16 +139,17 @@ -- 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" ':= T.Text- ] +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@@ -162,12 +166,15 @@ -- 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- ]+ 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':@@ -182,15 +189,17 @@ -- 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" ':= T.Text- ]+ 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':@@ -203,12 +212,14 @@ -- 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" ':= T.Text- ]+ 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'. --@@ -224,17 +235,19 @@ -- 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"-type StatusesUpdate = '[- "in_reply_to_status_id" ':= Integer- -- , "lat_long"- -- , "place_id"- , "display_coordinates" ':= Bool- , "trim_user" ':= Bool- , "media_ids" ':= [Integer]- , "tweet_mode" ':= T.Text- ]+ 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':@@ -247,11 +260,14 @@ -- 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- ]+ 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':@@ -262,24 +278,26 @@ -- -- >>> 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 ::+ 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-type StatusesUpdateWithMedia = '[- "possibly_sensitive" ':= Bool- , "in_reply_to_status_id" ':= Integer- -- , "lat_long"- -- , "place_id"- , "display_coordinates" ':= Bool- , "tweet_mode" ':= T.Text- ] +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':@@ -296,9 +314,10 @@ -- 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" ':= T.Text- ]++type StatusesLookup =+ '[ "include_entities" ':= Bool+ , "trim_user" ':= Bool+ , "map" ':= Bool+ , "tweet_mode" ':= TweetMode+ ]
Web/Twitter/Conduit/Stream.hs view
@@ -1,34 +1,33 @@ {-# LANGUAGE ConstraintKinds #-} {-# LANGUAGE DataKinds #-}-{-# LANGUAGE TypeOperators #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE TypeOperators #-} -module Web.Twitter.Conduit.Stream- (- -- * StreamingAPI- Userstream- , userstream- , StatusesFilter- , FilterParameter (..)- , statusesFilter- , statusesFilterByFollow- , statusesFilterByTrack- -- , statusesFilterByLocation- -- , statusesSample- -- , statusesFirehose- -- , sitestream- -- , sitestream'- , stream- , stream'- ) where+module Web.Twitter.Conduit.Stream (+ -- * StreamingAPI+ Userstream,+ userstream,+ StatusesFilter,+ FilterParameter (..),+ statusesFilter,+ statusesFilterByFollow,+ statusesFilterByTrack,+ -- , statusesFilterByLocation+ -- , statusesSample+ -- , statusesFirehose+ -- , sitestream+ -- , sitestream'+ stream,+ stream',+) where -import Web.Twitter.Conduit.Types import Web.Twitter.Conduit.Base-import Web.Twitter.Types import Web.Twitter.Conduit.Request import Web.Twitter.Conduit.Request.Internal import Web.Twitter.Conduit.Response+import Web.Twitter.Conduit.Types+import Web.Twitter.Types import Control.Monad.Catch import Control.Monad.IO.Class@@ -43,25 +42,25 @@ import qualified Network.HTTP.Conduit as HTTP stream ::- ( MonadResource m- , FromJSON responseType- , MonadThrow m- )- => TWInfo- -> HTTP.Manager- -> APIRequest apiName responseType- -> m (C.ConduitM () responseType m ())+ ( MonadResource m+ , FromJSON responseType+ , MonadThrow m+ ) =>+ TWInfo ->+ HTTP.Manager ->+ APIRequest apiName responseType ->+ m (C.ConduitM () responseType m ()) stream = stream' stream' ::- ( MonadResource m- , FromJSON value- , MonadThrow m- )- => TWInfo- -> HTTP.Manager- -> APIRequest apiName responseType- -> m (C.ConduitM () value m ())+ ( MonadResource m+ , FromJSON value+ , MonadThrow m+ ) =>+ TWInfo ->+ HTTP.Manager ->+ APIRequest apiName responseType ->+ m (C.ConduitM () value m ()) stream' info mgr req = do rsrc <- getResponse info mgr =<< liftIO (makeRequest req) return $ responseBody rsrc C..| CL.sequence sinkFromJSONIgnoreSpaces@@ -70,16 +69,17 @@ userstream :: APIRequest Userstream StreamingAPI userstream = APIRequest "GET" "https://userstream.twitter.com/1.1/user.json" []-type Userstream = '[- "language" ':= T.Text- , "filter_level" ':= T.Text- , "stall_warnings" ':= Bool- , "replies" ':= T.Text- ]+type Userstream =+ '[ "language" ':= T.Text+ , "filter_level" ':= T.Text+ , "stall_warnings" ':= Bool+ , "replies" ':= T.Text+ ] -- https://dev.twitter.com/streaming/overview/request-parameters-data FilterParameter = Follow [UserId]- | Track [T.Text]+data FilterParameter+ = Follow [UserId]+ | Track [T.Text] -- | Returns statuses/filter.json API query data. --@@ -110,11 +110,14 @@ -- -- >>> statusesFilterByTrack "haskell" -- APIRequest "POST" "https://stream.twitter.com/1.1/statuses/filter.json" [("track","haskell")]-statusesFilterByTrack :: T.Text -- ^ keyword- -> APIRequest StatusesFilter StreamingAPI+statusesFilterByTrack ::+ -- | keyword+ T.Text ->+ APIRequest StatusesFilter StreamingAPI statusesFilterByTrack keyword = statusesFilter [Track [keyword]]-type StatusesFilter = '[- "language" ':= T.Text- , "filter_level" ':= T.Text- , "stall_warnings" ':= Bool- ]++type StatusesFilter =+ '[ "language" ':= T.Text+ , "filter_level" ':= T.Text+ , "stall_warnings" ':= Bool+ ]
Web/Twitter/Conduit/Types.hs view
@@ -1,36 +1,41 @@ {-# LANGUAGE DeriveDataTypeable #-}-module Web.Twitter.Conduit.Types- ( TWToken (..)- , TWInfo (..)- , twitterOAuth- , setCredential- ) where +module Web.Twitter.Conduit.Types (+ TWToken (..),+ TWInfo (..),+ twitterOAuth,+ setCredential,+) where+ import Data.Default import Data.Typeable (Typeable)-import Web.Authenticate.OAuth import Network.HTTP.Conduit+import Web.Authenticate.OAuth data TWToken = TWToken { twOAuth :: OAuth , twCredential :: Credential- } deriving (Show, Read, Eq, Typeable)+ }+ deriving (Show, Read, Eq, Typeable) instance Default TWToken where def = TWToken twitterOAuth (Credential []) data TWInfo = TWInfo { twToken :: TWToken , twProxy :: Maybe Proxy- } deriving (Show, Read, Eq, Typeable)+ }+ deriving (Show, Read, Eq, Typeable) instance Default TWInfo where- def = TWInfo- { twToken = def- , twProxy = Nothing- }+ def =+ TWInfo+ { twToken = def+ , twProxy = Nothing+ } twitterOAuth :: OAuth twitterOAuth =- def { oauthServerName = "twitter"+ def+ { oauthServerName = "twitter" , oauthRequestUri = "https://api.twitter.com/oauth/request_token" , oauthAccessTokenUri = "https://api.twitter.com/oauth/access_token" , oauthAuthorizeUri = "https://api.twitter.com/oauth/authorize"@@ -52,8 +57,8 @@ -- >>> twProxy twinfo2 == Just proxy -- True setCredential :: OAuth -> Credential -> TWInfo -> TWInfo-setCredential oa cred env- = TWInfo- { twToken = TWToken oa cred- , twProxy = twProxy env- }+setCredential oa cred env =+ TWInfo+ { twToken = TWToken oa cred+ , twProxy = twProxy env+ }
sample/Setup.hs view
@@ -1,2 +1,3 @@ import Distribution.Simple+ main = defaultMain
sample/common/Common.hs view
@@ -1,6 +1,6 @@-{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE ConstraintKinds #-} {-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE OverloadedStrings #-} module Common where @@ -21,14 +21,16 @@ consumerSecret <- getEnv' "OAUTH_CONSUMER_SECRET" accessToken <- getEnv' "OAUTH_ACCESS_TOKEN" accessSecret <- getEnv' "OAUTH_ACCESS_SECRET"- let oauth = twitterOAuth- { oauthConsumerKey = consumerKey- , oauthConsumerSecret = consumerSecret- }- cred = Credential- [ ("oauth_token", accessToken)- , ("oauth_token_secret", accessSecret)- ]+ let oauth =+ twitterOAuth+ { oauthConsumerKey = consumerKey+ , oauthConsumerSecret = consumerSecret+ }+ cred =+ Credential+ [ ("oauth_token", accessToken)+ , ("oauth_token_secret", accessSecret)+ ] return (oauth, cred) where getEnv' = (S8.pack <$>) . getEnv@@ -36,18 +38,19 @@ getProxyEnv :: IO (Maybe Proxy) getProxyEnv = do env <- M.fromList . over (mapped . _1) CI.mk <$> getEnvironment- let u = M.lookup "https_proxy" env <|>- M.lookup "http_proxy" env <|>- M.lookup "proxy" env >>= URI.parseURI >>= URI.uriAuthority+ let u =+ M.lookup "https_proxy" env+ <|> M.lookup "http_proxy" env+ <|> M.lookup "proxy" env >>= URI.parseURI >>= URI.uriAuthority return $ Proxy <$> (S8.pack . URI.uriRegName <$> u) <*> (parsePort . URI.uriPort <$> u) where parsePort :: String -> Int- parsePort [] = 8080- parsePort (':':xs) = read xs- parsePort xs = error $ "port number parse failed " ++ xs+ parsePort [] = 8080+ parsePort (':' : xs) = read xs+ parsePort xs = error $ "port number parse failed " ++ xs getTWInfoFromEnv :: IO TWInfo getTWInfoFromEnv = do pr <- getProxyEnv (oa, cred) <- getOAuthTokens- return $ (setCredential oa cred def) { twProxy = pr }+ return $ (setCredential oa cred def) {twProxy = pr}
sample/fav.hs view
@@ -2,8 +2,8 @@ module Main where -import Web.Twitter.Conduit import Common+import Web.Twitter.Conduit import Control.Lens import System.Environment
sample/oauth_callback.hs view
@@ -7,19 +7,19 @@ module Main where -import Web.Scotty-import qualified Network.HTTP.Types as HT-import Web.Twitter.Conduit-import qualified Web.Authenticate.OAuth as OA-import qualified Data.Text.Lazy as LT+import Control.Monad.IO.Class import qualified Data.ByteString as S import qualified Data.ByteString.Char8 as S8+import Data.IORef import qualified Data.Map as M import Data.Maybe-import Data.IORef-import Control.Monad.IO.Class+import qualified Data.Text.Lazy as LT+import qualified Network.HTTP.Types as HT import System.Environment import System.IO.Unsafe+import qualified Web.Authenticate.OAuth as OA+import Web.Scotty+import Web.Twitter.Conduit callback :: String callback = "http://localhost:3000/callback"@@ -30,10 +30,10 @@ consumerSecret <- getEnv "OAUTH_CONSUMER_SECRET" return $ twitterOAuth- { oauthConsumerKey = S8.pack consumerKey- , oauthConsumerSecret = S8.pack consumerSecret- , oauthCallback = Just $ S8.pack callback- }+ { oauthConsumerKey = S8.pack consumerKey+ , oauthConsumerSecret = S8.pack consumerSecret+ , oauthCallback = Just $ S8.pack callback+ } type OAuthToken = S.ByteString @@ -43,8 +43,8 @@ takeCredential :: OAuthToken -> IORef (M.Map OAuthToken Credential) -> IO (Maybe Credential) takeCredential k ioref = atomicModifyIORef ioref $ \m ->- let (res, newm) = M.updateLookupWithKey (\_ _ -> Nothing) k m in- (newm, res)+ let (res, newm) = M.updateLookupWithKey (\_ _ -> Nothing) k m+ in (newm, res) storeCredential :: OAuthToken -> Credential -> IORef (M.Map OAuthToken Credential) -> IO () storeCredential k cred ioref =@@ -59,7 +59,8 @@ makeMessage :: OAuth -> Credential -> S.ByteString makeMessage tokens (Credential cred) =- S8.intercalate "\n"+ S8.intercalate+ "\n" [ "export OAUTH_CONSUMER_KEY=\"" <> oauthConsumerKey tokens <> "\"" , "export OAUTH_CONSUMER_SECRET=\"" <> oauthConsumerSecret tokens <> "\"" , "export OAUTH_ACCESS_TOKEN=\"" <> fromMaybe "" (lookup "oauth_token" cred) <> "\""@@ -80,7 +81,6 @@ let message = makeMessage tokens accessTokens liftIO . S8.putStrLn $ message text . LT.pack . S8.unpack $ message- Nothing -> do status HT.status404 text "temporary token is not found"
sample/oauth_pin.hs view
@@ -1,5 +1,5 @@-{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE OverloadedStrings #-} -- Example: -- $ export OAUTH_CONSUMER_KEY="your consumer key"@@ -8,12 +8,12 @@ module Main where -import Web.Twitter.Conduit hiding (lookup)-import Web.Authenticate.OAuth as OA import qualified Data.ByteString.Char8 as S8 import Data.Maybe import System.Environment import System.IO (hFlush, stdout)+import Web.Authenticate.OAuth as OA+import Web.Twitter.Conduit getTokens :: IO OAuth getTokens = do@@ -21,14 +21,16 @@ consumerSecret <- getEnv "OAUTH_CONSUMER_SECRET" return $ twitterOAuth- { oauthConsumerKey = S8.pack consumerKey- , oauthConsumerSecret = S8.pack consumerSecret- , oauthCallback = Just "oob"- }+ { oauthConsumerKey = S8.pack consumerKey+ , oauthConsumerSecret = S8.pack consumerSecret+ , oauthCallback = Just "oob"+ } -authorize :: OAuth -- ^ OAuth Consumer key and secret- -> Manager- -> IO Credential+authorize ::+ -- | OAuth Consumer key and secret+ OAuth ->+ Manager ->+ IO Credential authorize oauth mgr = do cred <- OA.getTemporaryCredential oauth mgr let url = OA.authorizeUrl oauth cred
sample/oslist.hs view
@@ -1,8 +1,9 @@ {-# LANGUAGE OverloadedStrings #-}+ module Main where -import Web.Twitter.Conduit import Common+import Web.Twitter.Conduit import Data.Conduit import qualified Data.Conduit.List as CL
sample/post.hs view
@@ -1,8 +1,9 @@ {-# LANGUAGE OverloadedStrings #-}+ module Main where -import Web.Twitter.Conduit import Common+import Web.Twitter.Conduit import qualified Data.Text as T import qualified Data.Text.IO as T
sample/postWithMedia.hs view
@@ -1,8 +1,9 @@ {-# LANGUAGE OverloadedStrings #-}+ module Main where -import Web.Twitter.Conduit import Common+import Web.Twitter.Conduit import qualified Data.Text as T import System.Environment
sample/postWithMultipleMedia.hs view
@@ -1,11 +1,11 @@-{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE OverloadedLabels #-}+{-# LANGUAGE OverloadedStrings #-} module Main where +import Common import Web.Twitter.Conduit import Web.Twitter.Types.Lens-import Common import Control.Lens import Control.Monad@@ -16,7 +16,7 @@ main :: IO () main = do- (status:filepathList) <- getArgs+ (status : filepathList) <- getArgs when (length filepathList > 4) $ do hPutStrLn stderr $ "You can upload upto 4 images in a single tweet, but we got " ++ show (length filepathList) ++ " images. abort." exitFailure@@ -28,5 +28,5 @@ putStrLn $ "Upload completed: media_id: " ++ ret ^. uploadedMediaId . to show ++ ", filepath: " ++ filepath return ret putStrLn $ "Post message: " ++ status- res <- call twInfo mgr $ statusesUpdate (T.pack status) & #media_ids ?~ (uploadedMediaList ^.. traversed . uploadedMediaId)+ res <- call twInfo mgr $ statusesUpdate (T.pack status) & #media_ids ?~ (uploadedMediaList ^.. traversed . uploadedMediaId) print res
sample/search.hs view
@@ -1,9 +1,10 @@ {-# LANGUAGE OverloadedStrings #-}+ module Main where +import Common import Web.Twitter.Conduit import Web.Twitter.Types.Lens-import Common import Control.Lens import qualified Data.Text as T
sample/searchSource.hs view
@@ -1,9 +1,10 @@ {-# LANGUAGE OverloadedStrings #-}+ module Main where +import Common import Web.Twitter.Conduit import Web.Twitter.Types.Lens-import Common import Control.Lens import Data.Conduit
sample/simple.hs view
@@ -1,6 +1,6 @@-{-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE OverloadedLabels #-} {-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE OverloadedLabels #-}+{-# LANGUAGE OverloadedStrings #-} module Main where @@ -17,15 +17,19 @@ import qualified Web.Authenticate.OAuth as OA tokens :: OAuth-tokens = twitterOAuth- { oauthConsumerKey = error "You MUST specify oauthConsumerKey parameter."- , oauthConsumerSecret = error "You MUST specify oauthConsumerSecret parameter."- }+tokens =+ twitterOAuth+ { oauthConsumerKey = error "You MUST specify oauthConsumerKey parameter."+ , oauthConsumerSecret = error "You MUST specify oauthConsumerSecret parameter."+ } -authorize :: OAuth -- ^ OAuth Consumer key and secret- -> (String -> IO String) -- ^ PIN prompt- -> Manager- -> IO Credential+authorize ::+ -- | OAuth Consumer key and secret+ OAuth ->+ -- | PIN prompt+ (String -> IO String) ->+ Manager ->+ IO Credential authorize oauth getPIN mgr = do cred <- OA.getTemporaryCredential oauth mgr let url = OA.authorizeUrl oauth cred@@ -49,15 +53,17 @@ mgr <- newManager tlsManagerSettings twInfo <- getTWInfo mgr putStrLn $ "# your home timeline (up to 800 tweets):"- runConduit $ sourceWithMaxId twInfo mgr (statusesHomeTimeline & #count ?~ 200)- .| CL.isolate 800- .| CL.mapM_- (\status -> do- T.putStrLn $- T.concat- [ T.pack . show $ status ^. statusId- , ": "- , status ^. statusUser . userScreenName- , ": "- , status ^. statusText- ])+ runConduit $+ sourceWithMaxId twInfo mgr (statusesHomeTimeline & #count ?~ 200)+ .| CL.isolate 800+ .| CL.mapM_+ ( \status -> do+ T.putStrLn $+ T.concat+ [ T.pack . show $ status ^. statusId+ , ": "+ , status ^. statusUser . userScreenName+ , ": "+ , status ^. statusText+ ]+ )
sample/userstream.hs view
@@ -1,9 +1,9 @@ {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE PatternGuards #-} +import Common import Web.Twitter.Conduit import Web.Twitter.Types.Lens-import Common import Control.Lens import Control.Monad@@ -39,30 +39,35 @@ runConduit $ src .| CL.mapM_ (liftIO . printTL) showStatus :: AsStatus s => s -> T.Text-showStatus s = T.concat [ s ^. user . userScreenName- , ":"- , s ^. text- ]+showStatus s =+ T.concat+ [ s ^. user . userScreenName+ , ":"+ , s ^. text+ ] printTL :: StreamingAPI -> IO () printTL (SStatus s) = T.putStrLn . showStatus $ s-printTL (SRetweetedStatus s) = T.putStrLn $ T.concat [ s ^. user . userScreenName- , ": RT @"- , showStatus (s ^. rsRetweetedStatus)- ]+printTL (SRetweetedStatus s) =+ T.putStrLn $+ T.concat+ [ s ^. user . userScreenName+ , ": RT @"+ , showStatus (s ^. rsRetweetedStatus)+ ] printTL (SEvent event)- | (event^.evEvent) == "favorite" || (event^.evEvent) == "unfavorite",- Just (ETStatus st) <- event ^. evTargetObject = do- let (fromUser, fromIcon) = evUserInfo (event^.evSource)- (toUser, _toIcon) = evUserInfo (event^.evTarget)- evUserInfo (ETUser u) = (u ^. userScreenName, u ^. userProfileImageURL)- evUserInfo _ = ("", Nothing)- header = T.concat [ event ^. evEvent, "[", fromUser, " -> ", toUser, "]"]- T.putStrLn $ T.concat [ header, " :: ", showStatus st ]- icon <- case fromIcon of- Just iconUrl -> Just <$> fetchIcon (T.unpack fromUser) (T.unpack iconUrl)- Nothing -> return Nothing- notifySend header (showStatus st) icon+ | (event ^. evEvent) == "favorite" || (event ^. evEvent) == "unfavorite"+ , Just (ETStatus st) <- event ^. evTargetObject = do+ let (fromUser, fromIcon) = evUserInfo (event ^. evSource)+ (toUser, _toIcon) = evUserInfo (event ^. evTarget)+ evUserInfo (ETUser u) = (u ^. userScreenName, u ^. userProfileImageURL)+ evUserInfo _ = ("", Nothing)+ header = T.concat [event ^. evEvent, "[", fromUser, " -> ", toUser, "]"]+ T.putStrLn $ T.concat [header, " :: ", showStatus st]+ icon <- case fromIcon of+ Just iconUrl -> Just <$> fetchIcon (T.unpack fromUser) (T.unpack iconUrl)+ Nothing -> return Nothing+ notifySend header (showStatus st) icon printTL s = print s notifySend :: T.Text -> T.Text -> Maybe FilePath -> IO ()@@ -70,9 +75,12 @@ let ic = maybe [] (\i -> ["-i", i]) icon void $ rawSystem "notify-send" $ [T.unpack header, T.unpack content] ++ ic -fetchIcon :: String -- ^ screen name- -> String -- ^ icon url- -> IO String+fetchIcon ::+ -- | screen name+ String ->+ -- | icon url+ String ->+ IO String fetchIcon sn url = do ipath <- iconPath let fname = ipath </> sn ++ "__" ++ takeFileName url
tests/ApiSpec.hs view
@@ -1,5 +1,5 @@-{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE CPP #-}+{-# LANGUAGE OverloadedStrings #-} module ApiSpec where @@ -9,7 +9,7 @@ import Network.HTTP.Conduit import System.IO.Unsafe import TestUtils-import Web.Twitter.Conduit (call, sourceWithCursor, TWInfo)+import Web.Twitter.Conduit (TWInfo, call, sourceWithCursor) import Web.Twitter.Conduit.Api import Web.Twitter.Conduit.Lens import qualified Web.Twitter.Conduit.Parameters as Param@@ -27,6 +27,7 @@ spec :: Spec spec = do unit+ #ifdef RUN_INTEGRATED_TEST integrated #endif
tests/BaseSpec.hs view
@@ -2,8 +2,8 @@ module BaseSpec where -import Web.Twitter.Conduit.Response import Web.Twitter.Conduit.Base+import Web.Twitter.Conduit.Response import Control.Applicative import Control.Lens@@ -80,9 +80,10 @@ data TestJSON = TestJSON { testField :: T.Text , testStatus :: Int- } deriving (Show, Eq)+ }+ deriving (Show, Eq) instance FromJSON TestJSON where parseJSON (Object o) = TestJSON <$> o .: "test"- <*> o .: "status"+ <*> o .: "status" parseJSON v = fail $ "Unexpected: " ++ show v
tests/StatusSpec.hs view
@@ -1,6 +1,6 @@-{-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE OverloadedLabels #-} {-# LANGUAGE CPP #-}+{-# LANGUAGE OverloadedLabels #-}+{-# LANGUAGE OverloadedStrings #-} module StatusSpec where @@ -10,13 +10,13 @@ import Data.Time import Network.HTTP.Conduit import System.IO.Unsafe-import Web.Twitter.Conduit (call, accountVerifyCredentials, sourceWithMaxId, TWInfo)+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 TestUtils import Test.Hspec+import TestUtils twInfo :: TWInfo twInfo = unsafePerformIO getTWInfo@@ -32,6 +32,7 @@ spec :: Spec spec = do unit+ #ifdef RUN_INTEGRATED_TEST integrated #endif@@ -42,7 +43,7 @@ integrated :: Spec integrated = do describe "mentionsTimeline" $ do- it "returns the 20 most resent mentions for user" $ 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@@ -55,13 +56,16 @@ 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 <-+ 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+ let src =+ sourceWithMaxId twInfo mgr $+ userTimeline (Param.ScreenNameParam "thimura") & #count ?~ 200 tl <- src $$ CL.isolate 600 =$ CL.consume length tl `shouldSatisfy` (== 600) @@ -95,3 +99,9 @@ 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 & metrics. More to come! \128073 https://t.co/rDE48yNiSw https://t.co/oFsvkpnDhS"
tests/TestUtils.hs view
@@ -12,14 +12,16 @@ consumerSecret <- getEnv' "OAUTH_CONSUMER_SECRET" accessToken <- getEnv' "OAUTH_ACCESS_TOKEN" accessSecret <- getEnv' "OAUTH_ACCESS_SECRET"- let oauth = twitterOAuth- { oauthConsumerKey = consumerKey- , oauthConsumerSecret = consumerSecret- }- cred = Credential- [ ("oauth_token", accessToken)- , ("oauth_token_secret", accessSecret)- ]+ let oauth =+ twitterOAuth+ { oauthConsumerKey = consumerKey+ , oauthConsumerSecret = consumerSecret+ }+ cred =+ Credential+ [ ("oauth_token", accessToken)+ , ("oauth_token_secret", accessSecret)+ ] return (oauth, cred) where getEnv' = (S8.pack <$>) . getEnv
tests/doctests.hs view
@@ -1,6 +1,6 @@ module Main where -import Build_doctests (flags, pkgs, module_sources)+import Build_doctests (flags, module_sources, pkgs) import Data.Foldable (traverse_) import Test.DocTest (doctest)
− tests/hlint.hs
@@ -1,20 +0,0 @@-module Main where--import Control.Monad-import Data.Maybe-import Language.Haskell.HLint-import System.Environment-import System.Exit--main :: IO ()-main = do- args <- getArgs- cabalMacros <- getCabalMacrosPath- hints <- hlint $ ["Web", "--cpp-define=HLINT", "--cpp-ansi", "--cpp-file=" ++ cabalMacros] ++ args- unless (null hints) exitFailure--getCabalMacrosPath :: IO FilePath-getCabalMacrosPath = do- env <- getEnvironment- let dist = fromMaybe "dist" $ lookup "HASKELL_DIST_DIR" env- return $ dist ++ "/build/autogen/cabal_macros.h"
twitter-conduit.cabal view
@@ -1,156 +1,138 @@-name: twitter-conduit-version: 0.5.1-license: BSD3-license-file: LICENSE-author: HATTORI Hiroki, Hideyuki Tanaka, Takahiro HIMURA-maintainer: Takahiro HIMURA <taka@himura.jp>-synopsis: Twitter API package with conduit interface and Streaming API support.-category: Web, Conduit-stability: Experimental-cabal-version: >= 1.10-build-type: Custom-homepage: https://github.com/himura/twitter-conduit--tested-with: GHC == 8.4.4, GHC == 8.6.5, GHC == 8.8.3+cabal-version: 1.24+name: twitter-conduit+version: 0.6.0+license: BSD3+license-file: LICENSE+maintainer: Takahiro HIMURA <taka@himura.jp>+author: HATTORI Hiroki, Hideyuki Tanaka, Takahiro HIMURA+stability: Experimental+tested-with: ghc ==8.8.4 ghc ==8.10.7 ghc ==9.0.1+homepage: https://github.com/himura/twitter-conduit+synopsis:+ Twitter API package with conduit interface and Streaming API support. description:- This package provides bindings to Twitter's APIs (see <https://dev.twitter.com/>).- .- This package uses the http-conduit package for accessing the Twitter API (see <http://hackage.haskell.org/package/http-conduit>).- This package also depends on the twitter-types package (see <http://hackage.haskell.org/package/twitter-types>).- .- You can find basic examples in the <https://github.com/himura/twitter-conduit/tree/master/sample> directory.- .- This package is under development. If you find something that has not been implemented yet, please send a pull request or open an issue on GitHub.+ This package provides bindings to Twitter's APIs (see <https://dev.twitter.com/>).+ .+ This package uses the http-conduit package for accessing the Twitter API (see <http://hackage.haskell.org/package/http-conduit>).+ This package also depends on the twitter-types package (see <http://hackage.haskell.org/package/twitter-types>).+ .+ You can find basic examples in the <https://github.com/himura/twitter-conduit/tree/master/sample> directory.+ .+ This package is under development. If you find something that has not been implemented yet, please send a pull request or open an issue on GitHub. +category: Web, Conduit+build-type: Custom extra-source-files:- .gitignore- README.md- ChangeLog.md- Warning.hs- sample/LICENSE- sample/twitter-conduit-sample.cabal- sample/*.hs- sample/common/*.hs- tests/*.hs+ .gitignore+ README.md+ ChangeLog.md+ Warning.hs+ sample/LICENSE+ sample/twitter-conduit-sample.cabal+ sample/*.hs+ sample/common/*.hs+ tests/*.hs source-repository head- type: git- location: git://github.com/himura/twitter-conduit.git+ type: git+ location: git://github.com/himura/twitter-conduit.git +custom-setup+ setup-depends:+ base,+ Cabal >=1.24,+ cabal-doctest >=1 && <1.1+ flag run-integrated-test- description: use debug output when running testsuites- default: False+ description: use debug output when running testsuites+ default: False library- ghc-options: -Wall-- build-depends:- base >= 4.9 && < 5- , aeson >= 0.7.0.5- , attoparsec >= 0.10- , authenticate-oauth >= 1.3- , bytestring >= 0.10.2- , conduit >= 1.3- , conduit-extra >= 1.3- , containers- , data-default >= 0.3- , exceptions >= 0.5- , ghc-prim- , http-client >= 0.5.0- , http-conduit >= 2.3 && < 2.4- , http-types- , lens >= 4.4- , lens-aeson >= 1- , resourcet >= 1.0- , text >= 0.11- , time- , transformers >= 0.2.2- , twitter-types >= 0.9- , twitter-types-lens >= 0.9-- exposed-modules:- Web.Twitter.Conduit- Web.Twitter.Conduit.Lens- Web.Twitter.Conduit.Types- Web.Twitter.Conduit.Api- Web.Twitter.Conduit.Stream- Web.Twitter.Conduit.Status- Web.Twitter.Conduit.Base- Web.Twitter.Conduit.Request- Web.Twitter.Conduit.Request.Internal- Web.Twitter.Conduit.Response- Web.Twitter.Conduit.Cursor- Web.Twitter.Conduit.Parameters- Web.Twitter.Conduit.ParametersDeprecated-- default-language: Haskell2010--test-suite hlint- type: exitcode-stdio-1.0- main-is: hlint.hs- hs-source-dirs: tests-- build-depends:- base- , hlint >= 1.7+ exposed-modules:+ Web.Twitter.Conduit+ Web.Twitter.Conduit.Lens+ Web.Twitter.Conduit.Types+ Web.Twitter.Conduit.Api+ Web.Twitter.Conduit.Stream+ Web.Twitter.Conduit.Status+ Web.Twitter.Conduit.Base+ Web.Twitter.Conduit.Request+ Web.Twitter.Conduit.Request.Internal+ Web.Twitter.Conduit.Response+ Web.Twitter.Conduit.Cursor+ Web.Twitter.Conduit.Parameters - default-language: Haskell2010+ default-language: Haskell2010+ ghc-options: -Wall+ build-depends:+ base >=4.9 && <5,+ aeson >=0.7.0.5,+ attoparsec >=0.10,+ authenticate-oauth >=1.3,+ bytestring >=0.10.2,+ conduit >=1.3,+ conduit-extra >=1.3,+ containers,+ data-default >=0.3,+ exceptions >=0.5,+ ghc-prim,+ http-client >=0.5.0,+ http-conduit >=2.3 && <2.4,+ http-types,+ lens >=4.4,+ lens-aeson >=1,+ resourcet >=1.0,+ text >=0.11,+ time,+ transformers >=0.2.2,+ twitter-types >=0.9,+ twitter-types-lens >=0.9 test-suite doctests- type: exitcode-stdio-1.0- main-is: doctests.hs- hs-source-dirs: tests-- build-depends:- base- , doctest-- default-language: Haskell2010+ type: exitcode-stdio-1.0+ main-is: doctests.hs+ hs-source-dirs: tests+ default-language: Haskell2010+ build-depends:+ base,+ doctest test-suite spec_main- type: exitcode-stdio-1.0- main-is: spec_main.hs- hs-source-dirs: tests-- if flag(run-integrated-test)- CPP-Options: -DRUN_INTEGRATED_TEST-- build-tool-depends: hspec-discover:hspec-discover >= 2.3.0- build-depends:- base- , aeson- , attoparsec- , authenticate-oauth- , bytestring- , conduit- , conduit-extra- , containers- , data-default- , hspec- , http-client- , http-conduit- , http-types- , lens- , lens-aeson- , resourcet- , text- , time- , twitter-conduit- , twitter-types- , twitter-types-lens-- other-modules:- Spec- ApiSpec- BaseSpec- StatusSpec- TestUtils+ type: exitcode-stdio-1.0+ main-is: spec_main.hs+ build-tool-depends: hspec-discover:hspec-discover >=2.3.0+ hs-source-dirs: tests+ other-modules:+ Spec+ ApiSpec+ BaseSpec+ StatusSpec+ TestUtils - default-language: Haskell2010+ default-language: Haskell2010+ build-depends:+ base,+ aeson,+ attoparsec,+ authenticate-oauth,+ bytestring,+ conduit,+ conduit-extra,+ containers,+ data-default,+ hspec,+ http-client,+ http-conduit,+ http-types,+ lens,+ lens-aeson,+ resourcet,+ text,+ time,+ twitter-conduit,+ twitter-types,+ twitter-types-lens -custom-setup- setup-depends:- base- , Cabal >= 1.24- , cabal-doctest >= 1 && < 1.1+ if flag(run-integrated-test)+ cpp-options: -DRUN_INTEGRATED_TEST