您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
acfun统计(目前只包含送出礼物和收到礼物的统计)
当前为
// ==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.2 // @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 // ==/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 = ` <div class="banner"> <a href="javascript:void(0)" class="tab fixed"> <i class="icon icon-bar-chart"></i>统计 </a> </div> `; // 监听点击事件 navItem.addEventListener('click', function(){ // 修改展示内容 document.querySelector('#area-main-right').innerHTML = ` <style> @font-face{ font-family:element-icons; src:url('https://cdnjs.cloudflare.com/ajax/libs/element-ui/2.14.1/theme-chalk/fonts/element-icons.ttf'); } #block-first .item{ font-family: verdana,Tahoma,Arial,"微软雅黑","宋体",sans-serif; margin: 0 0 16px; width : 50%; } #block-first .item .l .icon { font-size: 24px; color: #ccc; margin: 12px 8px 0 0; } #block-first .item .a { font-size: 18px; font-weight: bold; letter-spacing: -.1em; line-height: 1.2; color: #333; } #block-first .item .a .pts { font-family: Candara,verdana,Tahoma,Arial,"微软雅黑","宋体",sans-serif; font-size: 32px; margin: 0 8px; } #block-first .item .b { font-size: 12px; font-style: italic; line-height: 1.2; color: #999; } #block-detail .item{ width : 50%; } #block-detail .item li { list-style:none; color: #3a9bd9 !important; } #block-detail .item li span { color: #666; font-weight: bold; } #block-detail .item.l{ border-right: 1px solid #ddd; } .el-table__body-wrapper.is-scrolling-none::-webkit-scrollbar{ width: 10px; height: 1px; } .el-table__body-wrapper.is-scrolling-none::-webkit-scrollbar-thumb { border-radius: 10px; -webkit-box-shadow: inset 0 0 5px rgba(0,0,0,0.2); background: #535353; } .el-table__body-wrapper.is-scrolling-none::-webkit-scrollbar-track { -webkit-box-shadow: inset 0 0 5px rgba(0,0,0,0.2); border-radius: 10px; background: #EDEDED; } #btn-toggle-info{ border-top: 1px dashed #999; text-align: center; color: #08c; height: 16px; line-height: 16px; } #btn-toggle-info span{ display: block; margin-top: -8px; cursor: pointer; background-color: #fff; width: 128px; } </style> <div id="block-title-banner"> <p>统计</p> <div> <a href="/member/">AcFun</a><span class="d">Stat</span> </div> <span class="clearfix"></span> </div> <div id="block-banner-right" class="block banner"> <a href="#area=acfunstat" data-type="views" class="tab"> <i class="icon"></i>送出/收到礼物 </a> </div> <div id="acfunstat-container" v-cloak> <div class="block" v-if="isGettingSendGiftList"> <div class="mainer"> <span>正在获取送出礼物记录。已获取{{sendGiftList.length}}条记录</span> </div> </div> <div class="block" v-if="isGettingReceiveGiftList"> <div class="mainer"> <span>正在获取收到礼物记录。已获取{{receiveGiftList.length}}条记录</span> </div> </div> <template v-if="hasGetSendGiftList && hasGetReceiveGiftList"> <div id="block-first" class="block"> <div class="mainer"> <div class="banner"> <p class="tab fixed">总体{{filterText}}</p> <p class="r"> <i class="icon icon-cog" style="color: #3a9bd9;font-size:24px;" @click="openSettingDialog"></i> </p> </div> <div class="item l"> <div class="l"> <i style="color: #f69;" class="icon icon-clock-o"></i> </div> <div class="l"> <div class="a"> 送出礼物价值<span id="pts-online-splash" style="color: #f69;" class="pts">{{send}}</span>AC币 </div> <div class="b"> 用户送出礼物价值的AC币总和 </div> </div> <span class="clearfix"></span> </div> <div class="item r"> <div class="l"> <i style="color: #f69;" class="icon icon-clock-o"></i> </div> <div class="l"> <div class="a"> 收到礼物价值<span id="pts-online-splash" style="color: #f69;" class="pts">{{receive}}</span>钻石 </div> <div class="b"> 用户收到礼物价值的钻石总和 </div> </div> <span class="clearfix"></span> </div> <span class="clearfix"></span> </div> </div> <div id="block-detail" class="block"> <div class="mainer"> <div class="item l"> <div class="banner"> <p class="tab fixed">送出礼物用户排行</p> </div> <div id="send-gift-user-table"> <el-table :data="sendUserList" style="width: 100%" class="user-table" height="400"> <el-table-column prop="userName" label="用户名" min-width="50%"> <template slot-scope="scope"> <el-avatar shape="circle" fit="fill" size="40" :src="scope.row.photo"></el-avatar> <el-link style="cursor:pointer;" @click="toUserSpace(scope.row)">{{scope.row.userName}}</el-link> </template> </el-table-column> <el-table-column prop="uid" label="用户uid" min-width="30%"> </el-table-column> <el-table-column prop="send" label="AC币" min-width="20%"> </el-table-column> </el-table> </div> <span class="clearfix"></span> </div> <div class="item r"> <div class="banner"> <p class="tab fixed">收到礼物用户排行</p> </div> <div id="receive-gift-user-table"> <el-table :data="receiveUserList" style="width: 100%" class="user-table" height="400"> <el-table-column prop="userName" label="用户名" min-width="50%"> <template slot-scope="scope"> <el-avatar shape="circle" fit="fill" size="40" :src="scope.row.photo"></el-avatar> <el-link style="cursor:pointer;" @click="toUserSpace(scope.row)">{{scope.row.userName}}</el-link> </template> </el-table-column> <el-table-column prop="uid" label="用户uid" min-width="30%"> </el-table-column> <el-table-column prop="receive" label="钻石" min-width="20%"> </el-table-column> </el-table> </div> <span class="clearfix"></span> </div> <span class="clearfix"></span> </div> </div> <p id="btn-toggle-info" @click="showGiftDetail=true" v-if="!showGiftDetail"><span><i class="icon icon-chevron-down"></i>点击查看礼物详情</span></p> <div class="block" v-if="showGiftDetail"> <div class="mainer"> <div class="banner"> <p class="tab fixed">{{switchToSendGiftList?'送出礼物详情':'收到礼物详情'}}</p> <div class="r"> <el-button size="mini" type="primary" @click="switchToSendGiftList = !switchToSendGiftList">{{switchToSendGiftList?'切换至收到礼物详情':'切换至送出礼物详情'}}</el-button> </div> </div> <div v-if="switchToSendGiftList"> <div id="send-gift-table"> <el-table :data="sendGiftList" style="width: 100%" class="user-table" height="400"> <el-table-column prop="userName" label="用户名" min-width="30%"> <template slot-scope="scope"> <el-avatar shape="circle" fit="fill" size="40" :src="userInfo[scope.row.userId].photo"></el-avatar> <el-link style="cursor:pointer;" @click="toUserSpace(userInfo[scope.row.userId])">{{userInfo[scope.row.userId].userName}}</el-link> </template> </el-table-column> <el-table-column prop="userId" label="用户uid" min-width="20%"> </el-table-column> <el-table-column prop="createTimeText" label="送出时间" min-width="20%"> </el-table-column> <el-table-column prop="giftText" label="送出礼物" min-width="20%"> </el-table-column> <el-table-column prop="acoin" label="AC币" min-width="10%"> </el-table-column> </el-table> </div> </div> <div v-else> <div id="receive-gift-table"> <el-table :data="receiveGiftList" style="width: 100%" class="user-table" height="400"> <el-table-column prop="userName" label="用户名" min-width="30%"> <template slot-scope="scope"> <el-avatar shape="circle" fit="fill" size="40" :src="userInfo[scope.row.userId].photo"></el-avatar> <el-link style="cursor:pointer;" @click="toUserSpace(userInfo[scope.row.userId])">{{userInfo[scope.row.userId].userName}}</el-link> </template> </el-table-column> <el-table-column prop="userId" label="用户uid" min-width="20%"> </el-table-column> <el-table-column prop="createTimeText" label="收到时间" min-width="20%"> </el-table-column> <el-table-column prop="giftText" label="收到礼物" min-width="20%"> </el-table-column> <el-table-column prop="azuanAmount" label="钻石" min-width="10%"> </el-table-column> </el-table> </div> </div> </div> </div> </template> <el-dialog title="设置" :visible.sync="settingFormDialogVisible" :modal="false" width="550px" custom-class="setting-form-dialog" :modal-append-to-body="false"> <el-form :model="settingFormData" class="setting-form" ref="settingForm" :rules="settingFormRules" @submit.native.prevent> <el-form-item label="礼物筛选" label-width="130px" prop="isContainPeach"> <el-switch v-model="settingFormData.isContainPeach" active-text="包括桃子" inactive-text="不包括桃子"> </el-switch> </el-form-item> <el-form-item label="时间范围" label-width="130px" prop="dateRegion"> <el-date-picker v-model="settingFormData.dateRegion" type="daterange" align="right" unlink-panels range-separator="至" start-placeholder="开始日期" end-placeholder="结束日期" :picker-options="settingFormPickerOptions" style="width:100%" > </el-date-picker> </el-form-item> <el-form-item style="display:flex;justify-content:center;"> <el-button type="primary" @click="handleSettingFormSubmit">提交</el-button> </el-form-item> </el-form> </el-dialog> </div> `; history.replaceState(null, null, '/member/#area=acfunstat'); // 已初始化vue实例 if(vue){ return; } // 初始化实例 new Vue({ el : '#acfunstat-container', data : function(){ return { send : 0, receive : 0, sendGiftList : [], receiveGiftList : [], showGiftDetail : false, 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, userName, 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, }; } } else{ userInfo = { uid : uid, userName : userName, photo : 'https://tx-free-imgs.acfun.cn/style/image/defaultAvatar.jpg', }; } vue.userInfo[uid] = userInfo; if(_.isFunction(callback)){ callback(isSuccess, userInfo); } }); }, getSendGiftList : function(callback){ var vue = this; var startDate = null,endDate=null; var isContainPeach = this.settingFormData.isContainPeach; // 筛选了时间范围 if(this.settingFormData.dateRegion && this.settingFormData.dateRegion.length==2){ startDate = this.settingFormData.dateRegion[0].getTime(); endDate = this.settingFormData.dateRegion[1].getTime(); } // 获取数据时的游标 var pcursor = "0"; var getSendGiftList = function(){ vue.isGettingSendGiftList = true; if(pcursor == 'no_more'){ // 统计送出礼物总ac币 vue.send = _.sumBy(_.flatMap(vue.userInfo), 'send'); // 讲用户按送出ac币价值倒序 vue.sendUserList = _.sortBy(_.filter(vue.userInfo, (userInfo)=>{return userInfo.send>0;}), function(userInfo){ return -userInfo.send; }); 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'; getSendGiftList(); } else{ data = JSON.parse(data); pcursor = data['pcursor']; if(data.records || data.records.length==0){ var uids = {}; var uidUserNameMapper = {}; // 获取用户信息 data.records.forEach(function(record){ // 指定了起始时间 if(startDate && record.createTime<startDate){ pcursor = "no_more"; return; } // 指定了终止时间 else if(endDate && record.createTime>endDate){ return; } // 不包含桃子 else if(!isContainPeach && record.giftName == '桃子'){ return; } if(!(record.userId in uids)){ uids[record.userId] = []; } uidUserNameMapper[record.userId] = record.userName; // 设置送出时间 record.createTimeText = moment(record.createTime).format('YYYY-MM-DD hh:mm:ss'); record.giftText = `${record.giftCount}个${record.giftName}`; uids[record.userId].push(record); vue.sendGiftList.push(record); }); // 没有任何记录 if(Object.keys(uids).length==0){ getSendGiftList(); } else{ var nextGetSendGiftList = _.after(Object.keys(uids).length, getSendGiftList); for(var uid in uids){ vue.getUserInfo(uid, uidUserNameMapper[uid], function(isSuccess, userInfo){ // 无法获取用户信息 if(!isSuccess || userInfo==null){ return; } // 统计送出给用户AC币 if(userInfo.sendGiftList == null){ userInfo.sendGiftList = []; } if(userInfo.send==null){ userInfo.send = 0; } userInfo.sendGiftList.splice(userInfo.sendGiftList.length, 0, ...uids[userInfo.uid]); userInfo.send += _.sumBy(uids[userInfo.uid], 'acoin'); nextGetSendGiftList(); }); } } } else{ getSendGiftList(); } } }, ); } } getSendGiftList(); }, getReceiveGiftList : function(callback){ var vue = this; var startDate = null,endDate=null; var isContainPeach = this.settingFormData.isContainPeach; // 筛选了时间范围 if(this.settingFormData.dateRegion && this.settingFormData.dateRegion.length==2){ startDate = this.settingFormData.dateRegion[0].getTime(); endDate = this.settingFormData.dateRegion[1].getTime(); } // 获取数据时的游标 var pcursor = "0"; var getReceiveGiftList = function(){ vue.isGettingReceiveGiftList = true; if(pcursor == 'no_more'){ // 统计送出礼物总ac币 vue.receive = _.sumBy(_.flatMap(vue.userInfo), 'receive'); // 讲用户按送出ac币价值倒序 vue.receiveUserList = _.sortBy(_.filter(vue.userInfo, (userInfo)=>{return userInfo.receive>0;}), function(userInfo){ return -userInfo.receive; }); 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'; getReceiveGiftList(); } else{ data = JSON.parse(data); pcursor = data['pcursor']; if(data.records || data.records.length==0){ var uids = {}; var uidUserNameMapper = {}; // 获取用户信息 data.records.forEach(function(record){ // 指定了起始时间 if(startDate && record.createTime<startDate){ pcursor = "no_more"; return; } // 指定了终止时间 else if(endDate && record.createTime>endDate){ return; } // 不包含桃子 else if(!isContainPeach && record.giftName == '桃子'){ return; } if(!(record.userId in uids)){ uids[record.userId] = []; } uidUserNameMapper[record.userId] = record.userName; // 设置送出时间 record.createTimeText = moment(record.createTime).format('YYYY-MM-DD hh:mm:ss'); record.giftText = `${record.giftCount}个${record.giftName}`; uids[record.userId].push(record); vue.receiveGiftList.push(record); }); // 没有任何记录 if(Object.keys(uids).length==0){ getReceiveGiftList(); } else{ var nextGetReceiveGiftList = _.after(Object.keys(uids).length, getReceiveGiftList); for(var uid in uids){ vue.getUserInfo(uid, uidUserNameMapper[uid], function(isSuccess, userInfo){ // 无法获取用户信息 if(!isSuccess || userInfo==null){ return; } // 统计送出给用户AC币 if(userInfo.receiveGiftList == null){ userInfo.receiveGiftList = []; } if(userInfo.receive==null){ userInfo.receive = 0; } userInfo.receiveGiftList.splice(userInfo.receiveGiftList.length, 0, ...uids[userInfo.uid]); userInfo.receive += _.sumBy(uids[userInfo.uid], 'azuanAmount'); nextGetReceiveGiftList(); }); } } } else{ getReceiveGiftList(); } } }, ); } } getReceiveGiftList(); }, // 打开设置弹窗 openSettingDialog : function(){ this.settingFormDialogVisible = true; }, // 更改设置提交 handleSettingFormSubmit : function(){ var vue = this; this.$refs.settingForm.validate((valid) => { // 通过校验 if(valid){ this.hasGetSendGiftList = false; this.hasGetReceiveGiftList = false; this.send = 0; this.receive = 0; this.sendGiftList = []; this.receiveGiftList = []; this.sendUserList = []; this.receiveUserList = []; for(var uid in this.userInfo){ this.userInfo[uid] = { uid : uid, userName : this.userInfo[uid].userName, photo : this.userInfo[uid].photo, }; } this.$message({ type : 'info', message : '开始获取数据', }); this.settingFormDialogVisible = false; this.getSendGiftList(function(){ vue.getReceiveGiftList(function(){ vue.$message({ type : 'success', message : '数据获取成功', }); // 生成筛选文字 vue.filterText = vue.formatFilterText(); }); }); } }); }, // 跳转至用户主页 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(){ // 打开弹窗 this.openSettingDialog(); }, }); }); navEle.append(navItem); return true; } window.onload = function(){ var intervalHandler = window.setInterval(function(){ if(loadVue()){ window.clearInterval(intervalHandler); } }, 500); }; })();
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址