/*
* 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). Request ids: responses carry an X-Request-Id header with the id we log the request under. Quote it when reporting a problem. A valid x-request-id you send is reused as that id.
*
* The version of the OpenAPI document: 1.146.0
* Contact: support@zernio.com
* Generated by: https://openapi-generator.tech
*/
use super::{configuration, ContentType, Error};
use crate::{apis::ResponseContent, models};
use reqwest;
use serde::{de::Error as _, Deserialize, Serialize};
/// struct for typed errors of method [`create_comment_automation`]
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum CreateCommentAutomationError {
Status400(),
Status401(models::InlineObject1),
Status409(),
Status503(models::ErrorResponse),
UnknownValue(serde_json::Value),
}
/// struct for typed errors of method [`delete_comment_automation`]
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum DeleteCommentAutomationError {
Status401(models::InlineObject1),
Status404(models::InlineObject2),
UnknownValue(serde_json::Value),
}
/// struct for typed errors of method [`get_comment_automation`]
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum GetCommentAutomationError {
Status401(models::InlineObject1),
Status404(models::InlineObject2),
UnknownValue(serde_json::Value),
}
/// struct for typed errors of method [`list_comment_automation_logs`]
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum ListCommentAutomationLogsError {
Status400(models::ErrorResponse),
Status401(models::InlineObject1),
Status404(models::InlineObject2),
UnknownValue(serde_json::Value),
}
/// struct for typed errors of method [`list_comment_automations`]
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum ListCommentAutomationsError {
Status400(),
Status401(models::InlineObject1),
Status503(models::ErrorResponse),
UnknownValue(serde_json::Value),
}
/// struct for typed errors of method [`update_comment_automation`]
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum UpdateCommentAutomationError {
Status401(models::InlineObject1),
Status404(models::InlineObject2),
UnknownValue(serde_json::Value),
}
/// Create a keyword-triggered DM automation on an Instagram or Facebook account. When someone comments a matching keyword (or, with `trigger: story_reply`, replies to your Instagram story with one), they automatically receive a DM. To continue into a specific workflow after the recipient taps a button, use `{\"type\":\"postback\",\"title\":\"Send it\",\"payload\":\"zernio:workflow:<workflowId>\"}`. The target must be active and belong to the same account and profile. This also works for product-card buttons. The tap starts that workflow directly, without matching its keyword or first-message condition. A different live workflow in the conversation is exited; tapping the same live workflow does not restart it or consume a pending reply. Stale or invalid targets do nothing. The initial comment DM alone does not start the workflow: the recipient must tap. Triggers (`trigger`): * `comment` (default): fires on keyword comments on a post or reel. * `story_reply`: fires when someone replies to your Instagram story with a keyword, and answers them with a DM. Set `platformPostId` to a story media id to scope to one story, or omit it to match replies to any story. Targeting (comment trigger): * Per-post: set `platformPostId` to scope to one specific post (only one active per-post automation is allowed per post). * Account-wide (\"any post\"): omit `platformPostId` (and `postId`). The automation evaluates every comment on every post on the account. You can stack unlimited account-wide automations, each with its own keyword set, and they all run independently. Per-post automations take priority on their post. Audience (`audience`, Instagram only): restrict the automation to followers or non-followers, and/or to accounts above a follower count. Instagram only reveals the follow relationship for people who have messaged the account, so `audience.whenUnknown` decides what happens for everyone else - including `verify`, which sends a one-tap confirmation DM (`followGate`) and then delivers the real DM automatically. People we already know follow you skip the tap entirely. Set `alsoMatchInDms: true` on a `comment` automation to also answer people who send a keyword as a direct message instead of commenting it. One automation then covers both doors, and each door is deduplicated separately (someone who already got the DM from their comment still gets it if they later DM the keyword). Requires at least one keyword. Links in the DM's buttons can be click-tracked (`linkTracking`, on by default) and clickers optionally tagged (`clickTag`) for segmentation. Stats returned include delivered, read, and link clicks.
pub async fn create_comment_automation(
configuration: &configuration::Configuration,
create_comment_automation_request: models::CreateCommentAutomationRequest,
) -> Result<models::CreateCommentAutomation200Response, Error<CreateCommentAutomationError>> {
// add a prefix to parameters to efficiently prevent name collisions
let p_body_create_comment_automation_request = create_comment_automation_request;
let uri_str = format!("{}/v1/comment-automations", configuration.base_path);
let mut req_builder = configuration
.client
.request(reqwest::Method::POST, &uri_str);
if let Some(ref user_agent) = configuration.user_agent {
req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone());
}
if let Some(ref token) = configuration.bearer_access_token {
req_builder = req_builder.bearer_auth(token.to_owned());
};
req_builder = req_builder.json(&p_body_create_comment_automation_request);
let req = req_builder.build()?;
let resp = configuration.client.execute(req).await?;
let status = resp.status();
let content_type = resp
.headers()
.get("content-type")
.and_then(|v| v.to_str().ok())
.unwrap_or("application/octet-stream");
let content_type = super::ContentType::from(content_type);
if !status.is_client_error() && !status.is_server_error() {
let content = resp.text().await?;
match content_type {
ContentType::Json => serde_json::from_str(&content).map_err(Error::from),
ContentType::Text => return Err(Error::from(serde_json::Error::custom("Received `text/plain` content type response that cannot be converted to `models::CreateCommentAutomation200Response`"))),
ContentType::Unsupported(unknown_type) => return Err(Error::from(serde_json::Error::custom(format!("Received `{unknown_type}` content type response that cannot be converted to `models::CreateCommentAutomation200Response`")))),
}
} else {
let content = resp.text().await?;
let entity: Option<CreateCommentAutomationError> = serde_json::from_str(&content).ok();
Err(Error::ResponseError(ResponseContent {
status,
content,
entity,
}))
}
}
/// Permanently delete an automation and all its trigger logs.
pub async fn delete_comment_automation(
configuration: &configuration::Configuration,
automation_id: &str,
) -> Result<(), Error<DeleteCommentAutomationError>> {
// add a prefix to parameters to efficiently prevent name collisions
let p_path_automation_id = automation_id;
let uri_str = format!(
"{}/v1/comment-automations/{automationId}",
configuration.base_path,
automationId = crate::apis::urlencode(p_path_automation_id)
);
let mut req_builder = configuration
.client
.request(reqwest::Method::DELETE, &uri_str);
if let Some(ref user_agent) = configuration.user_agent {
req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone());
}
if let Some(ref token) = configuration.bearer_access_token {
req_builder = req_builder.bearer_auth(token.to_owned());
};
let req = req_builder.build()?;
let resp = configuration.client.execute(req).await?;
let status = resp.status();
if !status.is_client_error() && !status.is_server_error() {
Ok(())
} else {
let content = resp.text().await?;
let entity: Option<DeleteCommentAutomationError> = serde_json::from_str(&content).ok();
Err(Error::ResponseError(ResponseContent {
status,
content,
entity,
}))
}
}
/// Returns an automation with its configuration, stats, and recent trigger logs.
pub async fn get_comment_automation(
configuration: &configuration::Configuration,
automation_id: &str,
) -> Result<models::GetCommentAutomation200Response, Error<GetCommentAutomationError>> {
// add a prefix to parameters to efficiently prevent name collisions
let p_path_automation_id = automation_id;
let uri_str = format!(
"{}/v1/comment-automations/{automationId}",
configuration.base_path,
automationId = crate::apis::urlencode(p_path_automation_id)
);
let mut req_builder = configuration.client.request(reqwest::Method::GET, &uri_str);
if let Some(ref user_agent) = configuration.user_agent {
req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone());
}
if let Some(ref token) = configuration.bearer_access_token {
req_builder = req_builder.bearer_auth(token.to_owned());
};
let req = req_builder.build()?;
let resp = configuration.client.execute(req).await?;
let status = resp.status();
let content_type = resp
.headers()
.get("content-type")
.and_then(|v| v.to_str().ok())
.unwrap_or("application/octet-stream");
let content_type = super::ContentType::from(content_type);
if !status.is_client_error() && !status.is_server_error() {
let content = resp.text().await?;
match content_type {
ContentType::Json => serde_json::from_str(&content).map_err(Error::from),
ContentType::Text => return Err(Error::from(serde_json::Error::custom("Received `text/plain` content type response that cannot be converted to `models::GetCommentAutomation200Response`"))),
ContentType::Unsupported(unknown_type) => return Err(Error::from(serde_json::Error::custom(format!("Received `{unknown_type}` content type response that cannot be converted to `models::GetCommentAutomation200Response`")))),
}
} else {
let content = resp.text().await?;
let entity: Option<GetCommentAutomationError> = serde_json::from_str(&content).ok();
Err(Error::ResponseError(ResponseContent {
status,
content,
entity,
}))
}
}
/// Paginated list of every comment that triggered this automation, with send status and commenter info.
pub async fn list_comment_automation_logs(
configuration: &configuration::Configuration,
automation_id: &str,
status: Option<&str>,
limit: Option<i32>,
skip: Option<i32>,
) -> Result<models::ListCommentAutomationLogs200Response, Error<ListCommentAutomationLogsError>> {
// add a prefix to parameters to efficiently prevent name collisions
let p_path_automation_id = automation_id;
let p_query_status = status;
let p_query_limit = limit;
let p_query_skip = skip;
let uri_str = format!(
"{}/v1/comment-automations/{automationId}/logs",
configuration.base_path,
automationId = crate::apis::urlencode(p_path_automation_id)
);
let mut req_builder = configuration.client.request(reqwest::Method::GET, &uri_str);
if let Some(ref param_value) = p_query_status {
req_builder = req_builder.query(&[("status", ¶m_value.to_string())]);
}
if let Some(ref param_value) = p_query_limit {
req_builder = req_builder.query(&[("limit", ¶m_value.to_string())]);
}
if let Some(ref param_value) = p_query_skip {
req_builder = req_builder.query(&[("skip", ¶m_value.to_string())]);
}
if let Some(ref user_agent) = configuration.user_agent {
req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone());
}
if let Some(ref token) = configuration.bearer_access_token {
req_builder = req_builder.bearer_auth(token.to_owned());
};
let req = req_builder.build()?;
let resp = configuration.client.execute(req).await?;
let status = resp.status();
let content_type = resp
.headers()
.get("content-type")
.and_then(|v| v.to_str().ok())
.unwrap_or("application/octet-stream");
let content_type = super::ContentType::from(content_type);
if !status.is_client_error() && !status.is_server_error() {
let content = resp.text().await?;
match content_type {
ContentType::Json => serde_json::from_str(&content).map_err(Error::from),
ContentType::Text => return Err(Error::from(serde_json::Error::custom("Received `text/plain` content type response that cannot be converted to `models::ListCommentAutomationLogs200Response`"))),
ContentType::Unsupported(unknown_type) => return Err(Error::from(serde_json::Error::custom(format!("Received `{unknown_type}` content type response that cannot be converted to `models::ListCommentAutomationLogs200Response`")))),
}
} else {
let content = resp.text().await?;
let entity: Option<ListCommentAutomationLogsError> = serde_json::from_str(&content).ok();
Err(Error::ResponseError(ResponseContent {
status,
content,
entity,
}))
}
}
/// List all comment-to-DM automations for a profile. Returns automations with their stats.
pub async fn list_comment_automations(
configuration: &configuration::Configuration,
profile_id: Option<&str>,
) -> Result<models::ListCommentAutomations200Response, Error<ListCommentAutomationsError>> {
// add a prefix to parameters to efficiently prevent name collisions
let p_query_profile_id = profile_id;
let uri_str = format!("{}/v1/comment-automations", configuration.base_path);
let mut req_builder = configuration.client.request(reqwest::Method::GET, &uri_str);
if let Some(ref param_value) = p_query_profile_id {
req_builder = req_builder.query(&[("profileId", ¶m_value.to_string())]);
}
if let Some(ref user_agent) = configuration.user_agent {
req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone());
}
if let Some(ref token) = configuration.bearer_access_token {
req_builder = req_builder.bearer_auth(token.to_owned());
};
let req = req_builder.build()?;
let resp = configuration.client.execute(req).await?;
let status = resp.status();
let content_type = resp
.headers()
.get("content-type")
.and_then(|v| v.to_str().ok())
.unwrap_or("application/octet-stream");
let content_type = super::ContentType::from(content_type);
if !status.is_client_error() && !status.is_server_error() {
let content = resp.text().await?;
match content_type {
ContentType::Json => serde_json::from_str(&content).map_err(Error::from),
ContentType::Text => return Err(Error::from(serde_json::Error::custom("Received `text/plain` content type response that cannot be converted to `models::ListCommentAutomations200Response`"))),
ContentType::Unsupported(unknown_type) => return Err(Error::from(serde_json::Error::custom(format!("Received `{unknown_type}` content type response that cannot be converted to `models::ListCommentAutomations200Response`")))),
}
} else {
let content = resp.text().await?;
let entity: Option<ListCommentAutomationsError> = serde_json::from_str(&content).ok();
Err(Error::ResponseError(ResponseContent {
status,
content,
entity,
}))
}
}
/// Update an automation's keywords, DM message, inline buttons, comment reply, or active status. Pass `buttons: []` to clear all buttons. When `buttons` is non-empty, `dmMessage` (the new one if you're changing it, otherwise the stored one) must be 640 characters or less.
pub async fn update_comment_automation(
configuration: &configuration::Configuration,
automation_id: &str,
update_comment_automation_request: Option<models::UpdateCommentAutomationRequest>,
) -> Result<models::UpdateCommentAutomation200Response, Error<UpdateCommentAutomationError>> {
// add a prefix to parameters to efficiently prevent name collisions
let p_path_automation_id = automation_id;
let p_body_update_comment_automation_request = update_comment_automation_request;
let uri_str = format!(
"{}/v1/comment-automations/{automationId}",
configuration.base_path,
automationId = crate::apis::urlencode(p_path_automation_id)
);
let mut req_builder = configuration
.client
.request(reqwest::Method::PATCH, &uri_str);
if let Some(ref user_agent) = configuration.user_agent {
req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone());
}
if let Some(ref token) = configuration.bearer_access_token {
req_builder = req_builder.bearer_auth(token.to_owned());
};
req_builder = req_builder.json(&p_body_update_comment_automation_request);
let req = req_builder.build()?;
let resp = configuration.client.execute(req).await?;
let status = resp.status();
let content_type = resp
.headers()
.get("content-type")
.and_then(|v| v.to_str().ok())
.unwrap_or("application/octet-stream");
let content_type = super::ContentType::from(content_type);
if !status.is_client_error() && !status.is_server_error() {
let content = resp.text().await?;
match content_type {
ContentType::Json => serde_json::from_str(&content).map_err(Error::from),
ContentType::Text => return Err(Error::from(serde_json::Error::custom("Received `text/plain` content type response that cannot be converted to `models::UpdateCommentAutomation200Response`"))),
ContentType::Unsupported(unknown_type) => return Err(Error::from(serde_json::Error::custom(format!("Received `{unknown_type}` content type response that cannot be converted to `models::UpdateCommentAutomation200Response`")))),
}
} else {
let content = resp.text().await?;
let entity: Option<UpdateCommentAutomationError> = serde_json::from_str(&content).ok();
Err(Error::ResponseError(ResponseContent {
status,
content,
entity,
}))
}
}