569 lines
17 KiB
Plaintext
569 lines
17 KiB
Plaintext
syntax = "v1"
|
||
|
||
// M4 Studio: personas / plays / outbox / compose / own-posts / mentions
|
||
|
||
type (
|
||
PersonaDraftPublic {
|
||
Identity string `json:"identity"`
|
||
Tone string `json:"tone"`
|
||
Audience string `json:"audience"`
|
||
Hooks string `json:"hooks"`
|
||
LanguageFingerprint string `json:"language_fingerprint"`
|
||
Rhythm string `json:"rhythm"`
|
||
Punctuation string `json:"punctuation"`
|
||
ContentPatterns string `json:"content_patterns"`
|
||
KnowledgeTranslation string `json:"knowledge_translation"`
|
||
CtaStyle string `json:"cta_style"`
|
||
Examples string `json:"examples"`
|
||
Avoid string `json:"avoid"`
|
||
}
|
||
|
||
StyleDimensionPublic {
|
||
Summary string `json:"summary"`
|
||
Evidence []string `json:"evidence,optional"`
|
||
}
|
||
|
||
PersonaStylePublic {
|
||
Draft PersonaDraftPublic `json:"draft"`
|
||
DraftText string `json:"draft_text"`
|
||
Source string `json:"source"`
|
||
BenchmarkUsername string `json:"benchmark_username,optional"`
|
||
SourceLabel string `json:"source_label,optional"`
|
||
SampleCount int `json:"sample_count"`
|
||
AnalyzedAt int64 `json:"analyzed_at,optional"`
|
||
SamplePreviews []string `json:"sample_previews,optional"`
|
||
// 8D:d1Tone…d8Risk → { summary, evidence }
|
||
Dimensions map[string]StyleDimensionPublic `json:"dimensions,optional"`
|
||
}
|
||
|
||
PersonaGuardPublic {
|
||
Avoid []string `json:"avoid"`
|
||
MaxChars int `json:"max_chars"`
|
||
BanAiTone bool `json:"ban_ai_tone"`
|
||
}
|
||
|
||
PersonaPublic {
|
||
Id string `json:"id"`
|
||
Name string `json:"name"`
|
||
Brief string `json:"brief"`
|
||
Status string `json:"status"`
|
||
Style PersonaStylePublic `json:"style"`
|
||
Guard PersonaGuardPublic `json:"guard"`
|
||
Voice string `json:"voice,optional"`
|
||
Notes string `json:"notes,optional"`
|
||
CreatedAt int64 `json:"created_at"`
|
||
UpdatedAt int64 `json:"updated_at"`
|
||
}
|
||
|
||
PersonaListData {
|
||
List []PersonaPublic `json:"list"`
|
||
}
|
||
|
||
PersonaSaveReq {
|
||
Id string `json:"id,optional"`
|
||
Name string `json:"name"`
|
||
Brief string `json:"brief,optional"`
|
||
Status string `json:"status,optional"`
|
||
Style PersonaStylePublic `json:"style,optional"`
|
||
Guard PersonaGuardPublic `json:"guard,optional"`
|
||
Voice string `json:"voice,optional"`
|
||
Notes string `json:"notes,optional"`
|
||
}
|
||
|
||
PersonaIdPath {
|
||
Id string `path:"id"`
|
||
}
|
||
|
||
PersonaActiveData {
|
||
Id string `json:"id"`
|
||
}
|
||
|
||
PersonaSetActiveReq {
|
||
Id string `json:"id"`
|
||
}
|
||
|
||
PersonaAnalyzeTextReq {
|
||
Id string `path:"id"`
|
||
RawText string `json:"raw_text"`
|
||
SourceLabel string `json:"source_label,optional"`
|
||
}
|
||
|
||
PersonaAnalyzeAccountReq {
|
||
Id string `path:"id"`
|
||
Username string `json:"username"`
|
||
}
|
||
|
||
// --- Plays ---
|
||
ExternalTargetPublic {
|
||
Url string `json:"url"`
|
||
RawUrl string `json:"raw_url,optional"`
|
||
Shortcode string `json:"shortcode,optional"`
|
||
AuthorUsername string `json:"author_username,optional"`
|
||
TextPreview string `json:"text_preview,optional"`
|
||
MediaId string `json:"media_id,optional"`
|
||
ResolvedAt int64 `json:"resolved_at,optional"`
|
||
}
|
||
|
||
PlayStepPublic {
|
||
Id string `json:"id"`
|
||
SortOrder int `json:"sort_order"`
|
||
Kind string `json:"kind"`
|
||
AccountId string `json:"account_id"`
|
||
Text string `json:"text"`
|
||
DelayFromPreviousSec int `json:"delay_from_previous_sec"`
|
||
PersonaId string `json:"persona_id,optional"`
|
||
BrandId string `json:"brand_id,optional"`
|
||
ImageUrls []string `json:"image_urls,optional"`
|
||
}
|
||
|
||
PlayPublic {
|
||
Id string `json:"id"`
|
||
Title string `json:"title"`
|
||
Topic string `json:"topic"`
|
||
Status string `json:"status"`
|
||
LeadAccountId string `json:"lead_account_id"`
|
||
CastAccountIds []string `json:"cast_account_ids"`
|
||
TargetOwnPostId string `json:"target_own_post_id,optional"`
|
||
TargetExternal ExternalTargetPublic `json:"target_external,optional"`
|
||
Steps []PlayStepPublic `json:"steps"`
|
||
ScheduleStartAt int64 `json:"schedule_start_at"`
|
||
IntervalMinutes int `json:"interval_minutes,optional"`
|
||
CreatedAt int64 `json:"created_at"`
|
||
UpdatedAt int64 `json:"updated_at"`
|
||
}
|
||
|
||
PlayListData {
|
||
List []PlayPublic `json:"list"`
|
||
}
|
||
|
||
PlaySaveReq {
|
||
Id string `json:"id,optional"`
|
||
Title string `json:"title"`
|
||
Topic string `json:"topic,optional"`
|
||
Status string `json:"status,optional"`
|
||
LeadAccountId string `json:"lead_account_id"`
|
||
CastAccountIds []string `json:"cast_account_ids,optional"`
|
||
TargetOwnPostId string `json:"target_own_post_id,optional"`
|
||
TargetExternal ExternalTargetPublic `json:"target_external,optional"`
|
||
Steps []PlayStepPublic `json:"steps"`
|
||
ScheduleStartAt int64 `json:"schedule_start_at,optional"`
|
||
IntervalMinutes int `json:"interval_minutes,optional"`
|
||
}
|
||
|
||
PlayIdPath {
|
||
Id string `path:"id"`
|
||
}
|
||
|
||
PlayByPostReq {
|
||
OwnPostId string `form:"own_post_id"`
|
||
}
|
||
|
||
PlayByExternalReq {
|
||
Url string `form:"url"`
|
||
}
|
||
|
||
PlayResolveReq {
|
||
Url string `json:"url"`
|
||
}
|
||
|
||
// AI 產劇本一步(真 LLM;互回/串場共用)
|
||
PlayGenerateStepReq {
|
||
PersonaId string `json:"persona_id,optional"`
|
||
Context string `json:"context"` // 主貼/上一則/目標文
|
||
Topic string `json:"topic,optional"`
|
||
SpeakerLabel string `json:"speaker_label,optional"`
|
||
IsLead bool `json:"is_lead,optional"`
|
||
// mode: reply(預設)| root(串場主貼)
|
||
Mode string `json:"mode,optional"`
|
||
}
|
||
|
||
PlayGenerateStepData {
|
||
Text string `json:"text"`
|
||
}
|
||
|
||
// 一次產完整劇本(背景 job)
|
||
PlayGenerateScriptData {
|
||
JobId string `json:"job_id"`
|
||
Async bool `json:"async"`
|
||
}
|
||
|
||
// --- Outbox ---
|
||
OutboxStepPublic {
|
||
Id string `json:"id"`
|
||
StepId string `json:"step_id"`
|
||
SortOrder int `json:"sort_order"`
|
||
Kind string `json:"kind"`
|
||
AccountId string `json:"account_id"`
|
||
Text string `json:"text"`
|
||
Status string `json:"status"`
|
||
Error string `json:"error,optional"`
|
||
ScheduledAt int64 `json:"scheduled_at,optional"`
|
||
PublishedAt int64 `json:"published_at,optional"`
|
||
}
|
||
|
||
OutboxPublic {
|
||
Id string `json:"id"`
|
||
PlayId string `json:"play_id"`
|
||
Title string `json:"title"`
|
||
Status string `json:"status"`
|
||
Steps []OutboxStepPublic `json:"steps"`
|
||
CreatedAt int64 `json:"created_at"`
|
||
UpdatedAt int64 `json:"updated_at"`
|
||
}
|
||
|
||
OutboxListData {
|
||
List []OutboxPublic `json:"list"`
|
||
}
|
||
|
||
OutboxIdPath {
|
||
Id string `path:"id"`
|
||
}
|
||
|
||
OutboxRetryPath {
|
||
Id string `path:"id"`
|
||
StepId string `path:"stepId"`
|
||
}
|
||
|
||
// --- Compose ---
|
||
ComposeMimicReq {
|
||
SourceText string `json:"source_text"`
|
||
PersonaId string `json:"persona_id,optional"`
|
||
// 可選:從「我的貼文」結構分析帶過來的備註(鉤子/結構/可複製點)
|
||
StructureNotes string `json:"structure_notes,optional"`
|
||
}
|
||
|
||
// 仿寫改背景 Job:立刻回 job_id;完成後從 job.payload.result_text 取文
|
||
ComposeMimicData {
|
||
// 相容:同步路徑才有;Job 路徑為空
|
||
Text string `json:"text,optional"`
|
||
JobId string `json:"job_id,optional"`
|
||
Async bool `json:"async,optional"`
|
||
}
|
||
|
||
// 人設試產:依指紋真 LLM 產主貼 + 回文(可帶新聞話題)
|
||
ComposePersonaPreviewReq {
|
||
PersonaId string `json:"persona_id"`
|
||
// 手動話題;空則嘗試上網取新聞靈感
|
||
Topic string `json:"topic,optional"`
|
||
// 預設 true:無 topic 時抓即時新聞標題當靈感
|
||
UseNews bool `json:"use_news,optional"`
|
||
}
|
||
|
||
ComposePersonaPreviewData {
|
||
Topic string `json:"topic"`
|
||
TopicSource string `json:"topic_source"` // news | manual | fallback
|
||
PostText string `json:"post_text"`
|
||
ReplyText string `json:"reply_text"`
|
||
Notes string `json:"notes,optional"`
|
||
}
|
||
|
||
ComposeViralReq {
|
||
Text string `json:"text"`
|
||
}
|
||
|
||
ViralAnalysisPublic {
|
||
Hooks string `json:"hooks"`
|
||
Structure string `json:"structure"`
|
||
Emotion string `json:"emotion"`
|
||
Summary string `json:"summary"`
|
||
Cta string `json:"cta,optional"`
|
||
Copyable string `json:"copyable,optional"`
|
||
Risks string `json:"risks,optional"`
|
||
}
|
||
|
||
ComposePublishReq {
|
||
AccountId string `json:"account_id"`
|
||
Text string `json:"text"`
|
||
Title string `json:"title,optional"`
|
||
ImageUrls []string `json:"image_urls,optional"`
|
||
ScheduleStartAt int64 `json:"schedule_start_at,optional"`
|
||
// Threads 話題標籤(topic_tag,1~50 字,可不加 #)
|
||
TopicTag string `json:"topic_tag,optional"`
|
||
}
|
||
|
||
// --- Own posts ---
|
||
OwnPostReplyPublic {
|
||
Id string `json:"id"`
|
||
Username string `json:"username"`
|
||
Text string `json:"text"`
|
||
CreatedAt int64 `json:"created_at"`
|
||
LikeCount int `json:"like_count,optional"`
|
||
ReplyStatus string `json:"reply_status,optional"`
|
||
RepliedBy string `json:"replied_by,optional"`
|
||
RepliedAt int64 `json:"replied_at,optional"`
|
||
ParentReplyId string `json:"parent_reply_id,optional"`
|
||
IsMine bool `json:"is_mine,optional"`
|
||
}
|
||
|
||
OwnPostPublic {
|
||
Id string `json:"id"`
|
||
AccountId string `json:"account_id"`
|
||
MediaId string `json:"media_id,optional"`
|
||
Text string `json:"text"`
|
||
MediaType string `json:"media_type"`
|
||
MediaUrl string `json:"media_url,optional"`
|
||
ThumbnailUrl string `json:"thumbnail_url,optional"`
|
||
Permalink string `json:"permalink,optional"`
|
||
Shortcode string `json:"shortcode,optional"`
|
||
TopicTag string `json:"topic_tag,optional"`
|
||
LikeCount int `json:"like_count"`
|
||
ReplyCount int `json:"reply_count"`
|
||
RepostCount int `json:"repost_count"`
|
||
QuoteCount int `json:"quote_count"`
|
||
ViewCount int `json:"view_count"`
|
||
ShareCount int `json:"share_count"`
|
||
InsightsStatus string `json:"insights_status,optional"`
|
||
FormulaSummary string `json:"formula_summary,optional"`
|
||
Insight string `json:"insight,optional"`
|
||
FormulaDetail string `json:"formula_detail,optional"`
|
||
Replies []OwnPostReplyPublic `json:"replies"`
|
||
PublishedAt int64 `json:"published_at"`
|
||
}
|
||
|
||
OwnPostListData {
|
||
List []OwnPostPublic `json:"list"`
|
||
}
|
||
|
||
OwnPostListReq {
|
||
AccountId string `form:"account_id,optional"`
|
||
}
|
||
|
||
OwnPostSyncReq {
|
||
AccountId string `json:"account_id"`
|
||
}
|
||
|
||
OwnPostLastSyncedData {
|
||
LastSyncedAt int64 `json:"last_synced_at"`
|
||
}
|
||
|
||
OwnPostGenerateReplyReq {
|
||
PostId string `json:"post_id"`
|
||
ReplyId string `json:"reply_id,optional"`
|
||
PersonaId string `json:"persona_id,optional"`
|
||
}
|
||
|
||
OwnPostGenerateReplyData {
|
||
Text string `json:"text"`
|
||
}
|
||
|
||
OwnPostSendReplyReq {
|
||
PostId string `json:"post_id"`
|
||
ReplyId string `json:"reply_id,optional"`
|
||
Text string `json:"text"`
|
||
AccountId string `json:"account_id,optional"`
|
||
ImageUrls []string `json:"image_urls,optional"`
|
||
}
|
||
|
||
OwnPostAnalyzeReq {
|
||
PostId string `json:"post_id"`
|
||
}
|
||
|
||
// 點開貼文後再載留言(避免 sync 一次打爆 conversation API)
|
||
OwnPostLoadRepliesReq {
|
||
PostId string `json:"post_id"`
|
||
}
|
||
|
||
// --- Mentions ---
|
||
MentionPublic {
|
||
Id string `json:"id"`
|
||
AccountId string `json:"account_id"`
|
||
MediaId string `json:"media_id,optional"`
|
||
FromUsername string `json:"from_username"`
|
||
Text string `json:"text"`
|
||
ContextSnippet string `json:"context_snippet"`
|
||
Permalink string `json:"permalink,optional"`
|
||
Status string `json:"status"`
|
||
DraftText string `json:"draft_text,optional"`
|
||
CreatedAt int64 `json:"created_at"`
|
||
}
|
||
|
||
MentionListData {
|
||
List []MentionPublic `json:"list"`
|
||
}
|
||
|
||
MentionListReq {
|
||
AccountId string `form:"account_id,optional"`
|
||
}
|
||
|
||
MentionSyncReq {
|
||
AccountId string `json:"account_id"`
|
||
}
|
||
|
||
MentionIdPath {
|
||
Id string `path:"id"`
|
||
}
|
||
|
||
MentionGenerateReq {
|
||
Id string `path:"id"`
|
||
PersonaId string `json:"persona_id,optional"`
|
||
}
|
||
|
||
MentionMarkRepliedReq {
|
||
Id string `path:"id"`
|
||
Text string `json:"text,optional"`
|
||
ImageUrls []string `json:"image_urls,optional"`
|
||
}
|
||
)
|
||
|
||
@server (
|
||
jwt: Auth
|
||
group: personas
|
||
prefix: /api/v1/personas
|
||
middleware: AuthJWT
|
||
)
|
||
service gateway {
|
||
@handler ListPersonas
|
||
get / returns (PersonaListData)
|
||
|
||
@handler GetActivePersona
|
||
get /active returns (PersonaActiveData)
|
||
|
||
@handler SetActivePersona
|
||
post /active (PersonaSetActiveReq) returns (PersonaActiveData)
|
||
|
||
@handler GetPersona
|
||
get /:id (PersonaIdPath) returns (PersonaPublic)
|
||
|
||
@handler SavePersona
|
||
post / (PersonaSaveReq) returns (PersonaPublic)
|
||
|
||
@handler RemovePersona
|
||
delete /:id (PersonaIdPath) returns (OkData)
|
||
|
||
@handler AnalyzePersonaText
|
||
post /:id/analyze-text (PersonaAnalyzeTextReq) returns (PersonaPublic)
|
||
|
||
@handler AnalyzePersonaAccount
|
||
post /:id/analyze-account (PersonaAnalyzeAccountReq) returns (PersonaPublic)
|
||
}
|
||
|
||
@server (
|
||
jwt: Auth
|
||
group: plays
|
||
prefix: /api/v1/plays
|
||
middleware: AuthJWT
|
||
)
|
||
service gateway {
|
||
@handler ListPlays
|
||
get / returns (PlayListData)
|
||
|
||
@handler ListPlaysByPost
|
||
get /by-post (PlayByPostReq) returns (PlayListData)
|
||
|
||
@handler ListPlaysByExternal
|
||
get /by-external (PlayByExternalReq) returns (PlayListData)
|
||
|
||
@handler ResolveExternalLink
|
||
post /resolve-external (PlayResolveReq) returns (ExternalTargetPublic)
|
||
|
||
@handler GetPlay
|
||
get /:id (PlayIdPath) returns (PlayPublic)
|
||
|
||
@handler SavePlay
|
||
post / (PlaySaveReq) returns (PlayPublic)
|
||
|
||
@handler RemovePlay
|
||
delete /:id (PlayIdPath) returns (OkData)
|
||
|
||
@handler SubmitPlay
|
||
post /:id/submit (PlayIdPath) returns (OutboxPublic)
|
||
|
||
@handler GeneratePlayStep
|
||
post /generate-step (PlayGenerateStepReq) returns (PlayGenerateStepData)
|
||
|
||
@handler GeneratePlayScript
|
||
post /:id/generate-script (PlayIdPath) returns (PlayGenerateScriptData)
|
||
}
|
||
|
||
@server (
|
||
jwt: Auth
|
||
group: outbox
|
||
prefix: /api/v1/outbox
|
||
middleware: AuthJWT
|
||
)
|
||
service gateway {
|
||
@handler ListOutbox
|
||
get / returns (OutboxListData)
|
||
|
||
@handler GetOutbox
|
||
get /:id (OutboxIdPath) returns (OutboxPublic)
|
||
|
||
@handler RemoveOutbox
|
||
delete /:id (OutboxIdPath) returns (OkData)
|
||
|
||
@handler RetryOutboxStep
|
||
post /:id/steps/:stepId/retry (OutboxRetryPath) returns (OutboxPublic)
|
||
}
|
||
|
||
@server (
|
||
jwt: Auth
|
||
group: compose
|
||
prefix: /api/v1/compose
|
||
middleware: AuthJWT
|
||
)
|
||
service gateway {
|
||
@handler ComposeMimic
|
||
post /mimic (ComposeMimicReq) returns (ComposeMimicData)
|
||
|
||
@handler ComposePersonaPreview
|
||
post /persona-preview (ComposePersonaPreviewReq) returns (ComposePersonaPreviewData)
|
||
|
||
@handler ComposeAnalyzeViral
|
||
post /analyze-viral (ComposeViralReq) returns (ViralAnalysisPublic)
|
||
|
||
@handler ComposePublishSingle
|
||
post /publish-single (ComposePublishReq) returns (OutboxPublic)
|
||
}
|
||
|
||
@server (
|
||
jwt: Auth
|
||
group: ownposts
|
||
prefix: /api/v1/own-posts
|
||
middleware: AuthJWT
|
||
)
|
||
service gateway {
|
||
@handler ListOwnPosts
|
||
get / (OwnPostListReq) returns (OwnPostListData)
|
||
|
||
@handler OwnPostsLastSynced
|
||
get /last-synced-at returns (OwnPostLastSyncedData)
|
||
|
||
@handler SyncOwnPosts
|
||
post /sync (OwnPostSyncReq) returns (OwnPostListData)
|
||
|
||
@handler OwnPostGenerateReply
|
||
post /generate-reply (OwnPostGenerateReplyReq) returns (OwnPostGenerateReplyData)
|
||
|
||
@handler OwnPostSendReply
|
||
post /send-reply (OwnPostSendReplyReq) returns (OwnPostPublic)
|
||
|
||
@handler OwnPostAnalyze
|
||
post /analyze (OwnPostAnalyzeReq) returns (OwnPostPublic)
|
||
|
||
@handler OwnPostLoadReplies
|
||
post /load-replies (OwnPostLoadRepliesReq) returns (OwnPostPublic)
|
||
}
|
||
|
||
@server (
|
||
jwt: Auth
|
||
group: mentions
|
||
prefix: /api/v1/mentions
|
||
middleware: AuthJWT
|
||
)
|
||
service gateway {
|
||
@handler ListMentions
|
||
get / (MentionListReq) returns (MentionListData)
|
||
|
||
@handler SyncMentions
|
||
post /sync (MentionSyncReq) returns (MentionListData)
|
||
|
||
@handler MentionGenerateReply
|
||
post /:id/generate-reply (MentionGenerateReq) returns (MentionPublic)
|
||
|
||
@handler MentionMarkReplied
|
||
post /:id/mark-replied (MentionMarkRepliedReq) returns (MentionPublic)
|
||
|
||
@handler MentionSkip
|
||
post /:id/skip (MentionIdPath) returns (MentionPublic)
|
||
}
|