SegmentFault Automatic Reviewing | 思否自动审核

再也不用为某些用户每天无意义重复发文而苦恼了。

  1. // ==UserScript==
  2. // @name SegmentFault Automatic Reviewing | 思否自动审核
  3. // @namespace https://segmentfault.com/u/forkkillet
  4. // @version 0.1
  5. // @description 再也不用为某些用户每天无意义重复发文而苦恼了。
  6. // @author ForkKILLET
  7. // @match https://segmentfault.com/review/article_first
  8. // @grant GM_getValue
  9. // @grant GM_setValue
  10. // @grant unsafeWindow
  11. // ==/UserScript==
  12.  
  13. Array.range = (start, end) => Array.from({ length: end - start + 1 }, (_, i) => i + start)
  14.  
  15. const debug = true
  16. const log = m => console.log("[SFAR] " + m)
  17. const error = m => console.error("[SFAR] " + m)
  18.  
  19. const $ = unsafeWindow.$
  20. $(render)
  21.  
  22. function render() {
  23. if ($(".audit__content > div").length === 2)
  24. return debug && log("nothing to review.")
  25.  
  26. const $ui = $(`
  27. <style>
  28. .SFAR-ui * {
  29. outline: none !important;
  30. }
  31. .SFAR-ui .glyphicon {
  32. margin: 0 !important;
  33. }
  34. .SFAR-highlight {
  35. background-color: #bce8f1 !important;
  36. }
  37. .SFAR-config {
  38. display: block;
  39. width: 100%;
  40. min-height: 400px;
  41. }
  42. .SFAR-save {
  43. width: 54px;
  44. }
  45. .SFAR-result {
  46. display: none;
  47. margin: 0;
  48. }
  49. </style>
  50. <div class="SFAR-ui panel panel-default">
  51. <div class="panel-heading">
  52. <h5 class="mb0 mt0">[SFAR] 自动审核</h5>
  53. </div>
  54. <div class="panel-content">
  55. <textarea class="SFAR-config"/>
  56. </div>
  57. <div class="panel-footer">
  58. <button class="SFAR-save btn btn-default">保存</button>
  59. <button class="SFAR-execute btn btn-primary">执行</button>
  60. </div>
  61. <div class="SFAR-result alert alert-info"></div>
  62. </div>
  63. `)
  64. $(".audit-widget__reason").after($ui)
  65. const
  66. $config = $(".SFAR-config"),
  67. $save = $(".SFAR-save"),
  68. $execute = $(".SFAR-execute"),
  69. configDft = `
  70. {
  71. "rules": [
  72. {
  73. "title-equal": "人生苦短,开发用云-如何优雅完成程序员的侠客梦",
  74. "result": "reject",
  75. "why": "推广广告信息"
  76. },
  77. {
  78. "title-equal": "天源迪科与阿里云发布联合解决方案,基于阿里云原生产品打造卓越的数字化采购平台",
  79. "result": "reject",
  80. "why": "推广广告信息"
  81. },
  82. {
  83. "title-equal": "如何优化你的if-else?来试试“责任树模式”",
  84. "result": "reject",
  85. "why": "推广广告信息"
  86. }
  87. ],
  88. "auto": {
  89. "execute": true,
  90. "confirm": true,
  91. "next": false
  92. }
  93. }
  94. `.trim()
  95. let configNow = GM_getValue("config")
  96. configNow || GM_setValue("config", configNow = configDft)
  97. $config.val(configNow)
  98. $save.on("click", () => {
  99. GM_setValue("config", $config.val().trim())
  100. $save.html(`<span class="glyphicon glyphicon-check text-green mr10"></span>`)
  101. setTimeout(() => $save.html("保存"), 1000)
  102. debug && log("saved.")
  103. })
  104. $execute.on("click", execute)
  105. const config = JSON.parse(configNow)
  106. if (config.auto && config.auto.execute) execute(config)
  107. }
  108.  
  109. function execute(config) {
  110. config = config || JSON.parse(GM_getValue("config"))
  111. if (! config || ! config.rules) return
  112. const
  113. $form = $(".panel-content--form"),
  114. $author = $(".panel-content--inner > .audit__content-author .media-body"),
  115. article = {
  116. title: $form.find("input.form-control[name=title]").val(),
  117. text: $form.find("textarea.form-control[name=text]").val(),
  118. author: $author.children("a").text()
  119. },
  120. results = [ [ "反对", "reject" ], [ "同意", "pass", "accept" ], [ "中立", "ignore", "monkey" ] ],
  121. whys = [ "帖子式文章", "偏离社区主题", "内容及排版差", "推广广告信息", "违规内容", "不友善内容" ]
  122. debug && log(`\n# ${ article.title }\n${ article.text.slice(0, 42) }...`)
  123. let id = 0, r, y, finish = false
  124. for (let rule of config.rules) {
  125. [ "include", "match", "equal" ].forEach(how => {
  126. [ "title", "text" ].forEach(where => {
  127. const what = rule[where + "-" + how], content = article[where]
  128. if (! what) return
  129. if (
  130. how === "include" && content.includes(what) ||
  131. how === "match" && RegExp(what).test(content) ||
  132. how === "equal" && what === content
  133. ) {
  134. r = rule.result
  135. if (typeof r === "string") results.forEach((group, index) => {
  136. if (group.includes(r)) r = index
  137. })
  138. if (! Array.range(0, 3).includes(r))
  139. return error(`result ${r} isn't in [0, 2].`)
  140. debug && log(`result code is ${r}.`)
  141. if (r === 0) {
  142. y = rule.why
  143. if (typeof y === "string") y = whys.indexOf(y)
  144. if (! Array.range(0, 6).includes(y))
  145. return error(`why ${r} isn't in [0, 6].`)
  146. debug && log(`reason code is ${y}.`)
  147. finish = true
  148. }
  149. }
  150. })
  151. })
  152. id ++
  153. if (finish) break
  154. }
  155.  
  156. const
  157. auto = config.auto || {},
  158. $alert = $(".SFAR-result").show().html(finish ? `
  159. <p>
  160. 根据规则 ${id}#,态度为${ results[r][0] }${ r ? "" : `,原因为${ whys[y] }` }
  161. ${ auto.confirm ? `<a class="SFAR-result-ok">好</a> / <a class="SFAR-result-cancel">取消</a>` : "" }
  162. </p>
  163. ` : `无规则匹配。`)
  164. if (! finish) return
  165.  
  166. const
  167. $result = $(".js__audit-btn--" + results[r][1]).addClass("SFAR-highlight"),
  168. $why = $($(".audit__reasons-item:not(.audit__reasons-item--sub)")[y]).addClass("SFAR-highlight")
  169. function act() {
  170. $why.click()
  171. $result.click()
  172. if (auto.next) $(".audit-widget__vote-btn-next--inner").click()
  173. }
  174. setTimeout(() => {
  175. $(".audit__reasons-item--sub").click()
  176. if (auto.confirm) {
  177. $(".SFAR-result-ok").on("click", act)
  178. $(".SFAR-result-cancel").on("click", () => $alert.hide())
  179. }
  180. else act()
  181. }, 500)
  182. }
  183.