feat: Added edit profile func

This commit is contained in:
ITQ
2024-03-20 23:12:35 +03:00
parent 3ca80dea1f
commit 2d35f26b29
6 changed files with 197 additions and 7 deletions
+3
View File
@@ -5,6 +5,7 @@ from typing import Optional
from aiogram import Bot, Dispatcher from aiogram import Bot, Dispatcher
from aiogram.enums import ParseMode from aiogram.enums import ParseMode
from app.callbacks import profile
from app.config import Config from app.config import Config
from app.handlers import help_command, profile_command, start_command from app.handlers import help_command, profile_command, start_command
from app.middlewares.throttling import ThrottlingMiddleware from app.middlewares.throttling import ThrottlingMiddleware
@@ -18,9 +19,11 @@ async def main() -> None:
bot = Bot(bot_token, parse_mode=ParseMode.HTML) bot = Bot(bot_token, parse_mode=ParseMode.HTML)
dp.message.middleware(ThrottlingMiddleware(0.5)) dp.message.middleware(ThrottlingMiddleware(0.5))
# type: ignore
dp.include_routers( dp.include_routers(
start_command.router, start_command.router,
profile_command.router, profile_command.router,
profile.router,
help_command.router, help_command.router,
) )
+171
View File
@@ -0,0 +1,171 @@
# type: ignore
__all__ = ()
from aiogram import F, Router
from aiogram.exceptions import TelegramBadRequest
from aiogram.filters import StateFilter
from aiogram.fsm.context import FSMContext
from aiogram.types import CallbackQuery, Message, ReplyKeyboardRemove
from app import messages, session
from app.filters.user_filter import Registered, RegisteredCallback
from app.keyboards.builders import profile
from app.keyboards.profile import get
from app.models.user import User
from app.utils.states import UserAltering
router = Router(name="profile_callback")
@router.callback_query(
F.data.startswith("profile_change_"),
StateFilter(None),
RegisteredCallback(),
)
async def profile_change_callback(
callback: CallbackQuery,
state: FSMContext,
) -> None:
if callback.data is None or callback.message is None:
return
column = callback.data.replace("profile_change_", "")
if column == "username":
await callback.message.answer(messages.EDIT_USERNAME)
elif column == "age":
await callback.message.answer(messages.INPUT_AGE)
elif column == "bio":
await callback.message.answer(messages.EDIT_BIO)
elif column == "sex":
await callback.message.answer(
messages.INPUT_SEX,
reply_markup=profile(["Male", "Female"]),
)
elif column == "location":
await callback.message.answer(messages.INPUT_LOCATION)
await state.update_data(
column=column,
message_id=callback.message.message_id,
)
await state.set_state(UserAltering.value)
await callback.answer()
@router.message(UserAltering.value, F.text, Registered())
async def profile_change_entered(message: Message, state: FSMContext) -> None:
column = (await state.get_data()).get("column")
value = message.text.strip()
if column == "username":
try:
validated_value = User().validate_username(
key="username",
value=value,
)
except AssertionError as e:
await message.answer(str(e))
return
await state.update_data(value=validated_value)
elif column == "age":
try:
validated_age = User().validate_age(key="age", value=value)
except AssertionError as e:
await message.answer(str(e))
return
await state.update_data(value=validated_age)
elif column == "bio":
if value == "/skip":
await state.update_data(value=None)
else:
try:
validated_bio = User().validate_bio(key="bio", value=value)
except AssertionError as e:
await message.answer(str(e))
return
await state.update_data(value=validated_bio)
elif column == "sex":
value = value.lower()
if value not in ["male", "female"]:
await message.answer(messages.VALIDATION_ERROR_MESSAGE)
return
await state.update_data(value=value)
elif column == "location":
location = value.split(", ")
if len(location) != 2:
await message.answer(messages.VALIDATION_ERROR_MESSAGE)
return
country, city = location
try:
validated_country = User().validate_country(
key="country",
value=country,
)
except AssertionError as e:
await message.answer(str(e))
return
try:
validated_city = User().validate_city(
city=city,
country=validated_country,
)
except AssertionError as e:
await message.answer(str(e))
return
await state.update_data(value=[validated_country, validated_city])
state_data = await state.get_data()
user = User.get_user_queryset_by_telegram_id(message.from_user.id)
if isinstance(state_data["value"], list):
user.update(
{
"country": state_data["value"][0],
"city": state_data["value"][1],
},
)
else:
data = {state_data["column"]: state_data["value"]}
user.update(data)
session.commit()
user = user.first()
session.refresh(user)
try:
await message.bot.edit_message_text(
messages.PROFILE.format(
username=user.username,
age=user.age,
bio=user.bio if user.bio else messages.NOT_SET,
sex=user.sex.capitalize(),
country=user.country,
city=user.city,
),
message.chat.id,
state_data["message_id"],
reply_markup=get(),
)
except TelegramBadRequest:
pass
await message.answer(
"✅ Profile updated",
reply_markup=ReplyKeyboardRemove(),
)
await state.clear()
+2
View File
@@ -26,3 +26,5 @@ PROFILE = (
"\tCity: <b>{city}</b>" "\tCity: <b>{city}</b>"
) )
NOT_SET = "<i>Not set</i>" NOT_SET = "<i>Not set</i>"
EDIT_USERNAME = "Enter your username:\n<i>Allowed characters: a-z, A-Z, 0-9, _</i>\n<i>Length: 5-20 characters</i>"
EDIT_BIO = "Enter your bio (enter /skip if you want to set it to None):\n<i>Maximum length: 100 characters</i>"
+4
View File
@@ -73,6 +73,10 @@ class User(Base):
return normalized_value return normalized_value
@classmethod
def get_user_queryset_by_telegram_id(cls, telegram_id):
return session.query(cls).filter(cls.telegram_id == telegram_id)
@classmethod @classmethod
def get_user_by_telegram_id(cls, telegram_id): def get_user_by_telegram_id(cls, telegram_id):
return ( return (
+14 -6
View File
@@ -23,9 +23,13 @@ def validate_country(country: str):
if not geocode: if not geocode:
return False, None return False, None
is_loc_country = geocode.raw.get( is_loc_country = (
"type", None, geocode.raw.get(
) == "administrative" "type",
None,
)
== "administrative"
)
if is_loc_country: if is_loc_country:
normalized_country = geocode.raw.get("name", "Invalid country") normalized_country = geocode.raw.get("name", "Invalid country")
@@ -55,9 +59,13 @@ def validate_city(city: str, country: str):
if not geocode: if not geocode:
return False, None return False, None
check_in_valid = geocode.raw.get( check_in_valid = (
"type", None, geocode.raw.get(
) in valid_list "type",
None,
)
in valid_list
)
if geocode and check_in_valid: if geocode and check_in_valid:
normalized_country = geocode.raw.get("name", "Invalid city") normalized_country = geocode.raw.get("name", "Invalid city")
+3 -1
View File
@@ -12,4 +12,6 @@ class RegistrationForm(StatesGroup):
class UserAltering(StatesGroup): class UserAltering(StatesGroup):
new_value = State() message_id = State()
column = State()
value = State()