Instantly share code, notes, and snippets.

Embed
What would you like to do?
imaplibによるラズパイ遠隔操作のサンプルプログラム
import email
import ssl
import subprocess
import imaplib
from email.header import decode_header, make_header
# https://docs.python.jp/3/library/imaplib.html
# [参考]https://qiita.com/ekzemplaro/items/a35e15865d57372f1d2b
# https://docs.python.jp/3.6/library/email.message.html
# https://docs.python.org/ja/3.7/library/email.compat32-message.html#module-email.message
# https://docs.python.jp/3.6/library/email.parser.html
RASPI_MAIL_USERNAME = "username@gmail.com"
RASPI_MAIL_PASSWORD = "password"
def main():
# メールサーバにラズパイ宛のメールが存在するか探す
result = search_command()
if result is False:
return
command, reply_address = result
# コマンドを実行する
out, err = execute_command(command)
# コマンドの実行結果を返信する
sendmail(command, out+err, reply_address)
def search_command():
"""メールサーバにアクセスして、ラズパイ用のメールが存在するか探す"""
# IMAP4クライアントインスタンスを作成する
host = "imap.gmail.com"
port = 993
context = ssl.create_default_context()
imapclient = imaplib.IMAP4_SSL(host, port, ssl_context=context)
#imapclient.debug = 5 # 各命令をトレースする
# IMAPサーバーにログインする
username = RASPI_MAIL_USERNAME
password = RASPI_MAIL_PASSWORD
imapclient.login(username, password)
imapclient.select()
typ, data = imapclient.search(None, "ALL") # data = [b"1 2 3 4 ..."]
#typ, data = imapclient.search(None, "UNSEEN")
#typ, data = imapclient.search(None, "NEW")
#typ, data = imapclient.search(None, "SENTSINCE \"01-Apr-2018\"")
datas = data[0].split()
N = 3 # 取得するメッセージの数
auth_password = "qwerty" # 認証用の文字列
for num in datas[len(datas)-N::]:
typ, data = imapclient.fetch(num, '(RFC822)')
msg = email.message_from_bytes(data[0][1])
# Subjectが#raspiなら遠隔操作を受け付ける
subject = str(make_header(decode_header(msg["Subject"])))
if subject.lower() != "#raspi":
continue
# Multipartに対応させる
# 各パートのいずれかで、一行目が認証用の文字列ならOKとする
for part in msg.walk():
payload = part.get_payload(decode=True)
if payload is None:
continue
charset = part.get_content_charset()
if charset is not None:
payload = payload.decode(charset, "ignore")
lines = payload.splitlines()
# メールの1行目が認証用の文字列と一致するか確認する
if lines[0] != auth_password:
continue
# 認証をパスしたら、メールをサーバ上から削除した後、ログアウトする
imapclient.store(num, "+FLAGS", r"(\Deleted)")
imapclient.close()
imapclient.logout()
# 2行目の文字列と、返信先のメールアドレスを返して終了
return lines[1], str(make_header(decode_header(msg["From"])))
imapclient.close()
imapclient.logout()
return False
def execute_command(command):
""" コマンドを実行する"""
p = subprocess.run(command,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
env={'LANG':'C'},
shell=True
)
return p.stdout.decode("utf-8", "ignore"), p.stderr.decode("utf-8", "ignore")
def sendmail(command, result, reply_address):
import smtplib
from email.mime.text import MIMEText
from email.utils import formatdate
# MIMEメッセージを作成する
main_text = "{}\n".format(result)
charset = "utf-8"
msg = MIMEText(main_text, "plain", charset)
# MIMEメッセージに必要なヘッダを付ける
msg.replace_header("Content-Transfer-Encoding", "base64")
msg["Subject"] = "{}の実行結果".format(command)
msg["From"] = RASPI_MAIL_USERNAME
msg["To"] = reply_address
msg["Date"] = formatdate(None, True)
# SMTPクライアントインスタンスを作成する
host = "smtp.gmail.com"
port = 587
smtpclient = smtplib.SMTP(host, port, timeout=10)
smtpclient.ehlo()
smtpclient.starttls()
smtpclient.ehlo()
# サーバにログインする
username = RASPI_MAIL_USERNAME
password = RASPI_MAIL_PASSWORD
smtpclient.login(username, password)
# メールを送信する
smtpclient.send_message(msg)
smtpclient.quit()
if __name__ == "__main__":
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment