cwadayi commited on
Commit
52833f4
·
verified ·
1 Parent(s): d5d1d70

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +91 -83
app.py CHANGED
@@ -1,96 +1,104 @@
1
- from fastapi import FastAPI, HTTPException, Query
2
- from starlette.responses import JSONResponse
3
- from obspy.clients.fdsn import Client
4
- from obspy.core.utcdatetime import UTCDateTime
5
- import logging
6
- from datetime import date
 
 
 
7
 
8
- # Configure logging
9
- logging.basicConfig(level=logging.INFO)
 
10
 
11
- # Initialize the FastAPI app
12
- app = FastAPI(
13
- title="Dynamic Earthquake Catalog API",
14
- description="A robust API to fetch earthquake data from the IRIS FDSN web service using URL parameters.",
15
- version="2.1.0",
16
- )
17
 
18
- # Initialize the IRIS FDSN client
19
- try:
20
- client = Client("IRIS")
21
- logging.info("Successfully initialized IRIS FDSN client.")
22
- except Exception as e:
23
- client = None
24
- logging.error(f"Could not initialize IRIS FDSN client on startup: {e}")
25
 
26
- @app.get("/")
27
- def greet_json():
28
- """
29
- A simple 'Hello World' endpoint.
30
- """
31
- return {"Hello": "World!", "message": "Go to /docs to try the dynamic earthquake endpoint. cwadayi \n https://cwadayi-python-app.hf.space/earthquakes?start_date=2024-07-01&end_date=2024-07-07&min_magnitude=5.0"}
32
 
33
- @app.get("/earthquakes")
34
- async def get_earthquake_catalog(
35
- start_date: date = Query(default="2024-01-01", description="Start date in YYYY-MM-DD format"),
36
- end_date: date = Query(default="2024-01-07", description="End date in YYYY-MM-DD format"),
37
- min_magnitude: float = Query(default=4.5, description="Minimum earthquake magnitude (e.g., 5.5)", gt=0, le=10)
38
- ):
39
- """
40
- Fetches earthquake data based on date and magnitude parameters provided in the URL.
41
- """
42
- if not client:
43
- raise HTTPException(status_code=503, detail="The IRIS FDSN client is not available.")
44
-
45
- if start_date > end_date:
46
- raise HTTPException(status_code=400, detail="The start_date cannot be after the end_date.")
47
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
48
  try:
49
- starttime = UTCDateTime(start_date)
50
- endtime = UTCDateTime(end_date)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
51
 
52
- logging.info(f"Fetching events from {starttime} to {endtime} with min magnitude {min_magnitude}.")
53
- catalog = client.get_events(
54
- starttime=starttime,
55
- endtime=endtime,
56
- minmagnitude=min_magnitude,
57
- )
58
- logging.info(f"Found {len(catalog)} events. Now processing.")
59
 
60
- earthquake_list = []
61
- for event in catalog:
62
- try:
63
- origin = event.preferred_origin()
64
- magnitude = event.preferred_magnitude()
 
65
 
66
- if not origin or not magnitude:
67
- continue
 
 
 
 
68
 
69
- # --- Key Change Start ---
70
- # Safely get the description using getattr. This avoids errors if the 'descriptions'
71
- # attribute doesn't exist, by providing a default empty list [].
72
- place = "N/A"
73
- descriptions = getattr(event, 'descriptions', [])
74
- if descriptions:
75
- place = descriptions[0].text
76
- # --- Key Change End ---
77
 
78
- earthquake_list.append({
79
- "time": origin.time.isoformat(),
80
- "latitude": origin.latitude,
81
- "longitude": origin.longitude,
82
- "depth_km": origin.depth / 1000.0 if origin.depth is not None else None,
83
- "magnitude": magnitude.mag,
84
- "magnitude_type": magnitude.magnitude_type,
85
- "place": place,
86
- })
87
- except Exception as e:
88
- logging.warning(f"Skipping event due to unexpected processing error: {e}")
89
- continue
90
-
91
- logging.info(f"Successfully processed {len(earthquake_list)} events.")
92
- return JSONResponse(content={"earthquakes": earthquake_list})
93
 
94
- except Exception as e:
95
- logging.error(f"A major error occurred during the request: {e}")
96
- raise HTTPException(status_code=500, detail=f"An internal error occurred: {e}")
 
 
 
 
 
 
 
 
1
+ import os
2
+ from flask import Flask, request, abort
3
+ from linebot.v3 import WebhookHandler
4
+ from linebot.v3.exceptions import InvalidSignatureError
5
+ from linebot.v3.messaging import (
6
+ Configuration, ApiClient, MessagingApi,
7
+ ReplyMessageRequest, TextMessage
8
+ )
9
+ from linebot.v3.webhooks import MessageEvent, TextMessageContent
10
 
