Просмотр исходного кода

retry Airbnb calendar fetch on empty response

Airbnb uses eventual consistency (readConsistency: EVENTUAL) which can
return an empty days list on the first request. Mirrors the existing
Booking.com retry pattern with a 1s sleep and warning logs.
ZiB 4 месяцев назад
Родитель
Сommit
159560c56f
1 измененных файлов с 31 добавлено и 21 удалено
  1. 31 21
      room_cal/service_api/_airbnb.py

+ 31 - 21
room_cal/service_api/_airbnb.py

@@ -212,27 +212,37 @@ class _AirbnbClient:
 
 
   async def _fetch_month_days(self, month: int, year: int) -> list[dict]:
   async def _fetch_month_days(self, month: int, year: int) -> list[dict]:
     start, end = month_bounds(month, year)
     start, end = month_bounds(month, year)
-    resp = await self._gql(
-      'getDLSHostCalendar', _HASHES['getDLSHostCalendar'],
-      {
-        'listingId': self._global_id,
-        'startDate': start,
-        'endDate': end,
-        'hostCalendarViewType': 'MONTH_VIEW',
-        'lensTypes': ['NOTE', 'PRICE', 'PROMOTION'],
-        'timeZone': self._tz,
-        'localizationContext': {'timeZone': self._tz, 'firstDayOfWeek': 0},
-        'hostCalendarSectionTypes': ['MAIN_VIEW'],
-        'readConsistency': 'EVENTUAL',
-      },
-      f'https://www.airbnb.com/multicalendar/{self._listing_id}',
-    )
-    section = (
-      resp.get('data', {}).get('presentation', {})
-          .get('hostCalendar', {}).get('sections', {})
-          .get('calendarGridViewSection')
-    ) or {}
-    return section.get('days', [])
+    variables = {
+      'listingId': self._global_id,
+      'startDate': start,
+      'endDate': end,
+      'hostCalendarViewType': 'MONTH_VIEW',
+      'lensTypes': ['NOTE', 'PRICE', 'PROMOTION'],
+      'timeZone': self._tz,
+      'localizationContext': {'timeZone': self._tz, 'firstDayOfWeek': 0},
+      'hostCalendarSectionTypes': ['MAIN_VIEW'],
+      'readConsistency': 'EVENTUAL',
+    }
+    referer = f'https://www.airbnb.com/multicalendar/{self._listing_id}'
+
+    def _extract_days(resp: dict) -> list[dict]:
+      section = (
+        resp.get('data', {}).get('presentation', {})
+            .get('hostCalendar', {}).get('sections', {})
+            .get('calendarGridViewSection')
+      ) or {}
+      return section.get('days', [])
+
+    resp = await self._gql('getDLSHostCalendar', _HASHES['getDLSHostCalendar'], variables, referer)
+    days = _extract_days(resp)
+    if not days:
+      _log.warning('Airbnb: empty calendar for %d-%02d; retrying', year, month)
+      await asyncio.sleep(1)
+      resp = await self._gql('getDLSHostCalendar', _HASHES['getDLSHostCalendar'], variables, referer)
+      days = _extract_days(resp)
+      if not days:
+        _log.warning('Airbnb: still empty calendar for %d-%02d after retry', year, month)
+    return days
 
 
   async def _get_month_days(self, month: int, year: int) -> list[dict]:
   async def _get_month_days(self, month: int, year: int) -> list[dict]:
     key = (month, year)
     key = (month, year)