UNPKG

detritus-client

Version:

A Typescript NodeJS library to interact with Discord's API, both Rest and Gateway.

319 lines (318 loc) 11.6 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.BaseStructure = exports.Structure = exports.convertKey = void 0; const util_1 = require("util"); const collections_1 = require("../collections"); const constants_1 = require("../constants"); const utils_1 = require("../utils"); function convertKey(snake) { if (snake in constants_1.DetritusKeys) { return constants_1.DetritusKeys[snake]; } return utils_1.toCamelCase(snake); } exports.convertKey = convertKey; /** * The most basic Structure class, every structure extends this * @category Structure */ class Structure { _getFromSnake(key) { return this[convertKey(key)]; } _setFromSnake(key, value) { return this[convertKey(key)] = value; } difference(key, value) { if (value !== undefined) { const camelKey = convertKey(key); const old = this[camelKey]; if (old !== undefined && old !== value) { if (!!old !== !!value) { return [true, old]; } else if (old instanceof BaseStructure) { const differences = old.differences(value); if (differences) { return [true, differences]; } } else if (this.hasDifference(key, value)) { if (old instanceof collections_1.BaseCollection) { return [true, old.clone()]; } else if (old instanceof collections_1.BaseSet) { return [true, old.clone()]; } else { return [true, old]; } } } } return [false, null]; } differences(data) { let hasDifferences = false; const obj = {}; for (let key in data) { if (this._keysSkipDifference && this._keysSkipDifference.has(key)) { continue; } const [hasDifference, difference] = this.difference(key, data[key]); if (hasDifference) { obj[convertKey(key)] = difference; hasDifferences = true; } } if (hasDifferences) { return obj; } return null; } differencesBetween(structure) { let hasDifferences = false; const obj = {}; if (this._keys) { for (let key of this._keys) { if (this._keysSkipDifference && this._keysSkipDifference.has(key)) { continue; } const [hasDifference, difference] = this.difference(key, structure._getFromSnake(key)); if (hasDifference) { obj[convertKey(key)] = difference; hasDifferences = true; } } } if (hasDifferences) { return obj; } return null; } hasDifference(key, value) { if (value !== undefined) { const camelKey = convertKey(key); const old = this[camelKey]; // BigInts // -> Discord sends us a string version of this (like permissions) // -> Parse it as BigInt before comparing // Arrays // -> We either receive [string|int] or [{id: string}] or other arrays from discord // -> We either parse them as BaseSet or BaseCollection with the ID as the key (sometimes the key is the array number like embeds) if (old !== undefined && old !== value) { if (!!old !== !!value) { // this makes sure we dont compare null to BaseStructure, null to Date, etc.. return true; } else if (old instanceof BaseStructure) { // assume it's either an object or a BaseStructure if (value instanceof BaseStructure) { return old.hasDifferencesBetween(value); } else { return old.hasDifferences(value); } } else if (old instanceof collections_1.BaseCollection) { // assume we got an array, maybe try looking to see if each object has {id}? if (old.size !== value.length) { // compare sizes first return true; } else if (old.size) { // unknown of how to compare, so just see if the BaseCollection has a size return true; } } else if (old instanceof collections_1.BaseSet) { // assume we got an array of [string] or [int] if (old.size !== value.length) { return true; } else { return !value.every((item) => old.has(item)); } } else if (old instanceof Date) { // assume we either got a Date, Int, or Date String if (value instanceof Date) { // we got a Date object, usually from Structure.differencesBetween(Structure) return old.getTime() === value.getTime(); } else if (typeof (value) === 'number') { // we got a number, unsure of from where but might as well check return old.getTime() !== value; } return old.getTime() !== (new Date(value)).getTime(); } else if (Array.isArray(old)) { // assume we got an array of something if (old.length !== value.length) { // compare sizes first return true; } else if (old.length) { // unknown of how to compare, so just see if the Array has a size return true; } } else if (typeof (old) === 'object') { // assume we got an object too // this would be a rare compare, like role.tags if (typeof (value) === 'object') { // compare keys length to each other const oldKeys = Object.keys(old); const newKeys = Object.keys(value); if (newKeys.length !== oldKeys.length) { return true; } // see if both objects have the same keys if (!newKeys.every((key) => oldKeys.includes(key))) { return true; } // compare each value inside of the object return newKeys.every((key) => old[key] === value[key]); } else { return true; } } else if (typeof (old) === 'bigint') { // assume we got a BigInt in a string return old !== BigInt(value); } else { // good old compare return old !== value; } } } return false; } hasDifferences(data) { if (data) { for (let key in data) { if (this._keysSkipDifference && this._keysSkipDifference.has(key)) { continue; } const hasDifference = this.hasDifference(key, data[key]); if (hasDifference) { return true; } } } return false; } hasDifferencesBetween(structure) { if (this._keys) { for (let key of this._keys) { if (this._keysSkipDifference && this._keysSkipDifference.has(key)) { continue; } const hasDifference = this.hasDifference(key, structure._getFromSnake(key)); if (hasDifference) { return true; } } } return false; } merge(data) { if (!data) { return; } if (this._keys) { if (this._keysMerge) { for (let key of this._keysMerge) { if (this._keys.has(key)) { this.mergeValue(key, data[key]); } } } for (let key in data) { if (this._keysMerge && this._keysMerge.has(key)) { continue; } if (this._keys.has(key)) { let value = data[key]; if (value instanceof BaseStructure) { this._setFromSnake(key, value); continue; } this.mergeValue(key, value); } } } } mergeValue(key, value) { if (value !== undefined) { this._setFromSnake(key, value); } } toJSON() { const obj = {}; if (this._keys) { for (let key of this._keys) { let value = this._getFromSnake(key); if (typeof (value) === 'bigint') { value = String(value); } else if (value instanceof Structure) { value = value.toJSON(); } else if (Array.isArray(value) && value.some((x) => x instanceof Structure)) { value = value.map((x) => { if (x instanceof Structure) { return x.toJSON(); } return x; }); } obj[key] = value; } } return obj; } [util_1.inspect.custom]() { // https://github.com/abalabahaha/eris/blob/master/lib/structures/Base.js#L59 const copy = new ({ [this.constructor.name]: class { } })[this.constructor.name](); if (this._keys) { for (let key of this._keys) { key = convertKey(key); copy[key] = this[key]; } } return copy; } } exports.Structure = Structure; /** * Basic Structure class with an added ShardClient attached to it * @category Structure */ class BaseStructure extends Structure { constructor(client, data, isClone) { super(); this.client = client; this._clone = isClone; if (data) { this.merge(data); } } get isClone() { return !!this._clone; } get shardId() { return this.client.shardId; } clone() { if (this._uncloneable) { throw new Error('Cannot clone this object'); } const ClonedStructure = this.constructor; return new ClonedStructure(this.client, JSON.parse(JSON.stringify(this)), true); } } exports.BaseStructure = BaseStructure;