cwadayi commited on
Commit
7549469
ยท
verified ยท
1 Parent(s): 2930140

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +145 -26
app.py CHANGED
@@ -1,5 +1,6 @@
 
1
  import os
2
- os.environ["MPLCONFIGDIR"] = "/tmp/matplotlib"
3
 
4
  import uuid
5
  import tempfile
@@ -19,7 +20,7 @@ from linebot.v3.webhooks import MessageEvent, TextMessageContent
19
  import requests
20
  import pandas as pd
21
 
22
- # Matplotlib headless + CJK
23
  import matplotlib
24
  matplotlib.use("Agg")
25
  import matplotlib.pyplot as plt
@@ -27,31 +28,138 @@ from matplotlib.colors import Normalize
27
  import matplotlib.cm as cm
28
  from matplotlib import font_manager as fm
29
 
30
- # โœ… ๅŒฏๅ…ฅไฝ ๅ‰›ๆ‹†ๅ‡บ็š„ๆจก็ต„
31
- from fetch_cwa_alarm_list import fetch_cwa_alarm_list
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
32
 
33
- # ่จญๅฎš CJK ๅญ—ๅž‹๏ผˆๅฆ‚ๆœ‰๏ผ‰
34
- possible_fonts = [
 
 
35
  "/usr/share/fonts/opentype/noto/NotoSansCJK-Regular.ttc",
36
  "/usr/share/fonts/truetype/noto/NotoSansCJK-Regular.ttc",
 
37
  "/usr/share/fonts/truetype/arphic/ukai.ttc",
38
- "/usr/share/fonts/truetype/wqy/wqy-microhei.ttc"
39
- ]
40
- for font_path in possible_fonts:
41
- if os.path.exists(font_path):
42
- font_prop = fm.FontProperties(fname=font_path)
43
- matplotlib.rcParams["font.family"] = font_prop.get_name()
44
  break
45
 
 
 
 
46
  CHANNEL_ACCESS_TOKEN = os.getenv("CHANNEL_ACCESS_TOKEN")
47
  CHANNEL_SECRET = os.getenv("CHANNEL_SECRET")
48
 
49
- # ่‡ชๅ‹•ๅตๆธฌ HF Space ๅ…ฌ็ถฒไฝๅ€
50
  HF_SPACE_URL = os.getenv("SPACEURL")
51
  if not HF_SPACE_URL:
52
- space_id = os.getenv("SPACE_ID") # username/space-name
53
- if space_id and "/" in space_id:
54
- a, n = space_id.split("/", 1)
55
  HF_SPACE_URL = f"https://{a.replace('_','-')}-{n.replace('_','-')}.hf.space"
56
  else:
57
  HF_SPACE_URL = ""
@@ -63,6 +171,9 @@ app = Flask(__name__)
63
  configuration = Configuration(access_token=CHANNEL_ACCESS_TOKEN)
64
  handler = WebhookHandler(CHANNEL_SECRET)
65
 
 
 
 
66
  @app.route("/", methods=["GET"])
67
  def home():
68
  return "LINE Bot Server is Running"
@@ -75,7 +186,6 @@ def healthz():
75
  def serve_static(filename):
76
  return send_from_directory(STATIC_DIR, filename)
77
 
78
- # ---------- USGS ----------
79
  USGS_API_BASE_URL = "https://earthquake.usgs.gov/fdsnws/event/1/query"
80
 
81
  def _iso(dt: datetime) -> str:
@@ -137,7 +247,6 @@ def fetch_taiwan_df_this_year(min_mag=5.0) -> pd.DataFrame | str:
137
  except Exception as e:
138
  return f"โŒ ๆŸฅ่ฉขๅคฑๆ•—: {e}"
139
 
140
- # ---------- ๅœ–ๅƒ ----------
141
  def create_and_save_map(df: pd.DataFrame) -> str:
142
  fig, ax = plt.subplots(figsize=(9, 6), dpi=150)
143
  ax.set_xlim(118.5, 123.5)
@@ -168,7 +277,9 @@ def create_and_save_map(df: pd.DataFrame) -> str:
168
  def _base_url_for_images() -> str:
169
  return HF_SPACE_URL.rstrip("/") if HF_SPACE_URL else request.url_root.rstrip("/")
170
 
171
- # ---------- LINE Webhook ----------
 
 
172
  @app.route("/callback", methods=["POST"])
173
  def callback():
174
  signature = request.headers.get("X-Line-Signature")
@@ -185,7 +296,7 @@ def handle_message(event):
185
  with ApiClient(configuration) as api_client:
