您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
b站自动获取直播列表,并发送特殊的查询,再执行相应的操作
当前为
// ==UserScript== // @name 偷懒1号 // @namespace https://gitee.com/Liwker // @version 0.3 // @update 2024/5/21 // @description b站自动获取直播列表,并发送特殊的查询,再执行相应的操作 // @author Liwker 子木 // @match https://space.bilibili.com/* // @grant none // @license MIT // ==/UserScript== (async function () { 'use strict'; // 留存数据 const Data = { myUid: '', // 自己的uid csrf: '', isRun: false, // 是否开始执行 area_id: 321, // 直播区域,0 全部,321 原神 livePage: 1, // 查询区域列表 content: [], // 发送的消息 parent_area_id: 3, // 手游3 网游2 单机6 parent_id_List: [2, 3, 6], // 目前已有的大分区 wangyou_id_List: [329, 240], // 网游的id } // 工具函数 const Tools = { cookieToJson() { const cookieArr = document.cookie.split("; ") const obj = {} for(const i of cookieArr) { const arr = i.split('=') obj[arr[0]] = arr[1] } return obj }, // obj转url参数 toQuery(params, url) { const arr = [] for(const param in params) { arr.push(param + '=' + params[param]) } const query = arr.join('&') if(url) { return url + '?' + query } { return query } }, sleep(time) { return new Promise(resolve => setTimeout(resolve, time)) } } // 创建AJAX请求 /* option 参数 type: 'get' or 'post', url: '', header: {"Content-Type": "application/x-www-form-urlencoded"} or '', body: 'msg[sender_uid]=292297687&msg[receiver_id]=647193094' or null, */ function myAjax (option) { const xhr = new XMLHttpRequest() xhr.ontimeout = function () { alert("网络异常,请稍后重试!") } // 网络异常回调 xhr.onerror = function () { alert("您的网络似乎出了一些问题!") } // xhr.open('post', 'https://api.vc.bilibili.com/web_im/v1/web_im/send_msg?w_sender_uid=292297687&w_receiver_id=647193094&w_dev_id=23CC0EB2-8A23-4A59-B36F-047A60FA3F22&w_rid=6a507e3644f11685828e203925c28224&wts=1709401068', false) xhr.open(option.type, option.url, false) // false 是同步请求 if(option.header) { // 添加头 for(const head in option.header) { xhr.setRequestHeader(head, option.header[head]) } } // xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded") xhr.withCredentials = true // 设置为本页面的请求cookie // 发送请求 // xhr.send(`msg[sender_uid]=292297687&msg[receiver_id]=647193094&msg[receiver_type]=1&msg[msg_type]=1&msg[msg_status]=0&msg[content]={"content":"hello"}&msg[timestamp]=1709401067&msg[new_face_version]=0&msg[dev_id]=23CC0EB2-8A23-4A59-B36F-047A60FA3F22&from_firework=0&build=0&mobi_app=web&csrf_token=f50ebc4374670d2bc9442ee7ff56e434&csrf=f50ebc4374670d2bc9442ee7ff56e434`) xhr.send(option.body || null) // 同步请求的响应 if (xhr.readyState === 4) { if (xhr.status >= 200 && xhr.status < 300) { const res = xhr.response && JSON.parse(xhr.response) // console.log('xhr.response', res) return res } else { console.error('xhr.response', xhr.response) } } else { console.error('xhr', xhr) } } // 查询分区列表 /* option 参数 parent_area_id: 3, // 3 手游分区 area_id: 321, // 0全部,321原神 page: 1, */ function getList (option) { option.platform = 'web' const url = 'https://api.live.bilibili.com/xlive/web-interface/v1/second/getList' const ajaxOption = { type: 'get', url: Tools.toQuery(option, url), body: null, } const res = myAjax(ajaxOption) if(res && res.code == 0) { return res.data } else { console.error('获取直播列表失败', option, ajaxOption) } } // 查询主播 /* option 参数 search: xxxx, // 主播房间号 */ function searchAnchor (option) { option.search_type = 3 const url = 'https://api.live.bilibili.com/xlive/mcn-interface/v1/mcn_mng/SearchAnchor' const ajaxOption = { type: 'get', url: Tools.toQuery(option, url), body: null, } const res = myAjax(ajaxOption) if(res && res.code == 0) { return res.data } else { console.error('查询主播房间号失败', option, ajaxOption) } } // 发送消息 /* option 参数 w_receiver_id: xxxx, // 接受者uid content: '' // 消息内容 */ function sendMsg (option) { const query = { w_sender_uid: Data.myUid, // 发送者uid w_receiver_id: option.w_receiver_id, // 接受者uid w_dev_id: '00B09F06-90C5-4E87-B905-F8337F2B5612', w_rid: 'fcf0e090a8f54f944957ceb3c1b6316a', wts: new Date().getTime() } const body = { 'msg[sender_uid]': Data.myUid, // 发送者uid 'msg[receiver_id]': option.w_receiver_id, // 接受者uid 'msg[receiver_type]': 1, 'msg[msg_type]': 1, 'msg[msg_status]': 0, 'msg[content]': `{"content": "${option.content}"}`, // 消息内容 'msg[timestamp]': query.wts, 'msg[new_face_version]': 0, 'msg[dev_id]': 'EE21C3A5-2CBA-4B6B-79D9-3811024DD56C764335infoc', csrf: Data.csrf, csrf_token: Data.csrf, } const url = 'https://api.vc.bilibili.com/web_im/v1/web_im/send_msg' const ajaxOption = { type: 'post', url: Tools.toQuery(query, url), header: {"Content-Type": "application/x-www-form-urlencoded"}, body: Tools.toQuery(body), } const res = myAjax(ajaxOption) if((res && res.code == 0) || (res && res.message != '0')) { return res } else { console.error('发送消息失败', option, ajaxOption) } } // 创建消息El function addMsgEl (msg) { function addMsg(message, isLoading) { const liwkerMain = document.querySelector('div.liwkerMain') const msgEl = document.createElement('div') msgEl.classList.add('liwkerMsg') isLoading && msgEl.classList.add('liwkerLoading') msgEl.innerText = message msgEl.title = message liwkerMain.appendChild(msgEl) } // 移除已有的loading const liwkerLoading = document.querySelector('div.liwkerLoading') liwkerLoading && liwkerLoading.remove() if(msg) { addMsg(msg, false) // 添加加载消息 addMsg('正在查找新主播...', true) } else { if(Data.isRun) { addMsg('正在查找新主播...', true) } else { addMsg('——————执行结束——————', true) } } } // 主函数 async function main () { /* 步骤 1. 查询分区主播列表 2. 依次查询是否是新主播 3. 给新主播发送消息 4. 循环查询分区主播列表 */ let msgCount = 0 // 发送消息的人数 addMsgEl() // 开始加载信息 while(Data.isRun) { // 获取直播列表 const liveList = getList({ parent_area_id: Data.parent_area_id, // 3 手游分区 area_id: Data.area_id, // 0全部,321原神 page: Data.livePage, }) if(!liveList) { Data.livePage = 1 Data.isRun = false window.alert('获取直播列表失败') break } else if(liveList.list && liveList.list.length == 0) { // 结束 Data.livePage = 1 Data.isRun = false window.alert(`该分区已查询完毕!`) // 发送结束El break } else { Data.livePage++ } // console.log(liveList) // 遍历直播查询 for(const live of liveList.list) { if(!Data.isRun) break // console.log(live.roomid, live.title, live.uid) // 测试 const liverInfo = searchAnchor({ search: live.roomid }) if(!liverInfo) { window.alert('查询房间号失败') break } if(liverInfo.items[0].is_new_anchor !== 1) { // 不是新主播 // 休息,查询频率 2s await Tools.sleep(2000) // await Tools.sleep(500) // test continue } else { // 发送消息 const res = sendMsg({ w_receiver_id: live.uid, // 接受者uid // w_receiver_id: 647193094, // 测试 content: Array.isArray(Data.content) ? Data.content[Math.floor(Math.random() * Data.content.length)] || '' : ''// 消息内容 }) msgCount++ if(!res || res.message != 0) { addMsgEl(`${msgCount}. "${live.uname}"(${live.uid}) 发送消息失败!`) res.message && addMsgEl(`"${live.uname}":${res.message}`) } else { // 发送消息成功 addMsgEl(`${msgCount}. "${live.uname}"(${live.uid}) 发送消息成功~`) // console.log('发送消息成功', live.uid, live.uname) } // 休息,发消息频率30s await Tools.sleep(30000) // await Tools.sleep(1000) // test } } if(!Data.isRun) break // 休息 await Tools.sleep(5000) // if(Data.livePage === 3) { Data.isRun = false } // 测试 } Data.isRun = false // 重置按钮 const liBtnStart = document.querySelector('#liBtnStart') const liBtnEnd = document.querySelector('#liBtnEnd') liBtnStart.disabled = false liBtnStart.classList.remove('is-disabled') liBtnEnd.disabled = true liBtnEnd.classList.add('is-disabled') addMsgEl() // 添加结束消息 } /* 创建元素 */ function createEl() { // 创建css const style = document.createElement('style') style.innerHTML = ` #liwkerBtnShow { position: absolute; right: 15px; top: -40px; width: 80px; } #liwkerArea { width: 330px; height: 550px; position: fixed; top: 320px; right: 20px; box-shadow: 0 2px 12px 0 rgba(0,0,0,.1); background-color: #fff; border: 1px solid #ebeef5; transition: .3s; z-index: 999; } #liwkerArea > .top { width: 100%; height: 80px; background: #eee; display: flex; justify-content: space-evenly; } #liwkerArea > .top > .search { width: 220px; height: 100%; } #liwkerArea > .top > .search > #send_content { height: 35px; } #liwkerArea > .top > .search > select { width: 100%; height: 30px; margin-top: 7px; } .buttonBox { height: 100%; display: flex; flex-direction: column; justify-content: space-evenly; align-items: end; } .liwkerMain { width: 100%; height: 465px; box-sizing: border-box; padding: 5px 15px; padding-left: 10px; overflow: auto; } .liwkerMsg { width: 285px; height: 24px; line-height: 24px; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; } ` document.querySelector('head').appendChild(style) // 创建box const liwkerArea = document.createElement('div') liwkerArea.id = "liwkerArea" liwkerArea.innerHTML = ` <button id="liwkerBtnShow" type="button" class="el-button search-btn el-button--primary el-button--small">隐藏</button> <div class="top"> <div class="search"> <input type="text" id="send_content" autocomplete="off" placeholder="请输入发送的消息" class="el-input__inner"> <select id="area_select"> <option value="2">网游全部</option> <option value="3">手游全部</option> <option value="6">单机全部</option> <option value="321" selected>原神</option> <option value="549">崩坏:星穹铁道</option> <option value="329">无畏契约</option> <option value="240">APEX英雄</option> <option value="35">王者荣耀</option> <option value="256">和平精英</option> <option value="163">第五人格</option> <option value="395">LOL手游</option> <option value="255">明日方舟</option> <option value="872">来自星尘</option> <option value="571">蛋仔派对</option> <option value="822">元梦之星</option> <option value="874">鸣潮</option> <option value="777">晶核</option> <option value="36">阴阳师</option> <option value="343">DNF手游</option> <option value="662">绝区零</option> </select> </div> <div class="buttonBox"> <button type="button" id="liBtnStart" class="el-button search-btn el-button--primary el-button--small">开始</button> <button type="button" id="liBtnEnd" class="el-button search-btn el-button--primary el-button--small">终止</button> </div> </div> <div class="liwkerMain"> <!-- <div class="liwkerMsg"> 1. “高级的玫瑰”已成功发送消息 </div> --> </div> ` document.querySelector('body').appendChild(liwkerArea) // 绑定事件 // 1. 展示/隐藏按钮 const liwkerBtnShow = document.querySelector('#liwkerBtnShow') liwkerBtnShow.onclick = (e) => { const box = document.querySelector('#liwkerArea') const top = box.querySelector('.top') const main = box.querySelector('.liwkerMain') if(top.style.display !== 'none') { box.style.height = '0px' top.style.display = 'none' main.style.display = 'none' e.target.innerText = '显示' } else { box.style.height = '550px' top.style.display = 'flex' main.style.display = 'block' e.target.innerText = '隐藏' } } const send_content = document.querySelector('#send_content') const area_select = document.querySelector('#area_select') send_content.value = '主播你好呀~' // 2. 开始 const liBtnStart = document.querySelector('#liBtnStart') const liBtnEnd = document.querySelector('#liBtnEnd') if(Data.isRun) { liBtnStart.disabled = true liBtnStart.classList.add('is-disabled') liBtnEnd.disabled = false liBtnEnd.classList.remove('is-disabled') } else { liBtnStart.disabled = false liBtnStart.classList.remove('is-disabled') liBtnEnd.disabled = true liBtnEnd.classList.add('is-disabled') } liBtnStart.onclick = () => { if(!send_content.value || send_content.value.trim() == '') { window.alert('请输入发送的消息') return } // 输入框消息更新 Data.content = send_content.value.split('&') // 选择框更新 area_id & parent_area_id更新 if(Data.parent_id_List.includes(Number(area_select.value))) { Data.parent_area_id = area_select.value Data.area_id = 0 // 全部子分区 } else if(Data.wangyou_id_List.includes(Number(area_select.value))) { Data.parent_area_id = 2 // 网游 Data.area_id = area_select.value } else { Data.parent_area_id = 3 // 手游 Data.area_id = area_select.value } // 禁用按钮 send_content.disabled = true send_content.classList.add('is-disabled') area_select.disabled = true liBtnStart.disabled = true liBtnStart.classList.add('is-disabled') liBtnEnd.disabled = false liBtnEnd.classList.remove('is-disabled') Data.isRun = true Data.livePage = 1 // 执行操作 main() } // 3. 终止 liBtnEnd.onclick = () => { // 禁用按钮 send_content.disabled = false send_content.classList.remove('is-disabled') area_select.disabled = false liBtnStart.disabled = false liBtnStart.classList.remove('is-disabled') liBtnEnd.disabled = true liBtnEnd.classList.add('is-disabled') Data.isRun = false Data.livePage = 1 } } // 初始化 function init () { const cookie = Tools.cookieToJson() Data.myUid = cookie.DedeUserID // 自己的uid Data.csrf = cookie.bili_jct Data.isRun = false // 是否开始执行 Data.livePage = 1 // 是否开始执行 createEl() // 创建元素 } init() })();