40
40
import sys
41
41
from pathlib import Path
42
42
from typing import Any , Dict
43
+ import atexit
43
44
44
45
import aiohttp
45
46
from dotenv import load_dotenv
@@ -155,6 +156,20 @@ async def mute_customer(action: dict, flow_manager: FlowManager):
155
156
)
156
157
157
158
159
+ async def start_hold_music (action : dict , flow_manager : FlowManager ):
160
+ hold_music_args = flow_manager .state ["hold_music_args" ]
161
+ flow_manager .state ["hold_music_process" ] = await asyncio .create_subprocess_exec (
162
+ sys .executable ,
163
+ str (hold_music_args ["script_path" ]),
164
+ "-m" ,
165
+ hold_music_args ["room_url" ],
166
+ "-t" ,
167
+ hold_music_args ["token" ],
168
+ "-i" ,
169
+ hold_music_args ["wav_file_path" ],
170
+ )
171
+
172
+
158
173
async def make_customer_hear_only_hold_music (action : dict , flow_manager : FlowManager ):
159
174
"""Make it so the customer only hears hold music.
160
175
@@ -167,15 +182,7 @@ async def make_customer_hear_only_hold_music(action: dict, flow_manager: FlowMan
167
182
await transport .update_remote_participants (
168
183
remote_participants = {
169
184
customer_participant_id : {
170
- "permissions" : {
171
- "canReceive" : {
172
- "byUserId" : {
173
- "bot" : {
174
- "customAudio" : {"hold-music" : True },
175
- }
176
- }
177
- }
178
- }
185
+ "permissions" : {"canReceive" : {"byUserId" : {"hold-music" : True }}}
179
186
}
180
187
}
181
188
)
@@ -373,6 +380,7 @@ def create_transferring_to_human_agent_node() -> NodeConfig:
373
380
ActionConfig (type = "function" , handler = mute_customer ),
374
381
],
375
382
post_actions = [
383
+ ActionConfig (type = "function" , handler = start_hold_music ),
376
384
ActionConfig (type = "function" , handler = make_customer_hear_only_hold_music ),
377
385
ActionConfig (type = "function" , handler = print_human_agent_join_url ),
378
386
],
@@ -472,7 +480,6 @@ def get_human_agent_participant_id(transport: DailyTransport) -> str:
472
480
473
481
async def get_customer_token (daily_rest_helper : DailyRESTHelper , room_url : str ) -> str :
474
482
"""Gets a Daily token for the customer, configured with properties:
475
-
476
483
{
477
484
user_id: "customer",
478
485
permissions: {
@@ -484,50 +491,61 @@ async def get_customer_token(daily_rest_helper: DailyRESTHelper, room_url: str)
484
491
}
485
492
}
486
493
}
487
-
488
- Note that they'll join only being able to hear the bot.
489
494
"""
490
495
return await get_token (
491
496
user_id = "customer" ,
492
- permissions = {"canReceive" : {"base" : False , "byUserId" : {"bot" : True }}},
497
+ permissions = {
498
+ "canReceive" : {
499
+ "base" : False ,
500
+ "byUserId" : {
501
+ "bot" : True ,
502
+ },
503
+ }
504
+ },
493
505
daily_rest_helper = daily_rest_helper ,
494
506
room_url = room_url ,
495
507
)
496
508
497
509
498
510
async def get_human_agent_token (daily_rest_helper : DailyRESTHelper , room_url : str ) -> str :
499
511
"""Gets a Daily token for the human agent, configured with properties:
500
-
501
512
{
502
513
user_id: "agent",
503
514
permissions: {
504
515
canReceive: {
505
516
base: false,
506
517
byUserId: {
507
- bot: {
508
- audio: true,
509
- customAudio: { "hold-music": false }
510
- }
518
+ bot: true
511
519
}
512
520
}
513
521
}
514
522
}
515
-
516
- Note that they'll join only being able to hear the bot's audio (and not the hold music).
517
523
"""
518
524
return await get_token (
519
525
user_id = "agent" ,
520
526
permissions = {
521
527
"canReceive" : {
522
528
"base" : False ,
523
- "byUserId" : {"bot" : {"audio" : True , "customAudio" : {"hold-music" : False }}},
529
+ "byUserId" : {
530
+ "bot" : True ,
531
+ },
524
532
}
525
533
},
526
534
daily_rest_helper = daily_rest_helper ,
527
535
room_url = room_url ,
528
536
)
529
537
530
538
539
+ async def get_hold_music_player_token (daily_rest_helper : DailyRESTHelper , room_url : str ) -> str :
540
+ """Gets a Daily token for the hold music player"""
541
+ return await get_token (
542
+ user_id = "hold-music" ,
543
+ permissions = {},
544
+ daily_rest_helper = daily_rest_helper ,
545
+ room_url = room_url ,
546
+ )
547
+
548
+
531
549
async def get_token (
532
550
user_id : str , permissions : dict , daily_rest_helper : DailyRESTHelper , room_url : str
533
551
) -> str :
@@ -622,11 +640,11 @@ async def on_participant_left(
622
640
):
623
641
# NOTE: an opportunity for refinement here is to handle the customer leaving while on
624
642
# hold, informing the human agent if needed
625
- """If all non-bot participants have left, stop the bot"""
626
- non_bot_participants = {
627
- k : v for k , v in transport .participants ().items () if not v [ "info" ][ "isLocal" ]
643
+ """If all human participants have left, stop the bot"""
644
+ human_participants = {
645
+ k : v for k , v in transport .participants ().items () if v . get ( "info" , {}). get ( "userId" ) in { "agent" , "customer" }
628
646
}
629
- if not non_bot_participants :
647
+ if not human_participants :
630
648
await task .cancel ()
631
649
632
650
# Print URL for joining as customer, and store URL for joining as human agent, to be printed later
@@ -649,6 +667,27 @@ async def on_participant_left(
649
667
f"{ room_url } { '?' if '?' not in room_url else '&' } t={ human_agent_token } "
650
668
)
651
669
670
+ # Prepare hold music args
671
+ flow_manager .state ["hold_music_args" ] = {
672
+ "script_path" : Path (__file__ ).parent / "hold_music" / "hold_music.py" ,
673
+ "wav_file_path" : Path (__file__ ).parent / "hold_music" / "hold_music.wav" ,
674
+ "room_url" : room_url ,
675
+ "token" : await get_hold_music_player_token (
676
+ daily_rest_helper = daily_rest_helper , room_url = room_url
677
+ ),
678
+ }
679
+
680
+ # Clean up hold music process at exit, if needed
681
+ def cleanup_hold_music_process ():
682
+ hold_music_process = flow_manager .state .get ("hold_music_process" )
683
+ if hold_music_process :
684
+ try :
685
+ hold_music_process .terminate ()
686
+ except :
687
+ # Exception if process already done; we don't care, it didn't hurt to try
688
+ pass
689
+ atexit .register (cleanup_hold_music_process )
690
+
652
691
# Run the pipeline
653
692
runner = PipelineRunner ()
654
693
await runner .run (task )
0 commit comments