-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathserver.py
More file actions
112 lines (81 loc) · 3.23 KB
/
Copy pathserver.py
File metadata and controls
112 lines (81 loc) · 3.23 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
#
# Copyright (c) 2025, Daily
#
# SPDX-License-Identifier: BSD 2-Clause License
#
"""server.py
Webhook server to handle outbound call requests, initiate calls via Twilio API,
and handle subsequent WebSocket connections for Media Streams.
"""
import os
import uvicorn
from dotenv import load_dotenv
from fastapi import FastAPI, Request, WebSocket
from fastapi.responses import HTMLResponse, JSONResponse
from loguru import logger
from server_utils import (
DialoutResponse,
dialout_request_from_request,
generate_twiml,
make_twilio_call,
parse_twiml_request,
)
load_dotenv(override=True)
app = FastAPI()
@app.post("/dialout", response_model=DialoutResponse)
async def handle_dialout_request(request: Request) -> DialoutResponse:
"""Handle outbound call request and initiate call via Twilio.
Args:
request (Request): FastAPI request containing JSON with 'to_number' and 'from_number'.
Returns:
DialoutResponse: Response containing call_sid, status, and to_number.
Raises:
HTTPException: If request data is invalid or missing required fields.
"""
logger.info("Received outbound call request")
dialout_request = await dialout_request_from_request(request)
call_result = await make_twilio_call(dialout_request)
return DialoutResponse(
call_sid=call_result.call_sid,
status="call_initiated",
to_number=call_result.to_number,
)
@app.post("/twiml")
async def get_twiml(request: Request) -> HTMLResponse:
"""Return TwiML instructions for connecting call to WebSocket.
This endpoint is called by Twilio when a call is initiated. It returns TwiML
that instructs Twilio to connect the call to our WebSocket endpoint with
stream parameters containing call metadata.
Args:
request (Request): FastAPI request containing Twilio form data with 'To' and 'From'.
Returns:
HTMLResponse: TwiML XML response with Stream connection instructions.
"""
logger.info("Serving TwiML for outbound call")
twiml_request = await parse_twiml_request(request)
twiml_content = generate_twiml(twiml_request)
return HTMLResponse(content=twiml_content, media_type="application/xml")
@app.websocket("/ws")
async def websocket_endpoint(websocket: WebSocket):
"""Handle WebSocket connection from Twilio Media Streams.
This endpoint receives the WebSocket connection from Twilio's Media Streams
and runs the bot to handle the voice conversation. Stream parameters passed
from TwiML are available to the bot for customization.
Args:
websocket (WebSocket): FastAPI WebSocket connection from Twilio.
"""
from bot import bot
from pipecat.runner.types import WebSocketRunnerArguments
await websocket.accept()
logger.info("WebSocket connection accepted for outbound call")
try:
runner_args = WebSocketRunnerArguments(websocket=websocket)
await bot(runner_args)
except Exception as e:
logger.error(f"Error in WebSocket endpoint: {e}")
await websocket.close()
if __name__ == "__main__":
# Run the server
port = int(os.getenv("PORT", "7860"))
logger.info(f"Starting Twilio outbound chatbot server on port {port}")
uvicorn.run(app, host="0.0.0.0", port=port)