全站通知:
Widget:EditYear2024
刷
历
编
跳到导航
跳到搜索
<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>