FriendFeedを使い倒す
imaginary friend作るのが一番手間なんだけどー。JSActionsで脳内ともだち作成、フィード追加できるの作ったけどtako3とかからいっぱつでとれたりしないとやっぱりめんどい。
http://friendfeed.com/settings/imaginary ひらいて実行する。
作ったともだちに、ブックマークだけ入れて、そこの中でブックマークされてた数の多い順で出す。
アルファブックマーカーの注目するエントリー(なくなった?)の、アルファブックマーカーを自分で選べて調整できるバージョン。data sourceははてなブックマークでもいいしdel.icio.usでもいいしRSSが出てるやつならなんでもok.
そういうのほしかったけど、つくるのめんどいとおもってたら、FriendFeedのAPIがちょーべんりで、imaginary friendがこれまたおあつらえむきで、クロールしてデータ保持しといてくれてて、それをjsonでもってこれる。urlとタイトルしか入んないけど、urlとタイトルが入れられるクローラ付きのjsonでデータが取れるdbとして使える。といってもけっきょくFriendFeedのAPIがいっぺんには30くらいしか結果を返してくれないのでループまわさないとだめなんだけど、でもそれでも自分で作るのよりかはぜんぜんらく。
nisshi.yugop: About Samplingで書かれている、「美味しいとこだけイタダキマース」感と、自分で書いたScissors, Fools, Toolsの、想定してない使い方(そんなことなくてimaginary friendなんてものが実装されているところからして想定できるくらいの使い方だろうけど)がそこそこできて満足。
38人脳内ともだちつくってやってみて、でてきたのでおおかったのはGoogle Visualization APIを使ってみました - IT-Walker on hatenaで5人がブックマークしてた。PureRubyなRTMP(MP4/H.264)サーバをオープンソース化しました - takumalogも5人。まーホットエントリなんかよりぜんぜんいいけどどれくらいいいかはこれからしばらく見てみて判断。
こういうのはまじめになんか統計的にやらないとだめな気もする。
まじめに統計とったりできる人、たぶんfriendfeedをクローラにしてjsonで処理するとすごい楽できると思うのでぜひFriendFeedを粗末に使っておもしろいものつくってください。PHPとpythonのAPIライブラリが用意されてます。
if ( 0 ) { var histgram = {}; var startFrom = 0; for ( var i = 0; i< 25; i ++ ) { var req = new XMLHttpRequest(); var q = querystring( { start: startFrom, num: 30 } ); var u = "/api/feed/home?" + q; req.open( "GET",u , false); req.send(""); var res = eval("(" + req.responseText + ")"); console.log(res); res.entries.map( function (e) { var u = e.link; if ( histgram[u] ) with( histgram[u] ) { n++; who.push(e.user.name); } else histgram[u] = { n: 1, title: e.title, who: [e.user.name], url: u }; } ); startFrom += res.entries.length; } } var keys = []; for ( var i in histgram ) { keys.push(i); } keys keys.filter( function(k) histgram[k].n > 1 ).map( function(k) { return histgram[k] } ).sort( function (a,b) a.n < b.n ) ;
// this script contains Greasemonkey oriented code snippet. // === START LICENSE === // // Copyright 2004-2007 Aaron Boodman // Contributors: See contributors list in install.rdf // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // Note that this license applies only to the Greasemonkey extension source // files, not to the user scripts which it runs. User scripts are licensed // separately by their authors. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. // // === END LICENSE === // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. var DEBUG_TRACE = 1; function trace() { if ( (typeof DEBUG_TRACE != 'undefined') && ! DEBUG_TRACE ) return; var fn; if ( typeof Firebug == 'undefined' ) { fn = console; } else if ( Firebug && Firebug.Console && Firebug.Console.log ) { fn = Firebug.Console; } if ( fn ) { if ( arguments.length == 1 ) { fn.log(arguments[0]); } else { var args = []; for (var i = 0; i < arguments.length; i++) { args.push(arguments[i]); } fn.log(args); } } } { var XHR = function (opts) { try { var req = Components.classes["@mozilla.org/xmlextras/xmlhttprequest;1"]. createInstance(Components.interfaces.nsIXMLHttpRequest); if ( opts.onload ) { req.onload = function () { var responseState = { // can't support responseXML because security won't // let the browser call properties on it responseText:req.responseText, readyState:req.readyState, responseHeaders:(req.readyState == 4 ? req.getAllResponseHeaders() : ''), status:(req.readyState == 4 ? req.status : 0), statusText:(req.readyState == 4 ? req.statusText : ''), finalUrl:(req.readyState == 4 ? req.channel.URI.spec : '') }; return opts.onload.call(this, responseState); } } if ( opts.onerror ) { req.onerror = function () { var responseState = { // can't support responseXML because security won't // let the browser call properties on it responseText:req.responseText, readyState:req.readyState, responseHeaders:(req.readyState == 4 ? req.getAllResponseHeaders() : ''), status:(req.readyState == 4 ? req.status : 0), statusText:(req.readyState == 4 ? req.statusText : ''), finalUrl:(req.readyState == 4 ? req.channel.URI.spec : '') }; return opts.onerror.call(this, req); } } req.open(opts.method || "GET", opts.url, true); if ( opts.contentType ) { req.setRequestHeader("Content-Type", opts.contentType); } else { req.setRequestHeader("Content-Type", "application/x-www-form-urlencoded"); } req.send(opts.data || null); return req; } catch(e) { alert(e.fileName + e.lineNumber + ":" + e.message); } }; var querystring = function (q) { var p = []; for ( var i in q ) { p.push(encodeURIComponent(i)+"="+encodeURIComponent(q[i])); } return p.join("&") } } function getCookieFromManager(host, name) { var iter = Components.classes["@mozilla.org/cookiemanager;1"] .getService(Components.interfaces.nsICookieManager).enumerator; while (iter.hasMoreElements()){ var cookie = iter.getNext(); if (cookie instanceof Components.interfaces.nsICookie){ if ( cookie.host == host && cookie.name == name ) return cookie.value; } } return null; } if ( gContextMenu ) { var e = gContextMenu.target; var u; if ( e.tagName.toLowerCase() == 'a' ) { u = e.href; } else { u = e.ownerDocument.location.href; } } var serviceAdapters = [ { service: 'delicious', url: /del.icio.us\/(\w+)\b/, params: ['username'] }, { service: "blog", url: /(.*)()/, params: ['url', 'author'] } ]; var adapter; var m; for ( var i = 0; i < serviceAdapters.length; i++ ) { adapter = serviceAdapters[i]; if ( m = u.match(adapter.url) ) break; } var params = { at: getCookieFromManager("friendfeed.com", "AT"), //userid: "2c227538-fb39-11dc-bfed-003048343a40", service: adapter.service, }; adapter.params.forEach( function (name, i) { params[name] = m[i + 1]; } ); function getUniqueName() { if ( params.username ) { return params.username; } else { if ( u.match( /^https?:\/\/((?:[a-z0-9\-]+\.)*)([a-z0-9\-]+)\.[a-z]{2,6}\/(\w*)/i ) ) { if ( RegExp.$3 ) { return RegExp.$3; return window.prompt("input imaginary frield name", RegExp.$3); } else { return RegExp.$2; return window.prompt("input imaginary frield name", RegExp.$2); } } else { return window.prompt("input imaginary frield name", ""); } } } function createImaginaryFriend() { var name = getUniqueName(); var r = XHR( { url: "http://friendfeed.com/settings/createimaginary", data: querystring( { at: getCookieFromManager("friendfeed.com", "AT"), name: name } ), method: "POST", onload: function (res) { trace(res); var m; if ( m = res.finalUrl.match( /(([0-9a-f]+-){4})([0-9a-f]+)/ ) ) { params.userid = m[0]; add(params); } } } ); trace(name); }; createImaginaryFriend(); function add(params) { var r = XHR( { url: "http://friendfeed.com/settings/configureservice", data: querystring(params), method: "POST", onload: function (res) { trace(res, res.status) } } ); }