cli-web-airbnb 0.1.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- cli_web_airbnb-0.1.1/PKG-INFO +12 -0
- cli_web_airbnb-0.1.1/cli_web/airbnb/README.md +236 -0
- cli_web_airbnb-0.1.1/cli_web/airbnb/__init__.py +3 -0
- cli_web_airbnb-0.1.1/cli_web/airbnb/__main__.py +6 -0
- cli_web_airbnb-0.1.1/cli_web/airbnb/airbnb_cli.py +169 -0
- cli_web_airbnb-0.1.1/cli_web/airbnb/commands/autocomplete.py +100 -0
- cli_web_airbnb-0.1.1/cli_web/airbnb/commands/listings.py +294 -0
- cli_web_airbnb-0.1.1/cli_web/airbnb/commands/search.py +205 -0
- cli_web_airbnb-0.1.1/cli_web/airbnb/core/__init__.py +1 -0
- cli_web_airbnb-0.1.1/cli_web/airbnb/core/client.py +515 -0
- cli_web_airbnb-0.1.1/cli_web/airbnb/core/exceptions.py +84 -0
- cli_web_airbnb-0.1.1/cli_web/airbnb/core/models.py +137 -0
- cli_web_airbnb-0.1.1/cli_web/airbnb/skills/SKILL.md +132 -0
- cli_web_airbnb-0.1.1/cli_web/airbnb/tests/TEST.md +295 -0
- cli_web_airbnb-0.1.1/cli_web/airbnb/tests/test_core.py +647 -0
- cli_web_airbnb-0.1.1/cli_web/airbnb/tests/test_e2e.py +503 -0
- cli_web_airbnb-0.1.1/cli_web/airbnb/utils/config.py +24 -0
- cli_web_airbnb-0.1.1/cli_web/airbnb/utils/doctor.py +188 -0
- cli_web_airbnb-0.1.1/cli_web/airbnb/utils/helpers.py +107 -0
- cli_web_airbnb-0.1.1/cli_web/airbnb/utils/mcp_server.py +290 -0
- cli_web_airbnb-0.1.1/cli_web/airbnb/utils/output.py +12 -0
- cli_web_airbnb-0.1.1/cli_web/airbnb/utils/repl_skin.py +486 -0
- cli_web_airbnb-0.1.1/cli_web_airbnb.egg-info/PKG-INFO +12 -0
- cli_web_airbnb-0.1.1/cli_web_airbnb.egg-info/SOURCES.txt +28 -0
- cli_web_airbnb-0.1.1/cli_web_airbnb.egg-info/dependency_links.txt +1 -0
- cli_web_airbnb-0.1.1/cli_web_airbnb.egg-info/entry_points.txt +2 -0
- cli_web_airbnb-0.1.1/cli_web_airbnb.egg-info/requires.txt +4 -0
- cli_web_airbnb-0.1.1/cli_web_airbnb.egg-info/top_level.txt +1 -0
- cli_web_airbnb-0.1.1/setup.cfg +4 -0
- cli_web_airbnb-0.1.1/setup.py +23 -0
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: cli-web-airbnb
|
|
3
|
+
Version: 0.1.1
|
|
4
|
+
Summary: CLI for Airbnb — search stays, listings, and locations
|
|
5
|
+
Requires-Python: >=3.10
|
|
6
|
+
Requires-Dist: click>=8.0
|
|
7
|
+
Requires-Dist: curl_cffi>=0.5.10
|
|
8
|
+
Requires-Dist: rich>=13.0
|
|
9
|
+
Requires-Dist: prompt_toolkit>=3.0
|
|
10
|
+
Dynamic: requires-dist
|
|
11
|
+
Dynamic: requires-python
|
|
12
|
+
Dynamic: summary
|
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
# cli-web-airbnb
|
|
2
|
+
|
|
3
|
+
Search Airbnb stays, get listing details, and look up location suggestions — all from the terminal with structured JSON output.
|
|
4
|
+
|
|
5
|
+
> Generated by [CLI-Anything-Web](../../../../cli-anything-web-plugin/) from [airbnb.com](https://www.airbnb.com)
|
|
6
|
+
|
|
7
|
+
## Installation
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
pip install cli-web-airbnb
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Usage
|
|
14
|
+
|
|
15
|
+
### Search Stays
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
# Search for stays in a location
|
|
19
|
+
cli-web-airbnb search stays "London, UK" --json
|
|
20
|
+
|
|
21
|
+
# Search with dates and guests
|
|
22
|
+
cli-web-airbnb search stays "Paris, France" --checkin 2024-06-01 --checkout 2024-06-05 --adults 2 --json
|
|
23
|
+
|
|
24
|
+
# Filter by max price
|
|
25
|
+
cli-web-airbnb --json search stays "Tokyo, Japan" --max-price 150
|
|
26
|
+
|
|
27
|
+
# Filter by price and room type
|
|
28
|
+
cli-web-airbnb search stays "New York, NY" --max-price 200 --room-type entire_home
|
|
29
|
+
|
|
30
|
+
# Paginate results using a cursor from a previous response
|
|
31
|
+
cli-web-airbnb search stays "Tokyo, Japan" --cursor "eyJ..."
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
### Listings Detail
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
# Get full details for a listing
|
|
38
|
+
cli-web-airbnb listings get 770993223449115417 --json
|
|
39
|
+
|
|
40
|
+
# With dates for pricing context
|
|
41
|
+
cli-web-airbnb listings get 770993223449115417 --checkin 2024-06-01 --checkout 2024-06-05 --json
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
### Listing Reviews
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
# Get reviews for a listing (sorted by best quality)
|
|
48
|
+
cli-web-airbnb listings reviews 770993223449115417 --json
|
|
49
|
+
|
|
50
|
+
# Get most recent reviews
|
|
51
|
+
cli-web-airbnb listings reviews 770993223449115417 --sort RECENT --limit 10 --json
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### Availability Calendar
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
# Get 12-month availability calendar
|
|
58
|
+
cli-web-airbnb listings availability 770993223449115417 --json
|
|
59
|
+
|
|
60
|
+
# Get 3 months starting from a specific month
|
|
61
|
+
cli-web-airbnb listings availability 770993223449115417 --month 6 --year 2026 --count 3 --json
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### Autocomplete Locations
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
# Suggest locations for a partial query
|
|
68
|
+
cli-web-airbnb autocomplete locations "New Yor" --json
|
|
69
|
+
|
|
70
|
+
# With more results
|
|
71
|
+
cli-web-airbnb autocomplete locations "Lond" --num-results 10 --json
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## Auth
|
|
75
|
+
|
|
76
|
+
Airbnb is a **no-auth public site** — no login is required. All three command groups (`search`, `listings`, `autocomplete`) work without any credentials.
|
|
77
|
+
|
|
78
|
+
Optional cookie-based auth (for wishlists and booking) is not yet implemented. The public endpoints cover all search and listing detail functionality.
|
|
79
|
+
|
|
80
|
+
## JSON Output
|
|
81
|
+
|
|
82
|
+
Every command supports `--json` for structured output. The flag can be placed at the group level (before the subcommand) or at the subcommand level (after):
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
# Group-level --json (before subcommand)
|
|
86
|
+
cli-web-airbnb --json search stays "London, UK"
|
|
87
|
+
|
|
88
|
+
# Subcommand-level --json (after subcommand)
|
|
89
|
+
cli-web-airbnb search stays "London, UK" --json
|
|
90
|
+
|
|
91
|
+
# Listings detail
|
|
92
|
+
cli-web-airbnb listings get 770993223449115417 --json
|
|
93
|
+
|
|
94
|
+
# Autocomplete
|
|
95
|
+
cli-web-airbnb autocomplete locations "New Yor" --json
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
Example response for `search stays --json`:
|
|
99
|
+
|
|
100
|
+
```json
|
|
101
|
+
{
|
|
102
|
+
"success": true,
|
|
103
|
+
"count": 18,
|
|
104
|
+
"next_cursor": "eyJzZWN0aW9uX29mZnNldCI6MCwiaXRlbXNfb2Zmc2V0IjoxOH0=",
|
|
105
|
+
"total_count": 300,
|
|
106
|
+
"location_slug": "London--UK",
|
|
107
|
+
"listings": [
|
|
108
|
+
{
|
|
109
|
+
"id": "770993223449115417",
|
|
110
|
+
"id_b64": "RGVtYW5kU3RheUxpc3Rpbmc6NzcwOTkzMjIzNDQ5MTE1NDE3",
|
|
111
|
+
"name": "Room with Wembley view",
|
|
112
|
+
"rating": "4.98 (42)",
|
|
113
|
+
"price": "$142",
|
|
114
|
+
"price_qualifier": "total",
|
|
115
|
+
"latitude": 51.5589,
|
|
116
|
+
"longitude": -0.2789,
|
|
117
|
+
"badges": ["Guest favourite"],
|
|
118
|
+
"url": "https://www.airbnb.com/rooms/770993223449115417"
|
|
119
|
+
}
|
|
120
|
+
]
|
|
121
|
+
}
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
Example response for `listings get --json`:
|
|
125
|
+
|
|
126
|
+
```json
|
|
127
|
+
{
|
|
128
|
+
"success": true,
|
|
129
|
+
"id": "770993223449115417",
|
|
130
|
+
"name": "Room with Wembley view",
|
|
131
|
+
"rating": "4.98 (42)",
|
|
132
|
+
"review_count": 42,
|
|
133
|
+
"host_name": "Sean",
|
|
134
|
+
"description": "Indulge in a stylish retreat...",
|
|
135
|
+
"amenities": ["Body soap", "Shower gel", "Wifi"],
|
|
136
|
+
"price": null,
|
|
137
|
+
"bedrooms": null,
|
|
138
|
+
"bathrooms": null,
|
|
139
|
+
"max_guests": null,
|
|
140
|
+
"url": "https://www.airbnb.com/rooms/770993223449115417"
|
|
141
|
+
}
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
Example response for `listings reviews --json`:
|
|
145
|
+
|
|
146
|
+
```json
|
|
147
|
+
{
|
|
148
|
+
"success": true,
|
|
149
|
+
"listing_id": "770993223449115417",
|
|
150
|
+
"count": 24,
|
|
151
|
+
"reviews": [
|
|
152
|
+
{
|
|
153
|
+
"id": "1651523546402376930",
|
|
154
|
+
"rating": 5,
|
|
155
|
+
"date": "March 2026",
|
|
156
|
+
"reviewer": "Craig",
|
|
157
|
+
"reviewer_location": "Falkirk, United Kingdom",
|
|
158
|
+
"comment": "Sean was a brilliant host...",
|
|
159
|
+
"host_response": null
|
|
160
|
+
}
|
|
161
|
+
]
|
|
162
|
+
}
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
Example response for `listings availability --json`:
|
|
166
|
+
|
|
167
|
+
```json
|
|
168
|
+
{
|
|
169
|
+
"success": true,
|
|
170
|
+
"listing_id": "770993223449115417",
|
|
171
|
+
"months": [
|
|
172
|
+
{
|
|
173
|
+
"month": 4,
|
|
174
|
+
"year": 2026,
|
|
175
|
+
"days": [
|
|
176
|
+
{"date": "2026-04-06", "available": true, "checkin": true, "checkout": true, "min_nights": 1}
|
|
177
|
+
]
|
|
178
|
+
}
|
|
179
|
+
]
|
|
180
|
+
}
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
Example response for `autocomplete locations --json`:
|
|
184
|
+
|
|
185
|
+
```json
|
|
186
|
+
{
|
|
187
|
+
"success": true,
|
|
188
|
+
"suggestions": [
|
|
189
|
+
{
|
|
190
|
+
"query": "London, United Kingdom",
|
|
191
|
+
"place_id": "ChIJdd4hrwug2EcRmSrV3Vo6llI",
|
|
192
|
+
"display": "London, United Kingdom"
|
|
193
|
+
}
|
|
194
|
+
]
|
|
195
|
+
}
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
Error responses always follow the same shape:
|
|
199
|
+
|
|
200
|
+
```json
|
|
201
|
+
{"error": true, "code": "SERVER_ERROR", "message": "No StaysSearch data in page HTML"}
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
## REPL Mode
|
|
205
|
+
|
|
206
|
+
Run without arguments to enter interactive mode:
|
|
207
|
+
|
|
208
|
+
```bash
|
|
209
|
+
cli-web-airbnb
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
Type `help` for available commands, `quit` to exit.
|
|
213
|
+
|
|
214
|
+
## Testing
|
|
215
|
+
|
|
216
|
+
```bash
|
|
217
|
+
cd airbnb/agent-harness
|
|
218
|
+
pip install -e .
|
|
219
|
+
|
|
220
|
+
# Unit tests (no network)
|
|
221
|
+
python -m pytest cli_web/airbnb/tests/test_core.py -v
|
|
222
|
+
|
|
223
|
+
# All tests including E2E (requires live network)
|
|
224
|
+
python -m pytest cli_web/airbnb/tests/ -v -s
|
|
225
|
+
|
|
226
|
+
# Subprocess tests (requires installed binary on PATH)
|
|
227
|
+
CLI_WEB_FORCE_INSTALLED=1 python -m pytest cli_web/airbnb/tests/test_e2e.py -v -s
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
## Protocol
|
|
231
|
+
|
|
232
|
+
- **Website:** https://www.airbnb.com
|
|
233
|
+
- **Protocol:** SSR HTML + niobeClientData (embedded GraphQL) + REST API `/api/v2/`
|
|
234
|
+
- **Auth:** None required (public no-auth site)
|
|
235
|
+
- **Bot protection:** Akamai/DataDome — bypassed with `curl_cffi` Chrome impersonation
|
|
236
|
+
- **API key:** `d306zoyjsyarp7ifhu67rjxn52tv0t20` (used for `/api/v2/` REST calls only, not HTML pages)
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
"""cli-web-airbnb — Airbnb search and listings CLI."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import json as _json
|
|
6
|
+
import shlex
|
|
7
|
+
import sys
|
|
8
|
+
|
|
9
|
+
# Windows UTF-8 fix — reconfigure both stdout and stderr
|
|
10
|
+
for _stream in (sys.stdout, sys.stderr):
|
|
11
|
+
if (
|
|
12
|
+
_stream
|
|
13
|
+
and getattr(_stream, "encoding", None)
|
|
14
|
+
and _stream.encoding.lower() not in ("utf-8", "utf8")
|
|
15
|
+
):
|
|
16
|
+
try:
|
|
17
|
+
_stream.reconfigure(encoding="utf-8", errors="replace")
|
|
18
|
+
except AttributeError:
|
|
19
|
+
pass
|
|
20
|
+
|
|
21
|
+
import click
|
|
22
|
+
|
|
23
|
+
from .commands.autocomplete import autocomplete_group
|
|
24
|
+
from .commands.listings import listings
|
|
25
|
+
from .commands.search import search
|
|
26
|
+
from .core.exceptions import AirbnbError
|
|
27
|
+
from .utils.repl_skin import ReplSkin
|
|
28
|
+
|
|
29
|
+
_skin = ReplSkin("airbnb", version="0.1.0")
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
@click.group(invoke_without_command=True)
|
|
33
|
+
@click.option("--json", "json_mode", is_flag=True, help="Output all results as JSON.")
|
|
34
|
+
@click.option("--version", is_flag=True, help="Show version and exit.")
|
|
35
|
+
@click.pass_context
|
|
36
|
+
def cli(ctx, json_mode, version):
|
|
37
|
+
"""🏠 cli-web-airbnb — Search Airbnb stays, listings and locations.
|
|
38
|
+
|
|
39
|
+
Running without a subcommand enters interactive REPL mode.
|
|
40
|
+
|
|
41
|
+
Examples:
|
|
42
|
+
cli-web-airbnb search stays "London, UK"
|
|
43
|
+
cli-web-airbnb search stays "Paris, France" --checkin 2024-06-01 --checkout 2024-06-05 --json
|
|
44
|
+
cli-web-airbnb listings get 770993223449115417
|
|
45
|
+
cli-web-airbnb autocomplete locations "New Yor"
|
|
46
|
+
"""
|
|
47
|
+
ctx.ensure_object(dict)
|
|
48
|
+
ctx.obj["json"] = json_mode
|
|
49
|
+
|
|
50
|
+
if version:
|
|
51
|
+
click.echo("cli-web-airbnb 0.1.0")
|
|
52
|
+
return
|
|
53
|
+
|
|
54
|
+
if ctx.invoked_subcommand is None:
|
|
55
|
+
_repl(ctx)
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
cli.add_command(search)
|
|
59
|
+
cli.add_command(listings)
|
|
60
|
+
cli.add_command(autocomplete_group, name="autocomplete")
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def _print_repl_help() -> None:
|
|
64
|
+
"""Print REPL help listing all commands and key options."""
|
|
65
|
+
_skin.info("Available commands:")
|
|
66
|
+
print()
|
|
67
|
+
print(" search stays LOCATION Search for stays in a location")
|
|
68
|
+
print(" --checkin DATE Check-in date (YYYY-MM-DD)")
|
|
69
|
+
print(" --checkout DATE Check-out date (YYYY-MM-DD)")
|
|
70
|
+
print(" --adults N Number of adults (default: 1)")
|
|
71
|
+
print(" --children N Number of children")
|
|
72
|
+
print(" --infants N Number of infants")
|
|
73
|
+
print(" --pets N Number of pets (0 or 1)")
|
|
74
|
+
print(" --min-price N Minimum nightly price")
|
|
75
|
+
print(" --max-price N Maximum nightly price")
|
|
76
|
+
print(" --room-type TYPE Filter: entire_home|private_room|shared_room|hotel_room")
|
|
77
|
+
print(
|
|
78
|
+
" --amenity ID Filter by amenity ID (repeatable: 4=WiFi, 8=Kitchen, 40=AC, 33=Pool)"
|
|
79
|
+
)
|
|
80
|
+
print(" --cursor TOKEN Pagination cursor for next page")
|
|
81
|
+
print(" --page N Page number (note: Airbnb uses cursors, not pages)")
|
|
82
|
+
print(" --locale TEXT Language locale (default: en)")
|
|
83
|
+
print(" --currency TEXT Currency code (default: USD)")
|
|
84
|
+
print()
|
|
85
|
+
print(" listings get LISTING_ID Get details for a specific listing")
|
|
86
|
+
print(" --adults N Number of adults")
|
|
87
|
+
print(" --checkin DATE Check-in date")
|
|
88
|
+
print(" --checkout DATE Check-out date")
|
|
89
|
+
print(" --locale TEXT Language locale (default: en)")
|
|
90
|
+
print(" --currency TEXT Currency code (default: USD)")
|
|
91
|
+
print()
|
|
92
|
+
print(" listings reviews LISTING_ID Get guest reviews for a listing")
|
|
93
|
+
print(" --limit N Number of reviews (default: 24)")
|
|
94
|
+
print(" --offset N Pagination offset (default: 0)")
|
|
95
|
+
print(" --sort ORDER Sort: BEST_QUALITY|RECENT|RATING_DESC|RATING_ASC")
|
|
96
|
+
print()
|
|
97
|
+
print(" listings availability LISTING_ID Get 12-month availability calendar")
|
|
98
|
+
print(" --month N Starting month 1-12 (default: current)")
|
|
99
|
+
print(" --year N Starting year (default: current)")
|
|
100
|
+
print(" --count N Number of months to fetch (default: 12)")
|
|
101
|
+
print()
|
|
102
|
+
print(" autocomplete locations QUERY Suggest locations for a partial query")
|
|
103
|
+
print(" --num-results N Number of suggestions (default: 5)")
|
|
104
|
+
print()
|
|
105
|
+
print(" Global flags:")
|
|
106
|
+
print(" --json Output results as JSON (place before OR after subcommand)")
|
|
107
|
+
print(' Examples: --json search stays "London" OR: search stays "London" --json')
|
|
108
|
+
print()
|
|
109
|
+
print(" help Show this help")
|
|
110
|
+
print(" quit / exit Exit REPL")
|
|
111
|
+
print()
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def _repl(ctx: click.Context) -> None:
|
|
115
|
+
"""Interactive REPL loop."""
|
|
116
|
+
_skin.print_banner()
|
|
117
|
+
pt_session = _skin.create_prompt_session()
|
|
118
|
+
|
|
119
|
+
while True:
|
|
120
|
+
try:
|
|
121
|
+
line = _skin.get_input(pt_session)
|
|
122
|
+
except (KeyboardInterrupt, EOFError):
|
|
123
|
+
break
|
|
124
|
+
|
|
125
|
+
line = line.strip()
|
|
126
|
+
if not line:
|
|
127
|
+
continue
|
|
128
|
+
if line in ("help", "?", "h"):
|
|
129
|
+
_print_repl_help()
|
|
130
|
+
continue
|
|
131
|
+
if line in ("quit", "exit", "q"):
|
|
132
|
+
break
|
|
133
|
+
|
|
134
|
+
try:
|
|
135
|
+
args = shlex.split(line)
|
|
136
|
+
except ValueError as exc:
|
|
137
|
+
click.echo(f"Parse error: {exc}", err=True)
|
|
138
|
+
continue
|
|
139
|
+
|
|
140
|
+
repl_args = ["--json"] + args if ctx.obj.get("json") else args
|
|
141
|
+
try:
|
|
142
|
+
cli.main(args=repl_args, standalone_mode=False)
|
|
143
|
+
except SystemExit:
|
|
144
|
+
pass
|
|
145
|
+
except Exception as exc:
|
|
146
|
+
if ctx.obj.get("json"):
|
|
147
|
+
payload = (
|
|
148
|
+
exc.to_dict()
|
|
149
|
+
if isinstance(exc, AirbnbError)
|
|
150
|
+
else {"error": True, "code": "ERROR", "message": str(exc)}
|
|
151
|
+
)
|
|
152
|
+
click.echo(_json.dumps(payload))
|
|
153
|
+
else:
|
|
154
|
+
click.echo(f"Error: {exc}", err=True)
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
def main() -> None:
|
|
158
|
+
"""Entry point for cli-web-airbnb."""
|
|
159
|
+
cli()
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
# MCP server mode — exposes every command as an MCP tool over stdio.
|
|
163
|
+
# Canonical adapter: cli-web-core/cli_web_core/mcp_server.py (vendored copy).
|
|
164
|
+
from cli_web.airbnb import __version__ as _pkg_version # noqa: E402
|
|
165
|
+
from cli_web.airbnb.utils.doctor import register_doctor_command # noqa: E402
|
|
166
|
+
from cli_web.airbnb.utils.mcp_server import register_mcp_command # noqa: E402
|
|
167
|
+
|
|
168
|
+
register_mcp_command(cli, app_name="airbnb", version=_pkg_version)
|
|
169
|
+
register_doctor_command(cli, app_name="airbnb", pkg="airbnb")
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
"""Autocomplete command group — location suggestions."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import click
|
|
6
|
+
|
|
7
|
+
from ..core.client import AirbnbClient
|
|
8
|
+
from ..utils.helpers import handle_errors, print_json, resolve_json_mode
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@click.group(
|
|
12
|
+
"autocomplete",
|
|
13
|
+
invoke_without_command=True,
|
|
14
|
+
)
|
|
15
|
+
@click.pass_context
|
|
16
|
+
def autocomplete_group(ctx: click.Context) -> None:
|
|
17
|
+
"""Autocomplete helpers for Airbnb search inputs (locations, etc.)."""
|
|
18
|
+
if ctx.invoked_subcommand is None:
|
|
19
|
+
click.echo(ctx.get_help())
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
@autocomplete_group.command("locations")
|
|
23
|
+
@click.argument("query")
|
|
24
|
+
@click.option(
|
|
25
|
+
"--num-results",
|
|
26
|
+
"-n",
|
|
27
|
+
default=5,
|
|
28
|
+
show_default=True,
|
|
29
|
+
type=int,
|
|
30
|
+
help="Maximum number of suggestions to return.",
|
|
31
|
+
)
|
|
32
|
+
@click.option(
|
|
33
|
+
"--locale",
|
|
34
|
+
default="en",
|
|
35
|
+
show_default=True,
|
|
36
|
+
help="Locale for suggestion labels (e.g. en, fr, de).",
|
|
37
|
+
)
|
|
38
|
+
@click.option(
|
|
39
|
+
"--currency",
|
|
40
|
+
default="USD",
|
|
41
|
+
show_default=True,
|
|
42
|
+
help="Currency code used for price display context (e.g. USD, EUR).",
|
|
43
|
+
)
|
|
44
|
+
@click.option(
|
|
45
|
+
"--json",
|
|
46
|
+
"json_mode",
|
|
47
|
+
is_flag=True,
|
|
48
|
+
default=False,
|
|
49
|
+
help="Output as JSON.",
|
|
50
|
+
)
|
|
51
|
+
@click.pass_context
|
|
52
|
+
def locations(
|
|
53
|
+
ctx: click.Context,
|
|
54
|
+
query: str,
|
|
55
|
+
num_results: int,
|
|
56
|
+
locale: str,
|
|
57
|
+
currency: str,
|
|
58
|
+
json_mode: bool,
|
|
59
|
+
) -> None:
|
|
60
|
+
"""Suggest locations matching partial QUERY text.
|
|
61
|
+
|
|
62
|
+
Returns ranked location suggestions suitable for use as a destination
|
|
63
|
+
in search commands.
|
|
64
|
+
|
|
65
|
+
\b
|
|
66
|
+
Examples:
|
|
67
|
+
autocomplete locations "Lond"
|
|
68
|
+
autocomplete locations "New Y" --num-results 10
|
|
69
|
+
autocomplete locations "Paris" --locale fr --currency EUR --json
|
|
70
|
+
"""
|
|
71
|
+
json_mode = resolve_json_mode(json_mode, ctx)
|
|
72
|
+
with handle_errors(json_mode=json_mode):
|
|
73
|
+
with AirbnbClient(locale=locale, currency=currency) as client:
|
|
74
|
+
suggestions = client.autocomplete_locations(
|
|
75
|
+
query=query,
|
|
76
|
+
num_results=num_results,
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
if not suggestions:
|
|
80
|
+
if json_mode:
|
|
81
|
+
print_json({"success": True, "data": {"query": query, "suggestions": []}})
|
|
82
|
+
else:
|
|
83
|
+
click.echo(f"No location suggestions found for '{query}'.")
|
|
84
|
+
return
|
|
85
|
+
|
|
86
|
+
if json_mode:
|
|
87
|
+
print_json(
|
|
88
|
+
{
|
|
89
|
+
"success": True,
|
|
90
|
+
"data": {
|
|
91
|
+
"query": query,
|
|
92
|
+
"suggestions": [s.to_dict() for s in suggestions],
|
|
93
|
+
},
|
|
94
|
+
}
|
|
95
|
+
)
|
|
96
|
+
else:
|
|
97
|
+
click.echo(f"\nLocation suggestions for '{query}':\n")
|
|
98
|
+
for i, suggestion in enumerate(suggestions, start=1):
|
|
99
|
+
click.echo(f" {i}. {suggestion.display} [place_id: {suggestion.place_id}]")
|
|
100
|
+
click.echo()
|