Google Calendar API を使って1日の予定を一覧するPHPスクリプトを作る

そんなスクリプトをわざわざ作らなくても,Settings -> Calendars -> CALENDAR -> Notifications -> Daily agenda にチェックを入れておけば,5am(実際は4:30am頃?)にメールで予定一覧を送ってくれる.が,フォーマットが気に入らない(携帯メールに転送すると文字数制限をオーバーする),好きなメールアドレスに送れない(G-mailのフィルタで転送すればいいが)といった問題があるので, Google Calendar API を使って,1日の予定を一覧するPHPスクリプトを作ってみた.

Google Calendar API

Google Calendar の Settings -> Calendars -> CALENDAR -> Private Address: -> XML を選択すると,

http://www.google.com/calendar/feeds/USERID/private-MAGICCOOKIE/basic

のようなフォーマットのURLが表示される.USERID,MAGICCOOKIE は個々のユーザのカレンダーに固有の値(他人に知られるとよろしくない).ここで,basic の代わりに適当なパラメタを設定してやることで,例えばある日の予定をXML形式で得ることができる.Data API Reference Guideを参考に,11月4日の予定を取得するようにパラメタを設定すると,

http://www.google.com/calendar/feeds/USERID/private-MAGICCOOKIE/full?start-min=2009-11-04T00:00:00&start-max=2009-11-04T23:59:59&orderby=starttime&sortorder=a&singleevents=true

こんな感じになる.

この結果のXMLを適当にソートするなどして表示すれば,目的は達せられる.

本日の予定を一覧するPHPスクリプト

以下のPHPスクリプトでは,複数のカレンダーを統合できるようにした(カレンダーごとに USERID と MAGICCOOKIE が異なり,個別にXMLを取得しなければならない).

<?php
  class TEvent
  {
    public $StartTime = '';
    public $EndTime = '';
    public $Title = '';
    public $Description = '';
  }

  function CmpEvents($a, $b)
  {
    if ($a->StartTime == $b->StartTime)  return 0;
    if ($a->StartTime=="all day")  return -1;
    if ($b->StartTime=="all day")  return 1;
    return ($a->StartTime < $b->StartTime) ? -1 : 1;
  }

  function agenda_of_google_calendar($htitle,$userid,$magicCookie,&$events)
  {
    // $date= '2009-09-30';  // for test
    $date= date("Y-m-d");
    $target= 'http://www.google.com/calendar/feeds/'
            . $userid
            . '/private-'
            . $magicCookie
            . '/full?'
            . 'start-min='.$date.'T00:00:00'
            . '&start-max='.$date.'T23:59:59'
            . '&orderby=starttime&sortorder=a' .'&singleevents=true';
    $xml = simplexml_load_file($target);

    // 2009-05-15T14:30:00.000+09:00
    $date_pattern='([0-9]{4}-[0-9]{2}-[0-9]{2})T([0-9]{2}:[0-9]{2})(:[0-9]{2})';

    foreach ($xml->entry as $item)
    {
      $gd = $item->children('http://schemas.google.com/g/2005');
      $startTime = $gd->when->attributes()->startTime;
      $endTime = $gd->when->attributes()->endTime;
      $title = $item->title;
      $description = $item->content;

      if (ereg($date_pattern, $startTime, $sregs)
        && ereg($date_pattern, $endTime, $eregs))
      {
        $event= new TEvent();
        $event->StartTime=$sregs[2];
        $event->EndTime=$eregs[2];
        $event->Title=$title;
        $event->Description=$description.$htitle;
        $events[]=$event;
      }
      else
      {
        $event= new TEvent();
        $event->StartTime='all day';
        $event->Title=$title;
        $event->Description=$description.$htitle;
        $events[]=$event;
      }
    }
  }

  $events= array();

  $userid= 'USERID1';
  $magicCookie= 'MAGICCOOKIE1';
  agenda_of_google_calendar(' (Akihiko)', $userid, $magicCookie, $events);

  $userid= 'USERID2';
  $magicCookie= 'MAGICCOOKIE2';
  agenda_of_google_calendar(' (Lab.)', $userid, $magicCookie, $events);

  usort($events, CmpEvents);


  // style(hrml):
  // $eol="<br>\n";
  // $date_style=array('<font color="#882222">','</font>');
  // $cont_style=array('<font color="#0000ff">','</font>');
  // $indent='';
  // $space='&nbsp;';

  // style(text):
  $eol="\n";
  $date_style=array('','');
  $cont_style=array('','');
  $indent='';
  $space=' ';

  echo "Agenda@".date("Y-m-d(D)")."$eol";
  $prev_is_allday=false;
  $delim='';
  foreach ($events as $item)
  {
    if ($item->StartTime!='all day')
    {
      echo $delim;
      echo $indent.$date_style[0].$item->StartTime.'-'.$item->EndTime.': '.$date_style[1] .$eol
              .$indent.$space.$cont_style[0].$item->Title.'; '.$item->Description.$cont_style[1].$eol;
    }
    else
    {
      if (!$prev_is_allday)
      {
        echo $delim;
        echo $indent.$date_style[0].'all day: '.$date_style[1] .$eol;
        $prev_is_allday=true;
      }
      echo $indent.$space.$cont_style[0].$item->Title.'; '.$item->Description.$cont_style[1].$eol;
    }
    $delim=$eol;
  }
?>

すべてのイベントを開始時間でソートできるようにイベントクラス(TEvent)を定義している.CmpEvents は開始時間でソートするための比較関数.開始時間が all day(終日)の場合は最初にくるようにしている.

関数 agenda_of_google_calendar($htitle,$userid,$magicCookie,&$events) で予定を取得し,TEventクラスの配列 $events に保存する.$htitle には Description の最後に追加する文字列, $userid には USERID, $magicCookie には MAGICCOOKIE をそれぞれ指定する.
複数のカレンダーを統合するには,それぞれのカレンダーごとに USERID と MAGICCOOKIE を取得して,複数回 agenda_of_google_calendar を実行する.上のサンプルでは2つのカレンダーを統合している.

usort でイベントの配列を開始時間でソートする.それ以降は,出力のコード.ここではテキストフォーマットで出力するようにしているが, style(text): 以下5行をコメントアウトし, style(html): 以下5行をアンコメントすれば,HTML形式で出力できる.

おまけ:毎日メールで送信

CRON を使うのがてっとりばやいと思う. crontab -e で,以下のような一文を追加すれば,毎日4:30amにメールが(MAILADDRESSに)送られる.

30 4  *   *   *     php /home/akihiko/bin/php/google-calendar.php | nkf -s | mail -s "Agenda(`/home/akihiko/bin/linux/today`)" MAILADDRESS

なお, /home/akihiko/bin/php/google-calendar.php は上記のスクリプト,/home/akihiko/bin/linux/today は日付を返すbashスクリプト

#!/bin/bash
LANG=C
date +'%m/%d(%a)'

メールで送信するためには,常時稼働しているサーバマシン上で CRON を実行する必要がある.Debian でのメール送信には Exim4 を使うとよいようだ(以下の参考URLを参照).