
    qiU                     p   U d Z ddlZddlZddlZddlZddlZddlZddlmZ ddl	m
Z
 ddlmZ ddlmZmZmZmZmZmZ ddlZddlmZmZ ddlmZ dd	lmZmZmZmZ 	 ddlZdd
l m!Z! ddl"m#Z#m$Z$m%Z%m&Z&m'Z'm(Z( ddl)m*Z* ddlm+Z+m,Z,  e!d       dej`                  d<   g dZ1da2ee3   e4d<   da5e3e4d<   da6e7e4d<   d Z8de(dejr                  fdZ:dejr                  fdZ;d e$dejr                  fd!Z<d e$fd"Z=d e$dejr                  fd#Z>d e$dejr                  fd$Z?d e$dejr                  fd%Z@dejr                  fd&ZAd'e3d(e3fd)ZBd(ee3   fd*ZCd(e3fd+ZDd(e7fd,ZEd0d-eej                     fd.ZGeHd/k(  r eG        yy# e-$ r5Z. ej^                  de.         ej^                  d        e-d      e.dZ.[.ww xY w)1a  Pipecat development runner.

This development runner executes Pipecat bots and provides the supporting
infrastructure they need - creating Daily rooms and tokens, managing WebRTC
connections, and setting up telephony webhook/WebSocket infrastructure. It
supports multiple transport types with a unified interface.

Install with::

    pip install pipecat-ai[runner]

All bots must implement a `bot(runner_args)` async function as the entry point.
The server automatically discovers and executes this function when connections
are established.

Single transport example::

    async def bot(runner_args: RunnerArguments):
        transport = DailyTransport(
            runner_args.room_url,
            runner_args.token,
            "Bot",
            DailyParams(...)
        )
        # Your bot logic here
        await run_pipeline(transport)

    if __name__ == "__main__":
        from pipecat.runner.run import main
        main()

Multiple transport example::

    async def bot(runner_args: RunnerArguments):
        # Type-safe transport detection
        if isinstance(runner_args, DailyRunnerArguments):
            transport = setup_daily_transport(runner_args)  # Your application code
        elif isinstance(runner_args, SmallWebRTCRunnerArguments):
            transport = setup_webrtc_transport(runner_args)  # Your application code
        elif isinstance(runner_args, WebSocketRunnerArguments):
            transport = setup_telephony_transport(runner_args)  # Your application code

        # Your bot implementation
        await run_pipeline(transport)

Supported transports:

- Daily - Creates rooms and tokens, runs bot as participant
- WebRTC - Provides local WebRTC interface with prebuilt UI
- Telephony - Handles webhook and WebSocket connections for Twilio, Telnyx, Plivo, Exotel

To run locally:

- WebRTC: `python bot.py -t webrtc`
- ESP32: `python bot.py -t webrtc --esp32 --host 192.168.1.100`
- Daily (server): `python bot.py -t daily`
- Daily (direct, testing only): `python bot.py -d`
- Telephony: `python bot.py -t twilio -x your_username.ngrok.io`
- Exotel: `python bot.py -t exotel` (no proxy needed, but ngrok connection to HTTP 7860 is required)
    N)asynccontextmanager)
HTTPMethod)Path)AnyDictListOptional	TypedDictUnion)FileResponseResponse)logger)DailyRunnerArgumentsRunnerArgumentsSmallWebRTCRunnerArgumentsWebSocketRunnerArguments)load_dotenv)BackgroundTasksFastAPIHeaderHTTPExceptionRequest	WebSocket)CORSMiddleware)HTMLResponseRedirectResponsez#Runner dependencies not available: zDTo use Pipecat runners, install with: pip install pipecat-ai[runner]zJRunner dependencies required. Install with: pip install pipecat-ai[runner]T)overridelocalENV)twiliotelnyxplivoexotelRUNNER_DOWNLOADS_FOLDER	localhostRUNNER_HOSTi  RUNNER_PORTc                  D   ddl } t        j                  d   }t        |d      r|S 	 ddl}|S # t
        $ r Y nw xY wt        j                         }t        j                  |      D ]  }|j                  d      s|dk7  s	 |dd }| j                  j                  |t        j                  j                  ||            }| j                  j                  |      }|j                  j!                  |       t        |d      r|c S # t"        $ r Y w xY w t        d      )	z+Get the bot module from the calling script.r   N__main__botz.pyz	server.pyzLCould not find 'bot' function. Make sure your bot file has a 'bot' function.)importlib.utilsysmoduleshasattrr*   ImportErrorosgetcwdlistdirendswithutilspec_from_file_locationpathjoinmodule_from_specloaderexec_module	Exception)	importlibmain_moduler*   cwdfilenamemodule_namespecmodules           D/opt/pipecat/venv/lib/python3.12/site-packages/pipecat/runner/run.py_get_bot_modulerE   r   s    ++j)K {E"
 
 ))+CJJsO U#K(?&sm ~~==c8!< #88>''/65)!M *  V s   - 	99BD	DD	websocketargsc                 |   K   t               }t        |       }||_        |j                  |       d{    y7 w)z#Run a bot for telephony transports.)rF   N)rE   r   cli_argsr*   )rF   rG   
