diff --git a/src/api/routes/applications/#id/bot/index.ts b/src/api/routes/applications/#id/bot/index.ts
index 6d054c75..82f9ad2b 100644
--- a/src/api/routes/applications/#id/bot/index.ts
+++ b/src/api/routes/applications/#id/bot/index.ts
@@ -51,7 +51,6 @@ router.post("/", route({}), async (req: Request, res: Response) => {
settings: {},
extended_settings: {},
fingerprints: [],
- notes: {}
});
await user.save();
app.bot = user;
diff --git a/src/gateway/opcodes/LazyRequest.ts b/src/gateway/opcodes/LazyRequest.ts
index ea69779e..95a69a32 100644
--- a/src/gateway/opcodes/LazyRequest.ts
+++ b/src/gateway/opcodes/LazyRequest.ts
@@ -24,6 +24,7 @@ async function getMembers(guild_id: string, range: [number, number]) {
.leftJoinAndSelect("member.roles", "role")
.leftJoinAndSelect("member.user", "user")
.leftJoinAndSelect("user.sessions", "session")
+ .innerJoinAndSelect("user.settings", "settings")
.addSelect("CASE WHEN session.status = 'offline' THEN 0 ELSE 1 END", "_status")
.orderBy("role.position", "DESC")
.addOrderBy("_status", "DESC")
@@ -38,12 +39,19 @@ async function getMembers(guild_id: string, range: [number, number]) {
.map((m) => m.roles)
.flat()
.unique((r: Role) => r.id);
+ // add online role
+ member_roles.push(
+ member_roles.splice(
+ member_roles.findIndex((x => x.id == x.guild_id)),
+ 1
+ )[0]
+ );
const offlineItems = [];
for (const role of member_roles) {
// @ts-ignore
- const [role_members, other_members] = partition(members, (m: Member) => m.roles.find((r) => r.id === role.id));
+ const [role_members, other_members]: Member[][] = partition(members, (m: Member) => m.roles.find((r) => r.id === role.id));
const group = {
count: role_members.length,
id: role.id === guild_id ? "online" : role.id
@@ -55,9 +63,25 @@ async function getMembers(guild_id: string, range: [number, number]) {
for (const member of role_members) {
const roles = member.roles.filter((x: Role) => x.id !== guild_id).map((x: Role) => x.id);
- const session = member.user.sessions.first();
+ const statusPriority = {
+ "online": 0,
+ "idle": 1,
+ "dnd": 2,
+ "invisible": 3,
+ "offline": 4,
+ };
+ const sessions = member.user.sessions.sort((a, b) => {
+ // activities are higher priority than status
+ return (statusPriority[a.status] - statusPriority[b.status])
+ + ((a.activities.length - b.activities.length) * 2);
+ });
+ const session = sessions.first();
+
+ if (session?.status == "offline") {
+ // swap out for user settings status
+ session.status = member.user.settings.status;
+ }
- // TODO: properly mock/hide offline/invisible status
const item = {
member: {
...member,
@@ -70,8 +94,10 @@ async function getMembers(guild_id: string, range: [number, number]) {
}
}
};
-
- if (!member?.user?.sessions || !member.user.sessions.length) {
+
+ if (!session || session.status == "invisible" || session.status == "offline") {
+ item.member.presence.status = "offline";
+ item.member.presence.activities = [];
offlineItems.push(item);
group.count--;
continue;
@@ -122,12 +148,18 @@ export async function onLazyRequest(this: WebSocket, { d }: Payload) {
ops.forEach((op) => {
op.members.forEach(async (member) => {
+ if (!member) return;
if (this.events[member.user.id]) return; // already subscribed as friend
if (this.member_events[member.user.id]) return; // already subscribed in member list
this.member_events[member.user.id] = await listenEvent(member.user.id, handlePresenceUpdate.bind(this), this.listen_options);
});
});
+ const groups = ops
+ .map((x) => x.groups)
+ .flat()
+ .unique();
+
return Send(this, {
op: OPCODES.Dispatch,
s: this.sequence++,
@@ -138,14 +170,12 @@ export async function onLazyRequest(this: WebSocket, { d }: Payload) {
op: "SYNC",
range: x.range
})),
- online_count: member_count,
+ // remove offline members from count
+ online_count: member_count - (groups.find(x => x.id == "offline")?.count ?? 0),
member_count,
id: "everyone",
guild_id,
- groups: ops
- .map((x) => x.groups)
- .flat()
- .unique()
+ groups,
}
});
}
diff --git a/src/util/interfaces/Activity.ts b/src/util/interfaces/Activity.ts
index 3b36b4a6..7e9c6a56 100644
--- a/src/util/interfaces/Activity.ts
+++ b/src/util/interfaces/Activity.ts
@@ -33,6 +33,15 @@ export interface Activity {
};
instance?: boolean;
flags: string; // activity flags OR d together, describes what the payload includes
+ // spotify and other rich presence data
+ id?: string;
+ sync_id?: string;
+ metadata?: {
+ context_uri?: string;
+ album_id?: string;
+ artist_ids?: string[];
+ },
+ session_id?: string;
}
export enum ActivityType {
diff --git a/src/util/migrations/mariadb/1662626234189-notes.ts b/src/util/migrations/mariadb/1662626234189-notes.ts
new file mode 100644
index 00000000..57aaecbd
--- /dev/null
+++ b/src/util/migrations/mariadb/1662626234189-notes.ts
@@ -0,0 +1,14 @@
+import { MigrationInterface, QueryRunner, TableColumn } from "typeorm";
+
+export class notes1662626234189 implements MigrationInterface {
+ name = 'notes1662626234189'
+
+ public async up(queryRunner: QueryRunner): Promise<void> {
+ await queryRunner.dropColumn("users", "notes");
+ }
+
+ public async down(queryRunner: QueryRunner): Promise<void> {
+ await queryRunner.addColumn("users", new TableColumn({ name: "notes", type: "simple-json" }));
+ }
+
+}
diff --git a/src/util/migrations/postgres/1662626234189-notes.ts b/src/util/migrations/postgres/1662626234189-notes.ts
new file mode 100644
index 00000000..57aaecbd
--- /dev/null
+++ b/src/util/migrations/postgres/1662626234189-notes.ts
@@ -0,0 +1,14 @@
+import { MigrationInterface, QueryRunner, TableColumn } from "typeorm";
+
+export class notes1662626234189 implements MigrationInterface {
+ name = 'notes1662626234189'
+
+ public async up(queryRunner: QueryRunner): Promise<void> {
+ await queryRunner.dropColumn("users", "notes");
+ }
+
+ public async down(queryRunner: QueryRunner): Promise<void> {
+ await queryRunner.addColumn("users", new TableColumn({ name: "notes", type: "simple-json" }));
+ }
+
+}
diff --git a/src/util/migrations/sqlite/1662626234189-notes.ts b/src/util/migrations/sqlite/1662626234189-notes.ts
new file mode 100644
index 00000000..57aaecbd
--- /dev/null
+++ b/src/util/migrations/sqlite/1662626234189-notes.ts
@@ -0,0 +1,14 @@
+import { MigrationInterface, QueryRunner, TableColumn } from "typeorm";
+
+export class notes1662626234189 implements MigrationInterface {
+ name = 'notes1662626234189'
+
+ public async up(queryRunner: QueryRunner): Promise<void> {
+ await queryRunner.dropColumn("users", "notes");
+ }
+
+ public async down(queryRunner: QueryRunner): Promise<void> {
+ await queryRunner.addColumn("users", new TableColumn({ name: "notes", type: "simple-json" }));
+ }
+
+}
diff --git a/src/util/schemas/ActivitySchema.ts b/src/util/schemas/ActivitySchema.ts
index d94557ea..db42fff4 100644
--- a/src/util/schemas/ActivitySchema.ts
+++ b/src/util/schemas/ActivitySchema.ts
@@ -37,7 +37,16 @@ export const ActivitySchema = {
$match: String
},
$instance: Boolean,
- $flags: String
+ $flags: String,
+ // spotify and other rich presence data
+ $id: String,
+ $sync_id: String,
+ $metadata: {
+ $context_uri: String,
+ $album_id: String,
+ $artist_ids: [String],
+ },
+ $session_id: String,
}
],
$since: Number // unix time (in milliseconds) of when the client went idle, or null if the client is not idle
|