packages feed

tweet-hs 1.0.1.43 → 1.0.2.1

raw patch · 14 files changed

+212/−190 lines, 14 filesdep −aesondep −data-defaultdep −hspecdep ~basedep ~containersPVP: major bump suggested

API removals or changes: PVP suggests a major version bump

Dependencies removed: aeson, data-default, hspec

Dependency ranges changed: base, containers

API changes (from Hackage documentation)

- Web.Tweet.Parser.FastParser: FastTweet :: !Int -> !Text -> User -> Maybe FastTweet -> !Int -> !Int -> FastTweet
- Web.Tweet.Parser.FastParser: [favorite_count] :: FastTweet -> !Int
- Web.Tweet.Parser.FastParser: [idNum] :: FastTweet -> !Int
- Web.Tweet.Parser.FastParser: [quoted_status] :: FastTweet -> Maybe FastTweet
- Web.Tweet.Parser.FastParser: [retweet_count] :: FastTweet -> !Int
- Web.Tweet.Parser.FastParser: [text] :: FastTweet -> !Text
- Web.Tweet.Parser.FastParser: [user] :: FastTweet -> User
- Web.Tweet.Parser.FastParser: data FastTweet
- Web.Tweet.Parser.FastParser: fastParse :: ByteString -> Either String [FastTweet]
- Web.Tweet.Parser.FastParser: fromFast :: FastTweet -> TweetEntity
- Web.Tweet.Parser.FastParser: instance Data.Aeson.Types.FromJSON.FromJSON Web.Tweet.Parser.FastParser.FastTweet
- Web.Tweet.Parser.FastParser: instance Data.Aeson.Types.FromJSON.FromJSON Web.Tweet.Parser.FastParser.User
- Web.Tweet.Parser.FastParser: instance GHC.Classes.Eq Web.Tweet.Parser.FastParser.FastTweet
- Web.Tweet.Parser.FastParser: instance GHC.Classes.Eq Web.Tweet.Parser.FastParser.User
- Web.Tweet.Parser.FastParser: instance GHC.Generics.Generic Web.Tweet.Parser.FastParser.FastTweet
- Web.Tweet.Parser.FastParser: instance GHC.Generics.Generic Web.Tweet.Parser.FastParser.User
- Web.Tweet.Parser.FastParser: instance GHC.Show.Show Web.Tweet.Parser.FastParser.FastTweet
- Web.Tweet.Parser.FastParser: instance GHC.Show.Show Web.Tweet.Parser.FastParser.User
- Web.Tweet.Utils: getTweetsFast :: ByteString -> Either String Timeline
+ Web.Tweet: [_replyTo] :: TweetEntity -> Maybe Int
+ Web.Tweet: pricklyTweet :: Tweet
+ Web.Tweet: showReplies :: String -> Int -> Bool -> FilePath -> IO String
+ Web.Tweet.API: getReplies :: String -> Int -> FilePath -> IO (Either (ParseErrorBundle String Void) Timeline)
+ Web.Tweet.API: loopMentions :: Maybe Int -> Maybe Int -> Set String -> String -> FilePath -> IO ()
+ Web.Tweet.API: loopReplies :: Maybe Int -> Maybe Int -> String -> Int -> FilePath -> IO (Either (ParseErrorBundle String Void) Timeline)
+ Web.Tweet.API: muteMentions :: String -> FilePath -> IO ()
+ Web.Tweet.API: muteRepliers :: String -> Int -> FilePath -> IO [String]
+ Web.Tweet.API: searchMentions :: Maybe Int -> String -> FilePath -> IO (Either (ParseErrorBundle String Void) Timeline)
+ Web.Tweet.API: searchMentionsRaw :: Maybe Int -> String -> FilePath -> IO ByteString
+ Web.Tweet.API: searchRaw :: String -> FilePath -> IO ByteString
+ Web.Tweet.API: searchReplies :: Maybe Int -> String -> Int -> FilePath -> IO (Either (ParseErrorBundle String Void) Timeline)
+ Web.Tweet.API: searchRepliesRaw :: Maybe Int -> String -> Int -> FilePath -> IO ByteString
- Web.Tweet: TweetEntity :: String -> String -> String -> Int -> [String] -> Maybe TweetEntity -> Int -> Int -> TweetEntity
+ Web.Tweet: TweetEntity :: String -> Maybe Int -> String -> String -> Int -> [String] -> Maybe TweetEntity -> Int -> Int -> TweetEntity

