clit 0.4.0.2 → 0.4.0.3
raw patch · 5 files changed
+53/−18 lines, 5 filesdep ~clit
Dependency ranges changed: clit
Files
- clit.cabal +2/−2
- src/Web/Tweet.hs +17/−3
- src/Web/Tweet/Exec.hs +12/−4
- src/Web/Tweet/Types.hs +1/−1
- src/Web/Tweet/Utils.hs +21/−8
clit.cabal view
@@ -1,5 +1,5 @@ name: clit-version: 0.4.0.2+version: 0.4.0.3 cabal-version: >=1.10 build-type: Simple license: BSD3@@ -64,7 +64,7 @@ main-is: Main.hs build-depends: base >=4.9.1.0 && <4.10,- clit >=0.4.0.2 && <0.5+ clit >=0.4.0.3 && <0.5 default-language: Haskell2010 hs-source-dirs: app
src/Web/Tweet.hs view
@@ -44,7 +44,7 @@ import qualified Data.ByteString.Lazy.Char8 as BSL import qualified Data.Text as T import Data.Text.Encoding-import Data.Char (toLower)+import Data.Char import Web.Tweet.Types import Web.Tweet.Utils import Control.Monad@@ -55,6 +55,7 @@ import Web.Authenticate.OAuth import Web.Tweet.Sign import Data.List.Utils+import qualified Data.Text as T -- | thread tweets together nicely. Takes a string, a list of handles to reply to, plus the ID of the status you're replying to. -- If you need to thread tweets without replying, pass a `Nothing` as the third argument.@@ -105,6 +106,7 @@ request <- signRequest filepath $ initialRequest { method = "POST" } responseInt request manager +-- | Get tweets (text only) for some user getRaw screenName filepath = do let requestString = "?screen_name=" ++ screenName ++ "&count=3200" manager <- newManager tlsManagerSettings@@ -113,23 +115,30 @@ let fromRight (Right a) = a map (view _2) . fromRight . getTweets . BSL.unpack <$> responseBS request manager +-- | Get user profile given screen name and how many tweets to return getProfile screenName count filepath = do let requestString = "?screen_name=" ++ screenName ++ "&count=" ++ (show count) manager <- newManager tlsManagerSettings initialRequest <- parseRequest ("https://api.twitter.com/1.1/statuses/user_timeline.json" ++ requestString) request <- signRequest filepath $ initialRequest { method = "GET"}- responseBS request manager+ responseBS request manager -- TODO getTweets . BSL.unpack <$> responseBS request manager +-- | Show your DMs, given how many to return and whether or not to use color. showDMs count color filepath = showTweets color <$> getDMs count filepath -showProfile :: Show t => [Char] -> t -> Bool -> FilePath -> IO String+-- | Show a user profile given screen name, how many tweets to return (API+-- maximum is 3200), and whether to print them in color.+showProfile :: Show t => String -> t -> Bool -> FilePath -> IO String showProfile screenName count color filepath = showTweets color <$> getProfile screenName count filepath +-- | Display user timeline showTimeline count color filepath = showTweets color <$> getTimeline count filepath +-- | Display user timeline in color showTweets color = (either show id) . (fmap (if color then displayTimelineColor else displayTimeline)) +-- | Get user's DMs. getDMs count filepath = do let requestString = "?count=" ++ (show count) manager <- newManager tlsManagerSettings@@ -137,6 +146,7 @@ request <- signRequest filepath $ initialRequest { method = "GET" } getTweets . BSL.unpack <$> responseBS request manager +-- | Get a timeline getTimeline count filepath = do let requestString = "?count=" ++ (show count) manager <- newManager tlsManagerSettings@@ -144,17 +154,21 @@ request <- signRequest filepath $ initialRequest { method = "GET" } getTweets . BSL.unpack <$> responseBS request manager +-- | Delete a tweet given its id deleteTweet id filepath = do manager <- newManager tlsManagerSettings initialRequest <- parseRequest ("https://api.twitter.com/1.1/statuses/destroy/" ++ (show id) ++ ".json") request <- signRequest filepath $ initialRequest { method = "POST" } void $ responseBS request manager +-- | Return HTTP request's result as a bytestring responseBS :: Request -> Manager -> IO BSL.ByteString responseBS request manager = do response <- httpLbs request manager let code = statusCode $ responseStatus response putStr $ if (code == 200) then "" else "failed :(\n error code: " ++ (show code) ++ "\n"+ -- print $ generalCategory ('📚')+ -- print $ (toEnum (0x1F48C) :: Char) -- (0xF079) pure . responseBody $ response -- | print output of a request and return status id as an `Int`.
src/Web/Tweet/Exec.hs view
@@ -1,6 +1,7 @@ -- | Provides IO action that parses command line options and tweetInputs from stdin module Web.Tweet.Exec ( exec- , Program (Program)) where+ , Program (..)+ , Command (..)) where import Web.Tweet import Options.Applicative@@ -14,6 +15,7 @@ -- | Data type for our program: one optional path to a credential file, (optionally) the number of tweetInputs to make, the id of the status you're replying to, and a list of users you wish to mention. data Program = Program { subcommand :: Command , cred :: Maybe FilePath } +-- | Data type for a data Command = Timeline { count :: Maybe Int , color :: Bool } | SendInput { tweetInputs :: Maybe Int, replyId :: Maybe String, replyHandles :: Maybe [String] } | Profile { count :: Maybe Int , color :: Bool , screenName :: String } @@ -24,6 +26,7 @@ fromStdIn :: Int -> FilePath -> IO () fromStdIn = threadStdIn [] Nothing +-- | Tweet string given to us, as parsed from the command line fromCLI :: String -> Int -> FilePath -> IO () fromCLI str = thread str [] Nothing @@ -42,7 +45,7 @@ <> progDesc "SendInput from stdin!" <> header "clit - a Command Line Interface SendInputer") --- | Executes program+-- | Executes program given parsed `Program` select :: Program -> IO () select (Program (Send (Just n) Nothing Nothing input) Nothing) = fromCLI input n =<< (++ "/.cred") <$> getHomeDirectory select (Program (Send Nothing Nothing Nothing input) Nothing) = fromCLI input 15 =<< (++ "/.cred") <$> getHomeDirectory@@ -106,6 +109,7 @@ <> metavar "CREDENTIALS" <> help "path to credentials")) +-- | Parser for the view subcommand timeline :: Parser Command timeline = Timeline <$> (optional $ read <$> strOption@@ -118,12 +122,14 @@ <> short 'l' <> help "Display timeline with colorized terminal output.") +-- | Parser for the raw subcommand raw :: Parser Command raw = Raw <$> argument str (metavar "SCREEN_NAME" <> help "Screen name of user whose tweetInputs you want in bulk.") +-- | Parser for the user subcommand profile :: Parser Command profile = Profile <$> (optional $ read <$> strOption@@ -139,6 +145,7 @@ (metavar "SCREEN_NAME" <> help "Screen name of user you want to view.") +-- | Parser for the send subcommand tweet :: Parser Command tweet = Send <$> (optional $ read <$> strOption@@ -150,15 +157,16 @@ (long "reply" <> short 'r' <> help "id of status to reply to - be sure to include their handle, e.g. @my_build_errors"))- <*> (optional $ (some $ strOption $+ <*> (optional (some $ strOption $ (long "handle" <> short 'h' <> metavar "HANDLE1" <> help "Handles to include in replies")))- <*> (intercalate " " <$> (some $ argument str+ <*> (unwords <$> (some $ argument str (metavar "TEXT" <> help "text of tweet to be sent"))) +-- | Parser for the input command tweetInput :: Parser Command tweetInput = SendInput <$> (optional $ read <$> strOption
src/Web/Tweet/Types.hs view
@@ -22,7 +22,7 @@ , _replyID :: Maybe Int } deriving (Generic, Default) --- | Stores data like (text, screenName, favoriteCount, retweetCount)+-- | Stores data like (name, text, favoriteCount, retweetCount) type Timeline = [(String, String, String, String)] makeLenses ''Tweet
src/Web/Tweet/Utils.hs view
@@ -5,6 +5,7 @@ import Text.Megaparsec.String import Text.Megaparsec.Lexer as L import Text.Megaparsec+import Data.Char import Data.Monoid import Data.Maybe import Web.Tweet.Types@@ -12,29 +13,28 @@ import Text.PrettyPrint.ANSI.Leijen hiding ((<$>), char, (<>), string) import Control.Lens.Tuple --- example text--- Emma Watson, seins nus dans \u00abVanity Fair\u00bb, f\u00e9ministe ou hypocrite? \u27a1\ufe0f https:\/\/t.co\/MIPx1EpRSK https:\/\/t.co\/uSnLayoPi6---"screen_name":"C_mrmt"---toUnicode :: Parser String---toUnicode "\u00e9" = -+-- `FIXME` parseDMs = zip <$> (extractEvery 2 <$> filterStr "screen_name") <*> (filterStr "text") where extractEvery n = map snd . filter ((== n) . fst) . zip (cycle [1..n]) +-- | Display Timeline without color displayTimeline :: Timeline -> String displayTimeline ((user,content,fave,rts):rest) = (user <> ":\n " <> content) <> "\n " <> "♥ " {-- ♡💛--} <> fave <> " ♺ " <> rts <> "\n" <> (displayTimeline rest) -- displayTimeline [] = [] +-- | Display Timeline in color displayTimelineColor :: Timeline -> String displayTimelineColor ((user,content,fave,rts):rest) = ((show . yellow . text $ user) <> ":\n " <> content) <> "\n " <> (show . red . text $ "♥ ") {-- ♡💛--} <> fave <> (show . green . text $ " ♺ ") <> rts <> "\n" <> (displayTimelineColor rest) -- displayTimelineColor [] = [] --- | Get tweets from a response, disgarding all but author, favorites, retweets, and content+-- | Get a list of tweets from a response, returning author, favorites, retweets, and content. getTweets = parse parseTweet "" +-- | Parse some number of tweets parseTweet :: Parser Timeline parseTweet = many (try getData <|> (const ("","","","") <$> eof)) +-- | Parse a single tweet's: name, text, fave count, retweet count getData :: Parser (String, String, String, String) getData = do text <- filterStr "text"@@ -51,12 +51,14 @@ faves <- filterStr "favorite_count" pure (name, text, faves, rts) +-- | Throw out input until we get to a relevant tag. filterStr :: String -> Parser String filterStr str = do many $ try $ anyChar >> notFollowedBy (string ("\"" <> str <> "\":")) char ',' filterTag str +-- | Parse a field given its tag filterTag :: String -> Parser String filterTag str = do string $ "\"" <> str <> "\":"@@ -65,22 +67,33 @@ want <- many $ noneOf forbidden <|> specialChar '\"' <|> specialChar '/' <|> newlineChar <|> unicodeChar -- specialChar 'u' pure want +-- | Parse a newline newlineChar :: Parser Char newlineChar = do string "\\n" pure '\n' +--emoji :: Parser Char+--emoji = do+-- string "\\u"+ --d83d dc8c 💌++-- | Parser for unicode; twitter will give us something like "/u320a" unicodeChar :: Parser Char unicodeChar = do string "\\u"- num <- fromHex <$> count 4 anyChar+ num <- fromHex . filterEmoji <$> count 4 anyChar pure . toEnum . fromIntegral $ num +filterEmoji str = if head str == 'd' then "FFFD" else str++-- | Parse escaped characters specialChar :: Char -> Parser Char specialChar c = do string $ "\\" ++ pure c pure c +-- | Convert a string of four hexadecimal digits to an integer. fromHex :: String -> Integer fromHex = fromRight . (parse (L.hexadecimal :: Parser Integer) "") where fromRight (Right a) = a