自宅のグローバルIPの変更をGMailで通知する

## やること

1. pythonにてグローバルIPとローカルIPを調べて、変更があればGMailでその内容を送信するスクリプトを作る
2. タスクスケジューラで定期的にスクリプトを実行する

---

## 経緯

ipad proを買ったので、開発環境として使うために自宅のWindowsマシンをRDTで使用したいなぁと考えました。
ところが、自宅のPR-500KIはVPN対応しているのですが、光電話を契約していないため、グローバルIP変更通知機能が使えませんでした。
そこでRDTで接続するマシンからグローバルIPの変更を通知できるようにしました。
（ついでにWindowsマシンはDHCPなんでローカルIPも通知しています）

---

## GMailのアプリパスワードの発行

GMailでSMTPによるメール送信をするためには、アプリパスワードを発行しておく必要があります。

1. Googleアカウントにログインする
2. 「Googleへのログイン」を選択
3. 「アプリパスワード」を選択
4. ページ下部の「アプリ パスワードを生成するアプリと端末を選択してください。」で次のように設定
    - アプリを選択→「メール」
    - 端末を選択→「Windowsマシン」

そうするとパスワードが発行されるのでそのパスワードをメモっておく。

---

## スクリプト

スクリプトを作成する際に必要な知識については下記のサイトを参考にしました。

- GMailメールの送信方法
    - https://qiita.com/okhrn/items/630a87ce1a44778bbeb1
- IPアドレスの取得方法
    - http://edosha.hatenablog.jp/entry/2017/08/09/150636
- グローバルIPの取得方法
    - https://qiita.com/kanpou0108/items/734b947f5a95109e7bb9
- loggingの使い方
    - https://qiita.com/mimitaro/items/9fa7e054d60290d13bfc
    
``` python:notification_change_ipaddress.py
import requests
import json
import socket
import smtplib
from email.mime.text import MIMEText
from email.utils import formatdate
import logging
from logging import getLogger, StreamHandler,FileHandler, Formatter
LOGGER = getLogger('NCG')
LOGGER.setLevel(logging.DEBUG)

# 送信済みのIPの保存先
IPINFO_FILE = "./send_ipaddress.json"
# IP変更チェック実行ログ保存先
LOG_FILE = "notification_change_ipaddress.log"

# Gmailの設定
# https://qiita.com/okhrn/items/630a87ce1a44778bbeb1
FROM_ADDRESS = 'GMailのアドレス'
MY_PASSWORD = 'アプリパスワード'
TO_ADDRESS = '送信先のアドレス'
BCC = ''
SUBJECT = 'IPアドレス変更メール'
SMTP_ADDRESS = "smtp.gmail.com"
SMTP_PORT = 587

def create_message(from_addr, to_addr, bcc_addrs, subject, body):
    msg = MIMEText(body)
    msg['Subject'] = subject
    msg['From'] = from_addr
    msg['To'] = to_addr
    msg['Bcc'] = bcc_addrs
    msg['Date'] = formatdate()
    return msg

def send(from_addr, to_addrs, msg):
    smtpobj = smtplib.SMTP(SMTP_ADDRESS, SMTP_PORT)
    smtpobj.ehlo()
    smtpobj.starttls()
    smtpobj.ehlo()
    smtpobj.login(FROM_ADDRESS, MY_PASSWORD)
    smtpobj.sendmail(from_addr, to_addrs, msg.as_string())
    smtpobj.close()

def send_ipinfo(ipinfo):
    to_addr = TO_ADDRESS
    subject = SUBJECT
    body = json.dumps(ipinfo)
    msg = create_message(FROM_ADDRESS, to_addr, BCC, subject, body)
    send(FROM_ADDRESS, to_addr, msg)

def get_global_ip():
	url="http://inet-ip.info/json"
	headers = {"content-type": "application/json"}
	result = requests.get(url,headers)
	data = result.json()
	return data["IP"]

def get_json_from_file(ipinfo_file):
    '''
    JSONファイルの読み込み
    '''
    df ={}
    try:
        with open(ipinfo_file,"r") as f:
            df = json.load(f)
    except FileNotFoundError:
        LOGGER.debug("file not found :"+ ipinfo_file)
    finally:
        pass
    
    return df

def set_json_to_file(ipinfo_file,ipinfo):
    '''
    JSONファイルの書き込み
    '''
    with open(ipinfo_file,"w") as f:
        json.dump(ipinfo,f,indent=4,ensure_ascii=False)

# IPを保存するJSONのカラム名定義
GLOBALIP="globalip"
LOCALIP="localip"

def notification_change_ipaddress(ipinfo_file):
    """
    IPを調べて、前回メール送信した時と変わっていたら、再度新しいIP
    アドレスをメール送信する
    """
    LOGGER.debug('Check for address change')
    prev = get_json_from_file(ipinfo_file)
    if not prev :
        prev={
            GLOBALIP:"",
            LOCALIP:""
        }
    # global ip取得
    globalip = get_global_ip()
    # local ip取得
    localip = socket.gethostbyname_ex(socket.gethostname())

    # チェック 
    if prev[GLOBALIP] != globalip or prev[LOCALIP] != list(localip) :
        LOGGER.debug(' IPAddress change detected')
        prev[GLOBALIP] = globalip
        prev[LOCALIP] = localip
        # mail送信
        send_ipinfo(prev)
        # 送信した内容をローカルに保存
        set_json_to_file(ipinfo_file,prev)

if __name__ == "__main__":

    # loggerの使い方は下記のサイトを参照
    # https://qiita.com/mimitaro/items/9fa7e054d60290d13bfc

    # logging handlerの生成
    file_handler = FileHandler(LOG_FILE, 'a')
    # handlerのログレベル設定(ハンドラが出力するエラーメッセージのレベル)
    file_handler.setLevel(logging.DEBUG)
    # ログ出力フォーマット設定
    handler_format = Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
    file_handler.setFormatter(handler_format)
    LOGGER.addHandler(file_handler)

    # IPAddressチェック
    notification_change_ipaddress(IPINFO_FILE)
    
```

---

### batファイルの作成

windowsでスクリプトとして実行しますので、batファイルを作成します。
なお、python実行環境としてはvirtualenvにて仮想環境を作って動作させます。
あらかじめ環境は作成しておきましょう

> virtualenv env

また、「requests」を使用しますのでpipでインストールしましょう

> .\env\Scripts\activate.bat
> pip install requests

ここまで準備ができたらバッチファイルは次のようにします。

``` bat:notification_change_ipaddress.bat
@echo off

call .\env\Scripts\activate.bat
python notification_change_ipaddress.py
```

batファイルとpythonのスクリプトは同じディレクトリに配置しておきます。

---

## タスクスケジューラの設定

コルタナに「タスクスケジューラ」と打てばデスクトップアプリが見つかります。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/13360/0244a5df-498e-1f68-d3b9-58412b1f7c01.png)

1. 左側の「タスク作成...」を押します
![image.png](https://qiita-image-store.s3.amazonaws.com/0/13360/68fddf90-775a-6bc4-840e-be1be4f8297c.png)

2. 「操作」タブを選択し、下部にある「新規」ボタンを押下

3. 「プログラム/スクリプト」にbatファイルのパスを指定し、「開始」の欄にbatファイルを格納しているフォルダのパスを指定します
![image.png](https://qiita-image-store.s3.amazonaws.com/0/13360/59384483-e5e6-a7dc-0930-ed02eb314791.png)

4. 「トリガー」タブを選択し、「新規」ボタンを押下
5. この設定は好みですが、「毎日」「1時間毎に実行」するようにしています
![image.png](https://qiita-image-store.s3.amazonaws.com/0/13360/c4ad127c-1e6e-9e64-2bb9-279ca0391a2e.png)

これで１時間ごとにIPアドレスを調べて、変更があればGmailが送信されます。
---

## メールの送信内容

メールの本文は次のようになります

```
{"globalip": "xxx.xxx.xxx.xx","localip": [
"送信されたマシン名",[],["192.168.xxx.xxx",
"192.168.xxx.xxx","192.168.xxx.xxx",
"192.168.xxx.xxx"]]}
```