斗鱼鱼塘商城自动兑换工具 (注意手动设置个人用户ID以及房间号)
< Părere la script-ul 斗鱼鱼塘商城兑换工具
【自动钓鱼】风控拦截,请联系客服~
请求次数太多会被拦截,不给钓了
应该是启动兑换跑完没关,被系统屏蔽了一会。。。
那正常.. 手动兑换卡点开几秒就好了 提前开也会噶 😂完完全全暴力请求 啥都不检测的😂 一瞬间就请求频繁了
我在搞新脚本那个 打算整合钓鱼兑换一起 通过检测兑换状态才发送兑换, 尽量避免被拉黑.. 可是.. 太慢了 这两天1鱼翅都没兑完 其他都兑完了
那正常.. 手动兑换卡点开几秒就好了 提前开也会噶 😂完完全全暴力请求 啥都不检测的😂 一瞬间就请求频繁了
我在搞新脚本那个 打算整合钓鱼兑换一起 通过检测兑换状态才发送兑换, 尽量避免被拉黑.. 可是.. 太慢了 这两天1鱼翅都没兑完 其他都兑完了
我也是1鱼翅没换完 是因为被拉黑了吗
那正常.. 手动兑换卡点开几秒就好了 提前开也会噶 😂完完全全暴力请求 啥都不检测的😂 一瞬间就请求频繁了
我在搞新脚本那个 打算整合钓鱼兑换一起 通过检测兑换状态才发送兑换, 尽量避免被拉黑.. 可是.. 太慢了 这两天1鱼翅都没兑完 其他都兑完了我也是1鱼翅没换完 是因为被拉黑了吗
拉黑通常就几分钟, 你兑换之前F12->网络 兑换完后 看一下 convertOpt 的请求 Response应该会显示请求频繁😂 , 我还没摸到限制的频率 苦思冥想这么多要兑换的 要怎么整.. 一次性并发的话 就前面几条请求通过了 后面全被拉黑了 一定抢不到了.. 不并发那么多奖品怎么整 调延迟的话会被别人抢光.. 还有被拉黑后钓鱼也会挂 手机点开也会加载不了 所以又不能钓鱼一起整..
那正常.. 手动兑换卡点开几秒就好了 提前开也会噶 😂完完全全暴力请求 啥都不检测的😂 一瞬间就请求频繁了
我在搞新脚本那个 打算整合钓鱼兑换一起 通过检测兑换状态才发送兑换, 尽量避免被拉黑.. 可是.. 太慢了 这两天1鱼翅都没兑完 其他都兑完了我也是1鱼翅没换完 是因为被拉黑了吗
拉黑通常就几分钟, 你兑换之前F12->网络 兑换完后 看一下 convertOpt 的请求 Response应该会显示请求频繁😂 , 我还没摸到限制的频率 苦思冥想这么多要兑换的 要怎么整.. 一次性并发的话 就前面几条请求通过了 后面全被拉黑了 一定抢不到了.. 不并发那么多奖品怎么整 调延迟的话会被别人抢光.. 还有被拉黑后钓鱼也会挂 手机点开也会加载不了 所以又不能钓鱼一起整..
我也是,1鱼翅这两天一般能抢3个左右,其他的能兑换完,刚刚8点钟时提前3秒启动兑换跑到30时候手动关,结果一个没抢到......
那正常.. 手动兑换卡点开几秒就好了 提前开也会噶 😂完完全全暴力请求 啥都不检测的😂 一瞬间就请求频繁了
我在搞新脚本那个 打算整合钓鱼兑换一起 通过检测兑换状态才发送兑换, 尽量避免被拉黑.. 可是.. 太慢了 这两天1鱼翅都没兑完 其他都兑完了我也是1鱼翅没换完 是因为被拉黑了吗
拉黑通常就几分钟, 你兑换之前F12->网络 兑换完后 看一下 convertOpt 的请求 Response应该会显示请求频繁😂 , 我还没摸到限制的频率 苦思冥想这么多要兑换的 要怎么整.. 一次性并发的话 就前面几条请求通过了 后面全被拉黑了 一定抢不到了.. 不并发那么多奖品怎么整 调延迟的话会被别人抢光.. 还有被拉黑后钓鱼也会挂 手机点开也会加载不了 所以又不能钓鱼一起整..
我也是,1鱼翅这两天一般能抢3个左右,其他的能兑换完,刚刚8点钟时提前3秒启动兑换跑到30时候手动关,结果一个没抢到......
今天好像斗鱼把奖励缩减了 刚刚我0.1的换了2鱼翅就兑换完毕了
那正常.. 手动兑换卡点开几秒就好了 提前开也会噶 😂完完全全暴力请求 啥都不检测的😂 一瞬间就请求频繁了
我在搞新脚本那个 打算整合钓鱼兑换一起 通过检测兑换状态才发送兑换, 尽量避免被拉黑.. 可是.. 太慢了 这两天1鱼翅都没兑完 其他都兑完了我也是1鱼翅没换完 是因为被拉黑了吗
拉黑通常就几分钟, 你兑换之前F12->网络 兑换完后 看一下 convertOpt 的请求 Response应该会显示请求频繁😂 , 我还没摸到限制的频率 苦思冥想这么多要兑换的 要怎么整.. 一次性并发的话 就前面几条请求通过了 后面全被拉黑了 一定抢不到了.. 不并发那么多奖品怎么整 调延迟的话会被别人抢光.. 还有被拉黑后钓鱼也会挂 手机点开也会加载不了 所以又不能钓鱼一起整..
我也是,1鱼翅这两天一般能抢3个左右,其他的能兑换完,刚刚8点钟时提前3秒启动兑换跑到30时候手动关,结果一个没抢到......
今天好像斗鱼把奖励缩减了 刚刚我0.1的换了2鱼翅就兑换完毕了
哎 狗鱼, 怕脚本有延迟 而且现在我的版本改到乱七八糟不确定效果 上班没空 我在控制台直接粘贴代码去自动兑换, 结果.. 一个都没成功 去看网络请求..请求返回的时间显示准点 00分00秒 返回的数据是:
error: 30002
msg: "奖品库存不足~"
真是条狗鱼呜呜呜 你查看兑换记录呢 之前我兑换的时候是1鱼翅5个 0.5 10个 0.1 50个的哇.. (真不知道之前怎么清空的 明明并发了会被拒绝 拉黑 越研究兑越少.. 现在我就换了水手 哈哈哈哈哈哈哈哈哈哈或或或或)
async function main() {
async function sendRequest(index) {
try {
console.log('正在发送请求...');
const response = await fetch("https://www.douyu.com/japi/revenuenc/web/actfans/convert/convertOpt", {
"headers": {
"accept": "application/json, text/plain, */*",
"accept-language": "en-US,en;q=0.9,zh-CN;q=0.8,zh;q=0.7",
"content-type": "application/x-www-form-urlencoded",
"priority": "u=1, i",
"sec-ch-ua": "\"Google Chrome\";v=\"131\", \"Chromium\";v=\"131\", \"Not_A Brand\";v=\"24\"",
"sec-ch-ua-mobile": "?0",
"sec-ch-ua-platform": "\"Windows\"",
"sec-fetch-dest": "empty",
"sec-fetch-mode": "cors",
"sec-fetch-site": "same-origin"
},
"referrer": "https://www.douyu.com/pages/fish-act/shop",
"referrerPolicy": "strict-origin-when-cross-origin",
"body": `ctn=1&rid=11410656&index=${index}`,
"method": "POST",
"mode": "cors",
"credentials": "include"
});
console.log('请求发送成功,状态码:', response.status);
const data = await response.json();
console.log('请求响应数据:', data);
} catch (error) {
console.error('请求发送失败:', error);
}
}
setInterval(async () => {
const now = new Date();
if (now.getMinutes() === 0 && now.getSeconds() === 0) {
const indexes = ['PROP_2', 'YC_TY_1','YC_TY_2']; // 不同的 index 值列表 //之前尝试多选循环兑换
for (let i = 0; i < 5; i++) {
await sendRequest('YC_TY_3');
}
}
}, 1000);
}
main();
那正常.. 手动兑换卡点开几秒就好了 提前开也会噶 😂完完全全暴力请求 啥都不检测的😂 一瞬间就请求频繁了
我在搞新脚本那个 打算整合钓鱼兑换一起 通过检测兑换状态才发送兑换, 尽量避免被拉黑.. 可是.. 太慢了 这两天1鱼翅都没兑完 其他都兑完了我也是1鱼翅没换完 是因为被拉黑了吗
拉黑通常就几分钟, 你兑换之前F12->网络 兑换完后 看一下 convertOpt 的请求 Response应该会显示请求频繁😂 , 我还没摸到限制的频率 苦思冥想这么多要兑换的 要怎么整.. 一次性并发的话 就前面几条请求通过了 后面全被拉黑了 一定抢不到了.. 不并发那么多奖品怎么整 调延迟的话会被别人抢光.. 还有被拉黑后钓鱼也会挂 手机点开也会加载不了 所以又不能钓鱼一起整..
我也是,1鱼翅这两天一般能抢3个左右,其他的能兑换完,刚刚8点钟时提前3秒启动兑换跑到30时候手动关,结果一个没抢到......
今天好像斗鱼把奖励缩减了 刚刚我0.1的换了2鱼翅就兑换完毕了
上午我还在抢1鱼翅,中午直接显示兑换完毕,我查了下就兑换3个1鱼翅,还是凌晨兑换的,真恶心啊,意思明天所有鱼翅要减一半
那正常.. 手动兑换卡点开几秒就好了 提前开也会噶 😂完完全全暴力请求 啥都不检测的😂 一瞬间就请求频繁了
我在搞新脚本那个 打算整合钓鱼兑换一起 通过检测兑换状态才发送兑换, 尽量避免被拉黑.. 可是.. 太慢了 这两天1鱼翅都没兑完 其他都兑完了我也是1鱼翅没换完 是因为被拉黑了吗
拉黑通常就几分钟, 你兑换之前F12->网络 兑换完后 看一下 convertOpt 的请求 Response应该会显示请求频繁😂 , 我还没摸到限制的频率 苦思冥想这么多要兑换的 要怎么整.. 一次性并发的话 就前面几条请求通过了 后面全被拉黑了 一定抢不到了.. 不并发那么多奖品怎么整 调延迟的话会被别人抢光.. 还有被拉黑后钓鱼也会挂 手机点开也会加载不了 所以又不能钓鱼一起整..
我也是,1鱼翅这两天一般能抢3个左右,其他的能兑换完,刚刚8点钟时提前3秒启动兑换跑到30时候手动关,结果一个没抢到......
今天好像斗鱼把奖励缩减了 刚刚我0.1的换了2鱼翅就兑换完毕了
上午我还在抢1鱼翅,中午直接显示兑换完毕,我查了下就兑换3个1鱼翅,还是凌晨兑换的,真恶心啊,意思明天所有鱼翅要减一半
得明天在看看 本来一天能换15鱼翅 现在不知道剩多少了 狗鱼
那正常.. 手动兑换卡点开几秒就好了 提前开也会噶 😂完完全全暴力请求 啥都不检测的😂 一瞬间就请求频繁了
我在搞新脚本那个 打算整合钓鱼兑换一起 通过检测兑换状态才发送兑换, 尽量避免被拉黑.. 可是.. 太慢了 这两天1鱼翅都没兑完 其他都兑完了我也是1鱼翅没换完 是因为被拉黑了吗
拉黑通常就几分钟, 你兑换之前F12->网络 兑换完后 看一下 convertOpt 的请求 Response应该会显示请求频繁😂 , 我还没摸到限制的频率 苦思冥想这么多要兑换的 要怎么整.. 一次性并发的话 就前面几条请求通过了 后面全被拉黑了 一定抢不到了.. 不并发那么多奖品怎么整 调延迟的话会被别人抢光.. 还有被拉黑后钓鱼也会挂 手机点开也会加载不了 所以又不能钓鱼一起整..
我也是,1鱼翅这两天一般能抢3个左右,其他的能兑换完,刚刚8点钟时提前3秒启动兑换跑到30时候手动关,结果一个没抢到......
今天好像斗鱼把奖励缩减了 刚刚我0.1的换了2鱼翅就兑换完毕了
上午我还在抢1鱼翅,中午直接显示兑换完毕,我查了下就兑换3个1鱼翅,还是凌晨兑换的,真恶心啊,意思明天所有鱼翅要减一半
得明天在看看 本来一天能换15鱼翅 现在不知道剩多少了 狗鱼
而且现在更容易报403 Forbidden,之前快跑完手动关一般不会报
现在一天只给换8鱼翅了,1鱼翅3个,0.5鱼翅6个,0.1鱼翅20个,合起来8个,感觉不太需要太多房间挂了,一天只需要挂两个房间就足够了,挂钓鱼的收益不算兑换的话和消耗鱼饵对比也是亏本的,没必要挂太多了
是不是因为今天下午,兑换页面变了,导致兑换编码变了。我心动卡连着好几次兑换1个都换不到,反而1鱼翅我都换完了。。。
还有多房间钓鱼的那个脚本,为啥我窗口打开,没看到UI,也没看到油猴显示脚本被加载0.0
现在一天只给换8鱼翅了,1鱼翅3个,0.5鱼翅6个,0.1鱼翅20个,合起来8个,感觉不太需要太多房间挂了,一天只需要挂两个房间就足够了,挂钓鱼的收益不算兑换的话和消耗鱼饵对比也是亏本的,没必要挂太多了
还有鱼翅碎片也对等鱼翅,就是不知道现在给兑多少个了,估计和0.1和0.5的一样多
是不是因为今天下午,兑换页面变了,导致兑换编码变了。我心动卡连着好几次兑换1个都换不到,反而1鱼翅我都换完了。。。
还有多房间钓鱼的那个脚本,为啥我窗口打开,没看到UI,也没看到油猴显示脚本被加载0.0
诶 你要修改脚本一开始那个
@MATCH 为你希望启动脚本的网址, 我怕使用通配符 只要打开任何钓鱼界面就加载的话 只要不小心多开几个标签 应该百分百被拉黑 拒绝请求 哈哈哈哈
现在一天只给换8鱼翅了,1鱼翅3个,0.5鱼翅6个,0.1鱼翅20个,合起来8个,感觉不太需要太多房间挂了,一天只需要挂两个房间就足够了,挂钓鱼的收益不算兑换的话和消耗鱼饵对比也是亏本的,没必要挂太多了
还有鱼翅碎片也对等鱼翅,就是不知道现在给兑多少个了,估计和0.1和0.5的一样多
根据今天的兑换记录来看 心动卡 没兑成功(昨天是2张没错) 2 碎片现在还是5个了 5 碎片暂时还没兑换成功.. 之前是4个 鱼翅 0.1 20个 (本来50个) 鱼翅 0.5 6个 (本来10个) 鱼翅 1 (本来5个) 我还没兑成功 根据其他人反馈是 3个
至于为什么那么晚还没兑换成功.. 不知道是优化后的代码问题 还是被针对了 我查看网络请求 准点00秒发送的查询请求 全部显示状态为刷新中.. 只能10点再看情况了
"data": {
"time": 1736942400, //这个时间戳代表着 晚上8点整
"num": 151019,
"index": "PROP_3",
"awardInfo": {
"awardType": 10003,
"awardId": 2562,
"awardNum": 1,
"awardNum2": 0,
"awardName": "心动卡",
"awardPic": "https://gfs-op.douyucdn.cn/dygift/2019/01/23/d0f8e3bb5420eb11c66d301a3a2fa610.gif",
"awardSort": 1,
"awardNumShow": 1.0
},
"type": 1,
"badgeLimit": 6,
"consumeNum": 1500,
"giftId": 0,
"giftNum": 0,
"giftPrice": 0.0,
"status": 3, //3为刷新中
"circle": 1,
现在一天只给换8鱼翅了,1鱼翅3个,0.5鱼翅6个,0.1鱼翅20个,合起来8个,感觉不太需要太多房间挂了,一天只需要挂两个房间就足够了,挂钓鱼的收益不算兑换的话和消耗鱼饵对比也是亏本的,没必要挂太多了
还有鱼翅碎片也对等鱼翅,就是不知道现在给兑多少个了,估计和0.1和0.5的一样多
根据今天的兑换记录来看
心动卡 没兑成功(昨天是2张没错)
2 碎片现在还是5个了
5 碎片暂时还没兑换成功.. 之前是4个
鱼翅 0.1 20个 (本来50个)
鱼翅 0.5 6个 (本来10个)
鱼翅 1 (本来5个) 我还没兑成功 根据其他人反馈是 3个至于为什么那么晚还没兑换成功.. 不知道是优化后的代码问题 还是被针对了 我查看网络请求 准点00秒发送的查询请求 全部显示状态为刷新中.. 只能10点再看情况了
"data": { "time": 1736942400, //这个时间戳代表着 晚上8点整 "num": 151019, "index": "PROP_3", "awardInfo": { "awardType": 10003, "awardId": 2562, "awardNum": 1, "awardNum2": 0, "awardName": "心动卡", "awardPic": "https://gfs-op.douyucdn.cn/dygift/2019/01/23/d0f8e3bb5420eb11c66d301a3a2fa610.gif", "awardSort": 1, "awardNumShow": 1.0 }, "type": 1, "badgeLimit": 6, "consumeNum": 1500, "giftId": 0, "giftNum": 0, "giftPrice": 0.0, "status": 3, //3为刷新中 "circle": 1,
心动卡和碎片没变,只是减少鱼翅兑换数量了,至于你整点兑换不成功,是不是你时间取的本地时间,不是斗鱼的时间,从倒计时看,斗鱼的时间是比百度上的时间和本地网络同步的时间要快1秒+的
是不是因为今天下午,兑换页面变了,导致兑换编码变了。我心动卡连着好几次兑换1个都换不到,反而1鱼翅我都换完了。。。
还有多房间钓鱼的那个脚本,为啥我窗口打开,没看到UI,也没看到油猴显示脚本被加载0.0诶 你要修改脚本一开始那个
@MATCH 为你希望启动脚本的网址, 我怕使用通配符 只要打开任何钓鱼界面就加载的话 只要不小心多开几个标签 应该百分百被拉黑 拒绝请求 哈哈哈哈
我看了一下= =,要这么写才能应用。
https://www.douyu.com/pages/fish-act/mine?rid=房间号&isAnchorSide=0
后边这个&isAnchorSide=0 不加,我这边脚本怎么都运行不起来emmm
然后那个物品获取统计要怎么调出来0.0,我只看到有个显示记录,显示的以往钓鱼获得的东西。
我这边今天还差一个心动卡没兑换完,其他都兑换完了:
心动卡 还是2个,但是很难抢,我今天就抢到1个
0.1鱼翅 只有20个(之前是50个)
0.5鱼翅 只有6个 (之前10个)
1鱼翅 只有3个 (之前5个)
2鱼翅碎片 只有5个 (还是5个)
5鱼翅碎片 只有4个 (还是4个)
——————————
初级水手 10个
精英水手 5个(咋感觉之前这个是10个来着?)
然后这个多选兑换,貌似时间有些问题= =,我Linux虚拟机挂着自动兑换T T,2晚上没换到东西,手动都正常
然后这个多选兑换,貌似时间有些问题= =,我Linux虚拟机挂着自动兑换T T,2晚上没换到东西,手动都正常
可能你浏览器记忆了这个URL 每次输入自动填充, 因为我手动打是仅房间号, 你可以加 &... 也可以直接房间号后加一个 * 通配符 哈哈哈 因为match 不加通配符要完全符合URL才能运行
士官我测试的时候是5个 哈哈 10个水手=1鱼翅 5个士官=5鱼翅
linux虚拟机的话 是不是会自动锁屏之类的.. 离开虚拟机也不知道触发浏览器勤俭持家的机制.. 就像我之前说的那些 需要置顶 不可最小化钓鱼 不可最大化其他窗口 不能切别的标签页 要单独一个窗口 之类的一堆限制 , 不然浏览器就会开始勤俭持家 60秒才检测一次 你自动兑换的是1秒检测一次 如果浏览器在勤俭持家的时候不是00秒 例如是30秒 那就是每分钟30秒检测一次 永远不会触发自动兑换 哈哈哈哈哈
你试试在F12 控制台运行这个代码 然后按你平时的挂机方法 过半分钟 几分钟 打开虚拟机看看就知道结果了
let lastUpdateTime = null;
function timerFunction() {
const startTime = performance.now();
console.log(`定时器触发,当前时间: ${new Date().toISOString()}`);
if (lastUpdateTime!== null) {
const interval = startTime - lastUpdateTime;
if (interval > 1500) {
console.error(`定时器间隔超过 1500 毫秒,实际间隔为 ${interval} 毫秒`);
}
}
lastUpdateTime = startTime;
}
setInterval(timerFunction, 1000);
然后这个多选兑换,貌似时间有些问题= =,我Linux虚拟机挂着自动兑换T T,2晚上没换到东西,手动都正常
可能你浏览器记忆了这个URL 每次输入自动填充, 因为我手动打是仅房间号, 你可以加 &... 也可以直接房间号后加一个 * 通配符 哈哈哈 因为match 不加通配符要完全符合URL才能运行
士官我测试的时候是5个 哈哈 10个水手=1鱼翅 5个士官=5鱼翅
linux虚拟机的话 是不是会自动锁屏之类的.. 离开虚拟机也不知道触发浏览器勤俭持家的机制.. 就像我之前说的那些 需要置顶 不可最小化钓鱼 不可最大化其他窗口 不能切别的标签页 要单独一个窗口 之类的一堆限制 , 不然浏览器就会开始勤俭持家 60秒才检测一次
你自动兑换的是1秒检测一次 如果浏览器在勤俭持家的时候不是00秒 例如是30秒 那就是每分钟30秒检测一次 永远不会触发自动兑换 哈哈哈哈哈你试试在F12 控制台运行这个代码 然后按你平时的挂机方法 过半分钟 几分钟 打开虚拟机看看就知道结果了
let lastUpdateTime = null; function timerFunction() { const startTime = performance.now(); console.log(`定时器触发,当前时间: ${new Date().toISOString()}`); if (lastUpdateTime!== null) { const interval = startTime - lastUpdateTime; if (interval > 1500) { console.error(`定时器间隔超过 1500 毫秒,实际间隔为 ${interval} 毫秒`); } } lastUpdateTime = startTime; } setInterval(timerFunction, 1000);
emmmm,那个统计奖励的脚本能更新一下不,想看看钓鱼一直弄划算,还是就12点开始每个小时钓半个钟划算 T T,因为感觉有的时候运气特别好,光一天钓鱼回本了10多鱼翅
emm 我自动兑换剩下心动卡和1鱼翅 还没测试完 你改改房间号用着吧
目前版本测试兑换工具中
// ==UserScript==
// @name 斗鱼多房间钓鱼脚本+兑换工具v2.1
// @namespace https://github.com/your_username
// @version 1.3
// @description 多房间自动钓鱼脚本,支持并发钓鱼与数据记录
// @match https://www.douyu.com/pages/fish-act/mine?rid=11410656
// @grant GM_xmlhttpRequest
// @grant GM_setValue
// @grant GM_getValue
// @grant GM_addStyle
// ==/UserScript==
// 配置项
const CONFIG = {
CHECK_INTERVAL: 1000,//检查间隔
RETRY_DELAY: 1500,//重试间隔
MIN_OPERATION_INTERVAL:500,//队列间隔
STORAGE_KEYS: {
AUTO_FISH: 'ExSave_AutoFish',
EVENTAUTO_FISH: 'ExSave_EventAutoFish',
FISH_RECORDS: 'ExSave_FishRecords',
ERROR_LOGS: 'ExSave_ErrorLogs'
},
MAX_RECORDS: 1000, // 每个房间最多保存的记录数
MAX_ERRORS: 100, // 每个房间最多保存的错误数
API_ENDPOINTS: {
FISH_INFO: 'https://www.douyu.com/japi/revenuenc/web/actfans/achieve/accList',
HOMEPAGE: 'https://www.douyu.com/japi/revenuenc/web/actfans/fishing/homePage',
START_FISH: 'https://www.douyu.com/japi/revenuenc/web/actfans/fishing/fishing',
END_FISH: 'https://www.douyu.com/japi/revenuenc/web/actfans/fishing/reelIn',
FISH_SHOP: 'https://www.douyu.com/japi/revenuenc/web/actfans/convert/convertOpt'
}
};
// 房间配置
const rids = [8514651, 11805787, 11410656, 6282878, 5045057, 10313462, 10767547];
// 固定一个粉丝牌等级大于3的直播间 用于兑换功能
const erid = 11410656;
const roomStates = {};
// 初始化房间状态
rids.forEach(rid => {
roomStates[rid] = {
baitId: null,
nextFishEndTime: 0,
isFishing: false,
timer: null,
lock:false
};
});
// 工具函数
let lastUpdateTime = null;
const utils = {
sleep: (time) => new Promise((resolve) => setTimeout(resolve, time)),
setCookie: (name, value) => {
const exp = new Date();
exp.setTime(exp.getTime() + 3 * 60 * 60 * 1000);
document.cookie = `${name}=${escape(value)}; path=/; expires=${exp.toGMTString()}`;
},
getCookie: (name) => {
const reg = new RegExp(`(^| )${name}=([^;]*)(;|$)`);
const arr = document.cookie.match(reg);
return arr ? unescape(arr[2]) : null;
},
getCCN: () => {
let ccn = utils.getCookie("acf_ccn");
//console.log(`获取acf_ccn 数值为: ${ccn}`);
if (!ccn) {
utils.setCookie("acf_ccn", "1");
ccn = "1";
//console.log(`设置acf_ccn 数值为: ${ccn}`);
}
return ccn;
},
isActivityTime:() => {
let now = new Date();
let hour = now.getHours();
let minute = now.getMinutes();
// 判断是否在活动时间范围内(中午 12:00 到凌晨 00:30 每个准点开启半小时)
return (hour >= 12 && hour < 24 && minute < 30) || (hour === 0 && minute < 30);
},
updateTime:() =>{
// const timePanel = document.getElementById('time-panel');
// const now = new Date();
// timePanel.innerText = "最后检查时间: " + now.toLocaleString();
const timePanel = document.getElementById('time-panel');
const now = new Date();
const currentTime = now.getTime();
// 如果上次更新时间存在
if (lastUpdateTime!== null) {
// 计算时间差(毫秒)
const timeDiff = currentTime - lastUpdateTime;
// 如果时间差大于1秒
if (timeDiff > 1500) {
// 保存当前时间
const errorLogDiv = document.getElementById('error-log');
errorLogDiv.innerText += `Error: 时间间隔大于1秒${timeDiff},上次更新时间:${new Date(lastUpdateTime).toLocaleString()}\n`;
// // 延迟1秒后更新时间(可根据需要调整延迟时间)
// setTimeout(() => {
timePanel.innerText = "最后检查时间: " + now.toLocaleString();
lastUpdateTime = now.getTime();
// }, 1000);
} else {
// 正常更新时间
timePanel.innerText = "最后检查时间: " + now.toLocaleString();
lastUpdateTime = now.getTime();
}
} else {
// 首次更新时间
timePanel.innerText = "最后检查时间: " + now.toLocaleString();
lastUpdateTime = now.getTime();
}
}
};
// 1. 首先添加一个请求队列管理器类
class RequestQueueManager {
constructor() {
this.queue = [];
this.isProcessing = false;
}
// 添加请求到队列
async addRequest(requestFn, priority = 0) {
return new Promise((resolve, reject) => {
this.queue.push({
requestFn,
priority,
resolve,
reject,
timestamp: Date.now()
});
// 按优先级和时间戳排序
this.queue.sort((a, b) => {
if (a.priority !== b.priority) {
return b.priority - a.priority;
}
return a.timestamp - b.timestamp;
});
this.processQueue();
});
}
// 处理队列
async processQueue() {
if (this.isProcessing || this.queue.length === 0) return;
this.isProcessing = true;
try {
const request = this.queue.shift();
const result = await request.requestFn();
request.resolve(result);
} catch (error) {
const request = this.queue[0];
request.reject(error);
} finally {
this.isProcessing = false;
// 添加延迟以控制请求频率
await utils.sleep(CONFIG.MIN_OPERATION_INTERVAL);
// 继续处理队列中的下一个请求
if (this.queue.length > 0) {
this.processQueue();
}
}
}
// 清空队列
clearQueue() {
this.queue = [];
}
// 获取队列长度
get length() {
return this.queue.length;
}
}
class ApiManager {
constructor(queueManager) {
this.queueManager = queueManager;
}
async request(url, options = {}, priority = 0) {
return this.queueManager.addRequest(async () => {
try {
const defaultOptions = {
mode: "no-cors",
cache: "default",
credentials: "include",
headers: {
"Content-Type": "application/x-www-form-urlencoded",
}
};
const response = await fetch(url, {
...defaultOptions,
...options,
headers: {
...defaultOptions.headers,
...options.headers
}
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return await response.json();
} catch (error) {
console.error(`API请求失败: ${url}`, error);
return { error: -1, msg: `请求失败: ${error.message}` };
}
}, priority);
}
async getFishInfo(rid) {
return this.request(
`${CONFIG.API_ENDPOINTS.FISH_INFO}?rid=${rid}&type=1&period=1`,
{},
1
).then(response => response.data?.accList || []);
}
async getHomepageData(rid) {
return this.request(
`${CONFIG.API_ENDPOINTS.HOMEPAGE}?rid=${rid}&opt=1`,
{},
1
);
}
async startFish(rid, baitId) {
return this.request(
CONFIG.API_ENDPOINTS.START_FISH,
{
method: "POST",
body: `ctn=${utils.getCCN()}&rid=${rid}&baitId=${baitId}`
},
2
);
}
async endFish(rid) {
return this.request(
CONFIG.API_ENDPOINTS.END_FISH,
{
method: "POST",
body: `ctn=${utils.getCCN()}&rid=${rid}`
},
2
);
}
async startExchange(rid,selectedIndexValue) {
return this.request(
CONFIG.API_ENDPOINTS.FISH_SHOP,
{
method: "POST",
body: `ctn=${utils.getCCN()}&rid=${rid}&index=${selectedIndexValue}`
},
2
);
}
}
// 数据存储管理
class StorageManager {
constructor() {
this.initializeStorage();
}
initializeStorage() {
if (!localStorage.getItem(CONFIG.STORAGE_KEYS.FISH_RECORDS)) {
localStorage.setItem(CONFIG.STORAGE_KEYS.FISH_RECORDS, JSON.stringify({}));
}
if (!localStorage.getItem(CONFIG.STORAGE_KEYS.ERROR_LOGS)) {
localStorage.setItem(CONFIG.STORAGE_KEYS.ERROR_LOGS, JSON.stringify({}));
}
}
saveFishRecord(rid, record) {
const records = this.getFishRecords();
if (!records[rid]) records[rid] = [];
records[rid].unshift({
...record,
timestamp: new Date().toISOString()
});
// 限制记录数量
if (records[rid].length > CONFIG.MAX_RECORDS) {
records[rid] = records[rid].slice(0, CONFIG.MAX_RECORDS);
}
localStorage.setItem(CONFIG.STORAGE_KEYS.FISH_RECORDS, JSON.stringify(records));
}
saveErrorLog(rid, error) {
const errors = this.getErrorLogs();
if (!errors[rid]) errors[rid] = [];
errors[rid].unshift({
error,
timestamp: new Date().toISOString()
});
// 限制错误日志数量
if (errors[rid].length > CONFIG.MAX_ERRORS) {
errors[rid] = errors[rid].slice(0, CONFIG.MAX_ERRORS);
}
localStorage.setItem(CONFIG.STORAGE_KEYS.ERROR_LOGS, JSON.stringify(errors));
}
getFishRecords() {
return JSON.parse(localStorage.getItem(CONFIG.STORAGE_KEYS.FISH_RECORDS) || '{}');
}
getErrorLogs() {
return JSON.parse(localStorage.getItem(CONFIG.STORAGE_KEYS.ERROR_LOGS) || '{}');
}
clearRecords(rid) {
const records = this.getFishRecords();
if (rid) {
delete records[rid];
} else {
Object.keys(records).forEach(key => delete records[key]);
}
localStorage.setItem(CONFIG.STORAGE_KEYS.FISH_RECORDS, JSON.stringify(records));
}
clearErrors(rid) {
const errors = this.getErrorLogs();
if (rid) {
delete errors[rid];
} else {
Object.keys(errors).forEach(key => delete errors[key]);
}
localStorage.setItem(CONFIG.STORAGE_KEYS.ERROR_LOGS, JSON.stringify(errors));
}
}
class FishingManager {
constructor(storageManager, apiManager) {
this.fishInfo = [];
this.storageManager = storageManager;
this.apiManager = apiManager;
}
async init() {
try {
// 获取鱼类信息
this.fishInfo = await this.apiManager.getFishInfo(rids[0]);
// 检查初始状态
return this.checkInitialState();
} catch (error) {
console.error('初始化失败:', error);
this.storageManager.saveErrorLog('system', '初始化失败: ' + error.message);
return false;
}
}
async checkInitialState() {
try { //参考小淳大佬
for (const rid of rids) {
const homepageRes = await this.apiManager.getHomepageData(rid);
if (!homepageRes.data) {
console.error(`【房间 ${rid}】未能获取活动信息`);
this.storageManager.saveErrorLog(rid, '未能获取活动信息');
return false;
}
// 检查鱼饵
const baitData = homepageRes.data.baits.find(item => item.inUse);
if (!baitData) {
console.error(`【房间 ${rid}】请设置鱼饵`);
this.storageManager.saveErrorLog(rid, '请设置鱼饵');
return false;
}
roomStates[rid].baitId = baitData.id;
// 检查形象
if (!homepageRes.data.myCh) {
console.error(`【房间 ${rid}】请设置形象`);
this.storageManager.saveErrorLog(rid, '请设置形象');
return false;
}
// 检查钓鱼状态
await this.handleFishingState(rid, homepageRes.data.fishing);
}
return true;
} catch (error) {
console.error('检查初始状态失败:', error);
this.storageManager.saveErrorLog('system', '检查初始状态失败: ' + error.message);
return false;
}
}
async handleFishingState(rid, fishingData) {
const state = roomStates[rid];
switch(fishingData.stat) {
case 0: // 未开始
state.isFishing = false;
state.nextFishEndTime = 0;
break;
case 1: // 进行中
state.isFishing = true;
state.nextFishEndTime = fishingData.fishEtMs;
break;
case 2: // 未收杆
await this.endFishing(rid);
await utils.sleep(CONFIG.RETRY_DELAY);
break;
}
}
async startFishing(rid) {
const eventAutoFish = JSON.parse(localStorage.getItem(CONFIG.STORAGE_KEYS.EVENTAUTO_FISH))?.isAutoFish;
if (eventAutoFish) {
if (!utils.isActivityTime()) {
//console.log("不在活动时间内,不执行钓鱼操作");
return false;
}
}
try {
const state = roomStates[rid];
state.lock = true;
const fishRes = await this.apiManager.startFish(rid, state.baitId);
state.lock = false;
if (fishRes.error !== 0) {
const errorMsg = `【startFishing 函数:房间 ${rid}】${fishRes.msg}`;
console.error(errorMsg);
this.storageManager.saveErrorLog(rid, errorMsg);
//1001007 操作失败刷新重试
// if (fishRes.error === 1001007) await this.endFishing(rid);
// if (fishRes.error === 1001007) {
// setTimeout(async () => {
// await this.endFishing(rid);
// }, 1000);//1秒后重试
// }; 不需要重试 定时器会自动重试
//小淳大佬的错误代码 还不知道啥玩意 可能是没鱼饵了?
if (fishRes.error === 1005003) this.stopFishing(rid);
return false;
}
state.isFishing = true;
state.nextFishEndTime = fishRes.data.fishing.fishEtMs;
return true;
} catch (error) {
console.error(`【startFishing 函数:房间 ${rid}】开始钓鱼失败:`, error);
this.storageManager.saveErrorLog(rid, '开始钓鱼失败: ' + error.message);
return false;
}
}
async endFishing(rid) {
try {
const state = roomStates[rid];
state.lock = true;
const fishRes = await this.apiManager.endFish(rid);
state.lock = false;
if (fishRes.error !== 0) {
const errorMsg = `endFishing 函数1:房间 ${rid} 收杆失败: ${fishRes.msg || JSON.stringify(fishRes)}`;
console.error(errorMsg);
this.storageManager.saveErrorLog(rid, errorMsg);
const homepageRes = await this.apiManager.getHomepageData(rid);
if (homepageRes.data?.fishing.stat === 0) {
state.isFishing = false;
state.nextFishEndTime = 0;
}
return;
}
this.logFishingResult(rid, fishRes);
state.isFishing = false;
} catch (error) {
console.error(`【endFishing 函数2:房间 ${rid}】收杆失败:`, error);
this.storageManager.saveErrorLog(rid, '收杆失败: ' + error.message);
}
}
logFishingResult(rid, fishRes) {
try {
const record = {
fishId: fishRes.data.fish.id,
weight: fishRes.data.fish.wei,
awards: fishRes.data.awards || []
};
const fishData = this.fishInfo.find(item => item.fishId === record.fishId);
if (fishData) {
record.fishName = fishData.name;
}
// 保存记录到存储
this.storageManager.saveFishRecord(rid, record);
// 控制台输出
const messages = [`【房间 ${rid} 钓鱼】`];
if (fishData) {
messages.push(`获得${fishData.name}${record.weight}斤`);
}
if (record.awards.length > 0) {
const awards = record.awards.map(
award => `获得${award.awardName}x${award.awardNumShow}`
);
messages.push(...awards);
}
if (messages.length > 1) {
console.log(messages.join(fishData ? "," : ""));
}
} catch (error) {
console.error(`【房间 ${rid}】记录结果失败:`, error);
this.storageManager.saveErrorLog(rid, '记录结果失败: ' + error.message);
}
}
startAutoFishing(rid) {
const state = roomStates[rid];
state.timer = setInterval(async () => {
utils.updateTime();
try {
if(!state.lock)
{
if (state.isFishing) {
const now = new Date().getTime();
if (now <= state.nextFishEndTime) return;
// console.log(`${rid} 476开始收杆`);
await this.endFishing(rid);
} else {
// console.log(`${rid} 479开始抛竿`);
await this.startFishing(rid);
}
}
} catch (error) {
console.error(`【startautoFishing 函数:房间 ${rid}】自动钓鱼异常:`, error);
this.storageManager.saveErrorLog(rid, '自动钓鱼异常: ' + error.message);
}
}, CONFIG.CHECK_INTERVAL);
}
stopFishing(rid) {
if (roomStates[rid].timer) {
clearInterval(roomStates[rid].timer);
roomStates[rid].timer = null;
}
}
stopAllFishing() {
rids.forEach(rid => this.stopFishing(rid));
}
}
//鱼塘商城兑换
class ExchangeManager {
constructor(apiManager) {
this.apiManager = apiManager;
this.isRunning = false;
this.autoExchangeEnabled = false;
this.autoExchangeCount = 0;
this.selectedIndexValues = [];
this.exchangeInterval = null;
this.concurrentRequests = 0;
this.maxConcurrentRequests = 5; // 最大并发请求数
this.excludedIndexes = new Set(); // 用于存储需要排除的 index
this.pauseAllRequests = false; // 用于暂停所有请求的标志
}
async checkExchangeStatus(rid) {
return new Promise((resolve, reject) => {
fetch(`https://www.douyu.com/japi/revenuenc/web/actfans/convert/convertPanel?rid=${rid}`, {
"headers": {
"accept": "application/json, text/plain, */*",
"accept-language": "en-US,en;q=0.9,zh-CN;q=0.8,zh;q=0.7",
"priority": "u=1, i",
"sec-ch-ua": "\"Google Chrome\";v=\"131\", \"Chromium\";v=\"131\", \"Not_A Brand\";v=\"24\"",
"sec-ch-ua-mobile": "?0",
"sec-ch-ua-platform": "\"Windows\"",
"sec-fetch-dest": "empty",
"sec-fetch-mode": "cors",
"sec-fetch-site": "same-origin"
},
"referrer": "https://www.douyu.com/pages/fish-act/shop",
"referrerPolicy": "strict-origin-when-cross-origin",
"body": null,
"method": "GET",
"mode": "cors",
"credentials": "include"
})
.then(response => response.json())
.then(data => {
if (!Array.isArray(data.data.convertList)) {
console.error('convertList 不是一个数组', data);
reject(new Error('convertList 数据格式错误'));
} else {
resolve(data.data.convertList);
}
})
.catch(error => {
console.error('获取兑换状态失败:', error);
reject(error);
});
});
}
async directExchange(selectedIndexValue, rid) {
try {
await new Promise(resolve => setTimeout(resolve, 100));
const response = await fetch(CONFIG.API_ENDPOINTS.FISH_SHOP, {
method: "POST",
headers: {
"Content-Type": "application/x-www-form-urlencoded"
},
body: `ctn=${utils.getCCN()}&rid=${rid}&index=${selectedIndexValue}`,
credentials: "include"
});
const data = await response.json();
if (data.error === -1) {
console.log('操作频繁 等待500毫秒');
await new Promise(resolve => setTimeout(resolve, 500));
}
} catch (error) {
console.error('兑换请求失败:', error);
}
}
async runExchangeLoop(rid, updateUI) {
let count = 1;
const targetCount = 50;
while (this.isRunning && count <= targetCount) {
const promises = [];
// 并发发送所有选中商品的兑换请求
for (const selectedIndexValue of this.selectedIndexValues) {
for (let i = 0; i < 1; i++) { // 每个商品发送1次请求 //本来是3 修改了不移除for备用
promises.push(this.directExchange(selectedIndexValue, rid));
}
}
// 等待所有请求完成
await Promise.all(promises);
console.log(`当前兑换循环次数: ${count}`);
count++;
updateUI(this.isRunning, targetCount - count);
if (count >= targetCount) {
this.isRunning = false;
updateUI(this.isRunning, 0);
break;
}
// 短暂延迟避免请求过于密集
await new Promise(resolve => setTimeout(resolve, 20));
}
}
startAutoExchange(rid) {
if (this.exchangeInterval) return;
this.exchangeInterval = setInterval(async () => {
const currentTime = this.getCurrentTime();
if (this.isInTimeRange() && this.autoExchangeEnabled) {
this.excludedIndexes.clear(); // 重置排除的 index 集合
const loopTimesMap = {
"PROP_1": 10,
"PROP_2": 5,
"PROP_3": 2,
"FREE_PROP_1": 10,
"FREE_PROP_2": 10,
"FREE_PROP_3": 10,
"YC_TY_1": 50,
"YC_TY_2": 10,
"YC_TY_3": 5,
"YW_1": 10,
"YW_2": 10,
"YW_3": 10,
"YC_CHIP_1": 5,
"YC_CHIP_2": 4
};
console.log(`检查兑换状态 目前时间 ${currentTime.minutes}:${currentTime.seconds}`);
const convertList = await this.checkExchangeStatus(rid);
// const promises = [];
const statusMap = {
0: '积分不足',
1: '可兑换',
2: '无兑换次数',
3: '刷新中'
};
for (const item of convertList) {
if (item.status!== 1) {
this.excludedIndexes.add(item.index);
}
}
for (const selectedIndexValue of this.selectedIndexValues) {
console.log(`目前时间 ${currentTime.minutes}:${currentTime.seconds} 检查物品状态 ${selectedIndexValue} `);
const itemStatus = convertList.find(item => item.index === selectedIndexValue)?.status;
if (itemStatus === 1) {
const loopTimes = loopTimesMap[selectedIndexValue] || 1;
for (let i = 0; i < loopTimes; i++) {
console.log(`目前时间 ${currentTime.minutes}:${currentTime.seconds} 发送兑换请求(${selectedIndexValue}) `);
//await
this.executeWithDelayAndLimit(selectedIndexValue, rid);
}
}else{
const statusDescription = statusMap[itemStatus] || '未知状态';
console.log(`目前时间 ${currentTime.minutes}:${currentTime.seconds} 物品:${selectedIndexValue},状态为${statusDescription}`);
}
}
// console.log(`${currentTime.minutes}:${currentTime.seconds} 准备发送兑换请求`);
} else if (this.autoExchangeEnabled) {
this.autoExchangeCount++;
if (this.autoExchangeCount % 60 === 0) {
console.log(`自动兑换运行中 ${this.autoExchangeCount / 60}分钟 当前时间 ${currentTime.minutes}:${currentTime.seconds}`);
}
}
}, 1000);
}
executeWithDelayAndLimit(selectedIndexValue, rid) {
// setTimeout(() => {
if (this.excludedIndexes.has(selectedIndexValue)) {
console.log(`${selectedIndexValue} 已被标记为库存不足或不可兑换,不再发送请求`);
return;
}
if (this.pauseAllRequests) {
setTimeout(() => {
this.executeWithDelayAndLimit(selectedIndexValue, rid);
}, 50);
return;
}
if (this.concurrentRequests >= this.maxConcurrentRequests) {
setTimeout(() => {
this.executeWithDelayAndLimit(selectedIndexValue, rid);
}, 100); //如果达到最大并发数 等待100毫秒再重试发送
} else {
this.concurrentRequests++;
fetch(CONFIG.API_ENDPOINTS.FISH_SHOP, {
method: "POST",
headers: {
"Content-Type": "application/x-www-form-urlencoded"
},
body: `ctn=${utils.getCCN()}&rid=${rid}&index=${selectedIndexValue}`,
credentials: "include"
})
.then(response => response.json())
.then(data => {
if (data.error === -1) {
console.log('操作频繁 等待500毫秒');
console.log(`${selectedIndexValue} 兑换失败`)
this.pauseAllRequests = true; // 标记暂停所有请求
setTimeout(() => {
this.concurrentRequests--; //等待300毫秒再移除
this.pauseAllRequests = false; // 300 毫秒后恢复请求
//this.executeWithDelayAndLimit(selectedIndexValue, rid);
}, 300);
} else if(data.error === 30002){
this.concurrentRequests--;
console.log(`${selectedIndexValue} 奖品库存不足`);
this.excludedIndexes.add(selectedIndexValue); // 标记该 index 为排除状态
}
else{
this.concurrentRequests--;
console.log(`${selectedIndexValue} 兑换成功`);
}
})
.catch(error => {
console.error('兑换请求失败:', error);
this.concurrentRequests--;
});
}
// }, 100);
}
stopAutoExchange() {
if (this.exchangeInterval) {
clearInterval(this.exchangeInterval);
this.exchangeInterval = null;
}
}
getCurrentTime() {
const now = new Date();
return {
minutes: now.getMinutes(),
seconds: now.getSeconds()
};
}
isInTimeRange() { //提前几秒开始检测兑换状态 免得误差
const currentTime = this.getCurrentTime();
return (currentTime.minutes === 59 && currentTime.seconds >= 57) ||
(currentTime.minutes === 0 && currentTime.seconds <= 2);
}
setSelectedValues(values) {
this.selectedIndexValues = values;
}
toggleRunning() {
this.isRunning = !this.isRunning;
return this.isRunning;
}
toggleAutoExchange() {
this.autoExchangeEnabled = !this.autoExchangeEnabled;
return this.autoExchangeEnabled;
}
}
// UI管理器增强
class UIManager {
constructor(fishingManager, storageManager,apiManager) {
this.fishingManager = fishingManager;
this.storageManager = storageManager;
this.exchangeManager = new ExchangeManager(apiManager);
this.createUI();
this.createExchangeOverlay();
this.loadSavedState();
}
createExchangeOverlay() {
const overlay = document.createElement('div');
overlay.id = 'exchange-overlay';
GM_addStyle(`
#exchange-overlay {
position: fixed;
top: 26%;
left: 50%;
transform: translate(-50%, -50%);
background-color: rgba(255, 255, 255, 0.9);
padding: 20px;
border-radius: 10px;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.2);
display: none;
z-index: 9999;
cursor: move; /* 添加移动光标 */
}
.exchange-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 15px;
padding-bottom: 10px;
border-bottom: 1px solid #eee;
}
.exchange-title {
font-weight: bold;
font-size: 16px;
margin: 0;
}
.exchange-close {
background: none;
border: none;
font-size: 18px;
cursor: pointer;
color: #666;
padding: 5px;
}
.exchange-close:hover {
color: #333;
}
.exchange-button {
background-color: red;
color: white;
margin: 5px;
padding: 10px 20px;
font-size: 16px;
cursor: pointer;
border: none;
border-radius: 5px;
}
.exchange-select {
margin: 5px;
padding: 10px;
font-size: 16px;
border: 1px solid #ccc;
border-radius: 5px;
height: auto;
overflow-y: auto;
width: 300px;
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
}
`);
// 添加标题栏和关闭按钮
const header = document.createElement('div');
header.className = 'exchange-header';
header.innerHTML = `
<h3 class="exchange-title">商城兑换</h3>
<button class="exchange-close">×</button>
`;
const controlExchangeButton = document.createElement('button');
controlExchangeButton.textContent = '启动兑换 ✔';
controlExchangeButton.className = 'exchange-button controlExchange';
const autoExchangeButton = document.createElement('button');
autoExchangeButton.textContent = '自动兑换 ✖';
autoExchangeButton.className = 'exchange-button autoExchange';
const selectMenu = document.createElement('select');
selectMenu.className = 'exchange-select';
selectMenu.setAttribute('multiple', 'multiple');
const items = [
{ value: 'hahahahha', text: '请选择需要兑换的奖品' },
{ value: 'PROP_1', text: '3 级粉丝牌 初级水手 上限10' },
{ value: 'PROP_2', text: '3 级粉丝牌 精英士官 上限5' },
{ value: 'PROP_3', text: '6 级粉丝牌 心动卡 上限2' },
{ value: 'FREE_PROP_1', text: '10 陪伴印章' },
{ value: 'FREE_PROP_2', text: '30 陪伴印章' },
{ value: 'FREE_PROP_3', text: '50 陪伴印章' },
{ value: 'YC_TY_1', text: '0.1 鱼翅 上限50' },
{ value: 'YC_TY_2', text: '0.5 鱼翅 上限10' },
{ value: 'YC_TY_3', text: '1 鱼翅 上限5' },
{ value: 'YW_1', text: '100 鱼丸' },
{ value: 'YW_2', text: '200 鱼丸' },
{ value: 'YW_3', text: '500 鱼丸' },
{ value: 'YC_CHIP_1', text: '2 鱼翅碎片 上限5' },
{ value: 'YC_CHIP_2', text: '5 鱼翅碎片 上限4' }
];
items.forEach(item => {
const option = document.createElement('option');
option.value = item.value;
option.textContent = item.text;
selectMenu.appendChild(option);
});
// 添加关闭按钮事件
header.querySelector('.exchange-close').addEventListener('click', () => {
overlay.style.display = 'none';
});
this.bindExchangeEvents(overlay, selectMenu, controlExchangeButton, autoExchangeButton);
overlay.appendChild(header);
overlay.appendChild(controlExchangeButton);
overlay.appendChild(autoExchangeButton);
overlay.appendChild(selectMenu);
document.body.appendChild(overlay);
//makeDraggable(overlay);
this.makeOverlayDraggable(overlay);
}
makeOverlayDraggable(overlay) {
let startX, startY, offsetX, offsetY;
overlay.addEventListener('mousedown', (e) => {
startX = e.pageX;
startY = e.pageY;
offsetX = overlay.offsetLeft;
offsetY = overlay.offsetTop;
const drag = (e) => {
overlay.style.left = offsetX + e.pageX - startX + 'px';
overlay.style.top = offsetY + e.pageY - startY + 'px';
};
const stopDrag = () => {
document.removeEventListener('mousemove', drag);
document.removeEventListener('mouseup', stopDrag);
};
document.addEventListener('mousemove', drag);
document.addEventListener('mouseup', stopDrag);
});
}
bindExchangeEvents(overlay, selectMenu, controlExchangeButton, autoExchangeButton) {
selectMenu.addEventListener('change', () => {
const selectedValues = Array.from(selectMenu.selectedOptions).map(option => option.value);
this.exchangeManager.setSelectedValues(selectedValues);
console.log('目前选择的奖品:', selectedValues.map(value => {
const option = selectMenu.querySelector(`option[value="${value}"]`);
return option ? option.textContent : value;
}));
});
controlExchangeButton.addEventListener('click', () => {
const isRunning = this.exchangeManager.toggleRunning();
if (isRunning) {
controlExchangeButton.textContent = '停止兑换 ✖';
controlExchangeButton.style.backgroundColor = 'green';
this.exchangeManager.runExchangeLoop(erid, this.updateControlExchangeButton.bind(this));
} else {
controlExchangeButton.textContent = '启动兑换 ✔';
controlExchangeButton.style.backgroundColor = 'red';
}
});
autoExchangeButton.addEventListener('click', () => {
const isAutoEnabled = this.exchangeManager.toggleAutoExchange();
if (isAutoEnabled) {
autoExchangeButton.textContent = '自动兑换中 ✔';
autoExchangeButton.style.backgroundColor = 'green';
this.exchangeManager.startAutoExchange(erid);
} else {
autoExchangeButton.textContent = '自动兑换 ✖';
autoExchangeButton.style.backgroundColor = 'red';
this.exchangeManager.stopAutoExchange();
}
});
}
updateControlExchangeButton(isRunning, countLeft) {
const button = document.querySelector('.controlExchange');
if (button) {
if (isRunning) {
button.textContent = `停止兑换 ✖ 剩余请求 ${countLeft}`;
button.style.backgroundColor = 'green';
} else {
button.textContent = '启动兑换 ✔';
button.style.backgroundColor = 'red';
}
}
}
createUI() {
// 创建控制面板容器
const controlPanel = document.createElement('div');
Object.assign(controlPanel.style, {
position: 'fixed',
top: '50px',
//right: '20px',
zIndex: '9999',
backgroundColor: '#f0f0f0',
padding: '10px',
border: '1px solid #ccc',
borderRadius: '5px'
});
controlPanel.innerHTML = `
<label>
<input id="extool__autofish_start" type="checkbox" style="margin-top:5px;">
无限自动钓鱼
</label>
<label>
<input id="extool__eventautofish_start" type="checkbox" style="margin-top:10px;">
大赛时间段自动钓鱼
</label>
<button id="extool__show_records" style="margin-left: 10px;">显示记录</button>
<button id="extool__show_exchange" style="margin-left: 10px;">商城兑换</button>
<div id="time-panel"></div>
<div id="error-log"></div>
`;
document.body.appendChild(controlPanel);
this.makeOverlayDraggable(controlPanel);
// 创建记录查看窗口
const recordsWindow = document.createElement('div');
Object.assign(recordsWindow.style, {
display: 'none',
position: 'fixed',
top: '50%',
left: '50%',
transform: 'translate(-50%, -50%)',
zIndex: '10000',
backgroundColor: 'white',
padding: '20px',
border: '1px solid #ccc',
borderRadius: '5px',
boxShadow: '0 2px 10px rgba(0,0,0,0.1)',
maxHeight: '80vh',
width: '80vw',
overflowY: 'auto'
});
recordsWindow.innerHTML = `
<div style="display: flex; justify-content: space-between; margin-bottom: 15px;">
<select id="extool__room_select" style="padding: 5px;">
<option value="">选择房间</option>
${rids.map(rid => `<option value="${rid}">${rid}</option>`).join('')}
</select>
<div>
<button id="extool__clear_records">清除记录</button>
<button id="extool__close_records" style="margin-left: 10px;">关闭</button>
</div>
</div>
<div id="extool__records_content"></div>
`;
document.body.appendChild(recordsWindow);
this.bindEvents(recordsWindow);
}
bindEvents(recordsWindow) {
// 绑定自动钓鱼开关事件
const checkbox = document.getElementById("extool__autofish_start");
const eventcheckbox = document.getElementById("extool__eventautofish_start");
checkbox.addEventListener("click", async () => {
const isStart = checkbox.checked;
this.saveState(isStart);
if (!isStart) {
this.fishingManager.stopAllFishing();
return;
}else{ //如果checked 取消另一个checkbox勾选
this.saveEventState(!isStart);
eventcheckbox.checked = false;
}
console.log("【自动钓鱼】开始自动钓鱼");
const initialized = await this.fishingManager.init();
if (!initialized) {
checkbox.checked = false;
return;
}
//对每个房间开启计时器检查
rids.forEach(rid => this.fishingManager.startAutoFishing(rid));
});
// 绑定自动钓鱼开关事件
eventcheckbox.addEventListener("click", async () => {
const isStart = eventcheckbox.checked;
this.saveEventState(isStart);
if (!isStart) {
this.fishingManager.stopAllFishing();
return;
}else{ //如果checked 取消另一个checkbox勾选
this.saveState(!isStart);
checkbox.checked = false;
}
console.log("【大赛时间段自动钓鱼】开始自动钓鱼");
const initialized = await this.fishingManager.init();
if (!initialized) {
eventcheckbox.checked = false;
return;
}
rids.forEach(rid => this.fishingManager.startAutoFishing(rid));
});
// 绑定显示记录按钮事件
document.getElementById("extool__show_records").addEventListener("click", () => {
recordsWindow.style.display = 'block';
this.updateRecordsDisplay();
});
// 绑定关闭按钮事件
document.getElementById("extool__close_records").addEventListener("click", () => {
recordsWindow.style.display = 'none';
});
// 绑定房间选择事件
document.getElementById("extool__room_select").addEventListener("change", () => {
this.updateRecordsDisplay();
});
// 绑定清除记录按钮事件
document.getElementById("extool__clear_records").addEventListener("click", () => {
const rid = document.getElementById("extool__room_select").value;
this.storageManager.clearRecords(rid);
this.storageManager.clearErrors(rid);
this.updateRecordsDisplay();
});
document.getElementById("extool__show_exchange").addEventListener("click", () => {
const exchangeOverlay = document.getElementById('exchange-overlay');
exchangeOverlay.style.display = 'block';
});
}
updateRecordsDisplay() {
const rid = document.getElementById("extool__room_select").value;
const records = this.storageManager.getFishRecords();
const errors = this.storageManager.getErrorLogs();
const content = document.getElementById("extool__records_content");
let html = '';
// 计算总奖励统计的函数
const calculateTotalRewards = (recordsData) => {
const total = {};
Object.values(recordsData).flat().forEach(record => {
record.awards.forEach(award => {
if (!total[award.awardName]) {
total[award.awardName] = 0;
}
total[award.awardName] += award.awardNumShow;
});
});
return total;
};
// 生成奖励统计HTML的函数
const generateRewardsHtml = (rewards) => `
<div style="margin-bottom: 20px;">
<h4 style="margin: 0 0 10px 0;">奖励统计:</h4>
<table style="width: 100%; border-collapse: collapse; margin-bottom: 20px; background-color: #f5f5f5;">
<tr>
<td style="border: 1px solid #ddd; padding: 8px; width: 16.66%;">
<strong>心动卡:</strong>${rewards['心动卡'] || 0}
</td>
<td style="border: 1px solid #ddd; padding: 8px; width: 16.66%;">
<strong>甜蜜卡丁车:</strong>${rewards['甜蜜卡丁车'] || 0}
</td>
<td style="border: 1px solid #ddd; padding: 8px; width: 16.66%;">
<strong>陪伴印章:</strong>${rewards['陪伴印章'] || 0}
</td>
</tr>
<tr>
<td style="border: 1px solid #ddd; padding: 8px; width: 16.66%;">
<strong>鱼翅:</strong>${rewards['鱼翅'] || 0}
</td>
<td style="border: 1px solid #ddd; padding: 8px; width: 16.66%;">
<strong>鱼翅碎片:</strong>${rewards['鱼翅碎片'] || 0}
</td>
<td style="border: 1px solid #ddd; padding: 8px; width: 16.66%;">
<strong>鱼丸:</strong>${rewards['鱼丸'] || 0}
</td>
</tr>
</table>
</div>
`;
if (rid) {
// 显示特定房间的记录
html += '<h3>钓鱼记录</h3>';
if (records[rid] && records[rid].length > 0) {
const roomRewards = calculateTotalRewards({ [rid]: records[rid] });
html += generateRewardsHtml(roomRewards);
html += this.generateRecordsTable(records[rid]);
} else {
html += '<p>暂无钓鱼记录</p>';
}
html += '<h3>错误日志</h3>';
if (errors[rid] && errors[rid].length > 0) {
html += this.generateErrorsTable(errors[rid]);
} else {
html += '<p>暂无错误记录</p>';
}
} else {
// 显示所有房间的统计信息
const totalRewards = calculateTotalRewards(records);
html += generateRewardsHtml(totalRewards);
html += '<h3>房间统计</h3>';
html += this.generateStatsTable(records, errors);
}
content.innerHTML = html;
}
generateRecordsTable(records) {
return `
<table style="width: 100%; border-collapse: collapse; margin-bottom: 20px;">
<thead>
<tr>
<th style="border: 1px solid #ddd; padding: 8px;">时间</th>
<th style="border: 1px solid #ddd; padding: 8px;">鱼类</th>
<th style="border: 1px solid #ddd; padding: 8px;">重量</th>
<th style="border: 1px solid #ddd; padding: 8px;">奖励</th>
</tr>
</thead>
<tbody>
${records.map(record => `
<tr>
<td style="border: 1px solid #ddd; padding: 8px;">${new Date(record.timestamp).toLocaleString()}</td>
<td style="border: 1px solid #ddd; padding: 8px;">${record.fishName || '未知'}</td>
<td style="border: 1px solid #ddd; padding: 8px;">${record.weight}斤</td>
<td style="border: 1px solid #ddd; padding: 8px;">
${record.awards.map(award =>
`${award.awardName}x${award.awardNumShow}`
).join(', ') || '无'}
</td>
</tr>
`).join('')}
</tbody>
</table>
`;
}
generateErrorsTable(errors) {
return `
<table style="width: 100%; border-collapse: collapse;">
<thead>
<tr>
<th style="border: 1px solid #ddd; padding: 8px;">时间</th>
<th style="border: 1px solid #ddd; padding: 8px;">错误信息</th>
</tr>
</thead>
<tbody>
${errors.map(error => `
<tr>
<td style="border: 1px solid #ddd; padding: 8px;">${new Date(error.timestamp).toLocaleString()}</td><td style="border: 1px solid #ddd; padding: 8px;">${error.error}</td>
</tr>
`).join('')}
</tbody>
</table>
`;
}
generateStatsTable(records, errors) {
return `
<table style="width: 100%; border-collapse: collapse;">
<thead>
<tr>
<th style="border: 1px solid #ddd; padding: 8px;">房间号</th>
<th style="border: 1px solid #ddd; padding: 8px;">钓鱼次数</th>
<th style="border: 1px solid #ddd; padding: 8px;">总重量</th>
<th style="border: 1px solid #ddd; padding: 8px;">错误次数</th>
<th style="border: 1px solid #ddd; padding: 8px;">最后活动</th>
</tr>
</thead>
<tbody>
${rids.map(rid => {
const roomRecords = records[rid] || [];
const roomErrors = errors[rid] || [];
const totalWeight = roomRecords.reduce((sum, record) => sum + parseFloat(record.weight), 0);
const lastActivity = [...roomRecords, ...roomErrors]
.sort((a, b) => new Date(b.timestamp) - new Date(a.timestamp))[0]?.timestamp;
return `
<tr>
<td style="border: 1px solid #ddd; padding: 8px;">${rid}</td>
<td style="border: 1px solid #ddd; padding: 8px;">${roomRecords.length}</td>
<td style="border: 1px solid #ddd; padding: 8px;">${totalWeight.toFixed(1)}斤</td>
<td style="border: 1px solid #ddd; padding: 8px;">${roomErrors.length}</td>
<td style="border: 1px solid #ddd; padding: 8px;">
${lastActivity ? new Date(lastActivity).toLocaleString() : '无记录'}
</td>
</tr>
`;
}).join('')}
</tbody>
</table>
`;
}
saveState(isAutoFish) {
localStorage.setItem(CONFIG.STORAGE_KEYS.AUTO_FISH, JSON.stringify({ isAutoFish }));
}
saveEventState(isAutoFish) {
localStorage.setItem(CONFIG.STORAGE_KEYS.EVENTAUTO_FISH, JSON.stringify({ isAutoFish }));
}
loadSavedState() {
const saved = localStorage.getItem(CONFIG.STORAGE_KEYS.AUTO_FISH);
if (saved) {
const { isAutoFish } = JSON.parse(saved);
if (isAutoFish) {
document.getElementById("extool__autofish_start").click();
}
}
const eventSaved = localStorage.getItem(CONFIG.STORAGE_KEYS.EVENTAUTO_FISH);
if (eventSaved) {
const { isAutoFish } = JSON.parse(eventSaved);
if (isAutoFish) {
document.getElementById("extool__eventautofish_start").click();
}
}
}
}
// 初始化
(() => {
const queueManager = new RequestQueueManager();
const apiManager = new ApiManager(queueManager);
const storageManager = new StorageManager();
const fishingManager = new FishingManager(storageManager, apiManager);
new UIManager(fishingManager, storageManager,apiManager);
})();
哦对 奖励不知道会不会有问题 记录中钓鱼数量 斤 会不准确 少一些 多一些 不知道为啥 哈哈哈 反正图一乐 也比较难跟踪 我就没管了
哦对 奖励不知道会不会有问题 记录中钓鱼数量 斤 会不准确 少一些 多一些 不知道为啥 哈哈哈 反正图一乐 也比较难跟踪 我就没管了
感觉兑换速度没单个的那个脚本快T T,之前用单个脚本我调成50ms 尝试400次,大概剩余2 3秒开点,0点一波能抢空好几个来着T T
不知道新的脚本要改哪里。
【自动钓鱼】风控拦截,请联系客服~
请求次数太多会被拦截,不给钓了