VJudge Cookie Filler

自动抓取允许在 VJ 上使用自己账号提交问题的 OJ 的 Cookie

  1. // ==UserScript==
  2. // @name VJudge Cookie Filler
  3. // @name:zh-CN VJudge Cookie Filler
  4. // @version 1.0.1
  5. // @description Auto fill cookies for those OJ which can submit problems on VJ by using their cookies.
  6. // @description:zh-CN 自动抓取允许在 VJ 上使用自己账号提交问题的 OJ 的 Cookie
  7. // @author konglils
  8. // @license MIT
  9. // @namespace https://github.com/konglils/vjudge-cookie-filler
  10. // @supportURL https://github.com/konglils/vjudge-cookie-filler
  11. // @grant GM_xmlhttpRequest
  12. // @grant GM_openInTab
  13. // @grant GM_cookie
  14. // @run-at document-end
  15. // @connect vjudge.net
  16. // @match https://vjudge.net/*
  17. // @match https://codeforces.com/*
  18. // @match https://qoj.ac/*
  19. // @match https://onlinejudge.org/*
  20. // @match https://www.luogu.com.cn/*
  21. // @match https://www.nowcoder.com/*
  22. // ==/UserScript==
  23.  
  24. (function() {
  25. 'use strict';
  26.  
  27. const OJ = {
  28. "CodeForces": {
  29. url: "https://codeforces.com/",
  30. cookieNames: ["JSESSIONID"],
  31. },
  32. "Gym": {
  33. url: "https://codeforces.com/",
  34. cookieNames: ["JSESSIONID"],
  35. },
  36. "QOJ": {
  37. url: "https://qoj.ac/",
  38. cookieNames: ["UOJSESSID"],
  39. },
  40. "UVA": {
  41. url: "https://onlinejudge.org/",
  42. cookieNames: ["8da11fa2dbfcbf0b9ab349d4b3eba6a3"],
  43. },
  44. "洛谷": {
  45. url: "https://www.luogu.com.cn/",
  46. cookieNames: ["__client_id", "_uid"],
  47. },
  48. "牛客": {
  49. url: "https://www.nowcoder.com/",
  50. cookieNames: ["t"],
  51. },
  52. };
  53.  
  54. const VERIFY_REGEX = /^\/user\/verifiedAccount\?oj=([^&]+)$/;
  55. const CHECK_REGEX = /^\/user\/checkAccount\?oj=([^&]+)$/;
  56.  
  57. const _originOpen = XMLHttpRequest.prototype.open;
  58. const _originSend = XMLHttpRequest.prototype.send;
  59.  
  60. XMLHttpRequest.prototype.open = function(method, url, async, user, password) {
  61. this._url = url;
  62. return _originOpen.apply(this, arguments);
  63. };
  64.  
  65. // hook
  66. XMLHttpRequest.prototype.send = function(body) {
  67. this.addEventListener("readystatechange", function() {
  68. if (location.hostname === "vjudge.net" && this.readyState === 4 && this.status === 200) {
  69. let isVerified = true;
  70. let oj = "";
  71.  
  72. // verify
  73. const matchVerify = this._url.match(VERIFY_REGEX);
  74. if (matchVerify) {
  75. oj = decodeURI(matchVerify[1]);
  76. const response = JSON.parse(this.responseText);
  77. if (Object.keys(response).length == 0) {
  78. isVerified = false;
  79. }
  80. }
  81.  
  82. // check
  83. const matchCheck = this._url.match(CHECK_REGEX);
  84. if (matchCheck) {
  85. oj = decodeURI(matchCheck[1]);
  86. const response = JSON.parse(this.responseText);
  87. if (Object.keys(response).length == 0) {
  88. isVerified = false;
  89. }
  90. }
  91.  
  92. // not login
  93. if (!isVerified && oj in OJ) {
  94. autoFill(oj);
  95. }
  96. }
  97. });
  98. return _originSend.apply(this, arguments);
  99. };
  100.  
  101. function autoFill(oj, info = "Auto filling ...") {
  102. updatePendInfo(info);
  103.  
  104. GM_cookie.list({ url: OJ[oj].url }, (cookies, error) => {
  105. if (error) {
  106. updateFailInfo(oj, "Failed to read cookies");
  107. console.error(`Failed to read ${oj} cookies: ` + error);
  108. return;
  109. }
  110.  
  111. // filter cookies in order
  112. let ojCookies = [];
  113. for (let name of OJ[oj].cookieNames) {
  114. for (let cookie of cookies) {
  115. if (cookie.name == name && cookie.value !== "") {
  116. ojCookies.push({ "name": name, "value": cookie.value });
  117. }
  118. }
  119. }
  120.  
  121. if (ojCookies.length !== OJ[oj].cookieNames.length) {
  122. loginToOJ(oj);
  123. return;
  124. }
  125.  
  126. GM_xmlhttpRequest({ // new verify request
  127. method: "POST",
  128. url: "/user/verifyAccount",
  129. headers: { "Content-Type": "application/json;charset=UTF-8" },
  130. data: JSON.stringify({ "oj": oj, "proof": ojCookies }),
  131. onload(response) {
  132. response = JSON.parse(response.responseText);
  133. if (response.success) {
  134. updateSuccessInfo(response.accountDisplay);
  135. } else {
  136. loginToOJ(oj);
  137. }
  138. },
  139. onerror(error) {
  140. updateFailInfo(oj, "Network error");
  141. console.error("Network error: " + error);
  142. }
  143. });
  144. });
  145. }
  146.  
  147. let loggingIn = false;
  148. let currentOJ = "";
  149.  
  150. function loginToOJ(oj) {
  151. if (loggingIn) {
  152. return;
  153. }
  154. updateFailInfo(oj, oj + " verify failed");
  155. loggingIn = true;
  156. currentOJ = oj;
  157. GM_openInTab(OJ[oj].url, {
  158. active: true, // new tab
  159. insert: true, // insert right
  160. });
  161. }
  162.  
  163. window.addEventListener("focus", () => {
  164. if (loggingIn) { // If try to login to OJ, and then focus back to VJ
  165. autoFill(currentOJ, "Logging in ...");
  166. }
  167. })
  168.  
  169. function updatePendInfo(info) {
  170. let accountStatus = document.getElementsByClassName("my-account-status")[0];
  171. let accountInfo = document.getElementsByClassName("my-account")[0];
  172.  
  173. accountStatus.innerHTML = '<i class="fa fa-refresh fa-spin" title="Checking..." data-toggle="tooltip"></i>';
  174. accountInfo.innerHTML = info;
  175. }
  176.  
  177. function updateSuccessInfo(info) {
  178. let accountStatus = document.getElementsByClassName("my-account-status")[0];
  179. let accountInfo = document.getElementsByClassName("my-account")[0];
  180. let removeAccount = document.getElementsByClassName("remove-my-account")[0];
  181.  
  182. accountStatus.innerHTML = '<i class="fa fa-check text-success" title="" data-toggle="tooltip" data-original-title="Connected"></i>';
  183. accountInfo.innerHTML = info;
  184. removeAccount.style.display = "inline";
  185.  
  186. loggingIn = false;
  187. }
  188.  
  189. function updateFailInfo(oj, info) {
  190. let accountStatus = document.getElementsByClassName("my-account-status")[0];
  191. let accountInfo = document.getElementsByClassName("my-account")[0];
  192.  
  193. accountStatus.innerHTML = '<i class="fa fa-exclamation-triangle text-danger" title="" data-toggle="tooltip" data-original-title="Disconnected. Please re-verify."></i>';
  194. accountInfo.innerHTML = `${info} <a href="javascript:void(0)" id="oj-login-retry">Retry</a> `;
  195.  
  196. let fillRetry = document.getElementById("oj-login-retry");
  197. fillRetry.onclick = () => { autoFill(oj) };
  198.  
  199. loggingIn = false;
  200. }
  201. })();