From fc40a3757d289a14356c63dd1adb8db08074778d Mon Sep 17 00:00:00 2001 From: murdle Date: Sun, 4 Jan 2026 14:54:46 +0200 Subject: [PATCH] proper versioning and v6 guilds --- assets/openapi.json | Bin 845258 -> 845463 bytes assets/schemas.json | Bin 3410302 -> 3410459 bytes package-lock.json | Bin 369046 -> 369216 bytes src/gateway/events/Connection.ts | 4 +- src/gateway/opcodes/Identify.ts | 151 ++++++++++++++++++------------- 5 files changed, 88 insertions(+), 67 deletions(-) diff --git a/assets/openapi.json b/assets/openapi.json index 7385d82da42a7134ce9468df77b218e5c21039fc..241479f522f1938964e3c3e3a9522bd86464c274 100644 GIT binary patch delta 375 zcmX?g+Iad|;|)$M+s%%#nK4ce*u$ncz4ibbdvi2Pdo&B<@@N*O6^upG4bQTRPZwZe zc-oAJj)Aq%?m;>Xc zPkO>8H+@4bqu}HX5vr39oMWB-;XISw_HId*H{wi|7Sjv2vFT3VV8W&bHho$Wm)!J% zBsSjZlEy585Y13qMW%m{Vo{sM!X`U?K@yYL^oFDC>eDx{vPn-@_{XXRR?lcKz40%z z!gg33b3j3*~pa%`U_ z&9YIP$=qoALuHok=`T%K1c8cy?3BqDEEK0FY~-+<{@_2W^mKtcj9PH%Y0L`SrA=At ze5M^_QJCHk#mv9m&xYwd$-OmrP*!&x>DWJ5VK1 zHS0u`?Fz?OtC^R}z fdjB&Xwsy}GAj}TL96-zo#9Tnkz1{N!kDnX>PY!2_S{!OS7zaU(7DQ=F&`Cs% z!f`rTOXz>dl1VT+v`Bo#p>8e~OeLF(IO%BHrGw&kh=Y@BhabH6_`b&<-ut!GVU2o+ zvAaAMK0d>iH~Pb5e09`^x~nr+7hFBi*2V3|>=sqFk^?jy6HoFRPsE>!c>B)6HQG$; z22IC%Ei$VSg+4bNgBI=!5qi@OCdiKJ6Vy4!f?l^Sf&~6L1YwXtfe7?K6!yV>h=B@m zIKaDg%}6twiZ22MYSx3}RCVKn6q}SJni>^4G6Js>vN+W(b>Icu$`VURT<^P=eb*kOeQnYxzMTgDGtcRAQKtEd^N7!eN)H%ef5|J$C0>@DZ=-wkHTd{N=ld%` Gx%V4P|4~^0 delta 858 zcmd7Q&ubG=5C?GHc6YPgYO-rynl+l{M_U`C&5vEG7zji?RPI!ik{S_ z;$fv;imRDJQR=;*733ja>`hNDSWrYU9z~D^OE1OZcMJXp6+C?44fEb(n3?4VKHItP zGwFuztxqQJG@09EAql!flgYpC(wXOSmKus$7q1rO-#X3DmV4Y%TLuMGh=2y&phKA7 z2m2ukF)+Y{0}vObHou)`Bi@sbC7QXe7OB!m)!JNE#OO=@BPpGEj~1(=^z~--0=*uzqBJ(C^RRaYJ!6SD%`SK4JY&Y7owjqz%W5$i z`y_W$=aRX5D@}gNG3n_$B^N$Qdx2rl;Iy0&3wi6IM2)GQ zn7H~OwboBRSB4_)#<&Fs#m0E*>^!a1d;h0GT@(t3;4q}24>FL29OR)N24E15zz`JR zC=}rs9EV{jK^ZD&LbXs8Uo=~n=%Qw;9Grf*M6W676C=$dZX$-=Y!T`P;I$z%sUHi(46RA$-f pIlEanwkz&p(X51%O#k_vk$ZdG1I7x~=@;sldAEOIW%_V(`U81J z@yQ!@uuTv6&M4PzaDi#N!3E~hY10kznZ>uCO=HQEhp3KhV^K7j{%IMD_;kO$ENt6X d>|)WX1nb`Z%b0bK;`Ei#tTVSKgt0#G1^}haNX`HN diff --git a/src/gateway/events/Connection.ts b/src/gateway/events/Connection.ts index bc74dee6..f0c82750 100644 --- a/src/gateway/events/Connection.ts +++ b/src/gateway/events/Connection.ts @@ -111,8 +111,8 @@ export async function Connection( if (socket.encoding === "etf" && !erlpack) throw new Error("Erlpack is not installed: 'npm i @yukikaze-bot/erlpack'"); - socket.version = Number(searchParams.get("version")) || 8; - if (socket.version != 8) + socket.version = Number(searchParams.get("v")) || 9; + if (socket.version != 9 && socket.version != 6) return socket.close(CLOSECODES.Invalid_API_version); // @ts-ignore diff --git a/src/gateway/opcodes/Identify.ts b/src/gateway/opcodes/Identify.ts index abbe77a5..25f31b81 100644 --- a/src/gateway/opcodes/Identify.ts +++ b/src/gateway/opcodes/Identify.ts @@ -409,41 +409,66 @@ export async function onIdentify(this: WebSocket, data: Payload) { const pending_guilds: Guild[] = []; // Generate guilds list ( make them unavailable if user is bot ) - const guilds: GuildOrUnavailable[] = members.map((member) => { - member.guild.channels = member.guild.channels - /* - //TODO maybe implement this correctly, by causing create and delete events for users who can newly view and not view the channels, along with doing these checks correctly, as they don't currently take into account that the owner of the guild is always able to view channels, with potentially other issues - .filter((channel) => { - const perms = Permissions.finalPermission({ - user: { - id: member.id, - roles: member.roles.map((x) => x.id), - }, - guild: member.guild, - channel, - }); + let guilds: GuildOrUnavailable[]; - return perms.has("VIEW_CHANNEL"); - }) - */ + if (this.version === 6) { + const membersByGuild = new Map(); + + for (const member of members) { + if (!membersByGuild.has(member.guild_id)) { + membersByGuild.set(member.guild_id, []); + } + + membersByGuild.get(member.guild_id)!.push({ + ...member.toPublicMember(), + roles: member.roles + .filter((r) => r.id !== member.guild.id) + .map((r) => r.id), + user: user.toPublicUser(), + }); + } + + guilds = members.map((member) => { + const g = member.guild; + + return { + ...g.toJSON(), + joined_at: member.joined_at, + channels: g.channels, + roles: g.roles, + emojis: g.emojis, + stickers: g.stickers, + voice_states: g.voice_states ?? [], + members: membersByGuild.get(g.id) ?? [], + member_count: membersByGuild.get(g.id)?.length ?? 1, + presences: [], + guild_scheduled_events: [], + threads: [], + large: false, + }; + }); + } else { + guilds = members.map((member) => { + member.guild.channels = member.guild.channels .map((channel) => { channel.position = member.guild.channel_ordering.indexOf(channel.id); return channel; }) .sort((a, b) => a.position - b.position); - if (user.bot) { - pending_guilds.push(member.guild); - return { id: member.guild.id, unavailable: true }; - } + if (user.bot) { + pending_guilds.push(member.guild); + return { id: member.guild.id, unavailable: true }; + } - return { - ...member.guild.toJSON(), - joined_at: member.joined_at, - guild_scheduled_events: [], - threads: [], - }; - }); + return { + ...member.guild.toJSON(), + joined_at: member.joined_at, + guild_scheduled_events: [], + threads: [], + }; + }); + } const generateGuildsListTime = taskSw.getElapsedAndReset(); // Generate user_guild_settings @@ -555,7 +580,7 @@ export async function onIdentify(this: WebSocket, data: Payload) { d = { v: 6, ...base, - heartbeat_interval: 45_000, + heartbeat_interval: 45000, guilds, private_channels: channels, presences: [], @@ -745,50 +770,46 @@ export async function onIdentify(this: WebSocket, data: Payload) { }), ); - const pendingGuildsTime = Date.now(); + if (this.version == 9) { + const readySupplementalGuilds = (guilds.filter((guild) => !guild.unavailable) as Guild[]).map((guild) => { + return { + voice_states: guild.voice_states.map((state) => { + // quick fix -murdle + const voiceState: any = {}; + PublicVoiceStateProjection.forEach((x) => { + voiceState[x] = state[x]; + }); + return voiceState as PublicVoiceState; + }), + id: guild.id, + embedded_activities: [], + }; + }); - const readySupplementalGuilds = (guilds.filter((guild) => !guild.unavailable) as Guild[]).map((guild) => { - return { - voice_states: guild.voice_states.map((state) => { - // quick fix -murdle - const voiceState: any = {}; - PublicVoiceStateProjection.forEach((x) => { - voiceState[x] = state[x]; - }); - return voiceState as PublicVoiceState; - }), - id: guild.id, - embedded_activities: [], - }; - }); - - // TODO: ready supplemental - await Send(this, { - op: OPCodes.DISPATCH, - t: EVENTEnum.ReadySupplemental, - s: this.sequence++, - d: { - merged_presences: { - guilds: [], - friends: [], + // TODO: ready supplemental + await Send(this, { + op: OPCodes.DISPATCH, + t: EVENTEnum.ReadySupplemental, + s: this.sequence++, + d: { + merged_presences: { + guilds: [], + friends: [], + }, + // these merged members seem to be all users currently in vc in your guilds + merged_members: [], + lazy_private_channels: [], + guilds: readySupplementalGuilds, // { voice_states: [], id: string, embedded_activities: [] } + // embedded_activities are users currently in an activity? + disclose: [], // Config.get().general.uniqueUsernames ? ["pomelo"] : [] }, - // these merged members seem to be all users currently in vc in your guilds - merged_members: [], - lazy_private_channels: [], - guilds: readySupplementalGuilds, // { voice_states: [], id: string, embedded_activities: [] } - // embedded_activities are users currently in an activity? - disclose: [], // Config.get().general.uniqueUsernames ? ["pomelo"] : [] - }, - }); - - const readySupplementalTime = Date.now(); + }); + } //TODO send GUILD_MEMBER_LIST_UPDATE //TODO send VOICE_STATE_UPDATE to let the client know if another device is already connected to a voice channel await setupListener.call(this); - const setupListenerTime = Date.now(); - console.log(`[Gateway] IDENTIFY ${this.user_id} in ${totalSw.elapsed().totalMilliseconds}ms`, process.env.LOG_GATEWAY_TRACES ? JSON.stringify(d._trace, null, 2) : ""); }