enchance data

This commit is contained in:
2026-04-20 12:24:51 +03:00
parent 5d564de336
commit bb7d7392a8
5 changed files with 129 additions and 30 deletions

View File

@@ -1,7 +1,7 @@
// ==UserScript==
// @name Grepolis Remote Control
// @namespace http://tampermonkey.net/
// @version 1.2
// @version 1.3
// @description Polls grepo.haunter-pets.top for remote commands and executes them in-game
// @author Dimitrios
// @match https://*.grepolis.com/game/*
@@ -67,9 +67,13 @@
// Push town state to relay
// ----------------------------------------------------------------
function gatherState() {
const towns = uw.ITowns?.towns || {};
const player = uw.Game?.player_name || '';
const world = uw.Game?.world_id || '';
const towns = uw.ITowns?.towns || {};
const player = uw.Game?.player_name || '';
const player_id = uw.Game?.player_id ?? null;
const alliance_id = uw.Game?.alliance_id ?? null;
const total_points = uw.Game?.player_points ?? 0;
const world = uw.Game?.world_id || '';
const townList = Object.values(towns).map(town => {
const res = town.resources();
@@ -135,9 +139,35 @@
log(`storage capacity lookup failed: ${e}`);
}
// ---- Coordinates & sea zone -----------------------------------------
let x = null, y = null, sea = null;
try {
x = town.getIslandCoordinateX?.() ?? null;
y = town.getIslandCoordinateY?.() ?? null;
if (typeof x === 'number' && typeof y === 'number') {
sea = Math.floor(x / 100) * 10 + Math.floor(y / 100);
}
} catch (e) {}
// ---- Researches -----------------------------------------------------
let researches = {};
try {
const r = town.researches?.();
if (r) researches = r.attributes ?? (typeof r === 'object' ? r : {});
} catch (e) {}
// ---- Extra town flags -----------------------------------------------
let has_premium = false;
let bonuses = {};
let wonder_points = 0;
try { has_premium = town.hasPremium?.() || false; } catch (e) {}
try { bonuses = town.getBonus?.() || {}; } catch (e) {}
try { wonder_points = town.wonder_points || 0; } catch (e) {}
return {
town_id: town.id,
town_name: town.name,
x, y, sea,
wood: res.wood,
stone: res.stone,
iron: res.iron,
@@ -149,10 +179,14 @@
units: unitsObj,
buildingOrder: buildQueue,
buildData: buildDataMap,
researches,
has_premium,
bonuses,
wonder_points,
};
});
return { player, world_id: world, towns: townList };
return { player, player_id, alliance_id, total_points, world_id: world, towns: townList };
}
function pushState() {

18
db.py
View File

@@ -35,11 +35,29 @@ def init_db():
town_id TEXT PRIMARY KEY,
town_name TEXT,
player TEXT,
player_id TEXT,
alliance_id TEXT,
world_id TEXT,
x REAL,
y REAL,
sea INTEGER,
data TEXT NOT NULL, -- full JSON snapshot
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
)
''')
# Migration: add new columns if upgrading an existing database
for _col in [
'ALTER TABLE town_state ADD COLUMN player_id TEXT',
'ALTER TABLE town_state ADD COLUMN alliance_id TEXT',
'ALTER TABLE town_state ADD COLUMN x REAL',
'ALTER TABLE town_state ADD COLUMN y REAL',
'ALTER TABLE town_state ADD COLUMN sea INTEGER',
]:
try:
c.execute(_col)
except Exception:
pass # column already exists
conn.commit()
conn.close()

View File

@@ -17,26 +17,40 @@ def receive_state():
return jsonify({'error': 'no data'}), 400
towns = data.get('towns', [])
player = data.get('player', '')
world_id = data.get('world_id', '')
player = data.get('player', '')
player_id = data.get('player_id', '')
alliance_id = str(data.get('alliance_id', '') or '')
world_id = data.get('world_id', '')
conn = get_db()
c = conn.cursor()
for town in towns:
x = town.get('x')
y = town.get('y')
sea = town.get('sea')
c.execute('''
INSERT INTO town_state (town_id, town_name, player, world_id, data, updated_at)
VALUES (?, ?, ?, ?, ?, ?)
INSERT INTO town_state
(town_id, town_name, player, player_id, alliance_id, world_id, x, y, sea, data, updated_at)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
ON CONFLICT(town_id) DO UPDATE SET
town_name = excluded.town_name,
player = excluded.player,
world_id = excluded.world_id,
data = excluded.data,
updated_at = excluded.updated_at
town_name = excluded.town_name,
player = excluded.player,
player_id = excluded.player_id,
alliance_id = excluded.alliance_id,
world_id = excluded.world_id,
x = excluded.x,
y = excluded.y,
sea = excluded.sea,
data = excluded.data,
updated_at = excluded.updated_at
''', (
str(town['town_id']),
town.get('town_name', ''),
player,
player_id,
alliance_id,
world_id,
x, y, sea,
json.dumps(town),
datetime.utcnow().isoformat()
))

View File

@@ -23,7 +23,8 @@ def index():
def get_towns():
conn = get_db()
rows = conn.execute('''
SELECT town_id, town_name, player, world_id, data, updated_at
SELECT town_id, town_name, player, player_id, alliance_id,
world_id, x, y, sea, data, updated_at
FROM town_state
ORDER BY town_name ASC
''').fetchall()
@@ -33,11 +34,16 @@ def get_towns():
for row in rows:
d = json.loads(row['data'])
towns.append({
'town_id': row['town_id'],
'town_name': row['town_name'],
'player': row['player'],
'world_id': row['world_id'],
'updated_at': row['updated_at'],
'town_id': row['town_id'],
'town_name': row['town_name'],
'player': row['player'],
'player_id': row['player_id'],
'alliance_id': row['alliance_id'],
'world_id': row['world_id'],
'x': row['x'],
'y': row['y'],
'sea': row['sea'],
'updated_at': row['updated_at'],
'resources': {
'wood': d.get('wood', 0),
'stone': d.get('stone', 0),
@@ -45,12 +51,17 @@ def get_towns():
'storage': d.get('storage', 0),
'population': d.get('population', 0),
},
'buildings': d.get('buildings', {}),
'units': d.get('units', {}),
'points': d.get('points', 0),
'god': d.get('god', None),
'build_queue': d.get('buildingOrder', []),
'build_data': d.get('buildData', {}),
'buildings': d.get('buildings', {}),
'units': d.get('units', {}),
'points': d.get('points', 0),
'god': d.get('god', None),
'build_queue': d.get('buildingOrder', []),
'build_data': d.get('buildData', {}),
'researches': d.get('researches', {}),
'has_premium': d.get('has_premium', False),
'bonuses': d.get('bonuses', {}),
'wonder_points': d.get('wonder_points', 0),
'total_points': d.get('total_points', 0),
})
return jsonify(towns)

View File

@@ -253,6 +253,11 @@
<strong style="font-size: 0.75rem; color: #888; text-transform: uppercase;">Στρατος</strong>
<div id="td-units" style="font-size: 0.85rem; margin-top: 4px; line-height: 1.5; color: #a4c84a;"></div>
</div>
<div style="flex: 1; min-width: 130px;">
<strong style="font-size: 0.75rem; color: #888; text-transform: uppercase;">Ερευνες</strong>
<div id="td-researches" style="font-size: 0.8rem; margin-top: 4px; line-height: 1.5; color: #c8a44a;"></div>
</div>
</div>
</div>
@@ -474,8 +479,8 @@ function renderTowns() {
return `
<div class="town-card ${selected} ${stale ? 'town-stale' : ''}"
onclick="selectTown('${t.town_id}')">
<div class="town-name">${t.town_name}</div>
<div class="town-meta">${t.points} pts · ${t.god || 'No god'} · ${ageMin}m ago</div>
<div class="town-name">${t.town_name}${t.has_premium ? ' <span style="color:#c8a44a;font-size:0.7rem;">★</span>' : ''}</div>
<div class="town-meta">${t.sea != null ? `🌊 Θ${t.sea} · ` : ''}${t.points} pts · ${t.god || 'No god'} · ${ageMin}m ago</div>
<div class="town-res">
<div class="res-item"><div class="res-icon res-wood"></div>${fmt(t.resources.wood)}</div>
<div class="res-item"><div class="res-icon res-stone"></div>${fmt(t.resources.stone)}</div>
@@ -532,12 +537,29 @@ function renderTownDetails() {
`;
const godName = t.god ? t.god.charAt(0).toUpperCase() + t.god.slice(1) : 'Κανένας';
const seaStr = t.sea != null ? `Θ${t.sea}` : '—';
const coordStr = (t.x != null && t.y != null) ? ` (${Math.round(t.x)}:${Math.round(t.y)})` : '';
document.getElementById('td-general').innerHTML = `
<div>Πόντοι: <strong>${t.points}</strong></div>
<div>Πόντοι: <strong>${t.points}</strong>${t.wonder_points ? ` / Θαύμα: <strong>${t.wonder_points}</strong>` : ''}</div>
<div>Θεός: <strong>${godName}</strong></div>
<div>Παίκτης: <strong style="color:#aaa">${t.player}</strong></div>
<div>Θάλασσα: <strong style="color:#6fcfcf">${seaStr}</strong><span style="color:#666;font-size:0.72rem">${coordStr}</span></div>
<div>Παίκτης: <strong style="color:#aaa">${t.player}</strong>${t.has_premium ? ' <span style="color:#c8a44a" title="Premium">★</span>' : ''}</div>
${t.alliance_id ? `<div>Συμμαχία: <strong style="color:#aaa">${t.alliance_id}</strong></div>` : ''}
`;
// ---- Researches ----
const researchObj = t.researches || {};
const researchEntries = Object.entries(researchObj).filter(([, v]) => v === true || v === 1 || Number(v) > 0);
let resHtml = '';
if (researchEntries.length) {
resHtml = researchEntries.map(([k]) =>
`<div>✓ ${k.replace(/_/g, ' ')}</div>`
).join('');
} else {
resHtml = '<div style="color:#555">Καμία έρευνα</div>';
}
document.getElementById('td-researches').innerHTML = resHtml;
const unitsObj = t.units || {};
let unitsHtml = '';
for(const [unitKey, count] of Object.entries(unitsObj)) {