import os import sqlite3 import requests import tempfile import logging from io import BytesIO from flask import Flask, request, abort, send_from_directory from PIL import Image from linebot.v3.webhook import WebhookParser, WebhookHandler from linebot.v3.webhooks import MessageEvent, TextMessageContent, ImageMessageContent from linebot.v3.messaging import MessagingApi, Configuration, ApiClient, MessagingApiBlob from linebot.v3.messaging.models import ( TextMessage, ReplyMessageRequest, PushMessageRequest, FlexMessage, FlexBubble, FlexBox, FlexText, FlexButton, URIAction, QuickReply, QuickReplyItem, LocationAction, ImageMessage, DatetimePickerAction, MessageAction ) from linebot.v3.exceptions import InvalidSignatureError import google.generativeai as genai import json import datetime from apscheduler.schedulers.background import BackgroundScheduler import pytz CHANNEL_SECRET = os.environ.get("YOUR_CHANNEL_SECRET") CHANNEL_ACCESS_TOKEN = os.environ.get("YOUR_CHANNEL_ACCESS_TOKEN") GOOGLE_MAP_API_KEY = os.environ.get("GOOGLE_MAP_API_KEY") GOOGLE_API_KEY = os.environ.get("GOOGLE_API_KEY") base_url = os.environ.get("HF_SPACE_URL", "localhost") if not CHANNEL_SECRET or not CHANNEL_ACCESS_TOKEN or not GOOGLE_API_KEY: raise RuntimeError("Missing essential environment variables") app = Flask(__name__) BASE_DIR = os.path.dirname(os.path.abspath(__file__)) DB_PATH = os.path.join(BASE_DIR, "linebot.db") print("目前資料庫路徑:", DB_PATH) print("資料庫檔案是否存在:", os.path.exists(DB_PATH)) try: with open(DB_PATH, "ab") as f: f.write(b"") print("✅ 資料庫有寫入權限") except Exception as e: print("❌ 資料庫無法寫入:", e) static_tmp_path = "/tmp" os.makedirs(static_tmp_path, exist_ok=True) configuration = Configuration(access_token=CHANNEL_ACCESS_TOKEN) parser = WebhookParser(CHANNEL_SECRET) handler = WebhookHandler(CHANNEL_SECRET) genai.configure(api_key=GOOGLE_API_KEY) chat = genai.GenerativeModel(model_name="gemini-1.5-flash") text_system_prompt = "你是一個專業的中文藥物安全衛教AI,運行於Linebot平台,負責為台灣用戶提供用藥查詢、衛教提醒、藥品辨識與互動諮詢。所有回應必須以繁體中文呈現,語氣需保持專業、中立、清晰,嚴禁使用非正式語彙或網路用語。你的回答僅限於台灣現行合法藥品、常見用藥安全及一般衛教知識,絕不涉及診斷、處方或違法用途。遇重要藥品資訊或警語時,務必標示資料來源(如衛福部、健保署或官方藥物資料庫);無法查證時,需說明資訊有限並提醒用戶諮詢藥師。遇到模糊、非藥物相關、或疑似緊急情境(如中毒、嚴重過敏),請直接回覆:「請儘速就醫或聯絡藥師,Linebot無法提供緊急醫療協助。」回答時,優先給出簡明結論,再補充必要說明,遇複雜內容可分點陳述,藥品名稱、注意事項及用法用量需明顯標註。若用戶詢問非本功能範圍問題,請回覆:「本Linebot僅提供藥物安全與衛生教育資訊。」並簡要列舉可查詢主題(如用藥禁忌、藥物交互作用、藥品保存方式等)。所有資訊僅反映截至2025年6月之官方資料,若遇新藥、召回或重大警訊,應提醒用戶查閱衛福部或官方藥事機構。" logging.basicConfig(level=logging.INFO) app.logger.setLevel(logging.INFO) user_states = {} def init_reminders_table(): conn = sqlite3.connect(DB_PATH) cursor = conn.cursor() cursor.execute(""" CREATE TABLE IF NOT EXISTS reminders ( id INTEGER PRIMARY KEY AUTOINCREMENT, user_id TEXT NOT NULL, medicine TEXT NOT NULL, start_date TEXT NOT NULL, end_date TEXT NOT NULL, times TEXT NOT NULL, sent INTEGER DEFAULT 0 ); """) cursor.execute(""" CREATE TABLE IF NOT EXISTS reminders_log ( id INTEGER PRIMARY KEY AUTOINCREMENT, reminder_id INTEGER, date TEXT, time TEXT ); """) cursor.execute(""" CREATE TABLE IF NOT EXISTS drugs ( 中文品名 TEXT, 英文品名 TEXT, 適應症 TEXT ); """) conn.commit() conn.close() init_reminders_table() def add_reminder(user_id, medicine, start_date, end_date, times): print("[DEBUG] add_reminder 被呼叫") print(f"[DEBUG] 嘗試寫入提醒:{user_id}, {medicine}, {start_date}, {end_date}, {times}") conn = sqlite3.connect(DB_PATH) cursor = conn.cursor() cursor.execute( "INSERT INTO reminders (user_id, medicine, start_date, end_date, times, sent) VALUES (?, ?, ?, ?, ?, 0)", (user_id, medicine, start_date, end_date, json.dumps(times)) ) conn.commit() cursor.execute("SELECT * FROM reminders") print("[DEBUG] reminders 資料表內容:", cursor.fetchall()) conn.close() print("[DEBUG] ✅ 寫入 reminders 成功") def check_and_send_reminders(): tz = pytz.timezone('Asia/Taipei') now = datetime.datetime.now(tz) today = now.strftime("%Y-%m-%d") now_time = now.strftime("%H:%M") conn = sqlite3.connect(DB_PATH) cursor = conn.cursor() cursor.execute("SELECT id, user_id, medicine, start_date, end_date, times FROM reminders") rows = cursor.fetchall() for rid, user_id, medicine, start_date, end_date, times_json in rows: if start_date <= today <= end_date: times = json.loads(times_json) for t in times: cursor.execute("SELECT COUNT(*) FROM reminders_log WHERE reminder_id=? AND date=? AND time=?", (rid, today, t)) if now_time == t and cursor.fetchone()[0] == 0: print(f"[DEBUG] 發送提醒給 {user_id}:{medicine} @ {t}") with ApiClient(configuration) as api_client: messaging_api = MessagingApi(api_client) messaging_api.push_message( push_message_request=PushMessageRequest( to=user_id, messages=[TextMessage(text=f"⏰ 用藥提醒:該服用「{medicine}」囉!")] ) ) cursor.execute("INSERT INTO reminders_log (reminder_id, date, time) VALUES (?, ?, ?)", (rid, today, t)) conn.commit() conn.close() if not hasattr(app, "reminder_scheduler_started"): scheduler = BackgroundScheduler() scheduler.add_job(check_and_send_reminders, 'interval', seconds=20) scheduler.start() app.reminder_scheduler_started = True @app.route("/images/") def serve_image(filename): return send_from_directory(static_tmp_path, filename) @app.route("/") def home(): return {"message": "Line Webhook Server"} @app.route("/show_reminders") def show_reminders(): conn = sqlite3.connect(DB_PATH) cursor = conn.cursor() cursor.execute("SELECT * FROM reminders") rows = cursor.fetchall() conn.close() print("[DEBUG] /show_reminders 查詢結果:", rows) return {"reminders": rows} @app.route("/callback", methods=["POST"]) def callback(): signature = request.headers.get("X-Line-Signature", "") body = request.get_data(as_text=True) print(f"[DEBUG] 收到 callback 請求,body={body}") try: events = parser.parse(body, signature) except InvalidSignatureError: print("[DEBUG] InvalidSignatureError") abort(400) except Exception as e: print("[DEBUG] Webhook parse error:", e) abort(400) with ApiClient(configuration) as api_client: messaging_api = MessagingApi(api_client) blob_api = MessagingApiBlob(api_client) for event in events: print(f"[DEBUG] event.type={event.type}, event={event}") # ====== 用藥提醒對話流程 ====== if event.type == "message" and event.message.type == "text": user_id = event.source.user_id user_input = event.message.text.strip() print(f"[DEBUG] user_input: {user_input}, user_states: {user_states.get(user_id)}") # 修改用藥提醒選單 if user_input == "修改用藥提醒": conn = sqlite3.connect(DB_PATH) cursor = conn.cursor() cursor.execute("SELECT DISTINCT medicine FROM reminders WHERE user_id=?", (user_id,)) medicines = [row[0] for row in cursor.fetchall()] conn.close() if not medicines: reply_text = "你還沒有設定過任何藥物提醒。" reply_request = ReplyMessageRequest( reply_token=event.reply_token, messages=[TextMessage(text=reply_text)] ) messaging_api.reply_message(reply_message_request=reply_request) return "OK" quick_reply = QuickReply( items=[QuickReplyItem(action=MessageAction(label=med, text=med)) for med in medicines] ) reply_text = "請選擇你要修改的藥品:" reply_request = ReplyMessageRequest( reply_token=event.reply_token, messages=[TextMessage(text=reply_text, quick_reply=quick_reply)] ) messaging_api.reply_message(reply_message_request=reply_request) user_states[user_id] = {'step': 'edit_medicine'} return "OK" elif user_input == "用藥提醒": user_states[user_id] = {'step': 'ask_medicine'} print(f"[DEBUG] 進入 ask_medicine, user_id={user_id}") reply_text = "請輸入要提醒的藥品名稱:" reply_request = ReplyMessageRequest( reply_token=event.reply_token, messages=[TextMessage(text=reply_text)] ) messaging_api.reply_message(reply_message_request=reply_request) return "OK" elif user_id in user_states: state = user_states[user_id] print(f"[DEBUG] user_states[{user_id}] = {state}") if state.get('step') == 'ask_medicine': state['medicine'] = user_input state['step'] = 'ask_start' print(f"[DEBUG] 進入 ask_start, user_id={user_id}, medicine={user_input}") quick_reply = QuickReply( items=[ QuickReplyItem( action=DatetimePickerAction( label="選擇開始日期", data="start_date", mode="date" ) ) ] ) reply_text = "請選擇提醒開始日期:" reply_request = ReplyMessageRequest( reply_token=event.reply_token, messages=[TextMessage(text=reply_text, quick_reply=quick_reply)] ) messaging_api.reply_message(reply_message_request=reply_request) return "OK" elif state.get('step') == 'ask_times': print(f"[DEBUG] 進入 ask_times, user_id={user_id}, state={state}") times = [t.strip() for t in user_input.split(",") if t.strip()] # 檢查每個時間格式是否為 HH:MM import re valid = True for t in times: if not re.match(r"^(?:[01]\d|2[0-3]):[0-5]\d$", t): valid = False break if not times or not valid: reply_text = "時間格式錯誤,請重新輸入(24小時制,如 08:00,12:00,18:00):" reply_request = ReplyMessageRequest( reply_token=event.reply_token, messages=[TextMessage(text=reply_text)] ) messaging_api.reply_message(reply_message_request=reply_request) return "OK" # 時間格式正確才繼續 add_reminder(user_id, state['medicine'], state['start_date'], state['end_date'], times) reply_text = f"已設定提醒:{state['medicine']}\n從 {state['start_date']} 到 {state['end_date']}\n每天:{', '.join(times)}" reply_request = ReplyMessageRequest( reply_token=event.reply_token, messages=[TextMessage(text=reply_text)] ) messaging_api.reply_message(reply_message_request=reply_request) user_states.pop(user_id, None) print(f"[DEBUG] 完成提醒流程,user_states 移除 {user_id}") return "OK" # ====== 修改用藥提醒流程 ====== elif state.get('step') == 'edit_medicine': selected_medicine = user_input conn = sqlite3.connect(DB_PATH) cursor = conn.cursor() cursor.execute( "SELECT id, start_date, end_date, times FROM reminders WHERE user_id=? AND medicine=? ORDER BY id DESC LIMIT 1", (user_id, selected_medicine) ) row = cursor.fetchone() conn.close() if not row: reply_text = "查無此藥品提醒資料。" reply_request = ReplyMessageRequest( reply_token=event.reply_token, messages=[TextMessage(text=reply_text)] ) messaging_api.reply_message(reply_message_request=reply_request) user_states.pop(user_id, None) return "OK" reminder_id, start_date, end_date, times_json = row times = ','.join(json.loads(times_json)) reply_text = ( f"你目前的提醒設定:\n" f"藥品:{selected_medicine}\n" f"開始:{start_date}\n" f"結束:{end_date}\n" f"時間:{times}\n" "請選擇要修改的欄位,或輸入 完成 結束:" ) quick_reply = QuickReply( items=[ QuickReplyItem(action=MessageAction(label="開始日期", text="開始日期")), QuickReplyItem(action=MessageAction(label="結束日期", text="結束日期")), QuickReplyItem(action=MessageAction(label="提醒時間", text="提醒時間")), QuickReplyItem(action=MessageAction(label="完成", text="完成")), ] ) state['step'] = 'edit_field' state['reminder_id'] = reminder_id state['medicine'] = selected_medicine reply_request = ReplyMessageRequest( reply_token=event.reply_token, messages=[TextMessage(text=reply_text, quick_reply=quick_reply)] ) messaging_api.reply_message(reply_message_request=reply_request) return "OK" elif state.get('step') == 'edit_field': field = user_input.strip() if field == "開始日期": state['step'] = 'edit_start_date' quick_reply = QuickReply( items=[ QuickReplyItem( action=DatetimePickerAction( label="選擇開始日期", data="edit_start_date", mode="date" ) ) ] ) reply_text = "請選擇新的開始日期:" reply_request = ReplyMessageRequest( reply_token=event.reply_token, messages=[TextMessage(text=reply_text, quick_reply=quick_reply)] ) messaging_api.reply_message(reply_message_request=reply_request) return "OK" elif field == "結束日期": state['step'] = 'edit_end_date' quick_reply = QuickReply( items=[ QuickReplyItem( action=DatetimePickerAction( label="選擇結束日期", data="edit_end_date", mode="date" ) ) ] ) reply_text = "請選擇新的結束日期:" reply_request = ReplyMessageRequest( reply_token=event.reply_token, messages=[TextMessage(text=reply_text, quick_reply=quick_reply)] ) messaging_api.reply_message(reply_message_request=reply_request) return "OK" elif field == "提醒時間": state['step'] = 'edit_times' reply_text = "請輸入新的提醒時間(24小時制,用逗號分隔):" reply_request = ReplyMessageRequest( reply_token=event.reply_token, messages=[TextMessage(text=reply_text)] ) messaging_api.reply_message(reply_message_request=reply_request) return "OK" elif field.lower() == "完成": reply_text = "已結束修改。" user_states.pop(user_id, None) reply_request = ReplyMessageRequest( reply_token=event.reply_token, messages=[TextMessage(text=reply_text)] ) messaging_api.reply_message(reply_message_request=reply_request) return "OK" else: # 再次顯示選單 quick_reply = QuickReply( items=[ QuickReplyItem(action=MessageAction(label="開始日期", text="開始日期")), QuickReplyItem(action=MessageAction(label="結束日期", text="結束日期")), QuickReplyItem(action=MessageAction(label="提醒時間", text="提醒時間")), QuickReplyItem(action=MessageAction(label="完成", text="完成")), ] ) reply_text = "請選擇要修改的欄位,或輸入 完成 結束:" reply_request = ReplyMessageRequest( reply_token=event.reply_token, messages=[TextMessage(text=reply_text, quick_reply=quick_reply)] ) messaging_api.reply_message(reply_message_request=reply_request) return "OK" elif state.get('step') == 'edit_times': import re times = [t.strip() for t in user_input.split(",") if t.strip()] valid = all(re.match(r"^(?:[01]\d|2[0-3]):[0-5]\d$", t) for t in times) if not times or not valid: reply_text = "時間格式錯誤,請重新輸入(24小時制,如 08:00,12:00,18:00):" reply_request = ReplyMessageRequest( reply_token=event.reply_token, messages=[TextMessage(text=reply_text)] ) messaging_api.reply_message(reply_message_request=reply_request) return "OK" conn = sqlite3.connect(DB_PATH) cursor = conn.cursor() cursor.execute("UPDATE reminders SET times=? WHERE id=?", (json.dumps(times), state['reminder_id'])) conn.commit() conn.close() reply_text = "提醒時間已更新!" # 修改完繼續顯示選單 quick_reply = QuickReply( items=[ QuickReplyItem(action=MessageAction(label="開始日期", text="開始日期")), QuickReplyItem(action=MessageAction(label="結束日期", text="結束日期")), QuickReplyItem(action=MessageAction(label="提醒時間", text="提醒時間")), QuickReplyItem(action=MessageAction(label="完成", text="完成")), ] ) reply_text += "\n請選擇要繼續修改的欄位,或輸入 完成 結束:" reply_request = ReplyMessageRequest( reply_token=event.reply_token, messages=[TextMessage(text=reply_text, quick_reply=quick_reply)] ) messaging_api.reply_message(reply_message_request=reply_request) state['step'] = 'edit_field' return "OK" # ====== 其他功能區塊(查詢藥品、AI、藥局、圖片) ====== user_input = event.message.text.strip() print("[DEBUG] 進入原有功能區塊,收到訊息:", user_input) # AI 問答 if user_input.startswith("AI "): prompt = "你是一個中文的AI助手,請用繁體中文回答。\n" + user_input[3:].strip() try: response = chat.generate_content(prompt) reply_text = response.text except Exception as e: logging.exception("AI 問答發生錯誤") reply_text = "⚠️ AI 回答失敗,請稍後再試" reply_request = ReplyMessageRequest( reply_token=event.reply_token, messages=[TextMessage(text=reply_text)] ) messaging_api.reply_message(reply_message_request=reply_request) return "OK" # 查詢藥品 elif user_input == "查詢藥品": try: # 這裡應該要有 medicine_name 的來源,通常是 user_states 或請用戶再輸入 medicine_name = user_states.get(user_id, {}).get('medicine') if not medicine_name: reply_text = "請輸入要查詢的藥品名稱:" else: medicine_name = medicine_name.strip().lower() conn = sqlite3.connect(DB_PATH) cursor = conn.cursor() query = """ SELECT DISTINCT 中文品名, 英文品名, 適應症 FROM drugs WHERE LOWER(中文品名) = ? OR LOWER(英文品名) = ? LIMIT 1 """ cursor.execute(query, (medicine_name, medicine_name)) row = cursor.fetchone() conn.close() print(f"[DEBUG] 查詢 drugs 結果:{row}") if row: zh_name, en_name, indication = row # 副作用由 AI 產生 prompt = ( f"請只用簡短條列式(每點用-開頭,不要用*),僅列出副作用," f"針對藥品「{zh_name}」(英文名:{en_name})," "請用繁體中文回答,不要加任何說明、警語或強調語句。" ) try: ai_resp = chat.generate_content(prompt) side_effects = ai_resp.text.strip() except Exception as e: logging.exception("AI 產生副作用失敗") side_effects = f"AI 回答失敗:{e}" reply_text = ( f"🔹 中文品名:{zh_name}\n" f"📌 英文品名:{en_name}\n" f"📄 適應症:{indication}\n" f"⚠️ 副作用:\n{side_effects}" ) else: reply_text = "未找到相關藥品,請重新輸入" except Exception as e: logging.exception("查詢資料時發生錯誤") reply_text = f"⚠️ 查詢資料時發生錯誤,請稍後再試" reply_request = ReplyMessageRequest( reply_token=event.reply_token, messages=[TextMessage(text=reply_text.strip())] ) messaging_api.reply_message(reply_message_request=reply_request) #圖片查詢 elif user_input == "圖片查詢": reply_text = "請傳送藥品圖片:" reply_request = ReplyMessageRequest( reply_token=event.reply_token, messages=[TextMessage(text=reply_text)] ) messaging_api.reply_message(reply_message_request=reply_request) return "OK" # 查詢藥局 elif "查詢藥局" in user_input: try: quick_reply = QuickReply( items=[QuickReplyItem(action=LocationAction(label="傳送我的位置"))] ) reply_request = ReplyMessageRequest( reply_token=event.reply_token, messages=[TextMessage(text="請點選下方按鈕傳送你的位置,我才能幫你找附近藥局喔~", quick_reply=quick_reply)] ) messaging_api.reply_message(reply_message_request=reply_request) except Exception as e: logging.exception("查詢藥局發生錯誤") reply_text = "⚠️ 查詢藥局失敗,請稍後再試" reply_request = ReplyMessageRequest( reply_token=event.reply_token, messages=[TextMessage(text=reply_text)] ) messaging_api.reply_message(reply_message_request=reply_request) return "OK" else: try: medicine_name = user_input conn = sqlite3.connect(DB_PATH) cursor = conn.cursor() query = """ SELECT DISTINCT 中文品名, 英文品名, 適應症 FROM drugs WHERE 中文品名 LIKE ? OR 英文品名 LIKE ? LIMIT 1 """ like_param = f'%{medicine_name}%' cursor.execute(query, (like_param, like_param)) row = cursor.fetchone() conn.close() print(f"[DEBUG] 查詢 drugs 結果:{row}") if row: zh_name, en_name, indication = row # 副作用由 AI 產生 prompt = ( f"請只用簡短條列式(每點用-開頭,不要用*),僅列出副作用," f"針對藥品「{zh_name}」(英文名:{en_name})," "請用繁體中文回答,不要加任何說明、警語或強調語句。" ) try: ai_resp = chat.generate_content(prompt) side_effects = ai_resp.text.strip() except Exception as e: side_effects = f"AI 回答失敗:{e}" reply_text = ( f"🔹 中文品名:{zh_name}\n" f"📌 英文品名:{en_name}\n" f"📄 適應症:{indication}\n" f"⚠️ 副作用:\n{side_effects}" ) else: prompt = ( f"請用以下格式,幫我介紹藥品「{medicine_name}」," "只要條列資料本身,不要加任何說明、警語或強調語句:\n" "🔹 中文品名:\n" "📌 英文品名:\n" "📄 適應症:\n" "⚠️ 副作用:\n(請用-開頭條列,不要用*)" ) try: ai_resp = chat.generate_content(prompt) reply_text = ai_resp.text except Exception as e: reply_text = f"AI 回答失敗:{e}" except Exception as e: reply_text = f"⚠️ 查詢資料時發生錯誤:{str(e)}" reply_request = ReplyMessageRequest( reply_token=event.reply_token, messages=[TextMessage(text=reply_text.strip())] ) messaging_api.reply_message(reply_message_request=reply_request) elif event.type == "message" and event.message.type == "location": print("[DEBUG] 收到位置訊息") user_lat = event.message.latitude user_lng = event.message.longitude nearby_url = ( f"https://maps.googleapis.com/maps/api/place/nearbysearch/json?" f"location={user_lat},{user_lng}&radius=1000&type=pharmacy&language=zh-TW&key={GOOGLE_MAP_API_KEY}" ) nearby_res = requests.get(nearby_url).json() print(f"[DEBUG] nearby_res: {nearby_res}") if not nearby_res.get('results'): reply_request = ReplyMessageRequest( reply_token=event.reply_token, messages=[TextMessage(text="附近找不到藥局")] ) messaging_api.reply_message(reply_message_request=reply_request) return "OK" bubbles = [] for place in nearby_res['results'][:3]: place_id = place['place_id'] name = place.get('name', '藥局名稱未知') address = place.get('vicinity', '地址不詳') location = place['geometry']['location'] dest_lat, dest_lng = location['lat'], location['lng'] # 取得電話 details_url = ( f"https://maps.googleapis.com/maps/api/place/details/json?" f"place_id={place_id}&fields=name,formatted_phone_number&key={GOOGLE_MAP_API_KEY}" ) details_res = requests.get(details_url).json() phone = details_res.get('result', {}).get('formatted_phone_number', '電話不詳') # 取得距離 dist_url = ( f"https://maps.googleapis.com/maps/api/distancematrix/json?" f"origins={user_lat},{user_lng}&destinations={dest_lat},{dest_lng}&key={GOOGLE_MAP_API_KEY}" ) dist_res = requests.get(dist_url).json() distance = dist_res['rows'][0]['elements'][0]['distance']['text'] map_url = f"https://www.google.com/maps/search/?api=1&query={dest_lat},{dest_lng}" bubble = FlexBubble( body=FlexBox( layout="vertical", contents=[ FlexText(text=name, weight="bold", size="lg"), FlexText(text=f"地址:{address}", size="sm", color="#555555", wrap=True), FlexText(text=f"電話:{phone}", size="sm", color="#555555"), FlexText(text=f"距離:{distance}", size="sm", color="#777777"), ], ), footer=FlexBox( layout="vertical", contents=[ FlexButton( style="link", height="sm", action=URIAction(label="地圖導航", uri=map_url), ) ], ), ) bubbles.append(bubble) from linebot.v3.messaging.models import FlexCarousel, FlexMessage carousel = FlexCarousel(contents=bubbles) flex_message = FlexMessage( alt_text="附近藥局推薦", contents=carousel ) reply_request = ReplyMessageRequest( reply_token=event.reply_token, messages=[flex_message] ) messaging_api.reply_message(reply_message_request=reply_request) return "OK" elif event.type == "message" and event.message.type == "image": print("[DEBUG] 收到圖片訊息") try: content = blob_api.get_message_content(message_id=event.message.id) with tempfile.NamedTemporaryFile(dir=static_tmp_path, suffix=".jpg", delete=False) as tf: tf.write(content) filename = os.path.basename(tf.name) image = Image.open(tf.name) prompt = ( "請根據這張圖片判斷藥品資訊,若圖片無法判斷適應症或副作用,請根據藥品名稱推測並補充," "只要條列資料本身,不要加任何說明、警語或強調語句,也不要加**:\n" "🔹 中文品名:\n" "📌 英文品名:\n" "📄 適應症:\n" "⚠️ 副作用:\n(請用-開頭條列,不要用*)" ) response = chat.generate_content([image, prompt]) description = response.text reply_request = ReplyMessageRequest( reply_token=event.reply_token, messages=[TextMessage(text=description.strip())] ) messaging_api.reply_message(reply_message_request=reply_request) except Exception as e: logging.exception("圖片處理發生錯誤") reply_text = "⚠️ 圖片處理失敗,請稍後再試" reply_request = ReplyMessageRequest( reply_token=event.reply_token, messages=[TextMessage(text=reply_text)] ) messaging_api.reply_message(reply_message_request=reply_request) return "OK" elif event.type == "postback": user_id = event.source.user_id data = event.postback.data print(f"[DEBUG] postback data: {data}, user_states: {user_states.get(user_id)}") # 用藥提醒步驟分開訊息 if data == "start_date": user_states[user_id]['start_date'] = event.postback.params['date'] user_states[user_id]['step'] = 'ask_end' # 先回覆 reply_request = ReplyMessageRequest( reply_token=event.reply_token, messages=[TextMessage(text=f"你選擇的開始日期為:{event.postback.params['date']}")] ) messaging_api.reply_message(reply_message_request=reply_request) # 再推送下一步 quick_reply = QuickReply( items=[ QuickReplyItem( action=DatetimePickerAction( label="選擇結束日期", data="end_date", mode="date" ) ) ] ) messaging_api.push_message( push_message_request=PushMessageRequest( to=user_id, messages=[TextMessage(text="請選擇提醒結束日期:", quick_reply=quick_reply)] ) ) return "OK" elif data == "end_date": user_states[user_id]['end_date'] = event.postback.params['date'] user_states[user_id]['step'] = 'ask_times' reply_request = ReplyMessageRequest( reply_token=event.reply_token, messages=[TextMessage(text=f"你選擇的結束日期為:{event.postback.params['date']}")] ) messaging_api.reply_message(reply_message_request=reply_request) messaging_api.push_message( push_message_request=PushMessageRequest( to=user_id, messages=[TextMessage(text="請輸入每天要提醒的時間(24小時制,可多個,用逗號分隔,如 08:00,12:00,18:00):")] ) ) return "OK" # 修改用藥提醒步驟分開訊息 elif data == "edit_start_date": user_states[user_id]['step'] = 'edit_field' new_start = event.postback.params['date'] conn = sqlite3.connect(DB_PATH) cursor = conn.cursor() cursor.execute("UPDATE reminders SET start_date=? WHERE id=?", (new_start, user_states[user_id]['reminder_id'])) conn.commit() conn.close() reply_request = ReplyMessageRequest( reply_token=event.reply_token, messages=[TextMessage(text=f"開始日期已更新為:{new_start}")] ) messaging_api.reply_message(reply_message_request=reply_request) quick_reply = QuickReply( items=[ QuickReplyItem(action=MessageAction(label="開始日期", text="開始日期")), QuickReplyItem(action=MessageAction(label="結束日期", text="結束日期")), QuickReplyItem(action=MessageAction(label="提醒時間", text="提醒時間")), QuickReplyItem(action=MessageAction(label="完成", text="完成")), ] ) messaging_api.push_message( push_message_request=PushMessageRequest( to=user_id, messages=[TextMessage(text="請選擇要繼續修改的欄位,或輸入 完成 結束:", quick_reply=quick_reply)] ) ) return "OK" elif data == "edit_end_date": user_states[user_id]['step'] = 'edit_field' new_end = event.postback.params['date'] conn = sqlite3.connect(DB_PATH) cursor = conn.cursor() cursor.execute("UPDATE reminders SET end_date=? WHERE id=?", (new_end, user_states[user_id]['reminder_id'])) conn.commit() conn.close() reply_request = ReplyMessageRequest( reply_token=event.reply_token, messages=[TextMessage(text=f"結束日期已更新為:{new_end}")] ) messaging_api.reply_message(reply_message_request=reply_request) quick_reply = QuickReply( items=[ QuickReplyItem(action=MessageAction(label="開始日期", text="開始日期")), QuickReplyItem(action=MessageAction(label="結束日期", text="結束日期")), QuickReplyItem(action=MessageAction(label="提醒時間", text="提醒時間")), QuickReplyItem(action=MessageAction(label="完成", text="完成")), ] ) messaging_api.push_message( push_message_request=PushMessageRequest( to=user_id, messages=[TextMessage(text="請選擇要繼續修改的欄位,或輸入 完成 結束:", quick_reply=quick_reply)] ) ) return "OK" print("[DEBUG] callback 執行結束") return "OK" if __name__ == "__main__": app.run(host="0.0.0.0", port=7860)