Table of Contents
1 テストSMTPサーバーを立てる
emailのテストをするには、何はともあれSMTPサーバーが必要ですが、Pythonではとても簡単にテスト用のSMTPサーバーを立てることができます。
まずはpipでモジュールをインストールします。
pip install aiosmtpd
そして実行します。
python -m aiosmtpd -n
なんと、これだけ。aiosmtpd公式ドキュメントによるとlocalhostのポート8025をリッスンするようです。変えるには -l <[HOST][:PORT]>
オプションを与えます。
pipするのが面倒な方は、標準ライブラリにsmtpdがありますが、公式ドキュメントによるとdeprecatedなのでaiosmtpdを使うように書いてありました。以下で使えます。
python -m smtpd -n -c DebuggingServer <IP address>:<port>
2 テストメールを送る
SMTPサーバーを立てるほどではありませんが、テストメールの送信も簡単にできます。
import smtplib from email import utils from email.mime.text import MIMEText msg = MIMEText('This is the body') # メール本文 msg['To'] = utils.formataddr(( 'Recipient', 'test_recv@example.com')) msg['From'] = utils.formataddr(( 'Author', 'test_send@example.com')) msg['Subject'] = 'Test email subject' server = smtplib.SMTP('localhost', 8025) # server.set_debuglevel(True) # デバッグ用詳細メッセージ出力 try: server.sendmail('test_send@example.com', ['test_recv@example.com'], msg.as_string()) finally: server.quit()
3 テストしてみる
ターミナル上でテスト用のSMTPサーバーを走らせている状態で、上記のテストメール送信スクリプトを別ターミナルから実行してみます。
python emtest.py
あ、来ました。
$ python -m aiosmtpd -n ^[---------- MESSAGE FOLLOWS ---------- Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit To: Recipient <test_recv@example.com> From: Author <test_send@example.com> Subject: Simple test message X-Peer: ('::1', 52132, 0, 0) This is the body. ------------ END MESSAGE ------------
Pythonを使うとemailのテストがとても簡単にできますね。
上記スクリプトのデバッグ用詳細メッセージ出力の行のコメントを外すと、SMTPサーバーとの詳細なやりとりが見られます。(一部情報を伏せています)
$ python emtest.py send: 'ehlo localhost.localdomain\r\n' reply: b'250-localhost.localdomain\r\n' reply: b'250-8BITMIME\r\n' reply: b'250 HELP\r\n' reply: retcode (250); Msg: b'localhost.localdomain\n8BITMIME\nHELP' send: 'mail FROM:<test_send@example.com>\r\n' reply: b'250 OK\r\n' reply: retcode (250); Msg: b'OK' send: 'rcpt TO:<test_recv@example.com>\r\n' reply: b'250 OK\r\n' reply: retcode (250); Msg: b'OK' send: 'data\r\n' reply: b'354 End data with <CR><LF>.<CR><LF>\r\n' reply: retcode (354); Msg: b'End data with <CR><LF>.<CR><LF>' data: (354, b'End data with <CR><LF>.<CR><LF>') send: b'Content-Type: text/plain; charset="us-ascii"\r\nMIME-Version: 1.0\r\nContent-Transfer-Encoding: 7bit\r\nTo: Recipient <test_recv@example.com>\r\nFrom: Author <test_send@example.com>\r\nSubject: Simple test message\r\n\r\nThis is the body.\r\n.\r\n' reply: b'250 OK\r\n' reply: retcode (250); Msg: b'OK' data: (250, b'OK') send: 'quit\r\n' reply: b'221 Bye\r\n' reply: retcode (221); Msg: b'Bye'
4 宛先が表示されない?
あることに気がつきました。上で紹介した簡易smtpサーバーで表示される情報に、宛先(To/Cc/Bcc)が表示されないことがあるのです。調べてみたところ、宛先の情報はSMTP envelopeに含まれるRCPT TOが使われることがわかりました。実際の宛先が知りたい場合には、上記smtpサーバーで出力される情報では不足です。
そこで、aiosmtpdのドキュメントを参考(ほぼ丸写しとも言う)に、SMTPサーバー側も簡単なスクリプトを作成してRCPT TO情報を出力するようにしました。
import asyncio class MyHandler: async def handle_DATA(self, server, session, envelope): print(f'Msg from {envelope.mail_from}') # envelopeのRCPT TO情報を表示する print(f'Msg for {envelope.rcpt_tos}') print(f'Msg data:\n') for ln in envelope.content.decode('utf8', errors='replace').splitlines(): print(f'> {ln}'.strip()) print() print('End of msg') return '250 Message accepted for delivery' from aiosmtpd.controller import Controller ctrlr = Controller(MyHandler(), hostname='<IPアドレス>') ctrlr.start()
さて、どうでしょうか。To/Cc/Bccを含めない送信元からメールを出してみます。
Msg from <送り主emailアドレス> Msg for ['<宛先emailアドレス>'] # RCPT TOsアドレスのリスト Msg data: > Date: Fri, 5 Feb 2021 18:12:38 -0500 (EST) > From: <送り主emailアドレス> > Message-ID: <XXXXX2588.7.XXXXXXXX.JavaMail.root@XXXXX> > Subject: <タイトル文字列> > MIME-Version: 1.0 > Content-Type: text/plain; charset=us-ascii > Content-Transfer-Encoding: 7bit > > This is email contents. > End of msg
無事、宛先アドレスがわかるようになりました。(引用では伏せてあります)
なお、送信側を以下のように変えて、RCPT TOにToとは異なるアドレス(複数可能)を指定することができます。
try: server.sendmail('send@example.com', # mail fromアドレス ['recv1@example.com', 'recv2@example.com'], # RCPT TOsアドレスのリスト msg.as_string())