最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

fastapi - ERR_NGROK_3200 with Twilio - Stack Overflow

programmeradmin2浏览0评论

Below is the code that I am executing and which raises an Internal Server Error in a URL titled /make-call.

After further investigation, I see that I am getting an error on Ngrok: ERR_NGROK_3200

import os
import json
import base64
import asyncio
import websockets
import uvicorn
from fastapi import FastAPI, WebSocket, Request
from fastapi.responses import JSONResponse
from fastapi.responses import HTMLResponse
from fastapi.websockets import WebSocketDisconnect
from twilio.rest import Client
from twilio.twiml.voice_response import VoiceResponse, Connect, Say, Stream
from google import genai
from dotenv import load_dotenv
import logging


logger = logging.getLogger(__name__)
logging.basicConfig(
    filename='errors.log',
    encoding='utf-8',
    level=logging.INFO,
    format='%(asctime)s %(message)s'
)

load_dotenv()

# Initialize Google Gemini API
MODEL = os.getenv("MODEL")
genai_client = genai.Client(http_options={"api_version": "v1alpha"})


# Twilio credentials and config
TWILIO_ACCOUNT_SID = os.getenv("TWILIO_ACCOUNT_SID")
TWILIO_AUTH_TOKEN = os.getenv("TWILIO_AUTH_TOKEN")
TWILIO_PHONE_NUMBER = os.getenv("TWILIO_PHONE_NUMBER")
NGROK_URL = os.getenv("NGROK_URL")
# PORT = int(os.getenv("PORT", 5050))
PORT = os.getenv("PORT", 5050)

client = Client(TWILIO_ACCOUNT_SID, TWILIO_AUTH_TOKEN)

# FastAPI app
app = FastAPI()


@app.get("/", response_class=HTMLResponse)
async def index_page():
    logging.error("Inside '/'.")
    return {"message": "Twilio Media Stream Server is running!"}


@app.post("/make-call")
async def make_call(request: Request):
    logging.error("Inside in /make_call.")
    """
    Initiates a VoIP call.
    Handles both JSON and form-encoded requests.
    """
    try:
        logging.error("Inside try block 01 of /make-call.")
        # Parsing JSON payload
        data = await request.json()
        to_number = data.get("to")
        logging.error(f'Data:{data}')
    # except json.JSONDecodeError:
    except Exception as e:
        logging.error(f'Error at line 68:{e}')
        logging.error("Inside except block 01 of /make-call.")
        # Fallback to form data if JSON isn't provided
        form_data = await request.form()
        to_number = form_data.get("to")

    if not to_number:
        return {"error": "Recipient number is required"}, 400

    try:
        logging.error("Inside try block 02 of /make-call.")
        call = client.calls.create(
            url=f"{NGROK_URL}/outgoing-call",
            to=to_number,
            from_=TWILIO_PHONE_NUMBER
        )

        return {"message": "Call initiated",
                "call_sid": call.sid}, 200
    except Exception as e:
        logging.error(f"Error in /make_call: {e}")
        return {"error": str(e)}, 500


@app.api_route("/outgoing-call", methods=["GET", "POST"])
async def handle_outgoing_call(request: Request):
    logging.error("Inside in /outgoing-call.")
    """
    Handle outgoing call and return TwiML response to connect to Media Stream.
    """
    response = VoiceResponse()
    response.say("Please wait while we connect your call to the AI voice assistant...")
    response.pause(length=1)
    connect = Connect()
    connect.stream(url=f"wss://{request.url.hostname}/media-stream")
    response.append(connect)
    logging.error("Exiting /outgoing-call.")
    return HTMLResponse(content=str(response), media_type="application/xml")


@app.websocket("/media-stream")
async def handle_media_stream(websocket: WebSocket):
    logging.error("Inside /media_stream.")
    """
    Handle WebSocket connections between Twilio and Gemini API.
    """
    print("Client connected")
    await websocket.accept()
    try:
        async for message in websocket.iter_text():
            data = json.loads(message)
            logging.info(f'Data:{data}')
            if "event" in data and data["event"] == "media":
                print(f"Received audio payload: {data['media']['payload']}")
            # Process this data with Gemini if needed
    except WebSocketDisconnect:
        print("Client disconnected.")

    # Connect to Gemini session
    async with genai_client.aio.live.connect(model=MODEL) as gemini_ws:
        logging.error("Inside connect to gemini session")
        async def receive_from_twilio():
            logging.error("Inside receive_from_twilio.")
            """
            Receive audio data from Twilio and send it to Gemini API.
            """
            try:
                async for message in websocket.iter_text():
                    data = json.loads(message)
                    # logging.error("Inside connect to gemini session")
                    if data["event"] == "media":
                        audio = data["media"]["payload"]
                        await gemini_ws.send(input=base64.b64decode(audio), end_of_turn=False)
            except WebSocketDisconnect:
                print("Twilio disconnected")

        async def send_to_twilio():
            logging.error("Inside send_to_twilio.")
            """
            Receive audio or text data from Gemini and send back to Twilio.
            """
            async for chunk in gemini_ws.receive():
                if chunk.get("text"):
                    ai_text = chunk["text"]
                    print(f"Gemini Response: {ai_text}")
                    # Send text-to-speech via Twilio's media stream
                    await websocket.send_json({"text": ai_text})

                if chunk.get("audio"):
                    ai_audio = base64.b64encode(chunk["audio"]).decode("utf-8")
                    await websocket.send_json({"event": "media", "media": {"payload": ai_audio}})

        logging.error("Exiting /media-stream")

        await asyncio.gather(receive_from_twilio(), send_to_twilio())


if __name__ == "__main__":
    uvicorn.run(app, host="0.0.0.0", port=PORT)

This is how I am running this program on 3 different terminals:

Terminal 1: uvicorn <file-name>:app --port 5000. This gives the local server link.

Terminal 2: ngrok http 5000. This gives me a public URL, which I add in the .env file: NGROK_URL="ngrok-url" and in twilio webhooks: "ngrok-url/outgoing-call".

Terminal 3: I then run the final command which initiates the call: curl -X POST http://127.0.0.1:5000/make-call -d "to=+xxxxxxxxxxxx"

After running all 3 commands, a call is successfully initiated. I answer the call, but the call gets disconnected with the message:

'Sorry, an application error has occurred'.

Can you please help me in this issue?

Thank you in advance.

Solutions that I tried:

  1. I read the documentation of ngrok error 3200.
  2. Tried using GPT to get the error resolved.
  3. Watched YT videos related to it but I did not get any solution to this.
发布评论

评论列表(0)

  1. 暂无评论