mirror of
https://github.com/discordeno/discordeno.git
synced 2026-09-17 08:47:22 +00:00
formatter: Use semicolons (#4686)
I prefer semicolors, they also help avoiding certain pitfalls in JavaScript/TypeScript, such as the following code sample: ```js const xyz = "test" (something.else as string) = "another" ``` This results in a TypeError: "test" is not a function, this is because js thinks we are trying to call the string "test" as a function. To fix this it requires a `;` somewhere before the `(`, such as `;(something ... ` which in my opinion is ugly and less clean overall.
This commit is contained in:
@@ -5,162 +5,162 @@ export class Collection<K, V> extends Map<K, V> {
|
||||
* The maximum amount of items allowed in this collection. To disable cache, set it 0, set to undefined to make it infinite.
|
||||
* @default undefined
|
||||
*/
|
||||
maxSize: number | undefined
|
||||
maxSize: number | undefined;
|
||||
/** Handler to remove items from the collection every so often. */
|
||||
sweeper: (CollectionSweeper<K, V> & { intervalId?: NodeJS.Timeout }) | undefined
|
||||
sweeper: (CollectionSweeper<K, V> & { intervalId?: NodeJS.Timeout }) | undefined;
|
||||
|
||||
constructor(entries?: (ReadonlyArray<readonly [K, V]> | null) | Map<K, V>, options?: CollectionOptions<K, V>) {
|
||||
super(entries ?? [])
|
||||
super(entries ?? []);
|
||||
|
||||
this.maxSize = options?.maxSize
|
||||
this.maxSize = options?.maxSize;
|
||||
|
||||
if (!options?.sweeper) return
|
||||
if (!options?.sweeper) return;
|
||||
|
||||
this.startSweeper(options.sweeper)
|
||||
this.startSweeper(options.sweeper);
|
||||
}
|
||||
|
||||
startSweeper(options: CollectionSweeper<K, V>): NodeJS.Timeout {
|
||||
if (this.sweeper?.intervalId) clearInterval(this.sweeper.intervalId)
|
||||
if (this.sweeper?.intervalId) clearInterval(this.sweeper.intervalId);
|
||||
|
||||
this.sweeper = options
|
||||
this.sweeper = options;
|
||||
this.sweeper.intervalId = setInterval(() => {
|
||||
this.forEach((value, key) => {
|
||||
if (!this.sweeper?.filter(value, key, options.bot)) return
|
||||
if (!this.sweeper?.filter(value, key, options.bot)) return;
|
||||
|
||||
this.delete(key)
|
||||
return key
|
||||
})
|
||||
}, options.interval)
|
||||
this.delete(key);
|
||||
return key;
|
||||
});
|
||||
}, options.interval);
|
||||
|
||||
return this.sweeper.intervalId
|
||||
return this.sweeper.intervalId;
|
||||
}
|
||||
|
||||
stopSweeper(): void {
|
||||
return clearInterval(this.sweeper?.intervalId)
|
||||
return clearInterval(this.sweeper?.intervalId);
|
||||
}
|
||||
|
||||
changeSweeperInterval(newInterval: number): void {
|
||||
if (this.sweeper == null) return
|
||||
if (this.sweeper == null) return;
|
||||
|
||||
this.startSweeper({ filter: this.sweeper.filter, interval: newInterval })
|
||||
this.startSweeper({ filter: this.sweeper.filter, interval: newInterval });
|
||||
}
|
||||
|
||||
changeSweeperFilter(newFilter: (value: V, key: K, bot: PlaceHolderBot) => boolean): void {
|
||||
if (this.sweeper == null) return
|
||||
if (this.sweeper == null) return;
|
||||
|
||||
this.startSweeper({ filter: newFilter, interval: this.sweeper.interval })
|
||||
this.startSweeper({ filter: newFilter, interval: this.sweeper.interval });
|
||||
}
|
||||
|
||||
/** Add an item to the collection. Makes sure not to go above the maxSize. */
|
||||
set(key: K, value: V): this {
|
||||
// When this collection is maxSized make sure we can add first
|
||||
if ((this.maxSize !== undefined || this.maxSize === 0) && this.size >= this.maxSize) {
|
||||
return this
|
||||
return this;
|
||||
}
|
||||
|
||||
return super.set(key, value)
|
||||
return super.set(key, value);
|
||||
}
|
||||
|
||||
/** Add an item to the collection, no matter what the maxSize is. */
|
||||
forceSet(key: K, value: V): this {
|
||||
return super.set(key, value)
|
||||
return super.set(key, value);
|
||||
}
|
||||
|
||||
/** Convert the collection to an array. */
|
||||
array(): V[] {
|
||||
return [...this.values()]
|
||||
return [...this.values()];
|
||||
}
|
||||
|
||||
/** Retrieve the value of the first element in this collection. */
|
||||
first(): V | undefined {
|
||||
return this.values().next().value
|
||||
return this.values().next().value;
|
||||
}
|
||||
|
||||
/** Retrieve the value of the last element in this collection. */
|
||||
last(): V | undefined {
|
||||
return [...this.values()][this.size - 1]
|
||||
return [...this.values()][this.size - 1];
|
||||
}
|
||||
|
||||
/** Retrieve the value of a random element in this collection. */
|
||||
random(): V | undefined {
|
||||
const array = [...this.values()]
|
||||
return array[Math.floor(Math.random() * array.length)]
|
||||
const array = [...this.values()];
|
||||
return array[Math.floor(Math.random() * array.length)];
|
||||
}
|
||||
|
||||
/** Find a specific element in this collection. */
|
||||
find(callback: (value: V, key: K) => boolean): NonNullable<V> | undefined {
|
||||
for (const key of this.keys()) {
|
||||
const value = this.get(key)!
|
||||
if (callback(value, key)) return value
|
||||
const value = this.get(key)!;
|
||||
if (callback(value, key)) return value;
|
||||
}
|
||||
// If nothing matched
|
||||
}
|
||||
|
||||
/** Find all elements in this collection that match the given pattern. */
|
||||
filter(callback: (value: V, key: K) => boolean): Collection<K, V> {
|
||||
const relevant = new Collection<K, V>()
|
||||
const relevant = new Collection<K, V>();
|
||||
this.forEach((value, key) => {
|
||||
if (callback(value, key)) relevant.set(key, value)
|
||||
})
|
||||
if (callback(value, key)) relevant.set(key, value);
|
||||
});
|
||||
|
||||
return relevant
|
||||
return relevant;
|
||||
}
|
||||
|
||||
/** Converts the collection into an array by running a callback on all items in the collection. */
|
||||
map<T>(callback: (value: V, key: K) => T): T[] {
|
||||
const results = []
|
||||
const results = [];
|
||||
for (const key of this.keys()) {
|
||||
const value = this.get(key)!
|
||||
results.push(callback(value, key))
|
||||
const value = this.get(key)!;
|
||||
results.push(callback(value, key));
|
||||
}
|
||||
return results
|
||||
return results;
|
||||
}
|
||||
|
||||
/** Check if one of the items in the collection matches the pattern. */
|
||||
some(callback: (value: V, key: K) => boolean): boolean {
|
||||
for (const key of this.keys()) {
|
||||
const value = this.get(key)!
|
||||
if (callback(value, key)) return true
|
||||
const value = this.get(key)!;
|
||||
if (callback(value, key)) return true;
|
||||
}
|
||||
|
||||
return false
|
||||
return false;
|
||||
}
|
||||
|
||||
/** Check if all of the items in the collection matches the pattern. */
|
||||
every(callback: (value: V, key: K) => boolean): boolean {
|
||||
for (const key of this.keys()) {
|
||||
const value = this.get(key)!
|
||||
if (!callback(value, key)) return false
|
||||
const value = this.get(key)!;
|
||||
if (!callback(value, key)) return false;
|
||||
}
|
||||
|
||||
return true
|
||||
return true;
|
||||
}
|
||||
|
||||
/** Runs a callback on all items in the collection, merging them into a single value. */
|
||||
reduce<T>(callback: (accumulator: T, value: V, key: K) => T, initialValue?: T): T {
|
||||
let accumulator: T = initialValue!
|
||||
let accumulator: T = initialValue!;
|
||||
|
||||
for (const key of this.keys()) {
|
||||
const value = this.get(key)!
|
||||
accumulator = callback(accumulator, value, key)
|
||||
const value = this.get(key)!;
|
||||
accumulator = callback(accumulator, value, key);
|
||||
}
|
||||
|
||||
return accumulator
|
||||
return accumulator;
|
||||
}
|
||||
}
|
||||
|
||||
export interface CollectionOptions<K, V> {
|
||||
/** Handler to clean out the items in the collection every so often. */
|
||||
sweeper?: CollectionSweeper<K, V>
|
||||
sweeper?: CollectionSweeper<K, V>;
|
||||
/** The maximum number of items allowed in the collection. */
|
||||
maxSize?: number
|
||||
maxSize?: number;
|
||||
}
|
||||
|
||||
export interface CollectionSweeper<K, V> {
|
||||
/** The filter to determine whether an element should be deleted or not */
|
||||
filter: (value: V, key: K, ...args: any[]) => boolean
|
||||
filter: (value: V, key: K, ...args: any[]) => boolean;
|
||||
/** The interval in which the sweeper should run */
|
||||
interval: number
|
||||
interval: number;
|
||||
/** The bot object itself */
|
||||
bot?: PlaceHolderBot
|
||||
bot?: PlaceHolderBot;
|
||||
}
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
* @param data
|
||||
*/
|
||||
export function encode(data: Uint8Array | ArrayBuffer | string): string {
|
||||
const uint8 = typeof data === 'string' ? new TextEncoder().encode(data) : data instanceof Uint8Array ? data : new Uint8Array(data)
|
||||
return _encode(uint8, base64abc, false)
|
||||
const uint8 = typeof data === 'string' ? new TextEncoder().encode(data) : data instanceof Uint8Array ? data : new Uint8Array(data);
|
||||
return _encode(uint8, base64abc, false);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -14,35 +14,35 @@ export function encode(data: Uint8Array | ArrayBuffer | string): string {
|
||||
* @returns The base64url encoded string
|
||||
*/
|
||||
export function encodeBase64Url(data: Uint8Array | ArrayBuffer | string): string {
|
||||
const uint8 = typeof data === 'string' ? new TextEncoder().encode(data) : data instanceof Uint8Array ? data : new Uint8Array(data)
|
||||
return _encode(uint8, base64urlAbc, true)
|
||||
const uint8 = typeof data === 'string' ? new TextEncoder().encode(data) : data instanceof Uint8Array ? data : new Uint8Array(data);
|
||||
return _encode(uint8, base64urlAbc, true);
|
||||
}
|
||||
|
||||
/** @private */
|
||||
function _encode(data: Uint8Array, alpha: string[], skipPadding: boolean): string {
|
||||
let result = ''
|
||||
let i
|
||||
const l = data.length
|
||||
let result = '';
|
||||
let i;
|
||||
const l = data.length;
|
||||
for (i = 2; i < l; i += 3) {
|
||||
result += alpha[data[i - 2] >> 2]
|
||||
result += alpha[((data[i - 2] & 0x03) << 4) | (data[i - 1] >> 4)]
|
||||
result += alpha[((data[i - 1] & 0x0f) << 2) | (data[i] >> 6)]
|
||||
result += alpha[data[i] & 0x3f]
|
||||
result += alpha[data[i - 2] >> 2];
|
||||
result += alpha[((data[i - 2] & 0x03) << 4) | (data[i - 1] >> 4)];
|
||||
result += alpha[((data[i - 1] & 0x0f) << 2) | (data[i] >> 6)];
|
||||
result += alpha[data[i] & 0x3f];
|
||||
}
|
||||
if (i === l + 1) {
|
||||
// 1 octet yet to write
|
||||
result += alpha[data[i - 2] >> 2]
|
||||
result += alpha[(data[i - 2] & 0x03) << 4]
|
||||
if (!skipPadding) result += '=='
|
||||
result += alpha[data[i - 2] >> 2];
|
||||
result += alpha[(data[i - 2] & 0x03) << 4];
|
||||
if (!skipPadding) result += '==';
|
||||
}
|
||||
if (i === l) {
|
||||
// 2 octets yet to write
|
||||
result += alpha[data[i - 2] >> 2]
|
||||
result += alpha[((data[i - 2] & 0x03) << 4) | (data[i - 1] >> 4)]
|
||||
result += alpha[(data[i - 1] & 0x0f) << 2]
|
||||
if (!skipPadding) result += '='
|
||||
result += alpha[data[i - 2] >> 2];
|
||||
result += alpha[((data[i - 2] & 0x03) << 4) | (data[i - 1] >> 4)];
|
||||
result += alpha[(data[i - 1] & 0x0f) << 2];
|
||||
if (!skipPadding) result += '=';
|
||||
}
|
||||
return result
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -52,27 +52,27 @@ function _encode(data: Uint8Array, alpha: string[], skipPadding: boolean): strin
|
||||
*/
|
||||
export function decode(data: string): Uint8Array {
|
||||
if (data.length % 4 !== 0) {
|
||||
throw new Error('Unable to parse base64 string.')
|
||||
throw new Error('Unable to parse base64 string.');
|
||||
}
|
||||
const index = data.indexOf('=')
|
||||
const index = data.indexOf('=');
|
||||
if (index !== -1 && index < data.length - 2) {
|
||||
throw new Error('Unable to parse base64 string.')
|
||||
throw new Error('Unable to parse base64 string.');
|
||||
}
|
||||
const missingOctets = data.endsWith('==') ? 2 : data.endsWith('=') ? 1 : 0
|
||||
const n = data.length
|
||||
const result = new Uint8Array(3 * (n / 4))
|
||||
let buffer
|
||||
const missingOctets = data.endsWith('==') ? 2 : data.endsWith('=') ? 1 : 0;
|
||||
const n = data.length;
|
||||
const result = new Uint8Array(3 * (n / 4));
|
||||
let buffer;
|
||||
for (let i = 0, j = 0; i < n; i += 4, j += 3) {
|
||||
buffer =
|
||||
(getBase64Code(data.charCodeAt(i)) << 18) |
|
||||
(getBase64Code(data.charCodeAt(i + 1)) << 12) |
|
||||
(getBase64Code(data.charCodeAt(i + 2)) << 6) |
|
||||
getBase64Code(data.charCodeAt(i + 3))
|
||||
result[j] = buffer >> 16
|
||||
result[j + 1] = (buffer >> 8) & 0xff
|
||||
result[j + 2] = buffer & 0xff
|
||||
getBase64Code(data.charCodeAt(i + 3));
|
||||
result[j] = buffer >> 16;
|
||||
result[j + 1] = (buffer >> 8) & 0xff;
|
||||
result[j + 2] = buffer & 0xff;
|
||||
}
|
||||
return result.subarray(0, result.length - missingOctets)
|
||||
return result.subarray(0, result.length - missingOctets);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -81,13 +81,13 @@ export function decode(data: string): Uint8Array {
|
||||
*/
|
||||
function getBase64Code(charCode: number): number {
|
||||
if (charCode >= base64codes.length) {
|
||||
throw new Error('Unable to parse base64 string.')
|
||||
throw new Error('Unable to parse base64 string.');
|
||||
}
|
||||
const code = base64codes[charCode]
|
||||
const code = base64codes[charCode];
|
||||
if (code === 255) {
|
||||
throw new Error('Unable to parse base64 string.')
|
||||
throw new Error('Unable to parse base64 string.');
|
||||
}
|
||||
return code
|
||||
return code;
|
||||
}
|
||||
|
||||
// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
|
||||
@@ -156,7 +156,7 @@ const base64abc = [
|
||||
'9',
|
||||
'+',
|
||||
'/',
|
||||
]
|
||||
];
|
||||
|
||||
const base64urlAbc = [
|
||||
'A',
|
||||
@@ -223,7 +223,7 @@ const base64urlAbc = [
|
||||
'9',
|
||||
'-',
|
||||
'_',
|
||||
]
|
||||
];
|
||||
|
||||
// CREDIT: https://gist.github.com/enepomnyaschih/72c423f727d395eeaa09697058238727
|
||||
const base64codes = [
|
||||
@@ -231,4 +231,4 @@ const base64codes = [
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 62, 255, 255, 255, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 255, 255, 255,
|
||||
0, 255, 255, 255, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 255, 255, 255, 255, 255, 255, 26,
|
||||
27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51,
|
||||
]
|
||||
];
|
||||
|
||||
@@ -1,118 +1,118 @@
|
||||
import logger from './logger.js'
|
||||
import { delay } from './utils.js'
|
||||
import logger from './logger.js';
|
||||
import { delay } from './utils.js';
|
||||
|
||||
export class LeakyBucket implements LeakyBucketOptions {
|
||||
max: number
|
||||
refillInterval: number
|
||||
refillAmount: number
|
||||
max: number;
|
||||
refillInterval: number;
|
||||
refillAmount: number;
|
||||
|
||||
/** The amount of requests that have been used up already. */
|
||||
used: number = 0
|
||||
used: number = 0;
|
||||
/** The queue of requests to acquire an available request. Mapped by <shardId, resolve()> */
|
||||
queue: Array<(value: void | PromiseLike<void>) => void> = []
|
||||
queue: Array<(value: void | PromiseLike<void>) => void> = [];
|
||||
/** Whether or not the queue is already processing. */
|
||||
processing: boolean = false
|
||||
processing: boolean = false;
|
||||
/** The timeout id for the timer to reduce the used amount by the refill amount. */
|
||||
timeoutId?: NodeJS.Timeout
|
||||
timeoutId?: NodeJS.Timeout;
|
||||
/** The timestamp in milliseconds when the next refill is scheduled. */
|
||||
refillsAt?: number
|
||||
refillsAt?: number;
|
||||
/** Logger used in the leaky bucket */
|
||||
logger: Pick<typeof logger, 'debug' | 'info' | 'warn' | 'error' | 'fatal'>
|
||||
logger: Pick<typeof logger, 'debug' | 'info' | 'warn' | 'error' | 'fatal'>;
|
||||
|
||||
constructor(options?: LeakyBucketOptions) {
|
||||
this.max = options?.max ?? 1
|
||||
this.refillAmount = options?.refillAmount ? (options.refillAmount > this.max ? this.max : options.refillAmount) : 1
|
||||
this.refillInterval = options?.refillInterval ?? 5000
|
||||
this.logger = options?.logger ?? logger
|
||||
this.max = options?.max ?? 1;
|
||||
this.refillAmount = options?.refillAmount ? (options.refillAmount > this.max ? this.max : options.refillAmount) : 1;
|
||||
this.refillInterval = options?.refillInterval ?? 5000;
|
||||
this.logger = options?.logger ?? logger;
|
||||
}
|
||||
|
||||
/** The amount of requests that still remain. */
|
||||
get remaining(): number {
|
||||
return this.max < this.used ? 0 : this.max - this.used
|
||||
return this.max < this.used ? 0 : this.max - this.used;
|
||||
}
|
||||
|
||||
/** Refills the bucket as needed. */
|
||||
refillBucket(): void {
|
||||
this.logger.debug(`[LeakyBucket] Timeout for leaky bucket requests executed. Refilling bucket.`)
|
||||
this.logger.debug(`[LeakyBucket] Timeout for leaky bucket requests executed. Refilling bucket.`);
|
||||
// Lower the used amount by the refill amount
|
||||
this.used = this.refillAmount > this.used ? 0 : this.used - this.refillAmount
|
||||
this.used = this.refillAmount > this.used ? 0 : this.used - this.refillAmount;
|
||||
// Reset the refillsAt timestamp since it just got refilled
|
||||
this.refillsAt = undefined
|
||||
this.refillsAt = undefined;
|
||||
// Reset the timeoutId
|
||||
clearTimeout(this.timeoutId)
|
||||
this.timeoutId = undefined
|
||||
clearTimeout(this.timeoutId);
|
||||
this.timeoutId = undefined;
|
||||
|
||||
if (this.used > 0) {
|
||||
this.timeoutId = setTimeout(() => {
|
||||
this.refillBucket()
|
||||
}, this.refillInterval)
|
||||
this.refillsAt = Date.now() + this.refillInterval
|
||||
this.refillBucket();
|
||||
}, this.refillInterval);
|
||||
this.refillsAt = Date.now() + this.refillInterval;
|
||||
}
|
||||
}
|
||||
|
||||
/** Begin processing the queue. */
|
||||
async processQueue(): Promise<void> {
|
||||
this.logger.debug('[LeakyBucket] Processing queue')
|
||||
this.logger.debug('[LeakyBucket] Processing queue');
|
||||
|
||||
// There is already a queue that is processing
|
||||
if (this.processing) return this.logger.debug('[LeakyBucket] Queue is already processing.')
|
||||
if (this.processing) return this.logger.debug('[LeakyBucket] Queue is already processing.');
|
||||
|
||||
this.processing = true
|
||||
this.processing = true;
|
||||
|
||||
// Begin going through the queue.
|
||||
while (this.queue.length) {
|
||||
if (this.remaining) {
|
||||
this.logger.debug(`[LeakyBucket] Processing queue. Remaining: ${this.remaining} Length: ${this.queue.length}`)
|
||||
this.logger.debug(`[LeakyBucket] Processing queue. Remaining: ${this.remaining} Length: ${this.queue.length}`);
|
||||
// Resolves the promise allowing the paused execution of this request to resolve and continue.
|
||||
this.queue.shift()?.()
|
||||
this.queue.shift()?.();
|
||||
// A request can be made
|
||||
this.used++
|
||||
this.used++;
|
||||
|
||||
// Create a new timeout for this request if none exists.
|
||||
if (!this.timeoutId) {
|
||||
this.logger.debug(`[LeakyBucket] Creating new timeout for leaky bucket requests.`)
|
||||
this.logger.debug(`[LeakyBucket] Creating new timeout for leaky bucket requests.`);
|
||||
|
||||
this.timeoutId = setTimeout(() => {
|
||||
this.refillBucket()
|
||||
}, this.refillInterval)
|
||||
this.refillBucket();
|
||||
}, this.refillInterval);
|
||||
// Set the time for when this refill will occur.
|
||||
this.refillsAt = Date.now() + this.refillInterval
|
||||
this.refillsAt = Date.now() + this.refillInterval;
|
||||
}
|
||||
}
|
||||
|
||||
// Check if a refill is scheduled, since we have used up all available requests
|
||||
else if (this.refillsAt) {
|
||||
const now = Date.now()
|
||||
const now = Date.now();
|
||||
// If there is time left until next refill, just delay execution.
|
||||
if (this.refillsAt > now) {
|
||||
this.logger.debug(`[LeakyBucket] Delaying execution of leaky bucket requests for ${this.refillsAt - now}ms`)
|
||||
await delay(this.refillsAt - now)
|
||||
this.logger.debug(`[LeakyBucket] Resuming execution`)
|
||||
this.logger.debug(`[LeakyBucket] Delaying execution of leaky bucket requests for ${this.refillsAt - now}ms`);
|
||||
await delay(this.refillsAt - now);
|
||||
this.logger.debug(`[LeakyBucket] Resuming execution`);
|
||||
}
|
||||
|
||||
// If the refillsAt has passed but the timeout didn't yet execute delay the execution
|
||||
else {
|
||||
this.logger.debug(`[LeakyBucket] Delaying execution of leaky bucket requests for 1000ms`)
|
||||
await delay(1000)
|
||||
this.logger.debug(`[LeakyBucket] Delaying execution of leaky bucket requests for 1000ms`);
|
||||
await delay(1000);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Loop has ended mark false so it can restart later when needed
|
||||
this.processing = false
|
||||
this.processing = false;
|
||||
}
|
||||
|
||||
/** Pauses the execution until the request is available to be made. */
|
||||
async acquire(highPriority?: boolean): Promise<void> {
|
||||
return await new Promise((resolve) => {
|
||||
// High priority requests get added to the start of the queue
|
||||
if (highPriority) this.queue.unshift(resolve)
|
||||
if (highPriority) this.queue.unshift(resolve);
|
||||
// All other requests get pushed to the end.
|
||||
else this.queue.push(resolve)
|
||||
else this.queue.push(resolve);
|
||||
|
||||
// Each request should trigger the queue to be processed.
|
||||
void this.processQueue()
|
||||
})
|
||||
void this.processQueue();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -121,20 +121,20 @@ export interface LeakyBucketOptions {
|
||||
* Max requests allowed at once.
|
||||
* @default 1
|
||||
*/
|
||||
max?: number
|
||||
max?: number;
|
||||
/**
|
||||
* Interval in milliseconds between refills.
|
||||
* @default 5000
|
||||
*/
|
||||
refillInterval?: number
|
||||
refillInterval?: number;
|
||||
/**
|
||||
* Amount of requests to refill at each interval.
|
||||
* @default 1
|
||||
*/
|
||||
refillAmount?: number
|
||||
refillAmount?: number;
|
||||
/**
|
||||
* The logger that the leaky bucket will use
|
||||
* @default logger // The logger exported by `@discordeno/utils`
|
||||
*/
|
||||
logger?: Pick<typeof logger, 'debug' | 'info' | 'warn' | 'error' | 'fatal'>
|
||||
logger?: Pick<typeof logger, 'debug' | 'info' | 'warn' | 'error' | 'fatal'>;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { EmbedsBuilder } from './builders/embeds.js'
|
||||
import { EmbedsBuilder } from './builders/embeds.js';
|
||||
|
||||
export * from './builders/embeds.js'
|
||||
export * from './builders/embeds.js';
|
||||
|
||||
export const createEmbeds = (): EmbedsBuilder => new EmbedsBuilder()
|
||||
export const createEmbeds = (): EmbedsBuilder => new EmbedsBuilder();
|
||||
|
||||
@@ -6,7 +6,7 @@ import type {
|
||||
DiscordEmbedImage,
|
||||
DiscordEmbedThumbnail,
|
||||
DiscordEmbedVideo,
|
||||
} from '@discordeno/types'
|
||||
} from '@discordeno/types';
|
||||
|
||||
/**
|
||||
* A builder to help create Discord embeds.
|
||||
@@ -19,7 +19,7 @@ import type {
|
||||
* .setTitle('My Second Embed')
|
||||
*/
|
||||
export class EmbedsBuilder extends Array<DiscordEmbed> {
|
||||
#currentEmbedIndex: number = 0
|
||||
#currentEmbedIndex: number = 0;
|
||||
|
||||
/**
|
||||
* Adds a new field to the current embed.
|
||||
@@ -31,16 +31,16 @@ export class EmbedsBuilder extends Array<DiscordEmbed> {
|
||||
*/
|
||||
addField(name: string, value: string, inline?: boolean): this {
|
||||
if (this.#currentEmbed.fields === undefined) {
|
||||
this.#currentEmbed.fields = []
|
||||
this.#currentEmbed.fields = [];
|
||||
}
|
||||
|
||||
this.#currentEmbed.fields.push({
|
||||
name,
|
||||
value,
|
||||
inline,
|
||||
})
|
||||
});
|
||||
|
||||
return this
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -51,12 +51,12 @@ export class EmbedsBuilder extends Array<DiscordEmbed> {
|
||||
*/
|
||||
addFields(fields: DiscordEmbedField[]): this {
|
||||
if (this.#currentEmbed.fields === undefined) {
|
||||
this.#currentEmbed.fields = []
|
||||
this.#currentEmbed.fields = [];
|
||||
}
|
||||
|
||||
this.#currentEmbed.fields.push(...fields)
|
||||
this.#currentEmbed.fields.push(...fields);
|
||||
|
||||
return this
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -66,13 +66,13 @@ export class EmbedsBuilder extends Array<DiscordEmbed> {
|
||||
*/
|
||||
newEmbed(): this {
|
||||
if (this.length >= 10) {
|
||||
throw new Error('Maximum embed count exceeded. You can not have more than 10 embeds.')
|
||||
throw new Error('Maximum embed count exceeded. You can not have more than 10 embeds.');
|
||||
}
|
||||
|
||||
this.push({})
|
||||
this.setCurrentEmbed()
|
||||
this.push({});
|
||||
this.setCurrentEmbed();
|
||||
|
||||
return this
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -87,9 +87,9 @@ export class EmbedsBuilder extends Array<DiscordEmbed> {
|
||||
...this.#currentEmbed.author,
|
||||
...options,
|
||||
name,
|
||||
}
|
||||
};
|
||||
|
||||
return this
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -101,16 +101,16 @@ export class EmbedsBuilder extends Array<DiscordEmbed> {
|
||||
setColor(color: number | string): this {
|
||||
if (typeof color === 'string') {
|
||||
if (color.toLowerCase() === 'random') {
|
||||
return this.setRandomColor()
|
||||
return this.setRandomColor();
|
||||
}
|
||||
|
||||
const convertedValue = parseInt(color.replace('#', ''), 16)
|
||||
color = Number.isNaN(convertedValue) ? 0 : convertedValue
|
||||
const convertedValue = parseInt(color.replace('#', ''), 16);
|
||||
color = Number.isNaN(convertedValue) ? 0 : convertedValue;
|
||||
}
|
||||
|
||||
this.#currentEmbed.color = color
|
||||
this.#currentEmbed.color = color;
|
||||
|
||||
return this
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -123,18 +123,18 @@ export class EmbedsBuilder extends Array<DiscordEmbed> {
|
||||
*/
|
||||
setCurrentEmbed(index?: number): this {
|
||||
if (index === undefined) {
|
||||
this.#currentEmbedIndex = this.length - 1
|
||||
this.#currentEmbedIndex = this.length - 1;
|
||||
|
||||
return this
|
||||
return this;
|
||||
}
|
||||
|
||||
if (index >= this.length || index < 0) {
|
||||
throw new Error('Can not set the current embed to a index out of bounds.')
|
||||
throw new Error('Can not set the current embed to a index out of bounds.');
|
||||
}
|
||||
|
||||
this.#currentEmbedIndex = index
|
||||
this.#currentEmbedIndex = index;
|
||||
|
||||
return this
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -144,9 +144,9 @@ export class EmbedsBuilder extends Array<DiscordEmbed> {
|
||||
* @returns {EmbedsBuilder}
|
||||
*/
|
||||
setDescription(description: string): this {
|
||||
this.#currentEmbed.description = description
|
||||
this.#currentEmbed.description = description;
|
||||
|
||||
return this
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -156,9 +156,9 @@ export class EmbedsBuilder extends Array<DiscordEmbed> {
|
||||
* @returns {EmbedsBuilder}
|
||||
*/
|
||||
setFields(fields: DiscordEmbedField[]): this {
|
||||
this.#currentEmbed.fields = fields
|
||||
this.#currentEmbed.fields = fields;
|
||||
|
||||
return this
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -173,9 +173,9 @@ export class EmbedsBuilder extends Array<DiscordEmbed> {
|
||||
...this.#currentEmbed.footer,
|
||||
...options,
|
||||
text,
|
||||
}
|
||||
};
|
||||
|
||||
return this
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -190,9 +190,9 @@ export class EmbedsBuilder extends Array<DiscordEmbed> {
|
||||
...this.#currentEmbed.image,
|
||||
...options,
|
||||
url,
|
||||
}
|
||||
};
|
||||
|
||||
return this
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -206,9 +206,9 @@ export class EmbedsBuilder extends Array<DiscordEmbed> {
|
||||
this.#currentEmbed.provider = {
|
||||
name,
|
||||
url,
|
||||
}
|
||||
};
|
||||
|
||||
return this
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -217,7 +217,7 @@ export class EmbedsBuilder extends Array<DiscordEmbed> {
|
||||
* @returns {EmbedsBuilder}
|
||||
*/
|
||||
setRandomColor(): this {
|
||||
return this.setColor(Math.floor(Math.random() * (0xffffff + 1)))
|
||||
return this.setColor(Math.floor(Math.random() * (0xffffff + 1)));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -228,13 +228,13 @@ export class EmbedsBuilder extends Array<DiscordEmbed> {
|
||||
* @returns {EmbedsBuilder}
|
||||
*/
|
||||
setTitle(title: string, url?: string): this {
|
||||
this.#currentEmbed.title = title
|
||||
this.#currentEmbed.title = title;
|
||||
|
||||
if (url) {
|
||||
this.setUrl(url)
|
||||
this.setUrl(url);
|
||||
}
|
||||
|
||||
return this
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -244,9 +244,9 @@ export class EmbedsBuilder extends Array<DiscordEmbed> {
|
||||
* @returns {EmbedsBuilder}
|
||||
*/
|
||||
setTimestamp(timestamp?: string | number | Date): this {
|
||||
this.#currentEmbed.timestamp = new Date(timestamp ?? Date.now()).toISOString()
|
||||
this.#currentEmbed.timestamp = new Date(timestamp ?? Date.now()).toISOString();
|
||||
|
||||
return this
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -261,9 +261,9 @@ export class EmbedsBuilder extends Array<DiscordEmbed> {
|
||||
...this.#currentEmbed.thumbnail,
|
||||
...options,
|
||||
url,
|
||||
}
|
||||
};
|
||||
|
||||
return this
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -273,9 +273,9 @@ export class EmbedsBuilder extends Array<DiscordEmbed> {
|
||||
* @returns {EmbedsBuilder}
|
||||
*/
|
||||
setUrl(url: string): this {
|
||||
this.#currentEmbed.url = url
|
||||
this.#currentEmbed.url = url;
|
||||
|
||||
return this
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -290,9 +290,9 @@ export class EmbedsBuilder extends Array<DiscordEmbed> {
|
||||
...this.#currentEmbed.video,
|
||||
...options,
|
||||
url,
|
||||
}
|
||||
};
|
||||
|
||||
return this
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -301,81 +301,81 @@ export class EmbedsBuilder extends Array<DiscordEmbed> {
|
||||
* @returns {EmbedsBuilder}
|
||||
*/
|
||||
validate(): this {
|
||||
let totalCharacters = 0
|
||||
let totalCharacters = 0;
|
||||
|
||||
if (this.length > 10) {
|
||||
throw new Error('You can not have more than 10 embeds on a single message.')
|
||||
throw new Error('You can not have more than 10 embeds on a single message.');
|
||||
}
|
||||
|
||||
this.forEach(({ author, description, fields, footer, title }, index) => {
|
||||
if (title) {
|
||||
const trimmedTitle = title.trim()
|
||||
const trimmedTitle = title.trim();
|
||||
|
||||
if (trimmedTitle.length > 256) {
|
||||
throw new Error(`Title of embed ${index} can not be longer than 256 characters.`)
|
||||
throw new Error(`Title of embed ${index} can not be longer than 256 characters.`);
|
||||
}
|
||||
|
||||
totalCharacters += trimmedTitle.length
|
||||
totalCharacters += trimmedTitle.length;
|
||||
}
|
||||
|
||||
if (description) {
|
||||
const trimmedDescription = description.trim()
|
||||
const trimmedDescription = description.trim();
|
||||
|
||||
if (trimmedDescription.length > 4096) {
|
||||
throw new Error(`Description of embed ${index} can not be longer than 4096 characters.`)
|
||||
throw new Error(`Description of embed ${index} can not be longer than 4096 characters.`);
|
||||
}
|
||||
|
||||
totalCharacters += trimmedDescription.length
|
||||
totalCharacters += trimmedDescription.length;
|
||||
}
|
||||
|
||||
if (fields) {
|
||||
if (fields.length > 25) {
|
||||
throw new Error(`embed ${index} can not have more than 25 fields.`)
|
||||
throw new Error(`embed ${index} can not have more than 25 fields.`);
|
||||
}
|
||||
|
||||
fields.forEach(({ name, value }, fieldIndex) => {
|
||||
const trimmedName = name.trim()
|
||||
const trimmedValue = value.trim()
|
||||
const trimmedName = name.trim();
|
||||
const trimmedValue = value.trim();
|
||||
|
||||
if (trimmedName.length > 256) {
|
||||
throw new Error(`Name of field ${fieldIndex} on embed ${index} can not be longer than 256 characters.`)
|
||||
throw new Error(`Name of field ${fieldIndex} on embed ${index} can not be longer than 256 characters.`);
|
||||
}
|
||||
|
||||
if (trimmedValue.length > 4096) {
|
||||
throw new Error(`Value of field ${fieldIndex} on embed ${index} can not be longer than 1024 characters.`)
|
||||
throw new Error(`Value of field ${fieldIndex} on embed ${index} can not be longer than 1024 characters.`);
|
||||
}
|
||||
|
||||
totalCharacters += trimmedName.length
|
||||
totalCharacters += trimmedValue.length
|
||||
})
|
||||
totalCharacters += trimmedName.length;
|
||||
totalCharacters += trimmedValue.length;
|
||||
});
|
||||
}
|
||||
|
||||
if (footer) {
|
||||
const trimmedFooterText = footer.text.trim()
|
||||
const trimmedFooterText = footer.text.trim();
|
||||
|
||||
if (trimmedFooterText.length > 2048) {
|
||||
throw new Error(`Footer text of embed ${index} can not be longer than 2048 characters.`)
|
||||
throw new Error(`Footer text of embed ${index} can not be longer than 2048 characters.`);
|
||||
}
|
||||
|
||||
totalCharacters += trimmedFooterText.length
|
||||
totalCharacters += trimmedFooterText.length;
|
||||
}
|
||||
|
||||
if (author) {
|
||||
const trimmedAuthorName = author.name.trim()
|
||||
const trimmedAuthorName = author.name.trim();
|
||||
|
||||
if (trimmedAuthorName.length > 256) {
|
||||
throw new Error(`Author name of embed ${index} can not be longer than 256 characters.`)
|
||||
throw new Error(`Author name of embed ${index} can not be longer than 256 characters.`);
|
||||
}
|
||||
|
||||
totalCharacters += trimmedAuthorName.length
|
||||
totalCharacters += trimmedAuthorName.length;
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
if (totalCharacters > 6000) {
|
||||
throw new Error('Total character length of all embeds can not exceed 6000 characters.')
|
||||
throw new Error('Total character length of all embeds can not exceed 6000 characters.');
|
||||
}
|
||||
|
||||
return this
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -386,10 +386,10 @@ export class EmbedsBuilder extends Array<DiscordEmbed> {
|
||||
*/
|
||||
get #currentEmbed(): DiscordEmbed {
|
||||
if (this.length === 0) {
|
||||
this.newEmbed()
|
||||
this.setCurrentEmbed()
|
||||
this.newEmbed();
|
||||
this.setCurrentEmbed();
|
||||
}
|
||||
|
||||
return this[this.#currentEmbedIndex]
|
||||
return this[this.#currentEmbedIndex];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,65 +1,65 @@
|
||||
import type { Camelize, Snakelize } from '@discordeno/types'
|
||||
import type { Camelize, Snakelize } from '@discordeno/types';
|
||||
|
||||
export function camelize<T>(object: T): Camelize<T> {
|
||||
if (Array.isArray(object)) {
|
||||
return object.map((element) => camelize(element)) as Camelize<T>
|
||||
return object.map((element) => camelize(element)) as Camelize<T>;
|
||||
}
|
||||
|
||||
if (typeof object === 'object' && object !== null) {
|
||||
const obj = {} as Camelize<T>
|
||||
;(Object.keys(object) as Array<keyof T>).forEach((key) => {
|
||||
const obj = {} as Camelize<T>;
|
||||
(Object.keys(object) as Array<keyof T>).forEach((key) => {
|
||||
// @ts-expect-error js hack
|
||||
;(obj[snakeToCamelCase(key)] as Camelize<(T & object)[keyof T]>) = camelize(object[key])
|
||||
})
|
||||
return obj
|
||||
(obj[snakeToCamelCase(key)] as Camelize<(T & object)[keyof T]>) = camelize(object[key]);
|
||||
});
|
||||
return obj;
|
||||
}
|
||||
return object as Camelize<T>
|
||||
return object as Camelize<T>;
|
||||
}
|
||||
|
||||
export function snakelize<T>(object: T): Snakelize<T> {
|
||||
if (Array.isArray(object)) {
|
||||
return object.map((element) => snakelize(element)) as Snakelize<T>
|
||||
return object.map((element) => snakelize(element)) as Snakelize<T>;
|
||||
}
|
||||
|
||||
if (typeof object === 'object' && object !== null) {
|
||||
const obj = {} as Snakelize<T>
|
||||
;(Object.keys(object) as Array<keyof T>).forEach((key) => {
|
||||
const obj = {} as Snakelize<T>;
|
||||
(Object.keys(object) as Array<keyof T>).forEach((key) => {
|
||||
// @ts-expect-error js hack
|
||||
;(obj[camelToSnakeCase(key)] as Snakelize<(T & object)[keyof T]>) = snakelize(object[key])
|
||||
})
|
||||
return obj
|
||||
(obj[camelToSnakeCase(key)] as Snakelize<(T & object)[keyof T]>) = snakelize(object[key]);
|
||||
});
|
||||
return obj;
|
||||
}
|
||||
return object as Snakelize<T>
|
||||
return object as Snakelize<T>;
|
||||
}
|
||||
|
||||
export function snakeToCamelCase(str: string): string {
|
||||
if (!str.includes('_')) return str
|
||||
if (!str.includes('_')) return str;
|
||||
|
||||
let result = ''
|
||||
let result = '';
|
||||
for (let i = 0, len = str.length; i < len; ++i) {
|
||||
if (str[i] === '_') {
|
||||
result += str[++i].toUpperCase()
|
||||
result += str[++i].toUpperCase();
|
||||
|
||||
continue
|
||||
continue;
|
||||
}
|
||||
|
||||
result += str[i]
|
||||
result += str[i];
|
||||
}
|
||||
|
||||
return result
|
||||
return result;
|
||||
}
|
||||
|
||||
export function camelToSnakeCase(str: string): string {
|
||||
let result = ''
|
||||
let result = '';
|
||||
for (let i = 0, len = str.length; i < len; ++i) {
|
||||
if (str[i] >= 'A' && str[i] <= 'Z') {
|
||||
result += `_${str[i].toLowerCase()}`
|
||||
result += `_${str[i].toLowerCase()}`;
|
||||
|
||||
continue
|
||||
continue;
|
||||
}
|
||||
|
||||
result += str[i]
|
||||
result += str[i];
|
||||
}
|
||||
|
||||
return result
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -4,31 +4,31 @@
|
||||
// https://deno.land/std@0.153.0/fmt/colors.ts?source
|
||||
|
||||
export interface Code {
|
||||
open: string
|
||||
close: string
|
||||
regexp: RegExp
|
||||
open: string;
|
||||
close: string;
|
||||
regexp: RegExp;
|
||||
}
|
||||
|
||||
/** RGB 8-bits per channel. Each in range `0->255` or `0x00->0xff` */
|
||||
export interface Rgb {
|
||||
r: number
|
||||
g: number
|
||||
b: number
|
||||
r: number;
|
||||
g: number;
|
||||
b: number;
|
||||
}
|
||||
|
||||
let enabled = true
|
||||
let enabled = true;
|
||||
|
||||
/**
|
||||
* Set changing text color to enabled or disabled
|
||||
* @param value
|
||||
*/
|
||||
export function setColorEnabled(value: boolean) {
|
||||
enabled = value
|
||||
enabled = value;
|
||||
}
|
||||
|
||||
/** Get whether text color change is enabled or disabled. */
|
||||
export function getColorEnabled(): boolean {
|
||||
return enabled
|
||||
return enabled;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -41,7 +41,7 @@ function code(open: number[], close: number): Code {
|
||||
open: `\x1b[${open.join(';')}m`,
|
||||
close: `\x1b[${close}m`,
|
||||
regexp: new RegExp(`\\x1b\\[${close}m`, 'g'),
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -50,7 +50,7 @@ function code(open: number[], close: number): Code {
|
||||
* @param code color code to apply
|
||||
*/
|
||||
function run(str: string, code: Code): string {
|
||||
return enabled ? `${code.open}${str.replace(code.regexp, code.open)}${code.close}` : str
|
||||
return enabled ? `${code.open}${str.replace(code.regexp, code.open)}${code.close}` : str;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -58,7 +58,7 @@ function run(str: string, code: Code): string {
|
||||
* @param str text to reset
|
||||
*/
|
||||
export function reset(str: string): string {
|
||||
return run(str, code([0], 0))
|
||||
return run(str, code([0], 0));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -66,7 +66,7 @@ export function reset(str: string): string {
|
||||
* @param str text to make bold
|
||||
*/
|
||||
export function bold(str: string): string {
|
||||
return run(str, code([1], 22))
|
||||
return run(str, code([1], 22));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -74,7 +74,7 @@ export function bold(str: string): string {
|
||||
* @param str text to dim
|
||||
*/
|
||||
export function dim(str: string): string {
|
||||
return run(str, code([2], 22))
|
||||
return run(str, code([2], 22));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -82,7 +82,7 @@ export function dim(str: string): string {
|
||||
* @param str text to make italic
|
||||
*/
|
||||
export function italic(str: string): string {
|
||||
return run(str, code([3], 23))
|
||||
return run(str, code([3], 23));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -90,7 +90,7 @@ export function italic(str: string): string {
|
||||
* @param str text to underline
|
||||
*/
|
||||
export function underline(str: string): string {
|
||||
return run(str, code([4], 24))
|
||||
return run(str, code([4], 24));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -98,7 +98,7 @@ export function underline(str: string): string {
|
||||
* @param str text to invert its color
|
||||
*/
|
||||
export function inverse(str: string): string {
|
||||
return run(str, code([7], 27))
|
||||
return run(str, code([7], 27));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -106,7 +106,7 @@ export function inverse(str: string): string {
|
||||
* @param str text to hide
|
||||
*/
|
||||
export function hidden(str: string): string {
|
||||
return run(str, code([8], 28))
|
||||
return run(str, code([8], 28));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -114,7 +114,7 @@ export function hidden(str: string): string {
|
||||
* @param str text to strike through
|
||||
*/
|
||||
export function strikethrough(str: string): string {
|
||||
return run(str, code([9], 29))
|
||||
return run(str, code([9], 29));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -122,7 +122,7 @@ export function strikethrough(str: string): string {
|
||||
* @param str text to make black
|
||||
*/
|
||||
export function black(str: string): string {
|
||||
return run(str, code([30], 39))
|
||||
return run(str, code([30], 39));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -130,7 +130,7 @@ export function black(str: string): string {
|
||||
* @param str text to make red
|
||||
*/
|
||||
export function red(str: string): string {
|
||||
return run(str, code([31], 39))
|
||||
return run(str, code([31], 39));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -138,7 +138,7 @@ export function red(str: string): string {
|
||||
* @param str text to make green
|
||||
*/
|
||||
export function green(str: string): string {
|
||||
return run(str, code([32], 39))
|
||||
return run(str, code([32], 39));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -146,7 +146,7 @@ export function green(str: string): string {
|
||||
* @param str text to make yellow
|
||||
*/
|
||||
export function yellow(str: string): string {
|
||||
return run(str, code([33], 39))
|
||||
return run(str, code([33], 39));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -154,7 +154,7 @@ export function yellow(str: string): string {
|
||||
* @param str text to make blue
|
||||
*/
|
||||
export function blue(str: string): string {
|
||||
return run(str, code([34], 39))
|
||||
return run(str, code([34], 39));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -162,7 +162,7 @@ export function blue(str: string): string {
|
||||
* @param str text to make magenta
|
||||
*/
|
||||
export function magenta(str: string): string {
|
||||
return run(str, code([35], 39))
|
||||
return run(str, code([35], 39));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -170,7 +170,7 @@ export function magenta(str: string): string {
|
||||
* @param str text to make cyan
|
||||
*/
|
||||
export function cyan(str: string): string {
|
||||
return run(str, code([36], 39))
|
||||
return run(str, code([36], 39));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -178,7 +178,7 @@ export function cyan(str: string): string {
|
||||
* @param str text to make white
|
||||
*/
|
||||
export function white(str: string): string {
|
||||
return run(str, code([37], 39))
|
||||
return run(str, code([37], 39));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -186,7 +186,7 @@ export function white(str: string): string {
|
||||
* @param str text to make gray
|
||||
*/
|
||||
export function gray(str: string): string {
|
||||
return brightBlack(str)
|
||||
return brightBlack(str);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -194,7 +194,7 @@ export function gray(str: string): string {
|
||||
* @param str text to make bright-black
|
||||
*/
|
||||
export function brightBlack(str: string): string {
|
||||
return run(str, code([90], 39))
|
||||
return run(str, code([90], 39));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -202,7 +202,7 @@ export function brightBlack(str: string): string {
|
||||
* @param str text to make bright-red
|
||||
*/
|
||||
export function brightRed(str: string): string {
|
||||
return run(str, code([91], 39))
|
||||
return run(str, code([91], 39));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -210,7 +210,7 @@ export function brightRed(str: string): string {
|
||||
* @param str text to make bright-green
|
||||
*/
|
||||
export function brightGreen(str: string): string {
|
||||
return run(str, code([92], 39))
|
||||
return run(str, code([92], 39));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -218,7 +218,7 @@ export function brightGreen(str: string): string {
|
||||
* @param str text to make bright-yellow
|
||||
*/
|
||||
export function brightYellow(str: string): string {
|
||||
return run(str, code([93], 39))
|
||||
return run(str, code([93], 39));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -226,7 +226,7 @@ export function brightYellow(str: string): string {
|
||||
* @param str text to make bright-blue
|
||||
*/
|
||||
export function brightBlue(str: string): string {
|
||||
return run(str, code([94], 39))
|
||||
return run(str, code([94], 39));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -234,7 +234,7 @@ export function brightBlue(str: string): string {
|
||||
* @param str text to make bright-magenta
|
||||
*/
|
||||
export function brightMagenta(str: string): string {
|
||||
return run(str, code([95], 39))
|
||||
return run(str, code([95], 39));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -242,7 +242,7 @@ export function brightMagenta(str: string): string {
|
||||
* @param str text to make bright-cyan
|
||||
*/
|
||||
export function brightCyan(str: string): string {
|
||||
return run(str, code([96], 39))
|
||||
return run(str, code([96], 39));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -250,7 +250,7 @@ export function brightCyan(str: string): string {
|
||||
* @param str text to make bright-white
|
||||
*/
|
||||
export function brightWhite(str: string): string {
|
||||
return run(str, code([97], 39))
|
||||
return run(str, code([97], 39));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -258,7 +258,7 @@ export function brightWhite(str: string): string {
|
||||
* @param str text to make its background black
|
||||
*/
|
||||
export function bgBlack(str: string): string {
|
||||
return run(str, code([40], 49))
|
||||
return run(str, code([40], 49));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -266,7 +266,7 @@ export function bgBlack(str: string): string {
|
||||
* @param str text to make its background red
|
||||
*/
|
||||
export function bgRed(str: string): string {
|
||||
return run(str, code([41], 49))
|
||||
return run(str, code([41], 49));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -274,7 +274,7 @@ export function bgRed(str: string): string {
|
||||
* @param str text to make its background green
|
||||
*/
|
||||
export function bgGreen(str: string): string {
|
||||
return run(str, code([42], 49))
|
||||
return run(str, code([42], 49));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -282,7 +282,7 @@ export function bgGreen(str: string): string {
|
||||
* @param str text to make its background yellow
|
||||
*/
|
||||
export function bgYellow(str: string): string {
|
||||
return run(str, code([43], 49))
|
||||
return run(str, code([43], 49));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -290,7 +290,7 @@ export function bgYellow(str: string): string {
|
||||
* @param str text to make its background blue
|
||||
*/
|
||||
export function bgBlue(str: string): string {
|
||||
return run(str, code([44], 49))
|
||||
return run(str, code([44], 49));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -298,7 +298,7 @@ export function bgBlue(str: string): string {
|
||||
* @param str text to make its background magenta
|
||||
*/
|
||||
export function bgMagenta(str: string): string {
|
||||
return run(str, code([45], 49))
|
||||
return run(str, code([45], 49));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -306,7 +306,7 @@ export function bgMagenta(str: string): string {
|
||||
* @param str text to make its background cyan
|
||||
*/
|
||||
export function bgCyan(str: string): string {
|
||||
return run(str, code([46], 49))
|
||||
return run(str, code([46], 49));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -314,7 +314,7 @@ export function bgCyan(str: string): string {
|
||||
* @param str text to make its background white
|
||||
*/
|
||||
export function bgWhite(str: string): string {
|
||||
return run(str, code([47], 49))
|
||||
return run(str, code([47], 49));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -322,7 +322,7 @@ export function bgWhite(str: string): string {
|
||||
* @param str text to make its background bright-black
|
||||
*/
|
||||
export function bgBrightBlack(str: string): string {
|
||||
return run(str, code([100], 49))
|
||||
return run(str, code([100], 49));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -330,7 +330,7 @@ export function bgBrightBlack(str: string): string {
|
||||
* @param str text to make its background bright-red
|
||||
*/
|
||||
export function bgBrightRed(str: string): string {
|
||||
return run(str, code([101], 49))
|
||||
return run(str, code([101], 49));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -338,7 +338,7 @@ export function bgBrightRed(str: string): string {
|
||||
* @param str text to make its background bright-green
|
||||
*/
|
||||
export function bgBrightGreen(str: string): string {
|
||||
return run(str, code([102], 49))
|
||||
return run(str, code([102], 49));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -346,7 +346,7 @@ export function bgBrightGreen(str: string): string {
|
||||
* @param str text to make its background bright-yellow
|
||||
*/
|
||||
export function bgBrightYellow(str: string): string {
|
||||
return run(str, code([103], 49))
|
||||
return run(str, code([103], 49));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -354,7 +354,7 @@ export function bgBrightYellow(str: string): string {
|
||||
* @param str text to make its background bright-blue
|
||||
*/
|
||||
export function bgBrightBlue(str: string): string {
|
||||
return run(str, code([104], 49))
|
||||
return run(str, code([104], 49));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -362,7 +362,7 @@ export function bgBrightBlue(str: string): string {
|
||||
* @param str text to make its background bright-magenta
|
||||
*/
|
||||
export function bgBrightMagenta(str: string): string {
|
||||
return run(str, code([105], 49))
|
||||
return run(str, code([105], 49));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -370,7 +370,7 @@ export function bgBrightMagenta(str: string): string {
|
||||
* @param str text to make its background bright-cyan
|
||||
*/
|
||||
export function bgBrightCyan(str: string): string {
|
||||
return run(str, code([106], 49))
|
||||
return run(str, code([106], 49));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -378,7 +378,7 @@ export function bgBrightCyan(str: string): string {
|
||||
* @param str text to make its background bright-white
|
||||
*/
|
||||
export function bgBrightWhite(str: string): string {
|
||||
return run(str, code([107], 49))
|
||||
return run(str, code([107], 49));
|
||||
}
|
||||
|
||||
/* Special Color Sequences */
|
||||
@@ -390,7 +390,7 @@ export function bgBrightWhite(str: string): string {
|
||||
* @param min number to truncate from
|
||||
*/
|
||||
function clampAndTruncate(n: number, max = 255, min = 0): number {
|
||||
return Math.trunc(Math.max(Math.min(n, max), min))
|
||||
return Math.trunc(Math.max(Math.min(n, max), min));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -400,7 +400,7 @@ function clampAndTruncate(n: number, max = 255, min = 0): number {
|
||||
* @param color code
|
||||
*/
|
||||
export function rgb8(str: string, color: number): string {
|
||||
return run(str, code([38, 5, clampAndTruncate(color)], 39))
|
||||
return run(str, code([38, 5, clampAndTruncate(color)], 39));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -410,7 +410,7 @@ export function rgb8(str: string, color: number): string {
|
||||
* @param color code
|
||||
*/
|
||||
export function bgRgb8(str: string, color: number): string {
|
||||
return run(str, code([48, 5, clampAndTruncate(color)], 49))
|
||||
return run(str, code([48, 5, clampAndTruncate(color)], 49));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -430,9 +430,9 @@ export function bgRgb8(str: string, color: number): string {
|
||||
*/
|
||||
export function rgb24(str: string, color: number | Rgb): string {
|
||||
if (typeof color === 'number') {
|
||||
return run(str, code([38, 2, (color >> 16) & 0xff, (color >> 8) & 0xff, color & 0xff], 39))
|
||||
return run(str, code([38, 2, (color >> 16) & 0xff, (color >> 8) & 0xff, color & 0xff], 39));
|
||||
}
|
||||
return run(str, code([38, 2, clampAndTruncate(color.r), clampAndTruncate(color.g), clampAndTruncate(color.b)], 39))
|
||||
return run(str, code([38, 2, clampAndTruncate(color.r), clampAndTruncate(color.g), clampAndTruncate(color.b)], 39));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -452,9 +452,9 @@ export function rgb24(str: string, color: number | Rgb): string {
|
||||
*/
|
||||
export function bgRgb24(str: string, color: number | Rgb): string {
|
||||
if (typeof color === 'number') {
|
||||
return run(str, code([48, 2, (color >> 16) & 0xff, (color >> 8) & 0xff, color & 0xff], 49))
|
||||
return run(str, code([48, 2, (color >> 16) & 0xff, (color >> 8) & 0xff, color & 0xff], 49));
|
||||
}
|
||||
return run(str, code([48, 2, clampAndTruncate(color.r), clampAndTruncate(color.g), clampAndTruncate(color.b)], 49))
|
||||
return run(str, code([48, 2, clampAndTruncate(color.r), clampAndTruncate(color.g), clampAndTruncate(color.b)], 49));
|
||||
}
|
||||
|
||||
// https://github.com/chalk/ansi-regex/blob/02fa893d619d3da85411acc8fd4e2eea0e95a9d9/index.js
|
||||
@@ -464,12 +464,12 @@ const ANSI_PATTERN = new RegExp(
|
||||
'(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PR-TZcf-nq-uy=><~]))',
|
||||
].join('|'),
|
||||
'g',
|
||||
)
|
||||
);
|
||||
|
||||
/**
|
||||
* Remove ANSI escape codes from the string.
|
||||
* @param string to remove ANSI escape codes from
|
||||
*/
|
||||
export function stripColor(string: string): string {
|
||||
return string.replace(ANSI_PATTERN, '')
|
||||
return string.replace(ANSI_PATTERN, '');
|
||||
}
|
||||
|
||||
@@ -1 +1 @@
|
||||
export const DISCORDENO_VERSION = '22.0.0-beta.1'
|
||||
export const DISCORDENO_VERSION = '22.0.0-beta.1';
|
||||
|
||||
@@ -2,18 +2,18 @@ export function iconHashToBigInt(hash: string): bigint {
|
||||
// The icon is animated so it needs special handling
|
||||
if (hash.startsWith('a_')) {
|
||||
// Change the `a_` to just be `a`
|
||||
hash = `a${hash.substring(2)}`
|
||||
hash = `a${hash.substring(2)}`;
|
||||
} else {
|
||||
// The icon is not animated but it could be that it starts with a 0 so we just put a `b` in front so nothing breaks
|
||||
hash = `b${hash}`
|
||||
hash = `b${hash}`;
|
||||
}
|
||||
|
||||
return BigInt(`0x${hash}`)
|
||||
return BigInt(`0x${hash}`);
|
||||
}
|
||||
|
||||
export function iconBigintToHash(icon: bigint): string {
|
||||
// Convert the bigint back to a hash
|
||||
const hash = icon.toString(16)
|
||||
const hash = icon.toString(16);
|
||||
// Hashes starting with a are animated and with b are not so need to handle that
|
||||
return hash.startsWith('a') ? `a_${hash.substring(1)}` : hash.substring(1)
|
||||
return hash.startsWith('a') ? `a_${hash.substring(1)}` : hash.substring(1);
|
||||
}
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import { type BigString, type GetGuildWidgetImageQuery, type ImageFormat, type ImageSize, StickerFormatTypes } from '@discordeno/types'
|
||||
import { iconBigintToHash } from './hash.js'
|
||||
import { type BigString, type GetGuildWidgetImageQuery, type ImageFormat, type ImageSize, StickerFormatTypes } from '@discordeno/types';
|
||||
import { iconBigintToHash } from './hash.js';
|
||||
|
||||
export interface ImageOptions {
|
||||
size?: ImageSize
|
||||
format?: ImageFormat
|
||||
size?: ImageSize;
|
||||
format?: ImageFormat;
|
||||
}
|
||||
|
||||
/** Help format an image url. */
|
||||
export function formatImageUrl(url: string, size: ImageSize = 128, format?: ImageFormat): string {
|
||||
return `${url}.${format ?? (url.includes('/a_') ? 'gif' : 'webp')}?size=${size}`
|
||||
return `${url}.${format ?? (url.includes('/a_') ? 'gif' : 'webp')}?size=${size}`;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -23,7 +23,7 @@ export function formatImageUrl(url: string, size: ImageSize = 128, format?: Imag
|
||||
* The animated parameter is used to specify the animated query parameter valid for webp images or to force the gif if the format is not set to webp.
|
||||
*/
|
||||
export function emojiUrl(emojiId: BigString, animated = false, format: ImageFormat = 'png'): string {
|
||||
return `https://cdn.discordapp.com/emojis/${emojiId}.${animated ? (format === 'webp' ? 'webp' : 'gif') : format}${animated && format === 'webp' ? '?animated=true' : ''}`
|
||||
return `https://cdn.discordapp.com/emojis/${emojiId}.${animated ? (format === 'webp' ? 'webp' : 'gif') : format}${animated && format === 'webp' ? '?animated=true' : ''}`;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -39,7 +39,7 @@ export function avatarUrl(userId: BigString, avatar: BigString, options?: ImageO
|
||||
`https://cdn.discordapp.com/avatars/${userId}/${typeof avatar === 'string' ? avatar : iconBigintToHash(avatar)}`,
|
||||
options?.size ?? 128,
|
||||
options?.format,
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -50,10 +50,10 @@ export function avatarUrl(userId: BigString, avatar: BigString, options?: ImageO
|
||||
* @returns The user default avatar as an URL.
|
||||
*/
|
||||
export function defaultAvatarUrl(userId: BigString, discriminator: string) {
|
||||
const isLegacy = discriminator === '0' || discriminator === '0000'
|
||||
const index = isLegacy ? (BigInt(userId) >> 22n) % 6n : Number(discriminator) % 5
|
||||
const isLegacy = discriminator === '0' || discriminator === '0000';
|
||||
const index = isLegacy ? (BigInt(userId) >> 22n) % 6n : Number(discriminator) % 5;
|
||||
|
||||
return `https://cdn.discordapp.com/embed/avatars/${index}.png`
|
||||
return `https://cdn.discordapp.com/embed/avatars/${index}.png`;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -66,13 +66,13 @@ export function defaultAvatarUrl(userId: BigString, discriminator: string) {
|
||||
* @returns The user display avatar as an URL.
|
||||
*/
|
||||
export function displayAvatarUrl(userId: BigString, discriminator: string, avatar: BigString | undefined, options?: ImageOptions): string {
|
||||
return avatar ? avatarUrl(userId, avatar, options) : defaultAvatarUrl(userId, discriminator)
|
||||
return avatar ? avatarUrl(userId, avatar, options) : defaultAvatarUrl(userId, discriminator);
|
||||
}
|
||||
|
||||
export function avatarDecorationUrl(avatarDecoration: BigString): string {
|
||||
return `https://cdn.discordapp.com/avatar-decoration-presets/${
|
||||
typeof avatarDecoration === 'string' ? avatarDecoration : iconBigintToHash(avatarDecoration)
|
||||
}.png`
|
||||
}.png`;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -89,7 +89,7 @@ export function bannerUrl(userId: BigString, options?: ImageOptions & { banner?:
|
||||
options?.size ?? 128,
|
||||
options?.format,
|
||||
)
|
||||
: undefined
|
||||
: undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -106,7 +106,7 @@ export function guildBannerUrl(guildId: BigString, options: ImageOptions & { ban
|
||||
options.size ?? 128,
|
||||
options.format,
|
||||
)
|
||||
: undefined
|
||||
: undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -124,7 +124,7 @@ export function guildIconUrl(guildId: BigString, imageHash: BigString | undefine
|
||||
options?.size ?? 128,
|
||||
options?.format,
|
||||
)
|
||||
: undefined
|
||||
: undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -142,7 +142,7 @@ export function guildSplashUrl(guildId: BigString, imageHash: BigString | undefi
|
||||
options?.size ?? 128,
|
||||
options?.format,
|
||||
)
|
||||
: undefined
|
||||
: undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -160,7 +160,7 @@ export function guildDiscoverySplashUrl(guildId: BigString, imageHash: BigString
|
||||
options?.size ?? 128,
|
||||
options?.format,
|
||||
)
|
||||
: undefined
|
||||
: undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -177,7 +177,7 @@ export function guildScheduledEventCoverUrl(eventId: BigString, options: ImageOp
|
||||
options.size ?? 128,
|
||||
options.format,
|
||||
)
|
||||
: undefined
|
||||
: undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -188,13 +188,13 @@ export function guildScheduledEventCoverUrl(eventId: BigString, options: ImageOp
|
||||
* @returns The link to the resource.
|
||||
*/
|
||||
export function getWidgetImageUrl(guildId: BigString, options?: GetGuildWidgetImageQuery): string {
|
||||
let url = `https://discordapp.com/api/guilds/${guildId}/widget.png`
|
||||
let url = `https://discordapp.com/api/guilds/${guildId}/widget.png`;
|
||||
|
||||
if (options?.style) {
|
||||
url += `?style=${options.style}`
|
||||
url += `?style=${options.style}`;
|
||||
}
|
||||
|
||||
return url
|
||||
return url;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -214,7 +214,7 @@ export function memberAvatarUrl(guildId: BigString, userId: BigString, options?:
|
||||
options?.size ?? 128,
|
||||
options?.format,
|
||||
)
|
||||
: undefined
|
||||
: undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -234,7 +234,7 @@ export function memberBannerUrl(guildId: BigString, userId: BigString, options?:
|
||||
options?.size ?? 128,
|
||||
options?.format,
|
||||
)
|
||||
: undefined
|
||||
: undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -252,7 +252,7 @@ export function applicationIconUrl(applicationId: BigString, iconHash: BigString
|
||||
options?.size ?? 128,
|
||||
options?.format,
|
||||
)
|
||||
: undefined
|
||||
: undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -270,7 +270,7 @@ export function applicationCoverUrl(applicationId: BigString, coverHash: BigStri
|
||||
options?.size ?? 128,
|
||||
options?.format,
|
||||
)
|
||||
: undefined
|
||||
: undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -288,7 +288,7 @@ export function applicationAssetUrl(applicationId: BigString, assetId: BigString
|
||||
options?.size ?? 128,
|
||||
options?.format,
|
||||
)
|
||||
: undefined
|
||||
: undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -307,7 +307,7 @@ export function stickerPackBannerUrl(bannerAssetId: BigString | undefined, optio
|
||||
options?.size ?? 128,
|
||||
options?.format,
|
||||
)
|
||||
: undefined
|
||||
: undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -318,14 +318,14 @@ export function stickerPackBannerUrl(bannerAssetId: BigString | undefined, optio
|
||||
* @returns The link to the resource or `undefined`.
|
||||
*/
|
||||
export function stickerUrl(stickerId: BigString | number, options?: ImageOptions & { type?: StickerFormatTypes }): string | undefined {
|
||||
if (!stickerId) return
|
||||
if (!stickerId) return;
|
||||
|
||||
const url =
|
||||
options?.type === StickerFormatTypes.Gif
|
||||
? `https://media.discordapp.net/stickers/${stickerId}`
|
||||
: `https://cdn.discordapp.com/stickers/${stickerId}`
|
||||
: `https://cdn.discordapp.com/stickers/${stickerId}`;
|
||||
|
||||
return formatImageUrl(url, options?.size ?? 128, options?.format)
|
||||
return formatImageUrl(url, options?.size ?? 128, options?.format);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -343,7 +343,7 @@ export function teamIconUrl(teamId: BigString, iconHash: BigString | undefined,
|
||||
options?.size ?? 128,
|
||||
options?.format,
|
||||
)
|
||||
: undefined
|
||||
: undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -361,7 +361,7 @@ export function roleIconUrl(roleId: BigString, iconHash: BigString | undefined,
|
||||
options?.size ?? 128,
|
||||
options?.format,
|
||||
)
|
||||
: undefined
|
||||
: undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -373,7 +373,7 @@ export function roleIconUrl(roleId: BigString, iconHash: BigString | undefined,
|
||||
* @returns The link to the resource or `undefined` if no badge has been set.
|
||||
*/
|
||||
export function guildTagBadgeUrl(guildId: BigString, badgeHash: BigString | undefined, options?: ImageOptions): string | undefined {
|
||||
if (badgeHash === undefined) return undefined
|
||||
if (badgeHash === undefined) return undefined;
|
||||
|
||||
return formatImageUrl(`https://cdn.discordapp.com/guild-tag-badges/${guildId}/${badgeHash}`, options?.size ?? 128, options?.format)
|
||||
return formatImageUrl(`https://cdn.discordapp.com/guild-tag-badges/${guildId}/${badgeHash}`, options?.size ?? 128, options?.format);
|
||||
}
|
||||
|
||||
+19
-19
@@ -1,19 +1,19 @@
|
||||
export * from './base64.js'
|
||||
export * from './bucket.js'
|
||||
export * from './builders.js'
|
||||
export * from './Collection.js'
|
||||
export * from './casing.js'
|
||||
export * from './colors.js'
|
||||
export * from './constants.js'
|
||||
export * from './hash.js'
|
||||
export * from './images.js'
|
||||
export * from './logger.js'
|
||||
export * from './oauth2.js'
|
||||
export * from './permissions.js'
|
||||
export * from './reactions.js'
|
||||
export * from './snowflakes.js'
|
||||
export * from './token.js'
|
||||
export * from './typeguards.js'
|
||||
export * from './urls.js'
|
||||
export * from './urlToBase64.js'
|
||||
export * from './utils.js'
|
||||
export * from './base64.js';
|
||||
export * from './bucket.js';
|
||||
export * from './builders.js';
|
||||
export * from './Collection.js';
|
||||
export * from './casing.js';
|
||||
export * from './colors.js';
|
||||
export * from './constants.js';
|
||||
export * from './hash.js';
|
||||
export * from './images.js';
|
||||
export * from './logger.js';
|
||||
export * from './oauth2.js';
|
||||
export * from './permissions.js';
|
||||
export * from './reactions.js';
|
||||
export * from './snowflakes.js';
|
||||
export * from './token.js';
|
||||
export * from './typeguards.js';
|
||||
export * from './urls.js';
|
||||
export * from './urlToBase64.js';
|
||||
export * from './utils.js';
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { bgBrightMagenta, black, bold, cyan, gray, italic, red, yellow } from './colors.js'
|
||||
import { bgBrightMagenta, black, bold, cyan, gray, italic, red, yellow } from './colors.js';
|
||||
|
||||
export enum LogLevels {
|
||||
Debug,
|
||||
@@ -14,70 +14,70 @@ const prefixes = new Map<LogLevels, string>([
|
||||
[LogLevels.Warn, 'WARN'],
|
||||
[LogLevels.Error, 'ERROR'],
|
||||
[LogLevels.Fatal, 'FATAL'],
|
||||
])
|
||||
]);
|
||||
|
||||
const noColor: (str: string) => string = (msg) => msg
|
||||
const noColor: (str: string) => string = (msg) => msg;
|
||||
const colorFunctions = new Map<LogLevels, (str: string) => string>([
|
||||
[LogLevels.Debug, gray],
|
||||
[LogLevels.Info, cyan],
|
||||
[LogLevels.Warn, yellow],
|
||||
[LogLevels.Error, (str: string) => red(str)],
|
||||
[LogLevels.Fatal, (str: string) => red(bold(italic(str)))],
|
||||
])
|
||||
]);
|
||||
|
||||
export function createLogger({ logLevel = LogLevels.Info, name }: { logLevel?: LogLevels; name?: string } = {}) {
|
||||
function log(level: LogLevels, ...args: any[]) {
|
||||
if (level < logLevel) return
|
||||
if (level < logLevel) return;
|
||||
|
||||
let color = colorFunctions.get(level)
|
||||
if (!color) color = noColor
|
||||
let color = colorFunctions.get(level);
|
||||
if (!color) color = noColor;
|
||||
|
||||
const date = new Date()
|
||||
const date = new Date();
|
||||
const log = [
|
||||
bgBrightMagenta(black(`[${date.toLocaleDateString()} ${date.toLocaleTimeString()}]`)),
|
||||
color(prefixes.get(level) ?? 'DEBUG'),
|
||||
name ? `${name} >` : '>',
|
||||
...args,
|
||||
]
|
||||
];
|
||||
|
||||
switch (level) {
|
||||
case LogLevels.Debug:
|
||||
return console.debug(...log)
|
||||
return console.debug(...log);
|
||||
case LogLevels.Info:
|
||||
return console.info(...log)
|
||||
return console.info(...log);
|
||||
case LogLevels.Warn:
|
||||
return console.warn(...log)
|
||||
return console.warn(...log);
|
||||
case LogLevels.Error:
|
||||
return console.error(...log)
|
||||
return console.error(...log);
|
||||
case LogLevels.Fatal:
|
||||
return console.error(...log)
|
||||
return console.error(...log);
|
||||
default:
|
||||
return console.log(...log)
|
||||
return console.log(...log);
|
||||
}
|
||||
}
|
||||
|
||||
function setLevel(level: LogLevels) {
|
||||
logLevel = level
|
||||
logLevel = level;
|
||||
}
|
||||
|
||||
function debug(...args: any[]) {
|
||||
log(LogLevels.Debug, ...args)
|
||||
log(LogLevels.Debug, ...args);
|
||||
}
|
||||
|
||||
function info(...args: any[]) {
|
||||
log(LogLevels.Info, ...args)
|
||||
log(LogLevels.Info, ...args);
|
||||
}
|
||||
|
||||
function warn(...args: any[]) {
|
||||
log(LogLevels.Warn, ...args)
|
||||
log(LogLevels.Warn, ...args);
|
||||
}
|
||||
|
||||
function error(...args: any[]) {
|
||||
log(LogLevels.Error, ...args)
|
||||
log(LogLevels.Error, ...args);
|
||||
}
|
||||
|
||||
function fatal(...args: any[]) {
|
||||
log(LogLevels.Fatal, ...args)
|
||||
log(LogLevels.Fatal, ...args);
|
||||
}
|
||||
|
||||
return {
|
||||
@@ -88,8 +88,8 @@ export function createLogger({ logLevel = LogLevels.Info, name }: { logLevel?: L
|
||||
warn,
|
||||
error,
|
||||
fatal,
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export const logger = createLogger({ name: 'Discordeno' })
|
||||
export default logger
|
||||
export const logger = createLogger({ name: 'Discordeno' });
|
||||
export default logger;
|
||||
|
||||
@@ -1,26 +1,26 @@
|
||||
import type { BigString, DiscordApplicationIntegrationType, OAuth2Scope, PermissionStrings } from '@discordeno/types'
|
||||
import { encodeBase64Url } from './base64.js'
|
||||
import { calculateBits } from './permissions.js'
|
||||
import type { BigString, DiscordApplicationIntegrationType, OAuth2Scope, PermissionStrings } from '@discordeno/types';
|
||||
import { encodeBase64Url } from './base64.js';
|
||||
import { calculateBits } from './permissions.js';
|
||||
|
||||
export function createOAuth2Link(options: CreateOAuth2LinkOptions): string {
|
||||
const joinedScopeString = options.scope.join('%20')
|
||||
const joinedScopeString = options.scope.join('%20');
|
||||
|
||||
let url = `https://discord.com/oauth2/authorize?client_id=${options.clientId}&scope=${joinedScopeString}`
|
||||
let url = `https://discord.com/oauth2/authorize?client_id=${options.clientId}&scope=${joinedScopeString}`;
|
||||
|
||||
if (options.responseType) url += `&response_type=${options.responseType}`
|
||||
if (options.state) url += `&state=${encodeURIComponent(options.state)}`
|
||||
if (options.redirectUri) url += `&redirect_uri=${encodeURIComponent(options.redirectUri)}`
|
||||
if (options.prompt) url += `&prompt=${options.prompt}`
|
||||
if (options.permissions) url += `&permissions=${Array.isArray(options.permissions) ? calculateBits(options.permissions) : options.permissions}`
|
||||
if (options.guildId) url += `&guild_id=${options.guildId}`
|
||||
if (options.disableGuildSelect !== undefined) url += `&disable_guild_select=${options.disableGuildSelect}`
|
||||
if (options.integrationType) url += `&integration_type=${options.integrationType}`
|
||||
if (options.responseType) url += `&response_type=${options.responseType}`;
|
||||
if (options.state) url += `&state=${encodeURIComponent(options.state)}`;
|
||||
if (options.redirectUri) url += `&redirect_uri=${encodeURIComponent(options.redirectUri)}`;
|
||||
if (options.prompt) url += `&prompt=${options.prompt}`;
|
||||
if (options.permissions) url += `&permissions=${Array.isArray(options.permissions) ? calculateBits(options.permissions) : options.permissions}`;
|
||||
if (options.guildId) url += `&guild_id=${options.guildId}`;
|
||||
if (options.disableGuildSelect !== undefined) url += `&disable_guild_select=${options.disableGuildSelect}`;
|
||||
if (options.integrationType) url += `&integration_type=${options.integrationType}`;
|
||||
|
||||
// Options defined by RFC 7636 (https://datatracker.ietf.org/doc/html/rfc7636)
|
||||
if (options.codeChallenge) url += `&code_challenge=${options.codeChallenge}`
|
||||
if (options.codeChallengeMethod) url += `&code_challenge_method=${options.codeChallengeMethod}`
|
||||
if (options.codeChallenge) url += `&code_challenge=${options.codeChallenge}`;
|
||||
if (options.codeChallengeMethod) url += `&code_challenge_method=${options.codeChallengeMethod}`;
|
||||
|
||||
return url
|
||||
return url;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -36,9 +36,9 @@ export function createOAuth2Link(options: CreateOAuth2LinkOptions): string {
|
||||
* @see https://datatracker.ietf.org/doc/html/rfc7636#section-4.1 for why 32 octets is the default
|
||||
*/
|
||||
export function generateCodeVerifier(octetLength: number = 32) {
|
||||
const randomBytes = new Uint8Array(octetLength)
|
||||
crypto.getRandomValues(randomBytes)
|
||||
return encodeBase64Url(randomBytes)
|
||||
const randomBytes = new Uint8Array(octetLength);
|
||||
crypto.getRandomValues(randomBytes);
|
||||
return encodeBase64Url(randomBytes);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -51,8 +51,8 @@ export function generateCodeVerifier(octetLength: number = 32) {
|
||||
* This performs a SHA-256 hash on the verifier and encodes it using base64url encoding. Discord only supports 'S256' as the code challenge method.
|
||||
*/
|
||||
export async function createCodeChallenge(verifier: string) {
|
||||
const hashed = await crypto.subtle.digest('SHA-256', new TextEncoder().encode(verifier))
|
||||
return encodeBase64Url(hashed)
|
||||
const hashed = await crypto.subtle.digest('SHA-256', new TextEncoder().encode(verifier));
|
||||
return encodeBase64Url(hashed);
|
||||
}
|
||||
|
||||
export interface CreateOAuth2LinkOptions {
|
||||
@@ -62,24 +62,24 @@ export interface CreateOAuth2LinkOptions {
|
||||
* @remarks
|
||||
* Should be defined only if using either OAuth2 authorization, implicit or not, or [advanced bot authorization](https://discord.com/developers/docs/topics/oauth2#advanced-bot-authorization)
|
||||
*/
|
||||
responseType?: 'code' | 'token'
|
||||
responseType?: 'code' | 'token';
|
||||
/** The id of the application */
|
||||
clientId: BigString
|
||||
clientId: BigString;
|
||||
/** The scopes for the application */
|
||||
scope: OAuth2Scope[]
|
||||
scope: OAuth2Scope[];
|
||||
/**
|
||||
* The optional state for security
|
||||
*
|
||||
* @see https://discord.com/developers/docs/topics/oauth2#state-and-security
|
||||
*/
|
||||
state?: string
|
||||
state?: string;
|
||||
/**
|
||||
* The redirect uri for after the authentication
|
||||
*
|
||||
* @remarks
|
||||
* Should be defined only if using either OAuth2 authorization, implicit or not, or [advanced bot authorization](https://discord.com/developers/docs/topics/oauth2#advanced-bot-authorization)
|
||||
*/
|
||||
redirectUri?: string
|
||||
redirectUri?: string;
|
||||
/**
|
||||
* The type of prompt to give to the user
|
||||
*
|
||||
@@ -87,28 +87,28 @@ export interface CreateOAuth2LinkOptions {
|
||||
* If set to `none`, it will skip the authorization screen and redirect them back to your redirect URI without requesting their authorization.
|
||||
* For passthrough scopes, like bot and webhook.incoming, authorization is always required.
|
||||
*/
|
||||
prompt?: 'consent' | 'none'
|
||||
prompt?: 'consent' | 'none';
|
||||
/**
|
||||
* The permissions of the invited bot
|
||||
*
|
||||
* @remarks
|
||||
* Should be defined only in a [bot authorization flow](https://discord.com/developers/docs/topics/oauth2#bot-authorization-flow) or with [advanced bot authorization](https://discord.com/developers/docs/topics/oauth2#advanced-bot-authorization)
|
||||
*/
|
||||
permissions?: BigString | PermissionStrings[]
|
||||
permissions?: BigString | PermissionStrings[];
|
||||
/**
|
||||
* Pre-fills the dropdown picker with a guild for the user
|
||||
*
|
||||
* @remarks
|
||||
* Should be defined only in a [bot authorization flow](https://discord.com/developers/docs/topics/oauth2#bot-authorization-flow) or with [advanced bot authorization](https://discord.com/developers/docs/topics/oauth2#advanced-bot-authorization) or with the `webhook.incoming` scope
|
||||
*/
|
||||
guildId?: BigString
|
||||
guildId?: BigString;
|
||||
/**
|
||||
* Disallows the user from changing the guild dropdown if set to true
|
||||
*
|
||||
* @remarks
|
||||
* Should be defined only in a [bot authorization flow](https://discord.com/developers/docs/topics/oauth2#bot-authorization-flow), with [advanced bot authorization](https://discord.com/developers/docs/topics/oauth2#advanced-bot-authorization) or with the `webhook.incoming` scope
|
||||
*/
|
||||
disableGuildSelect?: boolean
|
||||
disableGuildSelect?: boolean;
|
||||
/**
|
||||
* Specifies the installation context for the authorization
|
||||
*
|
||||
@@ -119,13 +119,13 @@ export interface CreateOAuth2LinkOptions {
|
||||
*
|
||||
* The application must be configured in the Developer Portal to support the provided `integrationType`.
|
||||
*/
|
||||
integrationType?: DiscordApplicationIntegrationType
|
||||
integrationType?: DiscordApplicationIntegrationType;
|
||||
/**
|
||||
* The code challenge used to verify the authorization request
|
||||
*
|
||||
* @see https://datatracker.ietf.org/doc/html/rfc7636#section-4.1
|
||||
*/
|
||||
codeChallenge?: string
|
||||
codeChallenge?: string;
|
||||
/**
|
||||
* The challenge method used to generate the code challenge
|
||||
*
|
||||
@@ -134,5 +134,5 @@ export interface CreateOAuth2LinkOptions {
|
||||
*
|
||||
* @see https://datatracker.ietf.org/doc/html/rfc7636#section-4.2
|
||||
*/
|
||||
codeChallengeMethod?: 'S256'
|
||||
codeChallengeMethod?: 'S256';
|
||||
}
|
||||
|
||||
@@ -1,22 +1,22 @@
|
||||
import type { PermissionStrings } from '@discordeno/types'
|
||||
import { BitwisePermissionFlags } from '@discordeno/types'
|
||||
import type { PermissionStrings } from '@discordeno/types';
|
||||
import { BitwisePermissionFlags } from '@discordeno/types';
|
||||
|
||||
/** This function converts a bitwise string to permission strings */
|
||||
export function calculatePermissions(permissionBits: bigint): PermissionStrings[] {
|
||||
return Object.keys(BitwisePermissionFlags).filter((permission) => {
|
||||
// Since Object.keys() not only returns the permission names but also the bit values we need to return false if it is a Number
|
||||
if (Number(permission)) return false
|
||||
if (Number(permission)) return false;
|
||||
// Check if permissionBits has this permission
|
||||
return permissionBits & BitwisePermissionFlags[permission as PermissionStrings]
|
||||
}) as PermissionStrings[]
|
||||
return permissionBits & BitwisePermissionFlags[permission as PermissionStrings];
|
||||
}) as PermissionStrings[];
|
||||
}
|
||||
|
||||
/** This function converts an array of permissions into the bitwise string. */
|
||||
export function calculateBits(permissions: PermissionStrings[]): string {
|
||||
return permissions
|
||||
.reduce((bits, perm) => {
|
||||
bits |= BitwisePermissionFlags[perm]
|
||||
return bits
|
||||
bits |= BitwisePermissionFlags[perm];
|
||||
return bits;
|
||||
}, 0n)
|
||||
.toString()
|
||||
.toString();
|
||||
}
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
/** Converts an reaction emoji unicode string to the discord required form of name:id */
|
||||
export function processReactionString(reaction: string): string {
|
||||
if (reaction.startsWith('<:')) {
|
||||
return reaction.substring(2, reaction.length - 1)
|
||||
return reaction.substring(2, reaction.length - 1);
|
||||
}
|
||||
|
||||
if (reaction.startsWith('<a:')) {
|
||||
return reaction.substring(3, reaction.length - 1)
|
||||
return reaction.substring(3, reaction.length - 1);
|
||||
}
|
||||
|
||||
return reaction
|
||||
return reaction;
|
||||
}
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import type { BigString } from '@discordeno/types'
|
||||
import type { BigString } from '@discordeno/types';
|
||||
|
||||
export function snowflakeToBigint(snowflake: BigString): bigint {
|
||||
return BigInt(snowflake)
|
||||
return BigInt(snowflake);
|
||||
}
|
||||
|
||||
export function bigintToSnowflake(snowflake: BigString): string {
|
||||
return snowflake.toString()
|
||||
return snowflake.toString();
|
||||
}
|
||||
|
||||
export function snowflakeToTimestamp(snowflake: BigString): number {
|
||||
return Number(BigInt(snowflake) >> 22n) + 1420070400000
|
||||
return Number(BigInt(snowflake) >> 22n) + 1420070400000;
|
||||
}
|
||||
|
||||
@@ -1,21 +1,21 @@
|
||||
const validTokenPrefixes = ['Bot', 'Bearer']
|
||||
const validTokenPrefixes = ['Bot', 'Bearer'];
|
||||
|
||||
/** Removes the Bot/Bearer before the token. */
|
||||
export function removeTokenPrefix(token?: string, type: 'GATEWAY' | 'REST' = 'REST'): string {
|
||||
// If no token is provided, throw an error
|
||||
if (token === undefined) {
|
||||
throw new Error(`The ${type} was not given a token. Please provide a token and try again.`)
|
||||
throw new Error(`The ${type} was not given a token. Please provide a token and try again.`);
|
||||
}
|
||||
|
||||
const splittedToken = token.split(' ')
|
||||
const splittedToken = token.split(' ');
|
||||
|
||||
// If the token does not have a prefix just return token
|
||||
if (splittedToken.length < 2 || !validTokenPrefixes.includes(splittedToken[0])) return token
|
||||
if (splittedToken.length < 2 || !validTokenPrefixes.includes(splittedToken[0])) return token;
|
||||
// Remove the prefix and return only the token.
|
||||
return splittedToken.splice(1).join(' ')
|
||||
return splittedToken.splice(1).join(' ');
|
||||
}
|
||||
|
||||
/** Get the bot id from the bot token. WARNING: Discord staff has mentioned this may not be stable forever. Use at your own risk. However, note for over 5 years this has never broken. */
|
||||
export function getBotIdFromToken(token: string): bigint {
|
||||
return BigInt(atob(token.split('.')[0]))
|
||||
return BigInt(atob(token.split('.')[0]));
|
||||
}
|
||||
|
||||
@@ -6,25 +6,25 @@ import type {
|
||||
GetMessagesBefore,
|
||||
GetMessagesLimit,
|
||||
GetMessagesOptions,
|
||||
} from '@discordeno/types'
|
||||
import { hasProperty } from './utils.js'
|
||||
} from '@discordeno/types';
|
||||
import { hasProperty } from './utils.js';
|
||||
|
||||
export function isGetMessagesAfter(options: GetMessagesOptions): options is GetMessagesAfter {
|
||||
return hasProperty(options, 'after')
|
||||
return hasProperty(options, 'after');
|
||||
}
|
||||
|
||||
export function isGetMessagesBefore(options: GetMessagesOptions): options is GetMessagesBefore {
|
||||
return hasProperty(options, 'before')
|
||||
return hasProperty(options, 'before');
|
||||
}
|
||||
|
||||
export function isGetMessagesAround(options: GetMessagesOptions): options is GetMessagesAround {
|
||||
return hasProperty(options, 'around')
|
||||
return hasProperty(options, 'around');
|
||||
}
|
||||
|
||||
export function isGetMessagesLimit(options: GetMessagesOptions): options is GetMessagesLimit {
|
||||
return hasProperty(options, 'limit')
|
||||
return hasProperty(options, 'limit');
|
||||
}
|
||||
|
||||
export function isInviteWithMetadata(options: DiscordInviteCreate | DiscordInviteMetadata): options is DiscordInviteMetadata {
|
||||
return !hasProperty(options, 'channel_id')
|
||||
return !hasProperty(options, 'channel_id');
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { encode } from './base64.js'
|
||||
import { encode } from './base64.js';
|
||||
|
||||
/** Converts a url to base 64. Useful for example, uploading/creating server emojis. */
|
||||
export async function urlToBase64(url: string): Promise<string> {
|
||||
const buffer = await fetch(url).then(async (res) => await res.arrayBuffer())
|
||||
const imageStr = encode(buffer)
|
||||
const type = url.substring(url.lastIndexOf('.') + 1)
|
||||
return `data:image/${type};base64,${imageStr}`
|
||||
const buffer = await fetch(url).then(async (res) => await res.arrayBuffer());
|
||||
const imageStr = encode(buffer);
|
||||
const type = url.substring(url.lastIndexOf('.') + 1);
|
||||
return `data:image/${type};base64,${imageStr}`;
|
||||
}
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import type { BigString } from '@discordeno/types'
|
||||
import type { BigString } from '@discordeno/types';
|
||||
|
||||
export function skuLink(appId: BigString, skuId: BigString): string {
|
||||
return `https://discord.com/application-directory/${appId}/store/${skuId}`
|
||||
return `https://discord.com/application-directory/${appId}/store/${skuId}`;
|
||||
}
|
||||
|
||||
export function storeLink(appId: BigString): string {
|
||||
return `https://discord.com/application-directory/${appId}/store`
|
||||
return `https://discord.com/application-directory/${appId}/store`;
|
||||
}
|
||||
|
||||
export function soundLink(soundId: BigString): string {
|
||||
return `https://cdn.discordapp.com/soundboard-sounds/${soundId}`
|
||||
return `https://cdn.discordapp.com/soundboard-sounds/${soundId}`;
|
||||
}
|
||||
|
||||
@@ -3,16 +3,16 @@ export async function delay(ms: number): Promise<void> {
|
||||
return new Promise(
|
||||
(resolve): NodeJS.Timeout =>
|
||||
setTimeout((): void => {
|
||||
resolve()
|
||||
resolve();
|
||||
}, ms),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
// Typescript is not so good as we developers so we need this little utility function to help it out
|
||||
// Taken from https://fettblog.eu/typescript-hasownproperty/
|
||||
/** TS save way to check if a property exists in an object */
|
||||
export function hasProperty<T extends {}, Y extends PropertyKey = string>(obj: T, prop: Y): obj is T & Record<Y, unknown> {
|
||||
return obj.hasOwnProperty(prop)
|
||||
return obj.hasOwnProperty(prop);
|
||||
}
|
||||
|
||||
/** Convert `JSON.stringify`-unserializable record values for debugging purposes. */
|
||||
@@ -20,8 +20,8 @@ export function jsonSafeReplacer(_key: string, value: unknown): unknown {
|
||||
switch (typeof value) {
|
||||
case 'bigint':
|
||||
// Bigints are unserializable by `JSON.stringify`.
|
||||
return String(value)
|
||||
return String(value);
|
||||
default: // Any other unhandled type that isn't supposed to require conversion.
|
||||
return value
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,113 +1,113 @@
|
||||
import { Buffer } from 'node:buffer'
|
||||
import { expect } from 'chai'
|
||||
import { describe, it } from 'mocha'
|
||||
import { decode, encode, encodeBase64Url } from '../src/base64.js'
|
||||
import { Buffer } from 'node:buffer';
|
||||
import { expect } from 'chai';
|
||||
import { describe, it } from 'mocha';
|
||||
import { decode, encode, encodeBase64Url } from '../src/base64.js';
|
||||
|
||||
describe('base64.ts', () => {
|
||||
describe('encode', () => {
|
||||
it('can encode string to base64', () => {
|
||||
expect(encode('Man Ё𤭢')).to.be.equal('TWFuINCB8KStog==')
|
||||
})
|
||||
expect(encode('Man Ё𤭢')).to.be.equal('TWFuINCB8KStog==');
|
||||
});
|
||||
it('can encode Uint8Array to base64', () => {
|
||||
expect(encode(new Uint8Array([77, 97, 110, 32, 208, 129, 240, 164, 173, 162]))).to.be.equal('TWFuINCB8KStog==')
|
||||
expect(encode(new Uint8Array([77, 97, 110, 32, 208, 129, 240, 164, 173]))).to.be.equal('TWFuINCB8KSt')
|
||||
expect(encode(new Uint8Array([77, 97, 110, 32, 208, 129, 240, 164, 173, 162, 63]))).to.be.equal('TWFuINCB8KStoj8=')
|
||||
expect(encode(new Uint8Array([199, 239, 242]))).to.be.equal('x+/y')
|
||||
expect(encode(new Uint8Array([77, 97, 110, 32, 208, 129, 240, 164, 173, 162]))).to.be.equal('TWFuINCB8KStog==');
|
||||
expect(encode(new Uint8Array([77, 97, 110, 32, 208, 129, 240, 164, 173]))).to.be.equal('TWFuINCB8KSt');
|
||||
expect(encode(new Uint8Array([77, 97, 110, 32, 208, 129, 240, 164, 173, 162, 63]))).to.be.equal('TWFuINCB8KStoj8=');
|
||||
expect(encode(new Uint8Array([199, 239, 242]))).to.be.equal('x+/y');
|
||||
|
||||
// From https://datatracker.ietf.org/doc/html/rfc4648#section-10
|
||||
expect(encode(new Uint8Array([]))).to.be.equal('')
|
||||
expect(encode(new Uint8Array([102]))).to.be.equal('Zg==')
|
||||
expect(encode(new Uint8Array([102, 111]))).to.be.equal('Zm8=')
|
||||
expect(encode(new Uint8Array([102, 111, 111]))).to.be.equal('Zm9v')
|
||||
expect(encode(new Uint8Array([102, 111, 111, 98]))).to.be.equal('Zm9vYg==')
|
||||
expect(encode(new Uint8Array([102, 111, 111, 98, 97]))).to.be.equal('Zm9vYmE=')
|
||||
expect(encode(new Uint8Array([102, 111, 111, 98, 97, 114]))).to.be.equal('Zm9vYmFy')
|
||||
})
|
||||
expect(encode(new Uint8Array([]))).to.be.equal('');
|
||||
expect(encode(new Uint8Array([102]))).to.be.equal('Zg==');
|
||||
expect(encode(new Uint8Array([102, 111]))).to.be.equal('Zm8=');
|
||||
expect(encode(new Uint8Array([102, 111, 111]))).to.be.equal('Zm9v');
|
||||
expect(encode(new Uint8Array([102, 111, 111, 98]))).to.be.equal('Zm9vYg==');
|
||||
expect(encode(new Uint8Array([102, 111, 111, 98, 97]))).to.be.equal('Zm9vYmE=');
|
||||
expect(encode(new Uint8Array([102, 111, 111, 98, 97, 114]))).to.be.equal('Zm9vYmFy');
|
||||
});
|
||||
it('can encode Buffer to base64', () => {
|
||||
expect(encode(Buffer.from([77, 97, 110, 32, 208, 129, 240, 164, 173, 162]))).to.be.equal('TWFuINCB8KStog==')
|
||||
expect(encode(Buffer.from([77, 97, 110, 32, 208, 129, 240, 164, 173]))).to.be.equal('TWFuINCB8KSt')
|
||||
expect(encode(Buffer.from([77, 97, 110, 32, 208, 129, 240, 164, 173, 162, 63]))).to.be.equal('TWFuINCB8KStoj8=')
|
||||
expect(encode(Buffer.from([199, 239, 242]))).to.be.equal('x+/y')
|
||||
expect(encode(Buffer.from([77, 97, 110, 32, 208, 129, 240, 164, 173, 162]))).to.be.equal('TWFuINCB8KStog==');
|
||||
expect(encode(Buffer.from([77, 97, 110, 32, 208, 129, 240, 164, 173]))).to.be.equal('TWFuINCB8KSt');
|
||||
expect(encode(Buffer.from([77, 97, 110, 32, 208, 129, 240, 164, 173, 162, 63]))).to.be.equal('TWFuINCB8KStoj8=');
|
||||
expect(encode(Buffer.from([199, 239, 242]))).to.be.equal('x+/y');
|
||||
|
||||
// From https://datatracker.ietf.org/doc/html/rfc4648#section-10
|
||||
expect(encode(Buffer.from([]))).to.be.equal('')
|
||||
expect(encode(Buffer.from([102]))).to.be.equal('Zg==')
|
||||
expect(encode(Buffer.from([102, 111]))).to.be.equal('Zm8=')
|
||||
expect(encode(Buffer.from([102, 111, 111]))).to.be.equal('Zm9v')
|
||||
expect(encode(Buffer.from([102, 111, 111, 98]))).to.be.equal('Zm9vYg==')
|
||||
expect(encode(Buffer.from([102, 111, 111, 98, 97]))).to.be.equal('Zm9vYmE=')
|
||||
expect(encode(Buffer.from([102, 111, 111, 98, 97, 114]))).to.be.equal('Zm9vYmFy')
|
||||
})
|
||||
})
|
||||
expect(encode(Buffer.from([]))).to.be.equal('');
|
||||
expect(encode(Buffer.from([102]))).to.be.equal('Zg==');
|
||||
expect(encode(Buffer.from([102, 111]))).to.be.equal('Zm8=');
|
||||
expect(encode(Buffer.from([102, 111, 111]))).to.be.equal('Zm9v');
|
||||
expect(encode(Buffer.from([102, 111, 111, 98]))).to.be.equal('Zm9vYg==');
|
||||
expect(encode(Buffer.from([102, 111, 111, 98, 97]))).to.be.equal('Zm9vYmE=');
|
||||
expect(encode(Buffer.from([102, 111, 111, 98, 97, 114]))).to.be.equal('Zm9vYmFy');
|
||||
});
|
||||
});
|
||||
|
||||
describe('encode base64 url', () => {
|
||||
it('can encode string to base64 url', () => {
|
||||
expect(encodeBase64Url('Man Ё𤭢')).to.be.equal('TWFuINCB8KStog')
|
||||
})
|
||||
expect(encodeBase64Url('Man Ё𤭢')).to.be.equal('TWFuINCB8KStog');
|
||||
});
|
||||
it('can encode Uint8Array to base64 url', () => {
|
||||
expect(encodeBase64Url(new Uint8Array([77, 97, 110, 32, 208, 129, 240, 164, 173, 162]))).to.be.equal('TWFuINCB8KStog')
|
||||
expect(encodeBase64Url(new Uint8Array([77, 97, 110, 32, 208, 129, 240, 164, 173]))).to.be.equal('TWFuINCB8KSt')
|
||||
expect(encodeBase64Url(new Uint8Array([77, 97, 110, 32, 208, 129, 240, 164, 173, 162, 63]))).to.be.equal('TWFuINCB8KStoj8')
|
||||
expect(encodeBase64Url(new Uint8Array([199, 239, 242]))).to.be.equal('x-_y')
|
||||
expect(encodeBase64Url(new Uint8Array([77, 97, 110, 32, 208, 129, 240, 164, 173, 162]))).to.be.equal('TWFuINCB8KStog');
|
||||
expect(encodeBase64Url(new Uint8Array([77, 97, 110, 32, 208, 129, 240, 164, 173]))).to.be.equal('TWFuINCB8KSt');
|
||||
expect(encodeBase64Url(new Uint8Array([77, 97, 110, 32, 208, 129, 240, 164, 173, 162, 63]))).to.be.equal('TWFuINCB8KStoj8');
|
||||
expect(encodeBase64Url(new Uint8Array([199, 239, 242]))).to.be.equal('x-_y');
|
||||
|
||||
// From https://datatracker.ietf.org/doc/html/rfc4648#section-10
|
||||
expect(encodeBase64Url(new Uint8Array([]))).to.be.equal('')
|
||||
expect(encodeBase64Url(new Uint8Array([102]))).to.be.equal('Zg')
|
||||
expect(encodeBase64Url(new Uint8Array([102, 111]))).to.be.equal('Zm8')
|
||||
expect(encodeBase64Url(new Uint8Array([102, 111, 111]))).to.be.equal('Zm9v')
|
||||
expect(encodeBase64Url(new Uint8Array([102, 111, 111, 98]))).to.be.equal('Zm9vYg')
|
||||
expect(encodeBase64Url(new Uint8Array([102, 111, 111, 98, 97]))).to.be.equal('Zm9vYmE')
|
||||
expect(encodeBase64Url(new Uint8Array([102, 111, 111, 98, 97, 114]))).to.be.equal('Zm9vYmFy')
|
||||
})
|
||||
expect(encodeBase64Url(new Uint8Array([]))).to.be.equal('');
|
||||
expect(encodeBase64Url(new Uint8Array([102]))).to.be.equal('Zg');
|
||||
expect(encodeBase64Url(new Uint8Array([102, 111]))).to.be.equal('Zm8');
|
||||
expect(encodeBase64Url(new Uint8Array([102, 111, 111]))).to.be.equal('Zm9v');
|
||||
expect(encodeBase64Url(new Uint8Array([102, 111, 111, 98]))).to.be.equal('Zm9vYg');
|
||||
expect(encodeBase64Url(new Uint8Array([102, 111, 111, 98, 97]))).to.be.equal('Zm9vYmE');
|
||||
expect(encodeBase64Url(new Uint8Array([102, 111, 111, 98, 97, 114]))).to.be.equal('Zm9vYmFy');
|
||||
});
|
||||
it('can encode Buffer to base64 url', () => {
|
||||
expect(encodeBase64Url(Buffer.from([77, 97, 110, 32, 208, 129, 240, 164, 173, 162]))).to.be.equal('TWFuINCB8KStog')
|
||||
expect(encodeBase64Url(Buffer.from([77, 97, 110, 32, 208, 129, 240, 164, 173]))).to.be.equal('TWFuINCB8KSt')
|
||||
expect(encodeBase64Url(Buffer.from([77, 97, 110, 32, 208, 129, 240, 164, 173, 162, 63]))).to.be.equal('TWFuINCB8KStoj8')
|
||||
expect(encodeBase64Url(Buffer.from([199, 239, 242]))).to.be.equal('x-_y')
|
||||
expect(encodeBase64Url(Buffer.from([77, 97, 110, 32, 208, 129, 240, 164, 173, 162]))).to.be.equal('TWFuINCB8KStog');
|
||||
expect(encodeBase64Url(Buffer.from([77, 97, 110, 32, 208, 129, 240, 164, 173]))).to.be.equal('TWFuINCB8KSt');
|
||||
expect(encodeBase64Url(Buffer.from([77, 97, 110, 32, 208, 129, 240, 164, 173, 162, 63]))).to.be.equal('TWFuINCB8KStoj8');
|
||||
expect(encodeBase64Url(Buffer.from([199, 239, 242]))).to.be.equal('x-_y');
|
||||
|
||||
// From https://datatracker.ietf.org/doc/html/rfc4648#section-10
|
||||
expect(encodeBase64Url(Buffer.from([]))).to.be.equal('')
|
||||
expect(encodeBase64Url(Buffer.from([102]))).to.be.equal('Zg')
|
||||
expect(encodeBase64Url(Buffer.from([102, 111]))).to.be.equal('Zm8')
|
||||
expect(encodeBase64Url(Buffer.from([102, 111, 111]))).to.be.equal('Zm9v')
|
||||
expect(encodeBase64Url(Buffer.from([102, 111, 111, 98]))).to.be.equal('Zm9vYg')
|
||||
expect(encodeBase64Url(Buffer.from([102, 111, 111, 98, 97]))).to.be.equal('Zm9vYmE')
|
||||
expect(encodeBase64Url(Buffer.from([102, 111, 111, 98, 97, 114]))).to.be.equal('Zm9vYmFy')
|
||||
})
|
||||
})
|
||||
expect(encodeBase64Url(Buffer.from([]))).to.be.equal('');
|
||||
expect(encodeBase64Url(Buffer.from([102]))).to.be.equal('Zg');
|
||||
expect(encodeBase64Url(Buffer.from([102, 111]))).to.be.equal('Zm8');
|
||||
expect(encodeBase64Url(Buffer.from([102, 111, 111]))).to.be.equal('Zm9v');
|
||||
expect(encodeBase64Url(Buffer.from([102, 111, 111, 98]))).to.be.equal('Zm9vYg');
|
||||
expect(encodeBase64Url(Buffer.from([102, 111, 111, 98, 97]))).to.be.equal('Zm9vYmE');
|
||||
expect(encodeBase64Url(Buffer.from([102, 111, 111, 98, 97, 114]))).to.be.equal('Zm9vYmFy');
|
||||
});
|
||||
});
|
||||
|
||||
describe('decode', () => {
|
||||
it('can dencode string to Uint8Array', () => {
|
||||
expect(new TextDecoder().decode(decode('TWFuINCB8KStog=='))).to.be.equal('Man Ё𤭢')
|
||||
expect(new TextDecoder().decode(decode('TWFuINCB8KSt'))).to.be.equal('Man Ё\ufffd')
|
||||
expect(new TextDecoder().decode(decode('TWFuINCB8KStoj8='))).to.be.equal('Man Ё𤭢?')
|
||||
})
|
||||
expect(new TextDecoder().decode(decode('TWFuINCB8KStog=='))).to.be.equal('Man Ё𤭢');
|
||||
expect(new TextDecoder().decode(decode('TWFuINCB8KSt'))).to.be.equal('Man Ё\ufffd');
|
||||
expect(new TextDecoder().decode(decode('TWFuINCB8KStoj8='))).to.be.equal('Man Ё𤭢?');
|
||||
});
|
||||
|
||||
it('will throw an error with invalid string', () => {
|
||||
expect(() => decode('=adw')).to.throw('Unable to parse base64 string.')
|
||||
expect(() => decode('a')).to.throw('Unable to parse base64 string.')
|
||||
expect(() => decode('$avs')).to.throw('Unable to parse base64 string.')
|
||||
expect(() => decode('~daw')).to.throw('Unable to parse base64 string.')
|
||||
})
|
||||
})
|
||||
expect(() => decode('=adw')).to.throw('Unable to parse base64 string.');
|
||||
expect(() => decode('a')).to.throw('Unable to parse base64 string.');
|
||||
expect(() => decode('$avs')).to.throw('Unable to parse base64 string.');
|
||||
expect(() => decode('~daw')).to.throw('Unable to parse base64 string.');
|
||||
});
|
||||
});
|
||||
|
||||
/** Old test */
|
||||
it('[utils] encode some bytes to base64', () => {
|
||||
expect(encode(new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]))).to.be.deep.equal('AQIDBAUGBwgJCg==')
|
||||
})
|
||||
expect(encode(new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]))).to.be.deep.equal('AQIDBAUGBwgJCg==');
|
||||
});
|
||||
|
||||
it('[utils] decode some base64 to bytes', () => {
|
||||
expect(decode('AQIDBAUGBwgJCg==')).to.be.deep.equal(new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]))
|
||||
})
|
||||
expect(decode('AQIDBAUGBwgJCg==')).to.be.deep.equal(new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]));
|
||||
});
|
||||
|
||||
it('[utils] encode/decode base64 roundtrip should work', () => {
|
||||
for (let i = 0; i < 10; i++) {
|
||||
const bytes: number[] = []
|
||||
const bytes: number[] = [];
|
||||
for (let i = 0; i < 10000; i++) {
|
||||
bytes.push(Math.floor(Math.random() * 256))
|
||||
bytes.push(Math.floor(Math.random() * 256));
|
||||
}
|
||||
const data = new Uint8Array(bytes)
|
||||
expect(decode(encode(data))).to.be.deep.equal(data)
|
||||
const data = new Uint8Array(bytes);
|
||||
expect(decode(encode(data))).to.be.deep.equal(data);
|
||||
}
|
||||
})
|
||||
})
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,27 +1,27 @@
|
||||
import { expect } from 'chai'
|
||||
import { afterEach, beforeEach, describe, it } from 'mocha'
|
||||
import sinon from 'sinon'
|
||||
import { LeakyBucket } from '../src/bucket.js'
|
||||
import { expect } from 'chai';
|
||||
import { afterEach, beforeEach, describe, it } from 'mocha';
|
||||
import sinon from 'sinon';
|
||||
import { LeakyBucket } from '../src/bucket.js';
|
||||
|
||||
async function promiseState(p: Promise<any>): Promise<string> {
|
||||
const t = {}
|
||||
const t = {};
|
||||
return await Promise.race([p, t]).then(
|
||||
(v) => (v === t ? 'pending' : 'fulfilled'),
|
||||
() => 'rejected',
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
describe('bucket.ts', () => {
|
||||
let clock: sinon.SinonFakeTimers
|
||||
let clock: sinon.SinonFakeTimers;
|
||||
|
||||
beforeEach(() => {
|
||||
clock = sinon.useFakeTimers()
|
||||
})
|
||||
clock = sinon.useFakeTimers();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
sinon.restore()
|
||||
clock.restore()
|
||||
})
|
||||
sinon.restore();
|
||||
clock.restore();
|
||||
});
|
||||
|
||||
describe('LeakyBucket function', () => {
|
||||
it('will return bucket with given options', () => {
|
||||
@@ -33,12 +33,12 @@ describe('bucket.ts', () => {
|
||||
someThingElse: {
|
||||
thing: 'else',
|
||||
},
|
||||
}
|
||||
const bucket = new LeakyBucket(options)
|
||||
expect(bucket.max).to.equal(options.max)
|
||||
expect(bucket.refillInterval).to.equal(options.refillInterval)
|
||||
expect(bucket.refillAmount).to.equal(options.refillAmount)
|
||||
})
|
||||
};
|
||||
const bucket = new LeakyBucket(options);
|
||||
expect(bucket.max).to.equal(options.max);
|
||||
expect(bucket.refillInterval).to.equal(options.refillInterval);
|
||||
expect(bucket.refillAmount).to.equal(options.refillAmount);
|
||||
});
|
||||
|
||||
it('will return bucket with refillAmount within max', () => {
|
||||
const options = {
|
||||
@@ -46,10 +46,10 @@ describe('bucket.ts', () => {
|
||||
refillInterval: 2002,
|
||||
refillAmount: 3003,
|
||||
tokens: 4004,
|
||||
}
|
||||
const bucket = new LeakyBucket(options)
|
||||
expect(bucket.refillAmount).to.equal(options.max)
|
||||
})
|
||||
};
|
||||
const bucket = new LeakyBucket(options);
|
||||
expect(bucket.refillAmount).to.equal(options.max);
|
||||
});
|
||||
|
||||
it('will return bucket with tokensState within max', () => {
|
||||
const options = {
|
||||
@@ -57,118 +57,118 @@ describe('bucket.ts', () => {
|
||||
refillInterval: 2002,
|
||||
refillAmount: 3003,
|
||||
tokens: 4004,
|
||||
}
|
||||
const bucket = new LeakyBucket(options)
|
||||
expect(bucket.refillAmount).to.equal(options.max)
|
||||
})
|
||||
};
|
||||
const bucket = new LeakyBucket(options);
|
||||
expect(bucket.refillAmount).to.equal(options.max);
|
||||
});
|
||||
|
||||
it('will return bucket with default property', () => {
|
||||
const bucket = new LeakyBucket()
|
||||
expect(bucket.max).equals(1)
|
||||
expect(bucket.refillInterval).equals(5000)
|
||||
expect(bucket.refillAmount).equals(1)
|
||||
expect(bucket.queue).to.deep.equal([])
|
||||
})
|
||||
const bucket = new LeakyBucket();
|
||||
expect(bucket.max).equals(1);
|
||||
expect(bucket.refillInterval).equals(5000);
|
||||
expect(bucket.refillAmount).equals(1);
|
||||
expect(bucket.queue).to.deep.equal([]);
|
||||
});
|
||||
|
||||
it('will acquire a request', async () => {
|
||||
const bucket = new LeakyBucket({
|
||||
max: 120,
|
||||
refillInterval: 60000,
|
||||
refillAmount: 120,
|
||||
})
|
||||
});
|
||||
|
||||
await bucket.acquire(true)
|
||||
expect(bucket.remaining).to.be.equal(119)
|
||||
expect(bucket.used).to.be.equal(1)
|
||||
})
|
||||
await bucket.acquire(true);
|
||||
expect(bucket.remaining).to.be.equal(119);
|
||||
expect(bucket.used).to.be.equal(1);
|
||||
});
|
||||
|
||||
it('will handle multiple requests at once', async () => {
|
||||
const bucket = new LeakyBucket({
|
||||
max: 120,
|
||||
refillInterval: 60000,
|
||||
refillAmount: 120,
|
||||
})
|
||||
});
|
||||
|
||||
for (let i = 0; i < 10; i++) {
|
||||
bucket.acquire()
|
||||
bucket.acquire();
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
it('will handle too many requests', async () => {
|
||||
const bucket = new LeakyBucket({
|
||||
max: 5,
|
||||
refillInterval: 10000,
|
||||
refillAmount: 5,
|
||||
})
|
||||
});
|
||||
|
||||
for (let i = 0; i < 10; i++) {
|
||||
bucket.acquire()
|
||||
bucket.acquire();
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
it('bucket refills are done properly', async () => {
|
||||
const bucket = new LeakyBucket({
|
||||
max: 2,
|
||||
refillInterval: 500,
|
||||
refillAmount: 2,
|
||||
})
|
||||
});
|
||||
|
||||
await bucket.acquire()
|
||||
expect(bucket.remaining).equals(1)
|
||||
expect(bucket.used).equals(1)
|
||||
await clock.tickAsync(1000)
|
||||
expect(bucket.remaining).equals(2)
|
||||
expect(bucket.used).equals(0)
|
||||
await bucket.acquire();
|
||||
expect(bucket.remaining).equals(1);
|
||||
expect(bucket.used).equals(1);
|
||||
await clock.tickAsync(1000);
|
||||
expect(bucket.remaining).equals(2);
|
||||
expect(bucket.used).equals(0);
|
||||
|
||||
await bucket.acquire()
|
||||
await clock.tickAsync(1000)
|
||||
})
|
||||
await bucket.acquire();
|
||||
await clock.tickAsync(1000);
|
||||
});
|
||||
|
||||
it('bucket refills when refill amount is < max', async () => {
|
||||
const bucket = new LeakyBucket({
|
||||
max: 3,
|
||||
refillInterval: 800,
|
||||
refillAmount: 1,
|
||||
})
|
||||
});
|
||||
|
||||
await bucket.acquire()
|
||||
await bucket.acquire()
|
||||
expect(bucket.remaining).equals(1)
|
||||
expect(bucket.used).equals(2)
|
||||
await clock.tickAsync(1000)
|
||||
expect(bucket.remaining).equals(2)
|
||||
expect(bucket.used).equals(1)
|
||||
await bucket.acquire();
|
||||
await bucket.acquire();
|
||||
expect(bucket.remaining).equals(1);
|
||||
expect(bucket.used).equals(2);
|
||||
await clock.tickAsync(1000);
|
||||
expect(bucket.remaining).equals(2);
|
||||
expect(bucket.used).equals(1);
|
||||
|
||||
await clock.tickAsync(2000)
|
||||
expect(bucket.remaining).equals(3)
|
||||
expect(bucket.used).equals(0)
|
||||
})
|
||||
await clock.tickAsync(2000);
|
||||
expect(bucket.remaining).equals(3);
|
||||
expect(bucket.used).equals(0);
|
||||
});
|
||||
|
||||
it('bucket refills when refill interval is slow', async () => {
|
||||
const bucket = new LeakyBucket({
|
||||
max: 1,
|
||||
refillInterval: 500,
|
||||
refillAmount: 1,
|
||||
})
|
||||
});
|
||||
|
||||
const acquired1 = bucket.acquire()
|
||||
const acquired2 = bucket.acquire()
|
||||
const acquired1 = bucket.acquire();
|
||||
const acquired2 = bucket.acquire();
|
||||
|
||||
// js event loop
|
||||
await (async () => {})()
|
||||
await (async () => {})();
|
||||
|
||||
expect(await promiseState(acquired1)).to.equal('fulfilled')
|
||||
expect(await promiseState(acquired2)).to.equal('pending')
|
||||
expect(await promiseState(acquired1)).to.equal('fulfilled');
|
||||
expect(await promiseState(acquired2)).to.equal('pending');
|
||||
|
||||
await clock.tickAsync(499)
|
||||
expect(await promiseState(acquired2)).to.equal('pending')
|
||||
await clock.tickAsync(499);
|
||||
expect(await promiseState(acquired2)).to.equal('pending');
|
||||
|
||||
await clock.tickAsync(1)
|
||||
expect(await promiseState(acquired2)).to.equal('fulfilled')
|
||||
await clock.tickAsync(1);
|
||||
expect(await promiseState(acquired2)).to.equal('fulfilled');
|
||||
|
||||
expect(bucket.remaining).equals(0)
|
||||
expect(bucket.used).equals(1)
|
||||
})
|
||||
expect(bucket.remaining).equals(0);
|
||||
expect(bucket.used).equals(1);
|
||||
});
|
||||
|
||||
describe('remaining', () => {
|
||||
it('should be 0 even used too many', () => {
|
||||
@@ -176,23 +176,23 @@ describe('bucket.ts', () => {
|
||||
max: 1,
|
||||
refillInterval: 500,
|
||||
refillAmount: 1,
|
||||
})
|
||||
});
|
||||
// max is < used
|
||||
bucket.used = 2
|
||||
expect(bucket.remaining).equals(0)
|
||||
})
|
||||
})
|
||||
bucket.used = 2;
|
||||
expect(bucket.remaining).equals(0);
|
||||
});
|
||||
});
|
||||
|
||||
it("Don't process queue twice", () => {
|
||||
const bucket = new LeakyBucket({
|
||||
max: 1,
|
||||
refillInterval: 500,
|
||||
refillAmount: 1,
|
||||
})
|
||||
});
|
||||
// fake processing
|
||||
bucket.processing = true
|
||||
bucket.processing = true;
|
||||
// request when already processing
|
||||
bucket.processQueue()
|
||||
})
|
||||
})
|
||||
})
|
||||
bucket.processQueue();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,34 +1,34 @@
|
||||
import { expect } from 'chai'
|
||||
import { describe, it } from 'mocha'
|
||||
import { EmbedsBuilder } from '../src/builders.js'
|
||||
import { expect } from 'chai';
|
||||
import { describe, it } from 'mocha';
|
||||
import { EmbedsBuilder } from '../src/builders.js';
|
||||
|
||||
describe('builders/embeds.ts', () => {
|
||||
it('should create a new blank embed JSON', () => {
|
||||
expect(new EmbedsBuilder().newEmbed()).to.eql([{}])
|
||||
})
|
||||
expect(new EmbedsBuilder().newEmbed()).to.eql([{}]);
|
||||
});
|
||||
|
||||
it('should set the author name in the embed JSON', () => {
|
||||
expect(new EmbedsBuilder().setAuthor('Author')).to.eql([{ author: { name: 'Author' } }])
|
||||
})
|
||||
expect(new EmbedsBuilder().setAuthor('Author')).to.eql([{ author: { name: 'Author' } }]);
|
||||
});
|
||||
|
||||
it('should set the color in the embed JSON', () => {
|
||||
expect(new EmbedsBuilder().setColor('#000000')).to.eql([{ color: 0 }])
|
||||
expect(new EmbedsBuilder().setColor('#21fa99')).to.eql([{ color: 2226841 }])
|
||||
expect(new EmbedsBuilder().setColor('#thisisnotacolor')).to.eql([{ color: 0 }])
|
||||
expect(new EmbedsBuilder().setColor(13530)).to.eql([{ color: 13530 }])
|
||||
})
|
||||
expect(new EmbedsBuilder().setColor('#000000')).to.eql([{ color: 0 }]);
|
||||
expect(new EmbedsBuilder().setColor('#21fa99')).to.eql([{ color: 2226841 }]);
|
||||
expect(new EmbedsBuilder().setColor('#thisisnotacolor')).to.eql([{ color: 0 }]);
|
||||
expect(new EmbedsBuilder().setColor(13530)).to.eql([{ color: 13530 }]);
|
||||
});
|
||||
|
||||
it('should set the description in the embed JSON', () => {
|
||||
expect(new EmbedsBuilder().setDescription('My Description')).to.eql([{ description: 'My Description' }])
|
||||
})
|
||||
expect(new EmbedsBuilder().setDescription('My Description')).to.eql([{ description: 'My Description' }]);
|
||||
});
|
||||
|
||||
it('should add a field in the embed JSON', () => {
|
||||
expect(new EmbedsBuilder().addField('firstname', 'firstvalue')).to.eql([
|
||||
{
|
||||
fields: [{ name: 'firstname', value: 'firstvalue', inline: undefined }],
|
||||
},
|
||||
])
|
||||
})
|
||||
]);
|
||||
});
|
||||
|
||||
it('should set the fields in the embed JSON', () => {
|
||||
expect(
|
||||
@@ -43,8 +43,8 @@ describe('builders/embeds.ts', () => {
|
||||
{ name: 'secondname', value: 'secondvalue', inline: true },
|
||||
],
|
||||
},
|
||||
])
|
||||
})
|
||||
]);
|
||||
});
|
||||
|
||||
it('should add the fields in the embed JSON', () => {
|
||||
expect(
|
||||
@@ -60,28 +60,28 @@ describe('builders/embeds.ts', () => {
|
||||
{ name: 'thirdname', value: 'thirdvalue', inline: true },
|
||||
],
|
||||
},
|
||||
])
|
||||
})
|
||||
]);
|
||||
});
|
||||
|
||||
it('should set the footer text in the embed JSON', () => {
|
||||
expect(new EmbedsBuilder().setFooter('footer text')).to.eql([{ footer: { text: 'footer text' } }])
|
||||
})
|
||||
expect(new EmbedsBuilder().setFooter('footer text')).to.eql([{ footer: { text: 'footer text' } }]);
|
||||
});
|
||||
|
||||
it('should set the set a random color in the embed JSON', () => {
|
||||
expect(new EmbedsBuilder().setRandomColor()[0]).to.haveOwnProperty('color')
|
||||
})
|
||||
expect(new EmbedsBuilder().setRandomColor()[0]).to.haveOwnProperty('color');
|
||||
});
|
||||
|
||||
it('should set the timestamp in the embed JSON', () => {
|
||||
const now = new Date()
|
||||
const now = new Date();
|
||||
|
||||
expect(new EmbedsBuilder().setTimestamp(now)).to.eql([{ timestamp: now.toISOString() }])
|
||||
})
|
||||
expect(new EmbedsBuilder().setTimestamp(now)).to.eql([{ timestamp: now.toISOString() }]);
|
||||
});
|
||||
|
||||
it('should set the title in the embed JSON', () => {
|
||||
expect(new EmbedsBuilder().setTitle('My Title')).to.eql([{ title: 'My Title' }])
|
||||
})
|
||||
expect(new EmbedsBuilder().setTitle('My Title')).to.eql([{ title: 'My Title' }]);
|
||||
});
|
||||
|
||||
it('should set the url in the embed JSON', () => {
|
||||
expect(new EmbedsBuilder().setUrl('https://google.com')).to.eql([{ url: 'https://google.com' }])
|
||||
})
|
||||
})
|
||||
expect(new EmbedsBuilder().setUrl('https://google.com')).to.eql([{ url: 'https://google.com' }]);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { expect } from 'chai'
|
||||
import { describe, it } from 'mocha'
|
||||
import { camelize, snakelize, snakeToCamelCase } from '../src/casing.js'
|
||||
import { expect } from 'chai';
|
||||
import { describe, it } from 'mocha';
|
||||
import { camelize, snakelize, snakeToCamelCase } from '../src/casing.js';
|
||||
|
||||
describe('casting.ts', () => {
|
||||
describe('camelize function', () => {
|
||||
@@ -15,8 +15,8 @@ describe('casting.ts', () => {
|
||||
testAxByCz: 'dummy_dx_ey_fz',
|
||||
testgxhyiz: 'dummyjxkylz',
|
||||
32: 'adw_dw',
|
||||
})
|
||||
})
|
||||
});
|
||||
});
|
||||
|
||||
it('will convert array of snake case object to camel case object', () => {
|
||||
expect(
|
||||
@@ -35,15 +35,15 @@ describe('casting.ts', () => {
|
||||
{
|
||||
testGxHyIz: 'dummy_jx_ky_lz',
|
||||
},
|
||||
])
|
||||
})
|
||||
]);
|
||||
});
|
||||
|
||||
describe('snakeToCamelCase function', () => {
|
||||
it('will convert string snake case to camel case', () => {
|
||||
expect(snakeToCamelCase('sd_sd')).to.equal('sdSd')
|
||||
})
|
||||
})
|
||||
})
|
||||
expect(snakeToCamelCase('sd_sd')).to.equal('sdSd');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('snakelize function', () => {
|
||||
it('will convert snake case object to camel case object', () => {
|
||||
@@ -57,8 +57,8 @@ describe('casting.ts', () => {
|
||||
test_ax_by_cz: 'dummy_dx_ey_fz',
|
||||
testgxhyiz: 'dummyjxkylz',
|
||||
32: 'adw_dw',
|
||||
})
|
||||
})
|
||||
});
|
||||
});
|
||||
|
||||
it('will convert array of snake case object to camel case object', () => {
|
||||
expect(
|
||||
@@ -77,13 +77,13 @@ describe('casting.ts', () => {
|
||||
{
|
||||
test_gx_hy_iz: 'dummy_jx_ky_lz',
|
||||
},
|
||||
])
|
||||
})
|
||||
]);
|
||||
});
|
||||
|
||||
describe('snakeToCamelCase function', () => {
|
||||
it('will convert string snake case to camel case', () => {
|
||||
expect(snakeToCamelCase('sd_sd')).to.equal('sdSd')
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
expect(snakeToCamelCase('sd_sd')).to.equal('sdSd');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,75 +1,75 @@
|
||||
import { expect } from 'chai'
|
||||
import { afterEach, beforeEach, describe, it } from 'mocha'
|
||||
import sinon from 'sinon'
|
||||
import { Collection } from '../src/Collection.js'
|
||||
import { expect } from 'chai';
|
||||
import { afterEach, beforeEach, describe, it } from 'mocha';
|
||||
import sinon from 'sinon';
|
||||
import { Collection } from '../src/Collection.js';
|
||||
|
||||
describe('collection.ts', () => {
|
||||
afterEach(() => {
|
||||
sinon.restore()
|
||||
})
|
||||
sinon.restore();
|
||||
});
|
||||
|
||||
describe('Collection class', () => {
|
||||
let collection: Collection<any, any>
|
||||
let collection: Collection<any, any>;
|
||||
|
||||
beforeEach(() => {
|
||||
collection = new Collection([
|
||||
['best', 'tri'],
|
||||
['proficient', 'yui'],
|
||||
])
|
||||
})
|
||||
]);
|
||||
});
|
||||
|
||||
describe('.array() method', () => {
|
||||
it('will return values as array', () => {
|
||||
expect(collection.array()).to.be.deep.equal(['tri', 'yui'])
|
||||
})
|
||||
})
|
||||
expect(collection.array()).to.be.deep.equal(['tri', 'yui']);
|
||||
});
|
||||
});
|
||||
|
||||
describe('.random() method', () => {
|
||||
it('will get a random value', () => {
|
||||
expect(collection.random() ?? '').to.be.oneOf(['tri', 'yui'])
|
||||
expect(new Collection().random()).to.be.undefined
|
||||
})
|
||||
})
|
||||
expect(collection.random() ?? '').to.be.oneOf(['tri', 'yui']);
|
||||
expect(new Collection().random()).to.be.undefined;
|
||||
});
|
||||
});
|
||||
|
||||
describe('.set() method', () => {
|
||||
describe('without maxSize', () => {
|
||||
it('will set a value', () => {
|
||||
collection.set('best developer', 'triformine')
|
||||
collection.set('best developer', 'triformine');
|
||||
|
||||
expect(collection.size).to.be.equal(3)
|
||||
expect(collection.get('best developer')).to.be.equal('triformine')
|
||||
})
|
||||
})
|
||||
expect(collection.size).to.be.equal(3);
|
||||
expect(collection.get('best developer')).to.be.equal('triformine');
|
||||
});
|
||||
});
|
||||
|
||||
describe('with maxSize', () => {
|
||||
const maxSize = 2
|
||||
const maxSize = 2;
|
||||
|
||||
beforeEach(() => {
|
||||
collection = new Collection([], {
|
||||
maxSize,
|
||||
})
|
||||
})
|
||||
});
|
||||
});
|
||||
|
||||
it('will set a value when not over max size', () => {
|
||||
collection.set('foo', 'bar')
|
||||
collection.set('me', 'you')
|
||||
collection.set('foo', 'bar');
|
||||
collection.set('me', 'you');
|
||||
|
||||
expect(collection.size).to.be.equal(2)
|
||||
})
|
||||
expect(collection.size).to.be.equal(2);
|
||||
});
|
||||
|
||||
it('will not set a value when over max size', () => {
|
||||
collection.set('foo', 'bar')
|
||||
collection.set('me', 'you')
|
||||
expect(collection.size).to.be.equal(2)
|
||||
collection.set('foo', 'bar');
|
||||
collection.set('me', 'you');
|
||||
expect(collection.size).to.be.equal(2);
|
||||
|
||||
collection.set('this', 'not')
|
||||
expect(collection.size).to.be.equal(2)
|
||||
})
|
||||
})
|
||||
})
|
||||
collection.set('this', 'not');
|
||||
expect(collection.size).to.be.equal(2);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('.forceSet() method', () => {
|
||||
const maxSize = 2
|
||||
const maxSize = 2;
|
||||
|
||||
beforeEach(() => {
|
||||
collection = new Collection(
|
||||
@@ -78,85 +78,85 @@ describe('collection.ts', () => {
|
||||
['me', 'you'],
|
||||
],
|
||||
{ maxSize },
|
||||
)
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it('will ignore maxSize and set a value ', () => {
|
||||
collection.forceSet('this', 'not')
|
||||
collection.forceSet('this', 'not');
|
||||
|
||||
expect(collection.size).to.be.equal(3)
|
||||
})
|
||||
})
|
||||
expect(collection.size).to.be.equal(3);
|
||||
});
|
||||
});
|
||||
|
||||
describe('.first() method', () => {
|
||||
it('will get the value of the first element', () => {
|
||||
expect(collection.first()).to.be.equal('tri')
|
||||
})
|
||||
})
|
||||
expect(collection.first()).to.be.equal('tri');
|
||||
});
|
||||
});
|
||||
|
||||
describe('.last() method', () => {
|
||||
it('get the value of the last element', () => {
|
||||
expect(collection.last()).to.be.equal('yui')
|
||||
})
|
||||
})
|
||||
expect(collection.last()).to.be.equal('yui');
|
||||
});
|
||||
});
|
||||
|
||||
const testCollection = new Collection([
|
||||
['a', 1],
|
||||
['b', 2],
|
||||
['c', 3],
|
||||
])
|
||||
]);
|
||||
|
||||
describe('.find() method', () => {
|
||||
it('will find value by value', () => {
|
||||
expect(collection.find((v) => v === 'tri')).to.be.equal('tri')
|
||||
expect(collection.find((v) => v === 'skillz')).to.be.undefined
|
||||
})
|
||||
expect(collection.find((v) => v === 'tri')).to.be.equal('tri');
|
||||
expect(collection.find((v) => v === 'skillz')).to.be.undefined;
|
||||
});
|
||||
|
||||
it('will find value by key', () => {
|
||||
expect(collection.find((_v, k) => k === 'proficient')).to.be.equal('yui')
|
||||
expect(collection.find((_v, k) => k === 'skillz')).to.be.undefined
|
||||
})
|
||||
})
|
||||
expect(collection.find((_v, k) => k === 'proficient')).to.be.equal('yui');
|
||||
expect(collection.find((_v, k) => k === 'skillz')).to.be.undefined;
|
||||
});
|
||||
});
|
||||
|
||||
describe('.filter() method', () => {
|
||||
it('will filter by key', () => {
|
||||
expect(collection.filter((v) => v === 'yui').array()).to.deep.equal(['yui'])
|
||||
expect(collection.filter((v) => v === 'skillz').array()).to.deep.equal([])
|
||||
})
|
||||
expect(collection.filter((v) => v === 'yui').array()).to.deep.equal(['yui']);
|
||||
expect(collection.filter((v) => v === 'skillz').array()).to.deep.equal([]);
|
||||
});
|
||||
it('will filter by key', () => {
|
||||
expect(collection.filter((_v, k) => k === 'best').array()).to.deep.equal(['tri'])
|
||||
expect(collection.filter((_v, k) => k === 'skillz').array()).to.deep.equal([])
|
||||
})
|
||||
})
|
||||
expect(collection.filter((_v, k) => k === 'best').array()).to.deep.equal(['tri']);
|
||||
expect(collection.filter((_v, k) => k === 'skillz').array()).to.deep.equal([]);
|
||||
});
|
||||
});
|
||||
|
||||
it('map', () => {
|
||||
expect(testCollection.map((k, v) => `${v}${k}`)).to.be.deep.equal(['a1', 'b2', 'c3'])
|
||||
})
|
||||
expect(testCollection.map((k, v) => `${v}${k}`)).to.be.deep.equal(['a1', 'b2', 'c3']);
|
||||
});
|
||||
|
||||
it('some', () => {
|
||||
expect(testCollection.some((v, _) => v === 1)).to.be.equal(true)
|
||||
expect(testCollection.some((v, _) => v === 4)).to.be.equal(false)
|
||||
})
|
||||
expect(testCollection.some((v, _) => v === 1)).to.be.equal(true);
|
||||
expect(testCollection.some((v, _) => v === 4)).to.be.equal(false);
|
||||
});
|
||||
|
||||
it('every', () => {
|
||||
expect(testCollection.every((v, _) => v !== 0)).to.be.equal(true)
|
||||
expect(testCollection.every((v, _) => v === 1)).to.be.equal(false)
|
||||
})
|
||||
expect(testCollection.every((v, _) => v !== 0)).to.be.equal(true);
|
||||
expect(testCollection.every((v, _) => v === 1)).to.be.equal(false);
|
||||
});
|
||||
|
||||
it('reduce', () => {
|
||||
expect(testCollection.reduce((acc, val) => acc + val, 0)).to.be.equal(6)
|
||||
})
|
||||
expect(testCollection.reduce((acc, val) => acc + val, 0)).to.be.equal(6);
|
||||
});
|
||||
|
||||
describe('sweeper', () => {
|
||||
let clock: sinon.SinonFakeTimers
|
||||
let clock: sinon.SinonFakeTimers;
|
||||
|
||||
beforeEach(() => {
|
||||
clock = sinon.useFakeTimers()
|
||||
})
|
||||
clock = sinon.useFakeTimers();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
clock.restore()
|
||||
})
|
||||
clock.restore();
|
||||
});
|
||||
|
||||
it('start sweeper', async () => {
|
||||
const sweeperCollection = new Collection(
|
||||
@@ -170,49 +170,49 @@ describe('collection.ts', () => {
|
||||
interval: 50,
|
||||
},
|
||||
},
|
||||
)
|
||||
);
|
||||
|
||||
try {
|
||||
await clock.tickAsync(49)
|
||||
expect(sweeperCollection.size).to.be.equal(2)
|
||||
await clock.tickAsync(1)
|
||||
expect(sweeperCollection.size).to.be.equal(1)
|
||||
await clock.tickAsync(49);
|
||||
expect(sweeperCollection.size).to.be.equal(2);
|
||||
await clock.tickAsync(1);
|
||||
expect(sweeperCollection.size).to.be.equal(1);
|
||||
} catch (err) {
|
||||
sweeperCollection.stopSweeper()
|
||||
sweeperCollection.stopSweeper();
|
||||
|
||||
throw err
|
||||
throw err;
|
||||
}
|
||||
|
||||
sweeperCollection.stopSweeper()
|
||||
})
|
||||
sweeperCollection.stopSweeper();
|
||||
});
|
||||
|
||||
describe('.changeSweeperInterval() method', () => {
|
||||
it('will call startSweeper with new interval', () => {
|
||||
collection.startSweeper({ filter: () => false, interval: 1000 })
|
||||
collection.changeSweeperInterval(20000)
|
||||
expect(collection.sweeper?.interval).to.equal(20000)
|
||||
})
|
||||
collection.startSweeper({ filter: () => false, interval: 1000 });
|
||||
collection.changeSweeperInterval(20000);
|
||||
expect(collection.sweeper?.interval).to.equal(20000);
|
||||
});
|
||||
|
||||
it('will not startsweeper if not started', () => {
|
||||
collection.changeSweeperInterval(20000)
|
||||
expect(collection.sweeper).to.undefined
|
||||
})
|
||||
})
|
||||
collection.changeSweeperInterval(20000);
|
||||
expect(collection.sweeper).to.undefined;
|
||||
});
|
||||
});
|
||||
|
||||
describe('.changeSweeperFilter() method', () => {
|
||||
it('will call startSweeper with new interval', () => {
|
||||
const newFilter = () => true
|
||||
collection.startSweeper({ filter: () => false, interval: 1000 })
|
||||
collection.changeSweeperFilter(newFilter)
|
||||
expect(collection.sweeper?.filter).to.equal(newFilter)
|
||||
})
|
||||
const newFilter = () => true;
|
||||
collection.startSweeper({ filter: () => false, interval: 1000 });
|
||||
collection.changeSweeperFilter(newFilter);
|
||||
expect(collection.sweeper?.filter).to.equal(newFilter);
|
||||
});
|
||||
|
||||
it('will not startsweeper if not started', () => {
|
||||
const newFilter = () => true
|
||||
collection.changeSweeperFilter(newFilter)
|
||||
expect(collection.sweeper).to.undefined
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
const newFilter = () => true;
|
||||
collection.changeSweeperFilter(newFilter);
|
||||
expect(collection.sweeper).to.undefined;
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,38 +1,38 @@
|
||||
import { expect } from 'chai'
|
||||
import { describe, it } from 'mocha'
|
||||
import * as colors from '../src/colors.js'
|
||||
import { expect } from 'chai';
|
||||
import { describe, it } from 'mocha';
|
||||
import * as colors from '../src/colors.js';
|
||||
|
||||
const arrayOfColors = ['black', 'red', 'green', 'yellow', 'blue', 'magenta', 'cyan', 'white']
|
||||
const arrayOfColors = ['black', 'red', 'green', 'yellow', 'blue', 'magenta', 'cyan', 'white'];
|
||||
|
||||
describe('Colors', () => {
|
||||
it(`Color functions will return colored word`, () => {
|
||||
for (const [index, color] of arrayOfColors.entries()) {
|
||||
const value = (colors[color as keyof typeof colors] as typeof colors.black)('testWord')
|
||||
expect(value).equals(`\x1B[${30 + index}mtestWord\x1B[39m`)
|
||||
const value = (colors[color as keyof typeof colors] as typeof colors.black)('testWord');
|
||||
expect(value).equals(`\x1B[${30 + index}mtestWord\x1B[39m`);
|
||||
|
||||
const brightName = `bright${color.slice(0, 1).toUpperCase()}${color.slice(1)}`
|
||||
const brightValue = (colors[brightName as keyof typeof colors] as typeof colors.black)('testWord')
|
||||
expect(brightValue).to.equal(`\x1B[${90 + index}mtestWord\x1B[39m`)
|
||||
const brightName = `bright${color.slice(0, 1).toUpperCase()}${color.slice(1)}`;
|
||||
const brightValue = (colors[brightName as keyof typeof colors] as typeof colors.black)('testWord');
|
||||
expect(brightValue).to.equal(`\x1B[${90 + index}mtestWord\x1B[39m`);
|
||||
|
||||
const bgName = `bg${color.slice(0, 1).toUpperCase()}${color.slice(1)}`
|
||||
const bgValue = (colors[bgName as keyof typeof colors] as typeof colors.black)('testWord')
|
||||
expect(bgValue).to.equal(`\x1B[${40 + index}mtestWord\x1B[49m`, `Color ${color} has failed .`)
|
||||
const bgName = `bg${color.slice(0, 1).toUpperCase()}${color.slice(1)}`;
|
||||
const bgValue = (colors[bgName as keyof typeof colors] as typeof colors.black)('testWord');
|
||||
expect(bgValue).to.equal(`\x1B[${40 + index}mtestWord\x1B[49m`, `Color ${color} has failed .`);
|
||||
|
||||
const bgBrightName = `bgBright${color.slice(0, 1).toUpperCase()}${color.slice(1)}`
|
||||
const bgBrightValue = (colors[bgBrightName as keyof typeof colors] as typeof colors.black)('testWord')
|
||||
expect(bgBrightValue).to.equal(`\x1B[${100 + index}mtestWord\x1B[49m`)
|
||||
const bgBrightName = `bgBright${color.slice(0, 1).toUpperCase()}${color.slice(1)}`;
|
||||
const bgBrightValue = (colors[bgBrightName as keyof typeof colors] as typeof colors.black)('testWord');
|
||||
expect(bgBrightValue).to.equal(`\x1B[${100 + index}mtestWord\x1B[49m`);
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
it('Will return grey colored word', () => {
|
||||
expect(colors.gray('testWord')).to.equal(`\x1B[${90}mtestWord\x1B[39m`)
|
||||
})
|
||||
expect(colors.gray('testWord')).to.equal(`\x1B[${90}mtestWord\x1B[39m`);
|
||||
});
|
||||
|
||||
it('Set and get colors enabled value', () => {
|
||||
expect(colors.getColorEnabled()).to.equal(true)
|
||||
colors.setColorEnabled(false)
|
||||
expect(colors.getColorEnabled()).to.equal(false)
|
||||
colors.setColorEnabled(true)
|
||||
expect(colors.getColorEnabled()).to.equal(true)
|
||||
})
|
||||
})
|
||||
expect(colors.getColorEnabled()).to.equal(true);
|
||||
colors.setColorEnabled(false);
|
||||
expect(colors.getColorEnabled()).to.equal(false);
|
||||
colors.setColorEnabled(true);
|
||||
expect(colors.getColorEnabled()).to.equal(true);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,26 +1,26 @@
|
||||
import { expect } from 'chai'
|
||||
import { describe, it } from 'mocha'
|
||||
import { iconBigintToHash, iconHashToBigInt } from '../src/hash.js'
|
||||
import { expect } from 'chai';
|
||||
import { describe, it } from 'mocha';
|
||||
import { iconBigintToHash, iconHashToBigInt } from '../src/hash.js';
|
||||
|
||||
const iconHash = '4bbb271a13f7195031adcc06a2d867ce'
|
||||
const iconBigInt = 3843769888406823508519992434416504301518n
|
||||
const a_iconHash = 'a_4bbb271a13f7195031adcc06a2d867ce'
|
||||
const a_iconBigInt = 3503487521485885045056617826984736090062n
|
||||
const iconHash = '4bbb271a13f7195031adcc06a2d867ce';
|
||||
const iconBigInt = 3843769888406823508519992434416504301518n;
|
||||
const a_iconHash = 'a_4bbb271a13f7195031adcc06a2d867ce';
|
||||
const a_iconBigInt = 3503487521485885045056617826984736090062n;
|
||||
|
||||
describe('hash.ts', () => {
|
||||
it('[utils] icon hash to bigint', () => {
|
||||
expect(iconHashToBigInt(iconHash)).to.be.equal(iconBigInt)
|
||||
})
|
||||
expect(iconHashToBigInt(iconHash)).to.be.equal(iconBigInt);
|
||||
});
|
||||
|
||||
it('[utils] icon bigint to hash', () => {
|
||||
expect(iconBigintToHash(iconBigInt)).to.be.equal(iconHash)
|
||||
})
|
||||
expect(iconBigintToHash(iconBigInt)).to.be.equal(iconHash);
|
||||
});
|
||||
|
||||
it('[utils] icon hash to bigint a_ (animated)', () => {
|
||||
expect(iconHashToBigInt(a_iconHash)).to.be.equal(a_iconBigInt)
|
||||
})
|
||||
expect(iconHashToBigInt(a_iconHash)).to.be.equal(a_iconBigInt);
|
||||
});
|
||||
|
||||
it('[utils] icon bigint to hash a_ (animated)', () => {
|
||||
expect(iconBigintToHash(a_iconBigInt)).to.be.equal(a_iconHash)
|
||||
})
|
||||
})
|
||||
expect(iconBigintToHash(a_iconBigInt)).to.be.equal(a_iconHash);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { expect } from 'chai'
|
||||
import { describe, it } from 'mocha'
|
||||
import { expect } from 'chai';
|
||||
import { describe, it } from 'mocha';
|
||||
import {
|
||||
avatarUrl,
|
||||
defaultAvatarUrl,
|
||||
@@ -10,196 +10,196 @@ import {
|
||||
guildBannerUrl,
|
||||
guildIconUrl,
|
||||
guildSplashUrl,
|
||||
} from '../src/images.js'
|
||||
} from '../src/images.js';
|
||||
|
||||
describe('images.ts', () => {
|
||||
describe('formatImageUrl function', () => {
|
||||
it('will return formated url with default size 128 and webp', () => {
|
||||
expect(formatImageUrl('https://skillz.is.pro/image')).to.be.equal('https://skillz.is.pro/image.webp?size=128')
|
||||
})
|
||||
expect(formatImageUrl('https://skillz.is.pro/image')).to.be.equal('https://skillz.is.pro/image.webp?size=128');
|
||||
});
|
||||
|
||||
it('will return formated url with given size', () => {
|
||||
expect(formatImageUrl('https://skillz.is.pro/image', 1024)).to.be.equal('https://skillz.is.pro/image.webp?size=1024')
|
||||
})
|
||||
expect(formatImageUrl('https://skillz.is.pro/image', 1024)).to.be.equal('https://skillz.is.pro/image.webp?size=1024');
|
||||
});
|
||||
|
||||
it('will return formated url with given size and format', () => {
|
||||
expect(formatImageUrl('https://skillz.is.pro/image', 1024, 'gif')).to.be.equal('https://skillz.is.pro/image.gif?size=1024')
|
||||
})
|
||||
expect(formatImageUrl('https://skillz.is.pro/image', 1024, 'gif')).to.be.equal('https://skillz.is.pro/image.gif?size=1024');
|
||||
});
|
||||
|
||||
it('will return formated url with default size and format', () => {
|
||||
expect(formatImageUrl('https://skillz.is.pro/image', undefined, 'gif')).to.be.equal('https://skillz.is.pro/image.gif?size=128')
|
||||
})
|
||||
expect(formatImageUrl('https://skillz.is.pro/image', undefined, 'gif')).to.be.equal('https://skillz.is.pro/image.gif?size=128');
|
||||
});
|
||||
|
||||
describe('without format', () => {
|
||||
it('will use gif if a_ is found', () => {
|
||||
expect(formatImageUrl('https://cdn.discordapp.com/avatars/568505543511259840/a_482491d6dcf12e12746ccd3148f0c646')).to.be.equal(
|
||||
'https://cdn.discordapp.com/avatars/568505543511259840/a_482491d6dcf12e12746ccd3148f0c646.gif?size=128',
|
||||
)
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it('will use webp if no a_ is found', () => {
|
||||
expect(formatImageUrl('https://cdn.discordapp.com/avatars/568505543511259840/482491d6dcf12e12746ccd3148f0c646')).to.be.equal(
|
||||
'https://cdn.discordapp.com/avatars/568505543511259840/482491d6dcf12e12746ccd3148f0c646.webp?size=128',
|
||||
)
|
||||
})
|
||||
})
|
||||
})
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('emojiUrl function', () => {
|
||||
it('can format emoji url with png as default ext', () => {
|
||||
expect(emojiUrl('1079823706743918622')).to.equal('https://cdn.discordapp.com/emojis/1079823706743918622.png')
|
||||
})
|
||||
expect(emojiUrl('1079823706743918622')).to.equal('https://cdn.discordapp.com/emojis/1079823706743918622.png');
|
||||
});
|
||||
|
||||
it('can format emoji url with gif as ext', () => {
|
||||
expect(emojiUrl('1079584570661404724', true)).to.equal('https://cdn.discordapp.com/emojis/1079584570661404724.gif')
|
||||
})
|
||||
expect(emojiUrl('1079584570661404724', true)).to.equal('https://cdn.discordapp.com/emojis/1079584570661404724.gif');
|
||||
});
|
||||
|
||||
it('can format emoji url with webp as ext', () => {
|
||||
expect(emojiUrl('1079823706743918622', false, 'webp')).to.equal('https://cdn.discordapp.com/emojis/1079823706743918622.webp')
|
||||
})
|
||||
expect(emojiUrl('1079823706743918622', false, 'webp')).to.equal('https://cdn.discordapp.com/emojis/1079823706743918622.webp');
|
||||
});
|
||||
|
||||
it('can format emoji url with webp as ext when animated', () => {
|
||||
expect(emojiUrl('1079823706743918622', true, 'webp')).to.equal('https://cdn.discordapp.com/emojis/1079823706743918622.webp?animated=true')
|
||||
})
|
||||
})
|
||||
expect(emojiUrl('1079823706743918622', true, 'webp')).to.equal('https://cdn.discordapp.com/emojis/1079823706743918622.webp?animated=true');
|
||||
});
|
||||
});
|
||||
|
||||
describe('avatarUrl function', () => {
|
||||
it('will return the url for given avatar icon hash', () => {
|
||||
expect(avatarUrl('207324334904049664', 'db26a6fb924c985f66b79364cf5797b7')).to.equal(
|
||||
'https://cdn.discordapp.com/avatars/207324334904049664/db26a6fb924c985f66b79364cf5797b7.webp?size=128',
|
||||
)
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it('will return the url for given avatar icon bigint', () => {
|
||||
expect(avatarUrl('207324334904049664', 4034407661299384404326332419647968090039n)).to.equal(
|
||||
'https://cdn.discordapp.com/avatars/207324334904049664/db26a6fb924c985f66b79364cf5797b7.webp?size=128',
|
||||
)
|
||||
})
|
||||
})
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('defaultAvatarUrl function', () => {
|
||||
it('will return the url for default avatar', () => {
|
||||
expect(defaultAvatarUrl('207324334904049664', '9130')).to.equal('https://cdn.discordapp.com/embed/avatars/0.png')
|
||||
})
|
||||
})
|
||||
expect(defaultAvatarUrl('207324334904049664', '9130')).to.equal('https://cdn.discordapp.com/embed/avatars/0.png');
|
||||
});
|
||||
});
|
||||
|
||||
describe('displayAvatarUrl function', () => {
|
||||
it('will return the url for given avatar icon hash', () => {
|
||||
expect(displayAvatarUrl('207324334904049664', '9130', 'db26a6fb924c985f66b79364cf5797b7')).to.equal(
|
||||
'https://cdn.discordapp.com/avatars/207324334904049664/db26a6fb924c985f66b79364cf5797b7.webp?size=128',
|
||||
)
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it('will return the url for given avatar icon bigint', () => {
|
||||
expect(displayAvatarUrl('207324334904049664', '9130', 4034407661299384404326332419647968090039n)).to.equal(
|
||||
'https://cdn.discordapp.com/avatars/207324334904049664/db26a6fb924c985f66b79364cf5797b7.webp?size=128',
|
||||
)
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it('will return the url for default avatar', () => {
|
||||
expect(displayAvatarUrl('207324334904049664', '9130', undefined)).to.equal('https://cdn.discordapp.com/embed/avatars/0.png')
|
||||
})
|
||||
})
|
||||
expect(displayAvatarUrl('207324334904049664', '9130', undefined)).to.equal('https://cdn.discordapp.com/embed/avatars/0.png');
|
||||
});
|
||||
});
|
||||
|
||||
describe('guildBannerUrl function', () => {
|
||||
it("will return the url for given guild's banner's icon hash", () => {
|
||||
expect(guildBannerUrl('785384884197392384', { banner: '2fc0f64acd7a326e0c93c123db02eb1d' })).to.equal(
|
||||
'https://cdn.discordapp.com/banners/785384884197392384/2fc0f64acd7a326e0c93c123db02eb1d.webp?size=128',
|
||||
)
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it("will return the url for given guild's banner's icon big int", () => {
|
||||
expect(guildBannerUrl('785384884197392384', { banner: 3806581668328291509506503737571885116189n })).to.equal(
|
||||
'https://cdn.discordapp.com/banners/785384884197392384/2fc0f64acd7a326e0c93c123db02eb1d.webp?size=128',
|
||||
)
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it("will return the url for given guild's banner with format", () => {
|
||||
expect(guildBannerUrl('785384884197392384', { banner: '2fc0f64acd7a326e0c93c123db02eb1d', format: 'png' })).to.equal(
|
||||
'https://cdn.discordapp.com/banners/785384884197392384/2fc0f64acd7a326e0c93c123db02eb1d.png?size=128',
|
||||
)
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it("will return the url for given guild's banner with size", () => {
|
||||
expect(guildBannerUrl('785384884197392384', { banner: '2fc0f64acd7a326e0c93c123db02eb1d', size: 256 })).to.equal(
|
||||
'https://cdn.discordapp.com/banners/785384884197392384/2fc0f64acd7a326e0c93c123db02eb1d.webp?size=256',
|
||||
)
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it('will return undefined without given banner', () => {
|
||||
expect(guildBannerUrl('785384884197392384', {})).to.equal(undefined)
|
||||
})
|
||||
})
|
||||
expect(guildBannerUrl('785384884197392384', {})).to.equal(undefined);
|
||||
});
|
||||
});
|
||||
|
||||
describe('guildIconUrl function', () => {
|
||||
it("will return the url for given guild's icon's icon hash", () => {
|
||||
expect(guildIconUrl('785384884197392384', '7cb67c989d54d824239b2bb4270955b1')).to.equal(
|
||||
'https://cdn.discordapp.com/icons/785384884197392384/7cb67c989d54d824239b2bb4270955b1.webp?size=128',
|
||||
)
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it("will return the url for given guild's icon's icon big int", () => {
|
||||
expect(guildIconUrl('785384884197392384', 3908877832746069276949504774836813649329n)).to.equal(
|
||||
'https://cdn.discordapp.com/icons/785384884197392384/7cb67c989d54d824239b2bb4270955b1.webp?size=128',
|
||||
)
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it("will return the url for given guild's icon with format", () => {
|
||||
expect(guildIconUrl('785384884197392384', '7cb67c989d54d824239b2bb4270955b1', { format: 'png' })).to.equal(
|
||||
'https://cdn.discordapp.com/icons/785384884197392384/7cb67c989d54d824239b2bb4270955b1.png?size=128',
|
||||
)
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it("will return the url for given guild's icon with size", () => {
|
||||
expect(guildIconUrl('785384884197392384', '7cb67c989d54d824239b2bb4270955b1', { size: 256 })).to.equal(
|
||||
'https://cdn.discordapp.com/icons/785384884197392384/7cb67c989d54d824239b2bb4270955b1.webp?size=256',
|
||||
)
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it('will return undefined without given icon', () => {
|
||||
expect(guildIconUrl('785384884197392384', undefined)).to.equal(undefined)
|
||||
})
|
||||
})
|
||||
expect(guildIconUrl('785384884197392384', undefined)).to.equal(undefined);
|
||||
});
|
||||
});
|
||||
|
||||
describe('guildSplashUrl function', () => {
|
||||
it("will return the url for given guild's splash's icon big hash", () => {
|
||||
expect(guildSplashUrl('785384884197392384', '207961ff6c41f119874e10efc602858c')).to.equal(
|
||||
'https://cdn.discordapp.com/splashes/785384884197392384/207961ff6c41f119874e10efc602858c.webp?size=128',
|
||||
)
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it("will return the url for given guild's splash's icon big int", () => {
|
||||
expect(guildSplashUrl('785384884197392384', 3786271587545740215322752847582515594636n)).to.equal(
|
||||
'https://cdn.discordapp.com/splashes/785384884197392384/207961ff6c41f119874e10efc602858c.webp?size=128',
|
||||
)
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it("will return the url for given guild's splash with format", () => {
|
||||
expect(guildSplashUrl('785384884197392384', '207961ff6c41f119874e10efc602858c', { format: 'png' })).to.equal(
|
||||
'https://cdn.discordapp.com/splashes/785384884197392384/207961ff6c41f119874e10efc602858c.png?size=128',
|
||||
)
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it("will return the url for given guild's splash with size", () => {
|
||||
expect(guildSplashUrl('785384884197392384', '207961ff6c41f119874e10efc602858c', { size: 2048 })).to.equal(
|
||||
'https://cdn.discordapp.com/splashes/785384884197392384/207961ff6c41f119874e10efc602858c.webp?size=2048',
|
||||
)
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it('will return undefined without given icon', () => {
|
||||
expect(guildSplashUrl('785384884197392384', undefined)).to.equal(undefined)
|
||||
})
|
||||
})
|
||||
expect(guildSplashUrl('785384884197392384', undefined)).to.equal(undefined);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getWidgetImageUrl function', () => {
|
||||
it("will return the url for given guild's widget", () => {
|
||||
expect(getWidgetImageUrl('785384884197392384')).to.equal('https://discordapp.com/api/guilds/785384884197392384/widget.png')
|
||||
})
|
||||
expect(getWidgetImageUrl('785384884197392384')).to.equal('https://discordapp.com/api/guilds/785384884197392384/widget.png');
|
||||
});
|
||||
|
||||
it("will return the url for given guild's widget with the style", () => {
|
||||
expect(getWidgetImageUrl('785384884197392384', { style: 'banner2' })).to.equal(
|
||||
'https://discordapp.com/api/guilds/785384884197392384/widget.png?style=banner2',
|
||||
)
|
||||
})
|
||||
})
|
||||
})
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { describe, it } from 'mocha'
|
||||
import { describe, it } from 'mocha';
|
||||
|
||||
describe('index.ts', () => {
|
||||
it('will import without error', async () => {
|
||||
await import('../src/index.js')
|
||||
})
|
||||
})
|
||||
await import('../src/index.js');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,32 +1,32 @@
|
||||
import { expect } from 'chai'
|
||||
import { describe, it } from 'mocha'
|
||||
import { createLogger, LogLevels } from '../src/logger.js'
|
||||
import { expect } from 'chai';
|
||||
import { describe, it } from 'mocha';
|
||||
import { createLogger, LogLevels } from '../src/logger.js';
|
||||
|
||||
describe('Logger', () => {
|
||||
it('create logger with default options', () => {
|
||||
const loggy = createLogger()
|
||||
loggy.setLevel(LogLevels.Debug)
|
||||
loggy.debug('debugging')
|
||||
loggy.error('error')
|
||||
loggy.fatal('fatal')
|
||||
loggy.info('info')
|
||||
loggy.warn('warn')
|
||||
const loggy = createLogger();
|
||||
loggy.setLevel(LogLevels.Debug);
|
||||
loggy.debug('debugging');
|
||||
loggy.error('error');
|
||||
loggy.fatal('fatal');
|
||||
loggy.info('info');
|
||||
loggy.warn('warn');
|
||||
|
||||
loggy.debug('debugging')
|
||||
loggy.error('error')
|
||||
loggy.fatal('fatal')
|
||||
loggy.info('info')
|
||||
loggy.warn('warn')
|
||||
})
|
||||
loggy.debug('debugging');
|
||||
loggy.error('error');
|
||||
loggy.fatal('fatal');
|
||||
loggy.info('info');
|
||||
loggy.warn('warn');
|
||||
});
|
||||
|
||||
it('create logger with a name', () => {
|
||||
const loggy = createLogger({ name: 'loggy' })
|
||||
expect(loggy).to.exist
|
||||
})
|
||||
const loggy = createLogger({ name: 'loggy' });
|
||||
expect(loggy).to.exist;
|
||||
});
|
||||
|
||||
it('Handle fake level', () => {
|
||||
const loggy = createLogger({ name: 'fake level' })
|
||||
const level = 123 as LogLevels
|
||||
loggy.log(level, 'idk')
|
||||
})
|
||||
})
|
||||
const loggy = createLogger({ name: 'fake level' });
|
||||
const level = 123 as LogLevels;
|
||||
loggy.log(level, 'idk');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
import { expect } from 'chai'
|
||||
import { describe, it } from 'mocha'
|
||||
import { calculateBits, calculatePermissions } from '../src/permissions.js'
|
||||
import { expect } from 'chai';
|
||||
import { describe, it } from 'mocha';
|
||||
import { calculateBits, calculatePermissions } from '../src/permissions.js';
|
||||
|
||||
describe('permissions.ts', () => {
|
||||
describe('calculatePermissions function', () => {
|
||||
it('will return the array of permissions of bitwise string', () => {
|
||||
expect(calculatePermissions(34393292864n)).to.have.members(['ADD_REACTIONS', 'CREATE_PUBLIC_THREADS', 'USE_VAD'])
|
||||
})
|
||||
})
|
||||
expect(calculatePermissions(34393292864n)).to.have.members(['ADD_REACTIONS', 'CREATE_PUBLIC_THREADS', 'USE_VAD']);
|
||||
});
|
||||
});
|
||||
describe('calculateBits function', () => {
|
||||
it('will return the bitwise string of array of permissions', () => {
|
||||
expect(calculateBits(['ADD_REACTIONS', 'CREATE_PUBLIC_THREADS', 'USE_VAD'])).to.equal('34393292864')
|
||||
})
|
||||
})
|
||||
})
|
||||
expect(calculateBits(['ADD_REACTIONS', 'CREATE_PUBLIC_THREADS', 'USE_VAD'])).to.equal('34393292864');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,20 +1,20 @@
|
||||
import { expect } from 'chai'
|
||||
import { describe, it } from 'mocha'
|
||||
import { processReactionString } from '../src/reactions.js'
|
||||
import { expect } from 'chai';
|
||||
import { describe, it } from 'mocha';
|
||||
import { processReactionString } from '../src/reactions.js';
|
||||
|
||||
describe('Reactions', () => {
|
||||
it('Convert a unicode emoji to discord form', () => {
|
||||
const reaction = processReactionString('😄')
|
||||
expect(reaction).to.be.equal('😄')
|
||||
})
|
||||
const reaction = processReactionString('😄');
|
||||
expect(reaction).to.be.equal('😄');
|
||||
});
|
||||
|
||||
it('Convert a custom emoji to discord form', () => {
|
||||
const reaction = processReactionString('<:discordeno:785403373817823272>')
|
||||
expect(reaction).to.be.equal('discordeno:785403373817823272')
|
||||
})
|
||||
const reaction = processReactionString('<:discordeno:785403373817823272>');
|
||||
expect(reaction).to.be.equal('discordeno:785403373817823272');
|
||||
});
|
||||
|
||||
it('Convert an animated custom emoji to discord form', () => {
|
||||
const reaction = processReactionString('<a:discordeno:785403373817823272>')
|
||||
expect(reaction).to.be.equal('discordeno:785403373817823272')
|
||||
})
|
||||
})
|
||||
const reaction = processReactionString('<a:discordeno:785403373817823272>');
|
||||
expect(reaction).to.be.equal('discordeno:785403373817823272');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,26 +1,26 @@
|
||||
import { Buffer } from 'node:buffer'
|
||||
import { expect } from 'chai'
|
||||
import { describe, it } from 'mocha'
|
||||
import { getBotIdFromToken, removeTokenPrefix } from '../src/token.js'
|
||||
import { Buffer } from 'node:buffer';
|
||||
import { expect } from 'chai';
|
||||
import { describe, it } from 'mocha';
|
||||
import { getBotIdFromToken, removeTokenPrefix } from '../src/token.js';
|
||||
|
||||
describe('token.ts', () => {
|
||||
describe('token function', () => {
|
||||
it('Will remove token prefix when Bot is prefixed.', () => {
|
||||
expect(removeTokenPrefix('Bot discordeno is best lib')).to.be.equal('discordeno is best lib')
|
||||
})
|
||||
expect(removeTokenPrefix('Bot discordeno is best lib')).to.be.equal('discordeno is best lib');
|
||||
});
|
||||
|
||||
it('Will remove token prefix when Bot is NOT prefixed.', () => {
|
||||
expect(removeTokenPrefix('discordeno is best lib')).to.be.equal('discordeno is best lib')
|
||||
})
|
||||
expect(removeTokenPrefix('discordeno is best lib')).to.be.equal('discordeno is best lib');
|
||||
});
|
||||
|
||||
it('Will throw when token is undefined.', () => {
|
||||
expect(() => removeTokenPrefix(undefined)).to.throw()
|
||||
})
|
||||
})
|
||||
expect(() => removeTokenPrefix(undefined)).to.throw();
|
||||
});
|
||||
});
|
||||
|
||||
describe('getBotIdFromToken function', () => {
|
||||
it('Will get Bot Id from token', () => {
|
||||
expect(getBotIdFromToken(`${Buffer.from('1033452747380494366').toString('base64')}.zawsxedcrftvgybhu`)).to.equal(1033452747380494366n)
|
||||
})
|
||||
})
|
||||
})
|
||||
expect(getBotIdFromToken(`${Buffer.from('1033452747380494366').toString('base64')}.zawsxedcrftvgybhu`)).to.equal(1033452747380494366n);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { expect } from 'chai'
|
||||
import { describe, it } from 'mocha'
|
||||
import { isGetMessagesAfter, isGetMessagesAround, isGetMessagesBefore, isGetMessagesLimit } from '../src/typeguards.js'
|
||||
import { expect } from 'chai';
|
||||
import { describe, it } from 'mocha';
|
||||
import { isGetMessagesAfter, isGetMessagesAround, isGetMessagesBefore, isGetMessagesLimit } from '../src/typeguards.js';
|
||||
|
||||
describe('typeguard.ts', () => {
|
||||
describe('isGetMessagesAfter function', () => {
|
||||
@@ -9,13 +9,13 @@ describe('typeguard.ts', () => {
|
||||
isGetMessagesAfter({
|
||||
after: '684146387468463',
|
||||
}),
|
||||
).equal(true)
|
||||
})
|
||||
).equal(true);
|
||||
});
|
||||
|
||||
it("will return false if don't has after", () => {
|
||||
expect(isGetMessagesAfter({})).equal(false)
|
||||
})
|
||||
})
|
||||
expect(isGetMessagesAfter({})).equal(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('isGetMessagesBefore function', () => {
|
||||
it('will return true if has after', () => {
|
||||
@@ -23,13 +23,13 @@ describe('typeguard.ts', () => {
|
||||
isGetMessagesBefore({
|
||||
before: '684146387468463',
|
||||
}),
|
||||
).equal(true)
|
||||
})
|
||||
).equal(true);
|
||||
});
|
||||
|
||||
it("will return false if don't has after", () => {
|
||||
expect(isGetMessagesBefore({})).equal(false)
|
||||
})
|
||||
})
|
||||
expect(isGetMessagesBefore({})).equal(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('isGetMessagesAround function', () => {
|
||||
it('will return true if has after', () => {
|
||||
@@ -37,13 +37,13 @@ describe('typeguard.ts', () => {
|
||||
isGetMessagesAround({
|
||||
around: '684146387468463',
|
||||
}),
|
||||
).equal(true)
|
||||
})
|
||||
).equal(true);
|
||||
});
|
||||
|
||||
it("will return false if don't has after", () => {
|
||||
expect(isGetMessagesAround({})).equal(false)
|
||||
})
|
||||
})
|
||||
expect(isGetMessagesAround({})).equal(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('isGetMessagesAfter function', () => {
|
||||
it('will return true if has after', () => {
|
||||
@@ -51,11 +51,11 @@ describe('typeguard.ts', () => {
|
||||
isGetMessagesLimit({
|
||||
limit: 54,
|
||||
}),
|
||||
).equal(true)
|
||||
})
|
||||
).equal(true);
|
||||
});
|
||||
|
||||
it("will return false if don't has after", () => {
|
||||
expect(isGetMessagesLimit({})).equal(false)
|
||||
})
|
||||
})
|
||||
})
|
||||
expect(isGetMessagesLimit({})).equal(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,30 +1,30 @@
|
||||
import { expect } from 'chai'
|
||||
import { afterEach, beforeEach, describe, it } from 'mocha'
|
||||
import sinon from 'sinon'
|
||||
import { urlToBase64 } from '../src/urlToBase64.js'
|
||||
import { expect } from 'chai';
|
||||
import { afterEach, beforeEach, describe, it } from 'mocha';
|
||||
import sinon from 'sinon';
|
||||
import { urlToBase64 } from '../src/urlToBase64.js';
|
||||
|
||||
describe('urlToBase64.ts', () => {
|
||||
let fetchStub: sinon.SinonStub
|
||||
let fetchStub: sinon.SinonStub;
|
||||
|
||||
beforeEach(() => {
|
||||
fetchStub = sinon.stub(globalThis, 'fetch')
|
||||
})
|
||||
fetchStub = sinon.stub(globalThis, 'fetch');
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
sinon.restore()
|
||||
})
|
||||
sinon.restore();
|
||||
});
|
||||
|
||||
describe('urlToBase64 function', () => {
|
||||
it('Will convert a png image to base64', async () => {
|
||||
const mockArrayBuffer = new ArrayBuffer(8)
|
||||
const mockArrayBuffer = new ArrayBuffer(8);
|
||||
|
||||
fetchStub.resolves({
|
||||
arrayBuffer: () => Promise.resolve(mockArrayBuffer),
|
||||
})
|
||||
});
|
||||
|
||||
const url = await urlToBase64('https://example.com/image.png')
|
||||
const url = await urlToBase64('https://example.com/image.png');
|
||||
|
||||
expect(url).equal('data:image/png;base64,AAAAAAAAAAA=')
|
||||
})
|
||||
})
|
||||
})
|
||||
expect(url).equal('data:image/png;base64,AAAAAAAAAAA=');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,52 +1,52 @@
|
||||
import { expect } from 'chai'
|
||||
import { afterEach, beforeEach, describe, it } from 'mocha'
|
||||
import sinon from 'sinon'
|
||||
import { delay, hasProperty, jsonSafeReplacer } from '../src/utils.js'
|
||||
import { expect } from 'chai';
|
||||
import { afterEach, beforeEach, describe, it } from 'mocha';
|
||||
import sinon from 'sinon';
|
||||
import { delay, hasProperty, jsonSafeReplacer } from '../src/utils.js';
|
||||
|
||||
describe('utils.ts', () => {
|
||||
let clock: sinon.SinonFakeTimers
|
||||
let clock: sinon.SinonFakeTimers;
|
||||
|
||||
beforeEach(() => {
|
||||
clock = sinon.useFakeTimers()
|
||||
})
|
||||
clock = sinon.useFakeTimers();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
sinon.restore()
|
||||
clock.restore()
|
||||
})
|
||||
sinon.restore();
|
||||
clock.restore();
|
||||
});
|
||||
|
||||
describe('jsonSafe function', () => {
|
||||
it('will convert records to `JSON.stringify`-serializable', () => {
|
||||
// Example from issue#4196: https://github.com/discordeno/discordeno/issues/4196.
|
||||
const value = { limit: 0, userIds: [0n, 0n, 0n] }
|
||||
const expected = { limit: 0, userIds: ['0', '0', '0'] }
|
||||
expect(JSON.stringify(value, jsonSafeReplacer)).equal(JSON.stringify(expected))
|
||||
})
|
||||
})
|
||||
const value = { limit: 0, userIds: [0n, 0n, 0n] };
|
||||
const expected = { limit: 0, userIds: ['0', '0', '0'] };
|
||||
expect(JSON.stringify(value, jsonSafeReplacer)).equal(JSON.stringify(expected));
|
||||
});
|
||||
});
|
||||
|
||||
describe('delay function', () => {
|
||||
it('will delay/sleep for given time', async () => {
|
||||
let delayEnded = false
|
||||
let delayEnded = false;
|
||||
delay(31).then(() => {
|
||||
delayEnded = true
|
||||
})
|
||||
expect(delayEnded).to.be.false
|
||||
await clock.tickAsync(30)
|
||||
expect(delayEnded).to.be.false
|
||||
await clock.tickAsync(31)
|
||||
expect(delayEnded).to.be.true
|
||||
})
|
||||
})
|
||||
delayEnded = true;
|
||||
});
|
||||
expect(delayEnded).to.be.false;
|
||||
await clock.tickAsync(30);
|
||||
expect(delayEnded).to.be.false;
|
||||
await clock.tickAsync(31);
|
||||
expect(delayEnded).to.be.true;
|
||||
});
|
||||
});
|
||||
|
||||
describe('hasProperty funciton', async () => {
|
||||
const obj = { prop: 'lts372005' }
|
||||
const obj = { prop: 'lts372005' };
|
||||
|
||||
it('will return true if it does have property', () => {
|
||||
expect(hasProperty(obj, 'prop')).equal(true)
|
||||
})
|
||||
expect(hasProperty(obj, 'prop')).equal(true);
|
||||
});
|
||||
|
||||
it('will return false if it does not have property', () => {
|
||||
expect(hasProperty(obj, 'lts372005')).equal(false)
|
||||
})
|
||||
})
|
||||
})
|
||||
expect(hasProperty(obj, 'lts372005')).equal(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user