CSDN质量分显示按钮

用于快速查询编辑页CSDN博客质量分的浏览器脚本,详细描述见https://blog.csdn.net/qq_46106285/article/details/138357755

Pasang skrip ini?
Sugesti pemilik skrip

Kamu mungkin juga suka b站自律脚本.

Pasang skrip ini
  1. // ==UserScript==
  2. // @name CSDN质量分显示按钮
  3. // @namespace http://tampermonkey.net/
  4. // @version 1.3
  5. // @description 用于快速查询编辑页CSDN博客质量分的浏览器脚本,详细描述见https://blog.csdn.net/qq_46106285/article/details/138357755
  6. // @author shandianchengzi
  7. // @match https://editor.csdn.net/*
  8. // @match https://blog.csdn.net/*/article/details/*
  9. // @match https://*.blog.csdn.net/*
  10. // @icon https://www.google.com/s2/favicons?sz=64&domain=csdn.net
  11. // @grant GM_registerMenuCommand
  12. // @grant GM_getValue
  13. // @grant GM_setValue
  14. // ==/UserScript==
  15.  
  16. // 主要配置项 访问 https://blog.csdn.net/qq_46106285/article/details/138357755 查看具体获取方式
  17. var values_info = {
  18. "CSDN_QC_X-Ca-Key": {
  19. "hint": "X-Ca-Key",
  20. "storage_method": "gm"
  21. },
  22. "CSDN_QC_X-Ca-Nonce": {
  23. "hint": "X-Ca-Nonce",
  24. "storage_method": "gm"
  25. },
  26. "CSDN_QC_X-Ca-Signature": {
  27. "hint": "X-Ca-Signature",
  28. "storage_method": "gm"
  29. },
  30. "CSDN_QC_ID": {
  31. "hint": "CSDN ID (即个人主页 https://blog.csdn.net/{id} 中的{id})",
  32. "storage_method": "gm",
  33. "null_ok_if": "CSDN_QC_DOMAIN_ID"
  34. },
  35. "CSDN_QC_DOMAIN_ID": {
  36. "hint": "CSDN ID (即个人主页 https://{id}.blog.csdn.net 中的{id})",
  37. "storage_method": "gm",
  38. "null_ok_if": "CSDN_QC_ID"
  39. },
  40. }
  41.  
  42. var href = window.location.href;
  43.  
  44. // return true if value equals to null
  45. function isNull(value){
  46. let special_null = [null, "null", "", undefined, NaN, "undefined"];
  47. return special_null.includes(value);
  48. }
  49.  
  50. function createAButton(element,value,onclick,css,cla="temp"){
  51. var Button = document.createElement("input");
  52. Button.type="button";
  53. Button.value=value;
  54. Button.onclick=onclick;
  55. Button.setAttribute("style",css) ;
  56. Button.setAttribute("class",cla) ;
  57. element.appendChild(Button);
  58. return Button;
  59. }
  60.  
  61. function Toast(msg, duration) {
  62. duration = isNaN(duration) ? 3000 : duration;
  63. var m = document.createElement('div');
  64. m.innerHTML = msg;
  65. m.style.cssText = "font-family:siyuan;max-width:60%;min-width: 150px;padding:0 14px;height: 40px;color: rgb(255, 255, 255);line-height: 40px;text-align: center;border-radius: 4px;position: fixed;top: 50%;left: 50%;transform: translate(-50%, -50%);z-index: 999999;background: rgba(0, 0, 0,.7);font-size: 16px;";
  66. document.body.appendChild(m);
  67. setTimeout(function() {
  68. var d = 0.5;
  69. m.style.webkitTransition = '-webkit-transform ' + d + 's ease-in, opacity ' + d + 's ease-in';
  70. m.style.opacity = '0';
  71. setTimeout(function() {
  72. document.body.removeChild(m)
  73. }, d * 1000);
  74. }, duration);
  75. }
  76.  
  77. function webQuest(url, method, headers, data, callback) {
  78. /* url: 请求的 URL
  79. * method: 请求方法,'GET' 或 'POST'
  80. * headers: 请求头,一个 dict 对象
  81. * data: 请求体,一个字符串,形如 'key1=value1&key2=value2'
  82. * callback: 请求完成后的回调函数,参数是响应的内容
  83. */
  84. // 创建一个 XMLHttpRequest 对象
  85. var xhr = new XMLHttpRequest();
  86.  
  87. // 设置请求方法和 URL
  88. xhr.open(method, url, true);
  89.  
  90. // 设置请求头,如果有的话
  91. for (var key in headers) {
  92. xhr.setRequestHeader(key, headers[key]);
  93. }
  94.  
  95. // 发送请求
  96. xhr.send(data);
  97.  
  98. // 请求完成后的回调函数
  99. xhr.onreadystatechange = function () {
  100. if (xhr.readyState === 4 && xhr.status === 200) {
  101. // 处理响应
  102. callback(xhr.responseText);
  103. } else if (xhr.readyState === 4) {
  104. alert("请求 " + url + " 失败,状态码:" + xhr.status + ",data:" + data);
  105. }
  106. };
  107. }
  108.  
  109. function get_csdn_id(){
  110. // get csdn_id from href
  111. let id_info = { id: null, id_type: null}
  112. // eg: https://blog.csdn.net/{id}/article/details/138357755
  113. id_info.id = href.match(/blog.csdn.net\/(\w+)/);
  114. id_info.id_type = "CSDN_QC_ID";
  115. // eg: https://{id}.blog.csdn.net/article/details/138357755
  116. if (id_info.id == null){
  117. id_info.id = href.match(/(\w+)\.blog.csdn.net/);
  118. id_info.id_type = "CSDN_QC_DOMAIN_ID";
  119. }
  120. if (id_info.id != null){
  121. id_info.id = id_info.id[1];
  122. }
  123. return id_info;
  124. }
  125.  
  126. function get_article_id(href){
  127. // get article_id from href
  128. // eg: https://editor.csdn.net/md?not_checkout=1&spm=1011.2415.3001.6217&articleId=138357755
  129. let id = href.match(/articleId=(\d+)/);
  130. // eg: https://shandianchengzi.blog.csdn.net/article/details/138357755?spm=1001.2014.3001.5502
  131. if (id == null){
  132. id = href.match(/details\/(\d+)/);
  133. }
  134. if (id == null){
  135. return null;
  136. }
  137. return id[1];
  138. }
  139.  
  140. function get_article_url(values_info, href){
  141. let id = get_article_id(href);
  142. if (isNull(id)){
  143. alert("请先进入文章页面或编辑页再点击查询!");
  144. return "no need to fill";
  145. }
  146. if (!isNull(values_info["CSDN_QC_ID"]["value"])){
  147. return "https://blog.csdn.net/" + values_info["CSDN_QC_ID"]["value"] + "/article/details/" + id;
  148. }
  149. if (!isNull(values_info["CSDN_QC_DOMAIN_ID"]["value"])){
  150. return "https://" + values_info["CSDN_QC_DOMAIN_ID"]["value"] + ".blog.csdn.net/article/details/" + id;
  151. }
  152. alert("请先配置 CSDN ID 再重新查询!");
  153. return null;
  154. }
  155.  
  156. function get_headers(values_info){
  157. // check if the values are null
  158. if (isNull(values_info["CSDN_QC_X-Ca-Key"]["value"]) || isNull(values_info["CSDN_QC_X-Ca-Nonce"]["value"]) || isNull(values_info["CSDN_QC_X-Ca-Signature"]["value"])){
  159. alert("请先配置 X-Ca-Key、X-Ca-Nonce、X-Ca-Signature 再重新查询!");
  160. return null;
  161. }
  162. var headers = {
  163. "Content-Type": "application/x-www-form-urlencoded",
  164. "Accept": "application/json, text/plain, */*",
  165. "X-Ca-Key": values_info["CSDN_QC_X-Ca-Key"]["value"],
  166. "X-Ca-Nonce": values_info["CSDN_QC_X-Ca-Nonce"]["value"],
  167. "X-Ca-Signature": values_info["CSDN_QC_X-Ca-Signature"]["value"],
  168. "X-Ca-Signature-Headers": "x-ca-key,x-ca-nonce",
  169. "X-Ca-Signed-Content-Type": "multipart/form-data",
  170. }
  171. return headers;
  172. }
  173.  
  174. function questQC(values_info, href) {
  175. var url = "https://bizapi.csdn.net/trends/api/v1/get-article-score";
  176. var method = "POST";
  177. var callback = function (ret) {
  178. var response = JSON.parse(ret);
  179. Toast("质量分: " + response.data.score, 2000);
  180. }
  181. let article_url = get_article_url(values_info, href);
  182. let headers = get_headers(values_info);
  183. // check article_url and headers
  184. if (isNull(article_url) || isNull(headers)){
  185. fill_values(false, true);
  186. return;
  187. }
  188. // check if the url is start with http
  189. if (!article_url.startsWith("http")){
  190. return;
  191. }
  192. var data = "url=" + article_url;
  193. webQuest(url, method, headers, data, callback);
  194. }
  195.  
  196. function userSetValue(key, hint=null, mode="gm", get_value=true){
  197. /* key: the key of the value
  198. * hint: the hint of the value
  199. * mode: storage or gm, storage: store in local storage, gm: store in GM_setValue
  200. * get_value: if true, then get value from user, otherwise return the value directly
  201. * return: the value of the key
  202. */
  203. if (hint == null) hint = "请输入" + key + "的值";
  204. var local_value = null;
  205. // storage: store in local storage, just used on the current domain
  206. if (mode == "storage") {
  207. local_value = localStorage.getItem(key);
  208. // gm: store in GM_setValue, can be used on all domains
  209. } else if (mode == "gm") {
  210. local_value = GM_getValue(key);
  211. }
  212.  
  213. // if need to get value from user, prompt user to input
  214. if (get_value) {
  215. // get value from user
  216. let ret = window.prompt(hint, local_value);
  217. if (ret != null) {
  218. if (mode == "storage") {
  219. localStorage.setItem(key, ret);
  220. } else if (mode == "gm") {
  221. GM_setValue(key, ret);
  222. }
  223. local_value = ret;
  224. }
  225. }
  226.  
  227. return local_value;
  228. }
  229.  
  230. function fill_values(only_fill_null=false, get_value=true){
  231. for (var key in values_info) {
  232. var value = values_info[key];
  233. if (only_fill_null && !isNull(value["value"])) {
  234. continue;
  235. }
  236. if (!isNull(value["null_ok_if"])){
  237. // if the value is not null, then skip
  238. if (!isNull(values_info[value["null_ok_if"]]["value"])) {
  239. continue;
  240. }
  241. }
  242. values_info[key]["value"] = userSetValue(key, value["hint"], value["storage_method"], get_value);
  243. }
  244. }
  245.  
  246. function createManyButton(){
  247. // get all the article dom
  248. var elements = document.querySelectorAll("article");
  249. // add button to each #userSkin > div.user-profile-body > div > div.user-profile-body-right > div.navList-box > div.mainContent > div > article:nth-child(2)
  250. for (var i = 0; i < elements.length; i++) {
  251. var element = elements[i];
  252. var url = element.querySelector("a").href;
  253. // check if the button is already exist
  254. if (element.querySelector("input") != null){
  255. continue;
  256. }
  257. var button = createAButton(element, "质量", function(url){
  258. questQC(values_info, url);
  259. }.bind(null, url),"");
  260. }
  261. }
  262.  
  263. function init(){
  264. // try to get csdn_id from href
  265. let id_info = get_csdn_id();
  266. values_info[id_info.id_type]["value"] = id_info.id;
  267. // fill values but not get value from user
  268. fill_values(true, false);
  269. }
  270.  
  271. async function mainFunc(){
  272. // init headers and csdn_id
  273. init();
  274. // check href, https://blog.csdn.net/{id}?type=blog or type=lately
  275. if (href.match(/blog.csdn.net\/\w+\?type=blog/) || href.match(/blog.csdn.net\/\w+\?type=lately/)){
  276. createManyButton();
  277. // when load more, create button again
  278. window.addEventListener("scroll", function(){
  279. createManyButton();
  280. });
  281. }
  282. else {
  283. createAButton(document.body,"质量分查询",function(){questQC(values_info, href);},
  284. "height:75px;position:absolute;z-index:999;top:5%;left:1%");
  285. }
  286. }
  287.  
  288. (function() {
  289. 'use strict';
  290. // add mainFunc to onload
  291. var oldonload = window.onload;
  292. if (typeof window.onload != 'function') {
  293. mainFunc();
  294. }
  295. else {
  296. window.onload = function() {
  297. oldonload();
  298. mainFunc();
  299. }
  300. }
  301. // add menu
  302. GM_registerMenuCommand("参数填写", fill_values.bind(null, false, true));
  303. })();