GitHub Device Auth and Confirmation Autofill and Form Submit

Automatically fill and submit GitHub device authorization code, and handle confirmation page by form submission after fully loaded.

Tính đến 14-03-2024. Xem phiên bản mới nhất.

  1. // ==UserScript==
  2. // @name GitHub Device Auth and Confirmation Autofill and Form Submit
  3. // @namespace http://tampermonkey.net/
  4. // @version 1.0
  5. // @description Automatically fill and submit GitHub device authorization code, and handle confirmation page by form submission after fully loaded.
  6. // @author NingMengGuoRou
  7. // @match https://github.com/login/device*
  8. // @grant GM_xmlhttpRequest
  9. // @license MIT
  10. // ==/UserScript==
  11. (function() {
  12. 'use strict';
  13. // const fetchTokenUrl = '改成你自己的http(s)://ip:端口';
  14. const fetchTokenUrl = 'https://xxxx:9191';
  15. async function getUserCode() {
  16. return new Promise((resolve, reject) => {
  17. GM_xmlhttpRequest({
  18. method: "GET",
  19. url: fetchTokenUrl+"/get_user_code",
  20. onload: function(response) {
  21. try {
  22. const data = JSON.parse(response.responseText);
  23. if (data.user_code) {
  24. resolve(data.user_code);
  25. } else {
  26. reject("Failed to get user code");
  27. }
  28. } catch (e) {
  29. reject(e.toString());
  30. }
  31. },
  32. onerror: function(error) {
  33. reject(error.toString());
  34. }
  35. });
  36. });
  37. }
  38. function waitForElement(selector, delay = 50, maxAttempts = 20) {
  39. return new Promise((resolve, reject) => {
  40. let attempts = 0;
  41. const interval = setInterval(() => {
  42. const element = document.querySelector(selector);
  43. attempts++;
  44. if (element) {
  45. clearInterval(interval);
  46. resolve(element);
  47. } else if (attempts >= maxAttempts) {
  48. clearInterval(interval);
  49. reject(new Error(`Element ${selector} not found`));
  50. }
  51. }, delay);
  52. });
  53. }
  54. async function fillAndSubmitCode() {
  55. try {
  56. const userCode = await getUserCode();
  57. const codeParts = userCode.split('-');
  58. if (codeParts.length !== 2) {
  59. console.error("Invalid user code format.");
  60. return;
  61. }
  62. for (let i = 0; i < codeParts[0].length; i++) {
  63. waitForElement(`#user-code-${i}`).then(el => el.value = codeParts[0][i]);
  64. }
  65. for (let i = 0; i < codeParts[1].length; i++) {
  66. waitForElement(`#user-code-${i + 5}`).then(el => el.value = codeParts[1][i]);
  67. }
  68. waitForElement('input[type="submit"][name="commit"]').then(button => setTimeout(() => button.click(), 1000));
  69. } catch (error) {
  70. console.error(error);
  71. }
  72. }
  73. // function autoSubmitFormOnConfirmation() {
  74. // const observer = new MutationObserver((mutations, obs) => {
  75. // const form = document.querySelector('form');
  76. // if (form) {
  77. // const authorizeButton = form.querySelector('button[name="authorize"][value="1"]');
  78. // if (authorizeButton) {
  79. // setTimeout(() => {
  80. // form.submit();
  81. // obs.disconnect();
  82. // }, 1000);
  83. // }
  84. // }
  85. // });
  86. // observer.observe(document.body, { childList: true, subtree: true });
  87. // }
  88. function autoSubmitFormOnConfirmation() {
  89. console.log("--------------------------------------------------------")
  90. window.addEventListener('load', () => {
  91. waitForElement('form[action="/login/device/authorize"] button[name="authorize"][value="1"]')
  92. .then(button => setTimeout(() => button.click(), 1000))
  93. .catch(error => console.error(error));
  94. });
  95. }
  96. if (window.location.pathname.includes('/login/device')) {
  97. fillAndSubmitCode();
  98. }
  99. if (window.location.pathname.includes('/login/device/confirmation')) {
  100. autoSubmitFormOnConfirmation();
  101. }
  102. if (window.location.pathname.includes('/login/device/success')) {
  103. // Add a delay before executing the GM_xmlhttpRequest call
  104. setTimeout(function() {
  105. GM_xmlhttpRequest({
  106. method: 'GET',
  107. url: fetchTokenUrl+"/success",
  108. onload: function(response) {
  109. try {
  110. const data = JSON.parse(response.responseText);
  111. console.log(data);
  112. if (data.token) {
  113. const message = document.createElement('div');
  114. message.style.position = 'fixed';
  115. message.style.left = '0';
  116. message.style.top = '0'; // Changed from bottom to top
  117. message.style.width = '100%';
  118. message.style.backgroundColor = 'green';
  119. message.style.color = 'white';
  120. message.style.textAlign = 'center';
  121. message.style.padding = '10px';
  122. message.style.fontSize = '20px';
  123. message.style.zIndex = '1000'; // Ensure it's on top of other elements
  124. message.innerText = 'Authorization successful! You can now close this window GHU:'+data.token;
  125. document.body.appendChild(message);
  126. } else {
  127. console.error("Token fetch error: ", data.error);
  128. }
  129. } catch (e) {
  130. console.error("Response parsing error: ", e);
  131. }
  132. },
  133. onerror: function(error) {
  134. console.error("Request failed: ", error);
  135. }
  136. });
  137. }, 1000); // Delay set to 5000 milliseconds (5 seconds)
  138. }
  139. })();