Skip to content
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions phone-chatbot/daily-twilio-sip-dial-in/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,7 @@ class AgentRequest(BaseModel):
token: str
call_sid: str
sip_uri: str
to_phone: str
# Add your custom fields here
customer_name: str | None = None
account_id: str | None = None
Expand All @@ -191,6 +192,7 @@ agent_request = AgentRequest(
token=sip_config.token,
call_sid=call_data.call_sid,
sip_uri=sip_config.sip_endpoint,
to_phone=call_data.to_phone,
customer_name=customer_info.name,
account_id=customer_info.id,
)
Expand Down
11 changes: 10 additions & 1 deletion phone-chatbot/daily-twilio-sip-dial-in/bot.py
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,16 @@ async def on_dialin_ready(transport, sip_endpoint):
logger.info(f"Forwarding call {request.call_sid} to {request.sip_uri}")

try:
twilio_client = Client(os.getenv("TWILIO_ACCOUNT_SID"), os.getenv("TWILIO_AUTH_TOKEN"))
# Use to_phone to select Twilio credentials if you have multiple
# accounts (e.g., US vs EU). For now, we use a single set of credentials.
to_phone = request.to_phone
logger.info(f"Dialing in to {to_phone}")

account_sid = os.getenv("TWILIO_ACCOUNT_SID")
auth_token = os.getenv("TWILIO_AUTH_TOKEN")
logger.info(f"Using Twilio credentials for {to_phone}")

twilio_client = Client(account_sid, auth_token)

# Update the Twilio call with TwiML to forward to the Daily SIP endpoint
twilio_client.calls(request.call_sid).update(
Expand Down
5 changes: 4 additions & 1 deletion phone-chatbot/daily-twilio-sip-dial-in/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,9 @@ async def handle_call(request: Request):

call_data = await twilio_call_data_from_request(request)

sip_config = await create_daily_room(call_data, request.app.state.http_session)
sip_config = await create_daily_room(
call_data, request.app.state.http_session, sip_provider="daily"
)

# Make sure we have a SIP endpoint.
if not sip_config.sip_endpoint:
Expand All @@ -89,6 +91,7 @@ async def handle_call(request: Request):
token=sip_config.token,
call_sid=call_data.call_sid,
sip_uri=sip_config.sip_endpoint,
to_phone=call_data.to_phone,
)

# Start bot locally or in production.
Expand Down
26 changes: 24 additions & 2 deletions phone-chatbot/daily-twilio-sip-dial-in/server_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,13 @@
#

import os
import time

import aiohttp
from fastapi import HTTPException, Request
from loguru import logger
from pipecat.runner.daily import DailyRoomConfig, configure
from pipecat.transports.daily.utils import DailyRoomProperties, DailyRoomSipParams
from pydantic import BaseModel


Expand Down Expand Up @@ -41,6 +43,7 @@ class AgentRequest(BaseModel):
token: str
call_sid: str
sip_uri: str
to_phone: str


async def twilio_call_data_from_request(request: Request):
Expand All @@ -67,13 +70,16 @@ async def twilio_call_data_from_request(request: Request):


async def create_daily_room(
call_data: TwilioCallData, session: aiohttp.ClientSession
call_data: TwilioCallData,
session: aiohttp.ClientSession,
sip_provider: str | None = None,
) -> DailyRoomConfig:
"""Create a Daily room configured for PSTN dial-in.

Args:
call_data: Call data containing caller phone number and call details
session: Shared aiohttp session for making HTTP requests
sip_provider: Optional SIP provider name (e.g., "daily")

Returns:
DailyRoomConfig: Configuration object with room_url and token
Expand All @@ -82,7 +88,23 @@ async def create_daily_room(
HTTPException: If room creation fails
"""
try:
return await configure(session, sip_caller_phone=call_data.from_phone)
sip_params = DailyRoomSipParams(
display_name=call_data.from_phone,
video=False,
sip_mode="dial-in",
num_endpoints=1,
codecs=None,
provider=sip_provider,
)
room_props = DailyRoomProperties(
exp=time.time() + 600, # 10 minutes
eject_at_room_exp=True,
sip=sip_params,
enable_dialout=True,
start_video_off=True,
geo="us-east-1",
)
return await configure(session, room_properties=room_props)
except Exception as e:
logger.error(f"Error creating Daily room: {e}")
raise HTTPException(status_code=500, detail=f"Failed to create Daily room: {e!s}")
Expand Down
19 changes: 18 additions & 1 deletion phone-chatbot/daily-twilio-sip-dial-out/bot.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ def __init__(
):
self._transport = transport
self._sip_uri = dialout_settings.sip_uri
self._provider = dialout_settings.provider
self._max_retries = max_retries
self._attempt_count = 0
self._is_successful = False
Expand Down Expand Up @@ -80,7 +81,10 @@ async def attempt_dialout(self) -> bool:
f"Attempting dialout (attempt {self._attempt_count}/{self._max_retries}) to: {self._sip_uri}"
)

await self._transport.start_dialout({"sipUri": self._sip_uri})
params = {"sipUri": self._sip_uri}
if self._provider:
params["provider"] = self._provider
await self._transport.start_dialout(params)
return True

def mark_successful(self):
Expand Down Expand Up @@ -170,6 +174,19 @@ async def on_dialout_answered(transport, data):
logger.debug(f"Dial-out answered: {data}")
dialout_manager.mark_successful()

@transport.event_handler("on_dialout_connected")
async def on_dialout_connected(transport, data):
logger.debug(f"Dial-out connected: {data}")

@transport.event_handler("on_dialout_stopped")
async def on_dialout_stopped(transport, data):
logger.debug(f"Dial-out stopped: {data}")
await task.cancel()

@transport.event_handler("on_dialout_warning")
async def on_dialout_warning(transport, data):
logger.debug(f"Dial-out warning: {data}")

@transport.event_handler("on_dialout_error")
async def on_dialout_error(transport, data: Any):
logger.error(f"Dial-out error, retrying: {data}")
Expand Down
21 changes: 20 additions & 1 deletion phone-chatbot/daily-twilio-sip-dial-out/server_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,13 @@
"""

import os
import time

import aiohttp
from fastapi import HTTPException, Request
from loguru import logger
from pipecat.runner.daily import DailyRoomConfig, configure
from pipecat.transports.daily.utils import DailyRoomProperties
from pydantic import BaseModel


Expand All @@ -26,9 +28,11 @@ class DialoutSettings(BaseModel):

Attributes:
sip_uri: The SIP URI to dial
provider: Optional SIP provider name (e.g., "daily")
"""

sip_uri: str
provider: str | None = None
# Include any custom data here needed for the call


Expand Down Expand Up @@ -109,7 +113,22 @@ async def create_daily_room(
raise HTTPException(status_code=400, detail="Invalid SIP URI")

try:
return await configure(session, sip_caller_phone=sip_caller_phone)
# sip_params = DailyRoomSipParams(
# display_name=sip_caller_phone,
# video=False,
# sip_mode="dial-in",
# num_endpoints=1,
# provider="daily"
# )
room_props = DailyRoomProperties(
exp=time.time() + 600, # 10 minutes
eject_at_room_exp=True,
# sip=sip_params,
enable_dialout=True,
start_video_off=True,
geo="us-east-1", # select a region close to the from_number/pipecat agent
)
return await configure(session, room_properties=room_props)
except Exception as e:
logger.error(f"Error creating Daily room: {e}")
raise HTTPException(status_code=500, detail=f"Failed to create Daily room: {str(e)}")
Expand Down
Loading