Initial MCM project: FastAPI + Textual TUI unified messenger
MultiCustomerMessenger supporting Telegram (python-telegram-bot), WhatsApp (Green API) and SMS (python-gsmmodem-new). REST API with Bearer-token auth, SQLAlchemy models for MariaDB, APScheduler for background polling, and Textual TUI running in same asyncio event-loop.
This commit is contained in:
128
schemas.py
Normal file
128
schemas.py
Normal file
@@ -0,0 +1,128 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from datetime import datetime
|
||||
from enum import Enum
|
||||
from typing import Optional
|
||||
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
|
||||
# ── Enums ──────────────────────────────────────────────────────────────────────
|
||||
|
||||
class ChannelType(str, Enum):
|
||||
telegram = "telegram"
|
||||
whatsapp = "whatsapp"
|
||||
sms = "sms"
|
||||
|
||||
|
||||
class MessageStatus(str, Enum):
|
||||
pending = "pending"
|
||||
sent = "sent"
|
||||
delivered = "delivered"
|
||||
read = "read"
|
||||
failed = "failed"
|
||||
|
||||
|
||||
class MessageDirection(str, Enum):
|
||||
inbound = "inbound"
|
||||
outbound = "outbound"
|
||||
|
||||
|
||||
# ── Contact ────────────────────────────────────────────────────────────────────
|
||||
|
||||
class ContactCreate(BaseModel):
|
||||
name: str = Field(..., min_length=1, max_length=255)
|
||||
phone: Optional[str] = None
|
||||
email: Optional[str] = None
|
||||
telegram_id: Optional[str] = None
|
||||
telegram_username: Optional[str] = None
|
||||
whatsapp_phone: Optional[str] = None
|
||||
notes: Optional[str] = None
|
||||
|
||||
|
||||
class ContactUpdate(BaseModel):
|
||||
name: Optional[str] = Field(None, min_length=1, max_length=255)
|
||||
phone: Optional[str] = None
|
||||
email: Optional[str] = None
|
||||
telegram_id: Optional[str] = None
|
||||
telegram_username: Optional[str] = None
|
||||
whatsapp_phone: Optional[str] = None
|
||||
notes: Optional[str] = None
|
||||
|
||||
|
||||
class ContactResponse(BaseModel):
|
||||
model_config = {"from_attributes": True}
|
||||
|
||||
id: str
|
||||
name: str
|
||||
phone: Optional[str] = None
|
||||
email: Optional[str] = None
|
||||
telegram_id: Optional[str] = None
|
||||
telegram_username: Optional[str] = None
|
||||
whatsapp_phone: Optional[str] = None
|
||||
notes: Optional[str] = None
|
||||
created_at: datetime
|
||||
updated_at: Optional[datetime] = None
|
||||
|
||||
|
||||
# ── Message ────────────────────────────────────────────────────────────────────
|
||||
|
||||
class SendMessageRequest(BaseModel):
|
||||
channel: ChannelType
|
||||
# Empfänger – je nach Kanal eines der Felder befüllen
|
||||
recipient_phone: Optional[str] = Field(None, description="Telefonnummer für WhatsApp/SMS (E.164: +49…)")
|
||||
recipient_telegram_id: Optional[str] = Field(None, description="Telegram Chat-ID")
|
||||
# Optional: Kontakt-ID aus der DB
|
||||
contact_id: Optional[str] = None
|
||||
text: str = Field(..., min_length=1, max_length=4096)
|
||||
reply_to_id: Optional[str] = None
|
||||
|
||||
|
||||
class MessageResponse(BaseModel):
|
||||
model_config = {"from_attributes": True}
|
||||
|
||||
id: str
|
||||
conversation_id: str
|
||||
sender_id: Optional[str] = None
|
||||
channel: ChannelType
|
||||
channel_message_id: Optional[str] = None
|
||||
direction: MessageDirection
|
||||
text: str
|
||||
status: MessageStatus
|
||||
error_message: Optional[str] = None
|
||||
created_at: datetime
|
||||
sent_at: Optional[datetime] = None
|
||||
delivered_at: Optional[datetime] = None
|
||||
read_at: Optional[datetime] = None
|
||||
|
||||
|
||||
# ── Conversation ───────────────────────────────────────────────────────────────
|
||||
|
||||
class ConversationResponse(BaseModel):
|
||||
model_config = {"from_attributes": True}
|
||||
|
||||
id: str
|
||||
channel: ChannelType
|
||||
channel_conversation_id: Optional[str] = None
|
||||
title: Optional[str] = None
|
||||
is_group: bool
|
||||
is_archived: bool
|
||||
last_message_at: Optional[datetime] = None
|
||||
created_at: datetime
|
||||
last_message: Optional[MessageResponse] = None
|
||||
unread_count: int = 0
|
||||
|
||||
|
||||
# ── Channel Status ─────────────────────────────────────────────────────────────
|
||||
|
||||
class ChannelStatusResponse(BaseModel):
|
||||
channel: ChannelType
|
||||
enabled: bool
|
||||
connected: bool
|
||||
detail: Optional[str] = None
|
||||
|
||||
|
||||
class SystemStatusResponse(BaseModel):
|
||||
channels: list[ChannelStatusResponse]
|
||||
database: bool
|
||||
timestamp: datetime
|
||||
Reference in New Issue
Block a user