Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
settings.py
tests.py

# Byte-compiled / optimized / DLL files
__pycache__/
Expand Down
52 changes: 51 additions & 1 deletion bot.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,17 @@
import logging
from telegram.ext import Updater
from telegram.ext import (CommandHandler, MessageHandler, Filters, Updater,
ConversationHandler)
import settings

from calorie_calculation import (calorie_calculation_start, target_selection, target_weight,
сalculate_сalorie_сount)

from questionnaire import (questionnaire_start, questionnaire_gender, questionnaire_age,
questionnaire_height, questionnaire_current_weight, level_of_physical_activity,
data_validation, questionnaire_dontknow)

from handlers import greet_user, unknown_command

logging.basicConfig(filename="bot.log", format='%(asctime)s - %(message)s',
datefmt='%d-%b-%y %H:%M:%S', level=logging.INFO)

Expand All @@ -11,6 +21,46 @@ def main():

dp = bot.dispatcher

questionnaire = ConversationHandler(
entry_points=[
MessageHandler(Filters.regex('^(Заполнить данные)|^(Да, заполнить данные снова)'), questionnaire_start)
],
states={
"gender": [MessageHandler(Filters.text, questionnaire_gender)],
"age": [MessageHandler(Filters.text, questionnaire_age)],
"height": [MessageHandler(Filters.text, questionnaire_height)],
"current_weight": [MessageHandler(Filters.text, questionnaire_current_weight)],
"level_of_physical_activity": [MessageHandler(Filters.text, level_of_physical_activity)],
"data_validation": [MessageHandler(Filters.text, data_validation)]
},
fallbacks=[
MessageHandler(Filters.video | Filters.photo | Filters.document | Filters.location,
questionnaire_dontknow)
]
)

calorie_calculation = ConversationHandler(
entry_points=[
MessageHandler(Filters.regex('^(Расчёт калорий)'), calorie_calculation_start)
],
states={
"target_selection": [MessageHandler(Filters.text, target_selection)],
"target_weight": [MessageHandler(Filters.text, target_weight)],
"сalculate_сalorie_сount": [MessageHandler(Filters.text, сalculate_сalorie_сount)]
},
fallbacks=[
MessageHandler(Filters.video | Filters.photo | Filters.document | Filters.location,
questionnaire_dontknow)
]
)

dp.add_handler(questionnaire)
dp.add_handler(calorie_calculation)
dp.add_handler(CommandHandler("start", greet_user))
dp.add_handler(MessageHandler(
Filters.text | Filters.video | Filters.photo | Filters.document | Filters.location,
unknown_command))

logging.info("bot started")
bot.start_polling()
bot.idle()
Expand Down
87 changes: 87 additions & 0 deletions calorie_calculation.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
from untils import (emoji_to_the_number_of, initial_keyboard, calorie_сalculation,
activity_level_multiplier, keypad_with_weight_selection, keypad_with_target_selection,
main_keyboard)
from telegram.ext import ConversationHandler
from db import db, get_or_create_user, add_or_replace_something


def calorie_calculation_start(update, context):
user = get_or_create_user(db, update.effective_user, update.message.chat.id)
if 'questionnaire' not in user:
update.message.reply_text("Чтобы расчитать калорий нужно заполнить данные!",
reply_markup=initial_keyboard())
return ConversationHandler.END
update.message.reply_text("Укажите вашу цель",
reply_markup=keypad_with_target_selection())
return "target_selection"


def target_selection(update, context):
user_response = update.message.text
if "Сбросить вес" in user_response:
update.message.reply_text("Сколько кг вы хотите сбросить? Нажмите на кнопку или введите число с клавиатуры",
reply_markup=keypad_with_weight_selection())
context.user_data["calorie_calculation"] = {"change_multiplier": -1}
return "target_weight"
elif "Набрать вес" in user_response:
update.message.reply_text("Сколько кг вы хотите набрать? Нажмите на кнопку или введите число с клавиатуры",
reply_markup=keypad_with_weight_selection())
context.user_data["calorie_calculation"] = {"change_multiplier": 1}
return "target_weight"
elif "Сохранить вес" in user_response:
user = get_or_create_user(db, update.effective_user, update.message.chat.id)
current_weight = user["questionnaire"][-1]["current_weight"]
context.user_data["calorie_calculation"] = {"target_weight": current_weight}
сalculate_сalorie_сount(update, context)
return ConversationHandler.END
else:
update.message.reply_text("Выберите один из вариантов",
reply_markup=keypad_with_target_selection())
return "target_selection"


