maxbot 0.1.7

Автоматизация работы с чат-ботами MAX
Documentation
//! Методы для работы с чатами (групповыми и диалогами).

use crate::client::MaxClient;
use crate::error::Result;
use crate::types::{Chat, ChatMember, Message, UpdateChatInfo};

impl MaxClient {
    // -------------------------------------------------------------------------
    // Базовые операции с чатами
    // -------------------------------------------------------------------------

    /// Получает информацию о чате по его идентификатору.
    ///
    /// # Аргументы
    ///
    /// * `chat_id` - числовой идентификатор чата.
    pub async fn get_chat(&self, chat_id: i64) -> Result<Chat> {
        self.request_with_rate_limit(
            reqwest::Method::GET,
            &format!("/chats/{}", chat_id),
            &[],
            None,
        )
        .await
    }

    /// Возвращает список групповых чатов, в которых участвует бот.
    ///
    /// Поддерживает постраничный вывод с помощью параметров `count` и `marker`.
    ///
    /// # Аргументы
    ///
    /// * `count` - количество возвращаемых чатов (от 1 до 100).
    /// * `marker` - маркер для продолжения вывода (значение из предыдущего ответа).
    ///
    /// # Возвращаемое значение
    ///
    /// Кортеж `(чаты, следующий_маркер)`. Если `следующий_маркер` равен `None`, достигнут конец списка.
    pub async fn get_chats(
        &self,
        count: Option<u32>,
        marker: Option<i64>,
    ) -> Result<(Vec<Chat>, Option<i64>)> {
        let mut query = Vec::new();
        if let Some(c) = count {
            query.push(("count", c.to_string()));
        }
        if let Some(m) = marker {
            query.push(("marker", m.to_string()));
        }
        let resp: serde_json::Value = self
            .request_with_rate_limit(reqwest::Method::GET, "/chats", &query, None)
            .await?;
        let chats = serde_json::from_value(resp["chats"].clone())?;
        let next_marker = resp.get("marker").and_then(|v| v.as_i64());
        Ok((chats, next_marker))
    }

    /// Обновляет информацию о чате (название, описание, иконка, ссылка, публичность).
    ///
    /// # Аргументы
    ///
    /// * `chat_id` - идентификатор чата.
    /// * `info` - структура [`UpdateChatInfo`] с полями для обновления (все необязательные).
    pub async fn update_chat(&self, chat_id: i64, info: UpdateChatInfo) -> Result<Chat> {
        let mut body = serde_json::Map::new();

        if let Some(title) = info.title {
            body.insert("title".into(), title.into());
        }
        if let Some(desc) = info.description {
            body.insert("description".into(), desc.into());
        }

        // Иконка: объект с полем url или token (взаимоисключающие)
        if let Some(url) = info.icon_url {
            let mut icon_obj = serde_json::Map::new();
            icon_obj.insert("url".into(), url.into());
            body.insert("icon".into(), icon_obj.into());
        } else if let Some(token) = info.icon_token {
            let mut icon_obj = serde_json::Map::new();
            icon_obj.insert("token".into(), token.into());
            body.insert("icon".into(), icon_obj.into());
        }

        if let Some(link) = info.link {
            body.insert("link".into(), link.into());
        }
        if let Some(is_public) = info.is_public {
            body.insert("is_public".into(), is_public.into());
        }
        if let Some(pin) = info.pin {
            body.insert("pin".into(), pin.into());
        }
        if let Some(notify) = info.notify {
            body.insert("notify".into(), notify.into());
        }

        self.request_with_rate_limit(
            reqwest::Method::PATCH,
            &format!("/chats/{}", chat_id),
            &[],
            Some(serde_json::Value::Object(body)),
        )
        .await
    }

    /// Удаляет групповой чат (только для владельца чата).
    ///
    /// # Аргументы
    ///
    /// * `chat_id` - идентификатор чата.
    pub async fn delete_chat(&self, chat_id: i64) -> Result<()> {
        self.request_with_rate_limit::<serde_json::Value>(
            reqwest::Method::DELETE,
            &format!("/chats/{}", chat_id),
            &[],
            None,
        )
        .await?;
        Ok(())
    }

