Commit Graph

815 Commits

Author SHA1 Message Date
Fringg
0db00a8f90 style: format cryptobot.py with ruff 2026-02-10 07:33:42 +03:00
Fringg
fe5f5ded96 feat: add MULENPAY_WEBSITE_URL setting for post-payment redirect
Previously website_url was hardcoded to WEBHOOK_URL, redirecting users
to the webhook endpoint after payment. Now configurable via env var.
2026-02-10 07:25:58 +03:00
Fringg
2cb6d731e9 fix: stop CryptoBot webhook retry loop and save cabinet payments to DB
Cabinet was calling CryptoBotService.create_invoice() directly without
saving CryptoBotPayment to DB. When webhook arrived, payment lookup
failed and returned HTTP 400, causing infinite retries.

Now cabinet uses PaymentService.create_cryptobot_payment() (same as
miniapp) with proper USD conversion via currency_converter.

Also return HTTP 200 for unknown invoice_ids to stop retry spam.
2026-02-10 07:25:54 +03:00
Fringg
184c52d4ea feat: webhook protection — prevent sync/monitoring from overwriting webhook data
Add last_webhook_update_at timestamp to Subscription model. When a webhook
handler modifies a subscription, it stamps this field. Auto-sync, monitoring,
and force-check services skip subscriptions updated by webhook within the
last 60 seconds, preventing stale panel data from overwriting fresh
real-time changes.

- Add last_webhook_update_at column + migration
- Stamp all 8 webhook handlers with commit in every code path
- Add is_recently_updated_by_webhook() guard in 12 sync/monitoring paths
- Add REMNAWAVE_WEBHOOK_* variables to .env.example
- Add webhook setup documentation to README with Caddy/nginx examples
- Fix pre-existing yookassa webhook test (mock AsyncSessionLocal)
2026-02-10 07:16:22 +03:00
Fringg
8e85e244cb feat: handle errors.bandwidth_usage_threshold_reached_max_notifications webhook
Last remaining unhandled RemnaWave backend event — sends admin
notification when the bandwidth threshold notification limit is reached.
2026-02-10 06:34:20 +03:00
Fringg
43a326a98c feat: handle service.subpage_config_changed webhook event
Add admin notification when subscription page config is
created, updated or deleted in RemnaWave panel.
2026-02-10 06:32:52 +03:00
Fringg
d9de15a5a0 feat: add close button to all webhook notifications
Add dismissible close button (✖️) to every webhook notification message.
Users can now close any webhook notification by tapping the button,
which deletes the message via webhook:close callback handler.
2026-02-10 06:22:17 +03:00
Fringg
17ce64037f fix: build composite device name from platform + hwid short suffix
Show "tag (platform)" when tag is set, "iOS (ab12cd34)" when only
platform and hwid available, or just platform as last resort.
2026-02-10 06:16:37 +03:00
Fringg
79793c47bb fix: extract device name from nested hwidUserDevice object
RemnaWave sends device info in data.hwidUserDevice, not top-level.
Try tag, deviceName, platform, hwid fields from nested object first.
2026-02-10 06:14:01 +03:00
Fringg
7091eb9c14 fix: add action buttons to webhook notifications and fix empty device names
- Add keyboard buttons to all webhook notifications: renew, connect,
  my subscription, buy traffic — context-appropriate per event type
- Extract device name from multiple possible payload fields (deviceName,
  tag, hwid, device, platform, name) with fallback to dash
- Log payload keys for device events to identify correct field names
2026-02-10 06:09:00 +03:00
Fringg
dc1e96bbe9 fix: security and architecture fixes for webhook handlers
- Add html.escape() to all untrusted webhook data in admin and device
  notifications (prevents HTML/Telegram injection)
- Add public send_webhook_notification() and is_enabled property to
  AdminNotificationService (eliminates private method access)
- Add dedicated NotificationType enum values for device and not_connected
  events (fixes incorrect semantic mapping)
- Extend user resolution to handle nested user objects and userUuid for
  device-scope events
- Replace manual __anext__() DB session with AsyncSessionLocal context
  manager; skip DB session for admin-only events
- Replace deprecated datetime.utcnow() with datetime.now(UTC)
- Use db.flush() instead of db.commit() in handlers (router commits)
- Wrap _notify_user in try/except to prevent notification failures from
  rolling back successful DB mutations
