iceshrimp/packages/backend/src/server/api/stream/types.ts

316 lines
8.1 KiB
TypeScript

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 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";
import { Announcement } from "@/models/entities/announcement.js";
//#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"];
};
localUserUpdated: {
id: User["id"];
};
localUserDeleted: {
id: User["id"];
};
remoteUserUpdated: {
id: User["id"];
};
remoteUserDeleted: {
id: User["id"];
};
webhookCreated: Webhook;
webhookDeleted: Webhook;
webhookUpdated: Webhook;
antennaCreated: Antenna;
antennaDeleted: Antenna;
antennaUpdated: Antenna;
}
export interface BroadcastTypes {
emojiAdded: {
emoji: Packed<"Emoji">;
};
announcementAdded: Announcement;
announcementDeleted: Announcement["id"];
}
export interface UserStreamTypes {
terminate: Record<string, unknown>;
followChannel: Channel;
unfollowChannel: Channel;
updateUserProfile: UserProfile;
mute: User;
unmute: User;
follow: Packed<"UserDetailedNotMe">;
unfollow: Packed<"User">;
userAdded: Packed<"User">;
userHidden: User["id"];
userUnhidden: User["id"];
}
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"];
};
replied: {
id: Note["id"];
};
updated: {
updatedAt?: Note["updatedAt"];
};
}
type NoteStreamEventTypes = {
[key in keyof NoteStreamTypes]: {
id: Note["id"];
body: NoteStreamTypes[key];
};
};
export interface NoteUpdatesStreamTypes {
deleted: Note;
updated: Note;
}
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<T extends object> = { [K in keyof T]: { type: K; body: T[K] } };
type EventUnionFromDictionary<T extends object, U = Events<T>> = U[keyof U];
// name/messages(spec) pairs dictionary
export type StreamMessages = {
internal: {
name: "internal";
payload: EventUnionFromDictionary<InternalStreamTypes>;
};
broadcast: {
name: "broadcast";
payload: EventUnionFromDictionary<BroadcastTypes>;
};
user: {
name: `user:${User["id"]}`;
payload: EventUnionFromDictionary<UserStreamTypes>;
};
main: {
name: `mainStream:${User["id"]}`;
payload: EventUnionFromDictionary<MainStreamTypes>;
};
drive: {
name: `driveStream:${User["id"]}`;
payload: EventUnionFromDictionary<DriveStreamTypes>;
};
note: {
name: `noteStream:${Note["id"]}`;
payload: EventUnionFromDictionary<NoteStreamEventTypes>;
};
channel: {
name: `channelStream:${Channel["id"]}`;
payload: EventUnionFromDictionary<ChannelStreamTypes>;
};
userList: {
name: `userListStream:${UserList["id"]}`;
payload: EventUnionFromDictionary<UserListStreamTypes>;
};
antenna: {
name: `antennaStream:${Antenna["id"]}`;
payload: EventUnionFromDictionary<AntennaStreamTypes>;
};
messaging: {
name: `messagingStream:${User["id"]}-${User["id"]}`;
payload: EventUnionFromDictionary<MessagingStreamTypes>;
};
groupMessaging: {
name: `messagingStream:${UserGroup["id"]}`;
payload: EventUnionFromDictionary<GroupMessagingStreamTypes>;
};
messagingIndex: {
name: `messagingIndexStream:${User["id"]}`;
payload: EventUnionFromDictionary<MessagingIndexStreamTypes>;
};
admin: {
name: `adminStream:${User["id"]}`;
payload: EventUnionFromDictionary<AdminStreamTypes>;
};
notes: {
name: "notesStream";
payload: Note;
};
noteUpdates: {
name: "noteUpdatesStream";
payload: EventUnionFromDictionary<NoteUpdatesStreamTypes>;
};
};
// 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> = (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"];