添付ファイル付メールを受信したと同時に、他サーバに添付ファイルをコピーさせるプログラム作ってみた

どうも超面倒くさがりバックエンドエンジニアの「ささやーま」です。
面倒くさがりだからエンジニアになったといっても過言ではありません。
単純作業は、時間のあるときに自動化してやろうといつも考えております。
で、今回はブログのネタついでに、業務で手動作業しているものを自動化してみました。

作業の流れ

自動化前の流れ

自動化前

  1. 顧客からメール(添付画像付)がXserver上の「ささやーま」宛メールボックスに届く。
  2. 「ささやーま」がメールを受信して、添付画像ファイルをscpコマンドを用いてAサーバへコピー。
  3. Aサーバへ画像ファイルをコピー完了した旨を顧客へメールで返信。

自動化後の流れ

自動化後

  1. 顧客からメール(添付画像付)がXserver上の「ささやーま」宛メールボックスに届く。
  2. Xserver上の「ささやーま」宛メールボックスに届いたメールを同サーバ上でphpプログラムに転送。メール解析を行いphpプログラムが添付画像ファイルをAサーバへコピー。
  3. Aサーバへ画像ファイルをコピー完了した旨をphpプログラムが顧客へメールで自動返信。

自動化するための準備

条件

  • Xserverでメールサービスを利用してメールの送受信をしている
  • 上記Xserver上でLaravelを動かすことができる(Laravelのartisanコマンドを作成)
  • XserverからAサーバへSSH接続ができる

必要ライブラリ等のインストール

※使用しているサーバの都合上、デフォルトのphpバージョンを変えることができなかったため、phpはフルパス指定で実行しています。

Xserverへ自動化に必要なライブラリ等をインストールします。
SSHログイン時のホームディレクトリは以下として説明します。

/home/sasayama

composerインストール


$ cd
$ mkdir bin
$ cd bin
$ curl -sS https://getcomposer.org/installer | php
$ mv composer.phar composer

mailparseライブラリインストール

後述のphp-mime-mail-parserで必要になります。


$ cd
$ mkdir php_source
$ cd php_source/
$ wget https://pecl.php.net/get/mailparse-3.1.3.tgz
$ tar zxvf mailparse-3.1.3.tgz
$ cd mailparse-3.1.3
$ /opt/php-7.4.25/bin/phpize
$ ./configure  --with-php-config=/opt/php-7.4.25/bin/php-config
$ make

Laravelプロジェクト作成

自動化を行う処理をLaravelのコマンドラインプログラムで作成するため、Laravelプロジェクトを作成します。


$ cd
$ /usr/bin/php7.4 /home/sasayama/bin/composer create-project laravel/laravel:^8.0 byebye-sasayama

league/flysystem-sftpインストール

sftpで他サーバへファイルコピーするために使用します。


$ cd byebye-sasayama
$ /usr/bin/php7.4 /home/sasayama/bin/composer require league/flysystem-sftp "~1.0"

php-mime-mail-parserインストール

受信したメールを解析するために使用します。


$ /usr/bin/php7.4 /home/sasayama/bin/composer require php-mime-mail-parser/php-mime-mail-parser

Laravel設定

Laravelのファイルストレージでsftpを使用するために設定を追加します。

config/filesystems.php


'sftp' => [
    'driver' => 'sftp',
    'host' => 'xxx.xxx.xxx.xxxx',   // ← AサーバのIPアドレス
    'port' => 22,
    'username' => 'xxxxxxx',    // ← AサーバへのSSH接続アカウント
    'password' => 'xxxxxxx',    // ← AサーバへのSSH接続パスフレーズ
    'root' => '/home/xxxx/image',  // ← AサーバへのSSH接続後のデフォルトパス
    'privateKey' => '/xxxxx/id_rsa.pem',  // ← AサーバへのSSH接続秘密鍵 PEM形式
    'timeout' => 30,
]

※秘密鍵はPEM形式です。
すでにopenssh形式で作成している場合は以下のコマンドで変換できます。
openssh形式を上書きしてしまうので、残したい場合はコピーしておいてください。
パスフレーズはopenssh形式と同じにしてください。


$ cp ~/.ssh/id_rsa ~/.ssh/id_rsa.pem
$ ssh-keygen -p -m pem -f ~/.ssh/id_rsa.pem

自動化プログラム作成

Laravelコマンド生成して、処理を書いていきます。


$ /usr/bin/php7.4 artisan command:scp-attached-file

完了メール用のMailableクラスを以下コマンドで作成します。


$ /usr/bin/php7.4 artisan make:mail SendResultEmail

