Spaces:
Sleeping
Sleeping
| import os | |
| from flask import Flask, request, abort | |
| from linebot.v3 import WebhookHandler | |
| from linebot.v3.exceptions import InvalidSignatureError | |
| from linebot.v3.messaging import ( | |
| Configuration, ApiClient, MessagingApi, | |
| ReplyMessageRequest, TextMessage | |
| ) | |
| from linebot.v3.webhooks import MessageEvent, TextMessageContent | |
| import requests | |
| import pandas as pd | |
| from datetime import datetime, timedelta | |
| # --- 環境變數 --- | |
| CHANNEL_ACCESS_TOKEN = os.getenv('CHANNEL_ACCESS_TOKEN') | |
| CHANNEL_SECRET = os.getenv('CHANNEL_SECRET') | |
| # --- Flask & LINE Bot 初始化 --- | |
| app = Flask(__name__) | |
| configuration = Configuration(access_token=CHANNEL_ACCESS_TOKEN) | |
| handler = WebhookHandler(CHANNEL_SECRET) | |
| # --- ✨✨✨ 新增的歡迎頁面路由 ✨✨✨ --- | |
| def home(): | |
| # 當有人用瀏覽器訪問根網址時,回傳這段 HTML 訊息 | |
| return """ | |
| <html> | |
| <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> | |
| <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> | |
| </html> | |
| """ | |
| # --- 地震查詢核心邏輯 (與之前相同) --- | |
| USGS_API_BASE_URL = "https://earthquake.usgs.gov/fdsnws/event/1/query" | |
| def fetch_earthquake_data_for_line(): | |
| """查詢過去 24 小時內規模 5.0 以上的地震,並回傳格式化的文字。""" | |
| now = datetime.now() | |
| yesterday = now - timedelta(days=1) | |
| params = { | |
| "format": "geojson", | |
| "starttime": yesterday.strftime('%Y-%m-%d'), | |
| "endtime": now.strftime('%Y-%m-%d'), | |
| "minmagnitude": 5.0, | |
| "limit": 10, | |
| "orderby": "time" | |
| } | |
| try: | |
| response = requests.get(USGS_API_BASE_URL, params=params, timeout=15) | |
| response.raise_for_status() | |
| data = response.json() | |
| features = data.get('features', []) | |
| if not features: | |
| return "✅ 過去24小時內,全球無規模 5.0 以上的顯著地震。" | |
| reply_text = f"🚨 近 24 小時全球顯著地震 (M≥5.0):\n{'-'*20}\n" | |
| for feature in features: | |
| prop = feature['properties'] | |
| mag = prop['mag'] | |
| place = prop['place'] | |
| event_time = datetime.fromtimestamp(prop['time'] / 1000).strftime('%H:%M') | |
| reply_text += f"震級: {mag:.1f} | 時間: {event_time} (UTC)\n地點: {place}\n\n" | |
| return reply_text.strip() | |
| except requests.exceptions.RequestException as e: | |
| return f"❌ 查詢失敗,無法連接到 USGS 伺服器: {e}" | |
| except Exception as e: | |
| return f"❌ 處理資料時發生未知錯誤: {e}" | |
| # --- Flask Webhook 路由 (與之前相同) --- | |
| def callback(): | |
| signature = request.headers['X-Line-Signature'] | |
| body = request.get_data(as_text=True) | |
| app.logger.info("Request body: " + body) | |
| try: | |
| handler.handle(body, signature) | |
| except InvalidSignatureError: | |
| app.logger.info("Invalid signature. Please check your channel secret.") | |
| abort(400) | |
| return 'OK' | |
| # --- LINE 訊息處理 (與之前相同) --- | |
| def handle_message(event): | |
| user_message = event.message.text | |
| reply_text = "" | |
| if "地震" in user_message or "quake" in user_message.lower(): | |
| reply_text = fetch_earthquake_data_for_line() | |
| elif "你好" in user_message or "hi" in user_message.lower(): | |
| reply_text = "👋 你好!我是地震查詢機器人,試著傳送「地震」來查詢最新資訊吧!" | |
| else: | |
| return | |
| with ApiClient(configuration) as api_client: | |
| line_bot_api = MessagingApi(api_client) | |
| line_bot_api.reply_message_with_http_info( | |
| ReplyMessageRequest( | |
| reply_token=event.reply_token, | |
| messages=[TextMessage(text=reply_text)] | |
| ) | |
| ) | |