feat(subscription): add "subscribe all" and "unsubscribe all" buttons

feat(subscription): add check on unsubscribe to notify user if no active subscriptions

Signed-off-by: UdoChudo <stream@udochudo.ru>
This commit is contained in:
Udo Chudo 2025-06-19 23:52:48 +05:00
parent 55510a4379
commit 60f77b39eb
5 changed files with 152 additions and 23 deletions

View File

@ -1,13 +1,13 @@
from . import subscribe, active_triggers
from . import unsubscribe
from . import my_subscriptions
from . import cancel_input
from . import notification_switch_mode
from . import debug
from . import help
from . import my_subscriptions
from . import notification_switch_mode
from . import registration
from . import settings
from . import start
from . import debug
from . import subscribe, active_triggers
from . import unsubscribe
from ..states import UserStateManager
state_manager = UserStateManager()
@ -31,4 +31,6 @@ def register_handlers(bot, app):
def register_callbacks(bot, app):
notification_switch_mode.register_callback_notification(bot, app, state_manager)
active_triggers.register_callbacks_active_triggers(bot, app, state_manager)
cancel_input.register_callback_cancel_input(bot,state_manager)
cancel_input.register_callback_cancel_input(bot,state_manager)
subscribe.register_callback_subscribe(bot, app, state_manager)
unsubscribe.register_callback_unsubscribe(bot, app, state_manager)

View File

