bilibiliGetExpV2

(The 2nd version) Automatically finish DAU task at bilibili to get experience point.

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。

您需要先安装用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         bilibiliGetExpV2
// @namespace    https://iconquestion.github.io
// @version      2.22
// @description  (The 2nd version) Automatically finish DAU task at bilibili to get experience point.
// @author       ICONQUESTION
// @match        https://t.bilibili.com/*
// @icon         https://bilibili.com/favicon.ico
// @grant        none
// @require      https://cdn.bootcss.com/blueimp-md5/2.10.0/js/md5.js
// ==/UserScript==

var urlList = {
    checkTasks: 'https://api.bilibili.com/x/member/web/exp/reward',
    watchVideo: 'https://api.bilibili.com/x/click-interface/web/heartbeat',
    shareVideo: 'https://api.biliapi.net/x/share/finish',
    dynamic: 'https://api.bilibili.com/x/polymer/web-dynamic/v1/feed/all?timezone_offset=-480&type=video&page=1',
    videoProperty: 'https://api.bilibili.com/x/player/pagelist',
    getAccess_key: 'https://passport.bilibili.com/login/app/third?appkey=1d8b6e7d45233436&api=http://link.acg.tv/forum.php&sign=5f9c0a5c2360c80b858d546a23a4a9dd',
}

//将document.cookie中的'; '替换为'&', 从而满足生成URL对象的条件, 再利用URL对象的searchParam功能完成cookie检索
var cookies = new URL('http://hello.world/test?' + document.cookie.replaceAll('; ', '&'))
var shareVideoDone = cookies.searchParams.get('shareVideoDone'), watchVideoDone = cookies.searchParams.get('watchVideoDone')
var csrftoken = cookies.searchParams.get('bili_jct')
var mid = cookies.searchParams.get('DedeUserID')
var access_key = cookies.searchParams.get('access_key')

//date用来设置记录性cookies过期时间为第二天0:0:0
var date = new Date()
var currentTime = parseInt((date.getTime()) / 1000);
date.setTime(date.getTime() + 3600 * 24 * 1000)
date.setHours(0, 0, 0, 0)

//调试性开关
/*
var debugMode = {
    'true': '无论返回数据显示任务是否完成, 都再执行一次',
    'false': '根据返回数据情况, 有选择地执行(默认值)'
}
*/
var debugMode = localStorage && localStorage.getItem('debugMode') != undefined ? localStorage.getItem('debugMode') : false

/*
var cookieRecordComesFirst={
    'true':'以cookies中的任务完成记录为准(默认值)',
    'false':'以fetch请求返回的数据为准'
}
*/
var cookieRecordComesFirst = localStorage && localStorage.getItem('cookieRecordComesFirst') != undefined ? localStorage.getItem('cookieRecordComesFirst') : true



//从这里开始执行
window.onload = async function () {
    //1.检查登录态
    if (!csrftoken || !mid) {
        console.log('csrf或mid不存在, 请登录。如果您已经登录, 请尝试清空cookies后重新登录。')
        return;
    }

    //2.检查可完成的任务, 存储到taskList
    if (debugMode) console.log('debug模式已开启')

    if (cookieRecordComesFirst) {
        console.log('当前模式:cookies记录优先')
        var taskList = { 'share': shareVideoDone, 'watch': watchVideoDone }
    } else {
        console.log('当前模式:fetch请求优先')
        var taskList = await checkTasks()
    }
    console.log(taskList)

    if (!debugMode && taskList.share && taskList.watch) {
        console.log('所有任务已经完成!')
        return
    }

    //3.从动态列表拉取视频
    var videoProp = await grabVideo()
    //console.log(videoProp)

    //4.1 完成观看视频任务
    if (!taskList.watch || debugMode) {
        setTimeout(function(){
            watchVideo(videoProp)
        }, Math.random() * 100000)

    } else {
        console.log('观看视频任务已经完成!')
    }

    //4.2 完成分享视频任务
    if (!taskList.share || debugMode) {
        if (!access_key || access_key == 'null') {
            //4.2.1 获取access_key
            getAccessKey()
        } else {
            //4.2.1 分享视频
            setTimeout(function(){
                shareVideo(videoProp)
            }, Math.random() * 100000)
        }

    } else {
        console.log('分享视频任务已经完成!')
    }
}


