Twilio serializer reading dtmf websocket messages and generating InputDTMFFrame containing the corresponding value of KeypadEntry

This commit is contained in:
Maxim Makatchev
2025-01-16 17:43:12 +09:00
parent b8ffd7b16b
commit dcf317f2fa
2 changed files with 44 additions and 4 deletions

View File

@@ -5,6 +5,7 @@
#
from dataclasses import dataclass, field
from enum import Enum
from typing import TYPE_CHECKING, Any, Awaitable, Callable, List, Literal, Mapping, Optional, Tuple
from pipecat.audio.vad.vad_analyzer import VADParams
@@ -18,6 +19,23 @@ if TYPE_CHECKING:
from pipecat.observers.base_observer import BaseObserver
class KeypadEntry(str, Enum):
"""DTMF entries."""
ONE = "1"
TWO = "2"
THREE = "3"
FOUR = "4"
FIVE = "5"
SIX = "6"
SEVEN = "7"
EIGHT = "8"
NINE = "9"
ZERO = "0"
POUND = "#"
STAR = "*"
def format_pts(pts: int | None):
return nanoseconds_to_str(pts) if pts else None
@@ -615,6 +633,13 @@ class VisionImageRawFrame(InputImageRawFrame):
return f"{self.name}(pts: {pts}, text: [{self.text}], size: {self.size}, format: {self.format})"
@dataclass
class InputDTMFFrame(Frame):
"""A DTMF button input"""
button: KeypadEntry
#
# Control frames
#

View File

@@ -10,7 +10,14 @@ import json
from pydantic import BaseModel
from pipecat.audio.utils import pcm_to_ulaw, ulaw_to_pcm
from pipecat.frames.frames import AudioRawFrame, Frame, InputAudioRawFrame, StartInterruptionFrame
from pipecat.frames.frames import (
AudioRawFrame,
Frame,
InputAudioRawFrame,
InputDTMFFrame,
KeypadEntry,
StartInterruptionFrame,
)
from pipecat.serializers.base_serializer import FrameSerializer, FrameSerializerType
@@ -48,9 +55,7 @@ class TwilioFrameSerializer(FrameSerializer):
def deserialize(self, data: str | bytes) -> Frame | None:
message = json.loads(data)
if message["event"] != "media":
return None
else:
if message["event"] == "media":
payload_base64 = message["media"]["payload"]
payload = base64.b64decode(payload_base64)
@@ -61,3 +66,13 @@ class TwilioFrameSerializer(FrameSerializer):
audio=deserialized_data, num_channels=1, sample_rate=self._params.sample_rate
)
return audio_frame
elif message["event"] == "dtmf":
digit = message.get("dtmf", {}).get("digit")
try:
return InputDTMFFrame(KeypadEntry(digit))
except ValueError as e:
# Handle case where string doesn't match any enum value
return None
else:
return None