Newer
Older
percord / src / util / interfaces / Event.ts
@MathMan05 MathMan05 on 3 Feb 21 KB some thread fixes
/*
	Spacebar: A FOSS re-implementation and extension of the Discord.com backend.
	Copyright (C) 2023 Spacebar and Spacebar Contributors

	This program is free software: you can redistribute it and/or modify
	it under the terms of the GNU Affero General Public License as published
	by the Free Software Foundation, either version 3 of the License, or
	(at your option) any later version.

	This program is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU Affero General Public License for more details.

	You should have received a copy of the GNU Affero General Public License
	along with this program.  If not, see <https://www.gnu.org/licenses/>.
*/

import {
    ConnectedAccount,
    Interaction,
    Message,
    Invite,
    Role,
    Emoji,
    Channel,
    User,
    Sticker,
    Activity,
    Status,
    Presence,
    UserSettings,
    IReadyGuildDTO,
    ReadState,
    ReadyUserGuildSettingsEntries,
    ReadyPrivateChannel,
    GuildOrUnavailable,
    Snowflake,
} from "@spacebar/util";
import { JsonValue } from "@protobuf-ts/runtime";
import { ApplicationCommand, GuildCreateResponse, PartialEmoji, PublicMember, PublicUser, PublicVoiceState, RelationshipType, UserPrivate } from "@spacebar/schemas";
import { ThreadMember } from "../entities/ThreadMember";

export interface Event {
    guild_id?: string;
    user_id?: string;
    channel_id?: string;
    created_at?: Date;
    event: EVENT;
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    data?: any;
    origin?: string;
}

// ! Custom Events that shouldn't get sent to the client but processed by the server

export interface InvalidatedEvent extends Event {
    event: "INVALIDATED";
}

export interface PublicRelationship {
    id: string;
    user: PublicUser;
    type: RelationshipType;
    nickname?: string;
}

// ! END Custom Events that shouldn't get sent to the client but processed by the server

export interface ReadyEventData {
    v: number;
    user: UserPrivate;
    private_channels: ReadyPrivateChannel[]; // this will be empty for bots
    presences: Presence[];
    session_id: string; // resuming
    guilds: IReadyGuildDTO[] | GuildOrUnavailable[]; // depends on capability
    analytics_token?: string;
    connected_accounts?: ConnectedAccount[];
    consents?: {
        personalization?: {
            consented?: boolean;
        };
    };
    country_code?: string; // e.g. DE
    friend_suggestion_count?: number;
    geo_ordered_rtc_regions?: string[]; // ["europe","russie","india","us-east","us-central"]
    experiments?: [number, number, number, number, number][];
    guild_experiments?: [
        // ? what are guild_experiments?
        // this is the structure of it:
        number,
        null,
        number,
        [[number, { e: number; s: number }[]]],
        [number, [[number, [number, number]]]],
        { b: number; k: bigint[] }[],
    ][];
    guild_join_requests?: unknown[]; // ? what is this? this is new
    shard?: [number, number];
    user_settings?: UserSettings;
    user_settings_proto?: string;
    user_settings_proto_json?: JsonValue;
    relationships?: PublicRelationship[]; // TODO
    read_state: {
        entries: ReadState[]; // TODO
        partial: boolean;
        version: number;
    };
    user_guild_settings?: {
        entries: ReadyUserGuildSettingsEntries[];
        version: number;
        partial: boolean;
    };
    application?: {
        id: string;
        flags: number;
    };
    merged_members?: PublicMember[][];
    // probably all users who the user is in contact with
    users?: PublicUser[];
    sessions: unknown[];
    api_code_version: number;
    tutorial: number | null;
    resume_gateway_url: string;
    session_type: string;
    auth_session_id_hash: string;
    required_action?:
        | "REQUIRE_VERIFIED_EMAIL"
        | "REQUIRE_VERIFIED_PHONE"
        | "REQUIRE_CAPTCHA" // TODO: allow these to be triggered
        | "TOS_UPDATE_ACKNOWLEDGMENT"
        | "AGREEMENTS";
    notification_settings: {
        flags: number;
    };
    game_relationships: never[]; // what is this?
    auth_token?: string; // if enabled in capabilities
    _trace?: string[]; // trace of the request, used for debugging
}

