Compare commits

...

2 Commits

Author SHA1 Message Date
efa63f761f attack now button 2026-05-01 02:37:46 +03:00
ae37674bcc bandit fix 2026-05-01 02:33:35 +03:00
5 changed files with 77 additions and 3 deletions

View File

@@ -45,7 +45,11 @@ async function autoBootcampLoop() {
model = uw.MM.getModelByNameAndPlayerId('PlayerAttackSpot'); model = uw.MM.getModelByNameAndPlayerId('PlayerAttackSpot');
} catch (e) { return; } } catch (e) { return; }
if (!model) return; // Model not loaded yet (player hasn't opened the camp UI this session)
if (!model || typeof model.getLevel?.() === 'undefined') {
log('[bootcamp] PlayerAttackSpot model not ready — skipping');
return;
}
// ── 1. Claim reward if available ────────────────────────────── // ── 1. Claim reward if available ──────────────────────────────
try { try {
@@ -58,7 +62,6 @@ async function autoBootcampLoop() {
const stashable = reward.stashable; const stashable = reward.stashable;
if (isInstant && !isFavor) { if (isInstant && !isFavor) {
// Use instant rewards immediately
uw.gpAjax.ajaxPost('frontend_bridge', 'execute', { uw.gpAjax.ajaxPost('frontend_bridge', 'execute', {
model_url: `PlayerAttackSpot/${player_id}`, model_url: `PlayerAttackSpot/${player_id}`,
action_name: 'useReward', action_name: 'useReward',
@@ -88,7 +91,12 @@ async function autoBootcampLoop() {
// ── 2. Attack if no cooldown ─────────────────────────────────── // ── 2. Attack if no cooldown ───────────────────────────────────
try { try {
const cooldown = model.getCooldownDuration?.() ?? 1; // If getCooldownDuration is unavailable, skip safely
if (typeof model.getCooldownDuration !== 'function') {
log('[bootcamp] getCooldownDuration unavailable — skipping');
return;
}
const cooldown = model.getCooldownDuration();
if (cooldown > 0) { if (cooldown > 0) {
const minRemaining = Math.round(cooldown / 60); const minRemaining = Math.round(cooldown / 60);
await botLog(player_id, 'bootcamp', `Camp on cooldown — ${minRemaining} min remaining`); await botLog(player_id, 'bootcamp', `Camp on cooldown — ${minRemaining} min remaining`);

View File

@@ -52,6 +52,13 @@ async function pollAndExecute() {
lastKnownFarmSettings = cmdData.farm_settings || {}; lastKnownFarmSettings = cmdData.farm_settings || {};
lastKnownBotSettings = cmdData.bot_settings || {}; lastKnownBotSettings = cmdData.bot_settings || {};
// Handle manual bootcamp attack trigger
if (lastKnownBotSettings.attack_now) {
log('Manual bootcamp attack requested! Firing immediately...');
// Fire asynchronously so it doesn't block the rest of pollAndExecute
setTimeout(autoBootcampLoop, 0);
}
// Feature flags — default to all on if server doesn't send them (backward compatible) // Feature flags — default to all on if server doesn't send them (backward compatible)
const features = cmdData.enabled_features || ['farm', 'admin']; const features = cmdData.enabled_features || ['farm', 'admin'];
const farmOn = features.includes('farm'); const farmOn = features.includes('farm');

View File

@@ -206,6 +206,15 @@ def get_pending_command():
'rural_trade_ratio': bot_row['rural_trade_ratio'] if bot_row else 3, 'rural_trade_ratio': bot_row['rural_trade_ratio'] if bot_row else 3,
} }
# One-shot manual attack flag
attack_now_key = f'bootcamp_attack_now_{player_id}'
flag_row = c.execute('SELECT value FROM kv_store WHERE key = ?', (attack_now_key,)).fetchone()
if flag_row and flag_row['value'] == '1':
bot_settings['attack_now'] = True
c.execute("UPDATE kv_store SET value = '0' WHERE key = ?", (attack_now_key,))
else:
bot_settings['attack_now'] = False
# Feature flags — look up this player's authorized features from their clan # Feature flags — look up this player's authorized features from their clan
member_row = c.execute( member_row = c.execute(
'SELECT features FROM clan_members WHERE player_id = ?', (str(player_id),) 'SELECT features FROM clan_members WHERE player_id = ?', (str(player_id),)

View File

@@ -543,3 +543,23 @@ def bot_logs():
conn.close() conn.close()
return jsonify({'ok': True}) return jsonify({'ok': True})
# ------------------------------------------------------------------
# POST /dashboard/bootcamp-attack-now
# Sets a one-shot flag consumed by the TM bot on the next poll.
# ------------------------------------------------------------------
@dashboard.route('/dashboard/bootcamp-attack-now', methods=['POST'])
def bootcamp_attack_now():
player_id = (request.json or {}).get('player_id')
if not player_id:
return jsonify({'error': 'missing player_id'}), 400
key = f'bootcamp_attack_now_{player_id}'
conn = get_db()
conn.execute('''
INSERT INTO kv_store (key, value, updated_at) VALUES (?, '1', ?)
ON CONFLICT(key) DO UPDATE SET value='1', updated_at=excluded.updated_at
''', (key, datetime.utcnow().isoformat()))
conn.commit()
conn.close()
return jsonify({'ok': True})

View File

@@ -341,6 +341,11 @@
<button class="save-btn" onclick="saveBotSettings()">💾 Αποθήκευση</button> <button class="save-btn" onclick="saveBotSettings()">💾 Αποθήκευση</button>
<span class="save-status" id="bot-save-status">✓ Αποθηκεύτηκε</span> <span class="save-status" id="bot-save-status">✓ Αποθηκεύτηκε</span>
<div style="margin-top: 1rem; border-top: 1px solid #1a3040; padding-top: 1rem;">
<button class="save-btn" id="bootcamp-attack-btn" onclick="attackBootcampNow()" style="background: linear-gradient(135deg, #7a2a2a, #cc4a4a); width: 100%;">⚔️ Επίθεση Τώρα</button>
<div style="text-align: center; margin-top: 5px;"><span class="save-status" id="bootcamp-attack-status">Εντολή εστάλη!</span></div>
</div>
<h3 style="margin-top:1.5rem;font-size:0.9rem;color:#aaa;">📋 Ιστορικό</h3> <h3 style="margin-top:1.5rem;font-size:0.9rem;color:#aaa;">📋 Ιστορικό</h3>
<div id="bootcamp-log" style="background:#0a1520;border:1px solid #1a3040;border-radius:8px;padding:10px;max-height:180px;overflow-y:auto;font-size:0.78rem;font-family:monospace;color:#8ab4d0;"> <div id="bootcamp-log" style="background:#0a1520;border:1px solid #1a3040;border-radius:8px;padding:10px;max-height:180px;overflow-y:auto;font-size:0.78rem;font-family:monospace;color:#8ab4d0;">
<span style="color:#444;">Αναμονή δεδομένων...</span> <span style="color:#444;">Αναμονή δεδομένων...</span>
@@ -600,6 +605,31 @@
}); });
} }
function attackBootcampNow() {
const btn = document.getElementById('bootcamp-attack-btn');
const status = document.getElementById('bootcamp-attack-status');
const originalText = btn.innerText;
btn.innerText = '⏳ Αποστολή...';
btn.disabled = true;
fetch('/dashboard/bootcamp-attack-now', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ player_id: PLAYER_ID })
})
.then(r => r.json())
.then(() => {
btn.innerText = '✓ Εστάλη!';
status.style.opacity = '1';
setTimeout(() => {
btn.innerText = originalText;
btn.disabled = false;
status.style.opacity = '0';
}, 2000);
});
}
async function checkWarehouseStatus() { async function checkWarehouseStatus() {
try { try {
const res = await fetch(`/api/farm_status?player_id=${PLAYER_ID}`); const res = await fetch(`/api/farm_status?player_id=${PLAYER_ID}`);