commit f48aa34bc05b705f99ac03d121a25b66e83ec8aa Author: tomoron Date: Fri Mar 28 13:16:59 2025 +0100 initial commit diff --git a/__pycache__/login.cpython-312.pyc b/__pycache__/login.cpython-312.pyc new file mode 100644 index 0000000..d96f9c7 Binary files /dev/null and b/__pycache__/login.cpython-312.pyc differ diff --git a/login.py b/login.py new file mode 100755 index 0000000..435cb11 --- /dev/null +++ b/login.py @@ -0,0 +1,139 @@ +# **************************************************************************** # +# # +# ::: :::::::: # +# login.py :+: :+: :+: # +# +:+ +:+ +:+ # +# By: tomoron +#+ +:+ +#+ # +# +#+#+#+#+#+ +#+ # +# Created: 2024/11/25 16:22:08 by tomoron #+# #+# # +# Updated: 2025/03/26 13:10:42 by tomoron ### ########.fr # +# # +# **************************************************************************** # + +import requests +import time +import subprocess +import os +import re +import json +import urllib.parse +from getpass import getpass +from bs4 import BeautifulSoup + + +class Intra42(): + user_agent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0 .6167.160 Safari/537.36" + + def __init__(self): + self.cookieHeader = self.get_cookie_header() + print("token ready") + + def update_cookies(self, cookies, new_cookies, path): + cookies_arr = re.compile("^[A-Za-z_.0-9]+=.*?;", re.MULTILINE).findall("\n".join(new_cookies.split(', '))) + for x in cookies_arr: + cookies[x.split("=")[0]] = x.split("=")[1][:-1] + with open(os.path.expanduser(path), 'w') as f: + json.dump(cookies, f) + return(cookies) + + def cookie_to_header(self, cookies): + cookie_header = "" + for x in cookies: + cookie_header += x + "="+cookies[x] +"; " + cookie_header = cookie_header[:-2] + return(cookie_header) + + def login_user(self, username, password): + response = requests.get("https://profile.intra.42.fr/users/auth/keycloak_student", headers={"User-Agent": Intra42.user_agent}, allow_redirects=False) + login_url = response.headers["Location"] + response = requests.get(login_url, headers={"User-Agent":Intra42.user_agent}, allow_redirects=False); + cookies_arr = [x.split('; ')[0] for x in response.headers["Set-Cookie"].split(', ')] + cookies = {} + for x in cookies_arr: + cookies[x.split("=")[0]] = x.split("=")[1] + + page = BeautifulSoup(response.text, "html.parser") + form_html = page.find_all("form",id="kc-form-login")[0] + form_start = re.compile("^").findall(str(form_html))[0] + url = form_start.split(' ')[1].split("\"")[1] + + username = urllib.parse.quote(username) + password = urllib.parse.quote(password) + cookie_header = self.cookie_to_header(cookies) + req_body = f'username={username}&password={password}&rememberMe=on&credentialId=' + url = url.replace("amp;", "") + headers = { + "User-Agent":Intra42.user_agent, + "Cookie":cookie_header, + "Content-Type": "application/x-www-form-urlencoded", + } + response = requests.post(url, data=req_body ,headers=headers,allow_redirects=False) + if(response.status_code == 200): + return("error") + return(self.update_cookies(cookies, response.headers["Set-Cookie"],"~/.intra_refresh")) + + def refresh_session(self): + cookies = "error" + if(os.path.isfile(os.path.expanduser("~/.intra_refresh"))): + with open(os.path.expanduser("~/.intra_refresh"), 'r') as f: + cookies = json.load(f) + else: + while(cookies == "error"): + username = input("enter intra username :") + password = getpass("enter password :") + cookies = self.login_user(username, password) + response = requests.get("https://profile.intra.42.fr/users/auth/keycloak_student",headers={"User-Agent":Intra42.user_agent},allow_redirects=False) + intra_prod_cookie = response.cookies.get("_intra_42_session_production") + cookie_header = self.cookie_to_header(cookies) + response = requests.get(response.headers["Location"], headers={"User-Agent":Intra42.user_agent,"Cookie":cookie_header},allow_redirects=False) + if(response.status_code == 200): + os.remove(os.path.expanduser("~/.intra_refresh")) + return(self.refresh_session()) + self.update_cookies(cookies, response.headers["Set-Cookie"], "~/.intra_refresh") + response = requests.get(response.headers["Location"], headers={"User-Agent":Intra42.user_agent,"Cookie":f"_intra_42_session_production={intra_prod_cookie}"},allow_redirects=False) + profile_cookies = self.update_cookies({},response.headers["Set-Cookie"],"~/.intra_profile") + return(profile_cookies); + + def get_cookie_header(self): + profile_cookies = {} + if(os.path.isfile(os.path.expanduser("~/.intra_profile"))): + with open(os.path.expanduser("~/.intra_profile")) as f: + profile_cookies = json.load(f) + else: + profile_cookies = self.refresh_session() + return(self.cookie_to_header(profile_cookies)) + + def get_intra_home(self): + self.cookieHeader = self.get_cookie_header() + cookie_header = self.cookieHeader + cookie_header += "; intra=v2" + cookie_header += "; locale=en" + response = requests.get("https://profile.intra.42.fr/",headers = {"User-Agent":Intra42.user_agent,"Cookie":cookie_header},allow_redirects=False) + if(response.status_code == 302): + os.remove(os.path.expanduser("~/.intra_profile")) + return(get_intra_home(self.get_cookie_header())) + return(response.text) + + def get_goals(self): + self.cookieHeader = self.get_cookie_header() + response = requests.get("https://profile.intra.42.fr/users/me/goals?cursus=42cursus",headers = {"User-Agent":Intra42.user_agent,"Cookie":self.cookieHeader},allow_redirects=False) + if(response.status_code == 302): + os.remove(os.path.expanduser("~/.intra_profile")) + return(get_goals()) + return(response.text) + + def get_project_page(self, slug): + self.cookieHeader = self.get_cookie_header() + response = requests.get(f"https://projects.intra.42.fr/projects/{slug}",headers = {"User-Agent":Intra42.user_agent,"Cookie":self.cookieHeader},allow_redirects=False) + if(response.status_code == 302): + os.remove(os.path.expanduser("~/.intra_profile")) + return(get_project_page(slug)) + return(response.text) + + def get_available_slot(self, url): + self.cookieHeader = self.get_cookie_header() + response = requests.get(url, headers = { "User-Agent": Intra42.user_agent, "Cookie":self.cookieHeader}, allow_redirects=False) + if(response.status_code == 302): + os.remove(os.path.expanduser("~/.intra_profile")) + return(get_available_slot(url)) + return(json.loads(response.text)) diff --git a/scrap.py b/scrap.py new file mode 100644 index 0000000..a9d90e7 --- /dev/null +++ b/scrap.py @@ -0,0 +1,40 @@ +# **************************************************************************** # +# # +# ::: :::::::: # +# scrap.py :+: :+: :+: # +# +:+ +:+ +:+ # +# By: tomoron +#+ +:+ +#+ # +# +#+#+#+#+#+ +#+ # +# Created: 2024/11/25 16:39:19 by tomoron #+# #+# # +# Updated: 2025/03/28 13:13:48 by tomoron ### ########.fr # +# # +# **************************************************************************** # + +from login import Intra42 +from getpass import getpass +import re +import json +import threading +import time +import sys +import dateutil +from datetime import date, timedelta + +if(len(sys.argv) != 2): + print("missing team id, usage :", sys.argv[0], "") + exit(1); +project_id = sys.argv[1] +start_search_date = date.today().strftime("%Y-%m-%d") +end_search_date = (date.today() + timedelta(days=1)).strftime("%Y-%m-%d") +getUrl = f"https://projects.intra.42.fr/projects/{project_id}/slots.json?start={start_search_date}&end={end_search_date}" +connIntra = Intra42() +found = set(); +while(True): + res = connIntra.get_available_slot(getUrl) + for x in res: + if x["ids"] not in found: + start = dateutil.parser.isoparse(x["start"]).strftime("%d/%m %H:%M") + end = dateutil.parser.isoparse(x["end"]).strftime("%d/%m %H:%M") + print("\aslot found starting at", start, ",ending at", end); + found.add(x["ids"]) + time.sleep(1);