Greasy Fork is available in English.

UserscriptAPIWeb

https://gitee.com/liangjiancang/userscript/tree/master/lib/UserscriptAPI

Versione datata 10/10/2021. Vedi la nuova versione l'ultima versione.

Questo script non dovrebbe essere installato direttamente. È una libreria per altri script da includere con la chiave // @require https://update.greasyfork.org/scripts/432003/977807/UserscriptAPIWeb.js

  1. /**
  2. * UserscriptAPIWeb
  3. *
  4. * 依赖于 `UserscriptAPI`。
  5. *
  6. * 需要通过 `@grant` 引入 `GM_xmlhttpRequest` 或 `GM_download`。
  7. * @version 1.2.1.20211010
  8. * @author Laster2800
  9. * @see {@link https://gitee.com/liangjiancang/userscript/tree/master/lib/UserscriptAPI UserscriptAPI}
  10. */
  11. class UserscriptAPIWeb {
  12. /**
  13. * @param {UserscriptAPI} api `UserscriptAPI`
  14. */
  15. constructor(api) {
  16. this.api = api
  17. }
  18.  
  19. /**
  20. * @typedef {XMLHttpRequest} GM_XHR GM 定义的类 `XMLHttpRequest` 对象
  21. */
  22. /**
  23. * 发起网络请求,获取 `GM_XHR`
  24. * @param {Object} details 定义及细节类似于 `GM_xmlhttpRequest` `details`
  25. * @param {'GET' | 'HEAD' | 'POST'} [details.method='GET'] `METHOD`
  26. * @param {string} [details.url] `URL`
  27. * @param {number} [details.timeout] 超时时间
  28. * @param {(xhr: GM_XHR) => void} [details.ontimeout] 超时回调
  29. * @param {(xhr: GM_XHR) => void} [details.onerror] 错误回调
  30. * @param {(xhr: GM_XHR) => void} [details.onload] 加载回调
  31. * @param {string | URLSearchParams | FormData} [details.data] `DATA`
  32. * @param {Object} [options] 选项
  33. * @param {(xhr: GM_XHR) => boolean} [options.check] 检查 `GM_XHR` 是否符合条件
  34. * @param {boolean} [options.throwOnFailed = true] 失败时是否抛出异常,否则打印错误信息
  35. * @returns {Promise<GM_XHR>} `GM_XHR`
  36. * @throws 等待超时、达成终止条件、等待错误时抛出
  37. * @see {@link https://www.tampermonkey.net/documentation.php#GM_xmlhttpRequest GM_xmlhttpRequest}
  38. */
  39. requestXHR(details, options) {
  40. if (details) {
  41. const { api } = this
  42. const { check, throwOnFailed = true } = options ?? {}
  43. return new Promise((resolve, reject) => {
  44. if (details.data && details.data instanceof URLSearchParams) {
  45. details.data = details.data.toString()
  46. details.headers = {
  47. 'content-type': 'application/x-www-form-urlencoded',
  48. ...details.headers,
  49. }
  50. if (GM_info.scriptHandler === 'Violentmonkey' && !details.headers.origin) {
  51. details.headers.origin = ''
  52. }
  53. }
  54. details.ontimeout ??= xhr => fail('request: TIMEOUT', details, xhr)
  55. details.onerror ??= xhr => fail('request: ERROR', details, xhr)
  56. details.onload ??= xhr => {
  57. if (check && !check(xhr)) {
  58. fail('request: CHECK-FAIL', details, check, xhr)
  59. if (throwOnFailed) return
  60. }
  61. resolve(xhr)
  62. }
  63. GM_xmlhttpRequest(details)
  64.  
  65. function fail(msg, ...cause) {
  66. if (throwOnFailed) {
  67. reject(new Error(msg, cause.length > 0 ? { cause } : undefined))
  68. } else {
  69. api.logger.error(msg, ...cause)
  70. }
  71. }
  72. })
  73. }
  74. }
  75.  
  76. /**
  77. * 发起网络请求,获取解析结果
  78. * @param {Object} details 定义及细节类似于 `GM_xmlhttpRequest` `details`
  79. * @param {'GET' | 'HEAD' | 'POST'} [details.method='GET'] `METHOD`
  80. * @param {string} [details.url] `URL`
  81. * @param {number} [details.timeout] 超时时间
  82. * @param {(xhr: GM_XHR) => void} [details.ontimeout] 超时回调
  83. * @param {(xhr: GM_XHR) => void} [details.onerror] 错误回调
  84. * @param {(xhr: GM_XHR) => void} [details.onload] 加载回调
  85. * @param {string | URLSearchParams | FormData} [details.data] `DATA`
  86. * @param {Object} [options] 选项
  87. * @param {'json' | 'check' | 'silentCheck'} [options.parser='json'] ```text
  88. * json: 返回 JSON.parse(resp)
  89. * check: 返回 check(resp, xhr),检查失败时打印信息
  90. * silentCheck: 返回 check(resp, xhr),检查失败时不打印信息
  91. * ```
  92. * @param {(resp: Object, xhr: GM_XHR) => boolean} [options.check] 检查 `GM_XHR` 是否符合条件
  93. * @param {boolean} [options.throwOnFailed=true] 失败时是否抛出异常,否则打印错误信息
  94. * @returns {Promise<Object>} 解析结果
  95. * @see {@link https://www.tampermonkey.net/documentation.php#GM_xmlhttpRequest GM_xmlhttpRequest}
  96. */
  97. async request(details, options) {
  98. const { api } = this
  99. const { parser = 'json', check, throwOnFailed = true } = options ?? {}
  100. try {
  101. const xhr = await this.requestXHR(details)
  102. let resp = null
  103. try {
  104. resp = JSON.parse(xhr.response)
  105. } catch {
  106. fail('request: PARSE', details, xhr)
  107. return null
  108. }
  109. const checkResult = !check || check(resp, xhr)
  110. if (parser === 'silentCheck') {
  111. return checkResult
  112. } else if (parser === 'check') {
  113. if (!checkResult) {
  114. api.logger.error('request: CHECK-FAIL', details, check, resp, xhr)
  115. }
  116. return checkResult
  117. } else {
  118. if (!checkResult) {
  119. fail('request: CHECK-FAIL', details, check, resp, xhr)
  120. }
  121. return resp
  122. }
  123. } catch (e) {
  124. if (throwOnFailed) {
  125. throw e
  126. } else {
  127. api.logger.error(e)
  128. }
  129. }
  130.  
  131. function fail(msg, ...cause) {
  132. if (throwOnFailed) {
  133. throw new Error(msg, cause.length > 0 ? { cause } : undefined)
  134. } else {
  135. api.logger.error(msg, ...cause)
  136. }
  137. }
  138. }
  139.  
  140. /**
  141. * 下载资源
  142. * @param {Object} details 定义及细节同 `GM_download` `details`
  143. * @returns {() => void} 用于终止下载的方法
  144. * @see {@link https://www.tampermonkey.net/documentation.php#GM_download GM_download}
  145. */
  146. download(details) {
  147. if (details) {
  148. const { api } = this
  149. try {
  150. let { name } = details
  151. if (name.includes('.')) {
  152. // name「.」后内容会被误认为后缀导致一系列问题,从 URL 找出真正的后缀名以修复之
  153. let parts = details.url.split('/')
  154. const last = parts.at(-1).split('?')[0]
  155. if (last.includes('.')) {
  156. parts = last.split('.')
  157. name = `${name}.${parts.at(-1)}`
  158. } else {
  159. name = name.replaceAll('.', '_') // 实在找不到后缀时才用这种消极的方案
  160. }
  161. details.name = name
  162. }
  163. details.onerror ??= (error, details) => api.logger.error('download: ERROR', error, details)
  164. details.ontimeout ??= () => api.logger.error('download: TIMEOUT')
  165. GM_download(details)
  166. } catch (e) {
  167. api.logger.error('download: ERROR', e)
  168. }
  169. }
  170. return () => {}
  171. }
  172. }
  173.  
  174. /* global UserscriptAPI */
  175. // eslint-disable-next-line no-lone-blocks
  176. { UserscriptAPI.registerModule('web', UserscriptAPIWeb) }