Auto_Award_Muli

Steam自动打赏 — 极速多账户版

  1. // ==UserScript==
  2. // @name Auto_Award_Muli
  3. // @name:zh-CN Steam自动打赏【极速多账户版】
  4. // @namespace https://blog.chrxw.com
  5. // @contributionURL https://afdian.com/@chr233
  6. // @version 2.3
  7. // @description Steam自动打赏 — 极速多账户版
  8. // @description:zh-CN Steam自动打赏 — 极速多账户版
  9. // @author Chr_
  10. // @include /^https:\/\/steamcommunity\.com\/id\/[^/]+/?$/
  11. // @include /^https:\/\/steamcommunity\.com\/profiles\/\d+/?$/
  12. // @connect steamcommunity.com
  13. // @connect steampowered.com
  14. // @connect api.steampowered.com
  15. // @license AGPL-3.0
  16. // @icon https://blog.chrxw.com/favicon.ico
  17. // @grant GM_setValue
  18. // @grant GM_getValue
  19. // @grant GM_xmlhttpRequest
  20. // @grant GM_addStyle
  21. // @grant GM_setClipboard
  22. // @grant GM_registerMenuCommand
  23. // ==/UserScript==
  24.  
  25. (() => {
  26. 'use strict';
  27.  
  28. // 多语言
  29. const LANG = {
  30. 'ZH': {
  31. 'done': '完成',
  32. 'changeLang': '修改语言',
  33. 'langName': '中文',
  34. 'operating': '操作进行中……',
  35. 'runningLog': '运行日志',
  36. 'close': '关闭',
  37. 'logWaring': '【日志不会保存, 打赏记录可以在【打赏历史】中查看】',
  38. 'botListTitle': '【机器人账户管理】【ID | 账户名 | SteamID | 点数余额】',
  39. 'addCurrentAccount': '添加当前账号',
  40. 'delSelectAccount': '删除选中账号',
  41. 'reloadAccountPoints': '刷新所有账号点数',
  42. 'historyTitle': '【点数打赏历史记录】【ID | 昵称 | SteamID | 收到点数】',
  43. 'profile': '个人资料',
  44. 'deleteSelectedHistory': '删除选中',
  45. 'clearHistory': '清空历史',
  46. 'reloadHistory': '刷新历史',
  47. 'feedBack': '作者',
  48. 'feedBackTitle': '觉得好用也欢迎给我打赏',
  49. 'notSelected': '---自动使用当前登录(不可用)账号---',
  50. 'steamID64': 'Steam 64位 ID',
  51. 'awardPoints': '打赏点数(收到)',
  52. 'recommands': '评测',
  53. 'screenshots': '截图',
  54. 'artworks': '艺术作品',
  55. 'setToCurrentUser': '设置为当前用户',
  56. 'calculator': '打赏计算器',
  57. 'save': '保存',
  58. 'reset': '重置',
  59. 'awardHistory': '打赏历史',
  60. 'startAward': '开始打赏',
  61. 'stopAward': '停止打赏',
  62. 'stop': '停止',
  63. 'senderBotAccount': '打赏机器人账户: ',
  64. 'receverAccount': '被打赏人SteamID: ',
  65. 'sendPoints': '打赏点数(收到): ',
  66. 'awardPrefer': '打赏类型(优先级从上到下从左到右): ',
  67. 'botList': '机器人列表',
  68. 'fetchLoginAccount': '获取登陆账户……',
  69. 'fetchToken': '获取Token……',
  70. 'fetchPoints': '获取点数信息……',
  71. 'success': '成功',
  72. 'failure': '失败',
  73. 'error': '错误',
  74. 'confirm': '确认',
  75. 'tips': '提示',
  76. 'addAccountSuccessTips1': '添加账户成功',
  77. 'addAccountSuccessTips2': '当前账户可用点数',
  78. 'deleteAccountConfirmTips': '确定要删除选定的账号吗?',
  79. 'deleteAccountDoneTips1': '删除了',
  80. 'deleteAccountDoneTips2': '个机器人',
  81. 'notSelectedAnyBotsTips': '尚未选中任何机器人!',
  82. 'currentProcess': '当前进度',
  83. 'updateFailed': '更新出错',
  84. 'fetchingAccountPoints': '读取账户点数中……',
  85. 'allDataLoaded': '所有数据刷新完毕',
  86. 'someDataLoadFailed': '部分数据刷新失败, 如果点数显示为【-1】,代表数据刷新失败',
  87. 'botListEmpty': '机器人列表为空',
  88. 'noBotAccountTips': '-- 无机器人账号, 请使用【➕添加当前账号】自动添加 --',
  89. 'awardTaskWasResetTips': '机器人账号已修改, 打赏设置已重置!',
  90. 'noHistoryTips': '-- 无历史记录, 执行打赏任务后会自动记录 --',
  91. 'steamIDisEmpty': '未填入SteamID!',
  92. 'fetchingProfile': '获取个人资料中……',
  93. 'fetchingAwardableItems': '获取可打赏项目……',
  94. 'fetchError': '读取出错',
  95. 'awardableAmount': '可打赏约',
  96. 'nickName': '用户名',
  97. 'totalPoints': '总计点数',
  98. 'calcTips': '根据项目数量计算所得, 不准确',
  99. 'profileNotExistsTips': '个人资料不存在, 请检查SteamID是否正确, 或者使用【🤵设置为当前用户】自动获取。',
  100. 'profileLoadFailedTips': '网络错误, 读取个人资料失败',
  101. 'notSelectedAnyHistoryTips': '未选中历史记录!',
  102. 'clearHistoryConfirmTips': '确定要清除打赏历史记录吗?',
  103. 'clearHistorySuccess': '清除成功',
  104. 'historyListEmpty': '历史记录是空的!',
  105. 'deleteHistoryConfirmTips': '确定要删除选定的打赏历史记录吗?',
  106. 'deleteResultTips1': '删除了',
  107. 'deleteResultTips2': '条打赏历史记录',
  108. 'notSelectedAwardBotsTips': '尚未选择打赏机器人!',
  109. 'steamIDEmptyWithTips': '未填写【被打赏人SteamID】, 建议使用【🤵设置为当前用户】功能!',
  110. 'steamIDErrorWithTips': '【被打赏人SteamID】格式有误, 建议使用【🤵设置为当前用户】功能!',
  111. 'pointsErrorWithTips': '【打赏点数】格式有误, 只能为整数!',
  112. 'awardTypeEmptyTips': '请选择【打赏类型】!',
  113. 'awardReadyToStartTips': '设置保存成功, 可以【✅开始打赏】了',
  114. 'resetConfigConfirmTips': '确定要重置设定吗?',
  115. 'configResetSuccessTips': '设置已清除',
  116. 'awardTaskDataInvalid': '任务数据非法',
  117. 'fetchingTargetProfile': '读取被打赏人个人资料……',
  118. 'awardConfig': '打赏设置',
  119. 'targetNickName': '被打赏人昵称',
  120. 'targetReceivePoints': '预计收到点数',
  121. 'targetBot': '打赏机器人',
  122. 'taskReadyToStartTips': '打赏任务【2秒】后开始, 点击【⛔停止打赏】可以提前终止操作!',
  123. 'taskFailedProfileNotFound': '未找到个人资料, 打赏进程停止!',
  124. 'taskAlreadyStartTips': '打赏任务已经开始了!',
  125. 'taskEndManually': '打赏任务手动终止, 点击【❌关闭】可以关闭面板.',
  126. 'taskNotStart': '打赏任务未开始!',
  127. 'running': '运行',
  128. 'taskStartPointsSummary': '开始打赏, 剩余打赏 / 预计打赏',
  129. 'fetchAwardItemFailedRetry': '获取打赏项目失败, 重试……',
  130. 'fetchNoAwardItemSkip': '没有合适的打赏, 跳过……',
  131. 'beforeSendAward': '将要打赏',
  132. 'itemAndTotal': '项, 总计',
  133. 'points': '点',
  134. 'sendingAwards': '发送打赏中……',
  135. 'fetchSuccessAndFailed': '请求成功 / 请求失败',
  136. 'fetchAwardItemFailedRetryIn2Min': '获取打赏项目失败, 【2秒】后重试……',
  137. 'awardSuccess': '成功打赏',
  138. 'taskFinishedPointsSummary': '打赏完成, 剩余打赏 / 预计打赏',
  139. 'updateBotPointsBalance': '更新机器人点数余额……',
  140. 'bot': '机器人',
  141. 'pointsBalanceUpdateSuccess': '点数余额更新成功, 可用点数',
  142. 'lackOfPointsTaskEnd': '点数余额不足, 终止操作',
  143. 'pointBalanceUpdateFailed': '点数余额更新失败',
  144. 'fetchAwardItemFailedSkip': '获取打赏项目失败, 跳过……',
  145. 'taskEndListEmpty': '列表为空, 结束',
  146. 'fetchCompletedTotal': '获取成功, 共',
  147. 'entries': '个',
  148. 'objectID': '项目ID',
  149. 'noAwardableObjectSkip': '没有合适的打赏, 跳过',
  150. 'willAward': '将要打赏',
  151. 'requestsSummary': '请求成功 / 请求失败',
  152. 'wait2Seconds': '*等待2秒,防止打赏过多*',
  153. 'botDataError': '机器人数据错误, 无法开始打赏!',
  154. 'awardTaskFinish': '✅打赏任务完成, 点击【❌关闭】可以关闭面板。',
  155. 'awardTaskNotFinish': '⛔打赏任务未完成, 点击【❌关闭】可以关闭面板。',
  156. 'cancel': '取消',
  157. 'steamStoreNotLogin': '【STEAM商店】未登录(不可用),请重新登录(不可用)',
  158. 'parseDataFailedMaybeNetworkError': '解析数据失败, 可能是Token失效或者网络错误',
  159. 'typeError': 'type错误',
  160. 'networkError': '网络错误',
  161. 'parseError': '解析出错',
  162. 'importAccount': '手动导入账号',
  163. 'importAccountSteamId64': '请输入Steam64位ID',
  164. 'importAccountInvalidSteamId64': '请输入正确的SteamID',
  165. 'importAccountGetToken': '请访问【 https://store.steampowered.com/pointssummary/ajaxgetasyncconfig 】,并把所有内容粘贴到下面',
  166. 'importAccountNickName': '请输入机器人显示昵称',
  167. 'importAccountNickNameTips': '手动添加',
  168. 'importAccountSuccess': '添加账号成功, 请手动刷新点数',
  169. },
  170. 'EN': {
  171. 'done': 'Done',
  172. 'changeLang': 'Change Language',
  173. 'langName': 'English',
  174. 'operating': 'Operating……',
  175. 'runningLog': 'Log',
  176. 'close': 'Close',
  177. 'logWaring': '【Log will not save, award history will list in【History】】',
  178. 'botListTitle': '【Bot Accounts】【ID | NickName | SteamID | Points Balance】',
  179. 'addCurrentAccount': 'Add Account',
  180. 'delSelectAccount': 'Del Selected',
  181. 'reloadAccountPoints': 'Refresh Points',
  182. 'historyTitle': '【History】【ID | NickName | SteamID | Received Points】',
  183. 'profile': 'Profile',
  184. 'deleteSelectedHistory': 'Del Selected',
  185. 'clearHistory': 'Clear',
  186. 'reloadHistory': 'Reload',
  187. 'feedBack': 'Author',
  188. 'feedBackTitle': '觉得好用也欢迎给我打赏',
  189. 'notSelected': '---Use Current Account---',
  190. 'steamID64': 'Steam 64 ID',
  191. 'awardPoints': 'Points (Receive)',
  192. 'recommands': 'Recommands',
  193. 'screenshots': 'Screenshots',
  194. 'artworks': 'Artworks',
  195. 'setToCurrentUser': 'Set to page\'s user',
  196. 'calculator': 'Calculator',
  197. 'save': 'Save',
  198. 'reset': 'Reset',
  199. 'awardHistory': 'History',
  200. 'startAward': 'Start Award',
  201. 'stopAward': 'Stop',
  202. 'stop': 'Stopped',
  203. 'senderBotAccount': 'Award Bot: ',
  204. 'receverAccount': 'Target SteamID: ',
  205. 'sendPoints': 'Points (Receive): ',
  206. 'awardPrefer': 'Award Type (Up to down left to right): ',
  207. 'botList': 'Bots List',
  208. 'fetchLoginAccount': 'Fetching current logined account……',
  209. 'fetchToken': 'Fetching token……',
  210. 'fetchPoints': 'Fetching points balance……',
  211. 'success': 'Success',
  212. 'failure': 'Failure',
  213. 'error': 'Error',
  214. 'confirm': 'Confirm',
  215. 'tips': 'Tips',
  216. 'addAccountSuccessTips1': 'Add account successful',
  217. 'addAccountSuccessTips2': 'Current account\'s points balance',
  218. 'deleteAccountConfirmTips': 'Are sure to delete selected bots?',
  219. 'deleteAccountDoneTips1': 'Deleted',
  220. 'deleteAccountDoneTips2': 'bots',
  221. 'notSelectedAnyBotsTips': 'You have not selected any bot!',
  222. 'currentProcess': 'Current process',
  223. 'updateFailed': 'Update Failed',
  224. 'fetchingAccountPoints': 'Fetching points balance……',
  225. 'allDataLoaded': 'All data loaded',
  226. 'someDataLoadFailed': 'Some data loaded failed, if some bot\'s balance is 【-1】, it means fetch error',
  227. 'botListEmpty': 'Bot list is empty',
  228. 'noBotAccountTips': '-- No Bot Account, you can add via【➕Add ACcount】(Loginin required) --',
  229. 'awardTaskWasResetTips': 'Bot account had been modified, Award config reseted!',
  230. 'noHistoryTips': '-- No History Records, It Will Record When Awarding --',
  231. 'steamIDisEmpty': 'You must specify SteamID!',
  232. 'fetchingProfile': 'Fetching profile……',
  233. 'fetchingAwardableItems': 'Fetching awardable items……',
  234. 'fetchError': 'Fetch error',
  235. 'awardableAmount': '可打赏约',
  236. 'nickName': 'Nickname',
  237. 'totalPoints': 'Total points',
  238. 'calcTips': 'According to the total number of the items, inaccurate',
  239. 'profileNotExistsTips': 'Profile not found, please check if the steamID is correct, or use 【🤵Set to page\'s user】 instead',
  240. 'profileLoadFailedTips': 'Network error, fetch profile failed',
  241. 'notSelectedAnyHistoryTips': 'No records selected!',
  242. 'clearHistoryConfirmTips': 'Are you sure to clear all award records?',
  243. 'clearHistorySuccess': 'History cleared',
  244. 'historyListEmpty': 'History is empty!',
  245. 'deleteHistoryConfirmTips': 'Are you sure to delete seleted award records?',
  246. 'deleteResultTips1': 'Deleted',
  247. 'deleteResultTips2': 'history records',
  248. 'notSelectedAwardBotsTips': 'No bots selected!',
  249. 'steamIDEmptyWithTips': '【Target SteamID】is empty, It is recommended to use【🤵Set to page\'s user】!',
  250. 'steamIDErrorWithTips': '【Target SteamID】is invalid, It is recommended to use【🤵Set to page\'s user】!',
  251. 'pointsErrorWithTips': '【Points】is invalid, only integers are accepted!',
  252. 'awardTypeEmptyTips': 'Please select【Award Type】!',
  253. 'awardReadyToStartTips': 'Config saved, it is ready to【✅Start Award】',
  254. 'resetConfigConfirmTips': 'Are you sure to reset the config?',
  255. 'configResetSuccessTips': 'Config reseted!',
  256. 'awardTaskDataInvalid': 'Task data invalid',
  257. 'fetchingTargetProfile': 'Fetching target user\'s profile……',
  258. 'awardConfig': 'Award config',
  259. 'targetNickName': 'Target user\'s nickname',
  260. 'targetReceivePoints': 'Expected points received',
  261. 'targetBot': 'Selected bot',
  262. 'taskReadyToStartTips': 'Award task will start in 【2 seconds】, click【⛔Stop】to interrupt award task!',
  263. 'taskFailedProfileNotFound': 'Profile not found, award task end!',
  264. 'taskAlreadyStartTips': 'Award task is already running!',
  265. 'taskEndManually': 'Award task interrupted manually, click【❌Close】to hide the log panel.',
  266. 'taskNotStart': 'Award task not running!',
  267. 'running': 'Running',
  268. 'taskStartPointsSummary': 'Start sending award, points left / points expected',
  269. 'fetchAwardItemFailedRetry': 'Fetch awardable items failed, retry……',
  270. 'fetchNoAwardItemSkip': 'No suitable award items, skip……',
  271. 'beforeSendAward': 'Will send award',
  272. 'itemAndTotal': 'items, total',
  273. 'points': 'points',
  274. 'sendingAwards': 'Sending awards……',
  275. 'fetchSuccessAndFailed': 'Success / Failure',
  276. 'fetchAwardItemFailedRetryIn2Min': 'Fetch awardable items failed, will retry in【2 seconds】……',
  277. 'awardSuccess': 'Successful send award',
  278. 'taskFinishedPointsSummary': 'Award task complete, points left / points expected',
  279. 'updateBotPointsBalance': 'Update bot\'s points balance……',
  280. 'bot': 'bots',
  281. 'pointsBalanceUpdateSuccess': 'points balance, avilable points',
  282. 'lackOfPointsTaskEnd': 'Lack of points balance, stop operation',
  283. 'pointBalanceUpdateFailed': 'Update points balance failed',
  284. 'fetchAwardItemFailedSkip': 'Fetch awardable items failed, skip……',
  285. 'taskEndListEmpty': 'Award items list is empty, end',
  286. 'fetchCompletedTotal': 'Fetch success, total',
  287. 'entries': 'entries',
  288. 'objectID': 'Target ID',
  289. 'noAwardableObjectSkip': 'No suitable award items, skip...',
  290. 'willAward': 'Will send award',
  291. 'requestsSummary': 'Success / Failure',
  292. 'wait2Seconds': '*Delay 2 seconds, to avoid exceed award*',
  293. 'botDataError': 'Bot data error, can\'t start award task!',
  294. 'awardTaskFinish': '✅Award task completed, click【❌Close】to hide the log panel',
  295. 'awardTaskNotFinish': '⛔Award task not completed, click【❌close】to hide the log panel',
  296. 'cancel': 'Cancel',
  297. 'steamStoreNotLogin': '【STEAM Store】not logined, please sign in first',
  298. 'parseDataFailedMaybeNetworkError': 'Parse data failed, maybe token expired or network error',
  299. 'typeError': 'Type Error',
  300. 'networkError': 'Network Error',
  301. 'parseError': 'Parse Error',
  302. 'importAccount': '手动导入账号',
  303. 'importAccountSteamId64': '请输入Steam64位ID',
  304. 'importAccountInvalidSteamId64': '请输入正确的SteamID',
  305. 'importAccountGetToken': '请访问【 https://store.steampowered.com/pointssummary/ajaxgetasyncconfig 】,并把所有内容粘贴到下面',
  306. 'importAccountNickName': '请输入机器人显示昵称',
  307. 'importAccountNickNameTips': '手动添加',
  308. 'importAccountSuccess': '添加账号成功, 请手动刷新点数',
  309. }
  310. };
  311.  
  312. // 判断语言
  313. let language = GM_getValue("lang", null);
  314. if (!(language in LANG)) {
  315. showAlert('申明', `<p>本脚本现已免费提供</p><p>如果你在<a href="https://afdian.com/a/chr233">爱发电</a>以外的地方购买了本脚本, 请申请退款</p><p>觉得好用也欢迎给 <a href="https://steamcommunity.com/id/Chr_">作者</a> 打赏</p>`, true);
  316. language = "ZH";
  317. GM_setValue("lang", language);
  318. }
  319. // 获取翻译文本
  320. function t(key) {
  321. return LANG[language][key] || key;
  322. }
  323. {// 自动弹出提示
  324. const languageTips = GM_getValue("languageTips", true);
  325. if (languageTips && language === "ZH") {
  326. if (!document.querySelector("html").lang.startsWith("zh")) {
  327. ShowConfirmDialog("tips", "Auto Award now support English, switch?", "Using English", "Don't show again")
  328. .done(() => {
  329. GM_setValue("lang", "EN");
  330. GM_setValue("languageTips", false);
  331. window.location.reload();
  332. })
  333. .fail((bool) => {
  334. if (bool) {
  335. showAlert("", "You can switch the plugin's language using TamperMonkey's menu.");
  336. GM_setValue("languageTips", false);
  337. }
  338. });
  339. }
  340. }
  341. }
  342. GM_registerMenuCommand(`${t("changeLang")} (${t("langName")})`, () => {
  343. switch (language) {
  344. case "EN":
  345. language = "ZH";
  346. break;
  347. case "ZH":
  348. language = "EN";
  349. break;
  350. }
  351. GM_setValue("lang", language);
  352. window.location.reload();
  353. });
  354.  
  355. GM_registerMenuCommand(t("importAccount"), () => {
  356. const steamID = prompt(t("importAccountSteamId64"));
  357. const v_steamID = parseInt(steamID);
  358. if (v_steamID !== v_steamID) {
  359. alert(t("importAccountInvalidSteamId64"));
  360. return;
  361. }
  362. const ajaxJson = prompt(t("importAccountGetToken"));
  363. const json = JSON.parse(ajaxJson);
  364. const token = json?.data?.webapi_token;
  365. if (!token) {
  366. alert(t("parseError"));
  367. return;
  368. }
  369.  
  370. let botCount = 0;
  371. for (let _ in GBots) {
  372. botCount++;
  373. }
  374. const nick = prompt(t("importAccountNickName"), `${t("importAccountNickNameTips")} ${botCount}`);
  375.  
  376. alert(t("importAccountSuccess"));
  377. GBots[steamID] = { nick, token, points: -1 };
  378. GM_setValue('bots', GBots);
  379. flashBotList();
  380. });
  381.  
  382. //机器人账号
  383. let GBots = {};
  384. //打赏历史记录
  385. let GHistory = {};
  386. //打赏任务
  387. let GTask = {};
  388. //面板状态
  389. let GPanel = {};
  390. //控件字典
  391. let GObjs = {};
  392.  
  393. //初始化
  394. (() => {
  395. loadConf();
  396.  
  397. graphGUI();
  398. flashBotList();
  399. flashHistoryList();
  400.  
  401. const { panelMain, panelLeft } = GPanel;
  402. if (panelMain) {
  403. GPanel.panelMain = false;
  404. panelSwitch();
  405. }
  406. if (panelLeft) {
  407. GPanel.panelLeft = false;
  408. leftPanelSwitch();
  409. }
  410. if (!isEmptyObject(GTask)) {
  411. GTask.work = false;
  412. }
  413. appllyTask();
  414. })();
  415.  
  416. //====================================================================================
  417. //添加控制面板
  418. function graphGUI() {
  419. function genButton(text, foo, enable = true) {
  420. const b = document.createElement('button');
  421. b.textContent = text;
  422. b.className = 'aam_button';
  423. b.disabled = !enable;
  424. b.addEventListener('click', foo);
  425. return b;
  426. }
  427. function genDiv(cls = 'aam_div') {
  428. const d = document.createElement('div');
  429. d.className = cls;
  430. return d;
  431. }
  432. function genA(text, url) {
  433. const a = document.createElement('a');
  434. a.textContent = text;
  435. a.className = 'aam_a';
  436. a.target = '_blank';
  437. a.href = url;
  438. return a;
  439. }
  440. function genInput(value, tips, number = false) {
  441. const i = document.createElement('input');
  442. i.className = 'aam_input';
  443. if (value) { i.value = value; }
  444. if (tips) { i.placeholder = tips; }
  445. if (number) {
  446. i.type = 'number';
  447. i.step = 100;
  448. i.min = 0;
  449. }
  450. return i;
  451. }
  452. function genTextArea(value, tips) {
  453. const i = document.createElement('textarea');
  454. i.className = 'aam_textarea';
  455. if (value) { i.value = value; }
  456. if (tips) { i.placeholder = tips; }
  457. return i;
  458. }
  459. function genCheckbox(name, checked = false) {
  460. const l = document.createElement('label');
  461. const i = document.createElement('input');
  462. const s = genSpace(name);
  463. i.textContent = name;
  464. i.title = name;
  465. i.type = 'checkbox';
  466. i.className = 'aam_checkbox';
  467. i.checked = checked;
  468. l.appendChild(i);
  469. l.appendChild(s);
  470. return [l, i];
  471. }
  472. function genSelect(choose = [], choice = null) {
  473. const s = document.createElement('select');
  474. s.className = 'aam_select';
  475. choose.forEach(([text, value]) => {
  476. s.options.add(new Option(text, value));
  477. });
  478. if (choice) { s.value = choice; }
  479. return s;
  480. }
  481. function genList(choose = [], choice = null) {
  482. const s = genSelect(choose, choice);
  483. s.className = 'aam_list';
  484. s.setAttribute('multiple', 'multiple');
  485. return s;
  486. }
  487. function genP(text) {
  488. const p = document.createElement('p');
  489. p.textContent = text;
  490. return p;
  491. }
  492. function genSpan(text = ' ') {
  493. const s = document.createElement('span');
  494. s.textContent = text;
  495. return s;
  496. }
  497. const genSpace = genSpan;
  498. function genBr() {
  499. return document.createElement('br');
  500. }
  501. function genHr() {
  502. return document.createElement('hr');
  503. }
  504. function genMidBtn(text, foo) {
  505. const a = document.createElement('a');
  506. const s = genSpan(text);
  507. a.className = 'btn_profile_action btn_medium';
  508. a.addEventListener('click', foo);
  509. a.appendChild(s);
  510. return [a, s];
  511. }
  512.  
  513. const btnArea = document.querySelector('.profile_header_actions');
  514. const [btnSwitch, bSwitch] = genMidBtn('⭕', panelSwitch);
  515. btnArea.appendChild(genSpace());
  516. btnArea.appendChild(btnSwitch);
  517. btnArea.appendChild(genSpace());
  518.  
  519. const panelArea = document.querySelector('.profile_leftcol');
  520. const panelMain = genDiv('aam_panel profile_customization');
  521. panelMain.style.display = 'none';
  522. panelArea.insertBefore(panelMain, panelArea.firstChild);
  523.  
  524. const busyPanel = genDiv('aam_busy');
  525. const busyPanelContent = genDiv('aam_busy_content');
  526. const busyMessage = genP(t('operating'));
  527. const busyImg = new Image();
  528. busyImg.src = 'https://steamcommunity-a.akamaihd.net/public/images/login/throbber.gif';
  529.  
  530. busyPanelContent.appendChild(busyMessage);
  531. busyPanelContent.appendChild(busyImg);
  532.  
  533. busyPanel.appendChild(busyPanelContent);
  534.  
  535. panelMain.appendChild(busyPanel);
  536.  
  537. const workPanel = genDiv('aam_busy aam_work');
  538. const workLog = genTextArea('', t('runningLog'),);
  539. const workHide = genButton(`❌${t('close')}`, () => { workScreen(false, null); }, true);
  540.  
  541. workPanel.appendChild(workLog);
  542. workPanel.appendChild(genSpan(t('logWaring')));
  543. workPanel.appendChild(workHide);
  544.  
  545. panelMain.appendChild(workPanel);
  546.  
  547. const leftPanel = genDiv('aam_left');
  548. const accountPanel = genDiv('aam_account');
  549. const accountTitle = genSpan(t('botListTitle'));
  550. const accountList = genList([], null);
  551. const accountBtns = genDiv('aam_btns');
  552. const acAdd = genButton(`➕${t('addCurrentAccount')}`, accountAdd);
  553. const acDel = genButton(`➖${t('delSelectAccount')}`, accountDel);
  554. const acUpdate = genButton(`🔄${t('reloadAccountPoints')}`, flashAllAccounts);
  555.  
  556. accountBtns.appendChild(acAdd);
  557. accountBtns.appendChild(acDel);
  558. accountBtns.appendChild(acUpdate);
  559.  
  560. accountPanel.appendChild(accountTitle);
  561. accountPanel.appendChild(genBr());
  562. accountPanel.appendChild(accountList);
  563. accountPanel.appendChild(accountBtns);
  564.  
  565. leftPanel.appendChild(accountPanel);
  566.  
  567. const historyPanel = genDiv('aam_history');
  568. historyPanel.style.display = 'none';
  569.  
  570. const historyTitle = genSpan(t('historyTitle'));
  571. const historyList = genList([], null);
  572. const historyBtns = genDiv('aam_btns');
  573. const hsProfile = genButton(`🌏${t('profile')}`, showProfile);
  574. const hsDelete = genButton(`➖${t('deleteSelectedHistory')}`, deleteHistory);
  575. const hsClear = genButton(`🗑️${t('clearHistory')}`, clearHistory);
  576. const hsReload = genButton(`🔄${t('reloadHistory')}`, flashHistoryList);
  577.  
  578. historyBtns.appendChild(hsProfile);
  579. historyBtns.appendChild(hsDelete);
  580. historyBtns.appendChild(hsClear);
  581. historyBtns.appendChild(hsReload);
  582.  
  583. historyPanel.appendChild(historyTitle);
  584. historyPanel.appendChild(genBr());
  585. historyPanel.appendChild(historyList);
  586. historyPanel.appendChild(historyBtns);
  587.  
  588. leftPanel.appendChild(historyPanel);
  589. panelMain.appendChild(leftPanel);
  590.  
  591. const awardPanel = genDiv('aam_award');
  592. const feedbackLink = genA(t('feedBack'), 'https://steamcommunity.com/id/Chr_/');
  593. feedbackLink.title = t('feedBackTitle');
  594. const awardBot = genSelect([[t('notSelected'), '']], null);
  595. const awardSteamID = genInput('', t('steamID64'), false);
  596. const awardPoints = genInput('', t('awardPoints'), true);
  597. const [awardCProfile, awardProfile] = genCheckbox(t('profile'), true);
  598. const [awardCRecommand, awardRecommand] = genCheckbox(t('recommands'), true);
  599. const [awardCScreenshot, awardScreenshot] = genCheckbox(t('screenshots'), true);
  600. const [awardCImage, awardImage] = genCheckbox(t('artworks'), true);
  601. const awardBtns1 = genDiv('aam_btns');
  602. const awardBtnCurrent = genButton(`🤵${t('setToCurrentUser')}`, getCurrentProfile);
  603. const awardBtnCalc = genButton(`📊${t('calculator')}`, calcAwardItems);
  604. const awardBtns2 = genDiv('aam_btns');
  605. const awardBtnSet = genButton(`💾${t('save')}`, applyAwardConfig);
  606. const awardBtnReset = genButton(`🔨${t('reset')}`, restoreAwardConfig);
  607. const hSwitch = genButton(`🕒${t('awardHistory')}`, leftPanelSwitch);
  608. const awardBtns3 = genDiv('aam_btns aam_award_btns');
  609. const awardBtnStart = genButton(`✅${t('startAward')}`, startAward, false);
  610. const awardBtnStop = genButton(`⛔${t('stopAward')}`, stopAward, false);
  611. const awardStatus = genSpan(`🟥 ${t('stop')}`);
  612.  
  613. awardBtns1.appendChild(awardBtnCurrent);
  614. awardBtns1.appendChild(awardBtnCalc);
  615.  
  616. awardBtns2.appendChild(awardBtnSet);
  617. awardBtns2.appendChild(awardBtnReset);
  618. awardBtns2.appendChild(hSwitch);
  619.  
  620. awardBtns3.appendChild(awardBtnStart);
  621. awardBtns3.appendChild(awardBtnStop);
  622. awardBtns3.appendChild(awardStatus);
  623.  
  624. awardPanel.appendChild(genSpan(t('senderBotAccount')));
  625. awardPanel.appendChild(feedbackLink);
  626. awardPanel.appendChild(genBr());
  627. awardPanel.appendChild(awardBot);
  628. awardPanel.appendChild(genSpan(t('receverAccount')));
  629. awardPanel.appendChild(genBr());
  630. awardPanel.appendChild(awardSteamID);
  631. awardPanel.appendChild(awardBtns1);
  632. awardPanel.appendChild(genSpan(t('sendPoints')));
  633. awardPanel.appendChild(genBr());
  634. awardPanel.appendChild(awardPoints);
  635. awardPanel.appendChild(genSpan(t('awardPrefer')));
  636. awardPanel.appendChild(genBr());
  637. awardPanel.appendChild(awardCProfile);
  638. awardPanel.appendChild(awardCRecommand);
  639. awardPanel.appendChild(genBr());
  640. awardPanel.appendChild(awardCScreenshot);
  641. awardPanel.appendChild(awardCImage);
  642. awardPanel.appendChild(genBr());
  643. awardPanel.appendChild(awardBtns2);
  644. awardPanel.appendChild(genHr());
  645. awardPanel.appendChild(awardBtns3);
  646.  
  647. panelMain.appendChild(awardPanel);
  648.  
  649. Object.assign(GObjs, {
  650. bSwitch, hSwitch, panelMain,
  651. busyPanel, busyMessage, workPanel, workLog, workHide,
  652. accountPanel, accountList, historyPanel, historyList,
  653. awardBot, awardSteamID, awardPoints, awardStatus,
  654. awardProfile, awardRecommand, awardScreenshot, awardImage,
  655. awardBtnStart, awardBtnStop, awardBtnSet, awardBtnReset
  656. });
  657. }
  658. //面板显示开关
  659. function panelSwitch() {
  660. const { bSwitch, panelMain } = GObjs;
  661.  
  662. if (GPanel.panelMain !== true) {
  663. panelMain.style.display = '';
  664. bSwitch.textContent = '🔴';
  665. GPanel.panelMain = true;
  666. } else {
  667. panelMain.style.display = 'none';
  668. bSwitch.textContent = '⭕';
  669. GPanel.panelMain = false;
  670. }
  671. GM_setValue('panel', GPanel);
  672. }
  673. //左侧面板切换
  674. function leftPanelSwitch() {
  675. const { hSwitch, accountPanel, historyPanel } = GObjs;
  676. if (GPanel.panelLeft !== true) {
  677. accountPanel.style.display = 'none';
  678. historyPanel.style.display = '';
  679. hSwitch.textContent = `🤖${t('botList')}`;
  680. GPanel.panelLeft = true;
  681. } else {
  682. historyPanel.style.display = 'none';
  683. accountPanel.style.display = '';
  684. hSwitch.textContent = `🕒${t('awardHistory')}`;
  685. GPanel.panelLeft = false;
  686. }
  687. GM_setValue('panel', GPanel);
  688. }
  689. //添加账户
  690. function accountAdd() {
  691. let v_nick, v_token, v_steamID;
  692. loadScreen(true, t('fetchLoginAccount'));
  693. getMySteamID()
  694. .then(({ nick, steamID }) => {
  695. v_nick = nick;
  696. v_steamID = steamID;
  697. loadScreen(true, t('fetchToken'));
  698. return getToken();
  699. })
  700. .then((tk) => {
  701. v_token = tk;
  702. loadScreen(true, t('fetchPoints'));
  703. return getPoints(v_steamID, tk);
  704. })
  705. .then((points) => {
  706. showAlert(t('success'), `<p>${t('addAccountSuccessTips1')}</p><p>${t('addAccountSuccessTips2')}: ${points} ${t('points')}</p>`, true);
  707. GBots[v_steamID] = { nick: v_nick, token: v_token, points };
  708. GM_setValue('bots', GBots);
  709. flashBotList();
  710. })
  711. .catch((reason) => {
  712. showAlert(t('error'), reason, false);
  713. }).finally(() => {
  714. loadScreen(false, null);
  715. });
  716. }
  717. //删除账户
  718. function accountDel() {
  719. const { accountList } = GObjs;
  720. if (accountList.selectedIndex >= 0) {
  721. showConfirm(t('confirm'), t('deleteAccountConfirmTips'), () => {
  722. let i = 0;
  723. for (const opt of accountList.selectedOptions) {
  724. delete GBots[opt.value];
  725. i++;
  726. }
  727. flashBotList();
  728. GM_setValue('bots', GBots);
  729. showAlert(t('tips'), `${t('deleteAccountDoneTips1')} ${i} ${t('deleteAccountDoneTips2')}`, true);
  730. }, null);
  731. } else {
  732. showAlert(t('tips'), t('notSelectedAnyBotsTips'), false);
  733. }
  734. }
  735. //刷新账户点数
  736. async function flashAllAccounts() {
  737. //刷新点数
  738. function makePromise(sid, tk) {
  739. return new Promise((resolve, reject) => {
  740. getPoints(sid, tk)
  741. .then((points) => {
  742. GBots[sid].points = points;
  743. loadScreen(true, `${t('currentProcess')}: ${++fin} / ${count}`);
  744. }).catch((reason) => {
  745. GBots[sid].points = -1;
  746. // GBots[sid].nick = '读取失败';
  747. loadScreen(true, `${sid} ${t('updateFailed')}: ${reason}`);
  748. }).finally(() => {
  749. GM_setValue('bots', GBots);
  750. resolve();
  751. });
  752. });
  753. }
  754. let count = 0, fin = 0;
  755. for (const _ in GBots) {
  756. count++;
  757. }
  758. if (count > 0) {
  759. loadScreen(true, t('fetchingAccountPoints'));
  760. const pList = [];
  761. for (const steamID in GBots) {
  762. const { token } = GBots[steamID];
  763. pList.push(makePromise(steamID, token));
  764. }
  765. Promise.all(pList)
  766. .finally(() => {
  767. loadScreen(false, null);
  768. flashBotList();
  769. if (fin >= count) {
  770. showAlert(t('done'), t('allDataLoaded'), true);
  771. } else {
  772. showAlert(t('done'), t('someDataLoadFailed'), true);
  773. }
  774. });
  775. } else {
  776. showAlert(t('error'), t('botListEmpty'), false);
  777. }
  778. }
  779. //刷新账户列表
  780. function flashBotList() {
  781. const { bot } = GTask;
  782. const { accountList, awardBot } = GObjs;
  783. accountList.options.length = 0;
  784. awardBot.options.length = 0;
  785. awardBot.options.add(new Option(t('notSelected'), ''));
  786. let i = 1;
  787. let flag = false;
  788. if (!isEmptyObject(GBots)) {
  789. for (const steamID in GBots) {
  790. const { nick, points } = GBots[steamID];
  791. const pointsStr = parseInt(points).toLocaleString();
  792. accountList.options.add(new Option(`${i} | ${nick} | ${steamID} | ${pointsStr} 点`, steamID));
  793. awardBot.options.add(new Option(`${i++} | ${nick} | ${pointsStr} 点`, steamID));
  794. if (steamID === bot) {
  795. flag = true;
  796. awardBot.selectedIndex = i - 1;
  797. }
  798. }
  799. } else {
  800. accountList.options.add(new Option(t('noBotAccountTips'), ''));
  801. }
  802. if ((!isEmptyObject(GTask)) && (!flag)) {
  803. GTask = {};
  804. GM_setValue('task', GTask);
  805. appllyTask();
  806. showAlert(t('tips'), t('awardTaskWasResetTips'), false);
  807. }
  808. }
  809. //刷新历史记录列表
  810. function flashHistoryList() {
  811. const { historyList } = GObjs;
  812. historyList.options.length = 0;
  813. let i = 1;
  814. if (!isEmptyObject(GHistory)) {
  815. for (const steamID in GHistory) {
  816. const [nick, points] = GHistory[steamID];
  817. const pointsStr = parseInt(points).toLocaleString();
  818. historyList.options.add(new Option(`${i++} | ${nick} | ${steamID} | ${pointsStr} 点`, steamID));
  819. }
  820. } else {
  821. historyList.options.add(new Option(t('noHistoryTips'), ''));
  822. }
  823. }
  824. //历史记录增加点数
  825. function addHistory(steamID, nick, points) {
  826. if (GHistory[steamID] !== undefined) {
  827. GHistory[steamID] = [nick, GHistory[steamID][1] + points];
  828. } else {
  829. GHistory[steamID] = [nick, points];
  830. }
  831. GM_setValue('history', GHistory);
  832. }
  833. //获取当前个人资料
  834. function getCurrentProfile() {
  835. const { awardSteamID } = GObjs;
  836. awardSteamID.value = g_rgProfileData.steamid;
  837. }
  838. //计算可打赏项目
  839. function calcAwardItems() {
  840. const { awardSteamID } = GObjs;
  841. const steamID = awardSteamID.value.trim();
  842. if (steamID === '') {
  843. showAlert(t('error'), t('steamIDisEmpty!'), false);
  844. } else {
  845. loadScreen(true, t('fetchingProfile'));
  846. getProfile(steamID)
  847. .then(([succ, nick]) => {
  848. if (succ) {
  849. loadScreen(true, t('fetchingAwardableItems'));
  850. const pList = [
  851. getAwardCounts(steamID, 'r'),
  852. getAwardCounts(steamID, 's'),
  853. getAwardCounts(steamID, 'i')
  854. ];
  855. Promise.all(pList)
  856. .then((result) => {
  857. const data = {};
  858. let sum = 0;
  859. for (const [type, succ, count] of result) {
  860. if (succ) {
  861. const points = count * 6600;
  862. data[type] = `${count} , ${t('awardableAmount')}: ${points.toLocaleString()} ${t('points')}`;
  863. sum += points;
  864. } else {
  865. data[type] = t('fetchError');
  866. }
  867. }
  868. let text = `<p>${t('nickName')}: ${nick}</p><p>${t('recommands')}: ${data.r}</p><p>${t('screenshots')}: ${data.s}</p><p>${t('artworks')}: ${data.i}</p><p>${t('totalPoints')}: ${sum.toLocaleString()}</p><p>*${t('calcTips')}*</p>`;
  869. showAlert(t('tips'), text, true);
  870. })
  871. .finally(() => {
  872. loadScreen(false, null);
  873. });
  874. } else {
  875. showAlert(t('error'), t('profileNotExistsTips'), false);
  876. loadScreen(false, null);
  877. }
  878. })
  879. .catch((reason) => {
  880. showAlert(t('error'), `<p>${t('profileLoadFailedTips')}</p><p>${reason}</p>`, false);
  881. loadScreen(false, null);
  882. });
  883. }
  884. }
  885. //查看个人资料
  886. function showProfile() {
  887. const { historyList } = GObjs;
  888. const i = historyList.selectedIndex;
  889. if (i > -1) {
  890. const { value } = historyList.options[i];
  891. if (value != '') {
  892. window.open(`https://steamcommunity.com/profiles/${value}`);
  893. }
  894. } else {
  895. showAlert(t('tips'), t('notSelectedAnyHistoryTips'), false);
  896. }
  897. }
  898. //清除历史
  899. function clearHistory() {
  900. if (!isEmptyObject(GHistory)) {
  901. showConfirm(t('confirm'), t('clearHistoryConfirmTips'), () => {
  902. GHistory = {};
  903. flashHistoryList();
  904. GM_setValue('history', GHistory);
  905. showAlert(t('tips'), t('clearHistorySuccess'), true);
  906. }, null);
  907. } else {
  908. showAlert(t('tips'), t('historyListEmpty'), false);
  909. }
  910. }
  911. //删除历史
  912. function deleteHistory() {
  913. const { historyList } = GObjs;
  914. if (historyList.selectedIndex >= 0) {
  915. showConfirm(t('confirm'), t('deleteHistoryConfirmTips'), () => {
  916. let i = 0;
  917. for (const opt of historyList.selectedOptions) {
  918. delete GHistory[opt.value];
  919. i++;
  920. }
  921. flashHistoryList();
  922. GM_setValue('history', GHistory);
  923. showAlert(t('tips'), `${t('deleteResultTips1')} ${i} ${t('deleteResultTips2')}`, true);
  924. }, null);
  925. } else {
  926. showAlert(t('tips'), t('notSelectedAnyHistoryTips'), false);
  927. }
  928. }
  929. //保存打赏设置
  930. async function applyAwardConfig() {
  931. const {
  932. awardBtnStart, awardBtnStop,
  933. awardBot, awardSteamID, awardPoints,
  934. awardProfile, awardRecommand, awardScreenshot, awardImage
  935. } = GObjs;
  936.  
  937. awardBtnStart.disabled = awardBtnStop.disabled = true;
  938.  
  939. let bot = awardBot.value;
  940. let points = parseInt(awardPoints.value);
  941. let steamID = String(awardSteamID.value).trim();
  942.  
  943. let type = 0;
  944. if (!awardProfile.checked) { type += 1; }
  945. if (!awardRecommand.checked) { type += 2; }
  946. if (!awardScreenshot.checked) { type += 4; }
  947. if (!awardImage.checked) { type += 8; }
  948.  
  949. if (bot == '') {
  950. // 未选择机器人则自动使用当前登录(不可用)账号
  951. loadScreen(true, t('fetchLoginAccount'));
  952. const nick = document.querySelector("#account_pulldown")?.textContent?.trim();
  953. if (g_steamID && nick) {
  954. try {
  955. loadScreen(true, t('fetchToken'));
  956. const token = await getToken();
  957. loadScreen(true, t('fetchPoints'));
  958. const points = await getPoints(g_steamID, token);
  959. GBots[g_steamID] = { nick: nick, token: token, points };
  960. GM_setValue('bots', GBots);
  961. flashBotList();
  962. bot = g_steamID;
  963. } catch (reason) {
  964. showAlert(t('error'), reason, false);
  965. } finally {
  966. loadScreen(false, null);
  967. }
  968. }
  969. }
  970.  
  971. if (bot == '') {
  972. showAlert(t('error'), t('notSelectedAwardBotsTips'), false);
  973. } else if (steamID === '') {
  974. showAlert(t('error'), t('steamIDEmptyWithTips'), false);
  975. } else if (!steamID.match(/^\d+$/)) {
  976. showAlert(t('error'), t('steamIDErrorWithTips'), false);
  977. } else if (points !== points || points < 100) {
  978. showAlert(t('error'), t('pointsErrorWithTips'), false);
  979. } else if (type === 15) {
  980. showAlert(t('error'), t('awardTypeEmptyTips'), false);
  981. } else {
  982. points = Math.ceil(points / 100) * 100;
  983. GTask = { bot, steamID, points, type, work: false, nick: null };
  984. awardBtnStart.disabled = awardBtnStop.disabled = false;
  985. GM_setValue('task', GTask);
  986. showAlert(t('tips'), t('awardReadyToStartTips'), true);
  987. }
  988. }
  989. //重置打赏设置
  990. function restoreAwardConfig() {
  991. showConfirm(t('confirm'), t('resetConfigConfirmTips'), () => {
  992. GTask = {};
  993. GM_setValue('task', GTask);
  994. appllyTask();
  995. showAlert(t('tips'), t('configResetSuccessTips'), true);
  996. }, null);
  997. }
  998. //读取设置到界面
  999. function appllyTask() {
  1000. const {
  1001. awardBtnStart, awardBtnStop,
  1002. awardBot, awardSteamID, awardPoints,
  1003. awardProfile, awardRecommand, awardScreenshot, awardImage
  1004. } = GObjs;
  1005. const { bot, steamID, points, type } = GTask;
  1006.  
  1007. awardBtnStart.disabled = awardBtnStop.disabled = isEmptyObject(GTask);
  1008.  
  1009. awardBot.value = bot ? bot : '';
  1010. awardSteamID.value = steamID ? steamID : '';
  1011. awardPoints.value = points ? points : '';
  1012.  
  1013. awardProfile.checked = !Boolean(type & 1);
  1014. awardRecommand.checked = !Boolean(type & 2);
  1015. awardScreenshot.checked = !Boolean(type & 4);
  1016. awardImage.checked = !Boolean(type & 8);
  1017. }
  1018. //开始自动打赏
  1019. async function startAward() {
  1020. if (isEmptyObject(GTask)) {
  1021. showAlert(t('error'), t('awardTaskDataInvalid'), false);
  1022. return;
  1023. }
  1024. const { steamID, work, points, bot, nick: taskNick } = GTask;
  1025. const { nick: botNick } = GBots[bot];
  1026. const pointsStr = parseInt(points).toLocaleString();
  1027.  
  1028. if (!work) {
  1029. spaceLine(1);
  1030. if (!taskNick) {
  1031. loadScreen(true, t('fetchingTargetProfile'));
  1032. getProfile(steamID)
  1033. .then(([succ, nickName]) => {
  1034. if (succ) {
  1035. GTask.work = true;
  1036. GTask.nick = nickName;
  1037. GM_setValue('task', GTask);
  1038. print(`${t('awardConfig')}:\n${t('targetNickName')}: ${nickName}, ${t('targetReceivePoints')}: ${pointsStr}, ${t('targetBot')}: ${botNick}〗`);
  1039. print(t('taskReadyToStartTips'));
  1040. workScreen(true);
  1041. setTimeout(() => {
  1042. autoAward();
  1043. }, 2000);
  1044. } else {
  1045. print(t('taskFailedProfileNotFound'), 'E');
  1046. showAlert(t('error'), t('profileNotExistsTips'), false);
  1047. }
  1048. })
  1049. .catch((reason) => {
  1050. showAlert(t('error'), `<p>${t('profileLoadFailedTips')}</p><p>${reason}</p>`, false);
  1051. }).finally(() => {
  1052. loadScreen(false, null);
  1053. });
  1054. } else {
  1055. GTask.work = true;
  1056. GM_setValue('task', GTask);
  1057. print(`〖${t('targetNickName')}: ${taskNick}, ${t('targetReceivePoints')}: ${pointsStr}, ${t('targetBot')}: ${botNick}〗`);
  1058. print(t('taskReadyToStartTips'));
  1059. workScreen(true);
  1060. setTimeout(() => {
  1061. autoAward();
  1062. }, 2000);
  1063. }
  1064. } else {
  1065. print(t('taskAlreadyStartTips'));
  1066. }
  1067. }
  1068. //停止自动打赏
  1069. async function stopAward() {
  1070. if (isEmptyObject(GTask)) {
  1071. showAlert(t('error'), t('awardTaskDataInvalid'), false);
  1072. return;
  1073. }
  1074. const { work } = GTask;
  1075. if (work) {
  1076. spaceLine(4);
  1077. print(t('taskEndManually'));
  1078. GTask.work = false;
  1079. GM_setValue('task', GTask);
  1080. showStatus(t('stop'), false);
  1081. } else {
  1082. showAlert(t('error'), t('taskNotStart'), false);
  1083. }
  1084. }
  1085. //打赏项目
  1086. const reactionsDict = {
  1087. 1: 300, 2: 300, 3: 300, 4: 300, 5: 300, 6: 300, 7: 300, 8: 300, 9: 600,
  1088. 10: 1200, 11: 2400, 12: 300, 13: 2400, 14: 600, 15: 1200, 16: 600,
  1089. 17: 4800, 18: 300, 19: 600, 20: 1200, 21: 300, 22: 600, 23: 300
  1090. };
  1091. const reactionValues = [
  1092. 300, 300, 300, 300, 300, 300, 300, 300, 600, 1200, 2400, 300,
  1093. 2400, 600, 1200, 600, 4800, 300, 600, 1200, 300, 600, 300
  1094. ];
  1095. const reactionIDs = [
  1096. 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12,
  1097. 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23
  1098. ];
  1099. //自动打赏
  1100. async function autoAward() {
  1101. //打赏类型
  1102. const reactionType = {
  1103. 'p': ['3', t('profile')], 'r': ['1', t('recommands')], 's': ['2', t('screenshots')], 'i': ['2', t('artworks')]
  1104. };
  1105. const { bot, steamID, type, points: pointsGoal, nick: taskNick } = GTask;
  1106. const { nick: botNick, token } = GBots[bot];
  1107.  
  1108. appllyTask();
  1109. addHistory(steamID, taskNick, 0);
  1110. showStatus(t('running'), true);
  1111. let pointsLeft = pointsGoal;
  1112.  
  1113. if (token) {
  1114. const workflow = [];
  1115. if (!Boolean(type & 8)) { workflow.push('i'); };
  1116. if (!Boolean(type & 4)) { workflow.push('s'); };
  1117. if (!Boolean(type & 2)) { workflow.push('r'); };
  1118. if (!Boolean(type & 1)) { workflow.push('p'); };
  1119.  
  1120. while (GTask.work && workflow.length > 0) {
  1121. const award_type = workflow.pop();
  1122. const [target_type, target_name] = reactionType[award_type];
  1123. let process = genProgressBar((pointsGoal - pointsLeft) / pointsGoal * 100);
  1124.  
  1125. spaceLine(3);
  1126. print(`【${target_name}】${t('taskStartPointsSummary')}: ${pointsLeft.toLocaleString()} / ${pointsGoal.toLocaleString()} ${t('points')}`);
  1127. print(`${t('currentProcess')}: ${process}`);
  1128. spaceLine(3);
  1129.  
  1130. let coast = 0;
  1131.  
  1132. if (target_type === '3') { //个人资料
  1133. let GoldReactions = null;
  1134. for (let i = 0; i < 3; i++) { //重试3次
  1135. if (GoldReactions === null) { //旧打赏列表为空,重新读取并打赏
  1136. const [succOld, oldReactions] = await getAwardRecords(token, target_type, steamID);
  1137. if (!succOld) {
  1138. print(t('fetchAwardItemFailedRetry'));
  1139. continue;
  1140. }
  1141. GoldReactions = oldReactions;
  1142. const todoReactions = selectFitableReactions(pointsLeft, GoldReactions);
  1143. if (todoReactions.length === 0) {
  1144. print(`【${target_name}】${t('fetchNoAwardItemSkip')}`);
  1145. break;
  1146. }
  1147. coast = sumReactionsPoints(todoReactions);
  1148. print(`【${target_name}】${t('beforeSendAward')}: ${todoReactions.length} ${t('itemAndTotal')}: ${coast.toLocaleString()} ${t('points')}`);
  1149. const plist = [];
  1150. for (const id of todoReactions) {
  1151. plist.push(sendAwardReaction(token, target_type, steamID, id));
  1152. }
  1153. print(t('sendingAwards'));
  1154. const result = await Promise.all(plist);
  1155. const [succ, fail] = countSuccess(result);
  1156. print(`${t('fetchSuccessAndFailed')}: ${succ} / ${fail}`);
  1157. }
  1158. //统计新的打赏列表,计算打赏点数
  1159. const [succNew, newReactions] = await getAwardRecords(token, target_type, steamID);
  1160. if (!succNew) {
  1161. print(t('fetchAwardItemFailedRetryIn2Min'));
  1162. await aiosleep(2500);
  1163. continue;
  1164. }
  1165. const diffReactions = filterDiffReactions(newReactions, GoldReactions);
  1166. coast = sumReactionsPoints(diffReactions);
  1167. pointsLeft -= coast;
  1168. addHistory(steamID, taskNick, coast);
  1169. print(`【${target_name}】${t('awardSuccess')}: ${diffReactions.length} ${t('itemAndTotal')}: ${coast.toLocaleString()} ${t('points')}`);
  1170. break;
  1171. }
  1172. GTask.points = pointsLeft;
  1173. if (pointsLeft <= 0) {
  1174. GTask.work = false;
  1175. }
  1176. GM_setValue('task', GTask);
  1177. process = genProgressBar((pointsGoal - pointsLeft) / pointsGoal * 100);
  1178.  
  1179. spaceLine(3);
  1180. print(`【${target_name}】${t('taskFinishedPointsSummary')}: ${pointsLeft.toLocaleString()} / ${pointsGoal.toLocaleString()} ${t('points')}`);
  1181. print(`${t('currentProcess')}: ${process}`);
  1182. spaceLine(3);
  1183.  
  1184. print(t('updateBotPointsBalance'));
  1185.  
  1186. await getPoints(bot, token)
  1187. .then((p) => {
  1188. GBots[bot].points = p;
  1189. GM_setValue('bots', GBots);
  1190. print(`${t('bot')}【${botNick}】${t('pointsBalanceUpdateSuccess')}: ${p.toLocaleString()} ${t('points')}`);
  1191. if (p < 300) {
  1192. print(`${t('bot')}【${botNick}】${t('lackOfPointsTaskEnd')}`);
  1193. GTask.work = false;
  1194. }
  1195. }).catch((r) => {
  1196. print(`${t('bot')}【${botNick}】${t('pointBalanceUpdateFailed')}: ${r}`);
  1197. });
  1198.  
  1199.  
  1200. } else { //截图
  1201. let page = 1;
  1202. while (GTask.work) {
  1203. let j = 0;
  1204. print(t('fetchingAwardableItems'));
  1205. const [succ, items] = await getAwardItems(steamID, award_type, page++);
  1206. if (!succ) {
  1207. page--;
  1208. if (++j < 3) {
  1209. print(t('fetchAwardItemFailedRetryIn2Min'));
  1210. await aiosleep(2500);
  1211. continue;
  1212. } else {
  1213. print(t('fetchAwardItemFailedSkip'));
  1214. break;
  1215. }
  1216. }
  1217. if (items.length === 0) {
  1218. print(`【${target_name}】${t('taskEndListEmpty')}`);
  1219. break;
  1220. }
  1221.  
  1222. print(`【${target_name}】${t('fetchCompletedTotal')} ${items.length} ${t('entries')}`);
  1223.  
  1224. for (const itemID of items) {
  1225.  
  1226. print(`【${target_name}】${t('objectID')}: ${itemID}`);
  1227. let GoldReactions = null;
  1228.  
  1229. for (let i = 0; i < 3; i++) {
  1230. if (GoldReactions === null) { //旧打赏列表为空,重新读取并打赏
  1231. const [succOld, oldReactions] = await getAwardRecords(token, target_type, itemID);
  1232. if (!succOld) {
  1233. print(t('fetchAwardItemFailedRetry'));
  1234. continue;
  1235. }
  1236. GoldReactions = oldReactions;
  1237. const todoReactions = selectFitableReactions(pointsLeft, GoldReactions);
  1238. if (todoReactions.length === 0) {
  1239. print(`【${target_name}】${t('noAwardableObjectSkip')}`);
  1240. break;
  1241. }
  1242. coast = sumReactionsPoints(todoReactions);
  1243. print(`【${target_name}】${t('willAward')}: ${todoReactions.length} ${t('itemAndTotal')}: ${coast.toLocaleString()} ${t('points')}`);
  1244. const plist = [];
  1245. for (const id of todoReactions) {
  1246. plist.push(sendAwardReaction(token, target_type, itemID, id));
  1247. }
  1248. print(t('sendingAwards'));
  1249. const result = await Promise.all(plist);
  1250. const [succ, fail] = countSuccess(result);
  1251. print(`${t('requestsSummary')}: ${succ} / ${fail}`);
  1252. }
  1253. print(t('wait2Seconds'));
  1254. await asleep(2000);
  1255. //统计新的打赏列表,计算打赏点数
  1256. const [succNew, newReactions] = await getAwardRecords(token, target_type, itemID);
  1257. if (!succNew) {
  1258. print(t('fetchAwardItemFailedRetry'));
  1259. continue;
  1260. }
  1261. const diffReactions = filterDiffReactions(newReactions, GoldReactions);
  1262. coast = sumReactionsPoints(diffReactions);
  1263. pointsLeft -= coast;
  1264. addHistory(steamID, taskNick, coast);
  1265. print(`【${target_name}】${t('awardSuccess')}: ${diffReactions.length} ${t('itemAndTotal')}: ${coast.toLocaleString()} ${t('points')}`);
  1266. break;
  1267. }
  1268. GTask.points = pointsLeft;
  1269. if (pointsLeft <= 0) {
  1270. GTask.work = false;
  1271. }
  1272. GM_setValue('task', GTask);
  1273. process = genProgressBar((pointsGoal - pointsLeft) / pointsGoal * 100);
  1274.  
  1275. spaceLine(3);
  1276. print(`【${target_name}】${t('taskFinishedPointsSummary')}: ${pointsLeft.toLocaleString()} / ${pointsGoal.toLocaleString()} ${t('points')}`);
  1277. print(`${t('currentProcess')}: ${process}`);
  1278. spaceLine(3);
  1279.  
  1280. print(t('updateBotPointsBalance'));
  1281.  
  1282. await getPoints(bot, token)
  1283. .then((p) => {
  1284. GBots[bot].points = p;
  1285. GM_setValue('bots', GBots);
  1286. print(`${t('bot')}【${botNick}】${t('pointsBalanceUpdateSuccess')}: ${p.toLocaleString()} ${t('points')}`);
  1287. if (p < 300) {
  1288. print(`${t('bot')}【${botNick}】${t('lackOfPointsTaskEnd')}`);
  1289. GTask.work = false;
  1290. }
  1291. }).catch((r) => {
  1292. print(`${t('bot')}【${botNick}】${t('pointBalanceUpdateFailed')}: ${r}`);
  1293. });
  1294.  
  1295. if (!GTask.work) {
  1296. break;
  1297. }
  1298. }
  1299. }
  1300. }
  1301. if (workflow.length > 0) {
  1302. await aiosleep(1500);
  1303. }
  1304. }
  1305. } else {
  1306. delete GBots[bot];
  1307. GM_setValue('bots', GBots);
  1308. print(t('botDataError'));
  1309. showAlert(t('error'), t('botDataError'), false);
  1310. }
  1311. spaceLine(4);
  1312. if (pointsLeft <= 0) {
  1313. GTask = {};
  1314. print(t('awardTaskFinish'));
  1315. } else {
  1316. GTask.work = false;
  1317. print(t('awardTaskNotFinish'));
  1318. }
  1319. GM_setValue('task', GTask);
  1320. appllyTask();
  1321. showStatus(t('stop'), false);
  1322. flashHistoryList();
  1323. }
  1324. //====================================================================================
  1325. //显示提示
  1326. function showAlert(title, text, succ = true) {
  1327. ShowAlertDialog(`${succ ? '✅' : '❌'}${title}`, `<div>${text}</div>`);
  1328. }
  1329. //显示确认
  1330. function showConfirm(title, text, done = null, cancel = null) {
  1331. ShowConfirmDialog(`⚠️${title}`, `<div>${text}</div>`, t('confirm'), t('cancel'))
  1332. .done(() => {
  1333. if (done) { done(); }
  1334. })
  1335. .fail(() => {
  1336. if (cancel) { cancel(); }
  1337. });
  1338. }
  1339. //显示状态
  1340. function showStatus(text, run = true) {
  1341. const { awardStatus, workHide } = GObjs;
  1342. workHide.disabled = run;
  1343. awardStatus.textContent = `${run ? '🟩' : '🟥'} ${text}`;
  1344. }
  1345. //读取设置
  1346. function loadConf() {
  1347. const bots = GM_getValue('bots');
  1348. GBots = isEmptyObject(bots) ? {} : bots;
  1349. const hs = GM_getValue('history');
  1350. GHistory = isEmptyObject(hs) ? {} : hs;
  1351. const task = GM_getValue('task');
  1352. GTask = isEmptyObject(task) ? {} : task;
  1353. const panel = GM_getValue('panel');
  1354. GPanel = isEmptyObject(panel) ? {} : panel;
  1355. }
  1356. //保存设置
  1357. function saveConf() {
  1358. GM_setValue('bots', GBots);
  1359. GM_setValue('history', GHistory);
  1360. GM_setValue('task', GTask);
  1361. GM_setValue('panel', GPanel);
  1362. }
  1363. //是不是空对象
  1364. function isEmptyObject(obj) {
  1365. for (const _ in obj) { return false; }
  1366. return true;
  1367. }
  1368. //显示加载面板
  1369. function loadScreen(show = true, msg = t('operating')) {
  1370. const { busyPanel, busyMessage } = GObjs;
  1371. if (show) {
  1372. busyPanel.style.opacity = '1';
  1373. busyPanel.style.visibility = 'visible';
  1374. if (msg) {
  1375. busyMessage.textContent = msg;
  1376. }
  1377. } else {
  1378. busyPanel.style.opacity = '0';
  1379. busyPanel.style.visibility = 'hidden';
  1380. }
  1381. }
  1382. //显示日志面板
  1383. function workScreen(show = true) {
  1384. const { workPanel } = GObjs;
  1385. if (show) {
  1386. workPanel.style.opacity = '1';
  1387. workPanel.style.visibility = 'visible';
  1388. } else {
  1389. workPanel.style.opacity = '0';
  1390. workPanel.style.visibility = 'hidden';
  1391. }
  1392. }
  1393. //生成进度条
  1394. const BAR_STYLE = '⣀⣄⣤⣦⣶⣷⣿';
  1395. function genProgressBar(percent) {
  1396. const full_symbol = '⣿';
  1397. const none_symbol = '⣀';
  1398. const percentStr = ` ${percent.toFixed(2)}%`;
  1399. if (percent >= 100) {
  1400. return full_symbol.repeat(40) + percentStr;
  1401. } else {
  1402. percent = percent / 100;
  1403. let full = Math.floor(percent * 40);
  1404. let rest = percent * 40 - full;
  1405. let middle = Math.floor(rest * 6);
  1406. if (percent !== 0 && full === 0 && middle === 0) { middle = 1; }
  1407. let d = Math.abs(percent - (full + middle / 6) / 40) * 100;
  1408. if (d < Number.POSITIVE_INFINITY) {
  1409. let m = BAR_STYLE[middle];
  1410. if (full === 40) { m = ""; }
  1411. return full_symbol.repeat(full) + m + BAR_STYLE[0].repeat(39 - full) + percentStr;
  1412. }
  1413. return none_symbol.repeat(40) + percentStr;
  1414. }
  1415. }
  1416. //日志时间
  1417. function formatTime() {
  1418. const date = new Date();
  1419. return `${date.toLocaleDateString()} ${date.toTimeString().substr(0, 8)}`;
  1420. }
  1421. //输出日志
  1422. function print(msg, level = 'I') {
  1423. const { workLog } = GObjs;
  1424. const time = formatTime();
  1425. workLog.value += `${time} - ${level} - ${msg}\n`;
  1426. workLog.scrollTop = workLog.scrollHeight;
  1427. console.log(`${time} - ${level} - ${msg}`);
  1428. }
  1429. //画分割线
  1430. function spaceLine(style = 1) {
  1431. switch (style) {
  1432. case 1:
  1433. print('#'.repeat(68));
  1434. return;
  1435. case 2:
  1436. print('='.repeat(68));
  1437. return;
  1438. case 3:
  1439. print('+'.repeat(68));
  1440. return;
  1441. case 4:
  1442. print('~'.repeat(68));
  1443. return;
  1444. }
  1445. }
  1446. //异步延时
  1447. function asleep(ms) {
  1448. return new Promise(resolve => setTimeout(resolve, ms));
  1449. }
  1450. //====================================================================================
  1451. //计算合适的打赏项目
  1452. function selectFitableReactions(goal, doneList) {
  1453. const fitableList = [];
  1454. const aviableList = [];
  1455. for (const id of reactionIDs) {
  1456. if (doneList.indexOf(id) === -1) {
  1457. aviableList.push(id);
  1458. }
  1459. }
  1460. aviableList.sort((a, b) => { return reactionsDict[a] - reactionsDict[b]; });
  1461. for (const id of aviableList) {
  1462. if (goal < 100) {
  1463. break;
  1464. }
  1465. const value = reactionsDict[id] / 3;
  1466. if (goal >= value) {
  1467. fitableList.push(id);
  1468. goal -= value;
  1469. }
  1470. }
  1471. return fitableList;
  1472. }
  1473. //获取新增打赏项目
  1474. function filterDiffReactions(newList, oldList) {
  1475. const diffList = [];
  1476. for (const id of newList) {
  1477. if (oldList.indexOf(id) === -1) {
  1478. diffList.push(id);
  1479. }
  1480. }
  1481. return diffList;
  1482. }
  1483. //计算打赏项目点数开销
  1484. function sumReactionsPoints(reactions) {
  1485. let points = 0;
  1486. for (const id of reactions) {
  1487. points += reactionsDict[id];
  1488. }
  1489. return points / 3;
  1490. }
  1491. //统计成功失败
  1492. function countSuccess(result) {
  1493. let succ = 0, fail = 0;
  1494. for (const r of result) {
  1495. if (r) {
  1496. succ++;
  1497. } else {
  1498. fail++;
  1499. }
  1500. }
  1501. return ([succ, fail]);
  1502. }
  1503. //异步延时
  1504. function aiosleep(ms) {
  1505. return new Promise(resolve => setTimeout(resolve, ms));
  1506. }
  1507. //====================================================================================
  1508. function getMySteamID() {
  1509. return new Promise((resolve, reject) => {
  1510. try {
  1511. const steamID = g_steamID;
  1512. const nick = document.querySelector("div.playerAvatar>a>img")?.getAttribute("alt");
  1513.  
  1514. if (nick && steamID) {
  1515. resolve({ nick, steamID });
  1516. } else {
  1517. reject(t('steamStoreNotLogin'));
  1518. }
  1519. } catch (err) {
  1520. reject(err);
  1521. }
  1522. });
  1523. }
  1524. function getToken() {
  1525. return new Promise((resolve, reject) => {
  1526. try {
  1527. let token = document.querySelector("#application_config")?.getAttribute("data-loyalty_webapi_token");
  1528.  
  1529. if (isEmptyObject(token)) {
  1530. reject(t('steamStoreNotLogin'));
  1531. }
  1532. else {
  1533. token = token.replace(/"/g, "");
  1534. resolve(token);
  1535. }
  1536. } catch (err) {
  1537. reject(err);
  1538. }
  1539. });
  1540. }
  1541. function getPoints(steamID, token) {
  1542. return new Promise((resolve, reject) => {
  1543. $http.get(`https://api.steampowered.com/ILoyaltyRewardsService/GetSummary/v1/?access_token=${token}&steamid=${steamID}`)
  1544. .then(({ response }) => {
  1545. if (isEmptyObject(response)) {
  1546. reject(t('steamStoreNotLogin'));
  1547. }
  1548. try {
  1549. const points = parseInt(response.summary.points);
  1550. if (points === points) {
  1551. resolve(points);
  1552. } else {
  1553. reject(t('parseDataFailedMaybeNetworkError'));
  1554. }
  1555. } catch (e) {
  1556. reject(t('parseDataFailedMaybeNetworkError'));
  1557. }
  1558. })
  1559. .catch((reason) => {
  1560. reject(reason);
  1561. });
  1562. });
  1563. }
  1564. function getProfile(steamID) {
  1565. return new Promise((resolve, reject) => {
  1566. $http.getText(`https://steamcommunity.com/profiles/${steamID}/?xml=1`)
  1567. .then((text) => {
  1568. try {
  1569. const match = text.match(/<steamID><!\[CDATA\[([\s\S]*)\]\]><\/steamID>/) ||
  1570. text.match(/<steamID>([\s\S]*)<\/steamID>/);
  1571. if (match) {
  1572. resolve([true, match[1].substring()]);
  1573. } else {
  1574. resolve([false, null]);
  1575. }
  1576. } catch (e) {
  1577. reject(e);
  1578. }
  1579. })
  1580. .catch((reason) => {
  1581. reject(reason);
  1582. });
  1583. });
  1584. }
  1585. function getAwardCounts(steamID, type) {
  1586. let subPath, preg;
  1587. switch (type) {
  1588. case 'r':
  1589. subPath = 'recommended/?l=schinese';
  1590. preg = /共 (\d+) 项条目/;
  1591. break;
  1592. case 's':
  1593. subPath = 'screenshots/?l=schinese&appid=0&sort=newestfirst&browsefilter=myfiles&view=grid';
  1594. preg = /共 (\d+) 张/;
  1595. break;
  1596. case 'i':
  1597. subPath = 'images/?l=schinese&appid=0&sort=newestfirst&browsefilter=myfiles&view=grid';
  1598. preg = /共 (\d+) 张/;
  1599. break;
  1600. default:
  1601. throw 'type错误';
  1602. }
  1603. return new Promise((resolve, reject) => {
  1604. $http.getText(`https://steamcommunity.com/profiles/${steamID}/${subPath}`)
  1605. .then((text) => {
  1606. try {
  1607. const match = text.match(preg);
  1608. const count = match ? Number(match[1]) : 0;
  1609. resolve([type, true, count]);
  1610. } catch (e) {
  1611. resolve([type, false, 0]);
  1612. }
  1613. })
  1614. .catch((reason) => {
  1615. console.error(reason);
  1616. resolve([type, false, 0]);
  1617. });
  1618. });
  1619. }
  1620. function getAwardItems(steamID, type, p = 1) {
  1621. let subPath, preg;
  1622. switch (type) {
  1623. case 'r':
  1624. subPath = `recommended/?p=${p}&l=schinese`;
  1625. preg = /id="RecommendationVoteUpBtn(\d+)"/g;
  1626. break;
  1627. case 's':
  1628. subPath = `screenshots/?p=${p}&view=grid&l=schinese`;
  1629. preg = /id="imgWallHover(\d+)"/g;
  1630. break;
  1631. case 'i':
  1632. subPath = `images/?p=${p}&view=grid&l=schinese`;
  1633. preg = /id="imgWallHover(\d+)"/g;
  1634. break;
  1635. default:
  1636. throw t('typeError');
  1637. }
  1638. return new Promise((resolve, reject) => {
  1639. $http.getText(`https://steamcommunity.com/profiles/${steamID}/${subPath}`)
  1640. .then((text) => {
  1641. try {
  1642. const result = [];
  1643. const matches = text.matchAll(preg);
  1644. for (const match of matches) {
  1645. result.push(match[1]);
  1646. }
  1647. resolve([true, result]);
  1648. } catch (e) {
  1649. console.error(e);
  1650. resolve([false, e]);
  1651. }
  1652. })
  1653. .catch((reason) => {
  1654. console.error(reason);
  1655. resolve([false, reason]);
  1656. });
  1657. });
  1658. }
  1659. function getAwardRecords(token, targetType, targetID) {
  1660. return new Promise((resolve, reject) => {
  1661. const params = `access_token=${token}&target_type=${targetType}&targetid=${targetID}`;
  1662. $http.get('https://api.steampowered.com/ILoyaltyRewardsService/GetReactions/v1/?' + params)
  1663. .then(({ response }) => {
  1664. const { reactionids } = response;
  1665. resolve([true, reactionids || []]);
  1666. })
  1667. .catch((reason) => {
  1668. console.error(reason);
  1669. resolve([false, null]);
  1670. });
  1671. });
  1672. }
  1673. function sendAwardReaction(token, targetType, targetID, reactionID) {
  1674. return new Promise((resolve, reject) => {
  1675. const params = `access_token=${token}&target_type=${targetType}&targetid=${targetID}&reactionid=${reactionID}`;
  1676. $http.post('https://api.steampowered.com/ILoyaltyRewardsService/AddReaction/v1/?' + params)
  1677. .then((json) => {
  1678. console.log(json);
  1679. resolve(true);
  1680. })
  1681. .catch((reason) => {
  1682. console.error(reason);
  1683. resolve(false);
  1684. });
  1685. });
  1686. }
  1687. })();
  1688. //====================================================================================
  1689. class Request {
  1690. constructor(timeout = 3000) {
  1691. this.timeout = timeout;
  1692. }
  1693. get(url, opt = {}) {
  1694. return this.baseRequest(url, 'GET', opt, 'json');
  1695. }
  1696. getHtml(url, opt = {}) {
  1697. return this.baseRequest(url, 'GET', opt, '');
  1698. }
  1699. getText(url, opt = {}) {
  1700. return this.baseRequest(url, 'GET', opt, 'text');
  1701. }
  1702. post(url, data, opt = {}) {
  1703. opt.data = JSON.stringify(data);
  1704. return this.baseRequest(url, 'POST', opt, 'json');
  1705. }
  1706. baseRequest(url, method = 'GET', opt = {}, responseType = 'json') {
  1707. Object.assign(opt, {
  1708. url, method, responseType, timeout: this.timeout
  1709. });
  1710. return new Promise((resolve, reject) => {
  1711. opt.ontimeout = opt.onerror = reject;
  1712. opt.onload = ({ readyState, status, response, responseText }) => {
  1713. if (readyState === 4 && status === 200) {
  1714. if (responseType == 'json') {
  1715. resolve(response);
  1716. } else if (responseType == 'text') {
  1717. resolve(responseText);
  1718. }
  1719. } else {
  1720. console.error("网络错误");
  1721. console.log(readyState);
  1722. console.log(status);
  1723. console.log(response);
  1724. reject("解析失败");
  1725. }
  1726. };
  1727. GM_xmlhttpRequest(opt);
  1728. });
  1729. }
  1730. }
  1731. const $http = new Request();
  1732.  
  1733. //CSS表
  1734. GM_addStyle(`.aam_panel,
  1735. .aam_work {
  1736. padding: 10px;
  1737. display: flex;
  1738. }
  1739. .aam_work {
  1740. z-index: 500 !important;
  1741. }
  1742. .aam_busy {
  1743. width: 100%;
  1744. height: 100%;
  1745. z-index: 700;
  1746. position: absolute;
  1747. top: 0;
  1748. left: 0;
  1749. background: rgba(0, 0, 0, 0.7);
  1750. display: table;
  1751. visibility: hidden;
  1752. opacity: 0;
  1753. transition: all 0.1s;
  1754. }
  1755. .aam_busy_content {
  1756. display: table-cell;
  1757. vertical-align: middle;
  1758. text-align: center;
  1759. }
  1760. .aam_left {
  1761. width: 61%;
  1762. padding-right: 10px;
  1763. }
  1764. .aam_award {
  1765. width: 39%;
  1766. }
  1767. .aam_list,
  1768. .aam_select,
  1769. .aam_input,
  1770. .aam_textarea {
  1771. background-color: #fff !important;
  1772. color: #000 !important;
  1773. border: none !important;
  1774. border-radius: 0 !important;
  1775. }
  1776. .aam_input {
  1777. width: 98% !important;
  1778. text-align: center;
  1779. }
  1780. .aam_list {
  1781. height: 230px;
  1782. }
  1783. .aam_textarea {
  1784. height: calc(100% - 85px);
  1785. width: calc(100% - 45px);
  1786. resize: none;
  1787. font-size: 12px;
  1788. }
  1789. .aam_left > div > *,
  1790. .aam_award:not(span, button) > * {
  1791. width: 100%;
  1792. margin-bottom: 5px;
  1793. }
  1794. .aam_btns > button:not(:last-child) {
  1795. margin-right: 4px;
  1796. }
  1797. .aam_award_btns {
  1798. z-index: 600;
  1799. bottom: 10px;
  1800. position: absolute;
  1801. }
  1802. .aam_work > * {
  1803. position: absolute;
  1804. }
  1805. .aam_work > span {
  1806. bottom: 12%;
  1807. left: 70px;
  1808. }
  1809. .aam_work > button {
  1810. bottom: 11%;
  1811. }
  1812. .aam_a {
  1813. margin-left: 110px;
  1814. }`);

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址