回复
35
查看
1623
收藏
73

0

赠楼

0%

赠楼率

338

蒸汽

12

主题

1024

帖子

2197

积分
发表于 2025-2-22 15:11:05 · 亚太地区 | 显示全部楼层 |阅读模式
本文为 其乐用户(UID:617090) 发布的原创文章,转摘前请联系该用户获得许可
本帖最后由 617090 于 2025-3-3 19:18 编辑

不知道有多少人移除过喜+1之后还有找回来的想法,本身这部分人应该就不多,脚本应用面本身就很小,坛内搜索了下,23年初的提问给的回复是没有这套相关的,目前简单搜索下应该也没有,就自己搞了一个,如果火星了,或者目前有更好的方案,欢迎指出。

楼主不会前端,都是用deepseek+手动微调搞出来的,还有一些情况、特例都没有考虑到,很粗糙的代码,欢迎指出问题或者优化更新

坛内检索到的相关信息:
关于恢复被移除的游戏 - 平台技术问题 - 其乐 Keylol
教你如何恢复被手动移除且"不可恢复"的游戏 - 平台工具 - 其乐 Keylol
请问现在有快捷恢复游戏的脚本吗 - 平台技术问题 - 其乐 Keylol
查询账号里永久移除游戏的方法 - 平台工具 - 其乐 Keylol










简易筛选

感谢31楼老哥指正,之前家庭监护筛选的方式还能用,这个方法我试了下,可以筛选有入库许可(且支持家庭共享的游戏),可以快速得到一个列表,我用不能家庭共享的游戏测试是筛选不出来的,demo也只能筛出来一部分,包含已退款游戏(但好像不是全部),还有没有其他筛选不出来的不清楚,原帖说受限游戏也筛选不出。
说下我自己用的情况,从1310到1341是全面筛选筛选出的,这里进行到了三分之一,后面在家庭共享到了1344,全steam筛选到了一半是1345,可以确定这个方法不支持家庭共享、DLC、大部分demo、受限是筛选不出的,免费游戏等存疑,不过应该还是可以得到七八成的结果,具体按需取用吧。

使用方法:
家庭监护这个页面点击下一步后控制台运行以下脚本,脚本来源是相关信息第四个帖子。
  1. jQuery('#filterTable li input').each((i, el) => {
  2.     let appId = el.name.substr(6);
  3.     if (!GDynamicStore.s_rgOwnedApps[appId])
  4.         console.log(appId, el.parentElement.getAttribute('data-filter-meta'));
  5. });

复制代码
总比我自己搞的全站检索轻松不少。

暴力全面检索
先是检索,检索里23年的帖子引用了20年通过家庭监护查找的方法,近期家庭系统改版,应该是不能用了,全面筛查暂时想不出其他办法,那就暴力检索,思路是暴力依次检索appid对应客服页面有没有"此产品不在您的 Steam 库中。"并且包含日期的文本,当然这个思路不可取,遍历全steam游戏效率太低了,单线程挂满全steam一圈大约是100多小时,如果做一个多线程应该好不少,如果有其他更高效检索方式欢迎指出。非要这么用那就分段,每次检索一段,分十几天慢慢处理,反正就挂在后台,或者自行改个多线程,这里是单线程。

目前检测逻辑是页面有入库相关词语即视为匹配,例如“来自激活项目
”、”来自购买“、”通过礼物赠送或交易而获得“中的关键字,暂不清楚是否有其他入库方式显示的不一样,有需要请自行添加相关词语,注意避开页面其他元素是否有相关内容,没有写页面元素筛选,非中文显示的界面需要修改

