ãã®èšäºã§ã¯ããã€ãŒããVKontakteã¹ããŒã¿ã¹ã«åæçš¿ããå°ããªããã°ã©ã ã«çŠç¹ãåœãŠãŸãã
ã¿ã¹ã¯ã¯éåžžã«ã·ã³ãã«ã§ãå®å
šã«ç¬åµçã§ãã ããã¯ãã¹ãŠããã
ãpythonã§ã©ã®ããã«
解決ããããã«ã€ããŠã®Habrã«é¢ãã
èšäºãšphpã«é¢ããåæ§ã®
èšäºãèªãã ãšããäºå®ããå§ãŸããŸããã ã€ã³ã¿ãŒãããäžã§ã¯ãäžéšã®ãªã³ã©ã€ã³ãµãŒãã¹ã§ãããã®ã¿ã¹ã¯å°çšã§ããããã§ãã ããããããã§ã¯ããæ°ã«å
¥ãã®ããŒã«ã䜿çšããŠããã®åçŽãªåé¡ãèªåã§è§£æ±ºããããšãå
šäœã®ç®çã§ãã å®éãphpã®ãœãªã¥ãŒã·ã§ã³ã¯åŸã§åãç®çã§ç»å ŽããŸããã
ããŠãäœãæžããã®ã§ããïŒ ãã¹ã±ã«ã§ãnatÃŒrlichïŒ
ç§ããã¹ãŠãã©ããã£ãŠãã£ããããããŠãããã©ã®ããã«ç¹°ãè¿ããã«ã€ããŠãããå°ã詳ãã説æããŸãã ãããããçè§£ããã®ã«ç¹å¥ãªç¥èã¯å¿
èŠãããŸããã
ãšã³ããªãŒHaskellã®livejournalã®rssããã®åæçš¿ã«é¢ãã2ã€ã®èšäºãš
èšäºã¯ããœãªã¥ãŒã·ã§ã³ã®å®è£
ã«åœ¹ç«ã¡ãŸããã
æåãç§ã¯æ£çŽã«
twitter-apiãä»ããŠtwitterã®ä»äºããããã£ãã®ã§ããã察å¿ãã
ã©ã€ãã©ãªãããã«ãŒããçªããŸããããããã«ã¯æ©èœããããã®ãŸãŸã«ããŠ
ãããŸããã ãããŠãtwitterã¯rssã§æŸéãããŠãããhaskellã§rssãèªãããšã¯ãã§ã«è§£æ±ºãããåé¡ãªã®ã§ãç§ã¯ãã®æ¹æ³ã§è¡ã£ãã
ããã«ãããã¯ããæ®éçãªãœãªã¥ãŒã·ã§ã³ã§ãã ä»»æã®rssãã£ã³ãã«ãvkontakteã«ãããŒããã£ã¹ãã§ããŸãã ããã¯twitter2vkontakteã§ã¯ãªããrss2vkontakteã§ãããšèšãããšãã§ããŸãã
ããã«ã
vkontakte-apiã䜿çšããåä»»è
ã®ããã«ã¹ããŒã¿ã¹ãæ€çŽ¢ããããŒãžãè§£æããŸããã§ããã ããã¯ãã©ã¹ã ãšæããŸãã
æ®ãã¯æåŠçãã¹ã±ã«ã³ãŒãã§ãã ã€ãŸããã³ã¡ã³ãä»ãã®ã³ãŒãã§ã¯ãªããhaskellã®éåžžã®ãœãŒã¹ã§ããã³ãŒãçä»ãã®è©³çްãªã³ã¡ã³ãã§ãã ãã®æçš¿ã¯ãæ¡åŒµåã.lhsã®ãã¡ã€ã«ã«å
šäœãšããŠåçŽã«ä¿åããã€ã³ã¿ãŒããªã¿ãŒ/ã³ã³ãã€ã©ãŒã«æž¡ãããšãã§ããŸãã ãã¹ãŠæ£åžžã«åäœããã¯ãã§ãã
ãã¹ãŠã®äœæ¥ã³ãŒãã¯ãæ¬¡ã®æåã§åŒ·èª¿è¡šç€ºãããŸãïŒ
>
å¿
èŠãªæºåæ¢ã«Haskellã³ã³ãã€ã©ãšã³ã¢ã©ã€ãã©ãªã»ãããçšæãããŠããããšãåæãšããŠããŸãã ããã§ãªãå Žåãããã¯ç°¡åã«ä¿®æ£ã§ããŸã
-Haskell Platformãã€ã³ã¹ããŒã«ããå¿
èŠããã
ãŸã ã ãšãŠãç°¡åã§ãã
次ã«ã远å ã®ã©ã€ãã©ãªãã€ã³ã¹ããŒã«ããã«ã¯ãã³ã³ãœãŒã«ã«å
¥åããã ãã§ãïŒ
cabal update
cabal install regex-tdfa curl feed utf8-string
以äžã¯ãã€ã³ããŒãã®ç°¡åãªãªã¹ããšç°¡åãªèª¬æã§ãã
æ£èŠè¡šçŸãæ°å䜿çšããŸããïŒ
> import Text.Regex.TDFA ( (=~) )
ãªã¹ããã«ããã¢ã³ãããŒã¹ããããïŒ
> import Data.List ( intercalate )
ãã¹ãŠã®ã€ã³ã¿ãŒããããªã¯ãšã¹ãã«ãcurlã©ã€ãã©ãªã䜿çšããŸããã
> import Network.Curl ( curlGetString )
> import Network.Curl.Opts
RSSãã£ãŒããèªãã§è§£æããŸãã
> import Text.Feed.Import ( parseFeedString )
> import Text.Feed.Query ( getFeedItems , getItemSummary )
ãããŠãäžåºŠæååãUnicodeã«ãšã³ã³ãŒããããšããŠãïŒ
> import Codec.Binary.UTF8.String ( encodeString )
ããã«ãããå
å®ãããããããã¯äžå¿
èŠã«è©³çްãªèª¬æãå«ããããæçãªã³ãŒãããããŸã...
RSSçµç±ã®Twitteræåã«å¿
èŠãªã®ã¯ãrssãã€ãŒããã£ãŒãã®ã¢ãã¬ã¹ã§ãã TwitterããŒãžã§ååŸã§ããŸãã å¥ã®å®æ°ãååŸããŸãããïŒ
> feedUrl = "https://twitter.com/statuses/user_timeline/22251772.rss"
rssãã£ãŒããååŸããŠè§£æããæ¹æ³
ã«ã€ããŠã¯ãrss2ljã«é¢ããèšäºãåç
§ããŠãã ãã ã ããããç§ã¯ãã®ã©ã€ãã©ãªã䜿çšããŸããã§ããã ãã¡ããããã¹ãŠãããŸãã§ããŠããŸããã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)
ã¯ãurlããªãã·ã§ã³ã®ãªã¹ãã
CurlOk
ãæäœã³ãŒãïŒãã¹ãŠãããŸã
CurlOk
å Žåã¯
CurlOk
ïŒãšãµãŒããŒå¿çãæäŸããŸãã ãã®å Žåãtwitter-rssãã£ãŒããã¢ãã¬ã¹ãšããŠæå®ãããªãã·ã§ã³ã¯æå®ããŸããã å®äºã³ãŒãã«ã¯æ³šæãæããŸããã ããããçãã®å€§éšåã¯ãã£ãŒããšåŒã°ããŸãã
次ã®è¡ã¯å³ããå·Šã«èªãå¿
èŠããããŸãïŒãã£ãŒãèŠçŽ ïŒ
getItems feed
ïŒãæœåºãããªã¹ããååŸããããããæåã®èŠçŽ ïŒ
head
ïŒãååŸããããããã¡ãã»ãŒãžèªäœïŒ
getMsg
ïŒãæœåºããŠåºåã«è¿ããŸãã
ãããŠããããã®é¢æ°ã«ã€ããŠãåãé åºã§ããã«è©³ãã説æããŸãã ãããã¯ããããããã€ã³ãããªãŒã¹ã¿ã€ã«ã§ãã€ãŸããåŒæ°ãæå®ããã«ãä»ã®é¢æ°ã®æ§æïŒãããïŒãšããŠåçŽã«èšè¿°ãããŸãã
ã³ã³ããžã·ã§ã³ã¯ãå³ããå·Šãžããã€ã³ã
getItems
ã«èªã¿åãããšãã§ããŸãïŒã€ãŸãã颿°ã®é©çšé ïŒ
getItems
ãŸã
parseFeedString
颿°ïŒãã£ãŒãã©ã€ãã©ãªããïŒã䜿çšããŸããã¿ã€ãïŒ
parseFeedString
String -> Maybe Feed
ïŒãã€ãŸããããããçš®é¡ã®ããªããžãå«ãæååãåãåããŸãã rssã¿ã°ããçæãããŸãããæœè±¡ã¿ã€ãã®ãã£ãŒããäœæããŸããããã«ãããæ¢ã«äœããè¡ãããšãã§ããŸã
Maybe Feed
å€ãè¿ããããããããŒãµãŒãæå¶ãããŠ
Nothing
ãè¿ãããããšããããŸããããŒã¹ãããŸãããã°ãæã
ã¯ïŒã®å€ãååŸã«RSSè§£æãïŒå€±æÂ»
Just
ïŒãããã®åŸã§ã¯ãªãã«é©çšãããŸã æ©èœã«
getFeedItems
ãªã¹ãäžã®ãã£ãŒãèŠçŽ ããæœåºããããã®åå²ïŒ
Nothing
ã
Just ...
ïŒãæšæºçãªæ©èœãå®è£
ã
maybe
ã
getItems
æ©èœããåŸããã£ãŒãã¢ã€ãã ã®ãªã¹ããååŸããŸãïŒ
[Item]
ã å¿
èŠãªã®ã¯ãæåã®ãã®ïŒã€ãŸããæ¥ä»ã«ããæåŸã®ãã®ïŒã ãã§ãã
head
æ©èœã§ãããåãã ãããŠã
getMsg
ã¡ãã»ãŒãžããã¹ã
getMsg
ãæãäžããããš
getMsg
ãŸãã
ãã®é¢æ°ã®æ§é ã¯
getItems
䌌ãŠã
getItems
ãæåã«
getItemSummary
ã
getItemSummary
ã
Maybe String
ãè¿ãããŸãã ã³ã³ãã³ããæœåºã§ããªãã£ãå Žåã察å¿ãããšã©ãŒãçºè¡ããŸãã ãã以å€ã®å Žåã¯ãåä¿¡ããã¡ãã»ãŒãžããã©ãŒãããããŸãã
ãã©ãŒãããïŒ
format
ïŒã¯æ¬¡ã®ããã«ç°¡åã«å®è¡ãããŸãïŒåã³å³ããå·ŠãžïŒïŒæååãUnicodeã§ãšã³ã³ãŒãããïŒã¹ããŒã¹ã§ïŒåèªã«åå²ããæåã®åèªãåé€ãã代ããã«ãtwitterïŒããæ¿å
¥ãïŒãªãã·ã§ã³ïŒããã¹ãŠã®åèªã1è¡ã«æ»ããŸãã rssãã€ãŒãã®æåã®åèªã¯åžžã«ããªãã®ããã¯ããŒã ã§ãã ãããã£ãŠãç§ãã¡ã¯ãããæšãŠãŸãã
ããã¯ãã¹ãŠrssã§ãã ç§ã¯ãã¹ãŠãããŸãã«ã詳现ã«èª¬æãããããããŸããããèå³ããããhaskellã«äžæ
£ããªäººã«ãšã£ãŠã¯ããã®èª¬æã¯æå³ã®ãããã®ã ã£ããšæããŸãã
Vkontakte APIãŸããVKontakteãæäœããããã®å®æ°ãããã€ãäœæããŸãã
> email = " e-mail"
> uid = " user-id "
> pass = " "
ããã¯ãVKontakteã§ã®ç»é²ã«å¯Ÿå¿ããããŒã¿ã§ãã
ãã¹ãŠã®æäœã¯ã察å¿ããããªãããŒãªã¢ãã¬ã¹ã䜿çšããŠããµãŒããŒãžã®GETèŠæ±ïŒåã
curlGetString
颿°ïŒã«ãã£ãŠå®è¡ãããŸãã ãããã¯æ¬¡ã®ããã«æ§ç¯ãããŸãã
ããŒã¹ã¢ãã¬ã¹ïŒäŸïŒ
userapi.com/data ïŒïŒãšãã¢ã³ããµã³ãïŒã§åºåãããkey = valueã®åœ¢åŒã®ãã©ã¡ãŒã¿ã®ãªã¹ã
ãã®ãããªã¢ãã¬ã¹ã圢æããããã«ãããã€ãã®è£å©é¢æ°ãäœæããŸãã
> 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ã®ããŒã¹ã¢ãã¬ã¹ããªãã·ã§ã³
opts
ã®ãªã¹ãïŒãã¢ã®åœ¢åŒïŒãããã³ã»ãã·ã§ã³ID
sid
ïŒåŸã§ããã«ã€ããŠïŒãããå¿
èŠãªåœ¢åŒã®URLã圢æããŸãã
å
å®¹ã¯æ¬åŒ§å
ã«ãã
map
ãmapã¯é¢æ°ãšãªã¹ããåãããªã¹ãã®åèŠçŽ ã«é¢æ°ãé©çšããŸãã ã€ãŸãããã¢ã®ãªã¹ã
(, )
ãããæååã®ãªã¹ãã
"=&"
ãŸãã ãããŠã
concat
ããããã¹ãŠã®è¡ã1ã€ã«æ¥çããŸãïŒ
concatMap = concat . map
ïŒã
ã¿ã¹ã¯ããšã«ãªãã·ã§ã³ã®ã»ããã¯ç°ãªããŸããããã¹ãŠã®å ŽåããŠãŒã¶ãŒèå¥åïŒ
uid
ïŒãæå®ããå¿
èŠããããŸãããã®ãããæ¯åãã®ãªãã·ã§ã³ãèšè¿°ããªãããã«ããã®é¢æ°ã®å®çŸ©ã«è¿œå ããŸãã
äœããã®åœ¢ã§VKontakteã䜿çšããã«ã¯ãæåã«ãã°ã€ã³ããå¿
èŠããããŸãã ãµãŒããŒã¯ãCookieïŒCookieïŒãšã»ãã·ã§ã³IDïŒsid =ã»ãã·ã§ã³IDïŒãæäŸããŸãã ã¯ãããŒã¯äœ¿çšããŸããã§ãããããŠãŒã¶ãŒããŒã¿ã®ååŸ/倿Žã«é¢ããã»ãšãã©ãã¹ãŠã®æäœã«ã¯sidãå¿
èŠã§ãã
> 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
颿°ã䜿çšããŠãã®ã¢ãã¬ã¹ãäœæããé»åã¡ãŒã«ãšãã¹ã¯ãŒããæåŸã®2ã€ã®ãªãã·ã§ã³ã«æ¿å
¥ãããŸãã ãã ããsidãã©ã¡ãŒã¿ã¯ç©ºã®ãŸãŸã§ãããŸã æã£ãŠããŸãããå®éã«ã¯ã
login
颿°ãäœæããŸããã
ãã®äžã§äœãèµ·ãããïŒcurlãªã¯ãšã¹ãã
authUrl
ã«éä¿¡ããã
headers
ãè¿ãããŸãïŒãã®ããã«ã
CurlHeader
ãªãã·ã§ã³ã
CurlHeader
ïŒã å®éã«ã¯ãCookieããªãã€ã¬ã¯ãã¢ãã¬ã¹ãªã©ããããŸãã ããããµãŒããŒãéä¿¡ããã¢ãã¬ã¹ã§ããæ¢ããŠãããã®ã¯é ãããŠããŸãã ç§å¯ã®æ£èŠè¡šçŸææ³ã䜿çšããŠããsid = 35dfe55b09b599c9ââfx622fcx8cd83a37ããšãã圢åŒã®åæãããã»ãã·ã§ã³IDã
headers
ããåãåºãããŸãã
haskellã§ã®æ£èŠè¡šçŸã«ã€ããŠã¯èª¬æããŸãããããã¯å¥ã®ãããã¯ã§ãã ããã¯ãç®çã®ã¿ã€ãã®éšåæååã®åãªãæ€çŽ¢ã§ãããšæ³å®ã§ããŸãã
ãããïŒ sidãååŸããŸãããçŸåšãapiã®ãã¹ãŠã®å¯èœæ§ãé£çµ¡ããŠããŸãã ãã®ã¿ã¹ã¯ã§ã¯ãã¹ããŒã¿ã¹ã®å€æŽãšãã1ã€ã ããå¿
èŠã§ãã
ååãšããŠãVKontakteãšã®ããåãã¯æ¬¡ã®ã³ãã³ãã§ç¡æã§ãã
(_,answer) <- curlGetString someUrl []
someUrl
ã¯å¯Ÿå¿ãããªã¯ãšã¹ãïŒããã¥ã¡ã³ããåç
§ïŒã§ããã
answer
ã¯ãµãŒããŒã®å¿çã§ãã ã¹ããŒã¿ã¹å€æŽãªã¯ãšã¹ãã¯æ¬¡ã®ããã«ãªããŸãã
> setActivityUrl :: String -> String -> String
> setActivityUrl text = formUrl "http://userapi.com/data?" [( "act" , "set_activity" ), ( "text" , text)]
formUrl
颿°ã®3çªç®ã®ãã©ã¡ãŒã¿ãŒ
sid
ã¯æå®ãããŠããªãããšã«æ³šæããŠãã ããã ããã¯éšåçãªã¢ããªã±ãŒã·ã§ã³ã§ã-颿°ã«ã¯3ã€ã®ãã©ã¡ãŒã¿ãŒãããã2ã€ã ããæå®ããŸãããããã¯ãæ®ãã®1ã€ã®ãã©ã¡ãŒã¿ãŒãã颿°ãååŸããããšãæå³ããŸãã ã€ãŸãã
setActivityUrl
ã¯ã
text
ãã©ã¡ãŒã¿ãŒïŒå®éã«ã¯æ°ããã¹ããŒã¿ã¹ïŒã ãã§ãªãã2çªç®ã®
sid
ãã©ã¡ãŒã¿ãŒã®é¢æ°ã§ãããããã¯ããã®ãŸãŸå³åŽã«è¿œå ãããŸãã
ããäžã€ã®äºçްãªããšïŒãã€ãŒãã®ããã¹ãã«ã¯ã¹ããŒã¹ããããŸãããããã¯urlãªã¯ãšã¹ãã«ã¯åãå
¥ããããŸããã ãããã£ãŠããã¹ãŠã®ã¹ããŒã¹ãïŒ
20ã§çœ®ãæããåçŽãªé¢æ°ãäœæããŸãã
> escSpaces = intercalate "%20" . words
æååãåèªã®ãªã¹ãã«åå²ãããã®ãªã¹ãå
ã®é£æ¥ããã¢ã€ãã ã®éã«æååãïŒ
20ããæ¿å
¥ãããã¹ãŠãåã³1ã€ã®æååã«æ¥çããŸãïŒæåŸã®2ã€ã®ã¢ã¯ã·ã§ã³ã¯
intercalate
颿°ã«ãã£ãŠè¡ãããŸãïŒã
ããã§ãæ¢ã«èª¬æããéšåããã¹ããŒã¿ã¹ã倿Žããæ©èœãåéã§ããŸãã
> 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
ãã®é¢æ°ãããç°¡åã«1è¡ã§èšè¿°ããããšãã§ããŸãã
setStatus text sid = curlGetString (setActivityUrl (escSpaces text) sid) []
ããããæåã®ãªãã·ã§ã³ã¯ããæç¢ºã§ããµãŒããŒã®å¿çã確èªããŸã-åçã«
"ok":1
ãå«ãŸããå Žå
"ok":1
ããã¹ãŠãæ£åžžã§ã-ã¹ããŒã¿ã¹ã倿ŽããããŠãŒã¶ãŒã«ïŒã€ãŸããèªåèªèº«ã«ïŒéç¥ããŸãã
ããã ãã§ãïŒ ããã§ãã¢ã¶ã€ã¯ã®ãã¹ãŠã®éšåãæããçµã¿ç«ãŠãéåžžã«ç°¡åã«ãªããŸããã
ã¡ã€ã³ãããã®ãã¹ãŠã®æ©èœãäœã®ããã«æžããããïŒ
> main = do
> tweet <- getTweet
> sid <- login
> setStatus tweet sid
ãšãŠãã·ã³ãã«ã«èŠããŸãããïŒ ããã§ã¯ã³ã¡ã³ãã¯äžèŠã§ãã
ç§ã®èª¬æã§ã¯ãä»ã®ãã¹ãŠã®æ©èœã¯éåžžã«æç¢ºã«èŠãããšæããŸãã
ã40 LinesOfCodeã®ããã®çµ±èšã
ãããã«ãã®ã³ãŒããå®è¡ããã«ã¯ãæ¢ã«è¿°ã¹ãããã«ãæçš¿å
šäœãæ¡åŒµå.lhsã®ãã¡ã€ã«ã«ä¿åããã³ã³ãœãŒã«ã«å
¥åããå¿
èŠããããŸãã
runhaskell _.lhs
以äžã§ãã
ãã®ãªãªãŒã¹ãèªååããæ¹æ³ã«ã€ããŠè©±ãããã«ç¶ç·šãå¿
èŠãã©ããã¯ããããŸããã
ç§èªèº«ïŒMac OS Xã®ãŠãŒã¶ãŒãšããŠïŒã¯ãAutomatorã§ããµãŒãã¹ããäœæãããããããŒãå²ãåœãŠãããšã§ãããæ±ºå®ããŸãããããããã°ããåŒã³åºãããšã¯ãèµ·åãèªååããã ãã§ãããããã§ååã§ãã
ãããèªã人ã«ãšã£ãŠè峿·±ããã®ã§ãã£ãããšãé¡ã£ãŠããŸãã 質å/ææ¡/ç°è°ãåŸ
ã£ãŠããŸãïŒïŒ
updïŒããŒãå¥ããã°ã«ç§»åããŸããã