daily notify

This commit is contained in:
Administrator 2022-10-21 18:17:41 +03:00
parent 54c47001b7
commit 61436be630
8 changed files with 181 additions and 68 deletions

View File

@ -1,14 +1,14 @@
import datetime import datetime
import zoneinfo
from time import sleep from time import sleep
from helpers import now
from helpers.mongo import mongo from helpers.mongo import mongo
from helpers.ruz import ruz from helpers.ruz import ruz
from settings import MOSCOW_TIMEZONE
def fetch_schedule_for_user(user_hse_id: int): def fetch_schedule_for_user(user_hse_id: int):
zone = zoneinfo.ZoneInfo("Europe/Moscow") today = now()
today = datetime.datetime.now(zone)
next_day = today + datetime.timedelta(days=7) next_day = today + datetime.timedelta(days=7)
schedule = ruz.get_schedule(user_hse_id, today, next_day) schedule = ruz.get_schedule(user_hse_id, today, next_day)
if schedule is None: if schedule is None:
@ -27,7 +27,8 @@ def fetch_schedule_for_user(user_hse_id: int):
month=int(month), month=int(month),
day=int(day), day=int(day),
hour=int(begin_hour), hour=int(begin_hour),
minute=int(begin_minute) minute=int(begin_minute),
tzinfo=MOSCOW_TIMEZONE
) )
}) })
if lesson is None: if lesson is None:
@ -40,14 +41,16 @@ def fetch_schedule_for_user(user_hse_id: int):
month=int(month), month=int(month),
day=int(day), day=int(day),
hour=int(begin_hour), hour=int(begin_hour),
minute=int(begin_minute) minute=int(begin_minute),
tzinfo=MOSCOW_TIMEZONE
), ),
"end": datetime.datetime( "end": datetime.datetime(
year=int(year), year=int(year),
month=int(month), month=int(month),
day=int(day), day=int(day),
hour=int(end_hour), hour=int(end_hour),
minute=int(end_minute) minute=int(end_minute),
tzinfo=MOSCOW_TIMEZONE
), ),
"building": element['building'], "building": element['building'],
"lecturer": element['lecturer'], "lecturer": element['lecturer'],
@ -66,9 +69,7 @@ def process():
def delete_old(): def delete_old():
zone = zoneinfo.ZoneInfo("Europe/Moscow") mongo.lessons_collection.delete_many({"end": {"$lte": now() - datetime.timedelta(days=1)}})
today = datetime.datetime.now(zone)
mongo.lessons_collection.delete_many({"end": {"$lte": today - datetime.timedelta(days=1)}})
def fetch(): def fetch():

View File

@ -2,19 +2,42 @@ import datetime
import zoneinfo import zoneinfo
from time import sleep from time import sleep
import croniter
import pytz
from telebot.apihelper import ApiTelegramException from telebot.apihelper import ApiTelegramException
from daemons.bot import bot from daemons.bot import bot
from helpers import now
from helpers.keyboards import main_keyboard
from helpers.models import UserSchema
from helpers.mongo import mongo from helpers.mongo import mongo
from helpers.ruz import ruz
def process(): def process():
for user in mongo.users_collection.find({"hse_id": {"$ne": None}, "next_notify_time": {"$lte": now()}}):
try:
lessons = mongo.get_today_lessons(UserSchema().load(user))
if len(lessons) == 0:
ans = "Сегодня у тебя нет пар."
else:
ans = ruz.schedule_builder(lessons)
bot.send_message(
user['chat_id'],
"Напоминаю о занятиях сегодня!\n" + ans,
reply_markup=main_keyboard()
)
except:
pass
hours, minutes = user['notify_daily'].split(':')
cron = croniter.croniter(f"{minutes} {hours} * * *", now())
next_date = cron.get_next(datetime.datetime)
next_date = next_date.replace(tzinfo=pytz.timezone('Europe/Moscow'))
mongo.users_collection.update_one({"chat_id": user['chat_id']}, {"$set": {"next_notify_time": next_date}})
for user in mongo.users_collection.find({"notify_minutes": {"$ne": None}, "hse_id": {"$ne": None}}): for user in mongo.users_collection.find({"notify_minutes": {"$ne": None}, "hse_id": {"$ne": None}}):
zone = zoneinfo.ZoneInfo("Europe/Moscow")
now = datetime.datetime.now(zone)
for lesson in mongo.lessons_collection.find({ for lesson in mongo.lessons_collection.find({
"hse_user_id": user["hse_id"], "hse_user_id": user["hse_id"],
"begin": {"$lte": now + datetime.timedelta(minutes=5)}, "begin": {"$lte": now() + datetime.timedelta(minutes=5)},
"notified": False "notified": False
}): }):
ans = "" ans = ""

