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.py
or
python 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 off
if not "%~0"=="%~dp0.\%~nx0" (
start /min cmd /c,"%~dp0.\%~nx0" %*
exit
)
cd %~dp0
python main.py
exit
main.py
Global IPをチェックし, gmail通知をする全体のcode
xxxxxxxxxx
import sys
import datetime
import requests
import platform
import json
import 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 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
import 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通知が届く
以上.
動画