from __future__ import annotations from datetime import datetime from typing import Literal from pydantic import BaseModel, Field, HttpUrl class InviteClaim(BaseModel): display_name: str = Field(min_length=1, max_length=160) device_label: str = Field(default="Browser", max_length=160) class RecoveryRequest(BaseModel): email: str = Field(min_length=3, max_length=255) class RecoveryConsume(BaseModel): recovery_code: str = Field(min_length=6, max_length=300) device_label: str = Field(default="Recovered browser", max_length=160) class DeviceLinkStart(BaseModel): device_label: str = Field(default="New browser", max_length=160) class DeviceLinkCodeIn(BaseModel): code: str = Field(min_length=6, max_length=32) class DeviceLinkComplete(BaseModel): code: str = Field(min_length=6, max_length=32) device_label: str = Field(default="Linked browser", max_length=160) class GroupCreate(BaseModel): name: str = Field(min_length=2, max_length=180) description: str = Field(default="", max_length=4000) visibility: Literal["private", "public", "listed"] = "private" owner_display_name: str = Field(default="Group admin", max_length=160) class GroupPatch(BaseModel): name: str | None = Field(default=None, max_length=180) description: str | None = Field(default=None, max_length=4000) visibility: Literal["private", "public", "listed"] | None = None legacy_channel_status: Literal["none", "transition", "legacy"] | None = None transition_deadline: str | None = None class InviteCreate(BaseModel): label: str = Field(default="Group invite", max_length=160) scope: Literal["specific_member", "open_seat", "admin_invite"] = "open_seat" permission_role: Literal["guest", "member", "admin"] = "member" max_uses: int = Field(default=50, ge=1, le=500) expires_at: datetime | None = None member_id: str | None = None class AnnouncementCreate(BaseModel): title: str = Field(min_length=2, max_length=220) body: str = Field(default="", max_length=12000) priority: Literal["normal", "urgent"] = "normal" official: bool = True requires_ack: bool = False class EventCreate(BaseModel): title: str = Field(min_length=2, max_length=220) description: str | None = Field(default=None, max_length=8000) starts_at: datetime ends_at: datetime | None = None location_name: str | None = Field(default=None, max_length=220) location_address: str | None = Field(default=None, max_length=255) rsvp_required: bool = True class RsvpCreate(BaseModel): status: Literal["yes", "no", "maybe", "unknown"] note: str | None = Field(default=None, max_length=1000) class TaskCreate(BaseModel): title: str = Field(min_length=2, max_length=220) description: str | None = Field(default=None, max_length=8000) assigned_to_member_id: str | None = None due_at: datetime | None = None class TaskPatch(BaseModel): title: str | None = Field(default=None, max_length=220) description: str | None = Field(default=None, max_length=8000) assigned_to_member_id: str | None = None due_at: datetime | None = None status: Literal["open", "done", "cancelled"] | None = None class PollOptionIn(BaseModel): label: str = Field(min_length=1, max_length=220) class PollCreate(BaseModel): title: str = Field(min_length=2, max_length=220) description: str | None = Field(default=None, max_length=8000) closes_at: datetime | None = None options: list[PollOptionIn] = Field(min_length=2, max_length=12) class PollVoteCreate(BaseModel): option_id: str class ThreadCreate(BaseModel): title: str = Field(min_length=2, max_length=220) kind: Literal["discussion", "question", "archive"] = "discussion" class MessageCreate(BaseModel): body: str = Field(min_length=1, max_length=12000) class NotificationPreferencePatch(BaseModel): preferences: dict[str, Literal["immediate", "digest", "muted"]] class MigrationReminderRequest(BaseModel): frontend_origin: str | None = None class ConnectionTokenCreate(BaseModel): label: str = Field(default="Home server connection", max_length=160) group_id: str | None = None scopes: list[str] = Field(default_factory=lambda: ["sync:read"]) expires_at: datetime | None = None class RemoteConnect(BaseModel): server_url: HttpUrl connection_code: str = Field(min_length=10, max_length=300) class PasskeyStub(BaseModel): display_name: str | None = Field(default=None, max_length=160)