显示Greasyfork用户注册时间,识别恶意评论

如果你的脚本干涉了某些人的利益,收到恶意差评并不意外。恶意差评有两个特点,一是账号通常新注册,二是注册后不久就会给差评,且基本不会有后续活动。本脚本获取greasyfork用户注册时间,并显示在用户名旁边。如果用户名旁边显示的时间是未来的时间,那么这个用户很可能是恶意注册的账号。

当前为 2025-03-10 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name Display GreasyFork user registration time
  3. // @description If your script interferes with the interests of certain individuals, receiving malicious negative reviews is not surprising. Malicious negative reviews have two characteristics: First, the account is usually newly registered, and second, the review is given shortly after registration with little to no subsequent activity. This script retrieves the registration time of a GreasyFork user and displays it next to their username. If the time displayed next to the username is in the future, this user is likely to be a maliciously registered account.
  4. // @name:zh-CN 显示Greasyfork用户注册时间,识别恶意评论
  5. // @description:zh-CN 如果你的脚本干涉了某些人的利益,收到恶意差评并不意外。恶意差评有两个特点,一是账号通常新注册,二是注册后不久就会给差评,且基本不会有后续活动。本脚本获取greasyfork用户注册时间,并显示在用户名旁边。如果用户名旁边显示的时间是未来的时间,那么这个用户很可能是恶意注册的账号。
  6. // @grant GM_setValue
  7. // @grant GM_getValue
  8. // @grant GM_xmlhttpRequest
  9. // @match *://greasyfork.org/*
  10. // @match *://sleazyfork.org/*
  11. // @require https://code.jquery.com/jquery-3.7.1.min.js
  12. // @author yysk.org,人民的勤务员 <china.qinwuyuan@gmail.com>
  13. // @namespace https://github.com/ChinaGodMan/UserScripts
  14. // @supportURL https://github.com/ChinaGodMan/UserScripts/issues
  15. // @homepageURL https://github.com/ChinaGodMan/UserScripts
  16. // @license MIT
  17. // @icon https://raw.githubusercontent.com/ChinaGodMan/UserScriptsHistory/main/scriptsIcon/greasyfork-webhook-sync-enhanced.svg
  18. // @compatible chrome
  19. // @compatible firefox
  20. // @compatible edge
  21. // @compatible opera
  22. // @compatible safari
  23. // @compatible kiwi
  24. // @version 2025.03.10.0959
  25. // @created 2025-03-10 09:59:11
  26. // @modified 2025-03-10 09:59:11
  27. // ==/UserScript==
  28. /**
  29. * File: greasyfork-user-registration-time.user.js
  30. * Project: UserScripts
  31. * File Created: 2025/03/10,Monday 09:59:36
  32. * Author: 人民的勤务员@ChinaGodMan (china.qinwuyuan@gmail.com)
  33. * -----
  34. * Last Modified: 2025/03/10,Monday 12:12:45
  35. * Modified By: 人民的勤务员@ChinaGodMan (china.qinwuyuan@gmail.com)
  36. * -----
  37. * License: MIT License
  38. * Copyright © 2024 - 2025 ChinaGodMan,Inc
  39. */
  40.  
  41.  
  42. (function () {
  43. 'use strict'
  44. const interval = 3000
  45. const absoluteTime = 'relative-time1'//随便修改个类名,显示精确时间.比如relative-time1
  46. var lang = document.querySelector('html').lang.toLowerCase() || navigator.language.toLowerCase()
  47.  
  48. if (!['en', 'zh-cn', 'zh-tw', 'ja', 'ru', 'kr'].includes(lang)) lang = 'en'
  49. const i18n = {
  50. en: {
  51. title: 'Registration time',
  52. prefix: 'R'
  53. },
  54. 'zh-cn': {
  55. title: '注册时间',
  56. prefix: '注'
  57. },
  58. 'zh-tw': {
  59. title: '註冊時間',
  60. prefix: '注'
  61. },
  62. ja: {
  63. title: '登録時間',
  64. prefix: 'R'
  65. },
  66. ru: {
  67. title: 'время регистрации',
  68. prefix: 'R'
  69. },
  70. kr: {
  71. title: '등록 시간',
  72. prefix: 'R'
  73. }
  74. }
  75. const title = i18n[lang].title
  76. const prefix = i18n[lang].prefix
  77.  
  78. function pad(s, d) {
  79. s = `000000${s}`
  80. return s.substring(s.length - d)
  81. }
  82.  
  83. const formatUFn = (dt) => {
  84. return `${dt.getFullYear()}.${pad(dt.getMonth() + 1, 2)}.${pad(dt.getDate(), 2)} ${pad(dt.getHours(), 2)}:${pad(dt.getMinutes(), 2)}:${pad(dt.getSeconds(), 2)}`
  85. }
  86.  
  87. const formatFrFn = (dt) => {
  88. return `${pad(dt.getDate(), 2)}.${pad(dt.getMonth() + 1, 2)}.${dt.getFullYear()} ${pad(dt.getHours(), 2)}:${pad(dt.getMinutes(), 2)}:${pad(dt.getSeconds(), 2)}`
  89. }
  90.  
  91. function formatTime(utcTime, lang = 'en') {
  92. const dt = new Date(utcTime)
  93.  
  94. let formatFn = (lang === 'fr' || lang.startsWith('fr-')) ? formatFrFn : formatUFn
  95.  
  96. return formatFn(dt)
  97. }
  98.  
  99.  
  100.  
  101. async function genregtime(uid) {
  102. if (GM_getValue(uid) !== undefined && GM_getValue(uid) !== null) {
  103. return GM_getValue(uid)
  104. }
  105. const user_api = `https://api.greasyfork.org/users/${uid}.json`
  106. var created_at
  107. try {
  108. const response = await fetch(user_api)
  109. const data = await response.json()
  110. created_at = data.created_at
  111. console.log('🔍 ~ created_at:', created_at)
  112. } catch (error) {
  113. console.error('请求失败:', error)
  114. }
  115. GM_setValue(uid, created_at)
  116. return created_at
  117. }
  118. function gensnippet(regtime, uid) {
  119. return `<span class="regtime" style="margin-left: 10px; font-weight: bold; color: red;" title="uid ${uid} ${title}">${prefix} <${absoluteTime} datetime="${regtime}+00:00" prefix="">${regtime}</${absoluteTime}></span>`
  120. }
  121. let isProcessing = false
  122. function run() {
  123. if (isProcessing) return
  124. isProcessing = true
  125. var maxuid = 0
  126. const uids = document.documentElement.innerHTML.matchAll(/\/users\/(\d+)/g)
  127. for (const uid of uids) {
  128. if (parseInt(uid[1]) > maxuid) maxuid = parseInt(uid[1])
  129. }
  130. if (window.location.href.match(/(greasyfork|sleazyfork).org\/\w+-\w+\/users\/\d+(-[^/]*)?$/) && jQuery('section#about-user > span.regtime').length === 0) {
  131. let uid = jQuery('section#about-user > a.report-link').attr('href').match(/item_id=(\d+)/)[1]
  132. genregtime(uid).then(regtime => {
  133. regtime = formatTime(regtime, lang)
  134. jQuery('section#about-user > h2').after(gensnippet(regtime, uid))
  135. })
  136.  
  137. }
  138. jQuery('a.user-link, dd.script-list-author > span > a, dd.script-show-author > span > a, table.log-table > tbody > tr > td > a, i:contains(\'Deleted user \')').each(function (i, el) {
  139. var lastele = jQuery(el).parent().children().last()
  140. if (lastele.attr('class') === 'regtime' || lastele.attr('class') === 'small-btn') return
  141. var button = jQuery('<button>').text(i18n[lang].prefix).addClass('small-btn')
  142. var m = (el.tagName === 'A') ? jQuery(el).attr('href').match(/\/users\/(\d+)/) : jQuery(el).text().match(/Deleted user (\d+)/)
  143. if (!m) return
  144.  
  145. button.on('click', function () {
  146. isProcessing = true
  147. genregtime(m[1]).then(regtime => {
  148. regtime = formatTime(regtime, lang)
  149. lastele.after(gensnippet(regtime, m[1]))
  150. })
  151. button.remove()
  152. })
  153.  
  154. // 如果存在时间就直接插入,不存在显示按钮
  155. if (GM_getValue(m[1]) !== undefined && GM_getValue(m[1]) !== null) {
  156. lastele.after(gensnippet(formatTime(GM_getValue(m[1]), lang), m[1]))
  157. } else {
  158. lastele.after(button)
  159. }
  160.  
  161. })
  162. isProcessing = false
  163.  
  164. return run
  165. }
  166. setInterval(run(), interval)
  167. })()