IG-Add2Lib

indiegala 快速领取免费游戏

  1. // ==UserScript==
  2. // @name IG-Add2Lib
  3. // @namespace IG-Add2Lib
  4. // @version 1.1.1
  5. // @description indiegala 快速领取免费游戏
  6. // @author HCLonely
  7. // @license MIT
  8. // @iconURL https://auto-task-test.hclonely.com/img/favicon.ico
  9. // @homepage https://github.com/HCLonely/IG-Helper/
  10. // @supportURL https://github.com/HCLonely/IG-Helper/issues/
  11.  
  12. // @include *://keylol.com/*
  13. // @include *://www.indiegala.com/*
  14.  
  15. // @grant GM_addStyle
  16. // @grant GM_xmlhttpRequest
  17. // @grant GM_registerMenuCommand
  18. // @grant GM_cookie
  19. // @grant unsafeWindow
  20.  
  21. // @require https://cdn.jsdelivr.net/npm/jquery@3.4.1/dist/jquery.slim.min.js
  22. // @require https://cdn.jsdelivr.net/npm/regenerator-runtime@0.13.7/runtime.min.js
  23. // @require https://cdn.jsdelivr.net/npm/sweetalert2@9
  24. // @require https://cdn.jsdelivr.net/npm/promise-polyfill@8.1.3/dist/polyfill.min.js
  25. // @require https://greasyfork.org/scripts/418102-tm-request/code/TM_request.js?version=902218
  26. // @connect indiegala.com
  27. // @run-at document-end
  28. // @noframes
  29. // ==/UserScript==
  30.  
  31. /* global addToIndiegalaLibrary, syncIgLib */
  32. (function () {
  33. if (window.location.host === 'www.indiegala.com') {
  34. return;
  35. }
  36. function addButton () {
  37. for (const el of $('a[href*=".indiegala.com/"]:not(".ig-add2lib")')) {
  38. const $this = $(el).addClass('ig-add2lib');
  39. const href = $this.attr('href');
  40. if (/^https?:\/\/.+?\.indiegala\.com\/.+$/.test(href) && !['/login', '/library'].includes(new URL(href).pathname)) {
  41. $this.after(`<a class="add-to-library" href="javascript:void(0)" onclick="addToIndiegalaLibrary(this)" data-href="${href}" target="_self">入库</a>`);
  42. }
  43. }
  44. }
  45. unsafeWindow.addToIndiegalaLibrary = async function (el) {
  46. const href = typeof el === 'string' ? el : $(el).attr('data-href');
  47. Swal.fire({
  48. title: '正在获取入库链接...',
  49. text: href,
  50. icon: 'info'
  51. });
  52. const [url, csrf_token] = await TM_request({
  53. url: href,
  54. method: 'GET',
  55. anonymous: false,
  56. timeout: 30000,
  57. retry: 3
  58. })
  59. .then(response => {
  60. if (!response.responseText) {
  61. console.error(response);
  62. return null;
  63. }
  64. const pageId = response.responseText.match(/dataToSend\.(gala_page_)?id[\s]*?=[\s]*?'(.*?)';/)?.[2];
  65. if (!pageId) {
  66. if (response.responseText.includes('loginRedirect')) {
  67. Swal.update({
  68. title: '请先登录!',
  69. text: 'https://www.indiegala.com/login',
  70. icon: 'error'
  71. });
  72. return null;
  73. }
  74. Swal.update({
  75. title: '获取入库Id失败!',
  76. text: href,
  77. icon: 'error'
  78. });
  79. console.error(response);
  80. return null;
  81. }
  82. const csrf_token = response.responseText.match(/<input name="csrfmiddlewaretoken".+?value="(.+?)"/)?.[1];
  83. return [new URL(`/developers/ajax/add-to-library/${pageId}/${new URL(href).pathname.replace(/\//g, '')}/${new URL(href).hostname.replace('.indiegala.com', '')}`, href).href, csrf_token];
  84. })
  85. .catch(error => {
  86. console.error(error);
  87. return null;
  88. })
  89. if (!url || !csrf_token) {
  90. Swal.update({
  91. title: '获取入库链接失败!',
  92. text: href,
  93. icon: 'error'
  94. });
  95. return null;
  96. }
  97. Swal.update({
  98. title: '正在入库...',
  99. text: href,
  100. icon: 'info'
  101. });
  102. return TM_request({
  103. url,
  104. method: 'POST',
  105. responseType: 'json',
  106. nocache: true,
  107. headers: {
  108. 'content-type': 'application/json',
  109. "X-CSRF-Token": csrf_token
  110. },
  111. timeout: 30000,
  112. retry: 3
  113. })
  114. .then(response => {
  115. if (response.response?.status === 'ok') {
  116. Swal.update({
  117. title: '入库成功!',
  118. text: href,
  119. icon: 'success'
  120. });
  121. if (syncIgLib) {
  122. syncIgLib(false, false).then(allGames => {
  123. for (const el of $('a[href*=".indiegala.com/"]')) {
  124. const $this = $(el).addClass('ig-checked');
  125. const href = $this.attr('href');
  126. if (/^https?:\/\/[\w\d]+?\.indiegala\.com\/.+$/.test(href) && allGames.includes(new URL(href).pathname.replace(/\//g, ''))) {
  127. $this.addClass('ig-owned');
  128. }
  129. }
  130. })
  131. }
  132. return true;
  133. } else if (response.response?.status === 'added') {
  134. Swal.update({
  135. title: '已在库中!',
  136. text: href,
  137. icon: 'warning'
  138. });
  139. return true
  140. } else if (response.response?.status === 'login' || response.response?.status === 'auth') {
  141. Swal.fire({
  142. title: '请先登录!',
  143. icon: 'error',
  144. showCancelButton: true,
  145. confirmButtonText: '登录',
  146. cancelButtonText: '关闭'
  147. }).then(({ value }) => {
  148. if (value) {
  149. window.open('https://www.indiegala.com/login', '_blank');
  150. }
  151. });
  152. return false;
  153. } else {
  154. console.error(response);
  155. Swal.update({
  156. title: '入库失败!',
  157. text: href,
  158. icon: 'error'
  159. });
  160. return null;
  161. }
  162. })
  163. }
  164. GM_registerMenuCommand('入库所有', async () => {
  165. const links = $.makeArray($('a.add-to-library')).map((e, i) => {
  166. return $(e).prev().hasClass('ig-owned') ? null : $(e).attr('data-href');
  167. }).filter(e => e);
  168. const newLinks = [...new Set(links)];
  169. const failedLinks = [];
  170. for (const link of newLinks) {
  171. const result = await addToIndiegalaLibrary(link);
  172. if (result === false) {
  173. break;
  174. }
  175. if (!result) {
  176. failedLinks.push(`<a href="${link}" target=_blank">${link}</a>`);
  177. }
  178. }
  179. if (failedLinks.length === 0) {
  180. Swal.fire({
  181. title: '全部任务完成!',
  182. icon: 'success'
  183. });
  184. } else {
  185. Swal.fire({
  186. title: '以下任务未完成!',
  187. icon: 'warning',
  188. html: failedLinks.join('<br/>')
  189. });
  190. }
  191. })
  192. function getCookies() {
  193. return new Promise((resolve, reject) => {
  194. GM_cookie.list({ url: 'https://www.indiegala.com/library/showcase/1' }, function (cookies, error) {
  195. if (!error) {
  196. resolve(cookies.map((c) => `${c.name}=${c.value}`).join(';'));
  197. } else {
  198. reject(error);
  199. }
  200. });
  201. });
  202. }
  203. GM_addStyle('.add-to-library{margin-left:10px;}');
  204. addButton();
  205. const observer = new MutationObserver(addButton);
  206. observer.observe(document.documentElement, {
  207. attributes: true,
  208. characterData: true,
  209. childList: true,
  210. subtree: true
  211. });
  212. })();