late 0.0.751

API reference for Zernio. Authenticate with a Bearer API key. Base URL: https://zernio.com/api Versioning and deprecation: all endpoints are versioned in the URL path (current version: /v1). Breaking changes only ship in a new path version; existing versions keep working. Deprecated operations are marked 'deprecated: true' in this spec and announced in the changelog (https://zernio.com/changelog) before removal. Errors: every 4xx/5xx response is application/json with a machine-readable 'code' and a human-readable 'error' message (see the ErrorResponse schema).
Documentation
/*
 * Zernio API
 *
 * API reference for Zernio. Authenticate with a Bearer API key. Base URL: https://zernio.com/api  Versioning and deprecation: all endpoints are versioned in the URL path (current version: /v1). Breaking changes only ship in a new path version; existing versions keep working. Deprecated operations are marked 'deprecated: true' in this spec and announced in the changelog (https://zernio.com/changelog) before removal.  Errors: every 4xx/5xx response is application/json with a machine-readable 'code' and a human-readable 'error' message (see the ErrorResponse schema).
 *
 * The version of the OpenAPI document: 1.0.4
 * Contact: support@zernio.com
 * Generated by: https://openapi-generator.tech
 */

use crate::models;
use serde::{Deserialize, Serialize};

#[derive(Clone, Default, Debug, PartialEq, Serialize, Deserialize)]
pub struct CreateCommentAutomationRequest {
    #[serde(rename = "profileId")]
    pub profile_id: String,
    /// Instagram or Facebook account ID
    #[serde(rename = "accountId")]
    pub account_id: String,
    /// What fires the automation. 'comment' (keyword comment on a post) or 'story_reply' (keyword reply to an Instagram story). For 'story_reply', platformPostId is the story media id (omit for any story).
    #[serde(rename = "trigger", skip_serializing_if = "Option::is_none")]
    pub trigger: Option<Trigger>,
    /// Platform media/post ID (or story media id when trigger=story_reply). Omit for an account-wide (any-post / any-story) automation.
    #[serde(rename = "platformPostId", skip_serializing_if = "Option::is_none")]
    pub platform_post_id: Option<String>,
    /// Zernio post ID. Required only when also targeting a specific post via platformPostId.
    #[serde(rename = "postId", skip_serializing_if = "Option::is_none")]
    pub post_id: Option<String>,
    /// Post content snippet for display
    #[serde(rename = "postTitle", skip_serializing_if = "Option::is_none")]
    pub post_title: Option<String>,
    /// Automation label
    #[serde(rename = "name")]
    pub name: String,
    /// Trigger keywords (empty = any comment triggers)
    #[serde(rename = "keywords", skip_serializing_if = "Option::is_none")]
    pub keywords: Option<Vec<String>>,
    /// How a keyword is compared with the comment. 'contains' (default) matches anywhere, even inside another word (keyword 'app' fires on 'happy'). 'word' matches the keyword only as a standalone word. 'exact' requires the whole comment to be exactly the keyword.
    #[serde(rename = "matchMode", skip_serializing_if = "Option::is_none")]
    pub match_mode: Option<MatchMode>,
    /// Comments containing one of these never trigger the automation, even when a trigger keyword also matches. Compared using the same matchMode.
    #[serde(rename = "excludeKeywords", skip_serializing_if = "Option::is_none")]
    pub exclude_keywords: Option<Vec<String>>,
    /// Only with matchMode=word: also fire on close misspellings of a keyword (one edit for 4-7 character keywords, two from 8 up). Keywords shorter than 4 characters are never fuzzy-matched.
    #[serde(rename = "typoTolerance", skip_serializing_if = "Option::is_none")]
    pub typo_tolerance: Option<bool>,
    /// DM text to send to commenter. Max 640 chars when buttons are set, otherwise ~1000.
    #[serde(rename = "dmMessage")]
    pub dm_message: String,
    /// Optional inline DM buttons (1-3). Phone buttons are Facebook-only. Omit or pass [] for a plain-text DM.
    #[serde(rename = "buttons", skip_serializing_if = "Option::is_none")]
    pub buttons: Option<Vec<models::DmButton>>,
    /// Optional product card sent INSTEAD of the plain dmMessage bubble. Mutually exclusive with buttons. dmMessage stays required: it is what gets sent the moment the card is cleared.
    #[serde(
        rename = "template",
        default,
        with = "::serde_with::rust::double_option",
        skip_serializing_if = "Option::is_none"
    )]
    pub template: Option<Option<Box<models::CommentAutomationTemplate>>>,
    /// Optional public reply to the comment
    #[serde(rename = "commentReply", skip_serializing_if = "Option::is_none")]
    pub comment_reply: Option<String>,
    /// Optional alternate DM texts for random rotation. When set, each triggered comment sends one picked at random from [dmMessage, ...dmMessageVariations], so repeat commenters get slightly different DMs (helps avoid identical-message patterns). Up to 5. Buttons are attached to whichever text is picked, not varied.
    #[serde(
        rename = "dmMessageVariations",
        skip_serializing_if = "Option::is_none"
    )]
    pub dm_message_variations: Option<Vec<String>>,
    /// Optional alternate public replies, rotated at random alongside commentReply (picked independently of the DM). Up to 5.
    #[serde(
        rename = "commentReplyVariations",
        skip_serializing_if = "Option::is_none"
    )]
    pub comment_reply_variations: Option<Vec<String>>,
    /// Wrap link buttons in the DM in a tracked redirect so clicks are counted (Link Clicks / CTR). Pass false to send links exactly as written. Defaults to on.
    #[serde(rename = "linkTracking", skip_serializing_if = "Option::is_none")]
    pub link_tracking: Option<bool>,
    /// Optional tag applied to a contact when they click a tracked link (requires linkTracking). Lets you segment clickers for broadcasts/sequences.
    #[serde(rename = "clickTag", skip_serializing_if = "Option::is_none")]
    pub click_tag: Option<String>,
    /// Seconds to wait after the trigger before sending the DM. Omit or send 0 to reply immediately (the default). Max 86400 (24h). The trigger is still matched and deduplicated the moment the comment arrives, so a delay only moves when the response is sent.
    #[serde(rename = "dmDelaySeconds", skip_serializing_if = "Option::is_none")]
    pub dm_delay_seconds: Option<i32>,
    /// Seconds to wait before posting the public comment reply. Omit or send 0 to post it right after the DM (the default). The reply never goes out before the DM, so a value below dmDelaySeconds is raised to it. Ignored when trigger=story_reply, which has no public reply.
    #[serde(
        rename = "commentReplyDelaySeconds",
        skip_serializing_if = "Option::is_none"
    )]
    pub comment_reply_delay_seconds: Option<i32>,
    /// Also fire these keywords on a plain inbound DM, so the automation answers people who message the keyword instead of commenting it. Requires at least one keyword (an empty keyword list means 'match anything', which would answer every inbound message) and is rejected on story_reply automations, which already trigger on DMs. Dedup is per door: a contact who already received the DM from their comment can still receive it from a DM.
    #[serde(rename = "alsoMatchInDms", skip_serializing_if = "Option::is_none")]
    pub also_match_in_dms: Option<bool>,
    #[serde(rename = "audience", skip_serializing_if = "Option::is_none")]
    pub audience: Option<Box<models::CommentAutomationAudience>>,
    #[serde(rename = "followGate", skip_serializing_if = "Option::is_none")]
    pub follow_gate: Option<Box<models::CommentAutomationFollowGate>>,
}

