diff --git a/package.json b/package.json index 9973d36a..d7db36c3 100644 --- a/package.json +++ b/package.json @@ -118,7 +118,7 @@ "@spacebar/gateway": "dist/gateway", "@spacebar/util": "dist/util", "@spacebar/webrtc": "dist/webrtc", - "lambert-server": "dist/util/util/lambert-server" + "lambert-server": "dist/util/util/lambert-server" }, "optionalDependencies": { "@sendgrid/mail": "^8.1.6", diff --git a/src/api/routes/channels/#channel_id/recipients.ts b/src/api/routes/channels/#channel_id/recipients.ts index 025a86f6..14ab28e5 100644 --- a/src/api/routes/channels/#channel_id/recipients.ts +++ b/src/api/routes/channels/#channel_id/recipients.ts @@ -51,7 +51,7 @@ router.put( const recipients = [ ...(channel.recipients?.map((r) => r.user_id) || []), user_id, - ].unique(); + ].distinct(); const new_channel = await Channel.createDMChannel( recipients, diff --git a/src/api/routes/channels/#channel_id/typing.ts b/src/api/routes/channels/#channel_id/typing.ts index 7e6cba41..7ef36150 100644 --- a/src/api/routes/channels/#channel_id/typing.ts +++ b/src/api/routes/channels/#channel_id/typing.ts @@ -35,7 +35,7 @@ router.post( async (req: Request, res: Response) => { const { channel_id } = req.params; const user_id = req.user_id; - const timestamp = Date.nowSeconds(); + const timestamp = Math.floor(Date.now() / 1000); const channel = await Channel.findOneOrFail({ where: { id: channel_id }, }); diff --git a/src/api/routes/guilds/#guild_id/roles/#role_id/members.ts b/src/api/routes/guilds/#guild_id/roles/#role_id/members.ts index b1506b0b..4cfe21b9 100644 --- a/src/api/routes/guilds/#guild_id/roles/#role_id/members.ts +++ b/src/api/routes/guilds/#guild_id/roles/#role_id/members.ts @@ -17,7 +17,7 @@ */ import { Router, Request, Response } from "express"; -import { DiscordApiErrors, Member, partition } from "@spacebar/util"; +import { DiscordApiErrors, Member } from "@spacebar/util"; import { route } from "@spacebar/api"; const router = Router({ mergeParams: true }); @@ -38,8 +38,7 @@ router.patch( relations: ["roles"], }); - const [add, remove] = partition( - members, + const [add, remove] = members.partition( (member) => member_ids.includes(member.id) && !member.roles.map((role) => role.id).includes(role_id), diff --git a/src/api/util/utility/passwordStrength.ts b/src/api/util/utility/passwordStrength.ts index fcd42d8d..f48b8022 100644 --- a/src/api/util/utility/passwordStrength.ts +++ b/src/api/util/utility/passwordStrength.ts @@ -20,7 +20,7 @@ import { Config } from "@spacebar/util"; const reNUMBER = /[0-9]/g; const reUPPERCASELETTER = /[A-Z]/g; -const reSYMBOLS = /[A-Z,a-z,0-9]/g; +const reSYMBOLS = /[A-Za-z0-9]/g; // const blocklist: string[] = []; // TODO: update ones passwordblocklist is stored in db /* @@ -45,12 +45,12 @@ export function checkPassword(password: string): number { } // checks for amount of Numbers - if (password.count(reNUMBER) >= minNumbers - 1) { + if (password.match(reNUMBER)?.length ?? 0 >= minNumbers - 1) { strength += 0.05; } // checks for amount of Uppercase Letters - if (password.count(reUPPERCASELETTER) >= minUpperCase - 1) { + if (password.match(reUPPERCASELETTER)?.length ?? 0 >= minUpperCase - 1) { strength += 0.05; } @@ -61,8 +61,8 @@ export function checkPassword(password: string): number { // checks if password only consists of numbers or only consists of chars if ( - password.length == password.count(reNUMBER) || - password.length === password.count(reUPPERCASELETTER) + password.length == password.match(reNUMBER)?.length || + password.length === password.match(reUPPERCASELETTER)?.length ) { strength = 0; } diff --git a/src/gateway/opcodes/LazyRequest.ts b/src/gateway/opcodes/LazyRequest.ts index 3edda0e9..41ec4024 100644 --- a/src/gateway/opcodes/LazyRequest.ts +++ b/src/gateway/opcodes/LazyRequest.ts @@ -26,7 +26,6 @@ import { LazyRequestSchema, User, Presence, - partition, Channel, Permissions, } from "@spacebar/util"; @@ -108,7 +107,7 @@ async function getMembers(guild_id: string, range: [number, number]) { const member_roles = members .map((m) => m.roles) .flat() - .unique((r: Role) => r.id); + .distinctBy((r: Role) => r.id); member_roles.push( member_roles.splice( member_roles.findIndex((x) => x.id === x.guild_id), @@ -119,8 +118,7 @@ async function getMembers(guild_id: string, range: [number, number]) { const offlineItems = []; for (const role of member_roles) { - const [role_members, other_members] = partition( - members, + const [role_members, other_members] = members.partition( (m: Member) => !!m.roles.find((r) => r.id === role.id), ); const group = { @@ -284,9 +282,9 @@ export async function onLazyRequest(this: WebSocket, { d }: Payload) { channel.permission_overwrites.forEach((overwrite) => { const { id, allow, deny } = overwrite; - if (allow.toBigInt() & Permissions.FLAGS.VIEW_CHANNEL) + if (BigInt(allow) & Permissions.FLAGS.VIEW_CHANNEL) perms.push(`allow:${id}`); - else if (deny.toBigInt() & Permissions.FLAGS.VIEW_CHANNEL) + else if (BigInt(deny) & Permissions.FLAGS.VIEW_CHANNEL) perms.push(`deny:${id}`); }); @@ -307,7 +305,7 @@ export async function onLazyRequest(this: WebSocket, { d }: Payload) { const groups = ops .map((x) => x.groups) .flat() - .unique(); + .distinct(); await Send(this, { op: OPCODES.Dispatch, diff --git a/src/util/entities/Channel.ts b/src/util/entities/Channel.ts index f466474e..0624425f 100644 --- a/src/util/entities/Channel.ts +++ b/src/util/entities/Channel.ts @@ -27,7 +27,7 @@ import { } from "typeorm"; import { DmChannelDTO } from "../dtos"; import { ChannelCreateEvent, ChannelRecipientRemoveEvent } from "../interfaces"; -import { InvisibleCharacters, Snowflake, containsAll, emitEvent, getPermission, trimSpecial, Permissions, BitField } from "../util"; +import { InvisibleCharacters, Snowflake, emitEvent, getPermission, trimSpecial, Permissions, BitField } from "../util"; import { BaseClass } from "./BaseClass"; import { Guild } from "./Guild"; import { Invite } from "./Invite"; @@ -328,7 +328,7 @@ export class Channel extends BaseClass { creator_user_id: string, name?: string, ) { - recipients = recipients.unique().filter((x) => x !== creator_user_id); + recipients = recipients.distinct().filter((x) => x !== creator_user_id); // TODO: check config for max number of recipients /** if you want to disallow note to self channels, uncomment the conditional below @@ -354,7 +354,7 @@ export class Channel extends BaseClass { if (!ur.channel.recipients) continue; const re = ur.channel.recipients.map((r) => r.user_id); if (re.length === channelRecipients.length) { - if (containsAll(re, channelRecipients)) { + if (re.containsAll(channelRecipients)) { if (channel == null) { channel = ur.channel; await ur.assign({ closed: false }).save(); diff --git a/src/util/util/AutoUpdate.ts b/src/util/util/AutoUpdate.ts index 3b8efba7..e62a5c50 100644 --- a/src/util/util/AutoUpdate.ts +++ b/src/util/util/AutoUpdate.ts @@ -56,9 +56,9 @@ export function enableAutoUpdate(opts: { const latestVersion = await getLatestVersion(opts.packageJsonLink); if (currentVersion !== latestVersion) { rl.question( - `[Auto Update] Current version (${currentVersion}) is out of date, would you like to update? (yes/no)`, + `[Auto Update] Current version (${currentVersion}) is out of date, would you like to update? (Y/n)`, (answer) => { - if (answer.toBoolean()) { + if (answer === "" || answer.toLowerCase() === "y") { console.log(`[Auto update] updating ...`); download(opts.downloadUrl, opts.path); } else { diff --git a/src/util/util/FieldError.ts b/src/util/util/FieldError.ts index 3bab92e7..810a9971 100644 --- a/src/util/util/FieldError.ts +++ b/src/util/util/FieldError.ts @@ -23,7 +23,7 @@ export function FieldErrors( return new FieldError( 50035, "Invalid Form Body", - fields.map(({ message, code }) => ({ + (fields as Object).map(({ message, code }) => ({ _errors: [ { message, diff --git a/src/util/util/extensions/Array.ts b/src/util/util/extensions/Array.ts index 53d26ed6..d4b383ce 100644 --- a/src/util/util/extensions/Array.ts +++ b/src/util/util/extensions/Array.ts @@ -23,57 +23,102 @@ declare global { single(filter: (elem: T) => boolean): T | null; forEachAsync(callback: (elem: T, index: number, array: T[]) => Promise): Promise; remove(item: T): void; + first(): T | undefined; + last(): T | undefined; + distinct(): T[]; + distinctBy(key: (elem: T) => K): T[]; } } -export function containsAll(arr: T[], target: T[]) { +export function arrayContainsAll(arr: T[], target: T[]) { return target.every((v) => arr.includes(v)); } /* https://stackoverflow.com/a/50636286 */ -export function partition(array: T[], filter: (elem: T) => boolean): [T[], T[]] { +export function arrayPartition(array: T[], filter: (elem: T) => boolean): [T[], T[]] { const pass: T[] = [], fail: T[] = []; array.forEach((e) => (filter(e) ? pass : fail).push(e)); return [pass, fail]; } -export function single(array: T[], filter: (elem: T) => boolean): T | null { +export function arraySingle(array: T[], filter: (elem: T) => boolean): T | null { const results = array.filter(filter); if (results.length > 1) throw new Error("Array contains more than one matching element"); if (results.length === 0) return null; return results[0]; } -export async function forEachAsync(array: T[], callback: (elem: T, index: number, array: T[]) => Promise): Promise { +export async function arrayForEachAsync(array: T[], callback: (elem: T, index: number, array: T[]) => Promise): Promise { await Promise.all(array.map(callback)); } -export function remove(this: T[], item: T): void { +export function arrayRemove(this: T[], item: T): void { const index = this.indexOf(item); if (index > -1) { this.splice(index, 1); } } +export function arrayFirst(this: T[]): T | undefined { + return this[0]; +} + +export function arrayLast(this: T[]): T | undefined { + return this[this.length - 1]; +} + +export function arrayDistinct(this: T[]): T[] { + return Array.from(new Set(this)); +} + +export function arrayDistinctBy(this: T[], key: (elem: T) => K): T[] { + const seen = new Set(); + return this.filter((item) => { + const k = key(item); + if (seen.has(k)) { + return false; + } else { + seen.add(k); + return true; + } + }); +} + // register extensions if (!Array.prototype.containsAll) Array.prototype.containsAll = function (this: T[], target: T[]) { - return containsAll(this, target); + return arrayContainsAll(this, target); }; if (!Array.prototype.partition) Array.prototype.partition = function (this: T[], filter: (elem: T) => boolean) { - return partition(this, filter); + return arrayPartition(this, filter); }; if (!Array.prototype.single) Array.prototype.single = function (this: T[], filter: (elem: T) => boolean) { - return single(this, filter); + return arraySingle(this, filter); }; if (!Array.prototype.forEachAsync) Array.prototype.forEachAsync = function (this: T[], callback: (elem: T, index: number, array: T[]) => Promise) { - return forEachAsync(this, callback); + return arrayForEachAsync(this, callback); }; if (!Array.prototype.remove) Array.prototype.remove = function (this: T[], item: T) { - return remove.call(this, item); + return arrayRemove.call(this, item); }; +if (!Array.prototype.first) + Array.prototype.first = function (this: T[]) { + return arrayFirst.call(this); + }; +if (!Array.prototype.last) + Array.prototype.last = function (this: T[]) { + return arrayLast.call(this); + }; +if (!Array.prototype.distinct) + Array.prototype.distinct = function (this: T[]) { + return arrayDistinct.call(this); + }; +if (!Array.prototype.distinctBy) + Array.prototype.distinctBy = function (this: T[], key: (elem: T) => K) { + return arrayDistinctBy.call(this, key as ((elem: unknown) => unknown)); + }; \ No newline at end of file