refactor: centralize Bot instantiation via create_bot() factory
Replace all ~45 direct Bot() calls across the codebase with a centralized create_bot() factory function that automatically configures SOCKS5 proxy session when PROXY_URL is set. This ensures proxy support applies uniformly to all Telegram API traffic. Key changes: - Add app/bot_factory.py with create_bot() factory - Replace direct Bot() instantiation in 33 files - Fix session leaks in cloudpayments.py and auth.py (async with) - Replace 2 direct httpx calls to api.telegram.org with bot.create_invoice_link() (balance.py, wheel.py) - Remove now-unused imports (Bot, DefaultBotProperties, ParseMode, httpx)
This commit is contained in:
+3
-8
@@ -96,23 +96,18 @@ async def setup_bot() -> tuple[Bot, Dispatcher]:
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.warning('Кеш не инициализирован', error=e)
|
logger.warning('Кеш не инициализирован', error=e)
|
||||||
|
|
||||||
from aiogram.client.default import DefaultBotProperties
|
from app.bot_factory import create_bot
|
||||||
from aiogram.enums import ParseMode
|
|
||||||
|
bot = create_bot()
|
||||||
|
|
||||||
proxy_url = settings.get_proxy_url()
|
proxy_url = settings.get_proxy_url()
|
||||||
session = None
|
|
||||||
if proxy_url:
|
if proxy_url:
|
||||||
from urllib.parse import urlparse
|
from urllib.parse import urlparse
|
||||||
|
|
||||||
from aiogram.client.session.aiohttp import AiohttpSession
|
|
||||||
|
|
||||||
session = AiohttpSession(proxy=proxy_url)
|
|
||||||
parsed = urlparse(proxy_url)
|
parsed = urlparse(proxy_url)
|
||||||
masked = f'{parsed.scheme}://***@{parsed.hostname}:{parsed.port}' if parsed.username else proxy_url
|
masked = f'{parsed.scheme}://***@{parsed.hostname}:{parsed.port}' if parsed.username else proxy_url
|
||||||
logger.info('Proxy configured', proxy_url=masked)
|
logger.info('Proxy configured', proxy_url=masked)
|
||||||
|
|
||||||
bot = Bot(token=settings.BOT_TOKEN, default=DefaultBotProperties(parse_mode=ParseMode.HTML), session=session)
|
|
||||||
|
|
||||||
maintenance_service.set_bot(bot)
|
maintenance_service.set_bot(bot)
|
||||||
logger.info('Бот установлен в maintenance_service')
|
logger.info('Бот установлен в maintenance_service')
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,20 @@
|
|||||||
|
"""Factory for creating Bot instances with proxy support."""
|
||||||
|
|
||||||
|
from aiogram import Bot
|
||||||
|
from aiogram.client.default import DefaultBotProperties
|
||||||
|
from aiogram.enums import ParseMode
|
||||||
|
|
||||||
|
from app.config import settings
|
||||||
|
|
||||||
|
|
||||||
|
def create_bot(token: str | None = None, **kwargs) -> Bot:
|
||||||
|
"""Create a Bot instance with SOCKS5 proxy session if PROXY_URL is configured."""
|
||||||
|
proxy_url = settings.get_proxy_url()
|
||||||
|
session = None
|
||||||
|
if proxy_url:
|
||||||
|
from aiogram.client.session.aiohttp import AiohttpSession
|
||||||
|
|
||||||
|
session = AiohttpSession(proxy=proxy_url)
|
||||||
|
|
||||||
|
kwargs.setdefault('default', DefaultBotProperties(parse_mode=ParseMode.HTML))
|
||||||
|
return Bot(token=token or settings.BOT_TOKEN, session=session, **kwargs)
|
||||||
@@ -227,8 +227,7 @@ async def approve_application(
|
|||||||
|
|
||||||
# Notify user about approval
|
# Notify user about approval
|
||||||
try:
|
try:
|
||||||
from aiogram import Bot
|
from app.bot_factory import create_bot
|
||||||
|
|
||||||
from app.config import settings
|
from app.config import settings
|
||||||
from app.services.notification_delivery_service import notification_delivery_service
|
from app.services.notification_delivery_service import notification_delivery_service
|
||||||
|
|
||||||
@@ -240,7 +239,7 @@ async def approve_application(
|
|||||||
tg_message = (
|
tg_message = (
|
||||||
f'✅ Ваша заявка на партнёрство одобрена!\nКомиссия: {request.commission_percent}%{comment_text}'
|
f'✅ Ваша заявка на партнёрство одобрена!\nКомиссия: {request.commission_percent}%{comment_text}'
|
||||||
)
|
)
|
||||||
bot = Bot(token=settings.BOT_TOKEN)
|
bot = create_bot()
|
||||||
try:
|
try:
|
||||||
await notification_delivery_service.notify_partner_approved(
|
await notification_delivery_service.notify_partner_approved(
|
||||||
user=user,
|
user=user,
|
||||||
@@ -280,8 +279,7 @@ async def reject_application(
|
|||||||
|
|
||||||
# Notify user about rejection
|
# Notify user about rejection
|
||||||
try:
|
try:
|
||||||
from aiogram import Bot
|
from app.bot_factory import create_bot
|
||||||
|
|
||||||
from app.config import settings
|
from app.config import settings
|
||||||
from app.services.notification_delivery_service import notification_delivery_service
|
from app.services.notification_delivery_service import notification_delivery_service
|
||||||
|
|
||||||
@@ -291,7 +289,7 @@ async def reject_application(
|
|||||||
if user:
|
if user:
|
||||||
comment_text = f'\nПричина: {request.comment}' if request.comment else ''
|
comment_text = f'\nПричина: {request.comment}' if request.comment else ''
|
||||||
tg_message = f'❌ Ваша заявка на партнёрство отклонена.{comment_text}'
|
tg_message = f'❌ Ваша заявка на партнёрство отклонена.{comment_text}'
|
||||||
bot = Bot(token=settings.BOT_TOKEN)
|
bot = create_bot()
|
||||||
try:
|
try:
|
||||||
await notification_delivery_service.notify_partner_rejected(
|
await notification_delivery_service.notify_partner_rejected(
|
||||||
user=user,
|
user=user,
|
||||||
|
|||||||
@@ -4,14 +4,11 @@ import math
|
|||||||
from datetime import UTC, datetime, timedelta
|
from datetime import UTC, datetime, timedelta
|
||||||
|
|
||||||
import structlog
|
import structlog
|
||||||
from aiogram import Bot
|
|
||||||
from aiogram.client.default import DefaultBotProperties
|
|
||||||
from aiogram.enums import ParseMode
|
|
||||||
from fastapi import APIRouter, Depends, HTTPException, Query, status
|
from fastapi import APIRouter, Depends, HTTPException, Query, status
|
||||||
from pydantic import BaseModel
|
from pydantic import BaseModel
|
||||||
from sqlalchemy.ext.asyncio import AsyncSession
|
from sqlalchemy.ext.asyncio import AsyncSession
|
||||||
|
|
||||||
from app.config import settings
|
from app.bot_factory import create_bot
|
||||||
from app.database.models import PaymentMethod, User
|
from app.database.models import PaymentMethod, User
|
||||||
from app.services.payment_search_service import (
|
from app.services.payment_search_service import (
|
||||||
MAX_ALL_TIME_DAYS,
|
MAX_ALL_TIME_DAYS,
|
||||||
@@ -550,7 +547,7 @@ async def check_payment_status(
|
|||||||
old_is_paid = record.is_paid
|
old_is_paid = record.is_paid
|
||||||
|
|
||||||
# Run manual check
|
# Run manual check
|
||||||
bot = Bot(token=settings.BOT_TOKEN, default=DefaultBotProperties(parse_mode=ParseMode.HTML))
|
bot = create_bot()
|
||||||
try:
|
try:
|
||||||
payment_service = PaymentService(bot=bot)
|
payment_service = PaymentService(bot=bot)
|
||||||
updated = await run_manual_check(db, payment_method, payment_id, payment_service)
|
updated = await run_manual_check(db, payment_method, payment_id, payment_service)
|
||||||
|
|||||||
@@ -5,13 +5,11 @@ from datetime import UTC, datetime
|
|||||||
|
|
||||||
import structlog
|
import structlog
|
||||||
from aiogram import Bot
|
from aiogram import Bot
|
||||||
from aiogram.client.default import DefaultBotProperties
|
|
||||||
from aiogram.enums import ParseMode
|
|
||||||
from fastapi import APIRouter, Depends, HTTPException, Query, status
|
from fastapi import APIRouter, Depends, HTTPException, Query, status
|
||||||
from sqlalchemy import func, select, update
|
from sqlalchemy import func, select, update
|
||||||
from sqlalchemy.ext.asyncio import AsyncSession
|
from sqlalchemy.ext.asyncio import AsyncSession
|
||||||
|
|
||||||
from app.config import settings
|
from app.bot_factory import create_bot
|
||||||
from app.database.models import PinnedMessage, User
|
from app.database.models import PinnedMessage, User
|
||||||
from app.services.pinned_message_service import (
|
from app.services.pinned_message_service import (
|
||||||
broadcast_pinned_message,
|
broadcast_pinned_message,
|
||||||
@@ -77,10 +75,7 @@ _cached_bot: Bot | None = None
|
|||||||
def _get_bot() -> Bot:
|
def _get_bot() -> Bot:
|
||||||
global _cached_bot
|
global _cached_bot
|
||||||
if _cached_bot is None:
|
if _cached_bot is None:
|
||||||
_cached_bot = Bot(
|
_cached_bot = create_bot()
|
||||||
token=settings.BOT_TOKEN,
|
|
||||||
default=DefaultBotProperties(parse_mode=ParseMode.HTML),
|
|
||||||
)
|
|
||||||
return _cached_bot
|
return _cached_bot
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -8,15 +8,13 @@ from typing import Any
|
|||||||
|
|
||||||
import structlog
|
import structlog
|
||||||
from aiogram import Bot
|
from aiogram import Bot
|
||||||
from aiogram.client.default import DefaultBotProperties
|
|
||||||
from aiogram.enums import ParseMode
|
|
||||||
from aiogram.exceptions import TelegramBadRequest, TelegramForbiddenError
|
from aiogram.exceptions import TelegramBadRequest, TelegramForbiddenError
|
||||||
from aiogram.types import InlineKeyboardButton, InlineKeyboardMarkup
|
from aiogram.types import InlineKeyboardButton, InlineKeyboardMarkup
|
||||||
from fastapi import APIRouter, Depends, HTTPException, Query, status
|
from fastapi import APIRouter, Depends, HTTPException, Query, status
|
||||||
from pydantic import BaseModel, Field
|
from pydantic import BaseModel, Field
|
||||||
from sqlalchemy.ext.asyncio import AsyncSession
|
from sqlalchemy.ext.asyncio import AsyncSession
|
||||||
|
|
||||||
from app.config import settings
|
from app.bot_factory import create_bot
|
||||||
from app.database.crud.discount_offer import (
|
from app.database.crud.discount_offer import (
|
||||||
count_discount_offers,
|
count_discount_offers,
|
||||||
list_discount_offers,
|
list_discount_offers,
|
||||||
@@ -369,10 +367,7 @@ async def list_offers(
|
|||||||
|
|
||||||
def _get_bot() -> Bot:
|
def _get_bot() -> Bot:
|
||||||
"""Create bot instance for sending notifications."""
|
"""Create bot instance for sending notifications."""
|
||||||
return Bot(
|
return create_bot()
|
||||||
token=settings.BOT_TOKEN,
|
|
||||||
default=DefaultBotProperties(parse_mode=ParseMode.HTML),
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def _build_default_promo_message(
|
def _build_default_promo_message(
|
||||||
|
|||||||
@@ -511,9 +511,7 @@ async def revoke_role(
|
|||||||
from app.database.models import UserRole
|
from app.database.models import UserRole
|
||||||
|
|
||||||
# Lock the assignment row (FOR UPDATE held until commit)
|
# Lock the assignment row (FOR UPDATE held until commit)
|
||||||
result = await db.execute(
|
result = await db.execute(sa.select(UserRole).where(UserRole.id == assignment_id).with_for_update())
|
||||||
sa.select(UserRole).where(UserRole.id == assignment_id).with_for_update()
|
|
||||||
)
|
|
||||||
user_role = result.scalar_one_or_none()
|
user_role = result.scalar_one_or_none()
|
||||||
if not user_role:
|
if not user_role:
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
|
|||||||
@@ -479,14 +479,9 @@ async def reply_to_ticket(
|
|||||||
|
|
||||||
# Try to notify user via Telegram
|
# Try to notify user via Telegram
|
||||||
try:
|
try:
|
||||||
from aiogram import Bot
|
from app.bot_factory import create_bot
|
||||||
from aiogram.client.default import DefaultBotProperties
|
|
||||||
from aiogram.enums import ParseMode
|
|
||||||
|
|
||||||
bot = Bot(
|
bot = create_bot()
|
||||||
token=settings.BOT_TOKEN,
|
|
||||||
default=DefaultBotProperties(parse_mode=ParseMode.HTML),
|
|
||||||
)
|
|
||||||
try:
|
try:
|
||||||
from app.handlers.admin.tickets import notify_user_about_ticket_reply
|
from app.handlers.admin.tickets import notify_user_about_ticket_reply
|
||||||
|
|
||||||
|
|||||||
@@ -7,16 +7,13 @@ import time
|
|||||||
from datetime import UTC, datetime, timedelta
|
from datetime import UTC, datetime, timedelta
|
||||||
|
|
||||||
import structlog
|
import structlog
|
||||||
from aiogram import Bot
|
|
||||||
from aiogram.client.default import DefaultBotProperties
|
|
||||||
from aiogram.enums import ParseMode
|
|
||||||
from aiogram.types import BufferedInputFile
|
from aiogram.types import BufferedInputFile
|
||||||
from fastapi import APIRouter, Depends, HTTPException, Query, status
|
from fastapi import APIRouter, Depends, HTTPException, Query, status
|
||||||
from sqlalchemy import and_, func, select
|
from sqlalchemy import and_, func, select
|
||||||
from sqlalchemy.ext.asyncio import AsyncSession
|
from sqlalchemy.ext.asyncio import AsyncSession
|
||||||
from sqlalchemy.orm import selectinload
|
from sqlalchemy.orm import selectinload
|
||||||
|
|
||||||
from app.config import settings
|
from app.bot_factory import create_bot
|
||||||
from app.database.models import Subscription, Transaction, TransactionType, User
|
from app.database.models import Subscription, Transaction, TransactionType, User
|
||||||
from app.services.remnawave_service import RemnaWaveService
|
from app.services.remnawave_service import RemnaWaveService
|
||||||
|
|
||||||
@@ -680,10 +677,7 @@ async def export_traffic_csv(
|
|||||||
filename = f'traffic_usage_{period_label}_{timestamp}.csv'
|
filename = f'traffic_usage_{period_label}_{timestamp}.csv'
|
||||||
|
|
||||||
try:
|
try:
|
||||||
bot = Bot(
|
bot = create_bot()
|
||||||
token=settings.BOT_TOKEN,
|
|
||||||
default=DefaultBotProperties(parse_mode=ParseMode.HTML),
|
|
||||||
)
|
|
||||||
async with bot:
|
async with bot:
|
||||||
await bot.send_document(
|
await bot.send_document(
|
||||||
chat_id=admin.telegram_id,
|
chat_id=admin.telegram_id,
|
||||||
|
|||||||
@@ -199,8 +199,7 @@ async def approve_withdrawal(
|
|||||||
|
|
||||||
# Notify user about approval
|
# Notify user about approval
|
||||||
try:
|
try:
|
||||||
from aiogram import Bot
|
from app.bot_factory import create_bot
|
||||||
|
|
||||||
from app.config import settings
|
from app.config import settings
|
||||||
from app.services.notification_delivery_service import notification_delivery_service
|
from app.services.notification_delivery_service import notification_delivery_service
|
||||||
|
|
||||||
@@ -211,7 +210,7 @@ async def approve_withdrawal(
|
|||||||
formatted_amount = settings.format_price(withdrawal.amount_kopeks)
|
formatted_amount = settings.format_price(withdrawal.amount_kopeks)
|
||||||
comment_text = f'\n{request.comment}' if request.comment else ''
|
comment_text = f'\n{request.comment}' if request.comment else ''
|
||||||
tg_message = f'✅ Ваш запрос на вывод {formatted_amount} одобрен.{comment_text}'
|
tg_message = f'✅ Ваш запрос на вывод {formatted_amount} одобрен.{comment_text}'
|
||||||
bot = Bot(token=settings.BOT_TOKEN)
|
bot = create_bot()
|
||||||
try:
|
try:
|
||||||
await notification_delivery_service.notify_withdrawal_approved(
|
await notification_delivery_service.notify_withdrawal_approved(
|
||||||
user=user,
|
user=user,
|
||||||
@@ -251,8 +250,7 @@ async def reject_withdrawal(
|
|||||||
|
|
||||||
# Notify user about rejection
|
# Notify user about rejection
|
||||||
try:
|
try:
|
||||||
from aiogram import Bot
|
from app.bot_factory import create_bot
|
||||||
|
|
||||||
from app.config import settings
|
from app.config import settings
|
||||||
from app.services.notification_delivery_service import notification_delivery_service
|
from app.services.notification_delivery_service import notification_delivery_service
|
||||||
|
|
||||||
@@ -263,7 +261,7 @@ async def reject_withdrawal(
|
|||||||
formatted_amount = settings.format_price(withdrawal.amount_kopeks)
|
formatted_amount = settings.format_price(withdrawal.amount_kopeks)
|
||||||
comment_text = f'\nПричина: {request.comment}' if request.comment else ''
|
comment_text = f'\nПричина: {request.comment}' if request.comment else ''
|
||||||
tg_message = f'❌ Ваш запрос на вывод {formatted_amount} отклонён.{comment_text}'
|
tg_message = f'❌ Ваш запрос на вывод {formatted_amount} отклонён.{comment_text}'
|
||||||
bot = Bot(token=settings.BOT_TOKEN)
|
bot = create_bot()
|
||||||
try:
|
try:
|
||||||
await notification_delivery_service.notify_withdrawal_rejected(
|
await notification_delivery_service.notify_withdrawal_rejected(
|
||||||
user=user,
|
user=user,
|
||||||
|
|||||||
+10
-15
@@ -196,12 +196,10 @@ async def _process_campaign_bonus(
|
|||||||
user.referred_by_id = campaign.partner_user_id
|
user.referred_by_id = campaign.partner_user_id
|
||||||
await db.flush()
|
await db.flush()
|
||||||
try:
|
try:
|
||||||
from aiogram import Bot
|
from app.bot_factory import create_bot
|
||||||
from aiogram.client.default import DefaultBotProperties
|
|
||||||
from aiogram.enums import ParseMode
|
|
||||||
|
|
||||||
bot = Bot(token=settings.BOT_TOKEN, default=DefaultBotProperties(parse_mode=ParseMode.HTML))
|
async with create_bot() as bot:
|
||||||
await process_referral_registration(db, user.id, campaign.partner_user_id, bot=bot)
|
await process_referral_registration(db, user.id, campaign.partner_user_id, bot=bot)
|
||||||
logger.info(
|
logger.info(
|
||||||
'Referral set from campaign partner',
|
'Referral set from campaign partner',
|
||||||
user_id=user.id,
|
user_id=user.id,
|
||||||
@@ -255,12 +253,11 @@ async def _process_referral_code(
|
|||||||
return
|
return
|
||||||
user.referred_by_id = referrer.id
|
user.referred_by_id = referrer.id
|
||||||
await db.flush()
|
await db.flush()
|
||||||
from aiogram import Bot
|
|
||||||
from aiogram.client.default import DefaultBotProperties
|
|
||||||
from aiogram.enums import ParseMode
|
|
||||||
|
|
||||||
bot = Bot(token=settings.BOT_TOKEN, default=DefaultBotProperties(parse_mode=ParseMode.HTML))
|
from app.bot_factory import create_bot
|
||||||
await process_referral_registration(db, user.id, referrer.id, bot=bot)
|
|
||||||
|
async with create_bot() as bot:
|
||||||
|
await process_referral_registration(db, user.id, referrer.id, bot=bot)
|
||||||
logger.info('Referral applied from code', user_id=user.id, referrer_id=referrer.id, referral_code=referral_code)
|
logger.info('Referral applied from code', user_id=user.id, referrer_id=referrer.id, referral_code=referral_code)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error('Failed to process referral code', error=e, referral_code=referral_code)
|
logger.error('Failed to process referral code', error=e, referral_code=referral_code)
|
||||||
@@ -937,12 +934,10 @@ async def register_email_standalone(
|
|||||||
# Обработать реферальную регистрацию (если есть реферер)
|
# Обработать реферальную регистрацию (если есть реферер)
|
||||||
if referrer:
|
if referrer:
|
||||||
try:
|
try:
|
||||||
from aiogram import Bot
|
from app.bot_factory import create_bot
|
||||||
from aiogram.client.default import DefaultBotProperties
|
|
||||||
from aiogram.enums import ParseMode
|
|
||||||
|
|
||||||
bot = Bot(token=settings.BOT_TOKEN, default=DefaultBotProperties(parse_mode=ParseMode.HTML))
|
async with create_bot() as bot:
|
||||||
await process_referral_registration(db, user.id, referrer.id, bot=bot)
|
await process_referral_registration(db, user.id, referrer.id, bot=bot)
|
||||||
logger.info(
|
logger.info(
|
||||||
'Processed referral registration: user_id=, referrer_id', user_id=user.id, referrer_id=referrer.id
|
'Processed referral registration: user_id=, referrer_id', user_id=user.id, referrer_id=referrer.id
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -4,15 +4,12 @@ import math
|
|||||||
import time
|
import time
|
||||||
from decimal import ROUND_HALF_UP, Decimal, InvalidOperation
|
from decimal import ROUND_HALF_UP, Decimal, InvalidOperation
|
||||||
|
|
||||||
import httpx
|
|
||||||
import structlog
|
import structlog
|
||||||
from aiogram import Bot
|
|
||||||
from aiogram.client.default import DefaultBotProperties
|
|
||||||
from aiogram.enums import ParseMode
|
|
||||||
from fastapi import APIRouter, Depends, HTTPException, Query, status
|
from fastapi import APIRouter, Depends, HTTPException, Query, status
|
||||||
from sqlalchemy import desc, func, select
|
from sqlalchemy import desc, func, select
|
||||||
from sqlalchemy.ext.asyncio import AsyncSession
|
from sqlalchemy.ext.asyncio import AsyncSession
|
||||||
|
|
||||||
|
from app.bot_factory import create_bot
|
||||||
from app.config import settings
|
from app.config import settings
|
||||||
from app.database.crud.saved_payment_method import (
|
from app.database.crud.saved_payment_method import (
|
||||||
deactivate_payment_method,
|
deactivate_payment_method,
|
||||||
@@ -272,50 +269,36 @@ async def create_stars_invoice(
|
|||||||
|
|
||||||
# Create invoice through Telegram Bot API
|
# Create invoice through Telegram Bot API
|
||||||
try:
|
try:
|
||||||
bot_token = settings.BOT_TOKEN
|
from aiogram.types import LabeledPrice
|
||||||
api_url = f'https://api.telegram.org/bot{bot_token}/createInvoiceLink'
|
|
||||||
|
|
||||||
async with httpx.AsyncClient() as client:
|
async with create_bot() as bot:
|
||||||
response = await client.post(
|
invoice_url = await bot.create_invoice_link(
|
||||||
api_url,
|
title='Пополнение баланса VPN',
|
||||||
json={
|
description=f'Пополнение баланса на {normalized_kopeks / 100:.2f} ₽ ({stars_amount} ⭐)',
|
||||||
'title': 'Пополнение баланса VPN',
|
payload=payload,
|
||||||
'description': f'Пополнение баланса на {normalized_kopeks / 100:.2f} ₽ ({stars_amount} ⭐)',
|
provider_token='',
|
||||||
'payload': payload,
|
currency='XTR',
|
||||||
'provider_token': '', # Empty for Stars
|
prices=[LabeledPrice(label='Пополнение баланса', amount=stars_amount)],
|
||||||
'currency': 'XTR',
|
|
||||||
'prices': [{'label': 'Пополнение баланса', 'amount': stars_amount}],
|
|
||||||
},
|
|
||||||
)
|
)
|
||||||
|
|
||||||
result = response.json()
|
logger.info(
|
||||||
|
'Created Stars invoice for balance top-up: user=, amount= kopeks, stars',
|
||||||
|
user_id=user.id,
|
||||||
|
amount_kopeks=request.amount_kopeks,
|
||||||
|
stars_amount=stars_amount,
|
||||||
|
)
|
||||||
|
|
||||||
if not result.get('ok'):
|
return StarsInvoiceResponse(
|
||||||
logger.error('Telegram API error', result=result)
|
invoice_url=invoice_url,
|
||||||
raise HTTPException(
|
stars_amount=stars_amount,
|
||||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
amount_kopeks=normalized_kopeks,
|
||||||
detail='Failed to create Stars invoice',
|
)
|
||||||
)
|
|
||||||
|
|
||||||
invoice_url = result['result']
|
except Exception as e:
|
||||||
logger.info(
|
logger.error('Error creating Stars invoice', error=e)
|
||||||
'Created Stars invoice for balance top-up: user=, amount= kopeks, stars',
|
|
||||||
user_id=user.id,
|
|
||||||
amount_kopeks=request.amount_kopeks,
|
|
||||||
stars_amount=stars_amount,
|
|
||||||
)
|
|
||||||
|
|
||||||
return StarsInvoiceResponse(
|
|
||||||
invoice_url=invoice_url,
|
|
||||||
stars_amount=stars_amount,
|
|
||||||
amount_kopeks=normalized_kopeks,
|
|
||||||
)
|
|
||||||
|
|
||||||
except httpx.HTTPError as e:
|
|
||||||
logger.error('HTTP error creating Stars invoice', error=e)
|
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||||
detail='Failed to connect to Telegram API',
|
detail='Failed to create Stars invoice',
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -1202,7 +1185,7 @@ async def check_payment_status(
|
|||||||
old_is_paid = record.is_paid
|
old_is_paid = record.is_paid
|
||||||
|
|
||||||
# Run manual check
|
# Run manual check
|
||||||
bot = Bot(token=settings.BOT_TOKEN, default=DefaultBotProperties(parse_mode=ParseMode.HTML))
|
bot = create_bot()
|
||||||
try:
|
try:
|
||||||
payment_service = PaymentService(bot=bot)
|
payment_service = PaymentService(bot=bot)
|
||||||
updated = await run_manual_check(db, payment_method, payment_id, payment_service)
|
updated = await run_manual_check(db, payment_method, payment_id, payment_service)
|
||||||
|
|||||||
@@ -306,9 +306,9 @@ async def create_gift_purchase(
|
|||||||
else:
|
else:
|
||||||
# 2) Fall back to Bot API (works for public usernames the bot has seen)
|
# 2) Fall back to Bot API (works for public usernames the bot has seen)
|
||||||
try:
|
try:
|
||||||
from aiogram import Bot
|
from app.bot_factory import create_bot
|
||||||
|
|
||||||
async with Bot(token=settings.BOT_TOKEN) as bot:
|
async with create_bot() as bot:
|
||||||
chat = await asyncio.wait_for(bot.get_chat(chat_id=f'@{tg_username}'), timeout=5.0)
|
chat = await asyncio.wait_for(bot.get_chat(chat_id=f'@{tg_username}'), timeout=5.0)
|
||||||
pre_resolved_telegram_id = chat.id
|
pre_resolved_telegram_id = chat.id
|
||||||
except Exception:
|
except Exception:
|
||||||
@@ -371,9 +371,9 @@ async def create_gift_purchase(
|
|||||||
# Stars payments need a Bot instance to create invoice links
|
# Stars payments need a Bot instance to create invoice links
|
||||||
bot = None
|
bot = None
|
||||||
if body.payment_method == 'telegram_stars':
|
if body.payment_method == 'telegram_stars':
|
||||||
from aiogram import Bot
|
from app.bot_factory import create_bot
|
||||||
|
|
||||||
bot = Bot(token=settings.BOT_TOKEN)
|
bot = create_bot()
|
||||||
|
|
||||||
payment_service = PaymentService(bot=bot)
|
payment_service = PaymentService(bot=bot)
|
||||||
payment_result = await payment_service.create_guest_payment(
|
payment_result = await payment_service.create_guest_payment(
|
||||||
|
|||||||
@@ -3,13 +3,11 @@
|
|||||||
import mimetypes
|
import mimetypes
|
||||||
|
|
||||||
import structlog
|
import structlog
|
||||||
from aiogram import Bot
|
|
||||||
from aiogram.client.default import DefaultBotProperties
|
|
||||||
from aiogram.enums import ParseMode
|
|
||||||
from aiogram.types import BufferedInputFile
|
from aiogram.types import BufferedInputFile
|
||||||
from fastapi import APIRouter, Depends, File, Form, HTTPException, Request, Response, UploadFile, status
|
from fastapi import APIRouter, Depends, File, Form, HTTPException, Request, Response, UploadFile, status
|
||||||
from pydantic import BaseModel
|
from pydantic import BaseModel
|
||||||
|
|
||||||
|
from app.bot_factory import create_bot
|
||||||
from app.config import settings
|
from app.config import settings
|
||||||
from app.database.models import User
|
from app.database.models import User
|
||||||
|
|
||||||
@@ -98,10 +96,7 @@ async def upload_media(
|
|||||||
target_chat_id = _resolve_target_chat_id()
|
target_chat_id = _resolve_target_chat_id()
|
||||||
upload = BufferedInputFile(file_bytes, filename=file.filename or 'upload')
|
upload = BufferedInputFile(file_bytes, filename=file.filename or 'upload')
|
||||||
|
|
||||||
bot = Bot(
|
bot = create_bot()
|
||||||
token=settings.BOT_TOKEN,
|
|
||||||
default=DefaultBotProperties(parse_mode=ParseMode.HTML),
|
|
||||||
)
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
if media_type_normalized == 'photo':
|
if media_type_normalized == 'photo':
|
||||||
@@ -158,10 +153,7 @@ async def download_media(
|
|||||||
Download media file by file_id.
|
Download media file by file_id.
|
||||||
Used to display images/documents in ticket messages.
|
Used to display images/documents in ticket messages.
|
||||||
"""
|
"""
|
||||||
bot = Bot(
|
bot = create_bot()
|
||||||
token=settings.BOT_TOKEN,
|
|
||||||
default=DefaultBotProperties(parse_mode=ParseMode.HTML),
|
|
||||||
)
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
file = await bot.get_file(file_id)
|
file = await bot.get_file(file_id)
|
||||||
|
|||||||
@@ -178,12 +178,11 @@ async def apply_for_partner(
|
|||||||
|
|
||||||
# Уведомляем админов о новой заявке
|
# Уведомляем админов о новой заявке
|
||||||
try:
|
try:
|
||||||
from aiogram import Bot
|
from app.bot_factory import create_bot
|
||||||
|
|
||||||
from app.services.admin_notification_service import AdminNotificationService
|
from app.services.admin_notification_service import AdminNotificationService
|
||||||
|
|
||||||
if getattr(settings, 'ADMIN_NOTIFICATIONS_ENABLED', False) and settings.BOT_TOKEN:
|
if getattr(settings, 'ADMIN_NOTIFICATIONS_ENABLED', False) and settings.BOT_TOKEN:
|
||||||
bot = Bot(token=settings.BOT_TOKEN)
|
bot = create_bot()
|
||||||
try:
|
try:
|
||||||
notification_service = AdminNotificationService(bot)
|
notification_service = AdminNotificationService(bot)
|
||||||
await notification_service.send_partner_application_notification(
|
await notification_service.send_partner_application_notification(
|
||||||
|
|||||||
@@ -815,12 +815,11 @@ async def purchase_traffic(
|
|||||||
|
|
||||||
# Отправляем уведомление админам
|
# Отправляем уведомление админам
|
||||||
try:
|
try:
|
||||||
from aiogram import Bot
|
from app.bot_factory import create_bot
|
||||||
|
|
||||||
from app.services.admin_notification_service import AdminNotificationService
|
from app.services.admin_notification_service import AdminNotificationService
|
||||||
|
|
||||||
if getattr(settings, 'ADMIN_NOTIFICATIONS_ENABLED', False) and settings.BOT_TOKEN:
|
if getattr(settings, 'ADMIN_NOTIFICATIONS_ENABLED', False) and settings.BOT_TOKEN:
|
||||||
bot = Bot(token=settings.BOT_TOKEN)
|
bot = create_bot()
|
||||||
try:
|
try:
|
||||||
notification_service = AdminNotificationService(bot)
|
notification_service = AdminNotificationService(bot)
|
||||||
old_traffic = subscription.traffic_limit_gb - request.gb
|
old_traffic = subscription.traffic_limit_gb - request.gb
|
||||||
@@ -1043,12 +1042,11 @@ async def purchase_devices_legacy(
|
|||||||
|
|
||||||
# Отправляем уведомление админам
|
# Отправляем уведомление админам
|
||||||
try:
|
try:
|
||||||
from aiogram import Bot
|
from app.bot_factory import create_bot
|
||||||
|
|
||||||
from app.services.admin_notification_service import AdminNotificationService
|
from app.services.admin_notification_service import AdminNotificationService
|
||||||
|
|
||||||
if getattr(settings, 'ADMIN_NOTIFICATIONS_ENABLED', False) and settings.BOT_TOKEN:
|
if getattr(settings, 'ADMIN_NOTIFICATIONS_ENABLED', False) and settings.BOT_TOKEN:
|
||||||
bot = Bot(token=settings.BOT_TOKEN)
|
bot = create_bot()
|
||||||
try:
|
try:
|
||||||
notification_service = AdminNotificationService(bot)
|
notification_service = AdminNotificationService(bot)
|
||||||
await notification_service.send_subscription_update_notification(
|
await notification_service.send_subscription_update_notification(
|
||||||
@@ -1344,12 +1342,11 @@ async def activate_trial(
|
|||||||
|
|
||||||
# Send admin notification about trial activation
|
# Send admin notification about trial activation
|
||||||
try:
|
try:
|
||||||
from aiogram import Bot
|
from app.bot_factory import create_bot
|
||||||
|
|
||||||
from app.services.admin_notification_service import AdminNotificationService
|
from app.services.admin_notification_service import AdminNotificationService
|
||||||
|
|
||||||
if getattr(settings, 'ADMIN_NOTIFICATIONS_ENABLED', False) and settings.BOT_TOKEN:
|
if getattr(settings, 'ADMIN_NOTIFICATIONS_ENABLED', False) and settings.BOT_TOKEN:
|
||||||
bot = Bot(token=settings.BOT_TOKEN)
|
bot = create_bot()
|
||||||
try:
|
try:
|
||||||
notification_service = AdminNotificationService(bot)
|
notification_service = AdminNotificationService(bot)
|
||||||
charged_amount = settings.TRIAL_ACTIVATION_PRICE if requires_payment else None
|
charged_amount = settings.TRIAL_ACTIVATION_PRICE if requires_payment else None
|
||||||
@@ -1763,12 +1760,11 @@ async def submit_purchase(
|
|||||||
|
|
||||||
# Отправляем уведомление админам о покупке подписки
|
# Отправляем уведомление админам о покупке подписки
|
||||||
try:
|
try:
|
||||||
from aiogram import Bot
|
from app.bot_factory import create_bot
|
||||||
|
|
||||||
from app.services.admin_notification_service import AdminNotificationService
|
from app.services.admin_notification_service import AdminNotificationService
|
||||||
|
|
||||||
if getattr(settings, 'ADMIN_NOTIFICATIONS_ENABLED', False) and settings.BOT_TOKEN:
|
if getattr(settings, 'ADMIN_NOTIFICATIONS_ENABLED', False) and settings.BOT_TOKEN:
|
||||||
bot = Bot(token=settings.BOT_TOKEN)
|
bot = create_bot()
|
||||||
try:
|
try:
|
||||||
notification_service = AdminNotificationService(bot)
|
notification_service = AdminNotificationService(bot)
|
||||||
is_new_subscription = result.get('was_trial_conversion') or not context.subscription
|
is_new_subscription = result.get('was_trial_conversion') or not context.subscription
|
||||||
@@ -2161,12 +2157,11 @@ async def purchase_tariff(
|
|||||||
|
|
||||||
# Отправляем уведомление админам о покупке/продлении тарифа
|
# Отправляем уведомление админам о покупке/продлении тарифа
|
||||||
try:
|
try:
|
||||||
from aiogram import Bot
|
from app.bot_factory import create_bot
|
||||||
|
|
||||||
from app.services.admin_notification_service import AdminNotificationService
|
from app.services.admin_notification_service import AdminNotificationService
|
||||||
|
|
||||||
if getattr(settings, 'ADMIN_NOTIFICATIONS_ENABLED', False) and settings.BOT_TOKEN:
|
if getattr(settings, 'ADMIN_NOTIFICATIONS_ENABLED', False) and settings.BOT_TOKEN:
|
||||||
bot = Bot(token=settings.BOT_TOKEN)
|
bot = create_bot()
|
||||||
try:
|
try:
|
||||||
notification_service = AdminNotificationService(bot)
|
notification_service = AdminNotificationService(bot)
|
||||||
# Определяем тип покупки: новая подписка или продление
|
# Определяем тип покупки: новая подписка или продление
|
||||||
@@ -2418,12 +2413,11 @@ async def purchase_devices(
|
|||||||
|
|
||||||
# Отправляем уведомление админам
|
# Отправляем уведомление админам
|
||||||
try:
|
try:
|
||||||
from aiogram import Bot
|
from app.bot_factory import create_bot
|
||||||
|
|
||||||
from app.services.admin_notification_service import AdminNotificationService
|
from app.services.admin_notification_service import AdminNotificationService
|
||||||
|
|
||||||
if getattr(settings, 'ADMIN_NOTIFICATIONS_ENABLED', False) and settings.BOT_TOKEN:
|
if getattr(settings, 'ADMIN_NOTIFICATIONS_ENABLED', False) and settings.BOT_TOKEN:
|
||||||
bot = Bot(token=settings.BOT_TOKEN)
|
bot = create_bot()
|
||||||
try:
|
try:
|
||||||
notification_service = AdminNotificationService(bot)
|
notification_service = AdminNotificationService(bot)
|
||||||
await notification_service.send_subscription_update_notification(
|
await notification_service.send_subscription_update_notification(
|
||||||
@@ -4204,12 +4198,11 @@ async def switch_tariff(
|
|||||||
|
|
||||||
# Отправляем уведомление админам о смене тарифа
|
# Отправляем уведомление админам о смене тарифа
|
||||||
try:
|
try:
|
||||||
from aiogram import Bot
|
from app.bot_factory import create_bot
|
||||||
|
|
||||||
from app.services.admin_notification_service import AdminNotificationService
|
from app.services.admin_notification_service import AdminNotificationService
|
||||||
|
|
||||||
if getattr(settings, 'ADMIN_NOTIFICATIONS_ENABLED', False) and settings.BOT_TOKEN:
|
if getattr(settings, 'ADMIN_NOTIFICATIONS_ENABLED', False) and settings.BOT_TOKEN:
|
||||||
bot = Bot(token=settings.BOT_TOKEN)
|
bot = create_bot()
|
||||||
try:
|
try:
|
||||||
notification_service = AdminNotificationService(bot)
|
notification_service = AdminNotificationService(bot)
|
||||||
await notification_service.send_subscription_purchase_notification(
|
await notification_service.send_subscription_purchase_notification(
|
||||||
|
|||||||
+19
-35
@@ -5,7 +5,6 @@ API роуты колеса удачи для пользователей.
|
|||||||
import math
|
import math
|
||||||
import time
|
import time
|
||||||
|
|
||||||
import httpx
|
|
||||||
import structlog
|
import structlog
|
||||||
from fastapi import APIRouter, Depends, HTTPException, status
|
from fastapi import APIRouter, Depends, HTTPException, status
|
||||||
from pydantic import BaseModel
|
from pydantic import BaseModel
|
||||||
@@ -21,7 +20,6 @@ from app.cabinet.schemas.wheel import (
|
|||||||
WheelConfigResponse,
|
WheelConfigResponse,
|
||||||
WheelPrizeDisplay,
|
WheelPrizeDisplay,
|
||||||
)
|
)
|
||||||
from app.config import settings
|
|
||||||
from app.database.crud.wheel import (
|
from app.database.crud.wheel import (
|
||||||
get_or_create_wheel_config,
|
get_or_create_wheel_config,
|
||||||
get_user_spin_history,
|
get_user_spin_history,
|
||||||
@@ -251,44 +249,30 @@ async def create_stars_invoice(
|
|||||||
|
|
||||||
# Создаем invoice через Telegram Bot API
|
# Создаем invoice через Telegram Bot API
|
||||||
try:
|
try:
|
||||||
bot_token = settings.BOT_TOKEN
|
from aiogram.types import LabeledPrice
|
||||||
api_url = f'https://api.telegram.org/bot{bot_token}/createInvoiceLink'
|
|
||||||
|
|
||||||
async with httpx.AsyncClient() as client:
|
from app.bot_factory import create_bot
|
||||||
response = await client.post(
|
|
||||||
api_url,
|
async with create_bot() as bot:
|
||||||
json={
|
invoice_url = await bot.create_invoice_link(
|
||||||
'title': 'Колесо удачи',
|
title='Колесо удачи',
|
||||||
'description': f'Спин колеса удачи ({stars_amount} ⭐)',
|
description=f'Спин колеса удачи ({stars_amount} ⭐)',
|
||||||
'payload': payload,
|
payload=payload,
|
||||||
'provider_token': '', # Пустой для Stars
|
provider_token='',
|
||||||
'currency': 'XTR',
|
currency='XTR',
|
||||||
'prices': [{'label': 'Спин колеса', 'amount': stars_amount}],
|
prices=[LabeledPrice(label='Спин колеса', amount=stars_amount)],
|
||||||
},
|
|
||||||
)
|
)
|
||||||
|
|
||||||
result = response.json()
|
logger.info('Created Stars invoice for wheel spin: user=, stars', user_id=user.id, stars_amount=stars_amount)
|
||||||
|
|
||||||
if not result.get('ok'):
|
return StarsInvoiceResponse(
|
||||||
logger.error('Telegram API error', result=result)
|
invoice_url=invoice_url,
|
||||||
raise HTTPException(
|
stars_amount=stars_amount,
|
||||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
)
|
||||||
detail='Ошибка создания инвойса',
|
|
||||||
)
|
|
||||||
|
|
||||||
invoice_url = result['result']
|
except Exception as e:
|
||||||
logger.info(
|
logger.error('Error creating invoice', error=e)
|
||||||
'Created Stars invoice for wheel spin: user=, stars', user_id=user.id, stars_amount=stars_amount
|
|
||||||
)
|
|
||||||
|
|
||||||
return StarsInvoiceResponse(
|
|
||||||
invoice_url=invoice_url,
|
|
||||||
stars_amount=stars_amount,
|
|
||||||
)
|
|
||||||
|
|
||||||
except httpx.HTTPError as e:
|
|
||||||
logger.error('HTTP error creating invoice', error=e)
|
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||||
detail='Ошибка соединения с Telegram',
|
detail='Ошибка создания инвойса',
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -70,12 +70,11 @@ async def create_withdrawal(
|
|||||||
|
|
||||||
# Уведомляем админов о запросе на вывод
|
# Уведомляем админов о запросе на вывод
|
||||||
try:
|
try:
|
||||||
from aiogram import Bot
|
from app.bot_factory import create_bot
|
||||||
|
|
||||||
from app.services.admin_notification_service import AdminNotificationService
|
from app.services.admin_notification_service import AdminNotificationService
|
||||||
|
|
||||||
if getattr(settings, 'ADMIN_NOTIFICATIONS_ENABLED', False) and settings.BOT_TOKEN:
|
if getattr(settings, 'ADMIN_NOTIFICATIONS_ENABLED', False) and settings.BOT_TOKEN:
|
||||||
bot = Bot(token=settings.BOT_TOKEN)
|
bot = create_bot()
|
||||||
try:
|
try:
|
||||||
notification_service = AdminNotificationService(bot)
|
notification_service = AdminNotificationService(bot)
|
||||||
await notification_service.send_withdrawal_request_notification(
|
await notification_service.send_withdrawal_request_notification(
|
||||||
|
|||||||
@@ -67,9 +67,7 @@ async def get_cryptobot_payment_by_id(db: AsyncSession, payment_id: int) -> Cryp
|
|||||||
return result.scalar_one_or_none()
|
return result.scalar_one_or_none()
|
||||||
|
|
||||||
|
|
||||||
async def get_cryptobot_payment_by_invoice_id_for_update(
|
async def get_cryptobot_payment_by_invoice_id_for_update(db: AsyncSession, invoice_id: str) -> CryptoBotPayment | None:
|
||||||
db: AsyncSession, invoice_id: str
|
|
||||||
) -> CryptoBotPayment | None:
|
|
||||||
result = await db.execute(
|
result = await db.execute(
|
||||||
select(CryptoBotPayment)
|
select(CryptoBotPayment)
|
||||||
.options(selectinload(CryptoBotPayment.user))
|
.options(selectinload(CryptoBotPayment.user))
|
||||||
|
|||||||
@@ -540,10 +540,7 @@ async def create_invite_message(callback: types.CallbackQuery, db_user: User):
|
|||||||
# Compact share text for switch_inline_query (256-char limit)
|
# Compact share text for switch_inline_query (256-char limit)
|
||||||
share_text = invite_text
|
share_text = invite_text
|
||||||
if len(share_text) > 256:
|
if len(share_text) > 256:
|
||||||
share_text = (
|
share_text = texts.t('REFERRAL_INVITE_TITLE', '🎉 Присоединяйся к VPN сервису!') + f'\n\n👇 {bot_referral_link}'
|
||||||
texts.t('REFERRAL_INVITE_TITLE', '🎉 Присоединяйся к VPN сервису!')
|
|
||||||
+ f'\n\n👇 {bot_referral_link}'
|
|
||||||
)
|
|
||||||
if cabinet_referral_link and len(share_text) + len(cabinet_referral_link) + 5 <= 256:
|
if cabinet_referral_link and len(share_text) + len(cabinet_referral_link) + 5 <= 256:
|
||||||
share_text += f'\n🌐 {cabinet_referral_link}'
|
share_text += f'\n🌐 {cabinet_referral_link}'
|
||||||
share_text = share_text[:256]
|
share_text = share_text[:256]
|
||||||
|
|||||||
@@ -46,11 +46,10 @@ async def _send_admin_notification(
|
|||||||
if not getattr(settings, 'ADMIN_NOTIFICATIONS_ENABLED', False) or not settings.BOT_TOKEN:
|
if not getattr(settings, 'ADMIN_NOTIFICATIONS_ENABLED', False) or not settings.BOT_TOKEN:
|
||||||
return
|
return
|
||||||
try:
|
try:
|
||||||
from aiogram import Bot
|
from app.bot_factory import create_bot
|
||||||
|
|
||||||
from app.services.admin_notification_service import AdminNotificationService
|
from app.services.admin_notification_service import AdminNotificationService
|
||||||
|
|
||||||
async with Bot(token=settings.BOT_TOKEN) as bot:
|
async with create_bot() as bot:
|
||||||
service = AdminNotificationService(bot)
|
service = AdminNotificationService(bot)
|
||||||
await service.send_guest_purchase_notification(
|
await service.send_guest_purchase_notification(
|
||||||
purchase,
|
purchase,
|
||||||
@@ -546,9 +545,9 @@ async def _find_or_create_user(
|
|||||||
resolved_telegram_id: int | None = pre_resolved_telegram_id
|
resolved_telegram_id: int | None = pre_resolved_telegram_id
|
||||||
if resolved_telegram_id is None:
|
if resolved_telegram_id is None:
|
||||||
try:
|
try:
|
||||||
from aiogram import Bot
|
from app.bot_factory import create_bot
|
||||||
|
|
||||||
async with Bot(token=settings.BOT_TOKEN) as bot:
|
async with create_bot() as bot:
|
||||||
chat = await asyncio.wait_for(
|
chat = await asyncio.wait_for(
|
||||||
bot.get_chat(chat_id=f'@{username}'),
|
bot.get_chat(chat_id=f'@{username}'),
|
||||||
timeout=5.0,
|
timeout=5.0,
|
||||||
@@ -656,11 +655,10 @@ async def _send_telegram_gift_notification(
|
|||||||
try:
|
try:
|
||||||
import html as html_mod
|
import html as html_mod
|
||||||
|
|
||||||
from aiogram import Bot
|
|
||||||
from aiogram.client.default import DefaultBotProperties
|
|
||||||
from aiogram.enums import ParseMode
|
|
||||||
from aiogram.types import InlineKeyboardButton, InlineKeyboardMarkup
|
from aiogram.types import InlineKeyboardButton, InlineKeyboardMarkup
|
||||||
|
|
||||||
|
from app.bot_factory import create_bot
|
||||||
|
|
||||||
gift_from = ''
|
gift_from = ''
|
||||||
if purchase.contact_value:
|
if purchase.contact_value:
|
||||||
safe_name = html_mod.escape(purchase.contact_value)
|
safe_name = html_mod.escape(purchase.contact_value)
|
||||||
@@ -691,10 +689,7 @@ async def _send_telegram_gift_notification(
|
|||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
async with Bot(
|
async with create_bot() as bot:
|
||||||
token=settings.BOT_TOKEN,
|
|
||||||
default=DefaultBotProperties(parse_mode=ParseMode.HTML),
|
|
||||||
) as bot:
|
|
||||||
await bot.send_message(
|
await bot.send_message(
|
||||||
chat_id=user.telegram_id,
|
chat_id=user.telegram_id,
|
||||||
text=text,
|
text=text,
|
||||||
@@ -1205,8 +1200,7 @@ async def _send_stuck_purchase_alert(data: dict, retry_count: int, phase: str) -
|
|||||||
try:
|
try:
|
||||||
import html as html_mod
|
import html as html_mod
|
||||||
|
|
||||||
from aiogram import Bot
|
from app.bot_factory import create_bot
|
||||||
|
|
||||||
from app.services.admin_notification_service import AdminNotificationService, NotificationCategory
|
from app.services.admin_notification_service import AdminNotificationService, NotificationCategory
|
||||||
|
|
||||||
amount_rub = data['amount_kopeks'] / 100
|
amount_rub = data['amount_kopeks'] / 100
|
||||||
@@ -1225,7 +1219,7 @@ async def _send_stuck_purchase_alert(data: dict, retry_count: int, phase: str) -
|
|||||||
f'Requires manual investigation.'
|
f'Requires manual investigation.'
|
||||||
)
|
)
|
||||||
|
|
||||||
async with Bot(token=settings.BOT_TOKEN) as bot:
|
async with create_bot() as bot:
|
||||||
service = AdminNotificationService(bot)
|
service = AdminNotificationService(bot)
|
||||||
await service.send_admin_notification(text, category=NotificationCategory.ERRORS)
|
await service.send_admin_notification(text, category=NotificationCategory.ERRORS)
|
||||||
except Exception:
|
except Exception:
|
||||||
@@ -1244,8 +1238,7 @@ async def _send_amount_mismatch_alert(
|
|||||||
try:
|
try:
|
||||||
import html as html_mod
|
import html as html_mod
|
||||||
|
|
||||||
from aiogram import Bot
|
from app.bot_factory import create_bot
|
||||||
|
|
||||||
from app.services.admin_notification_service import AdminNotificationService, NotificationCategory
|
from app.services.admin_notification_service import AdminNotificationService, NotificationCategory
|
||||||
|
|
||||||
text = (
|
text = (
|
||||||
@@ -1260,7 +1253,7 @@ async def _send_amount_mismatch_alert(
|
|||||||
f'Requires manual investigation.'
|
f'Requires manual investigation.'
|
||||||
)
|
)
|
||||||
|
|
||||||
async with Bot(token=settings.BOT_TOKEN) as bot:
|
async with create_bot() as bot:
|
||||||
service = AdminNotificationService(bot)
|
service = AdminNotificationService(bot)
|
||||||
await service.send_admin_notification(text, category=NotificationCategory.ERRORS)
|
await service.send_admin_notification(text, category=NotificationCategory.ERRORS)
|
||||||
except Exception:
|
except Exception:
|
||||||
|
|||||||
@@ -452,18 +452,10 @@ class CloudPaymentsPaymentMixin:
|
|||||||
transaction: Any,
|
transaction: Any,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Send success notification to user via Telegram."""
|
"""Send success notification to user via Telegram."""
|
||||||
from aiogram import Bot
|
|
||||||
from aiogram.client.default import DefaultBotProperties
|
|
||||||
from aiogram.enums import ParseMode
|
|
||||||
|
|
||||||
from app.config import settings
|
from app.bot_factory import create_bot
|
||||||
from app.localization.texts import get_texts
|
from app.localization.texts import get_texts
|
||||||
|
|
||||||
bot = Bot(
|
|
||||||
token=settings.BOT_TOKEN,
|
|
||||||
default=DefaultBotProperties(parse_mode=ParseMode.HTML),
|
|
||||||
)
|
|
||||||
|
|
||||||
# Skip email-only users (no telegram_id)
|
# Skip email-only users (no telegram_id)
|
||||||
if not user.telegram_id:
|
if not user.telegram_id:
|
||||||
logger.debug('Skipping CloudPayments notification for email-only user', user_id=user.id)
|
logger.debug('Skipping CloudPayments notification for email-only user', user_id=user.id)
|
||||||
@@ -492,15 +484,16 @@ class CloudPaymentsPaymentMixin:
|
|||||||
if referrer_info:
|
if referrer_info:
|
||||||
message += f'\n\n{referrer_info}'
|
message += f'\n\n{referrer_info}'
|
||||||
|
|
||||||
try:
|
async with create_bot() as bot:
|
||||||
await bot.send_message(
|
try:
|
||||||
chat_id=user.telegram_id,
|
await bot.send_message(
|
||||||
text=message,
|
chat_id=user.telegram_id,
|
||||||
parse_mode='HTML',
|
text=message,
|
||||||
reply_markup=keyboard,
|
parse_mode='HTML',
|
||||||
)
|
reply_markup=keyboard,
|
||||||
except Exception as error:
|
)
|
||||||
logger.warning('Не удалось отправить уведомление пользователю', telegram_id=user.telegram_id, error=error)
|
except Exception as error:
|
||||||
|
logger.warning('Не удалось отправить уведомление пользователю', telegram_id=user.telegram_id, error=error)
|
||||||
|
|
||||||
async def _send_cloudpayments_fail_notification(
|
async def _send_cloudpayments_fail_notification(
|
||||||
self,
|
self,
|
||||||
@@ -508,27 +501,20 @@ class CloudPaymentsPaymentMixin:
|
|||||||
message: str,
|
message: str,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Send failure notification to user via Telegram."""
|
"""Send failure notification to user via Telegram."""
|
||||||
from aiogram import Bot
|
|
||||||
from aiogram.client.default import DefaultBotProperties
|
|
||||||
from aiogram.enums import ParseMode
|
|
||||||
|
|
||||||
from app.config import settings
|
from app.bot_factory import create_bot
|
||||||
|
|
||||||
bot = Bot(
|
|
||||||
token=settings.BOT_TOKEN,
|
|
||||||
default=DefaultBotProperties(parse_mode=ParseMode.HTML),
|
|
||||||
)
|
|
||||||
|
|
||||||
text = f'❌ <b>Оплата не прошла</b>\n\n{message}'
|
text = f'❌ <b>Оплата не прошла</b>\n\n{message}'
|
||||||
|
|
||||||
try:
|
async with create_bot() as bot:
|
||||||
await bot.send_message(
|
try:
|
||||||
chat_id=telegram_id,
|
await bot.send_message(
|
||||||
text=text,
|
chat_id=telegram_id,
|
||||||
parse_mode='HTML',
|
text=text,
|
||||||
)
|
parse_mode='HTML',
|
||||||
except Exception as error:
|
)
|
||||||
logger.warning('Не удалось отправить уведомление пользователю', telegram_id=telegram_id, error=error)
|
except Exception as error:
|
||||||
|
logger.warning('Не удалось отправить уведомление пользователю', telegram_id=telegram_id, error=error)
|
||||||
|
|
||||||
async def get_cloudpayments_payment_status(
|
async def get_cloudpayments_payment_status(
|
||||||
self,
|
self,
|
||||||
|
|||||||
@@ -509,14 +509,10 @@ class FreekassaPaymentMixin:
|
|||||||
# Lock payment row before finalization to prevent concurrent double-processing
|
# Lock payment row before finalization to prevent concurrent double-processing
|
||||||
locked = await freekassa_crud.get_freekassa_payment_by_id_for_update(db, payment.id)
|
locked = await freekassa_crud.get_freekassa_payment_by_id_for_update(db, payment.id)
|
||||||
if not locked:
|
if not locked:
|
||||||
logger.error(
|
logger.error('Freekassa status check: не удалось заблокировать платёж', payment_id=payment.id)
|
||||||
'Freekassa status check: не удалось заблокировать платёж', payment_id=payment.id
|
|
||||||
)
|
|
||||||
elif locked.is_paid:
|
elif locked.is_paid:
|
||||||
# Another concurrent handler already processed — skip
|
# Another concurrent handler already processed — skip
|
||||||
logger.info(
|
logger.info('Freekassa платеж уже оплачен после блокировки', order_id=locked.order_id)
|
||||||
'Freekassa платеж уже оплачен после блокировки', order_id=locked.order_id
|
|
||||||
)
|
|
||||||
payment = locked
|
payment = locked
|
||||||
else:
|
else:
|
||||||
payment = locked
|
payment = locked
|
||||||
|
|||||||
@@ -488,14 +488,10 @@ class KassaAiPaymentMixin:
|
|||||||
# Lock payment row before finalization to prevent concurrent double-processing
|
# Lock payment row before finalization to prevent concurrent double-processing
|
||||||
locked = await kassa_ai_crud.get_kassa_ai_payment_by_id_for_update(db, payment.id)
|
locked = await kassa_ai_crud.get_kassa_ai_payment_by_id_for_update(db, payment.id)
|
||||||
if not locked:
|
if not locked:
|
||||||
logger.error(
|
logger.error('KassaAI status check: не удалось заблокировать платёж', payment_id=payment.id)
|
||||||
'KassaAI status check: не удалось заблокировать платёж', payment_id=payment.id
|
|
||||||
)
|
|
||||||
elif locked.is_paid:
|
elif locked.is_paid:
|
||||||
# Another concurrent handler already processed — skip
|
# Another concurrent handler already processed — skip
|
||||||
logger.info(
|
logger.info('KassaAI платеж уже оплачен после блокировки', order_id=locked.order_id)
|
||||||
'KassaAI платеж уже оплачен после блокировки', order_id=locked.order_id
|
|
||||||
)
|
|
||||||
payment = locked
|
payment = locked
|
||||||
else:
|
else:
|
||||||
payment = locked
|
payment = locked
|
||||||
|
|||||||
@@ -264,7 +264,7 @@ class Pal24PaymentMixin:
|
|||||||
payment.callback_payload = callback
|
payment.callback_payload = callback
|
||||||
if payment_id is not None:
|
if payment_id is not None:
|
||||||
payment.payment_id = payment_id
|
payment.payment_id = payment_id
|
||||||
payment.payment_status = (callback.get('Status') or status)
|
payment.payment_status = callback.get('Status') or status
|
||||||
payment.payment_method = (
|
payment.payment_method = (
|
||||||
callback.get('payment_method')
|
callback.get('payment_method')
|
||||||
or callback.get('PaymentMethod')
|
or callback.get('PaymentMethod')
|
||||||
|
|||||||
@@ -1,11 +1,10 @@
|
|||||||
from datetime import UTC, datetime
|
from datetime import UTC, datetime
|
||||||
|
|
||||||
import structlog
|
import structlog
|
||||||
from aiogram import Bot
|
|
||||||
from aiogram.client.default import DefaultBotProperties
|
|
||||||
from sqlalchemy import select
|
from sqlalchemy import select
|
||||||
from sqlalchemy.ext.asyncio import AsyncSession
|
from sqlalchemy.ext.asyncio import AsyncSession
|
||||||
|
|
||||||
|
from app.bot_factory import create_bot
|
||||||
from app.config import settings
|
from app.config import settings
|
||||||
from app.database.crud.transaction import get_user_total_spent_kopeks
|
from app.database.crud.transaction import get_user_total_spent_kopeks
|
||||||
from app.database.crud.user import lock_user_for_update
|
from app.database.crud.user import lock_user_for_update
|
||||||
@@ -31,7 +30,7 @@ async def _notify_admins_about_auto_assignment(
|
|||||||
logger.debug('BOT_TOKEN не настроен — пропускаем уведомление о промогруппе')
|
logger.debug('BOT_TOKEN не настроен — пропускаем уведомление о промогруппе')
|
||||||
return
|
return
|
||||||
|
|
||||||
bot = Bot(token=bot_token, default=DefaultBotProperties(parse_mode='HTML'))
|
bot = create_bot(token=bot_token)
|
||||||
try:
|
try:
|
||||||
notification_service = AdminNotificationService(bot)
|
notification_service = AdminNotificationService(bot)
|
||||||
reason = (
|
reason = (
|
||||||
|
|||||||
@@ -251,8 +251,7 @@ async def _warn_if_no_superadmins(
|
|||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
logger.warning(
|
logger.warning(
|
||||||
'No active superadmin RBAC roles in DB. '
|
'No active superadmin RBAC roles in DB. Legacy config admins (ADMIN_IDS/ADMIN_EMAILS) still have access.',
|
||||||
'Legacy config admins (ADMIN_IDS/ADMIN_EMAILS) still have access.',
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import structlog
|
|||||||
from aiogram import Bot
|
from aiogram import Bot
|
||||||
from sqlalchemy.ext.asyncio import AsyncSession
|
from sqlalchemy.ext.asyncio import AsyncSession
|
||||||
|
|
||||||
|
from app.bot_factory import create_bot
|
||||||
from app.config import settings
|
from app.config import settings
|
||||||
from app.database.crud.subscription import (
|
from app.database.crud.subscription import (
|
||||||
add_subscription_servers,
|
add_subscription_servers,
|
||||||
@@ -337,7 +338,7 @@ async def with_admin_notification_service(
|
|||||||
|
|
||||||
bot: Bot | None = None
|
bot: Bot | None = None
|
||||||
try:
|
try:
|
||||||
bot = Bot(token=settings.BOT_TOKEN)
|
bot = create_bot()
|
||||||
service = AdminNotificationService(bot)
|
service = AdminNotificationService(bot)
|
||||||
await handler(service)
|
await handler(service)
|
||||||
except Exception as error: # pragma: no cover - defensive logging
|
except Exception as error: # pragma: no cover - defensive logging
|
||||||
|
|||||||
@@ -4,9 +4,6 @@ import mimetypes
|
|||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
import structlog
|
import structlog
|
||||||
from aiogram import Bot
|
|
||||||
from aiogram.client.default import DefaultBotProperties
|
|
||||||
from aiogram.enums import ParseMode
|
|
||||||
from aiogram.types import BufferedInputFile
|
from aiogram.types import BufferedInputFile
|
||||||
from fastapi import (
|
from fastapi import (
|
||||||
APIRouter,
|
APIRouter,
|
||||||
@@ -20,6 +17,7 @@ from fastapi import (
|
|||||||
status,
|
status,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
from app.bot_factory import create_bot
|
||||||
from app.config import settings
|
from app.config import settings
|
||||||
|
|
||||||
from ..dependencies import require_api_token
|
from ..dependencies import require_api_token
|
||||||
@@ -72,10 +70,7 @@ async def upload_media(
|
|||||||
target_chat_id = _resolve_target_chat_id()
|
target_chat_id = _resolve_target_chat_id()
|
||||||
upload = BufferedInputFile(file_bytes, filename=file.filename or 'upload')
|
upload = BufferedInputFile(file_bytes, filename=file.filename or 'upload')
|
||||||
|
|
||||||
bot = Bot(
|
bot = create_bot()
|
||||||
token=settings.BOT_TOKEN,
|
|
||||||
default=DefaultBotProperties(parse_mode=ParseMode.HTML),
|
|
||||||
)
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
if media_type_normalized == 'photo':
|
if media_type_normalized == 'photo':
|
||||||
@@ -121,10 +116,7 @@ async def download_media(
|
|||||||
file_id: str,
|
file_id: str,
|
||||||
_: Any = Security(require_api_token),
|
_: Any = Security(require_api_token),
|
||||||
) -> Response:
|
) -> Response:
|
||||||
bot = Bot(
|
bot = create_bot()
|
||||||
token=settings.BOT_TOKEN,
|
|
||||||
default=DefaultBotProperties(parse_mode=ParseMode.HTML),
|
|
||||||
)
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
file = await bot.get_file(file_id)
|
file = await bot.get_file(file_id)
|
||||||
|
|||||||
@@ -10,13 +10,13 @@ from typing import Any
|
|||||||
from uuid import uuid4
|
from uuid import uuid4
|
||||||
|
|
||||||
import structlog
|
import structlog
|
||||||
from aiogram import Bot
|
|
||||||
from fastapi import APIRouter, Depends, HTTPException, status
|
from fastapi import APIRouter, Depends, HTTPException, status
|
||||||
from pydantic import ValidationError
|
from pydantic import ValidationError
|
||||||
from sqlalchemy import select
|
from sqlalchemy import select
|
||||||
from sqlalchemy.ext.asyncio import AsyncSession
|
from sqlalchemy.ext.asyncio import AsyncSession
|
||||||
from sqlalchemy.orm import selectinload
|
from sqlalchemy.orm import selectinload
|
||||||
|
|
||||||
|
from app.bot_factory import create_bot
|
||||||
from app.config import settings
|
from app.config import settings
|
||||||
from app.database.crud.discount_offer import (
|
from app.database.crud.discount_offer import (
|
||||||
get_latest_claimed_offer_for_user,
|
get_latest_claimed_offer_for_user,
|
||||||
@@ -928,7 +928,7 @@ async def create_payment_link(
|
|||||||
detail='Failed to prepare Stars payment',
|
detail='Failed to prepare Stars payment',
|
||||||
) from exc
|
) from exc
|
||||||
|
|
||||||
bot = Bot(token=settings.BOT_TOKEN)
|
bot = create_bot()
|
||||||
invoice_payload = _build_balance_invoice_payload(user.id, amount_kopeks)
|
invoice_payload = _build_balance_invoice_payload(user.id, amount_kopeks)
|
||||||
try:
|
try:
|
||||||
payment_service = PaymentService(bot)
|
payment_service = PaymentService(bot)
|
||||||
@@ -1399,7 +1399,7 @@ async def create_payment_link(
|
|||||||
if not settings.BOT_TOKEN:
|
if not settings.BOT_TOKEN:
|
||||||
raise HTTPException(status.HTTP_500_INTERNAL_SERVER_ERROR, detail='Bot token is not configured')
|
raise HTTPException(status.HTTP_500_INTERNAL_SERVER_ERROR, detail='Bot token is not configured')
|
||||||
|
|
||||||
bot = Bot(token=settings.BOT_TOKEN)
|
bot = create_bot()
|
||||||
try:
|
try:
|
||||||
tribute_service = TributeService(bot)
|
tribute_service = TributeService(bot)
|
||||||
payment_url = await tribute_service.create_payment_link(
|
payment_url = await tribute_service.create_payment_link(
|
||||||
|
|||||||
@@ -4,13 +4,11 @@ from datetime import UTC, datetime
|
|||||||
from typing import Any, Optional
|
from typing import Any, Optional
|
||||||
|
|
||||||
from aiogram import Bot
|
from aiogram import Bot
|
||||||
from aiogram.client.default import DefaultBotProperties
|
|
||||||
from aiogram.enums import ParseMode
|
|
||||||
from fastapi import APIRouter, Depends, HTTPException, Query, status
|
from fastapi import APIRouter, Depends, HTTPException, Query, status
|
||||||
from sqlalchemy import func, select, update
|
from sqlalchemy import func, select, update
|
||||||
from sqlalchemy.ext.asyncio import AsyncSession
|
from sqlalchemy.ext.asyncio import AsyncSession
|
||||||
|
|
||||||
from app.config import settings
|
from app.bot_factory import create_bot
|
||||||
from app.database.models import PinnedMessage
|
from app.database.models import PinnedMessage
|
||||||
from app.services.pinned_message_service import (
|
from app.services.pinned_message_service import (
|
||||||
broadcast_pinned_message,
|
broadcast_pinned_message,
|
||||||
@@ -52,10 +50,7 @@ def _serialize_pinned_message(msg: PinnedMessage) -> PinnedMessageResponse:
|
|||||||
|
|
||||||
def _get_bot() -> Bot:
|
def _get_bot() -> Bot:
|
||||||
"""Создать экземпляр бота для API операций."""
|
"""Создать экземпляр бота для API операций."""
|
||||||
return Bot(
|
return create_bot()
|
||||||
token=settings.BOT_TOKEN,
|
|
||||||
default=DefaultBotProperties(parse_mode=ParseMode.HTML),
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@router.get('', response_model=PinnedMessageListResponse)
|
@router.get('', response_model=PinnedMessageListResponse)
|
||||||
|
|||||||
@@ -2,9 +2,6 @@ from __future__ import annotations
|
|||||||
|
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
from aiogram import Bot
|
|
||||||
from aiogram.client.default import DefaultBotProperties
|
|
||||||
from aiogram.enums import ParseMode
|
|
||||||
from fastapi import (
|
from fastapi import (
|
||||||
APIRouter,
|
APIRouter,
|
||||||
Depends,
|
Depends,
|
||||||
@@ -328,12 +325,9 @@ async def send_poll(
|
|||||||
total=0,
|
total=0,
|
||||||
)
|
)
|
||||||
|
|
||||||
from app.config import settings
|
from app.bot_factory import create_bot
|
||||||
|
|
||||||
bot = Bot(
|
bot = create_bot()
|
||||||
token=settings.BOT_TOKEN,
|
|
||||||
default=DefaultBotProperties(parse_mode=ParseMode.HTML),
|
|
||||||
)
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
result = await send_poll_to_users(bot, db, poll, users)
|
result = await send_poll_to_users(bot, db, poll, users)
|
||||||
|
|||||||
@@ -4,13 +4,10 @@ from datetime import UTC, datetime
|
|||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
import structlog
|
import structlog
|
||||||
from aiogram import Bot
|
|
||||||
from aiogram.client.default import DefaultBotProperties
|
|
||||||
from aiogram.enums import ParseMode
|
|
||||||
from fastapi import APIRouter, Depends, HTTPException, Query, Request, Security, status
|
from fastapi import APIRouter, Depends, HTTPException, Query, Request, Security, status
|
||||||
from sqlalchemy.ext.asyncio import AsyncSession
|
from sqlalchemy.ext.asyncio import AsyncSession
|
||||||
|
|
||||||
from app.config import settings
|
from app.bot_factory import create_bot
|
||||||
from app.database.crud.ticket import TicketCRUD, TicketMessageCRUD
|
from app.database.crud.ticket import TicketCRUD, TicketMessageCRUD
|
||||||
from app.database.models import Ticket, TicketMessage, TicketStatus
|
from app.database.models import Ticket, TicketMessage, TicketStatus
|
||||||
|
|
||||||
@@ -224,10 +221,7 @@ async def reply_to_ticket(
|
|||||||
media_caption=payload.media_caption,
|
media_caption=payload.media_caption,
|
||||||
)
|
)
|
||||||
|
|
||||||
bot = Bot(
|
bot = create_bot()
|
||||||
token=settings.BOT_TOKEN,
|
|
||||||
default=DefaultBotProperties(parse_mode=ParseMode.HTML),
|
|
||||||
)
|
|
||||||
try:
|
try:
|
||||||
from app.handlers.admin.tickets import notify_user_about_ticket_reply
|
from app.handlers.admin.tickets import notify_user_about_ticket_reply
|
||||||
|
|
||||||
@@ -286,10 +280,7 @@ async def get_ticket_message_media(
|
|||||||
raise HTTPException(status.HTTP_404_NOT_FOUND, 'Media not found for this message')
|
raise HTTPException(status.HTTP_404_NOT_FOUND, 'Media not found for this message')
|
||||||
|
|
||||||
media_url: str | None = None
|
media_url: str | None = None
|
||||||
bot = Bot(
|
bot = create_bot()
|
||||||
token=settings.BOT_TOKEN,
|
|
||||||
default=DefaultBotProperties(parse_mode=ParseMode.HTML),
|
|
||||||
)
|
|
||||||
try:
|
try:
|
||||||
file = await bot.get_file(message.media_file_id)
|
file = await bot.get_file(message.media_file_id)
|
||||||
if file.file_path:
|
if file.file_path:
|
||||||
|
|||||||
@@ -964,11 +964,10 @@ async def _send_crash_notification_on_error(error: Exception) -> None:
|
|||||||
return
|
return
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from aiogram import Bot
|
from app.bot_factory import create_bot
|
||||||
|
|
||||||
from app.services.startup_notification_service import send_crash_notification
|
from app.services.startup_notification_service import send_crash_notification
|
||||||
|
|
||||||
bot = Bot(token=settings.BOT_TOKEN)
|
bot = create_bot()
|
||||||
try:
|
try:
|
||||||
traceback_str = traceback.format_exc()
|
traceback_str = traceback.format_exc()
|
||||||
await send_crash_notification(bot, error, traceback_str)
|
await send_crash_notification(bot, error, traceback_str)
|
||||||
|
|||||||
Reference in New Issue
Block a user