Commit Graph

95 Commits

Author SHA1 Message Date
Fringg 6d9bd9915c fix: persist referral to Redis on /start to prevent loss when user opens miniapp
When user clicks /start ref_CODE, the referral code was stored only in
FSM state. If the user opened miniapp/cabinet before completing bot
registration, the referral was lost.

Now /start immediately saves pending_referral:{telegram_id} to Redis
(7-day TTL). The referral is consumed by whichever path creates the
user first — bot create_user(), cabinet auth (initdata/widget/oidc).
Redis key is cleared after consumption to prevent double-referral.

- referral_service: save/get/clear_pending_referral Redis helpers
- start.py: save pending referral for new users only
- crud/user.py: create_user checks Redis if no referred_by_id
- cabinet/auth.py: initdata/widget/oidc routes check + cleanup Redis
2026-03-26 11:55:20 +03:00
Fringg 94ed282381 fix: multi-tariff MEDIUM/LOW batch — 20 issues across 17 files
MEDIUM fixes:
- config: MAX_ACTIVE_SUBSCRIPTIONS=10 limit + check in tariff purchase
- tariff_purchase: add_user_balance return check → _persist_failed_refund
- tariff_purchase: promo restoration atomic with refund (commit=False)
- purchase: handle_toggle_daily_subscription_pause multi-tariff guard
- my_subscriptions: respect HIDE_SUBSCRIPTION_LINK setting
- multi_tariff: delete_subscription uses actual_status
- tariff_switch: guard against switching to already-owned tariff
- monitoring: i18n button texts via texts.t() + per-subscription dedup
- promo_offer_service: multi-tariff subscription resolution
- bot.py: register DisplayNameRestrictionMiddleware + SubscriptionStatusMiddleware on pre_checkout_query
- crud/user: add_user_balance accepts commit=False
- crud/subscription: update_daily_charge_time refresh only on commit, autopay days_before preserved
- daily_subscription: remove redundant outer commit in process_traffic_resets

LOW fixes:
- inline.py: remove dead get_subscription_expiring_keyboard function
- promocode_service: current_uses +1 in response (post-increment)
- yookassa: use already-resolved subscription for admin notification
- subscription_utils: remove dead ensure_single_subscription + update_or_create_subscription
- devices: merge identical daily/non-daily branches in confirm_change_devices
2026-03-26 11:00:15 +03:00
Fringg c6bedc6a06 fix: multi-tariff Stage 2 HIGH fixes — 18 issues across 12 files
Bot handlers (H1-H5):
- confirm_extend_subscription: error alert instead of wrong sub fallback
- open_subscription_link/subscription_connect: startswith registration
- handle_subscription_settings: multi-tariff guard
- confirm_reset_traffic: FSM state check in multi-tariff

Services (H6-H12):
- subscription_service: 5 UUID fallback fixes — no user.remnawave_uuid in
  multi-tariff, return None if subscription.remnawave_uuid missing
- auto_purchase: use cart subscription_id for tariff match
- remnawave_service: migrate_squad_users checks subscription.remnawave_uuid
- campaign_service: extend existing sub or create new in multi-tariff
- broadcast_service: check ALL subs for paid-subscription guard
- blocked_users_service: remnawave_uuids list, iterate in cleanup
- user_service: log sub.remnawave_uuid in multi-tariff

Admin (H13-H16):
- grant_trial/paid_subscription: allow in multi-tariff mode
- promo_offers: pick sub with URL, aggregate squads from all subs

CRUD/Frontend (H17-H18):
- get_users_list: .unique() for outerjoin dedup
- refreshTraffic: withSubId in params instead of body
2026-03-26 08:09:07 +03:00
c0mrade c3c2b8137b fix: RemnaWave sync finds user by Subscription.remnawave_uuid in multi-tariff
get_user_by_remnawave_uuid: fallback query searches Subscription table
when User-level UUID not found (multi-tariff stores UUID per-subscription).

Webhook _resolve_user_and_subscription: direct Subscription lookup before
returning None when user not found by telegram_id or User.remnawave_uuid.

Webhook user.deleted: refresh user.subscriptions before iterating to
ensure relationship is loaded from DB.

