GData にだってアクセスできるんだぜ
Google Calendar API に node.js でアクセスするサンプルです。 Google は JSのクライアント実装を提供してくれていますが、node.js だと使い物にならないので、必要な部分だけ書いてみました。
雰囲気だけつかんでいただければ。HTTP Call 自体も非同期なので、がつがつ呼び出してもさくさく動きます。
/** * GData Client */ var sys = require("sys"), EventEmitter = require("events").EventEmitter, http = require('http'), fs = require('fs'), path = require('path'), url = require('url'), querystring = require('querystring'); require(path.join(__dirname, '../ext')); var HOST = "www.google.com"; var PORT = 443; var SOURCE = "nodejs-gdata-0.0.1"; var Client = function(params){ if(!params){ params = {}; } this._httpsClient = http.createClient(443, HOST, true); this._httpClient = http.createClient(80, HOST, false); this._logger = params.logger || require('logger').DefaultLogger; } exports.Client = Client; Client.prototype.login = function(email, password, service, callback){ if( service == undefined ){ throw 'service parameter must be specified.'; } var event = new EventEmitter(); var self = this; var params = { 'accountType': 'HOSTED_OR_GOOGLE', 'Email' : email, 'Passwd' : password, 'service' : service, 'source' : SOURCE }; var body = querystring.stringify(params); var req = this._httpsClient.request( 'POST', '/accounts/ClientLogin', { 'Content-Type': "application/x-www-form-urlencoded" } ); req.write(body); req.on('response', function(res){ var body = ''; res.on('data', function(chunk){ body += chunk; }); res.on('end', function(){ var obj = {}; var lines = body.split('\n'); for(var i in lines){ var l = lines[i]; var kv = l.split('='); if( kv.length == 2 ){ obj[kv[0]] = kv[1]; } } if( res.statusCode == 200 ){ self._authEmail = email; self._authToken = obj['Auth']; self._authType = 'GoogleLogin'; self._logger.info('[GData] logged in as ' + email); self._logger.debug('[GData] token: ' + obj['Auth']); event.emit('success'); }else{ self._authToken = undefined; event.emit('failure'); } callback && callback(res.statusCode == 200, obj); }); }); req.end(); return event; }; function _handleResponse (event, obj, fun, payload){ return function(res){ if( res.statusCode >= 300 && res.statusCode < 400){ obj._logger.debug('[GData] << Redirect: ' + res.headers.location); var newurl = url.parse(res.headers.location); fun.apply(obj, [event, newurl.pathname + "?" + newurl.query, payload]); }else{ var body = ''; res.on('data', function(chunk){ body += chunk; }); res.on('end', function(){ obj._logger.debug('[GData] << ' + body); if( res.statusCode >= 200 && res.statusCode < 300 ){ event.emit('data', JSON.parse(body)); }else{ sys.puts(body); event.emit('error', body); } }); } }; } Client.prototype.getMetaFeeds = function(params){ var requestPath = this._getCalendarUrl('/calendar/feeds/default', params); var event = new EventEmitter(); this._getCalendarFeeds(event, requestPath); return event; } Client.prototype.getAllCalendarFeeds = function(params){ var requestPath = this._getCalendarUrl('/calendar/feeds/default/allcalendars/full', params); var event = new EventEmitter(); this._getCalendarFeeds(event, requestPath); return event; } Client.prototype.getPrivateCalendarFeeds = function(params){ var requestPath = this._getCalendarUrl('/calendar/feeds/default/private/full', params); var event = new EventEmitter(); this._getCalendarFeeds(event, requestPath); return event; } Client.prototype.getOwnCalendarFeeds = function(params){ var requestPath = this._getCalendarUrl('/calendar/feeds/default/owncalendars/full', params); var event = new EventEmitter(); this._getCalendarFeeds(event, requestPath); return event; } Client.prototype.addSingleOccurrenceEvent = function(data, id){ var event = new EventEmitter(); var requestPath; if( !id ){ id = 'default'; } requestPath = this._getCalendarUrl('/calendar/feeds/' + id + '/private/full'); var body = this._buildSingleOccurrenceEvent(data); this._postEvent(event, requestPath, body); return event; } Client.prototype._getCalendarUrl = function(basePath, params){ if( !params ){ params = {}; } if( params.id ){ basePath = basePath + '/' + params.id; } var query = {}; query.alt = 'json'; // force JSON return basePath + "?" + querystring.stringify(query); }; Client.prototype._postEvent = function(event, requestPath, body){ var self = this; var auth = self._authType + ' auth=' + self._authToken; var req = self._httpClient.request( 'POST', requestPath, { 'Content-Type': 'application/atom+xml', 'Content-Length': body.length, 'Authorization': auth } ); this._logger.debug('[GData] >> POST ' + requestPath); this._logger.debug('[GData] >> ' + body); req.write(body); req.end(); req.on('response', _handleResponse(event, self, self._postEvent, body)); return event; } Client.prototype._getCalendarFeeds = function(event, requestPath){ this._logger.debug('[GData] GET ' + requestPath); var self = this; var auth = self._authType + ' auth=' + self._authToken; var req = self._httpClient.request( 'GET', requestPath, { 'Content-Type': 'application/atom+xml', 'Authorization': auth } ); req.end(); req.on('response', _handleResponse(event, self, self._getCalendarFeeds)); return event; }; Client.prototype._buildSingleOccurrenceEvent = function(data){ var strings = ['<entry', 'xmlns="http://www.w3.org/2005/Atom"', 'xmlns:gd="http://schemas.google.com/g/2005">', '<category', 'scheme="http://schemas.google.com/g/2005#kind"', 'term="http://schemas.google.com/g/2005#event">', '</category>']; strings.push("<title type='text'>{title}</title>".format({ title: data.title.htmlEscape() })); strings.push("<content type='text'>{content}</content>".format({ content: data.content.htmlEscape() })); if( data.startTime || data.endTime ){ var when = ""; var st = Date.parseExt(data.startTime); var et = Date.parseExt(data.endTime); var tz; if( st ){ tz = st.strftime('%z'); when += ' startTime="{t}{tz}"'.format({ t: st.strftime("%Y-%m-%dT%T"), tz: tz.substr(0,3) + ":" + tz.substr(3) }); } if( et ){ tz = et.strftime('%z'); when += ' endTime="{t}{tz}"'.format({ t: et.strftime("%Y-%m-%dT%T"), tz: tz.substr(0,3) + ":" + tz.substr(3) }); } strings.push("<gd:when " + when + "></gd:when>"); } strings.push("</entry>"); return strings.join("\n"); }
XML のライブラリなんかいいのないかなぁ。。。