cwadayi commited on
Commit
3387e96
·
verified ·
1 Parent(s): d422181

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +44 -50
app.py CHANGED
@@ -17,13 +17,12 @@ from datetime import datetime, timedelta
17
  # --- 環境變數 ---
18
  CHANNEL_ACCESS_TOKEN = os.getenv('CHANNEL_ACCESS_TOKEN')
19
  CHANNEL_SECRET = os.getenv('CHANNEL_SECRET')
20
- # Secrets 讀取我們設定的 Space 公開網址
21
- HF_SPACE_URL = os.getenv('HF_SPACE_URL')
22
 
23
  # --- Flask & LINE Bot 初始化 ---
24
  app = Flask(__name__)
25
 
26
- # 建立一個資料夾來存放靜態圖片檔
27
  if not os.path.exists('static'):
28
  os.makedirs('static')
29
 
@@ -33,20 +32,21 @@ handler = WebhookHandler(CHANNEL_SECRET)
33
  # --- 歡迎頁面 & 靜態圖片服務路由 ---
34
  @app.route("/", methods=['GET'])
35
  def home():
36
- # ... (歡迎頁面 HTML,與之前相同) ...
37
- return "<h1>✓ LINE Bot Server is Running</h1>"
 
 
 
 
38
 
39
  @app.route('/static/<path:filename>')
40
  def serve_static(filename):
41
- """這個路由讓外部可以存取 static 資料夾內的檔案"""
42
  return send_from_directory('static', filename)
43
 
44
  # --- 地震查詢核心邏輯 ---
45
  USGS_API_BASE_URL = "https://earthquake.usgs.gov/fdsnws/event/1/query"
46
 
47
  def fetch_earthquake_data_for_line():
48
- """查詢全球地震並回傳格式化的文字"""
49
- # ... (此函式與之前完全相同) ...
50
  now = datetime.now()
51
  yesterday = now - timedelta(days=1)
52
  params = {"format": "geojson","starttime": yesterday.strftime('%Y-%m-%d'),"endtime": now.strftime('%Y-%m-%d'),"minmagnitude": 5.0,"limit": 10,"orderby": "time"}
@@ -64,7 +64,6 @@ def fetch_earthquake_data_for_line():
64
  except Exception as e: return f"❌ 查詢失敗: {e}"
65
 
66
  def fetch_taiwan_earthquake_data_df():
67
- """查詢今年台灣地震並回傳 DataFrame 或錯誤訊息(str)"""
68
  now = datetime.now()
69
  start_of_year = now.replace(month=1, day=1).strftime('%Y-%m-%d')
70
  today_str = now.strftime('%Y-%m-%d')
@@ -84,33 +83,18 @@ def fetch_taiwan_earthquake_data_df():
84
  return pd.DataFrame(earthquake_list)
85
  except Exception as e: return f"❌ 查詢失敗: {e}"
86
 
87
- # --- ✨✨✨ 新增的繪圖函式 ✨✨✨ ---
88
  def create_and_save_map(df):
89
- """接收 DataFrame,繪製地圖,儲存圖片,並回傳檔名"""
90
  fig = px.scatter_mapbox(
91
- df,
92
- lat="latitude", lon="longitude",
93
- size="magnitude", color="magnitude",
94
- hover_name="place",
95
- hover_data={'time': '|%Y-%m-%d %H:%M', 'magnitude': ':.1f'},
96
- color_continuous_scale=px.colors.sequential.YlOrRd,
97
- size_max=30,
98
- mapbox_style="carto-darkmatter",
99
- center={"lat": 23.5, "lon": 121.0}, # 地圖中心點設在台灣
100
- zoom=6
101
  )
102
- fig.update_layout(
103
- title=f"<b>今年 ({datetime.now().year}) 台灣區域顯著地震 (M≥5.0)</b>",
104
- margin={"r": 0, "t": 40, "l": 0, "b": 0}
105
- )
106
-
107
- # 產生一個獨一無二的檔名,避免檔案衝突
108
  filename = f"map_{uuid.uuid4().hex}.png"
109
  filepath = os.path.join('static', filename)
110
-
111
- # 儲存圖片,kaleido 引擎會在這裡作用
112
- fig.write_image(filepath, scale=2) # scale=2 讓圖片更清晰
113
-
114
  return filename
115
 
116
  # --- Flask Webhook 路由 ---
@@ -124,7 +108,7 @@ def callback():
124
  abort(400)
125
  return 'OK'
126
 
127
- # --- LINE 訊息處理 (有修改) ---
128
  @handler.add(MessageEvent, message=TextMessageContent)
129
  def handle_message(event):
130
  user_message = event.message.text.strip().lower()
@@ -132,48 +116,58 @@ def handle_message(event):
132
  with ApiClient(configuration) as api_client:
133
  line_bot_api = MessagingApi(api_client)
134
 
