DeepLearning101 commited on
Commit
00f4ca2
·
verified ·
1 Parent(s): 913a9b3

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +45 -82
app.py CHANGED
@@ -1,110 +1,73 @@
1
  import gradio as gr
2
  import os
3
  import pandas as pd
4
- import requests # 👈 改用 requests 發送 HTTP 請求
5
  from supabase import create_client, Client
6
- from datetime import datetime, timedelta, timezone
7
 
8
- # 設定台北時區 (雖然這個檔案目前沒用到,但保留著好習慣)
9
- TAIPEI_TZ = timezone(timedelta(hours=8))
10
-
11
- # --- 設定 ---
12
  SUPABASE_URL = os.getenv("SUPABASE_URL")
13
  SUPABASE_KEY = os.getenv("SUPABASE_KEY")
14
- GAS_MAIL_URL = os.getenv("GAS_MAIL_URL") # 👈 讀取 GAS 網址
15
- # ⚠️ 請務必確認這裡填的是您 Space A (前端) 的網址
16
- PUBLIC_SPACE_URL = "https://deeplearning101-ciecietaipei.hf.space"
17
 
18
  supabase: Client = create_client(SUPABASE_URL, SUPABASE_KEY)
19
-
20
- # --- 讀取管理員帳號密碼 (從 Secrets 讀取更安全) ---
21
  ADMIN_USER = os.getenv("ADMIN_USER")
22
  ADMIN_PASSWORD = os.getenv("ADMIN_PASSWORD")
23
 
24
  def get_bookings():
25
- response = supabase.table("bookings").select("*").order("created_at", desc=True).execute()
26
- if not response.data: return pd.DataFrame()
27
- df = pd.DataFrame(response.data)
28
- # 確保欄位順序與存在
29
- cols = ['id', 'date', 'time', 'name', 'tel', 'email', 'pax', 'remarks', 'status']
30
- for c in cols:
31
  if c not in df.columns: df[c] = ""
32
  return df[cols]
33
 
34
- def send_confirmation_email(booking_id):
35
- """透過 Google Apps Script 中繼站發信 (包含 確認 與 取消 雙按鈕)"""
36
  try:
37
- if not GAS_MAIL_URL:
38
- return "❌ 錯誤:未設定 GAS_MAIL_URL Secret"
39
-
40
- # 1. 抓取資料
41
  res = supabase.table("bookings").select("*").eq("id", booking_id).execute()
42
- if not res.data: return "❌ 找不到該訂單"
43
  booking = res.data[0]
 
 
44
 
45
- email = booking.get('email')
46
- if not email or "@" not in email: return "❌ 客人未填寫有效 Email"
47
-
48
- # 2. 產生連結 (⚠️ 修正點:補回取消連結)
49
  confirm_link = f"{PUBLIC_SPACE_URL}/?id={booking_id}&action=confirm"
50
  cancel_link = f"{PUBLIC_SPACE_URL}/?id={booking_id}&action=cancel"
51
 
52
- # 3. 準備信件內容 (HTML 包含雙按鈕)
53
- html_content = f"""
54
- <div style="padding: 20px; background: #111; color: #d4af37; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; border-radius: 10px; max-width: 600px; margin: 0 auto;">
55
- <h2 style="border-bottom: 1px solid #d4af37; padding-bottom: 15px; text-align: center; letter-spacing: 2px;">Cié Cié Taipei</h2>
56
-
57
- <p style="font-size: 16px; margin-top: 20px;">{booking['name']} 您好,</p>
58
- <p style="font-size: 16px; color: #ccc;">感謝您的預約,我們已為您保留座位:</p>
59
-
60
- <div style="background: #222; padding: 15px; border-radius: 8px; margin: 20px 0; border-left: 4px solid #d4af37;">
61
- <ul style="color: #eee; list-style: none; padding: 0; margin: 0; line-height: 1.8;">
62
- <li>📅 <b>日期:</b> {booking['date']}</li>
63
- <li>⏰ <b>時間:</b> {booking['time']}</li>
64
- <li>👥 <b>人數:</b> {booking['pax']} 位</li>
65
- <li>📝 <b>備註:</b> {booking.get('remarks') or '無'}</li>
66
- </ul>
 
 
67
  </div>
68
-
69
- <p style="text-align: center; margin-top: 30px; font-weight: bold; color: #fff;">請選擇您的回覆:</p>
70
-
71
- <div style="text-align: center; margin-top: 15px; margin-bottom: 30px;">
72
- <a href="{confirm_link}" style="display: inline-block; background: #d4af37; color: #000; padding: 12px 30px; text-decoration: none; border-radius: 5px; font-weight: bold; margin: 0 10px;">
73
- ✅ 確認出席
74
- </a>
75
-
76
- <a href="{cancel_link}" style="display: inline-block; background: transparent; color: #ff5252; padding: 11px 29px; text-decoration: none; border-radius: 5px; font-weight: bold; border: 1px solid #ff5252; margin: 0 10px;">
77
- 🚫 取消訂位
78
- </a>
79
- </div>
80
-
81
- <hr style="border: 0; border-top: 1px solid #333;">
82
- <p style="color: #666; font-size: 12px; text-align: center; margin-top: 20px;">
83
- 此郵件為系統自動發送。<br>
84
- 如需人工服務,請致電 02-2709-3446
85
- </p>
86
- </div>
87
- """
88
-
89
- # 4. 呼叫 GAS 發信
90
- payload = {
91
- "to": email,
92
- "subject": f"[{booking['date']}] Cié Cié Taipei 訂位確認信",
93
- "htmlBody": html_content,
94
- "name": "Cié Cié Taipei"
95
- }
96
-
97
- response = requests.post(GAS_MAIL_URL, json=payload)
98
-
99
- if response.status_code == 200:
100
- # 5. 更新狀態
101
- supabase.table("bookings").update({"status": "已發確認信"}).eq("id", booking_id).execute()
102
- return f"✅ 已透過 GAS 發送確認信給 {booking['name']} ({email})"
103
- else:
104
- return f"❌ GAS 回傳錯誤: {response.text}"
105
 
