import type { RowDataPacket } from 'mysql2' import { db, DB } from './db' import { executeSoapCommand } from './soap' import { getGameCharacters } from './characters' /** * Recuperar ítems (/restore-items): usa el sistema de recuperación de AzerothCore * por SOAP. `.item restore list ` lista los ítems borrados recuperables (el * core aplica las condiciones: calidad Raro+, equipable, se liga al recoger, * borrado < 7 días). La consulta tiene cooldown de 8 h por personaje. La restauración * de cada ítem se paga por **SumUp** (servicio `restore-item`, ejecuta `.item restore`). */ const SEARCH_COOLDOWN_MS = 8 * 60 * 60 * 1000 const SAFE_NAME = /^[A-Za-z]{1,12}$/ export interface DeletedItem { recoverId: number itemId: number name: string count: number } /** Parsea la salida de `.item restore list` (líneas "Recover id: X | Item: NAME (ID) | Count: N"). */ export function parseRecoverList(soap: string): DeletedItem[] { const items: DeletedItem[] = [] const re = /Recover id:\s*(\d+)\s*\|\s*Item:\s*(.+?)\s*\((\d+)\)\s*\|\s*Count:\s*(\d+)/g let m: RegExpExecArray | null while ((m = re.exec(soap)) !== null) { items.push({ recoverId: Number(m[1]), name: m[2].trim(), itemId: Number(m[3]), count: Number(m[4]) }) } return items } async function ownsCharacter(accountId: number, character: string): Promise { if (!SAFE_NAME.test(character)) return false const chars = await getGameCharacters(accountId) return chars.some((c) => c.name === character) } export interface ListResult { success: boolean error?: string minutes?: number items?: DeletedItem[] } /** Lista los ítems borrados recuperables de un personaje (cooldown 8 h). */ export async function listDeletedItems(accountId: number, character: string): Promise { if (!(await ownsCharacter(accountId, character))) return { success: false, error: 'characterNotOwned' } // Cooldown de 8 h por personaje. try { const [last] = await db(DB.default).query( 'SELECT used_at FROM itemsearchhistory WHERE character_name = ? ORDER BY used_at DESC LIMIT 1', [character], ) if (last[0]) { const diff = Date.now() - new Date(last[0].used_at).getTime() if (diff < SEARCH_COOLDOWN_MS) { return { success: false, error: 'cooldown', minutes: Math.ceil((SEARCH_COOLDOWN_MS - diff) / 60000) } } } } catch { /* ignore */ } const soap = await executeSoapCommand(`.item restore list ${character}`) if (soap === null) return { success: false, error: 'soapError' } await db(DB.default) .query('INSERT INTO itemsearchhistory (character_name, used_at) VALUES (?, NOW())', [character]) .catch(() => {}) return { success: true, items: parseRecoverList(soap) } }