186
  line_bot_api = MessagingApi(api_client)
187
 
188
- # ๅœฐ้œ‡้ ่ญฆ๏ผˆไฝฟ็”จๅค–้ƒจๆจก็ต„๏ผ‰
189
  if "ๅœฐ้œ‡้ ่ญฆ" in user_message:
190
  reply_text = fetch_cwa_alarm_list(limit=5)
191
  line_bot_api.reply_message_with_http_info(
@@ -193,6 +304,7 @@ def handle_message(event):
193
  )
194
  return
195
 
 
196
  if ("่‡บ็ฃๅœฐ้œ‡็•ซๅœ–" in user_message) or ("ๅฐ็ฃๅœฐ้œ‡็•ซๅœ–" in user_message):
197
  result = fetch_taiwan_df_this_year()
198
  if isinstance(result, pd.DataFrame):
@@ -210,14 +322,15 @@ def handle_message(event):
210
  line_bot_api.reply_message_with_http_info(reply)
211
  return
212
 
 
213
  if user_message == "/help":
214
  text = (
215
- "๐Ÿ“– ๅœฐ้œ‡้ ่ญฆ dayichen ๆŒ‡ไปค่ชชๆ˜Ž\n\n"
216
  "โžก๏ธ /help\n"
217
- "โžก๏ธ ๅœฐ้œ‡ / quake\n"
218
- "โžก๏ธ ่‡บ็ฃๅœฐ้œ‡ / ๅฐ็ฃๅœฐ้œ‡\n"
219
- "โžก๏ธ ่‡บ็ฃๅœฐ้œ‡็•ซๅœ– / ๅฐ็ฃๅœฐ้œ‡็•ซๅœ–\n"
220
- "โžก๏ธ ๅœฐ้œ‡้ ่ญฆ๏ผˆไธญๅคฎๆฐฃ่ฑก็ฝฒๆœ€ๆ–ฐ 5 ็ญ†๏ผ‰\n"
221
  "โžก๏ธ ไฝ ๅฅฝ"
222
  )
223
  line_bot_api.reply_message_with_http_info(
@@ -225,6 +338,7 @@ def handle_message(event):
225
  )
226
  return
227
 
 
228
  if ("่‡บ็ฃๅœฐ้œ‡" in user_message) or ("ๅฐ็ฃๅœฐ้œ‡" in user_message):
229
  result = fetch_taiwan_df_this_year()
230
  if isinstance(result, pd.DataFrame):
@@ -243,6 +357,7 @@ def handle_message(event):
243
  )
244
  return
245
 
 
246
  if ("ๅœฐ้œ‡" in user_message) or ("quake" in user_message):
247
  reply_text = fetch_global_last24h_text()
248
  line_bot_api.reply_message_with_http_info(
@@ -250,8 +365,12 @@ def handle_message(event):
250
  )
251
  return
252
 
 
253
  if ("ไฝ ๅฅฝ" in user_message) or ("hi" in user_message):
254
  line_bot_api.reply_message_with_http_info(
255
- ReplyMessageRequest(reply_token=event.reply_token, messages=[TextMessage(text="๐Ÿ‘‹ ไฝ ๅฅฝ๏ผ่ผธๅ…ฅ /help ๆŸฅ็œ‹ๆŒ‡ไปคใ€‚")])
 
 
 
256
  )
257
  return
 
1
+ # app.py
2
  import os
3
+ os.environ["MPLCONFIGDIR"] = "/tmp/matplotlib" # ้ฟๅ…ๅ”ฏ่ฎ€็›ฎ้Œ„่ญฆๅ‘Š
4
 
5
  import uuid
6
  import tempfile
 
20
  import requests
21
  import pandas as pd
22
 
23
+ # Matplotlib๏ผˆ็„ก้ ญ๏ผ‰
24
  import matplotlib
25
  matplotlib.use("Agg")
26
  import matplotlib.pyplot as plt
 
28
  import matplotlib.cm as cm
29
  from matplotlib import font_manager as fm
30
 
31
+ # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
32
+ # 1) ๅ˜—่ฉฆ่ผ‰ๅ…ฅใ€Œๅœฐ้œ‡้ ่ญฆใ€ๅค–้ƒจๆจก็ต„๏ผ›่‹ฅไธๅญ˜ๅœจๅฐฑ็”จๅ…งๅปบๅ‚™ๆด็‰ˆๆœฌ
33
+ # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
34
+ try:
35
+ from fetch_cwa_alarm_list import fetch_cwa_alarm_list # ไฝ ่‹ฅๆ”พไบ†ๅŒๅๆช”ๆกˆ๏ผŒๆœƒ็”จ้‚ฃๅ€‹
36
+ except Exception:
37
+ # ๅ…งๅปบๅ‚™ๆด๏ผš็›ดๆŽฅๅ‘ผๅซ CWA API ไธฆๆ ผๅผๅŒ–่ผธๅ‡บ
38
+ CWA_ALARM_API = "https://app-2.cwa.gov.tw/api/v1/earthquake/alarm/list"
39
+
40
+ def _parse_cwa_time(s: str):
41
+ """ๅ›žๅ‚ณ(ๅฐ็ฃๆ™‚้–“, UTC)ใ€‚่‹ฅ็„กๆ™‚ๅ€๏ผŒ่ฆ–็‚บๅฐ็ฃๆ™‚้–“(UTC+8)ใ€‚"""
42
+ if not s:
43
+ return ("ๆœช็Ÿฅ", "ๆœช็Ÿฅ")
44
+ try:
45
+ if "T" in s or s.endswith("Z") or "+" in s:
46
+ dt = datetime.fromisoformat(s.replace("Z", "+00:00"))
47
+ else:
48
+ dt = datetime.strptime(s, "%Y-%m-%d %H:%M:%S")
49
+ dt = dt.replace(tzinfo=timezone(timedelta(hours=8)))
50
+ tw = dt.astimezone(timezone(timedelta(hours=8))).strftime("%Y-%m-%d %H:%M")
51
+ utc = dt.astimezone(timezone.utc).strftime("%Y-%m-%d %H:%M")
52
+ return (tw, utc)
53
+ except Exception:
54
+ return (s, "ๆœช็Ÿฅ")
55
+
56
+ def fetch_cwa_alarm_list(limit: int = 5) -> str:
57
+ try:
58
+ r = requests.get(CWA_ALARM_API, timeout=10)
59
+ r.raise_for_status()
60
+ payload = r.json()
61
+ except Exception as e:
62
+ return f"โŒ ๅœฐ้œ‡้ ่ญฆๆŸฅ่ฉขๅคฑๆ•—๏ผš{e}"
63
+
64
+ items = None
65
+ if isinstance(payload, dict):
66
+ items = payload.get("data") or payload.get("records") or payload.get("list") or payload.get("items")
67
+ if items is None and isinstance(payload, list):
68
+ items = payload
69
+ if not items:
70
+ return "โœ… ็›ฎๅ‰ๆฒ’ๆœ‰ๅœฐ้œ‡้ ่ญฆใ€‚"
71
+
72
+ # ไพ originTime ๆ–ฐโ†’่ˆŠ
73
+ def _key(it):
74
+ s = it.get("originTime") or ""
75
+ try:
76
+ if "T" in s or s.endswith("Z") or "+" in s:
77
+ dt = datetime.fromisoformat(s.replace("Z", "+00:00"))
78
+ else:
79
+ dt = datetime.strptime(s, "%Y-%m-%d %H:%M:%S").replace(
80
+ tzinfo=timezone(timedelta(hours=8))
81
+ )
82
+ return dt.astimezone(timezone.utc)
83
+ except Exception:
84
+ return datetime.min.replace(tzinfo=timezone.utc)
85
+
86
+ try:
87
+ items = sorted(items, key=_key, reverse=True)
88
+ except Exception:
89
+ pass
90
+
91
+ def _num(x):
92
+ xs = str(x)
93
+ ok = xs.replace(".", "", 1).replace("-", "", 1).isdigit()
94
+ return float(xs) if ok else None
95
+
96
+ lines = ["๐Ÿšจ ๅœฐ้œ‡้ ่ญฆ๏ผˆๆœ€ๆ–ฐ๏ผ‰:", "-" * 20]
97
+ for it in items[:limit]:
98
+ identifier = it.get("identifier") or it.get("eventId") or it.get("id") or "โ€”"
99
+ status = it.get("status") or "โ€”"
100
+ msg_type = it.get("msgType") or "โ€”"
101
+ msg_no = it.get("msgNo") or it.get("msgSeq") or "โ€”"
102
+
103
+ mag = _num(it.get("magnitudeValue") or it.get("magnitude") or it.get("ml") or it.get("mw"))
104
+ mag_str = f"{mag:.1f}" if mag is not None else "โ€”"
105
+
106
+ depth = _num(it.get("depth"))
107
+ depth_str = f"{depth:.0f}" if depth is not None else "โ€”"
108
+
109
+ lat = _num(it.get("epicenterLat") or it.get("latitude") or it.get("lat"))
110
+ lon = _num(it.get("epicenterLon") or it.get("longitude") or it.get("lon"))
111
+ lat_str = f"{lat:.2f}" if lat is not None else "โ€”"
112
+ lon_str = f"{lon:.2f}" if lon is not None else "โ€”"
113
+
114
+ origin = it.get("originTime") or ""
115
+ tw_str, utc_str = _parse_cwa_time(origin)
116
+
117
+ areas = it.get("locationDesc") or it.get("areas") or it.get("alertAreas")
118
+ if isinstance(areas, list):
119
+ areas_txt = "ใ€".join(str(a) for a in areas if a)
120
+ elif isinstance(areas, str):
121
+ areas_txt = areas
122
+ else:
123
+ areas_txt = "๏ฟฝ๏ฟฝ๏ฟฝ"
124
+
125
+ lines.append(
126
+ f"ไบ‹ไปถ: {identifier} | ็‹€ๆ…‹: {status} | ้กžๅž‹: {msg_type}#{msg_no}\n"
127
+ f"้œ‡็ดš/ๆทฑๅบฆ: M{mag_str} / {depth_str} km\n"
128
+ f"้œ‡ไธญ: lat {lat_str}, lon {lon_str}\n"
129
+ f"ๆ™‚้–“: {tw_str}๏ผˆๅฐ็ฃ๏ผ‰ / {utc_str}๏ผˆUTC๏ผ‰\n"
130
+ f"้ ่ญฆๅœฐๅ€: {areas_txt}"
131
+ )
132
+ lines.append("")
133
+
134
+ if len(items) > limit:
135
+ lines.append(f"... ๅฆๆœ‰ {len(items) - limit} ็ญ†ใ€‚")
136
+ return "\n".join(lines).strip()
137
 