bot_modulerunner_argss       rD   _run_telephony_botrL      s4      "J +Y?KK
..
%%%s   2<:<c                 |   t               }|j                  t        dgddgdg       | j                  dk(  r&t	        ||        | j
                  rt        ||        |S | j                  dk(  rt        ||        |S | j                  t        v rt        ||        |S t        j                  d| j                          |S )z2Create FastAPI app with transport-specific routes.*T)allow_originsallow_credentialsallow_methodsallow_headerswebrtcdailyzUnknown transport type: )r   add_middlewarer   	transport_setup_webrtc_routeswhatsapp_setup_whatsapp_routes_setup_daily_routesTELEPHONY_TRANSPORTS_setup_telephony_routesr   warning)rG   apps     rD   _create_server_appr_      s    
)Ceee   ~~!S$'=="3- J 
7	"C& J 
/	/T* J 	1$..1ABCJ    r^   c           	      h  
 	 ddl m} ddlm ddlm
mmm}  G d dt        d	
       G fddt               G fddt        d	
      i | j                  d|       | j                  dd	      d        }| j                  d      dt        ffd       } |j                   j"                        | j%                  d      ddt&        ffd       | j)                  d      dffd       | j%                  d      dt*        ffd       }| j-                  dg d       d!t        d"t        dt*        dt&        f
fd#       }t.        d$t0        ffd%       }	t3        | |	       y# t        $ r"}t        j                  d|        Y d}~yd}~ww xY w)&zSet up WebRTC-specific routes.r   )SmallWebRTCPrebuiltUISmallWebRTCConnection)IceCandidateSmallWebRTCPatchRequestSmallWebRTCRequestSmallWebRTCRequestHandlerz-WebRTC transport dependencies not installed: Nc                   (    e Zd ZU eeee   f   ed<   y)'_setup_webrtc_routes.<locals>.IceServerurlsN)__name__
__module____qualname__r   strr   __annotations__ r`   rD   	IceServerrj      s    CcN##r`   rr   F)totalc                   "    e Zd ZU eW     ed<   y)'_setup_webrtc_routes.<locals>.IceConfig
iceServersN)rl   rm   rn   r   rp   )rr   s   rD   	IceConfigru      s    O#r`   rw   c                   ,    e Zd ZU eed<   eW     ed<   y),_setup_webrtc_routes.<locals>.StartBotResult	sessionId	iceConfigN)rl   rm   rn   ro   rp   r	   )rw   s   rD   StartBotResultry      s    I&&r`   r|   z/client/)include_in_schemac                  "   K   t        d      S w)z+Redirect root requests to client interface.z/client/)url)r   rq   r`   rD   root_redirectz+_setup_webrtc_routes.<locals>.root_redirect   s       J//s   z/files/{filename:path}r@   c                 (  K   j                   st        j                  d|  d       yt        j                         | z  }t        j
                  j                  |      st        d      t        j                  |      \  }}t        |||       S w)zHandle file downloads.zAttempting to dowload z!, but downloads folder not setup.N  )r7   
