IGNOU Telegram Bot v1.0

This commit is contained in:
roshan 2023-02-16 17:48:01 +05:30
commit 3c12b45d62
18 changed files with 1240 additions and 0 deletions

2
.gitignore vendored Normal file
View file

@ -0,0 +1,2 @@
__pycache__
*.session

1
Procfile Normal file
View file

@ -0,0 +1 @@
worker: python3 -m bot

18
bot/__main__.py Normal file
View file

@ -0,0 +1,18 @@
from .ignou import Ignou
from apscheduler.schedulers.asyncio import AsyncIOScheduler
from bot.database import Database
db = Database()
client = Ignou()
if __name__ == "__main__":
# schedular for checking result
scheduler = AsyncIOScheduler()
scheduler.add_job(client.tee_crawler, 'cron', day_of_week='0-6', hour="0-19", minute='0-59/20')
scheduler.add_job(client.grade_crawler, 'cron', day_of_week='0-6', hour="0-19", minute='0-59/25')
scheduler.start()
client.run()

15
bot/config.py Normal file
View file

@ -0,0 +1,15 @@
import os
class Config:
NAME = "IGNOU"
USERNAME = os.environ.get("USERNAME")
BOT_TOKEN = os.environ.get("BOT_TOKEN")
API_ID = os.environ.get("API_ID")
API_HASH = os.environ.get("API_HASH")
SUDO_CHAT = os.environ.get("SUDO_CHAT")
SUDO_ADMIN = os.environ.get("SUDO_ADMIN","").split(",")
DATABASE_URL = os.environ.get("DB_URL","localhost:27017")
SESSION_NAME = os.environ.get("SESSION_NAME")
FOOTER_CREDIT = f"\n<b>Result fetched using {USERNAME}</b>"
HELP_URL = os.environ.get("HELP_URL")

1
bot/database/__init__.py Normal file
View file

@ -0,0 +1 @@
from .database import Database

114
bot/database/database.py Normal file
View file

@ -0,0 +1,114 @@
import datetime
import motor.motor_asyncio
from bot.config import Config
class Singleton(type):
__instances__ = {}
def __call__(cls, *args, **kwargs):
if cls not in cls.__instances__:
cls.__instances__[cls] = super(Singleton, cls).__call__(*args, **kwargs)
return cls.__instances__[cls]
class Database(metaclass=Singleton):
def __init__(self):
self._client = motor.motor_asyncio.AsyncIOMotorClient(Config.DATABASE_URL)
self.db = self._client[Config.SESSION_NAME]
self.user = self.db.user
self.crawler = self.db.crawler
self.site = self.db.site
async def get_student(self, _id):
return await self.crawler.find_one({"_id" : _id})
async def get_user(self, id):
user = await self.user.find_one({"_id": id})
return user
def get_time(self):
return datetime.datetime.now().strftime("%d/%m/%Y %H:%M:%S")
async def add_user(self, _id, name):
user = dict(
_id=_id,
name = name,
join_date=datetime.date.today().isoformat(),
action = dict(
last_used_on=self.get_time(),
),
role_status=dict(
is_admin = False
)
)
await self.user.insert_one(user)
async def is_user_exist(self, id):
user = await self.get_user(id)
return True if user else False
async def total_users_count(self):
count = await self.user.count_documents({})
return count
async def total_crawlers_count(self):
count = await self.crawler.count_documents({})
return count
async def get_all_users(self):
all_users = self.user.find({})
return all_users
async def get_all_crawlers(self):
all_users = self.crawler.find({})
return all_users
async def get_last_used_on(self, _id):
user = await self.get_user(_id)
return user.get("last_used_on", datetime.date.today().isoformat())
# Last Used Time
async def update_last_used_on(self, _id):
await self.user.update_one(
{"_id": _id}, {"$set": {"action.last_used_on": self.get_time()}}
)
# Get When Last Time Used
async def get_last_used_on(self, _id):
user = await self.get_user(_id)
return user.last_used_on
# Last Action
async def update_last_action(self,_id, query):
query['last_used_on'] = self.get_time()
self.user.update_one(
{"_id": _id},
{"$set": {"action": query}})
async def update(self,col, _id, info_dict):
await col.update_one(
{"_id" : _id},
info_dict)
async def find(self, col,_id,info_dict = {}):
return await col.find_one(
{"_id" : _id },
info_dict)
async def insert(self, col, info_dict):
return await col.insert_one(info_dict)
async def get_site_update(self,site):
site_update = await self.site.find_one({"_id": site})
if site_update is None:
await self.site.insert_one({"_id": site})
return {}
return site_update