def target_weight(update, context):
user = get_or_create_user(db, update.effective_user, update.message.chat.id)
current_weight = user["questionnaire"][-1]["current_weight"]
change_multiplier = context.user_data["calorie_calculation"]["change_multiplier"]
user_response = update.message.text

if user_response.isdigit():
delta_weight = int(user_response)
target_weight = current_weight + change_multiplier*delta_weight
elif emoji_to_the_number_of(user_response).isdigit():
delta_weight = int(emoji_to_the_number_of(user_response))
target_weight = current_weight + change_multiplier*delta_weight
else:
update.message.reply_text("Введите целое число кг", reply_markup=keypad_with_weight_selection())
return "target_weight"

if target_weight < 0:
update.message.reply_text("Желаемый вес меньше нуля", reply_markup=keypad_with_weight_selection())
return "target_weight"
else:
context.user_data["calorie_calculation"]["target_weight"] = target_weight
сalculate_сalorie_сount(update, context)
return ConversationHandler.END


def сalculate_сalorie_сount(update, context):
user = get_or_create_user(db, update.effective_user, update.message.chat.id)
user_data = user["questionnaire"][-1]

gender = user_data["gender"]
age = user_data["age"]
height = user_data["height"]
weight = context.user_data["calorie_calculation"]["target_weight"]
level_of_physical_activity = user_data["level_of_physical_activity"]
multiplier_activity_level = activity_level_multiplier(level_of_physical_activity)

calorie_count = calorie_сalculation(
gender, age, height, weight, multiplier_activity_level
)

add_or_replace_something(db, user['user_id'], "calorie_count", calorie_count)
add_or_replace_something(db, user['user_id'], "target_weight", weight)

update.message.reply_text(f"Вам нужно потреблять {calorie_count} калорий ежедневно", reply_markup=main_keyboard())

44 changes: 44 additions & 0 deletions db.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
from datetime import datetime
from pymongo import MongoClient
import settings

client = MongoClient(settings.MONGO_LINK)

db = client[settings.MONGO_DB]


def get_or_create_user(db, effective_user, chat_id):
user = db.users.find_one({"user_id": effective_user.id})
if not user:
user = {
"user_id": effective_user.id,
"first_name": effective_user.first_name,
"last_name": effective_user.last_name,
"username": effective_user.username,
"chat_id": chat_id
}
db.users.insert_one(user)
return user


def save_questionnaire(db, user_id, questionnaire_data):
user = db.users.find_one({"user_id": user_id})
questionnaire_data['created'] = datetime.now()
if 'questionnaire' not in user:
db.users.update_one(
{'_id': user['_id']},
{'$set': {'questionnaire': [questionnaire_data]}}
)
else:
db.users.update_one(
{'_id': user['_id']},
{'$push': {'questionnaire': questionnaire_data}}
)


def add_or_replace_something(db, user_id, name, value):
user = db.users.find_one({"user_id": user_id})
db.users.update_one(
{'_id': user['_id']},
{'$set': {name: value}}
)
19 changes: 19 additions & 0 deletions handlers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
from untils import initial_keyboard, main_keyboard, get_emoji

from db import db, get_or_create_user


def greet_user(update, context):
user = get_or_create_user(db, update.effective_user, update.message.chat.id)
if 'questionnaire' not in user:
keyboard = initial_keyboard()
else:
keyboard = main_keyboard()
update.message.reply_text(
f"Здравствуй {user['first_name']}! {get_emoji('waving_hand')}",
reply_markup=keyboard,
)