135
- # --- ✨✨✨ 修改點:加入對「臺灣地震畫圖」的處理 ✨✨✨ ---
136
  if "臺灣地震畫圖" in user_message or "台灣地震畫圖" in user_message:
137
- # 1. 顯示「處理中」的提示,提升使用者體驗
138
- line_bot_api.reply_message(ReplyMessageRequest(
139
  reply_token=event.reply_token, messages=[TextMessage(text="🗺️ 收到指令!正在繪製地震分佈圖,請稍候...")]
140
  ))
141
-
142
- # 2. 獲取地震資料
143
  result = fetch_taiwan_earthquake_data_df()
144
-
145
- # 3. 判斷結果是 DataFrame 還是錯誤訊息
146
  if isinstance(result, pd.DataFrame):
147
- # 4. 如果是 DataFrame,繪製地圖並取得檔名
148
  filename = create_and_save_map(result)
149
- # 5. 組合圖片的公開 URL
150
  image_url = f"{HF_SPACE_URL}/static/{filename}"
151
-
152
- # 6. 這次不使用 reply_message,而是用 push_message 將圖片傳送給使用者
153
  line_bot_api.push_message(body={'to': event.source.user_id, 'messages': [
154
  ImageSendMessage(original_content_url=image_url, preview_image_url=image_url)
155
  ]})
156
  else:
157
- # 如果是錯誤訊息,一樣用 push_message 回傳
158
  line_bot_api.push_message(body={'to': event.source.user_id, 'messages': [TextMessage(text=result)]})
159
  return
160
 
161
- # --- 其他指令的處理邏輯 (與之前相同) ---
162
  reply_text = ""
163
  if user_message == "/help":
164
- reply_text = "📖 ... (幫助訊息) ..." # 為節省空間省略
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
165
  elif "臺灣地震" in user_message or "台灣地震" in user_message:
166
- # 為了避免與畫圖指令混淆,這裡可以只回傳文字
167
  result = fetch_taiwan_earthquake_data_df()
168
  if isinstance(result, pd.DataFrame):
169
- # ... (格式化 DataFrame 為文字) ...
170
- reply_text = "..."
 
 
 
 
 
 
171
  else:
172
  reply_text = result
173
  elif "地震" in user_message or "quake" in user_message:
174
  reply_text = fetch_earthquake_data_for_line()
175
  elif "你好" in user_message or "hi" in user_message:
176
- reply_text = "👋 ... (歡迎訊息) ..." # 為節省空間省略
177
  else:
178
  return
179
 
 
17
  # --- 環境變數 ---
18
  CHANNEL_ACCESS_TOKEN = os.getenv('CHANNEL_ACCESS_TOKEN')
19
  CHANNEL_SECRET = os.getenv('CHANNEL_SECRET')
20
+ # --- ✨✨✨ 修改點:讀取新的 Secret 名稱 ✨✨✨ ---
21
+ HF_SPACE_URL = os.getenv('SPACEURL')
22
 
23
  # --- Flask & LINE Bot 初始化 ---
24
  app = Flask(__name__)
25
 
 
26
  if not os.path.exists('static'):
27
  os.makedirs('static')
28
 
 
32
  # --- 歡迎頁面 & 靜態圖片服務路由 ---
33
  @app.route("/", methods=['GET'])
34
  def home():
35
+ return """
36
+ <html>
37
+ <head><title>LINE Bot Server</title><style>body{font-family: Arial, sans-serif; display: flex; justify-content: center; align-items: center; height: 100vh; background-color: #f0f2f5; margin: 0;} .container{text-align: center; padding: 40px; background-color: white; border-radius: 10px; box-shadow: 0 4px 8px rgba(0,0,0,0.1);} h1{color: #1dcd00;} p{color: #333; font-size: 1.2em;} .status{font-weight: bold; color: #28a745;}</style></head>
38
+ <body><div class="container"><h1>✓ LINE Bot Server is Running</h1><p>This is the backend service for the Earthquake Alert Bot.</p><p>The service is <span class="status">active</span> and listening for webhook events from LINE.</p></div></body>
39
+ </html>
40
+ """
41
 
42
  @app.route('/static/<path:filename>')
43
  def serve_static(filename):
 
44
  return send_from_directory('static', filename)
45
 
46
  # --- 地震查詢核心邏輯 ---
47
  USGS_API_BASE_URL = "https://earthquake.usgs.gov/fdsnws/event/1/query"
48
 
49
  def fetch_earthquake_data_for_line():
 
 
50
  now = datetime.now()
51
  yesterday = now - timedelta(days=1)
52
  params = {"format": "geojson","starttime": yesterday.strftime('%Y-%m-%d'),"endtime": now.strftime('%Y-%m-%d'),"minmagnitude": 5.0,"limit": 10,"orderby": "time"}
 
64
  except Exception as e: return f"❌ 查詢失敗: {e}"
65
 
66
  def fetch_taiwan_earthquake_data_df():
 
67
  now = datetime.now()
