diff --git a/src/opcodes/Identify.ts b/src/opcodes/Identify.ts
new file mode 100644
index 00000000..43368367
--- /dev/null
+++ b/src/opcodes/Identify.ts
@@ -0,0 +1,174 @@
+import { CLOSECODES, Payload, OPCODES } from "../util/Constants";
+import WebSocket from "../util/WebSocket";
+import {
+ ChannelModel,
+ checkToken,
+ GuildModel,
+ Intents,
+ MemberDocument,
+ MemberModel,
+ ReadyEventData,
+ UserModel,
+ toObject,
+ EVENTEnum,
+ Config,
+} from "@fosscord/server-util";
+import { setupListener } from "../listener/listener";
+import { IdentifySchema } from "../schema/Identify";
+import { Send } from "../util/Send";
+// import experiments from "./experiments.json";
+const experiments: any = [];
+import { check } from "./instanceOf";
+
+// TODO: bot sharding
+// TODO: check priviliged intents
+// TODO: check if already identified
+
+export async function onIdentify(this: WebSocket, data: Payload) {
+ clearTimeout(this.readyTimeout);
+ check.call(this, IdentifySchema, data.d);
+
+ const identify: IdentifySchema = data.d;
+
+ try {
+ const { jwtSecret } = Config.get().security;
+ var { decoded } = await checkToken(identify.token, jwtSecret); // will throw an error if invalid
+ } catch (error) {
+ console.error("invalid token", error);
+ return this.close(CLOSECODES.Authentication_failed);
+ }
+ this.user_id = decoded.id;
+ if (!identify.intents) identify.intents = 0b11111111111111n;
+ this.intents = new Intents(identify.intents);
+ if (identify.shard) {
+ this.shard_id = identify.shard[0];
+ this.shard_count = identify.shard[1];
+ if (
+ !this.shard_count ||
+ !this.shard_id ||
+ this.shard_id >= this.shard_count ||
+ this.shard_id < 0 ||
+ this.shard_count <= 0
+ ) {
+ return this.close(CLOSECODES.Invalid_shard);
+ }
+ }
+
+ const members = toObject(await MemberModel.find({ id: this.user_id }).exec());
+ const merged_members = members.map((x: any) => {
+ const y = { ...x, user_id: x.id };
+ delete y.settings;
+ delete y.id;
+ return [y];
+ }) as MemberDocument[][];
+ const user_guild_settings_entries = members.map((x) => x.settings);
+
+ const channels = await ChannelModel.find({ recipient_ids: this.user_id }).exec();
+ const user = await UserModel.findOne({ id: this.user_id }).exec();
+ if (!user) return this.close(CLOSECODES.Authentication_failed);
+
+ const public_user = {
+ username: user.username,
+ discriminator: user.discriminator,
+ id: user.id,
+ public_flags: user.public_flags,
+ avatar: user.avatar,
+ bot: user.bot,
+ };
+
+ const guilds = await GuildModel.find({ id: { $in: user.guilds } })
+ .populate({ path: "joined_at", match: { id: this.user_id } })
+ .exec();
+
+ const privateUser = {
+ avatar: user.avatar,
+ mobile: user.mobile,
+ desktop: user.desktop,
+ discriminator: user.discriminator,
+ email: user.email,
+ flags: user.flags,
+ id: user.id,
+ mfa_enabled: user.mfa_enabled,
+ nsfw_allowed: user.nsfw_allowed,
+ phone: user.phone,
+ premium: user.premium,
+ premium_type: user.premium_type,
+ public_flags: user.public_flags,
+ username: user.username,
+ verified: user.verified,
+ bot: user.bot,
+ accent_color: user.accent_color || 0,
+ banner: user.banner,
+ };
+
+ const d: ReadyEventData = {
+ v: 8,
+ user: privateUser,
+ user_settings: user.user_settings,
+ // @ts-ignore
+ guilds: toObject(guilds).map((x) => {
+ // @ts-ignore
+ x.guild_hashes = {
+ channels: { omitted: false, hash: "y4PV2fZ0gmo" },
+ metadata: { omitted: false, hash: "bs1/ckvud3Y" },
+ roles: { omitted: false, hash: "SxA+c5CaYpo" },
+ version: 1,
+ };
+ return x;
+ }),
+ guild_experiments: [], // TODO
+ geo_ordered_rtc_regions: [], // TODO
+ relationships: user.user_data.relationships,
+ read_state: {
+ // TODO
+ entries: [],
+ partial: false,
+ version: 304128,
+ },
+ user_guild_settings: {
+ entries: user_guild_settings_entries,
+ partial: false, // TODO partial
+ version: 642,
+ },
+ // @ts-ignore
+ private_channels: toObject(channels).map((x: ChannelDocument) => {
+ x.recipient_ids = x.recipients.map((y: any) => y.id);
+ delete x.recipients;
+ return x;
+ }),
+ session_id: "", // TODO
+ analytics_token: "", // TODO
+ connected_accounts: [], // TODO
+ consents: {
+ personalization: {
+ consented: false, // TODO
+ },
+ },
+ country_code: user.user_settings.locale,
+ friend_suggestion_count: 0, // TODO
+ // @ts-ignore
+ experiments: experiments, // TODO
+ guild_join_requests: [], // TODO what is this?
+ users: [
+ public_user,
+ ...toObject(channels)
+ .map((x: any) => x.recipients)
+ .flat(),
+ ].unique(), // TODO
+ merged_members: merged_members,
+ // shard // TODO: only for bots sharding
+ // application // TODO for applications
+ };
+
+ console.log("Send ready");
+
+ // TODO: send real proper data structure
+ await Send(this, {
+ op: OPCODES.Dispatch,
+ t: EVENTEnum.Ready,
+ s: this.sequence++,
+ d,
+ });
+
+ await setupListener.call(this);
+}
|