# █ █ ▀ █▄▀ ▄▀█ █▀█ ▀
# █▀█ █ █ █ █▀█ █▀▄ █
# © Copyright 2022
# https://t.me/hikariatama
#
# 🔒 Licensed under the GNU AGPLv3
# 🌐 https://www.gnu.org/licenses/agpl-3.0.html
# meta pic: https://static.dan.tatar/edutatar_icon.png
# meta banner: https://mods.hikariatama.ru/badges/edutatar.jpg
# meta developer: @hikarimods
# scope: hikka_only
# scope: hikka_min 1.2.10
import asyncio
import contextlib
import re
import time
from datetime import datetime, timedelta
import requests
from telethon.tl.types import Message
from .. import loader, utils
filters = {
"Иностранный язык (английский)": "🇺🇸 Англ",
"Физическая культура": "⛹️♂️ PE",
"Физика": "⚛️ Физон",
"Литература": "📕 Лит-ра",
"Математика": "📐 Maths",
"Основы безопасности жизнедеятельности": "🧰 ОБЖ",
"Родной язык": "🗣 Родной",
"История": "⚒ История",
"Родная литература": "📖 Родн.лит",
"География": "🗺 Гео",
"Информатика": "💻 IT",
"Обществознание": "⚖️ Общество",
"Русский язык": "✍️ Русский",
"Химия": "🧪 Химия",
"Биология": "🧬 Био",
"Технология": "🔩 Технология",
}
@loader.tds
class EduTatarMod(loader.Module):
"""Telegram client for edu.tatar.ru"""
strings = {
"name": "eduTatar",
"login_pass_not_specified": (
"🔑 Необходимо указать логин и пароль от edu.tatar.ru в конфиге"
),
"loading_info": "👩🏼🏫 Загружаю информацию с edu.tatar.ru...",
"host_error": (
"🚫 Error occured while parsing. Maybe edutatar host is down or you"
" forgot to change proxy in script?"
),
"no_hw": "📕 Нет д\\з",
}
strings_ru = {
"login_pass_not_specified": (
"🔑 Необходимо указать логин и пароль от edu.tatar.ru в конфиге"
),
"loading_info": "👩🏼🏫 Загружаю информацию с edu.tatar.ru...",
"host_error": (
"🚫 Произошла ошибка получения данных с edu.tatar.ru. Может, ты забыл"
" указать прокси?"
),
"no_hw": "📕 Нет д\\з",
"_cmd_doc_eduweek": "Показать расписание на неделю",
"_cmd_doc_eduday": "<день:число{0,}> - Показать расписание конкретного дня",
"_cmd_doc_eduterm": "Показать оценки за четверть",
"_cls_doc": "Телеграм клиент для edu.tatar.ru",
}
def __init__(self):
self.config = loader.ModuleConfig(
loader.ConfigValue(
"edu_tatar_login", doc=lambda: "Login from edu.tatar.ru"
),
loader.ConfigValue(
"edu_tatar_pass",
doc=lambda: "Password from edu.tatar.ru",
validator=loader.validators.Hidden(loader.validators.String()),
),
loader.ConfigValue(
"marks_parse_delay",
300,
lambda: "Delay for parsing new marks in seconds",
validator=loader.validators.Integer(minimum=0),
),
loader.ConfigValue("proxy", "", lambda: "Proxy for correct work of module"),
)
async def client_ready(self, client, db):
self.sess = {"DNSID": db.get("eduTatar", "sess", None)}
if self.sess["DNSID"] is None:
await self.revoke_token()
asyncio.ensure_future(self.parse_marks_async())
async def parse_marks_async(self):
while True:
await self.check_marks()
await asyncio.sleep(self.config["marks_parse_delay"])
async def eduweekcmd(self, message: Message):
"""Show schedule for a week"""
if not self.config["edu_tatar_login"] or not self.config["edu_tatar_pass"]:
await utils.answer(
message, self.strings("login_pass_not_specified", message)
)
await asyncio.sleep(3)
await message.delete()
return
await utils.answer(message, self.strings("loading_info", message))
data = await self.scrape_week()
await utils.answer(message, data)
async def edudaycmd(self, message: Message):
""" - Show schedule for today"""
if not self.config["edu_tatar_login"] or not self.config["edu_tatar_pass"]:
await utils.answer(
message, self.strings("login_pass_not_specified", message)
)
await asyncio.sleep(3)
await message.delete()
return
args = utils.get_args_raw(message)
if args == "":
offset = 0
with contextlib.suppress(Exception):
offset = abs(int(args))
offset = offset * 60 * 60 * 24
now = datetime.now()
today = now - timedelta(hours=now.hour, minutes=now.minute, seconds=now.second)
day = time.mktime(today.timetuple()) + offset
day_datetime = datetime.utcfromtimestamp(day)
await utils.answer(message, self.strings("loading_info", message))
weekdays = [
"Monday",
"Tuesday",
"Wednesday",
"Thursday",
"Friday",
"Saturday",
"Sunday",
"Monday",
]
data = (
f"📚 {weekdays[day_datetime.weekday() + 1]} 📚\n\n"
+ await self.scrape_date(day)
)
await utils.answer(message, data)
async def edutermcmd(self, message: Message):
"""Get term grades"""
if not self.config["edu_tatar_login"] or not self.config["edu_tatar_pass"]:
await utils.answer(
message, self.string("login_pass_not_specified", message)
)
await asyncio.sleep(3)
await message.delete()
return
await utils.answer(message, self.strings("loading_info", message))
data = await self.scrape_term(utils.get_args_raw(message))
await utils.answer(message, data)
async def revoke_token(self):
try:
answ = await utils.run_sync(
requests.post,
"https://edu.tatar.ru/logon",
headers={
"Host": "edu.tatar.ru",
"Connection": "keep-alive",
"Content-Length": "52",
"Pragma": "no-cache",
"Cache-Control": "no-cache",
"Upgrade-Insecure-Requests": "1",
"Origin": "https://edu.tatar.ru",
"Content-Type": "application/x-www-form-urlencoded",
"User-Agent": (
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"
" (KHTML, like Gecko) Chrome/92.0.4515.131 Safari/537.36"
),
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9",
"Sec-GPC": "1",
"Sec-Fetch-Site": "same-origin",
"Sec-Fetch-Mode": "navigate",
"Sec-Fetch-User": "?1",
"Sec-Fetch-Dest": "document",
"Referer": "https://edu.tatar.ru/logon",
"Accept-Encoding": "gzip, deflate, br",
"Accept-Language": "en-US,en;q=0.9",
},
data={
"main_login2": self.config["edu_tatar_login"],
"main_password2": self.config["edu_tatar_pass"],
},
allow_redirects=True,
proxies={"https": self.config["proxy"]},
)
except requests.exceptions.ProxyError:
return self.strings("host_error")
if "DNSID" in dict(answ.cookies):
self.sess = dict(answ.cookies)
else:
raise ValueError("Failed logging in")
self._db.set("eduTatar", "sess", self.sess["DNSID"])
async def check_marks(self):
marks_tmp = self._db.get("eduTatar", "marks", {}).copy()
await self.scrape_term("")
marks_new = self._db.get("eduTatar", "marks", {}).copy()
for subject, current_marks_2 in list(marks_new.items()):
current_marks_1 = [] if subject not in marks_tmp else marks_tmp[subject]
with contextlib.suppress(KeyError):
subject = filters[subject]
for i in range(min(len(current_marks_1), len(current_marks_2))):
if current_marks_1[i] != current_marks_2[i]:
await self._client.send_message(
"@userbot_notifies_bot",
utils.escape_html(
f"{subject}:"
f" {current_marks_1[i]}->{current_marks_2[i]}\n{' '.join(list(map(str, current_marks_2)))}"
),
)
await asyncio.sleep(0.5)
for i in range(
min(len(current_marks_1), len(current_marks_2)), len(current_marks_2)
):
await self._client.send_message(
"@userbot_notifies_bot",
utils.escape_html(
f"{subject}:"
f" {current_marks_2[i ]}\n{' '.join(list(map(str, current_marks_2)))}"
),
)
await asyncio.sleep(0.5)
async def scrape_date(self, date):
try:
answ = await utils.run_sync(
requests.get,
"https://edu.tatar.ru/user/diary/day?for=" + str(date),
cookies=self.sess,
headers={
"Host": "edu.tatar.ru",
"Connection": "keep-alive",
"Upgrade-Insecure-Requests": "1",
"User-Agent": (
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"
" (KHTML, like Gecko) Chrome/92.0.4515.131 Safari/537.36"
),
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9",
"Sec-GPC": "1",
"Sec-Fetch-Site": "same-origin",
"Sec-Fetch-Mode": "navigate",
"Sec-Fetch-User": "?1",
"Sec-Fetch-Dest": "document",
"Referer": "https://edu.tatar.ru/user/diary/week",
"Accept-Encoding": "gzip, deflate, br",
"Accept-Language": "en-US,en;q=0.9",
},
proxies={"https": self.config["proxy"]},
)
except requests.exceptions.ProxyError:
return self.strings("host_error")
day = re.findall(
r".*? | (.*?) | .*?(.*?)
.*?",
answ.text.replace("\n", ""),
)
if len(day) < 5:
await self.revoke_token()
return await self.scrape_date(date)
ans = ""
for sub in day:
hw = sub[1].strip()
if hw == "":
hw = self.strings("no_hw")
subject = sub[0].strip()
for from_, to_ in filters.items():
subject = subject.replace(from_, to_)
ans += f" {subject} - {hw}" + "\n"
return ans
async def scrape_week(self):
now = datetime.now()
monday = now - timedelta(
days=now.weekday(), hours=now.hour, minutes=now.minute, seconds=now.second
)
monday = time.mktime(monday.timetuple())
week = ""
weekdays = ["Понедельник", "Вторник", "Среда", "Четверг", "Пятница", "Суббота"]
for i in range(6):
week += f"📚 {weekdays[i]} 📚\n"
week += await self.scrape_date(monday + 60**2 * 24 * i)
return week
async def scrape_term(self, args):
try:
answ = await utils.run_sync(
requests.get,
"https://edu.tatar.ru/user/diary/term",
cookies=self.sess,
headers={
"Host": "edu.tatar.ru",
"Connection": "keep-alive",
"Upgrade-Insecure-Requests": "1",
"User-Agent": (
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"
" (KHTML, like Gecko) Chrome/92.0.4515.131 Safari/537.36"
),
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9",
"Sec-GPC": "1",
"Sec-Fetch-Site": "same-origin",
"Sec-Fetch-Mode": "navigate",
"Sec-Fetch-User": "?1",
"Sec-Fetch-Dest": "document",
"Referer": "https://edu.tatar.ru/user/diary/week",
"Accept-Encoding": "gzip, deflate, br",
"Accept-Language": "en-US,en;q=0.9",
},
proxies={"https": self.config["proxy"]},
)
except requests.exceptions.ProxyError:
return self.strings("host_error")
term = "={ Табель успеваемости }=\n"
rows = re.findall(
r".*?| (.*?) | (.*?)
", answ.text.replace("\n", "")
)
cols = {}
for row in rows[1:-1]:
subject = row[0]
processing = (
row[1][: row[1].find("