毎朝自動で メール を発するスクリプトが、その送信控えとして自身をcc宛先に入れている為、放っておくとストレージを喰い潰す一方です。そこで、 IMAP サーバ上の古い メール を定期的に 削除 する仕組みを Python で組んでみました。
導入
Raspberry PiやOpenWRTルータのvnstatによる日ごとのトラフィックレポートを機器の死活監視を兼ねて、毎朝発するようスケジューリングしています。
G suite無料版終了に伴い、メールサーバをGMailからロリポップへ変えて以来、時折メールが届かない事象に遭遇したことから、送信元メールアドレスをccに加えて送信控えとしていました。
メール送信に使うアドレスは普段は送信専用にしているので、メールクライアントで日常的にチェックすることも無く、放っておくと受信箱へccメールが溜まる一方なことから、今回、一定日数以前の古いメールを定期的に削除する仕組みを、Pythonの2つのライブラリ(imaplib, imapclient)を比較しながら構築してみます。
imaplibの基本
Pythonに標準で用意されているIMAP用ライブラリがimaplibです。
早速、対話コンソールでロリポップのメールサーバへIMAP接続、フォルダリストを取得してログアウトしてみます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
Python 3.6.9 (default, Mar 15 2022, 13:55:28) [GCC 8.4.0] on linux Type "help", "copyright", "credits" or "license" for more information. >>> import imaplib >>> import pprint >>> imap = imaplib.IMAP4("imap.lolipop.jp") >>> imap.login("##EMAIL_ADDR##", "##PASSWORD##") ('OK', [b'connected to proxy server.']) >>> pprint.pprint(imap.list()) [b'(\\Marked \\HasChildren) "." "INBOX"', b'(\\HasNoChildren) "." "INBOX.Trash"', b'(\\HasNoChildren) "." "INBOX.Drafts"', b'(\\HasNoChildren) "." "INBOX.Sent"'] >>> imap.logout() ('BYE', [b'Courier-IMAP server shutting down']) |

図1.ウェブメールから見たフォルダ一覧
ウェブメールでは4つのフォルダが並列に存在しているように見えますが、IMAP上では imap.list() の戻り値が示す通り、 INBOX の下にその他のフォルダがツリー状にぶら下がっています。
1 2 3 4 |
INBOX ├── Trash ├── Drafts └── Sent |
このライブラリでは、フォルダ名やメールにマルチバイト文字が使われていると、そのままでは正しく表示されないとの不便さがあるようなのですが、別途ライブラリをインストールしなくとも即使えるのは利点です。
imapclientの基本
対して、マルチバイト文字を手間なく正しく表示してくれると評判なのがこちらのimapclient、使う前にインストールが必要です。
1 2 3 4 5 6 7 |
$ sudo pip install imapclient Collecting imapclient Downloading https://files.pythonhosted.org/packages/f1/16/f35309119eb8ccb8e0aca9622e32fbadfd667a00c4bbeddcab18a4d35993/IMAPClient-2.2.0-py2.py3-none-any.whl (179kB) 100% |████████████████████████████████| 184kB 1.7MB/s Requirement already satisfied: six in /usr/local/lib/python3.6/dist-packages (from imapclient) (1.11.0) Installing collected packages: imapclient Successfully installed imapclient-2.2.0 |
基本的な使い方はimaplibと同じで、サーバへ接続してフォルダリストを取得する程度では差異は感じません。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
Python 3.6.9 (default, Mar 15 2022, 13:55:28) [GCC 8.4.0] on linux Type "help", "copyright", "credits" or "license" for more information. >>> from imapclient import IMAPClient >>> import pprint >>> imap = IMAPClient("imap.lolipop.jp") >>> imap.login("##EMAIL_ADDR##", "##PASSWORD##") b'connected to proxy server.' >>> pprint.pprint(imap.list_folders()) [((b'\\Marked', b'\\HasChildren'), b'.', 'INBOX'), ((b'\\HasNoChildren',), b'.', 'INBOX.Trash'), ((b'\\HasNoChildren',), b'.', 'INBOX.Drafts'), ((b'\\HasNoChildren',), b'.', 'INBOX.Sent')] >>> imap.logout() b'Courier-IMAP server shutting down' |
フォルダの選択
ここからは、個々の命題に対して、2つのライブラリそれぞれのアプローチを比較します。
まずは削除したいメールが収納されているフォルダを選択する動作を、imaplibで実現します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
Python 3.6.9 (default, Mar 15 2022, 13:55:28) [GCC 8.4.0] on linux Type "help", "copyright", "credits" or "license" for more information. >>> import imaplib >>> imap = imaplib.IMAP4("imap.lolipop.jp") >>> imap.login("##EMAIL_ADDR##", "##PASSWORD##") ('OK', [b'connected to proxy server.']) >>> typ, mbox = imap.select("INBOX") >>> print(mbox) [b'192'] >>> print(mbox[0].decode()) 192 >>> imap.close() ('OK', [b'mailbox closed.']) >>> imap.logout() ('BYE', [b'Courier-IMAP server shutting down']) |
フォルダを選択するとその戻り値として、中に収納されているメール数を得ることが出来ます。
続いて同じ動作をimapclientで実行してみると、
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
Python 3.6.9 (default, Mar 15 2022, 13:55:28) [GCC 8.4.0] on linux Type "help", "copyright", "credits" or "license" for more information. >>> from imapclient import IMAPClient >>> import pprint >>> imap = IMAPClient("imap.lolipop.jp") >>> imap.login("##EMAIL_ADDR##", "##PASSWORD##") b'connected to proxy server.' >>> mbox = imap.select_folder('INBOX') >>> pprint.pprint(mbox) {b'EXISTS': 192, b'FLAGS': (b'\\Draft', b'\\Answered', b'\\Flagged', b'\\Deleted', b'\\Seen', b'\\Recent'), b'MYRIGHTS': [b'"acdilrsw"'], b'PERMANENTFLAGS': (b'\\*', b'\\Draft', b'\\Answered', b'\\Flagged', b'\\Deleted', b'\\Seen'), b'READ-WRITE': True, b'RECENT': 0, b'UIDVALIDITY': 649646679} >>> print(mbox[b'EXISTS']) 192 >>> imap.close_folder() b'mailbox closed.' >>> imap.logout() b'Courier-IMAP server shutting down' |
こちらの場合は戻り値の情報が少し多くて複雑なのですが、フォルダ内のメール数は mbox[b'EXISTS'] と要素名を名指しすることで取得可能でした。
検索クエリ
次にある日付より古いメールを抽出するような、検索クエリの投げ方を確認してみます。まずはimaplibで試してみると、戻り値として返って来るメッセージ番号がスペース区切りで連結されているだけなので、「何件ヒットしたのか」を数えるには、一度それを split() してから len() に入れる手間が必要になります。
1 2 3 4 5 6 7 8 9 10 11 12 |
>>> typ, mbox = imap.select("INBOX") >>> typ, data = imap.search(None, 'ALL', 'BEFORE', '06-May-2022') >>> print(data) [b'1 2 3 4 5 6 7'] >>> print(len(data)) 1 >>> print(len(data[0])) 13 >>> print(data[0].split()) [b'1', b'2', b'3', b'4', b'5', b'6', b'7'] >>> print(len(data[0].split())) 7 |
同じことをimapclientで試してみると、戻り値はリスト型で得られるので、件数も素直に len() に入れて評価するだけ。
1 2 3 4 5 6 |
>>> mbox = imap.select_folder('INBOX') >>> data = imap.search(['BEFORE', '06-May-2022']) >>> print(data) [51, 52, 53, 54, 55, 56, 57] >>> print(len(data)) 7 |
メッセージの取得
検索クエリで得られたのは条件に合致したメールの番号のみに過ぎません。メールを削除する前に日付や件名と言った最小限の情報は確認したいので、メッセージ番号を元に必要なヘッダ情報を fetch() を使って取得してみます。
まずimaplibでは上述の通り、メッセージ番号はスペース区切りの数字の羅列に過ぎないので、一度 split() してから for ループで個々のメッセージ番号を fetch() 要求するのが常套手段なようです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
>>> typ, mbox = imap.select("INBOX") >>> typ, data = imap.search(None, 'ALL', 'BEFORE', '06-May-2022') >>> for num in data[0].split(): ... typ, fdata = imap.fetch(num, '(BODY.PEEK[HEADER.FIELDS (Date Subject)])') ... pprint.pprint(fdata) [(b'1 (BODY[HEADER.FIELDS ("Date" "Subject")] {86}', b'Date: Thu, 05 May 2022 06:14:02 +0800\r\nSubject: GL-AR750S_E4: Daily Traf' b'fic Report\r\n\r\n'), b')'] [(b'2 (BODY[HEADER.FIELDS ("Date" "Subject")] {86}', b'Date: Thu, 05 May 2022 06:34:03 +0800\r\nSubject: GL-AR750S_A7: Daily Traf' b'fic Report\r\n\r\n'), b')'] [(b'3 (BODY[HEADER.FIELDS ("Date" "Subject")] {77}', b'Date: Thu, 05 May 2022 07:02:11 +0800\r\nSubject: pi1: Daily Traffic Repor' b't\r\n\r\n'), b')'] [(b'4 (BODY[HEADER.FIELDS ("Date" "Subject")] {81}', b'Date: Thu, 05 May 2022 08:13:02 +0900\r\nSubject: homepi2: Daily Traffic R' b'eport\r\n\r\n'), b')'] [(b'5 (BODY[HEADER.FIELDS ("Date" "Subject")] {81}', b'Date: Thu, 05 May 2022 08:33:03 +0900\r\nSubject: homepi3: Daily Traffic R' b'eport\r\n\r\n'), b')'] [(b'6 (BODY[HEADER.FIELDS ("Date" "Subject")] {78}', b'Date: Thu, 05 May 2022 08:53:02 +0900\r\nSubject: homepi2: Daily RAID Repo' b'rt\r\n\r\n'), b')'] [(b'7 (BODY[HEADER.FIELDS ("Date" "Subject")] {82}', b'Date: Thu, 05 May 2022 11:01:04 +0900\r\nSubject: piaware1: Daily Traffic ' b'Report\r\n\r\n'), b')'] |
必要な情報に絞って成形してみます。
1 2 3 4 5 6 7 8 9 10 |
>>> for num in data[0].split(): ... typ, fdata = imap.fetch(num, '(BODY.PEEK[HEADER.FIELDS (Date Subject)])') ... print('Message %s %s' % (num.decode(), fdata[0][1].decode().replace('\r\n', ' '))) Message 1 Date: Thu, 05 May 2022 06:14:02 +0800 Subject: GL-AR750S_E4: Daily Traffic Report Message 2 Date: Thu, 05 May 2022 06:34:03 +0800 Subject: GL-AR750S_A7: Daily Traffic Report Message 3 Date: Thu, 05 May 2022 07:02:11 +0800 Subject: pi1: Daily Traffic Report Message 4 Date: Thu, 05 May 2022 08:13:02 +0900 Subject: homepi2: Daily Traffic Report Message 5 Date: Thu, 05 May 2022 08:33:03 +0900 Subject: homepi3: Daily Traffic Report Message 6 Date: Thu, 05 May 2022 08:53:02 +0900 Subject: homepi2: Daily RAID Report Message 7 Date: Thu, 05 May 2022 11:01:04 +0900 Subject: piaware1: Daily Traffic Report |
一方、imapclientではリスト型のメッセージ番号のかたまりをそのまま fetch() へ渡し、結果を for ループで順に処理する手法になります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
>>> mbox = imap.select_folder('INBOX') >>> data = imap.search(['BEFORE', '01-May-2022']) >>> for msgid, fdata in imap.fetch(data, ['FLAGS', 'BODY.PEEK[HEADER.FIELDS (Date Subject)]']).items(): ... pprint.pprint(fdata) ... {b'BODY[HEADER.FIELDS ("DATE" "SUBJECT")]': b'Date: Sat, 30 Apr 2022 06:14:02 ' b'+0800\r\nSubject: GL-AR750S_E4' b': Daily Traffic Report\r\n\r\n', b'FLAGS': (), b'SEQ': 1} {b'BODY[HEADER.FIELDS ("DATE" "SUBJECT")]': b'Date: Sat, 30 Apr 2022 06:34:02 ' b'+0800\r\nSubject: GL-AR750S_A7' b': Daily Traffic Report\r\n\r\n', b'FLAGS': (), b'SEQ': 2} {b'BODY[HEADER.FIELDS ("DATE" "SUBJECT")]': b'Date: Sat, 30 Apr 2022 08:13:02 ' b'+0900\r\nSubject: homepi2: Dai' b'ly Traffic Report\r\n\r\n', b'FLAGS': (), b'SEQ': 3} {b'BODY[HEADER.FIELDS ("DATE" "SUBJECT")]': b'Date: Sat, 30 Apr 2022 08:33:02 ' b'+0900\r\nSubject: homepi3: Dai' b'ly Traffic Report\r\n\r\n', b'FLAGS': (), b'SEQ': 4} {b'BODY[HEADER.FIELDS ("DATE" "SUBJECT")]': b'Date: Sat, 30 Apr 2022 08:53:03 ' b'+0900\r\nSubject: homepi2: Dai' b'ly RAID Report\r\n\r\n', b'FLAGS': (), b'SEQ': 5} {b'BODY[HEADER.FIELDS ("DATE" "SUBJECT")]': b'Date: Sat, 30 Apr 2022 11:01:04 ' b'+0900\r\nSubject: piaware1: Da' b'ily Traffic Report\r\n\r\n', b'FLAGS': (), b'SEQ': 6} |
読みやすく成形出力するとこのように。
1 2 3 4 5 6 7 8 9 |
>>> for msgid, fdata in imap.fetch(data, ['FLAGS', 'BODY.PEEK[HEADER.FIELDS (Date Subject)]']).items(): ... print('Message %s %s' % (fdata[b'SEQ'], fdata[b'BODY[HEADER.FIELDS ("DATE" "SUBJECT")]'].decode().replace('\r\n', ' '))) Message 1 Date: Tue, 03 May 2022 06:14:02 +0800 Subject: GL-AR750S_E4: Daily Traffic Report Message 2 Date: Tue, 03 May 2022 06:34:02 +0800 Subject: GL-AR750S_A7: Daily Traffic Report Message 3 Date: Tue, 03 May 2022 08:13:01 +0900 Subject: homepi2: Daily Traffic Report Message 4 Date: Tue, 03 May 2022 08:33:03 +0900 Subject: homepi3: Daily Traffic Report Message 5 Date: Tue, 03 May 2022 08:53:02 +0900 Subject: homepi2: Daily RAID Report Message 6 Date: Tue, 03 May 2022 08:50:44 +0800 Subject: pi1: Daily Traffic Report Message 7 Date: Tue, 03 May 2022 11:01:04 +0900 Subject: piaware1: Daily Traffic Report |
メールの削除
メールの削除とは、実際には \\Deleted というフラグをメールに付与するものです。その後、 expunge() するようにドキュメントに記述がありますが、実際には expunge() せずとも、フォルダを close() したり、 logout() してしまえばそれだけで削除が確定します。
削除が確定するともう元には戻せないのでゴミ箱へ移動したい場合は、フラグを付与する前にINBOX.Trashへメールをコピーしておきましょう。
imaplibでは、先ほどのメッセージヘッダ取得ループの中へ組み込んでしまいます。
1 2 3 4 5 6 7 8 |
for num in data[0].split(): typ, fdata = imap.fetch(num, '(BODY.PEEK[HEADER.FIELDS (Date Subject)])') print('Message %s %s' % (num.decode(), fdata[0][1].decode().replace('\r\n', ' '))) #imap.copy(num, "INBOX.Trash") imap.store(num, '+FLAGS', '\\Deleted') #imap.expunge() imap.close() |
imapclientでは、リスト型の複数のメッセージ番号を扱えるので、ループの外でフラグを付与することができます。
1 2 3 4 5 6 7 |
for msgid, fdata in imap.fetch(data, ['FLAGS', 'BODY.PEEK[HEADER.FIELDS (Date Subject)]']).items(): print('Message %s %s' % (fdata[b'SEQ'], fdata[b'BODY[HEADER.FIELDS ("DATE" "SUBJECT")]'].decode().replace('\r\n', ' '))) #imap.copy(data, 'INBOX.Trash') imap.add_flags(data, '\\Deleted') #imap.expunge() imap.close_folder() |
なお、imapclientでは close_folder() の他に unselect_folder() というメソッドがあり、 expunge() せずにフォルダをクローズする、とドキュメントに記されていました。
unselect_folder()
Unselect the current folder and release associated resources.
Unlike close_folder, the UNSELECT command does not expunge the mailbox,
keeping messages with Deleted flag set for example.
実際に試してみると、次のようなエラーで異常終了するので、これはサーバサイドでサポートされている必要がありそうです。
1 2 |
Returns the UNSELECT response string returned by the server → imapclient.exceptions.CapabilityError: Server does not support UNSELECT capability |
フォルダステータスの取得
メール削除後のフォルダ内のメール数を再び取得するのに、imaplibでは status() を使うのですが、この戻り値にもまた悩まされます。
1 2 3 4 5 6 7 8 |
>>> import re >>> typ, stat = imap.status("INBOX", "Messages") >>> print(stat) [b'"INBOX" (MESSAGES 199)'] >>> print(stat[0]) b'"INBOX" (MESSAGES 199)' >>> print(re.sub(r'\D', '', stat[0].decode())) 199 |
imapclientの folder_status() なら、辞書型(連想配列)で得られるので、戻り値をそのまま利用できます。
1 2 3 4 5 |
>>> stat = imap.folder_status("INBOX", "Messages") >>> print(stat) {b'MESSAGES': 199} >>> print(stat[b'MESSAGES']) 199 |
ちなみに上の例では”Messages”に限って取得していますが、ステータスとして得られる情報の全体像は次のようになっているようです。
1 2 3 |
>>> stat = imap.folder_status("INBOX") >>> print(stat) {b'MESSAGES': 199, b'RECENT': 0, b'UIDNEXT': 236, b'UIDVALIDITY': 649646679, b'UNSEEN': 196} |
n日前の日付文字列
実際のバッチスクリプトではメール保存日数を指定して運用するので、次のようなスクリプトで「今日からn日前の日付」を timedelta() で計算し、 strftime() で日付文字列をdd-MMM-yyyy形式で得てから、検索クエリに投げるようにします。
1 2 3 4 5 |
>>> import datetime >>> valDays = 14 >>> duedate = (datetime.date.today() - datetime.timedelta(days=valDays)).strftime('%d-%b-%Y') >>> print(duedate) 13-May-2022 |
imaplib版:古いメール削除スクリプト
解説が少し長くなってしまいましたが、ロリポップのメールサーバ上にある、n日以前の古いメールを削除するスクリプトはこのようになりました。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
import imaplib, re import datetime ## SET DUE DATE valDays = 26 dueDate = (datetime.date.today() - datetime.timedelta(days=valDays)).strftime('%d-%b-%Y') print('Due Date:', dueDate, '=', valDays, 'days') ## LOGIN IMAP SERVER imap = imaplib.IMAP4("imap.lolipop.jp") try: imap.login("##EMAIL_ADDR##", "##PASSWORD##") except Exception as err: print('ERROR:', err) exit() ## SELECT MAILBOX try: typ, mbox = imap.select("INBOX") except Exception as err: print('ERROR:', err) imap.logout() exit() ## GET TOTAL NO. MSGS IN INBOX BEFORE DELETION print('Messages: %s' % mbox[0].decode()) ## SEARCH QUERY typ, data = imap.search(None, 'ALL', 'BEFORE', dueDate) if len(data[0].split()) > 1: print('%d message(s) to be deleted' % len(data[0].split())) ## BROWSE RESULT / FLAG DELETED for num in data[0].split(): typ, fdata = imap.fetch(num, '(BODY.PEEK[HEADER.FIELDS (Date Subject)])') print('Message %s %s' % (num.decode(), fdata[0][1].decode().replace('\r\n', ' '))) #imap.copy(num, "INBOX.Trash") imap.store(num, '+FLAGS', '\\Deleted') #imap.expunge() imap.close() ## RE-GET TOTAL NO. MSGS IN INBOX AFTER CLOSE typ, stat = imap.status("INBOX", "Messages") print('Messages: %s' % re.sub(r'\D', '', stat[0].decode())) else: print('No Messages to be deleted') imap.close() ## LOGOUT imap.logout() |
その実行結果は次の通り。
1 2 3 4 5 6 7 8 9 10 11 12 |
$ python3 imaplib_delold.py Due Date: 05-May-2022 = 26 days Messages: 199 7 message(s) to be deleted Message 1 Date: Wed, 04 May 2022 06:14:03 +0800 Subject: GL-AR750S_E4: Daily Traffic Report Message 2 Date: Wed, 04 May 2022 06:34:02 +0800 Subject: GL-AR750S_A7: Daily Traffic Report Message 3 Date: Wed, 04 May 2022 07:02:11 +0800 Subject: pi1: Daily Traffic Report Message 4 Date: Wed, 04 May 2022 08:13:02 +0900 Subject: homepi2: Daily Traffic Report Message 5 Date: Wed, 04 May 2022 08:33:03 +0900 Subject: homepi3: Daily Traffic Report Message 6 Date: Wed, 04 May 2022 08:53:02 +0900 Subject: homepi2: Daily RAID Report Message 7 Date: Wed, 04 May 2022 11:01:04 +0900 Subject: piaware1: Daily Traffic Report Messages: 192 |
imapclient版:古いメール削除スクリプト
一方、imapclient版のスクリプトはこちらになります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 |
from imapclient import IMAPClient import datetime ## SET DUE DATE valDays = 27 dueDate = (datetime.date.today() - datetime.timedelta(days=valDays)).strftime('%d-%b-%Y') print('Due Date:', dueDate, '=', valDays, 'days') ## LOGIN IMAP SERVER imap = IMAPClient("imap.lolipop.jp") try: imap.login("iot@servercan.net", "GuzdRAYQA_n8Umi7") except Exception as err: print('ERROR:', err) exit() ## SELECT MAILBOX try: mbox = imap.select_folder('INBOX') except Exception as err: print('ERROR:', err) imap.logout() exit() ## GET TOTAL NO. MSGS IN INBOX BEFORE DELETION print('Messages: %s' % mbox[b'EXISTS']) ## SEARCH QUERY data = imap.search(['BEFORE', dueDate]) if len(data) > 1: print('%d message(s) to be deleted' % len(data)) ## BROWSE RESULT for msgid, fdata in imap.fetch(data, ['FLAGS', 'BODY.PEEK[HEADER.FIELDS (Date Subject)]']).items(): print('Message %s %s' % (fdata[b'SEQ'], fdata[b'BODY[HEADER.FIELDS ("DATE" "SUBJECT")]'].decode().replace('\r\n', ' '))) #pprint.pprint(fdata) ## FLAG DELETED #imap.copy(data, 'INBOX.Trash') imap.add_flags(data, '\\Deleted') #imap.expunge() imap.close_folder() ## RE-GET TOTAL NO. MSGS IN INBOX AFTER DELETION stat = imap.folder_status("INBOX", "Messages") print('Messages: %s' % stat[b'MESSAGES']) else: print('No Messages to be deleted') imap.close_folder() ## LOGOUT imap.logout() |
imaplib版と同じですが、実行結果は次の通りです。
1 2 3 4 5 6 7 8 9 10 11 12 |
$ python3 imapclient_delold.py Due Date: 04-May-2022 = 27 days Messages: 205 7 message(s) to be deleted Message 1 Date: Tue, 03 May 2022 06:14:02 +0800 Subject: GL-AR750S_E4: Daily Traffic Report Message 2 Date: Tue, 03 May 2022 06:34:02 +0800 Subject: GL-AR750S_A7: Daily Traffic Report Message 3 Date: Tue, 03 May 2022 08:13:01 +0900 Subject: homepi2: Daily Traffic Report Message 4 Date: Tue, 03 May 2022 08:33:03 +0900 Subject: homepi3: Daily Traffic Report Message 5 Date: Tue, 03 May 2022 08:53:02 +0900 Subject: homepi2: Daily RAID Report Message 6 Date: Tue, 03 May 2022 08:50:44 +0800 Subject: pi1: Daily Traffic Report Message 7 Date: Tue, 03 May 2022 11:01:04 +0900 Subject: piaware1: Daily Traffic Report Messages: 198 |
結果的に同じ動作をする2つのスクリプトが出来上がりましたが、imaplibのラッパーライブラリとも取れるimapclientを使った方が、今後のデバッグや改良に際しても良さそうです。