Good news for Twitter and no so good for developers:
Today(2013-06-11), we(Twitter) are retiring API v1 and fully transitioning to API v1.1.
What does it all mean? This means that all old services are no longer available. Twitter switched to new ones with mandatory OAuth authentication. From now, to work with twitter services we must register new apps and use OAuth.
Also, it means that:
- Script from ““F# Weekly” under the hood” does not work. Twitterizer is a dead project and will not be updated up to Twitter API v1.1.
- Beautiful script from “A Twitter search client in 10 lines of code with F# and the JSON type provider” does not work, because it uses v1 service.
As I know, there are two alternatives available instead of Twitterizer:
- Tweetsharp (TweetSharp is a fast, clean wrapper around the Twitter API.)
- LINQ to Twitter (An open source 3rd party LINQ Provider for the Twitter micro-blogging service.)
I have chosen Tweetsharp because its API similar to Twitterizer. This is a new F# Weekly under the hood script:
#r "Newtonsoft.Json.dll" #r "Hammock.ClientProfile.dll" #r "TweetSharp.dll" open TweetSharp open System open System.Net open System.Text.RegularExpressions let service = new TwitterService(_consumerKey, _consumerSecret) service.AuthenticateWith(_accessToken, _accessTokenSecret) let getTweets query = let rec collect maxId = let options = SearchOptions(Q = query, Count =Nullable(100), MaxId = Nullable(maxId), Resulttype = Nullable(TwitterSearchResultType.Recent)) printfn "Loading %s under id %d" query maxId let results = service.Search(options).Statuses |> Seq.toList printfn "\t Loaded %d tweets" results.Length if (results.Length = 0) then List.empty else let lastTweet = results |> List.rev |> List.head if (lastTweet.Id < maxId) then results |> List.append (collect (lastTweet.Id)) else results collect (Int64.MaxValue) |> List.rev let urlRegexp = Regex("http://([\\w+?\\.\\w+])+([a-zA-Z0-9\\~\\!\\@\\#\\$\\%\\^\\&\\*\\(\\)_\\-\\=\\+\\\\\\/\\?\\.\\:\\;\\'\\,]*)?", RegexOptions.IgnoreCase); let filterUniqLinks (tweets: TwitterStatus list) = let hash = new System.Collections.Generic.HashSet(); tweets |> List.fold (fun acc t -> let mathces = urlRegexp.Matches(t.Text) if (mathces.Count = 0) then acc else let urls = [0 .. (mathces.Count-1)] |> List.map (fun i -> mathces.[i].Value) |> List.filter (fun url -> not(hash.Contains(url))) if (List.isEmpty urls) then acc else urls |> List.iter(fun url -> hash.Add(url) |> ignore) t :: acc) [] |> List.rev let tweets = ["#fsharp";"#fsharpx";"@dsyme";"#websharper";"@c4fsharp"] |> List.map getTweets |> List.concat |> List.sortBy (fun t -> t.CreatedDate) |> filterUniqLinks let printTweetsInHtml filename (tweets: TwitterStatus list) = let formatTweet (text:string) = let matches = urlRegexp.Matches(text) seq {0 .. (matches.Count-1)} |> Seq.fold ( fun (t:string) i -> let url = matches.[i].Value t.Replace(url, (sprintf "<a href="\"%s\"" target="\"_blank\"">%s</a>" url url))) text let rows = tweets |> List.mapi (fun i t -> let id = (tweets.Length - i) let text = formatTweet(t.Text) sprintf "</pre> <table id="\"%d\""> <tbody> <tr> <td rowspan="\"2\"" width="\"30\"">%d</td> <td rowspan="\"2\"" width="\"80\""><a href="\"javascript:remove('%d')\"">Remove</a></td> <td rowspan="\"2\""><a href="\"https://twitter.com/%s\"" target="\"_blank\""><img alt="" src="\"%s\"/" /></a></td> <td><b>%s</b></td> </tr> <tr> <td>Created : %s</td> </tr> </tbody> </table> <pre> " id id id t.Author.ScreenName t.Author.ProfileImageUrl text (t.CreatedDate.ToString())) |> List.fold (fun s r -> s+" "+r) "" let html = sprintf "<script type="text/javascript">// <![CDATA[ function remove(id){return (elem=document.getElementById(id)).parentNode.removeChild(elem);} // ]]></script>%s" rows System.IO.File.WriteAllText(filename, html) printTweetsInHtml "d:\\tweets.html" tweets
Thanks on your marvelous posting! I definitely enjoyed reading it, you could be a great author.
I will be sure to bookmark your blog and will eventually come back
in the foreseeable future. I want to encourage you to definitely continue
your great job, have a nice morning!