Files

− CHANGELOG.md
@@ -1,5 +0,0 @@-# tweet-hs--## 1.0.1.42--  * Update for new `megaparsec`
app/Main.hs view
@@ -1,6 +1,7 @@ module Main where  import qualified Data.ByteString.Lazy.Char8 as BSL+import           Data.Foldable              (traverse_) import           Data.Maybe import           Data.Version import           Options.Applicative@@ -33,6 +34,9 @@     | Mute { screenName' :: String }     | Unmute { screenName' :: String }     | Dump { screenName' :: String }+    | Replies { screenName' :: String, twId :: Integer }+    | MuteReplies { screenName' :: String, twId :: Integer }+    | MuteMentions { screenName' :: String }  -- | query twitter to post stdin with no fancy options fromStdIn :: Int -> FilePath -> IO ()@@ -50,7 +54,7 @@  -- | Executes parser main :: IO ()-main = putStrLn bird >> execParser opts >>= select+main = execParser opts >>= select     where         versionInfo = infoOption ("tweet-hs version: " ++ showVersion version) (short 'v' <> long "version" <> help "Show version")         opts        = info (helper <*> versionInfo <*> program)@@ -74,6 +78,12 @@ selectCommand (SendInput maybeNum (Just rId) (Just h)) _ file = threadStdIn h (pure . read $ rId) (fromMaybe 15 maybeNum) file selectCommand (SendInput maybeNum (Just rId) Nothing) _ file = threadStdIn [] (pure . read $ rId) (fromMaybe 15 maybeNum) file selectCommand (SendInput maybeNum Nothing (Just h)) _ file = threadStdIn h Nothing (fromMaybe 15 maybeNum) file+selectCommand (Replies uname twid) c file = putStrLn =<< showReplies uname (fromIntegral twid) c file+selectCommand (MuteReplies uname twid) _ file = do+    muted <- muteRepliers uname (fromIntegral twid) file+    putStrLn "Muted:"+    traverse_ putStrLn muted+selectCommand (MuteMentions uname) _ file = muteMentions uname file selectCommand (Timeline maybeNum) c file = putStrLn =<< showTimeline (fromMaybe 11 maybeNum) c file selectCommand (Mentions maybeNum) c file = putStrLn =<< showTweets c <$> mentions (fromMaybe 11 maybeNum) file selectCommand (Profile maybeNum n False False) c file = putStrLn =<< showProfile (fromMaybe mempty n) (fromMaybe 11 maybeNum) c file@@ -139,12 +149,15 @@         <> command "rt" (info rt (progDesc "Retweet a tweet"))         <> command "follow" (info fol (progDesc "Follow a user"))         <> command "unfollow" (info unfol (progDesc "Unfollow a user"))+        <> command "replies" (info repliesParser (progDesc "Show replies to a tweet"))         <> command "list" (info list (progDesc "List a user's favorites"))         <> command "dump" (info dump (progDesc "Dump tweets (for debugging)"))         <> command "block" (info blockParser (progDesc "Block a user"))         <> command "unblock" (info unblockParser (progDesc "Unblock a user"))         <> command "mute" (info muteParser (progDesc "Mute a user"))         <> command "unmute" (info unmuteParser (progDesc "Unmute a user"))+        <> command "mute-replies" (info muteRepliesParser (progDesc "Mute everyone who replied to a particular tweet"))+        <> command "mute-mentions" (info muteMentionsParser (progDesc "Mute everyone who mentioned a particular user"))         <> command "mentions" (info mentionsParser (progDesc "Fetch mentions")))     <*> optional (strOption         (long "cred"@@ -239,6 +252,15 @@ getInt = read <$> argument str     (metavar "TWEET_ID"     <> help "ID of tweet")++repliesParser :: Parser Command+repliesParser = Replies <$> user <*> getInt++muteRepliesParser :: Parser Command+muteRepliesParser = MuteReplies <$> user <*> getInt++muteMentionsParser :: Parser Command+muteMentionsParser = MuteMentions <$> user  -- | Parser for the user subcommand profile :: Parser Command
bench/Bench.hs view
@@ -1,10 +1,9 @@ module Main where  import           Criterion.Main-import qualified Data.ByteString             as BS+import qualified Data.ByteString  as BS import           Text.Megaparsec import           Web.Tweet.Parser-import           Web.Tweet.Parser.FastParser  setupEnv :: IO BS.ByteString setupEnv = BS.readFile "test/data"@@ -15,13 +14,7 @@ main :: IO () main =     defaultMain [-            env setupEnv $ \ file ->-            bgroup "aeson parser"-                  [ bench "226" $ whnf fast file ]-            , env setupEnv' $ \ file ->+              env setupEnv' $ \ file ->               bgroup "handrolled parser"                   [ bench "226" $ whnf (parse parseTweet) file ]             ]-    where-        fast = fmap (fmap fromFast) . fastParse-
cabal.project.local view
@@ -1,2 +1,3 @@ constraints: tweet-hs +development optimization: 2+max-backjumps: 40000
src/Web/Tweet.hs view
@@ -40,7 +40,6 @@     ) where  import           Control.Monad-import           Data.Default import           Data.List.Split        (chunksOf) import           Data.Maybe import           Lens.Micro@@ -53,7 +52,7 @@  -- | Tweet a string given a path to credentials; return the id of the status. ----- > basicTweet "On the airplane." ".cred"+-- > basicTweet "On the airplane." ".cred.toml" basicTweet :: String -> FilePath -> IO Int basicTweet contents = tweetData (mkTweet contents) @@ -88,4 +87,4 @@  -- | Make a `Tweet` with only the contents. mkTweet :: String -> Tweet-mkTweet contents = over status (const contents) def+mkTweet contents = over status (pure contents) pricklyTweet
src/Web/Tweet/API.hs view
@@ -6,7 +6,11 @@ import           Control.Composition import           Control.Monad import qualified Data.ByteString.Lazy.Char8 as BSL+import           Data.Containers.ListUtils  (nubOrd)+import           Data.Foldable              (traverse_)+import           Data.Functor               (($>)) import           Data.Maybe                 (isJust)+import qualified Data.Set                   as S import           Data.Void import           Lens.Micro import           Lens.Micro.Extras@@ -23,13 +27,13 @@ getAll :: String -> Maybe Int -> FilePath -> IO Timeline getAll sn maxId filepath = do     tweets <- either (error "Parse tweets failed") id <$> getProfileMax sn 200 filepath maxId-    let lastId = _tweetId <$> tweets^?_last+    let lastId = _tweetId <$> tweets ^? _last     if lastId == maxId then         pure []     else         do             if isJust lastId-                then putStrLn $ "fetching tweets since " ++ show (lastId^?!_Just) ++ "..."+                then putStrLn $ "fetching tweets since " ++ show (lastId ^?! _Just) ++ "..."                 else pure ()             next <- getAll sn lastId filepath             pure (tweets ++ next)@@ -79,6 +83,86 @@ -- | Get mentions and parse response as a list of tweets mentionsMem :: Int -> Config -> IO (Either (ParseErrorBundle String Void) Timeline) mentionsMem = fmap (getTweets . BSL.toStrict) .* mentionsRawMem++searchRaw :: String -> FilePath -> IO BSL.ByteString+searchRaw str = getRequest ("https://api.twitter.com/1.1/search/tweets.json" ++ str)++searchMentionsRaw :: Maybe Int -- ^ Max ID+                  -> String -- ^ Username+                  -> FilePath+                  -> IO BSL.ByteString+searchMentionsRaw maxid uname = searchRaw ("?q=to%3A" ++ uname ++ maxidUrl)+    where maxidUrl = case maxid of+            Just id' -> "&max_id=" ++ show id'+            Nothing  -> ""++searchRepliesRaw :: Maybe Int -- ^ Max ID+                 -> String -- ^ Username+                 -> Int -- ^ Tweet ID+                 -> FilePath+                 -> IO BSL.ByteString+searchRepliesRaw maxid uname twid = searchRaw ("?q=to%3A" ++ uname ++ "&since_id=" ++ show twid ++ maxidUrl)+    where maxidUrl = case maxid of+            Just id' -> "&max_id=" ++ show id'+            Nothing  -> ""++loopMentions :: Maybe Int -> Maybe Int -> S.Set String -> String -> FilePath -> IO ()+loopMentions pastMax maxid alreadyMuted uname fp =+    if maxid == pastMax+        then pure ()+        else do+            next <- searchMentions maxid uname fp+            case next of+                Right [] -> pure ()+                Left{} -> pure ()+                Right tws -> do+                    let newMax = minimum (_tweetId <$> tws)+                    let toMute = [ tw | tw <- nubOrd (_screenName <$> tws), tw `S.notMember` alreadyMuted ]+                    let modAlready = thread [ S.insert k | k <- toMute ]+                    putStrLn "Muted:"+                    traverse_ (\u -> mute u fp *> putStrLn u) toMute+                    loopMentions maxid (Just newMax) (modAlready alreadyMuted) uname fp++muteMentions :: String -- ^ Screen name+             -> FilePath+             -> IO ()+muteMentions = loopMentions (Just 0) Nothing mempty++loopReplies :: Maybe Int -> Maybe Int -> String -> Int -> FilePath -> IO (Either (ParseErrorBundle String Void) Timeline)+loopReplies pastMax maxid uname twid fp =+    if maxid == pastMax+        then pure (Right [])+        else do+            next <- searchReplies maxid uname twid fp+            case next of+                Right [] -> pure (Right [])+                Left x -> pure (Left x)+                Right tws -> let newMax = minimum (_tweetId <$> tws)+                        in fmap (tws ++) <$> loopReplies maxid (Just newMax) uname twid fp++searchMentions :: Maybe Int -> String -> FilePath -> IO (Either (ParseErrorBundle String Void) Timeline)+searchMentions = fmap (getTweets . BSL.toStrict) .** searchMentionsRaw++searchReplies :: Maybe Int -> String -> Int -> FilePath -> IO (Either (ParseErrorBundle String Void) Timeline)+searchReplies = fmap (getTweets . BSL.toStrict) .*** searchRepliesRaw++getReplies :: String -- ^ Screen name+           -> Int -- ^ Tweet+           -> FilePath+           -> IO (Either (ParseErrorBundle String Void) Timeline)+getReplies str twid = fmap (fmap (filter p)) . loopReplies (Just 0) Nothing str twid+    where p entity = _replyTo entity == Just twid++muteRepliers :: String+             -> Int+             -> FilePath+             -> IO [String]+muteRepliers str twid fp = do+    us <- getReplies str twid fp+    case us of+        Left{} -> pure []+        -- TODO: add a delay so I don't get rate limited?+        Right xs -> let toMute = nubOrd (_screenName <$> xs) in traverse (\u -> mute u fp $> u) toMute  -- | Gets mentions mentionsRaw :: Int -> FilePath -> IO BSL.ByteString
src/Web/Tweet/API/Internal.hs view
@@ -22,6 +22,9 @@ showBest' :: String -> Int -> Bool -> FilePath -> IO String showBest' sn n color = fmap (showTweets color . pure . take n . hits') . getAll sn Nothing +showReplies :: String -> Int -> Bool -> FilePath -> IO String+showReplies uname twid color = fmap (showTweets color) . getReplies uname twid+ -- | Display user timeline showTimeline :: Int -> Bool -> FilePath -> IO String showTimeline count color = fmap (showTweets color) . getTimeline count
src/Web/Tweet/Parser.hs view
@@ -1,35 +1,44 @@ {-# OPTIONS_GHC -fno-warn-unused-do-bind #-}+{-# LANGUAGE LambdaCase        #-} {-# LANGUAGE OverloadedStrings #-}  -- | Module containing parsers for tweet and response data. module Web.Tweet.Parser ( parseTweet                         , getData ) where -import Control.Composition ((.*))-import qualified Data.ByteString as BS-import Text.Megaparsec.Byte-import Text.Megaparsec.Byte.Lexer as L-import Text.Megaparsec-import Web.Tweet.Types-import qualified Data.Map as M-import qualified Data.Text as T-import qualified Data.Text.Encoding as TE-import Control.Monad-import Data.Maybe-import Data.Void+import           Control.Composition        ((.*))+import           Control.Monad+import qualified Data.ByteString            as BS+import           Data.List                  (isInfixOf)+import qualified Data.Map                   as M+import           Data.Maybe+import qualified Data.Text                  as T+import qualified Data.Text.Encoding         as TE+import           Data.Void+import           Text.Megaparsec+import           Text.Megaparsec.Byte+import           Text.Megaparsec.Byte.Lexer as L+import           Web.Tweet.Types  type Parser = Parsec Void String  -- | Parse some number of tweets parseTweet :: Parser Timeline-parseTweet = many (try getData <|> (const (TweetEntity "" "" "" 0 mempty Nothing 0 0) <$> eof))+parseTweet = many (try getData <|> (TweetEntity "" Nothing "" "" 0 mempty Nothing 0 0 <$ eof)) +inReplyTo :: String -> Maybe Int+inReplyTo str =+    if "null" `isInfixOf` str+        then Nothing+        else pure (read str)+ -- | Parse a single tweet's: n, text, fave count, retweet count getData :: Parser TweetEntity getData = do-    idNum <- read <$> filterStr "id" +    idNum <- read <$> filterStr "id"     t <- filterStr "text"     skipMentions+    irt <- inReplyTo <$> filterStr "in_reply_to_status_id"     n <- filterStr "name"     screenn' <- filterStr "screen_name"     --withheldCountries <- (catMaybes . sequence) <$> optional filterList@@ -40,12 +49,12 @@         "false" -> do             rts <- read <$> filterStr "retweet_count"             faves <- read <$> filterStr "favorite_count"-            pure (TweetEntity t n screenn' idNum withheldCountries Nothing rts faves)+            pure (TweetEntity t irt n screenn' idNum withheldCountries Nothing rts faves)         "true" -> do             q <- parseQuoted             rts <- read <$> filterStr "retweet_count"             faves <- read <$> filterStr "favorite_count"-            pure $ TweetEntity t n screenn' idNum withheldCountries q rts faves+            pure $ TweetEntity t irt n screenn' idNum withheldCountries q rts faves         _ -> error "is_quote_status must have a value of \"true\" or \"false\""  -- | Parse a the quoted tweet@@ -55,7 +64,7 @@     contents <- optional $ string "\",\"quoted_status"     case contents of         (Just _) -> pure <$> getData-        _ -> pure Nothing+        _        -> pure Nothing  -- | Skip a set of square brackets [] skipInsideBrackets :: Parser ()@@ -91,7 +100,7 @@ unicodeChar :: Parser Char unicodeChar = toEnum . fromIntegral . f <$> go     where go = string "\\u" >> count 4 anySingle-          f = fromHex . filterEmoji . BS.pack . fmap (fromIntegral . fromEnum) +          f = fromHex . filterEmoji . BS.pack . fmap (fromIntegral . fromEnum)  emojiChar :: Parser Char emojiChar = go a@@ -103,7 +112,7 @@     where         go []             = []         go (a:b:c:d:rest) = let sym = convert16 [a,b] [c,d] in sym : go rest-        go _ = error "Internal error: decodeUtf16 failed."+        go _              = error "Internal error: decodeUtf16 failed."         convert16 x y = BS.pack [(read . ("0x"<>)) x, (read . ("0x"<>)) y]  -- | helper function to ignore emoji@@ -116,8 +125,8 @@     single '&'     innards <- many $ anySingleBut ';'     single ';'-    pure . (\case -        (Just a) -> a +    pure . (\case+        (Just a) -> a         Nothing -> '?') $ M.lookup innards (M.fromList [("amp",'&'),("gt",'>'),("lt",'<'),("quot",'"'),("euro",'€'),("ndash",'–'),("mdash",'—')])  -- | Parse escaped characters
− src/Web/Tweet/Parser/FastParser.hs
@@ -1,38 +0,0 @@-{-# LANGUAGE DeriveGeneric   #-}-{-# LANGUAGE RecordWildCards #-}--module Web.Tweet.Parser.FastParser ( fastParse-                                   , fromFast-                                   , FastTweet (..)-                                   ) where--import           Data.Aeson-import qualified Data.ByteString      as BS-import qualified Data.ByteString.Lazy as BSL-import           Data.Text            (unpack)-import qualified Data.Text            as T-import           GHC.Generics-import           Web.Tweet.Types      hiding (name, text)--data FastTweet = FastTweet-    { idNum          :: !Int-    , text           :: !T.Text-    , user           :: User-    , quoted_status  :: Maybe FastTweet-    , retweet_count  :: !Int-    , favorite_count :: !Int-    } deriving (Generic, Eq, Show)--data User = User { name        :: !T.Text-                 , screen_name :: !T.Text }-                 deriving (Generic, Eq, Show)--instance FromJSON FastTweet--instance FromJSON User--fromFast :: FastTweet -> TweetEntity-fromFast FastTweet{..} = TweetEntity (unpack text) (unpack . name $ user) (unpack . screen_name $ user) idNum mempty (fmap fromFast quoted_status) retweet_count favorite_count--fastParse :: BS.ByteString -> Either String [FastTweet]-fastParse = eitherDecode . BSL.fromStrict
src/Web/Tweet/Types.hs view
@@ -1,11 +1,6 @@-{-# LANGUAGE DeriveAnyClass #-}-{-# LANGUAGE DeriveGeneric  #-}- -- | Exports the `Tweet` type, a datatype for building tweets easily module Web.Tweet.Types where -import           Data.Default-import           GHC.Generics import           Lens.Micro import           Web.Authenticate.OAuth @@ -14,11 +9,12 @@     { _status  :: String     , _handles :: [String]     , _replyID :: Maybe Int-    } deriving (Generic, Default)+    }  -- | Data type for tweets as they are returned data TweetEntity = TweetEntity     { _text       :: String+    , _replyTo    :: Maybe Int     , _name       :: String     , _screenName :: String     , _tweetId    :: Int@@ -26,7 +22,10 @@     , _quoted     :: Maybe TweetEntity     , _retweets   :: Int     , _favorites  :: Int-    } deriving (Generic, Default, Eq, Show)+    } deriving (Eq)++pricklyTweet :: Tweet+pricklyTweet = Tweet undefined [] Nothing  -- | Stores data like (name, text, favoriteCount, retweetCount) type Timeline = [TweetEntity]
src/Web/Tweet/Utils.hs view
@@ -2,7 +2,6 @@ module Web.Tweet.Utils (     hits   , hits'-  , getTweetsFast   , getTweets   , displayTimeline   , displayTimelineColor@@ -15,15 +14,14 @@   ) where  import           Control.Composition-import qualified Data.ByteString             as BS2-import qualified Data.ByteString.Char8       as BS+import qualified Data.ByteString        as BS2+import qualified Data.ByteString.Char8  as BS import           Data.List import           Data.List.Extra import           Data.Void import           Lens.Micro.Extras import           Text.Megaparsec import           Web.Tweet.Parser-import           Web.Tweet.Parser.FastParser hiding (text) import           Web.Tweet.Types import           Web.Tweet.Utils.Colors @@ -51,14 +49,9 @@ getTweets :: BS2.ByteString -> Either (ParseErrorBundle String Void) Timeline getTweets = parse parseTweet "" . BS.unpack --- | Get a list of tweets from a response, returning author, favorites, retweets, and content.--- This version uses aeson, which it's far faster, but also has worse error messages.-getTweetsFast :: BS2.ByteString -> Either String Timeline-getTweetsFast = fmap (fmap fromFast) . fastParse- -- | Display Timeline without color displayTimeline :: Timeline -> String-displayTimeline (TweetEntity content u sn idTweet _ Nothing rts fave:rest) = concat [u+displayTimeline (TweetEntity content _ u sn idTweet _ Nothing rts fave:rest) = concat [u     , " ("     , sn     , ")"@@ -75,7 +68,7 @@     , show idTweet     ,"\n\n"     ,displayTimeline rest]-displayTimeline (TweetEntity content u sn idTweet _ (Just q) rts fave:rest) = concat [u+displayTimeline (TweetEntity content _ u sn idTweet _ (Just q) rts fave:rest) = concat [u     , " ("     , sn     , ")"@@ -104,7 +97,7 @@  -- | Display Timeline in color displayTimelineColor :: Timeline -> String-displayTimelineColor (TweetEntity content u sn idTweet _ Nothing rts fave:rest) = concat [toYellow u+displayTimelineColor (TweetEntity content _ u sn idTweet _ Nothing rts fave:rest) = concat [toYellow u     , " ("     , sn     , ")"@@ -120,7 +113,7 @@     , toBlue (show idTweet)     , "\n\n"     , displayTimelineColor rest]-displayTimelineColor (TweetEntity content u sn idTweet _ (Just q) rts fave:rest) = concat [toYellow u+displayTimelineColor (TweetEntity content _ u sn idTweet _ (Just q) rts fave:rest) = concat [toYellow u     , " ("     , sn     , ")"@@ -152,7 +145,7 @@ -- | sort tweets by most successful sortTweets :: Timeline -> Timeline sortTweets = sortBy compareTweet-    where compareTweet (TweetEntity _ _ _ _ _ _ r1 f1) (TweetEntity _ _ _ _ _ _ r2 f2) = compare (2*r2 + f2) (2*r1 + f1)+    where compareTweet (TweetEntity _ _ _ _ _ _ _ r1 f1) (TweetEntity _ _ _ _ _ _ _ r2 f2) = compare (2*r2 + f2) (2*r1 + f1)  -- | helper function to get the key as read from a file keyLinePie :: String -> String
src/Web/Tweet/Utils/API.hs view
@@ -9,6 +9,7 @@   , urlString   , strEncode ) where +import           Control.Monad              ((<=<)) import qualified Data.ByteString.Char8      as BS import qualified Data.ByteString.Lazy.Char8 as BSL import           Data.Char@@ -32,11 +33,11 @@  -- | Make a GET request to twitter given a request string getRequest :: String -> FilePath -> IO BSL.ByteString-getRequest = flip ((. getRequestMem) . (>>=) . mkConfigToml)+getRequest str = getRequestMem str <=< mkConfigToml  -- | Make a POST request to twitter given a request string postRequest :: String -> FilePath -> IO BSL.ByteString-postRequest = flip ((. postRequestMem) . (>>=) . mkConfigToml)+postRequest str = postRequestMem str <=< mkConfigToml  -- | Make a POST request to twitter given a request string postRequestMem :: String -> Config -> IO BSL.ByteString
− test/Spec.hs
@@ -1,17 +0,0 @@-{-# LANGUAGE OverloadedStrings #-}--import           System.Environment-import           Test.Hspec-import           Web.Tweet.Parser.FastParser-import           Web.Tweet.Sign--main :: IO ()-main = hspec $-    describe "fastParse" $ do-    -- file <- runIO $ BS.readFile "test/data"-    config <- runIO $ (<> "/.cred") <$> getEnv "HOME"-    configToml <- runIO $ (<> "/.cred.toml") <$> getEnv "HOME"-    parallel $ it "parses sample tweets wrong" $-        fastParse "" `shouldBe` Left "Error in $: not enough input"-    parallel $ it "parses a config file the same way with the toml parser" $-        ((==) <$> mkConfigToml configToml <*> mkConfig config) >>= (`shouldBe` True)
tweet-hs.cabal view
@@ -1,68 +1,62 @@-cabal-version: >=1.10-name: tweet-hs-version: 1.0.1.43-license: BSD3-license-file: LICENSE-copyright: 2016-2018 Vanessa McHale-maintainer: vamchale@gmail.com-author: Vanessa McHale-stability: stable-homepage: https://github.com/vmchale/command-line-tweeter#readme-synopsis: Command-line tool for twitter-description:-    a Command Line Interface Tweeter-category: Web-build-type: Simple+cabal-version:      >=1.10+name:               tweet-hs+version:            1.0.2.1+license:            BSD3+license-file:       LICENSE+copyright:          2016-2019 Vanessa McHale+maintainer:         vamchale@gmail.com+author:             Vanessa McHale+stability:          stable+synopsis:           Command-line tool for twitter+description:        a Command Line Interface Tweeter+category:           Web+build-type:         Simple extra-source-files:     README.md-    CHANGELOG.md     cabal.project.local     bash/mkCompletions     test/data  source-repository head-    type: git+    type:     git     location: https://hub.darcs.net/vmchale/tweet-hs  flag llvm-fast-    description:-        Enable build with llvm backend-    default: False+    description: Enable build with llvm backend+    default:     False  flag library-    description:-        Don't build an executable-    default: False+    description: Don't build an executable+    default:     False  flag development-    description:-        Enable -Werror-    default: False-    manual: True+    description: Enable -Werror+    default:     False+    manual:      True  flag parallel-gc-    description:-        Use parallel garbage collector-    default: False+    description: Use parallel garbage collector+    default:     False  library     exposed-modules:         Web.Tweet-        Web.Tweet.Parser.FastParser         Web.Tweet.Parser         Web.Tweet.Sign         Web.Tweet.API         Web.Tweet.Utils-    hs-source-dirs: src++    hs-source-dirs:   src     other-modules:         Web.Tweet.Types         Web.Tweet.Utils.Colors         Web.Tweet.API.Internal         Web.Tweet.Utils.API+     default-language: Haskell2010-    default-extensions: LambdaCase-    ghc-options: -Wall -Wincomplete-uni-patterns-                 -Wincomplete-record-updates+    ghc-options:+        -Wall -Wincomplete-uni-patterns -Wincomplete-record-updates+     build-depends:         base >=4.11 && <5,         http-client-tls -any,@@ -75,75 +69,59 @@         microlens -any,         unordered-containers -any,         htoml-megaparsec >=2.1.0.0,-        data-default -any,         text -any,-        containers -any,+        containers >=0.6.0.0,         ansi-wl-pprint -any,         composition-prelude -any,-        extra -any,-        aeson -any-    +        extra -any+     if flag(development)         ghc-options: -Werror  executable tweet-    main-is: Main.hs-    hs-source-dirs: app-    other-modules:-        Paths_tweet_hs+    main-is:          Main.hs+    hs-source-dirs:   app+    other-modules:    Paths_tweet_hs     default-language: Haskell2010-    ghc-options: -Wall -Wincomplete-uni-patterns-                 -Wincomplete-record-updates+    ghc-options:+        -Wall -Wincomplete-uni-patterns -Wincomplete-record-updates+     build-depends:         base -any,         tweet-hs -any,         optparse-applicative -any,         directory -any,         bytestring -any-    +     if flag(library)         buildable: False-    +     if flag(llvm-fast)         ghc-options: -fllvm -optlo-O3 -O3-    +     if flag(parallel-gc)         ghc-options: -rtsopts -with-rtsopts=-N-    -    if flag(development)-        ghc-options: -Werror -test-suite tweeths-test-    type: exitcode-stdio-1.0-    main-is: Spec.hs-    hs-source-dirs: test-    default-language: Haskell2010-    ghc-options: -threaded -rtsopts -with-rtsopts=-N -Wall-                 -Wincomplete-uni-patterns -Wincomplete-record-updates-    build-depends:-        base -any,-        tweet-hs -any,-        hspec -any-         if flag(development)         ghc-options: -Werror  benchmark tweeths-bench-    type: exitcode-stdio-1.0-    main-is: Bench.hs-    hs-source-dirs: bench+    type:             exitcode-stdio-1.0+    main-is:          Bench.hs+    hs-source-dirs:   bench     default-language: Haskell2010-    ghc-options: -Wall -Wincomplete-uni-patterns-                 -Wincomplete-record-updates+    ghc-options:+        -Wall -Wincomplete-uni-patterns -Wincomplete-record-updates+     build-depends:         base -any,         criterion -any,         tweet-hs -any,         bytestring -any,         megaparsec -any-    +     if flag(llvm-fast)         ghc-options: -fllvm -optlo-O3 -O3-    +     if flag(development)         ghc-options: -Werror