slack-framework-rs 0.1.2

`slack-framework-rs` is the library that [the author](https://gitlab.com/nogiro) uses to create the Slack App.
Documentation
use sfr_core as sc;
use sfr_server as ss;
use sfr_slack_api as ssa;
use sfr_types as st;

use derive_new::new;
use sc::{OauthV2AccessResponse, Slack, SlashCommandBody};
use ss::{
    HomeHandlerTrait, OauthHandlerTrait, OauthRedirectResponse, ResponseError, Server,
    SlashCommandHandlerTrait,
};
use ssa::Client;
use std::env;

#[tokio::main]
async fn main() -> Result<(), st::Error> {
    let args: Vec<String> = env::args().collect();
    let sock = args.get(1).unwrap();
    let url_prefix = env::var("SSCT_URL_PREFIX").unwrap(); // scheme - FQDN

    let id = env::var("SSCT_SLACK_ID").unwrap();
    let secret = env::var("SSCT_SLACK_SECRET").unwrap();
    let signing_secret = env::var("SSCT_SLACK_SIGNING_SECRET").unwrap();
    let verification_token = env::var("SSCT_SLACK_VERIFICATION_TOKEN").unwrap();
    let oauth_token = env::var("SSCT_SLACK_OAUTH_TOKEN").unwrap();
    let log = env::var("RUST_LOG").unwrap_or_else(|_| "info".to_string());

    let server_config = ss::Config::builder()
        .sock(sock.parse().unwrap())
        .log(Some(log))
        .build();

    let slack_creds = sc::AppCredentials {
        id,
        secret,
        signing_secret,
        verification_token,
    };
    let slack_config = sc::Config {
        timestamp_range: 300,
    };

    let slack = Slack::new(slack_creds, slack_config);
    let client = reqwest::Client::new();
    let dummy = DummyHandler;
    let handler = Handler::new(url_prefix);
    let oauth_handler = OauthHandler::new(oauth_token);

    let server = Server::new(server_config, slack, client, dummy, handler, oauth_handler);
    server.serve().await
}

#[derive(new)]
struct Handler {
    url_prefix: String,
}

#[derive(new)]
struct OauthHandler {
    token: String,
}

struct DummyHandler;

impl SlashCommandHandlerTrait for Handler {
    async fn handle_slash_command(
        &self,
        client: &Client,
        _body: SlashCommandBody,
    ) -> Result<st::SlashCommandResponse, ResponseError> {
        let external_id = uuid::Uuid::new_v4().to_string();
        let external_url = format!("{}/remote/loop1.wav", self.url_prefix);
        tracing::debug!("external_id: {}", external_id);
        tracing::debug!("external_url: {}", external_url);

        let request_body = ssa::RequestBuilder::files_remote_add()
            .external_id(external_id.clone())
            .external_url(external_url)
            .title("audio".to_string())
            .build()
            .inspect_err(|e| {
                tracing::error!("failed to build api object: {e}");
            })
            .map_err(|e| ResponseError::InternalServerError(Box::new(e)))?;
        tracing::debug!("request_body: {:?}", request_body);

        let response_body = client
            .request(request_body)
            .await
            .inspect_err(|e| tracing::error!("failed to request: {e}"))
            .map_err(|e| ResponseError::InternalServerError(Box::new(e)))?;

        tracing::info!("requested: {response_body:?}");

        let message = st::MessagePayloads {
            text: "loop1.wav".to_string(),
            blocks: vec![st::Block::File(st::FileBlock::new(st::ExternalId(
                external_id,
            )))],
            attachments: vec![],
            thread_ts: None,
            mrkdwn: None,
        };
        let layouts = st::Layouts::BlocksArray(message);
        let resp = st::SlashCommandResponse::Layouts(layouts);

        Ok(resp.into())
    }
}

impl OauthHandlerTrait for OauthHandler {
    async fn handle_oauth(
        &self,
        _client: reqwest::Client,
        body: OauthV2AccessResponse,
    ) -> Result<Option<OauthRedirectResponse>, ResponseError> {
        tracing::info!("body: {body:?}");

        Ok(None)
    }

    async fn take_oauth_token_from_team_id(&self, _team_id: &str) -> Result<String, ResponseError> {
        Ok(self.token.clone())
    }

    /// not use in the example.
    fn redirect_uri(&self) -> &str {
        unimplemented!()
    }
}

impl HomeHandlerTrait for DummyHandler {
    async fn handle_home(&self) -> Result<Vec<u8>, ResponseError> {
        const RESPONSE: &str = r#"<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><title></title></head><body>ok</body></html>"#;
        Ok(RESPONSE.into())
    }
}