Account merge: clear subscription-level remnawave_uuid/short_uuid on
secondary user's subscriptions to prevent orphaned panel users.
2026-03-25 17:05:12 +03:00
c0mrade 335be66980 feat: multi-subscription support (1 user = N subscriptions)
- Add MULTI_TARIFF_ENABLED feature flag for gradual rollout
- Migration 0041: remove unique constraint on subscriptions.user_id
- Migration 0042: add remnawave_short_id (NOT NULL, UNIQUE) to subscriptions
- Each subscription gets its own Remnawave user (user_{tg_id}_{short_id})
- Add _resolve_subscription() to all 30+ bot handlers for per-subscription routing
- Add my_subscriptions.py with list/detail views and delegation handlers
- Refactor cabinet subscription.py (4687 lines -> 10 focused modules)
- Add subscription_id parameter to all cabinet API endpoints
- Adapt all services: autopay, wheel, contests, campaign, guest purchase,
  monitoring, blocked users, account merge, promo codes, payments
- Replace all user.subscription (singular) with user.subscriptions iteration
- IDOR protection via get_subscription_by_id_for_user on all endpoints
- Full backward compatibility: MULTI_TARIFF_ENABLED=False = legacy behavior
2026-03-23 18:37:17 +03:00
Fringg 8175bc8bfe fix: comprehensive security hardening across payment and API layers
- CloudPayments: require webhook signature when secret configured (all 4 handlers)
- Platega: timing-safe HMAC comparison via hmac.compare_digest
- CryptoBot/Heleket: return False when API token unconfigured
- Tribute: return 503 when API key not configured
- Freekassa: use request.client.host instead of X-Forwarded-For
- Pal24: verify webhook amount matches stored payment amount
- YooKassa: reject test-mode payments in production; add YOOKASSA_TEST_MODE config
- CloudPayments: reject test-mode payments in production
- WebAPI: add upper bounds to duration_days, traffic_limit_gb, device_limit schemas
- WebAPI: bound balance update amount to ±100M kopeks
- WebAPI: sign-dispatch for balance updates (negative → subtract_user_balance)
- WebAPI miniapp: add blocked/deleted user checks, restriction_topup/subscription guards
- Admin handlers: add @admin_required and @error_handler to moderator panel
- add_user_balance: guard against negative amounts (use subtract_user_balance instead)
2026-03-23 16:44:38 +03:00
Fringg fad77f8c80 fix: phantom user merge on claim failure, referral assignment, account merge hardening
- Fix orphaned subscriptions/GuestPurchase when phantom claim fails with
  IntegrityError — now merges phantom into existing user across all 3 call sites
- Add explicit db.commit() after merge in both active-user and registration paths
- Fix remnawave_uuid transfer ordering (clear→flush→assign) to prevent unique
  constraint violation during flush
- Clear phantom.referral_code on soft-delete to prevent unique constraint issues
- Add status != DELETED filter to find_phantom_user_by_username (defense in depth)
- Add WARNING-level logging on phantom claims for admin audit trail
- Add functional index on lower(username) for phantom lookup performance (migration 0048)
- Add ON DELETE CASCADE to subscription_servers.subscription_id (migration 0047)
- Add admin endpoint POST /users/{id}/assign-referrer with recursive CTE cycle
  detection, self-enrichment prevention, and audit logging
- Harden account_merge_service: add SubscriptionServer, RioPayPayment,
  SeverPayPayment, SavedPaymentMethod, GuestPurchase, NewsArticle handling
- Fix logger key typo get= → error= in promocode activation
2026-03-23 13:59:38 +03:00
Fringg 826accba51 fix: MissingGreenlet при изменении количества устройств на CLASSIC подписках
lock_user_for_pricing не загружал User.subscription eagerly,
что вызывало lazy load в async контексте при обращении к db_user.subscription
в execute_change_devices.
2026-03-16 11:25:33 +03:00
Fringg 8d3cd50098 refactor: централизация всех расчётов цен в PricingEngine
- Мигрирован confirm_purchase() на calculate_classic_new_subscription_price()
- Мигрирован compute_simple_subscription_price на делегацию в PricingEngine
- Мигрирован handle_custom_confirm на calculate_tariff_purchase_price()
- Мигрированы daily confirm handlers (confirm_daily_tariff_purchase,
  confirm_daily_tariff_switch, confirm_instant_switch daily path)
