diff --git a/AGENTS.md b/AGENTS.md index eeaa6de9..4f83656e 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -47,6 +47,7 @@ npm run preview # 打开提供编译产物安装链接的本地预览网页 - `src/library/bili-api/` 中的 API 必须有详细的请求/响应类型定义 - 路径别名 `@/` 映射到 `src/` +- 代码中的 import 语句一律省略 TypeScript 文件扩展名(`.ts`、`.d.ts`)和目录的 `/index` 后缀 - Commit message 使用约定式提交格式 # 易踩坑点 diff --git a/CHANGELOG.md b/CHANGELOG.md index 071daacd..4e003cc8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,17 @@ ## [Unreleased] +## [7.3.6] - 2026-8-27 + +### 修复 + +- 修复粉丝勋章名单中同一个勋章重复出现的bug(B站接口有概率重复返回同一个勋章) + +### 调整 + +- 优化【隐藏/显示控制面板】按钮样式,不占用B站原生元素空间;如果空间不足与B站活动组件发生重叠,按钮会显示在上方 +- 直播任务板块部分日志优化 + ## [7.3.5] - 2026-8-26 ### 修复 diff --git a/package-lock.json b/package-lock.json index 0f4d35bc..a8c63ac4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "bilibili-live-tasks-helper", - "version": "7.3.5", + "version": "7.3.6", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "bilibili-live-tasks-helper", - "version": "7.3.5", + "version": "7.3.6", "dependencies": { "ajax-hook": "^3.0.3", "crypto-js": "^4.2.0", diff --git a/package.json b/package.json index 6c662d0e..4571f03f 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "bilibili-live-tasks-helper", "private": true, - "version": "7.3.5", + "version": "7.3.6", "type": "module", "scripts": { "dev": "vite", diff --git a/src/App.vue b/src/App.vue index 80b60f8e..1894599e 100644 --- a/src/App.vue +++ b/src/App.vue @@ -19,11 +19,21 @@ const isShowPanel = uiStore.uiConfig.isShowPanel // 太早显示会导致几个字悬浮在屏幕左上角的问题 uiStore.uiConfig.isShowPanel = false // 显示/隐藏控制面板按钮 -let button: HTMLButtonElement +let button: HTMLButtonElement | null = null +// 按钮的定位锚点(.normal-row-ctnr 的第一个子节点) +let buttonAnchor: HTMLElement | null = null /** - * 更新播放器的大小、位置和滚动条位置 + * 更新显示/隐藏控制面板按钮的位置 */ -function updatePosition() { +function updateButtonPosition() { + if (!button || !buttonAnchor) return + // 15 是按钮与左侧内容之间的间距 + button.style.left = `${buttonAnchor.offsetLeft + buttonAnchor.offsetWidth + 15}px` +} +/** + * 同步播放器的大小、位置和窗口滚动条位置到 uiStore + */ +function syncLivePlayerRect() { const rect: DOMRect = livePlayer!.getBoundingClientRect() uiStore.livePlayerRect.top = rect.top @@ -39,14 +49,17 @@ function updatePosition() { */ function buttonOnClick() { uiStore.changeShowPanel() - button.innerText = uiStore.isShowPanelButtonText + + if (button) { + button.innerText = uiStore.isShowPanelButtonText + } } // 节流,防止点击过快,减小渲染压力 const throttleButtonOnClick = _.throttle(buttonOnClick, 300) // 播放器节点出现在最初的html中,可以直接获取 const livePlayer = dq('.player-section') if (livePlayer) { - updatePosition() + syncLivePlayerRect() // 节点 #player-ctnr 在初始 html 中出现 waitForElement(dq('#player-ctnr')!, '.header-info-ctnr .normal-row-ctnr', 10e3) .then((normalRowCtnr) => { @@ -63,6 +76,18 @@ if (livePlayer) { logger.warn('.normal-row-ctnr 没有子节点', normalRowCtnr) normalRowCtnr.appendChild(button) } + // 记录按钮的定位锚点 + buttonAnchor = + normalRowCtnr.firstElementChild instanceof HTMLElement + ? normalRowCtnr.firstElementChild + : null + // 初始化按钮位置 + updateButtonPosition() + // 锚点宽度变化时更新按钮位置 + if (buttonAnchor) { + const buttonAnchorObserver = new ResizeObserver(() => updateButtonPosition()) + buttonAnchorObserver.observe(buttonAnchor) + } if (!isSelfTopFrame()) { // 在特殊直播间,脚本所在的目标 frame 只占屏幕中间一块地方 // 如果焦点不在里面,快捷键会失效 @@ -80,10 +105,10 @@ if (livePlayer) { .catch((e: Error) => logger.error(e)) // 监听页面缩放,调整控制面板大小 // 因为这个操作频率不高就不节流或防抖了 - window.addEventListener('resize', () => updatePosition()) + window.addEventListener('resize', () => syncLivePlayerRect()) // 监听 html 根节点和 body 节点 // 适配播放器网页模式 - const observer = new MutationObserver(() => updatePosition()) + const observer = new MutationObserver(() => syncLivePlayerRect()) observer.observe(document.body, { attributes: true }) observer.observe(document.documentElement, { attributes: true }) diff --git a/src/assets/css/base.css b/src/assets/css/base.css index 4c2648c3..0b5f8d6c 100644 --- a/src/assets/css/base.css +++ b/src/assets/css/base.css @@ -1,7 +1,13 @@ -/* 显示/隐藏控制面板按钮 */ +/** + 显示/隐藏控制面板按钮 + 按钮使用绝对定位,不参与 .normal-row-ctnr 的 flex 布局,避免挤占右侧活动组件的显示空间; + left 由 JS 根据锚点宽度动态设置 +*/ .blth-btn { + position: absolute; + top: 50%; + z-index: 1003; padding: 5px; - margin-left: 15px; font-size: small; line-height: 10px; color: #fff; @@ -10,6 +16,7 @@ border: none; border-radius: 4px; box-shadow: 0 0 2px #00000075; + transform: translateY(-50%); } .blth-btn:hover { @@ -17,9 +24,8 @@ } .blth-btn:hover:active { - position: relative; - top: 1px; background-color: #0e86b6; + transform: translateY(calc(-50% + 1px)); } /** diff --git a/src/library/bili-api/data.d.ts b/src/library/bili-api/data.d.ts index 44d30efd..72410c03 100644 --- a/src/library/bili-api/data.d.ts +++ b/src/library/bili-api/data.d.ts @@ -1363,7 +1363,7 @@ declare namespace LiveData { namespace GetWidgetBannerList { interface Data { - /** 键为组件 id 的字符串形式,没有任何组件时可能为 null */ + /** 键为组件 id 的字符串形式,没有任何组件时为 null */ list: Record | null room_id: number /** 主播 uid */ diff --git a/src/modules/dailyTasks/liveTasks/medalTasks/MedalModule.ts b/src/modules/dailyTasks/liveTasks/medalTasks/MedalModule.ts index f49f0cb4..8fe918c7 100644 --- a/src/modules/dailyTasks/liveTasks/medalTasks/MedalModule.ts +++ b/src/modules/dailyTasks/liveTasks/medalTasks/MedalModule.ts @@ -109,9 +109,9 @@ class MedalModule extends BaseModule { canTryNextPage: currentPage < totalPage && !hasUnlightedMedal, } } - this.logger.warn(`BAPI.live.fansMedalPanel(${page}) 失败`, response.message) + this.logger.warn(`fansMedalPanel(${page}) 获取直播状态失败`, response.message) } catch (error) { - this.logger.warn(`BAPI.live.fansMedalPanel(${page}) 出错`, error) + this.logger.warn(`fansMedalPanel(${page}) 获取直播状态出错`, error) } return { status: null, canTryNextPage: false } } @@ -159,9 +159,9 @@ class MedalModule extends BaseModule { MedalModule.liveStatusSnapshots.set(roomid, { liveStatus, observedAt: tsm() }) return liveStatus } - this.logger.warn(`BAPI.live.getInfoByRoom(${roomid}) 失败`, response.message) + this.logger.warn(`getInfoByRoom(${roomid}) 获取直播状态失败`, response.message) } catch (error) { - this.logger.warn(`BAPI.live.getInfoByRoom(${roomid}) 出错`, error) + this.logger.warn(`getInfoByRoom(${roomid}) 获取直播状态出错`, error) } return null @@ -175,9 +175,9 @@ class MedalModule extends BaseModule { MedalModule.liveStatusSnapshots.set(roomid, { liveStatus, observedAt: tsm() }) return liveStatus } - this.logger.warn(`BAPI.live.getRoomPlayInfo(${roomid}) 失败`, response.message) + this.logger.warn(`getRoomPlayInfo(${roomid}) 获取直播状态失败`, response.message) } catch (error) { - this.logger.warn(`BAPI.live.getRoomPlayInfo(${roomid}) 出错`, error) + this.logger.warn(`getRoomPlayInfo(${roomid}) 获取直播状态出错`, error) } return null @@ -595,14 +595,15 @@ class MedalModule extends BaseModule { try { const response = await BAPI.live.getActivatedMedalInfo(target_id) this.logger.log(`BAPI.live.getActivatedMedalInfo(${target_id}) response`, response) + if (response.code === 0) { return response.data } else { - this.logger.error(`BAPI.live.getActivatedMedalInfo(${target_id}) 失败`, response.message) + this.logger.error(`获取粉丝勋章信息失败(主播uid:${target_id})`, response.message) return null } } catch (error) { - this.logger.error(`BAPI.live.getActivatedMedalInfo(${target_id}) 出错`, error) + this.logger.error(`获取粉丝勋章信息出错(主播uid:${target_id})`, error) return null } }) diff --git a/src/modules/dailyTasks/liveTasks/medalTasks/miaoZaiTask.ts b/src/modules/dailyTasks/liveTasks/medalTasks/miaoZaiTask.ts index 4c7cc245..cdbc5a3e 100644 --- a/src/modules/dailyTasks/liveTasks/medalTasks/miaoZaiTask.ts +++ b/src/modules/dailyTasks/liveTasks/medalTasks/miaoZaiTask.ts @@ -74,7 +74,7 @@ class MiaoZaiTask extends MedalModule { * 依据直播间画面上方右侧的活动组件(挂件)列表里有没有亲密喂养挂件 * * @param medal 粉丝勋章 - * @param logMessage 直播间描述信息,用于日志 + * @param logMessage 粉丝勋章描述信息,用于日志 * @returns 支持返回 true,不支持返回 false;请求失败时无法判断,返回 true 交由后续流程处理 */ private async isMiaoZaiSupported( @@ -88,9 +88,9 @@ class MiaoZaiTask extends MedalModule { this.logger.log(`BAPI.live.getWidgetBannerList(${room_id}) response`, response) if (response.code !== 0) { - this.logger.error(`BAPI.live.getWidgetBannerList(${room_id}) 失败`, response.message) this.logger.warn( - `${logMessage} 无法判断该直播间是否支持亲密喂养活动,仍然尝试参与,后续任务流程可能会失败`, + `${logMessage}获取直播间活动组件失败,无法判断该直播间是否支持亲密喂养活动,仍然尝试参与,后续任务流程可能会失败`, + response.message, ) return true } @@ -105,9 +105,9 @@ class MiaoZaiTask extends MedalModule { return true } catch (error) { - this.logger.error(`BAPI.live.getWidgetBannerList(${room_id}) 出错`, error) this.logger.warn( - `${logMessage} 无法判断该直播间是否支持亲密喂养活动,仍然尝试参与,后续任务流程可能会失败`, + `${logMessage}获取直播间活动组件出错,无法判断该直播间是否支持亲密喂养活动,仍然尝试参与,后续任务流程可能会失败`, + error, ) return true } @@ -128,12 +128,20 @@ class MiaoZaiTask extends MedalModule { try { const response = await BAPI.live.Q3FansS1MiaoZaiHome(room_id, ruid) this.logger.log(`BAPI.live.Q3FansS1MiaoZaiHome(${room_id}, ${ruid}) response`, response) + if (response.code === 0) { return response.data } - this.logger.error(`BAPI.live.Q3FansS1MiaoZaiHome(${room_id}, ${ruid}) 失败`, response.message) + + this.logger.error( + `获取亲密喂养活动主页数据失败 room_id = ${room_id}, 主播uid = ${ruid}`, + response.message, + ) } catch (error) { - this.logger.error(`BAPI.live.Q3FansS1MiaoZaiHome(${room_id}, ${ruid}) 出错`, error) + this.logger.error( + `获取亲密喂养活动主页数据出错 room_id = ${room_id}, 主播uid = ${ruid}`, + error, + ) } return null @@ -153,12 +161,14 @@ class MiaoZaiTask extends MedalModule { * * 外层 code 和 data.code 都为 0 才算成功 * + * @param apiPath API 路径和参数,用于日志 * @param actionText 操作名称,用于日志 - * @param logMessage 直播间描述信息,用于日志 + * @param logMessage 粉丝勋章描述信息,用于日志 * @param requester 请求函数 * @returns 成功返回 ActionData,失败返回 null */ private async executeAction( + apiPath: string, actionText: string, logMessage: string, requester: () => Promise<{ @@ -169,14 +179,14 @@ class MiaoZaiTask extends MedalModule { ): Promise { try { const response = await requester() - this.logger.log(`${actionText} ${logMessage} response`, response) + this.logger.log(`${apiPath} response`, response) if (response.code !== 0) { - this.logger.error(`${actionText} ${logMessage} 失败`, response.message) + this.logger.error(`${logMessage}${actionText}失败`, response.message) return null } if (response.data.code !== 0) { - this.logger.error(`${actionText} ${logMessage} 失败`, response.data.msg) + this.logger.error(`${logMessage}${actionText}失败`, response.data.msg) return null } @@ -185,13 +195,13 @@ class MiaoZaiTask extends MedalModule { const levelUpInfo = response.data.level_up_list[0] const rewardInfo = levelUpInfo.rewards?.map((r) => `${r.name}${r.desc}`).join(',') this.logger.log( - `${actionText} ${logMessage} 已升级到 ${levelUpInfo.level_name}(Lv.${levelUpInfo.level})${rewardInfo ? `,获得奖励:${rewardInfo}` : ''}`, + `${logMessage}${actionText}后升级至 ${levelUpInfo.level_name}(Lv.${levelUpInfo.level})${rewardInfo ? `,获得奖励:${rewardInfo}` : ''}`, ) } return response.data } catch (error) { - this.logger.error(`${actionText} ${logMessage} 出错`, error) + this.logger.error(`${logMessage}${actionText}出错`, error) return null } } @@ -200,7 +210,7 @@ class MiaoZaiTask extends MedalModule { * 喂猫,消耗掉所有剩余猫粮 * * @param medal 粉丝勋章 - * @param logMessage 直播间描述信息,用于日志 + * @param logMessage 粉丝勋章描述信息,用于日志 * @param balance 剩余猫粮数量 * @returns 是否把猫粮都喂完了 */ @@ -210,22 +220,25 @@ class MiaoZaiTask extends MedalModule { balance: number, ): Promise { if (balance <= 0) { - this.logger.log(`${logMessage} 没有剩余猫粮,跳过喂猫`) + this.logger.log(`${logMessage}没有剩余猫粮,跳过喂猫`) return true } const ruid = medal.medal.target_id - this.logger.log(`${logMessage} 开始喂猫,剩余猫粮 ${balance} 份`) + this.logger.log(`${logMessage}开始喂猫,剩余猫粮 ${balance} 份`) for (let i = 0; i < MiaoZaiTask.FEED_CAT_MAX_TIMES; i++) { - const data = await this.executeAction('喂猫', logMessage, () => - BAPI.live.Q3FansS1MiaoZaiFeedCat(ruid), + const data = await this.executeAction( + `BAPI.live.Q3FansS1MiaoZaiFeedCat(${ruid})`, + '喂猫', + logMessage, + () => BAPI.live.Q3FansS1MiaoZaiFeedCat(ruid), ) if (!data) return false this.logger.log( - `${logMessage} 喂猫成功,获得 ${data.growth_delta} 点成长值(当前成长值 ${data.growth}),剩余猫粮 ${data.food_balance} 份`, + `${logMessage}喂猫成功,获得 ${data.growth_delta} 点成长值(当前成长值 ${data.growth}),剩余猫粮 ${data.food_balance} 份`, ) if (data.food_balance <= 0) return true @@ -234,7 +247,7 @@ class MiaoZaiTask extends MedalModule { } this.logger.warn( - `${logMessage} 喂猫次数已达上限(${MiaoZaiTask.FEED_CAT_MAX_TIMES} 次),停止喂猫`, + `${logMessage}喂猫次数已达上限(${MiaoZaiTask.FEED_CAT_MAX_TIMES} 次),停止喂猫`, ) return false } @@ -243,7 +256,7 @@ class MiaoZaiTask extends MedalModule { * 撸猫,直到连续多次没有获得成长值 * * @param medal 粉丝勋章 - * @param logMessage 直播间描述信息,用于日志 + * @param logMessage 粉丝勋章描述信息,用于日志 * @returns 是否正常完成今日撸猫任务 */ private async petCat(medal: LiveData.FansMedalPanel.List, logMessage: string): Promise { @@ -251,11 +264,14 @@ class MiaoZaiTask extends MedalModule { let zeroGrowthCount = 0 let totalGrowth = 0 - this.logger.log(`${logMessage} 开始撸猫`) + this.logger.log(`${logMessage}开始撸猫`) for (let i = 0; i < MiaoZaiTask.PET_CAT_MAX_TIMES; i++) { - const data = await this.executeAction('撸猫', logMessage, () => - BAPI.live.Q3FansS1MiaoZaiPetCat(ruid), + const data = await this.executeAction( + `BAPI.live.Q3FansS1MiaoZaiPetCat(${ruid})`, + '撸猫', + logMessage, + () => BAPI.live.Q3FansS1MiaoZaiPetCat(ruid), ) if (!data) return false @@ -264,15 +280,15 @@ class MiaoZaiTask extends MedalModule { zeroGrowthCount = 0 totalGrowth += data.growth_delta this.logger.log( - `${logMessage} 撸猫成功,获得 ${data.growth_delta} 点成长值(当前成长值 ${data.growth})`, + `${logMessage}撸猫成功,获得 ${data.growth_delta} 点成长值(当前成长值 ${data.growth})`, ) } else { zeroGrowthCount++ - this.logger.log(`${logMessage} 撸猫成功,但没有获得成长值(连续 ${zeroGrowthCount} 次)`) + this.logger.log(`${logMessage}撸猫成功,但没有获得成长值(连续 ${zeroGrowthCount} 次)`) if (zeroGrowthCount >= MiaoZaiTask.PET_CAT_ZERO_GROWTH_LIMIT) { this.logger.log( - `${logMessage} 连续 ${MiaoZaiTask.PET_CAT_ZERO_GROWTH_LIMIT} 次撸猫没有获得成长值,今日撸猫任务已完成,共获得 ${totalGrowth} 点成长值`, + `${logMessage}连续 ${MiaoZaiTask.PET_CAT_ZERO_GROWTH_LIMIT} 次撸猫没有获得成长值,今日撸猫任务已完成,共获得 ${totalGrowth} 点成长值`, ) return true } @@ -282,7 +298,7 @@ class MiaoZaiTask extends MedalModule { } this.logger.warn( - `${logMessage} 撸猫次数已达上限(${MiaoZaiTask.PET_CAT_MAX_TIMES} 次),停止撸猫`, + `${logMessage}撸猫次数已达上限(${MiaoZaiTask.PET_CAT_MAX_TIMES} 次),停止撸猫`, ) return false } @@ -291,7 +307,7 @@ class MiaoZaiTask extends MedalModule { * 复查养猫结果并输出喵崽的当前数据 * * @param medal 粉丝勋章 - * @param logMessage 直播间描述信息,用于日志 + * @param logMessage 粉丝勋章描述信息,用于日志 * @returns 签到和喂猫是否都已完成 */ private async verifyResult( @@ -301,27 +317,27 @@ class MiaoZaiTask extends MedalModule { const home = await this.fetchHome(medal) if (!home) { - this.logger.error(`${logMessage} 无法获取最新养猫活动数据,无法确认任务是否完成,默认已完成`) + this.logger.error(`${logMessage}无法获取最新养猫活动数据,无法确认任务是否完成,默认已完成`) return true } const cat = home.cat_info if (cat) { this.logger.log( - `${logMessage} 喵崽【${cat.cat_name}】当前为 ${cat.level_name}(Lv.${cat.level}),成长值 ${cat.growth}/${cat.next_level_growth}`, + `${logMessage}喵崽【${cat.cat_name}】当前为 ${cat.level_name}(Lv.${cat.level}),成长值 ${cat.growth}/${cat.next_level_growth}`, ) } let completed = true if (!MiaoZaiTask.isSignInDone(home)) { - this.logger.warn(`${logMessage} 签到任务仍未完成,下次运行会继续尝试`) + this.logger.warn(`${logMessage}签到任务仍未完成,下次运行会继续尝试`) completed = false } const balance = home.food_info?.balance ?? 0 if (balance > 0) { - this.logger.warn(`${logMessage} 仍剩余 ${balance} 份猫粮没有喂完,下次运行会继续尝试`) + this.logger.warn(`${logMessage}仍剩余 ${balance} 份猫粮没有喂完,下次运行会继续尝试`) completed = false } @@ -356,7 +372,7 @@ class MiaoZaiTask extends MedalModule { const logMessage = `粉丝勋章【${medal_name}】(主播【${nick_name}】,UID:${ruid},直播间:${room_id})` if (!(await this.isMiaoZaiSupported(medal, logMessage))) { - this.logger.warn(`${logMessage} 不支持亲密喂养活动,跳过该直播间`) + this.logger.warn(`${logMessage}不支持亲密喂养活动,跳过该直播间`) // 不支持养猫活动,视为完成 return 'completed' } @@ -365,7 +381,7 @@ class MiaoZaiTask extends MedalModule { let home = await this.fetchHome(medal) if (!home) { - this.logger.error(`${logMessage} 无法获取养猫活动数据,跳过该直播间`) + this.logger.error(`${logMessage}无法获取养猫活动数据,跳过该直播间`) return 'uncompleted' } @@ -378,14 +394,17 @@ class MiaoZaiTask extends MedalModule { // 还没选猫,先选一只 if (!home.cat_selected) { - this.logger.log(`${logMessage} 还没有选择喵崽,先选择一只`) + this.logger.log(`${logMessage}还没有选择喵崽,先选择一只`) - const data = await this.executeAction('选择喵崽', logMessage, () => - BAPI.live.Q3FansS1MiaoZaiSelectCat(ruid), + const data = await this.executeAction( + `BAPI.live.Q3FansS1MiaoZaiSelectCat(${ruid})`, + '选择喵崽', + logMessage, + () => BAPI.live.Q3FansS1MiaoZaiSelectCat(ruid), ) if (!data) { - this.logger.error(`${logMessage} 选择喵崽失败,跳过该直播间`) + this.logger.error(`${logMessage}选择喵崽失败,跳过该直播间`) return 'uncompleted' } @@ -394,7 +413,7 @@ class MiaoZaiTask extends MedalModule { // 重新获取主页数据,拿到选完猫之后的猫粮数量和任务状态 home = await this.fetchHome(medal) if (!home) { - this.logger.error(`${logMessage} 选择喵崽后无法获取养猫活动数据,跳过该直播间`) + this.logger.error(`${logMessage}选择喵崽后无法获取养猫活动数据,跳过该直播间`) return 'uncompleted' } } @@ -403,17 +422,20 @@ class MiaoZaiTask extends MedalModule { // 签到领猫粮 if (MiaoZaiTask.isSignInDone(home)) { - this.logger.log(`${logMessage} 今天已经签到过了`) + this.logger.log(`${logMessage}今天已经签到过了`) } else { - const data = await this.executeAction('签到', logMessage, () => - BAPI.live.Q3FansS1MiaoZaiSignIn(ruid), + const data = await this.executeAction( + `BAPI.live.Q3FansS1MiaoZaiSignIn(${ruid})`, + '签到', + logMessage, + () => BAPI.live.Q3FansS1MiaoZaiSignIn(ruid), ) if (data) { - this.logger.log(`${logMessage} 签到成功,当前共有 ${data.food_balance} 份猫粮`) + this.logger.log(`${logMessage}签到成功,当前共有 ${data.food_balance} 份猫粮`) balance = data.food_balance } else { - this.logger.error(`${logMessage} 签到失败,仍然尝试喂完已有的猫粮`) + this.logger.error(`${logMessage}签到失败,仍然尝试喂完已有的猫粮`) } await sleep(MiaoZaiTask.ACTION_DYNAMIC_LONG_INTERVAL) diff --git a/src/modules/dailyTasks/liveTasks/medalTasks/watchTask.ts b/src/modules/dailyTasks/liveTasks/medalTasks/watchTask.ts index 585aa843..d9fb5e6b 100644 --- a/src/modules/dailyTasks/liveTasks/medalTasks/watchTask.ts +++ b/src/modules/dailyTasks/liveTasks/medalTasks/watchTask.ts @@ -144,17 +144,16 @@ class RoomHeart { await this.waitForNextHeartbeat() return this.X() } else { - this.logger.error( - `BAPI.liveTrace.E(${this.id}, ${this.device}, ${this.ruid}) 失败`, - response.message, - ) this.logger.error( `直播间 ${this.roomID} 的 E 心跳失败,无法继续执行观看直播任务,跳过该房间`, + response.message, ) } } catch (error) { - this.logger.error(`BAPI.liveTrace.E(${this.id}, ${this.device}, ${this.ruid}) 出错`, error) - this.logger.error(`直播间 ${this.roomID} 的 E 心跳失败,无法继续执行观看直播任务,跳过该房间`) + this.logger.error( + `直播间 ${this.roomID} 的 E 心跳出错,无法继续执行观看直播任务,跳过该房间`, + error, + ) } } @@ -209,23 +208,17 @@ class RoomHeart { await this.waitForNextHeartbeat() // 继续下一轮 X 心跳 } else { - this.logger.error( - `BAPI.liveTrace.X(${s}, ${this.id}, ${this.device}, ${this.ruid}, ${this.timestamp}, ${this.secretKey}, ${this.heartBeatInterval}) 失败`, - response.message, - ) this.logger.error( `直播间 ${this.roomID} 的 X 心跳失败,无法继续执行观看直播任务,跳过该房间(目前已观看 ${this.watchedSeconds} 秒)`, + response.message, ) return } } catch (error) { this.logger.error( - `BAPI.liveTrace.X(s, ${this.id}, ${this.device}, ${this.ruid}, ${this.timestamp}, ${this.secretKey}, ${this.heartBeatInterval}) 出错`, + `直播间 ${this.roomID} 的 X 心跳出错,无法继续执行观看直播任务,跳过该房间(目前已观看 ${this.watchedSeconds} 秒)`, error, ) - this.logger.error( - `直播间 ${this.roomID} 的 X 心跳失败,无法继续执行观看直播任务,跳过该房间(目前已观看 ${this.watchedSeconds} 秒)`, - ) return } } diff --git a/src/modules/default/fansMedals.ts b/src/modules/default/fansMedals.ts index f8ce9dce..0391c88a 100644 --- a/src/modules/default/fansMedals.ts +++ b/src/modules/default/fansMedals.ts @@ -39,12 +39,13 @@ class FansMedals extends BaseModule { } else { this.logger.error(`获取粉丝勋章列表第${page}页失败,提前结束获取`, response.message) // 中途出错,返回已获取的粉丝勋章列表,不抛出错误 - return fansMedalList + return _.uniqBy(fansMedalList, (item) => item.medal.medal_id) } // 防止风控,稍微加点延时 await sleep(_.random(300, 500)) } - return fansMedalList + // 接口分页可能返回重复数据(同一勋章同时出现在上一页最后和下一页最前),按 medal_id 去重 + return _.uniqBy(fansMedalList, (item) => item.medal.medal_id) } catch (error: any) { useBiliStore().fansMedalsMeta.status = 'error' throw new ModuleError(this.moduleName, `获取粉丝勋章列表出错: ${error.message}`)