remove first/last

This commit is contained in:
MathMan05
2025-11-25 22:58:31 -06:00
parent 666ec3e855
commit d22e46d593
4 changed files with 46 additions and 126 deletions
+3 -16
View File
@@ -17,16 +17,7 @@
*/
import { WebSocket } from "@spacebar/gateway";
import {
emitEvent,
PresenceUpdateEvent,
PrivateSessionProjection,
Session,
SessionsReplace,
User,
VoiceState,
VoiceStateUpdateEvent,
} from "@spacebar/util";
import { emitEvent, PresenceUpdateEvent, PrivateSessionProjection, Session, SessionsReplace, User, VoiceState, VoiceStateUpdateEvent } from "@spacebar/util";
export async function Close(this: WebSocket, code: number, reason: Buffer) {
console.log("[WebSocket] closed", code, reason.toString());
@@ -44,11 +35,7 @@ export async function Close(this: WebSocket, code: number, reason: Buffer) {
});
// clear the voice state for this session if user was in voice channel
if (
voiceState &&
voiceState.session_id === this.session_id &&
voiceState.channel_id
) {
if (voiceState && voiceState.session_id === this.session_id && voiceState.channel_id) {
const prevGuildId = voiceState.guild_id;
const prevChannelId = voiceState.channel_id;
@@ -83,7 +70,7 @@ export async function Close(this: WebSocket, code: number, reason: Buffer) {
user_id: this.user_id,
data: sessions,
} as SessionsReplace);
const session = sessions.first() || {
const session = sessions[0] || {
activities: [],
client_status: {},
status: "offline",
+20 -76
View File
@@ -16,28 +16,11 @@
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import {
getDatabase,
getPermission,
listenEvent,
Member,
Role,
Session,
User,
Presence,
Channel,
Permissions,
} from "@spacebar/util";
import {
WebSocket,
Payload,
handlePresenceUpdate,
OPCODES,
Send,
} from "@spacebar/gateway";
import { getDatabase, getPermission, listenEvent, Member, Role, Session, User, Presence, Channel, Permissions } from "@spacebar/util";
import { WebSocket, Payload, handlePresenceUpdate, OPCODES, Send } from "@spacebar/gateway";
import murmur from "murmurhash-js/murmurhash3_gc";
import { check } from "./instanceOf";
import { LazyRequestSchema } from "@spacebar/schemas"
import { LazyRequestSchema } from "@spacebar/schemas";
// TODO: only show roles/members that have access to this channel
// TODO: config: to list all members (even those who are offline) sorted by role, or just those who are online
@@ -53,14 +36,10 @@ const getMostRelevantSession = (sessions: Session[]) => {
};
// sort sessions by relevance
sessions = sessions.sort((a, b) => {
return (
statusMap[a.status] -
statusMap[b.status] +
((a.activities?.length ?? 0) - (b.activities?.length ?? 0)) * 2
);
return statusMap[a.status] - statusMap[b.status] + ((a.activities?.length ?? 0) - (b.activities?.length ?? 0)) * 2;
});
return sessions.first();
return sessions[0];
};
async function getMembers(guild_id: string, range: [number, number]) {
@@ -79,10 +58,7 @@ async function getMembers(guild_id: string, range: [number, number]) {
.leftJoinAndSelect("member.user", "user")
.leftJoinAndSelect("user.sessions", "session")
.addSelect("user.settings")
.addSelect(
"CASE WHEN session.status IS NULL OR session.status = 'offline' OR session.status = 'invisible' THEN 0 ELSE 1 END",
"_status",
)
.addSelect("CASE WHEN session.status IS NULL OR session.status = 'offline' OR session.status = 'invisible' THEN 0 ELSE 1 END", "_status")
.orderBy("_status", "DESC")
.addOrderBy("role.position", "DESC")
.addOrderBy("user.username", "ASC")
@@ -118,9 +94,7 @@ async function getMembers(guild_id: string, range: [number, number]) {
const offlineItems = [];
for (const role of member_roles) {
const [role_members, other_members] = members.partition(
(m: Member) => !!m.roles.find((r) => r.id === role.id),
);
const [role_members, other_members] = members.partition((m: Member) => !!m.roles.find((r) => r.id === role.id));
const group = {
count: role_members.length,
id: role.id === guild_id ? "online" : role.id,
@@ -130,13 +104,9 @@ async function getMembers(guild_id: string, range: [number, number]) {
groups.push(group);
for (const member of role_members) {
const roles = member.roles
.filter((x: Role) => x.id !== guild_id)
.map((x: Role) => x.id);
const roles = member.roles.filter((x: Role) => x.id !== guild_id).map((x: Role) => x.id);
const session: Session | undefined = getMostRelevantSession(
member.user.sessions,
);
const session: Session | undefined = getMostRelevantSession(member.user.sessions);
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
@@ -157,11 +127,7 @@ async function getMembers(guild_id: string, range: [number, number]) {
},
};
if (
!session ||
session.status == "invisible" ||
session.status == "offline"
) {
if (!session || session.status == "invisible" || session.status == "offline") {
item.member.presence.status = "offline";
offlineItems.push(item);
group.count--;
@@ -188,24 +154,14 @@ async function getMembers(guild_id: string, range: [number, number]) {
items,
groups,
range,
members: items
.map((x) =>
"member" in x
? { ...x.member, settings: undefined }
: undefined,
)
.filter((x) => !!x),
members: items.map((x) => ("member" in x ? { ...x.member, settings: undefined } : undefined)).filter((x) => !!x),
};
}
async function subscribeToMemberEvents(this: WebSocket, user_id: string) {
if (this.events[user_id]) return false; // already subscribed as friend
if (this.member_events[user_id]) return false; // already subscribed in member list
this.member_events[user_id] = await listenEvent(
user_id,
handlePresenceUpdate.bind(this),
this.listen_options,
);
this.member_events[user_id] = await listenEvent(user_id, handlePresenceUpdate.bind(this), this.listen_options);
return true;
}
@@ -213,8 +169,7 @@ export async function onLazyRequest(this: WebSocket, { d }: Payload) {
const startTime = Date.now();
// TODO: check data
check.call(this, LazyRequestSchema, d);
const { guild_id, typing, channels, activities, members } =
d as LazyRequestSchema;
const { guild_id, typing, channels, activities, members } = d as LazyRequestSchema;
if (members) {
// Client has requested a PRESENCE_UPDATE for specific member
@@ -222,10 +177,7 @@ export async function onLazyRequest(this: WebSocket, { d }: Payload) {
await Promise.all([
members.map(async (x) => {
if (!x) return;
const didSubscribe = await subscribeToMemberEvents.call(
this,
x,
);
const didSubscribe = await subscribeToMemberEvents.call(this, x);
if (!didSubscribe) return;
// if we didn't subscribe just now, this is a new subscription
@@ -257,7 +209,7 @@ export async function onLazyRequest(this: WebSocket, { d }: Payload) {
if (!channels) throw new Error("Must provide channel ranges");
const channel_id = Object.keys(channels || {}).first();
const channel_id = Object.keys(channels || {})[0];
if (!channel_id) return;
const permissions = await getPermission(this.user_id, guild_id, channel_id);
@@ -267,9 +219,7 @@ export async function onLazyRequest(this: WebSocket, { d }: Payload) {
if (!Array.isArray(ranges)) throw new Error("Not a valid Array");
const member_count = await Member.count({ where: { guild_id } });
const ops = await Promise.all(
ranges.map((x) => getMembers(guild_id, x as [number, number])),
);
const ops = await Promise.all(ranges.map((x) => getMembers(guild_id, x as [number, number])));
let list_id = "everyone";
@@ -282,10 +232,8 @@ export async function onLazyRequest(this: WebSocket, { d }: Payload) {
channel.permission_overwrites.forEach((overwrite) => {
const { id, allow, deny } = overwrite;
if (BigInt(allow) & Permissions.FLAGS.VIEW_CHANNEL)
perms.push(`allow:${id}`);
else if (BigInt(deny) & Permissions.FLAGS.VIEW_CHANNEL)
perms.push(`deny:${id}`);
if (BigInt(allow) & Permissions.FLAGS.VIEW_CHANNEL) perms.push(`allow:${id}`);
else if (BigInt(deny) & Permissions.FLAGS.VIEW_CHANNEL) perms.push(`deny:${id}`);
});
if (perms.length > 0) {
@@ -317,9 +265,7 @@ export async function onLazyRequest(this: WebSocket, { d }: Payload) {
op: "SYNC",
range: x.range,
})),
online_count:
member_count -
(groups.find((x) => x.id == "offline")?.count ?? 0),
online_count: member_count - (groups.find((x) => x.id == "offline")?.count ?? 0),
member_count,
id: list_id,
guild_id,
@@ -327,7 +273,5 @@ export async function onLazyRequest(this: WebSocket, { d }: Payload) {
},
});
console.log(
`[Gateway] LAZY_REQUEST ${guild_id} ${channel_id} took ${Date.now() - startTime}ms`,
);
console.log(`[Gateway] LAZY_REQUEST ${guild_id} ${channel_id} took ${Date.now() - startTime}ms`);
}
+20 -22
View File
@@ -1,11 +1,10 @@
import moduleAlias from "module-alias";
moduleAlias();
import './Array';
import { describe, it } from 'node:test';
import assert from 'node:assert/strict';
import "./Array";
import { describe, it } from "node:test";
import assert from "node:assert/strict";
describe("Array extensions", () => {
it("containsAll", () => {
const arr = [1, 2, 3, 4, 5];
assert(arr.containsAll([1, 2]));
@@ -27,8 +26,14 @@ describe("Array extensions", () => {
it("single", () => {
const arr = [1, 2, 3, 4, 5];
assert.strictEqual(arr.single((n) => n === 3), 3);
assert.strictEqual(arr.single((n) => n === 6), null);
assert.strictEqual(
arr.single((n) => n === 3),
3,
);
assert.strictEqual(
arr.single((n) => n === 6),
null,
);
assert.throws(() => arr.single((n) => n > 2));
});
@@ -55,18 +60,6 @@ describe("Array extensions", () => {
assert.deepEqual(arr, [1, 2, 4, 5]);
});
it("first", () => {
const arr = [1, 2, 3];
assert.strictEqual(arr.first(), 1);
assert.strictEqual([].first(), undefined);
});
it("last", () => {
const arr = [1, 2, 3];
assert.strictEqual(arr.last(), 3);
assert.strictEqual([].last(), undefined);
});
it("distinct", () => {
const arr = [1, 2, 2, 3, 3, 3];
assert.deepEqual(arr.distinct(), [1, 2, 3]);
@@ -75,8 +68,14 @@ describe("Array extensions", () => {
it("distinctBy", () => {
const arr = [{ id: 1 }, { id: 2 }, { id: 1 }, { id: 3 }];
assert.deepEqual(arr.distinctBy((x) => x.id), [{ id: 1 }, { id: 2 }, { id: 3 }]);
assert.deepEqual([].distinctBy((x) => x), []);
assert.deepEqual(
arr.distinctBy((x) => x.id),
[{ id: 1 }, { id: 2 }, { id: 3 }],
);
assert.deepEqual(
[].distinctBy((x) => x),
[],
);
});
it("intersect", () => {
@@ -98,5 +97,4 @@ describe("Array extensions", () => {
// @ts-expect-error
assert.deepEqual([].except(arr2), []);
});
});
});
+3 -12
View File
@@ -24,8 +24,6 @@ declare global {
forEachAsync(callback: (elem: T, index: number, array: T[]) => Promise<void>): Promise<void>;
filterAsync(callback: (elem: T, index: number, array: T[]) => Promise<boolean>): Promise<T[]>;
remove(item: T): void;
first(): T | undefined;
last(): T | undefined;
distinct(): T[];
distinctBy<K>(key: (elem: T) => K): T[];
intersect(other: T[]): T[];
@@ -126,21 +124,14 @@ if (!Array.prototype.remove)
Array.prototype.remove = function <T>(this: T[], item: T) {
return arrayRemove.call(this, item);
};
if (!Array.prototype.first)
Array.prototype.first = function <T>(this: T[]) {
return arrayFirst.call(this);
};
if (!Array.prototype.last)
Array.prototype.last = function <T>(this: T[]) {
return arrayLast.call(this);
};
if (!Array.prototype.distinct)
Array.prototype.distinct = function <T>(this: T[]) {
return arrayDistinct.call(this);
};
if (!Array.prototype.distinctBy)
Array.prototype.distinctBy = function <T, K>(this: T[], key: (elem: T) => K) {
return arrayDistinctBy.call(this, key as ((elem: unknown) => unknown));
return arrayDistinctBy.call(this, key as (elem: unknown) => unknown);
};
if (!Array.prototype.intersect)
Array.prototype.intersect = function <T>(this: T[], other: T[]) {
@@ -149,4 +140,4 @@ if (!Array.prototype.intersect)
if (!Array.prototype.except)
Array.prototype.except = function <T>(this: T[], other: T[]) {
return arrayExcept.call(this, other);
};
};