- Мигрирован gift.py на calculate_tariff_purchase_price()
- Мигрированы FSM cache prices (select_period, select_devices, toggle_country)
- Добавлен lock_user_for_pricing в admin_buy_tariff_execute (TOCTOU fix)
- Добавлен lock + recompute в _auto_add_devices и _auto_add_traffic
- Исправлено двойное применение promo-offer в simple_subscription (критический баг)
- Унифицирован daily price display (group+offer) на всех 6 поверхностях
- PricingEngine.get_addon_discount_percent: добавлен promo_group= kwarg
- PricingEngine._calculate_switch_to/from_daily: добавлен promo-offer discount
- Удалён мёртвый код из common.py (_get_addon_discount_percent_for_user)
- Miniapp period_discounts: исправлен доступ через get_discount_percent()
2026-03-16 03:10:22 +03:00
Legacyyy777 ebee8348ca fix: implement case-insensitive email checks in authentication and user retrieval
Updated email queries in authentication routes and user CRUD operations to be case-insensitive. This change ensures that email comparisons ignore case, improving user experience and preventing potential registration/login issues with differently cased emails.
2026-03-14 04:39:11 +05:00
c0mrade 3306e02902 fix: add nested selectinload and referrer eager loading to prevent MissingGreenlet
Added selectinload(UserPromoGroup.promo_group) nested under
user_promo_groups to prevent lazy-load in get_primary_promo_group().
Added selectinload(User.referrer) for format_referrer_info().
Broadened except clause in format_referrer_info as safety net.
2026-03-14 00:14:42 +03:00
c0mrade 5442f288d4 fix: add selectinload to user lock queries to prevent MissingGreenlet
lock_user_for_update, subtract_user_balance, and add_user_balance use
select(User).with_for_update().populate_existing which expires loaded
relationships. Added selectinload for subscription, user_promo_groups
and promo_group to prevent lazy-load in async context.
2026-03-13 21:39:31 +03:00
Fringg b4ef52caa4 fix: payment providers — lock_user_for_update + commit=False atomicity
All payment providers now use lock_user_for_update before balance mutations
and commit=False pattern for atomic payment status + fulfillment.
Tribute service refund also uses proper locking.
2026-03-13 05:12:15 +03:00
Fringg b7775b72dc fix: guard rollback on commit flag, add flush to promo_offer_log
- subtract_user_balance: only rollback when commit=True, re-raise when
  commit=False so caller controls transaction lifecycle
- log_promo_offer_action: add db.flush() when commit=False to surface
  constraint errors immediately instead of deferring to caller's commit
2026-03-12 19:33:25 +03:00
Fringg ba54819f9c fix: atomicity refactor, review fixes, and DELETED recovery logging
- subtract_user_balance: add commit=False parameter for atomic balance+subscription ops
- extend_subscription: add commit=False parameter, propagate to clear_notifications
- wata_service: wire _MIN_EXPIRATION_MINUTES constant to actual usage
- admin_users: fix no-op ternary in sync_user_from_panel timezone normalization
- start.py: log warning when DELETED recovery zeros non-zero balance (3 locations)
- remnawave_service: preserve PromoCodeUse records and used_promocodes in force_cleanup
2026-03-12 19:26:36 +03:00
Fringg 641ff86bf6 fix: stars rate rounding + device/traffic purchase stats
- Stars: ceil → round в rubles_to_stars, нормализация kopeks в cabinet invoice
- Устройства/трафик: добавлен transaction_type в subtract_user_balance,
  покупки устройств и трафика теперь создают SUBSCRIPTION_PAYMENT (не WITHDRAWAL),
  что исправляет отображение в статистике продаж
2026-03-10 21:50:20 +03:00
Fringg 770b31d3d0 feat: auto-resume disabled daily subscriptions on balance topup
- Add try_resume_disabled_daily_after_topup() for instant resume when balance is topped up
- Fix all 5 resume paths to charge daily fee BEFORE activating subscription
- Remove unsafe inline auto-resume from add_user_balance() that bypassed fee charging
- Add NULL-safe is_daily_paused filter in subscription queries
- Use create_remnawave_user() instead of enable_remnawave_user() for full VPN panel sync
- Add DAILY_SUBSCRIPTION_RESUMED_AFTER_TOPUP localization key (ru, en, fa, zh, ua)
2026-03-08 21:36:17 +03:00
Fringg 9ba61a0879 feat: add telegram gift notification with inline activation button
- New gift_activation handler for gift_activate:{id} callback buttons
- Send Telegram notification to gift recipients with activate button
- Add skip_notification param to activate_purchase to prevent duplicates
- Fix telegram username regex minimum length (4→5 chars) in landing routes
- Add BOT_TOKEN guard in telegram gift notification sender
- Pre-resolve notification params before commit to avoid DetachedInstanceError
2026-03-07 20:39:04 +03:00
Fringg 6da61d7951 fix: убрать WITHDRAWAL из автонегации, добавить abs() в агрегации, исправить all_time_stats
- Убран WITHDRAWAL из автонегации в create_transaction (ломал profit,
  expenses и display flip в admin_users)