68
  start_of_year = now.replace(month=1, day=1).strftime('%Y-%m-%d')
69
  today_str = now.strftime('%Y-%m-%d')
 
83
  return pd.DataFrame(earthquake_list)
84
  except Exception as e: return f"❌ 查詢失敗: {e}"
85
 
86
+ # --- 繪圖函式 ---
87
  def create_and_save_map(df):
 
88
  fig = px.scatter_mapbox(
89
+ df, lat="latitude", lon="longitude", size="magnitude", color="magnitude",
90
+ hover_name="place", hover_data={'time': '|%Y-%m-%d %H:%M', 'magnitude': ':.1f'},
91
+ color_continuous_scale=px.colors.sequential.YlOrRd, size_max=30,
92
+ mapbox_style="carto-darkmatter", center={"lat": 23.5, "lon": 121.0}, zoom=6
 
 
 
 
 
 
93
  )
94
+ fig.update_layout(title=f"<b>今年 ({datetime.now().year}) 台灣區域顯著地震 (M≥5.0)</b>", margin={"r": 0, "t": 40, "l": 0, "b": 0})
 
 
 
 
 
95
  filename = f"map_{uuid.uuid4().hex}.png"
96
  filepath = os.path.join('static', filename)
97
+ fig.write_image(filepath, scale=2)
 
 
 
98
  return filename
99
 
100
  # --- Flask Webhook 路由 ---
 
108
  abort(400)
109
  return 'OK'
110
 
111
+ # --- LINE 訊息處理 ---
112
  @handler.add(MessageEvent, message=TextMessageContent)
113
  def handle_message(event):
114
  user_message = event.message.text.strip().lower()
 
116
  with ApiClient(configuration) as api_client:
117
  line_bot_api = MessagingApi(api_client)
118
 
 
119
  if "臺灣地震畫圖" in user_message or "台灣地震畫圖" in user_message:
120
+ line_bot_api.reply_message_with_http_info(ReplyMessageRequest(
 
121
  reply_token=event.reply_token, messages=[TextMessage(text="🗺️ 收到指令!正在繪製地震分佈圖,請稍候...")]
122
  ))
 
 
123
  result = fetch_taiwan_earthquake_data_df()
 
 
124
  if isinstance(result, pd.DataFrame):
 
125
  filename = create_and_save_map(result)
 
126
  image_url = f"{HF_SPACE_URL}/static/{filename}"
 
 
127
  line_bot_api.push_message(body={'to': event.source.user_id, 'messages': [
128
  ImageSendMessage(original_content_url=image_url, preview_image_url=image_url)
129
  ]})
130
  else:
 
131
  line_bot_api.push_message(body={'to': event.source.user_id, 'messages': [TextMessage(text=result)]})
132
  return
133
 
 
134
  reply_text = ""
135
  if user_message == "/help":
136
+ reply_text = """📖 地震預警dayichen 指令說明
137
+
138
+ 您可以傳送以下指令來與我互動:
139
+
140
+ ➡️ /help
141
+ 說明:顯示此幫助訊息,列出所有可用指令。
142
+
143
+ ➡️ 地震
144
+ 說明:查詢全球最近 24 小時內,芮氏規模 5.0 以上的顯著地震。
145
+
146
+ ➡️ 臺灣地震 (或 台灣地震)
147
+ 說明:查詢今年以來,在台灣區域 (緯度 21-26°, 經度 119-123°) 發生的芮氏規模 5.0 以上地震。
148
+
149
+ ➡️ 臺灣地震畫圖 (或 台灣地震畫圖)
150
+ 說明:將今年的台灣區域顯著地震繪製在地圖上並傳送圖片。
151
+
152
+ ➡️ 你好
153
+ 說明:顯示歡迎訊息。"""
154
  elif "臺灣地震" in user_message or "台灣地震" in user_message:
 
155
  result = fetch_taiwan_earthquake_data_df()
156
  if isinstance(result, pd.DataFrame):
157
+ count = len(result)
158
+ reply_text = f"🇹🇼 今年 ({datetime.now().year}年) 台灣區域顯著地震 (M≥5.0),共 {count} 筆:\n{'-'*20}\n"
159
+ for index, row in result.iterrows():
160
+ time_str = row['time'].strftime('%Y-%m-%d %H:%M')
161
+ reply_text += f"震級: {row['magnitude']:.1f} | 時間: {time_str} (UTC)\n地點: {row['place']}\n\n"
162
+ if len(reply_text) > 4500:
163
+ reply_text += "... (資料過多,僅顯示部分)"
164
+ break
165
  else:
166
  reply_text = result
167
  elif "地震" in user_message or "quake" in user_message:
168
  reply_text = fetch_earthquake_data_for_line()
169
  elif "你好" in user_message or "hi" in user_message:
170
+ reply_text = "👋 你好!我是地震查詢機器人。\n\n輸入 /help 查看所有指令。"
171
  else:
172
  return
173