import logging, config, sql, asyncio, wget, os
from aiogram import Bot, Dispatcher, executor, types
from aiogram.types import ParseMode
from datetime import datetime, time
from script import text_ical, message_form, dt_now, delta_time
# инициализируем токен
logging.basicConfig(level=logging.INFO)
bot = Bot(token=config.TOKEN)
dp = Dispatcher(bot)
# инициализируем соединение с БД
du = sql.Users('../db/users.db')
dc = sql.Clock('../db/clock.db')
# ПРИВЕТСТВЕННОЕ СООБЩЕНИЕ
@dp.message_handler(commands=['start', 'help'])
async def helps(message: types.Message):
tg_id = int(message.chat.id)
if not du.user_exists(tg_id):
du.add_user(tg_id)
buttons = [types.InlineKeyboardButton(text="КОМАНДЫ", callback_data="com"),
types.InlineKeyboardButton(text="АВТОР", callback_data="auth")]
keyboard = types.InlineKeyboardMarkup(row_width=2)
keyboard.add(*buttons)
await message.answer(text="Я.Календаркин - бот для оповещения о событиях из Яндекс.Календаря. "
"Для начала работы вам нужно всего лишь прислать в чат ссылку экспорта календаря в "
"формате ICal. После получения ссылки, бот начнёт оповещать о всех новых событиях "
"и появится возможность настройки оповещений. О том, какие команды есть для настройки, "
"вы можете ознакомиться по кнопке КОМАНДЫ",
parse_mode=ParseMode.HTML, reply_markup=keyboard)
@dp.callback_query_handler(text="auth")
async def author(call: types.CallbackQuery):
await call.message.answer(text='*| АВТОР |*\n\n*>>* Этот бот не коммерческий проект, для упрощенного получения '
'уведомлений о событиях в Яндекс.Календаре. Не многим этот бот будет полезен, но '
'людям, чья работа подразумевает его использование, он станет лишь удобным '
'инструментом. Я же пишу подобные небольшие проекты, о которых вы можете узнать '
'больше на моём [GitHub](https://github.com/IGlek).',
parse_mode=ParseMode.MARKDOWN)
@dp.callback_query_handler(text="com")
async def commands(call: types.CallbackQuery):
await call.message.answer(text='| КОМАНДЫ |\n\n'
'/help - вспомогательная функция для уточнения работы команд\n'
'/list - список событий календаря, запланированных на сегодняшний день\n'
'/notif - команда, отключающая рассылку уведомлений, даже при наличии событий в календаре\n'
'/daily - оповещение в 8 утра по вашему часовому поясу со списком событий на день\n'
'/moment - напоминание, приходящее в момент начала события\n\n'
'/get_alarm - информация о времени на которое настроены оповещения\n'
'/edit_alarm - изменение времени оповещений\n'
'/stop_alarm - команда, отключающая второе оповещение о событии',
parse_mode=types.ParseMode.HTML)
# КОМАНДЫ
@dp.message_handler(commands=['list'])
async def check_list(message: types.Message):
user_id = du.get_user_id(int(message.chat.id))
if du.url_exists(user_id):
txt = "Имеющиеся события на сегодня\n\n"
lst_events = sorted(text_ical(user_id, du.get_tz(user_id)))
today = dt_now(du.get_tz(user_id)).date()
counter = 0
for event in lst_events:
if event[0] == today:
counter += 1
txt += message_form(counter, event[3])
if counter:
await message.answer(text=txt, parse_mode=ParseMode.HTML)
else:
await message.answer(text="В данный момент событий на сегодня найдено не было!",
parse_mode=ParseMode.HTML)
else:
await message.answer("Для отображения событий вы должны прислать ical-ссылку на календарь!")
@dp.message_handler(commands=['notif'])
async def notif_up(message: types.Message):
user_id = du.get_user_id(int(message.chat.id))
if du.url_exists(user_id):
if du.get_status(user_id):
await message.answer("Уведомления о событиях выключены!")
else:
await message.answer("Уведомления о событиях включены!")
du.update_status(user_id)
else:
await message.answer("Для взаимодействия с событиями вы должны прислать ical-ссылку на календарь!")
@dp.message_handler(commands=['daily'])
async def daily_up(message: types.Message):
user_id = du.get_user_id(int(message.chat.id))
if dc.clock_exists(user_id):
if dc.get_daily(user_id):
await message.answer("Ежедневные утренние уведомления выключены!")
else:
await message.answer("Ежедневные утренние уведомления включены!")
dc.update_daily(user_id)
else:
await message.answer("Для взаимодействия с событиями вы должны прислать ical-ссылку на календарь!")
@dp.message_handler(commands=['moment'])
async def start_up(message: types.Message):
user_id = du.get_user_id(int(message.chat.id))
if dc.clock_exists(user_id):
if dc.get_start(user_id):
await message.answer("Уведомления в момент события выключены!")
else:
await message.answer("Уведомления в момент события включены!")
dc.update_start(user_id)
else:
await message.answer("Для взаимодействия с событиями вы должны прислать ical-ссылку на календарь!")
@dp.message_handler(commands=['get_alarm'])
async def start_up(message: types.Message):
user_id = du.get_user_id(int(message.chat.id))
if dc.clock_exists(user_id):
alarms = dc.get_alarm(user_id)
start = dc.get_start(user_id)
status2 = dc.get_status2(user_id)
txt = ""
if status2:
txt += "У вас работает два оповещения"
else:
txt += "У вас работает лишь первое оповещение"
if start:
txt += " и сообщение в момент начала события!"
else:
txt += "!"
await message.answer(text=(txt + f"\n\nПервое оповещение приходит за {alarms[0]} минут\n"
f"Второе оповещение приходит за {alarms[1]} минут"),
parse_mode=ParseMode.HTML)
else:
await message.answer("Для того, чтобы получить таймеры, вы должны прислать ical-ссылку на свой календарь!")
@dp.message_handler(commands=['edit_alarm'])
async def start_up(message: types.Message):
user_id = du.get_user_id(int(message.chat.id))
if dc.clock_exists(user_id):
await message.bot.send_photo(chat_id=message.chat.id, photo=open("../data/photo_edit_alarm.jpg", "rb"),
caption="Для изменения времени вам надо в ответ на это сообщение прислать два "
"числа через пробел: разница времени первого и второго таймера по ходу "
"времени соответственно")
else:
await message.answer("Для того, чтобы изменить таймеры, вы должны прислать ical-ссылку на свой календарь!")
@dp.message_handler(commands=['stop_alarm'])
async def daily_up(message: types.Message):
user_id = du.get_user_id(int(message.chat.id))
if dc.clock_exists(user_id):
if dc.get_status2(user_id):
await message.answer("Второе уведомление выключено!")
else:
await message.answer("Второе уведомление включено!")
dc.update_status2(user_id)
else:
await message.answer("Для взаимодействия с событиями вы должны прислать ical-ссылку на календарь!")
# ЗАГРУЗКА ССЫЛКИ
@dp.message_handler(content_types=['text'])
async def downloading_file_ics(message: types.Message):
user_id = du.get_user_id(int(message.chat.id))
if message.text[:5] == "https":
try:
wget.download(message.text, f'../data/icals/{str(user_id)}_new.ics')
try:
os.remove(f'../data/icals/{str(user_id)}.ics')
except FileNotFoundError:
pass
os.rename(f'../data/icals/{str(user_id)}_new.ics', f'../data/icals/{str(user_id)}.ics')
time_zone = message.text.split("=")[-1]
if not du.url_exists(user_id):
du.add_url(user_id, message.text, time_zone)
dc.add_clock(user_id)
else:
du.update_url(user_id, message.text, time_zone)
await message.answer("Ссылка успешно добавлена! Уведомления уже включены!")
except Exception:
await message.answer("Ошибка скачивания! Проверьте правильность ссылки и пришлите ещё раз")
if 'reply_to_message' in message and dc.clock_exists(user_id):
text = "Для изменения времени вам надо в ответ на это сообщение прислать два числа через " \
"пробел: разница времени первого и второго таймера по ходу времени соответственно"
if message.reply_to_message.caption == text:
alarm_new = message.text.split()
if int(alarm_new[0]) < 60 and int(alarm_new[1]) < 60:
dc.update_alarm1(user_id, int(alarm_new[0]))
dc.update_alarm2(user_id, int(alarm_new[1]))
await message.answer("Время отправки уведомлений успешно обновлено!")
else:
await message.answer("Время отправки уведомлений должно быть меньше 60 минут!")
# ПРОВЕРКА НА СОБЫТИЕ
async def alarm(wait_for):
while True:
await asyncio.sleep(wait_for)
users_id = du.all_users()
for user_id in users_id:
user_id = user_id[0]
if dc.clock_exists(user_id):
if du.get_status(user_id):
events = sorted(text_ical(user_id, du.get_tz(user_id)))
tg_id = str(du.get_first_user_id(user_id))
start = dc.get_start(user_id)
daily = dc.get_daily(user_id)
alarm = dc.get_alarm(user_id)
alarm2_status = dc.get_status2(user_id)
today = dt_now(du.get_tz(user_id)).date()
time_check = dt_now(du.get_tz(user_id)).time()
# ДЛЯ DAILY
counter = 0
txt = "События сегодня\n\n"
delta_daily1 = time(hour=8, minute=0)
delta_daily2 = time(hour=8, minute=1)
# --------------------------------
for event in events:
if event[0] == today:
d_event = datetime.combine(today, event[1])
if daily and delta_daily1 <= time_check < delta_daily2:
counter += 1
txt += message_form(counter, event[3])
delta_start = delta_time(d_event, 1, 0)
if start and delta_start[0] < time_check <= delta_start[1]:
await bot.send_message(chat_id=tg_id, parse_mode=ParseMode.HTML,
text=f"Событие начинается!\n\n" + message_form(0, event[3]))
delta_alarm1 = delta_time(d_event, alarm[0], alarm[0] - 1)
if delta_alarm1[0] < time_check <= delta_alarm1[1]:
await bot.send_message(chat_id=tg_id, parse_mode=ParseMode.HTML,
text=f"Напоминаю!\nЧерез {alarm[0]} минут "
f"будет событие:\n\n{message_form(0, event[3])}")
if alarm2_status:
delta_alarm2 = delta_time(d_event, alarm[1], alarm[1] - 1)
if delta_alarm2[0] < time_check <= delta_alarm2[1]:
await bot.send_message(chat_id=tg_id, parse_mode=ParseMode.HTML,
text=f"Напоминаю!\nЧерез {alarm[1]} минут "
f"будет событие:\n\n{message_form(0, event[3])}")
if daily and delta_daily1 <= time_check < delta_daily2:
if counter:
await bot.send_message(chat_id=tg_id, parse_mode=ParseMode.HTML,
text=txt)
else:
await bot.send_message(chat_id=tg_id, parse_mode=ParseMode.HTML,
text=f"Сегодня событий нет")
async def update(wait_for):
while True:
await asyncio.sleep(wait_for)
users_id = du.all_users()
for user_id in users_id:
user_id = user_id[0]
if du.url_exists(user_id):
if du.get_status(user_id):
wget.download(du.get_url(user_id), f'../data/icals/{str(user_id)}_new.ics')
os.remove(f'../data/icals/{str(user_id)}.ics')
os.rename(f'../data/icals/{str(user_id)}_new.ics', f'../data/icals/{str(user_id)}.ics')
if __name__ == '__main__':
loop = asyncio.get_event_loop()
loop.create_task(alarm(60)) # ПРОВЕРКА КАЖДУЮ 1 МИНУТУ
loop.create_task(update(780)) # ПРОВЕРКА КАЖДУЮ 13 МИНУТУ
executor.start_polling(dp, skip_updates=True)