{-# LANGUAGE OverloadedStrings, DeriveDataTypeable #-} module Web.Twitter.Types ( DateString , UserId , Friends , URIString , UserName , StatusId , LanguageCode , StreamingAPI(..) , Status(..) , SearchResult(..) , SearchStatus(..) , SearchMetadata(..) , RetweetedStatus(..) , DirectMessage(..) , EventTarget(..) , Event(..) , Delete(..) , User(..) , List(..) , Entities(..) , EntityIndices , Entity(..) , HashTagEntity(..) , UserEntity(..) , URLEntity(..) , MediaEntity(..) , MediaSize(..) , checkError ) where import Data.Aeson import Data.Aeson.Types (Parser) import Data.Text (Text) import Data.HashMap.Strict (HashMap) import Control.Applicative import Control.Monad type DateString = String type UserId = Integer type Friends = [UserId] type URIString = Text type UserName = Text type StatusId = Integer type LanguageCode = String data StreamingAPI = SStatus Status | SRetweetedStatus RetweetedStatus | SEvent Event | SDelete Delete -- -- | SScrubGeo ScrubGeo | SFriends Friends | SUnknown Value deriving (Show, Eq) checkError :: Object -> Parser () checkError o = do err <- o .:? "error" case err of Just msg -> fail msg Nothing -> return () instance FromJSON StreamingAPI where parseJSON v@(Object o) = SRetweetedStatus <$> js <|> SStatus <$> js <|> SEvent <$> js <|> SDelete <$> js <|> SFriends <$> (o .: "friends") <|> return (SUnknown v) where js :: FromJSON a => Parser a js = parseJSON v parseJSON _ = mzero data Status = Status { statusCreatedAt :: DateString , statusId :: StatusId , statusText :: Text , statusSource :: Text , statusTruncated :: Bool , statusEntities :: Maybe Entities , statusInReplyTo :: Maybe StatusId , statusInReplyToUser :: Maybe UserId , statusFavorite :: Maybe Bool , statusRetweetCount :: Maybe Integer , statusUser :: User } deriving (Show, Eq) instance FromJSON Status where parseJSON (Object o) = checkError o >> Status <$> o .: "created_at" <*> o .: "id" <*> o .: "text" <*> o .: "source" <*> o .: "truncated" <*> o .:? "entities" <*> o .:? "in_reply_to_status_id" <*> o .:? "in_reply_to_user_id" <*> o .:? "favorited" <*> o .:? "retweet_count" <*> o .: "user" parseJSON _ = mzero data SearchResult body = SearchResult { searchResultStatuses :: body , searchResultSearchMetadata :: SearchMetadata } deriving (Show, Eq) instance FromJSON body => FromJSON (SearchResult body) where parseJSON (Object o) = checkError o >> SearchResult <$> o .: "statuses" <*> o .: "search_metadata" parseJSON _ = mzero data SearchStatus = SearchStatus { searchStatusCreatedAt :: DateString , searchStatusId :: StatusId , searchStatusText :: Text , searchStatusSource :: Text , searchStatusUser :: User } deriving (Show, Eq) instance FromJSON SearchStatus where parseJSON (Object o) = checkError o >> SearchStatus <$> o .: "created_at" <*> o .: "id" <*> o .: "text" <*> o .: "source" <*> o .: "user" parseJSON _ = mzero data SearchMetadata = SearchMetadata { searchMetadataMaxId :: StatusId , searchMetadataSinceId :: StatusId , searchMetadataRefreshUrl :: URIString , searchMetadataNextResults :: Maybe URIString , searchMetadataCount :: Int , searchMetadataCompletedIn :: Maybe Float , searchMetadataSinceIdStr :: String , searchMetadataQuery :: String , searchMetadataMaxIdStr :: String } deriving (Show, Eq) instance FromJSON SearchMetadata where parseJSON (Object o) = checkError o >> SearchMetadata <$> o .: "max_id" <*> o .: "since_id" <*> o .: "refresh_url" <*> o .:? "next_results" <*> o .: "count" <*> o .:? "completed_in" <*> o .: "since_id_str" <*> o .: "query" <*> o .: "max_id_str" parseJSON _ = mzero data RetweetedStatus = RetweetedStatus { rsCreatedAt :: DateString , rsId :: StatusId , rsText :: Text , rsSource :: Text , rsTruncated :: Bool , rsEntities :: Maybe Entities , rsUser :: User , rsRetweetedStatus :: Status } deriving (Show, Eq) instance FromJSON RetweetedStatus where parseJSON (Object o) = checkError o >> RetweetedStatus <$> o .: "created_at" <*> o .: "id" <*> o .: "text" <*> o .: "source" <*> o .: "truncated" <*> o .:? "entities" <*> o .: "user" <*> o .: "retweeted_status" parseJSON _ = mzero data DirectMessage = DirectMessage { dmCreatedAt :: DateString , dmSenderScreenName :: Text , dmSender :: User , dmText :: Text , dmRecipientScreeName :: Text , dmId :: StatusId , dmRecipient :: User , dmRecipientId :: UserId , dmSenderId :: UserId } deriving (Show, Eq) instance FromJSON DirectMessage where parseJSON (Object o) = checkError o >> DirectMessage <$> o .: "created_at" <*> o .: "sender_screen_name" <*> o .: "sender" <*> o .: "text" <*> o .: "recipient_screen_name" <*> o .: "id" <*> o .: "recipient" <*> o .: "recipient_id" <*> o .: "sender_id" parseJSON _ = mzero data EventType = Favorite | Unfavorite | ListCreated | ListUpdated | ListMemberAdded | UserUpdate | Block | Unblock | Follow deriving (Show, Eq) data EventTarget = ETUser User | ETStatus Status | ETList List | ETUnknown Value deriving (Show, Eq) instance FromJSON EventTarget where parseJSON v@(Object o) = checkError o >> ETUser <$> parseJSON v <|> ETStatus <$> parseJSON v <|> ETList <$> parseJSON v <|> return (ETUnknown v) parseJSON _ = mzero data Event = Event { evCreatedAt :: DateString , evTargetObject :: Maybe EventTarget , evEvent :: Text , evTarget :: EventTarget , evSource :: EventTarget } deriving (Show, Eq) instance FromJSON Event where parseJSON (Object o) = checkError o >> Event <$> o .: "created_at" <*> o .:? "target_object" <*> o .: "event" <*> o .: "target" <*> o .: "source" parseJSON _ = mzero data Delete = Delete { delId :: StatusId , delUserId :: UserId } deriving (Show, Eq) instance FromJSON Delete where parseJSON (Object o) = checkError o >> do s <- o .: "delete" >>= (.: "status") Delete <$> s .: "id" <*> s .: "user_id" parseJSON _ = mzero data User = User { userId :: UserId , userName :: UserName , userScreenName :: Text , userDescription :: Maybe Text , userLocation :: Maybe Text , userProfileImageURL :: Maybe URIString , userURL :: Maybe URIString , userProtected :: Maybe Bool , userFollowers :: Maybe Int , userFriends :: Maybe Int , userTweets :: Maybe Int , userLangCode :: Maybe LanguageCode , userCreatedAt :: Maybe DateString } deriving (Show, Eq) instance FromJSON User where parseJSON (Object o) = checkError o >> User <$> o .: "id" <*> o .: "name" <*> o .: "screen_name" <*> o .:? "description" <*> o .:? "location" <*> o .:? "profile_image_url" <*> o .:? "url" <*> o .:? "protected" <*> o .:? "followers_count" <*> o .:? "friends_count" <*> o .:? "statuses_count" <*> o .:? "lang" <*> o .:? "created_at" parseJSON _ = mzero data List = List { listId :: Int , listName :: Text , listFullName :: Text , listMemberCount :: Int , listSubscriberCount :: Int , listMode :: Text , listUser :: User } deriving (Show, Eq) instance FromJSON List where parseJSON (Object o) = checkError o >> List <$> o .: "id" <*> o .: "name" <*> o .: "full_name" <*> o .: "member_count" <*> o .: "subscriber_count" <*> o .: "mode" <*> o .: "user" parseJSON _ = mzero data HashTagEntity = HashTagEntity { hashTagText :: Text -- ^ The Hashtag text } deriving (Show, Eq) instance FromJSON HashTagEntity where parseJSON (Object o) = HashTagEntity <$> o .: "text" parseJSON _ = mzero data UserEntity = UserEntity { userEntityUserId :: UserId , userEntityUserName :: UserName , userEntityUserScreenName :: Text } deriving (Show, Eq) instance FromJSON UserEntity where parseJSON (Object o) = UserEntity <$> o .: "id" <*> o .: "name" <*> o .: "screen_name" parseJSON _ = mzero data URLEntity = URLEntity { ueURL :: URIString -- ^ The URL that was extracted , ueExpanded :: URIString -- ^ The fully resolved URL (only for t.co links) , ueDisplay :: Text -- ^ Not a URL but a string to display instead of the URL (only for t.co links) } deriving (Show, Eq) instance FromJSON URLEntity where parseJSON (Object o) = URLEntity <$> o .: "url" <*> o .: "expanded_url" <*> o .: "display_url" parseJSON _ = mzero data MediaEntity = MediaEntity { meType :: Text , meId :: StatusId , meSizes :: HashMap Text MediaSize , meMediaURL :: URIString , meMediaURLHttps :: URIString , meURL :: URLEntity } deriving (Show, Eq) instance FromJSON MediaEntity where parseJSON v@(Object o) = MediaEntity <$> o .: "type" <*> o .: "id" <*> o .: "sizes" <*> o .: "media_url" <*> o .: "media_url_https" <*> parseJSON v parseJSON _ = mzero data MediaSize = MediaSize { msWidth :: Int , msHeight :: Int , msResize :: Text } deriving (Show, Eq) instance FromJSON MediaSize where parseJSON (Object o) = MediaSize <$> o .: "w" <*> o .: "h" <*> o .: "resize" parseJSON _ = mzero -- | Entity handling. data Entities = Entities { enHashTags :: [Entity HashTagEntity] , enUserMentions :: [Entity UserEntity] , enURLs :: [Entity URLEntity] , enMedia :: [Entity MediaEntity] } deriving (Show, Eq) instance FromJSON Entities where parseJSON (Object o) = Entities <$> o .:? "hashtags" .!= [] <*> o .:? "user_mentions" .!= [] <*> o .:? "urls" .!= [] <*> o .:? "media" .!= [] parseJSON _ = mzero -- | The character positions the Entity was extracted from -- -- This is experimental implementation. -- This may be replaced by more definite types. type EntityIndices = [Int] data Entity a = Entity { entityBody :: a -- ^ The detail information of the specific entity types (HashTag, URL, User) , entityIndices :: EntityIndices -- ^ The character positions the Entity was extracted from } deriving (Show, Eq) instance FromJSON a => FromJSON (Entity a) where parseJSON v@(Object o) = Entity <$> parseJSON v <*> o .: "indices" parseJSON _ = mzero