2026-02-10 05:55:48 +03:00
Fringg
1e37fd9dd2 feat: add all remaining RemnaWave webhook events (node, service, crm, device)
Handle all 44 webhook events: admin alerts for node health (connection
lost/restored), service security (login attempts), CRM billing reminders,
plus user-facing device added/deleted and not_connected notifications
with localized messages across all 5 languages.
2026-02-10 05:47:35 +03:00
Fringg
26637f0ae5 feat: unified notification delivery for webhook events (email + WS support)
- Replace direct bot.send_message with notification_delivery_service
- Email-only and OAuth users now receive webhook notifications via email/WS
- Add 10 new NotificationType enum values for webhook subscription events
- Map all webhook text_keys to NotificationType for unified routing
2026-02-10 05:16:59 +03:00
Fringg
6d67cad3e7 feat: add RemnaWave incoming webhooks for real-time subscription events
- Add FastAPI webhook endpoint with HMAC-SHA256 signature verification
- Handle 16 user events: expired, disabled, enabled, limited, traffic_reset,
  modified, deleted, revoked, created, expires_in_72h/48h/24h,
  expired_24h_ago, first_connected, bandwidth_threshold
- URL validation for subscription_url/subscription_crypto_link (XSS prevention)
- 64KB body size limit, 32-char minimum secret enforcement
- Sanitized percent value in bandwidth threshold notifications
- DB rollback on handler errors to prevent dirty session commits
- Localization for all 5 languages (ru, en, ua, zh, fa)
2026-02-10 05:13:39 +03:00
Fringg
90d9df8f0e fix: preserve payment initiation time in transaction created_at
Transaction created_at and completed_at showed identical timestamps
because webhook handlers created transactions with is_completed=True
in a single step. Now all 10 payment providers pass payment.created_at
to the transaction so created_at reflects when the user initiated
the payment, not when the webhook processed it.

Also: remove duplicate datetime import in inline.py, upgrade button
stats DB error logging from debug to warning, add index on
button_click_logs.button_type for analytics queries.
2026-02-10 04:26:23 +03:00
Fringg
994325360c fix: don't delete Heleket invoice message on status check
_process_heleket_payload deleted the invoice message on every call,
including manual "check status" presses. Now only deletes on final
statuses (paid, cancel, fail, etc.) so the payment UI stays visible
while the user is still waiting.

Also includes subscription fallback query fix (actual DB columns).
2026-02-10 03:50:21 +03:00
Fringg
f0e7f8e3be fix: use actual DB columns for subscription fallback query
Subscription.is_active is a Python property, not a column — query
status/end_date/is_trial columns instead. Also restore subscription=None
initialization to avoid UnboundLocalError on line 112.
2026-02-10 03:44:34 +03:00
Fringg
40d8a6dc8b fix: safe HTML preview truncation and lazy-load subscription fallback
Rules editor crashed when preview truncated mid-HTML tag (e.g.
<blockquote> cut to <blockquo), causing Telegram parse error.
Strip HTML tags before truncating preview text.

Also fix MissingGreenlet in build_topup_success_keyboard: fall back
to a direct DB query instead of showing wrong button text.
2026-02-10 03:32:20 +03:00
Fringg
a3903a252e refactor: remove smart auto-activation & activation prompt, fix production bugs
Remove AUTO_ACTIVATE_AFTER_TOPUP and SHOW_ACTIVATION_PROMPT_AFTER_TOPUP
features from all payment providers, config, system settings, and tests.
Cart auto-purchase (AUTO_PURCHASE_AFTER_TOPUP) is preserved.

Bug fixes:
- fix KeyError 'months' in devices.py for custom locale overrides
- fix IntegrityError on trial subscription retry (update existing PENDING instead of INSERT)
- fix PendingRollbackError cascade by adding db.rollback() before recovery
- fix TelegramForbiddenError not caught in photo_message.py
- fix "query is too old" spam in required_sub_channel_check
- add missing trial locale keys (TRIAL_PAYMENT_DESCRIPTION, TRIAL_REFUND_DESCRIPTION, TRIAL_ACTIVATION_ERROR)
2026-02-09 21:39:53 +03:00
Fringg
142ff14a50 perf: cache logo file_id to avoid re-uploading on every message
After first logo upload, Telegram returns a file_id that can be reused
for all subsequent sends. This eliminates 3-4 second delay per message
caused by re-uploading the same file from disk every time.
2026-02-09 18:14:54 +03:00
Fringg
49871f82f3 fix: prevent sync from overwriting end_date for non-ACTIVE panel users
sync_users_to_panel uses _safe_expire_at_for_panel which replaces past
end_dates with now+1min for expired subscriptions. When sync_users_from_panel
reads these artificial dates back, it treated them as legitimate "newer"
dates and overwrote all expired subscriptions' end_date to approximately
current time. This caused all subscription end dates to show as "just now"
after sync.

