diff --git a/bot.py b/bot.py
index 467a5909..3e03eb39 100644
--- a/bot.py
+++ b/bot.py
@@ -34,6 +34,7 @@
from models.user import UserAdmin
from processing.processing import processing_router
from repositories.button_media import ButtonMediaRepository
+from services.media import MediaService
from services.notification import NotificationService
from services.wallet import WalletService
from utils.utils import validate_i18n
@@ -91,16 +92,20 @@ async def on_startup():
if photos.total_count == 0:
photo_id_list = []
for admin_id in config.ADMIN_ID_LIST:
- msg = await bot.send_photo(chat_id=admin_id,
- photo=URLInputFile(url="https://i.imgur.com/CxWRPwY.png",
- filename="no_image.png"))
- bot_photo_id = msg.photo[-1].file_id
- photo_id_list.append(bot_photo_id)
+ try:
+ msg = await bot.send_photo(chat_id=admin_id,
+ photo=URLInputFile(url="https://img.freepik.com/premium-vector/no-photo-available-vector-icon-default-image-symbol-picture-coming-soon-web-site-mobile-app_87543-18055.jpg",
+ filename="no_image.png"))
+ bot_photo_id = msg.photo[-1].file_id
+ photo_id_list.append(bot_photo_id)
+ except Exception as _:
+ pass
bot_photo_id = photo_id_list[0]
else:
bot_photo_id = photos.photos[0][-1].file_id
with open("static/no_image.jpeg", "w") as f:
f.write(bot_photo_id)
+ await MediaService.update_inaccessible_media(bot)
validate_i18n()
await ButtonMediaRepository.init_buttons_media()
if config.CRYPTO_FORWARDING_MODE:
diff --git a/crypto_api/CryptoApiWrapper.py b/crypto_api/CryptoApiWrapper.py
index 5200000f..0c9e2571 100644
--- a/crypto_api/CryptoApiWrapper.py
+++ b/crypto_api/CryptoApiWrapper.py
@@ -20,13 +20,13 @@ async def fetch_api_request(url: str, params: dict | None = None, method: str =
async def get_crypto_prices() -> dict:
url = f"https://api.coingecko.com/api/v3/simple/price"
params = {
- "ids": "bitcoin,litecoin,solana,ethereum,binancecoin",
+ "ids": "bitcoin,litecoin,solana,ethereum,binancecoin,tether,usd-coin",
"vs_currencies": "usd,eur,gbp,jpy,cad"
}
return await CryptoApiWrapper.fetch_api_request(url, params)
@staticmethod
- async def get_wallet_balance() -> dict:
+ async def get_wallet_balance() -> dict[Cryptocurrency, float]:
url = f"{config.KRYPTO_EXPRESS_API_URL}/wallet"
headers = {
"X-Api-Key": config.KRYPTO_EXPRESS_API_KEY
@@ -35,7 +35,7 @@ async def get_wallet_balance() -> dict:
url,
headers=headers
)
- return {k: v for k, v in response.items() if v > 0}
+ return {Cryptocurrency(k): v for k, v in response.items()}
@staticmethod
async def withdrawal(cryptocurrency: Cryptocurrency,
diff --git a/handlers/admin/announcement.py b/handlers/admin/announcement.py
index 51779aeb..e0d56f7e 100644
--- a/handlers/admin/announcement.py
+++ b/handlers/admin/announcement.py
@@ -59,12 +59,8 @@ async def send_confirmation(**kwargs):
callback_data: AnnouncementCallback = kwargs.get("callback_data")
session: AsyncSession = kwargs.get("session")
language: Language = kwargs.get("language")
- msg = await AnnouncementService.send_announcement(callback, callback_data, session, language)
- if callback.message.caption:
- await callback.message.delete()
- await callback.message.answer(text=msg)
- elif callback.message.text:
- await callback.message.edit_text(text=msg)
+ await AnnouncementService.send_announcement(callback, callback_data, session, language)
+ await callback.message.delete()
@announcement_router.callback_query(AdminIdFilter(), AnnouncementCallback.filter())
diff --git a/i18n/de.json b/i18n/de.json
index 4c74218f..f1e975af 100644
--- a/i18n/de.json
+++ b/i18n/de.json
@@ -31,10 +31,8 @@
"credit_management_request_user_entity": "👤 Sende die Telegram-ID oder den Benutzernamen des Nutzers:",
"credit_management_user_not_found": "⚠️ Nutzer nicht gefunden.",
"crypto_withdraw": "👛 Wallet",
- "crypto_wallet": "\n\n🤖 Bot-Guthaben\n\n⚖️ BTC-Saldo: {btc_balance} BTC\n⚖️ LTC-Saldo: {ltc_balance} LTC\n⚖️ ETH-Saldo: {eth_balance} ETH\n⚖️ SOL-Saldo: {sol_balance} SOL\n⚖️ BNB-Saldo: {bnb_balance} BNB",
"current_stock_header": "🗂️ Aktueller Lagerbestand\n",
"deposits_statistics": "📊 Einzahlungsstatistik",
- "deposits_statistics_msg": "📊 Einzahlungsstatistiken der letzten {timedelta}.\n\n💸 Gesamteinzahlungen: {deposits_count}\n\n💰 BTC-Einzahlungen in Höhe von: {btc_amount} BTC\n💰 LTC-Einzahlungen in Höhe von: {ltc_amount} LTC\n💰 SOL-Einzahlungen in Höhe von: {sol_amount} SOL\n💰 ETH-Einzahlungen in Höhe von: {eth_amount} ETH\n💰 BNB-Einzahlungen in Höhe von: {bnb_amount} BNB\n\n💼 Gesamtbetrag in Fiat: {fiat_amount:.2f} {currency_text}",
"delete_entity": "🗑️ {entity} löschen",
"delete_entity_confirmation": "❓ Möchtest du {entity} mit dem Namen {entity_name} wirklich löschen?",
"get_database_file": "💾 Datenbankdatei herunterladen",
@@ -55,7 +53,6 @@
"restocking_message_header": "🆕 Neue Lagerbestände! 🆕\n",
"sales_statistics": "📊 Verkaufsstatistiken der letzten {timedelta}.\n💰 Gesamtgewinn: {currency_sym}{total_profit:.2f}\n🛍️ Verkaufte Artikel: {items_sold}\n💼 Gesamtkäufe: {buys_count}",
"send_everyone": "📢 An alle senden",
- "sending_result": "✅ Nachricht an {counter} von {len} aktiven Nutzern gesendet.\nGesamtzahl der Nutzer: {users_count}",
"sending_started": "🚀 Versand gestartet",
"stock": "📦 Vollständige Lagerbestandsmeldung",
"statistics": "📊 Analytik und Berichte",
@@ -135,7 +132,14 @@
"remove_review_image_confirmation": "❓ Sind Sie sicher, dass Sie das Bewertungsbild entfernen möchten?",
"reviews_management": "⭐ Bewertungsverwaltung",
"notification_new_deposit_id": "💰 Neue Einzahlung von Benutzer mit ID {telegram_id} für {currency_sym}{deposit_amount_fiat:.2f} mit 💰 {value} {crypto_name}\n💰 Empfehlungsbonus: {currency_sym}{referral_bonus:.2f}\n💰 Empfehlerbonus: {currency_sym}{referrer_bonus:.2f}",
- "notification_new_deposit_username": "💰 Neue Einzahlung von Benutzer mit Benutzername @{username} für {currency_sym}{deposit_amount_fiat:.2f} mit 💰 {value} {crypto_name}\n💰 Empfehlungsbonus: {currency_sym}{referral_bonus:.2f}\n💰 Empfehlerbonus: {currency_sym}{referrer_bonus:.2f}"
+ "notification_new_deposit_username": "💰 Neue Einzahlung von Benutzer mit Benutzername @{username} für {currency_sym}{deposit_amount_fiat:.2f} mit 💰 {value} {crypto_name}\n💰 Empfehlungsbonus: {currency_sym}{referral_bonus:.2f}\n💰 Empfehlerbonus: {currency_sym}{referrer_bonus:.2f}",
+ "crypto_wallet": "\n\n🤖 Bot-Guthaben\n\n{wallet_content}",
+ "crypto_wallet_line": "⚖️ {crypto_name} Guthaben: {crypto_balance:.8f} {crypto_name}",
+ "deposits_statistics_msg": "📊 Einzahlungsstatistiken der letzten {timedelta}.\n\n💰 Gesamteinzahlungen: {deposits_count}\n\n{deposits_content}\n\n💼 Gesamte Kryptowährungseinzahlungen im Wert: {fiat_amount:.2f} {currency_text}",
+ "deposits_statistics_line": "💰 Gesamte {crypto_name}-Einzahlungen im Wert: {crypto_amount:.8f} {crypto_name}",
+ "sending_result": "✅ Nachricht an {counter} von {len} aktiven Benutzern gesendet.\n👤 Benutzer insgesamt: {users_count}\nStatus: {status}",
+ "in_progress": "🟡 In Bearbeitung...",
+ "finished": "🟢 Abgeschlossen."
},
"common": {
"back_button": "⬅️ Zurück",
diff --git a/i18n/en.json b/i18n/en.json
index 01646975..ee680358 100644
--- a/i18n/en.json
+++ b/i18n/en.json
@@ -31,10 +31,8 @@
"credit_management_request_user_entity": "👤 Send the user's telegram id or their telegram username:",
"credit_management_user_not_found": "⚠️ The user with this data is not found in the database.",
"crypto_withdraw": "👛 Wallet",
- "crypto_wallet": "\n\n\uD83E\uDD16 Bot balances\n\n⚖\uFE0F BTC Balance: {btc_balance} BTC\n⚖\uFE0F LTC Balance: {ltc_balance} LTC\n⚖\uFE0F ETH Balance: {eth_balance} ETH\n⚖\uFE0F SOL Balance: {sol_balance} SOL\n⚖\uFE0F BNB Balance: {bnb_balance} BNB",
"current_stock_header": "🗂️ Current Stock\n",
"deposits_statistics": "📊 Deposits statistics",
- "deposits_statistics_msg": "📊 Deposit statistics for the last {timedelta}.\n\n\uD83D\uDCB8 Total deposits: {deposits_count}\n\n\uD83D\uDCB0 Total BTC deposits for the amount: {btc_amount} BTC\n\uD83D\uDCB0 Total LTC deposits for the amount: {ltc_amount} LTC\n\uD83D\uDCB0 Total SOL deposits for the amount: {sol_amount} SOL\n\uD83D\uDCB0 Total ETH deposits in amount: {eth_amount} ETH\n\uD83D\uDCB0 Total BNB deposits in amount: {bnb_amount} BNB\n\n\uD83D\uDCBC Total cryptocurrency deposits for the amount: {fiat_amount:.2f} {currency_text}",
"delete_entity": "🗑️ Delete {entity}",
"delete_entity_confirmation": "❓ Do you really want to delete the {entity} with name {entity_name}?",
"get_database_file": "💾 Get database file",
@@ -55,7 +53,6 @@
"restocking_message_header": "🆕 New Stock Alert! 🆕\n",
"sales_statistics": "📊 Sales statistics for the last {timedelta}.\n\uD83D\uDCB0 Total profit: {currency_sym}{total_profit:.2f}\n\uD83D\uDECD\uFE0F Items sold: {items_sold}\n\uD83D\uDCBC Total buys: {buys_count}",
"send_everyone": "📢 Send to Everyone",
- "sending_result": "✅ Message sent to {counter} out of {len} active users.\nTotal users:{users_count}",
"sending_started": "🚀 Sending started",
"stock": "📦 Full Inventory Message",
"statistics": "📊 Analytics & Reports ",
@@ -135,7 +132,14 @@
"remove_review_image_confirmation": "❓ Are you sure you want to remove the review image?",
"reviews_management": "⭐ Reviews Management",
"notification_new_deposit_id": "💰 New deposit by user with ID {telegram_id} for {currency_sym}{deposit_amount_fiat:.2f} with \uD83D\uDCB0 {value} {crypto_name}\n💰 Referral bonus: {currency_sym}{referral_bonus:.2f}\n💰 Referrer bonus: {currency_sym}{referrer_bonus:.2f}",
- "notification_new_deposit_username": "💰 New deposit by user with username @{username} for {currency_sym}{deposit_amount_fiat:.2f} with \uD83D\uDCB0 {value} {crypto_name}\n💰 Referral bonus: {currency_sym}{referral_bonus:.2f}\n💰 Referrer bonus: {currency_sym}{referrer_bonus:.2f}"
+ "notification_new_deposit_username": "💰 New deposit by user with username @{username} for {currency_sym}{deposit_amount_fiat:.2f} with \uD83D\uDCB0 {value} {crypto_name}\n💰 Referral bonus: {currency_sym}{referral_bonus:.2f}\n💰 Referrer bonus: {currency_sym}{referrer_bonus:.2f}",
+ "crypto_wallet": "\n\n\uD83E\uDD16 Bot balances\n\n{wallet_content}",
+ "crypto_wallet_line": "⚖\uFE0F {crypto_name} Balance: {crypto_balance:.8f} {crypto_name}",
+ "deposits_statistics_msg": "📊 Deposit statistics for the last {timedelta}.\n\n\uD83D\uDCB8 Total deposits: {deposits_count}\n\n{deposits_content}\n\n\uD83D\uDCBC Total cryptocurrency deposits for the amount: {fiat_amount:.2f} {currency_text}",
+ "deposits_statistics_line": "💰 Total {crypto_name} deposits for the amount: {crypto_amount:.8f} {crypto_name}",
+ "sending_result": "✅ Message sent to {counter} out of {len} active users.\n\uD83D\uDC64 Total users:{users_count}\nStatus: {status}",
+ "in_progress": "🟡 In progress...",
+ "finished": "🟢 Finished."
},
"common": {
"back_button": "⬅️ Back",
diff --git a/i18n/es.json b/i18n/es.json
index 12759db5..73b58ea6 100644
--- a/i18n/es.json
+++ b/i18n/es.json
@@ -31,10 +31,8 @@
"credit_management_request_user_entity": "👤 Envía el telegram id del usuario o su nombre de usuario:",
"credit_management_user_not_found": "⚠️ No se encontró al usuario con estos datos.",
"crypto_withdraw": "👛 Billetera",
- "crypto_wallet": "\n\n🤖 Saldos del bot\n\n⚖️ Saldo BTC: {btc_balance} BTC\n⚖️ Saldo LTC: {ltc_balance} LTC\n⚖️ Saldo ETH: {eth_balance} ETH\n⚖️ Saldo SOL: {sol_balance} SOL\n⚖️ Saldo BNB: {bnb_balance} BNB",
"current_stock_header": "🗂️ Inventario actual\n",
"deposits_statistics": "📊 Estadísticas de depósitos",
- "deposits_statistics_msg": "📊 Estadísticas de depósitos de los últimos {timedelta}.\n\n💸 Depósitos totales: {deposits_count}\n\n💰 Depósitos BTC por un monto de: {btc_amount} BTC\n💰 Depósitos LTC por un monto de: {ltc_amount} LTC\n💰 Depósitos SOL por un monto de: {sol_amount} SOL\n💰 Depósitos ETH por un monto de: {eth_amount} ETH\n💰 Depósitos BNB por un monto de: {bnb_amount} BNB\n\n💼 Total en moneda fiduciaria: {fiat_amount:.2f} {currency_text}",
"delete_entity": "🗑️ Eliminar {entity}",
"delete_entity_confirmation": "❓ ¿Realmente deseas eliminar {entity} con el nombre {entity_name}?",
"get_database_file": "💾 Obtener archivo de base de datos",
@@ -55,7 +53,6 @@
"restocking_message_header": "🆕 ¡Nueva llegada de stock! 🆕\n",
"sales_statistics": "📊 Estadísticas de ventas de los últimos {timedelta}.\n💰 Beneficio total: {currency_sym}{total_profit:.2f}\n🛍️ Artículos vendidos: {items_sold}\n💼 Total de compras: {buys_count}",
"send_everyone": "📢 Enviar a todos",
- "sending_result": "✅ Mensaje enviado a {counter} de {len} usuarios activos.\nUsuarios totales: {users_count}",
"sending_started": "🚀 Envío iniciado",
"stock": "📦 Mensaje de inventario completo",
"statistics": "📊 Analítica y reportes",
@@ -135,7 +132,14 @@
"remove_review_image_confirmation": "❓ ¿Estás seguro de que quieres eliminar la imagen de la reseña?",
"reviews_management": "⭐ Gestión de reseñas",
"notification_new_deposit_id": "💰 Nuevo depósito del usuario con ID {telegram_id} por {currency_sym}{deposit_amount_fiat:.2f} con 💰 {value} {crypto_name}\n💰 Bono por referido: {currency_sym}{referral_bonus:.2f}\n💰 Bono del referidor: {currency_sym}{referrer_bonus:.2f}",
- "notification_new_deposit_username": "💰 Nuevo depósito del usuario con nombre de usuario @{username} por {currency_sym}{deposit_amount_fiat:.2f} con 💰 {value} {crypto_name}\n💰 Bono por referido: {currency_sym}{referral_bonus:.2f}\n💰 Bono del referidor: {currency_sym}{referrer_bonus:.2f}"
+ "notification_new_deposit_username": "💰 Nuevo depósito del usuario con nombre de usuario @{username} por {currency_sym}{deposit_amount_fiat:.2f} con 💰 {value} {crypto_name}\n💰 Bono por referido: {currency_sym}{referral_bonus:.2f}\n💰 Bono del referidor: {currency_sym}{referrer_bonus:.2f}",
+ "crypto_wallet": "\n\n🤖 Saldos del Bot\n\n{wallet_content}",
+ "crypto_wallet_line": "⚖️ Saldo de {crypto_name}: {crypto_balance:.8f} {crypto_name}",
+ "deposits_statistics_msg": "📊 Estadísticas de depósitos de los últimos {timedelta}.\n\n💰 Depósitos totales: {deposits_count}\n\n{deposits_content}\n\n💼 Depósitos totales de criptomonedas por valor: {fiat_amount:.2f} {currency_text}",
+ "deposits_statistics_line": "💰 Depósitos totales de {crypto_name} por valor: {crypto_amount:.8f} {crypto_name}",
+ "sending_result": "✅ Mensaje enviado a {counter} de {len} usuarios activos.\n👤 Usuarios totales: {users_count}\nEstado: {status}",
+ "in_progress": "🟡 En progreso...",
+ "finished": "🟢 Finalizado."
},
"common": {
"back_button": "⬅️ Atrás",
diff --git a/i18n/fr.json b/i18n/fr.json
index 3a67930d..a791cf56 100644
--- a/i18n/fr.json
+++ b/i18n/fr.json
@@ -31,10 +31,8 @@
"credit_management_request_user_entity": "👤 Envoyez l’ID Telegram ou le nom d’utilisateur :",
"credit_management_user_not_found": "⚠️ Aucun utilisateur trouvé.",
"crypto_withdraw": "👛 Portefeuille",
- "crypto_wallet": "\n\n🤖 Soldes du bot\n\n⚖️ Solde BTC : {btc_balance} BTC\n⚖️ Solde LTC : {ltc_balance} LTC\n⚖️ Solde ETH : {eth_balance} ETH\n⚖️ Solde SOL : {sol_balance} SOL\n⚖️ Solde BNB : {bnb_balance} BNB",
"current_stock_header": "🗂️ Stock actuel\n",
"deposits_statistics": "📊 Statistiques de dépôts",
- "deposits_statistics_msg": "📊 Statistiques des dépôts pour les {timedelta} derniers jours.\n\n💸 Nombre total de dépôts : {deposits_count}\n\n💰 Dépôts BTC pour un montant de : {btc_amount} BTC\n💰 Dépôts LTC pour un montant de : {ltc_amount} LTC\n💰 Dépôts SOL pour un montant de : {sol_amount} SOL\n💰 Dépôts ETH pour un montant de : {eth_amount} ETH\n💰 Dépôts BNB pour un montant de : {bnb_amount} BNB\n\n💼 Total en monnaie fiduciaire : {fiat_amount:.2f} {currency_text}",
"delete_entity": "🗑️ Supprimer {entity}",
"delete_entity_confirmation": "❓ Voulez-vous vraiment supprimer {entity} nommé {entity_name} ?",
"get_database_file": "💾 Télécharger la base de données",
@@ -55,7 +53,6 @@
"restocking_message_header": "🆕 Nouveau stock disponible ! 🆕\n",
"sales_statistics": "📊 Statistiques des ventes des {timedelta} derniers jours.\n💰 Profit total : {currency_sym}{total_profit:.2f}\n🛍️ Articles vendus : {items_sold}\n💼 Nombre total d’achats : {buys_count}",
"send_everyone": "📢 Envoyer à tout le monde",
- "sending_result": "✅ Message envoyé à {counter} sur {len} utilisateurs actifs.\nTotal des utilisateurs : {users_count}",
"sending_started": "🚀 Envoi lancé",
"stock": "📦 Annonce complète du stock",
"statistics": "📊 Analyses et rapports",
@@ -135,7 +132,14 @@
"remove_review_image_confirmation": "❓ Êtes-vous sûr de vouloir supprimer l'image de l'avis?",
"reviews_management": "⭐ Gestion des avis",
"notification_new_deposit_id": "💰 Nouveau dépôt de l'utilisateur avec ID {telegram_id} pour {currency_sym}{deposit_amount_fiat:.2f} avec 💰 {value} {crypto_name}\n💰 Bonus parrainé: {currency_sym}{referral_bonus:.2f}\n💰 Bonus parrain: {currency_sym}{referrer_bonus:.2f}",
- "notification_new_deposit_username": "💰 Nouveau dépôt de l'utilisateur avec nom d'utilisateur @{username} pour {currency_sym}{deposit_amount_fiat:.2f} avec 💰 {value} {crypto_name}\n💰 Bonus parrainé: {currency_sym}{referral_bonus:.2f}\n💰 Bonus parrain: {currency_sym}{referrer_bonus:.2f}"
+ "notification_new_deposit_username": "💰 Nouveau dépôt de l'utilisateur avec nom d'utilisateur @{username} pour {currency_sym}{deposit_amount_fiat:.2f} avec 💰 {value} {crypto_name}\n💰 Bonus parrainé: {currency_sym}{referral_bonus:.2f}\n💰 Bonus parrain: {currency_sym}{referrer_bonus:.2f}",
+ "crypto_wallet": "\n\n🤖 Soldes du Bot\n\n{wallet_content}",
+ "crypto_wallet_line": "⚖️ Solde {crypto_name}: {crypto_balance:.8f} {crypto_name}",
+ "deposits_statistics_msg": "📊 Statistiques des dépôts des derniers {timedelta}.\n\n💰 Dépôts totaux: {deposits_count}\n\n{deposits_content}\n\n💼 Dépôts totaux de cryptomonnaies d'une valeur de: {fiat_amount:.2f} {currency_text}",
+ "deposits_statistics_line": "💰 Dépôts totaux de {crypto_name} d'une valeur de: {crypto_amount:.8f} {crypto_name}",
+ "sending_result": "✅ Message envoyé à {counter} sur {len} utilisateurs actifs.\n👤 Utilisateurs totaux: {users_count}\nStatut: {status}",
+ "in_progress": "🟡 En cours...",
+ "finished": "🟢 Terminé."
},
"common": {
"back_button": "⬅️ Retour",
diff --git a/i18n/it.json b/i18n/it.json
index b67459fc..07e3e682 100644
--- a/i18n/it.json
+++ b/i18n/it.json
@@ -31,10 +31,8 @@
"credit_management_request_user_entity": "👤 Invia l’ID Telegram o username:",
"credit_management_user_not_found": "⚠️ Nessun utente trovato.",
"crypto_withdraw": "👛 Portafoglio",
- "crypto_wallet": "\n\n🤖 Saldi del bot\n\n⚖️ Saldo BTC: {btc_balance} BTC\n⚖️ Saldo LTC: {ltc_balance} LTC\n⚖️ Saldo ETH: {eth_balance} ETH\n⚖️ Saldo SOL: {sol_balance} SOL\n⚖️ Saldo BNB: {bnb_balance} BNB",
"current_stock_header": "🗂️ Magazzino attuale\n",
"deposits_statistics": "📊 Statistiche depositi",
- "deposits_statistics_msg": "📊 Statistiche dei depositi degli ultimi {timedelta}.\n\n💸 Depositi totali: {deposits_count}\n\n💰 Depositi BTC per un importo di: {btc_amount} BTC\n💰 Depositi LTC per un importo di: {ltc_amount} LTC\n💰 Depositi SOL per un importo di: {sol_amount} SOL\n💰 Depositi ETH per un importo di: {eth_amount} ETH\n💰 Depositi BNB per un importo di: {bnb_amount} BNB\n\n💼 Totale in valuta fiat: {fiat_amount:.2f} {currency_text}",
"delete_entity": "🗑️ Elimina {entity}",
"delete_entity_confirmation": "❓ Vuoi davvero eliminare {entity} chiamato {entity_name}?",
"get_database_file": "💾 Scarica database",
@@ -55,7 +53,6 @@
"restocking_message_header": "🆕 Nuovo stock disponibile! 🆕\n",
"sales_statistics": "📊 Statistiche delle vendite degli ultimi {timedelta}.\n💰 Profitto totale: {currency_sym}{total_profit:.2f}\n🛍️ Articoli venduti: {items_sold}\n💼 Numero totale di acquisti: {buys_count}",
"send_everyone": "📢 Invia a tutti",
- "sending_result": "✅ Messaggio inviato a {counter} di {len} utenti attivi.\nTotale utenti: {users_count}",
"sending_started": "🚀 Invio avviato",
"stock": "📦 Annuncio completo magazzino",
"statistics": "📊 Statistiche e report",
@@ -135,7 +132,14 @@
"remove_review_image_confirmation": "❓ Sei sicuro di voler rimuovere l'immagine della recensione?",
"reviews_management": "⭐ Gestione recensioni",
"notification_new_deposit_id": "💰 Nuovo deposito dall'utente con ID {telegram_id} per {currency_sym}{deposit_amount_fiat:.2f} con 💰 {value} {crypto_name}\n💰 Bonus referral: {currency_sym}{referral_bonus:.2f}\n💰 Bonus referrer: {currency_sym}{referrer_bonus:.2f}",
- "notification_new_deposit_username": "💰 Nuovo deposito dall'utente con username @{username} per {currency_sym}{deposit_amount_fiat:.2f} con 💰 {value} {crypto_name}\n💰 Bonus referral: {currency_sym}{referral_bonus:.2f}\n💰 Bonus referrer: {currency_sym}{referrer_bonus:.2f}"
+ "notification_new_deposit_username": "💰 Nuovo deposito dall'utente con username @{username} per {currency_sym}{deposit_amount_fiat:.2f} con 💰 {value} {crypto_name}\n💰 Bonus referral: {currency_sym}{referral_bonus:.2f}\n💰 Bonus referrer: {currency_sym}{referrer_bonus:.2f}",
+ "crypto_wallet": "\n\n🤖 Saldi del Bot\n\n{wallet_content}",
+ "crypto_wallet_line": "⚖️ Saldo {crypto_name}: {crypto_balance:.8f} {crypto_name}",
+ "deposits_statistics_msg": "📊 Statistiche depositi degli ultimi {timedelta}.\n\n💰 Depositi totali: {deposits_count}\n\n{deposits_content}\n\n💼 Depositi totali di criptovalute per un valore di: {fiat_amount:.2f} {currency_text}",
+ "deposits_statistics_line": "💰 Depositi totali di {crypto_name} per un valore di: {crypto_amount:.8f} {crypto_name}",
+ "sending_result": "✅ Messaggio inviato a {counter} su {len} utenti attivi.\n👤 Utenti totali: {users_count}\nStato: {status}",
+ "in_progress": "🟡 In corso...",
+ "finished": "🟢 Completato."
},
"common": {
"back_button": "⬅️ Indietro",
diff --git a/i18n/zh.json b/i18n/zh.json
index ed8b62fc..d54077d5 100644
--- a/i18n/zh.json
+++ b/i18n/zh.json
@@ -31,10 +31,8 @@
"credit_management_request_user_entity": "👤 请输入 Telegram ID 或用户名:",
"credit_management_user_not_found": "⚠️ 未找到该用户。",
"crypto_withdraw": "👛 钱包",
- "crypto_wallet": "\n\n🤖 机器人余额\n\n⚖️ BTC 余额:{btc_balance} BTC\n⚖️ LTC 余额:{ltc_balance} LTC\n⚖️ ETH 余额:{eth_balance} ETH\n⚖️ SOL 余额:{sol_balance} SOL\n⚖️ BNB 余额:{bnb_balance} BNB",
"current_stock_header": "🗂️ 当前库存\n",
"deposits_statistics": "📊 充值统计",
- "deposits_statistics_msg": "📊 过去 {timedelta} 的充值统计。\n\n💸 总充值次数:{deposits_count}\n\n💰 BTC 充值金额:{btc_amount} BTC\n💰 LTC 充值金额:{ltc_amount} LTC\n💰 SOL 充值金额:{sol_amount} SOL\n💰 ETH 充值金额:{eth_amount} ETH\n💰 BNB 充值金额:{bnb_amount} BNB\n\n💼 加密货币折合法币总额:{fiat_amount:.2f} {currency_text}",
"delete_entity": "🗑️ 删除 {entity}",
"delete_entity_confirmation": "❓ 确定要删除名为 {entity_name} 的 {entity} 吗?",
"get_database_file": "💾 下载数据库",
@@ -55,7 +53,6 @@
"restocking_message_header": "🆕 新库存已上架! 🆕\n",
"sales_statistics": "📊 过去 {timedelta} 的销售统计。\n💰 总利润:{currency_sym}{total_profit:.2f}\n🛍️ 售出商品:{items_sold}\n💼 总购买次数:{buys_count}",
"send_everyone": "📢 群发消息",
- "sending_result": "✅ 消息已成功发送给 {counter}/{len} 名活跃用户。\n总用户数:{users_count}",
"sending_started": "🚀 开始发送",
"stock": "📦 全部库存公告",
"statistics": "📊 分析与统计",
@@ -135,7 +132,14 @@
"remove_review_image_confirmation": "❓ 确定要删除评价图片吗?",
"reviews_management": "⭐ 评价管理",
"notification_new_deposit_id": "💰 用户ID {telegram_id} 新存款 {currency_sym}{deposit_amount_fiat:.2f},使用 💰 {value} {crypto_name}\n💰 被邀请人奖金: {currency_sym}{referral_bonus:.2f}\n💰 邀请人奖金: {currency_sym}{referrer_bonus:.2f}",
- "notification_new_deposit_username": "💰 用户名 @{username} 新存款 {currency_sym}{deposit_amount_fiat:.2f},使用 💰 {value} {crypto_name}\n💰 被邀请人奖金: {currency_sym}{referral_bonus:.2f}\n💰 邀请人奖金: {currency_sym}{referrer_bonus:.2f}"
+ "notification_new_deposit_username": "💰 用户名 @{username} 新存款 {currency_sym}{deposit_amount_fiat:.2f},使用 💰 {value} {crypto_name}\n💰 被邀请人奖金: {currency_sym}{referral_bonus:.2f}\n💰 邀请人奖金: {currency_sym}{referrer_bonus:.2f}",
+ "crypto_wallet": "\n\n🤖 机器人余额\n\n{wallet_content}",
+ "crypto_wallet_line": "⚖️ {crypto_name} 余额: {crypto_balance:.8f} {crypto_name}",
+ "deposits_statistics_msg": "📊 最近 {timedelta} 存款统计\n\n💰 总存款次数: {deposits_count}\n\n{deposits_content}\n\n💼 加密货币存款总价值: {fiat_amount:.2f} {currency_text}",
+ "deposits_statistics_line": "💰 {crypto_name} 存款总额: {crypto_amount:.8f} {crypto_name}",
+ "sending_result": "✅ 消息已发送给 {len} 位活跃用户中的 {counter} 位\n👤 总用户数: {users_count}\n状态: {status}",
+ "in_progress": "🟡 进行中...",
+ "finished": "🟢 已完成。"
},
"common": {
"back_button": "⬅️ 返回",
diff --git a/repositories/button_media.py b/repositories/button_media.py
index a37c5569..31733ec7 100644
--- a/repositories/button_media.py
+++ b/repositories/button_media.py
@@ -1,9 +1,12 @@
-from sqlalchemy import select, update
+from sqlalchemy import select, update, distinct, union_all
from sqlalchemy.ext.asyncio import AsyncSession
from db import get_db_session, session_execute, session_commit
from enums.keyboard_button import KeyboardButton
from models.button_media import ButtonMedia, ButtonMediaDTO
+from models.category import Category
+from models.review import Review
+from models.subcategory import Subcategory
from utils.utils import get_bot_photo_id
@@ -35,3 +38,49 @@ async def update(button_media_dto: ButtonMediaDTO, session: AsyncSession):
.where(ButtonMedia.button == button_media_dto.button)
.values(**button_media_dto.model_dump()))
await session_execute(stmt, session)
+
+ @staticmethod
+ async def get_all_file_ids(session: AsyncSession) -> list[str]:
+ stmt = select(
+ distinct(
+ union_all(
+ select(Category.media_id).where(Category.media_id != None),
+ select(Subcategory.media_id).where(Subcategory.media_id != None),
+ select(ButtonMedia.media_id).where(ButtonMedia.media_id != None),
+ select(Review.image_id).where(Review.image_id != None),
+ ).subquery().c[0]
+ )
+ )
+ result = await session_execute(stmt, session)
+ return result.scalars().all()
+
+ @staticmethod
+ async def update_media_id(old_media_id: str, new_media_id: str, session: AsyncSession):
+ stmt_category = (
+ update(Category)
+ .where(Category.media_id == old_media_id)
+ .values(media_id=new_media_id)
+ )
+
+ stmt_subcategory = (
+ update(Subcategory)
+ .where(Subcategory.media_id == old_media_id)
+ .values(media_id=new_media_id)
+ )
+
+ stmt_buttonmedia = (
+ update(ButtonMedia)
+ .where(ButtonMedia.media_id == old_media_id)
+ .values(media_id=new_media_id)
+ )
+
+ stmt_review = (
+ update(Review)
+ .where(Review.image_id == old_media_id)
+ .values(image_id=new_media_id)
+ )
+ await session_execute(stmt_category, session)
+ await session_execute(stmt_subcategory, session)
+ await session_execute(stmt_buttonmedia, session)
+ await session_execute(stmt_review, session)
+
diff --git a/services/announcement.py b/services/announcement.py
index f740de9d..f85b0c87 100644
--- a/services/announcement.py
+++ b/services/announcement.py
@@ -14,6 +14,7 @@
from handlers.admin.constants import AdminConstants
from repositories.item import ItemRepository
from repositories.user import UserRepository
+from services.notification import NotificationService
from utils.utils import get_text
@@ -40,6 +41,15 @@ async def send_announcement(callback: CallbackQuery,
active_users = await UserRepository.get_active(session)
all_users_count = await UserRepository.get_all_count(session)
counter = 0
+ message_template = get_text(language, BotEntity.ADMIN, "sending_result")
+ message = await callback.message.answer(
+ text=message_template.format(
+ counter=counter,
+ len=len(active_users),
+ users_count=all_users_count,
+ status=get_text(language, BotEntity.ADMIN, "in_progress")
+ )
+ )
for user in active_users:
try:
await callback.message.copy_to(user.telegram_id, reply_markup=None)
@@ -55,9 +65,25 @@ async def send_announcement(callback: CallbackQuery,
except Exception as e:
logging.error(e)
finally:
+ if counter % 5 == 0:
+ msg = message_template.format(
+ counter=counter,
+ len=len(active_users),
+ users_count=all_users_count,
+ status=get_text(language, BotEntity.ADMIN, "in_progress")
+ )
+ await NotificationService.edit_message(message=msg,
+ source_message_id=message.message_id,
+ chat_id=message.chat.id)
if callback_data.announcement_type == AnnouncementType.RESTOCKING:
await ItemRepository.set_not_new(session)
await session_commit(session)
- return get_text(language, BotEntity.ADMIN, "sending_result").format(counter=counter,
- len=len(active_users),
- users_count=all_users_count)
+ await NotificationService.edit_message(
+ message=message_template.format(
+ counter=counter,
+ len=len(active_users),
+ users_count=all_users_count,
+ status=get_text(language, BotEntity.ADMIN, "finished")
+ ),
+ source_message_id=message.message_id,
+ chat_id=message.chat.id)
diff --git a/services/media.py b/services/media.py
index 3fa21e51..6d9b0e71 100644
--- a/services/media.py
+++ b/services/media.py
@@ -1,10 +1,12 @@
+from aiogram import Bot
+from aiogram.exceptions import TelegramBadRequest
from aiogram.fsm.context import FSMContext
from aiogram.types import Message, InputMediaPhoto, InputMediaVideo, InputMediaAnimation
from aiogram.utils.keyboard import InlineKeyboardBuilder
from sqlalchemy.ext.asyncio import AsyncSession
from callbacks import MediaManagementCallback, AdminMenuCallback
-from db import session_commit
+from db import session_commit, get_db_session
from enums.bot_entity import BotEntity
from enums.entity_type import EntityType
from enums.keyboard_button import KeyboardButton
@@ -15,7 +17,7 @@
from repositories.category import CategoryRepository
from repositories.subcategory import SubcategoryRepository
from services.notification import NotificationService
-from utils.utils import get_text
+from utils.utils import get_text, get_bot_photo_id
class MediaService:
@@ -150,3 +152,21 @@ def convert_to_media(media_id: str, caption: str) -> InputMediaPhoto | InputMedi
else:
media = InputMediaAnimation(media=category_media_id, caption=caption)
return media
+
+ @staticmethod
+ async def update_inaccessible_media(bot: Bot):
+ bot_photo_id = f"0{get_bot_photo_id()}"
+ async with get_db_session() as session:
+ unique_file_ids = await ButtonMediaRepository.get_all_file_ids(session)
+ inaccessible_media_list = []
+ for unique_id in unique_file_ids:
+ parsed_unique_id = unique_id
+ if parsed_unique_id[0] in ["0", "1", "2"]:
+ parsed_unique_id = unique_id[1:]
+ try:
+ await bot.get_file(parsed_unique_id)
+ except TelegramBadRequest as _:
+ inaccessible_media_list.append(unique_id)
+ for media_id in inaccessible_media_list:
+ await ButtonMediaRepository.update_media_id(media_id, bot_photo_id, session)
+ await session_commit(session)
diff --git a/services/notification.py b/services/notification.py
index f3966c5f..f13aa2df 100644
--- a/services/notification.py
+++ b/services/notification.py
@@ -182,9 +182,9 @@ async def new_buy(buy: BuyDTO, user: UserDTO, session: AsyncSession | Session):
category = await CategoryRepository.get_by_id(item_example.category_id, session)
subcategory = await SubcategoryRepository.get_by_id(item_example.subcategory_id, session)
if user.telegram_username:
- msg = get_text(Language.EN, BotEntity.ADMIN, "notification_purchase_with_tgid")
- else:
msg = get_text(Language.EN, BotEntity.ADMIN, "notification_purchase_with_username")
+ else:
+ msg = get_text(Language.EN, BotEntity.ADMIN, "notification_purchase_with_tgid")
cart_content.append(msg.format(
username=user.telegram_username,
telegram_id=user.telegram_id,
diff --git a/services/statistics.py b/services/statistics.py
index 50293208..b8be30df 100644
--- a/services/statistics.py
+++ b/services/statistics.py
@@ -199,40 +199,28 @@ async def get_statistics(callback_data: StatisticsCallback,
prices = await CryptoApiWrapper.get_crypto_prices()
chart = StatisticsService.build_statistics_chart(deposits, callback_data.timedelta, language, prices)
fiat_amount = 0.0
- btc_amount = sum(
- [btc_deposit.amount if btc_deposit.network == Cryptocurrency.BTC else 0 for btc_deposit in
- deposits])
- ltc_amount = sum(
- [ltc_deposit.amount if ltc_deposit.network == Cryptocurrency.LTC else 0 for ltc_deposit in
- deposits])
- sol_amount = sum(
- [sol_deposit.amount if sol_deposit.network == Cryptocurrency.SOL else 0 for sol_deposit in
- deposits])
- eth_amount = sum(
- [eth_deposit.amount if eth_deposit.network == Cryptocurrency.ETH else 0 for eth_deposit in
- deposits])
- bnb_amount = sum(
- [bnb_deposit.amount if bnb_deposit.network == Cryptocurrency.BNB else 0 for bnb_deposit in
- deposits])
- btc_amount = btc_amount / pow(10, Cryptocurrency.BTC.get_decimals())
- ltc_amount = ltc_amount / pow(10, Cryptocurrency.LTC.get_decimals())
- sol_amount = sol_amount / pow(10, Cryptocurrency.SOL.get_decimals())
- eth_amount = eth_amount / pow(10, Cryptocurrency.ETH.get_decimals())
- bnb_amount = bnb_amount / pow(10, Cryptocurrency.BNB.get_decimals())
- btc_price = prices[Cryptocurrency.BTC.get_coingecko_name()][config.CURRENCY.value.lower()]
- ltc_price = prices[Cryptocurrency.LTC.get_coingecko_name()][config.CURRENCY.value.lower()]
- sol_price = prices[Cryptocurrency.SOL.get_coingecko_name()][config.CURRENCY.value.lower()]
- eth_price = prices[Cryptocurrency.ETH.get_coingecko_name()][config.CURRENCY.value.lower()]
- bnb_price = prices[Cryptocurrency.BNB.get_coingecko_name()][config.CURRENCY.value.lower()]
- fiat_amount += ((btc_amount * btc_price) + (ltc_amount * ltc_price) + (sol_amount * sol_price)
- + (eth_amount * eth_price) + (bnb_amount * bnb_price))
+ crypto_amount_dict = {crypto: 0 for crypto in Cryptocurrency}
+ for deposit in deposits:
+ crypto_price = prices[deposit.network.get_coingecko_name()][config.CURRENCY.value.lower()]
+ crypto_friendly_amount = deposit.amount / pow(10, deposit.network.get_decimals())
+ crypto_amount_dict[deposit.network] += crypto_friendly_amount
+ fiat_amount += crypto_friendly_amount * crypto_price
+ fiat_amount = sum(crypto_amount_dict.values())
kb_builder.row(AdminConstants.back_to_main_button(language), callback_data.get_back_button(language))
+ deposits_content_list = []
+ for cryptocurrency, crypto_amount in crypto_amount_dict.items():
+ deposits_content_list.append(get_text(language,
+ BotEntity.ADMIN,
+ "deposits_statistics_line").format(
+ crypto_name=cryptocurrency.name.replace("_", " "),
+ crypto_amount=crypto_amount
+ ))
caption = get_text(language, BotEntity.ADMIN, "deposits_statistics_msg").format(
- timedelta=timedelta_localized, deposits_count=len(deposits),
- btc_amount=btc_amount, ltc_amount=ltc_amount,
- sol_amount=sol_amount, eth_amount=eth_amount,
- bnb_amount=bnb_amount,
- fiat_amount=fiat_amount, currency_text=config.CURRENCY.get_localized_text())
+ timedelta=timedelta_localized,
+ deposits_count=len(deposits),
+ deposits_content="\n".join(deposits_content_list),
+ fiat_amount=fiat_amount,
+ currency_text=config.CURRENCY.get_localized_text())
media = InputMediaPhoto(
media=BufferedInputFile(file=chart,
filename="chart.png"),
diff --git a/services/wallet.py b/services/wallet.py
index bc814903..5f9dc7f2 100644
--- a/services/wallet.py
+++ b/services/wallet.py
@@ -28,18 +28,21 @@ async def get_wallet_menu(language: Language) -> tuple[str, InlineKeyboardBuilde
async def get_withdraw_menu(language: Language) -> tuple[str, InlineKeyboardBuilder]:
kb_builder = InlineKeyboardBuilder()
wallet_balance = await CryptoApiWrapper.get_wallet_balance()
- [kb_builder.button(
- text=get_text(language, BotEntity.COMMON, f"{key.lower()}_top_up"),
- callback_data=WalletCallback.create(1, Cryptocurrency(key))
- ) for key in wallet_balance.keys()]
+ wallet_content = []
+ for cryptocurrency, amount in wallet_balance.items():
+ wallet_content.append(get_text(language, BotEntity.ADMIN, "crypto_wallet_line").format(
+ crypto_name=cryptocurrency.name.replace('_', " "),
+ crypto_balance=amount
+ ))
+ if amount > 0:
+ kb_builder.button(
+ text=get_text(language, BotEntity.COMMON, f"{cryptocurrency.name.lower()}_top_up"),
+ callback_data=WalletCallback.create(1, cryptocurrency)
+ )
kb_builder.adjust(1)
kb_builder.row(AdminConstants.back_to_main_button(language))
msg_text = get_text(language, BotEntity.ADMIN, "crypto_wallet").format(
- btc_balance=wallet_balance.get('BTC') or 0.0,
- ltc_balance=wallet_balance.get('LTC') or 0.0,
- sol_balance=wallet_balance.get('SOL') or 0.0,
- eth_balance=wallet_balance.get('ETH') or 0.0,
- bnb_balance=wallet_balance.get('BNB') or 0.0
+ wallet_content="\n".join(wallet_content)
)
if sum(wallet_balance.values()) > 0:
msg_text += get_text(language, BotEntity.ADMIN, "choose_crypto_to_withdraw")