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:
Fleny
2026-01-17 21:54:15 +01:00
committed by GitHub
parent f713b4ab7b
commit 27c261fee2
403 changed files with 11250 additions and 11217 deletions
+52 -52
View File
@@ -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;
}
+38 -38
View File
@@ -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,
]
];
+49 -49
View File
@@ -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'>;
}
+3 -3
View File
@@ -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();
+74 -74
View File
@@ -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];
}
}
+26 -26
View File
@@ -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;
}
+61 -61
View File
@@ -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
View File
@@ -1 +1 @@
export const DISCORDENO_VERSION = '22.0.0-beta.1'
export const DISCORDENO_VERSION = '22.0.0-beta.1';
+5 -5
View File
@@ -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);
}
+34 -34
View File
@@ -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
View File
@@ -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';
+24 -24
View File
@@ -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;
+33 -33
View File
@@ -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';
}
+8 -8
View File
@@ -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();
}
+3 -3
View File
@@ -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;
}
+4 -4
View File
@@ -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;
}
+6 -6
View File
@@ -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]));
}
+7 -7
View File
@@ -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');
}
+5 -5
View File
@@ -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}`;
}
+4 -4
View File
@@ -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}`;
}
+5 -5
View File
@@ -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;
}
}
+78 -78
View File
@@ -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);
}
})
})
});
});
+89 -89
View File
@@ -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();
});
});
});
+32 -32
View File
@@ -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' }]);
});
});
+20 -20
View File
@@ -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');
});
});
});
});
+109 -109
View File
@@ -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;
});
});
});
});
});
+25 -25
View File
@@ -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);
});
});
+16 -16
View File
@@ -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);
});
});
+80 -80
View File
@@ -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',
)
})
})
})
);
});
});
});
+4 -4
View File
@@ -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');
});
});
+24 -24
View File
@@ -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');
});
});
+10 -10
View File
@@ -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');
});
});
});
+13 -13
View File
@@ -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');
});
});
+15 -15
View File
@@ -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);
});
});
});
+24 -24
View File
@@ -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);
});
});
});
+16 -16
View File
@@ -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=');
});
});
});
+32 -32
View File
@@ -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);
});
});
});