v0.14.16 - Armory scraper now scavenges full store for all items
- Previously only scraped items visible in the current filter tab (had to visit each tab: New, Two-Handed, Light, Heavy, etc.) - Now also scans the full dynjs_equip store for ALL items in your account (305 total) — every filter tab's worth of data at once - Category guessed from tooltip type (Cloth, Light, Heavy, Weapon, etc.) - One visit to the Armory page + HV.scrapeGear() = complete inventory
This commit is contained in:
parent
d95409026f
commit
4aff813f01
5 changed files with 162 additions and 81 deletions
|
|
@ -1,7 +1,7 @@
|
||||||
// ==UserScript==
|
// ==UserScript==
|
||||||
// @name HV Unified
|
// @name HV Unified
|
||||||
// @namespace hvunified
|
// @namespace hvunified
|
||||||
// @version 0.14.15
|
// @version 0.14.16
|
||||||
// @description HV Unified — battle automation, guidance, and UI enhancements for HentaiVerse
|
// @description HV Unified — battle automation, guidance, and UI enhancements for HentaiVerse
|
||||||
// @author GaboGG + Hermes
|
// @author GaboGG + Hermes
|
||||||
// @match *://*.hentaiverse.org/*
|
// @match *://*.hentaiverse.org/*
|
||||||
|
|
@ -17,7 +17,7 @@
|
||||||
// CONFIG — default settings
|
// CONFIG — default settings
|
||||||
// ═══════════════════════════════════════════════════════════════════════
|
// ═══════════════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
const VERSION = '0.14.15';
|
const VERSION = '0.14.16';
|
||||||
|
|
||||||
const CFG = {
|
const CFG = {
|
||||||
// — Battle automation
|
// — Battle automation
|
||||||
|
|
@ -3401,37 +3401,64 @@ function scrapeCharacterEquip() {
|
||||||
function scrapeArmory() {
|
function scrapeArmory() {
|
||||||
const scraped = [];
|
const scraped = [];
|
||||||
const el = document.getElementById('equiplist');
|
const el = document.getElementById('equiplist');
|
||||||
if (!el) return scraped;
|
|
||||||
let cat = '';
|
|
||||||
|
|
||||||
el.querySelectorAll('table tr').forEach(row => {
|
// First, scrape any items visible in the current filter tab
|
||||||
if (row.classList.contains('eqtplabel')) { cat = (row.textContent || '').trim(); return; }
|
if (el) {
|
||||||
if (row.classList.contains('eqselall')) return;
|
let cat = '';
|
||||||
const omo = row.getAttribute('onmouseover') || '';
|
el.querySelectorAll('table tr').forEach(row => {
|
||||||
const idM = omo.match(/hover_equip\((\d+)\)/);
|
if (row.classList.contains('eqtplabel')) { cat = (row.textContent || '').trim(); return; }
|
||||||
const cb = row.querySelector('input[name="eqids[]"]');
|
if (row.classList.contains('eqselall')) return;
|
||||||
const itemId = idM ? idM[1] : (cb ? cb.value : '');
|
const omo = row.getAttribute('onmouseover') || '';
|
||||||
const label = row.querySelector('label');
|
const idM = omo.match(/hover_equip\((\d+)\)/);
|
||||||
let name = label ? (label.textContent || '').trim() : '';
|
const cb = row.querySelector('input[name="eqids[]"]');
|
||||||
if (cb && label) name = (label.textContent || '').replace(cb.outerHTML, '').trim();
|
const itemId = idM ? idM[1] : (cb ? cb.value : '');
|
||||||
if (!itemId && !name) return;
|
const label = row.querySelector('label');
|
||||||
|
let name = label ? (label.textContent || '').trim() : '';
|
||||||
const obj = { id: itemId, name: name || 'Unknown', category: cat, source: 'armory', scrapedAt: Date.now() };
|
if (cb && label) name = (label.textContent || '').replace(cb.outerHTML, '').trim();
|
||||||
if (itemId) {
|
if (!itemId && !name) return;
|
||||||
const data = getHVEquipData(itemId);
|
const obj = { id: itemId, name: name || 'Unknown', category: cat, source: 'armory', scrapedAt: Date.now() };
|
||||||
if (data) {
|
if (itemId) {
|
||||||
if (data.t) obj.name = data.t;
|
const data = getHVEquipData(itemId);
|
||||||
if (data.q != null) obj.quality = data.q;
|
if (data) {
|
||||||
if (data.d) obj.stats = parseEquipHTML(data.d);
|
if (data.t) obj.name = data.t;
|
||||||
|
if (data.q != null) obj.quality = data.q;
|
||||||
|
if (data.d) obj.stats = parseEquipHTML(data.d);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
scraped.push(obj);
|
||||||
scraped.push(obj);
|
});
|
||||||
});
|
}
|
||||||
|
|
||||||
|
// Second, scan the full store for items NOT in the visible list
|
||||||
|
// This catches all inventory regardless of which filter tab you're on
|
||||||
|
const store = getHVEquipStore();
|
||||||
|
if (store) {
|
||||||
|
const visibleIds = new Set(scraped.map(s => s.id));
|
||||||
|
Object.entries(store).forEach(([id, data]) => {
|
||||||
|
if (visibleIds.has(id)) return; // already scraped from DOM
|
||||||
|
const stats = data.d ? parseEquipHTML(data.d) : null;
|
||||||
|
// Try to determine category from stats type
|
||||||
|
let category = 'All Items';
|
||||||
|
if (stats && stats.type) {
|
||||||
|
if (stats.type.includes('Cloth')) category = 'Cloth Armor';
|
||||||
|
else if (stats.type.includes('Light')) category = 'Light Armor';
|
||||||
|
else if (stats.type.includes('Heavy')) category = 'Heavy Armor';
|
||||||
|
else if (stats.type.includes('Shield')) category = 'Shield';
|
||||||
|
else if (stats.type.includes('Staff')) category = 'Staff';
|
||||||
|
else if (stats.type.includes('Axe') || stats.type.includes('Mace') || stats.type.includes('Sword') || stats.type.includes('Estoc') || stats.type.includes('Rapier') || stats.type.includes('Club') || stats.type.includes('Dagger')) category = 'Weapon';
|
||||||
|
}
|
||||||
|
if (!stats || (!data.t && !data.d)) return; // skip empty entries
|
||||||
|
scraped.push({
|
||||||
|
id, name: data.t || 'Unknown', category,
|
||||||
|
quality: data.q, stats, source: 'armory', scrapedAt: Date.now()
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
if (scraped.length > 0) {
|
if (scraped.length > 0) {
|
||||||
const db = getGearDB();
|
const db = getGearDB();
|
||||||
saveGearDB([...db.filter(e => e.source !== 'armory'), ...scraped]);
|
saveGearDB([...db.filter(e => e.source !== 'armory'), ...scraped]);
|
||||||
console.log(`%c[HV] 📦 Armory: ${scraped.length} items (${scraped.filter(s => s.stats).length} with stats)`, 'color:#0f0');
|
console.log(`%c[HV] 📦 Armory: ${scraped.length} items (${scraped.filter(s => s.stats).length} with stats, ${scraped.length - (el ? el.querySelectorAll('table tr[onmouseover]').length : 0)} from store)`, 'color:#0f0');
|
||||||
}
|
}
|
||||||
return scraped;
|
return scraped;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
// ==UserScript==
|
// ==UserScript==
|
||||||
// @name HV Unified
|
// @name HV Unified
|
||||||
// @namespace hvunified
|
// @namespace hvunified
|
||||||
// @version 0.14.15
|
// @version 0.14.16
|
||||||
// @description HV Unified — battle automation, guidance, and UI enhancements for HentaiVerse
|
// @description HV Unified — battle automation, guidance, and UI enhancements for HentaiVerse
|
||||||
// @author GaboGG + Hermes
|
// @author GaboGG + Hermes
|
||||||
// @match *://*.hentaiverse.org/*
|
// @match *://*.hentaiverse.org/*
|
||||||
|
|
@ -17,7 +17,7 @@
|
||||||
// CONFIG — default settings
|
// CONFIG — default settings
|
||||||
// ═══════════════════════════════════════════════════════════════════════
|
// ═══════════════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
const VERSION = '0.14.15';
|
const VERSION = '0.14.16';
|
||||||
|
|
||||||
const CFG = {
|
const CFG = {
|
||||||
// — Battle automation
|
// — Battle automation
|
||||||
|
|
@ -3401,37 +3401,64 @@ function scrapeCharacterEquip() {
|
||||||
function scrapeArmory() {
|
function scrapeArmory() {
|
||||||
const scraped = [];
|
const scraped = [];
|
||||||
const el = document.getElementById('equiplist');
|
const el = document.getElementById('equiplist');
|
||||||
if (!el) return scraped;
|
|
||||||
let cat = '';
|
|
||||||
|
|
||||||
el.querySelectorAll('table tr').forEach(row => {
|
// First, scrape any items visible in the current filter tab
|
||||||
if (row.classList.contains('eqtplabel')) { cat = (row.textContent || '').trim(); return; }
|
if (el) {
|
||||||
if (row.classList.contains('eqselall')) return;
|
let cat = '';
|
||||||
const omo = row.getAttribute('onmouseover') || '';
|
el.querySelectorAll('table tr').forEach(row => {
|
||||||
const idM = omo.match(/hover_equip\((\d+)\)/);
|
if (row.classList.contains('eqtplabel')) { cat = (row.textContent || '').trim(); return; }
|
||||||
const cb = row.querySelector('input[name="eqids[]"]');
|
if (row.classList.contains('eqselall')) return;
|
||||||
const itemId = idM ? idM[1] : (cb ? cb.value : '');
|
const omo = row.getAttribute('onmouseover') || '';
|
||||||
const label = row.querySelector('label');
|
const idM = omo.match(/hover_equip\((\d+)\)/);
|
||||||
let name = label ? (label.textContent || '').trim() : '';
|
const cb = row.querySelector('input[name="eqids[]"]');
|
||||||
if (cb && label) name = (label.textContent || '').replace(cb.outerHTML, '').trim();
|
const itemId = idM ? idM[1] : (cb ? cb.value : '');
|
||||||
if (!itemId && !name) return;
|
const label = row.querySelector('label');
|
||||||
|
let name = label ? (label.textContent || '').trim() : '';
|
||||||
const obj = { id: itemId, name: name || 'Unknown', category: cat, source: 'armory', scrapedAt: Date.now() };
|
if (cb && label) name = (label.textContent || '').replace(cb.outerHTML, '').trim();
|
||||||
if (itemId) {
|
if (!itemId && !name) return;
|
||||||
const data = getHVEquipData(itemId);
|
const obj = { id: itemId, name: name || 'Unknown', category: cat, source: 'armory', scrapedAt: Date.now() };
|
||||||
if (data) {
|
if (itemId) {
|
||||||
if (data.t) obj.name = data.t;
|
const data = getHVEquipData(itemId);
|
||||||
if (data.q != null) obj.quality = data.q;
|
if (data) {
|
||||||
if (data.d) obj.stats = parseEquipHTML(data.d);
|
if (data.t) obj.name = data.t;
|
||||||
|
if (data.q != null) obj.quality = data.q;
|
||||||
|
if (data.d) obj.stats = parseEquipHTML(data.d);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
scraped.push(obj);
|
||||||
scraped.push(obj);
|
});
|
||||||
});
|
}
|
||||||
|
|
||||||
|
// Second, scan the full store for items NOT in the visible list
|
||||||
|
// This catches all inventory regardless of which filter tab you're on
|
||||||
|
const store = getHVEquipStore();
|
||||||
|
if (store) {
|
||||||
|
const visibleIds = new Set(scraped.map(s => s.id));
|
||||||
|
Object.entries(store).forEach(([id, data]) => {
|
||||||
|
if (visibleIds.has(id)) return; // already scraped from DOM
|
||||||
|
const stats = data.d ? parseEquipHTML(data.d) : null;
|
||||||
|
// Try to determine category from stats type
|
||||||
|
let category = 'All Items';
|
||||||
|
if (stats && stats.type) {
|
||||||
|
if (stats.type.includes('Cloth')) category = 'Cloth Armor';
|
||||||
|
else if (stats.type.includes('Light')) category = 'Light Armor';
|
||||||
|
else if (stats.type.includes('Heavy')) category = 'Heavy Armor';
|
||||||
|
else if (stats.type.includes('Shield')) category = 'Shield';
|
||||||
|
else if (stats.type.includes('Staff')) category = 'Staff';
|
||||||
|
else if (stats.type.includes('Axe') || stats.type.includes('Mace') || stats.type.includes('Sword') || stats.type.includes('Estoc') || stats.type.includes('Rapier') || stats.type.includes('Club') || stats.type.includes('Dagger')) category = 'Weapon';
|
||||||
|
}
|
||||||
|
if (!stats || (!data.t && !data.d)) return; // skip empty entries
|
||||||
|
scraped.push({
|
||||||
|
id, name: data.t || 'Unknown', category,
|
||||||
|
quality: data.q, stats, source: 'armory', scrapedAt: Date.now()
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
if (scraped.length > 0) {
|
if (scraped.length > 0) {
|
||||||
const db = getGearDB();
|
const db = getGearDB();
|
||||||
saveGearDB([...db.filter(e => e.source !== 'armory'), ...scraped]);
|
saveGearDB([...db.filter(e => e.source !== 'armory'), ...scraped]);
|
||||||
console.log(`%c[HV] 📦 Armory: ${scraped.length} items (${scraped.filter(s => s.stats).length} with stats)`, 'color:#0f0');
|
console.log(`%c[HV] 📦 Armory: ${scraped.length} items (${scraped.filter(s => s.stats).length} with stats, ${scraped.length - (el ? el.querySelectorAll('table tr[onmouseover]').length : 0)} from store)`, 'color:#0f0');
|
||||||
}
|
}
|
||||||
return scraped;
|
return scraped;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
// CONFIG — default settings
|
// CONFIG — default settings
|
||||||
// ═══════════════════════════════════════════════════════════════════════
|
// ═══════════════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
const VERSION = '0.14.15';
|
const VERSION = '0.14.16';
|
||||||
|
|
||||||
const CFG = {
|
const CFG = {
|
||||||
// — Battle automation
|
// — Battle automation
|
||||||
|
|
|
||||||
|
|
@ -158,37 +158,64 @@ function scrapeCharacterEquip() {
|
||||||
function scrapeArmory() {
|
function scrapeArmory() {
|
||||||
const scraped = [];
|
const scraped = [];
|
||||||
const el = document.getElementById('equiplist');
|
const el = document.getElementById('equiplist');
|
||||||
if (!el) return scraped;
|
|
||||||
let cat = '';
|
|
||||||
|
|
||||||
el.querySelectorAll('table tr').forEach(row => {
|
// First, scrape any items visible in the current filter tab
|
||||||
if (row.classList.contains('eqtplabel')) { cat = (row.textContent || '').trim(); return; }
|
if (el) {
|
||||||
if (row.classList.contains('eqselall')) return;
|
let cat = '';
|
||||||
const omo = row.getAttribute('onmouseover') || '';
|
el.querySelectorAll('table tr').forEach(row => {
|
||||||
const idM = omo.match(/hover_equip\((\d+)\)/);
|
if (row.classList.contains('eqtplabel')) { cat = (row.textContent || '').trim(); return; }
|
||||||
const cb = row.querySelector('input[name="eqids[]"]');
|
if (row.classList.contains('eqselall')) return;
|
||||||
const itemId = idM ? idM[1] : (cb ? cb.value : '');
|
const omo = row.getAttribute('onmouseover') || '';
|
||||||
const label = row.querySelector('label');
|
const idM = omo.match(/hover_equip\((\d+)\)/);
|
||||||
let name = label ? (label.textContent || '').trim() : '';
|
const cb = row.querySelector('input[name="eqids[]"]');
|
||||||
if (cb && label) name = (label.textContent || '').replace(cb.outerHTML, '').trim();
|
const itemId = idM ? idM[1] : (cb ? cb.value : '');
|
||||||
if (!itemId && !name) return;
|
const label = row.querySelector('label');
|
||||||
|
let name = label ? (label.textContent || '').trim() : '';
|
||||||
const obj = { id: itemId, name: name || 'Unknown', category: cat, source: 'armory', scrapedAt: Date.now() };
|
if (cb && label) name = (label.textContent || '').replace(cb.outerHTML, '').trim();
|
||||||
if (itemId) {
|
if (!itemId && !name) return;
|
||||||
const data = getHVEquipData(itemId);
|
const obj = { id: itemId, name: name || 'Unknown', category: cat, source: 'armory', scrapedAt: Date.now() };
|
||||||
if (data) {
|
if (itemId) {
|
||||||
if (data.t) obj.name = data.t;
|
const data = getHVEquipData(itemId);
|
||||||
if (data.q != null) obj.quality = data.q;
|
if (data) {
|
||||||
if (data.d) obj.stats = parseEquipHTML(data.d);
|
if (data.t) obj.name = data.t;
|
||||||
|
if (data.q != null) obj.quality = data.q;
|
||||||
|
if (data.d) obj.stats = parseEquipHTML(data.d);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
scraped.push(obj);
|
||||||
scraped.push(obj);
|
});
|
||||||
});
|
}
|
||||||
|
|
||||||
|
// Second, scan the full store for items NOT in the visible list
|
||||||
|
// This catches all inventory regardless of which filter tab you're on
|
||||||
|
const store = getHVEquipStore();
|
||||||
|
if (store) {
|
||||||
|
const visibleIds = new Set(scraped.map(s => s.id));
|
||||||
|
Object.entries(store).forEach(([id, data]) => {
|
||||||
|
if (visibleIds.has(id)) return; // already scraped from DOM
|
||||||
|
const stats = data.d ? parseEquipHTML(data.d) : null;
|
||||||
|
// Try to determine category from stats type
|
||||||
|
let category = 'All Items';
|
||||||
|
if (stats && stats.type) {
|
||||||
|
if (stats.type.includes('Cloth')) category = 'Cloth Armor';
|
||||||
|
else if (stats.type.includes('Light')) category = 'Light Armor';
|
||||||
|
else if (stats.type.includes('Heavy')) category = 'Heavy Armor';
|
||||||
|
else if (stats.type.includes('Shield')) category = 'Shield';
|
||||||
|
else if (stats.type.includes('Staff')) category = 'Staff';
|
||||||
|
else if (stats.type.includes('Axe') || stats.type.includes('Mace') || stats.type.includes('Sword') || stats.type.includes('Estoc') || stats.type.includes('Rapier') || stats.type.includes('Club') || stats.type.includes('Dagger')) category = 'Weapon';
|
||||||
|
}
|
||||||
|
if (!stats || (!data.t && !data.d)) return; // skip empty entries
|
||||||
|
scraped.push({
|
||||||
|
id, name: data.t || 'Unknown', category,
|
||||||
|
quality: data.q, stats, source: 'armory', scrapedAt: Date.now()
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
if (scraped.length > 0) {
|
if (scraped.length > 0) {
|
||||||
const db = getGearDB();
|
const db = getGearDB();
|
||||||
saveGearDB([...db.filter(e => e.source !== 'armory'), ...scraped]);
|
saveGearDB([...db.filter(e => e.source !== 'armory'), ...scraped]);
|
||||||
console.log(`%c[HV] 📦 Armory: ${scraped.length} items (${scraped.filter(s => s.stats).length} with stats)`, 'color:#0f0');
|
console.log(`%c[HV] 📦 Armory: ${scraped.length} items (${scraped.filter(s => s.stats).length} with stats, ${scraped.length - (el ? el.querySelectorAll('table tr[onmouseover]').length : 0)} from store)`, 'color:#0f0');
|
||||||
}
|
}
|
||||||
return scraped;
|
return scraped;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
// ==UserScript==
|
// ==UserScript==
|
||||||
// @name HV Unified
|
// @name HV Unified
|
||||||
// @namespace hvunified
|
// @namespace hvunified
|
||||||
// @version 0.14.15
|
// @version 0.14.16
|
||||||
// @description HV Unified — battle automation, guidance, and UI enhancements for HentaiVerse
|
// @description HV Unified — battle automation, guidance, and UI enhancements for HentaiVerse
|
||||||
// @author GaboGG + Hermes
|
// @author GaboGG + Hermes
|
||||||
// @match *://*.hentaiverse.org/*
|
// @match *://*.hentaiverse.org/*
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue