OhLifeというWebサービスがある。メールの送受信をフックとしたログ記録サービスで、以下のような特徴がある。

  • 1日1回、決まった時間にメールを送ってくる
  • このメールに返信すると、返信内容が記録される
  • OhLifeが送ってくるメールの本文には、過去のログが含まれる

OhLifeの主な機能には満足しているのだけど、一点だけ不満がある。それは、過去のログの選ばれる規則を設定できないこと。個人的な用途としては、前日からの進捗確認に使いたいので、必ず昨日のログを表示してほしい。しかし、OhLifeの過去ログ抽出規則は、「前日・1週間前・1ヶ月前」等があり、古いものが優先されているように思える。

類似サービスも探したのだが、どうも無いみたい。

無いなら作ろう。

ということで、作ってみた(現在、一般公開はしていない)。


サーバはさくらVPSで、OSはDebian Wheezy。メールサーバとしてはPostfixを使用。今回一番ハマったのはPostfixの設定だったり。

Postfixで受け取ったメールをPHPスクリプトに渡す方法としては、 この記事が参考になる。

また、メールをパースする方法としては、ライブラリを使用することにした。最初は illuminate/mailなども検討したのだけど、サンプルコードが探しやすかったので PEAR::Mail_MimeDecodeにした(サンプルコードは この記事を参考にした)。

インストールはComposerで。

{
    "repositories": [
        {
            "type": "pear",
            "url": "http://pear.php.net/"
        }
    ],
    "require": {
        "pear-pear.php.net/Mail_mimeDecode" : "*"
    }
}

メールを受信するプログラムはこんな感じ(受け取ったメールの本文をDBに保存する)。

<?php

require_once __DIR__ . '/vendor/autoload.php';

$raw_mail = file_get_contents('php://stdin'); // メール本文を標準入力から受け取る

$params = [];
$params['include_bodies'] = true;
$params['decode_bodies']  = true;
$params['decode_headers'] = true;
$params['input'] = $raw_mail;
$params['crlf'] = '\r\n';

$mail_data    = Mail_mimeDecode::decode($params);
$mail_address = $mail_data->headers['from'];
$mail_address = trim(substr($mail_address, strpos($mail_address, '<')), '<>');
$charset      = $mail_data->ctype_parameters['charset'];
$mail_body    = mb_convert_encoding($mail_data->body, 'UTF-8', $charset);

try {
    $dbh = require_once __DIR__ . '/db_connection.php'; // PDOオブジェクトを取得

    // メールアドレスからユーザーを取得
    $sql = 'SELECT * FROM users WHERE mail = :mail';
    $stmt = $dbh->prepare($sql);
    $result = $stmt->execute([':mail' => $mail_address]);

    // DBにメール本文を保存
    if (true === $result) {
        $row = $stmt->fetchAll(PDO::FETCH_ASSOC)[0];
        $sql = 'INSERT INTO logs (users_id, body, created_at) VALUES (:users_id, :body, :created_at)';
        $stmt = $dbh->prepare($sql);
        $stmt->bindParam('users_id', $row['id']);
        $stmt->bindParam('body', $mail_body);
        $stmt->bindParam('created_at', date('Y-m-d H:i:s'));
        $stmt->execute();
    }
} catch (Exception $e) {
    error_log($e->getMessage() . PHP_EOL, 3, __DIR__ . '/log/receiver.log');
    exit(1);
}

メール送信機能はmb_send_mail()使えば簡単。あとは、cronにメール送信スクリプトを登録して、定期実行すればOK。