106
- except Exception as e:
107
- return f"❌ 發信失敗: {str(e)}"
 
 
 
 
 
 
 
 
 
 
108
 
109
  # --- 介面 ---
110
  with gr.Blocks(title="Admin") as demo:
 
1
  import gradio as gr
2
  import os
3
  import pandas as pd
4
+ import requests
5
  from supabase import create_client, Client
 
6
 
 
 
 
 
7
  SUPABASE_URL = os.getenv("SUPABASE_URL")
8
  SUPABASE_KEY = os.getenv("SUPABASE_KEY")
9
+ GAS_MAIL_URL = os.getenv("GAS_MAIL_URL")
10
+ LINE_ACCESS_TOKEN = os.getenv("LINE_ACCESS_TOKEN")
11
+ PUBLIC_SPACE_URL = "https://deeplearning101-ciecietaipei.hf.space" # 請確認此網址正確
12
 
13
  supabase: Client = create_client(SUPABASE_URL, SUPABASE_KEY)
 
 
14
  ADMIN_USER = os.getenv("ADMIN_USER")
15
  ADMIN_PASSWORD = os.getenv("ADMIN_PASSWORD")
16
 
17
  def get_bookings():
18
+ res = supabase.table("bookings").select("*").order("created_at", desc=True).execute()
19
+ if not res.data: return pd.DataFrame()
20
+ df = pd.DataFrame(res.data)
21
+ cols = ['id', 'date', 'time', 'name', 'tel', 'email', 'pax', 'remarks', 'status', 'user_id']
22
+ for c in cols:
 
23
  if c not in df.columns: df[c] = ""
24
  return df[cols]
25
 
26
+ def send_confirmation_hybrid(booking_id):
 
27
  try:
 
 
 
 
28
  res = supabase.table("bookings").select("*").eq("id", booking_id).execute()
29
+ if not res.data: return "❌ 找不到訂單"
30
  booking = res.data[0]
31
+ email, user_id = booking.get('email'), booking.get('user_id')
32
+ log_msg = ""
33
 
 
 
 
 
34
  confirm_link = f"{PUBLIC_SPACE_URL}/?id={booking_id}&action=confirm"
35
  cancel_link = f"{PUBLIC_SPACE_URL}/?id={booking_id}&action=cancel"
36
 
37
+ # 1. Email 發送
38
+ if email and "@" in email:
39
+ html = f"""
40
+ <div style="padding:20px; background:#111; color:#d4af37; border-radius:10px; max-width:600px; margin:0 auto; font-family:sans-serif;">
41
+ <h2 style="border-bottom:1px solid #d4af37; padding-bottom:15px; text-align:center;">Cié Cié Taipei</h2>
42
+ <p>{booking['name']} 您好,已為您保留座位:</p>
43
+ <div style="background:#222; padding:15px; border-radius:8px;">
44
+ <ul style="color:#eee; list-style:none; padding:0; margin:0; line-height:1.8;">
45
+ <li>📅 {booking['date']} | {booking['time']}</li>
46
+ <li>👥 {booking['pax']} 位</li>
47
+ <li>📝 {booking.get('remarks') or '無'}</li>
48
+ </ul>
49
+ </div>
50
+ <div style="text-align:center; margin-top:25px;">
51
+ <a href="{confirm_link}" style="background:#d4af37; color:#000; padding:12px 25px; text-decoration:none; border-radius:5px; margin:0 10px; font-weight:bold;">✅ 確認出���</a>
52
+ <a href="{cancel_link}" style="border:1px solid #ff5252; color:#ff5252; padding:11px 24px; text-decoration:none; border-radius:5px; margin:0 10px; font-weight:bold;">🚫 取消</a>
53
+ </div>
54
  </div>
55
+ """
56
+ requests.post(GAS_MAIL_URL, json={"to": email, "subject": f"[{booking['date']}] 訂位確認", "htmlBody": html, "name": "Cié Cié Taipei"})
57
+ log_msg += f"✅ Email ok "
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
58
 
59
+ # 2. LINE 發送
60
+ if user_id and len(str(user_id)) > 10 and LINE_ACCESS_TOKEN:
61
+ try:
62
+ line_msg = f"【訂位確認】{booking['name']} 您好\n已為您保留 {booking['date']} {booking['time']} ({booking['pax']}位)。\n\n如需取消請直接回覆,期待您的光臨!"
63
+ requests.post("https://api.line.me/v2/bot/message/push", headers={"Authorization": f"Bearer {LINE_ACCESS_TOKEN}", "Content-Type": "application/json"}, json={"to": user_id, "messages": [{"type": "text", "text": line_msg}]})
64
+ log_msg += "| ✅ LINE ok"
65
+ except: log_msg += "| ❌ LINE fail"
66
+ else: log_msg += "| ℹ️ No LINE ID"
67
+
68
+ supabase.table("bookings").update({"status": "已發確認信"}).eq("id", booking_id).execute()
69
+ return log_msg
70
+ except Exception as e: return f"❌ Error: {str(e)}"
71
 
72
  # --- 介面 ---
73
  with gr.Blocks(title="Admin") as demo: