banner

Бот-диетолог под слив на нутру своими руками: часть 2

trafficcardinal написал 31.08.2022
16 мин
0
1058
Содержание
В прошлой части мы научились создавать бота-диетолога, способного рассчитывать калорийность рациона и необходимое количество нутриентов, исходя из биологических особенностей организма, образа жизни и целей пользователя.

Сегодня же мы «приделаем» к боту вес разных типов еды, который будет подгонять меню на день к рассчитанной калорийности и количеству нутриентов. Проще говоря, делаем бота «полноценным» диетологом, насколько это вообще возможно. Также мы добавим ему возможность время от времени выводить нутра-офферы, исходя из того, что именно хочет пользователь — похудеть или набрать массу.

*Спойлер: ближе к концу статьи будет ссылка на файл с исходным кодом бота.

Что уже умеет бот и что мы добавим

В актуальной версии бот имеет следующий функционал:
  1. Запрашивает согласие на отказ от претензий (все же бот — не врач).
  2. Анализирует указанные пользователем биологические данные.
  3. Сопоставляет их с образом жизни и целью пользователя.
  4. Сохраняет всю эту информацию в базе данных.
  5. Выводит рекомендованную калорийность и количество нутриентов в граммах.
Текущая версия функциональности бота
Текущая версия функциональности бота
Текущая версия функциональности бота

Мы же добавим к нему следующие фишки:
  1. Расчет количества грамм разных типов пищи (несколько вариантов по 4 блюда).
  2. Возможность самостоятельно добавлять продукты в меню (для владельца бота).
  3. Вывод вариантов питания пользователю.
  4. Вывод нутра-офферов.

Алгоритм составления плана питания

Для лучшего понимания того, как бот будет высчитывать граммы той или иной еды, распишем суть алгоритма. В целом эта информация представлена скорее для общего развития — вам же достаточно просто скопировать код. Поэтому если нет желания напрягать мозги — можете пролистать до следующего раздела. Если же вам интересны детали, то суть алгоритма в следующем:

  • Есть 4 разных типов продуктов — злаки/мучное, овощи/фрукты, мясо/рыба и молочка/яйца (некоторые типы продуктов совмещены, т. к. они близки по содержанию нутриентов, а нагружать сервер отдельным типом продуктов нет смысла).
  • Каждый тип продуктов (точнее, каждое отдельно взятое блюдо) имеет разные показатели калорийности и содержания нутриентов (см. таблицу с примером ниже).
  • Все эти продукты записаны в отдельных файлах — по 4 блюда разного типа (чтобы питание было сбалансированным).
  • Бот рассчитывает нужное количество грамм каждого блюда из 4 так, чтобы пропорция сошлась с ранее рассчитанной калорийностью и количеством нутриентов, с допустимым отклонением +/- 10% (есть грамм в грамм с расчетными цифрами все равно не получится, так как в реальности даже 2 разных кусочка мяса одного и того же веса будут иметь разную пищевую ценность).
  • Если пользователя устраивает предложенное ему блюдо — ок. Если не устраивает, он может выбрать другое. В конце статьи будут варианты меню по 4 продукта, но вы всегда можете увеличить их количество без внесения изменений в код бота (достаточно будет положить текстовые файлы в папку на сервере).
Примерно так это будет видеть пользователь
Примерно так это будет видеть пользователь
Примерно так это будет видеть пользователь

Также обращаем ваше внимание на важность строгого соответствия структуры текстовых файлов образцу. Если этот момент не будет соблюден, бот может перестать работать. Мы, все же, не программисты, чтобы расписывать тысячи строк кода на все случаи жизни. Наша цель — по-быстрому создать инструмент для залива трафа.

Повторимся — очень важно не «накосячить» при создании текстовиков с меню. Впрочем, если накосячите, не страшно. Вы всегда можете просто удалить файлы с ними с сервера и перезапустить бота :)

Улучшаем бота-диетолога

