Auto_Award_Muli

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

目前為 2023-01-15 提交的版本,檢視 最新版本

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

QingJ © 2025

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