0
bot/helper/__init__.py Normal file
View file

55
bot/helper/extractor.py Normal file
View file

@ -0,0 +1,55 @@
class User:
def __init__(self, user):
if user is None:
user = {}
self._id = user.get("_id")
self.name = user.get("name")
self.course = user.get("action", {}).get("course")
self.myenrollment = user.get("myenrollment")
self.following = user.get("following", {})
self.enrollment = user.get("action", {}).get("enrollment")
self.last_used_on = user.get("action", {}).get("last_used_on")
self.is_admin = user.get("role_status", {}).get("is_admin", False)
self.user = user
def dict(self):
return self.user
#
class Student:
def __init__(self, student):
if student is None:
student = {}
self._id = student.get("_id")
self.name = student.get("name")
self.course = student.get("course")
self.myenrollment = student.get("myenrollment")
self.followers = student.get("followers", {})
self.enrollment = student.get("action", {}).get("enrollment")
self.last_used_on = student.get("action", {}).get("last_used_on")
self.is_admin = student.get("role_status", {}).get("is_admin", False)
self.grade = self.Grade(student.get("grade"))
self.tee = self.Tee(student.get("tee"))
self.student = student
def dict(self):
return self.student
# grade card
class Grade:
def __init__(self, grade):
self.passed = grade.get("count",{}).get("passed", 0)
self.failed = grade.get("count",{}).get("failed", 0)
self.checked = grade.get("checked")
# tee card
class Tee:
def __init__(self, tee):
self.count = tee.get("count",0)
self.checked = tee.get("checked")

77
bot/helper/ignoubooks.py Normal file
View file

