Snowflakes
GameVox devuelve IDs en el formato snowflake de 64 bits de Discord allí donde lo haría la API de Discord. La distribución de bits, el orden y las operaciones para extraer la marca de tiempo son idénticos: tu código actual de análisis de snowflakes funciona sin cambios.
Formato
Idéntico al de Discord: marca de tiempo de 42 bits + worker de 5 bits + proceso de 5 bits + contador de 12 bits, big-endian dentro de un entero de 64 bits sin signo.
┌─ 42 bits ─────────────────┬─ 5 ─┬─ 5 ─┬─ 12 ────────┐
│ ms desde la época GameVox │ wkr │ prc │ increment │
└───────────────────────────┴─────┴─────┴─────────────┘
63 22 21 17 16 12 11 0 Cada segmento coincide con Discord:
- Marca de tiempo (42 bits) — milisegundos desde la época de GameVox, ver abajo.
- ID de worker (5 bits) — se asigna a cada tarea api en ejecución al arrancar mediante un contador atómico en Redis.
- ID de proceso (5 bits) — junto al worker forma un grupo de generación único.
- Contador (12 bits) — secuencia por milisegundo y por (worker, proceso).
Época
GameVox usa la misma época que Discord: 1420070400000 (01/01/2015 00:00:00 UTC). Un analizador de snowflakes escrito para Discord funciona con los IDs de GameVox sin modificar — no hay ninguna constante que cambiar.
const SNOWFLAKE_EPOCH = 1420070400000n; Formato de cable
Cada snowflake en el cable REST / gateway se serializa como cadena JSON, no como número. JavaScript pierde precisión por encima de Number.MAX_SAFE_INTEGER (253-1) y una snowflake lo supera con frecuencia. Analiza siempre con BigInt cuando necesites operar, y guárdala como cadena en el resto de casos.
// Correcto
const id = BigInt(message.id);
// Incorrecto — pérdida silenciosa de precisión
const id = Number(message.id); Extraer la marca de tiempo
JavaScript
function snowflakeToDate(snowflake) {
const EPOCH = 1420070400000n; // igual que Discord
const ms = Number((BigInt(snowflake) >> 22n) + EPOCH);
return new Date(ms);
}
console.log(snowflakeToDate('182955831900192769'));
// → objeto Date — cuándo se generó esta snowflake Python
from datetime import datetime, timezone
SNOWFLAKE_EPOCH = 1420070400000
def snowflake_to_dt(snowflake):
ms = (int(snowflake) >> 22) + SNOWFLAKE_EPOCH
return datetime.fromtimestamp(ms / 1000, tz=timezone.utc) Java
long SNOWFLAKE_EPOCH = 1420070400000L;
long ms = (Long.parseUnsignedLong(snowflake) >>> 22) + SNOWFLAKE_EPOCH;
Instant when = Instant.ofEpochMilli(ms); Orden
Como la marca de tiempo está en los bits altos, comparar dos snowflakes como cadenas las ordena cronológicamente, igual que en Discord. Por eso la paginación con before / after / around en el endpoint de mensajes funciona sin un campo de fecha explícito.
// En un array de mensajes, del más antiguo al más nuevo:
messages.sort((a, b) => a.id < b.id ? -1 : a.id > b.id ? 1 : 0); Datos nativos y la tabla de alias
Por dentro, los objetos nativos de GameVox (servidores, canales, usuarios, mensajes, etc.) usan UUIDv4. La capa de compatibilidad genera una snowflake estable la primera vez que un objeto se expone a un bot y guarda la correspondencia en la tabla snowflake_aliases. Cada referencia posterior a ese objeto —por REST o gateway— usa la misma snowflake.
En los mensajes rellenamos las snowflakes con el created_at del mensaje como marca de tiempo de origen, para que los mensajes históricos se ordenen bien cuando un bot entra por primera vez en un canal. Para todo lo demás el alias se genera de forma perezosa en la primera exposición usando NOW(), así que la snowflake refleja cuándo lo vio el bot, no necesariamente cuándo se creó.
Hash de sharding
La fórmula de shard estándar de Discord funciona tal cual:
shardId = (BigInt(guildId) >> 22n) % BigInt(numShards); Los bots con menos de 2.500 servidores funcionan en un solo shard ([0, 1]) y no necesitan pensar en esto. Los bots más grandes obtienen la misma distribución bastante uniforme que en Discord, porque los 42 bits superiores son monótonos en el tiempo, no aleatorios.
Detalles a tener en cuenta
- No compares snowflakes con
==contra un literal numérico. En el cable son cadenas: compara con una cadena o convierte ambas aBigInt. - La marca de tiempo de 42 bits desborda unos 139 años después de la época. Las snowflakes de GameVox son válidas hasta 2165.
- No deduzcas la fecha de creación de un servidor / canal / rol a partir de su snowflake. Los objetos nativos anteriores a la plataforma de bots recibieron su snowflake en la primera exposición a un bot, no al crearse. Los mensajes son la excepción (rellenados con
created_at).