KIIT-Connect Unlocker

Unlock kiitconnect for free

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。

您需要先安装用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         KIIT-Connect Unlocker
// @namespace    https://github.com/rohitaryal/kiitconnect-unlocker
// @version      3.0
// @description  Unlock kiitconnect for free
// @author       rohitaryal
// @match        https://www.kiitconnect.com/academic/*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=kiitconnect.com
// @grant        none
// @license      MIT
// ==/UserScript==

const debugMode = true;

const driveFile = "https://drive.google.com/file/d/";
const driveFolder = "https://drive.google.com/drive/folders/";
const youtubePlaylist = "https://www.kiitconnect.com/player?playlistId=";

const pyqsRegex = /academic\/PYQS\/Content\/PYQS\-(cse|csse|csce|it)\-[1-7]/;
const notesRegex = /academic\/PYQS\/Content\/Notes\-(cse|csse|csce|it)\-[1-7]/;

class Utils {
  constructor() { }
  /*
   * Get all script that doesn't have src attribute
   */
  static getScripts() {
    let scriptElements = document.querySelectorAll("script:not([src])");
    return [...scriptElements];
  }

  /*
   * Copy item to clipboard
   */
  static copy(text) {
    document.body.focus();
    navigator.clipboard.writeText(text);
  }

  /*
   * Logger for debug mode
   */
  static log(text) {
    if (debugMode) {
      console.log(text)
    }
  }

  /*
   * Clone node and remove its junk and replace with new one
   */
  static replaceNode(node) {
    if (!node) {
      throw new Error("Invalid node provided to clone");
    }

    const newNode = node.cloneNode(true);
    node.replaceWith(newNode);

    return newNode;
  }

  /*
   * Re-Serialize the json in our own way (For PYQS)
   */
  static parsePYQ(data) {
    if (typeof data !== 'object') {
      throw new Error(`Expected 'object' but obtained '${typeof data}' while parsing`);
    }

    // Skeleton for subjects pyq, syllabus + some extra detail holder
    let pyqDetails = {
      semester: data.semeseter, // Yes, thats the actual spelling
      branch: data.stream,
      subjects: [],
    };

    pyqDetails.subjects = data.data.semesters[0].subjects.map((subject) => {
      // Skeleton for subject detail holder
      let subDetails = {
        subjectName: subject.name,
        subjectCode: subject.SUBCODE,
        folder: driveFolder + subject.folderId, // Makes an accessable drive folder link
        subjectSyllabus: driveFile + subject.syllabus, // Makes an accessable drive file link
        playlist: [],
        papers: [],
      };

      // Parse individual playlist
      subDetails.playlist = subject.youtubePlaylist.map((playlist) => {
        return {
          id: playlist.id,
          title: playlist.title,
          videoCount: playlist.noOfVides,
          videoLink: `${youtubePlaylist}id&playlist=${playlist.title}`
        };
      });

      // Parse individual pyqs
      subDetails.papers = subject.pyqs.map((paper) => {
        return {
          name: paper.name,
          year: Number(paper.year) || 0,
          solution: driveFile + paper.solution, // Makes an accessable file link
          question: driveFile + paper.Question, // Same
        }
      });

      // Sort papers based on years
      subDetails.papers = subDetails.papers.sort((a, b) => a.year - b.year);

      return subDetails;
    });

    return pyqDetails;
  }

  /*
   * Re-Serialize the json in our own way (For Notes)
   */
  static parseNotes(data) {

  }

  /*
   * Parse script content (For PYQS)
   */
  static parseFromScriptPYQ(scriptContent) {
    // Pre-process and beautufy
    scriptContent = scriptContent.replaceAll(`\\"`, `"`);
    scriptContent = scriptContent.replaceAll(`\\n`, ``);

    const parsableContent = scriptContent.slice(
      scriptContent.indexOf(`self.__next_f.push([1,"1e:`) + 26, // <-- 26 is the length of this
      scriptContent.lastIndexOf(`"])`)                          //     string itself.
    );

    const jsonContent = JSON.parse(parsableContent)[3].children[3];

    return this.parsePYQ(jsonContent);
  }

