Tools 是非官方社区Wiki。社区文档正在编写中,欢迎参与。 Wiki编辑答疑群:717421103
版本250722.2
全站通知:

Widget:EditYear2024

来自WIKI实验室WIKI_BWIKI_哔哩哔哩
跳到导航 跳到搜索

<script> try{ (function (global) {

 'use strict';
 class BYearCacheDB {
   constructor(dbName = 'YeatEditDB', storeName = 'YeatEditData', version = 1) {
     this.dbName = dbName;
     this.storeName = storeName;
     this.version = version;
     this.db = null;
   }
   async init() {
     return new Promise((resolve, reject) => {
       const request = indexedDB.open(this.dbName, this.version);
       request.onupgradeneeded = (event) => {
         this.db = event.target.result;
         if (!this.db.objectStoreNames.contains(this.storeName)) {
           this.db.createObjectStore(this.storeName, {keyPath: 'key'});
         }
       };
       request.onsuccess = (event) => {
         this.db = event.target.result;
         resolve();
       };
       request.onerror = (event) => {
         console.error('IndexedDB 初始化失败:', event.target.error);
         reject(event.target.error);
       };
     });
   }
   async setCache(key, data) {
     return new Promise((resolve, reject) => {
       const transaction = this.db.transaction([this.storeName], 'readwrite');
       const store = transaction.objectStore(this.storeName);
       const cacheItem = {
         key: key,
         timestamp: Date.now(),
         data: data
       };
       const request = store.put(cacheItem);
       request.onsuccess = () => {
         resolve();
       };
       request.onerror = (event) => {
         console.error('设置缓存失败:', event.target.error);
         reject(event.target.error);
       };
     });
   }
   async getCache(key) {
     return new Promise((resolve, reject) => {
       const transaction = this.db.transaction([this.storeName], 'readonly');
       const store = transaction.objectStore(this.storeName);
       const request = store.get(key);
       request.onsuccess = (event) => {
         const result = event.target.result;
         if (result) {
           resolve(result);
         } else {
           resolve(null);
         }
       };
       request.onerror = (event) => {
         console.error('获取缓存失败:', event.target.error);
         reject(event.target.error);
       };
     });
   }
   // 删除缓存
   async deleteCache(key) {
     return new Promise((resolve, reject) => {
       const transaction = this.db.transaction([this.storeName], 'readwrite');
       const store = transaction.objectStore(this.storeName);
       const request = store.delete(key);
       request.onsuccess = () => {
         resolve();
       };
       request.onerror = (event) => {
         console.error('删除缓存失败:', event.target.error);
         reject(event.target.error);
       };
     });
   }
   // 清空所有缓存
   async clearCache() {
     return new Promise((resolve, reject) => {
       const transaction = this.db.transaction([this.storeName], 'readwrite');
       const store = transaction.objectStore(this.storeName);
       const request = store.clear();
       request.onsuccess = () => {
         resolve();
       };
       request.onerror = (event) => {
         console.error('清空缓存失败:', event.target.error);
         reject(event.target.error);
       };
     });
   }
 }
 // 成就管理模块
 var AchievementManager = {
   processAchievements: function (data) {
     var achievements = [];
     var config = EditYearCount.config.achievements;
     for (var i = 0; i < config.length; i++) {
       var achievement = config[i];
       try {
         if (achievement.condition(data)) {
           achievements.push(achievement);
         }
       } catch (e) {
         console.error('处理成就时出错:', achievement.name, e);
       }
     }
     return achievements;
   }
 };
 // 编辑成就模块
 var EditYearCount = {
   // 配置对象
   config: {
     cacheKey: 'editYearCache2024',
     cacheDuration: 24 * 60 * 60 * 1000, // 缓存持续时间 24小时
   },
   // 初始化函数
   init: function (useCache = true) {
     Utils.onApiReady(async function () {
       var textarea = document.getElementById('editOverviewJsonBox');
       if (textarea) {
         console.log("exist textarea",textarea.value)
       } else {
         HTMLLogger.log("
"); textarea = document.createElement('textarea'); textarea.id = 'editOverviewJsonBox'; textarea.rows = 10; textarea.name = 'editOverviewJsonBox233'; textarea.placeholder = "粘贴编辑概览API返回的JSON数据"; textarea.value = ""; HTMLLogger.log("
"); document.querySelector('#year-backup-inputs').innerHTML += '刷新缓存
你还可以在下方手动给出 这里 返回的JSON数据来查看编辑报告,粘贴内容后点击刷新缓存。'; document.querySelector('#year-backup-inputs').appendChild(textarea); }
       var refreshButton = document.querySelector('#refresh-achievement2');
       if(refreshButton){
         refreshButton.addEventListener('click', async function () {
           refreshButton.disabled = true;
           refreshButton.textContent = '刷新中...';
           await EditYearCount.refresh();
           refreshButton.disabled = false;
           refreshButton.textContent = '刷新缓存';
         });
       }
       var data = await EditYearCount.loadData(useCache);
       if (data){
         for (let i = 0; i < data.wiki.length; i++) {
           data.wiki[i] = EditYearCount.stat(data.wiki[i]);
         }
         // data = EditYearCount.stat(data);
         console.log("loadData end", data)
         window.data = data;
         console.log("可以在控制台访问 data 变量查看数据");
         // return
         Renderer.display(data);
       }
     });
   },
   stat:function (d) {
     var eidt_ns = (data, ns) => data.contributions.filter(t => t.ns === ns).length
     var edit_type = (data, type = ".css") => data.contributions.filter(t => t.title.endsWith(type)).length
     d.stat = {
       editcount: d.user.editcount,
       editcount_year: d.contributions.length,
       eidt_ns: {
         0: eidt_ns(d, 0),
         2: eidt_ns(d, 2),
         4: eidt_ns(d, 4),
         6: eidt_ns(d, 6),
         8: eidt_ns(d, 8),
         10: eidt_ns(d, 10),
         12: eidt_ns(d, 12),
         14: eidt_ns(d, 14),
         274: eidt_ns(d, 274),
         828: eidt_ns(d, 828),
       },
       edit_type:{
         "css": edit_type(d, ".css"),
         "js": edit_type(d, ".js"),
       },
       groups: d.user.groups,
       registration: d.user.registration,
     }
     return d;
   },
   // 加载数据(包括缓存处理)
   loadData: async function (useCache = true) {
     var cached = await Utils.getCache(EditYearCount.config.cacheKey);
     if (useCache && cached && !Utils.isCacheExpired(cached.timestamp, EditYearCount.config.cacheDuration)) {
       console.log("从缓存加载数据");
       return cached.data;
     } else {
       console.log("从API加载数据");
       var data = await DataFetcher.fetchAllData();
       if (data){
         Utils.setCache(EditYearCount.config.cacheKey, data).then(() => {});
       }
       return data;
     }
   },
   // 手动刷新数据接口
   refresh: async function () {
     console.log("手动刷新数据开始");
     EditYearCount.init(false);
   }
 };
 // 数据获取模块
 var DataFetcher = {
   fetchAllData: async function () {
     var textarea = document.getElementById('editOverviewJsonBox');
     console.log("textareatextarea", textarea)
     console.log("textareatextarea", textarea.value)
     var editOverview = {}
     if (textarea && textarea.value && textarea.value.length > 1){
       editOverview = JSON.parse(textarea.value).data;
       console.log("手动输入的数据", editOverview)
     }else{
       editOverview = await DataFetcher.getEditOverview();
       if (!editOverview || !editOverview.game_list) {
         console.error('获取编辑概览数据失败:', editOverview);
         HTMLLogger.log("获取编辑概览数据失败,推荐用电脑浏览器查看,掉登录也可能导致无法请求游戏中心API。")
         return null;
       }
     }
     var wikiList = [];
     // 使用 for 循环遍历数组
     for (let i = 0; i < editOverview.game_list.length; i++) {
         const obj = editOverview.game_list[i];
         wikiList.push({
             name: obj.game_name,
             path: obj.wiki_url.replace("https://wiki.biligame.com/","")// 只保留.com后边的部分
         });
     }
     var allData = {
       editOverview:{
         username: editOverview.mid,
         game_list: wikiList,
         game_count: editOverview.game_count,
         total_edit_count: editOverview.total_edit_count,
       },
       wiki:[]
     };
     HTMLLogger.log("开始获取数据!这可能需要一点时间,请耐心等待。数据会缓存在浏览器IndexDB中。
") // 查看有哪些wiki,挨个遍历 for (let i = 0; i < wikiList.length; i++) { let wiki_name = wikiList[i].name; let wiki_path = wikiList[i].path;
       var wiki_api_url = 'https://wiki.biligame.com/' + wiki_path + '/api.php';
       console.log("get wiki data", wiki_name, wiki_path, wiki_api_url)
       HTMLLogger.log("获取数据:" + wiki_name + " " + wiki_path + "
") const response = await fetch(wiki_api_url, { method: 'HEAD', redirect: 'follow' }); if (response.redirected) { // wiki 不在了 continue; } allData.wiki.push({ name: wiki_name, path: wiki_path, contributions: await DataFetcher.getUserContributions(wiki_path, allData.editOverview.username), user: await DataFetcher.getUserInfoNew(wiki_path, allData.editOverview.username), }) } return allData; },


   getEditOverview: async function () {
     var url = 'https://le3-api.game.bilibili.com/pc/game/wiki/edit_overview';
     var response = await fetch(url, {credentials: "include"});
     var data = await response.json();
     if (data && data.data){
       return data.data;
     }
     HTMLLogger.log("游戏中心API(跨域请求)出错,目前已知APP无法请求此API。");
     console.warn("edit_overview API 请求出错,数据可能不完整。", data);
     return {};
   },
   getUserContributions: async function (wikiName, username) {
     var allContributions = [];
     var api = new mw.Api();
     api.defaults.ajax.url = '/' + wikiName + '/api.php';
     var params = {
       action: 'query',
       list: 'usercontribs',
       ucuser: username, // 获取当前用户名
       ucstart: "2024-12-31T16:00:00Z", // 2025.1.1 -8小时 因为处于+8时区
       ucend: "2023-12-31T16:00:00Z", // 2024.1.1 -8小时 因为处于+8时区
       uclimit: 'max',
       ucprop: 'title|timestamp',
       format: 'json',
     };
     try {
       do {
         var response = await api.get(params);
         if (response && response.query && response.query.usercontribs) {
           allContributions = allContributions.concat(response.query.usercontribs);
         }
         params.uccontinue = response.continue?.uccontinue || null;
       } while (response.continue);
     } catch (e) {
       console.warn('API 请求出错,数据可能不完整。错误:', e);
     }
     return allContributions;
   },
   getUserInfoNew: async function (wikiName, username) {
     var params = {
       action: 'query',
       list: 'users',
       ususers: username,
       usprop: 'groups|editcount|registration',
     };
     var api = new mw.Api();
     api.defaults.ajax.url = '/' + wikiName + '/api.php';
     try{
       var response = await api.get(params);
       if (response.query && response.query.users && response.query.users[0]) {
         return response.query.users[0];
       } else {
         return {};
       }
     }catch (e) {
         HTMLLogger.log("获取用户信息失败,可能是因为API请求失败。",e.toString());
         return {};
     }
   },
   getUserInfo: async function (wikiName) {
     var params = {
       action: 'query',
       meta: 'userinfo',
       uiprop: 'groups|editcount|registrationdate',
     };
     var api = new mw.Api();
     api.defaults.ajax.url = '/' + wikiName + '/api.php';
     var response = await api.get(params);
     if (response.query && response.query.userinfo) {
       return response.query.userinfo;
     } else {
       return {};
     }
   }
 };
 var HTMLLogger = {
   log: function (message) {
     var containerId = 'year-edit-log';
     var container = document.getElementById(containerId);
     if (!container) {
       container = document.createElement('div');
       container.id = containerId;
       document.querySelector('.mw-parser-output').appendChild(container);
     }
     container.innerHTML += message;
   },
   clear: function () {
     var containerId = 'year-edit-log';
     var container = document.getElementById(containerId);
     if (!container) {
       container = document.createElement('div');
       container.id = containerId;
       document.querySelector('.mw-parser-output').appendChild(container);
     }
     container.innerHTML = "";
   }
 }
 // 展示模块
 var Renderer = {
   display: async function (allData) {
     // 插入容器
     var containerId = 'year-edit-container';
     var container = document.getElementById(containerId) || (() => {
       var newDiv = document.createElement('div');
       newDiv.id = containerId;
       document.querySelector('.mw-parser-output').appendChild(newDiv);
       return newDiv;
     })();
     // 插入按钮
     var containerBtnsId = 'year-edit-reload-btns';
     var containerBtns = document.getElementById(containerBtnsId) || (() => {
       var newDiv = document.createElement('div');
       newDiv.id = containerBtnsId;
       newDiv.style.clear = 'both';
       document.querySelector('.mw-parser-output').appendChild(newDiv);
       return newDiv;
     })();
     containerBtns.innerHTML = '刷新缓存';
     // 添加刷新按钮事件
     var refreshButton = document.querySelector('#refresh-achievement');
     refreshButton.addEventListener('click', async function () {
       refreshButton.disabled = true;
       refreshButton.textContent = '刷新中...';
       await EditYearCount.refresh();
       // 模拟同步刷新完成
       refreshButton.disabled = false;
       refreshButton.textContent = '刷新缓存';
     });
     var innerHTML = ;
     // 拼接 wikitext
     var wikitext = "";
     wikitext += `BWiki 2024年度编辑报告,文字版(一早上写的能看就行了)

\n\n\n\n`; wikitext += `从你加入BWiki至今,已经参与了${allData.editOverview.game_count}个wiki的建设,累计编辑${allData.editOverview.total_edit_count}次。

\n\n` if(allData.editOverview.total_edit_count < 100){ wikitext += `Wiki需要所有人参与,感谢你的加入!\n\n

` }else if (allData.editOverview.total_edit_count < 1000){ wikitext += `你是BWiki的中坚力量。\n\n

` }else if(allData.editOverview.total_edit_count < 5000){ wikitext += `你是BWiki的核心编辑。\n\n

` }else if(allData.editOverview.total_edit_count < 10000){ wikitext += `你是BWiki的大佬,所有读者都受益于你的编辑。\n\n

` }else{ wikitext += `你是BWiki的传奇!\n\n

` }

wikitext += `

编辑统计


`

     var year_edit = 0;
     var autoconfirmed = 0;
     var sysop = 0;
     var interfaceadmin = 0;
     var bureaucrat = 0;
     var editcss = 0;
     var editjs = 0;
     var editmodule = 0;
     var editwidget = 0;
     var editmw = 0;
     var edit_wiki_year = 0
     for (const wiki of allData.wiki) {
       year_edit += wiki.stat.editcount_year;
       if (wiki.stat.editcount_year > 0) {
         edit_wiki_year+=1;
       }
       autoconfirmed += wiki.stat.groups.includes("autoconfirmed") ? 1 : 0;
       sysop += wiki.stat.groups.includes("sysop") ? 1 : 0;
       interfaceadmin += wiki.stat.groups.includes("interface-admin") ? 1 : 0;
       bureaucrat += wiki.stat.groups.includes("bureaucrat") ? 1 : 0;
       editcss += wiki.stat.edit_type.css;
       editjs += wiki.stat.edit_type.js;
       editmodule += wiki.stat.eidt_ns[828];
       editwidget += wiki.stat.eidt_ns[274];
       editmw += wiki.stat.eidt_ns[8];
     }
     wikitext += `2024年度编辑次数:${year_edit}次。参与了 ${edit_wiki_year} 个wiki的编辑。\n\n

`
     if (bureaucrat > 0) {
       wikitext += `你是 ` + bureaucrat.toString() + ` 个wiki的行政员。`
       if (bureaucrat > 5) {
         wikitext += `对wiki的管理权限数量仅次于托奇(雾)。`
       }
       wikitext += `

\n\n` } if (sysop > 0) { wikitext += `你是 ` + sysop + ` 个wiki的管理员。` if (sysop > 5) { wikitext += `多个Wiki在你的管理下蓬勃发展。` } wikitext += `

\n\n` } if (interfaceadmin > 0) { wikitext += `你是 ` + interfaceadmin.toString() + ` 个wiki的界面管理员。` if (interfaceadmin > 5) { wikitext += `BWiki需要更多的前端!` } wikitext += `

\n\n` } if (autoconfirmed > 0) { wikitext += `你拥有 ` + autoconfirmed.toString() + `个wiki的免审权限。` if (autoconfirmed > 5) { wikitext += `你在各个Wiki的编辑质量被社区普遍认可。` } wikitext += `

\n\n` }
     wikitext += `
累计编辑不包括被删除的页面等情况,因此数量可能低于年度编辑(按照所有编辑列表计数)。
` // wikitext += `{| class="wikitable sortable" \n|-\n!首次访问!!wiki!!累计编辑!!年度编辑!!页面!!MediaWiki空间!!模板!!帮助!!Widget!!模块\n\n`

wikitext += '

<thead>\n' + '</thead><tbody>'
     allData.wiki.sort((a, b) => b.stat.editcount_year - a.stat.editcount_year);
     for (const wiki of allData.wiki) {
       if (wiki.stat.editcount < 1) {
         continue
       }
wikitext += ``
       // wikitext += `|-\n|${wiki.stat.registration.toString().split("T")[0]}||${wiki.name}||${wiki.stat.editcount}||${wiki.stat.editcount_year}||${wiki.stat.eidt_ns[0]}||${wiki.stat.eidt_ns[8]}||${wiki.stat.eidt_ns[10]}||${wiki.stat.eidt_ns[12]}||${wiki.stat.eidt_ns[274]}||${wiki.stat.eidt_ns[828]}\n\n`
     }
     // wikitext += `|}\n`
wikitext += `
首次访问wiki累计编辑年度编辑页面MediaWiki空间模板帮助Widget模块
${wiki.stat.registration.toString().split("T")[0]}${wiki.name}${wiki.stat.editcount}${wiki.stat.editcount_year}${wiki.stat.eidt_ns[0]}${wiki.stat.eidt_ns[8]}${wiki.stat.eidt_ns[10]}${wiki.stat.eidt_ns[12]}${wiki.stat.eidt_ns[274]}${wiki.stat.eidt_ns[828]}

\n\n`

     // datawiki
     let data_wiki = allData.wiki.find(obj => obj.path === "data");
     if (data_wiki) {
       wikitext += `(你今年在 BWIKI数据库 编辑了 ${data_wiki.stat.editcount_year} 次。这些编辑是由社区插件自动执行的,为了更新缓存数据。)\n\n`
     }
     wikitext += `\n\n`


     // // wikitext to html
     // var params = {
     //     action: 'parse',
     //     contentmodel: 'wikitext',
     //     text: wikitext,
     //     prop: 'text|headhtml',
     //     format: 'json'
     //   };
     // var api = new mw.Api();
     // var res = await api.get(params);
     // console.log("wikitext2html",res)
     // if (res.parse && res.parse.text) {
     //   innerHTML = res.parse.text["*"];
     // }
     innerHTML = wikitext;
     container.innerHTML = innerHTML; // 插入渲染结果
     HTMLLogger.clear();
     document.querySelector('#year-backup-inputs').innerHTML = ""; // 清空手动输入框
     try{
       $(".sortable").tablesorter();
       (window.RLQ = window.RLQ || []).push(['jquery','jquery.tablesorter',()=>{$(function() {
         $(".sortable").tablesorter();
       });}]);
     } catch (e) {/* nothing can do*/}
   }
 };
 // 工具模块
 var Utils = {
   // 缓存数据到 localStorage
   setCache: async function (key, data) {
     try {
       var db = new BYearCacheDB();
       await db.init();
       await db.setCache(key, data);
     } catch (e) {
       console.error('设置缓存失败:', e);
     }
   },
   // 获取缓存数据
   getCache: async function (key) {
     try {
       var db = new BYearCacheDB();
       await db.init();
       var cache = await db.getCache(key);
       if (cache) {
         return cache;
       }
       return null;
     } catch (e) {
       console.error('获取缓存失败:', e);
       return null;
     }
   },
   getUserID: function () {
     var username = (document.cookie.match(/DedeUserID=([^;]+)/) || [])[1] || "";
     if (!username) {
       console.warn('Cookie中未找到用户名');
       return ;
     }
     return username;
   },
   // 文档就绪回调
   onDocumentReady: function (callback) {
     if (document.readyState === 'complete' || document.readyState === 'interactive') {
       callback();
     } else {
       document.addEventListener('DOMContentLoaded', callback);
     }
   },


   // 检查缓存是否过期
   isCacheExpired: function (timestamp, duration) {
     var now = new Date().getTime();
     console.log("isCacheExpired", timestamp, now, duration, (now - timestamp) > duration)
     if (1735628493429 > timestamp){
       return true;
     }
     return (now - timestamp) > duration;
   },
   // 等待 mw.Api 加载完成
   onApiReady: function (callback) {
     var maxRetry = 32; // 最大重试次数
     var retryTimeout = 81; // 每次重试间隔时间 81ms * 32 ≈ 2600ms
     var retryCount = 0;
     function waitApi() {
       if (window.mw && typeof mw.Api === 'function') {
         setTimeout(callback, retryTimeout); // 81ms 后才开始执行, 以确保相关功能已加载且可用
       } else {
         retryCount++;
         if (retryCount < maxRetry) {
           setTimeout(waitApi, retryTimeout); // 等待 retryTimeout 毫秒
         } else {
           var errorLogText = "等待mw.Api加载超时。已经等待 " + maxRetry.toString() + " 次";
           errorLogText += "累计时间:" + (maxRetry * retryTimeout).toString() + " 毫秒。";
           console.error(errorLogText);
           // 触发错误处理
           ErrorHandler.handleError(new Error(errorLogText));
         }
       }
     }
     waitApi();
   }
 };


 // 将命名空间暴露到全局(可选)
 window.EditAchievement = EditYearCount;


 // 启动初始化
 EditYearCount.init();


})(this); } catch (e) {

 var log_div = (() => {
   var newDiv = document.createElement('div');
   newDiv.style.clear = 'both';
   document.querySelector('body').appendChild(newDiv);
   return newDiv;
 })();
 log_div.innerHTML = "报错信息:" + e.toString();

} </script>