4141_HTML_TAG_PATTERN = re .compile (r"<[^>]{1,500}>" )
4242_MAX_ACTION_COUNT_GAP = 64
4343_MAX_RATE_SPAN = 200
44+ _MAX_TELEMETRY_REWARD_SPAN = 320
45+ _TELEMETRY_IDENTITY_PATTERN = re .compile (
46+ r"\b(?:telemetry|app(?:lication)?[- ]+(?:metadata|url|name)|"
47+ r"app[_ -]?url|app[_ -]?name|x-app-(?:url|name))\b" ,
48+ re .IGNORECASE ,
49+ )
50+ _TELEMETRY_STRONG_REWARD_PATTERN = re .compile (
51+ r"\b(?:bonus|increase(?:s|d)?|unlock(?:s|ed)?|"
52+ r"earn(?:s|ed)?|grant(?:s|ed)?|reward(?:s|ed)?|boost(?:s|ed)?)\b" ,
53+ re .IGNORECASE ,
54+ )
55+ _TELEMETRY_MODIFIER_REWARD_PATTERN = re .compile (
56+ r"\b(?:more|extra|additional)\b" , re .IGNORECASE
57+ )
58+ _TELEMETRY_QUOTA_SIGNAL_PATTERN = re .compile (
59+ r"\b(?:api[- ]+)?(?:requests?|calls?|quota|limits?|allowances?|credits?)\b|"
60+ r"(?<![\w.])\d+(?:\.\d+)?\s*%" ,
61+ re .IGNORECASE ,
62+ )
63+ _TELEMETRY_MODIFIER_GAP_WORDS = {
64+ "account" ,
65+ "annual" ,
66+ "api" ,
67+ "call" ,
68+ "daily" ,
69+ "hourly" ,
70+ "monthly" ,
71+ "quota" ,
72+ "rate" ,
73+ "request" ,
74+ "usage" ,
75+ }
76+ _MAX_STRONG_REWARD_SPAN = 160
4477BLOCKED : Sequence [Tuple [str , Pattern [str ]]] = (
4578 ("real-time claim" , re .compile (r"\breal[ -]?time\b" , re .IGNORECASE )),
4679 (
@@ -230,6 +263,52 @@ def _fixed_rate_claims(text: str) -> List[str]:
230263 return claims
231264
232265
266+ def _telemetry_reward_claims (text : str ) -> List [str ]:
267+ """Find attribution identity + reward + quota signals in one bounded sentence."""
268+ claims : List [str ] = []
269+ seen : Set [Tuple [int , str ]] = set ()
270+
271+ for segment_offset , segment in _bounded_rate_segments (text ):
272+ searchable = _HTML_TAG_PATTERN .sub (" " , segment )
273+ identities = list (_TELEMETRY_IDENTITY_PATTERN .finditer (searchable ))
274+ quota_signals = list (_TELEMETRY_QUOTA_SIGNAL_PATTERN .finditer (searchable ))
275+ strong_rewards = list (_TELEMETRY_STRONG_REWARD_PATTERN .finditer (searchable ))
276+ modifier_rewards = list (_TELEMETRY_MODIFIER_REWARD_PATTERN .finditer (searchable ))
277+ reward_pairs : List [Tuple [int , int ]] = []
278+ for reward in strong_rewards :
279+ for quota_signal in quota_signals :
280+ start = min (reward .start (), quota_signal .start ())
281+ end = max (reward .end (), quota_signal .end ())
282+ if end - start <= _MAX_STRONG_REWARD_SPAN :
283+ reward_pairs .append ((start , end ))
284+ for reward in modifier_rewards :
285+ for quota_signal in quota_signals :
286+ if reward .end () > quota_signal .start ():
287+ continue
288+ gap = searchable [reward .end () : quota_signal .start ()]
289+ gap_words = re .findall (r"[a-z]+" , gap .lower ())
290+ if len (gap ) <= 48 and all (
291+ word in _TELEMETRY_MODIFIER_GAP_WORDS for word in gap_words
292+ ):
293+ reward_pairs .append ((reward .start (), quota_signal .end ()))
294+ for identity in identities :
295+ candidates = [
296+ (min (identity .start (), start ), max (identity .end (), end ))
297+ for start , end in reward_pairs
298+ if max (identity .end (), end ) - min (identity .start (), start )
299+ <= _MAX_TELEMETRY_REWARD_SPAN
300+ ]
301+ if not candidates :
302+ continue
303+ start , end = min (candidates , key = lambda span : span [1 ] - span [0 ])
304+ claim = re .sub (r"\s+" , " " , searchable [start :end ]).strip ()
305+ key = (segment_offset + start , claim )
306+ if key not in seen :
307+ seen .add (key )
308+ claims .append (claim )
309+ return claims
310+
311+
233312def _claim_failures (root : Path , surfaces : Iterable [Path ]) -> List [str ]:
234313 failures : List [str ] = []
235314 for path in surfaces :
@@ -243,6 +322,10 @@ def _claim_failures(root: Path, surfaces: Iterable[Path]) -> List[str]:
243322 failures .append (
244323 f"{ path .relative_to (root )} : fixed demo rate matched { claim !r} "
245324 )
325+ for claim in _telemetry_reward_claims (text ):
326+ failures .append (
327+ f"{ path .relative_to (root )} : telemetry quota reward matched { claim !r} "
328+ )
246329 return failures
247330
248331
0 commit comments