//检查可获得经验值的任务
async function checkTasks() {
    console.log('正在检查任务列表')

    var data = await fetch(urlList.checkTasks, {
        credentials: 'include',
        headers: {
            'Accept': 'application/json, text/plain, */*',
            'Accept-Encoding': 'gzip, deflate',
            'Accept-Language': 'zh-CN,zh;q=0.9',
        },
    }).then(function (res) {
        return res.headers.get('Content-Type').search('application/json') != -1 ? res.json() : undefined
    }).then(function (data) {
        //console.log(data)
        if (!data || !data.data) {
            throw 'fetch(urlList.checkTasks) 返回数据异常。'
        } else {
            //传回最内层data(对象)
            return data.data
        }
    })

    return data
}


//抓取视频
async function grabVideo() {
    console.log('正在抓取视频')

    //获取aid,bvid
    var videoProp = []

    await fetch(urlList.dynamic, {
        credentials: 'include',
    }).then(function (res) {
        return res.headers.get('Content-Type').search('application/json') != -1 ? res.json() : undefined
    }).then(function (data) {
        //console.log(data)
        if (!data || !data.data || !data.data.items || !data.data.items[0].basic.comment_id_str || !data.data.items[0].modules.module_dynamic.major.archive.bvid) {
            throw ('fetch(urlList.dynamic) 返回数据类型异常, 或返回列表为空, 或相应数据不存在')
        }

        videoProp[0] = data.data.items[0].basic.comment_id_str
        videoProp[1] = data.data.items[0].modules.module_dynamic.major.archive.bvid
    })

    //获取cid
    await fetch(urlList.videoProperty + '?bvid=' + videoProp[1] + '&jsonp=jsonp', {
        method: 'GET',
        credentials: 'include',
        headers: {
            'Accept': '*/*',
            'Content-Type': 'application/x-www-form-urlencoded',
            'Accept-Encoding': 'gzip, deflate',
            'Accept-Language': 'zh-CN,zh;q=0.9',
        },
    }).then(function (res) {
        return res.headers.get('Content-Type').search('application/json') != -1 ? res.json() : undefined
    }).then(function (data) {
        //console.log(data)
        if (!data || !data.data || !data.data[0].cid) {
            throw 'fetch(urlList.videoProperty) 返回数据类型异常, 或相应数据不存在'
        }
        videoProp[2] = data.data[0].cid
    })

    console.log('获取到以下视频数据: aid=' + videoProp[0] + ', bvid=' + videoProp[1] + ', cid=' + videoProp[2])
    return videoProp
}

//获取access_key
async function getAccessKey() {
    console.log('access_key不存在, 正在获取新access_key');

    await fetch(urlList.getAccess_key, {
        credentials: 'include',
    }).then(function (res) {
        return res.headers.get('Content-Type').search('application/json') != -1 ? res.json() : undefined
    }).then(function (data) {
        //console.log(data)
        if (!data || !data.data || !data.data.confirm_uri) {
            throw 'fetch(urlList.getAccess_key) 返回数据异常'
        }

        console.log('请右键以下链接, 点击"在新标签页中打开", 然后复制查询字符串中的access_key字段, 粘贴到对话框中')
        console.log(data.data.confirm_uri)

        var input1 = prompt('请打开浏览器控制台, 右键最下方的链接, 点击"在新标签页中打开", 然后复制查询字符串中的access_key字段, 粘贴到这里');

        if (input1) {
            document.cookie = 'access_key=' + input1 + '; max-age=15552000; domain=.bilibili.com';
            console.log('access_key已存储. 请刷新此页面以重新运行该脚本. 若需再次更改access_key, 您可手动访问浏览器的Cookies存储设置.')
        } else {
            console.log('用户已取消操作。')
        }
    })
}