- Добавлен func.abs() в by_type агрегацию (transaction.py)
- Добавлен func.abs() в total_spent user.py (_build_spending_stats_select)
- Исправлен all_time_stats в боте и webapi: передаём явный диапазон дат
  вместо дефолтного текущего месяца
2026-03-05 09:05:02 +03:00
Fringg a7a18dd0d1 fix: устранение race condition при покупке устройств через re-lock после коммита
subtract_user_balance() делает внутренний коммит, что освобождает
SELECT FOR UPDATE блокировки. Добавлен паттерн re-lock + re-validate +
refund после вызова subtract_user_balance во всех 8 путях мутации
device_limit:

- cabinet: /devices, /devices/purchase, /devices/reduce
- miniapp: /subscription/devices
- bot handlers: execute_change_devices, confirm_add_devices
- auto-purchase: _auto_add_devices
- CRUD: add_subscription_devices

Также добавлен populate_existing=True ко всем SELECT FOR UPDATE запросам
для корректного обновления SQLAlchemy identity map.
2026-03-05 08:15:00 +03:00
Fringg e4a6aad621 fix: centralize has_had_paid_subscription into subtract_user_balance
Add mark_as_paid_subscription parameter to subtract_user_balance() that
atomically sets has_had_paid_subscription=True within the same FOR UPDATE
transaction as the balance deduction. This closes ALL purchase paths:

- Cabinet renew: add SELECT FOR UPDATE row lock (fix race condition),
  set has_had_paid_subscription atomically, remove standalone call
- Cabinet purchase_tariff: pass consume_promo_offer to subtract_user_balance
  (fix: inline clearing was wiped by db.refresh), remove standalone call