media_typer@   )folderr   r]   r   r1   r7   existsr   	mimetypes
guess_typer   )r@   	file_pathr   _rG   s       rD   download_filez+_setup_webrtc_routes.<locals>.download_file   s{      {{NN3H:=^_`%0	ww~~i($$!,,Y7
AzHUUs   BB)
esp32_modehostz
/api/offerrequestbackground_tasksc                 b    K   df fd}j                   |       d{   }|S 7 w)z;Handle WebRTC offer requests via SmallWebRTCRequestHandler.
connectionc                    K   t               }t        | j                        }|_        j	                  |j
                  |       y w)N)webrtc_connectionbody)rE   r   request_datarI   add_taskr*   )r   rJ   rK   rG   r   r   s      rD   webrtc_connection_callbackzG_setup_webrtc_routes.<locals>.offer.<locals>.webrtc_connection_callback   sB     (*J4",73G3GK $(K %%jnnkBs   AA	)r   r   N)handle_web_request)r   r   r   answerrd   rG   small_webrtc_handlers   ``  rD   offerz#_setup_webrtc_routes.<locals>.offer   sD     
	C9N 	C ,>>'A ? 
 
 	
s   !/-/c                 z   K   t        j                  d|         j                  |        d{    ddiS 7 w)z)Handle WebRTC new ice candidate requests.zReceived patch request: Nstatussuccess)r   debughandle_patch_request)r   r   s    rD   ice_candidatez+_setup_webrtc_routes.<locals>.ice_candidate  sB      	/y9:"77@@@)$$ 	As   -;9	;/startc                   K   	 | j                          d{   }t        j                  d|        t        t        j                               }|j                  di       |<   d|i}|j                  d      r  dg      g	      |d
<   |S 7 z# t        $ r$}t        j                  d|        i }Y d}~d}~ww xY ww)z&Mimic Pipecat Cloud's /start endpoint.NReceived request: Failed to parse request body: r   rz   enableDefaultIceServerszstun:stun.l.google.com:19302)rk   )rv   r{   )	jsonr   r   r<   errorro   uuiduuid4get)	r   r   e
session_idresultrw   rr   r|   active_sessionss	        rD   
rtvi_startz(_setup_webrtc_routes.<locals>.rtvi_start  s     	!(/LLL-l^<= &
&2&6&6vr&B
#"-z!:56"+%,J+KLM#F; ! 0 	LL9!=>L	s>   CB BB ACB 	CB=8C=CCz"/sessions/{session_id}/{path:path})GETPOSTPUTPATCHDELETE)methodsr   r7   c                 h  K   j                  |       }|t        dd      S |j                  d      r	 |j                          d{   }|j                  t
        j                  j                  k(  rh |d   |d   |j                  d      |j                  d	      |j                  d
      xs |j                  d      xs |      } ||       d{   S |j                  t
        j                  j                  k(  rA |d   |j                  dg       D cg c]
  } 
di | c}      } |       d{   S 	 t        j                  d|        t        d      S 7 !7 c c}w 7 3# t        $ r/}	t        j                  d|	        t        dd      cY d}	~	S d}	~	ww xY ww)zMimic Pipecat Cloud's proxy.Nz#Invalid or not-yet-ready session_idr   )contentstatus_codez	api/offersdptypepc_id
restart_pcr   requestData)r   r   r   r   r   
candidates)r   r   z Failed to parse WebRTC request: zInvalid WebRTC request  zReceived request for path:    )r   rq   )r   r   r4   r   methodr   r   valuer   r<   r   r   info)r   r7   r   r   active_sessionr   webrtc_requestcpatch_requestr   re   rf   rg   r   r   r   s             rD   proxy_requestz+_setup_webrtc_routes.<locals>.proxy_request.  s     ),,Z8!$IWZ[[==%S%,\\^3>>Z__%:%::%7(/)&1*..w7#/#3#3L#A%1%5%5n%E &*'++M:&*)&N "'~7G!HHH^^z'7'7'='==$;*73?K?O?OP\^`?a#b!L$51$5#b%M "/}!=== > 	1$89C((/  4 I $c= S?sCD(@cRRSs   3F2E7 
E+BE7 E.E7 F2AE7 E0
.E7 E5E7 &F2+E7 .E7 0E7 7	F/ $F*$F/%F2*F//F2r^   c                L   K   d j                          d{    y7 w)z=Manage FastAPI application lifecycle and cleanup connections.N)close)r^   r   s    rD   smallwebrtc_lifespanz2_setup_webrtc_routes.<locals>.smallwebrtc_lifespanV  s      	"((***s   $"$))pipecat_ai_small_webrtc_prebuilt.frontendrb   )pipecat.transports.smallwebrtc.connectionrd   .pipecat.transports.smallwebrtc.request_handlerre   rf   rg   rh   r0   r   r   r
   mountr   ro   esp32r   postr   patchr   	api_router   r   _add_lifespan_to_app)r^   rG   rb   rh   r   r   r   r   r   r   re   rw   rr   rd   rf   rg   r|   r   r   r   r   s    `        @@@@@@@@@@@rD   rW   rW      s   SS	
 	
$IU $$I $'% '
 24O IIi./WWSEW*0 +0 	WW%&Vc V 'V 7P::DII7 	XXl/ ?  ( 	YY|%%< % % 	XXh'  , 	]],9  ")")"")-4")HW") ")	")H + + + 23_  DQCHIs   F 	F1F,,F1c                     t        | j                  d      rT| j                  j                  >| j                  j                  t        dt        ffd       }|| j                  _        y| j                  _        y)zAdd a new lifespan context manager to the app, combining with existing if present.

    Args:
        app: The FastAPI application instance
        new_lifespan: The new lifespan context manager to add
    lifespan_contextNr^   c                   K    |       4 d {     |       4 d {    d  d d d       d {    d d d       d {    y 7 ;7 ,7 # 1 d {  7  sw Y   )xY w7  # 1 d {  7  sw Y   y xY wwNrq   )r^   existing_lifespannew_lifespans    rD   combined_lifespanz/_add_lifespan_to_app.<locals>.combined_lifespank  sy     (-  ',            s   A>AA>A)AA)AA)AA)A>A'A>A)A)A$	AA$	 A)'A>)A;/A20A;7A>)r/   routerr   r   r   )r^   r   r   r   s    ` @rD   r   r   `  sh     szz-.3::3N3N3ZJJ77		 	 
	
 '8

# '3

#r`   c           
        	
 t        j                  d      t        j                  d      	t        j                  d      
t        j                  d      t        	
g      st        j                  d       y	 ddlm dd	lm} dd
l	m
 d| j                  ddd      dt        ffd       }| j                  ddd      t        d      fd|dt         dt        dt"        ffd       }t$        dt&        f	
