safe-vk 1.2.0

A simple library to create your own vk bot for conversations
Documentation
use super::{FromUpdate, FromUpdateParts, Parts, Update};
use crate::{
    responses::{ButtonPressCallback, Members, Message, User},
    util::NdArray,
    Error, RequestBuilder, VK,
};
use serde_json::json;
use std::sync::Arc;

#[derive(Clone)]
#[must_use]
pub struct Ctx<T> {
    request: Arc<RequestBuilder>,
    body: T,
}

impl Ctx<Message> {
    pub async fn reply(&self, message: impl Into<String>) -> crate::Result<()> {
        self.request
            .post(
                VK,
                "messages.send",
                &[
                    ("message", message.into()),
                    ("peer_id", self.message.peer_id.to_string()),
                    ("random_id", String::from("0")),
                ],
                {},
            )
            .await?;
        Ok(())
    }

    /// Send message using random id
    pub async fn reply_rnd(
        &self,
        message: impl Into<String>,
        random_id: String,
    ) -> crate::Result<()> {
        self.request
            .post(
                VK,
                "messages.send",
                &[
                    ("message", message.into()),
                    ("peer_id", self.message.peer_id.to_string()),
                    ("random_id", random_id),
                ],
                {},
            )
            .await?;
        Ok(())
    }

    pub async fn get_users(&self, user_ids: &[i32]) -> crate::Result<Vec<User>> {
        let serialized = serde_json::to_string(&user_ids[0])?;
        let response = self
            .request
            .post(VK, "users.get", &[("user_ids", &serialized)], {})
            .await?;
        Ok(crate::parse_response!(response, Vec<User>)?)
    }

    pub async fn get_members(
        &self,
        offset: Option<u16>,
        count: Option<u16>,
        extended: bool,
    ) -> crate::Result<Members> {
        let mut params = vec![("peer_id", self.message.peer_id.to_string())];

        if extended {
            params.push(("extended", String::from("1")));
        } else {
            params.push(("extended", String::from("0")));
        }

        if let Some(offset_val) = offset {
            params.push(("offset", offset_val.to_string()));
        }
        if let Some(count_val) = count {
            params.push(("count", count_val.to_string()));
        }

        let res = self
            .request
            .post(VK, "messages.getConversationMembers", &params, {})
            .await?;

        return Ok(crate::parse_response!(res, Members)?);
    }

    pub async fn keyboard<T: serde::Serialize, N: NdArray<T>>(
        &self,
        message: impl Into<String>,
        one_time: bool,
        inline: bool,
        buttons: N,
    ) -> crate::Result<()> {
        let dim_1 = buttons.shape().dims()[1];
        let dim_2 = buttons.shape().dims()[0];

        // Ensure that the first dimension (dim_1) is not greater than 5
        // This is to enforce a maximum shape of 5x1 for the array
        // Fore more info: https://dev.vk.com/ru/api/bots/development/keyboard
        if dim_1 > 5 {
            return Err(Error::DimOutOfRange {
                shape: buttons.shape(),
                dim: dim_1 as i32,
            });
        } else if dim_2 > 10 {
            return Err(Error::DimOutOfRange {
                shape: buttons.shape(),
                dim: dim_2 as i32,
            });
        }

        let keyboard = json!({
            "one_time": one_time,
            "inline": inline,
            "buttons": json!(buttons.slice()),
        });

        let keyboard = serde_json::to_string(&keyboard)?;

        self.request
            .post(
                VK,
                "messages.send",
                &[
                    ("message", message.into()),
                    ("peer_id", self.message.peer_id.to_string()),
                    ("keyboard", keyboard),
                    ("random_id", String::from("0")),
                ],
                {},
            )
            .await?;

        Ok(())
    }
}

impl Ctx<Update> {
    pub async fn reply(&self, message: &str) -> crate::Result<()> {
        for update in self.updates.as_ref().unwrap() {
            if let Some(peer_id) = update.object.get("peer_id") {
                self.request
                    .post(
                        VK,
                        "messages.send",
                        &[
                            ("message", message.to_string()),
                            ("peer_id", peer_id.to_string()),
                            ("random_id", String::from("0")),
                        ],
                        {},
                    )
                    .await?;
            }
        }
        Ok(())
    }

    pub async fn keyboard_callback<
        'de,
        T: serde::Serialize,
        A: serde::de::DeserializeOwned + PartialEq + serde::Serialize,
    >(
        &self,
        callback: T,
        payload: A,
    ) -> crate::Result<Option<A>> {
        for update in self.updates.as_ref().unwrap() {
            if let Ok(valid_data) =
                serde_json::from_value::<ButtonPressCallback<A>>(update.object.clone())
            {
                let event_data = serde_json::to_string(&callback).unwrap();
                // Safe unwraps here, because if response has object `payload` then VK api
                // guarantee that it will contain `user_id` field and `peer_id`
                let res = self
                    .request
                    .post(
                        VK,
                        "messages.sendMessageEventAnswer",
                        &[
                            ("event_data", event_data),
                            ("user_id", valid_data.user_id.to_string()),
                            ("event_id", valid_data.event_id),
                            ("peer_id", valid_data.peer_id.to_string()),
                        ],
                        {},
                    )
                    .await?;

                if let Ok(response) = crate::parse_response!(res, EventAnswer) {
                    if response.get_status().is_ok() {
                        return Ok(Some(payload));
                    }
                }
            }
        }
        Ok(None)
    }
}

use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, Debug)]
pub struct EventAnswer(i8);

impl EventAnswer {
    pub fn get_status(&self) -> crate::Result<i8> {
        let status = self.0;
        match status {
            1 => Ok(status),
            _ => Err(Error::EventAnswerUnkownStatus { status }),
        }
    }
}

impl<T> std::ops::Deref for Ctx<T> {
    type Target = T;

    fn deref(&self) -> &Self::Target {
        &self.body
    }
}

impl<S> FromUpdate<S> for Ctx<Message>
where
    S: Send + Sync,
{
    async fn from_update(
        update: Update,
        _state: &S,
        request: Arc<RequestBuilder>,
    ) -> Result<Self, ()> {
        let message: Message =
            serde_json::from_value(update.updates.unwrap()[0].object.clone()).unwrap();

        Ok(Ctx {
            request,
            body: message,
        })
    }
}

impl<S> FromUpdateParts<S> for Arc<RequestBuilder>
where
    S: Send + Sync,
{
    async fn from_update_parts(
        _parts: &mut Parts,
        _state: &S,
        request: Arc<RequestBuilder>,
    ) -> Result<Self, ()> {
        Ok(request)
    }
}

impl<S> FromUpdate<S> for Ctx<Update>
where
    S: Send + Sync,
{
    async fn from_update(
        update: Update,
        _state: &S,
        request: Arc<RequestBuilder>,
    ) -> Result<Self, ()> {
        Ok(Ctx {
            request,
            body: update,
        })
    }
}