イントロダクション
nodejsでgoogle calendar APIを使ってイベント取得するまでの流れを解説します。
なお取得するカレンダーはマイカレンダーだけでなく、閲覧権限のある他のカレンダーの情報も同時に取得します。
環境準備
大まかな流れはほぼ公式ドキュメント通りです。
公式ドキュメント
まずカレンダーAPIの有効化と、必要なキーを発行します。
※公式ドキュメント内の[ENABLE THE GOOGLE CALENDAR API]のボタンから簡単に発行できます。
credentials.jsonのファイルが生成されますので、実行ファイルと同じパスに保存しておきます。
次にgoogleカレンダーへ接続するためのクライアント用モジュールを追加します。
# npm install googleapis@27 --save
準備の手続きは以上になります。あとはソースを生成して実行するだけです。
カレンダーデータの取得
(※以下すべてのソースはwebpackを使ってビルドしています。直接nodejsで実行するソースの場合はimport記述はエラーになるためrequireに置き換えた記述方法にしてください。)
import fs from 'fs'
import readline from 'readline'
import {google} from 'googleapis'
import moment from 'moment'
export default class GoogleCal{
constructor(config){
this.config = Object.assign({},this.defaults(),config);
}
/**
* 初期値
*/
defaults(){
return {
scopes : ['https://www.googleapis.com/auth/calendar.readonly'],
tokenPath : '/app/script/token.json',
}
}
/**
* google Calendar APIへの接続と認証
*/
connect(){
return new Promise((resolve,reject) => {
fs.readFile('credentials.json', (err, content) => {
if (err){
console.log('Error loading client secret file:', err);
reject();
}
this.authorize(JSON.parse(content))
.then(auth => {
this.config.auth = auth;
resolve();
});
});
});
}
/**
* OAuth認証処理
* given callback function.
* @param {Object} credentials 認証データ
* @return (promise) object oAuth2Client.
*/
authorize(credentials) {
const {client_secret, client_id, redirect_uris} = credentials.installed;
const oAuth2Client = new google.auth.OAuth2(
client_id, client_secret, redirect_uris[0]);
return new Promise((resolve,reject) => {
// 保存してあるトークン情報を読み取り、なければトークン生成
fs.readFile(this.config.tokenPath, (err, token) => {
if (err) {
this.getAccessToken(oAuth2Client)
.then(() => {
resolve(oAuth2Client);
})
} else {
// アクセストークンは1時間ほどしか期限がないため、毎回リフレッシュトークンから新たなトークンを生成する
oAuth2Client.credentials = JSON.parse(token);
oAuth2Client.refreshAccessToken((err, token) => {
if (err) {
console.log(err);
reject();
}
oAuth2Client.setCredentials(token);
fs.writeFile(this.config.tokenPath, JSON.stringify(token), (err) => {
if (err){
console.error(err);
reject();
}
console.log('Token stored to', this.config.tokenPath);
});
});
resolve(oAuth2Client);
}
});
});
}
/**
* OAuthのアクセストークンを取得
* @param oAuth2Client {object} google.auth.OAuth2の戻り値
* @return (promise) object 有効なtokenを含めたgoogle.auth.OAuth2
*/
getAccessToken(oAuth2Client) {
const authUrl = oAuth2Client.generateAuthUrl({
access_type: 'offline',
scope: this.config.scopes,
});
console.log('右記のURLをブラウザで開いてください:', authUrl);
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
});
return new Promise((resolve,reject) => {
rl.question('表示されたコードを貼り付けてください: ', (code) => {
rl.close();
oAuth2Client.getToken(code, (err, token) => {
if (err) return console.error('トークンを発行できませんでした', err);
oAuth2Client.setCredentials(token);
// トークンデータをファイルに書き出し
fs.writeFile(this.config.tokenPath, JSON.stringify(token), (err) => {
if (err) {
console.error(err);
reject();
}
console.log('トークンを保存しました', this.config.tokenPath);
});
resolve(oAuth2Client);
});
});
});
}
/**
* イベントを取得
* @param params {object} 取得時のパラメータ
* @return (promise) Array イベントデータ一覧
*/
listEvents(params) {
if(params.calendarId.match(/#/)){
params.calendarId = encodeURIComponent(params.calendarId);
}
return new Promise((resolve,reject) => {
const calendar = google.calendar({version: 'v3', auth: this.config.auth});
calendar.events.list(params, (err, res) => { // events.listでイベント一覧を取得
if (err) {
console.log('The API returned an error: ' + err);
reject();
}
resolve(res.data.items);
});
});
}
/**
* カレンダー一覧を取得
* @return (promise) Array カレンダーデータ一覧
*/
calendarLists(){
return new Promise((resolve,reject) => {
const calendar = google.calendar({version: 'v3', auth : this.config.auth});
calendar.calendarList.list({},(err, res) => { // calendarList.listでカレンダーの一覧を取得
resolve(res.data.items);
})
});
}
/*
* 本日のイベントを取得
* @param filterFunc {function} イベントを取得するカレンダーリストの条件フィルター
*/
getTodayEvents(filterFunc = null){
// カレンダー毎に順次処理するためのタスク
const task = (calName,params,result) => {
return new Promise((resolve, reject) => {
this.listEvents(params).then(resp => {
const data = Object.assign([],result);
data.push({
calName,
items : resp
})
resolve(data)
});
});
}
return new Promise((resolve,reject) => {
this.calendarLists() // カレンダー一覧を取得
.then(calList => {
if(filterFunc){
calList = calList.filter(filterFunc); // イベント取得するカレンダーを絞り込み
}
calList.reduce((prev, current) => {
return prev.then(resp => {
const today = moment(moment().format("YYYY-MM-DD")).utcOffset("+09:00"); // 本日の00:00(日本時間)
const todayMax = moment(moment().format("YYYY-MM-DD")).add(1,'days').add(-1,'minutes').utcOffset("+09:00"); // 本日の23:59(日本時間)
const params = {
calendarId: current.id,
timeMin: today.format(), // 本日の0:00
timeMax: todayMax().format(), // 本日の23:59
singleEvents: true,
orderBy: 'startTime',
}
const summary = current.summaryOverride || current.summary; // カレンダー名
return task(summary,params,resp)
});
}, Promise.resolve([]))
.then(resp => {
resolve(resp);
})
.catch(()=>{
console.log("error!")
})
})
});
}
}
import GoogleCal from './googleCal'
const googleCal = new GoogleCal;
googleCal.connect()
.then(() => {
return googleCal.getTodayEvents(_val => !_val.id.match(/#/)); // 誕生日や祝日のカレンダーは取得対象から除外
})
.then(val => {
console.log(val);
});
実行
# node app.js
初回はtokenを持っていないため以下のような表示が出ますので、指示通りブラウザで指定のURLを開き、アカウント認証を行います。
アカウント認証が完了するとコードが表示されますので、それをコピペしてターミナルに入力します。
右記のURLをブラウザで開いてください: https://accounts.google.com/o/oauth2/auth?access_type=offline&scope=****
表示されたコードを貼り付けてください:
認証に成功すると、カレンダーとそのタスクが表示されますので(/app.jsの最後のconsole.log(val)の部分)
あとはうまく加工して表示してください。
ちなみにデータの形式はこんな感じです。
[
{
calName : xxx,
items : [
{
kind: 'calendar#event',
etag: '"1234567890"',
id: '20frmklfmerkfrefmqw',
status: 'confirmed',
htmlLink: 'https://www.google.com/calendar/event?eid=xxxxxxxxxxxxxx',
created: '2018-10-09T07:09:42.000Z',
updated: '2018-10-09T07:09:42.778Z',
summary: 'てすと',
},
… (item分繰り返し)
]
}
… (カレンダー分繰り返し)
]
所感
googleの公式ドキュメントが例文コード含め丁寧に解説されているため、わりと簡単に設置することができました。