Skip to content

Commit 2823d16

Browse files
hongj-srcmeta-codesync[bot]
authored andcommitted
Support new metrics
Summary: Support new metrics including is_new flag, versioning and language index Design for updated metrics: https://docs.google.com/document/d/1mZSy6xy-ql9JjznPB80GGS_lopHBsjzzTtGLx4inK2I/edit?tab=t.0 Differential Revision: D84115167 Privacy Context Container: L1384019 fbshipit-source-id: 94598946d81b044872fbd88ab65f2e1ff0e26580
1 parent 5c9ba61 commit 2823d16

2 files changed

Lines changed: 139 additions & 38 deletions

File tree

python/capi_param_builder/capi_param_builder/param_builder.py

Lines changed: 78 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
# This source code is licensed under the license found in the
55
# LICENSE file in the root directory of this source tree.
66

7+
import base64
8+
import os.path
79
import random
810
import re
911
import time
@@ -15,7 +17,7 @@
1517
from .util import EtldPlusOneResolver
1618

1719
DEFAULT_1PC_AGE: Final[int] = 90 * 24 * 3600 # 90 days
18-
LANGUAGE_TOKEN: Final[str] = "Ag"
20+
LANGUAGE_TOKEN: Final[str] = "Ag" # Python
1921
SUPPORTED_LANGUAGES_TOKENS: Final[List[str]] = ["AQ", "Ag", "Aw", "BA", "BQ", "Bg"]
2022
MIN_PAYLOAD_SPLIT_LENGTH: Final[int] = 4
2123
MAX_PAYLOAD_LENGTH_WITH_LANGUAGE_TOKEN: Final[int] = 5
@@ -25,6 +27,12 @@
2527
FBP_COOKIE_NAME: Final[str] = "_fbp"
2628
FBCLID_QUERY_PARAMS: Final[str] = "fbclid"
2729

30+
# Appendix constants - matches JavaScript Constants.js
31+
DEFAULT_FORMAT: Final[int] = 0x01
32+
LANGUAGE_TOKEN_INDEX: Final[int] = 0x02 # Python language token index
33+
APPENDIX_LENGTH_V1: Final[int] = 2
34+
APPENDIX_LENGTH_V2: Final[int] = 8
35+
2836

2937
class ParamBuilder:
3038
"""
@@ -47,6 +55,10 @@ def __init__(self, input: Union[EtldPlusOneResolver, List, None] = None) -> None
4755
self.etld_plus_one: Optional[str] = None
4856
self.domain_list: Optional[List] = None
4957
self.etld_plus_one_resolver: Optional[EtldPlusOneResolver] = None
58+
## Appendix with version number
59+
self.appendix_new: str = self._get_appendix(True)
60+
self.appendix_normal: str = self._get_appendix(False)
61+
5062
if isinstance(input, List):
5163
self.domain_list = []
5264
for domain in input:
@@ -55,6 +67,59 @@ def __init__(self, input: Union[EtldPlusOneResolver, List, None] = None) -> None
5567
elif isinstance(input, EtldPlusOneResolver):
5668
self.etld_plus_one_resolver = input
5769

70+
def _get_version(self) -> str:
71+
"""
72+
Extract version from setup.py file
73+
"""
74+
try:
75+
# Get the directory containing this Python file
76+
current_dir = os.path.dirname(os.path.abspath(__file__))
77+
# Navigate up to find setup.py
78+
setup_py_path = os.path.join(current_dir, "..", "..", "setup.py")
79+
setup_py_path = os.path.normpath(setup_py_path)
80+
81+
if os.path.exists(setup_py_path):
82+
with open(setup_py_path, "r") as f:
83+
content = f.read()
84+
# Extract version using regex
85+
import re
86+
87+
version_match = re.search(
88+
r'version\s*=\s*["\']([^"\']+)["\']', content
89+
)
90+
if version_match:
91+
return version_match.group(1)
92+
# Fallback version if not found
93+
return "1.0.1"
94+
except Exception:
95+
# Fallback version on any error
96+
return "1.0.1"
97+
98+
def _get_appendix(self, is_new: bool) -> str:
99+
version = self._get_version()
100+
version_parts = version.split(".")
101+
major = int(version_parts[0])
102+
minor = int(version_parts[1])
103+
patch = int(version_parts[2])
104+
105+
is_new_byte = 0x01 if is_new else 0x00
106+
107+
bytes_array = [
108+
DEFAULT_FORMAT,
109+
LANGUAGE_TOKEN_INDEX,
110+
is_new_byte,
111+
major,
112+
minor,
113+
patch,
114+
]
115+
116+
# Convert to bytes and then to base64url-safe string
117+
byte_data = bytes(bytes_array)
118+
base64_encoded = base64.b64encode(byte_data).decode("ascii")
119+
# Make it URL-safe by replacing characters
120+
base64url_safe = base64_encoded.replace("+", "-").replace("/", "_").rstrip("=")
121+
return base64url_safe
122+
58123
def _pre_process_cookies(
59124
self, cookies: dict[str, str], cookie_name: str
60125
) -> Optional[str]:
@@ -69,15 +134,18 @@ def _pre_process_cookies(
69134
):
70135
return None
71136

72-
if (
73-
len(cookie_split) == MAX_PAYLOAD_LENGTH_WITH_LANGUAGE_TOKEN
74-
and cookie_split[MAX_PAYLOAD_LENGTH_WITH_LANGUAGE_TOKEN - 1]
75-
not in SUPPORTED_LANGUAGES_TOKENS
76-
):
77-
return None
137+
# Validation for appendix
138+
if len(cookie_split) == MAX_PAYLOAD_LENGTH_WITH_LANGUAGE_TOKEN:
139+
appendix_value = cookie_split[MAX_PAYLOAD_LENGTH_WITH_LANGUAGE_TOKEN - 1]
140+
# Backward compatible with legacy appendix
141+
if len(appendix_value) == APPENDIX_LENGTH_V1:
142+
if appendix_value not in SUPPORTED_LANGUAGES_TOKENS:
143+
return None
144+
elif len(appendix_value) != APPENDIX_LENGTH_V2:
145+
return None
78146

79147
if len(cookie_split) == MIN_PAYLOAD_SPLIT_LENGTH:
80-
updated_cookie = cookie_value + "." + LANGUAGE_TOKEN
148+
updated_cookie = cookie_value + "." + self.appendix_normal
81149
self.cookies_to_set_dict[cookie_name] = CookieSettings(
82150
cookie_name, updated_cookie, self.etld_plus_one, DEFAULT_1PC_AGE
83151
)
@@ -213,7 +281,7 @@ def _get_updated_fbc_cookie(
213281
+ "."
214282
+ new_fbc_payload
215283
+ "."
216-
+ LANGUAGE_TOKEN
284+
+ self.appendix_new
217285
)
218286
# TODO: update etld+1 to get proper etld+1.
219287
udpated_cookie_setting = CookieSettings(
@@ -240,7 +308,7 @@ def _get_updated_fbp_cookie(
240308
+ "."
241309
+ new_fbp_payload
242310
+ "."
243-
+ LANGUAGE_TOKEN
311+
+ self.appendix_new
244312
)
245313
udpated_cookie_setting = CookieSettings(
246314
FBP_COOKIE_NAME, new_fbp, self.etld_plus_one, DEFAULT_1PC_AGE

0 commit comments

Comments
 (0)