经提醒去翻了下steam web api,很遗憾的是验证游戏许可要求有开发商权限认证,个人是验证不了的,对于没启动过,没成就,没卡牌什么都没有的游戏,以及包括赠礼,key,等等方式来源的游戏,我这里没有什么更好的方法全面筛选出来,暴力检索只能是没有办法下的无奈选择,如果有其他方法更高效获得已移除的appid,或者自己有记录,都比暴力检索来得强,也因此奉劝各位不要乱折腾了,不想看到就隐藏嘛,都比再找一圈来的轻松。
需要在客服相关页面下使用
  1. (async function findMissingAppIds() {
  2.   // 配置参数(自行调整)
  3.   const config = {
  4.     start: 10,    // 起始 AppID
  5.     end: 3000000,     // 结束 AppID   
  6.     step: 10,         // 每次递增步长
  7.     delay: 1,         // 请求间隔(ms)
  8.     targetText: "此产品不在您的 Steam 库中。" // 检索文本
  9.   };

  10.   // 结果存储
  11.   const foundIds = [];

  12.   const ownershipIndicators = [
  13.     'LineItemRow', // 购买记录容器
  14.     '购得',       // 直接购买
  15.     '激活',   // KEY激活
  16.     '礼物赠送',    // 礼物获得
  17.     '交易而获得'     // 市场交易
  18.   ];

  19.   for (let appid = config.start; appid <= config.end; appid += config.step) {
  20.     try {
  21.       // 发送请求
  22.       const response = await fetch(
  23.         `https://help.steampowered.com/zh-cn/wizard/HelpWithGame/?appid=${appid}`,
  24.         { credentials: "include" } // 包含 cookies
  25.       );
  26.       
  27.       // 解析响应
  28.       const text = await response.text();
  29.       
  30.       // 检查目标文本
  31.       if (text.includes(config.targetText)) {
  32.         if(ownershipIndicators.some(indicator => text.includes(indicator))){
  33.                       foundIds.push(appid);
  34.             console.log(`发现匹配项:${appid}`);
  35.         }
  36.       }

  37.       // 控制台进度提示
  38.       if (appid % 1000 === 0) {
  39.         console.log(`进度:${((appid/config.end)*100).toFixed(2)}%`);
  40.       }
  41.       // 在循环内添加定期保存
  42. if (appid % 1000 === 0) {
  43.   localStorage.setItem('steamScanProgress', JSON.stringify({
  44.     lastAppId: appid,
  45.     foundIds: foundIds
  46.   }));
  47. }

  48.       // 请求间隔
  49.       await new Promise(r => setTimeout(r, config.delay));
  50.       
  51.     } catch (error) {
  52.       console.error(`AppID ${appid} 请求失败:`, error);
  53.     }
  54.   }

  55.   // 最终结果输出
  56.   console.log("匹配的 AppID 列表:", foundIds);
  57.   return foundIds;
  58. })();
复制代码




批量恢复

批量恢复的想法和实现就正常很多。
参考信息的一个帖子提到了一个恢复用的接口,这个接口还没改,能用,接口需要三个参数,appid,packageid和sessionid,在检索部分可以轻易(花时间)找到有权限但是不在库中的appid,登陆后可以用sessionid,那么剩下的就是packageid,在另一篇帖子中提到恢复页面替换两个值可以恢复有问题无法恢复的游戏,那么说明在原页面本身就含有原appid恢复需要的对应的packageid,这个思路就很清晰了:获取appid对应客服页面→进入恢复页面→检索packageid→打包表单发出请求。

跑完了可以回头看一看,有没有报错或者有没有表单之类的再核实一下,这里只有支持客服页面恢复的游戏可以恢复,特殊情况,或者网络波动导致表单发送失败都需要手动处理。