export type TraceNode = { micros: number; calls: TraceNode[] } | { micros: number } | string;

export type TraceRoot = [string, { micros: number; calls: TraceNode[] }];

export interface ReadyEvent extends Event {
    event: "READY";
    data: ReadyEventData;
}

export interface ChannelCreateEvent extends Event {
    event: "CHANNEL_CREATE";
    data: Channel;
}

export interface ChannelUpdateEvent extends Event {
    event: "CHANNEL_UPDATE";
    data: Channel;
}

export interface ChannelDeleteEvent extends Event {
    event: "CHANNEL_DELETE";
    data: Channel;
}

export interface ChannelPinsUpdateEvent extends Event {
    event: "CHANNEL_PINS_UPDATE";
    data: {
        guild_id?: string;
        channel_id: string;
        /**
         * @format ISO8601
         */
        last_pin_timestamp?: string;
    };
}

export interface ChannelRecipientAddEvent extends Event {
    event: "CHANNEL_RECIPIENT_ADD";
    data: {
        channel_id: string;
        user: User;
    };
}

export interface ChannelRecipientRemoveEvent extends Event {
    event: "CHANNEL_RECIPIENT_REMOVE";
    data: {
        channel_id: string;
        user: User;
    };
}

export interface GuildCreateEvent extends Event {
    event: "GUILD_CREATE";
    data: IReadyGuildDTO & {
        joined_at: Date;
        // TODO: add them to guild
        guild_scheduled_events: never[];
        guild_hashes: unknown;
        presences: never[];
        stage_instances: never[];
        threads: never[];
        embedded_activities: never[];
    };
}

export interface GuildUpdateEvent extends Event {
    event: "GUILD_UPDATE";
    data: GuildCreateResponse;
}

export interface GuildDeleteEvent extends Event {
    event: "GUILD_DELETE";
    data: {
        id: string;
        unavailable?: boolean;
    };
}

export interface GuildBanAddEvent extends Event {
    event: "GUILD_BAN_ADD";
    data: {
        guild_id: string;
        user: User;
        delete_message_secs?: number;
    };
}

export interface GuildBanRemoveEvent extends Event {
    event: "GUILD_BAN_REMOVE";
    data: {
        guild_id: string;
        user: User;
    };
}

export interface GuildEmojisUpdateEvent extends Event {
    event: "GUILD_EMOJIS_UPDATE";
    data: {
        guild_id: string;
        emojis: Emoji[];
    };
}

export interface GuildStickersUpdateEvent extends Event {
    event: "GUILD_STICKERS_UPDATE";
    data: {
        guild_id: string;
        stickers: Sticker[];
    };
}

export interface GuildIntegrationUpdateEvent extends Event {
    event: "GUILD_INTEGRATIONS_UPDATE";
    data: {
        guild_id: string;
    };
}

export interface GuildMemberAddEvent extends Event {
    event: "GUILD_MEMBER_ADD";
    data: PublicMember & {
        guild_id: string;
    };
}

export interface GuildMemberRemoveEvent extends Event {
    event: "GUILD_MEMBER_REMOVE";
    data: {
        guild_id: string;
        user: User;
    };
}

export interface GuildMemberUpdateEvent extends Event {
    event: "GUILD_MEMBER_UPDATE";
    data: {
        guild_id: string;
        roles: string[];
        user: User;
        nick?: string;
        joined_at?: Date;
        premium_since?: number;
        pending?: boolean;
    };
}

export interface GuildMembersChunkEvent extends Event {
    event: "GUILD_MEMBERS_CHUNK";
    data: {
        guild_id: string;
        members: PublicMember[];
        chunk_index: number;
        chunk_count: number;
        not_found?: string[];
        presences?: Presence[];
        nonce?: string;
    };
}

export interface GuildRoleCreateEvent extends Event {
    event: "GUILD_ROLE_CREATE";
    data: {
        guild_id: string;
        role: Role;
    };
}

export interface GuildRoleUpdateEvent extends Event {
    event: "GUILD_ROLE_UPDATE";
    data: {
        guild_id: string;
        role: Role;
    };
}

export interface GuildRoleDeleteEvent extends Event {
    event: "GUILD_ROLE_DELETE";
    data: {
        guild_id: string;
        role_id: string;
    };
}

