Foro: autor con personaje aleatorio de la cuenta
- El nombre del autor muestra un personaje ALEATORIO de la cuenta (no la id). - Nombre con <Hermandad> en dorado (oculto si el personaje no tiene guild). - Avatar = imagen de raza del personaje (raza+género). - Cajas nuevas: puntos de logros, nivel y clase (con su color de clase). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
+77
-17
@@ -87,6 +87,23 @@ export interface PosterInfo {
|
||||
joindate: string | null
|
||||
postCount: number
|
||||
avatar: string
|
||||
charName: string | null
|
||||
race: number
|
||||
gender: number
|
||||
classId: number
|
||||
level: number
|
||||
guildName: string | null
|
||||
achPoints: number
|
||||
}
|
||||
|
||||
// Imagen de raza (CDN wowhead) según raza+género del personaje elegido.
|
||||
const FORUM_RACE_SLUG: Record<number, string> = {
|
||||
1: 'human', 2: 'orc', 3: 'dwarf', 4: 'nightelf', 5: 'scourge', 6: 'tauren',
|
||||
7: 'gnome', 8: 'troll', 9: 'goblin', 10: 'bloodelf', 11: 'draenei', 22: 'worgen',
|
||||
}
|
||||
function raceAvatar(race: number, gender: number): string {
|
||||
const slug = FORUM_RACE_SLUG[race] || 'human'
|
||||
return `https://wow.zamimg.com/images/wow/icons/large/race_${slug}_${gender === 1 ? 'female' : 'male'}.jpg`
|
||||
}
|
||||
|
||||
// Avatar por clase (FusionCMS) del personaje de más nivel de la cuenta. Solo clases
|
||||
@@ -383,20 +400,74 @@ export async function resolvePosters(ids: number[]): Promise<Map<number, PosterI
|
||||
|
||||
// Clase del personaje de más nivel de cada cuenta (para el avatar). Se tolera que
|
||||
// acore_characters no exista o esté vacía: el avatar cae a guerrero por defecto.
|
||||
const classByAccount = new Map<number, number>()
|
||||
// Personaje ALEATORIO por cuenta (nombre, raza, género, clase, nivel).
|
||||
const charByAccount = new Map<number, { guid: number; name: string; race: number; gender: number; class: number; level: number }>()
|
||||
try {
|
||||
const [chars] = await db(DB.characters).query<RowDataPacket[]>(
|
||||
`SELECT account, class FROM characters WHERE account IN (${placeholders}) ORDER BY level DESC, guid ASC`,
|
||||
`SELECT account, guid, name, race, gender, class, level FROM characters WHERE account IN (${placeholders})`,
|
||||
unique,
|
||||
)
|
||||
const byAcc = new Map<number, RowDataPacket[]>()
|
||||
for (const c of chars) {
|
||||
const acc = Number(c.account)
|
||||
if (!classByAccount.has(acc)) classByAccount.set(acc, Number(c.class))
|
||||
if (!byAcc.has(acc)) byAcc.set(acc, [])
|
||||
byAcc.get(acc)!.push(c)
|
||||
}
|
||||
for (const [acc, list] of byAcc) {
|
||||
const c = list[Math.floor(Math.random() * list.length)]
|
||||
charByAccount.set(acc, {
|
||||
guid: Number(c.guid), name: String(c.name), race: Number(c.race),
|
||||
gender: Number(c.gender), class: Number(c.class), level: Number(c.level),
|
||||
})
|
||||
}
|
||||
} catch {
|
||||
/* sin personajes: avatar por defecto */
|
||||
}
|
||||
|
||||
// Hermandad y puntos de logros de los personajes elegidos.
|
||||
const chosenGuids = [...charByAccount.values()].map((c) => c.guid)
|
||||
const guildByGuid = new Map<number, string>()
|
||||
const ptsByGuid = new Map<number, number>()
|
||||
if (chosenGuids.length) {
|
||||
const gp = chosenGuids.map(() => '?').join(',')
|
||||
try {
|
||||
const [gr] = await db(DB.characters).query<RowDataPacket[]>(
|
||||
`SELECT gm.guid, g.name FROM guild_member gm JOIN guild g ON g.guildid = gm.guildid WHERE gm.guid IN (${gp})`,
|
||||
chosenGuids,
|
||||
)
|
||||
for (const r of gr) guildByGuid.set(Number(r.guid), String(r.name))
|
||||
} catch { /* */ }
|
||||
try {
|
||||
const [pr] = await db(DB.characters).query<RowDataPacket[]>(
|
||||
`SELECT ca.guid, COALESCE(SUM(ap.points),0) AS pts
|
||||
FROM character_achievement ca JOIN acore_world.achievement_points ap ON ap.id = ca.achievement
|
||||
WHERE ca.guid IN (${gp}) GROUP BY ca.guid`,
|
||||
chosenGuids,
|
||||
)
|
||||
for (const r of pr) ptsByGuid.set(Number(r.guid), Number(r.pts))
|
||||
} catch { /* */ }
|
||||
}
|
||||
|
||||
const buildPoster = (id: number, base: Partial<PosterInfo>): PosterInfo => {
|
||||
const ch = charByAccount.get(id)
|
||||
return {
|
||||
id,
|
||||
name: base.name ?? `#${id}`,
|
||||
gmlevel: base.gmlevel ?? 0,
|
||||
isStaff: base.isStaff ?? false,
|
||||
joindate: base.joindate ?? null,
|
||||
postCount: 0,
|
||||
avatar: ch ? raceAvatar(ch.race, ch.gender) : avatarForClass(undefined),
|
||||
charName: ch?.name ?? null,
|
||||
race: ch?.race ?? 0,
|
||||
gender: ch?.gender ?? 0,
|
||||
classId: ch?.class ?? 0,
|
||||
level: ch?.level ?? 0,
|
||||
guildName: ch ? (guildByGuid.get(ch.guid) ?? null) : null,
|
||||
achPoints: ch ? (ptsByGuid.get(ch.guid) ?? 0) : 0,
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
const [accs] = await db(DB.auth).query<RowDataPacket[]>(
|
||||
`SELECT id, username, joindate FROM account WHERE id IN (${placeholders})`,
|
||||
@@ -411,15 +482,12 @@ export async function resolvePosters(ids: number[]): Promise<Map<number, PosterI
|
||||
|
||||
for (const a of accs) {
|
||||
const gmlevel = gm.get(Number(a.id)) ?? 0
|
||||
map.set(Number(a.id), {
|
||||
id: Number(a.id),
|
||||
map.set(Number(a.id), buildPoster(Number(a.id), {
|
||||
name: String(a.username || '').replace(/#\d+$/, '') || `#${a.id}`,
|
||||
gmlevel,
|
||||
isStaff: gmlevel >= STAFF_GMLEVEL,
|
||||
joindate: a.joindate ? new Date(a.joindate).toISOString() : null,
|
||||
postCount: 0,
|
||||
avatar: avatarForClass(classByAccount.get(Number(a.id))),
|
||||
})
|
||||
}))
|
||||
}
|
||||
} catch {
|
||||
/* acore_auth no disponible: se devuelven placeholders abajo */
|
||||
@@ -427,15 +495,7 @@ export async function resolvePosters(ids: number[]): Promise<Map<number, PosterI
|
||||
// Cualquier id no resuelto (cuenta borrada) recibe un placeholder.
|
||||
for (const id of unique) {
|
||||
if (!map.has(id)) {
|
||||
map.set(id, {
|
||||
id,
|
||||
name: `#${id}`,
|
||||
gmlevel: 0,
|
||||
isStaff: false,
|
||||
joindate: null,
|
||||
postCount: 0,
|
||||
avatar: avatarForClass(classByAccount.get(id)),
|
||||
})
|
||||
map.set(id, buildPoster(id, {}))
|
||||
}
|
||||
}
|
||||
return map
|
||||
|
||||
Reference in New Issue
Block a user