fd       }t)        | |       y# t        $ r"}t        j                  d|        Y d}~yd}~ww xY w)z Set up WhatsApp-specific routes.WHATSAPP_APP_SECRETWHATSAPP_PHONE_NUMBER_IDWHATSAPP_TOKEN#WHATSAPP_WEBHOOK_VERIFICATION_TOKENzMissing required environment variables for WhatsApp transport:
    WHATSAPP_APP_SECRET
    WHATSAPP_PHONE_NUMBER_ID
    WHATSAPP_TOKEN
    WHATSAPP_WEBHOOK_VERIFICATION_TOKEN
            Nr   rc   )WhatsAppWebhookRequest)WhatsAppClientz/WhatsApp transport dependencies not installed: z	/whatsappzVerify WhatsApp webhookz8Handles WhatsApp webhook verification requests from Meta)summarydescriptionr   c                   K   "t        j                  d       t        dd      t        | j                        }t        j
                  dt        |j                                       	 j                  |       d{   }t        j                  d       |S 7 # t        $ r*}t        j                  d	|        t        d
d      d}~ww xY ww)zVerify WhatsApp webhook endpoint.

        This endpoint is called by Meta's WhatsApp Business API to verify
        the webhook URL during setup. It validates the verification token
        and returns the challenge parameter if successful.
        N"WhatsApp client is not initialized  Service unavailabler   detailz3Webhook verification request received with params: )paramsexpected_verification_tokenzWebhook verification successfulzWebhook verification failed: i  zVerification failed)r   r   r   dictquery_paramsr   listkeyshandle_verify_webhook_requestr   
ValueErrorr]   )r   r   r   r   r   whatsapp_clients       rD   verify_webhookz._setup_whatsapp_routes.<locals>.verify_webhook  s      "LL=>C8MNNg**+J4PVP[P[P]K^J_`a	O*HH;^ I  F KK9:M	
  	ONN:1#>?C8MNN	Os<   A)C-B! BB! CB! !	C*%CCCzHandle WhatsApp webhook eventsz4Processes incoming WhatsApp messages and call eventsr   r   x_hub_signature_256c                   K   "t        j                  d       t        dd      | j                  dk7  r/t        j                  d| j                          t        dd	      t        j
                  d
| j                                 d	f
fd}	 |j                          d{   }j                  | |||       d{   }t        j
                  d|        dddS 7 >7 ## t        $ r6}t        j                  d|        t        ddt        |             d}~wt        $ r*}t        j                  d|        t        dd      d}~ww xY ww)zHandle incoming WhatsApp webhook events.

        For call events, establishes WebRTC connections and spawns bot instances
        in the background to handle real-time communication.
        Nr   r   r   r   whatsapp_business_accountzInvalid webhook object type: r   zInvalid object typezProcessing WhatsApp webhook: r   c                    K   t               }t        |       }|_        j                  |j                  |       yw)a  Handle new WebRTC connections from WhatsApp calls.

            Called when a WebRTC connection is established for a WhatsApp call.
            Spawns a bot instance to handle the conversation.

            Args:
                connection: The established WebRTC connection
            )r   N)rE   r   rI   r   r*   )r   rJ   rK   rG   r   s      rD   connection_callbackzM_setup_whatsapp_routes.<locals>.whatsapp_webhook.<locals>.connection_callback  s7      )*J4zRK#'K %%jnnkBs   ;>)sha256_signatureraw_bodyz Webhook processed successfully: r   zWebhook processed successfully)r   messagez Invalid webhook request format: zInvalid request: z#Internal error processing webhook:   z(Internal server error processing webhook)r   r   r   objectr]   r   
