文章目錄
- 方案概述
- 實現代碼
- 1. 安裝必要的庫
- 2. 主程序代碼
- 3. HTML模板 (templates/index.html)
- 功能說明
- 部署說明
- 擴展功能建議
- 注意事項

方案概述
- 使用Twilio的API進行電話呼叫
- 實現基本的呼叫邏輯
- 添加簡單的用戶界面
實現代碼
1. 安裝必要的庫
pip install twilio flask
2. 主程序代碼
from twilio.rest import Client
from flask import Flask, request, jsonify, render_template
import time
import threading
import logging
from datetime import datetime# 配置日志
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)# Twilio賬戶信息 - 請替換為你的實際賬戶信息
ACCOUNT_SID = 'your_account_sid'
AUTH_TOKEN = 'your_auth_token'
TWILIO_PHONE_NUMBER = '+1234567890' # 你的Twilio電話號碼# 初始化Twilio客戶端
client = Client(ACCOUNT_SID, AUTH_TOKEN)app = Flask(__name__)# 存儲呼叫任務
call_tasks = {}class CallTask:def __init__(self, phone_number, message, call_time=None):self.phone_number = phone_numberself.message = messageself.status = "pending"self.call_time = call_time or datetime.now()self.call_sid = Noneself.start_time = Noneself.end_time = Nonedef start_call(self):try:self.status = "calling"self.start_time = datetime.now()# 使用Twilio發起呼叫call = client.calls.create(url='http://demo.twilio.com/docs/voice.xml', # 這里使用Twilio的示例,實際應替換為你的TwiMLto=self.phone_number,from_=TWILIO_PHONE_NUMBER,record=True)self.call_sid = call.sidlogger.info(f"Call started to {self.phone_number}, SID: {self.call_sid}")# 檢查呼叫狀態self.monitor_call()except Exception as e:self.status = "failed"logger.error(f"Failed to start call: {str(e)}")def monitor_call):"""監控呼叫狀態"""while True:call = client.calls(self.call_sid).fetch()if call.status in ['completed', 'failed', 'busy', 'no-answer']:self.status = call.statusself.end_time = datetime.now()logger.info(f"Call ended with status: {call.status}")breaktime.sleep(5)@app.route('/')
def index():"""顯示主界面"""return render_template('index.html')@app.route('/make_call', methods=['POST'])
def make_call():"""發起呼叫的API接口"""data = request.jsonphone_number = data.get('phone_number')message = data.get('message', '')if not phone_number:return jsonify({'error': 'Phone number is required'}), 400# 創建呼叫任務task_id = str(int(time.time()))call_task = CallTask(phone_number, message)call_tasks[task_id] = call_task# 在新線程中啟動呼叫threading.Thread(target=call_task.start_call).start()return jsonify({'task_id': task_id,'status': 'queued','phone_number': phone_number})@app.route('/call_status/<task_id>')
def call_status(task_id):"""獲取呼叫狀態"""call_task = call_tasks.get(task_id)if not call_task:return jsonify({'error': 'Task not found'}), 404return jsonify({'task_id': task_id,'status': call_task.status,'phone_number': call_task.phone_number,'start_time': str(call_task.start_time) if call_task.start_time else None,'end_time': str(call_task.end_time) if call_task.end_time else None,'call_sid': call_task.call_sid})@app.route('/call_history')
def call_history():"""獲取呼叫歷史"""calls = client.calls.list(limit=20)call_history = []for call in calls:call_history.append({'sid': call.sid,'from': call.from_formatted,'to': call.to_formatted,'status': call.status,'start_time': str(call.start_time),'duration': call.duration})return jsonify(call_history)if __name__ == '__main__':app.run(debug=True)
3. HTML模板 (templates/index.html)
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>自動電話呼叫系統</title><style>body {font-family: Arial, sans-serif;max-width: 800px;margin: 0 auto;padding: 20px;}.form-group {margin-bottom: 15px;}label {display: block;margin-bottom: 5px;font-weight: bold;}input, textarea {width: 100%;padding: 8px;box-sizing: border-box;}button {background-color: #4CAF50;color: white;padding: 10px 15px;border: none;cursor: pointer;}button:hover {background-color: #45a049;}#status {margin-top: 20px;padding: 10px;border: 1px solid #ddd;}.call-item {border-bottom: 1px solid #eee;padding: 10px 0;}</style>
</head>
<body><h1>自動電話呼叫系統</h1><div class="form-group"><label for="phone_number">電話號碼:</label><input type="text" id="phone_number" placeholder="輸入電話號碼,包括國家代碼,例如 +8613800138000"></div><div class="form-group"><label for="message">消息內容 (可選):</label><textarea id="message" rows="4" placeholder="輸入要播放的消息內容"></textarea></div><button id="call_button">發起呼叫</button><div id="status"></div><h2>呼叫歷史</h2><div id="call_history"></div><script>document.getElementById('call_button').addEventListener('click', async () => {const phoneNumber = document.getElementById('phone_number').value;const message = document.getElementById('message').value;if (!phoneNumber) {alert('請輸入電話號碼');return;}const statusDiv = document.getElementById('status');statusDiv.innerHTML = '正在發起呼叫...';try {const response = await fetch('/make_call', {method: 'POST',headers: {'Content-Type': 'application/json',},body: JSON.stringify({phone_number: phoneNumber,message: message})});const data = await response.json();if (response.ok) {statusDiv.innerHTML = `呼叫已排隊,任務ID: ${data.task_id}`;// 輪詢檢查呼叫狀態checkCallStatus(data.task_id);} else {statusDiv.innerHTML = `錯誤: ${data.error}`;}} catch (error) {statusDiv.innerHTML = `請求失敗: ${error.message}`;}});async function checkCallStatus(taskId) {const statusDiv = document.getElementById('status');try {const response = await fetch(`/call_status/${taskId}`);const data = await response.json();if (response.ok) {statusDiv.innerHTML = `呼叫狀態: ${data.status}<br>電話號碼: ${data.phone_number}<br>開始時間: ${data.start_time || '未開始'}<br>結束時間: ${data.end_time || '未結束'}`;// 如果呼叫未完成,繼續輪詢if (data.status === 'pending' || data.status === 'calling') {setTimeout(() => checkCallStatus(taskId), 2000);}} else {statusDiv.innerHTML += `<br>獲取狀態失敗: ${data.error}`;}} catch (error) {statusDiv.innerHTML += `<br>獲取狀態請求失敗: ${error.message}`;}}// 加載呼叫歷史async function loadCallHistory() {const historyDiv = document.getElementById('call_history');try {const response = await fetch('/call_history');const data = await response.json();if (response.ok) {if (data.length === 0) {historyDiv.innerHTML = '<p>沒有呼叫記錄</p>';return;}let html = '';data.forEach(call => {html += `<div class="call-item"><strong>${call.from} → ${call.to}</strong><br>狀態: ${call.status}<br>時間: ${call.start_time}<br>時長: ${call.duration}秒</div>`;});historyDiv.innerHTML = html;} else {historyDiv.innerHTML = '<p>加載歷史記錄失敗</p>';}} catch (error) {historyDiv.innerHTML = '<p>加載歷史記錄請求失敗</p>';}}// 頁面加載時獲取呼叫歷史window.addEventListener('load', loadCallHistory);</script>
</body>
</html>
功能說明
-
發起呼叫:
- 輸入電話號碼和可選消息內容
- 點擊按鈕發起呼叫
- 系統會返回任務ID并顯示呼叫狀態
-
狀態監控:
- 實時顯示呼叫狀態(pending/calling/completed/failed等)
- 顯示呼叫開始和結束時間
-
呼叫歷史:
- 顯示最近的20條呼叫記錄
- 包括呼叫狀態、時長等信息
部署說明
- 注冊Twilio賬號并獲取ACCOUNT_SID和AUTH_TOKEN
- 購買Twilio電話號碼并替換代碼中的TWILIO_PHONE_NUMBER
- 創建TwiML應用或使用Twilio Studio定義呼叫流程
- 運行Flask應用:
python app.py
擴展功能建議
- 批量呼叫:添加CSV導入功能,支持批量呼叫
- 語音合成:集成TTS服務,動態生成語音內容
- 呼叫轉移:實現IVR菜單和呼叫轉移功能
- 數據庫集成:使用數據庫存儲呼叫記錄
- 認證系統:添加用戶認證和權限管理
注意事項
- 使用Twilio等服務需要遵守相關法律法規
- 自動呼叫系統可能受到不同國家/地區的法律限制
- 商業使用需要考慮服務費用和通話質量
- 需要處理各種異常情況(網絡問題、賬戶余額不足等)