# 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