export type ApiEnvelope = { data: T }; export interface AdminUser { username: string; must_change_password: boolean; csrf_token: string; } export interface BotRuntime { state: 'stopped' | 'starting' | 'running' | 'stopping' | 'failed'; pid?: number; port?: number; started_at?: string; error?: string; } export interface BotPermissionDefinition { key: string; name: string; group: string; description: string; } export interface BotRole { role_id: string; name: string; description: string; permissions: string[]; builtin: boolean; created_at?: string; updated_at?: string; } export interface BotProfile { bot_id: string; label: string; enabled: boolean; bot_token_configured: boolean; sudo_users_id: string[]; log_group_id: string; gban_log_group_id: string; message_dump_chat: string; role_ids: string[]; permissions: string[]; identity?: { id: string; username: string; name: string; can_connect_to_business?: boolean; } | null; business_assistant?: AssistantProviderConfig; ready_to_connect: boolean; created_at?: string; updated_at?: string; runtime: BotRuntime; } export interface AssistantProviderConfig { base_url: string; api_key_configured: boolean; model: string; timeout_seconds: number; max_output_tokens: number; configured: boolean; } export interface AssistantRuntimeStatus { polling_state: string; business_mode_supported: boolean; webhook_conflict: boolean; webhook_url: string; model_configured: boolean; last_error?: string; last_poll_at?: string; usage: AssistantUsage; } export interface AssistantUsage { ai_calls: number; customers: number; conversations: number; handoffs: number; human_paused: number; } export interface AssistantAccountSettings { connection_id: string; assistant_enabled: boolean; system_prompt: string; language: string; tone: 'professional' | 'friendly' | 'concise'; account_daily_limit: number; customer_daily_limit: number; human_pause_hours: number; notification_destination: 'owner' | 'ops' | 'both'; ops_group_id: string; timezone: string; digest_enabled: boolean; digest_time: string; handoff_message: string; unsupported_message: string; feishu_webhook_enabled: boolean; feishu_webhook_url?: string; feishu_webhook_signing_secret?: string; feishu_webhook_configured: boolean; feishu_signing_secret_configured: boolean; feishu_message_preview_enabled: boolean; feishu_outgoing_message_enabled: boolean; } export interface AssistantConnection { connection_id: string; user: { id: string; username?: string; first_name?: string; last_name?: string; }; user_chat_id: string; rights: Record; is_enabled: boolean; connected_at?: string; last_event_at?: string; settings: AssistantAccountSettings; } export interface AssistantKnowledgeEntry { entry_id: string; connection_id: string; question: string; aliases: string[]; keywords: string[]; answer: string; tags: string[]; priority: number; enabled: boolean; updated_at?: string; } export type AssistantKnowledgeSourceType = 'channel' | 'group' | 'business'; export type AssistantKnowledgePublicationMode = 'review' | 'auto'; export type AssistantKnowledgeAuthorPolicy = | 'admins_or_allowlist' | 'admins' | 'allowlist' | 'all'; export interface AssistantKnowledgeSource { source_id: string; connection_id: string; source_type: AssistantKnowledgeSourceType; chat_id: string; title: string; publication_mode: AssistantKnowledgePublicationMode; author_policy: AssistantKnowledgeAuthorPolicy; allowed_user_ids: string[]; include_linked_chat: boolean; linked_chat_id: string; linked_chat_title?: string; backfill_days: number; backfill_limit: number; enabled: boolean; access_status?: string; sync_status: 'idle' | 'queued' | 'running' | 'completed' | 'failed'; last_sync_processed?: number; last_event_at?: string; last_error?: string; updated_at?: string; } export type AssistantKnowledgeCandidateStatus = | 'pending' | 'published' | 'rejected' | 'stale'; export interface AssistantKnowledgeCandidate { candidate_id: string; connection_id: string; source_id: string; event_id: string; question: string; aliases: string[]; keywords: string[]; answer: string; tags: string[]; confidence: number; status: AssistantKnowledgeCandidateStatus; knowledge_entry_id?: string; stale_reason?: string; source_snapshot: { title: string; source_type: AssistantKnowledgeSourceType | 'discussion'; chat_id: string; message_id: string; author_id: string; }; updated_at?: string; } export type AssistantConversationStatus = | 'auto' | 'handoff' | 'human_paused' | 'closed'; export interface AssistantConversationMessage { message_key: string; direction: 'incoming' | 'assistant' | 'human'; telegram_message_id: string; text: string; created_at: string; metadata?: Record; } export interface AssistantConversation { conversation_id: string; connection_id: string; chat_id: string; customer?: { id: string; username?: string; first_name?: string; last_name?: string; }; status: AssistantConversationStatus; summary?: string; handoff_reason?: string; paused_until?: string | null; last_message_at?: string; updated_at?: string; messages?: AssistantConversationMessage[]; } export interface TelegramSettings { api_id?: number | null; api_hash_configured: boolean; api_ready: boolean; roles: BotRole[]; permission_catalog: BotPermissionDefinition[]; bots: BotProfile[]; } export interface Paged { items: T[]; total: number; page: number; page_size: number; } export interface ChatSummary { chat_id: string; title: string; username?: string | null; type: string; accessible: boolean; member_count?: number; bot_status?: string; bot_privileges?: string[]; last_seen_at?: string; } export interface ChatOverview extends ChatSummary { description?: string | null; permissions: Record; automation: AutomationSettings; } export interface ChannelOverview extends ChatSummary { description?: string | null; photo_data_url?: string | null; } export interface ChannelPost { post_id: string; chat_id: string; message_id?: string | null; source: 'admin' | 'telegram'; status: 'scheduled' | 'sending' | 'published' | 'uncertain' | 'failed' | 'canceled' | 'deleted'; text: string; media_type?: string | null; file_id?: string | null; media_filename?: string | null; media_size?: number | null; media_mime_type?: string | null; pin: boolean; publish_at?: string | null; published_at?: string | null; created_at: string; last_error?: string; pin_error?: string; } export interface TelegramUser { id: string; username?: string | null; first_name?: string | null; last_name?: string | null; is_bot: boolean; is_deleted: boolean; } export interface ChatMember { user: TelegramUser; status: string; custom_title?: string | null; privileges: string[]; until_date?: string | null; last_seen_at?: string | null; } export interface AutoReply { keyword: string; type: string; text: string; file_id?: string | null; } export interface RiskRule { rule_id: string; name: string; enabled: boolean; keywords: string[]; match_images: boolean; match_links: boolean; actions: Array<'delete' | 'warn' | 'mute' | 'kick' | 'ban'>; duration_seconds: number; } export interface AutomationSettings { chat_id: string; auto_replies: AutoReply[]; blacklist_words: string[]; risk_rules: RiskRule[]; risk_control?: { enabled: boolean; keywords: string[]; match_images: boolean; match_links: boolean; actions: Array<'delete' | 'warn' | 'mute' | 'kick' | 'ban'>; duration_seconds?: number; ban_duration_seconds?: number; }; identity_monitor: { enabled: boolean; notify_in_chat: boolean; }; welcome: { enabled: boolean; text: string; media?: { type: string; file_id: string } | null; }; captcha_enabled: boolean; chatbot_enabled: boolean; antiflood_enabled: boolean; } export interface PointRules { enabled: boolean; timezone: string; checkin_enabled: boolean; checkin_button_enabled: boolean; checkin_points: number; checkin_success_message: string; checkin_already_message: string; activity_enabled: boolean; activity_points: number; activity_cooldown_seconds: number; activity_daily_cap: number; upvote_enabled: boolean; upvote_points: number; upvote_pair_cooldown_seconds: number; upvote_daily_cap: number; } export interface PointAccount { chat_id: string; user_id: string; username?: string | null; first_name?: string | null; display_name?: string | null; balance: number; lifetime_earned: number; lifetime_spent: number; updated_at?: string; } export interface PointTransaction { transaction_id: string; chat_id: string; user_id: string; username?: string | null; first_name?: string | null; display_name?: string | null; delta: number; balance_after: number; source: string; actor_id?: string | null; reason?: string; reference_id?: string | null; created_at: string; } export interface MemberIdentitySnapshot { username?: string | null; first_name?: string | null; last_name?: string | null; display_name?: string | null; } export interface MemberIdentityChange { change_id: string; chat_id: string; user_id: string; before: MemberIdentitySnapshot; after: MemberIdentitySnapshot; changed_fields: Array<'username' | 'display_name'>; observed_at: string; } export interface Prize { name: string; count: number; points_reward: number; } export interface Giveaway { giveaway_id: string; chat_id: string; title: string; description?: string; status: string; prizes: Prize[]; minimum_points: number; entry_cost: number; max_tickets_per_user?: number; eligibility_targets?: Array<{ chat_id: string; title?: string; type?: string }>; eligibility_mode?: 'all' | 'any'; template_id?: string; period_key?: string; eligibility_error?: string; cancellation_reason?: string; participation_reward: number; starts_at?: string; ends_at: string; created_at: string; updated_at: string; winners?: Array<{ user_id: string; username?: string | null; first_name?: string | null; last_name?: string | null; display_name?: string | null; tier: string; points_reward: number; }>; } export interface GiveawayTemplate { template_id: string; chat_id: string; title: string; description: string; prizes: Prize[]; minimum_points: number; entry_cost: number; participation_reward: number; max_tickets_per_user: number; eligibility_targets: Array<{ chat_id: string; title?: string; type?: string }>; eligibility_mode: 'all' | 'any'; day_of_month: number; start_time: string; draw_day_offset: 0 | 1; draw_time: string; active: boolean; last_error?: string; } export interface AuditLog { source: string; actor_id?: string | null; actor_name: string; action: string; chat_id?: string | null; target_id?: string | null; summary?: string; success: boolean; error?: string; created_at: string; } export type DirectoryApplicationStatus = | 'none' | 'pending' | 'approved' | 'rejected' | 'revoked'; export interface DirectoryProfile { user_id: string; username?: string | null; first_name?: string | null; last_name?: string | null; display_name?: string | null; application_status: DirectoryApplicationStatus; application_reason?: string; application_source_bot_id?: string; listed: boolean; online: boolean; online_until?: string | null; applied_at?: string | null; decided_at?: string | null; location?: { source?: string; updated_at?: string; } | null; service_profile?: { is_complete: boolean; accepting_requests: boolean; service_modes: ServiceMode[]; packages: ServicePackage[]; } | null; } export interface DirectoryLocation { user_id: string; username?: string | null; display_name?: string | null; application_status: DirectoryApplicationStatus; longitude: number; latitude: number; source: string; updated_at: string; } export interface DirectoryEvent { event_id: string; event_type: string; user_id: string; actor_id?: string | null; actor_name?: string; source: string; reason?: string; bot_id?: string; chat_id?: string | null; created_at: string; } export interface DirectorySettings { online_duration_hours: number; nearby_radius_km: number; nearby_radius_options_km: number[]; } export type ServiceMode = 'at_store' | 'onsite'; export type ServiceOrderStatus = | 'requested' | 'quoted' | 'confirmed' | 'in_progress' | 'completion_pending' | 'completed' | 'rejected' | 'canceled_customer' | 'canceled_technician' | 'disputed' | 'voided'; export interface ServiceAddon { addon_id?: string; name: string; unit: string; price: string; extra_minutes: number; } export interface ServicePackage { package_id: string; name: string; category: string; description: string; tags: string[]; service_modes: ServiceMode[]; price_mode: 'fixed' | 'starting_at' | 'range' | 'negotiable'; currency: string; price_unit: 'per_service' | 'per_hour' | 'per_item' | 'per_visit'; min_price: string; max_price: string; price_display?: string; duration_minutes?: number | null; included_items?: string; excluded_items?: string; preparation?: string; addons: ServiceAddon[]; travel_fee: { mode: 'included' | 'fixed' | 'per_km' | 'quoted'; amount?: string; per_km?: string; description?: string; }; service_radius_km?: number | null; out_of_range_policy?: string; source_type?: 'template' | 'custom'; source_template_id?: string | null; source_template_version?: number | null; customized_from_template?: boolean; } export interface ServicePackageTemplate { template_id: string; version: number; name: string; admin_note?: string; package: ServicePackage; status: 'enabled' | 'disabled' | 'archived'; sort_order: number; created_at: string; updated_at: string; } export interface TechnicianServiceProfile { headline: string; bio: string; tags: string[]; contact_hours: string; service_modes: ServiceMode[]; accepting_requests: boolean; public_area_text: string; venue: { name: string; address_hint: string; }; onsite_policy: { description: string; }; packages: ServicePackage[]; is_complete: boolean; version?: number; updated_at?: string; } export interface TechnicianSelfServiceContext { user_id: string; display_name: string; username?: string | null; eligibility: { ready: boolean; issues: Array<{ code: string; message: string }>; }; service_profile?: TechnicianServiceProfile | null; review_topic?: { message_thread_id: number; topic_name: string; topic_url: string; status: 'active'; } | null; } export interface TechnicianMiniAppBootstrap { technician: TechnicianSelfServiceContext; templates: Array< Pick & { builtin: boolean; } >; limits: { max_packages: number; }; } export interface ServiceQuote { quote_id: string; order_id: string; version: number; currency: string; base_amount: string; travel_fee: string; addons: Array<{ name: string; quantity: number; unit_price: string }>; discount: string; total_amount: string; note?: string; created_at: string; } export interface ServiceOrder { order_id: string; customer_id: string; customer_name: string; technician_id: string; technician_name: string; package_snapshot: ServicePackage; category: string; service_mode: ServiceMode; requirements: string; distance_band: string; status: ServiceOrderStatus; version: number; quote_snapshot?: ServiceQuote; current_quote_id?: string; close_reason?: string; dispute_reason?: string; created_at: string; updated_at: string; closed_at?: string; quotes?: ServiceQuote[]; events?: Array>; qr_records?: ServiceReviewQr[]; } export interface ReviewQuestion { question_id: string; type: 'single_choice' | 'multiple_choice' | 'text'; label: string; description?: string; required: boolean; weight?: number; max_length?: number; options?: Array<{ value: string; label: string; score?: number; }>; } export interface ServiceReviewTemplate { template_id: string; version: number; status: 'active' | 'retired'; questions: ReviewQuestion[]; published_at: string; } export interface ServiceReview { review_id: string; technician_id: string; technician_name: string; customer_id: string; customer_name: string; source: 'qr_verified' | 'student_initiated'; category: string; package_snapshot: ServicePackage; score: number; anonymous: boolean; status: 'pending' | 'approved' | 'rejected' | 'voided'; choice_answers: Record; text_answers: Record; topic_url?: string; topic_message_id?: number; topic_publication?: { status: 'published' | 'unconfigured' | 'failed'; topic_url?: string; message?: string; }; customer_review_count: number; customer_review_count_30d: number; customer_review_count_90d: number; similar_content_count: number; created_at: string; moderated_at?: string; } export interface ServiceReviewQr { qr_id: string; order_id: string; technician_id: string; customer_id: string; status: 'issued' | 'claimed' | 'expired' | 'revoked' | 'voided'; expires_at: string; created_at: string; claimed_at?: string; } export interface ServiceLeaderboardItem { technician_id: string; display_name?: string; username?: string; category: string; review_count: number; average_score: number; category_average: number; rank_score: number; last_review_at: string; } export interface ServiceCustomerBlock { scope: 'global' | 'technician'; technician_id: string; customer_id: string; active: boolean; reason?: string; actor_id?: string; updated_at: string; } export interface ServiceReport { report_id: string; reporter_id: string; target_type: 'technician' | 'customer' | 'order' | 'review'; target_id: string; reason: string; status: 'open' | 'resolved' | 'dismissed'; created_at: string; handled_at?: string; handling_reason?: string; } export interface ServiceSettings { customer_max_open_orders: number; customer_daily_request_limit: number; review_qr_expiry_hours: number; review_forum_chat_id: string; review_forum_username: string; } export interface FulfillmentMetrics { counts: Record; valid_requests: number; quoted_requests: number; confirmed_requests: number; directory_views: number; technician_views: number; request_starts: number; request_submission_rate: number; quote_rate: number; customer_confirmation_rate: number; match_success_rate: number; fulfillment_rate: number; cancellation_rate: number; cancellation_distribution: { customer: number; technician: number }; scope_note: string; }