twitter-feed 0.1.1.1 → 0.1.1.2
raw patch · 4 files changed
+167/−139 lines, 4 filesdep +bytestringdep +twitter-feeddep ~HUnitdep ~aesondep ~authenticate-oauthPVP: major bump suggested
API removals or changes: PVP suggests a major version bump
Dependencies added: bytestring, twitter-feed
Dependency ranges changed: HUnit, aeson, authenticate-oauth, base, containers, http-conduit, test-framework-hunit
API changes (from Hackage documentation)
- Web.Twitter.Feed: Link :: Int -> Int -> String -> Link
- Web.Twitter.Feed: MediaEntity :: String -> BoundingIndices -> String -> MediaEntity
- Web.Twitter.Feed: URLEntity :: String -> BoundingIndices -> String -> URLEntity
- Web.Twitter.Feed: UserEntity :: String -> BoundingIndices -> UserEntity
- Web.Twitter.Feed: data Link
- Web.Twitter.Feed: data MediaEntity
- Web.Twitter.Feed: data Tweet
- Web.Twitter.Feed: data URLEntity
- Web.Twitter.Feed: data UserEntity
- Web.Twitter.Feed: displayMediaUrl :: MediaEntity -> String
- Web.Twitter.Feed: displayUrl :: URLEntity -> String
- Web.Twitter.Feed: endIndex :: Link -> Int
- Web.Twitter.Feed: instance Constructor C1_0Entities
- Web.Twitter.Feed: instance Constructor C1_0MediaEntity
- Web.Twitter.Feed: instance Constructor C1_0SimpleTweet
- Web.Twitter.Feed: instance Constructor C1_0Tweet
- Web.Twitter.Feed: instance Constructor C1_0URLEntity
- Web.Twitter.Feed: instance Constructor C1_0UserEntity
- Web.Twitter.Feed: instance Datatype D1Entities
- Web.Twitter.Feed: instance Datatype D1MediaEntity
- Web.Twitter.Feed: instance Datatype D1SimpleTweet
- Web.Twitter.Feed: instance Datatype D1Tweet
- Web.Twitter.Feed: instance Datatype D1URLEntity
- Web.Twitter.Feed: instance Datatype D1UserEntity
- Web.Twitter.Feed: instance Eq Link
- Web.Twitter.Feed: instance FromJSON Entities
- Web.Twitter.Feed: instance FromJSON MediaEntity
- Web.Twitter.Feed: instance FromJSON Tweet
- Web.Twitter.Feed: instance FromJSON URLEntity
- Web.Twitter.Feed: instance FromJSON UserEntity
- Web.Twitter.Feed: instance Generic Entities
- Web.Twitter.Feed: instance Generic MediaEntity
- Web.Twitter.Feed: instance Generic SimpleTweet
- Web.Twitter.Feed: instance Generic Tweet
- Web.Twitter.Feed: instance Generic URLEntity
- Web.Twitter.Feed: instance Generic UserEntity
- Web.Twitter.Feed: instance Selector S1_0_0Entities
- Web.Twitter.Feed: instance Selector S1_0_0MediaEntity
- Web.Twitter.Feed: instance Selector S1_0_0SimpleTweet
- Web.Twitter.Feed: instance Selector S1_0_0Tweet
- Web.Twitter.Feed: instance Selector S1_0_0URLEntity
- Web.Twitter.Feed: instance Selector S1_0_0UserEntity
- Web.Twitter.Feed: instance Selector S1_0_1Entities
- Web.Twitter.Feed: instance Selector S1_0_1MediaEntity
- Web.Twitter.Feed: instance Selector S1_0_1SimpleTweet
- Web.Twitter.Feed: instance Selector S1_0_1Tweet
- Web.Twitter.Feed: instance Selector S1_0_1URLEntity
- Web.Twitter.Feed: instance Selector S1_0_1UserEntity
- Web.Twitter.Feed: instance Selector S1_0_2Entities
- Web.Twitter.Feed: instance Selector S1_0_2MediaEntity
- Web.Twitter.Feed: instance Selector S1_0_2SimpleTweet
- Web.Twitter.Feed: instance Selector S1_0_2Tweet
- Web.Twitter.Feed: instance Selector S1_0_2URLEntity
- Web.Twitter.Feed: instance Selector S1_0_3Tweet
- Web.Twitter.Feed: instance Show Entities
- Web.Twitter.Feed: instance Show Link
- Web.Twitter.Feed: instance Show MediaEntity
- Web.Twitter.Feed: instance Show SimpleTweet
- Web.Twitter.Feed: instance Show Tweet
- Web.Twitter.Feed: instance Show URLEntity
- Web.Twitter.Feed: instance Show UserEntity
- Web.Twitter.Feed: instance ToJSON Entities
- Web.Twitter.Feed: instance ToJSON MediaEntity
- Web.Twitter.Feed: instance ToJSON SimpleTweet
- Web.Twitter.Feed: instance ToJSON Tweet
- Web.Twitter.Feed: instance ToJSON URLEntity
- Web.Twitter.Feed: instance ToJSON UserEntity
- Web.Twitter.Feed: mediaIndices :: MediaEntity -> BoundingIndices
- Web.Twitter.Feed: mediaUrl :: MediaEntity -> String
- Web.Twitter.Feed: newHtml :: Link -> String
- Web.Twitter.Feed: screenName :: UserEntity -> String
- Web.Twitter.Feed: startIndex :: Link -> Int
- Web.Twitter.Feed: urlIndices :: URLEntity -> BoundingIndices
- Web.Twitter.Feed: urlMessage :: URLEntity -> String
- Web.Twitter.Feed: userIndices :: UserEntity -> BoundingIndices
+ Web.Twitter.Types: Entities :: [UserEntity] -> [URLEntity] -> [MediaEntity] -> Entities
+ Web.Twitter.Types: Link :: Int -> Int -> String -> Link
+ Web.Twitter.Types: MediaEntity :: String -> BoundingIndices -> String -> MediaEntity
+ Web.Twitter.Types: SimpleTweet :: String -> String -> String -> SimpleTweet
+ Web.Twitter.Types: Tweet :: String -> String -> String -> Entities -> Tweet
+ Web.Twitter.Types: URLEntity :: String -> BoundingIndices -> String -> URLEntity
+ Web.Twitter.Types: UserEntity :: String -> BoundingIndices -> UserEntity
+ Web.Twitter.Types: body :: SimpleTweet -> String
+ Web.Twitter.Types: createdAt :: Tweet -> String
+ Web.Twitter.Types: created_at :: SimpleTweet -> String
+ Web.Twitter.Types: data Entities
+ Web.Twitter.Types: data Link
+ Web.Twitter.Types: data MediaEntity
+ Web.Twitter.Types: data SimpleTweet
+ Web.Twitter.Types: data Tweet
+ Web.Twitter.Types: data URLEntity
+ Web.Twitter.Types: data UserEntity
+ Web.Twitter.Types: displayMediaUrl :: MediaEntity -> String
+ Web.Twitter.Types: displayUrl :: URLEntity -> String
+ Web.Twitter.Types: endIndex :: Link -> Int
+ Web.Twitter.Types: entities :: Tweet -> Entities
+ Web.Twitter.Types: idStr :: Tweet -> String
+ Web.Twitter.Types: instance Constructor C1_0Entities
+ Web.Twitter.Types: instance Constructor C1_0MediaEntity
+ Web.Twitter.Types: instance Constructor C1_0SimpleTweet
+ Web.Twitter.Types: instance Constructor C1_0Tweet
+ Web.Twitter.Types: instance Constructor C1_0URLEntity
+ Web.Twitter.Types: instance Constructor C1_0UserEntity
+ Web.Twitter.Types: instance Datatype D1Entities
+ Web.Twitter.Types: instance Datatype D1MediaEntity
+ Web.Twitter.Types: instance Datatype D1SimpleTweet
+ Web.Twitter.Types: instance Datatype D1Tweet
+ Web.Twitter.Types: instance Datatype D1URLEntity
+ Web.Twitter.Types: instance Datatype D1UserEntity
+ Web.Twitter.Types: instance Eq Link
+ Web.Twitter.Types: instance FromJSON Entities
+ Web.Twitter.Types: instance FromJSON MediaEntity
+ Web.Twitter.Types: instance FromJSON Tweet
+ Web.Twitter.Types: instance FromJSON URLEntity
+ Web.Twitter.Types: instance FromJSON UserEntity
+ Web.Twitter.Types: instance Generic Entities
+ Web.Twitter.Types: instance Generic MediaEntity
+ Web.Twitter.Types: instance Generic SimpleTweet
+ Web.Twitter.Types: instance Generic Tweet
+ Web.Twitter.Types: instance Generic URLEntity
+ Web.Twitter.Types: instance Generic UserEntity
+ Web.Twitter.Types: instance Ord Link
+ Web.Twitter.Types: instance Selector S1_0_0Entities
+ Web.Twitter.Types: instance Selector S1_0_0MediaEntity
+ Web.Twitter.Types: instance Selector S1_0_0SimpleTweet
+ Web.Twitter.Types: instance Selector S1_0_0Tweet
+ Web.Twitter.Types: instance Selector S1_0_0URLEntity
+ Web.Twitter.Types: instance Selector S1_0_0UserEntity
+ Web.Twitter.Types: instance Selector S1_0_1Entities
+ Web.Twitter.Types: instance Selector S1_0_1MediaEntity
+ Web.Twitter.Types: instance Selector S1_0_1SimpleTweet
+ Web.Twitter.Types: instance Selector S1_0_1Tweet
+ Web.Twitter.Types: instance Selector S1_0_1URLEntity
+ Web.Twitter.Types: instance Selector S1_0_1UserEntity
+ Web.Twitter.Types: instance Selector S1_0_2Entities
+ Web.Twitter.Types: instance Selector S1_0_2MediaEntity
+ Web.Twitter.Types: instance Selector S1_0_2SimpleTweet
+ Web.Twitter.Types: instance Selector S1_0_2Tweet
+ Web.Twitter.Types: instance Selector S1_0_2URLEntity
+ Web.Twitter.Types: instance Selector S1_0_3Tweet
+ Web.Twitter.Types: instance Show Entities
+ Web.Twitter.Types: instance Show Link
+ Web.Twitter.Types: instance Show MediaEntity
+ Web.Twitter.Types: instance Show SimpleTweet
+ Web.Twitter.Types: instance Show Tweet
+ Web.Twitter.Types: instance Show URLEntity
+ Web.Twitter.Types: instance Show UserEntity
+ Web.Twitter.Types: instance ToJSON Entities
+ Web.Twitter.Types: instance ToJSON MediaEntity
+ Web.Twitter.Types: instance ToJSON SimpleTweet
+ Web.Twitter.Types: instance ToJSON Tweet
+ Web.Twitter.Types: instance ToJSON URLEntity
+ Web.Twitter.Types: instance ToJSON UserEntity
+ Web.Twitter.Types: mediaEntities :: Entities -> [MediaEntity]
+ Web.Twitter.Types: mediaIndices :: MediaEntity -> BoundingIndices
+ Web.Twitter.Types: mediaUrl :: MediaEntity -> String
+ Web.Twitter.Types: newHtml :: Link -> String
+ Web.Twitter.Types: screenName :: UserEntity -> String
+ Web.Twitter.Types: startIndex :: Link -> Int
+ Web.Twitter.Types: text :: Tweet -> String
+ Web.Twitter.Types: tweetId :: SimpleTweet -> String
+ Web.Twitter.Types: urlEntities :: Entities -> [URLEntity]
+ Web.Twitter.Types: urlIndices :: URLEntity -> BoundingIndices
+ Web.Twitter.Types: urlMessage :: URLEntity -> String
+ Web.Twitter.Types: userEntities :: Entities -> [UserEntity]
+ Web.Twitter.Types: userIndices :: UserEntity -> BoundingIndices
Files
- LICENSE +1/−1
- src/Web/Twitter/Feed.hs +37/−123
- src/Web/Twitter/Types.hs +112/−0
- twitter-feed.cabal +17/−15
LICENSE view
@@ -1,4 +1,4 @@-Copyright (c) 2014 Justin Leitgeb+Copyright (c) 2014 Stack Builders Inc. MIT License
src/Web/Twitter/Feed.hs view
@@ -1,127 +1,43 @@ {-# LANGUAGE OverloadedStrings, DeriveGeneric #-} module Web.Twitter.Feed- ( Tweet- , Link (..)- , URLEntity (..)- , UserEntity (..)- , MediaEntity (..)- , timeline+ ( timeline , addLink , timelineUrl , sortLinks- )-where+ ) where +import qualified Data.ByteString.Lazy as BS+ import Network.HTTP.Conduit import Web.Authenticate.OAuth import Data.Aeson-import GHC.Generics (Generic)-import Control.Applicative-import Data.List (sortBy)-import Control.Monad (mzero)+import Data.List (elemIndices, sort) import Data.Char (toLower)--type BoundingIndices = [Int]--data SimpleTweet = SimpleTweet- { body :: String- , tweetId :: String- , created_at :: String- } deriving (Show, Generic)--data Tweet = Tweet- { text :: String- , createdAt :: String- , idStr :: String- , entities :: Entities- } deriving (Show, Generic)--data UserEntity = UserEntity- { screenName :: String- , userIndices :: BoundingIndices- } deriving (Show, Generic)--data URLEntity = URLEntity- { urlMessage :: String- , urlIndices :: BoundingIndices- , displayUrl :: String- } deriving (Show, Generic)--data MediaEntity = MediaEntity- { mediaUrl :: String- , mediaIndices :: BoundingIndices- , displayMediaUrl :: String- } deriving (Show, Generic)--data Entities = Entities- { userEntities :: [UserEntity]- , urlEntities :: [URLEntity]- , mediaEntities :: [MediaEntity]- } deriving (Show, Generic)--data Link = Link- { startIndex :: Int- , endIndex :: Int- , newHtml :: String- } deriving (Show, Eq)--instance ToJSON SimpleTweet--instance ToJSON Tweet-instance FromJSON Tweet- where parseJSON (Object v) = Tweet- <$> v .: "text"- <*> v .: "created_at"- <*> v .: "id_str"- <*> v .: "entities"- parseJSON _ = mzero--instance ToJSON UserEntity-instance FromJSON UserEntity- where parseJSON (Object v) = UserEntity- <$> v .: "screen_name"- <*> v .: "indices" .!= []- parseJSON _ = mzero--instance ToJSON URLEntity-instance FromJSON URLEntity- where parseJSON (Object v) = URLEntity- <$> v .: "url"- <*> v .: "indices" .!= []- <*> v .: "display_url"- parseJSON _ = mzero--instance ToJSON MediaEntity-instance FromJSON MediaEntity- where parseJSON (Object v) = MediaEntity- <$> v .: "url"- <*> v .: "indices" .!= []- <*> v .: "display_url"- parseJSON _ = mzero--instance ToJSON Entities-instance FromJSON Entities- where parseJSON (Object v) = Entities- <$> v .:? "user_mentions" .!= []- <*> v .:? "urls" .!= []- <*> v .:? "media" .!= []- parseJSON _ = mzero+import Web.Twitter.Types timeline :: OAuth -> Credential -> Int -> Bool -> String -> IO (Either String [SimpleTweet]) timeline oauth credential count excludeReplies username = do- req <- parseUrl $ timelineUrl username count excludeReplies- res <- withManager $ \m -> do- signedreq <- signOAuth oauth credential req- httpLbs signedreq m+ req <- createRequest username count excludeReplies+ res <- getResponse oauth credential req+ return $+ case decodeTweets res of+ Left message -> Left message+ Right ts -> Right $ map (simplifyTweet . linkifyTweet) ts - let decoded = decode $ responseBody res+createRequest :: String -> Int -> Bool -> IO Request+createRequest username count excludeReplies = parseUrl $ timelineUrl username count excludeReplies - case decoded of- Nothing -> return $ Left "Unable to retrieve tweets!"- Just ts -> return $ Right $ map (simplifyTweet . linkifyTweet) ts+getResponse :: OAuth -> Credential -> Request -> IO (Response BS.ByteString)+getResponse oauth credential req =+ withManager $ \m -> do+ signedreq <- signOAuth oauth credential req+ httpLbs signedreq m +decodeTweets :: Response BS.ByteString -> Either String [Tweet]+decodeTweets = eitherDecode . responseBody+ timelineUrl :: String -> Int -> Bool -> String timelineUrl user count excludeReplies = "https://api.twitter.com/1.1/statuses/user_timeline.json?screen_name=" ++@@ -138,21 +54,17 @@ (entities tweet) processText :: String -> [UserEntity] -> [URLEntity] -> [MediaEntity] -> String-processText message users urls medias = foldr addLink message- (sortLinks urls users medias)+processText message users urls medias = foldr addLink message sortedLinks+ where sortedLinks = sortLinks urls users medias sortLinks :: [URLEntity] -> [UserEntity] -> [MediaEntity] -> [Link]-sortLinks urls users medias = sortBy sortDesc (map makeURLLink urls ++- map makeUserLink users ++- map makeMediaLink medias)- where sortDesc a b- | startIndex a < startIndex b = LT- | otherwise = GT+sortLinks urls users medias = sort (map makeURLLink urls +++ map makeUserLink users +++ map makeMediaLink medias) makeURLLink :: URLEntity -> Link makeURLLink urlEntity = Link x y url- where x = head (urlIndices urlEntity)- y = urlIndices urlEntity !! 1+ where (x, y) = urlIndices urlEntity urlText = displayUrl urlEntity href = urlMessage urlEntity url = "<a target=\"_blank\" href=\"" ++ href ++ "\">"@@ -160,8 +72,7 @@ makeMediaLink :: MediaEntity -> Link makeMediaLink mediaEntity = Link x y url- where x = head (mediaIndices mediaEntity)- y = mediaIndices mediaEntity !! 1+ where (x, y) = mediaIndices mediaEntity urlText = displayMediaUrl mediaEntity href = mediaUrl mediaEntity url = "<a target=\"_blank\" href=\"" ++ href ++ "\">"@@ -169,8 +80,7 @@ makeUserLink :: UserEntity -> Link makeUserLink userEntity = Link x y mention- where x = head (userIndices userEntity)- y = userIndices userEntity !! 1+ where (x, y) = userIndices userEntity username = screenName userEntity mention = "<a target=\"_blank\" href=\"//twitter.com/" ++ username ++ "\">@" ++ username ++ "</a>"@@ -182,6 +92,10 @@ , created_at = createdAt tweet } addLink :: Link -> String -> String-addLink link tweet = before ++ newHtml link ++ after- where before = fst (splitAt (startIndex link) tweet)- after = snd (splitAt (endIndex link) tweet)+addLink (Link 139 140 link) tweet = before ++ " " ++ link ++ after+ where before = fst (splitAt (lastBlank tweet) tweet)+ after = snd (splitAt 140 tweet)+ lastBlank = last . elemIndices ' '+addLink (Link start end link) tweet = before ++ link ++ after+ where before = fst (splitAt start tweet)+ after = snd (splitAt end tweet)
+ src/Web/Twitter/Types.hs view
@@ -0,0 +1,112 @@+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE DeriveGeneric #-}++module Web.Twitter.Types+ ( Tweet(..)+ , SimpleTweet(..)+ , Link(..)+ , Entities(..)+ , URLEntity(..)+ , UserEntity(..)+ , MediaEntity(..)+ ) where++import Control.Applicative+import Data.Aeson+import Data.Aeson.Types+import Data.Ord (comparing)+import GHC.Generics (Generic)++type BoundingIndices = (Int, Int)++data SimpleTweet = SimpleTweet+ { body :: String+ , tweetId :: String+ , created_at :: String+ } deriving (Show, Generic)++data Tweet = Tweet+ { text :: String+ , createdAt :: String+ , idStr :: String+ , entities :: Entities+ } deriving (Show, Generic)++data Entities = Entities+ { userEntities :: [UserEntity]+ , urlEntities :: [URLEntity]+ , mediaEntities :: [MediaEntity]+ } deriving (Show, Generic)++data UserEntity = UserEntity+ { screenName :: String+ , userIndices :: BoundingIndices+ } deriving (Show, Generic)++data URLEntity = URLEntity+ { urlMessage :: String+ , urlIndices :: BoundingIndices+ , displayUrl :: String+ } deriving (Show, Generic)++data MediaEntity = MediaEntity+ { mediaUrl :: String+ , mediaIndices :: BoundingIndices+ , displayMediaUrl :: String+ } deriving (Show, Generic)++data Link = Link+ { startIndex :: Int+ , endIndex :: Int+ , newHtml :: String+ } deriving (Show, Eq)++instance ToJSON SimpleTweet++instance ToJSON Tweet+instance FromJSON Tweet where+ parseJSON = withObject "Tweet" parseTweet++instance ToJSON Entities+instance FromJSON Entities where+ parseJSON = withObject "Entities" parseEntities++instance ToJSON UserEntity+instance FromJSON UserEntity where+ parseJSON = withObject "UserEntity" parseUserEntity++instance ToJSON URLEntity+instance FromJSON URLEntity where+ parseJSON = withObject "URLEntity" parseURLEntity++instance ToJSON MediaEntity+instance FromJSON MediaEntity where+ parseJSON = withObject "MediaEntity" parseMediaEntity++instance Ord Link where+ compare = comparing startIndex++parseTweet :: Object -> Parser Tweet+parseTweet v = Tweet <$> v .: "text"+ <*> v .: "created_at"+ <*> v .: "id_str"+ <*> v .: "entities"++parseUserEntity :: Object -> Parser UserEntity+parseUserEntity v = UserEntity <$> v .: "screen_name"+ <*> v .: "indices"++parseURLEntity :: Object -> Parser URLEntity+parseURLEntity v = URLEntity <$> v .: "url"+ <*> v .: "indices"+ <*> v .: "display_url"++parseMediaEntity :: Object -> Parser MediaEntity+parseMediaEntity v = MediaEntity <$> v .: "url"+ <*> v .: "indices"+ <*> v .: "display_url"++parseEntities :: Object -> Parser Entities+parseEntities v = Entities <$> v .:? "user_mentions" .!= []+ <*> v .:? "urls" .!= []+ <*> v .:? "media" .!= []
twitter-feed.cabal view
@@ -1,5 +1,5 @@ name: twitter-feed-version: 0.1.1.1+version: 0.1.1.2 synopsis: Client for fetching Twitter timeline via Oauth description: Fetches a user timeline from Twitter, and optionally linkifies the results using the Twitter entity API. homepage: https://github.com/stackbuilders/twitter-feed@@ -18,28 +18,30 @@ library exposed-modules: Web.Twitter.Feed+ Web.Twitter.Types ghc-options: -Wall- build-depends: base >=4.6 && <4.8- , authenticate-oauth >=1.4- , aeson >=0.7 && <0.8- , http-conduit >=1.4 && <2.0 + build-depends:+ base >= 4.6 && < 4.8,+ aeson >= 0.8 && < 0.9,+ authenticate-oauth >= 1.5 && < 1.6,+ http-conduit >= 2.1.4 && < 2.1.5,+ bytestring >= 0.10 && < 0.11+ hs-source-dirs: src default-language: Haskell2010 test-suite twitter-library type: exitcode-stdio-1.0- hs-source-dirs: test, src+ hs-source-dirs: test main-is: Suite.hs build-depends:- base >=4.6 && <4.8- , test-framework >=0.8 && <0.9- , HUnit- , test-framework-hunit- , containers-- , authenticate-oauth >=1.4- , aeson >=0.7 && <0.8- , http-conduit >=1.4 && <2.0+ base,+ twitter-feed,+ containers >= 0.5 && < 0.6,+ HUnit >= 1.2 && < 1.3,+ test-framework >= 0.8 && < 0.9,+ test-framework-hunit >= 0.3 && < 0.4 default-language: Haskell2010+ ghc-options: -Wall