@ -1,13 +1,13 @@
from telebot.types import Message, InlineKeyboardMarkup, InlineKeyboardButton
from telebot import TeleBot, logger
from telebot.types import Message, InlineKeyboardMarkup, InlineKeyboardButton, CallbackQuery
from app import Subscriptions
from app.bot.constants import UserStates
from app.bot.processors.subscribe_processor import process_subscription_button
from app.bot.processors.subscribe_processor import process_subscription_button, process_subscribe_all_regions
from app.bot.states import UserStateManager
from app.bot.utils.auth import auth
from app.bot.utils.regions import get_sorted_regions, format_regions_list, format_regions_list_marked
from telebot import TeleBot, logger
from app.bot.utils.regions import get_sorted_regions, format_regions_list_marked
def register_handlers(bot: TeleBot, app, state_manager: UserStateManager):
@ -36,6 +36,7 @@ def register_handlers(bot: TeleBot, app, state_manager: UserStateManager):
regions_text = format_regions_list_marked(regions, subscribed)
markup = InlineKeyboardMarkup()
markup.add(InlineKeyboardButton("Подписаться на все регионы", callback_data="subscribe_all"))
markup.add(InlineKeyboardButton(text="Отмена", callback_data="cancel_input"))
bot_message = bot.send_message(chat_id,
@ -44,3 +45,17 @@ def register_handlers(bot: TeleBot, app, state_manager: UserStateManager):
bot.register_next_step_handler(message, process_subscription_button, app, bot, chat_id, state_manager, bot_message.message_id)
def register_callback_subscribe(bot: TeleBot, app, state_manager):
@bot.callback_query_handler(func=lambda call: call.data == "subscribe_all")
def handle_subscribe_all_button(call: CallbackQuery):
chat_id = call.message.chat.id
username = f"{call.from_user.username}" if call.from_user.username else "N/A"
with app.app_context():
if not auth(chat_id, app):
bot.send_message(chat_id, "Вы не авторизованы для использования этого бота.")
logger.warning(f"Неавторизованный пользователь {chat_id} @{username}")
state_manager.set_state(chat_id, UserStates.REGISTRATION)
return
process_subscribe_all_regions(call, app, bot, chat_id, state_manager)

View File

@ -1,10 +1,12 @@
from telebot.types import InlineKeyboardMarkup, InlineKeyboardButton
from telebot import logger
from telebot import logger, TeleBot
from telebot.types import InlineKeyboardMarkup, InlineKeyboardButton, CallbackQuery
from app.bot.constants import UserStates
from app.bot.keyboards.settings_menu import get_settings_menu
from app.bot.processors.unsubscribe_processor import process_unsubscribe_button, process_unsubscribe_all_regions
from app.bot.utils.auth import auth
from app.bot.utils.helpers import get_user_subscribed_regions
from app.bot.utils.regions import format_regions_list
from app.bot.processors.unsubscribe_processor import process_unsubscribe_button
def register_handlers(bot, app, state_manager):
@ -14,22 +16,48 @@ def register_handlers(bot, app, state_manager):
with app.app_context():
chat_id = message.chat.id
username = f"{message.from_user.username}" if message.from_user.username else "N/A"
if not auth(chat_id, app):
bot.send_message(chat_id, "Вы не авторизованы для использования этого бота.")
logger.warning(f"Неавторизованный пользователь {chat_id} @{username}")
state_manager.set_state(chat_id, UserStates.REGISTRATION)
return
else:
state_manager.set_state(chat_id, UserStates.WAITING_INPUT)
user_subscriptions = get_user_subscribed_regions(chat_id)
# ✅ Предварительная проверка: есть ли подписки
if not user_subscriptions:
bot.send_message(chat_id, " У вас нет активных подписок для отписки.",reply_markup=get_settings_menu())
state_manager.set_state(chat_id, UserStates.SETTINGS_MENU)
return
# Есть подписки — предлагаем меню отписки
state_manager.set_state(chat_id, UserStates.WAITING_INPUT)
formated_user_subscriptions = format_regions_list(user_subscriptions)
markup = InlineKeyboardMarkup()
markup.add(InlineKeyboardButton("Отписаться от всех регионов", callback_data="unsubscribe_all"))
markup.add(InlineKeyboardButton("Отмена", callback_data="cancel_input"))
bot.send_message(chat_id,
f"Введите номер(а) региона(ов) через запятую подписки которых вы хотите удалить:\n\n{formated_user_subscriptions}",
f"Введите номер(а) региона(ов) через запятую, от которых вы хотите отписаться:\n\n{formated_user_subscriptions}",
reply_markup=markup)
bot.register_next_step_handler(message, process_unsubscribe_button, app, bot, chat_id, state_manager)
bot.register_next_step_handler(message, process_unsubscribe_button, app, bot, chat_id, state_manager)
def register_callback_unsubscribe(bot: TeleBot, app, state_manager):
@bot.callback_query_handler(func=lambda call: call.data == "unsubscribe_all")
def handle_subscribe_all_button(call: CallbackQuery):
chat_id = call.message.chat.id
username = f"{call.from_user.username}" if call.from_user.username else "N/A"
with app.app_context():
if not auth(chat_id, app):
bot.send_message(chat_id, "Вы не авторизованы для использования этого бота.")
logger.warning(f"Неавторизованный пользователь {chat_id} @{username}")
state_manager.set_state(chat_id, UserStates.REGISTRATION)
return
process_unsubscribe_all_regions(call, app, bot, state_manager)

View File

@ -75,3 +75,50 @@ def process_subscription_button(message: Message, app, bot, chat_id: int, state_
# Показываем меню
bot.send_message(chat_id, f"✅ Подписка на регионы: {', '.join(subbed_regions)} оформлена.", reply_markup=get_settings_menu())
def process_subscribe_all_regions(call, app, bot, chat_id: int, state_manager):
try:
with app.app_context():
username = f"{call.from_user.username}" if call.from_user.username else "N/A"
# Получаем все активные регионы
active_regions = Regions.query.filter_by(active=True).all()
active_region_ids = {r.region_id for r in active_regions}
# Получаем уже подписанные регионы
existing_subs = Subscriptions.query.filter_by(chat_id=chat_id).all()
existing_region_ids = {sub.region_id for sub in existing_subs if sub.active}
newly_added = []
for region in active_regions:
if region.region_id in existing_region_ids:
continue
existing = next((sub for sub in existing_subs if sub.region_id == region.region_id), None)
if existing:
existing.active = True
db.session.add(existing)
else:
new_sub = Subscriptions(chat_id=chat_id, region_id=region.region_id, active=True)
db.session.add(new_sub)
newly_added.append(str(region.region_id))
db.session.commit()
# Логирование действия
if newly_added:
log_user_event(chat_id, app, username, f"Подписался на все регионы: {', '.join(newly_added)}")
bot.answer_callback_query(call.id)
bot.clear_step_handler_by_chat_id(chat_id)
bot.delete_message(chat_id, call.message.message_id)
bot.send_message(chat_id,
"✅ Вы подписались на все доступные регионы.",
reply_markup=get_settings_menu())
state_manager.set_state(chat_id, UserStates.SETTINGS_MENU)
except Exception as e:
bot.send_message(chat_id, "⚠️ Не удалось выполнить подписку. Попробуйте позже.")

View File

@ -1,13 +1,15 @@
from flask import Flask
from telebot import TeleBot, logger
from telebot.types import Message, InlineKeyboardMarkup, InlineKeyboardButton
from app.bot.keyboards.settings_menu import get_settings_menu
from app.bot.utils.helpers import get_user_subscribed_regions
from telebot.types import Message, InlineKeyboardMarkup, InlineKeyboardButton, CallbackQuery
from app import Subscriptions
from app.bot.constants import UserStates
from app.bot.keyboards.settings_menu import get_settings_menu
from app.bot.states import UserStateManager
from app.bot.utils.helpers import get_user_subscribed_regions
from app.bot.utils.tg_audit import log_user_event
from app.extensions.db import db
from app.bot.states import UserStateManager
from app.bot.constants import UserStates
def process_unsubscribe_button(message: Message, app: Flask, bot: TeleBot, chat_id: int, state_manager: UserStateManager):
unsubbed_regions = []
@ -62,4 +64,39 @@ def process_unsubscribe_button(message: Message, app: Flask, bot: TeleBot, chat_
f"⚠ Регионы с ID {', '.join(invalid_regions)} не найдены среди ваших подписок и не были изменены.")
state_manager.set_state(chat_id, UserStates.SETTINGS_MENU)
bot.send_message(chat_id, "⚙ Вернулись в меню настроек.", reply_markup=get_settings_menu())
bot.send_message(chat_id, "⚙ Вернулись в меню настроек.", reply_markup=get_settings_menu())
def process_unsubscribe_all_regions(call: CallbackQuery, app: Flask, bot: TeleBot, state_manager: UserStateManager):
chat_id = call.message.chat.id
username = f"{call.from_user.username}" if call.from_user.username else "N/A"
try:
with app.app_context():
subscriptions = Subscriptions.query.filter_by(chat_id=chat_id, active=True).all()
if not subscriptions:
bot.answer_callback_query(call.id, text="У вас нет активных подписок.")
return
for sub in subscriptions:
sub.active = False
db.session.add(sub)
db.session.commit()
log_user_event(chat_id, app, username, "Отписался от всех регионов")
bot.answer_callback_query(call.id)
bot.clear_step_handler_by_chat_id(chat_id)
bot.delete_message(chat_id, call.message.message_id)
bot.send_message(chat_id,
"✅ Вы успешно отписались от всех регионов.",
reply_markup=get_settings_menu())
state_manager.set_state(chat_id, UserStates.SETTINGS_MENU)
except Exception as e:
state_manager.set_state(chat_id, UserStates.SETTINGS_MENU)
bot.send_message(chat_id, "⚠ Произошла ошибка при отписке. Попробуйте позже.",reply_markup=get_settings_menu())
return