mirror of
https://github.com/BEDOLAGA-DEV/remnawave-bedolaga-telegram-bot.git
synced 2026-01-20 03:40:26 +00:00
Merge pull request #1980 from belousotroll/add-policy-page
Add policy page at registration stage
This commit is contained in:
@@ -20,18 +20,20 @@ from app.database.crud.campaign import (
|
||||
from app.database.models import UserStatus, SubscriptionStatus
|
||||
from app.keyboards.inline import (
|
||||
get_rules_keyboard,
|
||||
get_privacy_policy_keyboard,
|
||||
get_main_menu_keyboard,
|
||||
get_post_registration_keyboard,
|
||||
get_language_selection_keyboard,
|
||||
)
|
||||
from app.localization.loader import DEFAULT_LANGUAGE
|
||||
from app.localization.texts import get_texts, get_rules
|
||||
from app.localization.texts import get_texts, get_rules, get_privacy_policy
|
||||
from app.services.referral_service import process_referral_registration
|
||||
from app.services.campaign_service import AdvertisingCampaignService
|
||||
from app.services.admin_notification_service import AdminNotificationService
|
||||
from app.services.subscription_service import SubscriptionService
|
||||
from app.services.support_settings_service import SupportSettingsService
|
||||
from app.services.main_menu_button_service import MainMenuButtonService
|
||||
from app.services.privacy_policy_service import PrivacyPolicyService
|
||||
from app.utils.user_utils import generate_unique_referral_code
|
||||
from app.utils.promo_offer import (
|
||||
build_promo_offer_hint,
|
||||
@@ -104,6 +106,7 @@ async def handle_potential_referral_code(
|
||||
|
||||
if current_state not in [
|
||||
RegistrationStates.waiting_for_rules_accept.state,
|
||||
RegistrationStates.waiting_for_privacy_policy_accept.state,
|
||||
RegistrationStates.waiting_for_referral_code.state,
|
||||
None
|
||||
]:
|
||||
@@ -611,12 +614,105 @@ async def process_language_selection(
|
||||
)
|
||||
|
||||
|
||||
async def _show_privacy_policy_after_rules(
|
||||
callback: types.CallbackQuery,
|
||||
state: FSMContext,
|
||||
db: AsyncSession,
|
||||
language: str,
|
||||
) -> bool:
|
||||
"""
|
||||
Показывает политику конфиденциальности после принятия правил.
|
||||
Возвращает True, если политика была показана, False если её нет или произошла ошибка.
|
||||
"""
|
||||
policy = await PrivacyPolicyService.get_policy(db, language, fallback=True)
|
||||
|
||||
if not policy or not policy.is_enabled:
|
||||
logger.info("⚠️ Политика конфиденциальности не включена, пропускаем её показ")
|
||||
return False
|
||||
|
||||
if not policy.content or not policy.content.strip():
|
||||
privacy_policy_text = get_privacy_policy(language)
|
||||
if not privacy_policy_text or not privacy_policy_text.strip():
|
||||
logger.info("⚠️ Политика конфиденциальности включена, но дефолтный текст пустой, пропускаем показ")
|
||||
return False
|
||||
logger.info(f"🔒 Используется дефолтный текст политики конфиденциальности из локализации для языка {language}")
|
||||
else:
|
||||
privacy_policy_text = policy.content
|
||||
logger.info(f"🔒 Используется политика конфиденциальности из БД для языка {language}")
|
||||
|
||||
try:
|
||||
await callback.message.edit_text(
|
||||
privacy_policy_text,
|
||||
reply_markup=get_privacy_policy_keyboard(language)
|
||||
)
|
||||
await state.set_state(RegistrationStates.waiting_for_privacy_policy_accept)
|
||||
logger.info(f"🔒 Политика конфиденциальности отправлена пользователю {callback.from_user.id}")
|
||||
return True
|
||||
except Exception as e:
|
||||
logger.error(f"Ошибка при показе политики конфиденциальности: {e}", exc_info=True)
|
||||
try:
|
||||
await callback.message.answer(
|
||||
privacy_policy_text,
|
||||
reply_markup=get_privacy_policy_keyboard(language)
|
||||
)
|
||||
await state.set_state(RegistrationStates.waiting_for_privacy_policy_accept)
|
||||
logger.info(f"🔒 Политика конфиденциальности отправлена новым сообщением пользователю {callback.from_user.id}")
|
||||
return True
|
||||
except Exception as e2:
|
||||
logger.error(f"Критическая ошибка при отправке политики конфиденциальности: {e2}", exc_info=True)
|
||||
return False
|
||||
|
||||
|
||||
async def _continue_registration_after_rules(
|
||||
callback: types.CallbackQuery,
|
||||
state: FSMContext,
|
||||
db: AsyncSession,
|
||||
language: str,
|
||||
) -> None:
|
||||
"""
|
||||
Продолжает регистрацию после принятия правил (реферальный код или завершение).
|
||||
"""
|
||||
data = await state.get_data() or {}
|
||||
texts = get_texts(language)
|
||||
|
||||
if data.get('referral_code'):
|
||||
logger.info(f"🎫 Найден реферальный код из deep link: {data['referral_code']}")
|
||||
|
||||
referrer = await get_user_by_referral_code(db, data['referral_code'])
|
||||
if referrer:
|
||||
data['referrer_id'] = referrer.id
|
||||
await state.set_data(data)
|
||||
logger.info(f"✅ Реферер найден: {referrer.id}")
|
||||
|
||||
await complete_registration_from_callback(callback, state, db)
|
||||
else:
|
||||
if settings.SKIP_REFERRAL_CODE:
|
||||
logger.info("⚙️ SKIP_REFERRAL_CODE включен - пропускаем запрос реферального кода")
|
||||
await complete_registration_from_callback(callback, state, db)
|
||||
else:
|
||||
try:
|
||||
await callback.message.edit_text(
|
||||
texts.t(
|
||||
"REFERRAL_CODE_QUESTION",
|
||||
"У вас есть реферальный код? Введите его или нажмите 'Пропустить'",
|
||||
),
|
||||
reply_markup=get_referral_code_keyboard(language)
|
||||
)
|
||||
await state.set_state(RegistrationStates.waiting_for_referral_code)
|
||||
logger.info(f"🔍 Ожидание ввода реферального кода")
|
||||
except Exception as e:
|
||||
logger.error(f"Ошибка при показе вопроса о реферальном коде: {e}")
|
||||
await complete_registration_from_callback(callback, state, db)
|
||||
|
||||
|
||||
async def process_rules_accept(
|
||||
callback: types.CallbackQuery,
|
||||
state: FSMContext,
|
||||
db: AsyncSession
|
||||
):
|
||||
|
||||
"""
|
||||
Обрабатывает принятие или отклонение правил пользователем.
|
||||
"""
|
||||
logger.info(f"📋 RULES: Начало обработки правил")
|
||||
logger.info(f"📊 Callback data: {callback.data}")
|
||||
logger.info(f"👤 User: {callback.from_user.id}")
|
||||
@@ -637,50 +733,16 @@ async def process_rules_accept(
|
||||
if callback.data == 'rules_accept':
|
||||
logger.info(f"✅ Правила приняты пользователем {callback.from_user.id}")
|
||||
|
||||
try:
|
||||
await callback.message.delete()
|
||||
logger.info(f"🗑️ Сообщение с правилами удалено")
|
||||
except Exception as e:
|
||||
logger.warning(f"⚠️ Не удалось удалить сообщение с правилами: {e}")
|
||||
try:
|
||||
await callback.message.edit_text(
|
||||
texts.t(
|
||||
"RULES_ACCEPTED_PROCESSING",
|
||||
"✅ Правила приняты! Завершаем регистрацию...",
|
||||
),
|
||||
reply_markup=None
|
||||
)
|
||||
except Exception:
|
||||
pass
|
||||
# Пытаемся показать политику конфиденциальности
|
||||
policy_shown = await _show_privacy_policy_after_rules(
|
||||
callback, state, db, language
|
||||
)
|
||||
|
||||
if data.get('referral_code'):
|
||||
logger.info(f"🎫 Найден реферальный код из deep link: {data['referral_code']}")
|
||||
|
||||
referrer = await get_user_by_referral_code(db, data['referral_code'])
|
||||
if referrer:
|
||||
data['referrer_id'] = referrer.id
|
||||
await state.set_data(data)
|
||||
logger.info(f"✅ Реферер найден: {referrer.id}")
|
||||
|
||||
await complete_registration_from_callback(callback, state, db)
|
||||
else:
|
||||
if settings.SKIP_REFERRAL_CODE:
|
||||
logger.info("⚙️ SKIP_REFERRAL_CODE включен - пропускаем запрос реферального кода")
|
||||
await complete_registration_from_callback(callback, state, db)
|
||||
else:
|
||||
try:
|
||||
await callback.message.answer(
|
||||
texts.t(
|
||||
"REFERRAL_CODE_QUESTION",
|
||||
"У вас есть реферальный код? Введите его или нажмите 'Пропустить'",
|
||||
),
|
||||
reply_markup=get_referral_code_keyboard(language)
|
||||
)
|
||||
await state.set_state(RegistrationStates.waiting_for_referral_code)
|
||||
logger.info(f"🔍 Ожидание ввода реферального кода")
|
||||
except Exception as e:
|
||||
logger.error(f"Ошибка при показе вопроса о реферальном коде: {e}")
|
||||
await complete_registration_from_callback(callback, state, db)
|
||||
# Если политика не была показана, продолжаем регистрацию
|
||||
if not policy_shown:
|
||||
await _continue_registration_after_rules(
|
||||
callback, state, db, language
|
||||
)
|
||||
|
||||
else:
|
||||
logger.info(f"❌ Правила отклонены пользователем {callback.from_user.id}")
|
||||
@@ -697,10 +759,13 @@ async def process_rules_accept(
|
||||
)
|
||||
except Exception as e:
|
||||
logger.error(f"Ошибка при показе сообщения об отклонении правил: {e}")
|
||||
await callback.message.edit_text(
|
||||
rules_required_text,
|
||||
reply_markup=get_rules_keyboard(language)
|
||||
)
|
||||
try:
|
||||
await callback.message.edit_text(
|
||||
rules_required_text,
|
||||
reply_markup=get_rules_keyboard(language)
|
||||
)
|
||||
except:
|
||||
pass
|
||||
|
||||
logger.info(f"✅ Правила обработаны для пользователя {callback.from_user.id}")
|
||||
|
||||
@@ -727,6 +792,128 @@ async def process_rules_accept(
|
||||
pass
|
||||
|
||||
|
||||
async def process_privacy_policy_accept(
|
||||
callback: types.CallbackQuery,
|
||||
state: FSMContext,
|
||||
db: AsyncSession
|
||||
):
|
||||
|
||||
logger.info(f"🔒 PRIVACY POLICY: Начало обработки политики конфиденциальности")
|
||||
logger.info(f"📊 Callback data: {callback.data}")
|
||||
logger.info(f"👤 User: {callback.from_user.id}")
|
||||
|
||||
current_state = await state.get_state()
|
||||
logger.info(f"📊 Текущее состояние: {current_state}")
|
||||
|
||||
language = DEFAULT_LANGUAGE
|
||||
texts = get_texts(language)
|
||||
|
||||
try:
|
||||
await callback.answer()
|
||||
|
||||
data = await state.get_data() or {}
|
||||
language = data.get('language', language)
|
||||
texts = get_texts(language)
|
||||
|
||||
if callback.data == 'privacy_policy_accept':
|
||||
logger.info(f"✅ Политика конфиденциальности принята пользователем {callback.from_user.id}")
|
||||
|
||||
try:
|
||||
await callback.message.delete()
|
||||
logger.info(f"🗑️ Сообщение с политикой конфиденциальности удалено")
|
||||
except Exception as e:
|
||||
logger.warning(f"⚠️ Не удалось удалить сообщение с политикой конфиденциальности: {e}")
|
||||
try:
|
||||
await callback.message.edit_text(
|
||||
texts.t(
|
||||
"PRIVACY_POLICY_ACCEPTED_PROCESSING",
|
||||
"✅ Политика конфиденциальности принята! Продолжаем регистрацию...",
|
||||
),
|
||||
reply_markup=None
|
||||
)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
if data.get('referral_code'):
|
||||
logger.info(f"🎫 Найден реферальный код из deep link: {data['referral_code']}")
|
||||
|
||||
referrer = await get_user_by_referral_code(db, data['referral_code'])
|
||||
if referrer:
|
||||
data['referrer_id'] = referrer.id
|
||||
await state.set_data(data)
|
||||
logger.info(f"✅ Реферер найден: {referrer.id}")
|
||||
|
||||
await complete_registration_from_callback(callback, state, db)
|
||||
else:
|
||||
if settings.SKIP_REFERRAL_CODE:
|
||||
logger.info("⚙️ SKIP_REFERRAL_CODE включен - пропускаем запрос реферального кода")
|
||||
await complete_registration_from_callback(callback, state, db)
|
||||
else:
|
||||
try:
|
||||
await state.set_data(data)
|
||||
await state.set_state(RegistrationStates.waiting_for_referral_code)
|
||||
|
||||
await callback.bot.send_message(
|
||||
chat_id=callback.from_user.id,
|
||||
text=texts.t(
|
||||
"REFERRAL_CODE_QUESTION",
|
||||
"У вас есть реферальный код? Введите его или нажмите 'Пропустить'",
|
||||
),
|
||||
reply_markup=get_referral_code_keyboard(language)
|
||||
)
|
||||
logger.info(f"🔍 Ожидание ввода реферального кода")
|
||||
except Exception as e:
|
||||
logger.error(f"Ошибка при показе вопроса о реферальном коде: {e}")
|
||||
await complete_registration_from_callback(callback, state, db)
|
||||
|
||||
else:
|
||||
logger.info(f"❌ Политика конфиденциальности отклонена пользователем {callback.from_user.id}")
|
||||
|
||||
privacy_policy_required_text = texts.t(
|
||||
"PRIVACY_POLICY_REQUIRED",
|
||||
"Для использования бота необходимо принять политику конфиденциальности.",
|
||||
)
|
||||
|
||||
try:
|
||||
await callback.message.edit_text(
|
||||
privacy_policy_required_text,
|
||||
reply_markup=get_privacy_policy_keyboard(language)
|
||||
)
|
||||
except Exception as e:
|
||||
logger.error(f"Ошибка при показе сообщения об отклонении политики конфиденциальности: {e}")
|
||||
try:
|
||||
await callback.message.edit_text(
|
||||
privacy_policy_required_text,
|
||||
reply_markup=get_privacy_policy_keyboard(language)
|
||||
)
|
||||
except:
|
||||
pass
|
||||
|
||||
logger.info(f"✅ Политика конфиденциальности обработана для пользователя {callback.from_user.id}")
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"❌ Ошибка обработки политики конфиденциальности: {e}", exc_info=True)
|
||||
await callback.answer(
|
||||
texts.t("ERROR_TRY_AGAIN", "❌ Произошла ошибка. Попробуйте еще раз."),
|
||||
show_alert=True,
|
||||
)
|
||||
|
||||
try:
|
||||
data = await state.get_data() or {}
|
||||
language = data.get('language', language)
|
||||
texts = get_texts(language)
|
||||
await callback.message.answer(
|
||||
texts.t(
|
||||
"ERROR_PRIVACY_POLICY_RETRY",
|
||||
"Произошла ошибка. Попробуйте принять политику конфиденциальности еще раз:",
|
||||
),
|
||||
reply_markup=get_privacy_policy_keyboard(language)
|
||||
)
|
||||
await state.set_state(RegistrationStates.waiting_for_privacy_policy_accept)
|
||||
except:
|
||||
pass
|
||||
|
||||
|
||||
async def process_referral_code_input(
|
||||
message: types.Message,
|
||||
state: FSMContext,
|
||||
@@ -1751,7 +1938,14 @@ def register_handlers(dp: Dispatcher):
|
||||
StateFilter(RegistrationStates.waiting_for_rules_accept)
|
||||
)
|
||||
logger.info("✅ Зарегистрирован process_rules_accept")
|
||||
|
||||
|
||||
dp.callback_query.register(
|
||||
process_privacy_policy_accept,
|
||||
F.data.in_(["privacy_policy_accept", "privacy_policy_decline"]),
|
||||
StateFilter(RegistrationStates.waiting_for_privacy_policy_accept)
|
||||
)
|
||||
logger.info("✅ Зарегистрирован process_privacy_policy_accept")
|
||||
|
||||
dp.callback_query.register(
|
||||
process_language_selection,
|
||||
F.data.startswith("language_select:"),
|
||||
|
||||
@@ -127,6 +127,21 @@ def get_rules_keyboard(language: str = DEFAULT_LANGUAGE) -> InlineKeyboardMarkup
|
||||
]
|
||||
])
|
||||
|
||||
def get_privacy_policy_keyboard(language: str = DEFAULT_LANGUAGE) -> InlineKeyboardMarkup:
|
||||
texts = get_texts(language)
|
||||
return InlineKeyboardMarkup(inline_keyboard=[
|
||||
[
|
||||
InlineKeyboardButton(
|
||||
text=texts.PRIVACY_POLICY_ACCEPT,
|
||||
callback_data="privacy_policy_accept"
|
||||
),
|
||||
InlineKeyboardButton(
|
||||
text=texts.PRIVACY_POLICY_DECLINE,
|
||||
callback_data="privacy_policy_decline"
|
||||
)
|
||||
]
|
||||
])
|
||||
|
||||
def get_channel_sub_keyboard(
|
||||
channel_link: Optional[str],
|
||||
language: str = DEFAULT_LANGUAGE,
|
||||
|
||||
@@ -1214,8 +1214,14 @@
|
||||
"RULES_ACCEPTED_PROCESSING": "✅ Rules accepted! Completing registration...",
|
||||
"RULES_DECLINE": "❌ I do not accept",
|
||||
"RULES_HEADER": "📋 <b>Service Rules</b>",
|
||||
"PRIVACY_POLICY_ACCEPT": "✅ Accept",
|
||||
"PRIVACY_POLICY_DECLINE": "❌ Decline",
|
||||
"PRIVACY_POLICY_REQUIRED": "You must accept the privacy policy to use the bot.",
|
||||
"PRIVACY_POLICY_ACCEPTED_PROCESSING": "✅ Privacy policy accepted! Continuing registration...",
|
||||
"ERROR_PRIVACY_POLICY_RETRY": "An error occurred. Please try accepting the privacy policy again:",
|
||||
"RULES_REQUIRED": "❗️ You must accept the rules to use the service!",
|
||||
"RULES_TEXT_DEFAULT": "📋 <b>Service Usage Rules</b>\n\n1. Do not use the service for illegal activity\n2. Avoid sharing pirated or malicious content\n3. Spam and phishing are prohibited\n4. Using the service for DDoS attacks is forbidden\n5. One account is intended for one person\n6. Refunds are provided only in exceptional cases\n7. The administration may block accounts that violate the rules\n\n<b>By using the service you agree to follow these rules.</b>",
|
||||
"PRIVACY_POLICY_TEXT_DEFAULT": "🔒 <b>Privacy Policy</b>\n\nWe are committed to protecting your privacy and personal data.\n\n<b>Data Collection:</b>\n• We collect only necessary information to provide services\n• Data is used exclusively for service operation\n\n<b>Data Protection:</b>\n• Your data is protected by modern encryption methods\n• We do not share data with third parties without your consent\n\n<b>Your Rights:</b>\n• You can request information about stored data\n• You can request deletion of your data\n\n<b>By using the service, you agree to the privacy policy.</b>",
|
||||
"SELECT_COUNTRIES": "Select countries:",
|
||||
"SELECT_DEVICES": "Number of devices:",
|
||||
"SELECT_PERIOD": "Choose period:",
|
||||
|
||||
@@ -1234,8 +1234,14 @@
|
||||
"RULES_ACCEPTED_PROCESSING": "✅ Правила приняты! Завершаем регистрацию...",
|
||||
"RULES_DECLINE": "❌ Не принимаю",
|
||||
"RULES_HEADER": "📋 <b>Правила сервиса</b>",
|
||||
"PRIVACY_POLICY_ACCEPT": "✅ Принять",
|
||||
"PRIVACY_POLICY_DECLINE": "❌ Отклонить",
|
||||
"PRIVACY_POLICY_REQUIRED": "Для использования бота необходимо принять политику конфиденциальности.",
|
||||
"PRIVACY_POLICY_ACCEPTED_PROCESSING": "✅ Политика конфиденциальности принята! Продолжаем регистрацию...",
|
||||
"ERROR_PRIVACY_POLICY_RETRY": "Произошла ошибка. Попробуйте принять политику конфиденциальности еще раз:",
|
||||
"RULES_REQUIRED": "❗️ Для использования сервиса необходимо принять правила!",
|
||||
"RULES_TEXT_DEFAULT": "📋 <b>Правила использования сервиса</b>\n\n1. Запрещено использовать сервис для противоправной деятельности\n2. Не распространяйте пиратский или вредоносный контент\n3. Запрещены спам и фишинг\n4. Нельзя использовать сервис для DDoS-атак\n5. Один аккаунт предназначен для одного пользователя\n6. Возвраты возможны только в исключительных случаях\n7. Администрация может заблокировать аккаунт при нарушении правил\n\n<b>Используя сервис, вы подтверждаете согласие с этими правилами.</b>",
|
||||
"PRIVACY_POLICY_TEXT_DEFAULT": "🔒 <b>Политика конфиденциальности</b>\n\nМы обязуемся защищать вашу конфиденциальность и личные данные.\n\n<b>Сбор данных:</b>\n• Мы собираем только необходимую информацию для предоставления услуг\n• Данные используются исключительно для работы сервиса\n\n<b>Защита данных:</b>\n• Ваши данные защищены современными методами шифрования\n• Мы не передаем данные третьим лицам без вашего согласия\n\n<b>Ваши права:</b>\n• Вы можете запросить информацию о хранимых данных\n• Вы можете запросить удаление ваших данных\n\n<b>Используя сервис, вы соглашаетесь с политикой конфиденциальности.</b>",
|
||||
"SELECT_COUNTRIES": "Выберите страны:",
|
||||
"SELECT_DEVICES": "Количество устройств:",
|
||||
"SELECT_PERIOD": "Выберите период:",
|
||||
|
||||
@@ -234,6 +234,19 @@ def _get_default_rules(language: str = DEFAULT_LANGUAGE) -> str:
|
||||
return fallback.get(default_key, "")
|
||||
|
||||
|
||||
def _get_default_privacy_policy(language: str = DEFAULT_LANGUAGE) -> str:
|
||||
default_key = "PRIVACY_POLICY_TEXT_DEFAULT"
|
||||
locale = load_locale(language)
|
||||
if default_key in locale:
|
||||
return locale[default_key]
|
||||
fallback = load_locale(DEFAULT_LANGUAGE)
|
||||
return fallback.get(default_key, "")
|
||||
|
||||
|
||||
def get_privacy_policy(language: str = DEFAULT_LANGUAGE) -> str:
|
||||
return _get_default_privacy_policy(language)
|
||||
|
||||
|
||||
def get_rules_sync(language: str = DEFAULT_LANGUAGE) -> str:
|
||||
if language in _cached_rules:
|
||||
return _cached_rules[language]
|
||||
|
||||
@@ -118,6 +118,7 @@ class AuthMiddleware(BaseMiddleware):
|
||||
registration_states = [
|
||||
RegistrationStates.waiting_for_language.state,
|
||||
RegistrationStates.waiting_for_rules_accept.state,
|
||||
RegistrationStates.waiting_for_privacy_policy_accept.state,
|
||||
RegistrationStates.waiting_for_referral_code.state
|
||||
]
|
||||
|
||||
@@ -128,7 +129,7 @@ class AuthMiddleware(BaseMiddleware):
|
||||
isinstance(event, CallbackQuery)
|
||||
and event.data
|
||||
and (
|
||||
event.data in ['rules_accept', 'rules_decline', 'referral_skip']
|
||||
event.data in ['rules_accept', 'rules_decline', 'privacy_policy_accept', 'privacy_policy_decline', 'referral_skip']
|
||||
or event.data.startswith('language_select:')
|
||||
)
|
||||
)
|
||||
|
||||
@@ -3,6 +3,7 @@ from aiogram.fsm.state import State, StatesGroup
|
||||
class RegistrationStates(StatesGroup):
|
||||
waiting_for_language = State()
|
||||
waiting_for_rules_accept = State()
|
||||
waiting_for_privacy_policy_accept = State()
|
||||
waiting_for_referral_code = State()
|
||||
|
||||
class SubscriptionStates(StatesGroup):
|
||||
|
||||
@@ -10,12 +10,15 @@ def is_registration_process(event: TelegramObject, current_state: Optional[str])
|
||||
registration_states = [
|
||||
RegistrationStates.waiting_for_language.state,
|
||||
RegistrationStates.waiting_for_rules_accept.state,
|
||||
RegistrationStates.waiting_for_privacy_policy_accept.state,
|
||||
RegistrationStates.waiting_for_referral_code.state
|
||||
]
|
||||
|
||||
registration_callbacks = [
|
||||
"rules_accept",
|
||||
"rules_decline",
|
||||
"privacy_policy_accept",
|
||||
"privacy_policy_decline",
|
||||
"referral_skip"
|
||||
]
|
||||
|
||||
|
||||
Reference in New Issue
Block a user