11
+ import requests
12
+ import pandas as pd
13
+ from datetime import datetime, timedelta
14
 
15
+ # --- 環境變數:我們將在 Hugging Face Spaces 的 Secrets 中設定 ---
16
+ CHANNEL_ACCESS_TOKEN = os.getenv('CHANNEL_ACCESS_TOKEN')
17
+ CHANNEL_SECRET = os.getenv('CHANNEL_SECRET')
 
 
 
18
 
19
+ # --- Flask & LINE Bot 初始化 ---
20
+ app = Flask(__name__)
 
 
 
 
 
21
 
22
+ configuration = Configuration(access_token=CHANNEL_ACCESS_TOKEN)
23
+ handler = WebhookHandler(CHANNEL_SECRET)
 
 
 
 
24
 
25
+ # --- 地震查詢核心邏輯 (與之前相同) ---
26
+ USGS_API_BASE_URL = "https://earthquake.usgs.gov/fdsnws/event/1/query"
 
 
 
 
 
 
 
 
 
 
 
 
27
 
28
+ def fetch_earthquake_data_for_line():
29
+ """查詢過去 24 小時內規模 5.0 以上的地震,並回傳格式化的文字。"""
30
+ now = datetime.now()
31
+ yesterday = now - timedelta(days=1)
32
+
33
+ params = {
34
+ "format": "geojson",
35
+ "starttime": yesterday.strftime('%Y-%m-%d'),
36
+ "endtime": now.strftime('%Y-%m-%d'),
37
+ "minmagnitude": 5.0,
38
+ "limit": 10,
39
+ "orderby": "time-desc"
40
+ }
41
+
42
  try:
43
+ response = requests.get(USGS_API_BASE_URL, params=params, timeout=15)
44
+ response.raise_for_status()
45
+ data = response.json()
46
+
47
+ features = data.get('features', [])
48
+ if not features:
49
+ return "✅ 過去24小時內,全球無規模 5.0 以上的顯著地震。"
50
+
51
+ reply_text = f"🚨 近 24 小時全球顯著地震 (M≥5.0):\n{'-'*20}\n"
52
+
53
+ for feature in features:
54
+ prop = feature['properties']
55
+ mag = prop['mag']
56
+ place = prop['place']
57
+ event_time = datetime.fromtimestamp(prop['time'] / 1000).strftime('%H:%M')
58
+
59
+ reply_text += f"震級: {mag:.1f} | 時間: {event_time} (UTC)\n地點: {place}\n\n"
60
+
61
+ return reply_text.strip()
62
 
63
+ except requests.exceptions.RequestException as e:
64
+ return f"❌ 查詢失敗,無法連接到 USGS 伺服器: {e}"
65
+ except Exception as e:
66
+ return f"❌ 處理資料時發生未知錯誤: {e}"
 
 
 
67
 
68
+ # --- Flask Webhook 路由 ---
69
+ @app.route("/callback", methods=['POST'])
70
+ def callback():
71
+ signature = request.headers['X-Line-Signature']
72
+ body = request.get_data(as_text=True)
73
+ app.logger.info("Request body: " + body)
74
 
75
+ try:
76
+ handler.handle(body, signature)
77
+ except InvalidSignatureError:
78
+ app.logger.info("Invalid signature. Please check your channel secret.")
79
+ abort(400)
80
+ return 'OK'
81
 
82
+ # --- LINE 訊息處理 ---
83
+ @handler.add(MessageEvent, message=TextMessageContent)
84
+ def handle_message(event):
85
+ user_message = event.message.text
86
+ reply_text = ""
 
 
 
87
 
88
+ if "地震" in user_message or "quake" in user_message.lower():
89
+ reply_text = fetch_earthquake_data_for_line()
90
+ elif "你好" in user_message or "hi" in user_message.lower():
91
+ reply_text = "👋 你好!我是地震查詢機器人,試著傳送「地震」來查詢最新資訊吧!"
92
+ else:
93
+ return
 
 
 
 
 
 
 
 
 
94
 
95
+ with ApiClient(configuration) as api_client:
96
+ line_bot_api = MessagingApi(api_client)
97
+ line_bot_api.reply_message_with_http_info(
98
+ ReplyMessageRequest(
99
+ reply_token=event.reply_token,
100
+ messages=[TextMessage(text=reply_text)]
101
+ )
102
+ )
103
+
104
+ # 注意:我們已經刪除了 if __name__ == "__main__": app.run() 區塊