# ruff: noqa: F401,F403,F405,I001 from ._runtime import * # noqa: F403,F405 class PanelIdentityMixin: def _extract_panel_traffic_details( self, panel_user_data: Dict[str, Any] ) -> Tuple[Optional[int], Optional[int], Optional[str]]: traffic_stats = panel_user_data.get("userTraffic") or {} used = traffic_stats.get("usedTrafficBytes") if used is None: used = panel_user_data.get("usedTrafficBytes") limit = panel_user_data.get("trafficLimitBytes") strategy = panel_user_data.get("trafficLimitStrategy") if strategy is None: strategy = traffic_stats.get("trafficLimitStrategy") return used, limit, strategy def _extract_lifetime_used_traffic(self, panel_user_data: Dict[str, Any]) -> Optional[int]: traffic_stats = panel_user_data.get("userTraffic") or {} lifetime = traffic_stats.get("lifetimeUsedTrafficBytes") if lifetime is None: lifetime = panel_user_data.get("lifetimeUsedTrafficBytes") try: if lifetime is None: return None return int(lifetime) except (TypeError, ValueError): return None async def _notify_admin_panel_user_creation_failed(self, user_id: int): if not self.bot or not self.i18n or not self.settings.ADMIN_IDS: return admin_lang = self.settings.DEFAULT_LANGUAGE _adm = lambda k, **kw: self.i18n.gettext(admin_lang, k, **kw) msg = _adm("admin_panel_user_creation_failed", user_id=user_id) for admin_id in self.settings.ADMIN_IDS: try: await self.bot.send_message(admin_id, msg) except Exception as e: logging.error( f"Failed to notify admin {admin_id} about panel user creation failure: {e}" ) def _telegram_id_for_panel(self, db_user: User) -> Optional[int]: if db_user.telegram_id: return int(db_user.telegram_id) if db_user.user_id and int(db_user.user_id) > 0: return int(db_user.user_id) return None async def _panel_username_for_user(self, session: AsyncSession, db_user: User) -> str: telegram_id = self._telegram_id_for_panel(db_user) if telegram_id and int(db_user.user_id) == telegram_id: return f"tg_{telegram_id}" referral_code = await user_dal.ensure_referral_code(session, db_user) return f"em_{referral_code}" def _panel_description_for_user(self, db_user: User) -> str: lines = [ db_user.email or "", db_user.username or "", db_user.first_name or "", db_user.last_name or "", ] return "\n".join(line for line in lines if line).strip() def _panel_identity_payload_for_user(self, db_user: User) -> Dict[str, Any]: payload: Dict[str, Any] = { "description": self._panel_description_for_user(db_user), } telegram_id = self._telegram_id_for_panel(db_user) if telegram_id: payload["telegramId"] = telegram_id if db_user.email: payload["email"] = db_user.email return payload async def _get_or_create_panel_user_link_details( self, session: AsyncSession, user_id: int, db_user: Optional[User] = None ) -> Tuple[Optional[str], Optional[str], Optional[str], bool]: if not db_user: db_user = await user_dal.get_user_by_id(session, user_id) if not db_user: logging.error( f"_get_or_create_panel_user_link_details: User {user_id} not found in local DB. Cannot proceed." # noqa: E501 ) return None, None, None, False current_local_panel_uuid = db_user.panel_user_uuid panel_username_on_panel_standard = await self._panel_username_for_user(session, db_user) telegram_id_for_panel = self._telegram_id_for_panel(db_user) panel_user_obj_from_api = None panel_user_created_or_linked_now = False panel_users_by_tg_id_list = None if telegram_id_for_panel: panel_users_by_tg_id_list = await self.panel_service.get_users_by_filter( telegram_id=telegram_id_for_panel ) if panel_users_by_tg_id_list and len(panel_users_by_tg_id_list) == 1: panel_user_obj_from_api = panel_users_by_tg_id_list[0] logging.info( f"Found panel user by telegramId {telegram_id_for_panel}: UUID {panel_user_obj_from_api.get('uuid')}, Username: {panel_user_obj_from_api.get('username')}" # noqa: E501 ) elif panel_users_by_tg_id_list and len(panel_users_by_tg_id_list) > 1: logging.error( f"CRITICAL: Multiple panel users found for telegramId {telegram_id_for_panel}. Manual intervention needed." # noqa: E501 ) return None, None, None, False if not panel_user_obj_from_api and db_user.email: panel_users_by_email_list = await self.panel_service.get_users_by_filter( email=db_user.email ) if panel_users_by_email_list and len(panel_users_by_email_list) == 1: panel_user_obj_from_api = panel_users_by_email_list[0] logging.info( f"Found panel user by email {db_user.email}: UUID {panel_user_obj_from_api.get('uuid')}, Username: {panel_user_obj_from_api.get('username')}" # noqa: E501 ) elif panel_users_by_email_list and len(panel_users_by_email_list) > 1: logging.error( f"CRITICAL: Multiple panel users found for email {db_user.email}. Manual intervention needed." # noqa: E501 ) return None, None, None, False if not panel_user_obj_from_api: if current_local_panel_uuid: logging.info( f"User {user_id} (local panel_uuid: {current_local_panel_uuid}) not found on panel by TG ID. Fetching by panel_uuid." # noqa: E501 ) panel_user_obj_from_api = await self.panel_service.get_user_by_uuid( current_local_panel_uuid ) if not panel_user_obj_from_api: logging.warning( f"Local panel_uuid {current_local_panel_uuid} for TG user {user_id} also not found on panel. User might be deleted from panel or UUID desynced." # noqa: E501 ) logging.info( f"Creating new panel user '{panel_username_on_panel_standard}' for TG user {user_id}." # noqa: E501 ) creation_response = await self.panel_service.create_panel_user( username_on_panel=panel_username_on_panel_standard, telegram_id=telegram_id_for_panel, email=db_user.email, description=self._panel_description_for_user(db_user), specific_squad_uuids=self.settings.parsed_user_squad_uuids, external_squad_uuid=self.settings.parsed_user_external_squad_uuid, default_traffic_limit_bytes=self.settings.user_traffic_limit_bytes, default_traffic_limit_strategy=self.settings.USER_TRAFFIC_STRATEGY, ) if ( creation_response and not creation_response.get("error") and creation_response.get("response") ): panel_user_obj_from_api = creation_response.get("response") panel_user_created_or_linked_now = True else: await self._notify_admin_panel_user_creation_failed(user_id) return None, None, None, False else: logging.info( f"No panel user by TG ID & no local panel_uuid for TG user {user_id}. Creating new panel user '{panel_username_on_panel_standard}'." # noqa: E501 ) creation_response = await self.panel_service.create_panel_user( username_on_panel=panel_username_on_panel_standard, telegram_id=telegram_id_for_panel, email=db_user.email, description=self._panel_description_for_user(db_user), specific_squad_uuids=self.settings.parsed_user_squad_uuids, external_squad_uuid=self.settings.parsed_user_external_squad_uuid, default_traffic_limit_bytes=self.settings.user_traffic_limit_bytes, default_traffic_limit_strategy=self.settings.USER_TRAFFIC_STRATEGY, ) if ( creation_response and not creation_response.get("error") and creation_response.get("response") ): panel_user_obj_from_api = creation_response.get("response") panel_user_created_or_linked_now = True elif creation_response and creation_response.get("errorCode") == "A019": logging.warning( f"Panel user '{panel_username_on_panel_standard}' already exists (errorCode A019). Fetching by username." # noqa: E501 ) fetched_by_username_list = await self.panel_service.get_users_by_filter( username=panel_username_on_panel_standard ) if fetched_by_username_list and len(fetched_by_username_list) == 1: panel_user_obj_from_api = fetched_by_username_list[0] if not panel_user_obj_from_api: logging.error( f"Failed to create or link panel user for TG_ID {user_id} with panel username '{panel_username_on_panel_standard}'. Response: {creation_response if 'creation_response' in locals() else 'N/A'}" # noqa: E501 ) await self._notify_admin_panel_user_creation_failed(user_id) return None, None, None, False if not panel_user_obj_from_api: logging.error( f"Could not obtain panel user object for TG user {user_id} after all checks." ) return ( current_local_panel_uuid if current_local_panel_uuid else None, None, None, panel_user_created_or_linked_now, ) actual_panel_uuid_from_api = panel_user_obj_from_api.get("uuid") panel_telegram_id_from_api = panel_user_obj_from_api.get("telegramId") if not actual_panel_uuid_from_api: logging.error( f"Panel user object for TG user {user_id} does not contain 'uuid'. Data: {panel_user_obj_from_api}" # noqa: E501 ) return ( current_local_panel_uuid, None, None, panel_user_created_or_linked_now, ) needs_local_panel_uuid_update = False if current_local_panel_uuid is None and actual_panel_uuid_from_api: needs_local_panel_uuid_update = True elif ( current_local_panel_uuid is not None and current_local_panel_uuid != actual_panel_uuid_from_api ): logging.warning( f"Local panel_uuid for user {user_id} ('{current_local_panel_uuid}') " f"differs from panel's UUID ('{actual_panel_uuid_from_api}') for their telegramId. " f"Will attempt to update local to panel's version." ) needs_local_panel_uuid_update = True if needs_local_panel_uuid_update: conflicting_user_record = await user_dal.get_user_by_panel_uuid( session, actual_panel_uuid_from_api ) if conflicting_user_record and conflicting_user_record.user_id != user_id: logging.error( f"CRITICAL CONFLICT: Panel UUID {actual_panel_uuid_from_api} (from panel for TG ID {user_id}) " # noqa: E501 f"is ALREADY LINKED in local DB to a different TG User {conflicting_user_record.user_id}. " # noqa: E501 f"Cannot update panel_user_uuid for user {user_id}. Manual data correction needed." # noqa: E501 ) return None, None, None, False else: update_data_for_local_user = {"panel_user_uuid": actual_panel_uuid_from_api} # Do not overwrite Telegram username with panel username. # Only update the local linkage to panel UUID here. await user_dal.update_user(session, user_id, update_data_for_local_user) db_user.panel_user_uuid = actual_panel_uuid_from_api panel_user_created_or_linked_now = True current_local_panel_uuid = actual_panel_uuid_from_api else: pass panel_telegram_id_int = None if panel_telegram_id_from_api is not None: try: panel_telegram_id_int = int(panel_telegram_id_from_api) except ValueError: pass if ( panel_user_obj_from_api and current_local_panel_uuid and telegram_id_for_panel and panel_telegram_id_int != telegram_id_for_panel ): logging.info( f"Panel user {current_local_panel_uuid} has telegramId '{panel_telegram_id_from_api}'. Updating on panel to '{telegram_id_for_panel}'." # noqa: E501 ) await self.panel_service.update_user_details_on_panel( current_local_panel_uuid, self._panel_identity_payload_for_user(db_user), ) panel_sub_link_id = panel_user_obj_from_api.get( "subscriptionUuid" ) or panel_user_obj_from_api.get("shortUuid") panel_short_uuid = panel_user_obj_from_api.get("shortUuid") if not panel_sub_link_id and current_local_panel_uuid: logging.warning( f"No subscriptionUuid or shortUuid found on panel for panel_user_uuid {current_local_panel_uuid} (TG ID: {user_id})." # noqa: E501 ) return ( current_local_panel_uuid, panel_sub_link_id, panel_short_uuid, panel_user_created_or_linked_now, ) def _build_panel_update_payload( self, *, panel_user_uuid: Optional[str] = None, expire_at: Optional[datetime] = None, status: Optional[str] = None, traffic_limit_bytes: Optional[int] = None, include_uuid: bool = True, traffic_limit_strategy: Optional[str] = None, hwid_device_limit: Optional[int] = None, include_default_squads: bool = True, ) -> Dict[str, Any]: payload: Dict[str, Any] = {} if include_uuid and panel_user_uuid: payload["uuid"] = panel_user_uuid if expire_at is not None: payload["expireAt"] = expire_at.isoformat(timespec="milliseconds").replace( "+00:00", "Z" ) if status is not None: payload["status"] = status if traffic_limit_bytes is not None: payload["trafficLimitBytes"] = traffic_limit_bytes payload["trafficLimitStrategy"] = ( traffic_limit_strategy or self.settings.USER_TRAFFIC_STRATEGY ) if hwid_device_limit is not None: try: hwid_limit_int = int(hwid_device_limit) if hwid_limit_int >= 0: payload["hwidDeviceLimit"] = hwid_limit_int except (TypeError, ValueError): pass if include_default_squads: if self.settings.parsed_user_squad_uuids: payload["activeInternalSquads"] = self.settings.parsed_user_squad_uuids if self.settings.parsed_user_external_squad_uuid: payload["externalSquadUuid"] = self.settings.parsed_user_external_squad_uuid return payload