Greasy Fork is available in English.

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.

Version au 14/03/2024. Voir la dernière version.

  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. })();