app/Console/Commands/ScpAttachedFile.php

最小限の処理だけ書きます。
*ソース中にもかきましたが、このままでは無条件にsftp転送が実行されますので条件を入れて必要なときだけ処理が実行されるようにする必要があります。


<?php

namespace App\Console\Commands;

use Illuminate\Console\Command;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Facades\Mail;
use eXorus\PhpMimeMailParser\Parser;
use eXorus\PhpMimeMailParser\Attachment;
use App\Mail\SendResultEmail;

class ScpAttachedFile extends Command
{
    /**
     * The name and signature of the console command.
     *
     * @var string
     */
    protected $signature = 'command:scp-attached-file';

    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = '受信メールしたメールの添付ファイルを指定サーバにSCPでコピーします';

    /**
     * Create a new command instance.
     *
     * @return void
     */
    public function __construct()
    {
        parent::__construct();
    }

    /**
     * Execute the console command.
     *
     * @return int
     */
    public function handle()
    {
        $parser = new Parser();
        $parser->setStream(fopen('php://stdin', 'r'));

        // to取得
        $rawHeaderto = $parser->getHeader('to');

        // from取得
        $rawHeaderFrom = $parser->getHeader('from');

        // 件名
        $subject = $parser->getHeader('subject');

        // メール本文取得
        $text = $parser->getMessageBody('text');
        $html = $parser->getMessageBody('html');
        $htmlEmbedded = $parser->getMessageBody('htmlEmbedded');
		
        // ここにいろいろ条件いれてください
        // 条件いれないとすべてのメールに対して処理が走ります
        // 例えば、toがAさんからだったらとか、subjectが「画像追加依頼」だったら処理するとか・・・。

        // 添付画像転送処理
        try {
            $attachments = $parser->getAttachments();
            foreach ($attachments as $attachment) {
                // typeがjpgだったら処理
                if ($attachment->getContentType() === 'image/jpeg') {
                    // ↓ たったこれだけでsfp転送できる
                    Storage::disk('sftp')->put($attachment->getFilename(), $attachment->getContent());

                    if (Storage::disk('sftp')->exists($attachment->getFilename())) {
                        // 転送したファイルの存在チェックOK
                    } else {
                        // 転送したファイルの存在チェックNO
                        throw \Exception("書き込みできませんでした);
                    }
                }
            }
        } catch (\Exception $e) {
            logs()->error($e->getMessage());
            return 0;
        }

        // 転送完了メール送信
        Mail::to('hoge@fuga.com')->send(new SendResultEmail());

        return 0;
    }
}

app/Mail/SendResultEmail.php

こちらも本記事では必要最小限の処理です。メールに何かしら処理の内容を渡したい場合はこちらで処理します。


<?php

namespace App\Mail;

use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Mail\Mailable;
use Illuminate\Queue\SerializesModels;

class SendResultEmail extends Mailable
{
    use Queueable, SerializesModels;

    /**
     * Create a new message instance.
     *
     * @return void
     */
    public function __construct()
    {
    }

    /**
     * Build the message.
     *
     * @return $this
     */
    public function build()
    {
        return $this->subject('転送処理完了しました')
                    ->text('emails.send_result_email');
    }
}

resources/views/emails/send_result_email.blade.php

送信するメール本文です。


いつもお世話になっております。
ジェイオンラインの笹山です。

転送処理完了しました。

ご確認よろしくお願いたします。

-------------------------------------------
株式会社ジェイオンライン
笹山 昭秀

Xserver設定

前述で準備は完了したので、作成したプログラムをXserverのメールボックスに受信したと同時に実行させます。

mailfilterの設定

届いたメールを振り分けるものになります。
この振り分け機能を用いて、受信したメールをプログラムの標準入力に渡します。

Xserverの場合は以下のファイルを編集します。

/home/[サーバーID]/[ドメイン名]/mail/[サブドメイン]/[アドレス名]/.alias
cc "!hoge@jonline.com"
cc "| /usr/bin/php7.4 -d extension=/home/sasayama/php_source/mailparse-3.1.3/modules/mailparse.so /home/sasayama/mohtool/artisan command:scp-attached-file"

※mailparse.soですがサーバの都合上php.iniに設定するのはやめて、自動化プログラムが動くときだけ指定して実行しています。

これでXserver上のメールボックスにメールが届いたと同時に自動化プログラムが実行されます。

まとめ

ながながとなりましたが、これで自動化できました。
バイバ~イ「ささやーま」!

スタッフ積極採用中

ジェイオンラインではスタッフを随時募集しております。
採用情報ページよりお気軽にお問い合わせください。