Предыстория
Однажды вечером я обнаружила, что последний раз ела в 10 утра, а сейчас уже девять вечера. За это время я написала 400 строк кода, два раза залила всё в git и ни разу не встала из-за стола. Это был знак.
Первая мысль — поставить таймер на телефоне. Скучно. Вторая мысль — написать бота. Вот это уже разговор.
Что в итоге получилось
Бот умеет:
- Три раза в день отправлять напоминание поесть (8:00, 13:00, 19:00)
- Ждать подтверждения в виде кнопки «Поела ✓»
- Если я не реагирую 15 минут — слать ещё одно сообщение, обидное
- Вести статистику: сколько раз в эту неделю я нормально питалась
Стек
Всё просто: python-telegram-bot для работы с Bot API,
APScheduler для расписания, SQLite через aiosqlite
для хранения статистики. Никакого Django, никаких лишних зависимостей.
python-telegram-bot==21.3
APScheduler==3.10.4
aiosqlite==0.20.0
Основной код
import asyncio
from telegram import Bot, InlineKeyboardButton, InlineKeyboardMarkup
from telegram.ext import Application, CallbackQueryHandler
from apscheduler.schedulers.asyncio import AsyncIOScheduler
import aiosqlite, os
TOKEN = os.environ["BOT_TOKEN"]
MOLLY_ID = int(os.environ["MOLLY_CHAT_ID"])
REMINDER_TIMES = [(8, 0), (13, 0), (19, 0)]
async def send_reminder(bot: Bot):
keyboard = InlineKeyboardMarkup([[
InlineKeyboardButton("Поела ✓", callback_data="ate"),
InlineKeyboardButton("Потом 😿", callback_data="later"),
]])
await bot.send_message(
chat_id=MOLLY_ID,
text="🍽 Молли, стоп. Брось компьютер. Поешь.",
reply_markup=keyboard,
)
async def handle_button(update, context):
query = update.callback_query
await query.answer()
if query.data == "ate":
await query.edit_message_text("Умница! 🐾 +1 к здоровью")
await log_meal()
else:
await query.edit_message_text("Хорошо, но через 15 минут спрошу ещё раз 😾")
async def log_meal():
async with aiosqlite.connect("meals.db") as db:
await db.execute("INSERT INTO meals (ts) VALUES (datetime('now'))")
await db.commit()
Запуск по расписанию
def main():
app = Application.builder().token(TOKEN).build()
app.add_handler(CallbackQueryHandler(handle_button))
scheduler = AsyncIOScheduler()
for hour, minute in REMINDER_TIMES:
scheduler.add_job(
send_reminder,
trigger="cron",
hour=hour, minute=minute,
args=[app.bot],
)
scheduler.start()
app.run_polling()
if __name__ == "__main__":
main()
BOT_TOKEN и MOLLY_CHAT_ID в переменных окружения,
никогда не в коде. Я использую .env файл + python-dotenv локально
и секреты systemd в продакшене.
Деплой
Бот крутится на самом дешёвом VPS в виде systemd-сервиса.
Без Docker, без Kubernetes — просто bot.service и автозапуск.
Работает уже три месяца без единого сбоя.
Результат
За последние три месяца я пропустила только 4 приёма пищи из 270. Это 98.5% успешности. Мой желудок доволен. Бот доволен. Я доволена.
Код лежит на GitHub, если хотите форкнуть и адаптировать под себя 🐾