impl CreateCommentAutomationRequest {
    pub fn new(
        profile_id: String,
        account_id: String,
        name: String,
        dm_message: String,
    ) -> CreateCommentAutomationRequest {
        CreateCommentAutomationRequest {
            profile_id,
            account_id,
            trigger: None,
            platform_post_id: None,
            post_id: None,
            post_title: None,
            name,
            keywords: None,
            match_mode: None,
            exclude_keywords: None,
            typo_tolerance: None,
            dm_message,
            buttons: None,
            template: None,
            comment_reply: None,
            dm_message_variations: None,
            comment_reply_variations: None,
            link_tracking: None,
            click_tag: None,
            dm_delay_seconds: None,
            comment_reply_delay_seconds: None,
            also_match_in_dms: None,
            audience: None,
            follow_gate: None,
        }
    }
}
/// What fires the automation. 'comment' (keyword comment on a post) or 'story_reply' (keyword reply to an Instagram story). For 'story_reply', platformPostId is the story media id (omit for any story).
#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Serialize, Deserialize)]
pub enum Trigger {
    #[serde(rename = "comment")]
    Comment,
    #[serde(rename = "story_reply")]
    StoryReply,
}

impl Default for Trigger {
    fn default() -> Trigger {
        Self::Comment
    }
}
/// How a keyword is compared with the comment. 'contains' (default) matches anywhere, even inside another word (keyword 'app' fires on 'happy'). 'word' matches the keyword only as a standalone word. 'exact' requires the whole comment to be exactly the keyword.
#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Serialize, Deserialize)]
pub enum MatchMode {
    #[serde(rename = "exact")]
    Exact,
    #[serde(rename = "contains")]
    Contains,
    #[serde(rename = "word")]
    Word,
}

impl Default for MatchMode {
    fn default() -> MatchMode {
        Self::Exact
    }
}