- Backend FastAPI con multi-LLM (Claude/OpenAI/Gemini) - Publishing su Facebook, Instagram, YouTube, TikTok - Calendario editoriale con awareness levels (PAS, AIDA, BAB...) - Design system Editorial Fresh (Fraunces + DM Sans) - Scheduler automatico, gestione commenti AI, affiliate links Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
321 lines
7.5 KiB
Python
321 lines
7.5 KiB
Python
from datetime import datetime
|
|
from typing import Optional
|
|
|
|
from pydantic import BaseModel
|
|
|
|
|
|
# === Auth ===
|
|
|
|
class LoginRequest(BaseModel):
|
|
username: str
|
|
password: str
|
|
|
|
|
|
class Token(BaseModel):
|
|
access_token: str
|
|
token_type: str = "bearer"
|
|
|
|
|
|
# === Characters ===
|
|
|
|
class CharacterBase(BaseModel):
|
|
name: str
|
|
niche: str
|
|
topics: list[str] = []
|
|
tone: Optional[str] = None
|
|
visual_style: dict = {}
|
|
social_accounts: dict = {}
|
|
affiliate_links: list[dict] = []
|
|
avatar_url: Optional[str] = None
|
|
is_active: bool = True
|
|
|
|
|
|
class CharacterCreate(CharacterBase):
|
|
pass
|
|
|
|
|
|
class CharacterUpdate(BaseModel):
|
|
name: Optional[str] = None
|
|
niche: Optional[str] = None
|
|
topics: Optional[list[str]] = None
|
|
tone: Optional[str] = None
|
|
visual_style: Optional[dict] = None
|
|
social_accounts: Optional[dict] = None
|
|
affiliate_links: Optional[list[dict]] = None
|
|
avatar_url: Optional[str] = None
|
|
is_active: Optional[bool] = None
|
|
|
|
|
|
class CharacterResponse(CharacterBase):
|
|
id: int
|
|
created_at: datetime
|
|
updated_at: Optional[datetime] = None
|
|
|
|
class Config:
|
|
from_attributes = True
|
|
|
|
|
|
# === Posts / Content ===
|
|
|
|
class PostCreate(BaseModel):
|
|
character_id: int
|
|
content_type: str = "text"
|
|
platform_hint: str = "instagram"
|
|
text_content: Optional[str] = None
|
|
hashtags: list[str] = []
|
|
image_url: Optional[str] = None
|
|
video_url: Optional[str] = None
|
|
media_urls: list[str] = []
|
|
affiliate_links_used: list[dict] = []
|
|
status: str = "draft"
|
|
|
|
|
|
class PostUpdate(BaseModel):
|
|
text_content: Optional[str] = None
|
|
hashtags: Optional[list[str]] = None
|
|
image_url: Optional[str] = None
|
|
video_url: Optional[str] = None
|
|
status: Optional[str] = None
|
|
affiliate_links_used: Optional[list[dict]] = None
|
|
|
|
|
|
class PostResponse(BaseModel):
|
|
id: int
|
|
character_id: int
|
|
content_type: str
|
|
text_content: Optional[str] = None
|
|
hashtags: list[str] = []
|
|
image_url: Optional[str] = None
|
|
video_url: Optional[str] = None
|
|
media_urls: list[str] = []
|
|
affiliate_links_used: list[dict] = []
|
|
llm_provider: Optional[str] = None
|
|
llm_model: Optional[str] = None
|
|
platform_hint: Optional[str] = None
|
|
status: str
|
|
created_at: datetime
|
|
updated_at: Optional[datetime] = None
|
|
|
|
class Config:
|
|
from_attributes = True
|
|
|
|
|
|
class GenerateContentRequest(BaseModel):
|
|
character_id: int
|
|
platform: str = "instagram"
|
|
content_type: str = "text"
|
|
topic_hint: Optional[str] = None
|
|
include_affiliates: bool = True
|
|
provider: Optional[str] = None # override default LLM
|
|
model: Optional[str] = None
|
|
|
|
|
|
class GenerateImageRequest(BaseModel):
|
|
character_id: int
|
|
prompt: Optional[str] = None # auto-generated if not provided
|
|
style_hint: Optional[str] = None
|
|
size: str = "1024x1024"
|
|
provider: Optional[str] = None # dalle, replicate
|
|
|
|
|
|
# === Affiliate Links ===
|
|
|
|
class AffiliateLinkBase(BaseModel):
|
|
character_id: Optional[int] = None
|
|
network: str
|
|
name: str
|
|
url: str
|
|
tag: Optional[str] = None
|
|
topics: list[str] = []
|
|
is_active: bool = True
|
|
|
|
|
|
class AffiliateLinkCreate(AffiliateLinkBase):
|
|
pass
|
|
|
|
|
|
class AffiliateLinkUpdate(BaseModel):
|
|
network: Optional[str] = None
|
|
name: Optional[str] = None
|
|
url: Optional[str] = None
|
|
tag: Optional[str] = None
|
|
topics: Optional[list[str]] = None
|
|
is_active: Optional[bool] = None
|
|
|
|
|
|
class AffiliateLinkResponse(AffiliateLinkBase):
|
|
id: int
|
|
click_count: int = 0
|
|
created_at: datetime
|
|
|
|
class Config:
|
|
from_attributes = True
|
|
|
|
|
|
# === Editorial Plans ===
|
|
|
|
class EditorialPlanBase(BaseModel):
|
|
character_id: int
|
|
name: str
|
|
frequency: str = "daily"
|
|
posts_per_day: int = 1
|
|
platforms: list[str] = []
|
|
content_types: list[str] = ["text"]
|
|
posting_times: list[str] = ["09:00"]
|
|
start_date: Optional[datetime] = None
|
|
end_date: Optional[datetime] = None
|
|
is_active: bool = False
|
|
|
|
|
|
class EditorialPlanCreate(EditorialPlanBase):
|
|
pass
|
|
|
|
|
|
class EditorialPlanUpdate(BaseModel):
|
|
name: Optional[str] = None
|
|
frequency: Optional[str] = None
|
|
posts_per_day: Optional[int] = None
|
|
platforms: Optional[list[str]] = None
|
|
content_types: Optional[list[str]] = None
|
|
posting_times: Optional[list[str]] = None
|
|
start_date: Optional[datetime] = None
|
|
end_date: Optional[datetime] = None
|
|
is_active: Optional[bool] = None
|
|
|
|
|
|
class EditorialPlanResponse(EditorialPlanBase):
|
|
id: int
|
|
created_at: datetime
|
|
updated_at: Optional[datetime] = None
|
|
|
|
class Config:
|
|
from_attributes = True
|
|
|
|
|
|
# === Scheduled Posts ===
|
|
|
|
class ScheduledPostCreate(BaseModel):
|
|
plan_id: Optional[int] = None
|
|
post_id: int
|
|
platform: str
|
|
scheduled_at: datetime
|
|
|
|
|
|
class ScheduledPostResponse(BaseModel):
|
|
id: int
|
|
plan_id: Optional[int] = None
|
|
post_id: int
|
|
platform: str
|
|
scheduled_at: datetime
|
|
published_at: Optional[datetime] = None
|
|
status: str
|
|
error_message: Optional[str] = None
|
|
external_post_id: Optional[str] = None
|
|
created_at: datetime
|
|
|
|
class Config:
|
|
from_attributes = True
|
|
|
|
|
|
# === Social Accounts ===
|
|
|
|
class SocialAccountCreate(BaseModel):
|
|
character_id: int
|
|
platform: str
|
|
account_name: Optional[str] = None
|
|
account_id: Optional[str] = None
|
|
access_token: Optional[str] = None
|
|
refresh_token: Optional[str] = None
|
|
page_id: Optional[str] = None
|
|
extra_data: dict = {}
|
|
|
|
|
|
class SocialAccountUpdate(BaseModel):
|
|
account_name: Optional[str] = None
|
|
access_token: Optional[str] = None
|
|
refresh_token: Optional[str] = None
|
|
page_id: Optional[str] = None
|
|
extra_data: Optional[dict] = None
|
|
is_active: Optional[bool] = None
|
|
|
|
|
|
class SocialAccountResponse(BaseModel):
|
|
id: int
|
|
character_id: int
|
|
platform: str
|
|
account_name: Optional[str] = None
|
|
account_id: Optional[str] = None
|
|
page_id: Optional[str] = None
|
|
is_active: bool
|
|
token_expires_at: Optional[datetime] = None
|
|
created_at: datetime
|
|
|
|
class Config:
|
|
from_attributes = True
|
|
|
|
|
|
# === Comments ===
|
|
|
|
class CommentResponse(BaseModel):
|
|
id: int
|
|
scheduled_post_id: Optional[int] = None
|
|
platform: str
|
|
external_comment_id: Optional[str] = None
|
|
author_name: Optional[str] = None
|
|
content: Optional[str] = None
|
|
ai_suggested_reply: Optional[str] = None
|
|
approved_reply: Optional[str] = None
|
|
reply_status: str
|
|
replied_at: Optional[datetime] = None
|
|
created_at: datetime
|
|
|
|
class Config:
|
|
from_attributes = True
|
|
|
|
|
|
class CommentAction(BaseModel):
|
|
action: str # approve, edit, ignore
|
|
reply_text: Optional[str] = None # for edit action
|
|
|
|
|
|
# === System Settings ===
|
|
|
|
class SettingUpdate(BaseModel):
|
|
key: str
|
|
value: dict | str | list | int | bool | None
|
|
|
|
|
|
class SettingResponse(BaseModel):
|
|
key: str
|
|
value: dict | str | list | int | bool | None
|
|
updated_at: Optional[datetime] = None
|
|
|
|
class Config:
|
|
from_attributes = True
|
|
|
|
|
|
# === Editorial Calendar (from PostGenerator) ===
|
|
|
|
class CalendarGenerateRequest(BaseModel):
|
|
topics: list[str]
|
|
format_narrativo: Optional[str] = None # PAS, AIDA, BAB, Storytelling, Listicle, Dato_Implicazione
|
|
awareness_level: Optional[int] = None # 1-5 (Schwartz levels)
|
|
num_posts: int = 7
|
|
start_date: Optional[str] = None # YYYY-MM-DD
|
|
character_id: Optional[int] = None
|
|
|
|
|
|
class CalendarSlotResponse(BaseModel):
|
|
indice: int
|
|
topic: str
|
|
formato_narrativo: str
|
|
awareness_level: int
|
|
awareness_label: str
|
|
data_pubblicazione: str
|
|
note: Optional[str] = None
|
|
|
|
|
|
class CalendarResponse(BaseModel):
|
|
slots: list[CalendarSlotResponse]
|
|
totale_post: int
|