>
cabal update
cabal install regex-tdfa curl feed utf8-string
> import Text.Regex.TDFA ( (=~) )
> import Data.List ( intercalate )
> import Network.Curl ( curlGetString )
> import Network.Curl.Opts
> import Text.Feed.Import ( parseFeedString )
> import Text.Feed.Query ( getFeedItems , getItemSummary )
> import Codec.Binary.UTF8.String ( encodeString )
> feedUrl = "https://twitter.com/statuses/user_timeline/22251772.rss"
> getTweet :: IO String
> getTweet = do
> (_,feed) <- curlGetString feedUrl []
> return $ getMsg $ head $ getItems feed
> where
> getItems = maybe (error "rss parsing failed!" ) getFeedItems . parseFeedString
> getMsg = maybe (error "rss-item parsing failed!" ) format . getItemSummary
> format = unwords . ( "twitter:" : ) . tail . words . encodeString
curlGetString :: URLString -> [CurlOption] -> IO (CurlCode, String)
takes a url address, a list of options, and issues an operation code ( CurlOk
, if everything went well) and a server response. In this case, we specify our twitter-rss feed as the address, and do not give any options. We do not pay attention to the completion code. And here we call the content part of the answer feed.
getItems feed
), we get the list, we take the first element ( head
) from it, we extract the message from it ( getMsg
) and return it to the output.
getItems
first uses the parseFeedString
function (from the Feed library), it has the type ( String -> Maybe Feed
), that is, it receives a string with any porridge at the input from rss-tags, and gives an abstract type of feed, with which you can already do something. Since the Maybe Feed
value is returned (“Maybe a feed”), it may happen that the parser chokes and returns Nothing
- then we give an error with the text «rss parsing failed!». If the parse goes well, we get a value of ( Just
), and then applied to not in function getFeedItems
, which is extracted from the feed elements in a list. This branching ( Nothing
or Just ...
) implemented a standard feature maybe
.
getItems
we get a list of feed items: [Item]
. We only need the first one (that is, the last one by date). We take its function head
. And now we want to pick out the message text from it: getMsg
.
getItems
: first, getItemSummary
is used, which returns a Maybe String
. If the content could not be retrieved, we give the corresponding error. Otherwise, format the received message.
> email = " e-mail"
> uid = " user-id "
> pass = " "
curlGetString
), with the corresponding tricky addresses. They are built as follows:
> param :: (String, String) -> String
> param (key, value) = key ++ "=" ++ value ++ "&"
> formUrl :: String -> [(String, String)] -> String -> String
> formUrl base opts sid = base ++ ( concatMap param (opts ++ [( "id" ,uid)]) ) ++ sid
base
, the list of options opts
(in the form of pairs), and the session identifier sid
(about it later).
map
takes a function and a list, and applies the function to each element of the list. That is, from the list of pairs (, )
, it makes the list of strings "=&"
. A concat
simply sticks all these strings together ( concatMap = concat . map
).
uid
), so in order not to write this option every time, we add it in the definition of this function.
> login :: IO String
> login = do
> (_,headers) <- curlGetString authUrl [CurlHeader True]
> return ( headers =~ "sid=[a-z0-9]*" :: String )
> where
> authUrl = formUrl "http://login.userapi.com/auth?"
> [( "site" , "2" ), ( "fccode" , "0" ),
> ( "fcsid" , "0" ), ( "login" , "force" ),
> ( "email" ,email), ( "pass" ,pass)] ""
formUrl
, and our email and password are inserted in the last two options. And the sid parameter is left empty - we don’t have one yet, and for this very purpose we wrote the login
function.
authUrl
, which returns headers headers
(the CurlHeader
option is set for this). They actually cookies, redirect address and something else. Here is the address where the server sends us, and what we are looking for is hidden. With the help of a secret regular expression technique, the coveted session id, of the form "sid = 35dfe55b09b599c9fx622fcx8cd83a37", is pulled out of headers
.
(_,answer) <- curlGetString someUrl []
someUrl
is the corresponding request (see the documentation), and answer
is the server response. Here is the status change request:
> setActivityUrl :: String -> String -> String
> setActivityUrl text = formUrl "http://userapi.com/data?" [( "act" , "set_activity" ), ( "text" , text)]
formUrl
function, sid
, is not specified. This is a partial application - the function has 3 parameters, and we gave only 2, it means we have a function of the remaining one parameter. That is, setActivityUrl
is a function not only of the text
parameter (the actual status itself), but also of the second parameter sid
, which appears to be written to the right.
> escSpaces = intercalate "%20" . words
intercalate
function).
> setStatus :: String -> String -> IO ()
> setStatus text sid = do
> (_,answer) <- curlGetString url []
> if answer =~ " \" ok \" :1" :: Bool
> then putStrLn text
> else error "something is bad with vkontakte-api..."
> where
> url = setActivityUrl (escSpaces text) sid
setStatus text sid = curlGetString (setActivityUrl (escSpaces text) sid) []
"ok":1
, then everything is fine - the status has changed, which we inform the user (that is, that is).
> main = do
> tweet <- getTweet
> sid <- login
> setStatus tweet sid
runhaskell _.lhs
Source: https://habr.com/ru/post/85894/