//这个...不用解释了吧
function shareVideo(videoProp) {
    if (!access_key) {
        console.log('缺少access_key, 无法分享视频')
        return;
    }

    var aid = videoProp[0], bvid = videoProp[1], cid = videoProp[2]

    //share_session_id生成方式和作用未知, 欢迎补充!
    var body = 'access_key=' + access_key + '&appkey=1d8b6e7d45233436&build=6800300&c_locale=zh_CN&channel=bili&disable_rcmd=0&from_spmid=dt.dt.video.0&mobi_app=android&oid=' + aid + '&panel_type=1&platform=android&s_locale=zh_CN&share_channel=biliDynamic&share_id=main.ugc-video-detail.0.0.pv&share_origin=vinfo_share&share_session_id=' + '6609bb15-ac05-4118-8f12-cb' + currentTime + '&sid=' + cid + '&spm_id=main.ugc-video-detail.0.0&statistics=%7B%22appId%22%3A1%2C%22platform%22%3A3%2C%22version%22%3A%226.80.0%22%2C%22abtest%22%3A%22%22%7D&success=true&ts=' + currentTime + '&sign='
    body = body + md5(body + '560c52ccd288fed045859ed18bffd973')

    console.log('正在分享视频, aid=' + aid)

    fetch(urlList.shareVideo, {
        method: 'post',
        mode: 'cors',
        //referrer: "no-referrer",
        headers: {
            //'Buvid': 'XXAF685A25ED66209F45C4248C26054E197A8',
            //'Fp_local': '9ca222f943ae8680669b6cdf2da959e120220715131357006bc66326e8302881',
            //'Fp_remote': '9ca222f943ae8680669b6cdf2da959e1202207131056235c836389e254a11b4e',
            //'Session_id': '831ec2b1',//暂时不知道如何处理
            //'Env': 'prod',
            //'App-Key': 'android',
            //'User-Agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 13_2_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.3 Mobile/15E148 Safari/604.1 Edg/109.0.0.0',
            //'X-Bili-Trace-Id': '390743e355a59747842729ca1962d222:8427c9ca1962d222:0:0',//暂时不知道如何处理
            //'X-Bili-Aurora-Eid': 'UlYITlUAD1ID',
            //'X-Bili-Mid': mid,
            //'X-Bili-Aurora-Zone': '',
            'Content-Type': 'application/x-www-form-urlencoded; charset=utf-8',
            //'Accept-Encoding': 'gzip',
        },
        body: body,
    }).then(function (res) {
        return res.headers.get('Content-Type').search('application/json') != -1 ? res.json() : undefined
    }).then(function (data) {
        //console.log(data)
        if (!data) {
            console.log('fetch(urlList.shareVideo) 返回数据异常')
            return
        }

        //每天第一次分享, 返回的toast不为空, 之后的分享toast为空
        console.log(data.data.toast ? data.data.toast : '分享视频任务成功完成')
        document.cookie = 'shareVideoDone=true; expires=' + date.toGMTString()
    })
}


//观看视频
function watchVideo(videoProp) {
    var aid = videoProp[0], bvid = videoProp[1], cid = videoProp[2]
    console.log('正在观看视频, aid=' + aid + ', bvid=' + bvid + ', cid=' + cid)
    fetch(urlList.watchVideo, {
        method: 'post',
        credentials: 'include',
        headers: {
            'Accept': 'application/json, text/javascript, */*; q=0.01',
            'Content-Type': 'application/x-www-form-urlencoded'
        },
        body:
            'aid=' + aid + '&cid=' + cid + '&bvid=' + bvid + '&mid=' + mid + '&csrf=' + csrftoken + '&played_time=11&real_played_time=12&realtime=11&start_ts=' + currentTime + '&type=3&dt=2&play_type=2&from_spmid=444.41.list.card_archive.click&spmid=333.788.0.0&auto_continued_play=0&refer_url=https%3A%2F%2Ft.bilibili.com%2F%3Ftab%3Dvideo&bsource='
    }).then(function (res) {
        return res.headers.get('Content-Type').search('application/json') != -1 ? res.json() : undefined
    }).then(function (data) {
        //console.log(data)
        if (!data) {
            console.log('fetch(urlList.watchVideo) 返回数据异常')
            return
        }

        //正常情况返回string'0', 否则返回具体信息
        console.log(data.message == '0' ? '观看视频任务成功完成' : data.message)
        document.cookie = 'watchVideoDone=true; expires=' + date.toGMTString()
    })
}