Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .env.template
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ KRYPTO_EXPRESS_API_URL = "https://kryptoexpress.pro/api"
KRYPTO_EXPRESS_API_SECRET = ""
REDIS_PASSWORD = ""
REDIS_HOST = "redis"
TELEGRAM_PROXY_URL = ""
CRYPTO_FORWARDING_MODE = "false"
BTC_FORWARDING_ADDRESS = ""
LTC_FORWARDING_ADDRESS = ""
Expand All @@ -38,4 +39,4 @@ TOTAL_BONUS_CAP_PERCENT = "12"
SQLADMIN_RAW_PASSWORD = ""
JWT_EXPIRE_MINUTES = "30"
JWT_ALGORITHM = "HS256"
JWT_SECRET_KEY = ""
JWT_SECRET_KEY = ""
107 changes: 59 additions & 48 deletions bot.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,15 @@
import logging
import sys
import traceback
from contextlib import asynccontextmanager
from pathlib import Path
from aiogram.client.default import DefaultBotProperties
from aiogram.fsm.storage.redis import RedisStorage
from aiogram.types import BufferedInputFile, URLInputFile
from redis.asyncio import Redis
from sqladmin import Admin

import config
from aiogram import Bot, Dispatcher
from aiogram.enums import ParseMode
from aiogram import Dispatcher
from fastapi import FastAPI, Request, status, HTTPException

from admin import authentication_backend
Expand All @@ -37,48 +36,16 @@
from services.media import MediaService
from services.notification import NotificationService
from services.wallet import WalletService
from utils.telegram import create_bot, create_telegram_session
from utils.utils import validate_i18n

redis = Redis(host=config.REDIS_HOST, password=config.REDIS_PASSWORD)
bot = Bot(config.TOKEN, default=DefaultBotProperties(parse_mode=ParseMode.HTML))
session = create_telegram_session()
bot = create_bot(config.TOKEN, session)
dp = Dispatcher(storage=RedisStorage(redis))
app = FastAPI()
admin = Admin(app=app, engine=engine, authentication_backend=authentication_backend)
admin.add_model_view(UserAdmin)
admin.add_model_view(BuyAdmin)
admin.add_model_view(ShippingOptionAdmin)
admin.add_model_view(CouponAdmin)
admin.add_model_view(CategoryAdmin)
admin.add_model_view(SubcategoryAdmin)
admin.add_model_view(ItemAdmin)
admin.add_model_view(DepositAdmin)
admin.add_model_view(BuyItemAdmin)
admin.add_model_view(PaymentAdmin)
admin.add_model_view(CartAdmin)
admin.add_model_view(CartItemAdmin)
admin.add_model_view(ReferralBonusAdmin)
admin.add_model_view(ReviewAdmin)

app.include_router(processing_router)


@app.post(config.WEBHOOK_PATH)
async def webhook(request: Request):
secret_token = request.headers.get("X-Telegram-Bot-Api-Secret-Token")
if secret_token != config.WEBHOOK_SECRET_TOKEN:
raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Unauthorized")

try:
update_data = await request.json()
await dp.feed_webhook_update(bot, update_data)
return {"status": "ok"}
except Exception as e:
logging.error(f"Error processing webhook: {e}")
return {"status": "error"}, status.HTTP_500_INTERNAL_SERVER_ERROR


