KiitConnect Unlocker

Free as in beer.

目前为 2024-05-04 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name KiitConnect Unlocker
  3. // @namespace https://www.kiitconnect.com/
  4. // @version 2.1
  5. // @license MIT
  6. // @description Free as in beer.
  7. // @author erucix
  8. // @match https://www.kiitconnect.com/*
  9. // @icon https://www.google.com/s2/favicons?sz=64&domain=kiitconnect.com
  10. // ==/UserScript==
  11.  
  12. (function () {
  13. 'use strict';
  14. // TODO: Make it false in production build or else code won't worlk.
  15.  
  16. let devModeFlag = true;
  17.  
  18. // If you find this flag as true please make it false.
  19.  
  20. // It is only for dev for debugging purpose and won't give you any
  21. // super powers
  22.  
  23. window.onload = function () {
  24. if (devModeFlag) {
  25. console.clear();
  26. console.log("[+] Unlocker Initiated.");
  27. }
  28.  
  29. if (false) {
  30. let script = document.createElement("script");
  31. script.src = "http://127.0.0.1:8080/kiit-connect.js";
  32. document.body.appendChild(script);
  33. } else {
  34. // Removes a node and replaces with its clone and helps in removing all event listeners.
  35. function remove(node) {
  36. node.classList.remove("text-yellow-500", "cursor-not-allowed");
  37. let cursorNotAllowedList = [...node.querySelectorAll(".cursor-not-allowed")];
  38.  
  39. cursorNotAllowedList.forEach(element => {
  40. element.classList.add("text-cyan-500");
  41. element.classList.remove("text-yellow-500", "cursor-not-allowed");
  42. });
  43.  
  44. if (node.innerText == "Not Available") {
  45. node.classList.add("whitespace-nowrap", "font-bold", "text-gray-400");
  46. } else {
  47. node.classList.add("text-cyan-500");
  48. }
  49.  
  50. var clonedNode = node.cloneNode(true);
  51. node.parentNode.replaceChild(clonedNode, node);
  52. }
  53.  
  54. // Its a single paged application so continuously check for change in URL instead of onload.
  55. setInterval(function () {
  56.  
  57. // Check if the script has already been loaded for given semester.
  58. if (!document.head.classList.contains(location.pathname)) {
  59.  
  60. // Clear all the previous class list.
  61. document.head.classList = "";
  62.  
  63. // Add a class value indicating the script has been loaded for this semester.
  64. document.head.classList.add(location.pathname);
  65.  
  66.  
  67.  
  68. let docRequired; // Type of document required. Can be: notes, pyqs
  69. let branchName; // Name of choosen branch. Can be: cse, csse, csce, it
  70. let semesterId; // Semester representation in number. Can be any between 1 to 6
  71.  
  72. let finalPath = location.pathname;
  73.  
  74. // Assign values only if we have sufficient information in URL.
  75.  
  76. if (finalPath.includes("/academic/PYQS") && finalPath != "/academic/PYQS") {
  77. finalPath = finalPath.substring(finalPath.lastIndexOf("/") + 1);
  78. finalPath = finalPath.split("-");
  79.  
  80. docRequired = location.search.substring(location.search.lastIndexOf("=") + 1);
  81. branchName = finalPath[1].toLowerCase();
  82. semesterId = Number(finalPath[2]);
  83.  
  84. // Fetching original source code so that we could get pdf links
  85. fetch(location.href)
  86. .then(data => data.text())
  87. .then(data => {
  88. // Making some adjustments to successfully parse it as JSON
  89.  
  90. let response = data.substring(data.lastIndexOf("1d:[\\") - 23);
  91. response = response.replace("self.__next_f.push(", "");
  92. response = response.substring(0, response.lastIndexOf(")</script>"));
  93.  
  94. let firstLevelRefined = JSON.parse(response);
  95. let secondLevelRefined = firstLevelRefined[1];
  96. secondLevelRefined = secondLevelRefined.substring(3);
  97.  
  98. let thirdLevelRefined = JSON.parse(secondLevelRefined);
  99.  
  100. let finalRefined = thirdLevelRefined[3].children[3].data.semesters[0].subjects;
  101.  
  102. if (devModeFlag) {
  103. console.log(finalRefined);
  104. }
  105.  
  106.  
  107. if (docRequired == "pyqs") {
  108. // Sort the list according to year
  109. finalRefined.forEach(element => {
  110. element.pyqs.sort((a, b) => {
  111. return Number(a.year) - Number(b.year);
  112. })
  113. });
  114.  
  115. if (devModeFlag) {
  116. console.log(finalRefined);
  117. }
  118.  
  119. let containerTables = document.querySelectorAll("table");
  120.  
  121. // Used for storing loaded table indexes. SO don't touch the table
  122. // again later.
  123. let storedIndexArray = [];
  124.  
  125. containerTables.forEach((element) => {
  126. let tableRowItems = [...element.querySelectorAll("tr")];
  127.  
  128. // Since the first 'tr' is table header
  129.  
  130. tableRowItems.shift();
  131.  
  132. // Find a table such that the [numbers of paper we have] = [number of rows in table]
  133. // This reduces hassle of creating a new table
  134.  
  135. let index = 0;
  136.  
  137. for (let i = 0; i < finalRefined.length; i++) { // Here we dont touch the loaded table
  138. if (finalRefined[i].pyqs.length == tableRowItems.length && !storedIndexArray.includes(i)) {
  139. index = i;
  140. storedIndexArray.push(i);
  141. break;
  142. }
  143. }
  144.  
  145. let choosenSubject = finalRefined[index].pyqs;
  146.  
  147.  
  148. // Overwrite the 'td' values by our better sorted list
  149. tableRowItems.forEach((item, index1) => {
  150. let tdList = item.querySelectorAll("td");
  151.  
  152. tdList[0].innerText = choosenSubject[index1].year;
  153. tdList[1].innerText = choosenSubject[index1].type;
  154.  
  155. let anchor1 = tdList[2].querySelector("a");
  156. anchor1.innerText = choosenSubject[index1].name;
  157. anchor1.href = "https://drive.google.com/file/d/" + choosenSubject[index1].Question;
  158.  
  159. // Replace the anchor
  160. remove(anchor1);
  161.  
  162. let anchor2 = tdList[3].querySelector("a");
  163.  
  164. if (choosenSubject[index1].solution != null) {
  165. if (anchor2 == null) {
  166. anchor2 = document.createElement("a");
  167. anchor2.target = "_blank";
  168.  
  169. tdList[3].innerText = "";
  170. tdList[3].appendChild(anchor2);
  171. }
  172.  
  173. anchor2.innerText = "Solution";
  174. anchor2.href = "https://drive.google.com/file/d/" + choosenSubject[index1].solution;
  175. } else {
  176. if (anchor2 != null) {
  177. anchor2.innerHTML = "Not Available";
  178. }
  179. }
  180.  
  181. if (anchor2 != null) {
  182. remove(anchor2);
  183. }
  184.  
  185. });
  186. });
  187. } else if (docRequired == "notes") {
  188.  
  189. let subjectNoteContainer = [...document.querySelector("main").querySelector("div").querySelector("div").childNodes];
  190.  
  191. // Since first one is unwanted jargon div.
  192. subjectNoteContainer.shift();
  193.  
  194. // Store manipulated div that should not be re-touched.
  195.  
  196. let storedIndexArray = [];
  197.  
  198. subjectNoteContainer.forEach(element => {
  199. let allHeadings = [...element.querySelectorAll("h1")];
  200.  
  201. let index = 0;
  202.  
  203. for (let i = 0; i < finalRefined.length; i++) {
  204. if (finalRefined[i].notes.length == allHeadings.length && !storedIndexArray.includes(i)) {
  205. index = i;
  206. storedIndexArray.push(i);
  207. break;
  208. }
  209. }
  210.  
  211. let currentSubject = finalRefined[index].notes;
  212.  
  213. allHeadings.forEach((element, index1) => {
  214. element.parentElement.parentElement.href = "https://drive.google.com/file/d/" + currentSubject[index1].Notes;
  215. element.innerText = currentSubject[index1].name;
  216.  
  217. remove(element.parentElement.parentElement);
  218. });
  219. });
  220. }
  221. });
  222. }
  223.  
  224. // Remove all get premium buttons from site.
  225. setTimeout(function () {
  226. let premiumButtons = document.querySelectorAll("a[href='/premiuminfo']");
  227. premiumButtons.forEach(item => item.remove());
  228.  
  229. }, 1000);
  230. }
  231. }, 500);
  232. }
  233. }
  234. })();