From f131e93830255a265ac80ad40b40cbb387114eda Mon Sep 17 00:00:00 2001 From: zhiyu1998 <542716863@qq.com> Date: Mon, 1 Apr 2024 20:26:00 +0800 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20feat:=20=20V1.6.3=20=E5=A2=9E?= =?UTF-8?q?=E5=8A=A0bili=E6=89=AB=E7=A0=81=E7=99=BB=E5=BD=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 现在使用 #R插件B站扫码 即可登录体验AI总结功能 --- apps/tools.js | 29 +++++++++-- config/help.yaml | 3 ++ config/version.yaml | 5 +- constants/tools.js | 14 ++++++ model/index.js | 20 ++++++++ package.json | 3 +- resources/img/icon/bqrcode.png | Bin 0 -> 3409 bytes utils/bilibili.js | 87 ++++++++++++++++++++++++++++----- 8 files changed, 140 insertions(+), 21 deletions(-) create mode 100644 resources/img/icon/bqrcode.png diff --git a/apps/tools.js b/apps/tools.js index 0421607..5d46c3e 100644 --- a/apps/tools.js +++ b/apps/tools.js @@ -12,7 +12,7 @@ import { downloadBFile, getBiliAudio, getDownloadUrl, - getDynamic, + getDynamic, getScanCodeData, getVideoInfo, m4sToMp3, mergeFileToMp4 @@ -50,10 +50,8 @@ import { TIKTOK_INFO, TWITTER_TWEET_INFO, XHS_REQ_LINK, - GENERAL_REQ_LINK, WEIBO_SINGLE_INFO, WEISHI_VIDEO_INFO + GENERAL_REQ_LINK, WEIBO_SINGLE_INFO, WEISHI_VIDEO_INFO, BILI_SCAN_CODE_GENERATE } from "../constants/tools.js"; -import child_process from 'node:child_process' -import { getAudio, getVideo } from "../utils/y2b.js"; import { processTikTokUrl } from "../utils/tiktok.js"; import { getDS } from "../utils/mihoyo.js"; import GeneralLinkAdapter from "../utils/general-link-adapter.js"; @@ -92,6 +90,11 @@ export class tools extends plugin { reg: "(www.tiktok.com)|(vt.tiktok.com)|(vm.tiktok.com)", fnc: "tiktok", }, + { + reg: "^#R插件B站扫码$", + fnc: "biliScan", + permission: 'master', + }, { reg: "(bilibili.com|b23.tv|t.bilibili.com)", fnc: "bili", @@ -339,6 +342,24 @@ export class tools extends plugin { return true; } + async biliScan(e) { + e.reply('R插件开源免责声明:\n您将通过扫码完成获取哔哩哔哩refresh_token以及ck。\n本Bot将不会保存您的登录状态。\n我方仅提供视频解析及相关B站内容服务,若您的账号封禁、被盗等处罚与我方无关。\n害怕风险请勿扫码 ~', { recallMsg: 180 }); + // 图片发送钩子 + const imgSendHook = function (e, path) { + e.reply([segment.image(path), segment.at(e.user_id), '请扫码以完成获取'], { recallMsg: 180 }) + }; + // 发送请求 + const saveCodePath = `${ this.defaultPath }qrcode.png`; + + const { SESSDATA, refresh_token } = await getScanCodeData(saveCodePath, 8, () => imgSendHook(e, saveCodePath)) + + // 更新到配置文件 + config.updateField("tools", "biliSessData", SESSDATA); + e.reply('登录成功!相关信息已保存至配置文件', true) + return true; + } + + // bilibi解析 async bili(e) { await this.limitUserUse(e, () => { diff --git a/config/help.yaml b/config/help.yaml index 1dd9eb8..abdc896 100644 --- a/config/help.yaml +++ b/config/help.yaml @@ -38,6 +38,9 @@ - icon: bilibili title: "bilibili/b23" desc: 哔哩哔哩分享实时下载 + - icon: bqrcode + title: "R插件B站扫码" + desc: R插件B站扫码 - icon: bilimusic title: "bili音乐+链接" desc: 哔哩哔哩音乐分享实时下载 diff --git a/config/version.yaml b/config/version.yaml index 431ea48..486d5a5 100644 --- a/config/version.yaml +++ b/config/version.yaml @@ -1,11 +1,10 @@ - { - version: 1.6.2, + version: 1.6.3, data: [ + 新增B站扫码功能, 新增即刻解析功能, 新增微视解析功能, - 新增小世界解析功能, - 新增贴吧解析功能, 支持锅巴插件,方便查看和修改配置, 添加#R帮助获取插件帮助, 添加#R版本获取插件版本, diff --git a/constants/tools.js b/constants/tools.js index 0bb034f..22bf60a 100644 --- a/constants/tools.js +++ b/constants/tools.js @@ -40,6 +40,20 @@ export const BILI_VIDEO_INFO = "http://api.bilibili.com/x/web-interface/view" */ export const BILI_NAV = "https://api.bilibili.com/x/web-interface/nav" +/** + * 扫码登录的二维码生成 + * https://github.com/SocialSisterYi/bilibili-API-collect/blob/master/docs/login/login_action/QR.md + * @type {string} + */ +export const BILI_SCAN_CODE_GENERATE = "https://passport.bilibili.com/x/passport-login/web/qrcode/generate" + +/** + * 扫码登录检测然后发送令牌数据 + * https://github.com/SocialSisterYi/bilibili-API-collect/blob/master/docs/login/login_action/QR.md + * @type {string} + */ +export const BILI_SCAN_CODE_DETECT = "https://passport.bilibili.com/x/passport-login/web/qrcode/poll?qrcode_key={}"; + /** * 米游社网页端获取文章 * https://github.com/UIGF-org/mihoyo-api-collect/blob/main/hoyolab/article/article.md#%E8%8E%B7%E5%8F%96%E5%AE%8C%E6%95%B4%E6%96%87%E7%AB%A0%E4%BF%A1%E6%81%AF diff --git a/model/index.js b/model/index.js index 32ef893..1c8a88b 100644 --- a/model/index.js +++ b/model/index.js @@ -28,6 +28,26 @@ class RConfig { return this.getYaml(name) } + // 获取指定配置的某个字段 + getField(name, field) { + const config = this.getConfig(name); + return config[field]; + } + + // 更新指定配置的某个字段 + updateField(name, field, value) { + let config = this.getConfig(name); + config[field] = value; // 更新字段值 + this.saveSet(name, config); // 保存更改 + } + + // 删除指定配置的某个字段 + deleteField(name, field) { + let config = this.getConfig(name); + delete config[field]; // 删除指定字段 + this.saveSet(name, config); // 保存更改 + } + /** * 获取配置yaml * @param name 名称 diff --git a/package.json b/package.json index 6758f3b..b34dd01 100644 --- a/package.json +++ b/package.json @@ -4,6 +4,7 @@ "type": "module", "dependencies": { "axios": "^1.3.4", - "tunnel": "^0.0.6" + "tunnel": "^0.0.6", + "qrcode": "^1.5.3" } } diff --git a/resources/img/icon/bqrcode.png b/resources/img/icon/bqrcode.png new file mode 100644 index 0000000000000000000000000000000000000000..e9078ed481c4ac337e53d16438925a208385b4bb GIT binary patch literal 3409 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 { @@ -53,9 +64,7 @@ export async function getDownloadUrl(url) { return axios .get(url, { headers: { - 'User-Agent': - 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.127 Safari/537.36', - referer: 'https://www.bilibili.com', + ...biliHeaders }, }) .then(({ data }) => { @@ -127,9 +136,7 @@ export async function m4sToMp3(m4sUrl, path) { .get(m4sUrl, { responseType: 'stream', headers: { - 'User-Agent': - 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.127 Safari/537.36', - referer: 'https://www.bilibili.com', + ...biliHeaders }, }).then(async res => { // 如果没有目录就创建一个 @@ -231,9 +238,7 @@ export async function getDynamic(dynamicId, SESSDATA) { const dynamicApi = BILI_DYNAMIC.replace("{}", dynamicId); return axios.get(dynamicApi, { headers: { - 'User-Agent': - 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.127 Safari/537.36', - 'referer': 'https://www.bilibili.com', + ...biliHeaders, Cookie: `SESSDATA=${ SESSDATA }` }, }).then(resp => { @@ -254,4 +259,60 @@ export async function getDynamic(dynamicId, SESSDATA) { dynamicDesc } }) +} + +/** + * 扫码 + * @param qrcodeSavePath 【必须】QR保存位置 + * @param detectTime 【可选】检测时间(默认10s检测一次) + * @param hook 【可选】钩子函数,目前只用来人机交互 + * @returns {Promise<{ + * SESSDATA, + * refresh_token + * }>} + */ +export async function getScanCodeData(qrcodeSavePath = 'qrcode.png', detectTime = 10, hook = () => {}) { + try { + const resp = await axios.get(BILI_SCAN_CODE_GENERATE, { ...biliHeaders }); + // 保存扫码的地址、扫码登录秘钥 + const { url: scanUrl, qrcode_key } = resp.data.data; + await qrcode.toFile(qrcodeSavePath, scanUrl); + + let code = 1; + + // 设置最大尝试次数 + let attemptCount = 0; + const maxAttempts = 3; + + let loginResp; + // 检测扫码情况默认 10s 检测一次,并且尝试3次,没扫就拜拜 + while (code !== 0 && attemptCount < maxAttempts) { + // 钩子函数,目前用于发送二维码给用户 + hook(); + loginResp = await axios.get(BILI_SCAN_CODE_DETECT.replace("{}", qrcode_key), { ...biliHeaders }); + code = loginResp.data.data.code; + await new Promise(resolve => setTimeout(resolve, detectTime * 1000)); // Wait for detectTime seconds + } + // 获取刷新令牌 + const { refresh_token } = loginResp.data.data; + + // 获取cookie + const cookies = loginResp.headers['set-cookie']; + const SESSDATA = cookies + .map(cookie => cookie.split(';').find(item => item.trim().startsWith('SESSDATA='))) + .find(item => item !== undefined) + ?.split('=')[1]; + + return { + SESSDATA, + refresh_token + }; + } catch (err) { + logger.error(err); + // 可能需要处理错误或返回一个默认值 + return { + SESSDATA: '', + refresh_token: '' + }; + } } \ No newline at end of file