From 18765486567b51005230ccc0f183be1bbd6c883f Mon Sep 17 00:00:00 2001 From: Windmill Bot Date: Sat, 21 Feb 2026 06:30:12 +0000 Subject: [PATCH] Auto-sync: 2026-02-21 06:30:12 --- .../shiraou_notification__flow/flow.yaml | 18 +++ .../変更確認・line通知.lock | 9 ++ .../変更確認・line通知.py | 127 ++++++++++++++++++ ...raou_notification_every_5min.schedule.yaml | 10 ++ workflows/wmill-lock.yaml | 2 + 5 files changed, 166 insertions(+) create mode 100644 workflows/f/shiraou/shiraou_notification__flow/flow.yaml create mode 100644 workflows/f/shiraou/shiraou_notification__flow/変更確認・line通知.lock create mode 100644 workflows/f/shiraou/shiraou_notification__flow/変更確認・line通知.py create mode 100644 workflows/f/shiraou/shiraou_notification_every_5min.schedule.yaml diff --git a/workflows/f/shiraou/shiraou_notification__flow/flow.yaml b/workflows/f/shiraou/shiraou_notification__flow/flow.yaml new file mode 100644 index 0000000..d310c98 --- /dev/null +++ b/workflows/f/shiraou/shiraou_notification__flow/flow.yaml @@ -0,0 +1,18 @@ +summary: 白皇集落営農 変更通知 +description: shiraou.keinafarm.net の予約・実績変更をポーリングし、変更があればLINEで管理者に通知する。5分毎に実行。 +value: + modules: + - id: a + summary: 変更確認・LINE通知 + value: + type: rawscript + content: '!inline 変更確認・line通知.py' + input_transforms: {} + lock: '!inline 変更確認・line通知.lock' + language: python3 +schema: + $schema: 'https://json-schema.org/draft/2020-12/schema' + type: object + order: [] + properties: {} + required: [] diff --git a/workflows/f/shiraou/shiraou_notification__flow/変更確認・line通知.lock b/workflows/f/shiraou/shiraou_notification__flow/変更確認・line通知.lock new file mode 100644 index 0000000..877c078 --- /dev/null +++ b/workflows/f/shiraou/shiraou_notification__flow/変更確認・line通知.lock @@ -0,0 +1,9 @@ +# py: 3.12 +anyio==4.12.1 +certifi==2026.1.4 +h11==0.16.0 +httpcore==1.0.9 +httpx==0.28.1 +idna==3.11 +typing-extensions==4.15.0 +wmill==1.640.0 \ No newline at end of file diff --git a/workflows/f/shiraou/shiraou_notification__flow/変更確認・line通知.py b/workflows/f/shiraou/shiraou_notification__flow/変更確認・line通知.py new file mode 100644 index 0000000..13eeb13 --- /dev/null +++ b/workflows/f/shiraou/shiraou_notification__flow/変更確認・line通知.py @@ -0,0 +1,127 @@ +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分) + state = wmill.get_state() or {} + last_checked = state.get("last_checked_at") + 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_state({"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") diff --git a/workflows/f/shiraou/shiraou_notification_every_5min.schedule.yaml b/workflows/f/shiraou/shiraou_notification_every_5min.schedule.yaml new file mode 100644 index 0000000..cd64c5b --- /dev/null +++ b/workflows/f/shiraou/shiraou_notification_every_5min.schedule.yaml @@ -0,0 +1,10 @@ +args: {} +cron_version: v2 +email: akiracraftwork@gmail.com +enabled: true +is_flow: true +no_flow_overlap: false +schedule: 0 */5 * * * * +script_path: f/shiraou/shiraou_notification +timezone: Asia/Tokyo +ws_error_handler_muted: false diff --git a/workflows/wmill-lock.yaml b/workflows/wmill-lock.yaml index b06152d..a5b92b0 100644 --- a/workflows/wmill-lock.yaml +++ b/workflows/wmill-lock.yaml @@ -5,6 +5,8 @@ locks: 'f/app_custom/system_heartbeat__flow+step2:_データ検証.py': d7f4e6e04ed116ba3836cb32793a0187a69359a3f2a807b533030b01d42bed39 'f/app_custom/system_heartbeat__flow+step3:_httpヘルスチェック.py': 5d3bce0ddb4f521444bf01bc80670e7321933ad09f935044f4d6123c658ca7a8 'f/app_custom/system_heartbeat__flow+step4:_年度判定_&_最終レポート.py': 6889bfac9a629fa42cf0505cbc945ba3782c59e1697b8493ce6101ef5ffa8b32 + f/shiraou/shiraou_notification__flow+__flow_hash: d8268404f9c59e04cbf16e9d05c90412e054b5e9e369e24b070234ca89708b0c + f/shiraou/shiraou_notification__flow+変更確認・line通知.py: abf0f2f17f9d4f85677db9f98a5a6368d4cb4a68a9611809afbee00528692b17 g/all/setup_app__app+__app_hash: d71add32e14e552d1a4c861c972a50d9598b07c0af201bbadec5b59bbd99d7e3 g/all/setup_app__app+change_account.deno.ts: 3c592cac27e9cdab0de6ae19270bcb08c7fa54355ad05253a12de2351894346b u/admin/hub_sync: aaf9fd803fa229f3029d1bb02bbe3cc422fce680cad39c4eec8dd1da115de102