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
extern crate glitch_in_the_matrix as gm;
extern crate futures;
extern crate tokio_core;

use std::rc::Rc;
use std::cell::RefCell;
use futures::{Future, Stream};
use gm::types::events::Event;
use gm::errors::*;
use gm::types::sync::SyncReply;
use gm::room::{Room, RoomExt};
use gm::{MatrixClient, MatrixFuture};
use tokio_core::reactor::Handle;

pub type Mx = Rc<RefCell<MatrixClient>>;

pub fn sync_boilerplate<Fut, F, C>(mx: Mx, reply: SyncReply, mut f: F) -> MatrixFuture<Vec<(Room<'static>, C)>>
    where Fut: Future<Item=Vec<C>, Error=MatrixError> + 'static,
          F: FnMut(Mx, &Room<'static>, &Event) -> Fut,
          C: 'static
{
    let mut futs: Vec<Box<Future<Item=Vec<(Room<'static>, C)>, Error=MatrixError>>> = vec![];
    for (room, evt) in reply.iter_events() {
        let rc = room.clone();
        if let Event::Full(ref meta, _) = *evt {
            futs.push(Box::new(
                rc.cli(&mut mx.borrow_mut())
                    .read_receipt(&meta.event_id)
                    .map(|_| vec![]))
            );
        }
        futs.push(Box::new(
            f(mx.clone(), room, evt)
                .map(move |x| x.into_iter()
                     .map(|x| (rc.clone(), x))
                     .collect::<Vec<_>>()
                )
        ));
    }
    Box::new(futures::future::join_all(futs.into_iter())
             .map(|vv| {
                 let mut ret = vec![];
                 for vec in vv {
                     ret.extend(vec.into_iter());
                 }
                 ret
             }))
}
pub trait MatrixBot {
    type Command;
    type SyncFuture: Future<Item=Vec<(Room<'static>, Self::Command)>, Error=MatrixError>;
    type CmdFuture: Future<Item=(), Error=MatrixError>;
    type ErrorFuture: Future<Item=(), Error=MatrixError>;

    fn on_login(&mut self, mx: Mx);
    fn on_sync(&mut self, reply: SyncReply) -> Self::SyncFuture;
    fn on_command(&mut self, room: Room<'static>, command: Self::Command) -> Self::CmdFuture;
    fn on_error(&mut self, room: Option<Room<'static>>, error: MatrixError) -> Self::ErrorFuture;
}
pub struct BoilerplateConfig {
    pub server: String,
    pub username: String,
    pub password: String,
}
pub fn make_bot_future<B>(hdl: Handle, cfg: BoilerplateConfig, mut bot: B) -> MatrixFuture<()>
    where B: MatrixBot + 'static {
    let login = MatrixClient::login(&cfg.username, &cfg.password, &cfg.server, &hdl);
    let fut = login.and_then(move |mx| {
        let ss = mx.get_sync_stream();
        let mx = Rc::new(RefCell::new(mx));
        bot.on_login(mx.clone());
        let bot = Rc::new(RefCell::new(bot));
        ss.skip(1).for_each(move |sync| {
            let bot2 = bot.clone();
            let bot4 = bot.clone();
            let fut = {
                let mut bot = bot.borrow_mut();
                bot.on_sync(sync)
            };
            fut
                .or_else(move |err| {
                    let mut bot = bot4.borrow_mut();
                    bot.on_error(None, err)
                        .map(|_| vec![])
                })
                .and_then(move |commands| {
                    let mut futs = vec![];
                    for (room, cmd) in commands {
                        let r2 = room.clone();
                        let fut = {
                            let mut bot = bot2.borrow_mut();
                            bot.on_command(room, cmd)
                        };
                        let bot3 = bot2.clone();
                        let fut = fut.or_else(move |err| {
                            let mut bot = bot3.borrow_mut();
                            bot.on_error(Some(r2), err)
                        });
                        futs.push(Box::new(fut));
                    }
                    futures::future::join_all(futs.into_iter())
                        .map(|_| ())
                        .map_err(|e| e.into())
                })
        })
    });
    Box::new(fut)
}