def unknown_command(update, context):
update.message.reply_text(f"Неизвестная команда {get_emoji('red_question_mark')}", reply_markup=main_keyboard())
105 changes: 105 additions & 0 deletions questionnaire.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
from telegram import ParseMode, ReplyKeyboardMarkup
from telegram.ext import ConversationHandler
from untils import (data_output, gender_selection_button, main_keyboard,
get_emoji, activity_level_selection_button,
list_of_activity_levels)
from db import db, get_or_create_user, save_questionnaire


def questionnaire_start(update, context):
update.message.reply_text(
"Выберете пол",
reply_markup=gender_selection_button()
)
return "gender"


def questionnaire_gender(update, context):
user_gender = update.message.text
if not ("Мужской" in user_gender or "Женский" in user_gender):
update.message.reply_text("Выберете пол")
return "gender"
else:
update.message.reply_text("Введите возраст")
context.user_data["questionnaire"] = {"gender": user_gender}
return "age"


def questionnaire_age(update, context):
user_age = update.message.text
if user_age.isdigit() is False or user_age == '0':
update.message.reply_text("Укажите корректный возраст")
return "age"
else:
update.message.reply_text("Введите рост")
context.user_data["questionnaire"]["age"] = int(user_age)
return "height"


def questionnaire_height(update, context):
user_height = update.message.text
if user_height.isdigit() is False or user_height == '0':
update.message.reply_text("Укажите корректный рост")
return "height"
else:
update.message.reply_text("Введите текущий вес")
context.user_data["questionnaire"]["height"] = int(user_height)
return "current_weight"


def questionnaire_current_weight(update, context):
user_weight = update.message.text
if user_weight.isdigit() is False or user_weight == '0':
update.message.reply_text("Укажите корректный вес")
return "current_weight"
else:
update.message.reply_text(
"Выберете уровень физической активности",
reply_markup=activity_level_selection_button()
)
context.user_data["questionnaire"]["current_weight"] = int(user_weight)
return "level_of_physical_activity"


def level_of_physical_activity(update, context):
user_response = update.message.text
level_of_physical_activity = [level[0] for level in list_of_activity_levels()]
if user_response not in level_of_physical_activity:
update.message.reply_text("Выберете уровень физической активности")
return "level_of_physical_activity"
else:
context.user_data["questionnaire"]["level_of_physical_activity"] = user_response
user_text = data_output(context)
keyboard = [
[f"Данные верны {get_emoji('check_mark')}",
f"Данные неверны. {get_emoji('cross_mark')}"]
]
update.message.reply_text(user_text, parse_mode=ParseMode.HTML,
reply_markup=ReplyKeyboardMarkup(keyboard, one_time_keyboard=True,
resize_keyboard=True)
)
return "data_validation"


def data_validation(update, context):
user = get_or_create_user(db, update.effective_user, update.message.chat.id)
user_response = update.message.text
if "Данные верны" in user_response:
save_questionnaire(db, user['user_id'], context.user_data['questionnaire'])
update.message.reply_text(f"Ваши данные сохранены {get_emoji('memo')}")
update.message.reply_text(f"Изменить данные можно в Профиле {get_emoji('bust_in_silhouette')}",
reply_markup=main_keyboard())
return ConversationHandler.END
else:
keyboard = [
[f"Да, заполнить данные снова {get_emoji('counterclockwise_arrows_button')}"],
[f"Нет. Вернутсья на главную {get_emoji('BACK_arrow')}"]
]
update.message.reply_text("Ваши данные не сохранены. Заполнить данные снова?",
reply_markup=ReplyKeyboardMarkup(keyboard, one_time_keyboard=True,
resize_keyboard=True))
return ConversationHandler.END


def questionnaire_dontknow(update, context):
update.message.reply_text("Некорректные данные(")
16 changes: 16 additions & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
APScheduler==3.6.3
cachetools==4.2.2
certifi==2022.12.7
dnspython==2.3.0
emoji==0.5.4
mypy==1.1.1
mypy-extensions==1.0.0
pymongo==4.3.3
python-telegram-bot==13.14
pytz==2022.7.1
pytz-deprecation-shim==0.1.0.post0
six==1.16.0
tornado==6.1
typing_extensions==4.5.0
tzdata==2022.7
tzlocal==4.2
Loading