- // ==UserScript==
- // @name Auto_Award_Muli
- // @name:zh-CN Steam自动打赏【极速多账户版】
- // @namespace https://blog.chrxw.com
- // @contributionURL https://afdian.com/@chr233
- // @version 2.3
- // @description Steam自动打赏 — 极速多账户版
- // @description:zh-CN Steam自动打赏 — 极速多账户版
- // @author Chr_
- // @include /^https:\/\/steamcommunity\.com\/id\/[^/]+/?$/
- // @include /^https:\/\/steamcommunity\.com\/profiles\/\d+/?$/
- // @connect steamcommunity.com
- // @connect steampowered.com
- // @connect api.steampowered.com
- // @license AGPL-3.0
- // @icon https://blog.chrxw.com/favicon.ico
- // @grant GM_setValue
- // @grant GM_getValue
- // @grant GM_xmlhttpRequest
- // @grant GM_addStyle
- // @grant GM_setClipboard
- // @grant GM_registerMenuCommand
- // ==/UserScript==
-
- (() => {
- 'use strict';
-
- // 多语言
- const LANG = {
- 'ZH': {
- 'done': '完成',
- 'changeLang': '修改语言',
- 'langName': '中文',
- 'operating': '操作进行中……',
- 'runningLog': '运行日志',
- 'close': '关闭',
- 'logWaring': '【日志不会保存, 打赏记录可以在【打赏历史】中查看】',
- 'botListTitle': '【机器人账户管理】【ID | 账户名 | SteamID | 点数余额】',
- 'addCurrentAccount': '添加当前账号',
- 'delSelectAccount': '删除选中账号',
- 'reloadAccountPoints': '刷新所有账号点数',
- 'historyTitle': '【点数打赏历史记录】【ID | 昵称 | SteamID | 收到点数】',
- 'profile': '个人资料',
- 'deleteSelectedHistory': '删除选中',
- 'clearHistory': '清空历史',
- 'reloadHistory': '刷新历史',
- 'feedBack': '作者',
- 'feedBackTitle': '觉得好用也欢迎给我打赏',
- 'notSelected': '---自动使用当前登录(不可用)账号---',
- 'steamID64': 'Steam 64位 ID',
- 'awardPoints': '打赏点数(收到)',
- 'recommands': '评测',
- 'screenshots': '截图',
- 'artworks': '艺术作品',
- 'setToCurrentUser': '设置为当前用户',
- 'calculator': '打赏计算器',
- 'save': '保存',
- 'reset': '重置',
- 'awardHistory': '打赏历史',
- 'startAward': '开始打赏',
- 'stopAward': '停止打赏',
- 'stop': '停止',
- 'senderBotAccount': '打赏机器人账户: ',
- 'receverAccount': '被打赏人SteamID: ',
- 'sendPoints': '打赏点数(收到): ',
- 'awardPrefer': '打赏类型(优先级从上到下从左到右): ',
- 'botList': '机器人列表',
- 'fetchLoginAccount': '获取登陆账户……',
- 'fetchToken': '获取Token……',
- 'fetchPoints': '获取点数信息……',
- 'success': '成功',
- 'failure': '失败',
- 'error': '错误',
- 'confirm': '确认',
- 'tips': '提示',
- 'addAccountSuccessTips1': '添加账户成功',
- 'addAccountSuccessTips2': '当前账户可用点数',
- 'deleteAccountConfirmTips': '确定要删除选定的账号吗?',
- 'deleteAccountDoneTips1': '删除了',
- 'deleteAccountDoneTips2': '个机器人',
- 'notSelectedAnyBotsTips': '尚未选中任何机器人!',
- 'currentProcess': '当前进度',
- 'updateFailed': '更新出错',
- 'fetchingAccountPoints': '读取账户点数中……',
- 'allDataLoaded': '所有数据刷新完毕',
- 'someDataLoadFailed': '部分数据刷新失败, 如果点数显示为【-1】,代表数据刷新失败',
- 'botListEmpty': '机器人列表为空',
- 'noBotAccountTips': '-- 无机器人账号, 请使用【➕添加当前账号】自动添加 --',
- 'awardTaskWasResetTips': '机器人账号已修改, 打赏设置已重置!',
- 'noHistoryTips': '-- 无历史记录, 执行打赏任务后会自动记录 --',
- 'steamIDisEmpty': '未填入SteamID!',
- 'fetchingProfile': '获取个人资料中……',
- 'fetchingAwardableItems': '获取可打赏项目……',
- 'fetchError': '读取出错',
- 'awardableAmount': '可打赏约',
- 'nickName': '用户名',
- 'totalPoints': '总计点数',
- 'calcTips': '根据项目数量计算所得, 不准确',
- 'profileNotExistsTips': '个人资料不存在, 请检查SteamID是否正确, 或者使用【🤵设置为当前用户】自动获取。',
- 'profileLoadFailedTips': '网络错误, 读取个人资料失败',
- 'notSelectedAnyHistoryTips': '未选中历史记录!',
- 'clearHistoryConfirmTips': '确定要清除打赏历史记录吗?',
- 'clearHistorySuccess': '清除成功',
- 'historyListEmpty': '历史记录是空的!',
- 'deleteHistoryConfirmTips': '确定要删除选定的打赏历史记录吗?',
- 'deleteResultTips1': '删除了',
- 'deleteResultTips2': '条打赏历史记录',
- 'notSelectedAwardBotsTips': '尚未选择打赏机器人!',
- 'steamIDEmptyWithTips': '未填写【被打赏人SteamID】, 建议使用【🤵设置为当前用户】功能!',
- 'steamIDErrorWithTips': '【被打赏人SteamID】格式有误, 建议使用【🤵设置为当前用户】功能!',
- 'pointsErrorWithTips': '【打赏点数】格式有误, 只能为整数!',
- 'awardTypeEmptyTips': '请选择【打赏类型】!',
- 'awardReadyToStartTips': '设置保存成功, 可以【✅开始打赏】了',
- 'resetConfigConfirmTips': '确定要重置设定吗?',
- 'configResetSuccessTips': '设置已清除',
- 'awardTaskDataInvalid': '任务数据非法',
- 'fetchingTargetProfile': '读取被打赏人个人资料……',
- 'awardConfig': '打赏设置',
- 'targetNickName': '被打赏人昵称',
- 'targetReceivePoints': '预计收到点数',
- 'targetBot': '打赏机器人',
- 'taskReadyToStartTips': '打赏任务【2秒】后开始, 点击【⛔停止打赏】可以提前终止操作!',
- 'taskFailedProfileNotFound': '未找到个人资料, 打赏进程停止!',
- 'taskAlreadyStartTips': '打赏任务已经开始了!',
- 'taskEndManually': '打赏任务手动终止, 点击【❌关闭】可以关闭面板.',
- 'taskNotStart': '打赏任务未开始!',
- 'running': '运行',
- 'taskStartPointsSummary': '开始打赏, 剩余打赏 / 预计打赏',
- 'fetchAwardItemFailedRetry': '获取打赏项目失败, 重试……',
- 'fetchNoAwardItemSkip': '没有合适的打赏, 跳过……',
- 'beforeSendAward': '将要打赏',
- 'itemAndTotal': '项, 总计',
- 'points': '点',
- 'sendingAwards': '发送打赏中……',
- 'fetchSuccessAndFailed': '请求成功 / 请求失败',
- 'fetchAwardItemFailedRetryIn2Min': '获取打赏项目失败, 【2秒】后重试……',
- 'awardSuccess': '成功打赏',
- 'taskFinishedPointsSummary': '打赏完成, 剩余打赏 / 预计打赏',
- 'updateBotPointsBalance': '更新机器人点数余额……',
- 'bot': '机器人',
- 'pointsBalanceUpdateSuccess': '点数余额更新成功, 可用点数',
- 'lackOfPointsTaskEnd': '点数余额不足, 终止操作',
- 'pointBalanceUpdateFailed': '点数余额更新失败',
- 'fetchAwardItemFailedSkip': '获取打赏项目失败, 跳过……',
- 'taskEndListEmpty': '列表为空, 结束',
- 'fetchCompletedTotal': '获取成功, 共',
- 'entries': '个',
- 'objectID': '项目ID',
- 'noAwardableObjectSkip': '没有合适的打赏, 跳过',
- 'willAward': '将要打赏',
- 'requestsSummary': '请求成功 / 请求失败',
- 'wait2Seconds': '*等待2秒,防止打赏过多*',
- 'botDataError': '机器人数据错误, 无法开始打赏!',
- 'awardTaskFinish': '✅打赏任务完成, 点击【❌关闭】可以关闭面板。',
- 'awardTaskNotFinish': '⛔打赏任务未完成, 点击【❌关闭】可以关闭面板。',
- 'cancel': '取消',
- 'steamStoreNotLogin': '【STEAM商店】未登录(不可用),请重新登录(不可用)',
- 'parseDataFailedMaybeNetworkError': '解析数据失败, 可能是Token失效或者网络错误',
- 'typeError': 'type错误',
- 'networkError': '网络错误',
- 'parseError': '解析出错',
- 'importAccount': '手动导入账号',
- 'importAccountSteamId64': '请输入Steam64位ID',
- 'importAccountInvalidSteamId64': '请输入正确的SteamID',
- 'importAccountGetToken': '请访问【 https://store.steampowered.com/pointssummary/ajaxgetasyncconfig 】,并把所有内容粘贴到下面',
- 'importAccountNickName': '请输入机器人显示昵称',
- 'importAccountNickNameTips': '手动添加',
- 'importAccountSuccess': '添加账号成功, 请手动刷新点数',
- },
- 'EN': {
- 'done': 'Done',
- 'changeLang': 'Change Language',
- 'langName': 'English',
- 'operating': 'Operating……',
- 'runningLog': 'Log',
- 'close': 'Close',
- 'logWaring': '【Log will not save, award history will list in【History】】',
- 'botListTitle': '【Bot Accounts】【ID | NickName | SteamID | Points Balance】',
- 'addCurrentAccount': 'Add Account',
- 'delSelectAccount': 'Del Selected',
- 'reloadAccountPoints': 'Refresh Points',
- 'historyTitle': '【History】【ID | NickName | SteamID | Received Points】',
- 'profile': 'Profile',
- 'deleteSelectedHistory': 'Del Selected',
- 'clearHistory': 'Clear',
- 'reloadHistory': 'Reload',
- 'feedBack': 'Author',
- 'feedBackTitle': '觉得好用也欢迎给我打赏',
- 'notSelected': '---Use Current Account---',
- 'steamID64': 'Steam 64 ID',
- 'awardPoints': 'Points (Receive)',
- 'recommands': 'Recommands',
- 'screenshots': 'Screenshots',
- 'artworks': 'Artworks',
- 'setToCurrentUser': 'Set to page\'s user',
- 'calculator': 'Calculator',
- 'save': 'Save',
- 'reset': 'Reset',
- 'awardHistory': 'History',
- 'startAward': 'Start Award',
- 'stopAward': 'Stop',
- 'stop': 'Stopped',
- 'senderBotAccount': 'Award Bot: ',
- 'receverAccount': 'Target SteamID: ',
- 'sendPoints': 'Points (Receive): ',
- 'awardPrefer': 'Award Type (Up to down left to right): ',
- 'botList': 'Bots List',
- 'fetchLoginAccount': 'Fetching current logined account……',
- 'fetchToken': 'Fetching token……',
- 'fetchPoints': 'Fetching points balance……',
- 'success': 'Success',
- 'failure': 'Failure',
- 'error': 'Error',
- 'confirm': 'Confirm',
- 'tips': 'Tips',
- 'addAccountSuccessTips1': 'Add account successful',
- 'addAccountSuccessTips2': 'Current account\'s points balance',
- 'deleteAccountConfirmTips': 'Are sure to delete selected bots?',
- 'deleteAccountDoneTips1': 'Deleted',
- 'deleteAccountDoneTips2': 'bots',
- 'notSelectedAnyBotsTips': 'You have not selected any bot!',
- 'currentProcess': 'Current process',
- 'updateFailed': 'Update Failed',
- 'fetchingAccountPoints': 'Fetching points balance……',
- 'allDataLoaded': 'All data loaded',
- 'someDataLoadFailed': 'Some data loaded failed, if some bot\'s balance is 【-1】, it means fetch error',
- 'botListEmpty': 'Bot list is empty',
- 'noBotAccountTips': '-- No Bot Account, you can add via【➕Add ACcount】(Loginin required) --',
- 'awardTaskWasResetTips': 'Bot account had been modified, Award config reseted!',
- 'noHistoryTips': '-- No History Records, It Will Record When Awarding --',
- 'steamIDisEmpty': 'You must specify SteamID!',
- 'fetchingProfile': 'Fetching profile……',
- 'fetchingAwardableItems': 'Fetching awardable items……',
- 'fetchError': 'Fetch error',
- 'awardableAmount': '可打赏约',
- 'nickName': 'Nickname',
- 'totalPoints': 'Total points',
- 'calcTips': 'According to the total number of the items, inaccurate',
- 'profileNotExistsTips': 'Profile not found, please check if the steamID is correct, or use 【🤵Set to page\'s user】 instead',
- 'profileLoadFailedTips': 'Network error, fetch profile failed',
- 'notSelectedAnyHistoryTips': 'No records selected!',
- 'clearHistoryConfirmTips': 'Are you sure to clear all award records?',
- 'clearHistorySuccess': 'History cleared',
- 'historyListEmpty': 'History is empty!',
- 'deleteHistoryConfirmTips': 'Are you sure to delete seleted award records?',
- 'deleteResultTips1': 'Deleted',
- 'deleteResultTips2': 'history records',
- 'notSelectedAwardBotsTips': 'No bots selected!',
- 'steamIDEmptyWithTips': '【Target SteamID】is empty, It is recommended to use【🤵Set to page\'s user】!',
- 'steamIDErrorWithTips': '【Target SteamID】is invalid, It is recommended to use【🤵Set to page\'s user】!',
- 'pointsErrorWithTips': '【Points】is invalid, only integers are accepted!',
- 'awardTypeEmptyTips': 'Please select【Award Type】!',
- 'awardReadyToStartTips': 'Config saved, it is ready to【✅Start Award】',
- 'resetConfigConfirmTips': 'Are you sure to reset the config?',
- 'configResetSuccessTips': 'Config reseted!',
- 'awardTaskDataInvalid': 'Task data invalid',
- 'fetchingTargetProfile': 'Fetching target user\'s profile……',
- 'awardConfig': 'Award config',
- 'targetNickName': 'Target user\'s nickname',
- 'targetReceivePoints': 'Expected points received',
- 'targetBot': 'Selected bot',
- 'taskReadyToStartTips': 'Award task will start in 【2 seconds】, click【⛔Stop】to interrupt award task!',
- 'taskFailedProfileNotFound': 'Profile not found, award task end!',
- 'taskAlreadyStartTips': 'Award task is already running!',
- 'taskEndManually': 'Award task interrupted manually, click【❌Close】to hide the log panel.',
- 'taskNotStart': 'Award task not running!',
- 'running': 'Running',
- 'taskStartPointsSummary': 'Start sending award, points left / points expected',
- 'fetchAwardItemFailedRetry': 'Fetch awardable items failed, retry……',
- 'fetchNoAwardItemSkip': 'No suitable award items, skip……',
- 'beforeSendAward': 'Will send award',
- 'itemAndTotal': 'items, total',
- 'points': 'points',
- 'sendingAwards': 'Sending awards……',
- 'fetchSuccessAndFailed': 'Success / Failure',
- 'fetchAwardItemFailedRetryIn2Min': 'Fetch awardable items failed, will retry in【2 seconds】……',
- 'awardSuccess': 'Successful send award',
- 'taskFinishedPointsSummary': 'Award task complete, points left / points expected',
- 'updateBotPointsBalance': 'Update bot\'s points balance……',
- 'bot': 'bots',
- 'pointsBalanceUpdateSuccess': 'points balance, avilable points',
- 'lackOfPointsTaskEnd': 'Lack of points balance, stop operation',
- 'pointBalanceUpdateFailed': 'Update points balance failed',
- 'fetchAwardItemFailedSkip': 'Fetch awardable items failed, skip……',
- 'taskEndListEmpty': 'Award items list is empty, end',
- 'fetchCompletedTotal': 'Fetch success, total',
- 'entries': 'entries',
- 'objectID': 'Target ID',
- 'noAwardableObjectSkip': 'No suitable award items, skip...',
- 'willAward': 'Will send award',
- 'requestsSummary': 'Success / Failure',
- 'wait2Seconds': '*Delay 2 seconds, to avoid exceed award*',
- 'botDataError': 'Bot data error, can\'t start award task!',
- 'awardTaskFinish': '✅Award task completed, click【❌Close】to hide the log panel',
- 'awardTaskNotFinish': '⛔Award task not completed, click【❌close】to hide the log panel',
- 'cancel': 'Cancel',
- 'steamStoreNotLogin': '【STEAM Store】not logined, please sign in first',
- 'parseDataFailedMaybeNetworkError': 'Parse data failed, maybe token expired or network error',
- 'typeError': 'Type Error',
- 'networkError': 'Network Error',
- 'parseError': 'Parse Error',
- 'importAccount': '手动导入账号',
- 'importAccountSteamId64': '请输入Steam64位ID',
- 'importAccountInvalidSteamId64': '请输入正确的SteamID',
- 'importAccountGetToken': '请访问【 https://store.steampowered.com/pointssummary/ajaxgetasyncconfig 】,并把所有内容粘贴到下面',
- 'importAccountNickName': '请输入机器人显示昵称',
- 'importAccountNickNameTips': '手动添加',
- 'importAccountSuccess': '添加账号成功, 请手动刷新点数',
- }
- };
-
- // 判断语言
- let language = GM_getValue("lang", null);
- if (!(language in LANG)) {
- showAlert('申明', `<p>本脚本现已免费提供</p><p>如果你在<a href="https://afdian.com/a/chr233">爱发电</a>以外的地方购买了本脚本, 请申请退款</p><p>觉得好用也欢迎给 <a href="https://steamcommunity.com/id/Chr_">作者</a> 打赏</p>`, true);
- language = "ZH";
- GM_setValue("lang", language);
- }
- // 获取翻译文本
- function t(key) {
- return LANG[language][key] || key;
- }
- {// 自动弹出提示
- const languageTips = GM_getValue("languageTips", true);
- if (languageTips && language === "ZH") {
- if (!document.querySelector("html").lang.startsWith("zh")) {
- ShowConfirmDialog("tips", "Auto Award now support English, switch?", "Using English", "Don't show again")
- .done(() => {
- GM_setValue("lang", "EN");
- GM_setValue("languageTips", false);
- window.location.reload();
- })
- .fail((bool) => {
- if (bool) {
- showAlert("", "You can switch the plugin's language using TamperMonkey's menu.");
- GM_setValue("languageTips", false);
- }
- });
- }
- }
- }
- GM_registerMenuCommand(`${t("changeLang")} (${t("langName")})`, () => {
- switch (language) {
- case "EN":
- language = "ZH";
- break;
- case "ZH":
- language = "EN";
- break;
- }
- GM_setValue("lang", language);
- window.location.reload();
- });
-
- GM_registerMenuCommand(t("importAccount"), () => {
- const steamID = prompt(t("importAccountSteamId64"));
- const v_steamID = parseInt(steamID);
- if (v_steamID !== v_steamID) {
- alert(t("importAccountInvalidSteamId64"));
- return;
- }
- const ajaxJson = prompt(t("importAccountGetToken"));
- const json = JSON.parse(ajaxJson);
- const token = json?.data?.webapi_token;
- if (!token) {
- alert(t("parseError"));
- return;
- }
-
- let botCount = 0;
- for (let _ in GBots) {
- botCount++;
- }
- const nick = prompt(t("importAccountNickName"), `${t("importAccountNickNameTips")} ${botCount}`);
-
- alert(t("importAccountSuccess"));
- GBots[steamID] = { nick, token, points: -1 };
- GM_setValue('bots', GBots);
- flashBotList();
- });
-
- //机器人账号
- let GBots = {};
- //打赏历史记录
- let GHistory = {};
- //打赏任务
- let GTask = {};
- //面板状态
- let GPanel = {};
- //控件字典
- let GObjs = {};
-
- //初始化
- (() => {
- loadConf();
-
- graphGUI();
- flashBotList();
- flashHistoryList();
-
- const { panelMain, panelLeft } = GPanel;
- if (panelMain) {
- GPanel.panelMain = false;
- panelSwitch();
- }
- if (panelLeft) {
- GPanel.panelLeft = false;
- leftPanelSwitch();
- }
- if (!isEmptyObject(GTask)) {
- GTask.work = false;
- }
- appllyTask();
- })();
-
- //====================================================================================
- //添加控制面板
- function graphGUI() {
- function genButton(text, foo, enable = true) {
- const b = document.createElement('button');
- b.textContent = text;
- b.className = 'aam_button';
- b.disabled = !enable;
- b.addEventListener('click', foo);
- return b;
- }
- function genDiv(cls = 'aam_div') {
- const d = document.createElement('div');
- d.className = cls;
- return d;
- }
- function genA(text, url) {
- const a = document.createElement('a');
- a.textContent = text;
- a.className = 'aam_a';
- a.target = '_blank';
- a.href = url;
- return a;
- }
- function genInput(value, tips, number = false) {
- const i = document.createElement('input');
- i.className = 'aam_input';
- if (value) { i.value = value; }
- if (tips) { i.placeholder = tips; }
- if (number) {
- i.type = 'number';
- i.step = 100;
- i.min = 0;
- }
- return i;
- }
- function genTextArea(value, tips) {
- const i = document.createElement('textarea');
- i.className = 'aam_textarea';
- if (value) { i.value = value; }
- if (tips) { i.placeholder = tips; }
- return i;
- }
- function genCheckbox(name, checked = false) {
- const l = document.createElement('label');
- const i = document.createElement('input');
- const s = genSpace(name);
- i.textContent = name;
- i.title = name;
- i.type = 'checkbox';
- i.className = 'aam_checkbox';
- i.checked = checked;
- l.appendChild(i);
- l.appendChild(s);
- return [l, i];
- }
- function genSelect(choose = [], choice = null) {
- const s = document.createElement('select');
- s.className = 'aam_select';
- choose.forEach(([text, value]) => {
- s.options.add(new Option(text, value));
- });
- if (choice) { s.value = choice; }
- return s;
- }
- function genList(choose = [], choice = null) {
- const s = genSelect(choose, choice);
- s.className = 'aam_list';
- s.setAttribute('multiple', 'multiple');
- return s;
- }
- function genP(text) {
- const p = document.createElement('p');
- p.textContent = text;
- return p;
- }
- function genSpan(text = ' ') {
- const s = document.createElement('span');
- s.textContent = text;
- return s;
- }
- const genSpace = genSpan;
- function genBr() {
- return document.createElement('br');
- }
- function genHr() {
- return document.createElement('hr');
- }
- function genMidBtn(text, foo) {
- const a = document.createElement('a');
- const s = genSpan(text);
- a.className = 'btn_profile_action btn_medium';
- a.addEventListener('click', foo);
- a.appendChild(s);
- return [a, s];
- }
-
- const btnArea = document.querySelector('.profile_header_actions');
- const [btnSwitch, bSwitch] = genMidBtn('⭕', panelSwitch);
- btnArea.appendChild(genSpace());
- btnArea.appendChild(btnSwitch);
- btnArea.appendChild(genSpace());
-
- const panelArea = document.querySelector('.profile_leftcol');
- const panelMain = genDiv('aam_panel profile_customization');
- panelMain.style.display = 'none';
- panelArea.insertBefore(panelMain, panelArea.firstChild);
-
- const busyPanel = genDiv('aam_busy');
- const busyPanelContent = genDiv('aam_busy_content');
- const busyMessage = genP(t('operating'));
- const busyImg = new Image();
- busyImg.src = 'https://steamcommunity-a.akamaihd.net/public/images/login/throbber.gif';
-
- busyPanelContent.appendChild(busyMessage);
- busyPanelContent.appendChild(busyImg);
-
- busyPanel.appendChild(busyPanelContent);
-
- panelMain.appendChild(busyPanel);
-
- const workPanel = genDiv('aam_busy aam_work');
- const workLog = genTextArea('', t('runningLog'),);
- const workHide = genButton(`❌${t('close')}`, () => { workScreen(false, null); }, true);
-
- workPanel.appendChild(workLog);
- workPanel.appendChild(genSpan(t('logWaring')));
- workPanel.appendChild(workHide);
-
- panelMain.appendChild(workPanel);
-
- const leftPanel = genDiv('aam_left');
- const accountPanel = genDiv('aam_account');
- const accountTitle = genSpan(t('botListTitle'));
- const accountList = genList([], null);
- const accountBtns = genDiv('aam_btns');
- const acAdd = genButton(`➕${t('addCurrentAccount')}`, accountAdd);
- const acDel = genButton(`➖${t('delSelectAccount')}`, accountDel);
- const acUpdate = genButton(`🔄${t('reloadAccountPoints')}`, flashAllAccounts);
-
- accountBtns.appendChild(acAdd);
- accountBtns.appendChild(acDel);
- accountBtns.appendChild(acUpdate);
-
- accountPanel.appendChild(accountTitle);
- accountPanel.appendChild(genBr());
- accountPanel.appendChild(accountList);
- accountPanel.appendChild(accountBtns);
-
- leftPanel.appendChild(accountPanel);
-
- const historyPanel = genDiv('aam_history');
- historyPanel.style.display = 'none';
-
- const historyTitle = genSpan(t('historyTitle'));
- const historyList = genList([], null);
- const historyBtns = genDiv('aam_btns');
- const hsProfile = genButton(`🌏${t('profile')}`, showProfile);
- const hsDelete = genButton(`➖${t('deleteSelectedHistory')}`, deleteHistory);
- const hsClear = genButton(`🗑️${t('clearHistory')}`, clearHistory);
- const hsReload = genButton(`🔄${t('reloadHistory')}`, flashHistoryList);
-
- historyBtns.appendChild(hsProfile);
- historyBtns.appendChild(hsDelete);
- historyBtns.appendChild(hsClear);
- historyBtns.appendChild(hsReload);
-
- historyPanel.appendChild(historyTitle);
- historyPanel.appendChild(genBr());
- historyPanel.appendChild(historyList);
- historyPanel.appendChild(historyBtns);
-
- leftPanel.appendChild(historyPanel);
- panelMain.appendChild(leftPanel);
-
- const awardPanel = genDiv('aam_award');
- const feedbackLink = genA(t('feedBack'), 'https://steamcommunity.com/id/Chr_/');
- feedbackLink.title = t('feedBackTitle');
- const awardBot = genSelect([[t('notSelected'), '']], null);
- const awardSteamID = genInput('', t('steamID64'), false);
- const awardPoints = genInput('', t('awardPoints'), true);
- const [awardCProfile, awardProfile] = genCheckbox(t('profile'), true);
- const [awardCRecommand, awardRecommand] = genCheckbox(t('recommands'), true);
- const [awardCScreenshot, awardScreenshot] = genCheckbox(t('screenshots'), true);
- const [awardCImage, awardImage] = genCheckbox(t('artworks'), true);
- const awardBtns1 = genDiv('aam_btns');
- const awardBtnCurrent = genButton(`🤵${t('setToCurrentUser')}`, getCurrentProfile);
- const awardBtnCalc = genButton(`📊${t('calculator')}`, calcAwardItems);
- const awardBtns2 = genDiv('aam_btns');
- const awardBtnSet = genButton(`💾${t('save')}`, applyAwardConfig);
- const awardBtnReset = genButton(`🔨${t('reset')}`, restoreAwardConfig);
- const hSwitch = genButton(`🕒${t('awardHistory')}`, leftPanelSwitch);
- const awardBtns3 = genDiv('aam_btns aam_award_btns');
- const awardBtnStart = genButton(`✅${t('startAward')}`, startAward, false);
- const awardBtnStop = genButton(`⛔${t('stopAward')}`, stopAward, false);
- const awardStatus = genSpan(`🟥 ${t('stop')}`);
-
- awardBtns1.appendChild(awardBtnCurrent);
- awardBtns1.appendChild(awardBtnCalc);
-
- awardBtns2.appendChild(awardBtnSet);
- awardBtns2.appendChild(awardBtnReset);
- awardBtns2.appendChild(hSwitch);
-
- awardBtns3.appendChild(awardBtnStart);
- awardBtns3.appendChild(awardBtnStop);
- awardBtns3.appendChild(awardStatus);
-
- awardPanel.appendChild(genSpan(t('senderBotAccount')));
- awardPanel.appendChild(feedbackLink);
- awardPanel.appendChild(genBr());
- awardPanel.appendChild(awardBot);
- awardPanel.appendChild(genSpan(t('receverAccount')));
- awardPanel.appendChild(genBr());
- awardPanel.appendChild(awardSteamID);
- awardPanel.appendChild(awardBtns1);
- awardPanel.appendChild(genSpan(t('sendPoints')));
- awardPanel.appendChild(genBr());
- awardPanel.appendChild(awardPoints);
- awardPanel.appendChild(genSpan(t('awardPrefer')));
- awardPanel.appendChild(genBr());
- awardPanel.appendChild(awardCProfile);
- awardPanel.appendChild(awardCRecommand);
- awardPanel.appendChild(genBr());
- awardPanel.appendChild(awardCScreenshot);
- awardPanel.appendChild(awardCImage);
- awardPanel.appendChild(genBr());
- awardPanel.appendChild(awardBtns2);
- awardPanel.appendChild(genHr());
- awardPanel.appendChild(awardBtns3);
-
- panelMain.appendChild(awardPanel);
-
- Object.assign(GObjs, {
- bSwitch, hSwitch, panelMain,
- busyPanel, busyMessage, workPanel, workLog, workHide,
- accountPanel, accountList, historyPanel, historyList,
- awardBot, awardSteamID, awardPoints, awardStatus,
- awardProfile, awardRecommand, awardScreenshot, awardImage,
- awardBtnStart, awardBtnStop, awardBtnSet, awardBtnReset
- });
- }
- //面板显示开关
- function panelSwitch() {
- const { bSwitch, panelMain } = GObjs;
-
- if (GPanel.panelMain !== true) {
- panelMain.style.display = '';
- bSwitch.textContent = '🔴';
- GPanel.panelMain = true;
- } else {
- panelMain.style.display = 'none';
- bSwitch.textContent = '⭕';
- GPanel.panelMain = false;
- }
- GM_setValue('panel', GPanel);
- }
- //左侧面板切换
- function leftPanelSwitch() {
- const { hSwitch, accountPanel, historyPanel } = GObjs;
- if (GPanel.panelLeft !== true) {
- accountPanel.style.display = 'none';
- historyPanel.style.display = '';
- hSwitch.textContent = `🤖${t('botList')}`;
- GPanel.panelLeft = true;
- } else {
- historyPanel.style.display = 'none';
- accountPanel.style.display = '';
- hSwitch.textContent = `🕒${t('awardHistory')}`;
- GPanel.panelLeft = false;
- }
- GM_setValue('panel', GPanel);
- }
- //添加账户
- function accountAdd() {
- let v_nick, v_token, v_steamID;
- loadScreen(true, t('fetchLoginAccount'));
- getMySteamID()
- .then(({ nick, steamID }) => {
- v_nick = nick;
- v_steamID = steamID;
- loadScreen(true, t('fetchToken'));
- return getToken();
- })
- .then((tk) => {
- v_token = tk;
- loadScreen(true, t('fetchPoints'));
- return getPoints(v_steamID, tk);
- })
- .then((points) => {
- showAlert(t('success'), `<p>${t('addAccountSuccessTips1')}</p><p>${t('addAccountSuccessTips2')}: ${points} ${t('points')}</p>`, true);
- GBots[v_steamID] = { nick: v_nick, token: v_token, points };
- GM_setValue('bots', GBots);
- flashBotList();
- })
- .catch((reason) => {
- showAlert(t('error'), reason, false);
- }).finally(() => {
- loadScreen(false, null);
- });
- }
- //删除账户
- function accountDel() {
- const { accountList } = GObjs;
- if (accountList.selectedIndex >= 0) {
- showConfirm(t('confirm'), t('deleteAccountConfirmTips'), () => {
- let i = 0;
- for (const opt of accountList.selectedOptions) {
- delete GBots[opt.value];
- i++;
- }
- flashBotList();
- GM_setValue('bots', GBots);
- showAlert(t('tips'), `${t('deleteAccountDoneTips1')} ${i} ${t('deleteAccountDoneTips2')}`, true);
- }, null);
- } else {
- showAlert(t('tips'), t('notSelectedAnyBotsTips'), false);
- }
- }
- //刷新账户点数
- async function flashAllAccounts() {
- //刷新点数
- function makePromise(sid, tk) {
- return new Promise((resolve, reject) => {
- getPoints(sid, tk)
- .then((points) => {
- GBots[sid].points = points;
- loadScreen(true, `${t('currentProcess')}: ${++fin} / ${count}`);
- }).catch((reason) => {
- GBots[sid].points = -1;
- // GBots[sid].nick = '读取失败';
- loadScreen(true, `${sid} ${t('updateFailed')}: ${reason}`);
- }).finally(() => {
- GM_setValue('bots', GBots);
- resolve();
- });
- });
- }
- let count = 0, fin = 0;
- for (const _ in GBots) {
- count++;
- }
- if (count > 0) {
- loadScreen(true, t('fetchingAccountPoints'));
- const pList = [];
- for (const steamID in GBots) {
- const { token } = GBots[steamID];
- pList.push(makePromise(steamID, token));
- }
- Promise.all(pList)
- .finally(() => {
- loadScreen(false, null);
- flashBotList();
- if (fin >= count) {
- showAlert(t('done'), t('allDataLoaded'), true);
- } else {
- showAlert(t('done'), t('someDataLoadFailed'), true);
- }
- });
- } else {
- showAlert(t('error'), t('botListEmpty'), false);
- }
- }
- //刷新账户列表
- function flashBotList() {
- const { bot } = GTask;
- const { accountList, awardBot } = GObjs;
- accountList.options.length = 0;
- awardBot.options.length = 0;
- awardBot.options.add(new Option(t('notSelected'), ''));
- let i = 1;
- let flag = false;
- if (!isEmptyObject(GBots)) {
- for (const steamID in GBots) {
- const { nick, points } = GBots[steamID];
- const pointsStr = parseInt(points).toLocaleString();
- accountList.options.add(new Option(`${i} | ${nick} | ${steamID} | ${pointsStr} 点`, steamID));
- awardBot.options.add(new Option(`${i++} | ${nick} | ${pointsStr} 点`, steamID));
- if (steamID === bot) {
- flag = true;
- awardBot.selectedIndex = i - 1;
- }
- }
- } else {
- accountList.options.add(new Option(t('noBotAccountTips'), ''));
- }
- if ((!isEmptyObject(GTask)) && (!flag)) {
- GTask = {};
- GM_setValue('task', GTask);
- appllyTask();
- showAlert(t('tips'), t('awardTaskWasResetTips'), false);
- }
- }
- //刷新历史记录列表
- function flashHistoryList() {
- const { historyList } = GObjs;
- historyList.options.length = 0;
- let i = 1;
- if (!isEmptyObject(GHistory)) {
- for (const steamID in GHistory) {
- const [nick, points] = GHistory[steamID];
- const pointsStr = parseInt(points).toLocaleString();
- historyList.options.add(new Option(`${i++} | ${nick} | ${steamID} | ${pointsStr} 点`, steamID));
- }
- } else {
- historyList.options.add(new Option(t('noHistoryTips'), ''));
- }
- }
- //历史记录增加点数
- function addHistory(steamID, nick, points) {
- if (GHistory[steamID] !== undefined) {
- GHistory[steamID] = [nick, GHistory[steamID][1] + points];
- } else {
- GHistory[steamID] = [nick, points];
- }
- GM_setValue('history', GHistory);
- }
- //获取当前个人资料
- function getCurrentProfile() {
- const { awardSteamID } = GObjs;
- awardSteamID.value = g_rgProfileData.steamid;
- }
- //计算可打赏项目
- function calcAwardItems() {
- const { awardSteamID } = GObjs;
- const steamID = awardSteamID.value.trim();
- if (steamID === '') {
- showAlert(t('error'), t('steamIDisEmpty!'), false);
- } else {
- loadScreen(true, t('fetchingProfile'));
- getProfile(steamID)
- .then(([succ, nick]) => {
- if (succ) {
- loadScreen(true, t('fetchingAwardableItems'));
- const pList = [
- getAwardCounts(steamID, 'r'),
- getAwardCounts(steamID, 's'),
- getAwardCounts(steamID, 'i')
- ];
- Promise.all(pList)
- .then((result) => {
- const data = {};
- let sum = 0;
- for (const [type, succ, count] of result) {
- if (succ) {
- const points = count * 6600;
- data[type] = `${count} , ${t('awardableAmount')}: ${points.toLocaleString()} ${t('points')}`;
- sum += points;
- } else {
- data[type] = t('fetchError');
- }
- }
- 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>`;
- showAlert(t('tips'), text, true);
- })
- .finally(() => {
- loadScreen(false, null);
- });
- } else {
- showAlert(t('error'), t('profileNotExistsTips'), false);
- loadScreen(false, null);
- }
- })
- .catch((reason) => {
- showAlert(t('error'), `<p>${t('profileLoadFailedTips')}</p><p>${reason}</p>`, false);
- loadScreen(false, null);
- });
- }
- }
- //查看个人资料
- function showProfile() {
- const { historyList } = GObjs;
- const i = historyList.selectedIndex;
- if (i > -1) {
- const { value } = historyList.options[i];
- if (value != '') {
- window.open(`https://steamcommunity.com/profiles/${value}`);
- }
- } else {
- showAlert(t('tips'), t('notSelectedAnyHistoryTips'), false);
- }
- }
- //清除历史
- function clearHistory() {
- if (!isEmptyObject(GHistory)) {
- showConfirm(t('confirm'), t('clearHistoryConfirmTips'), () => {
- GHistory = {};
- flashHistoryList();
- GM_setValue('history', GHistory);
- showAlert(t('tips'), t('clearHistorySuccess'), true);
- }, null);
- } else {
- showAlert(t('tips'), t('historyListEmpty'), false);
- }
- }
- //删除历史
- function deleteHistory() {
- const { historyList } = GObjs;
- if (historyList.selectedIndex >= 0) {
- showConfirm(t('confirm'), t('deleteHistoryConfirmTips'), () => {
- let i = 0;
- for (const opt of historyList.selectedOptions) {
- delete GHistory[opt.value];
- i++;
- }
- flashHistoryList();
- GM_setValue('history', GHistory);
- showAlert(t('tips'), `${t('deleteResultTips1')} ${i} ${t('deleteResultTips2')}`, true);
- }, null);
- } else {
- showAlert(t('tips'), t('notSelectedAnyHistoryTips'), false);
- }
- }
- //保存打赏设置
- async function applyAwardConfig() {
- const {
- awardBtnStart, awardBtnStop,
- awardBot, awardSteamID, awardPoints,
- awardProfile, awardRecommand, awardScreenshot, awardImage
- } = GObjs;
-
- awardBtnStart.disabled = awardBtnStop.disabled = true;
-
- let bot = awardBot.value;
- let points = parseInt(awardPoints.value);
- let steamID = String(awardSteamID.value).trim();
-
- let type = 0;
- if (!awardProfile.checked) { type += 1; }
- if (!awardRecommand.checked) { type += 2; }
- if (!awardScreenshot.checked) { type += 4; }
- if (!awardImage.checked) { type += 8; }
-
- if (bot == '') {
- // 未选择机器人则自动使用当前登录(不可用)账号
- loadScreen(true, t('fetchLoginAccount'));
- const nick = document.querySelector("#account_pulldown")?.textContent?.trim();
- if (g_steamID && nick) {
- try {
- loadScreen(true, t('fetchToken'));
- const token = await getToken();
- loadScreen(true, t('fetchPoints'));
- const points = await getPoints(g_steamID, token);
- GBots[g_steamID] = { nick: nick, token: token, points };
- GM_setValue('bots', GBots);
- flashBotList();
- bot = g_steamID;
- } catch (reason) {
- showAlert(t('error'), reason, false);
- } finally {
- loadScreen(false, null);
- }
- }
- }
-
- if (bot == '') {
- showAlert(t('error'), t('notSelectedAwardBotsTips'), false);
- } else if (steamID === '') {
- showAlert(t('error'), t('steamIDEmptyWithTips'), false);
- } else if (!steamID.match(/^\d+$/)) {
- showAlert(t('error'), t('steamIDErrorWithTips'), false);
- } else if (points !== points || points < 100) {
- showAlert(t('error'), t('pointsErrorWithTips'), false);
- } else if (type === 15) {
- showAlert(t('error'), t('awardTypeEmptyTips'), false);
- } else {
- points = Math.ceil(points / 100) * 100;
- GTask = { bot, steamID, points, type, work: false, nick: null };
- awardBtnStart.disabled = awardBtnStop.disabled = false;
- GM_setValue('task', GTask);
- showAlert(t('tips'), t('awardReadyToStartTips'), true);
- }
- }
- //重置打赏设置
- function restoreAwardConfig() {
- showConfirm(t('confirm'), t('resetConfigConfirmTips'), () => {
- GTask = {};
- GM_setValue('task', GTask);
- appllyTask();
- showAlert(t('tips'), t('configResetSuccessTips'), true);
- }, null);
- }
- //读取设置到界面
- function appllyTask() {
- const {
- awardBtnStart, awardBtnStop,
- awardBot, awardSteamID, awardPoints,
- awardProfile, awardRecommand, awardScreenshot, awardImage
- } = GObjs;
- const { bot, steamID, points, type } = GTask;
-
- awardBtnStart.disabled = awardBtnStop.disabled = isEmptyObject(GTask);
-
- awardBot.value = bot ? bot : '';
- awardSteamID.value = steamID ? steamID : '';
- awardPoints.value = points ? points : '';
-
- awardProfile.checked = !Boolean(type & 1);
- awardRecommand.checked = !Boolean(type & 2);
- awardScreenshot.checked = !Boolean(type & 4);
- awardImage.checked = !Boolean(type & 8);
- }
- //开始自动打赏
- async function startAward() {
- if (isEmptyObject(GTask)) {
- showAlert(t('error'), t('awardTaskDataInvalid'), false);
- return;
- }
- const { steamID, work, points, bot, nick: taskNick } = GTask;
- const { nick: botNick } = GBots[bot];
- const pointsStr = parseInt(points).toLocaleString();
-
- if (!work) {
- spaceLine(1);
- if (!taskNick) {
- loadScreen(true, t('fetchingTargetProfile'));
- getProfile(steamID)
- .then(([succ, nickName]) => {
- if (succ) {
- GTask.work = true;
- GTask.nick = nickName;
- GM_setValue('task', GTask);
- print(`${t('awardConfig')}:\n〖${t('targetNickName')}: ${nickName}, ${t('targetReceivePoints')}: ${pointsStr}, ${t('targetBot')}: ${botNick}〗`);
- print(t('taskReadyToStartTips'));
- workScreen(true);
- setTimeout(() => {
- autoAward();
- }, 2000);
- } else {
- print(t('taskFailedProfileNotFound'), 'E');
- showAlert(t('error'), t('profileNotExistsTips'), false);
- }
- })
- .catch((reason) => {
- showAlert(t('error'), `<p>${t('profileLoadFailedTips')}</p><p>${reason}</p>`, false);
- }).finally(() => {
- loadScreen(false, null);
- });
- } else {
- GTask.work = true;
- GM_setValue('task', GTask);
- print(`〖${t('targetNickName')}: ${taskNick}, ${t('targetReceivePoints')}: ${pointsStr}, ${t('targetBot')}: ${botNick}〗`);
- print(t('taskReadyToStartTips'));
- workScreen(true);
- setTimeout(() => {
- autoAward();
- }, 2000);
- }
- } else {
- print(t('taskAlreadyStartTips'));
- }
- }
- //停止自动打赏
- async function stopAward() {
- if (isEmptyObject(GTask)) {
- showAlert(t('error'), t('awardTaskDataInvalid'), false);
- return;
- }
- const { work } = GTask;
- if (work) {
- spaceLine(4);
- print(t('taskEndManually'));
- GTask.work = false;
- GM_setValue('task', GTask);
- showStatus(t('stop'), false);
- } else {
- showAlert(t('error'), t('taskNotStart'), false);
- }
- }
- //打赏项目
- const reactionsDict = {
- 1: 300, 2: 300, 3: 300, 4: 300, 5: 300, 6: 300, 7: 300, 8: 300, 9: 600,
- 10: 1200, 11: 2400, 12: 300, 13: 2400, 14: 600, 15: 1200, 16: 600,
- 17: 4800, 18: 300, 19: 600, 20: 1200, 21: 300, 22: 600, 23: 300
- };
- const reactionValues = [
- 300, 300, 300, 300, 300, 300, 300, 300, 600, 1200, 2400, 300,
- 2400, 600, 1200, 600, 4800, 300, 600, 1200, 300, 600, 300
- ];
- const reactionIDs = [
- 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12,
- 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23
- ];
- //自动打赏
- async function autoAward() {
- //打赏类型
- const reactionType = {
- 'p': ['3', t('profile')], 'r': ['1', t('recommands')], 's': ['2', t('screenshots')], 'i': ['2', t('artworks')]
- };
- const { bot, steamID, type, points: pointsGoal, nick: taskNick } = GTask;
- const { nick: botNick, token } = GBots[bot];
-
- appllyTask();
- addHistory(steamID, taskNick, 0);
- showStatus(t('running'), true);
- let pointsLeft = pointsGoal;
-
- if (token) {
- const workflow = [];
- if (!Boolean(type & 8)) { workflow.push('i'); };
- if (!Boolean(type & 4)) { workflow.push('s'); };
- if (!Boolean(type & 2)) { workflow.push('r'); };
- if (!Boolean(type & 1)) { workflow.push('p'); };
-
- while (GTask.work && workflow.length > 0) {
- const award_type = workflow.pop();
- const [target_type, target_name] = reactionType[award_type];
- let process = genProgressBar((pointsGoal - pointsLeft) / pointsGoal * 100);
-
- spaceLine(3);
- print(`【${target_name}】${t('taskStartPointsSummary')}: ${pointsLeft.toLocaleString()} / ${pointsGoal.toLocaleString()} ${t('points')}`);
- print(`${t('currentProcess')}: ${process}`);
- spaceLine(3);
-
- let coast = 0;
-
- if (target_type === '3') { //个人资料
- let GoldReactions = null;
- for (let i = 0; i < 3; i++) { //重试3次
- if (GoldReactions === null) { //旧打赏列表为空,重新读取并打赏
- const [succOld, oldReactions] = await getAwardRecords(token, target_type, steamID);
- if (!succOld) {
- print(t('fetchAwardItemFailedRetry'));
- continue;
- }
- GoldReactions = oldReactions;
- const todoReactions = selectFitableReactions(pointsLeft, GoldReactions);
- if (todoReactions.length === 0) {
- print(`【${target_name}】${t('fetchNoAwardItemSkip')}`);
- break;
- }
- coast = sumReactionsPoints(todoReactions);
- print(`【${target_name}】${t('beforeSendAward')}: ${todoReactions.length} ${t('itemAndTotal')}: ${coast.toLocaleString()} ${t('points')}`);
- const plist = [];
- for (const id of todoReactions) {
- plist.push(sendAwardReaction(token, target_type, steamID, id));
- }
- print(t('sendingAwards'));
- const result = await Promise.all(plist);
- const [succ, fail] = countSuccess(result);
- print(`${t('fetchSuccessAndFailed')}: ${succ} / ${fail}`);
- }
- //统计新的打赏列表,计算打赏点数
- const [succNew, newReactions] = await getAwardRecords(token, target_type, steamID);
- if (!succNew) {
- print(t('fetchAwardItemFailedRetryIn2Min'));
- await aiosleep(2500);
- continue;
- }
- const diffReactions = filterDiffReactions(newReactions, GoldReactions);
- coast = sumReactionsPoints(diffReactions);
- pointsLeft -= coast;
- addHistory(steamID, taskNick, coast);
- print(`【${target_name}】${t('awardSuccess')}: ${diffReactions.length} ${t('itemAndTotal')}: ${coast.toLocaleString()} ${t('points')}`);
- break;
- }
- GTask.points = pointsLeft;
- if (pointsLeft <= 0) {
- GTask.work = false;
- }
- GM_setValue('task', GTask);
- process = genProgressBar((pointsGoal - pointsLeft) / pointsGoal * 100);
-
- spaceLine(3);
- print(`【${target_name}】${t('taskFinishedPointsSummary')}: ${pointsLeft.toLocaleString()} / ${pointsGoal.toLocaleString()} ${t('points')}`);
- print(`${t('currentProcess')}: ${process}`);
- spaceLine(3);
-
- print(t('updateBotPointsBalance'));
-
- await getPoints(bot, token)
- .then((p) => {
- GBots[bot].points = p;
- GM_setValue('bots', GBots);
- print(`${t('bot')}【${botNick}】${t('pointsBalanceUpdateSuccess')}: ${p.toLocaleString()} ${t('points')}`);
- if (p < 300) {
- print(`${t('bot')}【${botNick}】${t('lackOfPointsTaskEnd')}`);
- GTask.work = false;
- }
- }).catch((r) => {
- print(`${t('bot')}【${botNick}】${t('pointBalanceUpdateFailed')}: ${r}`);
- });
-
-
- } else { //截图
- let page = 1;
- while (GTask.work) {
- let j = 0;
- print(t('fetchingAwardableItems'));
- const [succ, items] = await getAwardItems(steamID, award_type, page++);
- if (!succ) {
- page--;
- if (++j < 3) {
- print(t('fetchAwardItemFailedRetryIn2Min'));
- await aiosleep(2500);
- continue;
- } else {
- print(t('fetchAwardItemFailedSkip'));
- break;
- }
- }
- if (items.length === 0) {
- print(`【${target_name}】${t('taskEndListEmpty')}`);
- break;
- }
-
- print(`【${target_name}】${t('fetchCompletedTotal')} ${items.length} ${t('entries')}`);
-
- for (const itemID of items) {
-
- print(`【${target_name}】${t('objectID')}: ${itemID}`);
- let GoldReactions = null;
-
- for (let i = 0; i < 3; i++) {
- if (GoldReactions === null) { //旧打赏列表为空,重新读取并打赏
- const [succOld, oldReactions] = await getAwardRecords(token, target_type, itemID);
- if (!succOld) {
- print(t('fetchAwardItemFailedRetry'));
- continue;
- }
- GoldReactions = oldReactions;
- const todoReactions = selectFitableReactions(pointsLeft, GoldReactions);
- if (todoReactions.length === 0) {
- print(`【${target_name}】${t('noAwardableObjectSkip')}`);
- break;
- }
- coast = sumReactionsPoints(todoReactions);
- print(`【${target_name}】${t('willAward')}: ${todoReactions.length} ${t('itemAndTotal')}: ${coast.toLocaleString()} ${t('points')}`);
- const plist = [];
- for (const id of todoReactions) {
- plist.push(sendAwardReaction(token, target_type, itemID, id));
- }
- print(t('sendingAwards'));
- const result = await Promise.all(plist);
- const [succ, fail] = countSuccess(result);
- print(`${t('requestsSummary')}: ${succ} / ${fail}`);
- }
- print(t('wait2Seconds'));
- await asleep(2000);
- //统计新的打赏列表,计算打赏点数
- const [succNew, newReactions] = await getAwardRecords(token, target_type, itemID);
- if (!succNew) {
- print(t('fetchAwardItemFailedRetry'));
- continue;
- }
- const diffReactions = filterDiffReactions(newReactions, GoldReactions);
- coast = sumReactionsPoints(diffReactions);
- pointsLeft -= coast;
- addHistory(steamID, taskNick, coast);
- print(`【${target_name}】${t('awardSuccess')}: ${diffReactions.length} ${t('itemAndTotal')}: ${coast.toLocaleString()} ${t('points')}`);
- break;
- }
- GTask.points = pointsLeft;
- if (pointsLeft <= 0) {
- GTask.work = false;
- }
- GM_setValue('task', GTask);
- process = genProgressBar((pointsGoal - pointsLeft) / pointsGoal * 100);
-
- spaceLine(3);
- print(`【${target_name}】${t('taskFinishedPointsSummary')}: ${pointsLeft.toLocaleString()} / ${pointsGoal.toLocaleString()} ${t('points')}`);
- print(`${t('currentProcess')}: ${process}`);
- spaceLine(3);
-
- print(t('updateBotPointsBalance'));
-
- await getPoints(bot, token)
- .then((p) => {
- GBots[bot].points = p;
- GM_setValue('bots', GBots);
- print(`${t('bot')}【${botNick}】${t('pointsBalanceUpdateSuccess')}: ${p.toLocaleString()} ${t('points')}`);
- if (p < 300) {
- print(`${t('bot')}【${botNick}】${t('lackOfPointsTaskEnd')}`);
- GTask.work = false;
- }
- }).catch((r) => {
- print(`${t('bot')}【${botNick}】${t('pointBalanceUpdateFailed')}: ${r}`);
- });
-
- if (!GTask.work) {
- break;
- }
- }
- }
- }
- if (workflow.length > 0) {
- await aiosleep(1500);
- }
- }
- } else {
- delete GBots[bot];
- GM_setValue('bots', GBots);
- print(t('botDataError'));
- showAlert(t('error'), t('botDataError'), false);
- }
- spaceLine(4);
- if (pointsLeft <= 0) {
- GTask = {};
- print(t('awardTaskFinish'));
- } else {
- GTask.work = false;
- print(t('awardTaskNotFinish'));
- }
- GM_setValue('task', GTask);
- appllyTask();
- showStatus(t('stop'), false);
- flashHistoryList();
- }
- //====================================================================================
- //显示提示
- function showAlert(title, text, succ = true) {
- ShowAlertDialog(`${succ ? '✅' : '❌'}${title}`, `<div>${text}</div>`);
- }
- //显示确认
- function showConfirm(title, text, done = null, cancel = null) {
- ShowConfirmDialog(`⚠️${title}`, `<div>${text}</div>`, t('confirm'), t('cancel'))
- .done(() => {
- if (done) { done(); }
- })
- .fail(() => {
- if (cancel) { cancel(); }
- });
- }
- //显示状态
- function showStatus(text, run = true) {
- const { awardStatus, workHide } = GObjs;
- workHide.disabled = run;
- awardStatus.textContent = `${run ? '🟩' : '🟥'} ${text}`;
- }
- //读取设置
- function loadConf() {
- const bots = GM_getValue('bots');
- GBots = isEmptyObject(bots) ? {} : bots;
- const hs = GM_getValue('history');
- GHistory = isEmptyObject(hs) ? {} : hs;
- const task = GM_getValue('task');
- GTask = isEmptyObject(task) ? {} : task;
- const panel = GM_getValue('panel');
- GPanel = isEmptyObject(panel) ? {} : panel;
- }
- //保存设置
- function saveConf() {
- GM_setValue('bots', GBots);
- GM_setValue('history', GHistory);
- GM_setValue('task', GTask);
- GM_setValue('panel', GPanel);
- }
- //是不是空对象
- function isEmptyObject(obj) {
- for (const _ in obj) { return false; }
- return true;
- }
- //显示加载面板
- function loadScreen(show = true, msg = t('operating')) {
- const { busyPanel, busyMessage } = GObjs;
- if (show) {
- busyPanel.style.opacity = '1';
- busyPanel.style.visibility = 'visible';
- if (msg) {
- busyMessage.textContent = msg;
- }
- } else {
- busyPanel.style.opacity = '0';
- busyPanel.style.visibility = 'hidden';
- }
- }
- //显示日志面板
- function workScreen(show = true) {
- const { workPanel } = GObjs;
- if (show) {
- workPanel.style.opacity = '1';
- workPanel.style.visibility = 'visible';
- } else {
- workPanel.style.opacity = '0';
- workPanel.style.visibility = 'hidden';
- }
- }
- //生成进度条
- const BAR_STYLE = '⣀⣄⣤⣦⣶⣷⣿';
- function genProgressBar(percent) {
- const full_symbol = '⣿';
- const none_symbol = '⣀';
- const percentStr = ` ${percent.toFixed(2)}%`;
- if (percent >= 100) {
- return full_symbol.repeat(40) + percentStr;
- } else {
- percent = percent / 100;
- let full = Math.floor(percent * 40);
- let rest = percent * 40 - full;
- let middle = Math.floor(rest * 6);
- if (percent !== 0 && full === 0 && middle === 0) { middle = 1; }
- let d = Math.abs(percent - (full + middle / 6) / 40) * 100;
- if (d < Number.POSITIVE_INFINITY) {
- let m = BAR_STYLE[middle];
- if (full === 40) { m = ""; }
- return full_symbol.repeat(full) + m + BAR_STYLE[0].repeat(39 - full) + percentStr;
- }
- return none_symbol.repeat(40) + percentStr;
- }
- }
- //日志时间
- function formatTime() {
- const date = new Date();
- return `${date.toLocaleDateString()} ${date.toTimeString().substr(0, 8)}`;
- }
- //输出日志
- function print(msg, level = 'I') {
- const { workLog } = GObjs;
- const time = formatTime();
- workLog.value += `${time} - ${level} - ${msg}\n`;
- workLog.scrollTop = workLog.scrollHeight;
- console.log(`${time} - ${level} - ${msg}`);
- }
- //画分割线
- function spaceLine(style = 1) {
- switch (style) {
- case 1:
- print('#'.repeat(68));
- return;
- case 2:
- print('='.repeat(68));
- return;
- case 3:
- print('+'.repeat(68));
- return;
- case 4:
- print('~'.repeat(68));
- return;
- }
- }
- //异步延时
- function asleep(ms) {
- return new Promise(resolve => setTimeout(resolve, ms));
- }
- //====================================================================================
- //计算合适的打赏项目
- function selectFitableReactions(goal, doneList) {
- const fitableList = [];
- const aviableList = [];
- for (const id of reactionIDs) {
- if (doneList.indexOf(id) === -1) {
- aviableList.push(id);
- }
- }
- aviableList.sort((a, b) => { return reactionsDict[a] - reactionsDict[b]; });
- for (const id of aviableList) {
- if (goal < 100) {
- break;
- }
- const value = reactionsDict[id] / 3;
- if (goal >= value) {
- fitableList.push(id);
- goal -= value;
- }
- }
- return fitableList;
- }
- //获取新增打赏项目
- function filterDiffReactions(newList, oldList) {
- const diffList = [];
- for (const id of newList) {
- if (oldList.indexOf(id) === -1) {
- diffList.push(id);
- }
- }
- return diffList;
- }
- //计算打赏项目点数开销
- function sumReactionsPoints(reactions) {
- let points = 0;
- for (const id of reactions) {
- points += reactionsDict[id];
- }
- return points / 3;
- }
- //统计成功失败
- function countSuccess(result) {
- let succ = 0, fail = 0;
- for (const r of result) {
- if (r) {
- succ++;
- } else {
- fail++;
- }
- }
- return ([succ, fail]);
- }
- //异步延时
- function aiosleep(ms) {
- return new Promise(resolve => setTimeout(resolve, ms));
- }
- //====================================================================================
- function getMySteamID() {
- return new Promise((resolve, reject) => {
- try {
- const steamID = g_steamID;
- const nick = document.querySelector("div.playerAvatar>a>img")?.getAttribute("alt");
-
- if (nick && steamID) {
- resolve({ nick, steamID });
- } else {
- reject(t('steamStoreNotLogin'));
- }
- } catch (err) {
- reject(err);
- }
- });
- }
- function getToken() {
- return new Promise((resolve, reject) => {
- try {
- let token = document.querySelector("#application_config")?.getAttribute("data-loyalty_webapi_token");
-
- if (isEmptyObject(token)) {
- reject(t('steamStoreNotLogin'));
- }
- else {
- token = token.replace(/"/g, "");
- resolve(token);
- }
- } catch (err) {
- reject(err);
- }
- });
- }
- function getPoints(steamID, token) {
- return new Promise((resolve, reject) => {
- $http.get(`https://api.steampowered.com/ILoyaltyRewardsService/GetSummary/v1/?access_token=${token}&steamid=${steamID}`)
- .then(({ response }) => {
- if (isEmptyObject(response)) {
- reject(t('steamStoreNotLogin'));
- }
- try {
- const points = parseInt(response.summary.points);
- if (points === points) {
- resolve(points);
- } else {
- reject(t('parseDataFailedMaybeNetworkError'));
- }
- } catch (e) {
- reject(t('parseDataFailedMaybeNetworkError'));
- }
- })
- .catch((reason) => {
- reject(reason);
- });
- });
- }
- function getProfile(steamID) {
- return new Promise((resolve, reject) => {
- $http.getText(`https://steamcommunity.com/profiles/${steamID}/?xml=1`)
- .then((text) => {
- try {
- const match = text.match(/<steamID><!\[CDATA\[([\s\S]*)\]\]><\/steamID>/) ||
- text.match(/<steamID>([\s\S]*)<\/steamID>/);
- if (match) {
- resolve([true, match[1].substring()]);
- } else {
- resolve([false, null]);
- }
- } catch (e) {
- reject(e);
- }
- })
- .catch((reason) => {
- reject(reason);
- });
- });
- }
- function getAwardCounts(steamID, type) {
- let subPath, preg;
- switch (type) {
- case 'r':
- subPath = 'recommended/?l=schinese';
- preg = /共 (\d+) 项条目/;
- break;
- case 's':
- subPath = 'screenshots/?l=schinese&appid=0&sort=newestfirst&browsefilter=myfiles&view=grid';
- preg = /共 (\d+) 张/;
- break;
- case 'i':
- subPath = 'images/?l=schinese&appid=0&sort=newestfirst&browsefilter=myfiles&view=grid';
- preg = /共 (\d+) 张/;
- break;
- default:
- throw 'type错误';
- }
- return new Promise((resolve, reject) => {
- $http.getText(`https://steamcommunity.com/profiles/${steamID}/${subPath}`)
- .then((text) => {
- try {
- const match = text.match(preg);
- const count = match ? Number(match[1]) : 0;
- resolve([type, true, count]);
- } catch (e) {
- resolve([type, false, 0]);
- }
- })
- .catch((reason) => {
- console.error(reason);
- resolve([type, false, 0]);
- });
- });
- }
- function getAwardItems(steamID, type, p = 1) {
- let subPath, preg;
- switch (type) {
- case 'r':
- subPath = `recommended/?p=${p}&l=schinese`;
- preg = /id="RecommendationVoteUpBtn(\d+)"/g;
- break;
- case 's':
- subPath = `screenshots/?p=${p}&view=grid&l=schinese`;
- preg = /id="imgWallHover(\d+)"/g;
- break;
- case 'i':
- subPath = `images/?p=${p}&view=grid&l=schinese`;
- preg = /id="imgWallHover(\d+)"/g;
- break;
- default:
- throw t('typeError');
- }
- return new Promise((resolve, reject) => {
- $http.getText(`https://steamcommunity.com/profiles/${steamID}/${subPath}`)
- .then((text) => {
- try {
- const result = [];
- const matches = text.matchAll(preg);
- for (const match of matches) {
- result.push(match[1]);
- }
- resolve([true, result]);
- } catch (e) {
- console.error(e);
- resolve([false, e]);
- }
- })
- .catch((reason) => {
- console.error(reason);
- resolve([false, reason]);
- });
- });
- }
- function getAwardRecords(token, targetType, targetID) {
- return new Promise((resolve, reject) => {
- const params = `access_token=${token}&target_type=${targetType}&targetid=${targetID}`;
- $http.get('https://api.steampowered.com/ILoyaltyRewardsService/GetReactions/v1/?' + params)
- .then(({ response }) => {
- const { reactionids } = response;
- resolve([true, reactionids || []]);
- })
- .catch((reason) => {
- console.error(reason);
- resolve([false, null]);
- });
- });
- }
- function sendAwardReaction(token, targetType, targetID, reactionID) {
- return new Promise((resolve, reject) => {
- const params = `access_token=${token}&target_type=${targetType}&targetid=${targetID}&reactionid=${reactionID}`;
- $http.post('https://api.steampowered.com/ILoyaltyRewardsService/AddReaction/v1/?' + params)
- .then((json) => {
- console.log(json);
- resolve(true);
- })
- .catch((reason) => {
- console.error(reason);
- resolve(false);
- });
- });
- }
- })();
- //====================================================================================
- class Request {
- constructor(timeout = 3000) {
- this.timeout = timeout;
- }
- get(url, opt = {}) {
- return this.baseRequest(url, 'GET', opt, 'json');
- }
- getHtml(url, opt = {}) {
- return this.baseRequest(url, 'GET', opt, '');
- }
- getText(url, opt = {}) {
- return this.baseRequest(url, 'GET', opt, 'text');
- }
- post(url, data, opt = {}) {
- opt.data = JSON.stringify(data);
- return this.baseRequest(url, 'POST', opt, 'json');
- }
- baseRequest(url, method = 'GET', opt = {}, responseType = 'json') {
- Object.assign(opt, {
- url, method, responseType, timeout: this.timeout
- });
- return new Promise((resolve, reject) => {
- opt.ontimeout = opt.onerror = reject;
- opt.onload = ({ readyState, status, response, responseText }) => {
- if (readyState === 4 && status === 200) {
- if (responseType == 'json') {
- resolve(response);
- } else if (responseType == 'text') {
- resolve(responseText);
- }
- } else {
- console.error("网络错误");
- console.log(readyState);
- console.log(status);
- console.log(response);
- reject("解析失败");
- }
- };
- GM_xmlhttpRequest(opt);
- });
- }
- }
- const $http = new Request();
-
- //CSS表
- GM_addStyle(`.aam_panel,
- .aam_work {
- padding: 10px;
- display: flex;
- }
- .aam_work {
- z-index: 500 !important;
- }
- .aam_busy {
- width: 100%;
- height: 100%;
- z-index: 700;
- position: absolute;
- top: 0;
- left: 0;
- background: rgba(0, 0, 0, 0.7);
- display: table;
- visibility: hidden;
- opacity: 0;
- transition: all 0.1s;
- }
- .aam_busy_content {
- display: table-cell;
- vertical-align: middle;
- text-align: center;
- }
- .aam_left {
- width: 61%;
- padding-right: 10px;
- }
- .aam_award {
- width: 39%;
- }
- .aam_list,
- .aam_select,
- .aam_input,
- .aam_textarea {
- background-color: #fff !important;
- color: #000 !important;
- border: none !important;
- border-radius: 0 !important;
- }
- .aam_input {
- width: 98% !important;
- text-align: center;
- }
- .aam_list {
- height: 230px;
- }
- .aam_textarea {
- height: calc(100% - 85px);
- width: calc(100% - 45px);
- resize: none;
- font-size: 12px;
- }
- .aam_left > div > *,
- .aam_award:not(span, button) > * {
- width: 100%;
- margin-bottom: 5px;
- }
- .aam_btns > button:not(:last-child) {
- margin-right: 4px;
- }
- .aam_award_btns {
- z-index: 600;
- bottom: 10px;
- position: absolute;
- }
- .aam_work > * {
- position: absolute;
- }
- .aam_work > span {
- bottom: 12%;
- left: 70px;
- }
- .aam_work > button {
- bottom: 11%;
- }
- .aam_a {
- margin-left: 110px;
- }`);