import time import requests import os import json from datetime import datetime, timedelta from zoneinfo import ZoneInfo BASE_URL = "https://desk-api.channel.io/desk/channels/200605" TARGET_GROUP_CHAT_ID = "536194" HEADERS = { "accept-language": "ja", "x-account": os.getenv("dmsendertoken") } JST = ZoneInfo("Asia/Tokyo") # ---------------- 天気コード ---------------- { 0: "雲の発達なしまたは観測なし", 1: "雲が消散または弱まる", 2: "変化なし", 3: "雲が発生または発達", 4: "煙による視程低下", 5: "煙霧", 6: "広範囲の砂塵", 7: "風で砂や塵が舞う", 8: "砂塵旋風", 9: "砂嵐または塵嵐", 10: "霧", 11: "🌫️(斑状)", 12: "🌫️(連続)", 13: "⚡", 14: "⛆", 15: "⛆", 16: "⛆", 17: "⛈(降水なし)", 18: "スコール", 19: "漏斗雲", 20: "霧雨または氷晶", 21: "⛆", 22: "❄", 23: "雨と雪または氷あられ", 24: "凍る雨または霧雨", 25: "にわか雨", 26: "にわか雪または雨雪", 27: "にわか雹", 28: "🌫️", 29: "⛈", 30: "弱い砂嵐(減少)", 31: "弱い砂嵐(変化なし)", 32: "弱い砂嵐(増加)", 33: "激しい砂嵐(減少)", 34: "激しい砂嵐(変化なし)", 35: "激しい砂嵐(増加)", 36: "弱い地吹雪(低)", 37: "激しい地吹雪(低)", 38: "弱い地吹雪(高)", 39: "激しい地吹雪(高)", 40: "遠方の霧", 41: "斑状の霧", 42: "霧(薄くなった・空見える)", 43: "霧(薄くなった・空見えない)", 44: "霧(変化なし・空見える)", 45: "霧(変化なし・空見えない)", 46: "霧(濃くなった・空見える)", 47: "霧(濃くなった・空見えない)", 48: "着氷性の霧(空見える)", 49: "着氷性の霧(空見えない)", 50: "弱い霧雨(断続)", 51: "弱い霧雨(連続)", 52: "中程度の霧雨(断続)", 53: "中程度の霧雨(連続)", 54: "強い霧雨(断続)", 55: "強い霧雨(連続)", 56: "凍る霧雨(弱い)", 57: "凍る霧雨(強い)", 58: "霧雨と雨(弱い)", 59: "霧雨と雨(強い)", 60: "弱い雨(断続)", 61: "弱い雨(連続)", 62: "中程度の雨(断続)", 63: "中程度の雨(連続)", 64: "強い雨(断続)", 65: "強い雨(連続)", 66: "凍る雨(弱い)", 67: "凍る雨(強い)", 68: "雨または霧雨と雪(弱い)", 69: "雨または霧雨と雪(強い)", 70: "弱い雪(断続)", 71: "弱い雪(連続)", 72: "中程度の雪(断続)", 73: "中程度の雪(連続)", 74: "強い雪(断続)", 75: "強い雪(連続)", 76: "ダイヤモンドダスト", 77: "氷晶", 78: "星形雪結晶", 79: "氷あられ", 80: "弱いにわか雨", 81: "強いにわか雨", 82: "猛烈なにわか雨", 83: "弱い雨雪にわか", 84: "強い雨雪にわか", 85: "弱いにわか雪", 86: "強いにわか雪", 87: "弱い氷あられにわか", 88: "強い氷あられにわか", 89: "弱い雹にわか", 90: "強い雹にわか", 91: "弱い雨+直前雷雨", 92: "強い雨+直前雷雨", 93: "弱い雪等+直前雷雨", 94: "強い雪等+直前雷雨", 95: "雷雨(雹なし)", 96: "雷雨(雹あり)", 97: "激しい雷雨(雹なし)", 98: "雷雨+砂嵐", 99: "激しい雷雨(雹あり)" } def format_weather_code(code: int) -> str: if code < 10: return WEATHER_CODE_MAP.get(code, str(code)) main = code // 10 sub = code % 10 main_str = WEATHER_CODE_MAP.get(main) sub_str = WEATHER_CODE_MAP.get(sub) if main_str and sub_str: return f"{main_str}、{sub_str}" return str(code) # ---------------- Channel 投稿 ---------------- def post_group_message(chat_id, body): url = f"{BASE_URL}/groups/{chat_id}/messages" r = requests.post(url, headers=HEADERS, json=body) r.raise_for_status() return r.json() # ---------------- 時報 ---------------- def create_time_signal_body(): now = datetime.now(JST) hour = now.hour if hour < 12: period = "午前" display_hour = hour if hour != 0 else 12 else: period = "午後" display_hour = hour - 12 if hour > 12 else 12 message = f"時報\n{period}{display_hour}時をお知らせします。" return { "requestId": f"desk-web-time-signal-{int(time.time())}", "blocks": [{"type": "text", "value": message}], } # ---------------- 天気予報 ---------------- def load_todoufuken(): with open("todoufuken.json", encoding="utf-8") as f: lines = [] for line in f: stripped = line.lstrip() if stripped.startswith("//"): continue lines.append(line) json_str = "".join(lines) return json.loads(json_str) def fetch_weather(todoufuken): lats = ",".join(str(p["lat"]) for p in todoufuken) lons = ",".join(str(p["lon"]) for p in todoufuken) url = ( "https://api.open-meteo.com/v1/forecast" f"?latitude={lats}" f"&longitude={lons}" "&hourly=relative_humidity_2m,apparent_temperature,temperature_2m," "precipitation_probability,weather_code" "&forecast_days=1" "&timezone=Asia%2FTokyo" ) r = requests.get(url) r.raise_for_status() return r.json() def create_weather_body(): now = datetime.now(JST) today_7am = now.replace(hour=7, minute=0, second=0, microsecond=0) todoufuken = load_todoufuken() data = fetch_weather(todoufuken) lines = ["☀️ 本日の天気予報(7時以降・1時間ごと)"] for idx, pref in enumerate(todoufuken): hourly = data[idx]["hourly"] times = hourly["time"] lines.append(f"\n{pref['name']}:") TARGET_HOURS = {7, 10, 13, 16, 21} for i, t in enumerate(times): t_jst = ( datetime.fromisoformat(t) .replace(tzinfo=ZoneInfo("UTC")) .astimezone(JST) ) if t_jst.hour not in TARGET_HOURS: continue wc = hourly["weather_code"][i] at = hourly["apparent_temperature"][i] temp = hourly["temperature_2m"][i] pop = hourly["precipitation_probability"][i] rh = hourly["relative_humidity_2m"][i] wc_str = format_weather_code(wc) line = ( f" {t_jst.strftime('%H時')}:" f"{wc_str} " f"{at}℃ {temp}℃ {pop}% {rh}%" ) lines.append(line) message2 = "☀️・・・晴れ、☁️・・・曇り、🌫️・・・霧・霧雨、🌧️・・・雨、🌨️・・・雪、🌦️・・・にわか雨、⛈️・・・雷雨" return { "requestId": f"desk-web-weather-{int(time.time())}", "blocks": [{"type": "text", "value": "\n".join(lines)},{"type": "text", "value": message2}], } # ---------------- メインループ ---------------- def main_loop(): while True: try: now = datetime.now(JST) # 毎正時の時報 if now.minute == 0: body = create_time_signal_body() post_group_message(TARGET_GROUP_CHAT_ID, body) # 午後7時20分に天気予報 if now.hour == 21 and now.minute == 35: body = create_weather_body() post_group_message(TARGET_GROUP_CHAT_ID, body) time.sleep(60) except Exception as e: print("Error:", e) time.sleep(10) if __name__ == "__main__": main_loop()