import urllib.request import urllib.parse import json import ssl from datetime import datetime, timezone, timedelta import wmill JST = timezone(timedelta(hours=9)) def main(): # シークレット取得 api_key = wmill.get_variable("u/admin/NOTIFICATION_API_KEY") line_token = wmill.get_variable("u/admin/LINE_CHANNEL_ACCESS_TOKEN") line_to = wmill.get_variable("u/admin/LINE_TO") # 前回実行時刻を取得(初回は現在時刻 - 10分) try: last_checked = wmill.get_variable("u/admin/SHIRAOU_LAST_CHECKED_AT") if not last_checked: last_checked = None except Exception: last_checked = None if last_checked: since = last_checked else: since = (datetime.now(JST) - timedelta(minutes=10)).isoformat() print(f"[通知] 変更確認: since={since}") # API呼び出し ssl_ctx = ssl.create_default_context() ssl_ctx.check_hostname = False ssl_ctx.verify_mode = ssl.CERT_NONE params = urllib.parse.urlencode({"since": since}) url = f"https://shiraou.keinafarm.net/reservations/api/changes/?{params}" req = urllib.request.Request(url, headers={"X-API-Key": api_key}) with urllib.request.urlopen(req, context=ssl_ctx, timeout=30) as resp: data = json.loads(resp.read().decode("utf-8")) checked_at = data["checked_at"] reservations = data.get("reservations", []) usages = data.get("usages", []) print(f"[通知] checked_at={checked_at}, 予約={len(reservations)}件, 実績={len(usages)}件") # 変更があればLINE通知(エラー時は状態を更新しない) if reservations or usages: message = _format_message(reservations, usages) _send_line(line_token, line_to, message) print("[通知] LINE送信完了") else: print("[通知] 変更なし、通知スキップ") # 正常完了時のみ状態更新 wmill.set_variable("u/admin/SHIRAOU_LAST_CHECKED_AT", checked_at) print(f"[通知] last_checked_at更新: {checked_at}") return { "since": since, "checked_at": checked_at, "reservations_count": len(reservations), "usages_count": len(usages), "notified": bool(reservations or usages), } def _format_message(reservations, usages): lines = ["\U0001f4cb 営農システム 変更通知\n"] OP_R = { "create": ("\U0001f7e2", "予約作成"), "update": ("\U0001f535", "予約変更"), "cancel": ("\U0001f534", "予約キャンセル"), } OP_U = { "create": ("\U0001f7e2", "実績登録"), "update": ("\U0001f535", "実績修正"), "delete": ("\U0001f534", "実績削除"), } for r in reservations: start = r["start_at"][:16].replace("T", " ") end = r["end_at"][:16].replace("T", " ") icon, label = OP_R.get(r["operation"], ("\u26aa", r["operation"])) lines += [ f"{icon} {label}", f" 機械: {r['machine_name']}", f" 利用者: {r['user_name']}", f" 日時: {start} \uff5e {end}", ] if r.get("reason"): lines.append(f" 理由: {r['reason']}") lines.append("") for u in usages: start = u["start_at"][:16].replace("T", " ") icon, label = OP_U.get(u["operation"], ("\u26aa", u["operation"])) lines += [ f"{icon} {label}", f" 機械: {u['machine_name']}", f" 利用者: {u['user_name']}", f" 利用量: {u['amount']}{u['unit']}", f" 日: {start[:10]}", ] if u.get("reason"): lines.append(f" 理由: {u['reason']}") lines.append("") return "\n".join(lines).strip() def _send_line(token, to, message): payload = json.dumps({ "to": to, "messages": [{"type": "text", "text": message}], }).encode("utf-8") req = urllib.request.Request( "https://api.line.me/v2/bot/message/push", data=payload, headers={ "Authorization": f"Bearer {token}", "Content-Type": "application/json", }, method="POST", ) with urllib.request.urlopen(req, timeout=30) as resp: return resp.read().decode("utf-8")