|
@@ -9,7 +9,7 @@ from urllib.parse import parse_qs, urlparse
|
|
|
|
|
|
|
|
import httpx
|
|
import httpx
|
|
|
|
|
|
|
|
-from ._scripts import _assert_not_async, _booking_lock, _npm_env, _ts_dir, _ensure_installed
|
|
|
|
|
|
|
+from ._scripts import _booking_lock, run_refresh_script
|
|
|
from room_cal._utils import get_config
|
|
from room_cal._utils import get_config
|
|
|
from ._base import ServiceClientBase, _AUTH_HINT
|
|
from ._base import ServiceClientBase, _AUTH_HINT
|
|
|
from ._month_cache import MonthCache
|
|
from ._month_cache import MonthCache
|
|
@@ -28,6 +28,12 @@ def _is_past(d: str) -> bool:
|
|
|
_BASE = 'https://admin.booking.com'
|
|
_BASE = 'https://admin.booking.com'
|
|
|
_SENDER_LABELS: dict[str, str] = {'GUEST': 'Guest', 'PARTNER': 'Host'}
|
|
_SENDER_LABELS: dict[str, str] = {'GUEST': 'Guest', 'PARTNER': 'Host'}
|
|
|
|
|
|
|
|
|
|
+# Sync with scripts/har_audit.py via DRIFT_MANIFEST below — these are the only
|
|
|
|
|
+# copies; the manifest references them rather than duplicating the literals.
|
|
|
|
|
+_GQL_CLIENT_VERSION = 'EQSXMHMY'
|
|
|
|
|
+_GQL_TIMEOUT_MS = '15000'
|
|
|
|
|
+_MSG_CLIENT_VERSION = 'ABJaNSUc'
|
|
|
|
|
+
|
|
|
_UPDATE_MUTATION = """mutation updateRoomDateMutation($input: PartnerAvailabilityUpdateInput) {
|
|
_UPDATE_MUTATION = """mutation updateRoomDateMutation($input: PartnerAvailabilityUpdateInput) {
|
|
|
partnerAvailabilityMutations {
|
|
partnerAvailabilityMutations {
|
|
|
partnerAvailabilityUpdate(input: $input) {
|
|
partnerAvailabilityUpdate(input: $input) {
|
|
@@ -255,18 +261,8 @@ class _BookingClient(ServiceClientBase):
|
|
|
|
|
|
|
|
@staticmethod
|
|
@staticmethod
|
|
|
def refresh_credentials() -> None:
|
|
def refresh_credentials() -> None:
|
|
|
- _assert_not_async('refresh_credentials')
|
|
|
|
|
- import subprocess
|
|
|
|
|
- ts_dir = _ts_dir()
|
|
|
|
|
- _ensure_installed(ts_dir)
|
|
|
|
|
- _log.info('booking: starting credential refresh')
|
|
|
|
|
- with _booking_lock:
|
|
|
|
|
- try:
|
|
|
|
|
- subprocess.run(['npm', 'run', 'refresh-booking'], cwd=ts_dir, env=_npm_env(), check=True)
|
|
|
|
|
- except subprocess.CalledProcessError as e:
|
|
|
|
|
- _log.error('booking: credential refresh failed (exit %d)', e.returncode)
|
|
|
|
|
- raise ServiceAuthError(f'Booking.com credential refresh failed (exit {e.returncode})') from None
|
|
|
|
|
- _log.info('booking: credential refresh complete')
|
|
|
|
|
|
|
+ run_refresh_script('refresh-booking', service_name='Booking.com',
|
|
|
|
|
+ auth_error_cls=BookingAuthError, lock=_booking_lock)
|
|
|
|
|
|
|
|
def _actor(self) -> dict:
|
|
def _actor(self) -> dict:
|
|
|
return {'actorId': str(self._hotel_id), 'actorType': 'HOTEL'}
|
|
return {'actorId': str(self._hotel_id), 'actorType': 'HOTEL'}
|
|
@@ -329,9 +325,9 @@ class _BookingClient(ServiceClientBase):
|
|
|
'X-Booking-Pageview-ID': _page_view_id(),
|
|
'X-Booking-Pageview-ID': _page_view_id(),
|
|
|
'x-booking-csrf-token': 'undefined',
|
|
'x-booking-csrf-token': 'undefined',
|
|
|
'apollographql-client-name': 'b-partner-availability-extranet-mfe',
|
|
'apollographql-client-name': 'b-partner-availability-extranet-mfe',
|
|
|
- 'apollographql-client-version': 'EQSXMHMY',
|
|
|
|
|
- 'x-envoy-expected-rq-timeout-ms': '15000',
|
|
|
|
|
- 'x-booking-timeout-budget-ms': '15000',
|
|
|
|
|
|
|
+ 'apollographql-client-version': _GQL_CLIENT_VERSION,
|
|
|
|
|
+ 'x-envoy-expected-rq-timeout-ms': _GQL_TIMEOUT_MS,
|
|
|
|
|
+ 'x-booking-timeout-budget-ms': _GQL_TIMEOUT_MS,
|
|
|
'X-Booking-Topic': 'capla_browser_b-partner-availability-extranet-mfe',
|
|
'X-Booking-Topic': 'capla_browser_b-partner-availability-extranet-mfe',
|
|
|
'Cookie': self._cookies,
|
|
'Cookie': self._cookies,
|
|
|
}
|
|
}
|
|
@@ -353,7 +349,7 @@ class _BookingClient(ServiceClientBase):
|
|
|
'x-booking-csrf-token': 'undefined',
|
|
'x-booking-csrf-token': 'undefined',
|
|
|
'X-Booking-Topic': 'capla_browser_b-partner-messaging-extranet-mfe',
|
|
'X-Booking-Topic': 'capla_browser_b-partner-messaging-extranet-mfe',
|
|
|
'apollographql-client-name': 'b-partner-messaging-extranet-mfe',
|
|
'apollographql-client-name': 'b-partner-messaging-extranet-mfe',
|
|
|
- 'apollographql-client-version': 'ABJaNSUc',
|
|
|
|
|
|
|
+ 'apollographql-client-version': _MSG_CLIENT_VERSION,
|
|
|
'Cookie': self._cookies,
|
|
'Cookie': self._cookies,
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -708,3 +704,54 @@ class _BookingClient(ServiceClientBase):
|
|
|
issue['date'], issue['actual_rts'], issue['expected_rts'])
|
|
issue['date'], issue['actual_rts'], issue['expected_rts'])
|
|
|
await self.set_rooms_to_sell(issue['date'], issue['expected_rts'])
|
|
await self.set_rooms_to_sell(issue['date'], issue['expected_rts'])
|
|
|
return issues
|
|
return issues
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+# ── har_audit.py drift manifest ─────────────────────────────────────────────
|
|
|
|
|
+# Auditable identifiers for scripts/har_audit.py. References the constants
|
|
|
|
|
+# above rather than duplicating literals — this is the only copy.
|
|
|
|
|
+
|
|
|
|
|
+DRIFT_MANIFEST = {
|
|
|
|
|
+ 'domain': 'admin.booking.com',
|
|
|
|
|
+ 'endpoints': {
|
|
|
|
|
+ 'inventory/fetch': {
|
|
|
|
|
+ 'url_fragment': 'fresa/extranet/inventory/fetch',
|
|
|
|
|
+ 'hint': 'load the monthly calendar',
|
|
|
|
|
+ },
|
|
|
|
|
+ 'inventory/update': {
|
|
|
|
|
+ 'url_fragment': 'fresa/extranet/inventory/update',
|
|
|
|
|
+ 'hint': 'change a price on any date',
|
|
|
|
|
+ },
|
|
|
|
|
+ 'get_reservations': {
|
|
|
|
|
+ 'url_fragment': 'fresa/extranet/calendar/get_reservations',
|
|
|
|
|
+ 'hint': 'load the reservations list',
|
|
|
|
|
+ },
|
|
|
|
|
+ 'updateRoomDateMutation': {
|
|
|
|
|
+ 'url_fragment': 'dml/graphql.json',
|
|
|
|
|
+ 'operation_name': 'updateRoomDateMutation',
|
|
|
|
|
+ 'hint': 'block or unblock a date on the calendar',
|
|
|
|
|
+ 'headers': {
|
|
|
|
|
+ 'apollographql-client-version': _GQL_CLIENT_VERSION,
|
|
|
|
|
+ 'x-envoy-expected-rq-timeout-ms': _GQL_TIMEOUT_MS,
|
|
|
|
|
+ },
|
|
|
|
|
+ 'source': '_gql_headers()',
|
|
|
|
|
+ },
|
|
|
|
|
+ 'conversationMessages': {
|
|
|
|
|
+ 'url_fragment': 'dml/graphql.json',
|
|
|
|
|
+ 'operation_name': 'conversationMessages',
|
|
|
|
|
+ 'hint': 'open any guest message thread',
|
|
|
|
|
+ 'headers': {
|
|
|
|
|
+ 'apollographql-client-version': _MSG_CLIENT_VERSION,
|
|
|
|
|
+ },
|
|
|
|
|
+ 'source': '_msg_headers()',
|
|
|
|
|
+ },
|
|
|
|
|
+ 'postFreeTextMessage': {
|
|
|
|
|
+ 'url_fragment': 'dml/graphql.json',
|
|
|
|
|
+ 'operation_name': 'postFreeTextMessage',
|
|
|
|
|
+ 'hint': 'send a message to a guest',
|
|
|
|
|
+ 'headers': {
|
|
|
|
|
+ 'apollographql-client-version': _MSG_CLIENT_VERSION,
|
|
|
|
|
+ },
|
|
|
|
|
+ 'source': '_msg_headers()',
|
|
|
|
|
+ },
|
|
|
|
|
+ },
|
|
|
|
|
+}
|