export interface InviteCreateEvent extends Event {
    event: "INVITE_CREATE";
    data: Omit<Invite, "guild" | "channel"> & {
        channel_id: string;
        guild_id?: string;
    };
}

export interface InviteDeleteEvent extends Event {
    event: "INVITE_DELETE";
    data: {
        channel_id: string;
        guild_id?: string;
        code: string;
    };
}

export interface MessageCreateEvent extends Event {
    event: "MESSAGE_CREATE";
    data: Message;
}

export interface MessageUpdateEvent extends Event {
    event: "MESSAGE_UPDATE";
    data: Message;
}

export interface MessageDeleteEvent extends Event {
    event: "MESSAGE_DELETE";
    data: {
        id: string;
        channel_id: string;
        guild_id?: string;
    };
}

export interface MessageDeleteBulkEvent extends Event {
    event: "MESSAGE_DELETE_BULK";
    data: {
        ids: string[];
        channel_id: string;
        guild_id?: string;
    };
}

export interface MessageReactionAddEvent extends Event {
    event: "MESSAGE_REACTION_ADD";
    data: {
        user_id: string;
        channel_id: string;
        message_id: string;
        guild_id?: string;
        member?: PublicMember;
        emoji: PartialEmoji;
    };
}

export interface MessageReactionRemoveEvent extends Event {
    event: "MESSAGE_REACTION_REMOVE";
    data: {
        user_id: string;
        channel_id: string;
        message_id: string;
        guild_id?: string;
        emoji: PartialEmoji;
    };
}

export interface MessageReactionRemoveAllEvent extends Event {
    event: "MESSAGE_REACTION_REMOVE_ALL";
    data: {
        channel_id: string;
        message_id: string;
        guild_id?: string;
    };
}

export interface MessageReactionRemoveEmojiEvent extends Event {
    event: "MESSAGE_REACTION_REMOVE_EMOJI";
    data: {
        channel_id: string;
        message_id: string;
        guild_id?: string;
        emoji: PartialEmoji;
    };
}

export interface PresenceUpdateEvent extends Event {
    event: "PRESENCE_UPDATE";
    data: Presence;
}

export interface TypingStartEvent extends Event {
    event: "TYPING_START";
    data: {
        channel_id: string;
        user_id: string;
        timestamp: number;
        guild_id?: string;
        member?: PublicMember;
    };
}

export interface UserUpdateEvent extends Event {
    event: "USER_UPDATE";
    data: Omit<User, "data">;
}

export interface UserDeleteEvent extends Event {
    event: "USER_DELETE";
    data: {
        user_id: string;
    };
}

export interface UserConnectionsUpdateEvent extends Event {
    event: "USER_CONNECTIONS_UPDATE";
}

export interface VoiceStateUpdateEvent extends Event {
    event: "VOICE_STATE_UPDATE";
    data: PublicVoiceState & {
        member: PublicMember;
    };
}

export interface VoiceServerUpdateEvent extends Event {
    event: "VOICE_SERVER_UPDATE";
    data: {
        token: string;
        guild_id: string | null;
        endpoint: string;
        channel_id?: string;
    };
}

export interface StreamCreateEvent extends Event {
    event: "STREAM_CREATE";
    data: {
        stream_key: string;
        rtc_server_id: string;
        viewer_ids: string[];
        region: string;
        paused: boolean;
    };
}

export interface StreamServerUpdateEvent extends Event {
    event: "STREAM_SERVER_UPDATE";
    data: {
        token: string;
        stream_key: string;
        endpoint: string;
        guild_id: string | null;
    };
}

export interface StreamDeleteEvent extends Event {
    event: "STREAM_DELETE";
    data: {
        stream_key: string;
    };
}

export interface WebhooksUpdateEvent extends Event {
    event: "WEBHOOKS_UPDATE";
    data: {
        guild_id: string;
        channel_id: string;
    };
}

export type ApplicationCommandPayload = ApplicationCommand & {
    guild_id: string;
};

export interface ApplicationCommandCreateEvent extends Event {
    event: "APPLICATION_COMMAND_CREATE";
    data: ApplicationCommandPayload;
}