    /// Позволяет боту покинуть чат.
    ///
    /// # Аргументы
    ///
    /// * `chat_id` - идентификатор чата, из которого нужно выйти.
    pub async fn leave_chat(&self, chat_id: i64) -> Result<()> {
        self.request_with_rate_limit::<serde_json::Value>(
            reqwest::Method::DELETE,
            &format!("/chats/{}/members/me", chat_id),
            &[],
            None,
        )
        .await?;
        Ok(())
    }

    // -------------------------------------------------------------------------
    // Участники и администраторы
    // -------------------------------------------------------------------------

    /// Возвращает список участников чата с постраничным выводом.
    ///
    /// # Аргументы
    ///
    /// * `chat_id` - идентификатор чата.
    /// * `marker` - маркер для следующего вывода (необязательно).
    /// * `count` - количество участников на странице (необязательно).
    ///
    /// # Возвращаемое значение
    ///
    /// Кортеж `(участники, следующий_маркер)`.
    pub async fn get_chat_members(
        &self,
        chat_id: i64,
        marker: Option<i64>,
        count: Option<u32>,
    ) -> Result<(Vec<ChatMember>, Option<i64>)> {
        let mut query = Vec::new();
        if let Some(m) = marker {
            query.push(("marker", m.to_string()));
        }
        if let Some(c) = count {
            query.push(("count", c.to_string()));
        }
        let resp: serde_json::Value = self
            .request_with_rate_limit(
                reqwest::Method::GET,
                &format!("/chats/{}/members", chat_id),
                &query,
                None,
            )
            .await?;
        let members = serde_json::from_value(resp["members"].clone())?;
        let next_marker = resp.get("marker").and_then(|v| v.as_i64());
        Ok((members, next_marker))
    }

    /// Получает список администраторов чата.
    ///
    /// # Аргументы
    ///
    /// * `chat_id` - идентификатор чата.
    pub async fn get_chat_admins(&self, chat_id: i64) -> Result<Vec<ChatMember>> {
        let resp: serde_json::Value = self
            .request_with_rate_limit(
                reqwest::Method::GET,
                &format!("/chats/{}/members/admins", chat_id),
                &[],
                None,
            )
            .await?;
        Ok(serde_json::from_value(resp["admins"].clone())?)
    }

    /// Назначает пользователя администратором чата с указанными правами.
    ///
    /// # Аргументы
    ///
    /// * `chat_id` - идентификатор чата.
    /// * `user_id` - идентификатор пользователя.
    /// * `permissions` - список разрешений (например, `["manage_messages", "delete_messages"]`).
    pub async fn add_chat_admin(
        &self,
        chat_id: i64,
        user_id: i64,
        permissions: Vec<String>,
    ) -> Result<()> {
        let body = serde_json::json!({
            "user_id": user_id,
            "permissions": permissions,
        });
        self.request_with_rate_limit::<serde_json::Value>(
            reqwest::Method::POST,
            &format!("/chats/{}/members/admins", chat_id),
            &[],
            Some(body),
        )
        .await?;
        Ok(())
    }

    /// Снимает права администратора с пользователя.
    ///
    /// # Аргументы
    ///
    /// * `chat_id` - идентификатор чата.
    /// * `user_id` - идентификатор пользователя.
    pub async fn remove_chat_admin(&self, chat_id: i64, user_id: i64) -> Result<()> {
        self.request_with_rate_limit::<serde_json::Value>(
            reqwest::Method::DELETE,
            &format!("/chats/{}/members/admins/{}", chat_id, user_id),
            &[],
            None,
        )
        .await?;
        Ok(())
    }

