Refactor perform_sync function for readability
This commit is contained in:
@@ -3,6 +3,7 @@ from aiogram import Router, types, Bot
|
|||||||
from aiogram.filters import Command
|
from aiogram.filters import Command
|
||||||
from typing import Optional, Union
|
from typing import Optional, Union
|
||||||
from sqlalchemy.ext.asyncio import AsyncSession
|
from sqlalchemy.ext.asyncio import AsyncSession
|
||||||
|
from sqlalchemy import update, or_
|
||||||
from datetime import datetime, timezone
|
from datetime import datetime, timezone
|
||||||
|
|
||||||
from config.settings import Settings
|
from config.settings import Settings
|
||||||
@@ -10,14 +11,19 @@ from bot.services.panel_api_service import PanelApiService
|
|||||||
from bot.services.notification_service import NotificationService
|
from bot.services.notification_service import NotificationService
|
||||||
|
|
||||||
from db.dal import user_dal, subscription_dal, panel_sync_dal
|
from db.dal import user_dal, subscription_dal, panel_sync_dal
|
||||||
|
from db.models import Subscription
|
||||||
|
|
||||||
from bot.middlewares.i18n import JsonI18n
|
from bot.middlewares.i18n import JsonI18n
|
||||||
|
|
||||||
router = Router(name="admin_sync_router")
|
router = Router(name="admin_sync_router")
|
||||||
|
|
||||||
|
|
||||||
async def perform_sync(panel_service: PanelApiService, session: AsyncSession,
|
async def perform_sync(
|
||||||
settings: Settings, i18n_instance: JsonI18n) -> dict:
|
panel_service: PanelApiService,
|
||||||
|
session: AsyncSession,
|
||||||
|
settings: Settings,
|
||||||
|
i18n_instance: JsonI18n,
|
||||||
|
) -> dict:
|
||||||
"""
|
"""
|
||||||
Perform panel synchronization and return results
|
Perform panel synchronization and return results
|
||||||
Returns dict with status, details, and sync statistics
|
Returns dict with status, details, and sync statistics
|
||||||
@@ -52,7 +58,12 @@ async def perform_sync(panel_service: PanelApiService, session: AsyncSession,
|
|||||||
session, "success", status_msg, 0, 0
|
session, "success", status_msg, 0, 0
|
||||||
)
|
)
|
||||||
await session.commit()
|
await session.commit()
|
||||||
return {"status": "success", "details": status_msg, "users_synced": 0, "subs_synced": 0}
|
return {
|
||||||
|
"status": "success",
|
||||||
|
"details": status_msg,
|
||||||
|
"users_synced": 0,
|
||||||
|
"subs_synced": 0,
|
||||||
|
}
|
||||||
|
|
||||||
total_panel_users = len(panel_users_data)
|
total_panel_users = len(panel_users_data)
|
||||||
logging.info(f"Starting sync for {total_panel_users} panel users.")
|
logging.info(f"Starting sync for {total_panel_users} panel users.")
|
||||||
@@ -61,12 +72,16 @@ async def perform_sync(panel_service: PanelApiService, session: AsyncSession,
|
|||||||
try:
|
try:
|
||||||
panel_records_checked += 1
|
panel_records_checked += 1
|
||||||
panel_uuid = panel_user_dict.get("uuid")
|
panel_uuid = panel_user_dict.get("uuid")
|
||||||
panel_subscription_uuid = panel_user_dict.get("subscriptionUuid") or panel_user_dict.get("shortUuid")
|
panel_subscription_uuid = panel_user_dict.get("subscriptionUuid") or panel_user_dict.get(
|
||||||
|
"shortUuid"
|
||||||
|
)
|
||||||
telegram_id_from_panel = panel_user_dict.get("telegramId")
|
telegram_id_from_panel = panel_user_dict.get("telegramId")
|
||||||
|
|
||||||
if not panel_uuid:
|
if not panel_uuid:
|
||||||
sync_errors.append(f"Panel user missing UUID: {panel_user_dict}")
|
sync_errors.append(f"Panel user missing UUID: {panel_user_dict}")
|
||||||
logging.warning(f"Skipping panel user without UUID: {panel_user_dict}")
|
logging.warning(
|
||||||
|
f"Skipping panel user without UUID: {panel_user_dict}"
|
||||||
|
)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# Track users without telegram ID
|
# Track users without telegram ID
|
||||||
@@ -78,18 +93,31 @@ async def perform_sync(panel_service: PanelApiService, session: AsyncSession,
|
|||||||
|
|
||||||
# First, try to find by telegram ID if available
|
# First, try to find by telegram ID if available
|
||||||
if telegram_id_from_panel:
|
if telegram_id_from_panel:
|
||||||
existing_user = await user_dal.get_user_by_id(session, telegram_id_from_panel)
|
existing_user = await user_dal.get_user_by_id(
|
||||||
|
session, telegram_id_from_panel
|
||||||
|
)
|
||||||
if existing_user:
|
if existing_user:
|
||||||
logging.debug(f"Found user by telegramId {telegram_id_from_panel}")
|
logging.debug(
|
||||||
|
f"Found user by telegramId {telegram_id_from_panel}"
|
||||||
|
)
|
||||||
|
|
||||||
# If not found by telegram ID, try to find by panel UUID
|
# If not found by telegram ID, try to find by panel UUID
|
||||||
if not existing_user:
|
if not existing_user:
|
||||||
existing_user = await user_dal.get_user_by_panel_uuid(session, panel_uuid)
|
existing_user = await user_dal.get_user_by_panel_uuid(
|
||||||
|
session, panel_uuid
|
||||||
|
)
|
||||||
if existing_user:
|
if existing_user:
|
||||||
logging.info(f"Found user by panel UUID {panel_uuid}, telegramId: {existing_user.user_id}")
|
logging.info(
|
||||||
|
f"Found user by panel UUID {panel_uuid}, telegramId: {existing_user.user_id}"
|
||||||
|
)
|
||||||
# Update telegram ID if it was missing in panel data but we have local user
|
# Update telegram ID if it was missing in panel data but we have local user
|
||||||
if telegram_id_from_panel and existing_user.user_id != telegram_id_from_panel:
|
if (
|
||||||
logging.warning(f"TelegramId mismatch: panel={telegram_id_from_panel}, local={existing_user.user_id}")
|
telegram_id_from_panel
|
||||||
|
and existing_user.user_id != telegram_id_from_panel
|
||||||
|
):
|
||||||
|
logging.warning(
|
||||||
|
f"TelegramId mismatch: panel={telegram_id_from_panel}, local={existing_user.user_id}"
|
||||||
|
)
|
||||||
|
|
||||||
if not existing_user:
|
if not existing_user:
|
||||||
users_not_found_in_db += 1
|
users_not_found_in_db += 1
|
||||||
@@ -100,26 +128,36 @@ async def perform_sync(panel_service: PanelApiService, session: AsyncSession,
|
|||||||
"user_id": telegram_id_from_panel,
|
"user_id": telegram_id_from_panel,
|
||||||
"username": None, # Username will be updated when user interacts with bot
|
"username": None, # Username will be updated when user interacts with bot
|
||||||
"first_name": None, # Panel doesn't provide this info
|
"first_name": None, # Panel doesn't provide this info
|
||||||
"last_name": None, # Panel doesn't provide this info
|
"last_name": None, # Panel doesn't provide this info
|
||||||
"language_code": "ru", # Default language
|
"language_code": "ru", # Default language
|
||||||
"panel_user_uuid": panel_uuid,
|
"panel_user_uuid": panel_uuid,
|
||||||
"is_banned": False,
|
"is_banned": False,
|
||||||
"referred_by_id": None
|
"referred_by_id": None,
|
||||||
}
|
}
|
||||||
|
|
||||||
new_user, was_created = await user_dal.create_user(session, user_data)
|
new_user, was_created = await user_dal.create_user(
|
||||||
|
session, user_data
|
||||||
|
)
|
||||||
if was_created:
|
if was_created:
|
||||||
users_created += 1
|
users_created += 1
|
||||||
logging.info(f"Created new user {telegram_id_from_panel} from panel sync with UUID {panel_uuid}")
|
logging.info(
|
||||||
|
f"Created new user {telegram_id_from_panel} from panel sync with UUID {panel_uuid}"
|
||||||
|
)
|
||||||
|
|
||||||
existing_user = new_user
|
existing_user = new_user
|
||||||
|
|
||||||
except Exception as e_create:
|
except Exception as e_create:
|
||||||
sync_errors.append(f"Error creating user {telegram_id_from_panel}: {str(e_create)}")
|
sync_errors.append(
|
||||||
logging.error(f"Error creating user {telegram_id_from_panel}: {e_create}")
|
f"Error creating user {telegram_id_from_panel}: {str(e_create)}"
|
||||||
|
)
|
||||||
|
logging.error(
|
||||||
|
f"Error creating user {telegram_id_from_panel}: {e_create}"
|
||||||
|
)
|
||||||
continue
|
continue
|
||||||
else:
|
else:
|
||||||
logging.debug(f"Panel user with UUID {panel_uuid} (no telegramId) not found in local DB - skipping")
|
logging.debug(
|
||||||
|
f"Panel user with UUID {panel_uuid} (no telegramId) not found in local DB - skipping"
|
||||||
|
)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# User found in local DB
|
# User found in local DB
|
||||||
@@ -134,20 +172,29 @@ async def perform_sync(panel_service: PanelApiService, session: AsyncSession,
|
|||||||
existing_user.panel_user_uuid = panel_uuid
|
existing_user.panel_user_uuid = panel_uuid
|
||||||
user_was_updated = True
|
user_was_updated = True
|
||||||
users_uuid_updated += 1
|
users_uuid_updated += 1
|
||||||
logging.info(f"Updated panel UUID for user {actual_user_id}: {panel_uuid}")
|
logging.info(
|
||||||
|
f"Updated panel UUID for user {actual_user_id}: {panel_uuid}"
|
||||||
|
)
|
||||||
|
|
||||||
# Ensure panel description contains Telegram fields
|
# Ensure panel description contains Telegram fields
|
||||||
try:
|
try:
|
||||||
if panel_uuid and existing_user:
|
if panel_uuid and existing_user:
|
||||||
description_text = "\n".join([
|
description_text = "\n".join(
|
||||||
existing_user.username or "",
|
[
|
||||||
existing_user.first_name or "",
|
existing_user.username or "",
|
||||||
existing_user.last_name or "",
|
existing_user.first_name or "",
|
||||||
])
|
existing_user.last_name or "",
|
||||||
|
]
|
||||||
|
)
|
||||||
# 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()
|
current_panel_description = (
|
||||||
|
panel_user_dict.get("description") or ""
|
||||||
|
).strip()
|
||||||
desired_description = description_text.strip()
|
desired_description = description_text.strip()
|
||||||
if desired_description and desired_description != current_panel_description:
|
if (
|
||||||
|
desired_description
|
||||||
|
and desired_description != current_panel_description
|
||||||
|
):
|
||||||
await panel_service.update_user_details_on_panel(
|
await panel_service.update_user_details_on_panel(
|
||||||
panel_uuid, {"description": description_text}
|
panel_uuid, {"description": description_text}
|
||||||
)
|
)
|
||||||
@@ -173,6 +220,27 @@ async def perform_sync(panel_service: PanelApiService, session: AsyncSession,
|
|||||||
)
|
)
|
||||||
|
|
||||||
if subscription_uuid_from_panel:
|
if subscription_uuid_from_panel:
|
||||||
|
# Если панель говорит, что подписка ACTIVE — сначала деактивируем все другие активные
|
||||||
|
if panel_status == "ACTIVE":
|
||||||
|
await session.execute(
|
||||||
|
update(Subscription)
|
||||||
|
.where(
|
||||||
|
Subscription.panel_user_uuid == panel_uuid,
|
||||||
|
Subscription.is_active.is_(True),
|
||||||
|
or_(
|
||||||
|
Subscription.panel_subscription_uuid
|
||||||
|
!= subscription_uuid_from_panel,
|
||||||
|
Subscription.panel_subscription_uuid.is_(
|
||||||
|
None
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.values(
|
||||||
|
is_active=False,
|
||||||
|
status_from_panel="INACTIVE",
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
# Try to find subscription by its panel_subscription_uuid first (idempotent)
|
# Try to find subscription by its panel_subscription_uuid first (idempotent)
|
||||||
existing_sub_by_uuid = (
|
existing_sub_by_uuid = (
|
||||||
await subscription_dal.get_subscription_by_panel_subscription_uuid(
|
await subscription_dal.get_subscription_by_panel_subscription_uuid(
|
||||||
@@ -197,7 +265,8 @@ async def perform_sync(panel_service: PanelApiService, session: AsyncSession,
|
|||||||
subscriptions_updated += 1
|
subscriptions_updated += 1
|
||||||
user_was_updated = True
|
user_was_updated = True
|
||||||
logging.info(
|
logging.info(
|
||||||
f"Synced existing subscription {existing_sub_by_uuid.subscription_id} for user {actual_user_id}: expires {panel_expire_at}, status {panel_status}"
|
f"Synced existing subscription {existing_sub_by_uuid.subscription_id} "
|
||||||
|
f"for user {actual_user_id}: expires {panel_expire_at}, status {panel_status}"
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
# Create a new subscription only when we have a concrete subscription UUID
|
# Create a new subscription only when we have a concrete subscription UUID
|
||||||
@@ -221,12 +290,15 @@ async def perform_sync(panel_service: PanelApiService, session: AsyncSession,
|
|||||||
subscriptions_created += 1
|
subscriptions_created += 1
|
||||||
user_was_updated = True
|
user_was_updated = True
|
||||||
logging.info(
|
logging.info(
|
||||||
f"Created subscription {created_sub.subscription_id} for user {actual_user_id} by panel_sub_uuid {subscription_uuid_from_panel}"
|
f"Created subscription {created_sub.subscription_id} "
|
||||||
|
f"for user {actual_user_id} by panel_sub_uuid {subscription_uuid_from_panel}"
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
# No subscription UUID from panel: only update an already active subscription for this user/panel UUID
|
# No subscription UUID from panel: only update an already active subscription for this user/panel UUID
|
||||||
active_sub = await subscription_dal.get_active_subscription_by_user_id(
|
active_sub = (
|
||||||
session, actual_user_id, panel_uuid
|
await subscription_dal.get_active_subscription_by_user_id(
|
||||||
|
session, actual_user_id, panel_uuid
|
||||||
|
)
|
||||||
)
|
)
|
||||||
if active_sub:
|
if active_sub:
|
||||||
await subscription_dal.update_subscription(
|
await subscription_dal.update_subscription(
|
||||||
@@ -242,7 +314,8 @@ async def perform_sync(panel_service: PanelApiService, session: AsyncSession,
|
|||||||
subscriptions_updated += 1
|
subscriptions_updated += 1
|
||||||
user_was_updated = True
|
user_was_updated = True
|
||||||
logging.info(
|
logging.info(
|
||||||
f"Updated active subscription {active_sub.subscription_id} for user {actual_user_id}: expires {panel_expire_at}, status {panel_status}"
|
f"Updated active subscription {active_sub.subscription_id} "
|
||||||
|
f"for user {actual_user_id}: expires {panel_expire_at}, status {panel_status}"
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
# Without a concrete subscription UUID we avoid creating new records to keep sync idempotent
|
# Without a concrete subscription UUID we avoid creating new records to keep sync idempotent
|
||||||
@@ -251,14 +324,20 @@ async def perform_sync(panel_service: PanelApiService, session: AsyncSession,
|
|||||||
)
|
)
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
sync_errors.append(f"Error syncing subscription for user {actual_user_id}: {str(e)}")
|
sync_errors.append(
|
||||||
logging.error(f"Error syncing subscription for user {actual_user_id}: {e}")
|
f"Error syncing subscription for user {actual_user_id}: {str(e)}"
|
||||||
|
)
|
||||||
|
logging.error(
|
||||||
|
f"Error syncing subscription for user {actual_user_id}: {e}"
|
||||||
|
)
|
||||||
|
|
||||||
if user_was_updated:
|
if user_was_updated:
|
||||||
users_updated += 1
|
users_updated += 1
|
||||||
|
|
||||||
except Exception as e_user:
|
except Exception as e_user:
|
||||||
sync_errors.append(f"Error processing panel user {panel_user_dict.get('uuid', 'unknown')}: {str(e_user)}")
|
sync_errors.append(
|
||||||
|
f"Error processing panel user {panel_user_dict.get('uuid', 'unknown')}: {str(e_user)}"
|
||||||
|
)
|
||||||
logging.error(f"Error syncing user: {e_user}")
|
logging.error(f"Error syncing user: {e_user}")
|
||||||
|
|
||||||
# Update sync status
|
# Update sync status
|
||||||
@@ -267,14 +346,26 @@ async def perform_sync(panel_service: PanelApiService, session: AsyncSession,
|
|||||||
default_lang = settings.DEFAULT_LANGUAGE
|
default_lang = settings.DEFAULT_LANGUAGE
|
||||||
additional_stats = ""
|
additional_stats = ""
|
||||||
if users_without_telegram_id > 0:
|
if users_without_telegram_id > 0:
|
||||||
additional_stats += i18n_instance.gettext(default_lang, "admin_sync_no_telegram_id", count=users_without_telegram_id)
|
additional_stats += i18n_instance.gettext(
|
||||||
|
default_lang,
|
||||||
|
"admin_sync_no_telegram_id",
|
||||||
|
count=users_without_telegram_id,
|
||||||
|
)
|
||||||
if users_not_found_in_db > 0:
|
if users_not_found_in_db > 0:
|
||||||
additional_stats += i18n_instance.gettext(default_lang, "admin_sync_not_found_in_db", count=users_not_found_in_db)
|
additional_stats += i18n_instance.gettext(
|
||||||
|
default_lang,
|
||||||
|
"admin_sync_not_found_in_db",
|
||||||
|
count=users_not_found_in_db,
|
||||||
|
)
|
||||||
if sync_errors:
|
if sync_errors:
|
||||||
additional_stats += i18n_instance.gettext(default_lang, "admin_sync_errors", count=len(sync_errors))
|
additional_stats += i18n_instance.gettext(
|
||||||
|
default_lang, "admin_sync_errors", count=len(sync_errors)
|
||||||
|
)
|
||||||
|
|
||||||
# Build full details using localization
|
# Build full details using localization
|
||||||
details = i18n_instance.gettext(default_lang, "admin_sync_details",
|
details = i18n_instance.gettext(
|
||||||
|
default_lang,
|
||||||
|
"admin_sync_details",
|
||||||
panel_records_checked=panel_records_checked,
|
panel_records_checked=panel_records_checked,
|
||||||
users_found_in_db=users_found_in_db,
|
users_found_in_db=users_found_in_db,
|
||||||
users_created=users_created,
|
users_created=users_created,
|
||||||
@@ -282,11 +373,15 @@ async def perform_sync(panel_service: PanelApiService, session: AsyncSession,
|
|||||||
subscriptions_synced_count=subscriptions_synced_count,
|
subscriptions_synced_count=subscriptions_synced_count,
|
||||||
subscriptions_created=subscriptions_created,
|
subscriptions_created=subscriptions_created,
|
||||||
subscriptions_updated=subscriptions_updated,
|
subscriptions_updated=subscriptions_updated,
|
||||||
additional_stats=additional_stats
|
additional_stats=additional_stats,
|
||||||
)
|
)
|
||||||
|
|
||||||
await panel_sync_dal.update_panel_sync_status(
|
await panel_sync_dal.update_panel_sync_status(
|
||||||
session, status, details, panel_records_checked, subscriptions_synced_count
|
session,
|
||||||
|
status,
|
||||||
|
details,
|
||||||
|
panel_records_checked,
|
||||||
|
subscriptions_synced_count,
|
||||||
)
|
)
|
||||||
await session.commit()
|
await session.commit()
|
||||||
|
|
||||||
@@ -311,7 +406,7 @@ async def perform_sync(panel_service: PanelApiService, session: AsyncSession,
|
|||||||
"users_synced": users_found_in_db,
|
"users_synced": users_found_in_db,
|
||||||
"users_created": users_created,
|
"users_created": users_created,
|
||||||
"subs_synced": subscriptions_synced_count,
|
"subs_synced": subscriptions_synced_count,
|
||||||
"errors": sync_errors
|
"errors": sync_errors,
|
||||||
}
|
}
|
||||||
|
|
||||||
except Exception as e_sync_global:
|
except Exception as e_sync_global:
|
||||||
@@ -320,10 +415,18 @@ async def perform_sync(panel_service: PanelApiService, session: AsyncSession,
|
|||||||
error_detail = f"Unexpected error during sync: {str(e_sync_global)}"
|
error_detail = f"Unexpected error during sync: {str(e_sync_global)}"
|
||||||
|
|
||||||
await panel_sync_dal.update_panel_sync_status(
|
await panel_sync_dal.update_panel_sync_status(
|
||||||
session, "failed", error_detail, panel_records_checked, subscriptions_synced_count
|
session,
|
||||||
|
"failed",
|
||||||
|
error_detail,
|
||||||
|
panel_records_checked,
|
||||||
|
subscriptions_synced_count,
|
||||||
)
|
)
|
||||||
|
|
||||||
return {"status": "failed", "details": error_detail, "errors": [str(e_sync_global)]}
|
return {
|
||||||
|
"status": "failed",
|
||||||
|
"details": error_detail,
|
||||||
|
"errors": [str(e_sync_global)],
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@router.message(Command("sync"))
|
@router.message(Command("sync"))
|
||||||
@@ -375,7 +478,10 @@ async def sync_command_handler(
|
|||||||
if status == "failed":
|
if status == "failed":
|
||||||
await bot.send_message(target_chat_id, _("sync_failed_simple"))
|
await bot.send_message(target_chat_id, _("sync_failed_simple"))
|
||||||
elif status == "completed_with_errors":
|
elif status == "completed_with_errors":
|
||||||
await bot.send_message(target_chat_id, _("sync_errors_simple", errors_count=len(errors)))
|
await bot.send_message(
|
||||||
|
target_chat_id,
|
||||||
|
_("sync_errors_simple", errors_count=len(errors)),
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
await bot.send_message(target_chat_id, _("sync_success_simple"))
|
await bot.send_message(target_chat_id, _("sync_success_simple"))
|
||||||
|
|
||||||
@@ -383,15 +489,18 @@ async def sync_command_handler(
|
|||||||
try:
|
try:
|
||||||
notification_service = NotificationService(bot, settings, i18n)
|
notification_service = NotificationService(bot, settings, i18n)
|
||||||
await notification_service.notify_panel_sync(
|
await notification_service.notify_panel_sync(
|
||||||
status, details,
|
status,
|
||||||
|
details,
|
||||||
sync_result.get("users_processed", 0),
|
sync_result.get("users_processed", 0),
|
||||||
sync_result.get("subs_synced", 0)
|
sync_result.get("subs_synced", 0),
|
||||||
)
|
)
|
||||||
except Exception as e_notification:
|
except Exception as e_notification:
|
||||||
logging.error(f"Failed to send sync notification: {e_notification}")
|
logging.error(f"Failed to send sync notification: {e_notification}")
|
||||||
|
|
||||||
except Exception as e_sync_global:
|
except Exception as e_sync_global:
|
||||||
logging.error(f"Global error during /sync command: {e_sync_global}", exc_info=True)
|
logging.error(
|
||||||
|
f"Global error during /sync command: {e_sync_global}", exc_info=True
|
||||||
|
)
|
||||||
await bot.send_message(target_chat_id, _("sync_critical_error"))
|
await bot.send_message(target_chat_id, _("sync_critical_error"))
|
||||||
|
|
||||||
# Send notification to log channel about failure
|
# Send notification to log channel about failure
|
||||||
@@ -401,7 +510,9 @@ async def sync_command_handler(
|
|||||||
"failed", str(e_sync_global), 0, 0
|
"failed", str(e_sync_global), 0, 0
|
||||||
)
|
)
|
||||||
except Exception as e_notification:
|
except Exception as e_notification:
|
||||||
logging.error(f"Failed to send sync failure notification: {e_notification}")
|
logging.error(
|
||||||
|
f"Failed to send sync failure notification: {e_notification}"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@router.message(Command("syncstatus"))
|
@router.message(Command("syncstatus"))
|
||||||
@@ -420,7 +531,9 @@ async def sync_status_command_handler(
|
|||||||
if status_record_model:
|
if status_record_model:
|
||||||
last_time_val = status_record_model.last_sync_time
|
last_time_val = status_record_model.last_sync_time
|
||||||
last_time_str = (
|
last_time_str = (
|
||||||
last_time_val.strftime("%Y-%m-%d %H:%M:%S UTC") if last_time_val else "N/A"
|
last_time_val.strftime("%Y-%m-%d %H:%M:%S UTC")
|
||||||
|
if last_time_val
|
||||||
|
else "N/A"
|
||||||
)
|
)
|
||||||
|
|
||||||
details_val = status_record_model.details
|
details_val = status_record_model.details
|
||||||
|
|||||||
Reference in New Issue
Block a user