becoder

2024/1/23 15:25:23

  1. // ==UserScript==
  2. // @name becoder
  3. // @namespace Violentmonkey Scripts
  4. // @match https://www.becoder.com.cn/*
  5. // @grant none
  6. // @version 1.10
  7. // @author EarthMessenger
  8. // @description 2024/1/23 15:25:23
  9. // @grant GM.getValue
  10. // @grant unsafeWindow
  11. // @license MIT
  12. // ==/UserScript==
  13.  
  14. (() => {
  15. // utilities BEGIN
  16.  
  17. const removeNode = (element) => {
  18. if (element.parentNode) {
  19. element.parentNode.removeChild(element);
  20. }
  21. };
  22.  
  23. // utilities END
  24.  
  25. const fixRightClick = () => {
  26. unsafeWindow.oncontextmenu = null;
  27. document.oncontextmenu = null;
  28. };
  29.  
  30. const fixPDF = () => {
  31. const iframes = document.querySelectorAll("iframe");
  32. const pat =
  33. /https:\/\/www\.becoder\.com\.cn\/js\/pdf\/web\/viewer\.html\?file=(\S*)/gm;
  34. for (const oldPdf of iframes) {
  35. const res = pat.exec(oldPdf.src);
  36. if (res == null) continue;
  37. const url = res[1];
  38. fetch(url)
  39. .then((resp) => resp.blob())
  40. .then((blob) => blob.slice(0, blob.size, "application/pdf"))
  41. .then((blob) => {
  42. const newPdf = document.createElement("iframe");
  43. newPdf.src = URL.createObjectURL(blob);
  44. newPdf.width = oldPdf.width;
  45. newPdf.height = oldPdf.height;
  46. const parent = oldPdf.parentNode;
  47. parent.replaceChild(newPdf, oldPdf);
  48. });
  49. }
  50. };
  51.  
  52. const fixHomePage = () => {
  53. if (window.location.pathname !== "/index") return;
  54.  
  55. const mainContent = document.querySelector("body > main.main-content");
  56. removeNode(mainContent.querySelector(".carousel")); // AI 頭圖
  57. removeNode(mainContent.querySelector(".side-column > div:nth-child(2)")); // 提交統計
  58.  
  59.  
  60. Array.from(document.querySelectorAll("body > nav")) // NodeList is not array
  61. .slice(0, -1)
  62. .forEach((ele) => {
  63. removeNode(ele);
  64. });
  65.  
  66. document.body.style["background"] = "#f0f2f5";
  67. };
  68.  
  69. const redirectHomePage = () => {
  70. if (window.location.pathname === "/") {
  71. window.location.replace(new URL("/index", location.href).toString());
  72. }
  73. };
  74.  
  75. const fillLoginCredentials = async () => {
  76. if (window.location.pathname !== "/login") return;
  77.  
  78. // 根據 https://wiki.greasespot.net/GM.getValue,GM.getValue 應該是不支持 object 的,
  79. // 但是相信 2025 年不會有人使用 Greasemonkey 的。而且 Tampermonkey 和 Violentmonkey 都支持,
  80. // 那就假裝它確實是可行的。
  81. const credentials = await GM.getValue("credentials");
  82.  
  83. if (credentials === undefined) return;
  84.  
  85. try {
  86. const {username, password} = credentials;
  87.  
  88. document.getElementById("username").value = username;
  89. document.getElementById("password").value = password;
  90.  
  91. const captcha = document.getElementById("captcha");
  92.  
  93. const autoSubmitCaptcha = (ev) => {
  94. if (ev.target.value.length !== 4) return ;
  95. unsafeWindow.login();
  96. };
  97.  
  98. captcha.addEventListener("input", autoSubmitCaptcha);
  99.  
  100. captcha.focus();
  101. } catch (error) {
  102. console.error("填寫的憑據無效。");
  103. }
  104. };
  105.  
  106. const fixNavbar = () => {
  107. const body = document.body;
  108. const navbar = document.querySelector("body > nav > div");
  109.  
  110. if (navbar === null) return;
  111.  
  112. navbar.children[0].href = "/index"; // 舊主頁
  113.  
  114. const navbarNav = navbar.children[1];
  115. removeNode(navbarNav.children[6]); // 工具
  116. removeNode(navbarNav.children[0]); // 首頁
  117.  
  118. navbar.children[0].href = "/index"; // 使用舊首頁
  119. };
  120.  
  121. if (location.pathname.startsWith('/courses')) return;
  122.  
  123. redirectHomePage();
  124. fixRightClick();
  125. fixPDF();
  126. fixHomePage();
  127. fillLoginCredentials();
  128. fixNavbar();
  129. })();