3838from entrabot .graph_helpers import odata_escape
3939from entrabot .platform import get_credential_store
4040from entrabot .tools .audit import log_event
41- from entrabot .tools .rate_limit import RetryOn429Transport
41+ from entrabot .tools .rate_limit import RetryOn429Transport , parse_retry_after
4242from entrabot .url_safety import _is_graph_url
4343
4444logger = logging .getLogger ("entrabot.tools.teams" )
@@ -336,9 +336,7 @@ async def create_one_on_one_chat(
336336 agent_member : dict = {
337337 "@odata.type" : "#microsoft.graph.aadUserConversationMember" ,
338338 "roles" : ["owner" ],
339- 340- f"https://graph.microsoft.com/v1.0/users('{ _agent_bind_value } ')"
341- ),
339+ "[email protected] " : (
f"https://graph.microsoft.com/v1.0/users('{ _agent_bind_value } ')" ),
342340 }
343341
344342 payload = {
@@ -371,7 +369,7 @@ async def create_one_on_one_chat(
371369 logger .error ("400 creating 1:1 chat: %s" , error_msg )
372370 raise ValueError (f"Graph API rejected chat creation: { error_msg } " )
373371 if resp .status_code == 429 :
374- retry_after = int (resp .headers .get ("Retry-After" , "60" ) )
372+ retry_after = parse_retry_after (resp .headers .get ("Retry-After" ), default = 60 )
375373 raise RateLimitError (retry_after )
376374 resp .raise_for_status ()
377375
@@ -499,7 +497,7 @@ async def create_or_find_chat(
499497 "their actual UPN. Check ENTRABOT_HUMAN_USER_MAILS in .env."
500498 )
501499 if resp .status_code == 429 :
502- retry_after = int (resp .headers .get ("Retry-After" , "60" ) )
500+ retry_after = parse_retry_after (resp .headers .get ("Retry-After" ), default = 60 )
503501 raise RateLimitError (retry_after )
504502 resp .raise_for_status ()
505503
@@ -635,9 +633,7 @@ async def add_member(
635633 (
636634 s
637635 for s in sponsors
638- if any (
639- e .lower () == requester_lower for e in s .email_identifiers ()
640- )
636+ if any (e .lower () == requester_lower for e in s .email_identifiers ())
641637 ),
642638 None ,
643639 )
@@ -711,13 +707,8 @@ async def add_member(
711707 # Gate 2 (defense-in-depth): existing Graph membership check.
712708 members = await _fetch_chat_members_for_gate (chat_id )
713709 matched_user_id = matched_sponsor .user_id .lower ()
714- if not any (
715- (m .get ("user_id" ) or "" ).strip ().lower () == matched_user_id
716- for m in members
717- ):
718- err = RequesterNotInChatError (
719- requester = requester_email , chat_id = chat_id
720- )
710+ if not any ((m .get ("user_id" ) or "" ).strip ().lower () == matched_user_id for m in members ):
711+ err = RequesterNotInChatError (requester = requester_email , chat_id = chat_id )
721712 log_event (
722713 action = "teams.add_member" ,
723714 resource = audit_resource ,
@@ -771,11 +762,9 @@ async def add_member(
771762 error_msg = resp .text or "Not found"
772763 raise ChatNotFound (f"Could not add member: { error_msg } " )
773764 if resp .status_code == 401 :
774- raise TokenExpiredError (
775- "Agent User token expired — re-acquire via three-hop flow"
776- )
765+ raise TokenExpiredError ("Agent User token expired — re-acquire via three-hop flow" )
777766 if resp .status_code == 429 :
778- retry_after = int (resp .headers .get ("Retry-After" , "60" ) )
767+ retry_after = parse_retry_after (resp .headers .get ("Retry-After" ), default = 60 )
779768 raise RateLimitError (retry_after )
780769 resp .raise_for_status ()
781770
@@ -842,7 +831,7 @@ async def list_members(
842831 if resp .status_code == 401 :
843832 raise TokenExpiredError ("Agent User token expired — re-acquire via three-hop flow" )
844833 if resp .status_code == 429 :
845- retry_after = int (resp .headers .get ("Retry-After" , "60" ) )
834+ retry_after = parse_retry_after (resp .headers .get ("Retry-After" ), default = 60 )
846835 raise RateLimitError (retry_after )
847836 resp .raise_for_status ()
848837
@@ -929,7 +918,7 @@ async def send(
929918 if resp .status_code == 401 :
930919 raise TokenExpiredError ("Token expired — re-acquire" )
931920 if resp .status_code == 429 :
932- retry_after = int (resp .headers .get ("Retry-After" , "60" ) )
921+ retry_after = parse_retry_after (resp .headers .get ("Retry-After" ), default = 60 )
933922 raise RateLimitError (retry_after )
934923 if resp .status_code == 404 :
935924 raise ChatNotFound (f"Chat { chat_id } not found" )
@@ -985,7 +974,7 @@ async def post_thinking_placeholder(
985974 if resp .status_code == 404 :
986975 raise ChatNotFound (f"Chat { chat_id } not found" )
987976 if resp .status_code == 429 :
988- retry_after = int (resp .headers .get ("Retry-After" , "60" ) )
977+ retry_after = parse_retry_after (resp .headers .get ("Retry-After" ), default = 60 )
989978 raise RateLimitError (retry_after )
990979 resp .raise_for_status ()
991980
@@ -1039,7 +1028,7 @@ async def update_placeholder(
10391028 if resp .status_code == 401 :
10401029 raise TokenExpiredError ("Token expired — re-acquire" )
10411030 if resp .status_code == 429 :
1042- retry_after = int (resp .headers .get ("Retry-After" , "60" ) )
1031+ retry_after = parse_retry_after (resp .headers .get ("Retry-After" ), default = 60 )
10431032 raise RateLimitError (retry_after )
10441033 if 200 <= resp .status_code < 300 :
10451034 return {"message_id" : placeholder_id , "mode" : "edit" }
@@ -1098,7 +1087,7 @@ async def resolve_placeholder(
10981087 if resp .status_code == 401 :
10991088 raise TokenExpiredError ("Token expired — re-acquire" )
11001089 if resp .status_code == 429 :
1101- retry_after = int (resp .headers .get ("Retry-After" , "60" ) )
1090+ retry_after = parse_retry_after (resp .headers .get ("Retry-After" ), default = 60 )
11021091 raise RateLimitError (retry_after )
11031092 if 200 <= resp .status_code < 300 :
11041093 return {"message_id" : placeholder_id , "mode" : "edit" }
@@ -1121,7 +1110,7 @@ async def resolve_placeholder(
11211110 if sd_resp .status_code == 401 :
11221111 raise TokenExpiredError ("Token expired — re-acquire" )
11231112 if sd_resp .status_code == 429 :
1124- retry_after = int (sd_resp .headers .get ("Retry-After" , "60" ) )
1113+ retry_after = parse_retry_after (sd_resp .headers .get ("Retry-After" ), default = 60 )
11251114 raise RateLimitError (retry_after )
11261115 delete_ok = 200 <= sd_resp .status_code < 300
11271116 if not delete_ok :
@@ -1198,7 +1187,7 @@ async def delete_chat_message(
11981187 if resp .status_code == 401 :
11991188 raise TokenExpiredError ("Token expired — re-acquire" )
12001189 if resp .status_code == 429 :
1201- retry_after = int (resp .headers .get ("Retry-After" , "60" ) )
1190+ retry_after = parse_retry_after (resp .headers .get ("Retry-After" ), default = 60 )
12021191 raise RateLimitError (retry_after )
12031192 if 200 <= resp .status_code < 300 :
12041193 return True
@@ -1234,7 +1223,7 @@ async def fetch_hosted_image(*, token: str, url: str) -> bytes | None:
12341223 if resp .status_code == 404 :
12351224 return None
12361225 if resp .status_code == 429 :
1237- retry_after = int (resp .headers .get ("Retry-After" , "60" ) )
1226+ retry_after = parse_retry_after (resp .headers .get ("Retry-After" ), default = 60 )
12381227 raise RateLimitError (retry_after )
12391228 resp .raise_for_status ()
12401229 return resp .content
@@ -1324,7 +1313,7 @@ async def fetch_message(
13241313 if resp .status_code == 401 :
13251314 raise TokenExpiredError ("Token expired — re-acquire" )
13261315 if resp .status_code == 429 :
1327- retry_after = int (resp .headers .get ("Retry-After" , "60" ) )
1316+ retry_after = parse_retry_after (resp .headers .get ("Retry-After" ), default = 60 )
13281317 raise RateLimitError (retry_after )
13291318 if not (200 <= resp .status_code < 300 ):
13301319 return None
@@ -1366,7 +1355,7 @@ async def read(
13661355 if resp .status_code == 404 :
13671356 raise ChatNotFound (f"Chat { chat_id } not found" )
13681357 if resp .status_code == 429 :
1369- retry_after = int (resp .headers .get ("Retry-After" , "60" ) )
1358+ retry_after = parse_retry_after (resp .headers .get ("Retry-After" ), default = 60 )
13701359 raise RateLimitError (retry_after )
13711360 resp .raise_for_status ()
13721361
0 commit comments