diff --git a/GrepolisRemoteControl.user.js b/GrepolisRemoteControl.user.js index 353c07e..94cf07c 100644 --- a/GrepolisRemoteControl.user.js +++ b/GrepolisRemoteControl.user.js @@ -1,7 +1,7 @@ // ==UserScript== // @name Grepolis Remote Control // @namespace http://tampermonkey.net/ -// @version 2.6 +// @version 2.7 // @description Polls grepo.haunter-pets.top for remote commands and executes them in-game // @author Dimitrios // @match https://*.grepolis.com/game/* @@ -414,7 +414,36 @@ } // ---------------------------------------------------------------- - // Poll for and execute pending commands (build + recruit in parallel) + // Execute: Market + // ---------------------------------------------------------------- + async function executeMarketOffer(cmd) { + const { town_id, payload } = cmd; + const { offer, offer_type, demand, demand_type, max_delivery_time, visibility } = payload; + + const town = uw.ITowns?.getTown?.(town_id) || uw.ITowns?.towns?.[town_id]; + if (!town) { + return { ok: false, msg: `Town ${town_id} not found` }; + } + + const reactionMs = randInt(800, 2500); + log(`Waiting ${reactionMs}ms before firing market offer (reaction time)...`); + await sleep(reactionMs); + + uw.gpAjax.ajaxPost('frontend_bridge', 'execute', { + model_url: 'CreateOffers/' + town_id, + action_name: 'createOffer', + captcha: null, + arguments: { + offer, offer_type, demand, demand_type, max_delivery_time, visibility + } + }); + + await sleep(500); + return { ok: true, msg: `Market offer posted: ${offer} ${offer_type} => ${demand} ${demand_type}` }; + } + + // ---------------------------------------------------------------- + // Poll for and execute pending commands (build + recruit + market) // ---------------------------------------------------------------- async function pollAndExecute() { if (paused) return; @@ -428,9 +457,10 @@ return; } - // Build queue and Recruit queue are now independent + // Build queue, Recruit queue and Market queue are independent const buildCmd = cmdData.build; const recruitCmd = cmdData.recruit; + const marketCmd = cmdData.market; const execute = async (cmd) => { if (!cmd) return; @@ -439,6 +469,7 @@ try { if (cmd.type === 'build') result = await executeBuild(cmd); else if (cmd.type === 'recruit') result = await executeRecruit(cmd); + else if (cmd.type === 'market_offer') result = await executeMarketOffer(cmd); else result = { ok: false, msg: `Unknown type: ${cmd.type}` }; } catch (e) { result = { ok: false, msg: `Exception: ${e.message}` }; @@ -448,8 +479,8 @@ reportResult(cmd.id, finalStatus, result.msg); }; - // Run both queues concurrently — they do NOT block each other - await Promise.all([execute(buildCmd), execute(recruitCmd)]); + // Run concurrently — they do NOT block each other + await Promise.all([execute(buildCmd), execute(recruitCmd), execute(marketCmd)]); } // ---------------------------------------------------------------- diff --git a/routes/api.py b/routes/api.py index 9f01da8..f2b6ea9 100644 --- a/routes/api.py +++ b/routes/api.py @@ -93,13 +93,15 @@ def get_pending_command(): build_cmd = _fetch_pending_of_type(c, 'build') recruit_cmd = _fetch_pending_of_type(c, 'recruit') + market_cmd = _fetch_pending_of_type(c, 'market_offer') conn.commit() conn.close() return jsonify({ 'build': build_cmd, - 'recruit': recruit_cmd + 'recruit': recruit_cmd, + 'market': market_cmd }) diff --git a/routes/dashboard.py b/routes/dashboard.py index f87fe9d..997decb 100644 --- a/routes/dashboard.py +++ b/routes/dashboard.py @@ -144,8 +144,8 @@ def create_command(): return jsonify({'error': f'missing field: {field}'}), 400 cmd_type = data['type'] - if cmd_type not in ('build', 'recruit'): - return jsonify({'error': 'type must be build or recruit'}), 400 + if cmd_type not in ('build', 'recruit', 'market_offer'): + return jsonify({'error': 'type must be build, recruit, or market_offer'}), 400 # Reject if the Tampermonkey client is offline (no state push in last 150 s) conn = get_db() diff --git a/static/js/api.js b/static/js/api.js index db294b9..f2ccb64 100644 --- a/static/js/api.js +++ b/static/js/api.js @@ -114,7 +114,7 @@ window.sendCommand = async function() { } payload = { building_id }; - } else { + } else if (type === 'recruit') { const unit_id = document.getElementById('unit-select').value; const amount = parseInt(document.getElementById('recruit-amount').value) || 1; const uData = town.unit_data?.[unit_id]; @@ -142,6 +142,15 @@ window.sendCommand = async function() { } payload = { unit_id, amount }; + } else if (type === 'market_offer') { + const offer = parseInt(document.getElementById('market-offer-amount').value) || 1000; + const offer_type = document.getElementById('market-offer-type').value; + const demand = parseInt(document.getElementById('market-demand-amount').value) || 1000; + const demand_type = document.getElementById('market-demand-type').value; + const max_delivery_time = parseInt(document.getElementById('market-max-time').value) || 1800; + const visibility = document.getElementById('market-visibility').value; + + payload = { offer, offer_type, demand, demand_type, max_delivery_time, visibility }; } try { diff --git a/static/js/components/commandForm.js b/static/js/components/commandForm.js index d8d0630..079460a 100644 --- a/static/js/components/commandForm.js +++ b/static/js/components/commandForm.js @@ -7,6 +7,7 @@ window.onCmdTypeChange = function() { document.getElementById('build-options').style.display = type === 'build' ? '' : 'none'; document.getElementById('recruit-options').style.display = type === 'recruit' ? '' : 'none'; document.getElementById('amount-group').style.display = type === 'recruit' ? '' : 'none'; + document.getElementById('market-options').style.display = type === 'market_offer' ? '' : 'none'; }; window.renderBuildingDropdown = function() { diff --git a/templates/dashboard.html b/templates/dashboard.html index 08e3798..1a0c3d6 100644 --- a/templates/dashboard.html +++ b/templates/dashboard.html @@ -71,6 +71,7 @@ @@ -117,6 +118,60 @@ + +
+