125 lines
3.9 KiB
Python
125 lines
3.9 KiB
Python
|
|
"""Telephony service for managing SIP dispatch rules for room access."""
|
||
|
|
|
||
|
|
from logging import getLogger
|
||
|
|
|
||
|
|
from asgiref.sync import async_to_sync
|
||
|
|
from livekit.api import TwirpError
|
||
|
|
from livekit.protocol.sip import (
|
||
|
|
CreateSIPDispatchRuleRequest,
|
||
|
|
DeleteSIPDispatchRuleRequest,
|
||
|
|
ListSIPDispatchRuleRequest,
|
||
|
|
SIPDispatchRule,
|
||
|
|
SIPDispatchRuleDirect,
|
||
|
|
)
|
||
|
|
|
||
|
|
from core import utils
|
||
|
|
|
||
|
|
logger = getLogger(__name__)
|
||
|
|
|
||
|
|
|
||
|
|
class TelephonyException(Exception):
|
||
|
|
"""Exception raised when telephony operations fail."""
|
||
|
|
|
||
|
|
|
||
|
|
class TelephonyService:
|
||
|
|
"""Service for managing participant access through the telephony system (SIP)."""
|
||
|
|
|
||
|
|
def _rule_name(self, room_id):
|
||
|
|
"""Generate the rule name for a room based on its ID."""
|
||
|
|
return f"SIP_{str(room_id)}"
|
||
|
|
|
||
|
|
@async_to_sync
|
||
|
|
async def create_dispatch_rule(self, room):
|
||
|
|
"""Create a SIP inbound dispatch rule for direct room routing.
|
||
|
|
|
||
|
|
Configures telephony to route incoming SIP calls directly to the specified room
|
||
|
|
using the room's ID and PIN code for authentication.
|
||
|
|
"""
|
||
|
|
|
||
|
|
direct_rule = SIPDispatchRule(
|
||
|
|
dispatch_rule_direct=SIPDispatchRuleDirect(
|
||
|
|
room_name=str(room.id), pin=str(room.pin_code)
|
||
|
|
)
|
||
|
|
)
|
||
|
|
|
||
|
|
request = CreateSIPDispatchRuleRequest(
|
||
|
|
rule=direct_rule, name=self._rule_name(room.id)
|
||
|
|
)
|
||
|
|
|
||
|
|
lkapi = utils.create_livekit_client()
|
||
|
|
|
||
|
|
try:
|
||
|
|
await lkapi.sip.create_sip_dispatch_rule(create=request)
|
||
|
|
except TwirpError as e:
|
||
|
|
logger.exception(
|
||
|
|
"Unexpected error creating dispatch rule for room %s", room.id
|
||
|
|
)
|
||
|
|
raise TelephonyException("Could not create dispatch rule") from e
|
||
|
|
|
||
|
|
finally:
|
||
|
|
await lkapi.aclose()
|
||
|
|
|
||
|
|
async def _list_dispatch_rules_ids(self, room_id):
|
||
|
|
"""List SIP dispatch rule IDs for a specific room.
|
||
|
|
|
||
|
|
Fetches all existing SIP dispatch rules and filters them by room name
|
||
|
|
since LiveKit API doesn't support server-side filtering by 'room_name'.
|
||
|
|
This approach is acceptable for moderate scale but may need refactoring
|
||
|
|
for high-volume scenarios.
|
||
|
|
|
||
|
|
Note:
|
||
|
|
Feature request for server-side filtering: livekit/sip#405
|
||
|
|
"""
|
||
|
|
|
||
|
|
lkapi = utils.create_livekit_client()
|
||
|
|
|
||
|
|
try:
|
||
|
|
existing_rules = await lkapi.sip.list_sip_dispatch_rule(
|
||
|
|
list=ListSIPDispatchRuleRequest()
|
||
|
|
)
|
||
|
|
except TwirpError as e:
|
||
|
|
logger.exception("Failed to list dispatch rules for room %s", room_id)
|
||
|
|
raise TelephonyException("Could not list dispatch rules") from e
|
||
|
|
finally:
|
||
|
|
await lkapi.aclose()
|
||
|
|
|
||
|
|
if not existing_rules or not existing_rules.items:
|
||
|
|
return []
|
||
|
|
|
||
|
|
rule_name = self._rule_name(room_id)
|
||
|
|
|
||
|
|
return [
|
||
|
|
existing_rule.sip_dispatch_rule_id
|
||
|
|
for existing_rule in existing_rules.items
|
||
|
|
if existing_rule.name == rule_name
|
||
|
|
]
|
||
|
|
|
||
|
|
@async_to_sync
|
||
|
|
async def delete_dispatch_rule(self, room_id):
|
||
|
|
"""Delete all SIP inbound dispatch rules associated with a specific room."""
|
||
|
|
|
||
|
|
rules_ids = await self._list_dispatch_rules_ids(room_id)
|
||
|
|
|
||
|
|
if not rules_ids:
|
||
|
|
logger.info("No dispatch rules found for room %s", room_id)
|
||
|
|
return False
|
||
|
|
|
||
|
|
if len(rules_ids) > 1:
|
||
|
|
logger.error("Multiple dispatch rules found for room %s", room_id)
|
||
|
|
|
||
|
|
lkapi = utils.create_livekit_client()
|
||
|
|
try:
|
||
|
|
for rule_id in rules_ids:
|
||
|
|
await lkapi.sip.delete_sip_dispatch_rule(
|
||
|
|
delete=DeleteSIPDispatchRuleRequest(sip_dispatch_rule_id=rule_id)
|
||
|
|
)
|
||
|
|
|
||
|
|
return True
|
||
|
|
|
||
|
|
except TwirpError as e:
|
||
|
|
logger.exception("Failed to delete dispatch rules for room %s", room_id)
|
||
|
|
raise TelephonyException("Could not delete dispatch rules") from e
|
||
|
|
|
||
|
|
finally:
|
||
|
|
await lkapi.aclose()
|