export interface ApplicationCommandUpdateEvent extends Event {
    event: "APPLICATION_COMMAND_UPDATE";
    data: ApplicationCommandPayload;
}

export interface ApplicationCommandDeleteEvent extends Event {
    event: "APPLICATION_COMMAND_DELETE";
    data: ApplicationCommandPayload;
}

export interface InteractionCreateEvent extends Event {
    event: "INTERACTION_CREATE";
    data:
        | Interaction
        | {
              id: Snowflake;
              nonce?: string;
          };
}

export interface InteractionSuccessEvent extends Event {
    event: "INTERACTION_SUCCESS";
    data: {
        id: Snowflake;
        nonce: string;
    };
}

export interface InteractionFailureEvent extends Event {
    event: "INTERACTION_FAILURE";
    data: {
        id: Snowflake;
        nonce?: string;
        reason_code: number; // TODO: types?
    };
}

export interface MessageAckEvent extends Event {
    event: "MESSAGE_ACK";
    data: {
        channel_id: string;
        message_id: string;
        version?: number;
        manual?: boolean;
        mention_count?: number;
    };
}

export interface RelationshipAddEvent extends Event {
    event: "RELATIONSHIP_ADD";
    data: PublicRelationship & {
        should_notify?: boolean;
        user: PublicUser;
    };
}
export interface RelationshipUpdateEvent extends Event {
    event: "RELATIONSHIP_UPDATE";
    data: PublicRelationship & {
        should_notify?: boolean;
        user: PublicUser;
    };
}

export interface RelationshipRemoveEvent extends Event {
    event: "RELATIONSHIP_REMOVE";
    data: Omit<PublicRelationship, "nickname">;
}

export interface GatewaySessionClientInfo {
    version: number;
    os: string;
    client: string;
}

export interface GatewaySession {
    session_id: string;
    activities: Activity[];
    hidden_activities: Activity[];
    client_info: GatewaySessionClientInfo;
    status: Status;
    active?: boolean; // How is this even defined?
}

export interface SessionsReplace extends Event {
    event: "SESSIONS_REPLACE";
    data: GatewaySession[];
}

export interface GuildMemberListUpdate extends Event {
    event: "GUILD_MEMBER_LIST_UPDATE";
    data: {
        groups: { id: string; count: number }[];
        guild_id: string;
        id: string;
        member_count: number;
        online_count: number;
        ops: {
            index: number;
            item: {
                member?: PublicMember & { presence: Presence };
                group?: { id: string; count: number }[];
            };
        }[];
    };
}

export interface ThreadCreateEvent extends Event {
    event: "THREAD_CREATE";
    data: Channel & { newly_created: boolean };
}

export interface ThreadUpdatEvent extends Event {
    event: "THREAD_UPDATE";
    data: Channel;
}

export interface ThreadDeleteEvent extends Event {
    event: "THREAD_DELETE";
    data: Pick<Channel, "id" | "guild_id" | "parent_id" | "type">;
}

export interface ThreadListSyncEvent extends Event {
    event: "THREAD_LIST_SYNC";
    data: {
        guild_id: string;
        channel_ids?: string[];
        threads: Channel[];
        members: ThreadMember[];
    };
}

export interface ThreadMemberUpdateEvent extends Event {
    event: "THREAD_MEMBER_UPDATE";
    data: ThreadMember & { guild_id: string };
}

export interface ThreadMembersUpdateEvent extends Event {
    event: "THREAD_MEMBERS_UPDATE";
    data: {
        id: string;
        guild_id: string;
        member_count: number;
        added_members?: (ThreadMember & { user_id: string })[];
        removed_member_ids?: string[];
    };
}

