import { z } from "zod"; import { legalDocsSchema } from "./features/legal/schema"; import { settingsStorageDefaults, type SettingsStorageDefaults, } from "./settings"; const fileFromMessage = z.object({ name: z.string(), id: z.uuidv4(), type: z.enum(["image", "image_top_right", "file"]), }); export const message = z.object({ height: z.number(), not_encrypted: z.boolean().optional(), sent_by_self: z.boolean().optional(), send_time: z.number(), content: z.base64(), files: z.array(fileFromMessage).optional(), tint: z.string().length(7).startsWith("#").optional(), avatar: z.boolean().optional(), display: z.boolean().optional(), message_state: z .enum(["read", "received", "sent", "sending", "awaiting"]) .default("received"), // awaiting for 'internal' use }); export const failedUser = { display: "Failed", iota_id: 0, omikron_connections: [], online_status: "user_borked", public_key: "", sub_end: 0, sub_level: 0, user_id: 0, username: "unknown", } as z.infer; export type Contacts = z.infer< typeof ttp.challenge_response.response.shape.contacts >; export type Communities = z.infer< typeof ttp.challenge_response.response.shape.communities >; export type Calls = z.infer; // TTP const user = z.object({ about: z.string().max(255).optional(), avatar: z.string().optional(), display: z.string().min(1).max(15), iota_id: z.number(), omikron_connections: z.array(z.number()), omikron_id: z.number().optional(), online_status: z.enum([ "user_offline", "user_online", "user_dnd", "user_idle", "user_wc", "user_borked", "iota_offline", "iota_online", "iota_borked", ]), public_key: z.base64(), status: z.string().max(15).optional(), sub_end: z.number(), sub_level: z.number(), user_id: z.number(), username: z.string().min(1).max(15), }); export const ttp = { identification: { request: z.object({ version: z.string(), session_id: z.number(), user_id: z.number(), }), response: z.object({ challenge: z.string(), public_key: z.base64(), }), }, challenge_response: { request: z.object({ challenge: z.base64(), }), response: z.object({ communities: z.array(z.object({})).optional(), contacts: z.array( z.object({ last_message_at: z.number(), user_id: z.number(), last_message: z .object({ content: z.base64(), sender_id: z.number(), }) .optional(), messages: z.array(message), }), ), calls: z.array( z.object({ call_id: z.string(), call_secret: z.base64().optional(), call_members: z.array(z.number()), }), ), }), }, get_user_data: { request: z.object({ user_id: z.number().optional(), username: z.string().optional(), }), response: user, }, change_user_data: { request: user.partial(), response: z.object({}), }, ping: { request: z.object({ last_ping: z.number(), }), response: z.object({ ping_iota: z.number(), }), }, message_live: { request: z.object({}).optional(), response: z.object({ sender_id: z.number(), message, }), }, messages_get: { request: z.object({ user_id: z.number(), amount: z.number(), offset: z.number(), }), response: z.object({ messages: z.array(message), }), }, message_send: { request: z.object({ height: z.number(), content: z.base64(), receiver_id: z.number(), send_time: z.number(), files: z.array(fileFromMessage).optional(), }), response: z.object({}), }, add_conversation: { request: z.object({ chat_partner_id: z.number().optional(), chat_partner_name: z.string().min(1).max(15).optional(), }), response: z.object({}), }, message_state: { request: z.object({ chat_partner_id: z.number(), send_time: z.number(), message_state: message.shape.message_state, }), response: z.object({ chat_partner_id: z.number(), message_state: message.shape.message_state, send_time: z.number(), }), }, load_txt_record: { request: z.object({ path: z.string(), }), response: z.object({ content: z.string(), }), }, authenticate_app: { request: z.object({ app_identifier: z.string(), }), response: z.object({ challenge: z.base64(), }), }, create_app: { request: z.object({ app_public_key: z.base64(), app_identifier: z.string(), }), response: z.object({}), }, // Calls call_token: { request: z.object({ call_id: z.string(), }), response: z.object({ call_token: z.string(), }), }, call_data: { request: z.object({ call_id: z.string(), }), response: z.object({ user_ids: z.array(z.number()), }), }, call_invite: { request: z.object({ call_id: z.string(), call_secret: z.base64(), receiver_id: z.number(), }), response: z.object({ call_id: z.string().optional(), call_secret: z.base64().optional(), sender_id: z.number().optional(), }), }, } satisfies Record; export type TTP = typeof ttp; // Storage export interface Storage extends SettingsStorageDefaults { session_id: number; user_id: number; private_key: string; ppandtos_done: boolean; accepted_terms_of_service: boolean; accepted_privacy_policy: boolean; analytics_crash_reports: boolean; analytics_usage_data: boolean; analytics_done: boolean; legal_docs: z.infer; cached_contacts: Contacts; cached_communities: Communities; ttp_url: string; call_mute_range_start: number; call_mute_range_end: number; } export const storageDefaults: Storage = { session_id: 0, user_id: 0, private_key: "", ppandtos_done: false, accepted_terms_of_service: false, accepted_privacy_policy: false, analytics_crash_reports: true, analytics_usage_data: true, analytics_done: false, ...settingsStorageDefaults, legal_docs: { eula: { version: "0.0", hash: "000000000000", unix: 0, }, tos: { version: "0.0", hash: "000000000000", unix: 0, }, pp: { version: "0.0", hash: "000000000000", unix: 0, }, }, cached_contacts: [], cached_communities: [], ttp_url: "https://tensamin.net:959", call_mute_range_start: -55, call_mute_range_end: -45, }; // User Status export function getStatusColor( status: z.infer, ) { switch (status) { case "user_online": return "#22c55e"; case "iota_online": return "#22c55e"; case "user_dnd": return "#ef4444"; case "user_idle": return "#f59e0b"; case "user_wc": return "#3b82f6"; case "user_borked": case "iota_borked": return "#6b7280"; case "user_offline": case "iota_offline": default: return "#9ca3af"; } }