138
+ # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
139
+ # 2) ไธญๆ–‡ๅญ—ๅž‹๏ผˆ่‹ฅๅฎนๅ™จๆœ‰ Noto/WenQuanYi ไน‹้กžๆœƒ่‡ชๅ‹•ๅฅ—็”จ๏ผ‰
140
+ # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
141
+ for fp in [
142
  "/usr/share/fonts/opentype/noto/NotoSansCJK-Regular.ttc",
143
  "/usr/share/fonts/truetype/noto/NotoSansCJK-Regular.ttc",
144
+ "/usr/share/fonts/truetype/wqy/wqy-microhei.ttc",
145
  "/usr/share/fonts/truetype/arphic/ukai.ttc",
146
+ ]:
147
+ if os.path.exists(fp):
148
+ matplotlib.rcParams["font.family"] = fm.FontProperties(fname=fp).get_name()
 
 
 
149
  break
150
 
151
+ # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
152
+ # 3) ๅŸบๆœฌ่จญๅฎš
153
+ # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
154
  CHANNEL_ACCESS_TOKEN = os.getenv("CHANNEL_ACCESS_TOKEN")
155
  CHANNEL_SECRET = os.getenv("CHANNEL_SECRET")
156
 
157
+ # ๅ…ฌ็ถฒๅ€่‡ชๅ‹•ๅตๆธฌ๏ผˆHF Spaces๏ผ‰
158
  HF_SPACE_URL = os.getenv("SPACEURL")
159
  if not HF_SPACE_URL:
160
+ sid = os.getenv("SPACE_ID") # ๅฝขๅฆ‚ user/space
161
+ if sid and "/" in sid:
162
+ a, n = sid.split("/", 1)
163
  HF_SPACE_URL = f"https://{a.replace('_','-')}-{n.replace('_','-')}.hf.space"
164
  else:
165
  HF_SPACE_URL = ""
 
171
  configuration = Configuration(access_token=CHANNEL_ACCESS_TOKEN)
172
  handler = WebhookHandler(CHANNEL_SECRET)
173
 
174
+ # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
175
+ # 4) ๅทฅๅ…ทๅ‡ฝๅผ & ่ทฏ็”ฑ
176
+ # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
177
  @app.route("/", methods=["GET"])
178
  def home():
179
  return "LINE Bot Server is Running"
 
186
  def serve_static(filename):
187
  return send_from_directory(STATIC_DIR, filename)
188
 
 
189
  USGS_API_BASE_URL = "https://earthquake.usgs.gov/fdsnws/event/1/query"
190
 
191
  def _iso(dt: datetime) -> str:
 
