// ==UserScript== // @name acfun统计 // @description acfun统计(目前只包含送出礼物和收到礼物的统计) // @namespace syachiku // @author syachiku // @match https://www.acfun.cn/member* // @run-at document-end // @grant GM_addStyle // @grant GM_xmlhttpRequest // @version 0.0.1 // @require https://cdnjs.cloudflare.com/ajax/libs/qs/6.9.4/qs.min.js // @require https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.20/lodash.min.js // @require https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.29.1/moment.min.js // @require https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.12/vue.min.js // @require https://cdnjs.cloudflare.com/ajax/libs/element-ui/2.14.1/index.min.js // @downloadURL none // ==/UserScript== ;(async function(){ Vue.use(ELEMENT); Vue.prototype.$message = ELEMENT.Message; const config = { ACFUN_SERVER : 'https://www.acfun.cn', ACFUN_MOBILE_SERVER : 'https://m.acfun.cn', ACFUNLIVE_SERVER : 'https://live.acfun.cn', URLS : { ACFUN_USER : { INFO : '/rest/pc-direct/user/userInfo', SPACE : '/u' }, WALLET : { SEND_GIFT : '/rest/apph5-direct/pay/reward/giveRecords', RECEIVE_GIFT : '/rest/apph5-direct/pay/reward/receiveRecords', } }, }; // 是否为空 function isNullOrEmpty(val){ return _.isUndefined(val) || _.isNull(val) || _.isNaN(val) || (((_.isObject(val) && !_.isDate(val)) || _.isArray(val) || _.isString(val)) && _.isEmpty(val)) } // 通用请求 function commonRequrest(url, method, form, raw, callback, headers){ var isSuccess = false; var data = null; if(!headers){ headers = {}; } if(!raw){ if(method == 'post'){ headers['Content-Type'] = 'application/x-www-form-urlencoded'; if(form){ form = Qs.stringify(form); } } } if(method == 'get' && form){ form = Qs.stringify(form); url += '?' + form; } // 获取了token if(config.TOKEN){ headers['Authorization'] = `Token ${config.TOKEN}`; } GM_xmlhttpRequest({ synchronous : !_.isFunction(callback), method : method, url : url, data : form, headers : headers, onload : function(res){ // 200 if(res.status==200){ if(raw){ callback(true, res.responseText); } else{ res = JSON.parse(res.responseText); isSuccess = res[config.RESPONSE.FIELD.STATUS] == config.RESPONSE.STATUS.SUCCESS; data = res[config.RESPONSE.FIELD.DATA]; if(_.isFunction(callback)){ callback(isSuccess, data); } } } else{ if(_.isFunction(callback)){ callback(isSuccess, data); } } }, onerror : function(){ if(_.isFunction(callback)){ callback(isSuccess, data); } }, onabort : function(){ if(_.isFunction(callback)){ callback(isSuccess, data); } }, }); return [isSuccess, data]; } function addCssResource(url){ commonRequrest(url, 'get', null, true, function(isSuccess, css){ if(isSuccess){ GM_addStyle(css); } }) } // 添加element-ui样式 addCssResource('https://cdnjs.cloudflare.com/ajax/libs/element-ui/2.14.1/theme-chalk/index.min.css'); // 加载Vue实例 function loadVue(){ var vue = null; // 容器 var navEle = document.querySelector('#list-guide-left'); // 添加导航选项 var navItem = document.createElement('div'); navItem.classList.add('part-guide-left'); navItem.innerHTML = ` `; // 监听点击事件 navItem.addEventListener('click', function(){ // 修改展示内容 document.querySelector('#area-main-right').innerHTML = `

统计

