1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
//! The [`Context`] struct.

use crate::{
    core::Event,
    request::{CallbackAPIRequest, Object},
    response::Response,
};
use rvk::{error::Error, methods::messages, objects::Integer, APIClient, Params};
use std::sync::{Arc, Mutex};

/// Stores information necessary for handlers, allows to send the resulting
/// message.
#[derive(Debug)]
pub struct Context {
    group_id: i32,
    event: Event,
    object: Object,
    api: Arc<Mutex<APIClient>>,
    peer_id: Integer,
    response: Response,
}

impl Context {
    /// Creates a new [`Context`].
    ///
    /// # Panics
    /// - no user_id on object (only [`Event::MessageAllow`])
    /// - no from_id on object ([`Event::MessageTypingState`])
    /// - no peer_id on object (other events)
    pub fn new(event: Event, req: &CallbackAPIRequest, api: Arc<Mutex<APIClient>>) -> Self {
        let object = req.object();

        let peer_id = match event {
            Event::MessageAllow => object
                .user_id()
                .expect("no user_id on message_allow object"),
            Event::MessageTypingState => object
                .get_from_id()
                .expect("no from_id on message_typing_state object"),
            _ => object.peer_id().expect("no peer_id on object"),
        };

        Self {
            group_id: req.group_id(),
            event,
            object: object.clone(),
            api,
            peer_id,
            response: Response::new(),
        }
    }

    /// Returns the group ID.
    pub fn group_id(&self) -> i32 {
        self.group_id
    }

    /// Returns the original Callback API event type that caused this handler to
    /// run.
    pub fn event(&self) -> Event {
        self.event
    }

    /// Returns the object associated with the event (given by Callback API).
    pub fn object(&self) -> &Object {
        &self.object
    }

    /// Returns an [`rvk::APIClient`], wrapped into
    /// [`Arc`][`std::sync::Arc`]`<`[`Mutex`][`std::sync::Mutex`]`<...>>`.
    pub fn api(&self) -> Arc<Mutex<APIClient>> {
        Arc::clone(&self.api)
    }

    /// Returns the current pending response object (mutable).
    pub fn response(&mut self) -> &mut Response {
        &mut self.response
    }

    /// Sends the response.
    ///
    /// This does not erase the response object. You can send multiple messages.
    ///
    /// This method currently blocks until the [`rvk::APIClient`] is available,
    /// so only one message is being sent at a given time. This behavior may
    /// change.
    pub fn send(&self) -> Result<(), Error> {
        let api = self.api.lock().map_err(|e| Error::Other(e.to_string()))?;
        let mut params = Params::new();

        params.insert("peer_id".into(), format!("{}", self.peer_id));

        let res = &self.response;
        let msg = res.message();
        let attachments = res.attachments();
        let kbd = res.keyboard();

        if !msg.is_empty() {
            params.insert("message".into(), msg.clone());
        }

        if !attachments.is_empty() {
            params.insert(
                "attachment".into(),
                attachments
                    .iter()
                    .map(ToString::to_string)
                    .fold(String::new(), |acc, v| acc + "," + &v),
            );
        }

        if let Some(kbd) = kbd {
            params.insert(
                "keyboard".into(),
                serde_json::to_string(&kbd).expect("failed to serialize keyboard to String"),
            );
        }

        let random_id: i32 = rand::random();
        params.insert("random_id".into(), format!("{}", random_id));

        trace!("sending message {:#?}", params);

        messages::send(&*api, params).map(|_| ())
    }
}