Fix: only update end_date from panel when the panel user status is ACTIVE.
For EXPIRED/DISABLED users, the panel date may be a _safe_expire_at artifact
and should not override the real expiry date in the local database.
2026-02-09 17:39:25 +03:00
Fringg
e79f598d17 fix: skip users with active subscriptions in admin inactive cleanup
Admin "Clear all" button was deleting inactive users regardless of
subscription status, destroying paid subscriptions. Now matches the
monitoring service behavior by checking is_active before deletion.
2026-02-09 05:53:30 +03:00
Fringg
33d5155a8d style: format schemas and remnawave_service with ruff 2026-02-08 20:39:22 +03:00
Fringg
9828ff0845 fix: read bot version from pyproject.toml when VERSION env is not set
Previously the bot only checked os.getenv('VERSION'), returning
'UNKNOW' when unset. Now falls back to importlib.metadata and
direct pyproject.toml parsing, so the version stays correct after
release-please updates it.
2026-02-08 20:38:17 +03:00
Fringg
165965d8ea fix: add email/UUID fallback for OAuth user panel sync
OAuth users registering via cabinet have no telegram_id, causing
panel sync failures. All RemnaWave panel lookups now use a 3-level
chain: UUID → telegram_id → email. Also pass email and user_id to
format_remnawave_username to generate unique panel usernames.
2026-02-08 19:55:34 +03:00
Fringg
071c23dd52 fix: resolve multiple production errors and performance issues
- tickets.py: guard against non-text messages in waiting_for_title FSM state
- payments.py: fix Wata webhook using wrong field name (order_id vs orderId),
  add full payload to error log
- tariff.py: stop overwriting admin tariff settings on every bot restart,
  sync_default_tariff_from_config now only creates if no tariff exists
- start.py: catch TelegramBadRequest specifically for "message is not modified"
  instead of bare except with useless retry
- admin/tickets.py: downgrade ticket notification log from error to warning
  for expected case of OAuth/email users without telegram_id
- pricing.py, countries.py, purchase.py: guard against expired FSM state
  causing KeyError on 'period_days'
- blacklist_service.py: add 5-min in-memory cache to is_user_blacklisted()
  to reduce DB load from per-request checks
- remnawave_service.py: fix "Session is closed" race condition — create
  new RemnaWaveAPI instance per get_api_client() call instead of reusing
  shared instance whose aiohttp session gets overwritten by parallel coroutines
2026-02-08 17:40:51 +03:00
Fringg
a9eee19c95 fix: resolve HWID reset context manager bug and webhook FK violation
- Fix async context manager usage in sync_users: __aenter__() result
  was not assigned, so hwid_api_client held the context manager object
  instead of the actual API client, causing AttributeError on
  reset_user_devices()
- Add user existence check in _restore_missing_yookassa_payment before
  INSERT to prevent ForeignKeyViolationError when user_id from payment
  metadata no longer exists in users table
2026-02-08 16:48:07 +03:00
Fringg
116c8453bb feat: block registration with disposable email addresses
Add DisposableEmailService that fetches ~72k disposable email domains
from github.com/disposable/disposable-email-domains into an in-memory
frozenset with 24h auto-refresh via asyncio background task.

Integrated into three email entry points in cabinet auth routes:
- POST /email/register (link email to Telegram account)
- POST /email/register/standalone (standalone email registration)
- POST /email/change (change existing email)

Controlled by DISPOSABLE_EMAIL_CHECK_ENABLED setting (default: true).
Falls back to allowing all emails if domain list fetch fails.
2026-02-07 00:34:11 +03:00
Fringg
c4794db1dd feat: add TRIAL_DISABLED_FOR setting to disable trial by user type
New setting allows granular control over trial availability:
- none: trial available for all (default)
- email: trial disabled for email users
- telegram: trial disabled for telegram users
- all: trial disabled for everyone

