diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 0000000..d128a69
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1,3 @@
+* text=auto eol=lf
+*.jpg binary
+*.db binary
diff --git a/code/bot.py b/code/bot.py
index 1dbb350..4ecabb1 100644
--- a/code/bot.py
+++ b/code/bot.py
@@ -1,235 +1,8 @@
-import logging, config, sql, asyncio, wget, os
+import asyncio, logging, time, 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 минут!")
+from init import *
+from script import *
+from handlers import router
# ПРОВЕРКА НА СОБЫТИЕ
@@ -273,29 +46,27 @@ async def alarm(wait_for):
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,
+ await bot.send_message(chat_id=tg_id, disable_web_page_preview=True,
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,
+ await bot.send_message(chat_id=tg_id, disable_web_page_preview=True,
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,
+ await bot.send_message(chat_id=tg_id, disable_web_page_preview=True,
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)
+ await bot.send_message(chat_id=tg_id, disable_web_page_preview=True, text=txt)
else:
- await bot.send_message(chat_id=tg_id, parse_mode=ParseMode.HTML,
- text=f"Сегодня событий нет")
+ await bot.send_message(chat_id=tg_id, disable_web_page_preview=True, text=f"Сегодня событий нет")
async def update(wait_for):
@@ -314,8 +85,20 @@ async def update(wait_for):
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)
+async def main() -> None:
+ dp.include_router(router)
+ await bot.delete_webhook(drop_pending_updates=True)
+ await dp.start_polling(bot, allowed_updates=dp.resolve_used_update_types(), skipUpdates=True)
+
+
+async def process() -> None:
+ await asyncio.gather(main(), # ЗАПУСК ОСНОВНОЙ ПРОГРАММЫ
+ alarm(60), # ПРОВЕРКА КАЖДУЮ 1 МИНУТУ
+ update(780)) # ПРОВЕРКА КАЖДУЮ 13 МИНУТУ
+
+
+if __name__ == "__main__":
+ logging.basicConfig(level=logging.INFO)
+
+ try: asyncio.run(process())
+ except KeyboardInterrupt: pass
diff --git a/code/config.py b/code/config.py
index 1cf651a..54e365d 100644
--- a/code/config.py
+++ b/code/config.py
@@ -1 +1 @@
-TOKEN = "**********:***************************"
+TOKEN = "**********:***************************"
diff --git a/code/handlers.py b/code/handlers.py
new file mode 100644
index 0000000..c726169
--- /dev/null
+++ b/code/handlers.py
@@ -0,0 +1,197 @@
+from aiogram import F, Router
+from aiogram.types import Message, CallbackQuery, ContentType, InlineKeyboardMarkup, InlineKeyboardButton, FSInputFile
+from aiogram.filters import Command
+
+import wget, os
+
+from init import *
+from script import *
+
+router = Router()
+
+
+# ПРИВЕТСТВЕННОЕ СООБЩЕНИЕ
+@router.message(Command('start', 'help'))
+async def helps(msg: Message) -> None:
+ tg_id = int(msg.chat.id)
+ if not du.user_exists(tg_id):
+ du.add_user(tg_id)
+
+ buttons = [[InlineKeyboardButton(text="КОМАНДЫ", callback_data="com"),
+ InlineKeyboardButton(text="АВТОР", callback_data="auth")]]
+ keyboard = InlineKeyboardMarkup(inline_keyboard=buttons, row_width=2)
+
+ await msg.answer(text="Я.Календаркин - бот для оповещения о событиях из Яндекс.Календаря. "
+ "Для начала работы вам нужно всего лишь прислать в чат ссылку экспорта календаря в "
+ "формате ICal. После получения ссылки, бот начнёт оповещать о всех новых событиях "
+ "и появится возможность настройки оповещений. О том, какие команды есть для настройки, "
+ "вы можете ознакомиться по кнопке КОМАНДЫ", reply_markup=keyboard)
+
+
+@router.callback_query(F.data == "auth")
+async def author(call: CallbackQuery) -> None:
+ await call.message.answer(text='| АВТОР |\n\n>> Этот бот не коммерческий проект, для упрощенного '
+ 'получения уведомлений о событиях в Яндекс.Календаре. Не многим этот бот будет '
+ 'полезен, но людям, чья работа подразумевает его использование, он станет лишь '
+ 'удобным инструментом. Я же пишу подобные небольшие проекты, о которых вы можете '
+ 'узнать больше на моём GitHub.')
+
+
+@router.callback_query(F.data == "com")
+async def commands(call: CallbackQuery) -> None:
+ 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 - команда, отключающая второе оповещение о событии')
+
+
+# КОМАНДЫ
+@router.message(Command('list'))
+async def check_list(msg: Message) -> None:
+ user_id = du.get_user_id(int(msg.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 msg.answer(text=txt, disable_web_page_preview=True)
+ else: await msg.answer("В данный момент событий на сегодня найдено не было!")
+ else: await msg.answer("Для отображения событий вы должны прислать ical-ссылку на календарь!")
+
+
+@router.message(Command('notif'))
+async def notif_up(msg: Message) -> None:
+ user_id = du.get_user_id(int(msg.chat.id))
+
+ if du.url_exists(user_id):
+ if du.get_status(user_id): await msg.answer("Уведомления о событиях выключены!")
+ else: await msg.answer("Уведомления о событиях включены!")
+
+ du.update_status(user_id)
+ else: await msg.answer("Для взаимодействия с событиями вы должны прислать ical-ссылку на календарь!")
+
+
+@router.message(Command('daily'))
+async def daily_up(msg: Message) -> None:
+ user_id = du.get_user_id(int(msg.chat.id))
+
+ if dc.clock_exists(user_id):
+ if dc.get_daily(user_id): await msg.answer("Ежедневные утренние уведомления выключены!")
+ else: await msg.answer("Ежедневные утренние уведомления включены!")
+
+ dc.update_daily(user_id)
+ else: await msg.answer("Для взаимодействия с событиями вы должны прислать ical-ссылку на календарь!")
+
+
+@router.message(Command('moment'))
+async def moment_up(msg: Message) -> None:
+ user_id = du.get_user_id(int(msg.chat.id))
+
+ if dc.clock_exists(user_id):
+ if dc.get_start(user_id): await msg.answer("Уведомления в момент события выключены!")
+ else: await msg.answer("Уведомления в момент события включены!")
+
+ dc.update_start(user_id)
+ else: await msg.answer("Для взаимодействия с событиями вы должны прислать ical-ссылку на календарь!")
+
+
+@router.message(Command('get_alarm'))
+async def alarm_get(msg: Message) -> None:
+ user_id = du.get_user_id(int(msg.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 msg.answer(txt + f"\n\nПервое оповещение приходит за {alarms[0]} минут\n"
+ f"Второе оповещение приходит за {alarms[1]} минут")
+ else:
+ await msg.answer("Для того, чтобы получить таймеры, вы должны прислать ical-ссылку на свой календарь!")
+
+
+@router.message(Command('edit_alarm'))
+async def alarm_edit(msg: Message) -> None:
+ user_id = du.get_user_id(int(msg.chat.id))
+
+ if dc.clock_exists(user_id):
+ await msg.answer_photo(photo=FSInputFile(path="../data/photo_edit_alarm.jpg"),
+ caption="Для изменения времени вам надо в ответ на это сообщение прислать два "
+ "числа через пробел: разница времени первого и второго таймера по ходу "
+ "времени соответственно")
+ else:
+ await msg.answer("Для того, чтобы изменить таймеры, вы должны прислать ical-ссылку на свой календарь!")
+
+
+@router.message(Command('stop_alarm'))
+async def alarm_stop(msg: Message) -> None:
+ user_id = du.get_user_id(int(msg.chat.id))
+
+ if dc.clock_exists(user_id):
+ if dc.get_status2(user_id): await msg.answer("Второе уведомление выключено!")
+ else: await msg.answer("Второе уведомление включено!")
+
+ dc.update_status2(user_id)
+ else: await msg.answer("Для взаимодействия с событиями вы должны прислать ical-ссылку на календарь!")
+
+
+# ЗАГРУЗКА ССЫЛКИ
+@router.message(F.content_type == ContentType.TEXT)
+async def downloading_file_ics(msg: Message) -> None:
+ user_id = du.get_user_id(int(msg.chat.id))
+
+ if msg.text[:5] == "https":
+ try:
+ wget.download(msg.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 = msg.text.split("=")[-1]
+ if not du.url_exists(user_id):
+ du.add_url(user_id, msg.text, time_zone)
+ dc.add_clock(user_id)
+ else:
+ du.update_url(user_id, msg.text, time_zone)
+
+ await msg.answer("Ссылка успешно добавлена! Уведомления уже включены!")
+ except Exception:
+ await msg.answer("Ошибка скачивания! Проверьте правильность ссылки и пришлите ещё раз")
+
+ if (msg.reply_to_message is not None) and dc.clock_exists(user_id):
+ text = "Для изменения времени вам надо в ответ на это сообщение прислать два числа через " \
+ "пробел: разница времени первого и второго таймера по ходу времени соответственно"
+
+ if msg.reply_to_message.caption == text:
+ alarm_new = msg.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 msg.answer("Время отправки уведомлений успешно обновлено!")
+ else:
+ await msg.answer("Время отправки уведомлений должно быть меньше 60 минут!")
diff --git a/code/init.py b/code/init.py
new file mode 100644
index 0000000..b6733a4
--- /dev/null
+++ b/code/init.py
@@ -0,0 +1,15 @@
+from aiogram import Bot, Dispatcher
+from aiogram.enums.parse_mode import ParseMode
+from aiogram.fsm.storage.memory import MemoryStorage
+from aiogram.client.bot import DefaultBotProperties
+
+from config import botToken
+
+bot = Bot(token=botToken, default=DefaultBotProperties(parse_mode=ParseMode.HTML))
+dp = Dispatcher(storage=MemoryStorage())
+
+
+from sql import Users, Clock
+
+du = Users('../db/users.db')
+dc = Clock('../db/clock.db')
diff --git a/code/script.py b/code/script.py
index 3a3ab37..9729d67 100644
--- a/code/script.py
+++ b/code/script.py
@@ -1,112 +1,112 @@
-from datetime import datetime, time
-import icalendar
-
-from dateutil.rrule import *
-from dateutil.parser import *
-
-
-def text_ical(user_id, tz):
- date = dt_now(tz)
- path = f'../data/icals/{user_id}.ics'
-
- e = open(path, 'rb')
-
- ecal = icalendar.Calendar.from_ical(e.read())
- events = []
-
- for i, component in enumerate(ecal.walk()):
- if component.name == "VEVENT":
-
- # НАЧАЛО
- dt = icalendar.vDDDTypes.to_ical(component.get('dtstart')).decode('utf-8')
- dt = dt.split("T")
-
- dt_start = component.decoded("dtstart")
- dt_end = component.decoded("dtend")
-
- if len(dt) == 1:
- dt_start = datetime.combine(dt_start, time(minute=1))
- dt_end = datetime.combine(dt_end, time(minute=1))
- else:
- dt_start.replace(tzinfo=None)
- dt_start = dt_start.astimezone(tz)
-
- dt_end.replace(tzinfo=None)
- dt_end = dt_end.astimezone(tz)
-
- # НАСТРОЙКА
- r_rule = component.get('rrule')
- if r_rule:
- list_rrule = icalendar.vRecur.to_ical(r_rule).decode('utf-8').split(';')
-
- until, u_flag = [elem for elem in list_rrule if 'UNTIL' in elem], True
- list_rrule = [elem for elem in list_rrule if 'UNTIL' not in elem]
-
- if until:
- until_old = until[0].split('=')[1]
- until = isoparse(until_old).replace(tzinfo=None)
-
- if until.replace(tzinfo=None) <= date.replace(tzinfo=None):
- until, u_flag = "UNTIL=" + until_old, False
-
- if u_flag:
- until = "UNTIL=" + "".join(date.date().isoformat().split("-")) + "T235900Z"
-
- list_rrule.append(until)
- list_rrule = ";".join(list_rrule)
-
- date_iso = "".join(dt_start.isoformat().split("-"))
- iso = "".join(date_iso.split(":")).split("+")[0] + "Z"
-
- dates = list(map(lambda x: x.replace(tzinfo=None), list(rrulestr(list_rrule, dtstart=parse(iso)))))
-
- if dates:
- dif = dt_end - dt_start
-
- dt_start = dates[-1]
- dt_end = dt_start + dif
-
- if date.date() == dt_start.date():
- org = component.get("organizer")
- desc = component.get("description")
-
- event = {"name": component.get('summary'), "desc": desc if desc else "отсутствует",
- "org": org if org else "не назначен", "datetime": [dt_start, dt_end]}
-
- events.append([dt_start.date(), dt_start.time(), i, event])
- e.close()
-
- return events
-
-
-def message_form(k, event):
- txt = ""
-
- if k:
- txt += f"-------\n"
- txt += f"{k}. {event['name']}\n"
- else:
- txt += f"{event['name']}\n"
-
- txt += f"Описание: {event['desc']}\n"
- txt += f"Организатор: {event['org']}\n\n"
- txt += f"Начало: {event['datetime'][0].strftime('%H:%M - %d.%m.%Y года')}\n"
- txt += f"Конец: {event['datetime'][1].strftime('%H:%M - %d.%m.%Y года')}\n"
-
- return txt
-
-
-def delta_time(d_event, start, end):
- d_start = datetime.combine(d_event.date(), time(hour=0, minute=start))
- d_end = datetime.combine(d_event.date(), time(hour=0, minute=end))
-
- zero = datetime.combine(d_event.date(), time(0, 0, 0, 0))
-
- dt_start = zero + (d_event - d_start)
- dt_end = zero + (d_event - d_end)
-
- return [dt_start.time(), dt_end.time()]
-
-
-def dt_now(tz):
- return datetime.now(tz=tz)
+from datetime import datetime, time
+import icalendar
+
+from dateutil.rrule import *
+from dateutil.parser import *
+
+
+def text_ical(user_id, tz):
+ date = dt_now(tz)
+ path = f'../data/icals/{user_id}.ics'
+
+ e = open(path, 'rb')
+
+ ecal = icalendar.Calendar.from_ical(e.read())
+ events = []
+
+ for i, component in enumerate(ecal.walk()):
+ if component.name == "VEVENT":
+
+ # НАЧАЛО
+ dt = icalendar.vDDDTypes.to_ical(component.get('dtstart')).decode('utf-8')
+ dt = dt.split("T")
+
+ dt_start = component.decoded("dtstart")
+ dt_end = component.decoded("dtend")
+
+ if len(dt) == 1:
+ dt_start = datetime.combine(dt_start, time(minute=1))
+ dt_end = datetime.combine(dt_end, time(minute=1))
+ else:
+ dt_start.replace(tzinfo=None)
+ dt_start = dt_start.astimezone(tz)
+
+ dt_end.replace(tzinfo=None)
+ dt_end = dt_end.astimezone(tz)
+
+ # НАСТРОЙКА
+ r_rule = component.get('rrule')
+ if r_rule:
+ list_rrule = icalendar.vRecur.to_ical(r_rule).decode('utf-8').split(';')
+
+ until, u_flag = [elem for elem in list_rrule if 'UNTIL' in elem], True
+ list_rrule = [elem for elem in list_rrule if 'UNTIL' not in elem]
+
+ if until:
+ until_old = until[0].split('=')[1]
+ until = isoparse(until_old).replace(tzinfo=None)
+
+ if until.replace(tzinfo=None) <= date.replace(tzinfo=None):
+ until, u_flag = "UNTIL=" + until_old, False
+
+ if u_flag:
+ until = "UNTIL=" + "".join(date.date().isoformat().split("-")) + "T235900Z"
+
+ list_rrule.append(until)
+ list_rrule = ";".join(list_rrule)
+
+ date_iso = "".join(dt_start.isoformat().split("-"))
+ iso = "".join(date_iso.split(":")).split("+")[0] + "Z"
+
+ dates = list(map(lambda x: x.replace(tzinfo=None), list(rrulestr(list_rrule, dtstart=parse(iso)))))
+
+ if dates:
+ dif = dt_end - dt_start
+
+ dt_start = dates[-1]
+ dt_end = dt_start + dif
+
+ if date.date() == dt_start.date():
+ org = component.get("organizer")
+ desc = component.get("description")
+
+ event = {"name": component.get('summary'), "desc": desc if desc else "отсутствует",
+ "org": org if org else "не назначен", "datetime": [dt_start, dt_end]}
+
+ events.append([dt_start.date(), dt_start.time(), i, event])
+ e.close()
+
+ return events
+
+
+def message_form(k, event):
+ txt = ""
+
+ if k:
+ txt += f"-------\n"
+ txt += f"{k}. {event['name']}\n"
+ else:
+ txt += f"{event['name']}\n"
+
+ txt += f"Описание: {event['desc']}\n"
+ txt += f"Организатор: {event['org']}\n\n"
+ txt += f"Начало: {event['datetime'][0].strftime('%H:%M - %d.%m.%Y года')}\n"
+ txt += f"Конец: {event['datetime'][1].strftime('%H:%M - %d.%m.%Y года')}\n"
+
+ return txt
+
+
+def delta_time(d_event, start, end):
+ d_start = datetime.combine(d_event.date(), time(hour=0, minute=start))
+ d_end = datetime.combine(d_event.date(), time(hour=0, minute=end))
+
+ zero = datetime.combine(d_event.date(), time(0, 0, 0, 0))
+
+ dt_start = zero + (d_event - d_start)
+ dt_end = zero + (d_event - d_end)
+
+ return [dt_start.time(), dt_end.time()]
+
+
+def dt_now(tz):
+ return datetime.now(tz=tz)