@app.on_event("startup")
async def on_startup():
async def _startup() -> None:
await create_db_and_tables()
await bot.set_webhook(
url=config.WEBHOOK_URL,
Expand Down Expand Up @@ -110,30 +77,74 @@ async def on_startup():
await ButtonMediaRepository.init_buttons_media()
if config.CRYPTO_FORWARDING_MODE:
for cryptocurrency in Cryptocurrency:
is_addr_valid = WalletService.validate_withdrawal_address(
cryptocurrency.get_forwarding_address(),
cryptocurrency
)
forwarding_address = cryptocurrency.get_forwarding_address()
is_addr_valid = WalletService.validate_withdrawal_address(forwarding_address, cryptocurrency)
if is_addr_valid is False:
logging.debug(
f"Your withdrawal address for {cryptocurrency.name} cryptocurrency is not valid!"
logging.error(
"Your withdrawal address for %s cryptocurrency is not configured correctly: %s",
cryptocurrency.name,
forwarding_address
)
sys.exit()
sys.exit(1)
for admin in config.ADMIN_ID_LIST:
try:
await bot.send_message(admin, 'Bot is working')
except Exception as e:
logging.warning(e)


@app.on_event("shutdown")
async def on_shutdown():
async def _shutdown() -> None:
logging.warning('Shutting down..')
await bot.delete_webhook()
await dp.storage.close()
await bot.session.close()
logging.warning('Bye!')


@asynccontextmanager
async def lifespan(app: FastAPI):
await _startup()
try:
yield
finally:
await _shutdown()


app = FastAPI(lifespan=lifespan)
admin = Admin(app=app, engine=engine, authentication_backend=authentication_backend)
admin.add_model_view(UserAdmin)
admin.add_model_view(BuyAdmin)
admin.add_model_view(ShippingOptionAdmin)
admin.add_model_view(CouponAdmin)
admin.add_model_view(CategoryAdmin)
admin.add_model_view(SubcategoryAdmin)
admin.add_model_view(ItemAdmin)
admin.add_model_view(DepositAdmin)
admin.add_model_view(BuyItemAdmin)
admin.add_model_view(PaymentAdmin)
admin.add_model_view(CartAdmin)
admin.add_model_view(CartItemAdmin)
admin.add_model_view(ReferralBonusAdmin)
admin.add_model_view(ReviewAdmin)

app.include_router(processing_router)


@app.post(config.WEBHOOK_PATH)
async def webhook(request: Request):
secret_token = request.headers.get("X-Telegram-Bot-Api-Secret-Token")
if secret_token != config.WEBHOOK_SECRET_TOKEN:
raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Unauthorized")

try:
update_data = await request.json()
await dp.feed_webhook_update(bot, update_data)
return {"status": "ok"}
except Exception as e:
logging.error(f"Error processing webhook: {e}")
return {"status": "error"}, status.HTTP_500_INTERNAL_SERVER_ERROR


@app.exception_handler(Exception)
async def exception_handler(request: Request, exc: Exception):
traceback_str = traceback.format_exc()
Expand Down
3 changes: 2 additions & 1 deletion config.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from enums.runtime_environment import RuntimeEnvironment
from utils.utils import get_sslipio_external_url, start_ngrok, hash_password

load_dotenv(".env")
load_dotenv(".env.bot.dev")
RUNTIME_ENVIRONMENT = RuntimeEnvironment(os.environ.get("RUNTIME_ENVIRONMENT"))
if RUNTIME_ENVIRONMENT == RuntimeEnvironment.DEV:
WEBHOOK_HOST = start_ngrok()
Expand Down Expand Up @@ -35,6 +35,7 @@
WEBHOOK_SECRET_TOKEN = os.environ.get("WEBHOOK_SECRET_TOKEN")
REDIS_HOST = os.environ.get("REDIS_HOST", "redis")
REDIS_PASSWORD = os.environ.get("REDIS_PASSWORD")
TELEGRAM_PROXY_URL = os.environ.get("TELEGRAM_PROXY_URL")
# VARIABLES FOR CRYPTO FORWARDING
CRYPTO_FORWARDING_MODE = os.environ.get("CRYPTO_FORWARDING_MODE", False) == 'true'
BTC_FORWARDING_ADDRESS = os.environ.get("BTC_FORWARDING_ADDRESS")
Expand Down
6 changes: 4 additions & 2 deletions handlers/admin/announcement.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,10 @@ async def send_generated_msg(**kwargs):
language: Language = kwargs.get("language")
kb_builder = AnnouncementsConstants.get_confirmation_builder(callback_data.announcement_type,
language)
msg = await ItemService.create_announcement_message(callback_data.announcement_type, session, language)
await callback.message.answer(text=msg, reply_markup=kb_builder.as_markup())
messages = await ItemService.create_announcement_message(callback_data.announcement_type, session, language)
for index, message_text in enumerate(messages):
reply_markup = kb_builder.as_markup() if index == 0 else None
await callback.message.answer(text=message_text, reply_markup=reply_markup)


async def send_confirmation(**kwargs):
Expand Down
11 changes: 7 additions & 4 deletions handlers/user/my_profile.py
Original file line number Diff line number Diff line change
Expand Up @@ -156,10 +156,13 @@ async def receive_top_up_amount(message: Message,
session,
language)
state_data = await state.get_data()
await message.bot.edit_message_media(chat_id=state_data.get("chat_id"),
message_id=state_data.get("msg_id"),
media=media,
reply_markup=kb_builder.as_markup())
if state_data.get("chat_id") and state_data.get("msg_id"):
await message.bot.edit_message_media(chat_id=state_data.get("chat_id"),
message_id=state_data.get("msg_id"),
media=media,
reply_markup=kb_builder.as_markup())
else:
await NotificationService.answer_media(message, media, kb_builder.as_markup())


@my_profile_router.callback_query(MyProfileCallback.filter(), IsUserExistFilter())
Expand Down
5 changes: 3 additions & 2 deletions i18n/de.json
Original file line number Diff line number Diff line change
Expand Up @@ -304,7 +304,8 @@
"referral_code_section": "\n\n<b>🔗 Ihr Empfehlungslink</b>\nt.me/{bot_username}?start={referral_code}\nTeilen Sie Ihren Link und wachsen Sie gemeinsam 🚀",
"referral_button": "✨ Empfehlungssystem",
"top_up_balance_deposit_msg": "💵 <b>Überweisen Sie den gewünschten Betrag in {crypto_name} an die Adresse, um das Bot-Guthaben aufzuladen.</b>\n\nZahlungsstatus: {status}\nZahlung gültig bis {payment_lifetime}.\n\n<b>Wichtig</b>\n<i>Für jede Einzahlung wird eine eindeutige {crypto_name}-Adresse vergeben\nDie Aufladung erfolgt innerhalb von 5 Minuten nach der Überweisung.\n\nNach erfolgreicher Guthabenaktualisierung erhalten Sie eine Benachrichtigung vom Bot.</i>\n\n<b>Ihre {crypto_name}-Adresse\n</b><code>{addr}</code>",
"top_up_balance_payment_msg": "💵 <b>Überweisen Sie <code>{crypto_amount}</code> {crypto_name} an die Adresse, um das Bot-Guthaben für {fiat_amount} {currency_text} aufzuladen</b>\n\nZahlungsstatus: {status}\nZahlung gültig bis {payment_lifetime}.\n\n<b>Wichtig</b>\n<i>Für jede Einzahlung wird eine eindeutige {crypto_name}-Adresse vergeben\nDie Aufladung erfolgt innerhalb von 5 Minuten nach der Überweisung.\n\nNach erfolgreicher Guthabenaktualisierung erhalten Sie eine Benachrichtigung vom Bot.</i>\n\n<b>Ihre {crypto_name}-Adresse\n</b><code>{addr}</code>",
"top_up_balance_request_fiat": "💵 <b>Bitte senden Sie den Betrag, den Sie in <u>{currency_text}</u> aufladen möchten\n⚠️ Achtung! Mindesteinzahlungsbetrag 5 {currency_text}</b>"
"top_up_balance_payment_msg": "💵 <b>Senden Sie genau <code>{crypto_amount}</code> {crypto_name}</b>\n\n⚠️ <b>SENDEN SIE NICHT WENIGER.</b>\n⚠️ <b>SENDEN SIE NICHT MEHR.</b>\n⚠️ <b>NUR DER EXAKTE BETRAG WIRD AKZEPTIERT.</b>\n\n❌ Unterzahlung ist verboten.\n❌ Überzahlung ist verboten.\n❗ Wenn Sie einen anderen Betrag als <code>{crypto_amount}</code> {crypto_name} senden, gehen Ihre Gelder <b>dauerhaft verloren</b> und <b>können nicht wiederhergestellt</b> werden.\n\nZahlungsstatus: {status}\n⏳ Gültig bis: {payment_lifetime}\n\n<b>Einzahlungsadresse:</b>\n<code>{addr}</code>\n\nℹ️ Dies ist eine eindeutige {crypto_name}-Adresse, die nur für diese Zahlung erstellt wurde.\n✅ Das Guthaben wird innerhalb von 5 Minuten nach Eingang der Transaktion gutgeschrieben.",
"top_up_balance_request_fiat": "💵 <b>Bitte senden Sie den Betrag, den Sie in <u>{currency_text}</u> aufladen möchten\n⚠️ Achtung! Mindesteinzahlungsbetrag 5 {currency_text}</b>",
"top_up_balance_invalid_fiat_amount": "⚠️ <b>Ungültiger Betrag.</b>\nBitte senden Sie einen positiven Betrag von <code>5</code> bis unter <code>1000000</code> {currency_text} mit höchstens 2 Dezimalstellen."
}
}
5 changes: 3 additions & 2 deletions i18n/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -303,7 +303,8 @@
"referral_code_section": "\n\n<b>\uD83D\uDD17 Your Referral Link</b>\nt.me/{bot_username}?start={referral_code}\nShare your link and grow together \uD83D\uDE80",
"referral_button": "✨ Referral System",
"top_up_balance_deposit_msg": "💵 <b>Deposit to the address the amount you want in {crypto_name} to top up the balance of bot.</b>\n\nPayment status: {status}\nPayment lifetime until {payment_lifetime}.\n\n<b>Important</b>\n<i>A unique {crypto_name} addresses is given for each deposit\nThe top up takes place within 5 minutes after the transfer.\n\nAfter a successful balance refresh, you will receive a notification from bot.</i>\n\n<b>Your {crypto_name} address\n</b><code>{addr}</code>",
"top_up_balance_payment_msg": "\uD83D\uDCB5 <b>Deposit to the address the <code>{crypto_amount}</code> {crypto_name} to top up the balance of bot for {fiat_amount} {currency_text}</b>\n\nPayment status: {status}\nPayment lifetime until {payment_lifetime}.\n\n<b>Important</b>\n<i>A unique {crypto_name} addresses is given for each deposit\nThe top up takes place within 5 minutes after the transfer.\n\nAfter a successful balance refresh, you will receive a notification from bot.</i>\n\n<b>Your {crypto_name} address\n</b><code>{addr}</code>",
"top_up_balance_request_fiat": "\uD83D\uDCB5 <b>Please send the amount you wish to top up balance in <u>{currency_text}</u>\n⚠\uFE0F Attention! Minimal deposit amount 5 {currency_text}</b>"
"top_up_balance_payment_msg": "💵 <b>Send exactly <code>{crypto_amount}</code> {crypto_name}</b>\n\n⚠️ <b>DO NOT SEND LESS.</b>\n⚠️ <b>DO NOT SEND MORE.</b>\n⚠️ <b>ONLY THE EXACT AMOUNT IS ACCEPTED.</b>\n\n❌ Underpayment is forbidden.\n❌ Overpayment is forbidden.\n❗ If you send any amount other than <code>{crypto_amount}</code> {crypto_name}, your funds will be <b>lost permanently</b> and <b>cannot be recovered</b>.\n\nPayment status: {status}\n⏳ Valid until: {payment_lifetime}\n\n<b>Deposit address:</b>\n<code>{addr}</code>\n\nℹ️ This is a unique {crypto_name} address created only for this payment.\n✅ The balance will be credited within 5 minutes after the transaction is received.",
"top_up_balance_request_fiat": "\uD83D\uDCB5 <b>Please send the amount you wish to top up balance in <u>{currency_text}</u>\n⚠\uFE0F Attention! Minimal deposit amount 5 {currency_text}</b>",
"top_up_balance_invalid_fiat_amount": "⚠️ <b>Invalid amount.</b>\nPlease send a positive amount from <code>5</code> to less than <code>1000000</code> {currency_text} using up to 2 decimal places."
}
}
5 changes: 3 additions & 2 deletions i18n/es.json
Original file line number Diff line number Diff line change
Expand Up @@ -304,7 +304,8 @@
"referral_code_section": "\n\n<b>🔗 Tu Enlace de Referido</b>\nt.me/{bot_username}?start={referral_code}\nComparte tu enlace y crece junto con otros 🚀",
"referral_button": "✨ Sistema de Referidos",
"top_up_balance_deposit_msg": "💵 <b>Deposite en la dirección la cantidad que desee en {crypto_name} para recargar el saldo del bot.</b>\n\nEstado del pago: {status}\nVigencia del pago hasta {payment_lifetime}.\n\n<b>Importante</b>\n<i>Se asigna una dirección única de {crypto_name} para cada depósito\nLa recarga se realiza en 5 minutos después de la transferencia.\n\nTras una actualización exitosa del saldo, recibirá una notificación del bot.</i>\n\n<b>Su dirección de {crypto_name}\n</b><code>{addr}</code>",
"top_up_balance_payment_msg": "💵 <b>Deposite en la dirección <code>{crypto_amount}</code> {crypto_name} para recargar el saldo del bot por {fiat_amount} {currency_text}</b>\n\nEstado del pago: {status}\nVigencia del pago hasta {payment_lifetime}.\n\n<b>Importante</b>\n<i>Se asigna una dirección única de {crypto_name} para cada depósito\nLa recarga se realiza en 5 minutos después de la transferencia.\n\nTras una actualización exitosa del saldo, recibirá una notificación del bot.</i>\n\n<b>Su dirección de {crypto_name}\n</b><code>{addr}</code>",
"top_up_balance_request_fiat": "💵 <b>Por favor, envíe la cantidad que desea recargar en <u>{currency_text}</u>\n⚠️ ¡Atención! Depósito mínimo 5 {currency_text}</b>"
"top_up_balance_payment_msg": "💵 <b>Envíe exactamente <code>{crypto_amount}</code> {crypto_name}</b>\n\n⚠️ <b>NO ENVÍE MENOS.</b>\n⚠️ <b>NO ENVÍE MÁS.</b>\n⚠️ <b>SOLO SE ACEPTA LA CANTIDAD EXACTA.</b>\n\n❌ El pago insuficiente está prohibido.\n❌ El pago en exceso está prohibido.\n❗ Si envía cualquier cantidad distinta de <code>{crypto_amount}</code> {crypto_name}, sus fondos se <b>perderán permanentemente</b> y <b>no podrán recuperarse</b>.\n\nEstado del pago: {status}\n⏳ Válido hasta: {payment_lifetime}\n\n<b>Dirección de depósito:</b>\n<code>{addr}</code>\n\nℹ️ Esta es una dirección única de {crypto_name} creada solo para este pago.\n✅ El saldo se acreditará dentro de los 5 minutos posteriores a la recepción de la transacción.",
"top_up_balance_request_fiat": "💵 <b>Por favor, envíe la cantidad que desea recargar en <u>{currency_text}</u>\n⚠️ ¡Atención! Depósito mínimo 5 {currency_text}</b>",
"top_up_balance_invalid_fiat_amount": "⚠️ <b>Cantidad no válida.</b>\nEnvíe una cantidad positiva desde <code>5</code> hasta menos de <code>1000000</code> {currency_text} usando hasta 2 decimales."
}
}
5 changes: 3 additions & 2 deletions i18n/fr.json
Original file line number Diff line number Diff line change
Expand Up @@ -304,7 +304,8 @@
"referral_code_section": "\n\n<b>🔗 Votre Lien de Parrainage</b>\nt.me/{bot_username}?start={referral_code}\nPartagez votre lien et grandissez ensemble 🚀",
"referral_button": "✨ Système de Parrainage",
"top_up_balance_deposit_msg": "💵 <b>Déposez sur l'adresse le montant souhaité en {crypto_name} pour recharger le solde du bot.</b>\n\nStatut du paiement: {status}\nValidité du paiement jusqu'au {payment_lifetime}.\n\n<b>Important</b>\n<i>Une adresse {crypto_name} unique est attribuée pour chaque dépôt\nLe rechargement s'effectue dans les 5 minutes suivant le transfert.\n\nAprès une actualisation réussie du solde, vous recevrez une notification du bot.</i>\n\n<b>Votre adresse {crypto_name}\n</b><code>{addr}</code>",
"top_up_balance_payment_msg": "💵 <b>Déposez sur l'adresse <code>{crypto_amount}</code> {crypto_name} pour recharger le solde du bot pour {fiat_amount} {currency_text}</b>\n\nStatut du paiement: {status}\nValidité du paiement jusqu'au {payment_lifetime}.\n\n<b>Important</b>\n<i>Une adresse {crypto_name} unique est attribuée pour chaque dépôt\nLe rechargement s'effectue dans les 5 minutes suivant le transfert.\n\nAprès une actualisation réussie du solde, vous recevrez une notification du bot.</i>\n\n<b>Votre adresse {crypto_name}\n</b><code>{addr}</code>",
"top_up_balance_request_fiat": "💵 <b>Veuillez envoyer le montant que vous souhaitez recharger en <u>{currency_text}</u>\n⚠️ Attention ! Dépôt minimum 5 {currency_text}</b>"
"top_up_balance_payment_msg": "💵 <b>Envoyez exactement <code>{crypto_amount}</code> {crypto_name}</b>\n\n⚠️ <b>N'ENVOYEZ PAS MOINS.</b>\n⚠️ <b>N'ENVOYEZ PAS PLUS.</b>\n⚠️ <b>SEUL LE MONTANT EXACT EST ACCEPTÉ.</b>\n\n❌ Le sous-paiement est interdit.\n❌ Le surpaiement est interdit.\n❗ Si vous envoyez un montant différent de <code>{crypto_amount}</code> {crypto_name}, vos fonds seront <b>perdus définitivement</b> et <b>ne pourront pas être récupérés</b>.\n\nStatut du paiement: {status}\n⏳ Valide jusqu'au : {payment_lifetime}\n\n<b>Adresse de dépôt :</b>\n<code>{addr}</code>\n\nℹ️ Il s'agit d'une adresse {crypto_name} unique créée uniquement pour ce paiement.\n✅ Le solde sera crédité dans les 5 minutes suivant la réception de la transaction.",
"top_up_balance_request_fiat": "💵 <b>Veuillez envoyer le montant que vous souhaitez recharger en <u>{currency_text}</u>\n⚠️ Attention ! Dépôt minimum 5 {currency_text}</b>",
"top_up_balance_invalid_fiat_amount": "⚠️ <b>Montant invalide.</b>\nVeuillez envoyer un montant positif de <code>5</code> à moins de <code>1000000</code> {currency_text} avec au maximum 2 décimales."
}
}
Loading
Loading