model_dumpr   handle_webhook_requestr   ro   r<   )r   r   r   r   r   r   r   ver   rd   rG   r   s    `       rD   whatsapp_webhookz0_setup_whatsapp_routes.<locals>.whatsapp_webhook  s_      "LL=>C8MNN ;;55NN:4;;-HIC8MNN4T__5F4GHI	C2G 	C	d$\\^+H*AA)<OZb B  F LL;F8DE'4TUU ,
  	WNN=bTBCC:KCPRG98UVV 	dLL>qcBCC8bcc	dsZ   BE C- *C)+C- C+ C- (E )C- +C- -	E61D''E3%EEE r^   c                  K   t        j                         4 d{   } |      t        j                  d       	 d t        j                  d       rj	                          d{    t        j                  d       ddd      d{    y7 7 ,# t        j                  d       rj	                          d{  7   t        j                  d       w xY w7 S# 1 d{  7  sw Y   yxY ww)z9Manage WhatsApp client lifecycle and cleanup connections.N)whatsapp_tokenwhatsapp_secretphone_number_idsessionz(WhatsApp client initialized successfullyz(Cleaning up WhatsApp client resources...zWhatsApp cleanup completed)aiohttpClientSessionr   r   terminate_all_calls)r^   r	  r   r   r   r   r   s     rD   whatsapp_lifespanz1_setup_whatsapp_routes.<locals>.whatsapp_lifespan  s      ((* 	: 	:g,- 3 8	O KKBC: FG")==???89!	: 	: 	: @ FG")==???89!	: 	: 	: 	:sz   DBD"C-B!*C-2B3C-DC+DC-!+C(C
C((C-+D-C?3C64C?;D)r1   getenvallr   r   r   rd   pipecat.transports.whatsapp.apir   "pipecat.transports.whatsapp.clientr   r0   r   r   r   r   r   ro   r   r   r   )r^   rG   r   r   r   r  r  rd   r   r   r   r   r   r   s    `     @@@@@@@rD   rY   rY   w  sq   ))$9:!yy)CDYY/0N*,))4Y*Z'$/		
 		
 	SJE 15OWW)N  
Og O
O0 	XX0J   $*$<	1d$1d)1d 1d !	1d
1df :W : : :0 /0k  FqcJKs   D 	E
(EE
c                     | j                  d      fd       }| j                  d      dt        ffd       }j                  r!| j                  d      dt        ffd       }yy)	zSet up Daily-specific routes.r}   c                    K   t        d       ddl} ddlm}  | j                         4 d{   } ||       d{   \  }}t               }t        ||      }|_        t        j                  |j                  |             t        |      cddd      d{    S 7 u7 g7 	# 1 d{  7  sw Y   yxY ww)z(Launch a Daily bot and redirect to room.z?Starting bot with Daily transport and redirecting to Daily roomr   N	configureroom_urltoken)printr
  pipecat.runner.dailyr  r  rE   r   rI   asynciocreate_taskr*   r   )r
  r  r	  r  r  rJ   rK   rG   s          rD   create_room_and_start_agentz8_setup_daily_routes.<locals>.create_room_and_start_agent  s      	OP2(7((* 	. 	.g$-g$66OHe )*J.NK#'K 
{ ;<#H-	. 	. 	.6	. 	. 	. 	.sU   +B?B$B?B*B&AB*B?B(B?&B*(B?*B<0B31B<8B?r   r   c                   K   t        d       	 | j                          d{   }t        j                  d|        |j                  dd      }|j                  di       }|j                  dd      }|j                  d	d      }t               }t        j                  d
      }d}	|s|rddl
}
ddlm} ddlm}m}  |
j                          4 d{   }d}|r!	  |di |}t        j                  d|        d}|r!	  |di |}t        j                  d|         ||||       d{   \  }}t#        |||      }||t%        t'        j(                               d}	ddd      d{    nt+        |      }_        t/        j0                  |j3                  |             |	S 7 # t        $ r%}t        j
                  d|        i }Y d}~d}~ww xY w7 # t        $ r#}t        j
                  d|        Y d}~d}~ww xY w# t        $ r#}t        j
                  d|        Y d}~d}~ww xY w7 7 # 1 d{  7  sw Y   xY ww)a]  Handler for /start endpoints.

        Expects POST body like::
            {
                "createDailyRoom": true,
                "dailyRoomProperties": { "start_video_off": true },
                "dailyMeetingTokenProperties": { "is_owner": true, "user_name": "Bot" },
                "body": { "custom_data": "value" }
            }
        z!Starting bot with Daily transportNr   r   createDailyRoomFr   dailyRoomPropertiesdailyMeetingTokenPropertiesDAILY_ROOM_URLr   r  )DailyMeetingTokenPropertiesDailyRoomPropertieszUsing custom room properties: z%Failed to parse dailyRoomProperties: zUsing custom token properties: z-Failed to parse dailyMeetingTokenProperties: )room_propertiestoken_propertiesr  r  r   	dailyRoom
dailyTokenrz   )r   rq   )r  r   r   r   r<   r   r   rE   r1   r  r
  r  r  pipecat.transports.daily.utilsr#  r$  r  r   ro   r   r   r   rI   r  r  r*   )r   r   r   create_daily_roomr   daily_room_properties_dictdaily_token_properties_dictrJ   existing_room_urlr   r
  r  r#  r$  r	  r%  r&  r  r  rK   rG   s                       rD   start_agentz(_setup_daily_routes.<locals>.start_agent"  sv     	12	!(/LLL-l^<=
 ),,->F+%1%5%56KT%R"&2&6&67TVZ&[#$&