Подробнее о том, как добавить Telegram бота на сервер, мы писали в прошлой статье. Подразумевается, что читатель уже умеет это. Если нет — ознакомьтесь с прошлым материалом. Сейчас же достаточно сделать следующее:

  1. Открыть файл bot.py на сервере.
  2. Удалить исходный код из прошлой части (мы специально заменим код целиком, чтобы точно не возникло каких-либо ошибок).
  3. Добавить новый вариант кода:
import telebot
import time
import os
import random
from telebot import types
from random import randint
bot = telebot.TeleBot('ТОКЕН СЮДА')

@bot.message_handler(commands=['start'])
def start(message):
markup = types.ReplyKeyboardMarkup(resize_keyboard=True)
btn1 = types.KeyboardButton("Принять")
btn2 = types.KeyboardButton("Отказаться")
markup.add(btn1, btn2)
policy = open('policy.txt', 'r').read()
bot.send_message(message.chat.id, text="Привет, {0.first_name}! \n".format(message.from_user)+policy, reply_markup=markup, parse_mode="Markdown")
user_id = str(message.from_user.id)
t=0
file = open('log.txt')
for num_line, line in enumerate(file):
if user_id in line:
t=1
my_file = open('users/'+user_id+'.txt', 'w')
my_file.write("0" + '\n')
my_file.close()
break
file.close()
if(t == 0):
file = open('log.txt', 'a')
file.write(user_id + '\n')
file.close()
my_file = open('users/'+user_id+'.txt', 'w')
my_file.write("0" + '\n')
my_file.close()
@bot.message_handler(content_types=['text'])
def func(message):
time.sleep(3)
user_id = str(message.from_user.id)
my_file = open('users/'+user_id+'.txt', 'r')
num_lines = sum(1 for line in my_file)
print(num_lines)
my_file.close()
text=message.text
if(message.text == "Изменить профиль"):
my_file = open('users/'+user_id+'.txt', 'w')
my_file.write("0" + '\n')
my_file.close()
markup = types.ReplyKeyboardMarkup(resize_keyboard=True)
btn1 = types.KeyboardButton("Принять")
btn2 = types.KeyboardButton("Отказаться")
markup.add(btn1, btn2)
policy = open('policy.txt', 'r').read()
bot.send_message(message.chat.id, text="Привет, {0.first_name}! \n".format(message.from_user)+policy, reply_markup=markup, parse_mode="Markdown")
if(message.text == "Отказаться"):
my_file = open('users/'+user_id+'.txt', 'w')
my_file.write("0" + '\n')
my_file.close()
markup = types.ReplyKeyboardMarkup(resize_keyboard=True)
btn1 = types.KeyboardButton("Принять")
btn2 = types.KeyboardButton("Отказаться")
markup.add(btn1, btn2)
policy = open('policy.txt', 'r').read()
bot.send_message(message.chat.id, text="Привет, {0.first_name}! \n".format(message.from_user)+policy, reply_markup=markup, parse_mode="Markdown")
if(num_lines == 1 and message.text != "Изменить профиль" and message.text != "Выбрать диету" and message.text != "Отказаться"):
markup = types.ReplyKeyboardMarkup(resize_keyboard=True)
btn1 = types.KeyboardButton("Мужской")
btn2 = types.KeyboardButton("Женский")
markup.add(btn1, btn2)
bot.send_message(message.chat.id, text="Выберите ваш пол", reply_markup=markup)
if(num_lines == 2 and message.text != "Изменить профиль" and message.text != "Выбрать диету" and message.text != "Отказаться"):
markup = types.ReplyKeyboardRemove()
bot.send_message(message.chat.id, text="Выберите ваш возраст (только цифру)", reply_markup=markup)
if(num_lines == 3 and message.text != "Изменить профиль" and message.text != "Выбрать диету" and message.text != "Отказаться"):
bot.send_message(message.chat.id, text="Выберите ваш рост в сантиметрах (только цифру)")
if(num_lines == 4 and message.text != "Изменить профиль" and message.text != "Выбрать диету" and message.text != "Отказаться"):
bot.send_message(message.chat.id, text="Выберите ваш вес в киллограмах (только цифру)")
if(num_lines == 5 and message.text != "Изменить профиль" and message.text != "Выбрать диету" and message.text != "Отказаться"):
markup = types.ReplyKeyboardMarkup(resize_keyboard=True)
btn1 = types.KeyboardButton("Малоактивный")
btn2 = types.KeyboardButton("Среднеактивный")
btn3 = types.KeyboardButton("Активный")
btn4 = types.KeyboardButton("Очень активный")
btn5 = types.KeyboardButton("Ежедневные тренировки")
btn6 = types.KeyboardButton("До двух тренировок в день")
btn7 = types.KeyboardButton("Очень активные и тяжелые физические нагрузки ежедневно")
markup.add(btn1, btn2, btn3, btn4, btn5, btn6, btn7)
bot.send_message(message.chat.id, text="Укажите ваш образ жизни", reply_markup=markup)
if(num_lines == 6 and message.text != "Изменить профиль" and message.text != "Выбрать диету" and message.text != "Отказаться"):
markup = types.ReplyKeyboardMarkup(resize_keyboard=True)
btn1 = types.KeyboardButton("Экстримальное похудение")
btn2 = types.KeyboardButton("Безопасное похудение")
btn3 = types.KeyboardButton("Поддержание веса")
btn4 = types.KeyboardButton("Набор мышечной массы")
btn5 = types.KeyboardButton("Сушка ")
markup.add(btn1, btn2, btn3, btn4, btn5)
bot.send_message(message.chat.id, text="Выберите цель диеты", reply_markup=markup)
if(num_lines == 7 and message.text != "Изменить профиль" and message.text != "Выбрать диету" and message.text != "Отказаться"):
markup = types.ReplyKeyboardMarkup(resize_keyboard=True)
btn1 = types.KeyboardButton("Расчитать каллорийность и норму БЖУ")
markup.add(btn1)
bot.send_message(message.chat.id, text="Погнали?", reply_markup=markup)
if(num_lines == 8 and message.text != "Изменить профиль" and message.text != "Выбрать диету" and message.text != "Отказаться"):
f = open('users/'+user_id+'.txt', 'r')
fd = f.readlines()
if("Мужской" in fd[2]):
pol=5
elif("Женский" in fd[2]):
pol=-161
if("Малоактивный" in fd[6]):
fizo=1.2
elif("Среднеактивный" in fd[6]):
fizo=1.37
elif("Активный" in fd[6]):
fizo=1.45
elif("Очень активный" in fd[6]):
fizo=1.55
elif("Ежедневные тренировки" in fd[6]):
fizo=1.65
elif("До двух тренировок в день" in fd[6]):
fizo=1.725
elif("Очень активные и тяжелые физические нагрузки ежедневно" in fd[6]):
fizo=1.9
vozrast=int(fd[3])
rost=int(fd[4])
ves=int(fd[5])
summ=int(((9.99*ves+6.25*rost-4.92*vozrast+pol)*fizo))
if("Экстримальное похудение" in fd[7]):
probel=0.35
projir=0.35
prougl=0.3
ksum=0.7
elif("Безопасное похудение" in fd[7]):
probel=0.3
projir=0.3
prougl=0.4
ksum=0.8
elif("Поддержание веса" in fd[7]):
probel=0.25
projir=0.25
prougl=0.5
ksum=1
elif("Набор мышечной массы" in fd[7]):
probel=0.3
projir=0.25
prougl=0.5
ksum=1.2
elif("Сушка" in fd[7]):
probel=0.35
projir=0.25
prougl=0.40
ksum=1.1
summa=int(summ*ksum)
bel=int((probel*summa)/4)
jir=int((projir*summa)/9)
ugl=int((prougl*summa)/4)
my_file = open('users/'+user_id+'.txt', 'a')
my_file.write(str(summa) + '\n')
my_file.write(str(bel) + '\n')
my_file.write(str(jir) + '\n')
my_file.write(str(ugl) + '\n')
my_file.close()
markup = types.ReplyKeyboardMarkup(resize_keyboard=True)
btn1 = types.KeyboardButton("Выбрать диету")
btn2 = types.KeyboardButton("Изменить профиль")
markup.add(btn1, btn2)
bot.send_message(message.chat.id, text="Ваша дневная норма:\n"+str(summa)+" ккал.\n"+str(bel)+" грамм белка.\n"+str(jir)+" грамм жиров.\n"+str(ugl)+" грамм углеводов.", reply_markup=markup)
my_file.close()
if(num_lines > 0 and num_lines <= 9 and message.text != "Изменить профиль" and message.text != "Выбрать диету" and message.text != "Отказаться"):
my_file = open('users/'+user_id+'.txt', 'a')
my_file.write(text + '\n')
my_file.close()
if(num_lines == 13 and message.text == "Выбрать диету"):
markup = types.ReplyKeyboardMarkup(resize_keyboard=True)
f = open('users/'+user_id+'.txt', 'r')
fd = f.readlines()
etalon = [int(fd[8]), int(fd[9]), int(fd[10]), int(fd[11])]
prop=[0.3,0.4,0.3]
DIRI=['zavtrak', 'obed', 'ujin']
priem=['Завтрак:', 'Обед:', 'Ужин:']
for g in range(3):
etalon = [float(fd[8])*float(prop[g]), float(fd[9])*float(prop[g]), float(fd[10])*float(prop[g]), float(fd[11])*float(prop[g])]
DIR = DIRI[g]
ed=open(os.path.join(DIR, random.choice(os.listdir(DIR))), 'rt')
eda = []
i = 0
for line in ed:
lines = line.split(', ')
lst = []
for ln in lines:
ln = ln.rstrip()
if ln != '':
num = ln
lst = lst + [num]
eda = eda + [lst]
ed.close()
size=len(eda)
bludo_koef=[(1) for i in range(size)]
bludo=[([i],eda[i][0],eda[i][1]*bludo_koef[i],eda[i][2]*bludo_koef[i],eda[i][3]*bludo_koef[i],eda[i][4]*bludo_koef[i]) for i in range(size)]
for i in range(size):
summ=[0,0,0,0]
for i in range(2,6):
n=float(0)
for j in range(1,size):
n=float(n)+float(bludo[j][i])
summ[i-2]=n
test=[0,0,0,0]
for i in range(4):
test[i]=abs(100-(etalon[i]/summ[i])*100)
max_number = max(test)
num=test.index(max_number)
max_number = max_number+100
test1=round(etalon[num]/summ[num],2)
bludo_koef=[(test1) for i in range(size)]
bludo=[([i],eda[i][0],float(eda[i][1])*bludo_koef[i],float(eda[i][2])*bludo_koef[i],float(eda[i][3])*bludo_koef[i],float(eda[i][4])*bludo_koef[i]) for i in range(1,size)]
for i in range(2,6):
n=0
for j in range(4):
n=n+bludo[j][i]
summ[i-2]=n
test=[0,0,0,0]
for i in range(4):
test[i]=100-(etalon[i]/summ[i])*100
max_number = max(test)
num=test.index(max_number)
while summ[num] > etalon[num]*0.9 or summ[num] < etalon[num]*1.1:
if summ[num] < etalon[num]*1.1 and summ[num] > etalon[num]*0.9:
break
for i in range(size):
if (i==1):
bludo_koef[i]=bludo_koef[i]-0.02
if summ[num] < etalon[num]*1.1 and summ[num] > etalon[num]*0.9:
break
else:
bludo_koef[i]=bludo_koef[i]
bludo=[([i],eda[i][0],float(eda[i][1])*bludo_koef[i],float(eda[i][2])*bludo_koef[i],float(eda[i][3])*bludo_koef[i],float(eda[i][4])*bludo_koef[i]) for i in range(1,size)]
for i in range(2,6):
n=0
for j in range(4):
n=n+bludo[j][i]
summ[i-2]=n
text_menu=0
text_menu=[]
text_menu=[priem[g]+'\n']
for i in range(size-1):
text_menu.append(str(bludo[i][1])+": "+str(round(bludo_koef[i+1]*100))+"грамм")
time.sleep(5)
bot.send_message(message.chat.id, text=str('\n'.join(text_menu)), reply_markup=markup)
num_lines=None
e=randint(0, 100)
if (e>70 and "Экстримальное похудение" in fd[7]) or (e>70 and "Безопасное похудение" in fd[7]):
offer = open('offers/offer1.txt', 'r').read()
bot.send_message(message.chat.id, text="Привет, {0.first_name}! \n".format(message.from_user)+offer, reply_markup=markup, parse_mode="Markdown")
elif (e>70 and "Набор мышечной массы" in fd[7]):
offer = open('offers/offer2.txt', 'r').read()
bot.send_message(message.chat.id, text="Привет, {0.first_name}! \n".format(message.from_user)+offer, reply_markup=markup, parse_mode="Markdown")
elif (e>70 and "Сушка" in fd[7]):
offer = open('offers/offer3.txt', 'r').read()
bot.send_message(message.chat.id, text="Привет, {0.first_name}! \n".format(message.from_user)+offer, reply_markup=markup, parse_mode="Markdown")
elif (e>70 and "Поддержание веса" in fd[7]):
offer = open('offers/offer4.txt', 'r').read()
bot.send_message(message.chat.id, text="Привет, {0.first_name}! \n".format(message.from_user)+offer, reply_markup=markup, parse_mode="Markdown")
bot.polling(none_stop=True)

  1. Создать папки zavtrak, obed, ujin.
  2. Открыть блокнот, прописать в нем следующую структуру:
  • Продукт, Ка, Б, Ж, У
  • Блюдо1, 100, 1.1, 0.1, 7
  • Блюдо2, 200, 2.2, 3.5, 0.7
  • Блюдо3, 300, 18, 0.6, 1.5
  • Блюдо4, 400, 7.9, 1, 51.9
