@@ -34,7 +34,7 @@ import { randomString, secureRandomBase64Url } from "../randomstring.ts";
3434import  {  EncryptionKeysEventContent  }  from  "./types.ts" ; 
3535import  {  decodeBase64 ,  encodeUnpaddedBase64  }  from  "../base64.ts" ; 
3636import  {  KnownMembership  }  from  "../@types/membership.ts" ; 
37- import  {  MatrixError ,  safeGetRetryAfterMs  }  from  "../http-api/errors.ts" ; 
37+ import  {  HTTPError ,   MatrixError ,  safeGetRetryAfterMs  }  from  "../http-api/errors.ts" ; 
3838import  {  MatrixEvent  }  from  "../models/event.ts" ; 
3939import  {  isLivekitFocusActive  }  from  "./LivekitFocus.ts" ; 
4040import  {  ExperimentalGroupCallRoomMemberState  }  from  "../webrtc/groupCall.ts" ; 
@@ -1031,18 +1031,17 @@ export class MatrixRTCSession extends TypedEventEmitter<MatrixRTCSessionEvent, M
10311031                const  prepareDelayedDisconnection  =  async  ( ) : Promise < void >  =>  { 
10321032                    try  { 
10331033                        // TODO: If delayed event times out, re-join! 
1034-                         const  res  =  await  this . client . _unstable_sendDelayedStateEvent ( 
1034+                         const  res  =  await  resendIfRateLimited ( ( )   =>   this . client . _unstable_sendDelayedStateEvent ( 
10351035                            this . room . roomId , 
10361036                            { 
10371037                                delay : 8000 , 
10381038                            } , 
10391039                            EventType . GroupCallMemberPrefix , 
10401040                            { } ,  // leave event 
10411041                            stateKey , 
1042-                         ) ; 
1042+                         ) ) ; 
10431043                        this . disconnectDelayId  =  res . delay_id ; 
10441044                    }  catch  ( e )  { 
1045-                         // TODO: Retry if rate-limited 
10461045                        logger . error ( "Failed to prepare delayed disconnection event:" ,  e ) ; 
10471046                    } 
10481047                } ; 
@@ -1058,12 +1057,12 @@ export class MatrixRTCSession extends TypedEventEmitter<MatrixRTCSessionEvent, M
10581057                // TODO: Remove this once MSC4140 is stable & doesn't cancel own delayed state 
10591058                if  ( this . disconnectDelayId  !==  undefined )  { 
10601059                    try  { 
1061-                         await  this . client . _unstable_updateDelayedEvent ( 
1062-                             this . disconnectDelayId , 
1060+                         const  knownDisconnectDelayId  =  this . disconnectDelayId ; 
1061+                         await  resendIfRateLimited ( ( )  =>  this . client . _unstable_updateDelayedEvent ( 
1062+                             knownDisconnectDelayId , 
10631063                            UpdateDelayedEventAction . Restart , 
1064-                         ) ; 
1064+                         ) ) ; 
10651065                    }  catch  ( e )  { 
1066-                         // TODO: Make embedded client include errcode, and retry only if not M_NOT_FOUND (or rate-limited) 
10671066                        logger . warn ( "Failed to update delayed disconnection event, prepare it again:" ,  e ) ; 
10681067                        this . disconnectDelayId  =  undefined ; 
10691068                        await  prepareDelayedDisconnection ( ) ; 
@@ -1076,13 +1075,13 @@ export class MatrixRTCSession extends TypedEventEmitter<MatrixRTCSessionEvent, M
10761075                let  sentDelayedDisconnect  =  false ; 
10771076                if  ( this . disconnectDelayId  !==  undefined )  { 
10781077                    try  { 
1079-                         await  this . client . _unstable_updateDelayedEvent ( 
1080-                             this . disconnectDelayId , 
1078+                         const  knownDisconnectDelayId  =  this . disconnectDelayId ; 
1079+                         await  resendIfRateLimited ( ( )  =>  this . client . _unstable_updateDelayedEvent ( 
1080+                             knownDisconnectDelayId , 
10811081                            UpdateDelayedEventAction . Send , 
1082-                         ) ; 
1082+                         ) ) ; 
10831083                        sentDelayedDisconnect  =  true ; 
10841084                    }  catch  ( e )  { 
1085-                         // TODO: Retry if rate-limited 
10861085                        logger . error ( "Failed to send our delayed disconnection event:" ,  e ) ; 
10871086                    } 
10881087                    this . disconnectDelayId  =  undefined ; 
@@ -1111,10 +1110,10 @@ export class MatrixRTCSession extends TypedEventEmitter<MatrixRTCSessionEvent, M
11111110
11121111    private  readonly  delayDisconnection  =  async  ( ) : Promise < void >  =>  { 
11131112        try  { 
1114-             await  this . client . _unstable_updateDelayedEvent ( this . disconnectDelayId ! ,  UpdateDelayedEventAction . Restart ) ; 
1113+             const  knownDisconnectDelayId  =  this . disconnectDelayId ! ; 
1114+             await  resendIfRateLimited ( ( )  =>  this . client . _unstable_updateDelayedEvent ( knownDisconnectDelayId ,  UpdateDelayedEventAction . Restart ) ) ; 
11151115            this . scheduleDelayDisconnection ( ) ; 
11161116        }  catch  ( e )  { 
1117-             // TODO: Retry if rate-limited 
11181117            logger . error ( "Failed to delay our disconnection event:" ,  e ) ; 
11191118        } 
11201119    } ; 
@@ -1162,3 +1161,27 @@ export class MatrixRTCSession extends TypedEventEmitter<MatrixRTCSessionEvent, M
11621161        this . sendEncryptionKeysEvent ( newKeyIndex ) ; 
11631162    } ; 
11641163} 
1164+ 
1165+ async  function  resendIfRateLimited < T > ( func : ( )  =>  Promise < T > ,  numRetriesAllowed : number  =  1 ) : Promise < T >  { 
1166+     while  ( true )  { 
1167+         try  { 
1168+             return  await  func ( ) ; 
1169+         }  catch  ( e )  { 
1170+             if  ( numRetriesAllowed  >  0  &&  e  instanceof  HTTPError  &&  e . isRateLimitError ( ) )  { 
1171+                 numRetriesAllowed -- ; 
1172+                 let  resendDelay : number ; 
1173+                 const  defaultMs  =  5000 ; 
1174+                 try  { 
1175+                     resendDelay  =  e . getRetryAfterMs ( )  ??  defaultMs ; 
1176+                     logger . info ( `Rate limited by server, retrying in ${ resendDelay }  ms` ) ; 
1177+                 }  catch  ( e )  { 
1178+                     logger . warn ( `Error while retrieving a rate-limit retry delay, retrying after default delay of ${ defaultMs }  ` ,  e ) ; 
1179+                     resendDelay  =  defaultMs ; 
1180+                 } 
1181+                 await  sleep ( resendDelay ) ; 
1182+             }  else  { 
1183+                 throw  e ; 
1184+             } 
1185+         } 
1186+     } ; 
1187+ } 
0 commit comments