承認これくしょん

my black histories

雨雲レーダーを監視してメール通知する

某雨雲レーダーを監視し、指定範囲内のメッシュが一定の割合を超えるとGmailからメールを送信します。
PIL以外は全て標準ライブラリを使っているので、AndroidのQPythonでも動きました。
事前にcurlなどでリフレッシュトークンを取得しておいてください。

ソース

# -*- coding: utf-8 -*-

import base64, io, json, logging, time, urllib, urllib2
from datetime import datetime, timedelta
from PIL import Image
from email.mime.text import MIMEText
from email.header import Header

# レーダー設定
AMESH_BASE_URL = "http://tokyo-ame.jwa.or.jp/mesh/100/{0}.gif"
CROP_BOX = (100, 100, 100, 100) # 選択範囲
NOTIFY_PERCENTAGE = 10 # 10%以上で通知
NOTIFY_INTERVAL = 30 # 30分間隔で確認

# Gmail認証
CLIENT_ID = "YOUR_CLIENT_ID"
CLIENT_SECRET = "YOUR_CLIENT_SECRET"
REFRESH_TOKEN = "YOUR_REFRESH_TOKEN"

# 送信メール設定
MAIL_TO = "foo@bar.ne.jp" # 宛先
MAIL_SUBJECT = u"雨雲レーダー" # 件名
MAIL_BODY = u"雨雲が接近しています" # 本文

last_notified = datetime(1, 1, 1)
interval_time = timedelta(minutes = NOTIFY_INTERVAL)

def main():
    global NOTIFY_PERCENTAGE
    global NOTIFY_INTERVAL
    global scheduler
    global last_notified
    global interval_time
    logging.info("処理を開始します")
    now = datetime.now()
    if now - last_notified > interval_time:
        logging.info("レーダー画像を取得します")
        clouds_exists = check_clouds(now)
        if clouds_exists:
            token = get_token()
            if token:
                send_mail(token, now)
        else:
            logging.info("メッシュが%d%%以下です", NOTIFY_PERCENTAGE)
    else:
        logging.info("最後の通知から%d分以内です", NOTIFY_INTERVAL)
    logging.info("処理が終了しました")

def check_clouds(now):
    try:
        timestamp = get_timestamp(now)
        # レーダー画像をダウンロード
        img_file = io.BytesIO(urllib2.urlopen(AMESH_BASE_URL.format(timestamp)).read())
        # PILで開く
        img = Image.open(img_file)
        # 選択範囲でトリミングしてRGB値を取得
        img = img.crop(CROP_BOX)
        img_rgb = img.convert("RGB").getdata()
        # 雨雲のメッシュを数える
        mesh_count = 0.0
        for rgb in img_rgb:
            if rgb != (0, 0, 1):
                mesh_count += 1
        # 割合を算出
        mesh_percentage = mesh_count / len(img_rgb) * 100
        logging.info("現在のメッシュ: %s%%", mesh_percentage)
        # 閾値を超えた場合は通知
        if mesh_percentage > NOTIFY_PERCENTAGE:
            return True
        return False

    except Exception as e:
        logging.error("画像取得でエラーが発生しました: %s", e.message)
        return False

def get_timestamp(now):
    return now.strftime("%Y%m%d%H") + str(now.minute / 5 * 5).zfill(2)

def get_token():
    global CLIENT_ID
    global CLIENT_SECRET
    global REFRESH_TOKEN
    url = "https://accounts.google.com/o/oauth2/token"
    params = {"client_id": CLIENT_ID,
              "client_secret": CLIENT_SECRET,
              "refresh_token": REFRESH_TOKEN,
              "grant_type": "refresh_token"}

    try:
        request = urllib2.Request(url, urllib.urlencode(params))
        response = urllib2.urlopen(request)
        token = json.load(response)["access_token"]
        logging.info("トークンを取得しました")
        return token

    except Exception as e:
        logging.error("トークン取得でエラーが発生しました: %s", e.message)

def send_mail(token, now):
    global MAIL_TO
    global MAIL_SUBJECT
    global MAIL_BODY
    global last_notified
    url = "https://www.googleapis.com/gmail/v1/users/me/messages/send"
    # メールを作成
    encoding = "utf-8"
    message = MIMEText(MAIL_BODY, "plain", encoding)
    message['to'] = MAIL_TO
    message['from'] = "me"
    message['subject'] = Header(MAIL_SUBJECT, encoding)
    data = {'raw': base64.urlsafe_b64encode(message.as_string())}
    # リクエスト送信
    try:
        request = urllib2.Request(url, json.dumps(data))
        request.add_header("Content-Type", "application/json")
        request.add_header("Authorization", "Bearer " + token)
        urllib2.urlopen(request)
        logging.info("メールを送信しました")
        last_notified = now

    except Exception as e:
        logging.error("メール送信でエラーが発生しました: %s", e.message)

if __name__ == "__main__":
    logging.basicConfig(level = logging.INFO, format = "%(asctime)s %(levelname)s %(message)s")
    while True:
        main()
        # 5分間隔で実行
        time.sleep(300)

Base64エンコードする際、URLセーフにするのがポイントではないでしょうか。
しかしglobalが多すぎる、これはひどい