Rene0119 commited on
Commit
e49e5d9
·
1 Parent(s): a48d4df

整理歷史後的初始提交

Browse files
DrugUse_LineBot_backup/.gitattributes ADDED
@@ -0,0 +1,35 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ *.7z filter=lfs diff=lfs merge=lfs -text
2
+ *.arrow filter=lfs diff=lfs merge=lfs -text
3
+ *.bin filter=lfs diff=lfs merge=lfs -text
4
+ *.bz2 filter=lfs diff=lfs merge=lfs -text
5
+ *.ckpt filter=lfs diff=lfs merge=lfs -text
6
+ *.ftz filter=lfs diff=lfs merge=lfs -text
7
+ *.gz filter=lfs diff=lfs merge=lfs -text
8
+ *.h5 filter=lfs diff=lfs merge=lfs -text
9
+ *.joblib filter=lfs diff=lfs merge=lfs -text
10
+ *.lfs.* filter=lfs diff=lfs merge=lfs -text
11
+ *.mlmodel filter=lfs diff=lfs merge=lfs -text
12
+ *.model filter=lfs diff=lfs merge=lfs -text
13
+ *.msgpack filter=lfs diff=lfs merge=lfs -text
14
+ *.npy filter=lfs diff=lfs merge=lfs -text
15
+ *.npz filter=lfs diff=lfs merge=lfs -text
16
+ *.onnx filter=lfs diff=lfs merge=lfs -text
17
+ *.ot filter=lfs diff=lfs merge=lfs -text
18
+ *.parquet filter=lfs diff=lfs merge=lfs -text
19
+ *.pb filter=lfs diff=lfs merge=lfs -text
20
+ *.pickle filter=lfs diff=lfs merge=lfs -text
21
+ *.pkl filter=lfs diff=lfs merge=lfs -text
22
+ *.pt filter=lfs diff=lfs merge=lfs -text
23
+ *.pth filter=lfs diff=lfs merge=lfs -text
24
+ *.rar filter=lfs diff=lfs merge=lfs -text
25
+ *.safetensors filter=lfs diff=lfs merge=lfs -text
26
+ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
27
+ *.tar.* filter=lfs diff=lfs merge=lfs -text
28
+ *.tar filter=lfs diff=lfs merge=lfs -text
29
+ *.tflite filter=lfs diff=lfs merge=lfs -text
30
+ *.tgz filter=lfs diff=lfs merge=lfs -text
31
+ *.wasm filter=lfs diff=lfs merge=lfs -text
32
+ *.xz filter=lfs diff=lfs merge=lfs -text
33
+ *.zip filter=lfs diff=lfs merge=lfs -text
34
+ *.zst filter=lfs diff=lfs merge=lfs -text
35
+ *tfevents* filter=lfs diff=lfs merge=lfs -text
DrugUse_LineBot_backup/.gitignore ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ medicine.db
2
+ .DS_Store
3
+ .env
DrugUse_LineBot_backup/Dockerfile ADDED
@@ -0,0 +1,30 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # 使用官方 Python 映像作為基礎
2
+ FROM python:3.9-slim
3
+
4
+ # 設定工作目錄
5
+ WORKDIR /app
6
+
7
+ # 將 requirements.txt 複製到工作目錄
8
+ COPY requirements.txt .
9
+
10
+ # 安裝依賴套件
11
+ RUN pip install --no-cache-dir -r requirements.txt
12
+
13
+ # 將專案中的所有檔案複製到工作目錄
14
+ COPY . .
15
+
16
+ # 新增:更改 /app 目錄的權限,使其對所有使用者可寫
17
+ RUN chmod -R 777 /app
18
+
19
+ # 設定環境變數 PORT (Hugging Face Spaces 通常會提供這個環境變數)
20
+ # 您的 app.py 應該從 os.getenv("PORT", "7860") 讀取端口
21
+ # 如果您的 app.py 中寫死了端口,例如 5000,這裡可以改為 EXPOSE 5000
22
+ # 但最好是讓 Flask 監聽 $PORT
23
+ ENV PORT 7860
24
+
25
+ # 開放應用程式運行的端口 (與上面 ENV PORT 一致,或者您 Flask 監聽的端口)
26
+ EXPOSE 7860
27
+
28
+ # 執行應用程式的指令
29
+ # 確保您的 app.py 中有 if __name__ == "__main__": app.run(host="0.0.0.0", port=int(os.environ.get("PORT", 7860)))
30
+ CMD ["python", "app.py"]
DrugUse_LineBot_backup/Linebot Drug Use.json ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "type": "service_account",
3
+ "project_id": "linebot-druguse",
4
+ "private_key_id": "b80390a9e6d6c96a18e564e65515ace30683018d",
5
+ "private_key": "-----BEGIN PRIVATE KEY-----\nMIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDQS9MpQmXme4IV\nrx2RkWH7jy0JNc9sdxY2kovOfth9ONAyHjQ453CaQtX7f80Sk59pggN3nmBzS1lE\nXYDfmrL+FfIa8xDXgsjytJq6A43ZRkahnpvp8wMx0hFa9nNZYNT2KYbnXpDh/Pdb\nRaeWa06YtxvrOsX1M9mI3vfth8sfh3935NHIshBRIVFBZbhEci9qkMMKW/WvPHcu\nfAystdCvf3nDpwyyjA6Yy8tCVj05tDm1qGpC4rdyRex1ZR7Kpn/9ctDFyBu+lJYi\niWJcxlBr170Jo7bq//9GUcdzQO07egpvo6EubI58jFeuTYdNIT1iMgP6jYn65Ehw\n2Ru+kOAtAgMBAAECggEADk1VMtUiNzdonkcKHiDlTAHPyz0Ei0jI/8HDs4/I8HOF\nHhqDUPCZQ3T5NDMuraAbwcGW2IY0JAPQMw4YMiJGjdo2vQwkA6bcAog37d5JGtpa\nXAYOjruBebtYDDl1qvUnt5Lwi59AuPem31ToIH9T4/oCAtPkBvBYDIUXcRnnbqMT\naP66kMgindSkUOAXfHpveFd53IG04jSX7Tzi87jbvcsI2mmKnA30qAYruLjKtk0G\nUJ2ANdylW73aiBEprWayrkyZRxGnKJeXDwIf/j4d5Kd9HDG827LVirqXZKk2gmt+\nAo81UFfYABRK90ibsy5eynnZ5/j8XX8/qdPUoGeFcQKBgQD37a9wyr6y7hdtr7Pz\nTwSqJ7R88niLgwmtBbe8CBaA5uCijS/B7mvZWA65vRH0T2m56DH8ydich8gzwG7r\n8E1d3W/51iCbI4qaSeDn7rjqcDR1ujeh9KK3reJdkChkbBfesNBCYekAcRleb66q\nJekcXHDwK0MMKwQxwZlCOUXH0QKBgQDXE9Tj3KnRDjhRKDnIOdQFLANBbLFyqerA\nASdUpJOCUl+NQQ2yxkFmmfAF6piwhoViGlJpeEoDYSHrNfBqR2/e0dVJDD7V6/5T\ntQ20TwgV7ZopP2qUrxRuwX9XMaGQJ9LTkVux/1wbGSXMwc8tuKn0ALnAAbOdRLTi\nw2+rFcpFnQKBgQDXCuY9It29PR49GUUwKL2XCNFX/sfn2kSuOmNfSHScYaU92NAv\nmakZ33MGT6QfEDJX+bpB1KF5Q5y2ppaPNuMkI0lYng2yAmXZ5XKaOUlSrvgRlO9k\nHlh0kPHizYL7d3ZMxXgd2+bJZ0/lxWDuW+uRmeyrpBqHl1gQACf6dMq9wQKBgDf6\nKGF50BTiD5RH1Oog6ubBssM3Z+PBB6Eba8Ii52V2zPm1obwSz3jFveu8SCtBh7ul\nwCdFMHY6tJpEOqNenmYu1MUjU8NXnaTprXxtSVxzr7jmkXXLL5U39U2bVEJxqvR3\nk5b7MFy5KdZl5ucudcVU3wNzLe1TsYSHeyvbCv8dAoGAEdu6yoOoGxwe78rW7QvG\nTyS1xtSlP7ZMvdm3UuSLPGa8L6B77qHAxUbaYRjfHjgNg2whtxYKrcnrYmg5NYR0\nWmI/ldXhQTztBpo4Xmab3qNoU1dyBVUNgfeb5h1fvUEBmaWJUvtec1RdeBz8SSbX\nOX6WwlSDUzxIcOcdGC3u4Ek=\n-----END PRIVATE KEY-----\n",
6
+ "client_email": "linebot-db-accessor@linebot-druguse.iam.gserviceaccount.com",
7
+ "client_id": "108614875347090756394",
8
+ "auth_uri": "https://accounts.google.com/o/oauth2/auth",
9
+ "token_uri": "https://oauth2.googleapis.com/token",
10
+ "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
11
+ "client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/linebot-db-accessor%40linebot-druguse.iam.gserviceaccount.com",
12
+ "universe_domain": "googleapis.com"
13
+ }
DrugUse_LineBot_backup/README.md ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ title: DrugUse LineBot
3
+ emoji: 👀
4
+ colorFrom: pink
5
+ colorTo: indigo
6
+ sdk: docker
7
+ pinned: false
8
+ ---
9
+
10
+ 這是我的 DrugUse LineBot 專案。
DrugUse_LineBot_backup/app.py ADDED
@@ -0,0 +1,368 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import sqlite3
3
+ import requests
4
+ import tempfile
5
+ import logging
6
+ from io import BytesIO
7
+
8
+ from flask import Flask, request, abort, send_from_directory
9
+ from PIL import Image
10
+
11
+ from linebot.v3.webhook import WebhookParser, WebhookHandler
12
+ from linebot.v3.webhooks import MessageEvent, TextMessageContent, ImageMessageContent
13
+ from linebot.v3.messaging import MessagingApi, Configuration, ApiClient, MessagingApiBlob
14
+ from linebot.v3.messaging.models import (
15
+ TextMessage, ReplyMessageRequest,
16
+ FlexMessage, FlexBubble, FlexBox, FlexText, FlexButton, URIAction,
17
+ QuickReply, QuickReplyItem, LocationAction, ImageMessage
18
+ )
19
+ from linebot.v3.exceptions import InvalidSignatureError
20
+
21
+ import google.generativeai as genai
22
+ import io
23
+ import json
24
+ from dotenv import load_dotenv
25
+
26
+ # --- Google Drive API 相關導入 ---
27
+ from google.oauth2 import service_account # 修改這裡
28
+ from googleapiclient.discovery import build
29
+ from googleapiclient.http import MediaIoBaseDownload
30
+ # --- END Google Drive API 相關導入 ---
31
+
32
+ load_dotenv()
33
+
34
+ app = Flask(__name__)
35
+
36
+ # 從環境變數讀取 LINE Bot 設定
37
+ LINE_CHANNEL_SECRET = os.environ.get("LINE_CHANNEL_SECRET")
38
+ LINE_CHANNEL_ACCESS_TOKEN = os.environ.get("LINE_CHANNEL_ACCESS_TOKEN")
39
+
40
+ if not LINE_CHANNEL_SECRET or not LINE_CHANNEL_ACCESS_TOKEN:
41
+ raise RuntimeError("Missing essential environment variables")
42
+
43
+ print(f"LINE_CHANNEL_SECRET: {LINE_CHANNEL_SECRET}")
44
+ print(f"LINE_CHANNEL_ACCESS_TOKEN: {LINE_CHANNEL_ACCESS_TOKEN}")
45
+ #print(f"GOOGLE_MAP_API_KEY: {GOOGLE_MAP_API_KEY}")
46
+
47
+ # --- 資料庫路徑設定 ---
48
+ BASE_DIR = os.path.dirname(os.path.abspath(__file__))
49
+ DB_FILENAME = "medicine.db"
50
+ DB_PATH = os.path.join(BASE_DIR, DB_FILENAME) # 確保 DB_PATH 指向容器內的預期路徑
51
+ # --- END 資料庫路徑設定 ---
52
+
53
+ # --- Google Drive 下載函式 ---
54
+ def download_db_from_google_drive():
55
+ print("Attempting to download database from Google Drive...")
56
+ google_creds_json_str = os.getenv("GOOGLE_CREDENTIALS_JSON")
57
+ file_id = os.getenv("GOOGLE_DRIVE_FILE_ID")
58
+
59
+ if not google_creds_json_str:
60
+ print("Error: GOOGLE_CREDENTIALS_JSON secret not found.")
61
+ return False
62
+ if not file_id:
63
+ print("Error: GOOGLE_DRIVE_FILE_ID secret not found.")
64
+ return False
65
+
66
+ try:
67
+ # 將 JSON 字串轉換為字典
68
+ creds_info = json.loads(google_creds_json_str)
69
+ # 從服務帳戶資訊建立憑證
70
+ creds = service_account.Credentials.from_service_account_info(
71
+ creds_info,
72
+ scopes=['https://www.googleapis.com/auth/drive.readonly'] # 只需要唯讀權限
73
+ )
74
+ drive_service = build('drive', 'v3', credentials=creds)
75
+
76
+ request_dl = drive_service.files().get_media(fileId=file_id)
77
+ # fh = io.BytesIO() # 記憶體中處理
78
+ # downloader = MediaIoBaseDownload(fh, request_dl)
79
+
80
+ # 直接寫入檔案
81
+ with open(DB_PATH, 'wb') as fh:
82
+ downloader = MediaIoBaseDownload(fh, request_dl)
83
+ done = False
84
+ while done is False:
85
+ status, done = downloader.next_chunk()
86
+ print(f"Download {int(status.progress() * 100)}%.")
87
+ print(f"Database '{DB_FILENAME}' downloaded successfully to '{DB_PATH}'.")
88
+ return True
89
+ except Exception as e:
90
+ print(f"Error downloading database from Google Drive: {e}")
91
+ return False
92
+ # --- END Google Drive 下載函式 ---
93
+
94
+ # --- 在應用程式啟動時執行資料庫下載 ---
95
+ DOWNLOAD_SUCCESS = False # Initialize
96
+ # Check if running in Hugging Face Space or if secrets are generally available
97
+ # In HF Spaces, secrets are environment variables. Locally, .env would be used.
98
+ if os.getenv("GOOGLE_CREDENTIALS_JSON") and os.getenv("GOOGLE_DRIVE_FILE_ID"):
99
+ print("Found Google Drive credentials, attempting download...")
100
+ DOWNLOAD_SUCCESS = download_db_from_google_drive()
101
+ else:
102
+ print("Warning: GOOGLE_CREDENTIALS_JSON or GOOGLE_DRIVE_FILE_ID not found in environment. Skipping DB download.")
103
+ print("If running locally, ensure they are in your .env file or environment.")
104
+ print("If on Hugging Face, ensure secrets are set in Space settings.")
105
+
106
+ if not DOWNLOAD_SUCCESS:
107
+ print("CRITICAL: Database download failed or was skipped. The application might not function as expected if the database is required at startup.")
108
+ # --- END 應用程式啟動時執行資料庫下載 ---
109
+
110
+ configuration = Configuration(access_token=LINE_CHANNEL_ACCESS_TOKEN)
111
+ parser = WebhookParser(LINE_CHANNEL_SECRET)
112
+ handler = WebhookHandler(LINE_CHANNEL_SECRET)
113
+
114
+ genai.configure(api_key=GOOGLE_API_KEY)
115
+ chat = genai.GenerativeModel(model_name="gemini-1.5-flash")
116
+
117
+ logging.basicConfig(level=logging.INFO)
118
+ app.logger.setLevel(logging.INFO)
119
+
120
+ @app.route("/images/<filename>")
121
+ def serve_image(filename):
122
+ return send_from_directory(static_tmp_path, filename)
123
+
124
+ @app.route("/")
125
+ def home():
126
+ return {"message": "Line Webhook Server"}
127
+
128
+ @app.route("/callback", methods=["POST"])
129
+ def callback():
130
+
131
+ # 檢查資料庫是否已成功下載 (可選,但建議)
132
+ if not DOWNLOAD_SUCCESS and not os.path.exists(DB_PATH):
133
+ print("Database not available, aborting callback.")
134
+ abort(500) # 或返回一個提示用戶稍後再試的訊息
135
+
136
+ signature = request.headers.get("X-Line-Signature", "")
137
+ body = request.get_data(as_text=True)
138
+
139
+ try:
140
+ events = parser.parse(body, signature)
141
+ except InvalidSignatureError:
142
+ abort(400)
143
+ except Exception as e:
144
+ print("Webhook parse error:", e)
145
+ abort(400)
146
+
147
+ with ApiClient(configuration) as api_client:
148
+ messaging_api = MessagingApi(api_client)
149
+ blob_api = MessagingApiBlob(api_client)
150
+
151
+ for event in events:
152
+ if event.type == "message":
153
+ # 文字訊息
154
+ if event.message.type == "text":
155
+ user_input = event.message.text.strip()
156
+ print("📨 收到訊息:", user_input)
157
+
158
+ # AI 問答
159
+ if user_input.startswith("AI "):
160
+ prompt = "你是一個中文的AI助手,請用繁體中文回答。\n" + user_input[3:].strip()
161
+ try:
162
+ response = chat.generate_content(prompt)
163
+ reply_text = response.text
164
+ except Exception as e:
165
+ reply_text = f"AI 回答失敗:{e}"
166
+
167
+ reply_request = ReplyMessageRequest(
168
+ reply_token=event.reply_token,
169
+ messages=[TextMessage(text=reply_text)]
170
+ )
171
+ messaging_api.reply_message(reply_message_request=reply_request)
172
+
173
+ # 查詢藥品
174
+ elif user_input == "查詢藥品":
175
+ reply_text = "請輸入藥品名稱,例如:口服感冒藥"
176
+ reply_request = ReplyMessageRequest(
177
+ reply_token=event.reply_token,
178
+ messages=[TextMessage(text=reply_text)]
179
+ )
180
+ messaging_api.reply_message(reply_message_request=reply_request)
181
+
182
+ # 查詢藥局
183
+ elif "查詢藥局" in user_input:
184
+ quick_reply = QuickReply(
185
+ items=[QuickReplyItem(action=LocationAction(label="傳送我的位置"))]
186
+ )
187
+ reply_request = ReplyMessageRequest(
188
+ reply_token=event.reply_token,
189
+ messages=[TextMessage(text="請點選下方按鈕傳送你的位置,我才能幫你找附近藥局喔~", quick_reply=quick_reply)]
190
+ )
191
+ messaging_api.reply_message(reply_message_request=reply_request)
192
+
193
+ # 其他:查詢藥品資料庫,副作用永遠由AI產生
194
+ else:
195
+ medicine_name = user_input.lower()
196
+ try:
197
+ conn = sqlite3.connect(DB_PATH)
198
+ cursor = conn.cursor()
199
+ query = """
200
+ SELECT DISTINCT 中文品名, 英文品名, 適應症
201
+ FROM drugs
202
+ WHERE LOWER(中文品名) LIKE ?
203
+ LIMIT 3
204
+ """
205
+ like_param = f'%{medicine_name}%'
206
+ cursor.execute(query, (like_param,))
207
+ rows = cursor.fetchall()
208
+ conn.close()
209
+
210
+ if rows:
211
+ zh_name, en_name, indication = rows[0]
212
+ # 副作用由 AI 產生
213
+ prompt = (
214
+ f"請用簡短條列式,僅列出副作用,針對藥品「{zh_name}」(英文名:{en_name}),"
215
+ "請用繁體中文回答,若無法判斷請推測。"
216
+ )
217
+ try:
218
+ ai_resp = chat.generate_content(prompt)
219
+ side_effects = ai_resp.text.strip()
220
+ except Exception as e:
221
+ side_effects = f"AI 回答失敗:{e}"
222
+ reply_text = (
223
+ f"🔹 中文品名:{zh_name}\n"
224
+ f"📌 英文品名:{en_name}\n"
225
+ f"📄 適應症:{indication}\n"
226
+ f"⚠️ 副作用:{side_effects}"
227
+ )
228
+ else:
229
+ # 全部請AI生成
230
+ prompt = (
231
+ f"請用以下格式,幫我介紹藥品「{medicine_name}」,若無法查到請盡量推測:\n"
232
+ "🔹 中文品名:\n"
233
+ "📌 英文品名:\n"
234
+ "📄 適應症:\n"
235
+ "⚠️ 副作用:"
236
+ )
237
+ try:
238
+ ai_resp = chat.generate_content(prompt)
239
+ reply_text = ai_resp.text
240
+ except Exception as e:
241
+ reply_text = f"AI 回答失敗:{e}"
242
+
243
+ except Exception as e:
244
+ reply_text = f"⚠️ 查詢資料時發生錯誤:{str(e)}"
245
+
246
+ reply_request = ReplyMessageRequest(
247
+ reply_token=event.reply_token,
248
+ messages=[TextMessage(text=reply_text.strip())]
249
+ )
250
+ messaging_api.reply_message(reply_message_request=reply_request)
251
+
252
+ # 處理位置訊息(查詢附近藥局)
253
+ elif event.message.type == "location":
254
+ user_lat = event.message.latitude
255
+ user_lng = event.message.longitude
256
+
257
+ nearby_url = (
258
+ f"https://maps.googleapis.com/maps/api/place/nearbysearch/json?"
259
+ f"location={user_lat},{user_lng}&radius=1000&type=pharmacy&key={GOOGLE_MAP_API_KEY}"
260
+ )
261
+ nearby_res = requests.get(nearby_url).json()
262
+
263
+ if not nearby_res.get('results'):
264
+ reply_request = ReplyMessageRequest(
265
+ reply_token=event.reply_token,
266
+ messages=[TextMessage(text="附近找不到藥局")]
267
+ )
268
+ messaging_api.reply_message(reply_message_request=reply_request)
269
+ continue
270
+
271
+ place = nearby_res['results'][0]
272
+ place_id = place['place_id']
273
+ name = place.get('name', '藥局名稱未知')
274
+ location = place['geometry']['location']
275
+ dest_lat, dest_lng = location['lat'], location['lng']
276
+
277
+ details_url = (
278
+ f"https://maps.googleapis.com/maps/api/place/details/json?"
279
+ f"place_id={place_id}&fields=name,formatted_phone_number&key={GOOGLE_MAP_API_KEY}"
280
+ )
281
+ details_res = requests.get(details_url).json()
282
+ phone = details_res.get('result', {}).get('formatted_phone_number', '電話不詳')
283
+
284
+ dist_url = (
285
+ f"https://maps.googleapis.com/maps/api/distancematrix/json?"
286
+ f"origins={user_lat},{user_lng}&destinations={dest_lat},{dest_lng}&key={GOOGLE_MAP_API_KEY}"
287
+ )
288
+ dist_res = requests.get(dist_url).json()
289
+ distance = dist_res['rows'][0]['elements'][0]['distance']['text']
290
+
291
+ map_url = f"https://www.google.com/maps/search/?api=1&query={dest_lat},{dest_lng}"
292
+
293
+ bubble = FlexBubble(
294
+ body=FlexBox(
295
+ layout="vertical",
296
+ contents=[
297
+ FlexText(text=name, weight="bold", size="lg"),
298
+ FlexText(text=f"電話:{phone}", size="sm", color="#555555"),
299
+ FlexText(text=f"距離:{distance}", size="sm", color="#777777"),
300
+ ],
301
+ ),
302
+ footer=FlexBox(
303
+ layout="vertical",
304
+ contents=[
305
+ FlexButton(
306
+ style="link",
307
+ height="sm",
308
+ action=URIAction(label="地圖導航", uri=map_url),
309
+ )
310
+ ],
311
+ ),
312
+ )
313
+
314
+ flex_message = FlexMessage(
315
+ alt_text="附近藥局推薦",
316
+ contents=bubble
317
+ )
318
+
319
+ reply_request = ReplyMessageRequest(
320
+ reply_token=event.reply_token,
321
+ messages=[flex_message]
322
+ )
323
+ messaging_api.reply_message(reply_message_request=reply_request)
324
+
325
+ # 圖片訊息:用 Gemini AI 以藥品格式解釋圖片
326
+ elif event.message.type == "image":
327
+ try:
328
+ content = blob_api.get_message_content(message_id=event.message.id)
329
+ with tempfile.NamedTemporaryFile(dir=static_tmp_path, suffix=".jpg", delete=False) as tf:
330
+ tf.write(content)
331
+ filename = os.path.basename(tf.name)
332
+ image_url = f"https://{base_url}/images/{filename}"
333
+ image = Image.open(tf.name)
334
+
335
+ # Gemini 圖片說明(指定格式,四欄都AI產生)
336
+ prompt = (
337
+ "請根據這張圖片判斷藥品資訊,並用以下格式回答,若無法判斷請盡量推測:\n"
338
+ "🔹 中文品名:\n"
339
+ "📌 英文品名:\n"
340
+ "📄 適應症:\n"
341
+ "⚠️ 副作用:"
342
+ )
343
+ response = chat.generate_content([image, prompt])
344
+ description = response.text
345
+
346
+ reply_request = ReplyMessageRequest(
347
+ reply_token=event.reply_token,
348
+ messages=[
349
+ ImageMessage(
350
+ original_content_url=image_url,
351
+ preview_image_url=image_url
352
+ ),
353
+ TextMessage(text=description)
354
+ ]
355
+ )
356
+ messaging_api.reply_message(reply_message_request=reply_request)
357
+ except Exception as e:
358
+ reply_request = ReplyMessageRequest(
359
+ reply_token=event.reply_token,
360
+ messages=[TextMessage(text=f"圖片處理失敗:{e}")]
361
+ )
362
+ messaging_api.reply_message(reply_message_request=reply_request)
363
+
364
+ return "OK"
365
+
366
+ if __name__ == "__main__":
367
+ port = int(os.environ.get("PORT", 7860)) # 讀取環境變數 PORT,預設為 7860
368
+ app.run(host="0.0.0.0", port=port, debug=False)
DrugUse_LineBot_backup/requirements.txt ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ flask
2
+ line-bot-sdk
3
+ transformers
4
+ torch
5
+ # torchvision # 如果您確定沒有用到 torchvision,可以考慮移除或註解掉
6
+ # torchaudio # 同上
7
+ diffusers
8
+ accelerate
9
+ Pillow
10
+ python-dotenv
11
+ google-api-python-client
12
+ google-auth-httplib2
13
+ google-auth-oauthlib