Remove panel unreachable placeholders and enhance sync

This commit is contained in:
Machka Pasla
2025-06-26 00:57:48 +03:00
parent b97774c5b5
commit b2a0ea5bd2
7 changed files with 717 additions and 521 deletions
+142 -102
View File
@@ -16,11 +16,14 @@ router = Router(name="admin_sync_router")
@router.message(Command("sync"))
async def sync_command_handler(message_event: Union[types.Message,
types.CallbackQuery],
bot: Bot, settings: Settings, i18n_data: dict,
panel_service: PanelApiService,
session: AsyncSession):
async def sync_command_handler(
message_event: Union[types.Message, types.CallbackQuery],
bot: Bot,
settings: Settings,
i18n_data: dict,
panel_service: PanelApiService,
session: AsyncSession,
):
current_lang = i18n_data.get("current_language", settings.DEFAULT_LANGUAGE)
i18n: Optional[JsonI18n] = i18n_data.get("i18n_instance")
if not i18n:
@@ -33,14 +36,15 @@ async def sync_command_handler(message_event: Union[types.Message,
return
_ = lambda key, **kwargs: i18n.gettext(current_lang, key, **kwargs)
target_chat_id = message_event.chat.id if isinstance(
message_event, types.Message) else (
message_event.message.chat.id if message_event.message else None)
target_chat_id = (
message_event.chat.id
if isinstance(message_event, types.Message)
else (message_event.message.chat.id if message_event.message else None)
)
if not target_chat_id:
logging.error("Sync handler: could not determine target_chat_id.")
if isinstance(message_event, types.CallbackQuery):
await message_event.answer("Error initiating sync.",
show_alert=True)
await message_event.answer("Error initiating sync.", show_alert=True)
return
if isinstance(message_event, types.Message):
@@ -59,21 +63,21 @@ async def sync_command_handler(message_event: Union[types.Message,
if panel_users_data is None:
error_msg = "Failed to fetch users from panel or panel API issue."
sync_errors.append(error_msg)
await panel_sync_dal.update_panel_sync_status(
session, "failed", error_msg)
await panel_sync_dal.update_panel_sync_status(session, "failed", error_msg)
await session.commit()
await bot.send_message(target_chat_id,
_("sync_failed", details=error_msg))
await bot.send_message(target_chat_id, _("sync_failed", details=error_msg))
return
if not panel_users_data:
status_msg = "No users found in the panel to sync."
await panel_sync_dal.update_panel_sync_status(
session, "success", status_msg, 0, 0)
session, "success", status_msg, 0, 0
)
await session.commit()
await bot.send_message(
target_chat_id,
_("sync_completed", status="Success", details=status_msg))
_("sync_completed", status="Success", details=status_msg),
)
return
total_panel_users = len(panel_users_data)
@@ -81,9 +85,9 @@ async def sync_command_handler(message_event: Union[types.Message,
for panel_user_dict in panel_users_data:
users_processed_count += 1
panel_uuid = panel_user_dict.get('uuid')
telegram_id_from_panel_str = panel_user_dict.get('telegramId')
panel_username = panel_user_dict.get('username')
panel_uuid = panel_user_dict.get("uuid")
telegram_id_from_panel_str = panel_user_dict.get("telegramId")
panel_username = panel_user_dict.get("username")
if not panel_uuid:
logging.warning(
@@ -111,26 +115,22 @@ async def sync_command_handler(message_event: Union[types.Message,
continue
bot_user = await user_dal.get_user_by_id(session,
telegram_id_from_panel)
bot_user = await user_dal.get_user_by_id(session, telegram_id_from_panel)
if not bot_user:
user_data_to_create = {
"user_id":
telegram_id_from_panel,
"username":
panel_username,
"panel_user_uuid":
panel_uuid,
"language_code":
settings.DEFAULT_LANGUAGE,
"registration_date":
datetime.fromisoformat(
panel_user_dict['createdAt'].replace("Z", "+00:00"))
if panel_user_dict.get('createdAt') else datetime.now(
timezone.utc)
"user_id": telegram_id_from_panel,
"username": panel_username,
"panel_user_uuid": panel_uuid,
"language_code": settings.DEFAULT_LANGUAGE,
"registration_date": (
datetime.fromisoformat(
panel_user_dict["createdAt"].replace("Z", "+00:00")
)
if panel_user_dict.get("createdAt")
else datetime.now(timezone.utc)
),
}
bot_user = await user_dal.create_user(session,
user_data_to_create)
bot_user = await user_dal.create_user(session, user_data_to_create)
logging.info(
f"Sync: Created new local user {telegram_id_from_panel} from panel data {panel_uuid}."
)
@@ -142,8 +142,12 @@ async def sync_command_handler(message_event: Union[types.Message,
)
conflicting_user = await user_dal.get_user_by_panel_uuid(
session, panel_uuid)
if conflicting_user and conflicting_user.user_id != telegram_id_from_panel:
session, panel_uuid
)
if (
conflicting_user
and conflicting_user.user_id != telegram_id_from_panel
):
sync_errors.append(
f"Panel UUID {panel_uuid} for TG {telegram_id_from_panel} already linked to another TG user {conflicting_user.user_id}."
)
@@ -151,60 +155,64 @@ async def sync_command_handler(message_event: Union[types.Message,
continue
await user_dal.update_user(
session, telegram_id_from_panel, {
"panel_user_uuid": panel_uuid,
"username": panel_username
})
session,
telegram_id_from_panel,
{"panel_user_uuid": panel_uuid, "username": panel_username},
)
logging.info(
f"Sync: Updated panel_uuid for local user {telegram_id_from_panel} to {panel_uuid}."
)
panel_sub_link_id = panel_user_dict.get(
'subscriptionUuid') or panel_user_dict.get('shortUuid')
"subscriptionUuid"
) or panel_user_dict.get("shortUuid")
if panel_sub_link_id:
end_date_str = panel_user_dict.get('expireAt')
start_date_str = panel_user_dict.get('createdAt')
end_date_str = panel_user_dict.get("expireAt")
start_date_str = panel_user_dict.get("createdAt")
if end_date_str:
try:
end_date_obj = datetime.fromisoformat(
end_date_str.replace("Z", "+00:00"))
start_date_obj = datetime.fromisoformat(
start_date_str.replace(
"Z",
"+00:00")) if start_date_str else datetime.now(
timezone.utc)
end_date_str.replace("Z", "+00:00")
)
start_date_obj = (
datetime.fromisoformat(
start_date_str.replace("Z", "+00:00")
)
if start_date_str
else datetime.now(timezone.utc)
)
status_from_panel = panel_user_dict.get(
'status', 'UNKNOWN').upper()
is_active_flag = 1 if status_from_panel == 'ACTIVE' and end_date_obj > datetime.now(
timezone.utc) else 0
"status", "UNKNOWN"
).upper()
is_active_flag = (
1
if status_from_panel == "ACTIVE"
and end_date_obj > datetime.now(timezone.utc)
else 0
)
sub_payload = {
"user_id":
telegram_id_from_panel,
"panel_user_uuid":
panel_uuid,
"panel_subscription_uuid":
panel_sub_link_id,
"start_date":
start_date_obj,
"end_date":
end_date_obj,
"is_active":
is_active_flag,
"status_from_panel":
status_from_panel,
"traffic_limit_bytes":
panel_user_dict.get('trafficLimitBytes'),
"traffic_used_bytes":
panel_user_dict.get('usedTrafficBytes')
"user_id": telegram_id_from_panel,
"panel_user_uuid": panel_uuid,
"panel_subscription_uuid": panel_sub_link_id,
"start_date": start_date_obj,
"end_date": end_date_obj,
"is_active": is_active_flag,
"status_from_panel": status_from_panel,
"traffic_limit_bytes": panel_user_dict.get(
"trafficLimitBytes"
),
"traffic_used_bytes": panel_user_dict.get(
"usedTrafficBytes"
),
}
await subscription_dal.deactivate_other_active_subscriptions(
session, panel_uuid, panel_sub_link_id)
await subscription_dal.upsert_subscription(
session, sub_payload)
session, panel_uuid, panel_sub_link_id
)
await subscription_dal.upsert_subscription(session, sub_payload)
subscriptions_synced_count += 1
users_synced_successfully += 1
except ValueError as e_date:
@@ -217,7 +225,8 @@ async def sync_command_handler(message_event: Union[types.Message,
except Exception as e_sub_sync:
logging.error(
f"Sync: Error syncing subscription for panel user {panel_uuid} (TG ID: {telegram_id_from_panel}): {e_sub_sync}",
exc_info=True)
exc_info=True,
)
sync_errors.append(
f"Sub sync error for panel user {panel_uuid} (TG ID: {telegram_id_from_panel})."
)
@@ -228,7 +237,8 @@ async def sync_command_handler(message_event: Union[types.Message,
else:
await subscription_dal.deactivate_other_active_subscriptions(
session, panel_uuid, None)
session, panel_uuid, None
)
logging.info(
f"Sync: Panel user {panel_uuid} (TG ID: {telegram_id_from_panel}) has no subscription link on panel. Deactivated local subs if any."
)
@@ -239,6 +249,17 @@ async def sync_command_handler(message_event: Union[types.Message,
f"Sync progress: {users_processed_count}/{total_panel_users} users processed from panel."
)
panel_uuid_set = {u.get("uuid") for u in panel_users_data if u.get("uuid")}
local_users_with_uuid = await user_dal.get_all_users_with_panel_uuid(session)
for local_user in local_users_with_uuid:
if local_user.panel_user_uuid not in panel_uuid_set:
await subscription_dal.deactivate_other_active_subscriptions(
session, local_user.panel_user_uuid, None
)
logging.info(
f"Sync: Local user {local_user.user_id} with panel UUID {local_user.panel_user_uuid} not found on panel. Deactivated local subs."
)
status_msg_key = "sync_completed_details"
final_status_type = "success"
@@ -251,36 +272,51 @@ async def sync_command_handler(message_event: Union[types.Message,
details_for_db = f"Successfully processed {users_processed_count} users. Synced {subscriptions_synced_count} subscriptions."
await panel_sync_dal.update_panel_sync_status(
session, final_status_type, details_for_db, users_processed_count,
subscriptions_synced_count)
session,
final_status_type,
details_for_db,
users_processed_count,
subscriptions_synced_count,
)
await session.commit()
final_user_message = _(status_msg_key,
total_checked=total_panel_users,
users_synced=users_synced_successfully,
subs_synced=subscriptions_synced_count,
errors_count=len(sync_errors),
error_details_preview=error_preview
if sync_errors else _("no_errors_placeholder"))
final_user_message = _(
status_msg_key,
total_checked=total_panel_users,
users_synced=users_synced_successfully,
subs_synced=subscriptions_synced_count,
errors_count=len(sync_errors),
error_details_preview=(
error_preview if sync_errors else _("no_errors_placeholder")
),
)
await bot.send_message(target_chat_id, final_user_message)
except Exception as e_sync_global:
await session.rollback()
logging.error(f"Global error during /sync command: {e_sync_global}",
exc_info=True)
error_detail_for_db = f"An unexpected error occurred during sync: {str(e_sync_global)[:200]}"
logging.error(
f"Global error during /sync command: {e_sync_global}", exc_info=True
)
error_detail_for_db = (
f"An unexpected error occurred during sync: {str(e_sync_global)[:200]}"
)
await panel_sync_dal.update_panel_sync_status(
session, "failed", error_detail_for_db, users_processed_count,
subscriptions_synced_count)
session,
"failed",
error_detail_for_db,
users_processed_count,
subscriptions_synced_count,
)
await bot.send_message(target_chat_id,
_("sync_failed", details=error_detail_for_db))
await bot.send_message(
target_chat_id, _("sync_failed", details=error_detail_for_db)
)
@router.message(Command("syncstatus"))
async def sync_status_command_handler(message: types.Message, i18n_data: dict,
settings: Settings,
session: AsyncSession):
async def sync_status_command_handler(
message: types.Message, i18n_data: dict, settings: Settings, session: AsyncSession
):
current_lang = i18n_data.get("current_language", settings.DEFAULT_LANGUAGE)
i18n: Optional[JsonI18n] = i18n_data.get("i18n_instance")
if not i18n:
@@ -292,13 +328,16 @@ async def sync_status_command_handler(message: types.Message, i18n_data: dict,
response_text = ""
if status_record_model:
last_time_val = status_record_model.last_sync_time
last_time_str = last_time_val.strftime(
'%Y-%m-%d %H:%M:%S UTC') if last_time_val else "N/A"
last_time_str = (
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_str = (details_val[:200] +
"...") if details_val and len(details_val) > 200 else (
details_val or "N/A")
details_str = (
(details_val[:200] + "...")
if details_val and len(details_val) > 200
else (details_val or "N/A")
)
response_text = (
f"<b>{_('admin_stats_last_sync_header')}</b>\n"
@@ -306,7 +345,8 @@ async def sync_status_command_handler(message: types.Message, i18n_data: dict,
f" {_('admin_stats_sync_status')}: {status_record_model.status}\n"
f" {_('admin_stats_sync_users_processed')}: {status_record_model.users_processed_from_panel}\n"
f" {_('admin_stats_sync_subs_synced')}: {status_record_model.subscriptions_synced}\n"
f" {_('admin_stats_sync_details_label')}: {details_str}")
f" {_('admin_stats_sync_details_label')}: {details_str}"
)
else:
response_text = _("admin_sync_status_never_run")