b站统计真实评分

b站统计真实评分,使用后在番剧介绍页和游戏介绍页会多一个“计算真实评分”的按钮,点击将会收集**所有能进行访问的评论**进行统计,得出真实评分。

  1. // ==UserScript==
  2. // @name b站统计真实评分
  3. // @namespace http://tampermonkey.net/
  4. // @version 0.2.2
  5. // @description b站统计真实评分,使用后在番剧介绍页和游戏介绍页会多一个“计算真实评分”的按钮,点击将会收集**所有能进行访问的评论**进行统计,得出真实评分。
  6. // @author thunder-sword【b站up主:月雨洛然】
  7. // @match https://www.bilibili.com/bangumi/media/md*
  8. // @match https://www.biligame.com/detail/?id=*
  9. // @icon https://www.google.com/s2/favicons?sz=64&domain=bilibili.com
  10. // @require https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.0.0/crypto-js.min.js
  11. // @license MIT
  12. // @grant none
  13. // ==/UserScript==
  14.  
  15. //常量作用:全局设置
  16. const settings = {
  17. "game_float_button": 1, //游戏区页面是否启用悬浮窗按钮,启用后会在页面右下角多一个“计算真实评分”的悬浮按钮,因为页面元素不同缘故,有些游戏不能不好插入页面按钮,所以可以点击悬浮窗运行代码
  18. "reTryGapTime": 500, //失败时重试等待时间
  19. }
  20.  
  21. //作用:检验页面作用域,并执行对应函数
  22. function mainFunction(){
  23. let id = 0;
  24. if(location.href.match(/media\/md(\d+)/)){
  25. console.log("当前位于视频页面");
  26. id = location.href.match(/media\/md(\d+)/)[1];
  27. statMedia(id);
  28. } else if(location.href.match(/biligame.com\/detail\/\?id=(\d+)/)) {
  29. console.log("当前位于游戏页面");
  30. id = location.href.match(/biligame.com\/detail\/\?id=(\d+)/)[1];
  31. statGame(id);
  32. } else{
  33. alert("未知的页面,无法运行脚本");
  34. throw new Error("未知的页面,无法运行脚本");
  35. }
  36. }
  37.  
  38. //作用:运行时的进度条渲染
  39. function beforeRender(type) {
  40. const dialog = document.createElement('div');
  41. document.body.appendChild(dialog);
  42. dialog.style.position = 'fixed';
  43. dialog.style.width = '100%';
  44. dialog.style.height = '100%';
  45. dialog.style.background = 'rgba(0,0,0,.8)';
  46. dialog.style.top = '0';
  47. dialog.style.left = '0';
  48. dialog.style.zIndex = '999';
  49. dialog.style.display = 'flex';
  50. dialog.style.alignItems = 'center';
  51. dialog.style.justifyContent = 'center';
  52.  
  53.  
  54. const dialogContent = document.createElement('div');
  55. dialog.appendChild(dialogContent);
  56.  
  57. dialogContent.style.width = '455px';
  58. dialogContent.style.height = '200px';
  59. dialogContent.style.background = '#fff';
  60. dialogContent.style.borderRadius = '6px';
  61. dialogContent.style.padding = '51px 0';
  62.  
  63. var render = undefined;
  64.  
  65. if("media"==type){
  66. const shortWrap = document.createElement('div');
  67. dialogContent.appendChild(shortWrap);
  68. const longWrap = document.createElement('div');
  69. dialogContent.appendChild(longWrap);
  70.  
  71. shortWrap.style.width = longWrap.style.width = '455px';
  72. shortWrap.style.height = longWrap.style.height = '100px';
  73. shortWrap.style.display = longWrap.style.display = 'flex';
  74. shortWrap.style.alignItems = longWrap.style.alignItems = 'center';
  75. shortWrap.style.justifyContent = longWrap.style.justifyContent = 'center';
  76.  
  77. // --------------
  78. const shortw1 = document.createElement('div');
  79. const longw1 = document.createElement('div');
  80. shortWrap.appendChild(shortw1);
  81. longWrap.appendChild(longw1);
  82. shortw1.innerText = '短评:';
  83. longw1.innerText = '长评:';
  84. longw1.style.fontSize = shortw1.style.fontSize = '14px';
  85. longw1.style.color = shortw1.style.color = '#333';
  86. longw1.style.marginRight = shortw1.style.marginRight = '16px';
  87.  
  88.  
  89. const shortw2 = document.createElement('div');
  90. const longw2 = document.createElement('div');
  91. shortWrap.appendChild(shortw2);
  92. longWrap.appendChild(longw2);
  93. longw2.style.width = shortw2.style.width = '300px';
  94. longw2.style.height = shortw2.style.height = '32px';
  95. longw2.style.background = shortw2.style.background = '#eee';
  96. longw2.style.position = shortw2.style.position = 'relative';
  97.  
  98.  
  99. const shortPrg = document.createElement('div');
  100. const longPrg = document.createElement('div');
  101. shortw2.appendChild(shortPrg);
  102. longw2.appendChild(longPrg);
  103.  
  104. longPrg.style.position = shortPrg.style.position = 'absolute';
  105. longPrg.style.left = shortPrg.style.left = '0';
  106. longPrg.style.top = shortPrg.style.top = '0';
  107. longPrg.style.width = shortPrg.style.width = '0%';
  108. longPrg.style.height = shortPrg.style.height = '100%';
  109. longPrg.style.background = shortPrg.style.background = '#ff85ad';
  110.  
  111.  
  112. render = function (type, percent) {
  113. const dom = type == 'long' ? longPrg : shortPrg;
  114. let width = percent + '%';
  115. dom.style.width = width;
  116. }
  117. } else if("game"==type){
  118. const nomalWrap = document.createElement('div');
  119. dialogContent.appendChild(nomalWrap);
  120.  
  121. nomalWrap.style.width = '455px';
  122. nomalWrap.style.height = '100px';
  123. nomalWrap.style.display = 'flex';
  124. nomalWrap.style.alignItems = 'center';
  125. nomalWrap.style.justifyContent = 'center';
  126.  
  127. // --------------
  128. const nomalw1 = document.createElement('div');
  129. nomalWrap.appendChild(nomalw1);
  130. nomalw1.innerText = '评价:';
  131. nomalw1.style.fontSize = '14px';
  132. nomalw1.style.color = '#333';
  133. nomalw1.style.marginRight = '16px';
  134.  
  135. const nomalw2 = document.createElement('div');
  136. nomalWrap.appendChild(nomalw2);
  137. nomalw2.style.width = '300px';
  138. nomalw2.style.height = '32px';
  139. nomalw2.style.background = '#eee';
  140. nomalw2.style.position = 'relative';
  141.  
  142.  
  143. const nomalPrg = document.createElement('div');
  144. nomalw2.appendChild(nomalPrg);
  145.  
  146. nomalPrg.style.position = 'absolute';
  147. nomalPrg.style.left = '0';
  148. nomalPrg.style.top = '0';
  149. nomalPrg.style.width = '0%';
  150. nomalPrg.style.height = '100%';
  151. nomalPrg.style.background = '#ff85ad';
  152.  
  153.  
  154. render = function (percent) {
  155. const dom = nomalPrg;
  156. let width = percent + '%';
  157. dom.style.width = width;
  158. }
  159. } else{
  160. alert(`未知的参数值${type}`);
  161. throw new Error(`未知的参数值${type}`);
  162. }
  163.  
  164. var rmDialog = function () {
  165. document.body.removeChild(dialog);
  166. }
  167.  
  168. return {"render": render, "rmDialog": rmDialog}
  169. }
  170.  
  171. async function delay(ms) {
  172. return new Promise(resolve => setTimeout(resolve, ms));
  173. }
  174.  
  175. //作用:单次请求视频评论
  176. //参数说明:
  177. /*
  178. *next:单次查询光标位置
  179. *type:选择长评或短评
  180. */
  181. async function mediaGetScore(mid, next, type) {
  182. while(true){
  183. let url = `https://api.bilibili.com/pgc/review/${type}/list?media_id=${mid}&ps=12575&sort=0`;
  184. if (next) {
  185. url += `&cursor=${next}`;
  186. }
  187. const res = await fetch(url, { "method": "GET" });
  188. const { data } = await res.json();
  189. if(data!==undefined) return data;
  190. await delay(settings.reTryGapTime);
  191. }
  192. }
  193. //作用:根据长评或短评统计视频评论主函数,统计的同时调用render进行进度条渲染,返回当前总分数和总人数
  194. async function mediaGetAllScores(mid, type, render) {
  195. let score=0, //目前总分数
  196. count=0; //目前总人数
  197. let { list, next, total } = await mediaGetScore(mid, undefined, type);
  198. count+=list.length;
  199. for (let i = 0; i < list.length; i++) {
  200. score += list[i].score;
  201. }
  202. render(type, count * 100 / total);
  203.  
  204. while (true) {
  205. const data = await mediaGetScore(mid, next, type);
  206. count+=data.list.length;
  207. for (let i = 0; i < data.list.length; i++) {
  208. score += data.list[i].score;
  209. }
  210. render(type, count * 100 / total);
  211. next = data.next;
  212. if (next == 0) {
  213. return {"score": score, "count": count};
  214. }
  215. }
  216. }
  217. //作用:视频评分统计主函数,返回最后评分和评分人数
  218. async function mediaScoreMain(id){
  219. //所用参数
  220. let shortScore = 0, //短评总分数
  221. shortCount = 0, //短评人数
  222. shortAverage = 0, //短评平均分
  223. longScore = 0,
  224. longCount = 0,
  225. longAverage = 0,
  226. totalCount = 0,
  227. totalAverage = 0;
  228. const func = beforeRender("media");
  229. const shortData = await mediaGetAllScores(id, "short", func.render);
  230. shortScore = shortData.score, shortCount = shortData.count;
  231. shortAverage = shortScore / shortCount;
  232. console.log(`短评评分人数:${shortCount}`);
  233. console.log(`短评平均分数:${shortAverage}`);
  234. const longData = await mediaGetAllScores(id, "long", func.render);
  235. longScore = longData.score, longCount = longData.count;
  236. longAverage = longScore / longCount;
  237. console.log(`长评评分人数:${longCount}`);
  238. console.log(`长评平均分数:${longAverage}`);
  239. func.rmDialog();
  240. totalCount = shortCount + longCount;
  241. totalAverage = (shortScore + longScore) / totalCount;
  242. console.log(`总平均分数:${totalAverage}`);
  243. showStackToast("统计结束");
  244. showStackToast(`共统计${totalCount}条评价`);
  245. showStackToast(`真实评分为:${totalAverage.toFixed(1)}`);
  246. return {"score": totalAverage, "count": totalCount};
  247. }
  248.  
  249. //作用:视频评分最后添加元素对真实评分进行显示
  250. function mediaShowScore(score, count){
  251. const trueScore = score.toFixed(1);
  252. const starLc = parseInt(Math.round(trueScore / 2));
  253. const starHc = 5 - starLc;
  254. let container = document.querySelector("#app > div.media-info-wrp > div.media-info-content > div > div.media-info-r > div.media-info-datas > div.media-info-score-wrp");
  255. if(!container){
  256. alert("获取评分容器失败,不能添加元素!");
  257. throw new Error("获取评分容器失败,不能添加元素!");
  258. }
  259.  
  260. let parent = document.createElement("div");
  261. container.appendChild(parent);
  262. parent.className="media-info-score";
  263. parent.innerHTML=`<div class="media-info-score-content" style="color: #00aeec">${trueScore}</div> <div class="media-info-star-wrapper"><span class="review-stars"></span><div class="media-info-review-times" style="color: #00aeec">${count}人评</div> <div class="to-review-btn"><i class="icon-edit"></i>
  264. 我要点评
  265. </div></div>`;
  266.  
  267. //创建星星样式
  268. let style = document.createElement("style");
  269. style.innerText = `.icon-star-light-mod:before {
  270. content: "\\E906";
  271. color: #00aeec;
  272. }`;
  273. document.head.appendChild(style);
  274.  
  275. let starsDom = parent.querySelector("span.review-stars");
  276. for (let i = 0; i < starLc; i++) {
  277. const star = document.createElement('i');
  278. star.className = "icon-star icon-star-light-mod";
  279. starsDom.appendChild(star);
  280. }
  281. for (let i = 0; i < starHc; i++) {
  282. const star = document.createElement('i');
  283. star.className = "icon-star";
  284. starsDom.appendChild(star);
  285. }
  286. }
  287.  
  288. //作用:视频评分统计主函数
  289. function statMedia(mid){
  290. //添加开始统计按钮
  291. var container=document.querySelector("#app > div.media-info-wrp > div.media-info-content > div > div.media-info-r > div.media-info-btns");
  292. if(!container){
  293. alert("获取按钮容器失败,请刷新重试!");
  294. throw new Error("获取按钮容器失败,请刷新重试!");
  295. }
  296. var button=document.createElement("div");
  297. button.setAttribute("class", "btn-pay-wrapper");
  298. button.setAttribute("style", "margin-left: 20px");
  299. button.innerHTML=`<a href="javascript:;" class="pay-btn ">计算真实评分</a> <div class="pic-wrapper"></div>`;
  300. button.addEventListener("click", async () => {
  301. let { score, count } = await mediaScoreMain(mid);
  302. mediaShowScore(score, count);
  303. });
  304. container.appendChild(button);
  305. }
  306.  
  307. class NetworkError extends Error {
  308. constructor(message) {
  309. super(message);
  310. this.name = "NetworkError";
  311. }
  312. }
  313.  
  314. //作用:根据传入的原型params生成sign访问api,并将访问结果返回
  315. async function gameQuery(params){
  316. const url = "https://line1-h5-pc-api.biligame.com/game/comment/page";
  317. const sign = CryptoJS.MD5(params + "BdiI92bjmZ9QRcjJBWv2EEssyjekAGKt");
  318. const data = await fetch(url+"?"+params+"&sign="+sign)
  319. .then(function(response) {
  320. if(!response.ok) {
  321. throw new NetworkError(`HTTP error! status: ${response.status}`);
  322. }
  323. return response.json();
  324. }).catch(error => {
  325. if (error instanceof NetworkError) {
  326. console.error("Network error: ", error.message);
  327. } else {
  328. console.error("Other error: ", error);
  329. alert("获取api数据失败,请联系开发者");
  330. }
  331. });
  332. return data;
  333. }
  334.  
  335. //作用:获取request_id,即一串32位长度的字符串
  336. function getRequestId(){
  337. const a = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"];
  338. let request_id = "";
  339. for(let i = 0;i < 32;i++){
  340. request_id+=a[Math.floor(Math.random()*32)];
  341. }
  342. return request_id;
  343. }
  344.  
  345. //作用:统计游戏评论主函数,统计的同时调用render进行进度条渲染,返回当前总分数和总人数
  346. async function gameGetAllScores(gid, render) {
  347. let score=0, //目前总分数
  348. count=0; //目前总人数
  349. let request_id = getRequestId();
  350. let params = `appkey=h9Ejat5tFh81cq8V&game_base_id=${gid}&page_num=1&page_size=10&rank_type=3&request_id=${request_id}&ts=9692695382316`;
  351. let { code, data } = await gameQuery(params);
  352. if(0!==code){
  353. alert("访问api失败,请联系开发者");
  354. console.log(code, data);
  355. throw new Error("访问api失败,请联系开发者");
  356. }
  357. const page_count = data.page_count + 1;
  358. const total = page_count * 10;
  359. count+=data.list.length;
  360. //console.log(render);
  361. //console.log(count, total);
  362. for (let i = 0; i < data.list.length; i++) {
  363. score += data.list[i].grade;
  364. }
  365. render(count * 100 / total);
  366.  
  367. for (let page = 2; page <= page_count; page++) {
  368. request_id = getRequestId();
  369. params = `appkey=h9Ejat5tFh81cq8V&game_base_id=${gid}&page_num=${page}&page_size=10&rank_type=3&request_id=${request_id}&ts=9692695382316`;
  370. const data = await gameQuery(params);
  371. if ( -703===data.code ) break; //返回数据为空
  372. if ( 0!==data.code ){
  373. alert("api访问异常,请联系开发者");
  374. console.log(data);
  375. throw new Error("api访问异常,请联系开发者");
  376. }
  377. count+=data.data.list.length;
  378. for (let i = 0; i < data.data.list.length; i++) {
  379. score += data.data.list[i].grade;
  380. }
  381. render(count * 100 / total);
  382. }
  383. return {"score": score, "count": count};
  384. }
  385.  
  386. //作用:游戏评分统计主函数,返回最后评分和评分人数
  387. async function gameScoreMain(id){
  388. //所用参数
  389. let totalAverage = 0;
  390. const func = beforeRender("game");
  391. const { score, count } = await gameGetAllScores(id, func.render);
  392. totalAverage = score / count;
  393. console.log(`评分人数:${count}`);
  394. console.log(`平均分数:${totalAverage}`);
  395. func.rmDialog();
  396. showStackToast("统计结束");
  397. showStackToast(`共统计${count}条评价`);
  398. showStackToast(`真实评分为:${totalAverage.toFixed(1)}`);
  399. return {"score": totalAverage, "count": count};
  400. }
  401.  
  402. //作用:游戏评分最后添加元素对真实评分进行显示
  403. function gameShowScore(score, count){
  404. const trueScore = score.toFixed(1);
  405. const starLc = parseInt(Math.round(trueScore / 2));
  406. const starHc = 5 - starLc;
  407. const container = document.querySelector("body > div.bui-gc > div.header-bar.one-row > div.right-panel > div > div > div.game-introduce > div.introduce-title > div.introduce-info");
  408. if(!container){
  409. showStackToast("获取评分容器失败,无法将评分放入页面内!");
  410. throw new Error("获取评分容器失败,无法将评分放入页面内!");
  411. }
  412. const brother = container.querySelector("div.introduce-count");
  413. if(!brother){
  414. alert("获取brother评分容器失败,不能添加元素!");
  415. throw new Error("获取brother评分容器失败,不能添加元素!");
  416. }
  417.  
  418. let parent = document.createElement("div");
  419. container.insertBefore(parent, brother);
  420. parent.className="introduce-rate";
  421. parent.innerHTML=`<div class="mini-grade-summary"><span class="bui-star"></span><span class="grade introduce-grade" style="color: #00aeec !important">${trueScore}分</span></div>`;
  422.  
  423. //创建星星样式
  424. let style = document.createElement("style");
  425. style.innerText = `.bui-star .bui-icon-star.filled-mod {
  426. fill: #00aeec;
  427. }`;
  428. document.head.appendChild(style);
  429.  
  430. let starsDom = parent.querySelector("span.bui-star");
  431. for (let i = 0; i < starLc; i++) {
  432. const star = document.createElementNS("http://www.w3.org/2000/svg", "svg");
  433. star.setAttribute("class","bui-icon bui-icon-star filled-mod");
  434. star.setAttribute("x",'0');
  435. star.setAttribute("y",'0');
  436. star.setAttribute("viewBox",'0 0 20 20');
  437. const path = document.createElementNS("http://www.w3.org/2000/svg", "path");
  438. path.setAttribute("d", "M17.7,5.6l-2.3-0.3c-0.8-0.1-1.6-0.7-2-1.5l-1.1-2.3c-1-2-3.7-2-4.7,0l-1,2.3c-0.4,0.8-1.1,1.4-2,1.5L2.2,5.6\n\t\tc-2.2,0.3-3,3.2-1.4,4.8l1.5,1.6c0.6,0.6,0.9,1.6,0.8,2.5l-0.4,2.3c-0.4,2.3,1.8,4,3.8,3l2.2-1.2c0.7-0.4,1.6-0.4,2.4,0l2.2,1.2\n\t\tc1.9,1,4.2-0.7,3.8-3l-0.4-2.3c-0.2-0.9,0.1-1.9,0.8-2.5l1.6-1.6C20.8,8.8,19.9,5.9,17.7,5.6z");
  439. star.appendChild(path);
  440. starsDom.appendChild(star);
  441. }
  442. for (let i = 0; i < starHc; i++) {
  443. const star = document.createElementNS("http://www.w3.org/2000/svg", "svg");
  444. star.setAttribute("class","bui-icon bui-icon-star");
  445. star.setAttribute("x",'0');
  446. star.setAttribute("y",'0');
  447. star.setAttribute("viewBox",'0 0 20 20');
  448. const path = document.createElementNS("http://www.w3.org/2000/svg", "path");
  449. path.setAttribute("d", "M17.7,5.6l-2.3-0.3c-0.8-0.1-1.6-0.7-2-1.5l-1.1-2.3c-1-2-3.7-2-4.7,0l-1,2.3c-0.4,0.8-1.1,1.4-2,1.5L2.2,5.6\n\t\tc-2.2,0.3-3,3.2-1.4,4.8l1.5,1.6c0.6,0.6,0.9,1.6,0.8,2.5l-0.4,2.3c-0.4,2.3,1.8,4,3.8,3l2.2-1.2c0.7-0.4,1.6-0.4,2.4,0l2.2,1.2\n\t\tc1.9,1,4.2-0.7,3.8-3l-0.4-2.3c-0.2-0.9,0.1-1.9,0.8-2.5l1.6-1.6C20.8,8.8,19.9,5.9,17.7,5.6z");
  450. star.appendChild(path);
  451. starsDom.appendChild(star);
  452. }
  453. }
  454.  
  455. //作用:创建一个在右下角出现的悬浮窗按钮,点击即可执行对应函数
  456. function createFloatButton(name, func){
  457.  
  458. // 创建一个 div 元素
  459. var floatWindow = document.createElement('div');
  460.  
  461. // 设置 div 的内容
  462. //floatWindow.innerHTML = '点我执行代码';
  463. floatWindow.innerHTML = name;
  464.  
  465. // 设置 div 的样式
  466. floatWindow.style.position = 'fixed';
  467. floatWindow.style.bottom = '10px';
  468. floatWindow.style.right = '10px';
  469. floatWindow.style.padding = '5px 10px';
  470. floatWindow.style.backgroundColor = '#333';
  471. floatWindow.style.color = '#fff';
  472. floatWindow.style.cursor = 'pointer';
  473.  
  474. // 将悬浮窗的优先级提高
  475. floatWindow.style.zIndex = "99999";
  476.  
  477. var isDragging = false;
  478. var currentX;
  479. var currentY;
  480. var initialX;
  481. var initialY;
  482. var xOffset = 0;
  483. var yOffset = 0;
  484. var cursorX;
  485. var cursorY;
  486.  
  487. floatWindow.addEventListener("mousedown", function(e) {
  488. if (!isDragging) {
  489. cursorX = e.clientX;
  490. cursorY = e.clientY;
  491. initialX = cursorX - xOffset;
  492. initialY = cursorY - yOffset;
  493. isDragging = true;
  494. }
  495. });
  496. floatWindow.addEventListener("mousemove", function(e) {
  497. if (isDragging) {
  498. e.preventDefault();
  499. currentX = e.clientX - initialX;
  500. currentY = e.clientY - initialY;
  501.  
  502. xOffset = currentX;
  503. yOffset = currentY;
  504.  
  505. setTranslate(currentX, currentY, floatWindow);
  506. }
  507. });
  508. floatWindow.addEventListener("mouseup", async function(e) {
  509. initialX = currentX;
  510. initialY = currentY;
  511.  
  512. isDragging = false;
  513. // 如果点击时鼠标的位置没有改变,就认为是真正的点击
  514. if (cursorX === e.clientX && cursorY === e.clientY) {
  515. await func();
  516. }
  517. });
  518.  
  519. // 为悬浮窗添加事件处理程序,用来监听触摸开始和触摸移动事件
  520. // 这些事件处理程序的实现方式与上面的鼠标事件处理程序类似
  521. floatWindow.addEventListener('touchstart', (event) => {
  522. if (!isDragging) {
  523. cursorX = event.touches[0].clientX;
  524. cursorY = event.touches[0].clientY;
  525. initialX = cursorX - xOffset;
  526. initialY = cursorY - yOffset;
  527. isDragging = true;
  528. }
  529. });
  530. floatWindow.addEventListener('touchmove', (event) => {
  531. if (isDragging) {
  532. currentX = event.touches[0].clientX - initialX;
  533. currentY = event.touches[0].clientY - initialY;
  534.  
  535. xOffset = currentX;
  536. yOffset = currentY;
  537.  
  538. setTranslate(currentX, currentY, floatWindow);
  539. }
  540. });
  541.  
  542. // 为悬浮窗添加事件处理程序,用来监听触摸结束事件
  543. // 这个事件处理程序的实现方式与上面的鼠标事件处理程序类似
  544. floatWindow.addEventListener('touchend', async () => {
  545. initialX = currentX;
  546. initialY = currentY;
  547.  
  548. isDragging = false;
  549. // 如果点击时鼠标的位置没有改变,就认为是真正的点击
  550. if (cursorX === event.touches[0].clientX && cursorY === event.touches[0].clientY) {
  551. await func();
  552. }
  553. });
  554.  
  555. function setTranslate(xPos, yPos, el) {
  556. el.style.transform = "translate3d(" + xPos + "px, " + yPos + "px, 0)";
  557. }
  558.  
  559. // 将悬浮窗添加到 body 元素中
  560. document.body.appendChild(floatWindow);
  561. }
  562.  
  563. //作用:游戏评分统计主函数
  564. function statGame(gid){
  565. //添加悬浮窗按钮
  566. if(settings["game_float_button"]){
  567. createFloatButton("计算真实评分", async () => {
  568. let { score, count } = await gameScoreMain(gid);
  569. gameShowScore(score, count);
  570. });
  571. }
  572. setTimeout( () => {
  573. //添加开始统计按钮
  574. var container=document.querySelector("body > div.bui-gc > div.header-bar.one-row > div.right-panel > div > div > div.game-download > div.download-buttons.not-exist-app-store");
  575. if(!container){
  576. showStackToast("获取按钮容器失败,请使用右下角悬浮窗按钮计算真实评分!");
  577. throw new Error("获取按钮容器失败,请使用右下角悬浮窗按钮计算真实评分!");
  578. }
  579. var button=document.createElement("a");
  580. button.innerHTML=`<span>计算真实评分</span>`;
  581. button.addEventListener("click", async () => {
  582. let { score, count } = await gameScoreMain(gid);
  583. gameShowScore(score, count);
  584. });
  585. container.insertBefore(button, container.firstChild);
  586. }, 800);
  587. }
  588.  
  589. //作用:生成toast,让其在toast_container中,显示在页面中上部,会永久性向页面添加一个id为ths_toast_container的div标签
  590. function showStackToast(message, timeout=3000){
  591. //没有容器则生成容器
  592. let box=document.querySelector("body > div#ths_toast_container");
  593. if(!box){
  594. box=document.createElement('div');
  595. box.id="ths_toast_container";
  596. box.style.cssText = `
  597. position: fixed;
  598. top: 10px;
  599. left: 50%;
  600. transform: translateX(-50%);
  601. right: 10px;
  602. width: 300px;
  603. height: auto;
  604. display: flex;
  605. z-index: 9999;
  606. flex-direction: column-reverse;`;
  607. document.body.appendChild(box);
  608. }
  609. //创建toast
  610. const toast = document.createElement('div');
  611. toast.innerText = message;
  612. toast.style.cssText = `
  613. padding: 10px;
  614. background-color: rgb(76, 175, 80);
  615. color: rgb(255, 255, 255);
  616. border-radius: 10px;
  617. font-size: 24px;
  618. font-weight: bold;
  619. text-align: center;
  620. box-shadow: rgb(0 0 0 / 30%) 0px 5px 10px;
  621. opacity: 1;
  622. transition: opacity 0.3s ease-in-out 0s;
  623. z-index: 9999;
  624. margin: 5px;
  625. `;
  626. box.appendChild(toast);
  627. toast.style.opacity = 1;
  628. if(timeout > 0){
  629. setTimeout(() => {
  630. toast.style.opacity = 0;
  631. setTimeout(() => {
  632. box.removeChild(toast);
  633. }, 300);
  634. }, timeout);
  635. }
  636. return toast;
  637. }
  638.  
  639. (function() {
  640. 'use strict';
  641. mainFunction();
  642. })();
  643.  
  644.  
  645.  
  646.  
  647.  
  648.  
  649.  
  650.  
  651.  
  652.  

QingJ © 2025

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