hazelhh commited on
Commit
9be4e75
·
verified ·
1 Parent(s): 9d8ba37

Update main.py

Browse files
Files changed (1) hide show
  1. main.py +59 -22
main.py CHANGED
@@ -9,10 +9,15 @@ import requests
9
  import base64
10
  from collections import defaultdict
11
  import uvicorn
 
 
 
 
12
 
13
  # 檢查環境變數
14
- if not all(k in os.environ for k in ["CHANNEL_ACCESS_TOKEN", "CHANNEL_SECRET", "GOOGLE_API_KEY", "IMGUR_CLIENT_ID"]):
15
- raise ValueError("Missing environment variables. Please set CHANNEL_ACCESS_TOKEN, CHANNEL_SECRET, GOOGLE_API_KEY, and IMGUR_CLIENT_ID.")
 
16
 
17
  # Line Bot API 設定
18
  line_bot_api = LineBotApi(os.environ["CHANNEL_ACCESS_TOKEN"])
@@ -32,7 +37,7 @@ user_states = defaultdict(lambda: {
32
  MAX_IMAGES_PER_TYPE = 3
33
  GEMINI_API_URL = "https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash-preview-05-20:generateContent"
34
  GOOGLE_API_KEY = os.environ["GOOGLE_API_KEY"]
35
- IMGUR_CLIENT_ID = os.environ["IMGUR_CLIENT_ID"]
36
 
37
  app = FastAPI()
38
 
@@ -62,25 +67,33 @@ async def webhook(
62
  return "ok"
63
 
64
  def get_base64_image(message_id: str):
 
65
  message_content = line_bot_api.get_message_content(message_id)
66
  image_bytes = message_content.content
67
  return base64.b64encode(image_bytes).decode('utf-8')
68
 
69
- def upload_to_imgur(image_base64: str):
70
  """
71
- 將 Base64 編碼的圖片上傳到 Imgur
72
  """
73
- headers = {"Authorization": f"Client-ID {IMGUR_CLIENT_ID}"}
74
- payload = {"image": image_base64}
 
 
 
 
75
  try:
76
- response = requests.post("https://api.imgur.com/3/image", headers=headers, data=payload)
77
  response.raise_for_status()
78
- return response.json()["data"]["link"]
 
 
79
  except requests.exceptions.RequestException as e:
80
- print(f"Error uploading to Imgur: {e}")
81
  return None
82
 
83
  def get_gemini_response(prompt: str, images: list):
 
84
  payload = {
85
  "contents": [
86
  {
@@ -93,17 +106,28 @@ def get_gemini_response(prompt: str, images: list):
93
  ]
94
  }
95
 
96
- response = requests.post(f"{GEMINI_API_URL}?key={GOOGLE_API_KEY}", json=payload)
97
- if response.status_code == 200:
98
- return response.json()['candidates'][0]['content']['parts'][0]['text']
99
- else:
100
- return f"Gemini API 請求失敗:{response.status_code}, {response.text}"
 
 
 
 
 
 
 
 
 
 
101
 
102
  def get_virtual_tryon_image(user_photo, upper_body_image, lower_body_image):
103
  """
104
  模擬虛擬試穿 API,返回一個試穿後的圖片 URL。
105
  在實際應用中,這裡會呼叫一個運行 IDM-VTON 的獨立服務。
106
  """
 
107
  # 這裡的邏輯需要您自行實現,可以是呼叫一個公開的 API,
108
  # 或者將圖片傳輸到一個運行 IDM-VTON 的伺服器。
109
  # 由於沒有實際的 IDM-VTON 服務,我們使用佔位符圖片來展示功能。
@@ -115,12 +139,14 @@ def handle_text_message(event):
115
  text = event.message.text.lower()
116
  reply_token = event.reply_token
117
 
 
 
118
  # 新增 重置 功能
119
- if text == "重置" or text == "重來" or text == "重新開始" or text == "再一次":
120
  user_states[user_id] = defaultdict(lambda: {"upper_body_images": [], "lower_body_images": [], "current_mode": None, "is_ready_for_outfit": False, "is_ready_for_photo": False, "user_info": {}, "personal_photo": None})[user_id]
121
  line_bot_api.reply_message(
122
  reply_token,
123
- TextSendMessage(text="狀態已重置。請重新輸入個人資訊,格式為:身高,胸圍,腰圍,臀圍,場合。例如:165,85,65,90,約會")
124
  )
125
  return
126
 
@@ -150,6 +176,7 @@ def handle_text_message(event):
150
  )
151
  return
152
  except ValueError:
 
153
  line_bot_api.reply_message(
154
  reply_token,
155
  TextSendMessage(text="請依照格式輸入:身高,三圍胸圍,腰圍,臀圍,場合。例如:165,85,65,90,約會")
@@ -184,8 +211,11 @@ def handle_image_message(event):
184
  user_id = event.source.user_id
185
  reply_token = event.reply_token
186
 
 
 
187
  # 檢查是否已準備好處理圖片
188
  if not user_states[user_id]["is_ready_for_outfit"]:
 
189
  line_bot_api.reply_message(
190
  reply_token,
191
  TextSendMessage(text="請先輸入個人資訊,格式為:身高,胸圍,腰圍,臀圍,場合。例如:165,85,65,90,約會")
@@ -197,8 +227,8 @@ def handle_image_message(event):
197
  try:
198
  image_id = event.message.id
199
  base64_img = get_base64_image(image_id)
200
- # 將個人照片上傳到 Imgur,並儲存 URL
201
- photo_url = upload_to_imgur(base64_img)
202
  user_states[user_id]["personal_photo"] = photo_url
203
  user_states[user_id]["is_ready_for_photo"] = True
204
 
@@ -250,6 +280,7 @@ def handle_image_message(event):
250
  return
251
 
252
  except Exception as e:
 
253
  line_bot_api.reply_message(
254
  reply_token,
255
  TextSendMessage(text=f"圖片處理失敗,請稍後再試。錯誤:{e}")
@@ -259,6 +290,7 @@ def handle_image_message(event):
259
  # 處理衣物圖片上傳
260
  mode = user_states[user_id]["current_mode"]
261
  if not mode:
 
262
  line_bot_api.reply_message(
263
  reply_token,
264
  TextSendMessage(text="請先傳送「上衣」或「褲子」來選擇要上傳的衣服類型。")
@@ -268,12 +300,13 @@ def handle_image_message(event):
268
  try:
269
  image_id = event.message.id
270
  base64_img = get_base64_image(image_id)
271
- # 上傳圖片到 Imgur,並儲存 URL
272
- image_url = upload_to_imgur(base64_img)
273
  if not image_url:
 
274
  line_bot_api.reply_message(
275
  reply_token,
276
- TextSendMessage(text="圖片上傳 Imgur 失敗,請稍後再試。")
277
  )
278
  return
279
 
@@ -281,6 +314,7 @@ def handle_image_message(event):
281
  if len(user_states[user_id]["upper_body_images"]) < MAX_IMAGES_PER_TYPE:
282
  user_states[user_id]["upper_body_images"].append(image_url)
283
  else:
 
284
  line_bot_api.reply_message(
285
  reply_token,
286
  TextSendMessage(text="上衣數量已滿,請傳送「褲子」來上傳褲子圖片。")
@@ -290,6 +324,7 @@ def handle_image_message(event):
290
  if len(user_states[user_id]["lower_body_images"]) < MAX_IMAGES_PER_TYPE:
291
  user_states[user_id]["lower_body_images"].append(image_url)
292
  else:
 
293
  line_bot_api.reply_message(
294
  reply_token,
295
  TextSendMessage(text="褲子數量已滿,請傳送「上衣」來上傳上衣圖片。")
@@ -305,12 +340,14 @@ def handle_image_message(event):
305
  TextSendMessage(text=f"已接收。上衣: {upper_count}/{MAX_IMAGES_PER_TYPE},褲子/裙子: {lower_count}/{MAX_IMAGES_PER_TYPE}。")
306
  )
307
  else:
 
308
  line_bot_api.reply_message(
309
  reply_token,
310
  TextSendMessage(text="已收到所有衣物圖片!接下來,請上傳一張您個人的全身照片,以便進行虛擬試穿。")
311
  )
312
 
313
  except Exception as e:
 
314
  line_bot_api.reply_message(
315
  reply_token,
316
  TextSendMessage(text=f"圖片處理失敗,請稍後再試。錯誤:{e}")
 
9
  import base64
10
  from collections import defaultdict
11
  import uvicorn
12
+ import logging
13
+
14
+ # 設置日誌記錄
15
+ logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
16
 
17
  # 檢查環境變數
18
+ if not all(k in os.environ for k in ["CHANNEL_ACCESS_TOKEN", "CHANNEL_SECRET", "GOOGLE_API_KEY", "IMGBB_API_KEY"]):
19
+ logging.error("Missing environment variables. Please set CHANNEL_ACCESS_TOKEN, CHANNEL_SECRET, GOOGLE_API_KEY, and IMGBB_API_KEY.")
20
+ raise ValueError("Missing environment variables. Please set CHANNEL_ACCESS_TOKEN, CHANNEL_SECRET, GOOGLE_API_KEY, and IMGBB_API_KEY.")
21
 
22
  # Line Bot API 設定
23
  line_bot_api = LineBotApi(os.environ["CHANNEL_ACCESS_TOKEN"])
 
37
  MAX_IMAGES_PER_TYPE = 3
38
  GEMINI_API_URL = "https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash-preview-05-20:generateContent"
39
  GOOGLE_API_KEY = os.environ["GOOGLE_API_KEY"]
40
+ IMGBB_API_KEY = os.environ["IMGBB_API_KEY"]
41
 
42
  app = FastAPI()
43
 
 
67
  return "ok"
68
 
69
  def get_base64_image(message_id: str):
70
+ logging.info(f"Fetching image content for message ID: {message_id}")
71
  message_content = line_bot_api.get_message_content(message_id)
72
  image_bytes = message_content.content
73
  return base64.b64encode(image_bytes).decode('utf-8')
74
 
75
+ def upload_to_imgbb(image_base64: str):
76
  """
77
+ 將 Base64 編碼的圖片上傳到 Imgbb
78
  """
79
+ logging.info("Attempting to upload image to Imgbb.")
80
+ url = "https://api.imgbb.com/1/upload"
81
+ payload = {
82
+ "key": IMGBB_API_KEY,
83
+ "image": image_base64
84
+ }
85
  try:
86
+ response = requests.post(url, data=payload)
87
  response.raise_for_status()
88
+ imgbb_link = response.json()["data"]["url"]
89
+ logging.info(f"Image successfully uploaded to Imgbb: {imgbb_link}")
90
+ return imgbb_link
91
  except requests.exceptions.RequestException as e:
92
+ logging.error(f"Error uploading to Imgbb: {e}")
93
  return None
94
 
95
  def get_gemini_response(prompt: str, images: list):
96
+ logging.info("Sending request to Gemini API.")
97
  payload = {
98
  "contents": [
99
  {
 
106
  ]
107
  }
108
 
109
+ try:
110
+ response = requests.post(f"{GEMINI_API_URL}?key={GOOGLE_API_KEY}", json=payload, timeout=60) # Increased timeout
111
+ response.raise_for_status()
112
+ response_data = response.json()
113
+ if 'candidates' in response_data and response_data['candidates']:
114
+ gemini_text = response_data['candidates'][0]['content']['parts'][0]['text']
115
+ logging.info("Successfully received response from Gemini.")
116
+ return gemini_text
117
+ else:
118
+ logging.error(f"Gemini API response has no candidates: {response.text}")
119
+ return "Gemini API 暫時無法提供服務,請稍後再試。"
120
+ except requests.exceptions.RequestException as e:
121
+ logging.error(f"Gemini API request failed: {e}")
122
+ return f"Gemini API 請求失敗:{e}"
123
+
124
 
125
  def get_virtual_tryon_image(user_photo, upper_body_image, lower_body_image):
126
  """
127
  模擬虛擬試穿 API,返回一個試穿後的圖片 URL。
128
  在實際應用中,這裡會呼叫一個運行 IDM-VTON 的獨立服務。
129
  """
130
+ logging.info("Simulating virtual try-on and returning a placeholder image.")
131
  # 這裡的邏輯需要您自行實現,可以是呼叫一個公開的 API,
132
  # 或者將圖片傳輸到一個運行 IDM-VTON 的伺服器。
133
  # 由於沒有實際的 IDM-VTON 服務,我們使用佔位符圖片來展示功能。
 
139
  text = event.message.text.lower()
140
  reply_token = event.reply_token
141
 
142
+ logging.info(f"Received text message from user {user_id}: '{text}'")
143
+
144
  # 新增 重置 功能
145
+ if text in ["重置", "重來", "重新��始", "再一次"]:
146
  user_states[user_id] = defaultdict(lambda: {"upper_body_images": [], "lower_body_images": [], "current_mode": None, "is_ready_for_outfit": False, "is_ready_for_photo": False, "user_info": {}, "personal_photo": None})[user_id]
147
  line_bot_api.reply_message(
148
  reply_token,
149
+ TextSendMessage(text="狀態已重置。請重新輸入個人資訊,格式為:身高,胸圍,腰圍,臀圍,場合。")
150
  )
151
  return
152
 
 
176
  )
177
  return
178
  except ValueError:
179
+ logging.error(f"Invalid user info format: '{text}'")
180
  line_bot_api.reply_message(
181
  reply_token,
182
  TextSendMessage(text="請依照格式輸入:身高,三圍胸圍,腰圍,臀圍,場合。例如:165,85,65,90,約會")
 
211
  user_id = event.source.user_id
212
  reply_token = event.reply_token
213
 
214
+ logging.info(f"Received image message from user {user_id}")
215
+
216
  # 檢查是否已準備好處理圖片
217
  if not user_states[user_id]["is_ready_for_outfit"]:
218
+ logging.warning("User is not in outfit collection mode.")
219
  line_bot_api.reply_message(
220
  reply_token,
221
  TextSendMessage(text="請先輸入個人資訊,格式為:身高,胸圍,腰圍,臀圍,場合。例如:165,85,65,90,約會")
 
227
  try:
228
  image_id = event.message.id
229
  base64_img = get_base64_image(image_id)
230
+ # 將個人照片上傳到 Imgbb,並儲存 URL
231
+ photo_url = upload_to_imgbb(base64_img)
232
  user_states[user_id]["personal_photo"] = photo_url
233
  user_states[user_id]["is_ready_for_photo"] = True
234
 
 
280
  return
281
 
282
  except Exception as e:
283
+ logging.error(f"Error processing personal photo: {e}")
284
  line_bot_api.reply_message(
285
  reply_token,
286
  TextSendMessage(text=f"圖片處理失敗,請稍後再試。錯誤:{e}")
 
290
  # 處理衣物圖片上傳
291
  mode = user_states[user_id]["current_mode"]
292
  if not mode:
293
+ logging.warning("User is not in a defined upload mode.")
294
  line_bot_api.reply_message(
295
  reply_token,
296
  TextSendMessage(text="請先傳送「上衣」或「褲子」來選擇要上傳的衣服類型。")
 
300
  try:
301
  image_id = event.message.id
302
  base64_img = get_base64_image(image_id)
303
+ # 上傳圖片到 Imgbb,並儲存 URL
304
+ image_url = upload_to_imgbb(base64_img)
305
  if not image_url:
306
+ logging.error("Failed to upload image to Imgbb.")
307
  line_bot_api.reply_message(
308
  reply_token,
309
+ TextSendMessage(text="圖片上傳失敗,請稍後再試。")
310
  )
311
  return
312
 
 
314
  if len(user_states[user_id]["upper_body_images"]) < MAX_IMAGES_PER_TYPE:
315
  user_states[user_id]["upper_body_images"].append(image_url)
316
  else:
317
+ logging.info("Upper body image count is full.")
318
  line_bot_api.reply_message(
319
  reply_token,
320
  TextSendMessage(text="上衣數量已滿,請傳送「褲子」來上傳褲子圖片。")
 
324
  if len(user_states[user_id]["lower_body_images"]) < MAX_IMAGES_PER_TYPE:
325
  user_states[user_id]["lower_body_images"].append(image_url)
326
  else:
327
+ logging.info("Lower body image count is full.")
328
  line_bot_api.reply_message(
329
  reply_token,
330
  TextSendMessage(text="褲子數量已滿,請傳送「上衣」來上傳上衣圖片。")
 
340
  TextSendMessage(text=f"已接收。上衣: {upper_count}/{MAX_IMAGES_PER_TYPE},褲子/裙子: {lower_count}/{MAX_IMAGES_PER_TYPE}。")
341
  )
342
  else:
343
+ logging.info("All outfit images received. Prompting for personal photo.")
344
  line_bot_api.reply_message(
345
  reply_token,
346
  TextSendMessage(text="已收到所有衣物圖片!接下來,請上傳一張您個人的全身照片,以便進行虛擬試穿。")
347
  )
348
 
349
  except Exception as e:
350
+ logging.error(f"Error processing outfit image: {e}")
351
  line_bot_api.reply_message(
352
  reply_token,
353
  TextSendMessage(text=f"圖片處理失敗,請稍後再試。錯誤:{e}")