buddy

node MVC discord bot
Log | Files | Refs | README

Snowflake.js (2900B)


      1 'use strict';
      2 
      3 const Util = require('../util/Util');
      4 
      5 // Discord epoch (2015-01-01T00:00:00.000Z)
      6 const EPOCH = 1420070400000;
      7 let INCREMENT = 0;
      8 
      9 /**
     10  * A container for useful snowflake-related methods.
     11  */
     12 class SnowflakeUtil {
     13   constructor() {
     14     throw new Error(`The ${this.constructor.name} class may not be instantiated.`);
     15   }
     16 
     17   /**
     18    * A Twitter snowflake, except the epoch is 2015-01-01T00:00:00.000Z
     19    * ```
     20    * If we have a snowflake '266241948824764416' we can represent it as binary:
     21    *
     22    * 64                                          22     17     12          0
     23    *  000000111011000111100001101001000101000000  00001  00000  000000000000
     24    *       number of ms since Discord epoch       worker  pid    increment
     25    * ```
     26    * @typedef {string} Snowflake
     27    */
     28 
     29   /**
     30    * Generates a Discord snowflake.
     31    * <info>This hardcodes the worker ID as 1 and the process ID as 0.</info>
     32    * @param {number|Date} [timestamp=Date.now()] Timestamp or date of the snowflake to generate
     33    * @returns {Snowflake} The generated snowflake
     34    */
     35   static generate(timestamp = Date.now()) {
     36     if (timestamp instanceof Date) timestamp = timestamp.getTime();
     37     if (typeof timestamp !== 'number' || isNaN(timestamp)) {
     38       throw new TypeError(
     39         `"timestamp" argument must be a number (received ${isNaN(timestamp) ? 'NaN' : typeof timestamp})`,
     40       );
     41     }
     42     if (INCREMENT >= 4095) INCREMENT = 0;
     43     // eslint-disable-next-line max-len
     44     const BINARY = `${(timestamp - EPOCH).toString(2).padStart(42, '0')}0000100000${(INCREMENT++)
     45       .toString(2)
     46       .padStart(12, '0')}`;
     47     return Util.binaryToID(BINARY);
     48   }
     49 
     50   /**
     51    * A deconstructed snowflake.
     52    * @typedef {Object} DeconstructedSnowflake
     53    * @property {number} timestamp Timestamp the snowflake was created
     54    * @property {Date} date Date the snowflake was created
     55    * @property {number} workerID Worker ID in the snowflake
     56    * @property {number} processID Process ID in the snowflake
     57    * @property {number} increment Increment in the snowflake
     58    * @property {string} binary Binary representation of the snowflake
     59    */
     60 
     61   /**
     62    * Deconstructs a Discord snowflake.
     63    * @param {Snowflake} snowflake Snowflake to deconstruct
     64    * @returns {DeconstructedSnowflake} Deconstructed snowflake
     65    */
     66   static deconstruct(snowflake) {
     67     const BINARY = Util.idToBinary(snowflake)
     68       .toString(2)
     69       .padStart(64, '0');
     70     const res = {
     71       timestamp: parseInt(BINARY.substring(0, 42), 2) + EPOCH,
     72       workerID: parseInt(BINARY.substring(42, 47), 2),
     73       processID: parseInt(BINARY.substring(47, 52), 2),
     74       increment: parseInt(BINARY.substring(52, 64), 2),
     75       binary: BINARY,
     76     };
     77     Object.defineProperty(res, 'date', {
     78       get: function get() {
     79         return new Date(this.timestamp);
     80       },
     81       enumerable: true,
     82     });
     83     return res;
     84   }
     85 }
     86 
     87 module.exports = SnowflakeUtil;