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:
@@ -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:
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
@@ -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": "📊 Статус очередей сообщений",
|
||||||
|
|||||||
Reference in New Issue
Block a user