  /*
   * Parse script content (For Notes)
   */
  static parseFromScriptNotes(scriptContent) {

  }

  /*
   * Replace pyq table + headings with from our own serialized form
   */
  static rewritePYQPage(subjectArray) {
    if (typeof subjectArray !== 'object') {
      throw new Error(`Expected 'object' but obtained '${typeof subjectArray}' while rewriting`);
    }

    // After successful injection head will contain this classlist
    // which is an indicator to stop further injection
    if(document.head.classList.contains("injected")) {
      return;
    }

    Utils.log("Re-writing the pyq page.")

    const parentContainer = [...document.querySelector("main div div").childNodes].slice(1);
    
    for (let i = 0; i < parentContainer.length; i++) {
      let totalRows = parentContainer[i].querySelectorAll("table tbody").length; // Get count of rows
      for (let j = 0; j < subjectArray.length; j++) {
          let totalPapers = subjectArray[j].papers.length;
  
          if (totalRows === totalPapers) {
              let temp = subjectArray[j];
              subjectArray[j] = subjectArray[i];
              subjectArray[i] = temp;
              break;
          }
      }
  }
  

    for (let i = 0; i < subjectArray.length; i++) {
      const subject = subjectArray[i];
      const parent = parentContainer[i];
      const [titleDiv, tableContainer] = parent.childNodes;

      const syllabusButton = titleDiv.querySelector("a");
      syllabusButton.href = subject.subjectSyllabus;

      // New button with folder link
      const folderButton = syllabusButton.cloneNode(true);
      folderButton.innerText = "Open Folder";
      folderButton.href = subject.folder;

      
      // Modify title (middle one is TEXT_NODE)
      const title = titleDiv.childNodes[1];
      title.textContent = subject.subjectName;
      
      // Modify the table
      const tableRows = tableContainer.querySelectorAll("table tbody");

      for (let j = 0; j < tableRows.length; j++) {
        const row = tableRows[j];
        const paper = subject.papers[j];

        const td = row.querySelectorAll("td:not(.hidden)");

        // Year of paper
        td[0].textContent = paper.year;

        // Link to question paper
        let paperAnchor = td[1].querySelector("a");
        paperAnchor = this.replaceNode(paperAnchor);

        paperAnchor.textContent = paper.name;
        paperAnchor.href = paper.question;
        paperAnchor.classList.add("text-cyan-500");
        paperAnchor.classList.remove("text-yellow-500", "cursor-not-allowed");

        // Link to solution paper
        let solutionAnchor = td[2].querySelector("a");

        // Since solution is the last element
        if(!solutionAnchor)
          continue;

        solutionAnchor = this.replaceNode(solutionAnchor);

        if (solutionAnchor.classList.contains("text-gray-400")) {
          solutionAnchor.textContent = "Not Available";
        } else {
          solutionAnchor.classList.add("text-cyan-500");
          solutionAnchor.classList.remove("text-yellow-500", "cursor-not-allowed");

          solutionAnchor.href = paper.solution
        }
      }
    }

    // Mark as succrssful injection
    document.head.classList.add("injected");
  }
}

(function () {
  'use strict';
  // Last script will always contain the paper's JSON
  let scriptContent = Utils.getScripts().at(-1).textContent;


  if (!!location.pathname.match(pyqsRegex)) { // Means we are in 'pyqs' page
    const parsedContent = Utils.parseFromScriptPYQ(scriptContent);

    window.addEventListener('load', function () {
      Utils.log("DOMContent has been loaded");
      this.setInterval(function () {
        try {
          Utils.rewritePYQPage(parsedContent.subjects);
        } catch(e) {
          console.log(e)
        }
      }, 2000)
    });

  } else if (!!location.pathname.match(notesRegex)) { // Means we are in 'notes' page

  }
})();