elf_utils 0.1.4

elf_rust utils
Documentation
mod utils;
mod msg;

use serde_derive::{Deserialize, Serialize};
use msg::{Text, Card};
use std::thread;
use std::time::Duration;
use reqwest::blocking::Client;

#[derive(Debug, Serialize, Deserialize)]
pub struct WebHookInput {
    timestamp: String,
    sign: String,
    msg_type: String,
    content: Option<Text>,
    card: Option<Card>,
}

#[derive(Debug, Serialize, Deserialize)]
pub struct WebHookOutput {
    #[serde(rename = "StatusCode")]
    status_code: i64,

    #[serde(rename = "StatusMessage")]
    status_message: String,
}

#[allow(dead_code)]
pub fn send_text(msg: String) {
    let timestamp = chrono::Local::now().timestamp();
    let input = WebHookInput {
        timestamp: timestamp.to_string(),
        sign: utils::gen_sign(utils::get_fs_secret().as_str(), timestamp),
        msg_type: "text".to_string(),
        content: Some(Text::new(msg)),
        card: None,
    };
    if let Err(e) = send(&input) {
        log::error!("send text to feishu failed: {:?}", e)
    };
}
#[allow(dead_code)]
pub fn send_card(card: Card) {
    let disable_feishu = std::env::var("DISABLED_NOTIFIER");
    if disable_feishu.is_ok() && disable_feishu.unwrap() == "true" {
        return;
    }
    let timestamp = chrono::Local::now().timestamp();
    let input = WebHookInput {
        timestamp: timestamp.to_string(),
        sign: utils::gen_sign(utils::get_fs_secret().as_str(), timestamp),
        msg_type: "interactive".to_string(),
        content: None,
        card: Some(card),
    };
    if let Err(e) = send(&input) {
        log::error!("send card to feishu failed: {:?}", e)
    };
}

#[allow(dead_code)]
pub fn report_card(title: &str, msg: &str, at: &str) {
    let now = chrono::Local::now()
        .format("%Y/%m/%d %H:%M")
        .to_string();
    let message = std::format!("detail:\n{}", msg);
    let is_err = msg.contains("错误") || msg.contains("err");
    let user = if !at.is_empty() && is_err {
        std::format!("<at id={}></at>\n", at)
    } else {
        "".to_string()
    };
    let time = std::format!("time:\n {}", now.as_str());
    let mut card = Card::new(
        title.to_string(), //标题
        std::vec![message, user, time],
        None, //链接
    );
    if is_err {
        card.set_title_color("red");
    }
    send_card(card);
}


pub fn send(input: &WebHookInput) -> Result<WebHookOutput, String> {
    log::debug!("send to feishu input: {:?}", input);

    let client = Client::new();
    let mut retries = 3;
    let mut wait_time = Duration::from_secs(5);

    while retries > 0 {
        let response = client
            .post(utils::get_fs_url())
            .header("content-type", "application/json")
            .json(&input)
            .send();

        match response {
            Ok(resp) => {
                let http_code = resp.status();
                let body = match resp.text() {
                    Ok(text) => text,
                    Err(e) => return Err(format!("Failed to read response text: {:?}", e)),
                };
                log::debug!(
                    "send to feishu http status code: {:?}, body: {:?}",
                    http_code,
                    body
                );

                let output: WebHookOutput = match serde_json::from_str(&body) {
                    Ok(data) => data,
                    Err(e) => return Err(format!("Failed to parse response body: {:?}", e)),
                };

                if http_code == reqwest::StatusCode::OK && output.status_code == 0 {
                    return Ok(output);
                } else {
                    log::error!(
                        "send to feishu failed with status code: {}, message: {}",
                        output.status_code,
                        output.status_message
                    );
                    if http_code.is_server_error() {
                        retries -= 1;
                        thread::sleep(wait_time);
                        wait_time *= 2;
                    } else {
                        return Err(format!(
                            "send to feishu failed with status code: {}, message: {}",
                            output.status_code, output.status_message
                        ));
                    }
                }
            }
            Err(e) => {
                if retries == 1 {
                    return Err(format!("send to feishu failed: {:?}", e));
                }
                log::error!("send to feishu failed: {:?}", e);
                retries -= 1;
                thread::sleep(wait_time);
                wait_time *= 2;
            }
        }
    }
    Err("send to feishu failed after retries".to_string())
}