- Cabinet switch_tariff: add mark_as_paid_subscription=True
- Auto-extend: add mark_as_paid_subscription, remove standalone call
- Auto-purchase tariff: add consume_promo_offer + mark_as_paid_subscription
- Auto-purchase daily: add mark_as_paid_subscription
- Bot purchase/extend/trial handlers: add mark_as_paid_subscription
- All 8 tariff_purchase.py handlers: add mark_as_paid_subscription
- Both simple_subscription.py handlers: add mark_as_paid_subscription
- Menu smart activation: add mark_as_paid_subscription
- Monitoring autopay: add mark_as_paid_subscription
- Renewal service finalize: add mark_as_paid_subscription
- MiniApp purchase service: add mark_as_paid_subscription, remove standalone
- MiniApp renewal/tariff/switch: add mark_as_paid_subscription
2026-03-05 06:29:34 +03:00
Fringg 2664b4956d feat: account merge system — atomic user merge with full FK coverage
- Реализован execute_merge: атомарное слияние двух аккаунтов (primary поглощает secondary)
- Покрыты все 54 FK на users.id (38 таблиц): платежи, подписки, реферралы, тикеты, аудит
- Admin-actor FK (created_by, processed_by, admin_id, assigned_by, actor_user_id) — SET NULL
- User-ownership FK — переназначение на primary
- Dedup-then-reassign для таблиц с unique constraints
- Cross-referral deletion для ReferralEarning и ReferralContestEvent
- UserRole secondary удаляются (защита от эскалации привилегий)
- Merge token: Redis GETDEL (атомарное потребление), restore при ошибке
- Preview endpoint с rate limiting по IP
- Перенос баланса, email, telegram_id, OAuth провайдеров, партнёрского статуса
2026-03-05 05:23:39 +03:00
Fringg 18c2477173 feat: add referral code tracking to all cabinet auth methods + email_templates migration
Referral links from cabinet (?ref=CODE) were only tracked for email registration.
Now referral_code is accepted and processed in Telegram initData, Telegram Widget,
and OAuth authentication endpoints. Includes self-referral protection by email
for OAuth, proper error logging, and the missing email_templates table migration.
2026-02-18 23:59:29 +03:00
Fringg eb18994b7d fix: complete datetime.utcnow() → datetime.now(UTC) migration
- Migrate 660+ datetime.utcnow() across 153 files to datetime.now(UTC)
- Migrate 30+ datetime.now() without UTC to datetime.now(UTC)
- Convert all 170 DateTime columns to DateTime(timezone=True)
- Add migrate_datetime_to_timestamptz() in universal_migration with SET LOCAL timezone='UTC' safety
- Remove 70+ .replace(tzinfo=None) workarounds
- Fix utcfromtimestamp → fromtimestamp(..., tz=UTC)
- Fix fromtimestamp() without tz= (system_logs, backup_service, referral_diagnostics)
- Fix fromisoformat/isoparse to ensure aware output (platega, yookassa, wata, miniapp, nalogo)
- Fix strptime() to add .replace(tzinfo=UTC) (backup_service, referral_diagnostics)
- Fix datetime.combine() to include tzinfo=UTC (remnawave_sync, traffic_monitoring)
- Fix datetime.max/datetime.min sentinels with .replace(tzinfo=UTC)
- Rename panel_datetime_to_naive_utc → panel_datetime_to_utc
- Remove DTZ003 from ruff ignore list
2026-02-17 04:45:40 +03:00
Fringg a93a32f3a7 fix: resolve MissingGreenlet error when accessing subscription.tariff
Add .selectinload(Subscription.tariff) chain to all User queries that
load subscriptions, preventing lazy loading of the tariff relationship
in async context. Also replace unsafe getattr(subscription, 'tariff')
with explicit async get_tariff_by_id() in handle_extend_subscription.
2026-02-16 17:54:43 +03:00
Fringg 1f0fef114b refactor: complete structlog migration with contextvars, kwargs, and logging hardening
- Add ContextVarsMiddleware for automatic user_id/chat_id/username binding
  via structlog contextvars (aiogram) and http_method/http_path (FastAPI)
- Use bound_contextvars() context manager instead of clear_contextvars()
  to safely restore previous state instead of wiping all context
- Register ContextVarsMiddleware as outermost middleware (before GlobalError)
  so all error logs include user context
- Replace structlog.get_logger() with structlog.get_logger(__name__) across
  270 calls in 265 files for meaningful logger names
- Switch wrapper_class from BoundLogger to make_filtering_bound_logger()
  for pre-processor level filtering (performance optimization)
- Migrate 1411 %-style positional arg logger calls to structlog kwargs
  style across 161 files via AST script
- Migrate log_rotation_service.py from stdlib logging to structlog
- Add payment module prefixes to TelegramNotifierProcessor.IGNORED_LOGGER_PREFIXES
  and ExcludePaymentFilter.PAYMENT_MODULES to prevent payment data leaking
  to Telegram notifications and general log files
- Fix LoggingMiddleware: add from_user null-safety for channel posts,
  switch time.time() to time.monotonic() for duration measurement
- Remove duplicate logger assignments in purchase.py, config.py,
  inline.py, and admin/payments.py