II&67
  16
 -w,,.  '"&-R*=*[@Z*['EoEV%WX $( .Z+F ,9,( 'FGWFX%YZ
 )2_O_) #% 3HEX\]!)"'!$TZZ\!27  B *t4K  $ 	JNN;78M 0 	LL9!=>L	6 % R'LQC%PQQR % Z'TUVTW%XYYZ#/   s   I!F5 F2F5  BI!G&I!I G)>I H#I1I26I(I!3I
4>I!2F5 5	G#>GI!G##I!)	H2H
IHI	I!H?9I?II
I!IIII!/daily-dialin-webhookc           	        K   t        j                  d       	 | j                          d{   t        j                  d        j                  d      sj                  d	      rt        j                  d
       ddiS t        fddD              st        dd      ddl}ddl	m
} ddlm}m}  |j                         4 d{   }	  ||j                  d             d{   }ddd      d{    t!        j"                  dd      }t!        j"                  d      }	|	s"t        j                  d       t        dd       |j                  d      j                  d      j                  d      j                  d      j                  d             }
 ||
|	|!      }t%               }t'        j(                  |j*                  |j-                         "      }|_        t1        j2                  |j5                  |             t        t7        j8                               }|j(                  |j*                  |d#S 7 %# t        $ r*}t        j                  d|        t        dd      d}~ww xY w7 7 # t        $ r6}t        j                  d|        t        ddt        |             d}~ww xY w7 # 1 d{  7  sw Y   xY ww)$a  Handle incoming Daily PSTN dial-in webhook.

            This endpoint mimics Pipecat Cloud's dial-in webhook handler.
            It receives Daily webhook data, creates a SIP-enabled room, and starts the bot.

            Expected webhook payload::

                {
                    "From": "+15551234567",
                    "To": "+15559876543",
                    "callId": "uuid-call-id",
                    "callDomain": "uuid-call-domain",
                    "sipHeaders": {...}  // optional
                }

            Returns::

                {
                    "dailyRoom": "https://...",
                    "dailyToken": "...",
                    "sessionId": "uuid"
                }
            zReceived Daily dial-in webhookNzWebhook data: zFailed to parse webhook data: r   zInvalid JSON payloadr   testTestz"Webhook verification test receivedr   OKc              3   &   K   | ]  }|v  
 y wr   rq   ).0keydatas     rD   	<genexpr>zE_setup_daily_routes.<locals>.handle_dialin_webhook.<locals>.<genexpr>  s     Ussd{Us   )FromTocallId
callDomainz5Missing required fields: From, To, callId, callDomainr   r  )DailyDialinRequestDialinSettingsr;  )sip_caller_phonezFailed to create Daily room: r   DAILY_API_URLzhttps://api.daily.co/v1DAILY_API_KEYz&DAILY_API_KEY not found in environmentz&DAILY_API_KEY not configured on serverr=  r>  r<  
sipHeaders)call_idcall_domainr<  r;  sip_headers)dialin_settingsdaily_api_keydaily_api_urlr'  r(  )r   r   r   r<   r   r   r   r  r
  r  r  pipecat.runner.typesr?  r@  r  ro   r1   r  rE   r   r  r  r  rI   r  r  r*   r   r   )r   r   r
  r  r?  r@  r	  room_configrJ  rI  rH  request_bodyrJ   rK   r   r9  rG   s                  @rD   handle_dialin_webhookz2_setup_daily_routes.<locals>.handle_dialin_webhook|  s    2 LL9:T$\\^+~dV45 xx488F#3AB $'' U.TUU# #R 
 6O -w,,.  '(1'DHHU[L\(]"]K  IIo7PQM IIo6M EF# #,T 
 -* HH\288D>XXf% HH\2O . /++L )*J.$--!''!,,.K
 $(K 
{ ;< TZZ\*J )11)//' W , T=aSAB#<RSST,"]  LL#@!DE'$'2OPSTUPVx0X    s   K-I II BK-JK-KJ0J1J5K- KEK-I 	J	%JJ		K-J	K1KKKK-K*K K*%K-N)r   r   r   dialin)r^   rG   r  r0  rN  s    `   rD   rZ   rZ     s{     	WWS\. .$ 	XXhU7 U Un {{	)	*j	 j	 
+j	 r`   c                    dj                    ddj                    ddj                    dd| j                  d      fd       }| j                  d	      d
t        ffd       }| j	                  d      fd       }y)z!Set up telephony-specific routes.zU<?xml version="1.0" encoding="UTF-8"?>
<Response>
  <Connect>
    <Stream url="wss://z>/ws"></Stream>
  </Connect>
  <Pause length="40"/>
</Response>zV/ws" bidirectionalMode="rtp"></Stream>
  </Connect>
  <Pause length="40"/>
</Response>z<?xml version="1.0" encoding="UTF-8"?>
<Response>
  <Stream bidirectional="true" keepCallAlive="true" contentType="audio/x-mulaw;rate=8000">wss://z/ws</Stream>
</Response>)r    r!   r"   r}   c                  0  K   j                   dk(  r)t        j                  d       ddj                   dddS t        j                  dj                   j	                          d	       j                  j                   d
      } t        | d      S w)z1Handle telephony webhook and return XML response.r#   zPOST Exotel endpoint - not usedz Exotel doesn't use POST webhookszwss:///wszKConfigure the WebSocket URL above in your Exotel App Bazaar Voicebot Applet)r   websocket_urlnotezPOST z XMLz<Response></Response>zapplication/xml)r   r   )rV   r   r   proxyupperr   r   )xml_contentXML_TEMPLATESrG   s    rD   
start_callz+_setup_telephony_routes.<locals>.start_call  s      >>X%LL:;;#)$**S!9e  LL5!5!5!7 8=>'++DNN<STK@QRRs   BBrR  rF   c                    K   | j                          d{    t        j                  d       t        |        d{    y7 .7 w)z+Handle WebSocket connections for telephony.NzWebSocket connection accepted)acceptr   r   rL   )rF   rG   s    rD   websocket_endpointz3_setup_telephony_routes.<locals>.websocket_endpoint  sA         45 D111 	!1s!   A
A(A
 AA
A
c                  .   K   dd j                    iS w)z0Simple status endpoint for telephony transports.r   zBot started with )rV   )rG   s   rD   r0  z,_setup_telephony_routes.<locals>.start_agent  s!      -dnn-=>??s   N)rU  r   rF   r   r   )r^   rG   rY  r\  r0  rX  s    `   @rD   r\   r\     s     