Enforced in bot handlers, cabinet API, and miniapp routes.
Automatically appears in admin panel as dropdown via CHOICES.
2026-02-07 00:19:25 +03:00
Fringg
966a599c2c fix: enforce blacklist via middleware instead of per-handler checks
Add BlacklistMiddleware for aiogram that blocks all message/callback/pre_checkout
from blacklisted users globally. Add blacklist check to cabinet API dependency.
Fix case-insensitive username matching. Remove 10 redundant manual checks from handlers.
2026-02-06 15:48:21 +03:00
Egor
02eca28bc0 Merge pull request #2534 from BEDOLAGA-DEV/feat/version-notification-redesign
feat(notifications): redesign version update notification
2026-02-05 07:32:46 +03:00
Fringg
3f7ca7be3a feat(notifications): redesign version update notification
- Add GitHub Markdown to Telegram HTML converter utility
- Place release description in blockquote expandable
- Auto-truncate description to fit 4096 char message limit
- Clean compact layout with clickable version link
- Convert markdown headers, bold, italic, code, links, strikethrough
2026-02-05 07:29:55 +03:00
Fringg
992a5cb97f fix(autopay): add 6h cooldown for insufficient balance notifications
- Use Redis key with 6h TTL to prevent notification spam on each monitoring cycle
- Fallback to sending notification if Redis is unavailable
- Key auto-expires when user tops up balance and autopay succeeds
2026-02-05 07:17:25 +03:00
Egor
3d94e63c3c Merge pull request #2532 from BEDOLAGA-DEV/fix/daily-tariff-autopay
fix(autopay): exclude daily subscriptions from global autopay
2026-02-05 07:12:03 +03:00
Fringg
b9352a5bd5 fix(autopay): exclude daily subscriptions from global autopay
- Skip daily tariff subscriptions in monitoring autopay cycle
- Filter daily subscriptions in get_subscriptions_for_autopay CRUD
- Block autopay menu and toggle for daily tariffs in bot handler
- Reject autopay enable for daily subscriptions in Cabinet API (HTTP 400)
- Reject autopay enable for daily subscriptions in MiniApp API (HTTP 400)
2026-02-05 07:10:52 +03:00
Fringg
13ebfdb5c4 fix(broadcast): stabilize mass broadcast for 100k+ users
- Add real-time progress bar with updates every 500 msgs / 5 sec
- Fix Telegram rate limiting: batch=25, delay=1.0s (~25 msg/sec)
- Add global flood_wait_until to prevent semaphore slot starvation
- Add parse_mode=HTML for web API broadcasts
- Separate error handling for FloodWait, Forbidden, BadRequest
- Convert ORM objects to scalars before long broadcast operations
- Add email recipients dataclass to prevent detached ORM state
2026-02-05 07:10:43 +03:00
Fringg
b8682adbbf fix(broadcast): resolve SQLAlchemy connection closed errors during long broadcasts
- Extract scalar values from ORM objects before long operations
- Create fresh DB sessions for persist operations with retry mechanism
- Replace ORM User objects with telegram_id integers in broadcast loops
- Update .gitignore to exclude Python cache, IDE files, and local configs

Fixes: InterfaceError "connection is closed" and MissingGreenlet errors
during mass message broadcasts
2026-02-05 05:42:31 +03:00
Egor
c6a5e0d4be Add files via upload 2026-02-04 04:50:38 +03:00
Egor
afb4f162d0 Update admin_notification_service.py 2026-02-04 03:55:59 +03:00
Egor
bd1a0d4a4e Update wata.py 2026-02-04 03:05:22 +03:00
Egor
bf72e81d55 Update promocode_service.py 2026-02-04 02:06:13 +03:00
Egor
bb4f496b21 Add files via upload 2026-02-03 03:50:33 +03:00
Egor
47c1de1cc8 Add files via upload 2026-02-03 03:50:05 +03:00
Egor
03875b593e Add files via upload 2026-02-03 03:38:14 +03:00
Egor
06224d798d Add files via upload 2026-02-03 03:37:43 +03:00
Egor
4941fe9469 Update cloudpayments.py 2026-02-03 03:31:48 +03:00
Egor
39742499b8 Update cloudpayments.py 2026-02-03 03:27:53 +03:00
Egor
7eb302aab0 Update cloudpayments.py 2026-02-03 03:23:23 +03:00
Egor
0adc7145a1 Update user_service.py 2026-02-03 03:14:39 +03:00
Egor
4f1c14fda0 Update blocked_users_service.py 2026-02-02 03:18:46 +03:00