AcFunStat
正在获取送出礼物记录。已获取{{totalSendGiftList.length}}条记录
正在获取收到礼物记录。已获取{{totalReceiveGiftList.length}}条记录
提交
`; history.replaceState(null, null, '/member/#area=acfunstat'); // 已初始化vue实例 if(vue){ return; } // 初始化实例 new Vue({ el : '#acfunstat-container', data : function(){ return { totalSend : 0, totalReceive : 0, send : 0, receive : 0, totalSendGiftList : [], totalReceiveGiftList : [], sendGiftList : [], receiveGiftList : [], sendUserList : [], receiveUserList : [], userInfo : {}, isGettingSendGiftList : false, isGettingReceiveGiftList : false, hasGetReceiveGiftList : false, hasGetSendGiftList : false, filterText : '', settingFormDialogVisible : false, settingFormData : { isContainPeach : true, }, settingFormRules : { }, settingFormPickerOptions : { shortcuts : [{ text : '本周', onClick(picker) { const end = moment().endOf('week').add(1, 'days')._d; const start = moment().startOf('week').add(1, 'days')._d; picker.$emit('pick', [start, end]); }, },{ text : '上周', onClick(picker) { const end = moment().subtract(1, 'weeks').endOf('week').add(1, 'days')._d; const start = moment().subtract(1, 'weeks').startOf('week').add(1, 'days')._d; picker.$emit('pick', [start, end]); }, },{ text : '本月', onClick(picker) { const end = moment().endOf('month')._d; const start = moment().startOf('month')._d; picker.$emit('pick', [start, end]); }, },{ text : '上月', onClick(picker) { const end = moment().subtract(1, 'months').endOf('month')._d; const start = moment().subtract(1, 'months').startOf('month')._d; picker.$emit('pick', [start, end]); }, },] }, switchToSendGiftList : true, }; }, methods : { getUserInfo : function(uid, callback){ var vue = this; if(uid in this.userInfo){ if(_.isFunction(callback)){ callback(true, this.userInfo[uid]); } return; } // 获取用户信息 commonRequrest(config.ACFUNLIVE_SERVER + config.URLS.ACFUN_USER.INFO + `?userId=${uid}`, 'get', null, true, function(isSuccess, data){ var userInfo = null; if(data){ data = JSON.parse(data); if(data.result == 0){ userInfo = { uid : uid, photo : data.profile.headUrl, userName : data.profile.name, }; } } vue.userInfo[uid] = userInfo; if(_.isFunction(callback)){ callback(isSuccess, userInfo); } }); }, getSendGiftList : function(callback){ // 已经获取过礼物列表 if(this.hasGetSendGiftList){ if(_.isFunction(callback)){ callback(); } return; } var vue = this; // 获取数据时的游标 var pcursor = "0"; var getSendGiftList = function(){ vue.isGettingSendGiftList = true; if(pcursor == 'no_more'){ // 统计送出礼物总ac币 vue.totalSend = _.sumBy(_.flatMap(vue.userInfo), 'totalSend'); vue.send = vue.totalSend; // 讲用户按送出ac币价值倒序 vue.sendUserList = _.sortBy(_.filter(vue.userInfo, (userInfo)=>{return userInfo.totalSend>0;}), function(userInfo){ return -userInfo.totalSend; }); vue.sendGiftList = vue.totalSendGiftList; vue.isGettingSendGiftList = false; vue.hasGetSendGiftList = true; if(_.isFunction(callback)){ callback(); } return; } else{ commonRequrest(config.ACFUN_MOBILE_SERVER + config.URLS.WALLET.SEND_GIFT, 'get',{pcursor : pcursor,},true, function(isSuccess, data){ // 获取数据失败 if(!isSuccess){ vue.$message({ type : 'error', message : '获取送出礼物列表失败', }); pcursor = 'no_more'; return; } data = JSON.parse(data); pcursor = data['pcursor']; if(data.records){ var uids = {}; // 获取用户信息 data.records.forEach(function(record){ if(!(record.userId in uids)){ uids[record.userId] = []; } // 设置送出时间 record.createTimeText = moment(record.createTime).format('YYYY-MM-DD hh:mm:ss'); record.giftText = `${record.giftCount}个${record.giftName}`; uids[record.userId].push(record); vue.totalSendGiftList.push(record); }); var nextGetSendGiftList = _.after(Object.keys(uids).length, getSendGiftList); for(var uid in uids){ vue.getUserInfo(uid, function(isSuccess, userInfo){ // 无法获取用户信息 if(!isSuccess || userInfo==null){ return; } // 统计送出给用户AC币 if(userInfo.totalSendGiftList == null){ userInfo.totalSendGiftList = []; userInfo.sendGiftList = []; } if(userInfo.totalSend==null){ userInfo.totalSend = 0; userInfo.send = 0; } userInfo.totalSendGiftList.splice(userInfo.totalSendGiftList.length, 0, ...uids[userInfo.uid]); userInfo.sendGiftList.splice(userInfo.sendGiftList.length, 0, ...uids[userInfo.uid]); userInfo.totalSend += _.sumBy(uids[userInfo.uid], 'acoin'); userInfo.send = userInfo.totalSend; nextGetSendGiftList(); }); } } else{ getSendGiftList(); } }, ); } } getSendGiftList(); }, getReceiveGiftList : function(callback){ // 已经获取过礼物列表 if(this.hasGetReceiveGiftList){ if(_.isFunction(callback)){ callback(); } return; } var vue = this; // 获取数据时的游标 var pcursor = "0"; var getReceiveGiftList = function(){ vue.isGettingReceiveGiftList = true; if(pcursor == 'no_more'){ // 统计送出礼物总ac币 vue.totalReceive = _.sumBy(_.flatMap(vue.userInfo), 'totalReceive'); vue.receive = vue.totalReceive; // 讲用户按送出ac币价值倒序 vue.receiveUserList = _.sortBy(_.filter(vue.userInfo, (userInfo)=>{return userInfo.totalReceive>0;}), function(userInfo){ return -userInfo.totalReceive; }); vue.receiveGiftList = vue.totalReceiveGiftList; vue.isGettingReceiveGiftList = false; vue.hasGetReceiveGiftList = true; if(_.isFunction(callback)){ callback(); } return; } else{ commonRequrest(config.ACFUN_MOBILE_SERVER + config.URLS.WALLET.RECEIVE_GIFT, 'get',{pcursor : pcursor,},true, function(isSuccess, data){ // 获取数据失败 if(!isSuccess){ vue.$message({ type : 'error', message : '获取送出礼物列表失败', }); pcursor = 'no_more'; return; } data = JSON.parse(data); pcursor = data['pcursor']; if(data.records){ var uids = {}; // 获取用户信息 data.records.forEach(function(record){ if(!(record.userId in uids)){ uids[record.userId] = []; } record.createTimeText = moment(record.createTime).format('YYYY-MM-DD hh:mm:ss'); record.giftText = `${record.giftCount}个${record.giftName}`; uids[record.userId].push(record); vue.totalReceiveGiftList.push(record); }); var nextGetReceiveGiftList = _.after(Object.keys(uids).length, getReceiveGiftList); for(var uid in uids){ vue.getUserInfo(uid, function(isSuccess, userInfo){ // 无法获取用户信息 if(!isSuccess || userInfo==null){ return; } // 统计送出给用户AC币 if(userInfo.totalReceiveGiftList == null){ userInfo.totalReceiveGiftList = []; userInfo.receiveGiftList = []; } if(userInfo.totalReceive==null){ userInfo.totalReceive = 0; userInfo.receive = 0; } userInfo.totalReceiveGiftList.splice(userInfo.totalReceiveGiftList.length, 0, ...uids[userInfo.uid]); userInfo.receiveGiftList.splice(userInfo.receiveGiftList.length, 0, ...uids[userInfo.uid]); userInfo.totalReceive += _.sumBy(uids[userInfo.uid], 'azuanAmount'); userInfo.receive = userInfo.totalReceive; nextGetReceiveGiftList(); }); } } else{ getReceiveGiftList(); } }, ); } } getReceiveGiftList(); }, // 打开设置弹窗 openSettingDialog : function(){ this.settingFormDialogVisible = true; }, // 更改设置提交 handleSettingFormSubmit : function(){ var vue = this; this.$refs.settingForm.validate((valid) => { // 通过校验 if(valid){ var startDate = null,endDate=null; // 筛选了时间范围 if(this.settingFormData.dateRegion && this.settingFormData.dateRegion.length==2){ startDate = this.settingFormData.dateRegion[0].getTime(); endDate = this.settingFormData.dateRegion[1].getTime(); } this.sendGiftList = _.filter(this.totalSendGiftList, function(record){ var flag = null; // 指定了起始时间 if(startDate && record.createTimeendDate){ flag = false; } // 不包含桃子 else if(!vue.settingFormData.isContainPeach && record.giftName == '桃子'){ flag = false; } else{ flag = true; } record.flag = flag; return flag; }); this.receiveGiftList = _.filter(this.totalReceiveGiftList, function(record){ var flag = null; // 指定了起始时间 if(startDate && record.createTimeendDate){ flag = false; } // 不包含桃子 else if(!vue.settingFormData.isContainPeach && record.giftName == '桃子'){ flag = false; } else{ flag = true; } record.flag = flag; return flag; }); // 重新计算收到礼物和送出礼物 for(uid in this.userInfo){ this.userInfo[uid].send = 0; this.userInfo[uid].receive = 0; this.userInfo[uid].sendGiftList = _.filter(this.userInfo[uid].totalSendGiftList, function(record){ if(record.flag){ vue.userInfo[uid].send += record.acoin; } return record.flag; }); this.userInfo[uid].receiveGiftList = _.filter(this.userInfo[uid].totalReceiveGiftList, function(record){ if(record.flag){ vue.userInfo[uid].receive += record.azuanAmount; } return record.flag; }); } // 重新排名 // 统计送出礼物总ac币 this.send = _.sumBy(_.flatMap(this.userInfo), 'send'); // 讲用户按送出ac币价值倒序 this.sendUserList = _.sortBy(_.filter(this.userInfo, (userInfo)=>{return userInfo.send>0;}), function(userInfo){ return -userInfo.send; }); // 统计收到礼物总ac币 this.receive = _.sumBy(_.flatMap(this.userInfo), 'receive'); // 讲用户按收到ac币价值倒序 this.receiveUserList = _.sortBy(_.filter(this.userInfo, (userInfo)=>{return userInfo.receive>0;}), function(userInfo){ return -userInfo.receive; }); // 生成筛选文字 this.filterText = this.formatFilterText(); this.settingFormDialogVisible = false; } }); }, // 跳转至用户主页 toUserSpace : function(userInfo){ window.open(config.ACFUN_SERVER + config.URLS.ACFUN_USER.SPACE + `/${userInfo.uid}`); }, formatFilterText : function(){ var dateRegionText = null,isContainPeachText=null; // 筛选了时间范围 if(this.settingFormData.dateRegion && this.settingFormData.dateRegion.length==2){ dateRegionText = `${moment(this.settingFormData.dateRegion[0]).format('YYYY-MM-DD')} 至 ${moment(this.settingFormData.dateRegion[1]).format('YYYY-MM-DD')}`; } if(!this.settingFormData.isContainPeach){ isContainPeachText = '不包含桃子'; } var texts = _.filter([dateRegionText, isContainPeachText]).join('、'); if(isNullOrEmpty(texts)){ return ''; } else{ return '(' + texts + ')'; } }, }, computed : { }, mounted : function(){ var vue = this; this.getSendGiftList(function(){ vue.getReceiveGiftList(); }); }, }); }); navEle.append(navItem); return true; } window.onload = function(){ var intervalHandler = window.setInterval(function(){ if(loadVue()){ window.clearInterval(intervalHandler); } }, 500); }; })();