| $ 

| $aaeakak`l mM* 	XXc]S S 	]]52I 2 2 	WWS\@ @r`   c                   K   	 ddl m} t        j
                  d       t        j                         4 d{   } ||       d{   \  }}t        ||      }d|_	        | |_
        t               }t        d|        t        d	       t                |j                  |       d{    ddd      d{    y# t        $ r}t        j                  d       Y d}~yd}~ww xY w7 7 7 D7 6# 1 d{  7  sw Y   yxY ww)
z9Run Daily bot with direct connection (no FastAPI server).r   r  z+Daily transport dependencies not installed.Nz'Running with direct Daily connection...r  Tu   📞 Joining Daily room: z-   (Direct connection - no web server needed))r  r  r0   r   r   r   r
  r  r   handle_sigintrI   rE   r  r*   )rG   r  r   r	  r  r  rK   rJ   s           rD   _run_daily_directr`    s     2
 KK9:$$& * *' )' 22% +HEJ$(!# %&
)(45=>nn[)))* * *  BC*2 	** * * *s   D	C -D	C,D	C4C.A#C4+C0,C40D	;C2<D		C)
C$D	$C))D	.C40C42D	4D:C=;DD	rU  returnc                     | s| S | }| j                  d      r1| j                  dd      d   } t        j                  d|  d| d       | j	                  d      } | S )z@Validate and clean proxy hostname, removing protocol if present.)zhttp://zhttps://z://   z(Removed protocol from proxy URL. Using 'z' instead of 'zK'. The --proxy argument expects only the hostname (e.g., 'mybot.ngrok.io').r}   )
startswithsplitr   r]   rstrip)rU  original_proxys     rD   _validate_and_clean_proxyrh  ;  sq    N /0E1%a(6ug^NK[ \W X	
 LLELr`   c                      t         S )z=Returns the folder where files are stored for later download.)r$   rq   r`   rD   runner_downloads_folderrj  P  s    ""r`   c                      t         S )z%Returns the host name of this runner.)r&   rq   r`   rD   runner_hostrl  U      r`   c                      t         S )z Returns the port of this runner.)r'   rq   r`   rD   runner_portro  Z  rm  r`   parserc                    | st        j                  d      } | j                  dt        t        d       | j                  dt
        t        d       | j                  dd	t        d
