Snowflakes
O GameVox devolve IDs no formato snowflake de 64 bits do Discord sempre que a API do Discord faria isso. A disposição dos bits, a ordenação e a matemática de extração do carimbo de tempo são idênticas — seu código de análise atual funciona sem mudanças.
Formato
Idêntico ao do Discord: carimbo de 42 bits + worker de 5 bits + processo de 5 bits + contador de 12 bits, big-endian dentro de um inteiro de 64 bits sem sinal.
┌─ 42 bits ─────────────────┬─ 5 ─┬─ 5 ─┬─ 12 ────────┐
│ ms desde a época GameVox │ wkr │ prc │ increment │
└───────────────────────────┴─────┴─────┴─────────────┘
63 22 21 17 16 12 11 0 Cada segmento corresponde ao do Discord:
- Carimbo de tempo (42 bits) — milissegundos desde a época do GameVox, veja abaixo.
- ID do worker (5 bits) — atribuído a cada tarefa api em execução na inicialização, via contador atômico no Redis.
- ID do processo (5 bits) — junto com o worker forma um pool de geração único.
- Contador (12 bits) — sequência por milissegundo, por (worker, processo).
Época
O GameVox usa a mesma época do Discord: 1420070400000 (01/01/2015 00:00:00 UTC). Um analisador de snowflakes escrito para o Discord funciona com IDs do GameVox sem modificação — nenhuma constante para trocar.
const SNOWFLAKE_EPOCH = 1420070400000n; Formato de transporte
Toda snowflake no transporte REST / gateway é serializada como string JSON, não como número. O JavaScript perde precisão acima de Number.MAX_SAFE_INTEGER (253-1) e uma snowflake passa disso com frequência. Sempre analise via BigInt quando precisar fazer contas, e guarde como string no resto dos casos.
// Correto
const id = BigInt(message.id);
// Errado — perda silenciosa de precisão
const id = Number(message.id); Extraindo o carimbo de tempo
JavaScript
function snowflakeToDate(snowflake) {
const EPOCH = 1420070400000n; // igual ao Discord
const ms = Number((BigInt(snowflake) >> 22n) + EPOCH);
return new Date(ms);
}
console.log(snowflakeToDate('182955831900192769'));
// → objeto Date — quando esta snowflake foi gerada 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); Ordenação
Como o carimbo fica nos bits mais altos, comparar duas snowflakes como strings já as ordena cronologicamente — igual ao Discord. É por isso que a paginação com before / after / around no endpoint de mensagens funciona sem um campo de data explícito.
// Em um array de mensagens, da mais antiga à mais nova:
messages.sort((a, b) => a.id < b.id ? -1 : a.id > b.id ? 1 : 0); Dados nativos e a tabela de alias
Internamente, os objetos nativos do GameVox (servidores, canais, usuários, mensagens etc.) usam UUIDv4. A camada de compatibilidade gera uma snowflake estável na primeira vez que um objeto é exposto a um bot e guarda o par na tabela snowflake_aliases. Toda referência posterior a esse objeto — por REST ou gateway — usa a mesma snowflake.
Para mensagens, preenchemos as snowflakes retroativamente com o created_at da mensagem como carimbo de origem, para que mensagens antigas ordenem corretamente quando um bot entra num canal pela primeira vez. Para todo o resto o alias é gerado sob demanda na primeira exposição, com NOW() como carimbo, então a snowflake reflete quando o bot viu o objeto, não necessariamente quando ele foi criado.
Hash de sharding
A fórmula de shard padrão do Discord funciona sem alteração:
shardId = (BigInt(guildId) >> 22n) % BigInt(numShards); Bots com menos de 2.500 servidores rodam em um único shard ([0, 1]) e não precisam pensar nisso. Bots maiores obtêm a mesma distribuição razoavelmente uniforme do Discord, porque os 42 bits mais altos são monotônicos no tempo, não aleatórios.
Pegadinhas
- Não compare snowflakes com
==contra um literal numérico. No transporte são strings — compare com uma string ou converta as duas paraBigInt. - O carimbo de 42 bits estoura cerca de 139 anos após a época. As snowflakes do GameVox valem até 2165.
- Não deduza a data de criação de um servidor / canal / cargo a partir da snowflake dele. Objetos nativos anteriores à plataforma de bots ganharam a snowflake na primeira exposição a um bot, não na criação. As mensagens são exceção (preenchidas com
created_at).