You've already forked Travel-Agent
feat: Added travel detail, added proccessing message when changing/setting user location, improvements in messages text
This commit is contained in:
+11
-2
@@ -93,8 +93,17 @@ async def travels_callback(
|
||||
|
||||
await callback.message.answer(
|
||||
messages.TRAVELS,
|
||||
reply_markup=travels_keyboard(travels, page, pages),
|
||||
reply_markup=travels_keyboard(
|
||||
travels,
|
||||
page,
|
||||
pages,
|
||||
user.telegram_id,
|
||||
),
|
||||
)
|
||||
|
||||
await callback.message.delete()
|
||||
try:
|
||||
await callback.message.delete()
|
||||
except TelegramBadRequest:
|
||||
pass
|
||||
|
||||
await callback.answer()
|
||||
|
||||
@@ -152,11 +152,20 @@ async def profile_change_entered(message: Message, state: FSMContext) -> None:
|
||||
elif column == "location":
|
||||
location = value.split(", ")
|
||||
|
||||
proccessing_message = await message.answer(messages.PROCCESSING)
|
||||
|
||||
if len(location) != 2:
|
||||
await handle_validation_error(
|
||||
message,
|
||||
await delete_message_from_state(
|
||||
state,
|
||||
messages.VALIDATION_ERROR,
|
||||
message.chat.id,
|
||||
message.bot,
|
||||
)
|
||||
await proccessing_message.edit_text(messages.VALIDATION_ERROR)
|
||||
await message.delete()
|
||||
|
||||
error_message = proccessing_message
|
||||
await state.update_data(
|
||||
error_message_id=error_message.message_id,
|
||||
)
|
||||
|
||||
return
|
||||
@@ -169,7 +178,18 @@ async def profile_change_entered(message: Message, state: FSMContext) -> None:
|
||||
value=country,
|
||||
)
|
||||
except AssertionError as e:
|
||||
await handle_validation_error(message, state, e)
|
||||
await delete_message_from_state(
|
||||
state,
|
||||
message.chat.id,
|
||||
message.bot,
|
||||
)
|
||||
await proccessing_message.edit_text("❌ " + str(e))
|
||||
await message.delete()
|
||||
|
||||
error_message = proccessing_message
|
||||
await state.update_data(
|
||||
error_message_id=error_message.message_id,
|
||||
)
|
||||
|
||||
return
|
||||
|
||||
@@ -179,7 +199,18 @@ async def profile_change_entered(message: Message, state: FSMContext) -> None:
|
||||
country=validated_country,
|
||||
)
|
||||
except AssertionError as e:
|
||||
await handle_validation_error(message, state, e)
|
||||
await delete_message_from_state(
|
||||
state,
|
||||
message.chat.id,
|
||||
message.bot,
|
||||
)
|
||||
await proccessing_message.edit_text("❌ " + str(e))
|
||||
await message.delete()
|
||||
|
||||
error_message = proccessing_message
|
||||
await state.update_data(
|
||||
error_message_id=error_message.message_id,
|
||||
)
|
||||
|
||||
return
|
||||
|
||||
@@ -204,6 +235,11 @@ async def profile_change_entered(message: Message, state: FSMContext) -> None:
|
||||
"city": state_data["value"][1],
|
||||
},
|
||||
)
|
||||
|
||||
try:
|
||||
await proccessing_message.delete()
|
||||
except TelegramBadRequest:
|
||||
pass
|
||||
else:
|
||||
data = {state_data["column"]: state_data["value"]}
|
||||
user.update(data)
|
||||
|
||||
@@ -9,6 +9,8 @@ from app import messages
|
||||
from app.config import Config
|
||||
from app.filters.user import RegisteredCallback
|
||||
from app.keyboards.builders import travels_keyboard
|
||||
from app.keyboards.travel import get
|
||||
from app.models.travel import Travel
|
||||
from app.models.user import User
|
||||
|
||||
|
||||
@@ -40,5 +42,32 @@ async def travels_callback(callback: CallbackQuery) -> None:
|
||||
|
||||
await callback.message.edit_text(
|
||||
messages.TRAVELS,
|
||||
reply_markup=travels_keyboard(travels, page, pages),
|
||||
reply_markup=travels_keyboard(
|
||||
travels,
|
||||
page,
|
||||
pages,
|
||||
user.telegram_id,
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
@router.callback_query(
|
||||
F.data.startswith("travel_detail"),
|
||||
RegisteredCallback(),
|
||||
StateFilter(None),
|
||||
)
|
||||
async def travel_detail_callback(callback: CallbackQuery) -> None:
|
||||
if callback.data is None or not isinstance(callback.message, Message):
|
||||
return
|
||||
|
||||
travel_id = int(callback.data.replace("travel_detail_", ""))
|
||||
|
||||
travel = Travel().get_travel_by_id(travel_id)
|
||||
|
||||
if not travel:
|
||||
return
|
||||
|
||||
await callback.message.edit_text(
|
||||
travel.get_travel_text(),
|
||||
reply_markup=get(travel_id),
|
||||
)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
__all__ = ("router",)
|
||||
|
||||
from aiogram import F, Router
|
||||
from aiogram.exceptions import TelegramBadRequest
|
||||
from aiogram.filters import CommandStart, StateFilter
|
||||
from aiogram.fsm.context import FSMContext
|
||||
from aiogram.types import Message, ReplyKeyboardRemove
|
||||
@@ -166,13 +167,22 @@ async def location_handler(message: Message, state: FSMContext) -> None:
|
||||
if message.text is None or message.from_user is None:
|
||||
return
|
||||
|
||||
proccessing_message = await message.answer(messages.PROCCESSING)
|
||||
|
||||
location = message.text.strip().split(", ")
|
||||
|
||||
if len(location) != 2:
|
||||
await handle_validation_error(
|
||||
message,
|
||||
await delete_message_from_state(
|
||||
state,
|
||||
messages.VALIDATION_ERROR,
|
||||
message.chat.id,
|
||||
message.bot,
|
||||
)
|
||||
await proccessing_message.edit_text(messages.VALIDATION_ERROR)
|
||||
await message.delete()
|
||||
|
||||
error_message = proccessing_message
|
||||
await state.update_data(
|
||||
error_message_id=error_message.message_id,
|
||||
)
|
||||
|
||||
return
|
||||
@@ -185,7 +195,18 @@ async def location_handler(message: Message, state: FSMContext) -> None:
|
||||
value=country,
|
||||
)
|
||||
except AssertionError as e:
|
||||
await handle_validation_error(message, state, e)
|
||||
await delete_message_from_state(
|
||||
state,
|
||||
message.chat.id,
|
||||
message.bot,
|
||||
)
|
||||
await proccessing_message.edit_text("❌ " + str(e))
|
||||
await message.delete()
|
||||
|
||||
error_message = proccessing_message
|
||||
await state.update_data(
|
||||
error_message_id=error_message.message_id,
|
||||
)
|
||||
|
||||
return
|
||||
|
||||
@@ -195,10 +216,26 @@ async def location_handler(message: Message, state: FSMContext) -> None:
|
||||
country=validated_country,
|
||||
)
|
||||
except AssertionError as e:
|
||||
await handle_validation_error(message, state, e)
|
||||
await delete_message_from_state(
|
||||
state,
|
||||
message.chat.id,
|
||||
message.bot,
|
||||
)
|
||||
await proccessing_message.edit_text("❌ " + str(e))
|
||||
await message.delete()
|
||||
|
||||
error_message = proccessing_message
|
||||
await state.update_data(
|
||||
error_message_id=error_message.message_id,
|
||||
)
|
||||
|
||||
return
|
||||
|
||||
try:
|
||||
await proccessing_message.delete()
|
||||
except TelegramBadRequest:
|
||||
pass
|
||||
|
||||
await delete_message_from_state(state, message.chat.id, message.bot)
|
||||
|
||||
await state.update_data(location=[validated_country, validated_city])
|
||||
|
||||
@@ -32,5 +32,10 @@ async def command_help_handler(message: Message) -> None:
|
||||
|
||||
await message.answer(
|
||||
messages.TRAVELS,
|
||||
reply_markup=travels_keyboard(travels, page, pages),
|
||||
reply_markup=travels_keyboard(
|
||||
travels,
|
||||
page,
|
||||
pages,
|
||||
user.telegram_id,
|
||||
),
|
||||
)
|
||||
|
||||
@@ -16,7 +16,7 @@ def sex_keyboard(choices: str | list):
|
||||
return builder.as_markup(resize_keyboard=True)
|
||||
|
||||
|
||||
def travels_keyboard(travels: list, page: int, pages: int):
|
||||
def travels_keyboard(travels: list, page: int, pages: int, user_id: int):
|
||||
builder = InlineKeyboardBuilder()
|
||||
rows = []
|
||||
|
||||
@@ -24,9 +24,14 @@ def travels_keyboard(travels: list, page: int, pages: int):
|
||||
end_index = min((page + 1) * Config.PAGE_SIZE, len(travels))
|
||||
|
||||
for travel in travels[start_index:end_index]:
|
||||
button_text = travel.title
|
||||
|
||||
if travel.author_id == user_id:
|
||||
button_text += " 👑"
|
||||
|
||||
rows.append(
|
||||
InlineKeyboardButton(
|
||||
text=travel.title,
|
||||
text=button_text,
|
||||
callback_data=f"travel_detail_{travel.id}",
|
||||
),
|
||||
)
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
__all__ = ("get",)
|
||||
|
||||
from aiogram import types
|
||||
from aiogram.utils.keyboard import InlineKeyboardBuilder
|
||||
|
||||
|
||||
def get(travel_id: int):
|
||||
builder = InlineKeyboardBuilder()
|
||||
|
||||
builder.row(
|
||||
types.InlineKeyboardButton(
|
||||
text="📝 Change title",
|
||||
callback_data=f"travel_change_{travel_id}_title",
|
||||
),
|
||||
types.InlineKeyboardButton(
|
||||
text="ℹ️ Change description",
|
||||
callback_data=f"travel_change_{travel_id}_description",
|
||||
),
|
||||
)
|
||||
builder.row(
|
||||
types.InlineKeyboardButton(
|
||||
text="➕ Add location",
|
||||
callback_data=f"travel_add_{travel_id}_location",
|
||||
),
|
||||
types.InlineKeyboardButton(
|
||||
text="➕ Add user",
|
||||
callback_data=f"travel_add_{travel_id}_user",
|
||||
),
|
||||
)
|
||||
builder.row(
|
||||
types.InlineKeyboardButton(
|
||||
text="⬅️",
|
||||
callback_data="menu_travels",
|
||||
),
|
||||
)
|
||||
|
||||
return builder.as_markup()
|
||||
+13
-6
@@ -2,7 +2,7 @@
|
||||
|
||||
MENU = "<b>Menu:</b>"
|
||||
|
||||
TRAVELS = "📃 <b>Travels:</b>"
|
||||
TRAVELS = "📃 <b>Travels:</b>\n<i>👑 - owner</i>"
|
||||
NO_TRAVELS = "No travels yet. You can create one with /create_travel command."
|
||||
CREATE_TRAVEL = (
|
||||
"🧳 Let's create new travel!\n<i>Enter /cancel to cancel creating.</i>"
|
||||
@@ -14,9 +14,14 @@ INPUT_TRAVEL_CALLBACK = (
|
||||
"All right, travel <b>{key}</b> is set to: <b>{value}</b>"
|
||||
)
|
||||
INPUT_TRAVEL_DESCRIPTION = "Enter travel description (enter /skip if you want to skip this step):\n<i>Maximum length: 100 characters</i>"
|
||||
INPUT_TRAVEL_DESCRIPTION_SKIPPED = "Sure. You can always fill it later."
|
||||
INPUT_TRAVEL_DESCRIPTION_SKIPPED = "✅ Sure. You can always fill it later."
|
||||
TRAVEL_CREATED = "Travel <b>{title}</b> successfully created! You can now view and edit it in the travels list (/travels command)."
|
||||
ACTION_CANCELED = "❌ Action canceled"
|
||||
TRAVEL_DETAIL = (
|
||||
"📝 <b>Travel detail</b>\n\n"
|
||||
"\tTitle: <b>{title}</b>\n"
|
||||
"\tDescription: <b>{description}</b>\n"
|
||||
)
|
||||
|
||||
WELCOME_MESSAGE = "Hello, <b>{name}</b>! Welcome to the ✈️ Travel Agent bot! Let's start our journey by filling out some information about you."
|
||||
WELCOME_AGAIN_MESSAGE = "Hello, <b>{name}</b>! Welcome back to the ✈️ Travel Agent bot! If you get lost, you can always call the /help command for assistance."
|
||||
@@ -39,14 +44,14 @@ INPUT_USERNAME = "Enter your username (this will be used to interact with other
|
||||
INPUT_AGE = "Enter your age:\n<i>Range: 13-120</i>"
|
||||
INPUT_SEX = "Enter your sex:\n<i>Options: Male or Female</i>"
|
||||
INPUT_BIO = "Enter your bio (enter /skip if you want to skip this step):\n<i>Maximum length: 100 characters</i>"
|
||||
INPUT_BIO_SKIPPED = "Sure. You can always fill it later."
|
||||
INPUT_BIO_SKIPPED = "✅ Sure. You can always fill it later."
|
||||
INPUT_LOCATION = "Enter your location in this format:\n<i>Format: country, city</i>\n<i>Example: Russia, Moscow</i>"
|
||||
INPUT_CALLBACK = "All right, your <b>{key}</b> is set to: <b>{value}</b>"
|
||||
VALIDATION_ERROR = "Invalid input. Please try again."
|
||||
INPUT_CALLBACK = "✅ All right, your <b>{key}</b> is set to: <b>{value}</b>"
|
||||
VALIDATION_ERROR = "❌ Invalid input. Please try again."
|
||||
CANCEL_CHANGE = "<i>Enter /cancel to cancel change.</i>"
|
||||
|
||||
PROFILE = (
|
||||
"<b>Your profile:</b>\n\n"
|
||||
"<b>👤 Your profile:</b>\n\n"
|
||||
"\tUsername: <b>{username}</b>\n"
|
||||
"\tAge: <b>{age}</b>\n"
|
||||
"\tSex: <b>{sex}</b>\n"
|
||||
@@ -60,3 +65,5 @@ EDIT_USERNAME = "Enter your username:\n<i>Allowed characters: a-z, A-Z, 0-9, _</
|
||||
EDIT_BIO = "Enter your bio (enter /skip if you want to set it to None):\n<i>Maximum length: 100 characters</i>"
|
||||
PROFILE_UPDATED = "✅ Profile updated"
|
||||
CHANGE_CANCELED = "❌ Change canceled"
|
||||
|
||||
PROCCESSING = "⌛️ Processing..."
|
||||
|
||||
+12
-1
@@ -3,7 +3,7 @@ __all__ = ("Travel", "Location")
|
||||
import sqlalchemy as sa
|
||||
from sqlalchemy.orm import relationship, validates
|
||||
|
||||
from app import session
|
||||
from app import messages, session
|
||||
from app.models import Base
|
||||
from app.models.user import User
|
||||
|
||||
@@ -55,6 +55,7 @@ class Travel(Base):
|
||||
@validates("title")
|
||||
def validate_title(self, key, value):
|
||||
assert len(value) <= 30, "Title must be 30 characters or fewer."
|
||||
assert "👑" not in value, "👑 is not allowed symbol."
|
||||
|
||||
if session.query(Travel).filter(Travel.title == value).first():
|
||||
raise AssertionError("This title is already taken.")
|
||||
@@ -70,6 +71,16 @@ class Travel(Base):
|
||||
|
||||
return value
|
||||
|
||||
def get_travel_text(self):
|
||||
return messages.TRAVEL_DETAIL.format(
|
||||
title=self.title,
|
||||
description=self.description,
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def get_travel_by_id(cls, travel_id):
|
||||
return session.query(Travel).filter(Travel.id == travel_id).first()
|
||||
|
||||
|
||||
class Location(Base):
|
||||
__tablename__ = "locations"
|
||||
|
||||
+1
-1
@@ -56,7 +56,7 @@ async def handle_validation_error(
|
||||
message.bot,
|
||||
)
|
||||
|
||||
error_message = await message.answer(str(e))
|
||||
error_message = await message.answer("❌ " + str(e))
|
||||
await state.update_data(
|
||||
error_message_id=error_message.message_id,
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user