dgt        dd       | j                  ddd       | j                  ddddd       | j                  ddt        d       | j                  ddddd        | j                  d!ddd"       | j                  d#ddd$       | j                  d%ddd&       | j                         }|j                  rt        |j                        |_	        |j                  r|j                  dk(  rd
|_        n1|j                  r%|j                  d
k7  rt        j                  d'       y(|j                  r%|j                   d)k(  rt        j                  d*       y(|j"                  r%|j                  d
k7  rt        j                  d+       y(t        j$                          t        j&                  t(        j*                  |j,                  rd,nd-.       |j                  r>t/                t/        d/       t/                t1        j2                  t5        |             y(|j                  dk(  rvt/                |j                  rt/        d0       n#|j6                  rt/        d1       nt/        d2       t/        d3|j                    d4|j8                   d5       t/                n|j                  d
k(  rt/                t/        d2       |j"                  r2t/        d6|j                    d4|j8                   d7       t/        d8       n&t/        d3|j                    d4|j8                   d9       t/                |j:                  a|j                   a|j8                  at?        |      }tA        j2                  ||j                   |j8                  :       y();a  Start the Pipecat development runner.

    Parses command-line arguments and starts a FastAPI server configured
    for the specified transport type.

    The runner discovers and runs any ``bot(runner_args)`` function found in the
    calling module.

    Command-line arguments:
       - --host: Server host address (default: localhost) 879
       - --port: Server port (default: 7860)
       - -t/--transport: Transport type (daily, webrtc, twilio, telnyx, plivo, exotel)
       - -x/--proxy: Public proxy hostname for telephony webhooks
       - -d/--direct: Connect directly to Daily room (automatically sets transport to daily)
       - -f/--folder: Path to downloads folder
       - --dialin: Enable Daily PSTN dial-in webhook handling (requires Daily transport)
       - --esp32: Enable SDP munging for ESP32 compatibility (requires --host with IP address)
       - --whatsapp: Ensure requried WhatsApp environment variables are present
       - -v/--verbose: Increase logging verbosity

    Args:
        parser: Optional custom argument parser. If provided, default runner
            arguments are added to it so bots can define their own CLI
            arguments. Custom arguments should not conflict with the default
            ones. Custom args are accessible via `runner_args.cli_args`.

    zPipecat Development Runner)r   z--hostzHost address)r   defaulthelpz--portzPort numberz-tz--transportrT   rS   zTransport type)r   choicesrr  rs  z-xz--proxyzPublic proxy host name)rs  z-dz--direct
store_trueFzFConnect directly to Daily room (automatically sets transport to daily))actionrr  rs  z-fz--folderzPath to downloads folder)r   rs  z-vz	--verbosecountr   zIncrease logging verbosityz--dialinzEEnable Daily PSTN dial-in webhook handling (requires Daily transport)z--esp32zLEnable SDP munging for ESP32 compatibility (requires --host with IP address)z
--whatsappz:Ensure requried WhatsApp environment variables are presentz8--direct flag only works with Daily transport (-t daily)Nr%   zDFor ESP32, you need to specify `--host IP` so we can do SDP munging.z8--dialin flag only works with Daily transport (-t daily)TRACEDEBUG)levelu)   🚀 Connecting directly to Daily room...u   🚀 Bot ready! (ESP32 mode)u   🚀 Bot ready! (WhatsApp)u   🚀 Bot ready!u      → Open http://:z/client in your browseru%      → Daily dial-in webhook: http://r1  u=      → Configure this URL in your Daily phone number settingsz# in your browser to start a session)r   port)!argparseArgumentParseradd_argumentro   r&   intr'   r[   
parse_argsrU  rh  directrV   r   r   r   r   rO  removeaddr-   stderrverboser  r  runr`  rX   r|  r   r$   r_   uvicorn)rp  rG   r^   s      rD   mainr  _  si   < ((5QR
sKnU
sKmT
(:%9:   i.FG
U   js9ST
k'1;W   T	   [	   I	   D zz.tzz:
 {{t~~1 	72OP zzdii;.[\ {{t~~0OP MMO
JJszzDLLgF {{9: 	%d+, ~~!::02]].0O%#DII;a		{:QRS	7	"!;;7		{!DII;Ncd QS'		{!DII;>abc"kk))K))K T
"C KK$))$))4r`   r)   r   )I__doc__r}  r  r   r1   r-   r   
contextlibr   httpr   pathlibr   typingr   r   r   r	   r
   r   r
  fastapi.responsesr   r   logurur   rK  r   r   r   r   r  dotenvr   fastapir   r   r   r   r   r   fastapi.middleware.corsr   r   r   r0   r   r   environr[   r$   ro   rp   r&   r'   r  rE   	NamespacerL   r_   rW   r   rY   rZ   r\   r`  rh  rj  rl  ro  r~  r  rl   rq   r`   rD   <module>r     s  ;z    	 
  *   > >  4  "[[6@ T 

5 > )- # -S S 'T&	 &9K9K &X// 6[4g [4X-?-? [4|3g 3.R1 R1x/A/A R1j[W [H,>,> [|2@ 2@0B0B 2@j*("4"4 *8S S *## #
S 
S 
M5(112 M5` zF _  FLL6qc:;FLLWX
Ts   (E; ;F5 0F00F5