DeepLearning101 commited on
Commit
b3670eb
·
verified ·
1 Parent(s): c085d48

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +33 -64
app.py CHANGED
@@ -3,113 +3,82 @@ import os
3
  import pandas as pd
4
  import requests
5
  from supabase import create_client, Client
6
- from datetime import datetime, timedelta, timezone
7
 
8
- # --- 設定 ---
9
  SUPABASE_URL = os.getenv("SUPABASE_URL")
10
  SUPABASE_KEY = os.getenv("SUPABASE_KEY")
11
  GAS_MAIL_URL = os.getenv("GAS_MAIL_URL")
12
- LINE_ACCESS_TOKEN = os.getenv("LINE_ACCESS_TOKEN") # 記得在 Space B Secrets 也要加這個
13
- # ⚠️ 請確認這裡填的是您 Space A 的網址
14
- PUBLIC_SPACE_URL = "https://deeplearning101-ciecietaipei.hf.space"
15
 
16
  supabase: Client = create_client(SUPABASE_URL, SUPABASE_KEY)
17
  ADMIN_USER = os.getenv("ADMIN_USER")
18
  ADMIN_PASSWORD = os.getenv("ADMIN_PASSWORD")
19
 
20
  def get_bookings():
21
- response = supabase.table("bookings").select("*").order("created_at", desc=True).execute()
22
- if not response.data: return pd.DataFrame()
23
- df = pd.DataFrame(response.data)
24
  cols = ['id', 'date', 'time', 'name', 'tel', 'email', 'pax', 'remarks', 'status', 'user_id']
25
- for c in cols:
26
  if c not in df.columns: df[c] = ""
27
  return df[cols]
28
 
29
  def send_confirmation_hybrid(booking_id):
30
- """混合發送:Email (必發) + LINE (如果客人有 ID)"""
31
  try:
32
- if not GAS_MAIL_URL: return "❌ 錯誤:未設定 GAS_MAIL_URL Secret"
33
-
34
- # 1. 抓取資料
35
  res = supabase.table("bookings").select("*").eq("id", booking_id).execute()
36
- if not res.data: return "❌ 找不到該訂單"
37
  booking = res.data[0]
 
 
38
 
39
- email = booking.get('email')
40
- user_id = booking.get('user_id') # 抓取 LINE ID
41
-
42
- # 產生連結
43
  confirm_link = f"{PUBLIC_SPACE_URL}/?id={booking_id}&action=confirm"
44
  cancel_link = f"{PUBLIC_SPACE_URL}/?id={booking_id}&action=cancel"
45
-
46
- log_msg = ""
47
 
48
- # --- A. 發送 Email ---
49
  if email and "@" in email:
50
- html_content = f"""
51
- <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;">
52
- <h2 style="border-bottom: 1px solid #d4af37; padding-bottom: 15px; text-align: center; letter-spacing: 2px;">Cié Cié Taipei</h2>
53
- <p style="font-size: 16px; margin-top: 20px;">{booking['name']} 您好,感謝預約,已為您保留座位:</p>
54
- <div style="background: #222; padding: 15px; border-radius: 8px; margin: 20px 0; border-left: 4px solid #d4af37;">
55
- <ul style="color: #eee; list-style: none; padding: 0; margin: 0; line-height: 1.8;">
56
- <li>📅 {booking['date']}</li>
57
- <li>⏰ {booking['time']}</li>
58
  <li>👥 {booking['pax']} 位</li>
59
  <li>📝 {booking.get('remarks') or '無'}</li>
60
  </ul>
61
  </div>
62
- <div style="text-align: center; margin-top: 20px;">
63
- <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;">✅ 確認出席</a>
64
- <a href="{cancel_link}" style="display: inline-block; border: 1px solid #ff5252; color: #ff5252; padding: 11px 29px; text-decoration: none; border-radius: 5px; font-weight: bold; margin: 0 10px;">🚫 取消訂位</a>
65
  </div>
66
- <hr style="border: 0; border-top: 1px solid #333; margin-top: 30px;">
67
- <p style="color: #666; font-size: 12px; text-align: center;">此郵件為系統自動發送。</p>
68
  </div>
69
  """