相应代码如下,同样需要在客服页面下使用
  1. // ==用户配置区==
  2. const targetAppIds = []; // 填入需要检测的AppID数组
  3. const config = {
  4.   checkDelay: 1500, // 页面加载检测间隔
  5.   stepDelay: 500 // 操作步骤间隔
  6. };

  7. // ==核心逻辑==
  8. async function processAppId(appid) {
  9.   try {
  10.     // 第一阶段:获取初始页面
  11.     const firstPageUrl = `https://help.steampowered.com/zh-cn/wizard/HelpWithGame/?appid=${appid}`;
  12.     const firstPage = await fetch(firstPageUrl).then(r => r.text());
  13.    
  14.     // 解析第一阶段页面
  15.     const parser = new DOMParser();
  16.     const firstDoc = parser.parseFromString(firstPage, 'text/html');
  17.    
  18.     // 检测"不在库中"链接
  19.     const missingLink = [...firstDoc.querySelectorAll('a.help_wizard_button')]
  20.       .find(a => a.textContent.includes('不在我的库中'));
  21.    
  22.     if (!missingLink) {
  23.       console.log(`[跳过] AppID ${appid} 未找到初始链接`);
  24.       return;
  25.     }

  26.     // 第二阶段:访问问题页面
  27.     const secondPageUrl = new URL(missingLink.href, firstPageUrl).href;
  28.     await new Promise(r => setTimeout(r, config.stepDelay));
  29.    
  30.     const secondPage = await fetch(secondPageUrl).then(r => r.text());
  31.     const secondDoc = parser.parseFromString(secondPage, 'text/html');

  32.     // 获取关键参数
  33.     const form = secondDoc.getElementById('submit_restore_package_form');
  34.     if (!form) {
  35.       console.log(`[错误] AppID ${appid} 未找到恢复表单`);
  36.       return;
  37.     }

  38.     // 精确提取参数
  39.     const packageid = secondDoc.querySelector('input[name="packageid"]')?.value;
  40.     const appIdValue = secondDoc.querySelector('input[name="appid"]')?.value;
  41.     const sessionid = document.cookie.match(/sessionid=([^;]+)/)?.[1];

  42.     if (!packageid || !appIdValue) {
  43.       console.log(`[错误] AppID ${appid} 缺少必要参数`);
  44.       return;
  45.     }

  46.     // 构建请求参数
  47.     const params = new URLSearchParams();
  48.     params.append('packageid', packageid);
  49.     params.append('appid', appIdValue);
  50.     params.append('sessionid', sessionid);
  51.     params.append('wizard_ajax', '1');

  52.     // 发送恢复请求
  53.     const result = await fetch('https://help.steampowered.com/zh-cn/wizard/AjaxDoPackageRestore', {
  54.       method: 'POST',
  55.       headers: {
  56.         'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'
  57.       },
  58.       body: params,
  59.       credentials: 'include'
  60.     });

  61.     console.log(`[成功] AppID ${appid} 恢复请求完成,状态码: ${result.status}`);
  62.   } catch (err) {
  63.     console.error(`处理 AppID ${appid} 时出错:`, err);
  64.   }
  65. }

  66. // ==执行控制器==
  67. (async () => {
  68.   for (const appid of targetAppIds) {
  69.     console.log(`开始处理 AppID: ${appid}`);
  70.     await processAppId(appid);
  71.     await new Promise(r => setTimeout(r, config.checkDelay));
  72.   }
  73.   console.log('全部操作完成');
  74. })();
复制代码

个人测试下来是成功的(基本都是添加后又移除的demo)


完毕











本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

×

本帖被以下淘专辑推荐:

回复

使用道具 举报

浏览本版块需要:
1. 初阶会员或更高等级;
2. (点击此处)绑定Steam账号
您需要登录后才可以回帖 登录 | 注册

本版积分规则

欢迎发帖参与讨论 o(*≧▽≦)ツ,请注意
1. 寻求帮助或答案的帖子请发到问题互助版块,悬赏有助于问题解决的速度。发错可能失去在该板块发布主题的权限(了解更多
2. 表达观点可以,也请务必注意语气和用词,以免影响他人浏览,特别是针对其他会员的内容。如觉得违规可使用举报功能 交由管理人员处理,请勿引用对方的内容。
3. 开箱晒物交易中心游戏互鉴福利放送版块请注意额外的置顶版规。
4. 除了提问帖和交易帖以外,不确认发在哪个版块的帖子可以先发在谈天说地

  作为民间站点,自 2004 年起为广大中文 Steam 用户提供技术支持与讨论空间。历经二十余载风雨,如今已发展为国内最大的正版玩家据点。

列表模式 · · 微博 · Bilibili频道 · Steam 群组 · 贴吧 · QQ群 
Keylol 其乐 ©2004-2025 Chinese Steam User Fan Site.
Designed by Lee in Balestier, Powered by Discuz!
推荐使用 ChromeMicrosoft Edge 来浏览本站
广告投放|手机版|广州数趣信息科技有限公司 版权所有|其乐 Keylol ( 粤ICP备17068105号 )
GMT+8, 2025-3-17 22:17
快速回复 返回顶部 返回列表