Skip to content

Commit e91d7f3

Browse files
authored
handles EU cookie consent (#91)
* handles eu cookie consent * embedded eu cookie consent into the app * implemented further passengers options (children and infants)
1 parent 913173c commit e91d7f3

File tree

6 files changed

+268
-54
lines changed

6 files changed

+268
-54
lines changed

docs/filters.md

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,23 @@ passengers = Passengers(
6767
)
6868
```
6969

70+
CLI & programmatic examples
71+
72+
- From the bundled example CLI (`example.py`) you can supply the number of children with the `--children` flag:
73+
74+
```bash
75+
python example.py --origin LHR --destination SLC --depart_date 2026-05-23 --return_date 2026-05-30 --adults 2 --children 1
76+
```
77+
78+
- Programmatically, pass the children count into `Passengers` when creating a filter or calling `get_flights`:
79+
80+
```python
81+
from fast_flights import Passengers, create_filter, get_flights
82+
83+
passengers = Passengers(adults=2, children=1)
84+
# use create_filter(...) or pass passengers to get_flights(...)
85+
```
86+
7087
## Example
7188
Here's a simple example on how to create a filter:
7289

@@ -88,3 +105,64 @@ filter: TFSData = create_filter(
88105
filter.as_b64() # Base64-encoded (bytes)
89106
filter.to_string() # Serialize to string
90107
```
108+
109+
## Passing cookies (binary parameter)
110+
111+
Both `get_flights_from_filter(...)` and `get_flights(...)` accept a new optional parameter `cookies: bytes | None` which allows you to embed cookies as a binary payload that will be forwarded to the underlying fetchers.
112+
113+
Supported cookie formats (the function will try them in this order):
114+
115+
- JSON bytes: UTF-8 JSON encoding of a dict (e.g. `{ "CONSENT": "...", "SOCS": "..." }`). This will be set as `request_kwargs['cookies']`.
116+
- Pickle bytes: a pickled `dict` of cookie-name -> value; this will be set as `request_kwargs['cookies']`.
117+
- Raw Cookie header: if the bytes can't be parsed as JSON or pickle, they are decoded as UTF-8 and set as the `Cookie` HTTP header (`request_kwargs['headers']['Cookie']`).
118+
119+
Examples:
120+
121+
- JSON-encoded cookies as bytes (recommended):
122+
123+
```python
124+
import json
125+
cookies = {"CONSENT": "PENDING+987", "SOCS": "CAESH..."}
126+
cookies_bytes = json.dumps(cookies).encode("utf-8")
127+
result = get_flights_from_filter(filter, cookies=cookies_bytes)
128+
```
129+
130+
- Raw header example (when you already have a Cookie header string):
131+
132+
```python
133+
cookies_bytes = b"CONSENT=PENDING+987; SOCS=CAESH..."
134+
result = get_flights_from_filter(filter, cookies=cookies_bytes)
135+
```
136+
137+
Backward compatibility:
138+
139+
- You can still pass cookies the old way via `request_kwargs={'cookies': {'CONSENT': '...', 'SOCS': '...'}}` — this is honored when `cookies` (binary) is not provided.
140+
- If both `cookies` (binary) and `request_kwargs` are provided, the parsed binary `cookies` take precedence and will override any `cookies` or `Cookie` header in `request_kwargs`.
141+
142+
Security and privacy note:
143+
144+
- Cookies may contain sensitive values. Avoid logging or committing cookies into source control and only supply cookies you trust.
145+
146+
### Configurable default consent cookies
147+
148+
The library embeds a small default consent cookie bundle used to bypass common Google consent gating. If you prefer to disable or explicitly control this behavior, both `get_flights_from_filter` and `get_flights` accept a boolean flag `cookie_consent: bool` (default `True`).
149+
150+
- To use the embedded default cookies (the default behavior):
151+
152+
```python
153+
# will apply embedded default cookies unless you provide cookies/request_kwargs explicitly
154+
result = get_flights_from_filter(filter, cookie_consent=True)
155+
```
156+
157+
- To disable automatic application of the embedded default cookies:
158+
159+
```python
160+
# will NOT add any embedded cookies; you can still pass cookies explicitly via request_kwargs or the binary `cookies` parameter
161+
result = get_flights_from_filter(filter, cookie_consent=False)
162+
```
163+
164+
You can also pass the flag through `get_flights`:
165+
166+
```python
167+
result = get_flights(..., cookie_consent=False)
168+
```

example.py

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ def main():
2929
parser.add_argument('--depart_date', required=True, help="Beginning trip date (YYYY-MM-DD)")
3030
parser.add_argument('--return_date', required=True, help="Ending trip date (YYYY-MM-DD)")
3131
parser.add_argument('--adults', type=int, default=1, help="Number of adult passengers")
32+
parser.add_argument('--children', type=int, default=0, help="Number of children passengers")
3233
parser.add_argument('--type', type=str, default="economy", help="Fare class (economy, premium-economy, business or first)")
3334
parser.add_argument('--max_stops', type=int, help="Maximum number of stops (optional, [0|1|2])")
3435
parser.add_argument('--fetch_mode', type=str, default="common", help="Fetch mode: common, fallback, force-fallback, local, bright-data")
@@ -54,7 +55,7 @@ def main():
5455
seat=args.type, # Seat (economy, premium-economy, business or first)
5556
passengers=Passengers(
5657
adults=args.adults,
57-
children=0,
58+
children=args.children,
5859
infants_in_seat=0,
5960
infants_on_lap=0
6061
),
@@ -66,10 +67,17 @@ def main():
6667
"https://www.google.com/travel/flights?tfs=%s" % b64
6768
)
6869

