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 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 oauth_handler = OauthHandler::new(oauth_token);

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

struct Handler;

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

impl SlashCommandHandlerTrait for Handler {
    async fn handle_slash_command(
        &self,
        client: &Client,
        body: SlashCommandBody,
    ) -> Result<st::SlashCommandResponse, ResponseError> {
        let client = client.clone();

        tokio::spawn(async move {
            let request_body = ssa::RequestBuilder::files_upload()
                .channels(vec![body.channel_id.clone()])
                .file("./assets/remote/loop1.wav".to_string())
                .filename("loop1.wav".to_string())
                .build()
                .inspect_err(|e| tracing::error!("failed to build api object: {e}"));
            tracing::debug!("request_body: {:?}", request_body);

            if let Ok(request_body) = request_body {
                let response_body = client
                    .request(request_body)
                    .await
                    .inspect_err(|e| tracing::error!("failed to request: {e}"));
                tracing::info!("requested: {:?}", response_body);
            }
        });

        Ok(st::SlashCommandResponse::empty().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 Handler {
    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())
    }
}