export type EventData =
    | InvalidatedEvent
    | ReadyEvent
    | ChannelCreateEvent
    | ChannelUpdateEvent
    | ChannelDeleteEvent
    | ChannelPinsUpdateEvent
    | ChannelRecipientAddEvent
    | ChannelRecipientRemoveEvent
    | GuildCreateEvent
    | GuildUpdateEvent
    | GuildDeleteEvent
    | GuildBanAddEvent
    | GuildBanRemoveEvent
    | GuildEmojisUpdateEvent
    | GuildIntegrationUpdateEvent
    | GuildMemberAddEvent
    | GuildMemberRemoveEvent
    | GuildMemberUpdateEvent
    | GuildMembersChunkEvent
    | GuildMemberListUpdate
    | GuildRoleCreateEvent
    | GuildRoleUpdateEvent
    | GuildRoleDeleteEvent
    | InviteCreateEvent
    | InviteDeleteEvent
    | MessageCreateEvent
    | MessageUpdateEvent
    | MessageDeleteEvent
    | MessageDeleteBulkEvent
    | MessageReactionAddEvent
    | MessageReactionRemoveEvent
    | MessageReactionRemoveAllEvent
    | MessageReactionRemoveEmojiEvent
    | PresenceUpdateEvent
    | TypingStartEvent
    | UserUpdateEvent
    | UserDeleteEvent
    | UserConnectionsUpdateEvent
    | VoiceStateUpdateEvent
    | VoiceServerUpdateEvent
    | WebhooksUpdateEvent
    | ApplicationCommandCreateEvent
    | ApplicationCommandUpdateEvent
    | ApplicationCommandDeleteEvent
    | InteractionCreateEvent
    | InteractionSuccessEvent
    | InteractionFailureEvent
    | MessageAckEvent
    | RelationshipAddEvent
    | RelationshipRemoveEvent
    | ThreadCreateEvent
    | ThreadUpdatEvent
    | ThreadDeleteEvent
    | ThreadListSyncEvent
    | ThreadMemberUpdateEvent
    | ThreadMembersUpdateEvent;

// located in collection events

export enum EVENTEnum {
    Ready = "READY",
    ReadySupplemental = "READY_SUPPLEMENTAL",
    ChannelCreate = "CHANNEL_CREATE",
    ChannelUpdate = "CHANNEL_UPDATE",
    ChannelDelete = "CHANNEL_DELETE",
    ChannelPinsUpdate = "CHANNEL_PINS_UPDATE",
    ChannelRecipientAdd = "CHANNEL_RECIPIENT_ADD",
    ChannelRecipientRemove = "CHANNEL_RECIPIENT_REMOVE",
    GuildCreate = "GUILD_CREATE",
    GuildUpdate = "GUILD_UPDATE",
    GuildDelete = "GUILD_DELETE",
    GuildBanAdd = "GUILD_BAN_ADD",
    GuildBanRemove = "GUILD_BAN_REMOVE",
    GuildEmojUpdate = "GUILD_EMOJI_UPDATE",
    GuildIntegrationsUpdate = "GUILD_INTEGRATIONS_UPDATE",
    GuildMemberAdd = "GUILD_MEMBER_ADD",
    GuildMemberRempve = "GUILD_MEMBER_REMOVE",
    GuildMemberUpdate = "GUILD_MEMBER_UPDATE",
    GuildMemberSpeaking = "GUILD_MEMBER_SPEAKING",
    GuildMembersChunk = "GUILD_MEMBERS_CHUNK",
    GuildMemberListUpdate = "GUILD_MEMBER_LIST_UPDATE",
    GuildRoleCreate = "GUILD_ROLE_CREATE",
    GuildRoleDelete = "GUILD_ROLE_DELETE",
    GuildRoleUpdate = "GUILD_ROLE_UPDATE",
    InviteCreate = "INVITE_CREATE",
    InviteDelete = "INVITE_DELETE",
    MessageCreate = "MESSAGE_CREATE",
    MessageUpdate = "MESSAGE_UPDATE",
    MessageDelete = "MESSAGE_DELETE",
    MessageDeleteBulk = "MESSAGE_DELETE_BULK",
    MessageReactionAdd = "MESSAGE_REACTION_ADD",
    MessageReactionRemove = "MESSAGE_REACTION_REMOVE",
    MessageReactionRemoveAll = "MESSAGE_REACTION_REMOVE_ALL",
    MessageReactionRemoveEmoji = "MESSAGE_REACTION_REMOVE_EMOJI",
    PresenceUpdate = "PRESENCE_UPDATE",
    TypingStart = "TYPING_START",
    UserUpdate = "USER_UPDATE",
    UserDelete = "USER_DELETE",
    UserConnectionsUpdate = "USER_CONNECTIONS_UPDATE",
    WebhooksUpdate = "WEBHOOKS_UPDATE",
    InteractionCreate = "INTERACTION_CREATE",
    InteractionSuccess = "INTERACTION_SUCCESS",
    InteractionFailure = "INTERACTION_FAILURE",
    VoiceStateUpdate = "VOICE_STATE_UPDATE",
    VoiceServerUpdate = "VOICE_SERVER_UPDATE",
    ApplicationCommandCreate = "APPLICATION_COMMAND_CREATE",
    ApplicationCommandUpdate = "APPLICATION_COMMAND_UPDATE",
    ApplicationCommandDelete = "APPLICATION_COMMAND_DELETE",
    SessionsReplace = "SESSIONS_REPLACE",
    ThreadCreate = "THREAD_CREATE",
    ThreadUpdate = "THREAD_UPDATE",
    ThreadDelete = "THREAD_DELETE",
    ThreadListSync = "THREAD_LIST_SYNC",
    ThreadMemberUpdate = "THREAD_MEMBER_UPDATE",
    ThreadMembersUpdate = "THREAD_MEMBERS_UPDATE",
}

