fix: avoid repeated panel identity syncs

This commit is contained in:
3252a8
2026-05-22 14:11:38 +03:00
parent 81707d9c7c
commit 2254b9ad19
2 changed files with 67 additions and 19 deletions
+53 -19
View File
@@ -42,8 +42,55 @@ def _normalize_description(value: Optional[str]) -> str:
return "\n".join((value or "").split()).strip() return "\n".join((value or "").split()).strip()
def _repair_cp1251_mojibake(value: str) -> str:
try:
return value.encode("latin1").decode("cp1251")
except (UnicodeEncodeError, UnicodeDecodeError):
return value
def _description_variants(value: Optional[str]) -> set[str]:
normalized = _normalize_description(value)
variants = {normalized}
repaired = _normalize_description(_repair_cp1251_mojibake(normalized))
if repaired:
variants.add(repaired)
return variants
def _description_matches(current: Optional[str], desired: str) -> bool: def _description_matches(current: Optional[str], desired: str) -> bool:
return _normalize_description(current) == _normalize_description(desired) return bool(_description_variants(current) & _description_variants(desired))
def _panel_identity_matches_user(
panel_user: dict[str, Any],
user: User,
desired_description: str,
) -> bool:
if desired_description and not _description_matches(
panel_user.get("description"),
desired_description,
):
return False
if user.email and _normalize_panel_email(panel_user.get("email")) != user.email.strip().lower():
return False
if user.telegram_id and _coerce_panel_telegram_id(panel_user.get("telegramId")) != int(
user.telegram_id
):
return False
return True
def _panel_identity_update_payload(user: User, description_text: str) -> dict[str, Any]:
payload: dict[str, Any] = {"description": description_text}
if user.email:
payload["email"] = user.email
if user.telegram_id:
payload["telegramId"] = user.telegram_id
return payload
def _datetime_matches(current: Optional[datetime], desired: datetime) -> bool: def _datetime_matches(current: Optional[datetime], desired: datetime) -> bool:
@@ -589,28 +636,15 @@ async def _perform_sync_impl(
if line if line
) )
# Update description only when it differs from the current one on panel # Update description only when it differs from the current one on panel
current_panel_description = (
panel_user_dict.get("description") or ""
).strip()
desired_description = description_text.strip() desired_description = description_text.strip()
if desired_description and not _description_matches( if desired_description and not _panel_identity_matches_user(
current_panel_description, desired_description panel_user_dict,
existing_user,
desired_description,
): ):
await panel_service.update_user_details_on_panel( await panel_service.update_user_details_on_panel(
panel_uuid, panel_uuid,
{ _panel_identity_update_payload(existing_user, description_text),
"description": description_text,
**(
{"email": existing_user.email}
if existing_user.email
else {}
),
**(
{"telegramId": existing_user.telegram_id}
if existing_user.telegram_id
else {}
),
},
) )
except Exception as e_desc: except Exception as e_desc:
logging.warning( logging.warning(
+14
View File
@@ -14,6 +14,20 @@ def test_description_match_ignores_whitespace_shape():
assert _description_matches("email@example.com username", "email@example.com\nusername") assert _description_matches("email@example.com username", "email@example.com\nusername")
def test_description_match_accepts_cp1251_mojibake_from_panel():
desired = "user@example.com\nalice\nАлексей\nЧерников"
panel_value = "user@example.com\nalice\nÀëåêñåé\n×åðíèêîâ"
assert _description_matches(panel_value, desired)
def test_description_match_rejects_different_identity_after_mojibake_repair():
desired = "user@example.com\nalice\nАлексей"
panel_value = "other@example.com\nalice\nÀëåêñåé"
assert not _description_matches(panel_value, desired)
def test_panel_telegram_id_is_coerced_to_int(): def test_panel_telegram_id_is_coerced_to_int():
assert _coerce_panel_telegram_id("12345") == 12345 assert _coerce_panel_telegram_id("12345") == 12345
assert _coerce_panel_telegram_id("") is None assert _coerce_panel_telegram_id("") is None