actix-telegram 0.0.5

telegram bot api lib based on actix
#[cfg(feature = "tls-server")]
mod tls_server;
mod types;

use self::types::{OptionFlags, ReqState};
use super::TelegramApi;
use crate::application::{IntoUpdateHandler, UpdateHandler};
use crate::methods::SetWebhook;
use crate::types::{InputFile, True, Update};
use actix::{Actor, Addr, Context, Handler};
use actix_net::server::Server;
use actix_web::dev::HttpResponseBuilder;
use actix_web::{http::Method, server::HttpServer, App as ActixApp, HttpResponse, Json, State};
use failure::Error;
use futures::Future;

#[cfg(feature = "tls-server")]
pub use self::tls_server::*;

pub struct TelegramServer<F, H, UH>
where
    H: IntoUpdateHandler<Handler = UH> + 'static,
    UH: UpdateHandler + 'static,
    F: Fn() -> H + Send + Clone + 'static,
{
    addr: String,
    host: String,
    url: Option<String>,
    token: String,
    threads: usize,
    factory: F,
    server: Option<Addr<Server>>,
    options: OptionFlags,
    #[cfg(feature = "tls-server")]
    cert_and_key: Option<CertAndKey>,
}

impl<F, H, UH> TelegramServer<F, H, UH>
where
    H: IntoUpdateHandler<Handler = UH> + 'static,
    UH: UpdateHandler + 'static,
    F: Fn() -> H + Send + Clone + 'static,
{
    pub fn new(addr: String, token: String, host: String, factory: F) -> Self {
        Self {
            addr,
            host,
            url: None,
            threads: 1,
            factory,
            server: None,
            token,
            #[cfg(feature = "tls-server")]
            cert_and_key: None,
            options: OptionFlags::default(),
        }
    }

    pub fn set_workers(mut self, num: usize) -> Self {
        self.threads = num;
        self
    }

    pub fn set_url(mut self, url: String) -> Self {
        self.url = Some(url);
        self
    }

    pub fn url(&self) -> &str {
        self.url.as_ref().map_or(self.token.as_str(), |url| url)
    }

    pub fn full_url(&self) -> String {
        format!("{}/{}", self.host, self.url())
    }

    #[cfg(feature = "tls-server")]
    pub fn set_certificate_and_key(mut self, cert_and_key: CertAndKey, self_signed: bool) -> Self {
        self.cert_and_key = Some(cert_and_key);
        self.options.set(OptionFlags::SELF_SIGNED, self_signed);
        self
    }

    #[cfg(feature = "tls-server")]
    pub fn certificate_input_file(&self) -> Option<InputFile> {
        self.cert_and_key
            .as_ref()
            .map(|cert_and_key| cert_and_key.into())
    }

    pub fn set_send_set_webhook(mut self, send_set_webhook: bool) -> Self {
        self.options
            .set(OptionFlags::SEND_SET_WEBHOOK, send_set_webhook);
        self
    }
}

impl<F, H, UH> Actor for TelegramServer<F, H, UH>
where
    H: IntoUpdateHandler<Handler = UH> + 'static,
    UH: UpdateHandler + 'static,
    F: Fn() -> H + Send + Clone + 'static,
{
    type Context = Context<Self>;

    fn started(&mut self, _ctx: &mut Context<Self>) {
        debug!("TelegramServer is alive");
        let token = self.token.clone();
        let url = self.url().to_owned();
        let apps = self.factory.clone();
        let telegram_api = TelegramApi::new(token, 10).start();
        let clone = telegram_api.clone();
        let mut server = HttpServer::new(move || {
            let apps = (apps)().into_handler();
            let telegram_api = clone.clone();
            let state = ReqState { telegram_api, apps };
            ActixApp::with_state(state).resource(&url, |r| r.method(Method::POST).with(handler))
        })
        .workers(self.threads)
        .server_hostname(self.host.clone());
        let mut set_webhook = SetWebhook::new(self.full_url());
        #[cfg(feature = "tls-server")]
        {
            match self.cert_and_key.as_ref() {
                Some(cert_and_key) => {
                    #[cfg(feature = "rust-tls")]
                    {
                        server = server
                            .bind_rustls(self.addr.clone(), cert_and_key.get_acceptor())
                            .unwrap();
                    }
                    #[cfg(feature = "ssl")]
                    {
                        server = server
                            .bind_ssl(self.addr.clone(), cert_and_key.get_acceptor())
                            .unwrap();
                    }
                    #[cfg(feature = "tls")]
                    {
                        server = server
                            .bind_tls(self.addr.clone(), cert_and_key.get_acceptor())
                            .unwrap();
                    }
                    if self.options.contains(OptionFlags::SELF_SIGNED) {
                        set_webhook.set_certificate(Some(cert_and_key));
                    }
                }
                _ => {
                    server = server.bind(self.addr.clone()).unwrap();
                }
            }
        }
        #[cfg(not(feature = "tls-server"))]
        {
            server = server.bind(self.addr.clone()).unwrap();
        }
        if self.options.contains(OptionFlags::SEND_SET_WEBHOOK) {
            telegram_api.do_send(set_webhook);
        }
        self.server = Some(server.start());
    }

    fn stopped(&mut self, _ctx: &mut Context<Self>) {
        debug!("TelegramServer is stopped");
    }
}

impl<F, H, UH> Handler<SetWebhook> for TelegramServer<F, H, UH>
where
    H: IntoUpdateHandler<Handler = UH> + 'static,
    UH: UpdateHandler + 'static,
    F: Fn() -> H + Send + Clone + 'static,
{
    type Result = Box<Future<Item = True, Error = Error>>;

    fn handle(&mut self, msg: SetWebhook, _: &mut Context<Self>) -> Self::Result {
        let telegram_api = TelegramApi::new(self.token.clone(), 10).start();
        println!("set webhook {:?}", msg);
        Box::new(telegram_api.send(msg).from_err().and_then(|res| res))
    }
}

fn handler<H>((update, state): (Json<Update>, State<ReqState<H>>)) -> HttpResponseBuilder
where
    H: UpdateHandler + 'static,
{
    let msg = update.into_inner();
    debug!("TelegramServer.Update received {:?}", msg);
    match state.apps.handle(msg, &state.telegram_api) {
        None => {
            debug!("handled");
        }
        Some(_) => {
            debug!("not handled");
        }
    }
    HttpResponse::Ok()
}