Greasy Fork镜像 API

Parse information on gf.qytechs.cn

当前为 2022-05-29 提交的版本,查看 最新版本

此脚本不应直接安装。它是供其他脚本使用的外部库,要使用该库请加入元指令 // @require https://update.gf.qytechs.cn/scripts/445697/1055427/Greasy%20Fork%20API.js

  1. // ==UserScript==
  2. // @name Greasy Fork镜像 API
  3. // @namespace -
  4. // @version 1.0.0
  5. // @description Parse information on gf.qytechs.cn
  6. // @author NotYou
  7. // @license LGPL-3.0
  8. // @grant none
  9. // ==/UserScript==
  10.  
  11. /*
  12. "Program" is a set of instructions that a computer uses to perform a specific function.
  13. "Library" is a program that is embedded in other programs.
  14. "This library" refers to "Greasy Fork镜像 API" library.
  15. "Account" is an arrangement in which a person uses the Internet or email services of a particular company, organization or individual person's web resource.
  16. "I" refers to individual person that owns internet account "NotYou" at web resource "gf.qytechs.cn".
  17.  
  18. I does not responsible for any
  19. damage caused by this library
  20. */
  21.  
  22. /*
  23. TODO:
  24. - [x] Feedback
  25. - [ ] Convesations
  26. - [ ] Versions
  27. */
  28.  
  29. class GreasyFork {
  30. error(errorable, message) {
  31. if(!errorable) throw new Error(message)
  32. }
  33.  
  34. languages() {
  35. return [
  36. 'ar', 'bg', 'cs', 'da', 'de', 'el', 'en', 'eo', 'es', 'fi', 'fr', 'fr-CA', 'he', 'hu', 'id', 'it', 'ja', 'ko', 'nb', 'nl', 'pl', 'pt-BR', 'ro', 'ru', 'sk', 'sr', 'sv', 'th', 'tr', 'uk', 'ug', 'vi', 'zh-CN', 'zh-TW'
  37. ]
  38. }
  39.  
  40. parseScriptElement(i) {
  41. if(!i||!i.dataset.scriptAuthors) return null
  42. let _ = null,
  43. data = i.dataset,
  44. rating = i.children[0].children[1].children[7] ? i.children[0].children[1].children[7].children[0].innerText.split('\n').join(' ').split(' ') : _,
  45. scriptLink = i.children[0].children[0].children[0] ? i.children[0].children[0].children[0].href : _,
  46. source = !scriptLink ? _ : data.scriptType == 'library' ? scriptLink.slice(0, scriptLink.indexOf('-'))+'/code/index.js' : scriptLink.slice(0, scriptLink.indexOf('-'))+'/code/script.user.js'
  47. return {
  48. name: data.scriptName,
  49. id: +(data.scriptId),
  50. version: data.scriptVersion,
  51. lang: data.scriptLanguage,
  52. installs: +(data.scriptTotalInstalls),
  53. dailyInstalls: +(data.scriptDailyInstalls),
  54. rating: +(data.scriptRatingScore),
  55. ratingGood: rating ? rating[0] == '0' ? 0 : +(rating[0]) : _,
  56. ratingOk: rating ? rating[1] == '0' ? 0 : +(rating[1]) : _,
  57. ratingBad: rating ? rating[2] == '0' ? 0 : +(rating[2]) : _,
  58. authors: JSON.parse(data.scriptAuthors) || data.scriptAuthors.replaceAll('"', '"'),
  59. createDate: data.scriptCreatedDate,
  60. updateDate: data.scriptUpdatedDate,
  61. type: data.scriptType,
  62. cssAsJs: data.cssAvailableAsJs == 'true' ? true : false,
  63. sensitive: data.sensitive == 'true' ? true : false,
  64. element: i,
  65. url: scriptLink,
  66. source: source
  67. }
  68. }
  69.  
  70. parseScriptCodeMeta(code) {
  71. if(typeof code != 'string'||!code.match(/\/\/ ==UserScript==/)) return null
  72. var arr = code.replaceAll('\n', '').match(/\/\/ ==UserScript==.*\/\/ ==\/UserScript==/)[0].split('// ')
  73. for (let i = 0; i < 2; i++) arr.shift()
  74. arr.pop()
  75. let result = []
  76. for (let i of arr) {
  77. let data = i.replace(/ . +/, ' ').split(' ')
  78. let name = data[0]
  79. data.shift()
  80. let value = data.join(' ')
  81. result.push({
  82. meta: name,
  83. value: value
  84. })
  85. }
  86. return result
  87. }
  88.  
  89. ls() {
  90. function ls(id) {
  91. if(document.querySelector(`#${id}`)) {
  92. let result = []
  93. for (let i of document.querySelector(`#${id}`).children) {
  94. result.push(new GreasyFork().parseScriptElement(i))
  95. }
  96. return result
  97. } return null
  98. }
  99. return {
  100. scripts: ls('user-script-list'),
  101. browsed: ls('browse-script-list'),
  102. libraries: ls('user-library-script-list')
  103. }
  104. }
  105.  
  106. get(q) {
  107. let parser = new DOMParser(),
  108. error = new GreasyFork().error,
  109. langs = new GreasyFork().languages(),
  110. str = str => str.replaceAll('\n', '').replaceAll(' ', ''),
  111. _ = null
  112.  
  113. return {
  114. script(query = q) {
  115. let errMsg = 'Argument 1 is not defined'
  116. return {
  117. async asJSON(id = query || q) {
  118. error(id, errMsg)
  119. return fetch(`https://gf.qytechs.cn/scripts/${id}.json`).then((r) =>
  120. r.json()
  121. ).then((c) => {
  122. return c
  123. })
  124. },
  125. async code(id = query || q) {
  126. error(id, errMsg)
  127. return fetch(`https://gf.qytechs.cn/scripts/${id}/code`).then((r) =>
  128. r.text()
  129. ).then((c) => {
  130. return parser.parseFromString(c, 'text/html').querySelector('pre').innerText
  131. })
  132. },
  133. async history(id = query || q) {
  134. error(id, errMsg)
  135. return fetch(`https://gf.qytechs.cn/scripts/${id}/versions`).then((r) =>
  136. r.text()
  137. ).then((c) => {
  138. let list = parser.parseFromString(c, 'text/html').querySelectorAll('.history_versions li'), result = []
  139. for (let i of list) {
  140. let ver = i.children[1].children[0],
  141. time = i.children[2],
  142. log = i.children[3]
  143.  
  144. result.push({
  145. version: {
  146. id: +(ver.href.match(/\?version=\d.+/)[0].replace('?version=', '')),
  147. text: ver.innerText,
  148. url: ver.href
  149. },
  150. time: {
  151. text: time.innerText,
  152. iso: time.attributes.datetime.value
  153. },
  154. changelog: log ? {
  155. html: log.innerHTML,
  156. text: log.innerText
  157. } : _
  158. })
  159. }
  160. return result
  161. })
  162. },
  163. async feedback(id = query || q) {
  164. error(id, errMsg)
  165. return fetch(`https://gf.qytechs.cn/scripts/${id}/feedback`).then((r) =>
  166. r.text()
  167. ).then((c) => {
  168. let list = parser.parseFromString(c, 'text/html').querySelectorAll('.discussion-list-container'), result = []
  169. for (let i of list) {
  170. let di = i.children[0],
  171. ti = di.children[1],
  172. mt = di.children[0],
  173. mt0 = mt.children[0],
  174. mt1 = mt.children[1],
  175. a0 = mt0.children[0],
  176. a1 = mt1.children[0].children[0],
  177. url = ti.href,
  178. last = mt1.children[0].children
  179.  
  180. result.push({
  181. id: +(url.match(/ns\/\d.+/)[0].replace('ns/', '')),
  182. url: url,
  183. rating: ti.children[0].classList.contains('rating-icon') ? ti.children[0].innerText : _,
  184. text: ti.children[1] ? ti.children[1].innerText.replace('\n ', '').replace('\n ', '') : ti.children[0].innerText.replace('\n ', '').replace('\n ', ''),
  185. meta: {
  186. first: {
  187. id: +(a0.href.match(/\d.+-/)[0].replace(/-.+/, '').replace('-', '')),
  188. url: a0.href,
  189. nickname: a0.innerText
  190. },
  191. last: {
  192. id: +(a1.href.match(/\d.+-/)[0].replace(/-.+/, '').replace('-', '')),
  193. url: a1.href,
  194. nickname: a1.innerText
  195. },
  196. time: {
  197. first: {
  198. iso: mt0.children[1].attributes.datetime.value,
  199. text: mt0.children[1].innerText
  200. },
  201. last: last[last.length-1].attributes.datetime ? {
  202. iso: last[last.length-1].attributes.datetime.value,
  203. text: last[last.length-1].innerText
  204. } : _
  205. }
  206. }
  207. })
  208. }
  209. return result.length > 1 ? result : _
  210. })
  211. },
  212. async stats(id = query || q) {
  213. error(id, errMsg)
  214. return fetch(`https://gf.qytechs.cn/scripts/${id}/stats.json`).then((r) =>
  215. r.json()
  216. ).then((c) => {
  217. return c
  218. })
  219. },
  220. async info(id = query || q, options = {}) {
  221. error(id, errMsg)
  222. return fetch(str(`https://gf.qytechs.cn/
  223. ${options.locale && langs.includes(options.locale) ? options.locale : 'en'}
  224. /scripts/${id}${options.locale ? `&locale_override=1` : ''}`)).then((r) =>
  225. r.text()
  226. ).then((c) => {
  227. let doc = parser.parseFromString(c, 'text/html'),
  228. screenshots = _,
  229. addInfo = _,
  230. appliesTo = [],
  231. comp = _,
  232. support = doc.querySelector('#script-feedback-suggestion').children.length == 3 ? support = doc.querySelector('#script-feedback-suggestion').children[0].href : _,
  233. getInfo = value => doc.querySelector(`dd.script-${value}`),
  234. license = getInfo('show-license'),
  235. d_installs = getInfo('show-daily-installs') ? +(getInfo('show-daily-installs').innerText.replaceAll(',', '')) : _,
  236. installs = getInfo('show-total-installs') ? +(getInfo('show-total-installs').innerText.replaceAll(',', '')) : _,
  237. r_g = doc.querySelector('.good-rating-count') ? +(doc.querySelector('.good-rating-count').innerText) : _,
  238. r_o = doc.querySelector('.ok-rating-count') ? +(doc.querySelector('.ok-rating-count').innerText) : _,
  239. r_b = doc.querySelector('.bad-rating-count') ? +(doc.querySelector('.bad-rating-count').innerText) : _,
  240. c_date_text = getInfo('show-created-date').innerText,
  241. c_date_iso = doc.querySelector('dd.script-show-created-date').children[0].children[0].attributes.datetime.value,
  242. u_date_text = getInfo('show-updated-date').innerText,
  243. u_date_iso = doc.querySelector('dd.script-show-updated-date').children[0].children[0].attributes.datetime.value,
  244. global = doc.querySelector('#script-info header'),
  245. install = doc.querySelector('#install-area') ? doc.querySelectorAll('#install-area *:not(.install-help-link)') : _,
  246. data = install ? install[0].dataset : _,
  247. yours = doc.querySelector('#script-links').children,
  248. require = doc.querySelector('#script-content p code') ? doc.querySelector('#script-content p code').innerText.replace('// @require ', '') : _
  249.  
  250. if(doc.querySelector('.user-screenshots')) {
  251. screenshots = []
  252. doc.querySelectorAll('.user-screenshots a').forEach((e) => {
  253. screenshots.push({
  254. source: e.href,
  255. thumbnail: e.children[0].src
  256. })
  257. })
  258. }
  259. doc.querySelectorAll('dd.script-show-applies-to li').forEach((e) => {
  260. appliesTo.push(e.innerText)
  261. })
  262. if(doc.querySelector('.script-show-compatibility')) {
  263. comp = []
  264. doc.querySelectorAll('.script-show-compatibility img').forEach((e) => {
  265. comp.push({
  266. browser: e.alt.replace('Compatible with ', ''),
  267. value: e.title.replaceAll('\n', ' '),
  268. })
  269. })
  270. }
  271. if(doc.querySelector('#additional-info')) addInfo = doc.querySelector('#additional-info')
  272. return {
  273. id: +(id),
  274. name: global.children[0].innerText,
  275. desc: global.children[1].innerText,
  276. version: getInfo('show-version').innerText,
  277. isYour: yours[yours.length-1].children[0].href.match(/\/admin/) ? true : false,
  278. isPrevVersion: data ? data.isPreviousVersion == 'true' ? true : false : _,
  279. installs: {
  280. daily: d_installs,
  281. total: installs,
  282. },
  283. rating: {
  284. good: r_g,
  285. ok: r_o,
  286. bad: r_b,
  287. },
  288. date: {
  289. created: {
  290. text: c_date_text,
  291. iso: c_date_iso,
  292. },
  293. updated: {
  294. text: u_date_text,
  295. iso: u_date_iso,
  296. }
  297. },
  298. license: {
  299. name: license.innerText,
  300. url: license.children[0].children[0] ? license.children[0].children[0].href : _
  301. },
  302. screenshots: screenshots,
  303. additionalInfo: addInfo ? [
  304. {
  305. html: addInfo.innerHTML,
  306. text: addInfo.innerText
  307. }
  308. ] : _,
  309. appliesTo: appliesTo,
  310. compatibility: comp,
  311. supportUrl: support,
  312. requireUrl: require
  313. }
  314. })
  315. },
  316. async set(id = query || q, options = {}) {
  317. error(id, errMsg)
  318. return id && fetch(str(`https://gf.qytechs.cn/
  319. ${options.locale && langs.includes(options.locale) ? options.locale : 'en'}
  320. /scripts?set=${id}
  321. ${options.sort ? `&sort=${options.sort}` : ''}
  322. ${options.page ? `&page=${options.page}` : ''}
  323. ${options.localeFilter ? `&filter_locale=${options.localeFilter}` : ''}
  324. ${options.locale ? `&locale_override=1` : ''}`)).then(r =>
  325. r.text()
  326. ).then((c) => {
  327. let result = [], list = parser.parseFromString(c, 'text/html').querySelectorAll('#browse-script-list li')
  328. for (let i of list) {
  329. result.push(new GreasyFork().parseScriptElement(i))
  330. }
  331. return result
  332. })
  333. },
  334. }
  335. },
  336.  
  337. async user(id = q) {
  338. error(id, 'Argument 1 is not defined')
  339. return fetch(`https://gf.qytechs.cn/users/${id}`).then((r) =>
  340. r.text()
  341. ).then((c) => {
  342. let doc = parser.parseFromString(c, 'text/html'), url = new URL(doc.baseURI)
  343. return {
  344. nickname: doc.querySelector('#about-user h2').firstChild.data,
  345. id: +(id),
  346. isMod: doc.querySelector('#about-user > h2 .badge-moderator') ? true : false,
  347. scripts: (() => {
  348. if(doc.querySelector('#user-script-list')) {
  349. let result = []
  350. for (let i of doc.querySelector('#user-script-list').children) {
  351. result.push(new GreasyFork().parseScriptElement(i))
  352. }
  353. return result
  354. } return _
  355. })(),
  356. libraries: (() => {
  357. if(doc.querySelector('#user-library-script-list')) {
  358. let result = []
  359. for (let i of doc.querySelector('#user-library-script-list').children) {
  360. result.push(new GreasyFork().parseScriptElement(i))
  361. }
  362. return result
  363. } return _
  364. })(),
  365. scriptSets: (() => {
  366. if(doc.querySelector('#user-script-sets')) {
  367. let result = []
  368. for (let i of doc.querySelector('#user-script-sets').children) {
  369. if(i.firstChild.data === 'Favorites ') return _
  370. let data = i.firstChild.data.split(':')
  371. result.push({
  372. name: data[0],
  373. desc: data[1].replace(' ', '').split('').reverse().join('').replace(' ', '').split('').reverse().join(''),
  374. id: i.children[0].href.replace(/.*?\/scripts\?set=/, '')
  375. })
  376. }
  377. return result
  378. } return _
  379. })(),
  380. recentComments: (() => {
  381. if(doc.querySelectorAll('#user-discussions section ul li a')) {
  382. let result = []
  383. for (let i of doc.querySelectorAll('#user-discussions section ul li a')) {
  384. result.push({
  385. title: i.lastChild.innerText,
  386. id: !i.href.match(/ns\/\d+/) ? +(i.href.match(/\d+/)[0]) : +(i.href.match(/ns\/\d+/)[0].replace('ns/', '')),
  387. action: i.firstChild.data.replace(': ', ''),
  388. URL: i.href
  389. })
  390. }
  391. return result.length > 1 ? result : _
  392. } return _
  393. })()
  394. }
  395. })
  396. },
  397.  
  398. async search(query = q, type, options = {}) {
  399. if(typeof query != 'string') throw new Error('Argument 1 is not string')
  400. function searchURL(path) {
  401. return str(`https://gf.qytechs.cn/
  402. ${options.locale && langs.includes(options.locale) ? options.locale : 'en'}/
  403. ${path}${options.asJSON == true ? '.json' : ''}?q=${query ? query : ''}
  404. ${options.sort ? `&sort=${options.sort}` : ''}
  405. ${options.page ? `&page=${options.page}` : ''}
  406. ${options.locale ? `&locale_override=1` : ''}`)
  407. }
  408. if(type === 'script') {
  409. return fetch(searchURL('scripts')).then((r) =>
  410. options.asJSON == true ? r.json() : r.text()
  411. ).then((c) => {
  412. if(options.asJSON == true) return c
  413. let result = [], list = parser.parseFromString(c, 'text/html').querySelectorAll('#browse-script-list li:not(.ad-entry)')
  414. for (let i of list) {
  415. result.push(new GreasyFork().parseScriptElement(i))
  416. }
  417. return result
  418. })
  419. }
  420. if(type === 'library') {
  421. return fetch(searchURL('scripts/libraries')).then((r) =>
  422. options.asJSON == true ? r.json() : r.text()
  423. ).then((c) => {
  424. if(options.asJSON == true) return c
  425. let result = [], list = parser.parseFromString(c, 'text/html').querySelectorAll('#browse-script-list li')
  426. for (let i of list) {
  427. result.push(new GreasyFork().parseScriptElement(i))
  428. }
  429. return result
  430. })
  431. } if(type === 'user') {
  432. return fetch(searchURL('users')).then((r) =>
  433. options.asJSON == true ? r.json() : r.text()
  434. ).then((c) => {
  435. if(options.asJSON == true) return c
  436. let result = [], list = parser.parseFromString(c, 'text/html').querySelectorAll('#browse-user-list li')
  437. for (let i of list) {
  438. result.push({
  439. nickname: i.children[0].innerText,
  440. id: +(i.children[0].href.replace(/.*?\/users\//, '').split('-')[0]),
  441. badge: (i.children[1] ? i.children[1].innerText.toLowerCase() : 'none'),
  442. url: i.children[0].href,
  443. scripts: +(i.innerHTML.match(/- .+/, '')[0].match(/\d/)[0])
  444. })
  445. }
  446. return result
  447. })
  448. } if(type === 'list') {
  449. return [
  450. 'script',
  451. 'library',
  452. 'user',
  453. 'list'
  454. ]
  455. } else throw new Error(`Argument 1 ${type} is not defined`)
  456. },
  457.  
  458. async modlog(options = {}) {
  459. return fetch(str(`https://gf.qytechs.cn/
  460. ${options.locale && langs.includes(options.locale) ? options.locale : 'en'}
  461. /moderator_actions
  462. ${options.page ? `?page=${options.page}` : '?page=1'}
  463. ${options.locale ? `&locale_override=1` : ''}`)).then(r =>
  464. r.text()
  465. ).then(c => {
  466. let trs = parser.parseFromString(c, 'text/html').querySelectorAll('.log-table > tbody > tr'), result = []
  467. for (let i of trs) {
  468. let time = i.children[0].children[0],
  469. userUrl = i.children[1].children[0].href,
  470. item = i.children[2].children[0],
  471. res = i.children[4]
  472.  
  473. result.push({
  474. time: {
  475. text: time.innerText,
  476. iso: time.attributes.datetime.value
  477. },
  478. mod: {
  479. id: userUrl.match(/\d.+-/) ? +(userUrl.match(/\d.+-/)[0].replace('-', '')) : userUrl.match(/\d-/) ? +(userUrl.match(/\d-/)[0].replace('-', '')) : _,
  480. url: userUrl,
  481. nickname: i.children[1].children[0].innerText
  482. },
  483. item: i.children[2] ? {
  484. name: item ? item.innerText : _,
  485. url: item ? item.href : _,
  486. type: item ? str(item.previousSibling.data.replace(': ', '')) : _
  487. } : _,
  488. action: i.children[3].innerText,
  489. reason: {
  490. name: res.innerText.replaceAll(' ', '').replaceAll('\n', ''),
  491. url: res.children[0].children[0] ? res.children[0].children[0].href : _
  492. }
  493. })
  494. }
  495. return result
  496. })
  497. },
  498. }
  499. }
  500.  
  501. async action(action, options = {}) {
  502. let error = new GreasyFork().error,
  503. parser = new DOMParser()
  504.  
  505. if(action === 'install') {
  506. error(options.id, 'Argument 2 { id } is not defined')
  507. setTimeout(() => {
  508. window.open(`https://gf.qytechs.cn/scripts/${options.id}/code/source.user.${options.lang === 'css' ? 'css' : 'js'}`, '_top')
  509. }, options.timeout * 1e3 || 0)
  510. }
  511. if(action === 'signout') {
  512. setTimeout(() => {
  513. fetch('https://gf.qytechs.cn/users/sign_out')
  514. }, options.timeout * 1e3 || 0)
  515. } if(action === 'list') {
  516. return [
  517. 'install',
  518. 'signout',
  519. 'list'
  520. ]
  521. }
  522. else throw new Error(`Argument 1 ${action} is not defined`)
  523. }
  524.  
  525. version() {
  526. return '1.0.0'
  527. }
  528. }
  529.  
  530.  
  531.  
  532.  
  533.  
  534.  
  535.  
  536.  
  537.  
  538.  
  539.  
  540.  
  541.  
  542.  
  543.  
  544.  
  545.  
  546.  
  547.  
  548.  
  549.  
  550.  
  551.  
  552.  
  553.  
  554.  
  555.  
  556.  
  557.  

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址