69-
# Get flights with the filter
70-
result = get_flights_from_filter(filter,
71-
mode=args.fetch_mode
72-
)
70+
# Previously we constructed CONSENT/SOCS cookies here and passed them to
71+
# get_flights_from_filter; the library now embeds a small default consent
72+
# cookie bundle that will be applied automatically when no cookies are
73+
# provided. To override or disable this behavior you can pass the
74+
# `cookie_consent=False` flag or supply your own `cookies`/`request_kwargs`.
75+
76+
# Preferred: rely on the embedded default consent cookies (no explicit cookies passed)
77+
result = get_flights_from_filter(filter, mode=args.fetch_mode)
78+
79+
# If you need to disable the embedded cookies and handle cookies yourself:
80+
# result = get_flights_from_filter(filter, mode=args.fetch_mode, cookie_consent=False)
7381

7482
try:
7583
# Manually convert the result to a dictionary before serialization
@@ -80,8 +88,8 @@ def main():
8088
print(result)
8189
print("Error details:", str(e))
8290

83-
# Print price information
84-
print("The price is currently", result.current_price)
91+
# Print price information safely (result may be decoded or None)
92+
print("The price is currently", getattr(result, 'current_price', None))
8593

8694
if __name__ == "__main__":
87-
main()
95+
main()

fast_flights/__init__.py

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
1-
from .cookies_impl import Cookies
1+
# Lazy import Cookies to avoid heavy protobuf dependencies during import-time in tests
2+
def get_cookies_class():
3+
from .cookies_impl import Cookies
4+
return Cookies
5+
26
from .core import get_flights_from_filter, get_flights
37
from .filter import create_filter
48
from .flights_impl import Airport, FlightData, Passengers, TFSData
@@ -18,3 +22,21 @@
1822
"Cookies",
1923
"get_flights",
2024
]
25+
26+
# Backwards-compatible name: try to resolve Cookies lazily if accessed
27+
try:
28+
# Provide a module-level name that will import on access
29+
class _CookiesProxy:
30+
def __getattr__(self, name):
31+
Cookies = get_cookies_class()
32+
return getattr(Cookies, name)
33+
34+
Cookies = get_cookies_class()
35+
except Exception:
36+
# If import fails, expose a simple proxy that will import when used
37+
class _CookiesProxy:
38+
def __getattr__(self, name):
39+
Cookies = get_cookies_class()
40+
return getattr(Cookies, name)
41+
42+
Cookies = _CookiesProxy()

0 commit comments

Comments
 (0)