export type EVENT =
    | "READY"
    | "CHANNEL_CREATE"
    | "CHANNEL_UPDATE"
    | "CHANNEL_DELETE"
    | "CHANNEL_PINS_UPDATE"
    | "CHANNEL_RECIPIENT_ADD"
    | "CHANNEL_RECIPIENT_REMOVE"
    | "GUILD_CREATE"
    | "GUILD_UPDATE"
    | "GUILD_DELETE"
    | "GUILD_BAN_ADD"
    | "GUILD_BAN_REMOVE"
    | "GUILD_EMOJIS_UPDATE"
    | "GUILD_STICKERS_UPDATE"
    | "GUILD_INTEGRATIONS_UPDATE"
    | "GUILD_MEMBER_ADD"
    | "GUILD_MEMBER_REMOVE"
    | "GUILD_MEMBER_UPDATE"
    | "GUILD_MEMBER_SPEAKING"
    | "GUILD_MEMBERS_CHUNK"
    | "GUILD_MEMBER_LIST_UPDATE"
    | "GUILD_ROLE_CREATE"
    | "GUILD_ROLE_DELETE"
    | "GUILD_ROLE_UPDATE"
    | "INVITE_CREATE"
    | "INVITE_DELETE"
    | "MESSAGE_CREATE"
    | "MESSAGE_UPDATE"
    | "MESSAGE_DELETE"
    | "MESSAGE_DELETE_BULK"
    | "MESSAGE_REACTION_ADD"
    // TODO: add a new event: bulk add reaction:
    // | "MESSAGE_REACTION_BULK_ADD"
    | "MESSAGE_REACTION_REMOVE"
    | "MESSAGE_REACTION_REMOVE_ALL"
    | "MESSAGE_REACTION_REMOVE_EMOJI"
    | "PRESENCE_UPDATE"
    | "TYPING_START"
    | "USER_UPDATE"
    | "USER_DELETE"
    | "USER_CONNECTIONS_UPDATE"
    | "USER_NOTE_UPDATE"
    | "WEBHOOKS_UPDATE"
    | "INTERACTION_CREATE"
    | "INTERACTION_SUCCESS"
    | "INTERACTION_FAILURE"
    | "VOICE_STATE_UPDATE"
    | "VOICE_SERVER_UPDATE"
    | "STREAM_CREATE"
    | "STREAM_SERVER_UPDATE"
    | "STREAM_DELETE"
    | "APPLICATION_COMMAND_CREATE"
    | "APPLICATION_COMMAND_UPDATE"
    | "APPLICATION_COMMAND_DELETE"
    | "MESSAGE_ACK"
    | "RELATIONSHIP_ADD"
    | "RELATIONSHIP_REMOVE"
    | "RELATIONSHIP_UPDATE"
    | "SESSIONS_REPLACE"
    | "USER_SETTINGS_PROTO_UPDATE"
    | "THREAD_CREATE"
    | "THREAD_UPDATE"
    | "THREAD_DELETE"
    | "THREAD_LIST_SYNC"
    | "THREAD_MEMBER_UPDATE"
    | "THREAD_MEMBERS_UPDATE"
    | CUSTOMEVENTS;

export type CUSTOMEVENTS = "INVALIDATED" | "RATELIMIT";