import type { RowDataPacket } from 'mysql2' import { db, DB } from './db' import { checkSecurityToken } from './security-token' import { executeSoapCommand } from './soap' /** * Renombrar hermandad: el usuario elige una hermandad de la que sea Maestro * (guild_member.rank = 0) y le pone un nombre nuevo. Cuesta 1000 PD y requiere * el token de seguridad. El renombrado se aplica por SOAP (.guild rename). */ export const GUILD_RENAME_PD = 1000 /** Precio equivalente en euros (100 PD = 1 €) para pagar con Stripe/SumUp. */ export const GUILD_RENAME_EUR = GUILD_RENAME_PD / 100 /** Nombre de hermandad: A-Za-z y espacio, máx. 24 (regla del juego / diseño). */ const NAME_RE = /^[A-Za-z ]{1,24}$/ export interface GmGuild { guildid: number name: string } /** Hermandades de las que la cuenta tiene un personaje Maestro (rank 0). */ export async function getGuildMasterGuilds(accountId: number): Promise { if (!accountId) return [] try { const [rows] = await db(DB.characters).query( `SELECT g.guildid, g.name FROM guild g JOIN guild_member gm ON g.guildid = gm.guildid JOIN characters c ON c.guid = gm.guid WHERE c.account = ? AND gm.rank = 0 ORDER BY g.name`, [accountId], ) return rows.map((r) => ({ guildid: Number(r.guildid), name: r.name })) } catch { return [] } } /** * Comprueba que la cuenta puede renombrar la hermandad (antes de cobrar): * nombre válido, token de seguridad correcto, es Maestro de esa hermandad y el * nuevo nombre no está en uso. Devuelve el nombre antiguo si todo es correcto. */ export async function checkGuildRenameEligibility(params: { accountId: number guildId: number newName: string token: string }): Promise<{ ok: boolean; error?: string; oldName?: string }> { const { accountId, guildId, token } = params const newName = (params.newName || '').trim() if (!accountId || !guildId || !newName || !token) return { ok: false, error: 'missingFields' } if (!NAME_RE.test(newName)) return { ok: false, error: 'invalidName' } if (!(await checkSecurityToken(accountId, token))) return { ok: false, error: 'invalidToken' } // La hermandad debe ser una de las que la cuenta dirige (Maestro). const guilds = await getGuildMasterGuilds(accountId) const guild = guilds.find((g) => g.guildid === guildId) if (!guild) return { ok: false, error: 'notGuildMaster' } const oldName = guild.name if (/["\r\n]/.test(oldName)) return { ok: false, error: 'genericError' } // nombre antiguo no seguro para SOAP // El nuevo nombre no puede coincidir con otra hermandad existente. try { const [dup] = await db(DB.characters).query( 'SELECT guildid FROM guild WHERE name = ? AND guildid <> ? LIMIT 1', [newName, guildId], ) if (dup[0]) return { ok: false, error: 'nameTaken' } } catch { return { ok: false, error: 'genericError' } } return { ok: true, oldName } } /** * Aplica el renombrado de hermandad por SOAP (post-pago). Busca el nombre * antiguo por `guildId` y ejecuta `.guild rename`. Convención del proyecto: * respuesta no nula = aplicado (igual que soapFulfill en paid-services). */ export async function renameGuildBySoap(guildId: number, newNameRaw: string): Promise { const newName = (newNameRaw || '').trim() if (!guildId || !NAME_RE.test(newName)) return false let oldName: string try { const [rows] = await db(DB.characters).query( 'SELECT name FROM guild WHERE guildid = ? LIMIT 1', [guildId], ) if (!rows[0]) return false oldName = String(rows[0].name) } catch { return false } if (/["\r\n]/.test(oldName)) return false const soap = await executeSoapCommand(`.guild rename "${oldName}" "${newName}"`) return soap !== null }