Global IPの変更をGmail APIを使って通知する
過去記事
を活かし, 常時起動しているWindowsマシンにGlobal IPを監視させ, 変更があった際にメール通知が来るようにした.
目次
Requirement
OS: Mac, Windows
python3
- datetime
- requests
- platform
- json
- import pickle
- import os.path
- from googleapiclient.discovery import build
- from google_auth_oauthlib.flow import InstalledAppFlow
- from google.auth.transport.requests import Request
- import base64
- from email.mime.text import MIMEText
- from apiclient import errors
Install Gmail Libraries
pip install --upgrade google-api-python-client google-auth-httplib2 google-auth-oauthlib
Tested Environment
Mac
- macOS Big Sur 11.5.2 on M1 MacBook Air 2020
Windows
- Windows 10 pro on Desktop PC
Usage
Python単体で使用する場合は下記
python3 main.pyorpython main.py
Code関連
ディレクトリ構成
実行前
.├── README.md├── gmailSender.py├── mail-settings.json├── main.bat├── main.py└── token.pickle
実行後
.├── README.md├── gmailSender.py├── global-ip-log.txt├── global-ip.txt├── mail-settings.json├── main.bat├── main.py└── token.pickle
Codes
main.bat
下記のmain.pyをサイレント実行するためのwindowsバッチファイル
@echo offif not "%~0"=="%~dp0.\%~nx0" (start /min cmd /c,"%~dp0.\%~nx0" %*exit)cd %~dp0python main.pyexit
main.py
Global IPをチェックし, gmail通知をする全体のcode
xxxxxxxxxximport sysimport datetimeimport requestsimport platformimport jsonimport gmailSender
def getGlobalIP(): response = requests.get('http://inet-ip.info/ip') _globalIP = ''.join(response.text.splitlines()) return _globalIP
def readPreviousGlobalIP(_fileName): try: f = open(_fileName, 'r', encoding='UTF-8') _previousGlobalIP = f.read() f.close() except: _previousGlobalIP = "123.123.123.123" return _previousGlobalIP
def updateMailSettings(_jsonPath, _message): _json = json.loads(open(_jsonPath, 'r').read()) _json["message"] = _message with open(_jsonPath, 'w') as f: json.dump(_json, f, indent=2, ensure_ascii=False)
def checkGlobalIPChangingAndNotification(_globalIP, _previousGlobalIP): if _globalIP != _previousGlobalIP: _platformSystem = platform.system() _message = "New Global IP: " + _globalIP updateMailSettings("mail-settings.json", _message) gmailSender.mainProcess()
_changedFlag = ", changed" else: _changedFlag = "" return _changedFlag
def writeGlobalIPIntoLog(_logFileName, _globalIP, _changedFlagStr): dtNow = datetime.datetime.now().strftime('%Y-%m-%d_%H-%M-%S') f = open(_logFileName, 'a', encoding='UTF-8') f.write(dtNow + ', ' + _globalIP + _changedFlagStr + '\n') f.close()
def writeGlobalIPIntoTxt(_checkFileName, _globalIP): f = open(_checkFileName, 'w', encoding='UTF-8') f.write(_globalIP) f.close()
def mainProcess(): checkFileName = 'global-ip.txt' logFileName = 'global-ip-log.txt' globalIP = getGlobalIP() if IsPrint: print(globalIP) previousGlobalIP = readPreviousGlobalIP(checkFileName) changedFlagStr = checkGlobalIPChangingAndNotification(globalIP, previousGlobalIP) writeGlobalIPIntoLog(logFileName, globalIP, changedFlagStr) writeGlobalIPIntoTxt(checkFileName, globalIP)
# ==============================================================================================================if __name__ == "__main__": IsPrint = True if len(sys.argv) == 2: if sys.argv[1] == "noPrint": IsPrint = False
mainProcess()
2021/10/16 追記
某場所からアクセスした際に, グローバルIPの値が謎の文字列になってしまうことがあったため, グローバルIPの取得先を変更した
gmailSender.py
Gmailメッセージを送る自作ライブラリ. 詳細は過去記事: Gmail APIを使ってPythonでメール送信する を参照.
import pickleimport os.pathfrom googleapiclient.discovery import buildfrom google_auth_oauthlib.flow import InstalledAppFlowfrom google.auth.transport.requests import Requestimport base64from email.mime.text import MIMETextfrom apiclient import errorsimport json
def getSettings(_jsonPath): _json = json.loads(open(_jsonPath, 'r').read()) return _json
def createMessage(sender, to, subject, message_text): message = MIMEText(message_text) message['to'] = to message['from'] = sender message['subject'] = subject encode_message = base64.urlsafe_b64encode(message.as_bytes()) return {'raw': encode_message.decode()}
def sendMessage(service, user_id, message): try: message = (service.users().messages().send(userId=user_id, body=message).execute()) print('Message Id: %s' % message['id']) return message except errors.HttpError as error: print('An error occurred: %s' % error)
def getAccessToken(_scopes, _token, _credentialJson): creds = None if os.path.exists(_token): with open(_token, 'rb') as token: creds = pickle.load(token) if not creds or not creds.valid: if creds and creds.expired and creds.refresh_token: creds.refresh(Request()) else: flow = InstalledAppFlow.from_client_secrets_file(_credentialJson, _scopes) creds = flow.run_local_server() with open(_token, 'wb') as token: pickle.dump(creds, token) _service = build('gmail', 'v1', credentials=creds) return _service
def mainProcess(): json = getSettings("mail-settings.json") service = getAccessToken(json["scopes"], json["gglToken"], json["gglJson"]) message = createMessage(json["sender"], json["to"], json["subject"], json["message"]) sendMessage(service, 'me', message)
# ===================================================if __name__ == '__main__': mainProcess()
settings.json
メールの設定, 行き先, 本文など
{ "scopes": ["https://www.googleapis.com/auth/gmail.send"], "gglToken": "token.pickle", "gglJson": "credentials.json", "sender": "1234567890@gmail.com", "to": "1234567890@gmail.com", "subject": "Global IP was changed", "message": ""}
token.pickle
Gmail APIを認証した後のトークン
手順
単発での実行確認
- 下図のようなファイル構成にし, 実行する

- GmailにGlobal IP変更のメッセージが届く

定期実行化
Windows
タスクスケジューラを利用する.
毎日1回
- タスクスケジューラを起動

- タスクスケジュールライブラリを右クリックし, 新しいフォルダをクリック

- 新しいフォルダ名を入力し, OKをクリック

- 右上にある基本タスクの作成をクリック

- 基本タスクのなめを入力し, 次へをクリック

- 毎日など自分の好きな周期を選択し次へをクリック

- 開始時刻を設定し, 次へをクリック

- プログラムの開始を選択し, 次へをクリック

- プログラム/スクリプトに上記の
main.batを絶対パスで入力し, 次へをクリック

- 完了をクリック. これで毎日1回実行されるようになる.

1時間に1回
- 上記手順で作成した基本タスクを右クリックし, プロパティをクリック

- トリガータブを選択し, 先程の毎日設定のトリガーを選択した状態で, 編集をクリック

- 繰り返し間隔を1時間に設定し, 継続時間を無期限に設定し, OKをクリック

- OKをクリック

- 時刻になると自動実行される

- Global IPが変更された場合にはGmail通知が届く

以上.
動画