(ЭТО ЛИШЬ ПРИМЕР СТРУКТУРЫ! Разумеется, прописывать нужно реальные блюда/продукты и их пищевую ценность).

Примерно так будут выглядеть файлы с пищевой ценностью разных комбинаций блюд
Примерно так будут выглядеть файлы с пищевой ценностью разных комбинаций блюд
Примерно так будут выглядеть файлы с пищевой ценностью разных комбинаций блюд

  1. Сохранить файл под любым именем.
  2. Загрузить его в папку zavtrak, obed или ujin (хотя бы 1 файл должен быть в каждой папке).
  3. Добавить нужно количество вариантов меню, повторяя шаги 5-7 (имена файлов должны быть разные).
  4. Создать папку offers.
  5. Открыть блокнот, прописать в нем текст оффера.
  6. Сохранить файлы под именами offer1, offer2, offer3 и offer4.
  7. Загрузить их в папку offers.
  8. Запустить бота нажатием RUN.
  9. Проверить результат.
Готово!

Результат работы — разные варианты меню
Результат работы — разные варианты меню
Результат работы — разные варианты меню

Результат работы — пример возможного вывода оффера по нутре
Результат работы — пример возможного вывода оффера по нутре
Результат работы — пример возможного вывода оффера по нутре

Подводя итоги

Теперь бот-диетолог, который мы создали в первой части гайда, стал универсальнее и с большим шансом сможет вовлечь аудиторию в воронку продаж. Надеемся, предложенный нами инструмент окажется полезен вам при заливе трафа.
telegram-icon-small
telegram-icon-big

TrafficCardinal в телеграме

Только полезный контент и бесплатные плюшки.

2-3 раза в день