70
- requests.post(GAS_MAIL_URL, json={"to": email, "subject": f"[{booking['date']}] Cié Cié Taipei 訂位確認信", "htmlBody": html_content, "name": "Cié Cié Taipei"})
71
- log_msg += f"✅ Email 已發 ({email}) "
72
- else:
73
- log_msg += "⚠️ Email 無效 "
74
-
75
- # --- B. 發送 LINE 通知 (如果有 ID) ---
76
  if user_id and len(str(user_id)) > 10 and LINE_ACCESS_TOKEN:
77
  try:
78
- line_text = f"【訂位確認】{booking['name']} 您好\n我們已為您保留 {booking['date']} {booking['time']} ({booking['pax']}位)。\n\n如需取消,請點擊 Email 中的連結,或直接回覆此訊息。期待您的光臨!"
79
- requests.post(
80
- "https://api.line.me/v2/bot/message/push",
81
- headers={"Authorization": f"Bearer {LINE_ACCESS_TOKEN}", "Content-Type": "application/json"},
82
- json={"to": user_id, "messages": [{"type": "text", "text": line_text}]}
83
- )
84
- log_msg += "| ✅ LINE 通知已發"
85
- except Exception as e:
86
- log_msg += f"| ❌ LINE 失敗: {e}"
87
- else:
88
- log_msg += "| ℹ️ 無 LINE ID (僅發 Email)"
89
 
90
- # 更新狀態
91
  supabase.table("bookings").update({"status": "已發確認信"}).eq("id", booking_id).execute()
92
  return log_msg
93
-
94
- except Exception as e:
95
- return f"❌ 發信失敗: {str(e)}"
96
 
97
- # --- 介面 ---
98
  with gr.Blocks(title="Admin") as demo:
99
  gr.Markdown("# 🍷 訂位管理後台")
100
  refresh_btn = gr.Button("🔄 重新整理")
101
  booking_table = gr.Dataframe(interactive=False)
102
-
103
  with gr.Row():
104
  id_input = gr.Number(label="訂單 ID", precision=0)
105
- action_btn = gr.Button("📧 發送確認信 (自動判斷)", variant="primary")
106
-
107
- log_output = gr.Textbox(label="執行結果")
108
-
109
  refresh_btn.click(get_bookings, outputs=booking_table)
110
  action_btn.click(send_confirmation_hybrid, inputs=id_input, outputs=log_output)
111
 
112
  if __name__ == "__main__":
113
- user = ADMIN_USER if ADMIN_USER else "admin"
114
- pwd = ADMIN_PASSWORD if ADMIN_PASSWORD else "123456"
115
- demo.launch(auth=(user, pwd))
 
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如需取消請直接回覆,或點擊 Email 中的連結。期待您的光臨!"
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
  with gr.Blocks(title="Admin") as demo:
73
  gr.Markdown("# 🍷 訂位管理後台")
74
  refresh_btn = gr.Button("🔄 重新整理")
75
  booking_table = gr.Dataframe(interactive=False)
 
76
  with gr.Row():
77
  id_input = gr.Number(label="訂單 ID", precision=0)
78
+ action_btn = gr.Button("📧 發送確認信 (Hybrid)", variant="primary")
79
+ log_output = gr.Textbox(label="結果")
 
 
80
  refresh_btn.click(get_bookings, outputs=booking_table)
81
  action_btn.click(send_confirmation_hybrid, inputs=id_input, outputs=log_output)
82
 
83
  if __name__ == "__main__":
84
+ demo.launch(auth=(ADMIN_USER or "admin", ADMIN_PASSWORD or "123456"))