# region: libraries from telegram import Bot, Update, InlineKeyboardButton, InlineKeyboardMarkup from telegram.ext import Application, CallbackQueryHandler, MessageHandler, ContextTypes, filters import re # endregion # -------------------- # -------------------- # region: authorize TOKEN = "8198757888:AAHNrH8HDJjHWUI6hjscpwGQYwKdP5xdQPM" TARGET_CHANNEL_ID = "https://t.me/+yfGMMjpOEzw0YzYy" # endregion # -------------------- # -------------------- # region: phrases in the "direction" section FORM_MESSAGES = [ "Выберите направление:", "Пожалуйста, укажите какие страны вы планируете посетить:\n- Страна/страны\n- На какой срок вам необходима виза\n- Как к вам можно обращаться?\n- Ваш номер телефона, E-mail, Имя пользователя в соц. сетях\n- Удобный вариант для связи с вами (Telegram, WhatsApp, VK, E-Mail)", "Выберите один из вопросов:\n- Корпоративные клиенты\n- Когда и как я получу документы на тур\n- Возможно ли изменение цены тура\n- Могу ли я отменить тур и как происходит возврат денежных средств\n- Защита персональных данных", "Компания «MaWay Travel Agency» предлагает как пакетные туры, так и авторские программы. Пожалуйста, отправьте ваши данные:\n- Как к вам можно обращаться?\n- Ваш номер телефона, E-mail, Имя пользователя в соц. сетях\n- Удобный вариант для связи с вами (Telegram, WhatsApp, VK, E-Mail)" ] # endregion # region: FAQ question and answer FAQ_RESPONSES = { "Корпоративные клиенты": "Компания MaWay Travel оказывает услуги по организации и сопровождению деловых поездок. Мы обеспечиваем надежное качество, заботимся о комфорте клиентов во время путешествий, помогаем оптимизировать затраты на деловые поездки и выездные корпоративные мероприятия.", "Когда и как я получу документы на тур": "После того, как ваш тур забронирован, на почту придет подтверждение от вашего персонального менеджера. Первый документ – это договор, в котором прописаны основные параметры тура.", "Возможно ли изменение цены тура": "Да, в неоплаченной части тура возможны изменения. Подробные условия по каждому направлению указываются в договоре.", "Могу ли я отменить тур и как происходит возврат денежных средств": "Да, вы можете отменить тур в соответствии с условиями возврата денежных средств, указанными в договоре.", "Защита персональных данных": "В целях выполнения договорных обязательств для организации вашего тура нам могут понадобиться ваши паспортные данные, а также информация о ваших диетических предпочтениях и медицинских ограничениях. Защита персональных данных является важной задачей для нашей компании." } # endregion # navigation through the context menu DIRECTION_NAMES = { "0": "Пляжный отдых", "1": "Приключения в горах", "2": "Круизы", "3": "Экскурсионные туры", "4": "Путешествия по России", "5": "Специальные предложения с ранним бронированием", "6": "Специальное предложение с горящими турами" } DIRECTION_TEXTS = { "0": ( "Вы выбрали Пляжный отдых.\n\n" "Пожалуйста, отправьте данные по следующему шаблону:\n" "1. Желаемая продолжительность отдыха\n" "2. Состав участников (есть ли дети, указать возраст)\n" "3. Желаемые границы бюджета\n" "4. Количество времени в пути к отдыху мечты\n" "5. Особые пожелания (по проживанию, питанию, виду из номера и т.д.)\n" "6. Ваш номер телефона, E-mail, Имя пользователя в соц. сетях\n" "7. Как с вами связаться (номер телефона, email, мессенджеры)" ), "1": ( "Вы выбрали Приключения в горах.\n\n" "Пожалуйста, отправьте данные по следующему шаблону:\n" "1. Уровень экстрима (прогулки/экскурсии/восхождения)\n" "2. Количество человек\n" "3. Особые пожелания (по питанию/проживанию/виду из номера и любые другие)\n" "4. Как к вам можно обращаться?\n" "5. Ваш номер телефона, E-mail, Имя пользователя в соц. сетях\n" "6. Как с вами связаться (номер телефона, email, мессенджеры)" ), "2": ( "Вы выбрали Круизы.\n\n" "Пожалуйста, отправьте данные по следующему шаблону:\n" "1. Состав участников путешествия (есть ли дети, указать кол-во и возраст)\n" "2. Желаемые границы бюджета\n" "3. Количество времени в пути к отдыху мечты\n" "4. Особые пожелания (по питанию/проживанию/виду из номера и любые другие)\n" "5. Как к вам можно обращаться?\n" "6. Ваш номер телефона, E-mail, Имя пользователя в соц. сетях\n" "7. Как с вами связаться (номер телефона, email, мессенджеры)" ), "3": ( "Вы выбрали Экскурсионные туры.\n\n" "Пожалуйста, отправьте данные по следующему шаблону:\n" "1. Россия/зарубежный тур\n" "2. Желаемая продолжительность тура\n" "3. Состав участников (количество человек, возраст)\n" "4. Желаемые границы бюджета\n" "5. Количество времени в пути к отдыху мечты\n" "6. Особые пожелания (по питанию/проживанию/виду из номера и любые другие)\n" "7. Как к вам можно обращаться?\n" "8. Ваш номер телефона, E-mail, Имя пользователя в соц. сетях\n" "9. Как с вами связаться (номер телефона, email, мессенджеры)" ), "4": ( "Вы выбрали Путешествия по России.\n\n" "Пожалуйста, отправьте данные по следующему шаблону:\n" "1. Желаемая продолжительность\n" "2. Состав участников путешествия (есть ли дети, указать кол-во и возраст)\n" "3. Желаемые границы бюджета\n" "4. Количество времени в пути к отдыху мечты\n" "5. Как к вам можно обращаться?\n" "6. Ваш номер телефона, E-mail, Имя пользователя в соц. сетях\n" "7. Как с вами связаться (номер телефона, email, мессенджеры)" ), "5": ( "Вы выбрали Специальные предложения с ранним бронированием.\n\n" "Пожалуйста, отправьте данные по следующему шаблону:\n" "1. Как к вам можно обращаться?\n" "2. Ваш номер телефона, E-mail, Имя пользователя в соц. сетях\n" "3. Как с вами связаться (номер телефона, email, мессенджеры)" ), "6": ( "Вы выбрали Специальное предложение с горящими турами.\n\n" "Пожалуйста, отправьте данные по следующему шаблону:\n" "1. Как к вам можно обращаться?\n" "2. Ваш номер телефона, E-mail, Имя пользователя в соц. сетях\n" "3. Как с вами связаться (номер телефона, email, мессенджеры)" ), } # endregion # -------------------- # -------------------- # region: showing the menu async def display_main_menu(update: Update, context: ContextTypes.DEFAULT_TYPE): keyboard = [ [InlineKeyboardButton("Направления", callback_data="form_0")], [InlineKeyboardButton("Визы", callback_data="form_1")], [InlineKeyboardButton("FAQ", callback_data="form_2")], [InlineKeyboardButton("Оставить заявку", callback_data="form_3")], ] reply_markup = InlineKeyboardMarkup(keyboard) if update.callback_query: query = update.callback_query await query.answer() await query.delete_message() await query.message.reply_text("Выберите одну из форм:", reply_markup=reply_markup) else: await update.message.reply_text("Выберите одну из форм:", reply_markup=reply_markup) # endregion # region: keyboard data def get_form_keyboard(form_type): keyboard = [] if form_type == "direction": keyboard = [ [InlineKeyboardButton("Пляжный отдых", callback_data="direction_0")], [InlineKeyboardButton("Приключения в горах", callback_data="direction_1")], [InlineKeyboardButton("Круизы", callback_data="direction_2")], [InlineKeyboardButton("Экскурсионные туры", callback_data="direction_3")], [InlineKeyboardButton("Путешествия по России", callback_data="direction_4")], [InlineKeyboardButton("Специальные предложения с ранним бронированием", callback_data="direction_5")], [InlineKeyboardButton("Специальное предложение с горящими турами", callback_data="direction_6")], ] keyboard.append([InlineKeyboardButton("Вернуться к разделам", callback_data="back")]) return InlineKeyboardMarkup(keyboard) # endregion # region: menu cache async def button_handler(update: Update, context: ContextTypes.DEFAULT_TYPE): query = update.callback_query await query.answer() form_index = int(query.data.split("_")[1]) history = context.user_data.get("history", []) history.append(form_index) context.user_data["history"] = history context.user_data["current_form"] = form_index if form_index == 0: await query.delete_message() await query.message.reply_text( text=FORM_MESSAGES[form_index], reply_markup=get_form_keyboard("direction") ) elif form_index == 1: await query.delete_message() await query.message.reply_text( text=FORM_MESSAGES[form_index], reply_markup=get_form_keyboard("visa") ) elif form_index == 2: await query.delete_message() await show_faq_options(update, context) elif form_index == 3: await query.delete_message() await query.message.reply_text( text=FORM_MESSAGES[form_index], reply_markup=get_form_keyboard("application") ) # endregion # region: showing sections async def show_direction_options(update: Update, context: ContextTypes.DEFAULT_TYPE): keyboard = [ [InlineKeyboardButton("Пляжный отдых", callback_data="direction_0")], [InlineKeyboardButton("Приключения в горах", callback_data="direction_1")], [InlineKeyboardButton("Круизы", callback_data="direction_2")], [InlineKeyboardButton("Экскурсионные туры", callback_data="direction_3")], [InlineKeyboardButton("Путешествия по России", callback_data="direction_4")], [InlineKeyboardButton("Специальные предложения с ранним бронированием", callback_data="direction_5")], [InlineKeyboardButton("Специальное предложение с горящими турами", callback_data="direction_6")], [InlineKeyboardButton("Назад", callback_data="back_to_main")], ] reply_markup = InlineKeyboardMarkup(keyboard) await update.callback_query.message.edit_text("Выберите направление:", reply_markup=reply_markup) # endregion # region: answers to the faq async def show_faq_options(update: Update, context: ContextTypes.DEFAULT_TYPE): keyboard = [ [InlineKeyboardButton("Корпоративные клиенты", callback_data="faq_0")], [InlineKeyboardButton("Когда и как я получу документы на тур", callback_data="faq_1")], [InlineKeyboardButton("Возможно ли изменение цены тура", callback_data="faq_2")], [InlineKeyboardButton("Могу ли я отменить тур", callback_data="faq_3")], [InlineKeyboardButton("Защита персональных данных", callback_data="faq_4")], ] reply_markup = InlineKeyboardMarkup(keyboard) await update.callback_query.message.reply_text("Выберите вопрос:", reply_markup=reply_markup) # endregion # region: updating subcategories async def direction_handler(update: Update, context: ContextTypes.DEFAULT_TYPE): query = update.callback_query await query.answer() direction = query.data.split("_")[1] direction_name = DIRECTION_NAMES.get(direction, "Неизвестное направление") context.user_data["direction_name"] = direction_name direction_text = DIRECTION_TEXTS.get(direction, "Выбрано неизвестное направление.") keyboard = [[InlineKeyboardButton("Назад", callback_data="back_to_directions")]] reply_markup = InlineKeyboardMarkup(keyboard) await query.message.edit_text(direction_text, reply_markup=reply_markup) # endregion # -------------------- # -------------------- # region: step back async def back_to_directions_handler(update: Update, context: ContextTypes.DEFAULT_TYPE): query = update.callback_query await query.answer() await show_direction_options(update, context) async def back_to_main_handler(update: Update, context: ContextTypes.DEFAULT_TYPE): query = update.callback_query await query.answer() await display_main_menu(update, context) # endregion # -------------------- # -------------------- # region: faq step async def faq_handler(update: Update, context: ContextTypes.DEFAULT_TYPE): query = update.callback_query await query.answer() faq_index = int(query.data.split("_")[1]) faq_question = list(FAQ_RESPONSES.keys())[faq_index] faq_response = FAQ_RESPONSES[faq_question] keyboard = [ [InlineKeyboardButton("Вернуться к вопросам FAQ", callback_data="form_2")], [InlineKeyboardButton("Вернуться к разделам", callback_data="back")] ] reply_markup = InlineKeyboardMarkup(keyboard) await query.message.edit_text(f"{faq_response}", reply_markup=reply_markup) # endregion # region: sending a message to a group async def message_handler(update: Update, context: ContextTypes.DEFAULT_TYPE): fixed_bug = False user_message = update.message.text current_form = context.user_data.get("current_form") user = update.message.from_user user_profile_link = f"@{user.username}" if user.username else f"ID: {user.id}" if current_form is None: await update.message.reply_text("Выберите форму из доступных вариантов.") return if current_form == 1: context.user_data["visa_data"] = user_message elif current_form == 3: context.user_data["application_data"] = user_message elif current_form == 0: direction_name = context.user_data.get("direction_name", "error") if direction_name == "error": await update.message.reply_text("Выберите направление перед отправкой заявки.") await display_main_menu(update, context) return context.user_data["direction_data"] = user_message if "visa_data" in context.user_data: user_data = context.user_data["visa_data"] message_to_send = f"Новая заявка на визу от {user_profile_link}:\n{user_data}" elif "application_data" in context.user_data: user_data = context.user_data["application_data"] message_to_send = f"Новая заявка от {user_profile_link}:\n{user_data}" elif "direction_data" in context.user_data: direction_name = context.user_data.get("direction_name", "error") user_data = context.user_data["direction_data"] message_to_send = f"Новая заявка на *{direction_name}* от {user_profile_link}:\n{user_data}" else: fixed_bug = True #message_to_send = f"неккоректно от {user_profile_link}" try: if not fixed_bug: # if direction_name == "error": # await update.message.reply_text("Выберите одно из направлений.") # await display_main_menu(update, context) # return await context.bot.send_message(chat_id=TARGET_CHANNEL_ID, text=message_to_send, parse_mode="Markdown") except Exception as error: # print(f"Error when sending a message: {error}") await update.message.reply_text("Убедитесь, что Вы выбрали форму.") try: if direction_name == "error": await update.message.reply_text("Выберите одно из направлений.") await display_main_menu(update, context) else: await update.message.reply_text("Спасибо за обращение! Менеджер команды MAWAY вернется к вам с информацией по доступным направлениям.") context.user_data.clear() await display_main_menu(update, context) except Exception as error: print(f"Error at shutdown: {error}") context.user_data.clear() await display_main_menu(update, context) # endregion # region: return to step back async def return_to_faq(update: Update, context: ContextTypes.DEFAULT_TYPE): query = update.callback_query await query.answer() keyboard = [ [InlineKeyboardButton("Корпоративные клиенты", callback_data="faq_0")], [InlineKeyboardButton("Когда и как я получу документы на тур", callback_data="faq_1")], [InlineKeyboardButton("Возможно ли изменение цены тура", callback_data="faq_2")], [InlineKeyboardButton("Могу ли я отменить тур", callback_data="faq_3")], [InlineKeyboardButton("Защита персональных данных", callback_data="faq_4")], ] reply_markup = InlineKeyboardMarkup(keyboard) await query.message.edit_text("Выберите один из вопросов FAQ:", reply_markup=reply_markup) # endregion # region: return to the previous context async def back_handler(update: Update, context: ContextTypes.DEFAULT_TYPE): query = update.callback_query await query.answer() history = context.user_data.get("history", []) if len(history) <= 1: await display_main_menu(update, context) return history.pop() previous_form = history[-1] context.user_data["history"] = history if previous_form == 0 or previous_form == 1 or previous_form == 2 or previous_form == 3: await display_main_menu(update, context) else: await display_main_menu(update, context) # endregion # -------------------- # -------------------- # region: callbacks async def start(update: Update, context: ContextTypes.DEFAULT_TYPE): await display_main_menu(update, context) if __name__ == "__main__": application = Application.builder().token(TOKEN).build() application.add_handler(MessageHandler(filters.COMMAND, start)) application.add_handler(CallbackQueryHandler(button_handler, pattern="^form_\\d$")) application.add_handler(CallbackQueryHandler(direction_handler, pattern="^direction_\\d$")) application.add_handler(CallbackQueryHandler(faq_handler, pattern="^faq_\\d$")) application.add_handler(CallbackQueryHandler(back_to_directions_handler, pattern="^back_to_directions$")) application.add_handler(CallbackQueryHandler(back_to_main_handler, pattern="^back_to_main$")) application.add_handler(CallbackQueryHandler(return_to_faq, pattern="^form_2$")) application.add_handler(CallbackQueryHandler(back_handler, pattern="^back$")) application.add_handler(MessageHandler(filters.TEXT & ~filters.COMMAND, message_handler)) application.run_polling() # endregion