detritus-client
Version:
A Typescript NodeJS library to interact with Discord's API, both Rest and Gateway.
319 lines (318 loc) • 11.6 kB
JavaScript
"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;