自动打电话软件设计与实现

文章目录

方案概述

  1. 使用Twilio的API进行电话呼叫
  2. 实现基本的呼叫逻辑
  3. 添加简单的用户界面

实现代码

1. 安装必要的库

bash 复制代码
pip install twilio flask

2. 主程序代码

python 复制代码
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_number
        self.message = message
        self.status = "pending"
        self.call_time = call_time or datetime.now()
        self.call_sid = None
        self.start_time = None
        self.end_time = None
    
    def 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的示例,实际应替换为你的TwiML
                to=self.phone_number,
                from_=TWILIO_PHONE_NUMBER,
                record=True
            )
            
            self.call_sid = call.sid
            logger.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.status
                self.end_time = datetime.now()
                logger.info(f"Call ended with status: {call.status}")
                break
            
            time.sleep(5)

@app.route('/')
def index():
    """显示主界面"""
    return render_template('index.html')

@app.route('/make_call', methods=['POST'])
def make_call():
    """发起呼叫的API接口"""
    data = request.json
    phone_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'}), 404
    
    return 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)

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>

功能说明

  1. 发起呼叫

    • 输入电话号码和可选消息内容
    • 点击按钮发起呼叫
    • 系统会返回任务ID并显示呼叫状态
  2. 状态监控

    • 实时显示呼叫状态(pending/calling/completed/failed等)
    • 显示呼叫开始和结束时间
  3. 呼叫历史

    • 显示最近的20条呼叫记录
    • 包括呼叫状态、时长等信息

部署说明

  1. 注册Twilio账号并获取ACCOUNT_SID和AUTH_TOKEN
  2. 购买Twilio电话号码并替换代码中的TWILIO_PHONE_NUMBER
  3. 创建TwiML应用或使用Twilio Studio定义呼叫流程
  4. 运行Flask应用:python app.py

扩展功能建议

  1. 批量呼叫:添加CSV导入功能,支持批量呼叫
  2. 语音合成:集成TTS服务,动态生成语音内容
  3. 呼叫转移:实现IVR菜单和呼叫转移功能
  4. 数据库集成:使用数据库存储呼叫记录
  5. 认证系统:添加用户认证和权限管理

注意事项

  1. 使用Twilio等服务需要遵守相关法律法规
  2. 自动呼叫系统可能受到不同国家/地区的法律限制
  3. 商业使用需要考虑服务费用和通话质量
  4. 需要处理各种异常情况(网络问题、账户余额不足等)
相关推荐
sunshine64134 分钟前
【CSS】实现文本颜色渐变
css·html·css3
海的诗篇_1 小时前
前端开发面试题总结-vue2框架篇(四)
前端·css·面试·vue·html
大然Ryan1 小时前
MCP实战:从零开始写基于 Python 的 MCP 服务(附源码)
python·llm·mcp
学不好python的小猫1 小时前
7-4 身份证号处理
开发语言·python·算法
木木黄木木2 小时前
HTML5 火焰字体效果教程
前端·html·html5
MAOX7892 小时前
基于python的web系统界面登录
前端·python
一颗红心丶2 小时前
Windows系统上离线部署Python运行飞桨(PaddlePaddle) OCR服务
windows·python·paddlepaddle
我爱音乐yyy2 小时前
pythonday50
pytorch·python·深度学习
white.tie3 小时前
一个手机请求头的随机库
开发语言·javascript·python
Mikhail_G3 小时前
Python初学者入门指南
大数据·运维·开发语言·python·数据分析