Portail développeurs
Expérimental La plateforme de bots & d’apps est en développement actif. Les serveurs auto-hébergés font tourner les bots, les commandes slash et les composants de message, avec quelques différences par rapport au cloud. Voir ce qui est pris en charge.
← Docs

Snowflakes

GameVox renvoie des ID au format snowflake 64 bits de Discord partout où l’API Discord le ferait. La disposition des bits, l’ordre de tri et le calcul d’extraction de l’horodatage sont identiques — votre code d’analyse existant fonctionne sans modification.

Format

Identique à Discord : horodatage 42 bits + worker 5 bits + processus 5 bits + compteur 12 bits, big-endian dans un entier 64 bits non signé.

┌─ 42 bits ─────────────────┬─ 5 ─┬─ 5 ─┬─ 12 ────────┐
│ ms depuis l’époque GameVo │ wkr │ prc │ increment   │
└───────────────────────────┴─────┴─────┴─────────────┘
 63                       22  21 17 16 12 11           0

Chaque segment correspond à Discord :

  • Horodatage (42 bits) — millisecondes depuis l’époque GameVox, voir ci-dessous.
  • ID de worker (5 bits) — attribué à chaque tâche api au démarrage via un compteur atomique Redis.
  • ID de processus (5 bits) — associé au worker pour former un pool de génération unique.
  • Compteur (12 bits) — séquence par milliseconde et par (worker, processus).

Époque

GameVox utilise la même époque que Discord : 1420070400000 (01/01/2015 00:00:00 UTC). Un analyseur de snowflakes écrit pour Discord fonctionne sur les ID GameVox sans modification — aucune constante à changer.

const SNOWFLAKE_EPOCH = 1420070400000n;

Format de transport

Chaque snowflake sur le canal REST / gateway est sérialisée en chaîne JSON, pas en nombre. JavaScript perd en précision au-delà de Number.MAX_SAFE_INTEGER (253-1), et une snowflake dépasse régulièrement cette valeur. Analysez toujours via BigInt quand vous devez calculer, et stockez-la comme chaîne partout ailleurs.

// Correct
const id = BigInt(message.id);

// Incorrect — perte de précision silencieuse
const id = Number(message.id);

Extraire l’horodatage

JavaScript

function snowflakeToDate(snowflake) {
  const EPOCH = 1420070400000n;          // identique à Discord
  const ms = Number((BigInt(snowflake) >> 22n) + EPOCH);
  return new Date(ms);
}

console.log(snowflakeToDate('182955831900192769'));
// → objet Date — quand cette snowflake a été générée

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);

Ordre de tri

Comme l’horodatage occupe les bits de poids fort, une comparaison lexicographique de deux snowflakes les classe chronologiquement — exactement comme sur Discord. C’est pour cela que la pagination before / after / around de l’endpoint des messages fonctionne sans champ d’horodatage explicite.

// Dans un tableau de messages, du plus ancien au plus récent :
messages.sort((a, b) => a.id < b.id ? -1 : a.id > b.id ? 1 : 0);

Données natives et table d’alias

En interne, les objets natifs de GameVox (serveurs, salons, utilisateurs, messages, etc.) utilisent des UUIDv4. La couche de compatibilité génère une snowflake stable la première fois qu’un objet est exposé à un bot et enregistre la correspondance dans la table snowflake_aliases. Toute référence ultérieure à cet objet — via REST ou gateway — utilise la même snowflake.

Pour les messages, nous renseignons rétroactivement les snowflakes avec le created_at du message comme horodatage source, afin que les messages anciens se trient correctement lorsqu’un bot rejoint un salon pour la première fois. Pour tout le reste, l’alias est généré paresseusement à la première exposition avec NOW() comme horodatage : la snowflake reflète donc le moment où le bot a vu l’objet, pas nécessairement sa création.

Mentions in message text

A mention in a message your bot sends can name its target by snowflake or by GameVox UUID. Users, roles and channels carry both (id and gamevox_id), so use whichever your bot already has: a snowflake from the gateway, or a UUID copied out of GameVox.

<@182955831900192769>                          // a member
<@1f0c9a52-6d1e-4c8b-9a3f-2b7e5d4c1a90>
<@&183005462390591489>                         // a role
<@&7c2e4b19-3a5d-4f60-8e21-9d0b6a3c5f47>
<#183005462390591490>                          // a channel link
<#4a8d2f61-0b7c-4e93-a5d8-1c6e9f2b3a07>

Messages your bot receives always use snowflakes (<@123>, <@&123>, <#123>), with mentions, mention_roles and mention_everyone filled in, as on Discord. Your library parses them unchanged.

allowed_mentions applies to every form, and its users and roles lists accept either kind of ID. A mention it excludes is sent as plain @name text that pings no one.

The server's mention settings apply to your bot as they do to members. If your bot's role may only mention users in a channel, its role mentions there arrive as plain @name text.

Hachage de sharding

La formule de shard standard de Discord fonctionne telle quelle :

shardId = (BigInt(guildId) >> 22n) % BigInt(numShards);

Les bots présents sur moins de 2 500 serveurs tournent sur un seul shard ([0, 1]) et n’ont pas à s’en préoccuper. Les bots plus gros obtiennent la même répartition à peu près uniforme que sur Discord, car les 42 bits de poids fort sont monotones dans le temps, pas aléatoires.

Pièges

  • Ne comparez pas une snowflake avec == à un littéral numérique. Sur le canal, ce sont des chaînes — comparez à une chaîne ou convertissez les deux en BigInt.
  • L’horodatage 42 bits déborde environ 139 ans après l’époque. Les snowflakes de GameVox sont valables jusqu’en 2165.
  • Ne déduisez pas la date de création d’un serveur / salon / rôle à partir de sa snowflake. Les objets natifs antérieurs à la plateforme de bots ont reçu leur snowflake à la première exposition à un bot, pas à leur création. Les messages font exception (renseignés avec created_at).

← Retour à la documentation