import type { EventEmitter } from "events"; import type Emitter from "strict-event-emitter-types"; import type { Channel } from "@/models/entities/channel.js"; import type { User } from "@/models/entities/user.js"; import type { UserProfile } from "@/models/entities/user-profile.js"; import type { Note } from "@/models/entities/note.js"; import type { Antenna } from "@/models/entities/antenna.js"; import type { DriveFile } from "@/models/entities/drive-file.js"; import type { DriveFolder } from "@/models/entities/drive-folder.js"; import { Emoji } from "@/models/entities/emoji.js"; import type { UserList } from "@/models/entities/user-list.js"; import type { MessagingMessage } from "@/models/entities/messaging-message.js"; import type { UserGroup } from "@/models/entities/user-group.js"; import type { AbuseUserReport } from "@/models/entities/abuse-user-report.js"; import type { Signin } from "@/models/entities/signin.js"; import type { Page } from "@/models/entities/page.js"; import type { Packed } from "@/misc/schema.js"; import type { Webhook } from "@/models/entities/webhook"; //#region Stream type-body definitions export interface InternalStreamTypes { userChangeSuspendedState: { id: User["id"]; isSuspended: User["isSuspended"]; }; userChangeSilencedState: { id: User["id"]; isSilenced: User["isSilenced"] }; userChangeModeratorState: { id: User["id"]; isModerator: User["isModerator"]; }; userTokenRegenerated: { id: User["id"]; oldToken: User["token"]; newToken: User["token"]; }; remoteUserUpdated: { id: User["id"] }; webhookCreated: Webhook; webhookDeleted: Webhook; webhookUpdated: Webhook; antennaCreated: Antenna; antennaDeleted: Antenna; antennaUpdated: Antenna; } export interface BroadcastTypes { emojiAdded: { emoji: Packed<"Emoji">; }; } export interface UserStreamTypes { terminate: Record; followChannel: Channel; unfollowChannel: Channel; updateUserProfile: UserProfile; mute: User; unmute: User; follow: Packed<"UserDetailedNotMe">; unfollow: Packed<"User">; userAdded: Packed<"User">; } export interface MainStreamTypes { notification: Packed<"Notification">; mention: Packed<"Note">; reply: Packed<"Note">; renote: Packed<"Note">; follow: Packed<"UserDetailedNotMe">; followed: Packed<"User">; unfollow: Packed<"User">; meUpdated: Packed<"User">; pageEvent: { pageId: Page["id"]; event: string; var: any; userId: User["id"]; user: Packed<"User">; }; urlUploadFinished: { marker?: string | null; file: Packed<"DriveFile">; }; readAllNotifications: undefined; unreadNotification: Packed<"Notification">; unreadMention: Note["id"]; readAllUnreadMentions: undefined; unreadSpecifiedNote: Note["id"]; readAllUnreadSpecifiedNotes: undefined; readAllMessagingMessages: undefined; messagingMessage: Packed<"MessagingMessage">; unreadMessagingMessage: Packed<"MessagingMessage">; readAllAntennas: undefined; unreadAntenna: Antenna; readAllAnnouncements: undefined; readAllChannels: undefined; unreadChannel: Note["id"]; myTokenRegenerated: undefined; signin: Signin; registryUpdated: { scope?: string[]; key: string; value: any | null; }; driveFileCreated: Packed<"DriveFile">; readAntenna: Antenna; receiveFollowRequest: Packed<"User">; } export interface DriveStreamTypes { fileCreated: Packed<"DriveFile">; fileDeleted: DriveFile["id"]; fileUpdated: Packed<"DriveFile">; folderCreated: Packed<"DriveFolder">; folderDeleted: DriveFolder["id"]; folderUpdated: Packed<"DriveFolder">; } export interface NoteStreamTypes { pollVoted: { choice: number; userId: User["id"]; }; deleted: { deletedAt: Date; }; reacted: { reaction: string; emoji?: { name: string; url: string; } | null; userId: User["id"]; }; unreacted: { reaction: string; userId: User["id"]; }; } type NoteStreamEventTypes = { [key in keyof NoteStreamTypes]: { id: Note["id"]; body: NoteStreamTypes[key]; }; }; export interface ChannelStreamTypes { typing: User["id"]; } export interface UserListStreamTypes { userAdded: Packed<"User">; userRemoved: Packed<"User">; } export interface AntennaStreamTypes { note: Note; } export interface MessagingStreamTypes { read: MessagingMessage["id"][]; typing: User["id"]; message: Packed<"MessagingMessage">; deleted: MessagingMessage["id"]; } export interface GroupMessagingStreamTypes { read: { ids: MessagingMessage["id"][]; userId: User["id"]; }; typing: User["id"]; message: Packed<"MessagingMessage">; deleted: MessagingMessage["id"]; } export interface MessagingIndexStreamTypes { read: MessagingMessage["id"][]; message: Packed<"MessagingMessage">; } export interface AdminStreamTypes { newAbuseUserReport: { id: AbuseUserReport["id"]; targetUserId: User["id"]; reporterId: User["id"]; comment: string; }; } //#endregion // 辞書(interface or type)から{ type, body }ユニオンを定義 // https://stackoverflow.com/questions/49311989/can-i-infer-the-type-of-a-value-using-extends-keyof-type // VS Codeの展開を防止するためにEvents型を定義 type Events = { [K in keyof T]: { type: K; body: T[K] } }; type EventUnionFromDictionary> = U[keyof U]; // name/messages(spec) pairs dictionary export type StreamMessages = { internal: { name: "internal"; payload: EventUnionFromDictionary; }; broadcast: { name: "broadcast"; payload: EventUnionFromDictionary; }; user: { name: `user:${User["id"]}`; payload: EventUnionFromDictionary; }; main: { name: `mainStream:${User["id"]}`; payload: EventUnionFromDictionary; }; drive: { name: `driveStream:${User["id"]}`; payload: EventUnionFromDictionary; }; note: { name: `noteStream:${Note["id"]}`; payload: EventUnionFromDictionary; }; channel: { name: `channelStream:${Channel["id"]}`; payload: EventUnionFromDictionary; }; userList: { name: `userListStream:${UserList["id"]}`; payload: EventUnionFromDictionary; }; antenna: { name: `antennaStream:${Antenna["id"]}`; payload: EventUnionFromDictionary; }; messaging: { name: `messagingStream:${User["id"]}-${User["id"]}`; payload: EventUnionFromDictionary; }; groupMessaging: { name: `messagingStream:${UserGroup["id"]}`; payload: EventUnionFromDictionary; }; messagingIndex: { name: `messagingIndexStream:${User["id"]}`; payload: EventUnionFromDictionary; }; admin: { name: `adminStream:${User["id"]}`; payload: EventUnionFromDictionary; }; notes: { name: "notesStream"; payload: Note; }; }; // API event definitions // ストリームごとのEmitterの辞書を用意 type EventEmitterDictionary = { [x in keyof StreamMessages]: Emitter< EventEmitter, { [y in StreamMessages[x]["name"]]: ( e: StreamMessages[x]["payload"], ) => void; } >; }; // 共用体型を交差型にする型 https://stackoverflow.com/questions/54938141/typescript-convert-union-to-intersection type UnionToIntersection = (U extends any ? (k: U) => void : never) extends ( k: infer I, ) => void ? I : never; // Emitter辞書から共用体型を作り、UnionToIntersectionで交差型にする export type StreamEventEmitter = UnionToIntersection< EventEmitterDictionary[keyof StreamMessages] >; // { [y in name]: (e: spec) => void }をまとめてその交差型をEmitterにかけるとts(2590)にひっかかる // provide stream channels union export type StreamChannels = StreamMessages[keyof StreamMessages]["name"];