extern crate dotenv;
extern crate mailchimp;
extern crate paho_mqtt as mqtt;
extern crate serde;
#[macro_use]
extern crate serde_derive;
extern crate serde_json;
use dotenv::dotenv;
use std::env;
use std::process;
use mailchimp::types::{
ApiRootType, AutomationWorkflowType, CampaignType, ListType, StatisticsType,
};
use mailchimp::{
ApiRoot, Automations, AutomationsFilter, CampaignFilter, Campaigns, ListFilter, Lists,
MailchimpApi,
};
use std::collections::HashMap;
use std::time::Duration;
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct MailchimpAccountStats {
#[serde(default)]
pub measurement_name: String,
#[serde(default)]
pub client_name: String,
#[serde(default)]
pub total_subscribers: u64,
#[serde(default)]
pub industry_stats_open_rate: f32,
#[serde(default)]
pub industry_stats_bounce_rate: f32,
#[serde(default)]
pub industry_stats_click_rate: f32,
}
impl MailchimpAccountStats {
pub fn create_stats(ar: &ApiRootType) -> Self {
MailchimpAccountStats {
measurement_name: "mailchimp_account".to_string(),
client_name: ar.account_name.clone(),
total_subscribers: ar.total_subscribers,
industry_stats_open_rate: ar.industry_stats.open_rate,
industry_stats_bounce_rate: ar.industry_stats.bounce_rate,
industry_stats_click_rate: ar.industry_stats.click_rate,
}
}
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct MailchimpAutomationStats {
#[serde(default)]
pub measurement_name: String,
#[serde(default)]
pub client_name: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub status: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub emails_sent: Option<u64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub recipients_list_name: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub title: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub report_summary_opens: Option<u64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub report_summary_unique_opens: Option<u64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub report_summary_open_rate: Option<f32>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub report_summary_clicks: Option<u64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub report_summary_subscriber_clicks: Option<u64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub report_summary_click_rate: Option<f32>,
}
impl MailchimpAutomationStats {
pub fn create_stats<'a>(data: &AutomationWorkflowType, account_name: &'a str) -> Self {
let r_list_name = if let Some(r) = &data.recipients {
r.list_name.clone()
} else {
None
};
let s_title = if let Some(r) = &data.settings {
r.title.clone()
} else {
None
};
let mut rs_v = (0 as u64, 0 as u64, 0.0, 0 as u64, 0 as u64, 0.0);
if let Some(rp) = &data.report_summary {
rs_v.0 = rp.opens;
rs_v.1 = rp.unique_opens;
rs_v.2 = rp.open_rate;
rs_v.3 = rp.clicks;
rs_v.4 = rp.subscriber_clicks;
rs_v.5 = rp.click_rate;
}
MailchimpAutomationStats {
measurement_name: "mailchimp_automation".to_string(),
client_name: account_name.to_string(),
status: data.status.clone(),
emails_sent: data.emails_sent.clone(),
recipients_list_name: r_list_name,
title: s_title,
report_summary_opens: Some(rs_v.0),
report_summary_unique_opens: Some(rs_v.1),
report_summary_open_rate: Some(rs_v.2),
report_summary_clicks: Some(rs_v.3),
report_summary_subscriber_clicks: Some(rs_v.4),
report_summary_click_rate: Some(rs_v.5),
}
}
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct MailchimpCampaignStats {
#[serde(default)]
pub measurement_name: String,
#[serde(default)]
pub client_name: String,
#[serde(default, skip_serializing_if = "Option::is_none", rename = "type")]
pub campaign_type: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub status: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub emails_sent: Option<u64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub recipients_list_name: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub recipients_recipient_count: Option<u64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub title: Option<String>,
#[serde(default)]
pub report_summary_opens: u64,
#[serde(default)]
pub report_summary_unique_opens: u64,
#[serde(default)]
pub report_summary_open_rate: f32,
#[serde(default)]
pub report_summary_clicks: u64,
#[serde(default)]
pub report_summary_subscriber_clicks: u64,
#[serde(default)]
pub report_summary_click_rate: f32,
#[serde(default)]
pub report_summary_ecommerce_total_orders: u64,
#[serde(default)]
pub report_summary_ecommerce_total_spent: f32,
#[serde(default)]
pub report_summary_ecommerce_total_revenue: f32,
}
impl MailchimpCampaignStats {
pub fn create_stats<'a>(data: &CampaignType, account_name: &'a str) -> Self {
let mut settings = (Some(String::new()), None, Some(String::new()));
if let Some(rc) = data.recipients.as_ref() {
settings.0 = rc.list_name.clone();
settings.1 = rc.recipient_count;
}
if let Some(ss) = data.settings.as_ref() {
settings.2 = ss.title.clone();
}
let mut report_summary = (0, 0, 0.0, 0, 0, 0.0, 0, 0.0, 0.0);
if let Some(rs) = data.report_summary.as_ref() {
report_summary.0 = rs.opens;
report_summary.1 = rs.unique_opens;
report_summary.2 = rs.open_rate;
report_summary.3 = rs.clicks;
report_summary.4 = rs.subscriber_clicks;
report_summary.5 = rs.click_rate;
if let Some(e) = &rs.ecommerce {
report_summary.6 = e.total_orders;
report_summary.7 = e.total_spent;
report_summary.8 = e.total_revenue;
}
}
MailchimpCampaignStats {
measurement_name: "mailchimp_campaigns".to_string(),
client_name: account_name.to_string(),
campaign_type: data.campaign_type.clone(),
status: data.status.clone(),
emails_sent: data.emails_sent,
recipients_list_name: settings.0,
recipients_recipient_count: settings.1,
title: settings.2,
report_summary_opens: report_summary.0,
report_summary_unique_opens: report_summary.1,
report_summary_open_rate: report_summary.2,
report_summary_clicks: report_summary.3,
report_summary_subscriber_clicks: report_summary.4,
report_summary_click_rate: report_summary.5,
report_summary_ecommerce_total_orders: report_summary.6,
report_summary_ecommerce_total_spent: report_summary.7,
report_summary_ecommerce_total_revenue: report_summary.8,
}
}
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct MailchimpListStats {
#[serde(default)]
pub measurement_name: String,
#[serde(default)]
pub client_name: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub name: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub stats: Option<StatisticsType>,
}
impl MailchimpListStats {
pub fn create_stats<'a>(data: &ListType, account_name: &'a str) -> Self {
MailchimpListStats {
measurement_name: "mailchimp_lists".to_string(),
client_name: account_name.to_string(),
name: data.name.clone(),
stats: data.stats.clone(),
}
}
}
fn connect_mqtt<'a>(host: &'a str, user_name: &'a str, password: &'a str) -> mqtt::Client {
let endpoint = String::from("tcp://") + host;
let create_options = mqtt::CreateOptionsBuilder::new()
.server_uri(endpoint)
.persistence(mqtt::PersistenceType::None)
.finalize();
let cli = mqtt::Client::new(create_options).unwrap_or_else(|err| {
println!("Error creating the client: {:?}", err);
process::exit(1);
});
let conn_opts = mqtt::ConnectOptionsBuilder::new()
.keep_alive_interval(Duration::from_secs(20))
.user_name(user_name)
.password(password)
.clean_session(true)
.finalize();
match cli.connect(conn_opts) {
Ok(_) => println!("Connect: Successs \n"),
Err(e) => {
println!("Unable to connect:\n\t{:?}", e);
process::exit(1);
}
};
cli
}
fn send_message<'a, T>(cli: &mqtt::Client, topic: &'a str, mas: &T)
where
T: serde::Serialize,
{
let payload = serde_json::to_string(mas);
println!("Topic: {:?} payload {:?}", topic, payload);
let msg = mqtt::MessageBuilder::new()
.topic(topic)
.payload(payload.unwrap().as_str())
.qos(1)
.finalize();
if let Err(e) = cli.publish(msg) {
println!("Error sending message: {:?}", e);
}
}
fn disconnect(cli: &mqtt::Client) {
cli.disconnect(None).unwrap();
}
fn main() {
dotenv().ok();
let mut env_mailchimp = env::vars().filter(|e| e.0.to_string().contains("MAILCHIMP_"));
let apk = env_mailchimp.next().unwrap().1;
let mut mqtt_settings = (String::new(), String::new(), String::new());
for v in env::vars() {
if v.0 == "MQTT_USER" {
mqtt_settings.0 = v.1.clone();
}
if v.0 == "MQTT_PASSWORD" {
mqtt_settings.1 = v.1.clone();
}
if v.0 == "MQTT_HOST" {
mqtt_settings.2 = v.1.clone();
}
}
println!("MQTT Settings: {:?}", mqtt_settings);
let mqtt_client = connect_mqtt(
mqtt_settings.2.as_str(),
mqtt_settings.0.as_str(),
mqtt_settings.1.as_str(),
);
let api = MailchimpApi::new(&apk);
let api_root = ApiRoot::new(api.clone());
let account_info = api_root.get_info(HashMap::new());
let mut account_name = String::new();
match account_info {
Ok(account) => {
let stats = MailchimpAccountStats::create_stats(&account);
account_name = account.account_name.clone();
send_message(&mqtt_client, "mailchimp/stats/account", &stats);
}
Err(e) => println!("Error: {:?}", e),
}
let automation = Automations::new(api.clone());
for aut in automation.iter(AutomationsFilter::default()) {
let stats = MailchimpAutomationStats::create_stats(&aut, &account_name);
send_message(&mqtt_client, "mailchimp/stats/automations", &stats);
}
let campaigns = Campaigns::new(api.clone());
for data in campaigns.iter(CampaignFilter::default()) {
let stats = MailchimpCampaignStats::create_stats(&data, &account_name);
send_message(&mqtt_client, "mailchimp/stats/campaigns", &stats);
}
let lists = Lists::new(api.clone());
for data in lists.iter(ListFilter::default()) {
let stats = MailchimpListStats::create_stats(&data, &account_name);
send_message(&mqtt_client, "mailchimp/stats/lists", &stats);
}
disconnect(&mqtt_client);
}