View File

@ -0,0 +1,8 @@
import datetime
import zoneinfo
def now():
zone = zoneinfo.ZoneInfo("Europe/Moscow")
today = datetime.datetime.now(zone)
return today

View File

@ -1,8 +1,14 @@
import telebot import datetime
import croniter
import pytz
from telebot.types import Message from telebot.types import Message
from daemons.bot import bot from daemons.bot import bot
from daemons.fetch import fetch_schedule_for_user from daemons.fetch import fetch_schedule_for_user
from helpers import now
from helpers.keyboards import main_keyboard, notify_keyboard, yes_no_keyboard, again_keyboard, groups_keyboard, \
no_daily_notify
from helpers.models import UserSchema, User from helpers.models import UserSchema, User
from helpers.mongo import mongo from helpers.mongo import mongo
from helpers.ruz import ruz from helpers.ruz import ruz
@ -12,9 +18,21 @@ class BaseAnswer:
def process(self, message: Message): def process(self, message: Message):
user = mongo.users_collection.find_one({"chat_id": message.chat.id}) user = mongo.users_collection.find_one({"chat_id": message.chat.id})
if user is None: if user is None:
user = User(chat_id=message.chat.id) cron = croniter.croniter("0 9 * * *", now())
next_date = cron.get_next(datetime.datetime)
next_date = next_date.replace(tzinfo=pytz.timezone('Europe/Moscow'))
user = User(chat_id=message.chat.id, next_notify_time=next_date)
mongo.users_collection.insert_one(UserSchema().dump(user)) mongo.users_collection.insert_one(UserSchema().dump(user))
else: else:
if "next_notify_time" not in user:
cron = croniter.croniter("0 9 * * *", now())
next_date = cron.get_next(datetime.datetime)
next_date = next_date.replace(tzinfo=pytz.timezone('Europe/Moscow'))
user["next_notify_time"] = next_date
mongo.users_collection.update_one(
{"chat_id": message.chat.id},
{"$set": {"next_notify_time": next_date}}
)
user = UserSchema().load(user) user = UserSchema().load(user)
attr = getattr(self, "handle_state_" + user.state, None) attr = getattr(self, "handle_state_" + user.state, None)
if attr is None: if attr is None:
@ -36,7 +54,6 @@ class Answer(BaseAnswer):
self.set_state(user, "wait_for_name") self.set_state(user, "wait_for_name")
def handle_state_wait_for_name(self, message: Message, user: User): def handle_state_wait_for_name(self, message: Message, user: User):
kb = telebot.types.ReplyKeyboardMarkup(True, False)
data = ruz.find_person(message.text) data = ruz.find_person(message.text)
if data is None: if data is None:
bot.send_message( bot.send_message(
@ -47,8 +64,6 @@ class Answer(BaseAnswer):
if len(data) == 0: if len(data) == 0:
bot.send_message(user.chat_id, "К сожалению, в РУЗе не нашлось такого студента, попробуй еще раз.") bot.send_message(user.chat_id, "К сожалению, в РУЗе не нашлось такого студента, попробуй еще раз.")
return return
for entity in data:
kb.row(entity['description'])
user.name = message.text user.name = message.text
mongo.users_collection.update_one( mongo.users_collection.update_one(
{"chat_id": user.chat_id}, {"chat_id": user.chat_id},
@ -56,7 +71,7 @@ class Answer(BaseAnswer):
bot.send_message( bot.send_message(
user.chat_id, user.chat_id,
"Отлично! Теперь выбери из списка свою группу.", "Отлично! Теперь выбери из списка свою группу.",
reply_markup=kb reply_markup=groups_keyboard(data)
) )
self.set_state(user, "wait_for_group") self.set_state(user, "wait_for_group")
@ -90,55 +105,46 @@ class Answer(BaseAnswer):
) )
success = fetch_schedule_for_user(user.hse_id) success = fetch_schedule_for_user(user.hse_id)
if success: if success:
kb = telebot.types.ReplyKeyboardMarkup(True, False)
kb.row("Пары сегодня")
kb.row("Уведомления")
kb.row("Сброс настроек")
lessons = mongo.get_today_lessons(user) lessons = mongo.get_today_lessons(user)
if len(lessons) == 0: if len(lessons) == 0:
bot.send_message( bot.send_message(
user.chat_id, user.chat_id,
"Сегодня у тебя нет пар.", "Сегодня у тебя нет пар.",
reply_markup=kb reply_markup=main_keyboard()
) )
else: else:
bot.send_message( bot.send_message(
user.chat_id, user.chat_id,
ruz.schedule_builder(lessons), ruz.schedule_builder(lessons),
reply_markup=kb reply_markup=main_keyboard()
) )
self.set_state(user, "ready") self.set_state(user, "ready")
def handle_state_ready(self, message: Message, user: User): def handle_state_ready(self, message: Message, user: User):
kb = telebot.types.ReplyKeyboardMarkup(True, False)
kb.row("Пары сегодня")
kb.row("Уведомления")
kb.row("Сброс настроек")
if message.text == "Пары сегодня": if message.text == "Пары сегодня":
lessons = mongo.get_today_lessons(user) lessons = mongo.get_today_lessons(user)
if len(lessons) == 0: if len(lessons) == 0:
text = "Сегодня у тебя нет пар." text = "Сегодня у тебя нет пар."
else: else:
text = ruz.schedule_builder(lessons) text = ruz.schedule_builder(lessons)
elif message.text == "Уведомления": elif message.text == "Уведомления о парах":
kb = telebot.types.ReplyKeyboardMarkup(True, False)
kb.row("Не уведомлять")
kb.row("5 минут")
kb.row("10 минут")
kb.row("15 минут")
kb.row("20 минут")
bot.send_message( bot.send_message(
user.chat_id, user.chat_id,
"Выбери когда мне нужно тебе написать о предстоящей паре", "Выбери когда мне нужно тебе написать о предстоящей паре",
reply_markup=kb reply_markup=notify_keyboard()
) )
self.set_state(user, "wait_for_notify") self.set_state(user, "wait_for_notify")
return return
elif message.text == "Ежедневные уведомления":
bot.send_message(
user.chat_id,
"Пришли время в формате чч:мм во сколько ты хочешь получать уведомления о парах в этот день. Например, 09:30",
reply_markup=no_daily_notify()
)
self.set_state(user, "wait_for_daily_notify")
return
elif message.text == "Сброс настроек": elif message.text == "Сброс настроек":
kb = telebot.types.ReplyKeyboardMarkup(True, False) bot.send_message(user.chat_id, "Ты уверен что хочешь сбросить все настройки и больше не получать уведомления?", reply_markup=yes_no_keyboard())
kb.row("Да")
kb.row("Нет")
bot.send_message(user.chat_id, "Ты уверен что хочешь сбросить все настройки и больше не получать уведомления?", reply_markup=kb)
self.set_state(user, "reset") self.set_state(user, "reset")
return return
else: else:
@ -146,7 +152,7 @@ class Answer(BaseAnswer):
bot.send_message( bot.send_message(
user.chat_id, user.chat_id,
text, text,
reply_markup=kb reply_markup=main_keyboard()
) )
def handle_state_wait_for_notify(self, message: Message, user: User): def handle_state_wait_for_notify(self, message: Message, user: User):
@ -162,46 +168,71 @@ class Answer(BaseAnswer):
elif text == "20 минут": elif text == "20 минут":
user.notify_minutes = 20 user.notify_minutes = 20
else: else:
kb = telebot.types.ReplyKeyboardMarkup(True, False) bot.send_message(
kb.row("Не уведомлять") user.chat_id,
kb.row("5 минут") "Я не понимаю такой команды, используй кнопки.",
kb.row("10 минут") reply_markup=notify_keyboard()
kb.row("15 минут") )
kb.row("20 минут")
bot.send_message(user.chat_id, "Я не понимаю такой команды, используй кнопки.", reply_markup=kb)
return return
mongo.users_collection.update_one({"chat_id": user.chat_id}, {"$set": {"notify_minutes": user.notify_minutes}}) mongo.users_collection.update_one({"chat_id": user.chat_id}, {"$set": {"notify_minutes": user.notify_minutes}})
if user.notify_minutes is not None: if user.notify_minutes is not None:
text = f"Принято! Буду уведомлять тебя за {text}." text = f"Принято! Буду уведомлять тебя за {text}."
else: else:
text = f"Принято! Я не уведомлять тебя." text = f"Принято! Я не уведомлять тебя."
kb = telebot.types.ReplyKeyboardMarkup(True, False) bot.send_message(user.chat_id, text, reply_markup=main_keyboard())
kb.row("Пары сегодня")
kb.row("Уведомления")
kb.row("Сброс настроек")
bot.send_message(user.chat_id, text, reply_markup=kb)
self.set_state(user, "ready") self.set_state(user, "ready")
def handle_state_reset(self, message: Message, user: User): def handle_state_reset(self, message: Message, user: User):
if message.text == "Да": if message.text == "Да":
mongo.users_collection.delete_one({"hse_id": user.hse_id}) mongo.users_collection.delete_one({"hse_id": user.hse_id})
kb = telebot.types.ReplyKeyboardMarkup(True, False) bot.send_message(user.chat_id, "Настройки сброшены, ждем твоего возвращения", reply_markup=again_keyboard())
kb.row("Начать заново")
bot.send_message(user.chat_id, "Настройки сброшены, ждем твоего возвращения", reply_markup=kb)
elif message.text == "Нет": elif message.text == "Нет":
kb = telebot.types.ReplyKeyboardMarkup(True, False) bot.send_message(user.chat_id, "Возращаюсь к прежнему режиму", reply_markup=main_keyboard())
kb.row("Пары сегодня")
kb.row("Уведомления")
kb.row("Сброс настроек")
bot.send_message(user.chat_id, "Возращаюсь к прежнему режиму", reply_markup=kb)
self.set_state(user, "ready") self.set_state(user, "ready")
else: else:
kb = telebot.types.ReplyKeyboardMarkup(True, False)
kb.row("Да")
kb.row("Нет")
bot.send_message(user.chat_id, bot.send_message(user.chat_id,
"Я не понимаю, используй кнопки", "Я не понимаю, используй кнопки",
reply_markup=kb) reply_markup=yes_no_keyboard())
def _check_time_correct(self, line: str) -> bool:
if len(line) != 5:
return False
if line.count(':') != 1:
return False
hours, minutes = line.split(':')
try:
hours = int(hours)
minutes = int(minutes)
except:
return False
if hours < 0 or hours > 23 or minutes < 0 or minutes > 59:
return False
return True
def handle_state_wait_for_daily_notify(self, message: Message, user: User):
text = message.text
if not self._check_time_correct(text):
bot.send_message(
user.chat_id,
"Ты прислал что-то неправильное. Пришли время в формате чч:мм. Например, 09:30",
reply_markup=no_daily_notify()
)
return
user.notify_daily = message.text
hours, minutes = user.notify_daily.split(':')
cron = croniter.croniter(f"{minutes} {hours} * * *", now())
next_date = cron.get_next(datetime.datetime)
next_date = next_date.replace(tzinfo=pytz.timezone('Europe/Moscow'))
mongo.users_collection.update_one({"chat_id": user.chat_id}, {"$set": {
"notify_daily": message.text,
"next_notify_time": next_date
}})
bot.send_message(
user.chat_id,
f"Принято! Буду каждый день уведомлять тебя в {message.text}",
reply_markup=main_keyboard()
)
self.set_state(user, "ready")
answer = Answer() answer = Answer()

46
helpers/keyboards.py Normal file
View File

@ -0,0 +1,46 @@
import telebot
def main_keyboard():
kb = telebot.types.ReplyKeyboardMarkup(True, False)
kb.row("Пары сегодня")
kb.row("Уведомления о парах")
kb.row("Ежедневные уведомления")
kb.row("Сброс настроек")
return kb
def notify_keyboard():
kb = telebot.types.ReplyKeyboardMarkup(True, False)
kb.row("Не уведомлять")
kb.row("5 минут")
kb.row("10 минут")
kb.row("15 минут")
kb.row("20 минут")
return kb
def yes_no_keyboard():
kb = telebot.types.ReplyKeyboardMarkup(True, False)
kb.row("Да")
kb.row("Нет")
return kb
def again_keyboard():
kb = telebot.types.ReplyKeyboardMarkup(True, False)
kb.row("Начать заново")
return kb
def groups_keyboard(data):
kb = telebot.types.ReplyKeyboardMarkup(True, False)
for entity in data:
kb.row(entity['description'])
return kb
def no_daily_notify():
kb = telebot.types.ReplyKeyboardMarkup(True, False)
kb.row("Не уведомлять")
return kb

View File

@ -1,3 +1,4 @@
import datetime
from dataclasses import dataclass from dataclasses import dataclass
from typing import Optional from typing import Optional
@ -8,6 +9,8 @@ from marshmallow import EXCLUDE
@dataclass @dataclass
class User: class User:
chat_id: int chat_id: int
next_notify_time: datetime.datetime
notify_daily: Optional[str] = "09:00"
name: Optional[str] = None name: Optional[str] = None
group: Optional[str] = None group: Optional[str] = None
hse_id: Optional[int] = None hse_id: Optional[int] = None

View File

@ -5,6 +5,7 @@ from functools import cached_property
import pymongo import pymongo
import settings import settings
from helpers import now
from helpers.models import UserSchema, User from helpers.models import UserSchema, User
@ -50,8 +51,7 @@ class Mongo:
return self["lessons"] return self["lessons"]
def get_today_lessons(self, user: User): def get_today_lessons(self, user: User):
zone = zoneinfo.ZoneInfo("Europe/Moscow") today = now()
today = datetime.datetime.now(zone)
tomorrow = today + datetime.timedelta(days=1) tomorrow = today + datetime.timedelta(days=1)
tomorrow = datetime.datetime(year=tomorrow.year, month=tomorrow.month, day=tomorrow.day) tomorrow = datetime.datetime(year=tomorrow.year, month=tomorrow.month, day=tomorrow.day)
lessons = [] lessons = []

View File

@ -1,5 +1,5 @@
import os import os
import zoneinfo
MONGO_USER = os.getenv("MONGO_USER", "mongo") MONGO_USER = os.getenv("MONGO_USER", "mongo")
MONGO_PASSWORD = os.getenv("MONGO_PASSWORD", "password") MONGO_PASSWORD = os.getenv("MONGO_PASSWORD", "password")
@ -7,3 +7,4 @@ MONGO_HOST = os.getenv("MONGO_HOST", "localhost")
DEBUG = os.getenv("DEBUG", "true") == "true" DEBUG = os.getenv("DEBUG", "true") == "true"
RUZ_API = "https://ruz.hse.ru/api/" RUZ_API = "https://ruz.hse.ru/api/"
MOSCOW_TIMEZONE = zoneinfo.ZoneInfo("Europe/Moscow")