Enhance bulk promo code creation with CSV export functionality

- Added the ability to generate and send a CSV file containing created promo codes, improving data accessibility for admins.
- Updated success messages to inform users about the CSV file and the number of created promo codes.
- Refactored the promo code creation logic to include detailed validity information and links for activation in the CSV output.
- Improved localization for bulk promo creation messages to enhance user experience.
This commit is contained in:
machka-pasla
2025-08-06 17:58:44 +03:00
parent a126c05365
commit 859263dc2d
4 changed files with 58 additions and 14 deletions
+55 -9
View File
@@ -1,6 +1,8 @@
import logging import logging
import random import random
import string import string
import csv
import io
from aiogram import Router, F, types from aiogram import Router, F, types
from aiogram.filters import StateFilter from aiogram.filters import StateFilter
from aiogram.fsm.context import FSMContext from aiogram.fsm.context import FSMContext
@@ -421,15 +423,52 @@ async def create_bulk_promo_codes_final(callback_or_message,
) )
) )
# Create CSV file with promo codes if any were created
csv_file = None
if created_codes: if created_codes:
success_lines.append("\n🎟 <b>Созданные коды:</b>") success_lines.append(f"\n🎟 <b>Создано {len(created_codes)} промокодов</b>")
# Show first 20 codes, then indicate if there are more success_lines.append("📄 CSV файл с промокодами отправлен отдельным сообщением")
codes_to_show = created_codes[:20]
for code in codes_to_show:
success_lines.append(f"<code>{code}</code>")
if len(created_codes) > 20: # Create CSV file
success_lines.append(f"... и еще {len(created_codes) - 20} кодов") output = io.StringIO()
writer = csv.writer(output)
# CSV headers
writer.writerow([
"Промокод", "Бонусные дни", "Макс. активации", "Действителен до",
"Команда для старта", "Ссылка для активации"
])
# Add bot username from settings
bot_username = getattr(settings, 'BOT_USERNAME', 'your_bot')
for code in created_codes:
# Determine validity info
if data.get("validity_days"):
valid_until = (datetime.now(timezone.utc) + timedelta(days=data["validity_days"])).strftime("%Y-%m-%d %H:%M:%S")
else:
valid_until = "Без ограничений"
start_command = f"/start promo_{code}"
telegram_link = f"https://t.me/{bot_username}?start=promo_{code}"
writer.writerow([
code,
data["bonus_days"],
data["max_activations"],
valid_until,
start_command,
telegram_link
])
output.seek(0)
# Create file for sending
filename = f"bulk_promo_codes_{datetime.now().strftime('%Y%m%d_%H%M%S')}.csv"
csv_file = types.BufferedInputFile(
output.getvalue().encode('utf-8-sig'), # BOM for correct Excel display
filename=filename
)
if failed_codes: if failed_codes:
success_lines.append(f"\n❌ <b>Ошибки ({len(failed_codes)}):</b>") success_lines.append(f"\n❌ <b>Ошибки ({len(failed_codes)}):</b>")
@@ -447,19 +486,26 @@ async def create_bulk_promo_codes_final(callback_or_message,
reply_markup=get_back_to_admin_panel_keyboard(current_lang, i18n), reply_markup=get_back_to_admin_panel_keyboard(current_lang, i18n),
parse_mode="HTML" parse_mode="HTML"
) )
message_obj = callback_or_message.message
except Exception: except Exception:
await callback_or_message.message.answer( message_obj = await callback_or_message.message.answer(
success_text, success_text,
reply_markup=get_back_to_admin_panel_keyboard(current_lang, i18n), reply_markup=get_back_to_admin_panel_keyboard(current_lang, i18n),
parse_mode="HTML" parse_mode="HTML"
) )
await callback_or_message.answer()
else: # Message else: # Message
await callback_or_message.answer( message_obj = await callback_or_message.answer(
success_text, success_text,
reply_markup=get_back_to_admin_panel_keyboard(current_lang, i18n), reply_markup=get_back_to_admin_panel_keyboard(current_lang, i18n),
parse_mode="HTML" parse_mode="HTML"
) )
# Send CSV file if created
if csv_file:
csv_caption = f"📄 Промокоды для массового создания\n💫 Всего: {len(created_codes)} промокодов\n🎁 Бонус: {data['bonus_days']} дней каждый"
await message_obj.answer_document(csv_file, caption=csv_caption)
await state.clear() await state.clear()
except Exception as e: except Exception as e:
+1 -1
View File
@@ -224,7 +224,7 @@ async def start_command_handler(message: types.Message,
if promo_code_to_apply: if promo_code_to_apply:
try: try:
from bot.services.promo_code_service import PromoCodeService from bot.services.promo_code_service import PromoCodeService
promo_code_service = PromoCodeService() promo_code_service = PromoCodeService(settings, subscription_service, bot, i18n)
success, result = await promo_code_service.apply_promo_code( success, result = await promo_code_service.apply_promo_code(
session, user_id, promo_code_to_apply, current_lang session, user_id, promo_code_to_apply, current_lang
-2
View File
@@ -55,7 +55,6 @@ class PromoCodeService:
reason=f"promo code {code_input_upper}") reason=f"promo code {code_input_upper}")
if new_end_date: if new_end_date:
activation_recorded = await promo_code_dal.record_promo_activation( activation_recorded = await promo_code_dal.record_promo_activation(
session, promo_data.promo_code_id, user_id, payment_id=None) session, promo_data.promo_code_id, user_id, payment_id=None)
promo_incremented = await promo_code_dal.increment_promo_code_usage( promo_incremented = await promo_code_dal.increment_promo_code_usage(
@@ -83,5 +82,4 @@ class PromoCodeService:
) )
return False, _("error_applying_promo_bonus") return False, _("error_applying_promo_bonus")
else: else:
return False, _("error_applying_promo_bonus") return False, _("error_applying_promo_bonus")
+2 -2
View File
@@ -154,8 +154,8 @@
"admin_back_to_panel": "⬅️ В панель", "admin_back_to_panel": "⬅️ В панель",
"admin_promo_unlimited": "♾️ Неограниченно", "admin_promo_unlimited": "♾️ Неограниченно",
"admin_bulk_promo_created_title": "📦 Массовое создание завершено", "admin_bulk_promo_created_title": "📦 Массовое создание завершено",
"admin_bulk_promo_created_stats": " Создано промокодов: {created_count}\n📅 Бонусные дни: {bonus_days}\n🔢 Максимальные активации: {max_activations}\n⏰ Действительны: {validity_info}", "admin_bulk_promo_created_stats": "📊 Создано: <b>{created}</b> из <b>{total}</b>",
"admin_bulk_promo_settings": "📝 Настройки промокодов", "admin_bulk_promo_settings": "🎁 Бонусные дни: <b>{bonus_days}</b>\n📊 Макс. активаций: <b>{max_activations}</b>\n⏰ Срок действия: <b>{validity}</b>",
"admin_promo_list_page_info": "Страница {current}/{total} ({count} промокодов)", "admin_promo_list_page_info": "Страница {current}/{total} ({count} промокодов)",
"admin_queue_status_button": "📊 Статус очередей", "admin_queue_status_button": "📊 Статус очередей",
"admin_queue_status_title": "📊 Статус очередей сообщений", "admin_queue_status_title": "📊 Статус очередей сообщений",