247
  except Exception as e:
248
  return f"โŒ ๆŸฅ่ฉขๅคฑๆ•—: {e}"
249
 
 
250
  def create_and_save_map(df: pd.DataFrame) -> str:
251
  fig, ax = plt.subplots(figsize=(9, 6), dpi=150)
252
  ax.set_xlim(118.5, 123.5)
 
277
  def _base_url_for_images() -> str:
278
  return HF_SPACE_URL.rstrip("/") if HF_SPACE_URL else request.url_root.rstrip("/")
279
 
280
+ # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
281
+ # 5) LINE Webhook
282
+ # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
283
  @app.route("/callback", methods=["POST"])
284
  def callback():
285
  signature = request.headers.get("X-Line-Signature")
 
296
  with ApiClient(configuration) as api_client:
297
  line_bot_api = MessagingApi(api_client)
298
 
299
+ # ๅœฐ้œ‡้ ่ญฆ๏ผˆCWA๏ผ‰
300
  if "ๅœฐ้œ‡้ ่ญฆ" in user_message:
301
  reply_text = fetch_cwa_alarm_list(limit=5)
302
  line_bot_api.reply_message_with_http_info(
 
304
  )
305
  return
306
 
307
+ # ่‡บ็ฃๅœฐ้œ‡็•ซๅœ–
308
  if ("่‡บ็ฃๅœฐ้œ‡็•ซๅœ–" in user_message) or ("ๅฐ็ฃๅœฐ้œ‡็•ซๅœ–" in user_message):
309
  result = fetch_taiwan_df_this_year()
310
  if isinstance(result, pd.DataFrame):
 
322
  line_bot_api.reply_message_with_http_info(reply)
323
  return
324
 
325
+ # ่ชชๆ˜Ž
326
  if user_message == "/help":
327
  text = (
328
+ "๐Ÿ“– ๅœฐ้œ‡้ ่ญฆ dayichen ๆŒ‡ไปค\n\n"
329
  "โžก๏ธ /help\n"
330
+ "โžก๏ธ ๅœฐ้œ‡ / quake๏ผˆๅ…จ็ƒ่ฟ‘24ๅฐๆ™‚๏ผŒๅซๆ—ฅๆœŸๆ™‚้–“๏ผ‰\n"
331
+ "โžก๏ธ ่‡บ็ฃๅœฐ้œ‡ / ๅฐ็ฃๅœฐ้œ‡๏ผˆไปŠๅนดๅฐ็ฃๅ€ๅŸŸๆธ…ๅ–ฎ๏ผ‰\n"
332
+ "โžก๏ธ ่‡บ็ฃๅœฐ้œ‡็•ซๅœ– / ๅฐ็ฃๅœฐ้œ‡็•ซๅœ–๏ผˆไปŠๅนดๅฐ็ฃๅ€ๅŸŸๅˆ†ไฝˆๅœ–๏ผ‰\n"
333
+ "โžก๏ธ ๅœฐ้œ‡้ ่ญฆ๏ผˆCWAๆœ€ๆ–ฐ5็ญ†๏ผ‰\n"
334
  "โžก๏ธ ไฝ ๅฅฝ"
335
  )
336
  line_bot_api.reply_message_with_http_info(
 
338
  )
339
  return
340
 
341
+ # ๅฐ็ฃๆธ…ๅ–ฎ
342
  if ("่‡บ็ฃๅœฐ้œ‡" in user_message) or ("ๅฐ็ฃๅœฐ้œ‡" in user_message):
343
  result = fetch_taiwan_df_this_year()
344
  if isinstance(result, pd.DataFrame):
 
357
  )
358
  return
359
 
360
+ # ๅ…จ็ƒ 24 ๅฐๆ™‚
361
  if ("ๅœฐ้œ‡" in user_message) or ("quake" in user_message):
362
  reply_text = fetch_global_last24h_text()
363
  line_bot_api.reply_message_with_http_info(
 
365
  )
366
  return
367
 
368
+ # ๆ‹›ๅ‘ผ
369
  if ("ไฝ ๅฅฝ" in user_message) or ("hi" in user_message):
370
  line_bot_api.reply_message_with_http_info(
371
+ ReplyMessageRequest(
372
+ reply_token=event.reply_token,
373
+ messages=[TextMessage(text="๐Ÿ‘‹ ไฝ ๅฅฝ๏ผ่ผธๅ…ฅ /help ๆŸฅ็œ‹ๆŒ‡ไปคใ€‚")]
374
+ )
375
  )
376
  return