    /// Добавляет пользователей в чат.
    ///
    /// # Аргументы
    ///
    /// * `chat_id` - идентификатор чата.
    /// * `user_ids` - вектор идентификаторов пользователей.
    pub async fn add_chat_members(&self, chat_id: i64, user_ids: Vec<i64>) -> Result<()> {
        let body = serde_json::json!({ "user_ids": user_ids });
        self.request_with_rate_limit::<serde_json::Value>(
            reqwest::Method::POST,
            &format!("/chats/{}/members", chat_id),
            &[],
            Some(body),
        )
        .await?;
        Ok(())
    }

    /// Удаляет участника из чата.
    ///
    /// # Аргументы
    ///
    /// * `chat_id` - идентификатор чата.
    /// * `user_id` - идентификатор пользователя.
    pub async fn remove_chat_member(&self, chat_id: i64, user_id: i64) -> Result<()> {
        self.request_with_rate_limit::<serde_json::Value>(
            reqwest::Method::DELETE,
            &format!("/chats/{}/members", chat_id),
            &[("user_id", user_id.to_string())],
            None,
        )
        .await?;
        Ok(())
    }

    /// Возвращает информацию о боте как об участнике чата.
    ///
    /// # Аргументы
    ///
    /// * `chat_id` - идентификатор чата.
    pub async fn get_bot_member(&self, chat_id: i64) -> Result<ChatMember> {
        self.request_with_rate_limit(
            reqwest::Method::GET,
            &format!("/chats/{}/members/me", chat_id),
            &[],
            None,
        )
        .await
    }

    // -------------------------------------------------------------------------
    // Действия в чате (печать, загрузка и т.п.)
    // -------------------------------------------------------------------------

    /// Отправляет действие бота в чате (например, "typing", "upload_photo").
    ///
    /// # Аргументы
    ///
    /// * `chat_id` - идентификатор чата.
    /// * `action` - строка, описывающая действие. Допустимые значения:
    ///   `"typing"`, `"upload_photo"`, `"record_video"`, `"upload_video"`,
    ///   `"record_audio"`, `"upload_audio"`, `"upload_document"`, `"find_location"`.
    pub async fn send_chat_action(&self, chat_id: i64, action: &str) -> Result<()> {
        let body = serde_json::json!({ "action": action });
        self.request_with_rate_limit::<serde_json::Value>(
            reqwest::Method::POST,
            &format!("/chats/{}/actions", chat_id),
            &[],
            Some(body),
        )
        .await?;
        Ok(())
    }

    // -------------------------------------------------------------------------
    // Закреплённые сообщения
    // -------------------------------------------------------------------------

    /// Возвращает закреплённое сообщение в чате.
    ///
    /// # Аргументы
    ///
    /// * `chat_id` - идентификатор чата.
    pub async fn get_pinned_message(&self, chat_id: i64) -> Result<Message> {
        self.request_with_rate_limit(
            reqwest::Method::GET,
            &format!("/chats/{}/pin", chat_id),
            &[],
            None,
        )
        .await
    }

    /// Закрепляет сообщение в чате.
    ///
    /// # Аргументы
    ///
    /// * `chat_id` - идентификатор чата.
    /// * `message_mid` - строковый идентификатор сообщения (`mid`).
    pub async fn pin_message(&self, chat_id: i64, message_mid: &str) -> Result<()> {
        let body = serde_json::json!({ "mid": message_mid });
        self.request_with_rate_limit::<serde_json::Value>(
            reqwest::Method::PUT,
            &format!("/chats/{}/pin", chat_id),
            &[],
            Some(body),
        )
        .await?;
        Ok(())
    }

    /// Открепляет закреплённое сообщение в чате.
    ///
    /// # Аргументы
    ///
    /// * `chat_id` - идентификатор чата.
    pub async fn unpin_message(&self, chat_id: i64) -> Result<()> {
        self.request_with_rate_limit::<serde_json::Value>(
            reqwest::Method::DELETE,
            &format!("/chats/{}/pin", chat_id),
            &[],
            None,
        )
        .await?;
        Ok(())
    }
}