@ -0,0 +1,77 @@
import requests
import os
class IgnouBooks:
def __init__(self,course=None,subject='None') -> None:
self.headers = {'User-Agent':'Dalvik/2.1.0 (Linux; U; Android 10; Redmi Note 7 Pro Build/QQ3A.200605.001)','b8S3lCfLoGo4DVAzNQnl5OpMyALyq8e2WpWbZTMlwZ5iPHr.UaVNW':'J0lta3zy@19' }
self.course = course.upper() # Course Name like BCA or MCA
self.subject = subject.upper() # If subject code passed like BCS011 or MCS023
self.pId = '' # IGNOU course id like BCA 23 and MCA 25
self.cId = '' # IGNOU Course subject id list
self.courseList = '' # get all subject of course
def courseCode(self):
courses = ['https://egkapp.ignouonline.ac.in/api/programmes/type/Bachelor','https://egkapp.ignouonline.ac.in/api/programmes/type/Master']
for courseurl in courses:
response = requests.get(courseurl,headers = self.headers).json()
for res in response:
if '(' not in res['pCode']:
if self.course == res['pCode'].upper():
self.pId = res['pId']
return
elif '(' in res['pCode'] and 'English' == res['pMedium']:
if self.course == res['pCode'].split("(")[0].upper():
self.pId = res['pId']
return
def subjectCode(self):
self.courseCode()
subjects = 'https://egkapp.ignouonline.ac.in/api/courses/p/' + str(self.pId)
response = requests.get(subjects,headers = self.headers).json()
substring ='' # 〽️
for res in response:
fullname = res['cCode']
con = any(map(str.isdigit, fullname))
if not con:
con = False
continue
if '-' in fullname:
first = fullname.split('-')[0]
last = str(int(fullname.split('-')[1])//1)
finalname = first + last
if finalname == self.subject:
self.cId = res['cId']
title = res['cTitle']
coursename = res['cpId']['pCode']
substring += f'{title} \n〽️ `/book {coursename} {finalname}`\n\n'
self.courseList = substring
def getCourseSubjectlist(self):
self.courseCode()
self.subjectCode()
return self.courseList
def getDownload(self):
self.courseCode()
self.subjectCode()
downloads = 'https://egkapp.ignouonline.ac.in/api/blocks/c/' + str(self.cId)
response = requests.get(downloads,headers = self.headers).json()
namelist = []
for res in response:
downurl = 'https://egkapp.ignouonline.ac.in/api/blocks/download/' + str(res['bId'])
downloadresponse = requests.get(downurl,headers = self.headers)
open(f"{res['bCode']} [{self.subject}][@IGNOUpyBoT].pdf", 'wb').write(downloadresponse.content)
namelist.append(f"{res['bCode']} [{self.subject}][@IGNOUpyBoT].pdf")
print(f"{res['bCode']} [{self.subject}][@IGNOUpyBoT].pdf")
return namelist

233
bot/helper/ignoucrawler.py Normal file
View file

@ -0,0 +1,233 @@
import asyncio
from bot.helper.ignouresult import IgnouResult
from bot.database import Database
from bot.helper.extractor import Student
import datetime
import time
from pyrogram import Client
from pyrogram.errors import FloodWait,PeerIdInvalid
db = Database()
class IgnouCrawler:
def __init__(self,client) -> None:
self.db = db
self.client = client
self.greeted = {
"grade" : {},
"tee" : {}
}
self.greet_msg = {
"grade" : "Grade Card Updated Today ",
"tee" : "One more result out Today 🤒"
}
self.todayDate = datetime.datetime.today().strftime('%B %d, %Y')
async def greet_user(self, result_type, user_id):
if not self.greeted.get(result_type).get(user_id):
self.greeted[result_type][user_id] = True
try:
await self.client.send_message(
user_id,
f"<b>{self.greet_msg.get(result_type)}👩🏻‍🎨</b>",parse_mode='html')
except FloodWait as e:
time.sleep(e.x)
await self.client.send_message(
user_id,
f"<b>{self.greet_msg.get(result_type)}👩🏻‍🎨</b>",parse_mode='html')
except PeerIdInvalid as e:
print(f"{user_id} -> {e}")
async def teeCrawl(self, student: Student):
data = IgnouResult('roshan'+ student._id).teeResultString()
if data and student.tee.count != data.get("count"):
title = '<pre>' + f'Name : {student.name} -> {student.course}\n' + '</pre>'
for user_id in student.followers:
if not self.greeted.get("tee").get(user_id):
await self.greet_user("tee", user_id)
try:
await self.client.send_message(
chat_id=user_id,
text= title + data.get("result"),
parse_mode='html')
except FloodWait as e:
time.sleep(e.x)
await self.client.send_message(
chat_id=user_id,
text= title + data.get("result"),
parse_mode='html')
except PeerIdInvalid as e:
print(f"{user_id} -> {e}")
await self.db.update(
self.db.crawler,
student._id,
{
"$set": {
"tee.count": data.get("count"),
"tee.checked": self.todayDate
}
}
)
async def teeTask(self):
print("Tee Result Crawling : {}".format(datetime.datetime.today().strftime("%d/%m/%Y %H:%M:%S")))
await self.db.update(
self.db.site,
"ignou",
{
"$set" : {
"tee_checked": datetime.datetime.today().strftime("%d/%m/%Y %H:%M:%S")
}
}
)
tasks = []
students = await db.get_all_crawlers()
self.greeted['tee'] = {}
# Check first TEE SIte Updated or not
site = await IgnouResult().teeCardUpdated()
if not site.get("updated"):
# print("Tee card Site not Updated")
return
# if site updated check for results
async for student in students:
student_info = Student(student)
if student_info.tee.checked == self.todayDate:
continue
tasks.append(
asyncio.create_task(
self.teeCrawl(student_info)
)
)
await asyncio.gather(*tasks)
await self.db.update(
self.db.site,
"ignou",
{
"$set" : {
"tee" : site.get("date"),
"tee_checked": datetime.datetime.today().strftime("%d/%m/%Y %H:%M:%S")
}
}
)
async def gradeCrawl(self,student: Student):
data = IgnouResult(student.course + student._id).gradeResultString()
grade_passed = data.get("json", {}).get("count", {}).get("passed", 0)
grade_failed = data.get("json", {}).get("count", {}).get("failed", 0)
if data and (int(student.grade.passed) != grade_passed or int(student.grade.failed) != grade_failed) or True:
for user_id in student.followers:
if not self.greeted.get("grade").get(user_id):
await self.greet_user("grade", user_id)
try:
await self.client.send_message(
chat_id= user_id,
text = data.get("result"),
parse_mode ='html')
except FloodWait as e:
time.sleep(e.x)
await self.client.send_message(
chat_id= user_id,
text = data.get("result"),
parse_mode ='html')
except PeerIdInvalid as e:
print(f"{user_id} -> {e}")
await self.db.update(
self.db.crawler,
student._id,
{
"$set" : {
"grade.count.passed": grade_passed,
"grade.count.failed": grade_failed,
"grade.checked": self.todayDate
}
}
)
async def gradeTask(self):
print("Grade Card Crawling : {}".format(datetime.datetime.today().strftime("%d/%m/%Y %H:%M:%S")))
await self.db.update(
self.db.site,
"ignou",
{
"$set": {
"grade_checked": datetime.datetime.today().strftime("%d/%m/%Y %H:%M:%S")
}
}
)
tasks = []
students = await db.get_all_crawlers()
self.greeted['grade'] = {}
# Check first Grade Site Updated or not
site = await IgnouResult().gradeCardUpdated()
if not site.get("updated"):
# print("Grade card Site not Updated")
return
# if site updated check for results
async for student in students:
student_info = Student(student)
if student_info.grade.checked == self.todayDate:
continue
tasks.append(
asyncio.create_task(
self.gradeCrawl(student_info)
)
)
await asyncio.gather(*tasks)
await self.db.update(
self.db.site,
"ignou",
{
"$set" : {
"grade": site.get("date"),
"grade_checked": datetime.datetime.today().strftime("%d/%m/%Y %H:%M:%S")
}
}
)
if __name__ == "__main__":
pass

218
bot/helper/ignouresult.py Normal file
View file

@ -0,0 +1,218 @@
import requests
from bs4 import BeautifulSoup
from prettytable import PrettyTable
import re
from bot.database import Database
db = Database()
class IgnouResult:
def __init__(self,text='Bca 197940316') -> None:
self.text = text.upper().strip()
self.enrollmentNo = ''
self.courseId = ''
self.extractCourseIDandEnrollmentNo()
self.sem = 'Dec20'
self.session = requests.Session()
self.session.headers.update({
'Connection': 'keep-alive',
'Cache-Control': 'max-age=0',
'Upgrade-Insecure-Requests': '1',
'Content-Type': 'application/x-www-form-urlencoded',
'User-Agent': 'Mozilla/5.0 (Linux; Android 6.0.1; Moto G (4)) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.96 Mobile 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',
'Accept-Language': 'en-US,en;q=0.9,ru;q=0.8',
}
)
def extractCourseIDandEnrollmentNo(self):
self.courseId = re.findall("\D+", self.text)[0].strip()
self.enrollmentNo = re.findall('\d+', self.text)[0].strip()
return self.courseId,self.enrollmentNo
def teeResultJson(self):
data = {
'eno': self.enrollmentNo,
'myhide': 'OK'
}
teeUrl = f'https://termendresult.ignou.ac.in/TermEnd{self.sem}/TermEnd{self.sem}.asp'
response = self.session.post(teeUrl, data=data)
if 'Not found' in response.text:
return {'status':'notok'} # result not found
soup = BeautifulSoup(response.text,'lxml')
trs = soup.find_all('tr')[1:]
resultJson = list()
for tr in trs:
info = tr.find_all("td")
data = list()
for index,col in enumerate(info):
if index == len(info) - 1:
break
data.append(info[index].text.strip())
resultJson.append(data)
return {'result' : resultJson,'count' : len(trs),'html':response.text, 'status':'ok'}
def gradeResultJson(self):
data = {
'Program': self.courseId,
'eno': self.enrollmentNo,
'submit': 'Submit',
'hidden_submit': 'OK'
}
if self.courseId in ['BCA', 'MCA', 'MP', 'MBP', 'PGDHRM', 'PGDFM', 'PGDOM', 'PGDMM', 'PGDFMP']:
url = 'https://gradecard.ignou.ac.in/gradecardM/Result.asp'
elif self.courseId in ['ASSSO', 'BA', 'BCOM', 'BDP', 'BSC']:
url = 'https://gradecard.ignou.ac.in/gradecardB/Result.asp'
else:
url = 'https://gradecard.ignou.ac.in/gradecardR/Result.asp'
response = self.session.post(url, data=data)
soup = BeautifulSoup(response.text,'lxml')
if 'Not found' in response.text or response.status_code != 200:
return {'status':'notok'} # result not found
trs = soup.find_all("tr")[1:]
resultJson = list()
student = dict()
btags = soup.find_all('b')[3:6]
for n,ba in enumerate(btags,1):
if n == 1:
student['enrollment'] = ba.text.split(":")[1].strip()
elif n == 2:
student['name'] = ba.text.split(":")[1].strip()
else:
student['course'] = ba.text.split(":")[1].strip()
count = dict()
count['passed'] = 0
count['failed'] = 0
count['total'] = 0
for tr in trs:
data = list()
td = tr.find_all("td")
data.append(td[0].string.strip())
data.append(td[1].string.strip())
data.append(td[2].string.strip())
data.append(td[6].string.strip())
status = True if 'Not' not in td[7].string.strip() else False # ✅ ❌
data.append(status)
if status:
count['passed'] += 1
else:
count['failed'] += 1
count['total'] += 1
resultJson.append(data)
return {'result' : resultJson,'student' : student,'count' : count,'html':response.text,'status':'ok'}
def gradeResultString(self):
x = PrettyTable()
x.field_names = ["Course","Asign","Lab","Term","Status"]
gradeJson = self.gradeResultJson()
if gradeJson['status'] != 'ok':
return False
header = 'Name : {}\n -> {} -> {}\n'.format(gradeJson['student']['name'],self.courseId,self.enrollmentNo)
for sub in gradeJson['result']:
tick = '' if sub[-1] else ''
sub[-1] = tick
x.add_row(sub)
footer = ['count','T:{}'.format(gradeJson['count']['total']),
'P:{}'.format(gradeJson['count']['passed']),
'F:{}'.format(gradeJson['count']['failed']),
'L:{}'.format(gradeJson['count']['total']-gradeJson['count']['passed'])]
x.add_row(footer)
return {
'enrollmentno' : self.enrollmentNo,
'course' : self.courseId,
'result' : '<pre>' + header + x.get_string() + '</pre>',
'json' : gradeJson
}
def teeResultString(self):
x = PrettyTable()
header = 'Enrollment no : {} ({})\n'.format(self.enrollmentNo,self.sem)
x.field_names = ["Course","Marks","Max","Month","Updation"]
teeJson = self.teeResultJson()
if teeJson['status'] != 'ok':
return False
for sub in teeJson['result']:
x.add_row(sub)
return {
'enrollmentno' : self.enrollmentNo,
'course' : self.courseId,
'count' : teeJson['count'],
'result' : '<pre>' + header + x.get_string() + '</pre>',
'json' : teeJson
}
async def gradeCardUpdated(self):
response = self.gradeResultJson()
if response['status'] != 'ok':
return False
updated = re.findall('([\w]+ ?[\d]+, ?[\d]+)',response['html'])[0]
last_updateed = await db.get_site_update("ignou")
if updated != last_updateed.get("grade",''):
return {"date" : updated,"updated" : True}
return {"date" : updated,"updated" : False}
async def teeCardUpdated(self):
teeUrl = f'https://termendresult.ignou.ac.in/TermEnd{self.sem}/TermEnd{self.sem}.asp'
response = self.session.post(teeUrl)
if response.status_code != 200:
return False
updated = re.findall('([\w]+ ?[\d]+, ?[\d]+)',response.text)[0]
# todayDate = datetime.datetime.today().strftime('%B %d, %Y')
last_updateed = await db.get_site_update("ignou")
if updated != last_updateed.get("tee",''):
return {"date" : updated,"updated" : True}
return {"date" : updated,"updated" : False}

24
bot/ignou.py Normal file
View file

@ -0,0 +1,24 @@
from pyrogram import Client
from bot.config import Config
from bot.helper.ignoucrawler import IgnouCrawler
class Ignou(Client):
def __init__(self):
super().__init__(
session_name=Config.SESSION_NAME,
bot_token=Config.BOT_TOKEN,
api_id=Config.API_ID,
api_hash=Config.API_HASH,
plugins=dict(root="bot/plugins"),
)
self.IgnouCrawler = IgnouCrawler(self)
async def grade_crawler(self):
await self.IgnouCrawler.gradeTask()
async def tee_crawler(self):
await self.IgnouCrawler.teeTask()

0
bot/plugins/__init__.py Normal file
View file

33
bot/plugins/book.py Normal file
View file

@ -0,0 +1,33 @@
from pyrogram import Client
from pyrogram import filters
from bot.helper.ignoubooks import IgnouBooks
from bot.config import Config
# Completed Add DB Support
@Client.on_message(filters.command(['book']) & filters.chat(Config.SUDO_CHAT))
def books(client: Client, message):
text = message.text
try:
course_code = text.split(" ")[1].upper()
subject_code = text.split(" ")[2].upper()
except IndexError:
message.reply_text('Wrong Course Name or Subject Code 😐')
return
if books.find_one(subject_code):
# add code for database books for sending from cache
pass
if subject_code.upper() == 'LIST':
message.reply_text(IgnouBooks(course=course_code).get_courseSubjectlist())
else:
message.reply_text("please wait.. sending books")
files = IgnouBooks(course=course_code, subject=subject_code).getDownload()
for file in files:
client.send_document(
message.
chat,
id,
file,
caption=f'Downloaded using {Config.USERNAME}')

272
bot/plugins/result.py Normal file
View file

@ -0,0 +1,272 @@
from pyrogram import Client
from pyrogram import filters
from pyrogram.types import (
InlineKeyboardMarkup,
InlineKeyboardButton,
CallbackQuery,
Message
)
import datetime
from bot.database import Database
from bot.config import Config
from bot.helper.ignouresult import IgnouResult
from bot.helper.extractor import User
db = Database()
# Completed
@Client.on_message(filters.regex('^(m|M)y'))
@Client.on_message(filters.command(['my']))
async def my(_, message):
user: User = User(await db.get_user(message.from_user.id))
if 'my' == message.text.lower():
if user.myenrollment:
await result_card(_, message)
else:
await message.reply_text("Set Your Enrollment using \n my 197xx00xx22")
return
else:
student = message.text.split()
try:
await db.update(
db.user,
message.from_user.id,
{"$set": {"myenrollment": student[1] + student[2]}})
await message.reply_text("Data Saved Successfully ")
except IndexError:
await message.reply_text("First Set your Enrollment using \n my course_code enrollment")
# Completed
@Client.on_message(filters.regex("^\D+\d{8,10}") | filters.regex("^\d{8,10}"))
async def result_card(_, message: Message):
get_course = False
user: User = User(await db.get_user(message.from_user.id))
if message.text.isnumeric():
if not user.course:
await message.reply("You must check once result with Course code \n example : bca 197xx00xx22")
return
else:
get_course = user.course
# elif 'my' in message.text.lower() or 'last' in message.text.lower():
# user: User = await db.get_user(message.from_user.id)
result_query = ''
if get_course:
result_query = get_course + message.text
elif 'my' in message.text.lower():
result_query = user.myenrollment
elif "last" in message.text.lower():
result_query = user.course + user.enrollment
else:
result_query = message.text
student: IgnouResult = IgnouResult(result_query)
result = student.gradeResultString()
if not result:
await message.reply_text('Enrollment no is not correct or \nGrade Card Site is Down 🤕')
return
if not user.following.get(student.enrollmentNo):
inline_keyboard = InlineKeyboardMarkup(
[
[ # First row
InlineKeyboardButton( # Generates a callback query when pressed
"Add to Watch List 👀",
callback_data=f"add_{student.courseId}_{student.enrollmentNo}"
),
],
[
InlineKeyboardButton(
"Share this 🤖",
switch_inline_query=f"\nTry this Easy IGNOU Bot\n 👉🏻 {Config.USERNAME} \n\n Created by @r0sh7n"
)
]
]
)
else:
inline_keyboard = InlineKeyboardMarkup(
[
[
InlineKeyboardButton(
"Share this 🤖",
switch_inline_query=f"\nTry this Easy IGNOU Bot\n 👉🏻 {Config.USERNAME} \n Created by @r0sh7n"
)
]
]
)
await message.reply_text(
result.get("result", '') + Config.FOOTER_CREDIT,
parse_mode='html',
reply_markup=inline_keyboard)
await db.update_last_action(
message.from_user.id,
{"course": student.courseId, "enrollment": student.enrollmentNo})
# Tee Result Card
result: IgnouResult = IgnouResult(result_query).teeResultString()
if result:
await message.reply_text(
result.get("result", '') + Config.FOOTER_CREDIT,
parse_mode='html')
@Client.on_callback_query(filters.regex("^add") | filters.regex("^remove"))
async def watch_list(_, callback_query: CallbackQuery):
"""
callback_query example:
add_bca_192112313
remove_bca_1092313
"""
_, course, enrollment = callback_query.data.split("_")
today_date = datetime.datetime.today().strftime('%B %d, %Y')
# info about follower who is following enrollment
user_dict = {
"username": callback_query.from_user.username,
"name": callback_query.from_user.first_name,
"_id": callback_query.from_user.id,
"added_on": datetime.date.today().isoformat()
}
# add enrollment in following list
if 'add' in callback_query.data:
# Grade Card
student = IgnouResult(course + enrollment)
result = student.gradeResultJson()
# if unable to fetch result from ignou site
if result.get("status") != "ok":
await callback_query.answer("Unable to fetch details\nTry after sometime ", show_alert=True)
return
# student data
student_info = result.get("student", {})
# result pass fail in dict(passed,failed)
count = result.get("count", {})
# student info in user following section
await db.update(
db.user,
callback_query.from_user.id,
{
"$set": {
f"following.{enrollment}": {
"name": student_info.get("name"),
"course": student_info.get("course")
}
}
})
# this dict gonna update in crawler db
info_dict = {
"_id": enrollment
}
# check if student is already in crawler db
if not await db.find(
db.crawler,
enrollment
):
# collection info then update in info_dict : Dict variable
student_db = {
"name": student_info.get("name", ""),
"course": course,
"grade": {
"count": {
"passed": count.get("passed", 0),
"failed": count.get("failed", 0)
},
"checked": today_date
},
}
# updating info_dict : Dict with student_db
info_dict.update(student_db)
# Tee Card Json
result = student.teeResultJson()
# list of out result in tee
count = result.get("count", 0)
# this for tee section in crawler
student_db = {
"tee": {
"count": count or 0,
"checked": today_date
},
}
# updating again info_dict with new tee student_db
info_dict.update(student_db)
# adding follower info in info_dict
info_dict.update(
{
"followers": {
str(callback_query.from_user.id): user_dict
}
}
)
# inserting first time student in crawlre db
await db.insert(
db.crawler,
info_dict
)
# if student is already in crawler db then
# just update following and followers secion
# in user db in crawler db
else:
await db.update(
db.crawler,
enrollment,
{
"$set": {
f"followers.{callback_query.from_user.id}": user_dict}
})
# remove inline_button from result
await callback_query.edit_message_reply_markup()
# push notification to user
await callback_query.answer(
f"{student_info.get('name')} Added in Watch List 🙃\n😇You will automatically receive result once it published in IGNOU 🌐",
show_alert=True)
# this conditon for remove followed user
# from crawler followers section and in user following section
elif "remove" in callback_query.data:
await db.update(
db.user,
callback_query.from_user.id,
{
"$unset": {
f"following.{enrollment}": ""
}
})
await db.update(
db.crawler,
enrollment,
{
"$unset": {
f"followers.{callback_query.from_user.id}": ""
}
}
)
await callback_query.answer("User Removed from Watchlist 👀")

141
bot/plugins/user.py Normal file
View file

@ -0,0 +1,141 @@
import re
from pyrogram import Client
from pyrogram import filters
from pyrogram.types import (
InlineKeyboardButton,
InlineKeyboardMarkup,
CallbackQuery,
Message
)
from bot.database import Database
from bot.helper.extractor import User, Student
from bot.config import Config
from bot.plugins.result import result_card
db = Database()
@Client.on_message(filters.command(['start', 'help']))
async def start(_, message):
if 'start' in message.text.lower():
if not await db.is_user_exist(message.from_user.id):
await db.add_user(message.from_user.id, message.from_user.first_name)
await message.reply_text(f"Welcome , {message.from_user.first_name} 🥳 \n \
[Click here for more details 🔍]({Config.HELP_URL})", disable_web_page_preview=True)
elif 'help' in message.text.lower():
await message.reply_text(f"{Config.HELP_URL}")
await db.update_last_used_on(message.from_user.id)
@Client.on_message(filters.command(["stats"]))
async def stats(client: Client,message : Message):
user: User = User(await db.get_user(message.from_user.id))
total_user = await db.total_users_count()
total_crawler = await db.total_crawlers_count()
total_following = len(user.following)
msg = f"""
Hi, {message.from_user.first_name}
Your Stat 🙃
TG 🆔 : {message.from_user.id}
Following 🕵 : {total_following}
"""
if user.is_admin or message.from_user.id in Config.SUDO_ADMIN:
try:
user_enrollment_not = re.findall("\d+",user.myenrollment)[0]
user_crawler_info: Student = Student(await db.get_student(user_enrollment_not))
msg += f" Followers 👼 : {len(user_crawler_info.followers)}\n"
except (IndexError, AttributeError, TypeError):
pass
site_info = await db.get_site_update("ignou")
grade_checked = site_info.get("grade_checked","error in monitoring")
tee_checked = site_info.get("tee_checked","error in monitoring ")
msg += f"""
{Config.USERNAME} Stat 🤖
Total User 🙆: {total_user}
Result Monitoring 😎: {total_crawler}
👀 Last Grade Card Checked
🕗 -> {grade_checked}
Last Tee Result Check
🕗 -> {tee_checked}
"""
await message.reply_text(msg)
@Client.on_callback_query(filters.regex("^user"))
async def user_info(_, callback_query: CallbackQuery):
_, enrollment = callback_query.data.split("_")
user: User = User(await db.get_user(callback_query.from_user.id))
student: Student = Student(await db.find(
db.crawler,
enrollment,
{"_id" : 0}
))
followed_by = len(student.followers)
msg_string = f"""👩🏻‍🎓 {student.name}
🆔 {enrollment} ({student.course})
Grade Card : {student.grade.passed+student.grade.failed} {student.grade.passed} {student.grade.failed}
Grade Card Updated on {student.grade.checked}
"""
if user.is_admin or callback_query.from_user.id in Config.SUDO_ADMIN:
msg_string += f"Followed by {followed_by} 👀"
await callback_query.answer(msg_string, show_alert=True)
@Client.on_message(filters.command(['watchlist']))
async def followed_list(_, message: Message):
user: User = User(await db.get_user(message.from_user.id))
if len(user.following) == 0:
await message.reply_text("Not followed anyone")
return
buttons = []
for enrollment, usr in user.following.items():
row = [
InlineKeyboardButton(
usr.get("name").split()[0],
callback_data=f"user_{enrollment}"
),
InlineKeyboardButton(
"🗑",
callback_data=f"remove_{usr.get('course')}_{enrollment}"
),
]
buttons.append(
row
)
await message.reply_text(
"<b>👩🏻‍🎓 Users in 👀 Watchlist</b>",
reply_markup=InlineKeyboardMarkup(buttons),
parse_mode="html")
@Client.on_message(filters.command(['last']))
async def last_result_check(_, message):
user: User = User(await db.get_user(message.from_user.id))
if user.enrollment:
await result_card(_, message)
else:
await message.reply_text("No recent result checked")

35
requirements.txt Normal file
View file

@ -0,0 +1,35 @@
aiohttp==3.8.4
aiosignal==1.3.1
APScheduler==3.10.0
async-timeout==4.0.2
asyncio==3.4.3
attrs==22.2.0
beautifulsoup4==4.11.2
certifi==2022.12.7
cffi==1.15.1
charset-normalizer==3.0.1
cryptography==39.0.1
dnspython==2.3.0
frozenlist==1.3.3
idna==3.4
lxml==4.9.2
motor==3.1.1
multidict==6.0.4
prettytable==3.6.0
pyaes==1.6.1
pycparser==2.21
pymongo==4.3.3
pyOpenSSL==23.0.0
Pyrogram==2.0.99
PySocks==1.7.1
pytz==2022.7.1
pytz-deprecation-shim==0.1.0.post0
requests==2.28.2
six==1.16.0
soupsieve==2.4
TgCrypto==1.2.5
tzdata==2022.7
tzlocal==4.2
urllib3==1.26.14
wcwidth==0.2.6
yarl==1.8.2

1
runtime.txt Normal file
View file

@ -0,0 +1 @@
python-3.9.2