2026-02-16 09:18:12 +03:00
Fringg c5124b97b6 fix: payment race conditions, balance atomicity, renewal rollback safety
- YooKassa: SELECT FOR UPDATE on payment row to prevent concurrent double-processing
- subtract_user_balance: row locking to prevent concurrent balance race conditions
- subtract_user_balance: transaction creation before commit for atomicity
- subscription renewal: compensating refund if extend_subscription fails after charge
- StaleDataError: use savepoint instead of full rollback to protect parent transaction
2026-02-11 21:49:37 +03:00
Ali Morshedzadeh 5482e609f8 Add initial Persian locale support and language handling updates 2026-02-09 16:53:50 +03:30
Fringg 41633af763 refactor: fix transaction boundaries, extract _finalize_oauth_login, replace deprecated datetime.utcnow 2026-02-07 02:35:55 +03:00
Fringg 97be4afbff feat: add OAuth 2.0 authorization (Google, Yandex, Discord, VK)
- Add OAuth provider config vars and helpers to config.py
- Add google_id, yandex_id, discord_id, vk_id columns to User model
- Create OAuth provider service with state management and 4 providers
- Add CRUD functions for OAuth user lookup, linking, and creation
- Add 3 API endpoints: providers list, authorize URL, callback
- Add alembic migration and universal_migration support
- Fix trial disable logic to cover OAuth auth_types
2026-02-07 01:58:55 +03:00
Egor d7e1b8fd5d Update user.py 2026-02-01 11:28:36 +03:00
Egor d611eecece Update user.py 2026-02-01 00:54:17 +03:00
Egor 9cc2a285dc Update user.py 2026-01-31 20:12:25 +03:00
Egor 25aba75413 Update user.py 2026-01-30 16:06:06 +03:00
Egor 6f40cdd09c Update user.py 2026-01-25 11:52:24 +03:00
Egor 3d9a8c39dc Update user.py 2026-01-24 09:51:13 +03:00
Egor b38e06383d Update user.py 2026-01-19 23:57:03 +03:00
PEDZEO 6b69ec750e feat: add cabinet (personal account) backend API
- Add JWT authentication for cabinet users
- Add Telegram WebApp authentication
- Add subscription management endpoints
- Add balance and transactions endpoints
- Add referral system endpoints
- Add tickets support for cabinet
- Add webhooks and websocket for real-time updates
- Add email verification service

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-01 23:20:20 +03:00
gy9vin 8e6082ce15 fix Черный список, мониторинг суточно графика по регламенту
Исправленные файлы:

  1. app/services/traffic_monitoring_service.py — удалены неиспользуемые импорты Decimal, aiohttp
  2. app/services/blacklist_service.py — удалён неиспользуемый импорт re
  3. app/database/crud/user.py:998 — создана отсутствующая функция get_users_with_active_subscriptions:
  async def get_users_with_active_subscriptions(db: AsyncSession) -> List[User]:
  3. Функция:
    - Возвращает пользователей с активными подписками
    - Фильтрует по remnawave_uuid IS NOT NULL (нужен для API Remnawave)
    - Проверяет end_date > now и status == ACTIVE
2025-12-30 23:11:54 +03:00
gy9vin 180cba4561 fix Расширение фильтров 2025-12-30 22:14:09 +03:00
gy9vin 54ffe3e126 feat(transactions): добавлен параметр payment_method для ручных пополнений баланса
Добавлена поддержка указания способа оплаты при пополнении баланса:

- add_user_balance(): новый параметр payment_method для передачи в транзакцию
- add_user_balance_by_id(): поддержка payment_method
- UserService: ручные пополнения админом пом
2025-12-26 08:53:08 +03:00
gy9vin 80785f22b0 Черный список, мониторинг суточно графика по регламенту 2025-12-10 19:13:52 +03:00
c0mrade c6c112fd17 refactor: improve logging level for balance subtraction debug info 2025-11-12 10:21:28 +03:00
Egor 7d7c5f3972 Update user.py 2025-11-05 18:51:35 +03:00
Egor c15b7a63fb Flush user before returning from create_user_no_commit 2025-11-05 18:19:22 +03:00
reshifter1 3201b60ff2 Merge branch 'dev4' into main 2025-11-04 22:57:38 +03:00
Pavel Stryuk 427011fe41 1) Отображение скидки на кнопках (красивое!)
2) У промогрупп появится приоритет
3) У пользователя может быть несколько промогрупп, но влиять будет только с наивысшим приоритетом
4) К промокодам можно будет добавить промогруппу. Все активировавшие промокод получат её
5) При выводе пользователей с промогруппой будет также выводиться ссылка на каждого. Можно будет отследить сливы промокодов "для своих". Я в целом это добавлю во все места, где пользователь выводится в админке
6) Исправить баг исчезновения триалки при пополнении
7) Исправить падающие тесты и добавить новых
8) Трафик: 0 ГБ в тестовой подписке исправить на Трафик: Безлимит
2025-11-04 13:05:02 +01:00
Egor 71c219e6ea Update user.py 2025-11-04 09:29:46 +03:00
Egor 06323f7168 Enhance search functionality for telegram_id
Added error handling for converting search input to int for telegram_id.
2025-11-04 07:39:12 +03:00
Mikhail 92cb1e3971 Merge branch 'Fr1ngg:main' into main 2025-11-02 15:08:01 +03:00