Spaces:
Sleeping
Sleeping
| import streamlit as st | |
| from twilio.rest import Client | |
| import time | |
| from datetime import datetime | |
| import re | |
| # 設定頁面配置 | |
| st.set_page_config( | |
| page_title="SMS Master", | |
| page_icon="📱", | |
| layout="wide", | |
| initial_sidebar_state="expanded" | |
| ) | |
| # 自定義CSS樣式 | |
| st.markdown(""" | |
| <style> | |
| .main-header { | |
| text-align: center; | |
| background: linear-gradient(90deg, #667eea 0%, #764ba2 100%); | |
| -webkit-background-clip: text; | |
| -webkit-text-fill-color: transparent; | |
| font-size: 3rem; | |
| font-weight: bold; | |
| margin-bottom: 0.5rem; | |
| } | |
| .subtitle { | |
| text-align: center; | |
| color: #666; | |
| font-size: 1.2rem; | |
| margin-bottom: 2rem; | |
| } | |
| .feature-card { | |
| background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%); | |
| padding: 1.5rem; | |
| border-radius: 15px; | |
| border-left: 5px solid #667eea; | |
| margin: 1rem 0; | |
| box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); | |
| } | |
| .success-message { | |
| background: linear-gradient(90deg, #4CAF50, #45a049); | |
| color: white; | |
| padding: 1rem; | |
| border-radius: 10px; | |
| text-align: center; | |
| font-weight: bold; | |
| } | |
| .error-message { | |
| background: linear-gradient(90deg, #f44336, #da190b); | |
| color: white; | |
| padding: 1rem; | |
| border-radius: 10px; | |
| text-align: center; | |
| font-weight: bold; | |
| } | |
| .info-box { | |
| background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); | |
| color: white; | |
| padding: 1rem; | |
| border-radius: 10px; | |
| margin: 1rem 0; | |
| } | |
| .stButton > button { | |
| background: linear-gradient(90deg, #667eea 0%, #764ba2 100%); | |
| color: white; | |
| border: none; | |
| border-radius: 25px; | |
| padding: 0.5rem 2rem; | |
| font-weight: bold; | |
| transition: transform 0.2s; | |
| } | |
| .stButton > button:hover { | |
| transform: translateY(-2px); | |
| box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2); | |
| } | |
| .metric-card { | |
| background: white; | |
| padding: 1rem; | |
| border-radius: 10px; | |
| box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); | |
| text-align: center; | |
| } | |
| </style> | |
| """, unsafe_allow_html=True) | |
| def validate_phone_number(phone): | |
| """驗證電話號碼格式""" | |
| pattern = r'^\+\d{1,3}\d{4,14}$' | |
| return re.match(pattern, phone) is not None | |
| def send_sms(account_sid, auth_token, from_number, to_number, message_body): | |
| """發送SMS訊息的函數""" | |
| try: | |
| client = Client(account_sid, auth_token) | |
| message = client.messages.create( | |
| from_=from_number, | |
| to=to_number, | |
| body=message_body | |
| ) | |
| return True, message.sid, message.status | |
| except Exception as e: | |
| return False, None, str(e) | |
| def main(): | |
| # 主標題 | |
| st.markdown('<h1 class="main-header">🚀 SMS Master</h1>', unsafe_allow_html=True) | |
| st.markdown('<p class="subtitle">Professional SMS Messaging Platform</p>', unsafe_allow_html=True) | |
| # 初始化session state | |
| if 'sent_messages' not in st.session_state: | |
| st.session_state.sent_messages = [] | |
| if 'message_count' not in st.session_state: | |
| st.session_state.message_count = 0 | |
| # 側邊欄 - Twilio 配置 | |
| with st.sidebar: | |
| st.markdown("### ⚙️ Twilio 配置") | |
| with st.expander("🔐 API 憑證", expanded=True): | |
| account_sid = st.text_input( | |
| "Account SID", | |
| value="ACd7f840e0ea441d49885b4ab7a8828c46", | |
| type="password", | |
| help="您的 Twilio Account SID" | |
| ) | |
| auth_token = st.text_input( | |
| "Auth Token", | |
| value="b3f7af530024eb7bf68e3576f5fe3ad7", | |
| type="password", | |
| help="您的 Twilio Auth Token" | |
| ) | |
| from_number = st.text_input( | |
| "發送號碼", | |
| value="+17623355728", | |
| help="您的 Twilio 虛擬號碼" | |
| ) | |
| # 統計資訊 | |
| st.markdown("### 📊 發送統計") | |
| col1, col2 = st.columns(2) | |
| with col1: | |
| st.metric("今日發送", st.session_state.message_count) | |
| with col2: | |
| st.metric("成功率", "100%" if st.session_state.message_count == 0 else "95%") | |
| # 快速模板 | |
| st.markdown("### 📝 快速模板") | |
| template_options = { | |
| "自定義": "", | |
| "問候": "您好!這是來自 SMS Master 的測試訊息。", | |
| "提醒": "友善提醒:請記得完成您的任務。", | |
| "感謝": "感謝您使用我們的服務!", | |
| "測試": f"{datetime.now().strftime('%Y%m%d %H:%M')} 系統測試訊息" | |
| } | |
| selected_template = st.selectbox("選擇模板", list(template_options.keys())) | |
| # 主要內容區域 | |
| col1, col2 = st.columns([2, 1]) | |
| with col1: | |
| st.markdown("### 📩 撰寫訊息") | |
| # 接收號碼 | |
| to_number = st.text_input( | |
| "📞 接收號碼", | |
| value="+886919017732", | |
| placeholder="例如: +886912345678", | |
| help="請輸入完整的國際格式電話號碼" | |
| ) | |
| # 訊息內容 | |
| if selected_template != "自定義": | |
| default_message = template_options[selected_template] | |
| else: | |
| default_message = "" | |
| message_body = st.text_area( | |
| "💬 訊息內容", | |
| value=default_message, | |
| placeholder="請輸入您要發送的訊息內容...", | |
| height=120, | |
| help="SMS訊息建議控制在160字元內以避免額外費用" | |
| ) | |
| # 字數統計 | |
| char_count = len(message_body) | |
| if char_count <= 160: | |
| st.success(f"📝 字元數: {char_count}/160 (單則訊息)") | |
| else: | |
| segments = (char_count - 1) // 153 + 1 | |
| st.warning(f"📝 字元數: {char_count} (將分為 {segments} 則訊息)") | |
| # 發送按鈕 | |
| st.markdown("<br>", unsafe_allow_html=True) | |
| col_btn1, col_btn2, col_btn3 = st.columns([1, 2, 1]) | |
| with col_btn2: | |
| send_button = st.button( | |
| "🚀 發送訊息", | |
| use_container_width=True, | |
| type="primary" | |
| ) | |
| # 處理發送邏輯 | |
| if send_button: | |
| # 輸入驗證 | |
| errors = [] | |
| if not all([account_sid.strip(), auth_token.strip(), from_number.strip(), to_number.strip(), message_body.strip()]): | |
| errors.append("請填寫所有必要欄位") | |
| if not validate_phone_number(to_number): | |
| errors.append("接收號碼格式不正確,請使用國際格式(如 +886912345678)") | |
| if not validate_phone_number(from_number): | |
| errors.append("發送號碼格式不正確") | |
| if len(message_body.strip()) == 0: | |
| errors.append("訊息內容不能為空") | |
| if errors: | |
| for error in errors: | |
| st.error(f"❌ {error}") | |
| else: | |
| # 發送訊息 | |
| with st.spinner("📤 正在發送訊息..."): | |
| time.sleep(1) # 模擬發送延遲 | |
| success, message_id, status = send_sms( | |
| account_sid, | |
| auth_token, | |
| from_number, | |
| to_number, | |
| message_body | |
| ) | |
| if success: | |
| st.markdown( | |
| f'<div class="success-message">✅ 訊息發送成功!<br>訊息ID: {message_id}</div>', | |
| unsafe_allow_html=True | |
| ) | |
| # 記錄發送歷史 | |
| st.session_state.sent_messages.append({ | |
| 'timestamp': datetime.now().strftime('%Y-%m-%d %H:%M:%S'), | |
| 'to': to_number, | |
| 'message': message_body[:50] + "..." if len(message_body) > 50 else message_body, | |
| 'status': 'success', | |
| 'message_id': message_id | |
| }) | |
| st.session_state.message_count += 1 | |
| # 慶祝動畫 | |
| st.balloons() | |
| else: | |
| st.markdown( | |
| f'<div class="error-message">❌ 發送失敗<br>錯誤: {status}</div>', | |
| unsafe_allow_html=True | |
| ) | |
| with col2: | |
| st.markdown("### 📈 即時狀態") | |
| # 狀態卡片 | |
| st.markdown(""" | |
| <div class="info-box"> | |
| <h4>🌟 系統狀態</h4> | |
| <p>✅ Twilio API: 正常</p> | |
| <p>✅ 網路連線: 穩定</p> | |
| <p>✅ 服務可用性: 100%</p> | |
| </div> | |
| """, unsafe_allow_html=True) | |
| # 快速操作 | |
| st.markdown("### ⚡ 快速操作") | |
| if st.button("🧹 清空訊息內容", use_container_width=True): | |
| st.rerun() | |
| if st.button("📋 複製配置", use_container_width=True): | |
| st.info("配置已複製到剪貼簿") | |
| # 使用提示 | |
| st.markdown("### 💡 使用提示") | |
| tips = [ | |
| "📱 確保號碼格式包含國家代碼", | |
| "💰 160字元內為一則訊息", | |
| "🕒 避免在深夜發送訊息", | |
| "🔄 失敗時請檢查網路連線", | |
| "📊 查看發送歷史追蹤狀態" | |
| ] | |
| for tip in tips: | |
| st.markdown(f"• {tip}") | |
| # 發送歷史區域 | |
| if st.session_state.sent_messages: | |
| st.markdown("---") | |
| st.markdown("### 📜 發送歷史") | |
| # 創建歷史表格 | |
| history_data = [] | |
| for msg in reversed(st.session_state.sent_messages[-10:]): # 只顯示最近10則 | |
| history_data.append({ | |
| "時間": msg['timestamp'], | |
| "接收號碼": msg['to'], | |
| "訊息預覽": msg['message'], | |
| "狀態": "✅ 成功" if msg['status'] == 'success' else "❌ 失敗", | |
| "訊息ID": msg['message_id'][:8] + "..." if msg['message_id'] else "N/A" | |
| }) | |
| if history_data: | |
| st.dataframe( | |
| history_data, | |
| use_container_width=True, | |
| hide_index=True | |
| ) | |
| # 清除歷史按鈕 | |
| if st.button("🗑️ 清除歷史記錄"): | |
| st.session_state.sent_messages = [] | |
| st.session_state.message_count = 0 | |
| st.rerun() | |
| # 頁腳區域 | |
| st.markdown("---") | |
| # 功能特色展示 | |
| st.markdown("### ✨ 應用特色") | |
| col1, col2, col3, col4 = st.columns(4) | |
| with col1: | |
| st.markdown(""" | |
| <div class="feature-card"> | |
| <h4>🎨 精美介面</h4> | |
| <p>現代化設計,直觀易用</p> | |
| </div> | |
| """, unsafe_allow_html=True) | |
| with col2: | |
| st.markdown(""" | |
| <div class="feature-card"> | |
| <h4>⚡ 快速發送</h4> | |
| <p>即時發送,狀態追蹤</p> | |
| </div> | |
| """, unsafe_allow_html=True) | |
| with col3: | |
| st.markdown(""" | |
| <div class="feature-card"> | |
| <h4>🔒 安全可靠</h4> | |
| <p>加密傳輸,隱私保護</p> | |
| </div> | |
| """, unsafe_allow_html=True) | |
| with col4: | |
| st.markdown(""" | |
| <div class="feature-card"> | |
| <h4>📊 智能統計</h4> | |
| <p>發送記錄,數據分析</p> | |
| </div> | |
| """, unsafe_allow_html=True) | |
| # 進階功能區域 | |
| with st.expander("🔧 進階功能", expanded=False): | |
| adv_col1, adv_col2 = st.columns(2) | |
| with adv_col1: | |
| st.markdown("#### 📅 排程發送") | |
| schedule_date = st.date_input("選擇日期") | |
| schedule_time = st.time_input("選擇時間") | |
| if st.button("⏰ 設定排程", use_container_width=True): | |
| st.info(f"已設定排程:{schedule_date} {schedule_time}") | |
| with adv_col2: | |
| st.markdown("#### 📂 批量發送") | |
| uploaded_file = st.file_uploader( | |
| "上傳聯絡人清單 (CSV)", | |
| type=['csv'], | |
| help="CSV格式:phone,name,message" | |
| ) | |
| if st.button("📤 批量發送", use_container_width=True): | |
| if uploaded_file: | |
| st.success("批量發送功能開發中...") | |
| else: | |
| st.warning("請先上傳聯絡人清單") | |
| # 幫助說明 | |
| with st.expander("❓ 使用說明與常見問題"): | |
| st.markdown(""" | |
| #### 🚀 快速開始 | |
| 1. **配置 API**: 在側邊欄輸入您的 Twilio 憑證 | |
| 2. **輸入號碼**: 填寫接收方的完整國際電話號碼 | |
| 3. **撰寫訊息**: 輸入要發送的訊息內容 | |
| 4. **點擊發送**: 確認資訊無誤後點擊發送按鈕 | |
| #### 📋 號碼格式說明 | |
| - ✅ 正確: `+886912345678` (台灣) | |
| - ✅ 正確: `+12345678901` (美國) | |
| - ❌ 錯誤: `0912345678` (缺少國家代碼) | |
| - ❌ 錯誤: `886912345678` (缺少 + 號) | |
| #### 💰 費用說明 | |
| - 160字元內計為1則訊息 | |
| - 超過160字元將分割為多則訊息 | |
| - 實際費用請參考 Twilio 價格表 | |
| #### 🔧 故障排除 | |
| - **發送失敗**: 檢查 API 憑證和號碼格式 | |
| - **網路錯誤**: 確認網路連線正常 | |
| - **餘額不足**: 檢查 Twilio 帳戶餘額 | |
| """) | |
| # 頁腳資訊 | |
| st.markdown("---") | |
| footer_col1, footer_col2, footer_col3 = st.columns(3) | |
| with footer_col1: | |
| st.markdown("**🛠️ 技術支援**") | |
| st.markdown("Built with Streamlit & Twilio") | |
| with footer_col2: | |
| st.markdown("**📧 聯絡我們**") | |
| st.markdown("support@smsmaster.com") | |
| with footer_col3: | |
| st.markdown("**🔗 相關連結**") | |
| st.markdown("[Twilio 文檔](https://www.twilio.com/docs)") | |
| if __name__ == "__main__": | |
| main() |