on 2021-01-12
Intro
Gmail 提供 IMAP & SMTP 的功能
內送郵件 (IMAP) 伺服器
- imap.gmail.com
- 需要安全資料傳輸層 (SSL):是
- 通訊埠:993
外寄郵件 (SMTP) 伺服器
- smtp.gmail.com
- 需要安全資料傳輸層 (SSL):是
- 需要傳輸層安全性 (TLS):是 (如果可用)
- 需要驗證:是
- 安全資料傳輸層 (SSL) 通訊埠:465
- 傳輸層安全性 (TLS)/STARTTLS 通訊埠:587
簡單的說 IMAP(Internet Message Access Protocol) 可以存取該 mail 的信件
SMTP(Simple Mail Transfer Protocol) 是可以利用該 mail 發信
詳細說明可以參照 wiki
另外還有 POP3(Post Office Protocol) 這個和 IMAP 類似的協定
主要 POP3 和 IMAP 的差異主要差在 POP3 是把信件內容拉下來, IMAP 不會, IMAP 是即時連線存取, 但是 IMAP 的好處就是即時連線同步, 這樣在多個裝置就可以即時的更新信件的狀態
所以在一些常見的客戶端程式例如 outlook 或是 Mac OS 裡面的郵件都是利用這些協定在處理 email
這邊當然也可以利用支援這些協定的程式語言寫一些功能來處理 email
使用情境
因為公司的服務每天會有大量的 feedback mail 寄到設定的 Gmail 信箱
雖然會有實習生協助整理和處理 feedback mail 但是還是很花人力
這年頭最貴的就是人力了
所以公司有個基於 Python 的程式在協助處理 feedback mail
那程式主要做的事情就是簡單的辨識 SPAM 與替 feedback 的內容打上 tag 以便於之後快速處理使用者的 feedback
How to do?
- 先開啟 Gmail 裡面的 IMAP 的功能
- 設定用於第三方應用程式驗證的密碼(並非 Google OAuth)
- 開始寫程式
怎麼開始寫 code?
這邊以 Python 為例
在開始前得先搞懂流程要幹什麼
- 連接 IMAP server
- 撈信件
- 讀取信件主旨(subject) 和信件內容(content) 和確認附件(attachement)
smaple code
以下使用 Python
建立 IMAP server 連去信箱讀取信件
from datetime import datetime, timedelta, date
import imaplib
import email
from email.header import decode_header
# pip install imap-tools
from imap_tools.imap_utf7 import encode, decode
ACCOUNT = '@gmail.com'
PWD = ''
mail = imaplib.IMAP4_SSL('imap.gmail.com')
mail.login(ACCOUNT, PWD)
status, data = mail.list()
mail.select('inbox')
# 取前一天的時間
today = (date.today() - timedelta(1)).strftime('%d-%b-%Y')
# 找出前一天未讀的信件
typ, data = mail.search(None, '(UNSEEN)', '(SENTSINCE {0})'.format(today))
# 取到 message id list(message id 就是每封信的 id)
ids = data[0]
id_list = ids.split()
for i in id_list:
typ, data = mail.fetch(i, '(RFC822)')
if typ == 'OK':
print('get mail ok')
for response_part in data:
if isinstance(response_part, tuple):
rawMail = response_part[1].decode()
# 取得信件
msg = email.message_from_string(rawMail)
print(msg)
基本上到這裡拿到 msg 就是這封信件的資訊了
就可以取出來做一些處理
例如: 判斷來信者來回信, 判斷內容替信件添加標籤 等等
關於 imap.search
可以如何使用可參考
Refer - Python’s imaplib (Example)
回信
幾本上回信也是寄信的一種
寄信的部分就是得用到 SMTP
幾本上的流程就是
-
連接 Gmail 的 SMTP server
-
寫信
-
寄信
-
關閉連接
以下為寄信範例
import email
import smtplib
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
smtp_server = smtplib.SMTP_SSL('smtp.gmail.com')
ACCOUNT = '@gmail.com'
PWD = ''
smtp_server.login(ACCOUNT, PWD)
send_mail = MIMEMultipart('mixed')
body = MIMEMultipart('alternative')
body.attach(MIMEText('test send mail', 'plain'))
body.attach(MIMEText('<div>test send mail<div>', 'html'))
send_mail.attach(body)
send_mail['message-id'] = email.utils.make_msgid()
send_mail['subject'] = ''
send_mail['to'] = ''
send_mail['from'] = '@gmail.com'
smtp_server.sendmail('@gmail.com', send_mail['to'], send_mail.as_string())
smtp_server.quit()
需要回信的話就得在發送時指定 references 和指定要回覆給哪封信(每一封信都有獨立的 message id)
所以會先用 imap 取到 mail list 這樣就會有 message id 的資訊了
以下為回信的範例
from datetime import datetime, timedelta, date
import imaplib
import email
from email.header import decode_header
# pip install imap-tools
from imap_tools.imap_utf7 import encode, decode
ACCOUNT = '@gmail.com'
PWD = ''
mail = imaplib.IMAP4_SSL('imap.gmail.com')
mail.login(ACCOUNT, PWD)
status, data = mail.list()
mail.select('inbox')
# 取前一天的時間
today = (date.today() - timedelta(1)).strftime('%d-%b-%Y')
# 找出前一天未讀的信件
typ, data = mail.search(None, '(UNSEEN)', '(SENTSINCE {0})'.format(today))
# 取到 message id list(message id 就是每封信的 id)
ids = data[0]
id_list = ids.split()
for i in id_list:
typ, data = mail.fetch(i, '(RFC822)')
if typ == 'OK':
print('get mail ok')
for response_part in data:
if isinstance(response_part, tuple):
raw_mail = response_part[1].decode()
# 取得信件
msg = email.message_from_string(raw_mail)
print(msg)
var_subject = msg['subject']
subject, encoding = decode_header(var_subject)[0]
# add origin content
for part in msg.walk():
if (part.get('Content-Disposition') and part.get('Content-Disposition').startswith("attachment")):
part.set_type('text/plain')
part.set_payload('Attachment removed: %s (%s, %d bytes)' % (part.get_filename(), part.get_content_type(), len(part.get_payload(decode=True))))
del part['Content-Disposition']
del part['Content-Transfer-Encoding']
reply_mail = MIMEMultipart('mixed')
body = MIMEMultipart('alternative')
body.attach(MIMEText('reply mail', 'plain'))
body.attach(MIMEText('<div>reply mail</div>', 'html'))
reply_mail.attach(body)
reply_mail['message-id'] = email.utils.make_msgid()
reply_mail['in-reply-to'] = msg['message-id']
reply_mail['references'] = msg['message-id']
reply_mail['subject'] = 're: ' + msg['subject']
reply_mail['to'] = msg['reply-to'] or msg['from']
reply_mail['from'] = '@gmail.com'
smtp_server.sendmail('@gmail.com', new['to'], new.as_string())