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 cancel_input
from . import notification_switch_mode from . import debug
from . import help from . import help
from . import my_subscriptions
from . import notification_switch_mode
from . import registration from . import registration
from . import settings from . import settings
from . import start from . import start
from . import debug from . import subscribe, active_triggers
from . import unsubscribe
from ..states import UserStateManager from ..states import UserStateManager
state_manager = UserStateManager() state_manager = UserStateManager()
@ -31,4 +31,6 @@ def register_handlers(bot, app):
def register_callbacks(bot, app): def register_callbacks(bot, app):
notification_switch_mode.register_callback_notification(bot, app, state_manager) notification_switch_mode.register_callback_notification(bot, app, state_manager)
active_triggers.register_callbacks_active_triggers(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 import Subscriptions
from app.bot.constants import UserStates 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.states import UserStateManager
from app.bot.utils.auth import auth from app.bot.utils.auth import auth
from app.bot.utils.regions import get_sorted_regions, format_regions_list, format_regions_list_marked from app.bot.utils.regions import get_sorted_regions, format_regions_list_marked
from telebot import TeleBot, logger
def register_handlers(bot: TeleBot, app, state_manager: UserStateManager): 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) regions_text = format_regions_list_marked(regions, subscribed)
markup = InlineKeyboardMarkup() markup = InlineKeyboardMarkup()
markup.add(InlineKeyboardButton("Подписаться на все регионы", callback_data="subscribe_all"))
markup.add(InlineKeyboardButton(text="Отмена", callback_data="cancel_input")) markup.add(InlineKeyboardButton(text="Отмена", callback_data="cancel_input"))
bot_message = bot.send_message(chat_id, 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) 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, TeleBot
from telebot import logger from telebot.types import InlineKeyboardMarkup, InlineKeyboardButton, CallbackQuery
from app.bot.constants import UserStates 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.auth import auth
from app.bot.utils.helpers import get_user_subscribed_regions from app.bot.utils.helpers import get_user_subscribed_regions
from app.bot.utils.regions import format_regions_list 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): def register_handlers(bot, app, state_manager):
@ -14,22 +16,48 @@ def register_handlers(bot, app, state_manager):
with app.app_context(): with app.app_context():
chat_id = message.chat.id chat_id = message.chat.id
username = f"{message.from_user.username}" if message.from_user.username else "N/A" username = f"{message.from_user.username}" if message.from_user.username else "N/A"
if not auth(chat_id, app): if not auth(chat_id, app):
bot.send_message(chat_id, "Вы не авторизованы для использования этого бота.") bot.send_message(chat_id, "Вы не авторизованы для использования этого бота.")
logger.warning(f"Неавторизованный пользователь {chat_id} @{username}") logger.warning(f"Неавторизованный пользователь {chat_id} @{username}")
state_manager.set_state(chat_id, UserStates.REGISTRATION) state_manager.set_state(chat_id, UserStates.REGISTRATION)
return return
else:
state_manager.set_state(chat_id, UserStates.WAITING_INPUT)
user_subscriptions = get_user_subscribed_regions(chat_id) 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) formated_user_subscriptions = format_regions_list(user_subscriptions)
markup = InlineKeyboardMarkup() markup = InlineKeyboardMarkup()
markup.add(InlineKeyboardButton("Отписаться от всех регионов", callback_data="unsubscribe_all"))
markup.add(InlineKeyboardButton("Отмена", callback_data="cancel_input")) markup.add(InlineKeyboardButton("Отмена", callback_data="cancel_input"))
bot.send_message(chat_id, bot.send_message(chat_id,
f"Введите номер(а) региона(ов) через запятую подписки которых вы хотите удалить:\n\n{formated_user_subscriptions}", f"Введите номер(а) региона(ов) через запятую, от которых вы хотите отписаться:\n\n{formated_user_subscriptions}",
reply_markup=markup) 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()) 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 flask import Flask
from telebot import TeleBot, logger from telebot import TeleBot, logger
from telebot.types import Message, InlineKeyboardMarkup, InlineKeyboardButton from telebot.types import Message, InlineKeyboardMarkup, InlineKeyboardButton, CallbackQuery
from app.bot.keyboards.settings_menu import get_settings_menu
from app.bot.utils.helpers import get_user_subscribed_regions
from app import Subscriptions 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.bot.utils.tg_audit import log_user_event
from app.extensions.db import db 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): def process_unsubscribe_button(message: Message, app: Flask, bot: TeleBot, chat_id: int, state_manager: UserStateManager):
unsubbed_regions = [] unsubbed_regions = []
@ -62,4 +64,39 @@ def process_unsubscribe_button(message: Message, app: Flask, bot: TeleBot, chat_
f"⚠ Регионы с ID {', '.join(invalid_regions)} не найдены среди ваших подписок и не были изменены.") f"⚠ Регионы с ID {', '.join(invalid_regions)} не найдены среди ваших подписок и не были изменены.")
state_manager.set_state(chat_id, UserStates.SETTINGS_MENU) 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